Messaging
Messaging lets you bind an external chat (a Telegram DM, a WhatsApp contact, or a Lark/Feishu chat) to a Craft Agent session. Messages you send in that chat drive the agent; the agent’s output is rendered back into the same chat.
Supported Platforms
Section titled “Supported Platforms”Telegram
Bot token (via @BotFather) — works over in-process long-poll or webhook. Supports inline buttons, photos, documents, voice, and video attachments up to 20 MB.
QR-code or code pairing via a Baileys-based worker subprocess. Supports text, media attachments, and a “self-chat” mode so you don’t need a second phone to test.
Lark / Feishu
App ID + App Secret pairing via Lark Open Platform (international) or Feishu Open Platform (China). Use the “Built for agents” Create button on the app-creation page to skip the scope and event-subscription setup. Long-connection mode, rich-text via Lark post, interactive cards, image/file attachments.
How It Works
Section titled “How It Works”Enable a platform in Settings
Open Settings → Messaging and configure Telegram (paste a bot token) or WhatsApp (scan a QR code). Each workspace has its own messaging config.
Bind a chat to a session
From the external chat, send /new to create a fresh session, /bind to pick from recent sessions, or /pair <code> to redeem a pairing code generated from the app.
Chat drives the agent
Once bound, everything you type is forwarded to the agent as a prompt. The agent’s reply is rendered back into the chat using your chosen response mode.
Commands
Section titled “Commands”Available inside any chat — the gateway treats any message starting with / as a command.
| Command | What it does |
|---|---|
/new [name] | Create a new session and bind this chat to it. Optional name. |
/bind | List up to 10 recent sessions. On Telegram, tap an inline button. On WhatsApp, reply with /bind <number>. |
/bind <id> | Bind directly to a session by its ID or by list index. |
/pair <code> | Redeem a 6-digit pairing code generated from the session menu in the app. |
/unbind | Disconnect this chat from its current session. |
/status | Show the bound session, approval channel, and response mode. |
/stop | Abort the current agent run. |
/help | Show available commands. |
Response Modes
Section titled “Response Modes”How the agent’s output is rendered back to the chat.
| Mode | What you see | When to use |
|---|---|---|
progress (default) | One evolving message per run. A ”💭 thinking…” bubble appears on first activity, edits in place as tools run, and is replaced by the final answer when complete. If the run ends on a tool call without emitting a non-intermediate final, the most recent assistant text is used as the answer instead of leaving the thinking bubble behind. | Keep the chat tidy — most users. |
streaming | Live edits during the final turn, plus every intermediate text_complete as its own message. Multiple messages per run. | Parity with the in-app streaming experience. |
final_only | Silent until the run completes, then one message with the final text. If no non-intermediate final arrived, the most recent assistant text is posted instead; genuinely empty runs stay silent. | Quiet chats, batch-style workflows. |
Response mode is configured per binding — you can have one chat on progress and another on final_only in the same workspace.
Pairing Codes
Section titled “Pairing Codes”When you already have a session open in the app and want to continue it from your phone, use a pairing code instead of typing the session ID:
Generate a code from the session menu
In the app, open the session you want to bind, click the three-dot menu, and choose Pair to messaging…. A 6-digit code is shown.
Redeem it from the chat
In the external chat, send /pair 123456 (use your real code). The gateway validates the code, binds the chat, and confirms.
Security:
- Codes expire after a short TTL.
- The
/paircommand is rate-limited per sender — wrong guesses still consume the budget. - Codes only work inside the workspace that issued them.
Attachments
Section titled “Attachments”Supported on Telegram: photos, documents, voice messages, video, and audio. Files are downloaded to a temp location and forwarded to the session as FileAttachment objects — the same way uploads from the app are handled. Hard cap: 20 MB per attachment.
WhatsApp attachment forwarding follows the same pattern; platform-specific limits and MIME handling are documented in the WhatsApp page.
Approval Channel
Section titled “Approval Channel”When the bound session is in Ask permission mode, the agent asks for approval before running a bash command. The approvalChannel per binding decides where that prompt appears:
| Value | Behaviour |
|---|---|
chat (Telegram default) | The approval prompt is posted to the chat — you reply Approve/Deny inline. |
app (WhatsApp default, enforced) | The prompt is shown only in the desktop app. WhatsApp bindings don’t support inline approvals. |
The session’s own permission mode is still authoritative — approvalChannel only controls where the prompt is shown, not whether it happens.
Plan Submission
Section titled “Plan Submission”When the agent submits a plan in Explore mode, Telegram bindings get inline ✅ Accept plan / ♻️ Accept & compact buttons (plus the plan content inline or as a plan.md attachment). WhatsApp bindings get a text pointer telling you to open the desktop app — plans can’t be accepted from WhatsApp yet. See WhatsApp → Plan Submission for the reasons.
Security & Scope
Section titled “Security & Scope”- Per-workspace. Each workspace has its own messaging config, bindings, and pairing codes. Binding in workspace A never accepts codes issued by workspace B.
- Plan-token revocation. Plan tokens (used for bash-approval flows) are keyed by binding — rebinding a chat invalidates outstanding tokens for the old binding.
- Rate limiting.
/pairis throttled per sender. Inbound messages are routed through a per-binding queue so spam doesn’t back-pressure other bindings. - No group/channel chats. Telegram group and channel messages are rejected at the adapter boundary — only private DMs can drive a session.
Configuration Location
Section titled “Configuration Location”Messaging config is persisted per workspace:
~/.craft-agent/workspaces/{workspaceId}/messaging/ ├── config.json # platform enable flags ├── bindings.json # chat → session mappings └── whatsapp-session/ # Baileys credentials (WhatsApp only)Edits to bindings.json take effect on the next inbound message. Deleting whatsapp-session/ forces a re-pair.
Headless Server
Section titled “Headless Server”The gateway also runs inside the standalone headless Bun server (packages/server). Telegram uses webhook mode on the server (you configure a webhook URL), while WhatsApp still runs its Baileys worker subprocess. See Server for deployment details.