A pi extension that bridges Discord and Telegram channels to a sandboxed pi session. Each connected channel gets its own Gondolin micro-VM with persistent workspace, shared storage, memory, and skills.
# Install
pi install /path/to/pi-chat
# or
pi -e /path/to/pi-chat
# Configure accounts and channels
/chat-config
# Connect
/chat-connect- QEMU installed (
brew install qemuon macOS) - Gondolin guest image (downloaded automatically on first connect)
- A Discord bot token or Telegram bot token
- Discord server channels and Telegram DMs/groups
- Gondolin VM sandbox per connection — tools run inside an isolated Alpine Linux micro-VM
- Persistent workspace and shared storage across sessions
- Streamed preview responses with edit-in-place
- Reply-to-trigger — bot replies are attached to the triggering message
- Durable memory — account-wide and channel-specific memory files
- Skills — agent-created reusable tools, auto-discovered and injected into the prompt
- Encrypted secret exchange — securely pass credentials via browser-based encryption
- Remote control — stop, compact, new session, and status via chat commands
- Chat history tool for searching older messages
- File attachments — send and receive files between chat and the VM
- Create a bot at Discord Developer Portal
- Enable Message Content Intent under Bot settings
- Run
/chat-config→ Create account → Discord - Enter your bot token
- Invite the bot to a server (the setup flow provides the invite URL)
- Select a server and configure channels
- Create a bot via @BotFather
- Run
/chat-config→ Create account → Telegram - Enter your bot token
- Add DMs or groups through the guided setup
| Command | Description |
|---|---|
/chat-config |
Configure accounts, channels, and secrets |
/chat-connect |
Connect to a configured channel |
/chat-disconnect |
Disconnect the current channel |
/chat-status |
Show connection status, model, usage, context |
/chat-list |
List configured channels |
/chat-new |
Start a new pi session, keeping the chat connection |
Users in the connected chat can send these commands (with or without mentioning the bot):
| Command | Effect |
|---|---|
stop |
Abort the current turn |
status |
Show model, usage, context stats |
compact |
Trigger context compaction |
new |
Start a new pi session |
Everything lives under ~/.pi/agent/chat/:
~/.pi/agent/chat/
├── config.json # Accounts, channels, secrets
├── cache/ # Discovery cache
└── accounts/<account>/
├── shared/ # Mounted as /shared in VM
│ ├── memory.md # Account-wide persistent memory
│ └── skills/ # Account-wide skills
└── channels/<channel>/
├── channel.jsonl # Chat log
├── .lock # Runtime lock
├── workspace/ # Mounted as /workspace in VM
│ ├── memory.md # Channel-specific persistent memory
│ ├── skills/ # Channel-specific skills
│ ├── incoming/ # Downloaded attachments
│ ├── .secrets/ # Encrypted secrets
│ └── SYSTEM.md # Environment modification log
└── gondolin/ # VM state
└── session.json
Each connection starts a Gondolin micro-VM with:
- Alpine Linux with bash pre-installed
/workspace→ channel workspace directory/shared→ account shared directory- Tools:
read,write,edit,bash - All outbound HTTP/TLS open by default
The agent sees /workspace as its working directory.
Two persistent memory files, injected into the system prompt on every turn:
| File | VM Path | Scope |
|---|---|---|
| Account memory | /shared/memory.md |
Shared across all channels for this account |
| Channel memory | /workspace/memory.md |
Specific to this channel |
The agent is instructed to write durable facts and preferences to these files when asked to remember something. Account-wide goes to /shared/memory.md, channel-specific to /workspace/memory.md.
The agent can create reusable tools as skills, following the Agent Skills standard:
- Account-wide:
/shared/skills/ - Channel-specific:
/workspace/skills/
A skill is either a single .md file (e.g. skills/foo.md) or a directory with SKILL.md plus supporting files (e.g. skills/foo/SKILL.md, skills/foo/run.sh).
Each skill needs YAML frontmatter:
---
name: skill-name
description: Short description of what this skill does
---Skills are automatically discovered and listed in the system prompt. The agent reads the full skill file before using it.
Configure secrets at three levels via /chat-config:
- Global — shared across all accounts
- Per account — shared across channels of that account
- Per channel — specific to one channel
Each secret has a value and allowed host patterns. Gondolin replaces placeholder env vars with real values only for outbound HTTP requests to allowed hosts. The agent never sees the real secret value.
For credentials the agent needs at runtime (API keys for skills, OAuth files, etc.):
- Agent calls the
chat_request_secrettool - A link to
pi.dev/secretis sent to the chat with an embedded public key - User clicks, pastes the secret, and gets an encrypted blob
- User pastes the blob back into chat
- pi-chat decrypts it (RSA-OAEP + AES-256-GCM) and stores it at
/workspace/.secrets/<name> - Agent is notified and can use the file
The encrypted blob is useless without the ephemeral private key held in pi-chat's memory.
| Tool | Description |
|---|---|
read |
Read files (routed through Gondolin VM) |
write |
Create/overwrite files |
edit |
Precise in-place edits |
bash |
Execute commands (runs /bin/bash in the VM) |
chat_history |
Search older messages from the chat log |
chat_attach |
Queue files to send with the next reply |
chat_request_secret |
Request a secret from the user via encrypted exchange |
pi-chat includes vendored/adapted logic inspired by Vercel Chat SDK (MIT):
src/render/format.tssrc/render/streaming-markdown.tssrc/render/streaming.ts
MIT