> ## Documentation Index
> Fetch the complete documentation index at: https://docs.myrmagent.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Multi-Channel

> Connect your agent to 35+ messaging platforms with channel-aware output.

# Multi-Channel Integration

Access your agent from any messaging platform — 35+ channels with automatic format adaptation.

## Supported Channels

| Category          | Channels                                                                  | Count |
| ----------------- | ------------------------------------------------------------------------- | ----- |
| Instant Messaging | Discord, Slack, Telegram, WhatsApp, Signal, Line, Matrix, Mattermost, IRC | 9     |
| China Ecosystem   | WeChat, WeCom, WeCom AIBot, DingTalk, Feishu, QQ, OneBot, WeChat Official | 8     |
| Developer         | GitHub (Webhook + PR Comment)                                             | 1     |
| Enterprise        | Microsoft Teams, Google Chat                                              | 2     |
| Voice             | Discord Voice (with DAVE E2E encryption)                                  | 1     |
| Other             | Email, SMS, iMessage, Webhook, Zalo                                       | 5     |

## iMessage Deep Integration

Connect to iMessage through [BlueBubbles](https://bluebubbles.app/) — a free, open-source macOS relay. Myrm implements the full BlueBubbles Private API for a native iMessage experience:

| Feature                   | Description                                                                                         |
| ------------------------- | --------------------------------------------------------------------------------------------------- |
| Quoted Replies            | Agent responses reference the original message with a visible quote bubble                          |
| Read Receipts             | Incoming messages are marked as "Read" — users know the agent received their message                |
| Tapback Reactions         | Agent can react with native iMessage tapbacks (heart, thumbs up/down, laugh, exclamation, question) |
| Typing Indicator          | "Typing..." bubble appears while the agent processes (55s auto-refresh keepalive)                   |
| Webhook Auto-Registration | Configure once — webhook registers on start, unregisters on stop. No manual BlueBubbles setup       |
| Media Attachments         | Full support for images, videos, audio, documents, and contact cards (vCard)                        |
| Group Chat                | Supports group conversations with per-group context and sender identification                       |

### Setup

1. Install [BlueBubbles](https://bluebubbles.app/) on a macOS machine with iMessage active
2. Navigate to **Settings > Channels > iMessage** in the GUI
3. Enter your BlueBubbles **API URL** and **password**
4. Optionally provide a **Webhook URL** — the agent registers it automatically on connect
5. Enable **Private API** in BlueBubbles for quoted replies, read receipts, and typing indicators

### Graceful Degradation

When the BlueBubbles Private API or its helper process is unavailable, all features that require it (typing indicators, read receipts, quoted replies) are silently skipped — no wasted network requests, no errors in logs. Basic messaging continues to work without interruption.

## Setup

Navigate to **Settings > Channels** in the GUI to connect a platform. Each channel has its own configuration panel with connection status indicators.

### WeChat (via iLink)

Connect your personal WeChat account in under a minute with QR code login:

1. Navigate to **Settings > Channels > WeChat** in the GUI
2. Click **Scan to Connect** — a QR code dialog appears
3. Open WeChat on your phone and scan the QR code
4. Confirm on your phone — the dialog updates in real-time (SSE streaming)
5. Done — your WeChat is connected

The login flow provides real-time status updates: generating QR → waiting for scan → verifying → connected. If the QR code expires, click **Retry** to generate a fresh one.

**Typing indicator reliability**: iLink typing tickets expire after 600 seconds. For long-running agent tasks (>10 minutes), the system proactively refreshes tickets before expiry (540s threshold with 60s safety buffer) using a monotonic clock for drift safety. This ensures the "typing..." indicator always disappears when the agent finishes — no frozen typing states.

### WeChat Official Account (draft publishing)

For **subscription/service accounts** (not personal WeChat), configure **AppID + AppSecret** and push formatted HTML to the **draft box** after WebUI confirmation.

Full guide: [WeChat Official Account Publishing](/docs/guides/wechat-official-publishing).

1. **Settings → Channels → WeChat Official** — AppID, AppSecret, IP whitelist, test connection.
2. Format vault Markdown with **wechat-article-formatter** → `.wechat.html` artifact.
3. Preview in chat → **Push to WeChat Draft** (title + cover; first inline image auto-suggested).
4. Publish manually from [mp.weixin.qq.com](https://mp.weixin.qq.com/) draft box.

<Note>
  Draft push is **HITL only** — no Agent draft tool (Prompt Cache–friendly, no accidental mass send).
</Note>

### WhatsApp (via Baileys)

1. Navigate to **Settings > Channels > WhatsApp** in the GUI
2. Click **Scan to Connect** — same QR code dialog as WeChat
3. Open WhatsApp on your phone → **Linked Devices** → scan the QR code
4. Connection established automatically

Both WeChat and WhatsApp use the same `AsyncLoginProtocol` — consistent UX across channels.

### Email (IMAP + SMTP)

1. Open **Settings > Channels > Email**
2. Configure IMAP (inbox) and SMTP (outbox) server, port, and credentials
3. Save — polling starts automatically

**Smart Forwarded Email Parsing**: Forward any email to your Agent, and it automatically separates your instructions from the original email content.

* Supports Gmail, Outlook, QQ Mail, 163 Mail, Foxmail, and other major clients
* Three-layer detection: Subject prefix (Fwd:/FW:/转发:/轉發:) → Body separator → MIME attachment
* Just write "expense this" or "translate to English" when forwarding — Agent sees both your instruction and the full original email, then executes precisely
* Full CJK email client support with adaptive charset decoding (gb2312/GBK/UTF-8)

**Smart HTML Cleaning**: When emails contain only HTML (Amazon orders, Jira notifications, Outlook rich text), the content is automatically converted to clean Markdown — preserving tables, links, and lists while reducing content size by 65–74% (measured), so the Agent understands faster and more accurately.

**Multi-Agent Email Routing**: If you have multiple Agents configured, start your email body with a slash command to route to a specific agent:

* `/coder review this PR` → routes to your "coder" agent
* `/accountant expense this` → routes to your "accountant" agent
* No command → routes to default agent

### Telegram

Two paths to connect Telegram — choose whichever fits your situation:

**Path A: Onboarding Wizard (recommended for first-time users)**

During the initial setup wizard, a **Telegram Personal Assistant** step appears after configuring your model and routing preferences:

1. Paste your **Bot Token** (get one from [@BotFather](https://t.me/BotFather))
2. Optionally enter your **Webhook URL** (required only for VPS / cloud deployments behind a public domain)
3. Optionally name your assistant (defaults to `Myrm Assistant`)
4. Click **Activate** — the system atomically validates the token, writes credentials, sets DM policy to "open", and binds your default Agent

If any step fails, all changes are automatically rolled back — no half-configured state. The wizard also handles concurrent submissions gracefully: if another request is already in progress, it retries automatically and shows a friendly message instead of a raw error.

**Path B: Settings page (for adding Telegram later)**

1. Navigate to **Settings > Channels > Telegram**
2. Enter your **Bot Token** and optional **Webhook URL**
3. Save — the bot connects and begins receiving messages

**Webhook vs. Polling**: Without a webhook URL, the bot uses long-polling (suitable for local / desktop setups). With a webhook URL, Telegram pushes updates to your server (required for VPS / cloud deployments where the bot must be reachable from the internet).

**Rich Message rendering**: Telegram supports native Rich Message via Bot API 10.1 — tables, LaTeX formulas, and nested lists render natively. See [Telegram Rich Message](#telegram-rich-message-bot-api-101) for details.

### SMS (Twilio)

1. Open **Settings > Channels > SMS**
2. Enter your Twilio **Account SID**, **Auth Token**, and **Phone Number** (E.164 format, e.g. `+15551234567`)
3. Copy the auto-generated **Webhook URL** and paste it into your Twilio Console > Phone Numbers > Messaging webhook
4. Click **Test Connection** to verify

* Bidirectional: receive SMS from any phone → Agent processes → replies via SMS
* Long messages are automatically split at natural boundaries (max 1600 chars per segment)
* Twilio signature verification ensures only legitimate messages are processed

## Message Types

All channels support:

* Text messages with platform-appropriate formatting
* Image / document / audio / video attachments (`MediaType` enum)
* File uploads with automatic type detection

## Bi-directional File Transfer

Agent-generated files are automatically pushed to IM channels — no manual download needed.

### Inbound (User → Agent)

When you send a document (PDF, Excel, Word, etc.) to the agent via IM, the content is automatically extracted and injected into the LLM context. The agent immediately understands the file without needing to call a file-reading tool.

### Outbound (Agent → User)

When the agent creates files (charts, reports, spreadsheets, compiled documents), they are automatically delivered as media attachments in the chat. Supported artifact types include all files generated through code execution, file writes, or document generation tools.

| Feature          | Details                                                     |
| ---------------- | ----------------------------------------------------------- |
| Size limit       | 5 MB per file (larger files gracefully skipped)             |
| Failure handling | Automatic retry with media-strip fallback — text never lost |
| Supported types  | IMAGE, DOCUMENT, AUDIO, VIDEO (auto-detected from filename) |
| Multi-file       | All generated files delivered as separate attachments       |

### Artifact Deep Links

For interactive artifacts (HTML pages, PDFs, documents), the agent automatically generates a clickable **deep link button** in the IM message instead of sending the raw file. Tapping the button opens the artifact directly in the user's browser — no download needed.

| Feature            | Details                                                             |
| ------------------ | ------------------------------------------------------------------- |
| Trigger            | Automatically for shareable artifact types (HTML, PDF, Document)    |
| Security           | HMAC-signed, time-limited, read-only share tokens                   |
| Multiple artifacts | Each gets a named button (e.g. "💻 dashboard.html")                 |
| Redundancy removal | Raw file attachment is removed when a deep link is available        |
| Safe degradation   | Falls back to raw file if no public URL, DB error, or token failure |
| i18n               | Button labels localized (English / Chinese)                         |

## Channel-Aware Output Adaptation

Myrm uses a **triple-layer** architecture to ensure output is both clean and correctly formatted on every platform:

### Outbound Noise Filtering

Each channel has its own `RenderStyle` configuration controlling what content the user sees:

| Control                | Options                  | Default                                                     |
| ---------------------- | ------------------------ | ----------------------------------------------------------- |
| `reasoning_display`    | OFF / COLLAPSED / INLINE | OFF (WeChat, WeCom, WhatsApp) or COLLAPSED (Telegram)       |
| `tool_summary_display` | OFF / COMPACT / DETAILED | OFF (WeChat, WeCom, WhatsApp) or COMPACT (Feishu, DingTalk) |

With defaults, IM users only receive **progress labels, final results, and decision prompts** — no tool call parameters, reasoning tokens, or thinking tags. The `strip_thinking_tags()` function provides an additional safety net that removes any LLM thinking blocks that leak through.

Progress labels are enriched with **input summaries** so you see exactly what the agent is doing:

* `🔍 Searching: AI coding tools 2025` (not just "Searching the web...")
* `📄 Reading: config/settings.yaml` (not just "Reading file...")
* `⏳ **notion_query** — Search meeting notes` (custom MCP tools get automatic coverage)

All tools are covered — registered tools show their specific operation, and unregistered tools (including any MCP server you configure) get a generic fallback with input extraction. Updates are throttled (2s minimum interval) to avoid API rate limits.

### Pre-hoc Guidance (channel\_output\_hints)

At agent initialization, a channel-specific format hint is injected into the system prompt. This tells the LLM what the platform supports *before* it generates content:

* **Telegram**: Tables, LaTeX, and nested lists render natively via Bot API 10.1 Rich Message; the hint is only active when Rich is unavailable
* **WhatsApp**: "Markdown does NOT render — use plain text only"
* **Discord**: "Messages over 2000 chars are split automatically"
* **Voice**: "Speak as you would to a person in conversation"

28 channel hints are pre-configured and KV-Cache safe (injected once at init).

### IM Behavior Persona (auto-injected)

For restricted IM channels (no message editing, no Markdown support), the system automatically injects a behavioral strategy:

* **Concise chat style**: replies like a chat conversation, not a textbook
* **Long content → file + summary**: content exceeding \~200 characters is written to a workspace file; the IM reply contains only a one-sentence summary + Artifact deep link
* **Focused clarification**: asks at most 1-2 key questions when user intent is unclear

Auto-triggered channels: WeChat iLink, WeChat Official, Signal, iMessage, LINE, SMS, OneBot(QQ), IRC, Zalo, Voice (10 total).

Zero configuration required — detected automatically from `ChannelCapabilities`. Channels with Markdown or edit support (Telegram, Discord, Slack, etc.) are unaffected.

### Post-hoc Format Downgrade (renderer)

Even if the LLM ignores hints, the rendering pipeline applies format downgrades:

* `supports_tables=false` → Markdown tables converted to bullet lists
* `supports_latex=false` → LaTeX formulas stripped to plain text
* HTML/SVG code blocks replaced with short placeholders on IM channels
* Orphan citation markers auto-cleaned when no sources are present

### Per-Message Cost Display

When `enableCostEstimation` is enabled (default: true), each agent response in IM channels includes a cost footer showing the model used, token count, and estimated cost:

```
💰 claude-sonnet-4-20250514 | 2.5k tokens | ~$0.0035
```

* **Zero-cost replies are hidden** — cached or free-model responses show no footer
* **Emoji toggle** — disable the `💰` prefix via user config while keeping the data
* **Feishu cards** — cost appears as a native `note` element alongside the timestamp
* **Precision** — costs always show 4 decimal places for accurate tracking

This works automatically with no additional configuration. The cost data is extracted from the harness `token_usage` event during streaming.

### Smart Message Splitting

Long messages are automatically split at natural boundaries:

* Code fence state machine ensures code blocks are properly closed and reopened across chunks
* Intelligent line splitting at whitespace and punctuation boundaries
* Configurable overflow tolerance for semantic preservation
* Per-channel length limits (e.g., Discord 2000 chars, Telegram 4096 HTML / 32000 Rich Message)

### Telegram Rich Message (Bot API 10.1)

Telegram channels support native Rich Message rendering with a 3-tier fallback:

1. **Rich Message** — tables, LaTeX formulas, and nested lists render natively (32KB limit)
2. **HTML** — auto-converted with ASCII monospace table degradation (4096 UTF-16 limit)
3. **Plain text** — last resort when HTML parsing fails

Streaming uses **flicker-free draft previews** (`sendRichMessageDraft` → `sendMessageDraft` → `editMessageText`), with a 20-char minimum threshold to suppress initial token flicker. CJK content receives full Rich formatting — no degradation or workarounds.

## Group Chat Features

### AllowPolicy

Three preset policies control when the agent responds in group chats:

| Policy    | DM               | Group                         |
| --------- | ---------------- | ----------------------------- |
| OPEN      | Allow all        | Allow all (no mention needed) |
| SELECTIVE | Allow all        | Mention required              |
| STRICT    | Mention required | Mention required              |

### GroupTriggerMode

Fine-grained control over when the agent activates in group chats:

| Mode          | Behavior                                                                        |
| ------------- | ------------------------------------------------------------------------------- |
| ALL           | Every message in the group triggers the agent                                   |
| MENTION\_ONLY | Only responds when @mentioned                                                   |
| PREFIX        | Responds when message starts with a configured prefix (prefix is auto-stripped) |

### Topic Binding

Three-tier granularity binds different agents to different conversation scopes:

* **Thread level**: Bound to a specific topic/thread
* **Chat level**: Bound to a specific group/conversation
* **Channel level**: Bound to the entire channel

Lookup order is Thread→Chat→Channel with graceful fallback to the default agent.

### Topic Workspace Binding (Vault / Project)

Bind each IM topic to a real workspace so remote messages do not fall into an empty JIT sandbox.

| Method       | How                                                                  |
| ------------ | -------------------------------------------------------------------- |
| **GUI**      | **Settings → Channel Routing** → per-topic **Project** dropdown      |
| **IM**       | `/bind workspace=project:<uuid>` or `/bind workspace=/path/to/vault` |
| **Combined** | `/bind agent=my-agent workspace=project:<uuid>`                      |

Before each Agent run, the system syncs the topic binding into the chat SSOT (`project_id` or `workspace_dir`) using the same `resolve_effective_chat_workspace` chain as WebUI sessions. Unbinding clears both fields on the chat. If the Project or path is unavailable, execution fails loudly instead of silently using an empty sandbox.

**Honest limits:** IM `/status` still shows `project:uuid` (not a friendly name). There is no GUI path picker — GUI users bind Projects; path binding is IM-first.

**Test coverage:** 9 server pytest cases (sync, topic\_config validation, unbind clear, effective\_workspace chain). Frontend label helper: 3 vitest cases.

**vs OpenClaw:** OpenClaw binds `workspaceDir` at the Agent config level — one workspace per Agent. Myrm binds at Topic granularity, so the same Agent can serve different Telegram threads pointing at different vaults.

### Obsidian Vault Write Fidelity

When a topic is bound to an Obsidian vault, Agent edits go to your **real vault files** (not a copy). Myrm adds two code-level safeguards:

1. **Frontmatter preserve** — if the LLM drops the `---` YAML block while editing the body, the write guard reinjects the pre-edit frontmatter before save (`date:`, `tags:`, Dataview fields stay intact).
2. **No prettier on vault notes** — FormatObserver skips `.md` files under a `.obsidian/` vault root so automated formatting cannot strip metadata.

**Honest limits:** Block-level reinject only (not field-by-field merge). Wikilink integrity relies on the `obsidian-notes` skill — no vault-wide backlink scan yet.

**Tests:** 44 pytest cases (Jul 2026): harness guard + server parser + service integration + format observer.

### Thread Follow-Up

After the agent responds in a thread, a **GroupFollowUpTracker** activates for that thread (TTL: 10 minutes). During this window, subsequent messages in the same thread don't require @mention — the agent automatically continues the conversation, just like a human would.

To stop the follow-up, send `/mute`, `/shutup`, `闭嘴`, or `别吵` in the thread. The agent immediately stops responding. To reactivate, simply @mention the agent again.

### Guest Mention

Even in groups not explicitly enabled for the agent, users can @mention the agent for a one-shot response (if guest mode is enabled). This allows ad-hoc AI assistance without adding the group to the whitelist.

### Precise Bot ID Matching

Every channel provider performs exact bot identity matching — Telegram checks `bot_username`, Feishu checks `bot_open_id`, Teams checks `app_id`, WhatsApp checks `mentionedJids`. This means @mentions are always precisely routed to the correct bot, with zero false triggers across all 35+ channels.

### GroupContextBuffer

Non-trigger messages in group chats are accumulated in a per-group ring buffer. When a trigger message arrives (e.g., @agent), the buffer is drained and injected as context — giving the agent conversational awareness of the group discussion.

### SessionGate

Rapid-fire messages from the same conversation are debounced (default 300ms window) and merged into a single request, preventing duplicate processing.

## Agent-Initiated Notifications

Your agent can proactively push notifications to configured channels — no manual polling needed.

### How It Works

1. In **Agent settings → Notification Channels**, pick a channel from your **currently running** integrations (dynamic list with human-readable names — new channels appear without a frontend release), then select a recipient from paired contacts (or enter an ID manually)
2. The agent calls `channel_notify_tool` when it needs to alert you (task completion, anomaly detection, scheduled report ready)
3. Optionally attach files or images — local paths and URLs are auto-resolved with correct media type detection
4. Delivery uses `send_tracked` for a synchronous success/failure result (same retry policy as critical channel messages), not fire-and-forget queue drops

> **vs Notification Deliveries**: **Agent settings → Notification Channels** whitelists `channel_notify_tool` only. **Settings → Notification Deliveries** routes **system events** (OAuth, budget, pairing, channel health). They are not interchangeable.

### Security

| Layer               | Protection                                                                                                                      |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| Whitelist           | Only user-configured targets are reachable — no arbitrary recipients                                                            |
| Rate limit          | Per-session cap prevents notification spam (default: 10 per session)                                                            |
| Content cap         | Messages exceeding `max_body_length` (4000 chars) are automatically truncated                                                   |
| Attachments         | Local paths must stay within the agent workspace (`declared_allowed_roots`); URL filenames parsed safely (handles query params) |
| Audit trail         | Every notification target used is recorded in session state                                                                     |
| Sub-agent isolation | Sub-agents cannot call `channel_notify_tool` by default (harness L1 blocklist)                                                  |
| Failure visibility  | Sync delivery failures enter DLQ and trigger a WebUI toast (presync dedupe; not persisted across restarts)                      |
| Layer               | Server `outbound_notify/` (business layer, not harness kernel), co-located with ChannelGateway                                  |

> **Test coverage (2026-07-07)**: **127 pytest + 1 live agent-stream E2E (MiniMax-M2.7, RUN\_E2E\_TESTS=1) + 7 vitest + Chrome full user flow round42 (source chat tool steps + recipient inbox UI)** passing — ChatChannel in-app inbox delivery, DLQ/toast chain, sub-agent isolation, attachment path sandbox, dynamic running-channel picker verified.

### Example Scenarios

* Scheduled task completes at 3 AM → agent sends summary + report PDF to your Telegram
* Long-running code analysis finishes → results + generated chart pushed to Slack channel
* Anomaly detected in monitored data → alert with dashboard screenshot sent to configured target

## Web Push (Offline Notifications)

Receive critical alerts even after closing your browser — no native app required.

When the PWA is closed or backgrounded, the server sends push notifications via the W3C Web Push standard (VAPID). This covers scenarios where IM channels are not configured or the user simply wants browser-native alerts.

### Supported Events

| Event                | Example                                                 |
| -------------------- | ------------------------------------------------------- |
| Approval request     | Agent needs permission to execute a sensitive operation |
| Goal completed       | Background task finished successfully                   |
| Goal failed          | Task encountered an unrecoverable error                 |
| Goal verification    | Results ready for your review                           |
| Health alert         | System detected a service disruption                    |
| Budget alert         | Usage approaching configured limits                     |
| Background task done | Long-running task completed                             |
| System notification  | Security events, pairing requests, OAuth callbacks      |

### Setup

1. Navigate to **Settings > System > Push Notifications**
2. Toggle **Enable** — your browser asks for notification permission
3. Done. Notifications arrive even with the browser tab closed.

A **Test** button lets you verify delivery immediately.

### Platform Notes

| Platform                                 | Support                                    |
| ---------------------------------------- | ------------------------------------------ |
| Desktop browsers (Chrome, Firefox, Edge) | ✅ Full support                             |
| Android (Chrome, Firefox)                | ✅ Full support                             |
| iOS / iPadOS (Safari 16.4+)              | ✅ Requires "Add to Home Screen" (PWA mode) |
| Tauri desktop app                        | N/A — uses native OS notifications instead |

On iOS, the card automatically detects whether the app is running in standalone PWA mode and shows an install guide if needed.

### Security & Maintenance

* **VAPID key auto-generation** — server creates and persists keys on first boot, zero configuration
* **Expired subscription cleanup** — push failures (410/404) automatically remove stale subscriptions
* **No third-party service** — pushes go directly to browser vendor endpoints (Google FCM, Apple APNs, Mozilla autopush)
* **Tauri-aware** — the setting card is hidden in desktop app builds where native notifications already work

### One-click approval deep links

Tapping a push notification opens the exact chat and **approval drawer** — not just the homepage.

* **Approval requests** navigate to `/{chat_id}?approval={id}`; the WebUI opens the global ApprovalDrawer and strips the query param for a clean URL
* **Service Worker routing** sanitizes same-origin paths and, when your chat tab is already open, calls `navigate()` instead of focus-only — so a new approval on an open tab still opens the drawer (OpenClaw's SW only compares pathname and misses this)
* **Chrome MCP E2E verified** — hot tab + cold start deeplink paths (2026-07)

## Background Task Auto-Reply

When you start a background task via `/btw` in any IM channel, the result is automatically pushed back to your original conversation when the task finishes:

* **Thread-precise delivery** — replies land in the exact thread where you started the task
* **Localized notifications** — messages respect your language preference (English, Chinese, and more)
* **Failure alerts** — if a task fails, you get an error summary instead of silence
* **Reliable delivery** — uses the same retry infrastructure as all channel messages
* **Non-disruptive** — runs independently of your notification settings; this is a direct reply, not a broadcast

Start a task in Discord, switch to something else, and come back to find the result waiting in your thread.

## Long-Task Heartbeat (Edit-in-Place)

When the agent works on a complex task for an extended period without sending any output, it automatically provides a live heartbeat message so you know it hasn't stalled — without flooding your chat with multiple notifications.

### How It Works

1. A parallel background monitor tracks the time since the last activity (message sent, progress update, etc.)
2. After 2 minutes of silence, a heartbeat message is sent to the originating IM channel
3. On subsequent heartbeats, the **same message is edited in place** with updated elapsed time, step count, and stage — no new messages are created
4. If the channel does not support message editing, only a single message is sent (no spam)
5. Up to 3 heartbeat cycles per task
6. Uses NORMAL priority — the heartbeat arrives silently without triggering a push notification on your phone

### Example Message

> ⏳ Working — 3 min (12 steps, data analysis)

The message updates in place as the task progresses:

> ⏳ Working — 5 min (18 steps, code generation)

### Smart Silence Detection

Unlike simple interval timers, the monitor **resets whenever the agent produces output**. If the agent sends a progress update at minute 1, the 2-minute silence window restarts from that point. This avoids unnecessary reminders when the agent is actively communicating.

### Channel-Aware Adaptation

The heartbeat automatically detects whether the channel supports message editing (`ChannelCapabilities.edit`). Channels like Telegram and Discord support edit-in-place; channels that don't will receive a single heartbeat message and no further updates to avoid spam.

If an edit operation fails (e.g., message too old to edit), the system gracefully falls back to sending a new message.

### Details

| Aspect            | Behavior                                                                                                           |
| ----------------- | ------------------------------------------------------------------------------------------------------------------ |
| Silence threshold | 120 seconds (configurable via `_SILENCE_REASSURANCE_THRESHOLD`)                                                    |
| Max heartbeats    | 3 per task (configurable via `_MAX_REASSURANCE_COUNT`)                                                             |
| Update mode       | Edit-in-place (falls back to single send for channels without edit support)                                        |
| Priority          | NORMAL — silent delivery, no push notification                                                                     |
| Language          | Fully internationalized (English, Simplified Chinese, Traditional Chinese, Japanese — extensible via `.ftl` files) |
| Typing indicator  | Runs in parallel — "Typing..." keeps showing during the silence window                                             |
| Prompt cache      | Zero impact — heartbeat is outbound-only, never modifies the system prompt                                         |
| Error handling    | Send/edit failures are logged silently; the main task is never interrupted                                         |

## In-Chat Agent Switching

Switch between configured agents directly inside any IM channel — no need to open the Web UI or edit configuration files.

### Telegram (`/agent` command)

1. Send `/agent` in any chat
2. An InlineKeyboard appears listing all available agents — the currently bound agent shows a checkmark indicator
3. Tap your desired agent — the picker message is replaced with a confirmation text ("Switched to: AgentName")
4. All subsequent messages in this topic go to the new agent (independent system prompt, model, tools, memory)

### Quick Switch Commands

Agents that define `command_bindings` in their configuration register shortcut commands (e.g., `/claude`, `/gpt`).

**Dual-mode routing:**

* `/cc fix this bug` — **one-shot routing**: this message is handled by the Claude agent, but your binding stays unchanged (next message goes to your previous agent)
* `/cc` (no arguments) — **persistent binding**: switches your default agent to Claude for all subsequent messages

This design eliminates the friction of switching back and forth when you temporarily need a different agent.

### Topic Binding Granularity

Agent bindings follow a **three-tier resolution** hierarchy:

| Level   | Scope                                     | Example                                     |
| ------- | ----------------------------------------- | ------------------------------------------- |
| Thread  | A single reply thread within a group      | Different threads bound to different agents |
| Chat    | A DM conversation or entire group         | Default for most use cases                  |
| Channel | All conversations from one channel source | Fallback when no thread/chat binding exists |

### i18n Support

All picker and confirmation messages are fully internationalized. The agent resolves the user's language preference automatically (currently English and Chinese, extensible via `.ftl` translation files).

## Cross-Device Task Sync

All background tasks are stored server-side and accessible from any connected client — Desktop app, Web UI, or mobile browser:

* **Single source of truth** — task state lives in the server's Kanban system, not on any single device
* **Real-time updates everywhere** — SSE events push status changes (completion, failure, progress) to every connected client simultaneously
* **Consistent UI** — Desktop (Tauri) embeds the same Web UI frontend, so the experience is identical across surfaces
* **IM channel integration** — BtwTaskNotifier pushes results to IM channels in parallel with the UI updates
* **IM Kanban management** — use `/kanban` (or `/kb`) directly in Telegram, Discord, Slack, etc. to create, list, edit, complete, block, and archive tasks without leaving the chat. Zero LLM cost — commands bypass the Agent pipeline entirely

Start a task on your desktop, check progress from your phone, manage tasks via `/kanban` in Telegram, and receive the completion notification on Slack — all without any configuration.

## Cross-Platform Handoff

Transfer a conversation from one platform to another without losing context.

**Two entry points:**

* **Web UI** — right-click any conversation in the sidebar → "Transfer to..." → a dialog auto-discovers all connected channels with icons and status indicators → select and transfer in one click
* **IM command** — type `/handoff <target_channel>` in any IM channel (Telegram, Discord, Slack, etc.)

**How it works:** The handoff rebinds the session key to the target channel in a single atomic DB update — millisecond latency, zero data copy, full prompt cache preserved. If the target key is already occupied by another session, it is automatically unbound to avoid conflicts.

**Session policies:** Three modes control how sessions behave after transfer — `persistent` (never resets), `daily` (resets at a configured hour), or `idle` (resets after inactivity). Agent identity (`agent_id`) is preserved across transfers, so the same Agent personality, tools, and memory continue on the new channel.

**Test coverage:** 17 unit tests + 7 API integration tests = 24 tests covering all edge cases (conflict resolution, same-channel rejection, inactive pairing, agent preservation, policy modes).

### Automatic "Continue in Browser" Button

Every IM reply from the Agent includes a **"Continue in browser"** button at the bottom. Tapping it opens the full Web UI conversation page — complete with tool call steps, code diffs, and file history — in one click.

* **Precise routing** — The button links to `/{chatUUID}` using the database Chat UUID (not the IM platform's peer ID), so it always opens the correct conversation
* **Background task notifications** — When a `/btw` background task finishes, the IM notification also includes the button
* **Graceful degradation** — Channels that don't support interactive buttons (e.g., WeChat) automatically fall back to a text link; if no public URL is available, the button is silently omitted without blocking the reply
* **WebUI filtering** — Messages already in the Web UI do not show the button (no redundant self-links)

**Test coverage:** 22 tests (11 unit + 8 integration + 3 regression) covering URL construction, DB UUID resolution, channel filtering, error handling, and graceful degradation.

## Session Continuity & Context Preservation

Remote control through IM never loses context — four layers guarantee seamless continuity:

| Layer                       | What it does                                                                                                                                     |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Session Strategy**        | Choose Persistent (never reset), Daily (fresh each morning), or Idle (reset after N minutes of inactivity)                                       |
| **Auto-Reset Notification** | When a session resets, the agent is told "this is a fresh conversation" and the user sees a notice — no hallucinated references to prior context |
| **History Backfill**        | On cold start, the last 15 messages from the channel are automatically replayed into the session so the agent has immediate context              |
| **Shared Context**          | Cross-agent and cross-channel memory sharing — work started on desktop continues seamlessly when you switch to mobile IM                         |

Unlike message-forwarding architectures (e.g., Coze → Claude Code) that lose workspace context in transit, Myrm's agent runs directly on your workspace. It has full filesystem access, can run linters, check git status, and explore the project tree — no IDE state synchronization layer needed.

## Undo, Retry & File Revert in IM

Send `/undo` or `/retry` in any IM channel to roll back the agent's last turn — **including automatic file restoration**.

| Command  | What happens                                                                                                            |
| -------- | ----------------------------------------------------------------------------------------------------------------------- |
| `/undo`  | Deletes the last user + assistant message pair **and** reverts any files the agent modified during that turn            |
| `/retry` | Deletes the last assistant reply, reverts its file changes, **then** re-sends your original question for a fresh answer |

**How it works:** When the agent processes a message, a consistent `message_id` is tracked from the agent's SnapshotStore through to the database. On `/undo`, the system retrieves the deleted message IDs, looks up the corresponding file snapshots, and restores them. The user sees a localized confirmation (e.g., "↩ Undone: 2 message(s) removed. ↩ Reverted 3 file(s).").

**Why this matters:** Competitors (OpenClaw, Hermes, DeerFlow) only support file revert in their desktop GUI or CLI. No other product offers file revert linked to undo/retry commands within IM channels. This means you can safely let the agent edit files from your phone — if something goes wrong, one `/undo` command fixes everything.

## Channel Self-Healing & Reliability

Every channel runs under a **ChannelGateway** that continuously monitors health and automatically recovers from failures — no human intervention required.

### Health Loop

The gateway runs a periodic health check cycle (60s interval). If a channel fails two consecutive checks, it enters **DEGRADED** status and triggers the self-healing pipeline:

1. **Exponential backoff** — restart delay grows from 5s to 300s max, preventing resource thrashing
2. **Jitter** — 25% random variation prevents thundering herd when multiple channels fail simultaneously
3. **Full restart** — executes `stop()` (releases all handles) then `start()` (fresh initialization)
4. **Status broadcast** — frontend `ConnectionBadge` updates in real-time via EventEmitter

### Message Recovery (InboundJournal)

Messages received during a crash window are **never lost**. The `InboundJournal` persists incoming messages before processing. On restart, unprocessed entries are automatically replayed through the routing pipeline — the user never knows a crash happened.

### Dead Letter Queue

Messages that fail delivery after multiple retries are moved to a DLQ with full metadata. They can be inspected, retried, or archived — nothing disappears silently.

### Generic Reconnect Loop

WebSocket-based channels (Discord, Slack, Feishu, etc.) share a common `reconnect_loop` utility with exponential backoff and jitter. This provides consistent, battle-tested reconnection behavior across all long-lived connections.

### Platform-Specific Rate Limiting

Each channel gets its own **TokenBucket** rate limiter tuned to the platform's API limits. WeChat channels have dedicated parameters:

| Channel         | Rate    | Effect                               |
| --------------- | ------- | ------------------------------------ |
| WeChat (iLink)  | 2 msg/s | Prevents personal account throttling |
| WeChat Official | 1 msg/s | Respects Official Account API quotas |

When the platform returns a rate-limit error (e.g., WeChat errcode 45011/45015/45047 or iLink errcode -2), the system:

1. Maps the error to a `RateLimitError` with a platform-specific `retry_after` delay
2. Retries with exponential backoff, respecting the `retry_after` value
3. Falls back to DLQ if all retries exhaust — text messages are never silently dropped

### Circuit Breaker & Graceful Degradation

When a channel's outbound API encounters repeated failures, two complementary protection layers activate:

**Channel-Level Circuit Breaker** — after 5 consecutive send failures, the circuit trips and pauses outbound dispatch for 30 seconds. Messages queue safely in the PriorityQueue (max 256) and are delivered once the circuit closes. This prevents flooding a degraded platform API.

**Streaming Graceful Degradation** — during live streaming updates (e.g., real-time message editing), failures trigger a 5-level progressive slowdown instead of a hard stop:

| Level       | Multiplier | Effect                                    |
| ----------- | ---------- | ----------------------------------------- |
| NORMAL      | 1×         | Standard update frequency                 |
| DEGRADED\_1 | 2×         | Slightly slower updates                   |
| DEGRADED\_2 | 4×         | Noticeable delay, still responsive        |
| DEGRADED\_3 | 8×         | Significant delay                         |
| DEGRADED\_4 | 16×        | Maximum slowdown, updates still delivered |

The key principle: **users always receive updates** — they may arrive slower during degradation, but never stop completely. This is superior to traditional circuit breakers that create sudden 30-second blackout periods with zero feedback.

### What This Means for Users

* **Zero-downtime experience** — temporary network issues or platform outages resolve themselves
* **No lost messages** — even if the server crashes, pending messages are recovered on restart
* **No account bans** — per-channel rate limiting prevents exceeding platform API quotas
* **Visible status** — channel health is always visible in Settings > Channels via color-coded badges
* **No manual restarts** — channels heal themselves; you only see the result, not the recovery process
* **No sudden blackouts** — streaming updates degrade gracefully instead of stopping abruptly

## GitHub Channel & CI/CD Integration

The **GitHub Channel** turns your agent into an automated code reviewer that listens for repository events and responds with PR comments.

### Supported Events

| Event                 | Trigger                 | Agent Action                    |
| --------------------- | ----------------------- | ------------------------------- |
| `pull_request`        | PR opened / updated     | Automatic code review + comment |
| `issues`              | Issue created / updated | Triage, labeling, response      |
| `issue_comment`       | New comment on Issue/PR | Contextual reply                |
| `push`                | Commits pushed          | Change analysis                 |
| `pull_request_review` | Review submitted        | Follow-up discussion            |

### Setup

1. Navigate to **Settings > Channels > GitHub**
2. Enter your **Personal Access Token** (with `repo` scope for posting comments)
3. Enter your **Webhook Secret** (for signature verification)
4. Add the webhook URL (`https://your-server/channels/github/webhook`) to your GitHub repo settings

### Security

All incoming webhooks are verified using **X-Hub-Signature-256** (HMAC-SHA256). Invalid signatures are rejected with a 401 response.

### CI/CD Pipeline Integration

Beyond GitHub, any CI/CD platform (Jenkins, GitLab CI, 云效, CodePipeline) can trigger your agent via the REST API:

```bash theme={null}
curl -X POST https://your-server/api/chats/ \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"message": "Review the latest commit on branch feature/xyz"}'
```

The **Webhook Channel** can deliver results to any external system by POSTing structured JSON to a URL you specify.

## Remote Approval (HITL)

When controlling your agent remotely via IM channels, high-risk tool calls (file edits, shell commands) trigger an **in-channel approval prompt** — the industry's only IM-native HITL system.

### How It Works

1. Agent encounters a dangerous tool call
2. A message with approve/deny buttons is sent to your IM channel
3. You approve, reject, or use batch commands
4. Agent resumes or halts based on your decision

### Approval Methods

| Method           | Example                                                          |
| ---------------- | ---------------------------------------------------------------- |
| Slash commands   | `/approve`, `/deny`, `/approve-always`                           |
| Deny with reason | `/deny <reason>` — Agent uses your reason to adjust its approach |
| Quick shortcuts  | `1` (approve), `2` (deny), `y`, `n`                              |
| Emoji reactions  | 👍 (once), ♾️ (always), 👎 (deny)                                |
| Batch            | `/batch a,d,a` (approve, deny, approve)                          |
| Chinese          | `同意`, `拒绝`, `是`, `不`                                             |

### Interactive Buttons (ActionButton)

On channels that support interactive elements (Telegram InlineKeyboard, Slack Block Kit, Discord buttons, Feishu cards, MS Teams Adaptive Cards), approval prompts include native **Approve / Deny buttons**. Clicking a button:

1. Resolves the approval in the database (PENDING-only — duplicate clicks are safely ignored)
2. Edits the original message to show the outcome (e.g. "Approved by alice")
3. Resumes the interrupted agent automatically

This works alongside WebUI approval — both paths lead to the same persistent record.

### Security

* **Group chat protection**: Only the original requester or configured co-approvers can approve
* **Idempotency guard**: Already-resolved approvals cannot be re-resolved or status-flipped (DB-level PENDING check)
* **Timeout guard**: Unanswered approvals auto-deny (configurable)
* **Persistent allowlist**: "Always allow" decisions survive restarts
* **Anti-retry**: After 3 denials, agent stops retrying

## Outbound HITL (Draft Review)

For enterprise sales and customer service scenarios, AI-generated replies may need human review before sending. Myrm's **Outbound HITL** intercepts agent replies as drafts, requiring explicit approval.

### Configuration

Each topic can be independently configured via **Settings → Channel Routing**:

| Setting    | Options                 | Description                                             |
| ---------- | ----------------------- | ------------------------------------------------------- |
| Reply Mode | Auto / Draft Review     | Auto sends immediately; Draft Review holds for approval |
| Timeout    | 1 min – 1 hour          | How long to wait before auto-action                     |
| On Expiry  | Auto Reject / Auto Send | What happens when nobody reviews in time                |

### How It Works

1. Message arrives on IM channel → Agent generates a reply
2. Reply is intercepted and stored as an `ApprovalRecord` (not sent)
3. Reviewer sees the draft in WebUI Approval Drawer or IM ActionButton
4. **Approve** → message is sent to the channel
5. **Reject** → message is discarded
6. **Timeout** → configurable auto-send or auto-reject

### Review Channels

* **WebUI Approval Drawer**: Shows the original customer message, AI-generated draft, channel/topic info
* **IM ActionButtons**: Approve/Deny buttons on the approval notification in the channel

### Use Cases

| Scenario                        | Why Draft Review                                |
| ------------------------------- | ----------------------------------------------- |
| Customer quotes/pricing         | Incorrect prices create legal liability         |
| Contract terms                  | AI may hallucinate non-standard clauses         |
| Compliance-regulated industries | Financial/medical communications require review |
| Brand-sensitive communications  | Tone and accuracy matter                        |

## Intelligent Silent Filtering

When your agent's system prompt includes instructions like "reply with `[SILENT]` when nothing needs attention", Myrm automatically detects and filters these silent replies before delivery:

* **Group chats**: Agent is mentioned, checks, finds nothing notable → sends no message (zero noise)
* **Scheduled tasks**: Periodic checks come back normal → auto-silent, only pushes when issues found
* **Placeholder cleanup**: "Processing..." placeholder messages are silently removed

### Detection Rules

| Format                         |        Silent?       |
| ------------------------------ | :------------------: |
| `[SILENT]`                     |           ✅          |
| With whitespace `  [SILENT]  ` |           ✅          |
| Wrapped in markdown fence      |           ✅          |
| `[SILENT] nothing to report`   | ❌ Delivered normally |
| Contains other text            | ❌ Delivered normally |

This ensures a "notify only when needed" experience — your agent only sends messages to IM channels when it has something meaningful to say.

## Deep Feishu Integration

Myrm's Feishu support goes far beyond basic messaging — the built-in SDK deeply integrates 6 capability modules, allowing you to operate Feishu documents, spreadsheets, Wiki, and comments directly in conversations.

### Supported Capabilities

| Module      | Capability                                 | Use Case                                            |
| ----------- | ------------------------------------------ | --------------------------------------------------- |
| Drive Meta  | File listing, metadata, permission queries | "List all documents in the shared folder"           |
| Docx Blocks | Block-level document read/write            | "Read meeting minutes and extract action items"     |
| Comments    | Batch query, create, reply to comments     | "@AI in a document comment, AI auto-replies"        |
| Wiki        | Knowledge base node search                 | "Find the deployment guide in Wiki"                 |
| Bitable     | Multi-dimensional table CRUD               | "Write analysis results to project tracking table"  |
| CardKit     | Card message streaming updates             | "Send progress card and update status in real-time" |

### Typical Workflows

**Scenario: Read meeting minutes → Extract action items → Write to spreadsheet**

```
User: Please read last week's project meeting minutes, extract all action items, and write them to the project tracking Bitable
```

Agent automatically:

1. Locates target document via Drive Meta
2. Reads full document using Docx Blocks
3. LLM extracts structured action items (assignee, deadline, priority)
4. Writes to project tracking table via Bitable

**Scenario: Document comment-driven Q\&A**

When a user @mentions AI in a Feishu document comment:

1. Feishu WebSocket pushes comment event in real-time
2. Agent reads comment content + document context
3. Generates reply and auto-posts to comment thread

### Setup

1. Go to **Settings > Channels > Feishu**
2. Enter App ID and App Secret
3. Enable **WebSocket long connection** (no public IP required)
4. Enable Feishu app permissions as needed:
   * `drive:drive` — Document access
   * `wiki:wiki` — Wiki access
   * `bitable:bitable` — Table operations
   * `im:message` — Messaging
   * `contact:user.id:readonly` — User identification

### WeCom Dual Channels

WeCom provides two integration modes:

| Mode             | Use Case                     | Features                                          |
| ---------------- | ---------------------------- | ------------------------------------------------- |
| Standard Webhook | Enterprise self-built apps   | Crypto verification + passive reply + active push |
| AI Bot           | Intelligent customer service | Official AI Bot protocol + streaming replies      |

Both modes support:

* XML message encryption/decryption (AES-CBC)
* Callback signature verification (anti-forgery)
* Message deduplication (prevent duplicate processing)

### WeChat Personal Dual Channels

| Mode             | Protocol                   | Features                                           |
| ---------------- | -------------------------- | -------------------------------------------------- |
| iLink            | Third-party protocol layer | Personal account messaging + group chat            |
| Official Account | Official API               | Passive reply + template messages + draft box HITL |
