From ab8177cbc06f77a8513592d1edb7aec9eee29bce Mon Sep 17 00:00:00 2001 From: Prasad Thammineni Date: Wed, 28 Jan 2026 07:18:20 -0800 Subject: [PATCH] docs: add unified channel message flow architecture documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Comprehensive comparison of WhatsApp, Discord, and Gmail message flows: - Unified architecture overview showing shared components - Channel comparison matrix (connection, reception, routing, delivery) - Detailed sequence diagrams for each channel - Key files and functions for each step - Channel-specific features (threads, reactions, webhooks) - Data structure comparison (inbound messages, session keys) - Guide for adding new channels πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- docs/architecture/channel-message-flows.md | 769 +++++++++++++++++++++ 1 file changed, 769 insertions(+) create mode 100644 docs/architecture/channel-message-flows.md diff --git a/docs/architecture/channel-message-flows.md b/docs/architecture/channel-message-flows.md new file mode 100644 index 000000000..89679dab6 --- /dev/null +++ b/docs/architecture/channel-message-flows.md @@ -0,0 +1,769 @@ +# Channel Message Flow Architecture + +This document describes how messages flow through Moltbot's channel system, comparing WhatsApp, Discord, and Gmail to highlight the unified architecture and channel-specific differences. + +## Architecture Overview + +All channels in Moltbot follow a common pattern: + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ External β”‚ β”‚ Channel β”‚ β”‚ Shared β”‚ β”‚ Pi Agent β”‚ +β”‚ Platform │───▢│ Adapter │───▢│ Router │───▢│ Framework β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β–² β”‚ + β”‚ β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + Response Delivery +``` + +### Shared Components (All Channels) + +| Component | Location | Purpose | +|-----------|----------|---------| +| Agent Router | `src/routing/resolve-route.ts` | Routes messages to agents | +| Reply Dispatcher | `src/auto-reply/reply/reply-dispatcher.ts` | Queues & delivers responses | +| Agent Runner | `src/auto-reply/reply/get-reply.ts` | Sets up agent execution | +| Pi Framework | `src/agents/pi-embedded-runner/` | Executes AI agents | +| Outbound Service | `src/infra/outbound/` | Cross-channel delivery | + +### Channel-Specific Components + +| Channel | Adapter Location | Protocol | +|---------|------------------|----------| +| WhatsApp | `src/web/` | Baileys (WhatsApp Web) | +| Discord | `src/discord/` | Carbon (@buape/carbon) | +| Gmail | `src/hooks/` | Webhooks + gog CLI | +| Telegram | `src/telegram/` | grammY | +| Slack | `src/slack/` | Bolt | +| Signal | `src/signal/` | signal-cli | +| iMessage | `src/imessage/` | AppleScript/sqlite | + +--- + +## Channel Comparison Matrix + +### Connection Method + +| Aspect | WhatsApp | Discord | Gmail | +|--------|----------|---------|-------| +| **Protocol** | WebSocket (Baileys) | WebSocket (Gateway) | HTTP Webhooks | +| **Auth** | QR Code scan | Bot Token | OAuth2 (gog CLI) | +| **Connection Type** | Persistent | Persistent | Push notifications | +| **Library** | `@whiskeysockets/baileys` | `@buape/carbon` | `gog` CLI | +| **Reconnect** | Auto with backoff | Auto with backoff | N/A (stateless) | + +### Message Reception + +| Aspect | WhatsApp | Discord | Gmail | +|--------|----------|---------|-------| +| **Event Source** | `messages.upsert` | `MESSAGE_CREATE` | Pub/Sub push | +| **Debouncing** | Yes (configurable) | Yes (300ms default) | No | +| **Deduplication** | By message ID | By message ID | By message ID | +| **Media Download** | On reception | On demand | Via gog CLI | + +### Routing & Gating + +| Aspect | WhatsApp | Discord | Gmail | +|--------|----------|---------|-------| +| **Peer Types** | DM, Group | DM, Channel, Thread | Single inbox | +| **Mention Required** | Configurable (groups) | Configurable | N/A | +| **Allowlist** | Phone numbers | Users, Channels, Guilds | N/A | +| **Session Key Format** | `agent:wa:phone:+1234` | `agent:discord:channel:123` | `hook:gmail:msgId` | + +### Response Delivery + +| Aspect | WhatsApp | Discord | Gmail | +|--------|----------|---------|-------| +| **Max Message Length** | ~65KB | 2000 chars | Unlimited | +| **Chunking** | Markdown-aware | Markdown-aware | N/A | +| **Media Support** | Image, Audio, Video, Doc | Image, Video, File | N/A (use gog) | +| **Typing Indicator** | Yes | Yes | N/A | +| **Reactions** | Limited | Full support | N/A | +| **Threading** | Reply-to | Native threads | N/A | + +--- + +## Detailed Flow Diagrams + +### Unified Message Flow + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ MOLTBOT UNIFIED MESSAGE FLOW β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ WhatsApp β”‚ β”‚ Discord β”‚ β”‚ Gmail β”‚ + β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ + β”‚ β”‚ β”‚ + β–Ό β–Ό β–Ό + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ Baileys β”‚ β”‚ Carbon β”‚ β”‚ gog β”‚ + β”‚ Socket β”‚ β”‚ Client β”‚ β”‚ watch β”‚ + β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ + β”‚ β”‚ β”‚ + β”‚ WebSocket β”‚ WebSocket β”‚ HTTP POST + β–Ό β–Ό β–Ό +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ CHANNEL ADAPTER LAYER β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ monitorWebInbox β”‚ β”‚ DiscordMessage β”‚ β”‚ createHooksReq β”‚ β”‚ +β”‚ β”‚ () β”‚ β”‚ Listener β”‚ β”‚ Handler() β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β”‚ β”‚ β”‚ β”‚ +β”‚ β–Ό β–Ό β–Ό β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ Extract: β”‚ β”‚ Extract: β”‚ β”‚ Extract: β”‚ β”‚ +β”‚ β”‚ - body β”‚ β”‚ - content β”‚ β”‚ - from β”‚ β”‚ +β”‚ β”‚ - sender β”‚ β”‚ - author β”‚ β”‚ - subject β”‚ β”‚ +β”‚ β”‚ - media β”‚ β”‚ - attachments β”‚ β”‚ - body β”‚ β”‚ +β”‚ β”‚ - group info β”‚ β”‚ - guild/channel β”‚ β”‚ - snippet β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ β”‚ β”‚ + β–Ό β–Ό β–Ό +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ VALIDATION LAYER β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ checkInbound β”‚ β”‚ preflightDiscordβ”‚ β”‚ validateToken β”‚ β”‚ +β”‚ β”‚ AccessControl() β”‚ β”‚ Message() β”‚ β”‚ () β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β”‚ β”‚ β”‚ β”‚ +β”‚ β–Ό β–Ό β–Ό β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ - Allowlist β”‚ β”‚ - DM Policy β”‚ β”‚ - Hook token β”‚ β”‚ +β”‚ β”‚ - Group gating β”‚ β”‚ - Guild access β”‚ β”‚ - Path matching β”‚ β”‚ +β”‚ β”‚ - Mention check β”‚ β”‚ - Mention check β”‚ β”‚ β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ β”‚ β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β–Ό +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ ROUTING LAYER (SHARED) β”‚ +β”‚ β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ resolveAgentRoute β”‚ β”‚ +β”‚ β”‚ () β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β”‚ β”‚ +β”‚ β–Ό β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ Returns: β”‚ β”‚ +β”‚ β”‚ - agentId β”‚ β”‚ +β”‚ β”‚ - sessionKey β”‚ β”‚ +β”‚ β”‚ - channel β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β–Ό +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ AGENT LAYER (SHARED) β”‚ +β”‚ β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ getReplyFromConfig β”‚ β”‚ +β”‚ β”‚ () β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β”‚ β”‚ +β”‚ β–Ό β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ runEmbeddedPiAgent β”‚ β”‚ +β”‚ β”‚ () β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β”‚ β”‚ +β”‚ β–Ό β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ LLM Provider β”‚ β”‚ +β”‚ β”‚ (Claude/GPT/Gemini) β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β–Ό +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ DISPATCH LAYER (SHARED) β”‚ +β”‚ β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ createReplyDispatcherβ”‚ β”‚ +β”‚ β”‚ () β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β”‚ β”‚ +β”‚ β–Ό β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ Queue responses: β”‚ β”‚ +β”‚ β”‚ - Block replies β”‚ β”‚ +β”‚ β”‚ - Tool results β”‚ β”‚ +β”‚ β”‚ - Final reply β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ β”‚ β”‚ + β–Ό β–Ό β–Ό +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ DELIVERY LAYER β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ deliverWebReply β”‚ β”‚ deliverDiscord β”‚ β”‚ deliverOutbound β”‚ β”‚ +β”‚ β”‚ () β”‚ β”‚ Reply() β”‚ β”‚ Payloads() β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β”‚ β”‚ β”‚ β”‚ +β”‚ β–Ό β–Ό β–Ό β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ - Chunk text β”‚ β”‚ - Chunk text β”‚ β”‚ - Route to last β”‚ β”‚ +β”‚ β”‚ - Send via β”‚ β”‚ - Send via REST β”‚ β”‚ known channel β”‚ β”‚ +β”‚ β”‚ Baileys β”‚ β”‚ - Add reactions β”‚ β”‚ - Or default β”‚ β”‚ +β”‚ β”‚ - Retry logic β”‚ β”‚ - Threading β”‚ β”‚ (WhatsApp) β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ β”‚ β”‚ + β–Ό β–Ό β–Ό + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚WhatsApp β”‚ β”‚ Discord β”‚ β”‚ Any Chanβ”‚ + β”‚ Server β”‚ β”‚ Server β”‚ β”‚ nel β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +--- + +## WhatsApp Flow + +### Sequence Diagram + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ WhatsApp β”‚ β”‚ Baileys β”‚ β”‚ Monitor β”‚ β”‚ Router β”‚ β”‚ Agent β”‚ β”‚Dispatcherβ”‚ +β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”‚ message β”‚ β”‚ β”‚ β”‚ β”‚ + │───────────▢│ β”‚ β”‚ β”‚ β”‚ + β”‚ β”‚ upsert β”‚ β”‚ β”‚ β”‚ + β”‚ │──────────▢│ β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚ debounce β”‚ β”‚ β”‚ + β”‚ β”‚ │───┐ β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚β—€β”€β”€β”˜ β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚ route β”‚ β”‚ β”‚ + β”‚ β”‚ │──────────▢│ β”‚ β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚ agentId β”‚ β”‚ β”‚ + β”‚ β”‚ │◀──────────│ β”‚ β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚ (group?) check mention β”‚ + β”‚ β”‚ │───┐ β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚β—€β”€β”€β”˜ β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚ dispatch β”‚ β”‚ β”‚ + β”‚ β”‚ │─────────────────────▢│ β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚ run Pi β”‚ + β”‚ β”‚ β”‚ β”‚ │───┐ β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚β—€β”€β”€β”˜ β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚ reply β”‚ + β”‚ β”‚ β”‚ β”‚ │──────────▢│ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚ deliver β”‚ β”‚ β”‚ + β”‚ │◀────────────────────────────────────────────── + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”‚ send β”‚ β”‚ β”‚ β”‚ β”‚ + │◀───────────│ β”‚ β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ +``` + +### Key Files + +| Step | File | Function | +|------|------|----------| +| Connect | `src/web/session.ts` | `createWaSocket()` | +| Receive | `src/web/inbound/monitor.ts` | `monitorWebInbox()` | +| Validate | `src/web/auto-reply/monitor/on-message.ts` | `createWebOnMessageHandler()` | +| Route | `src/routing/resolve-route.ts` | `resolveAgentRoute()` | +| Process | `src/web/auto-reply/monitor/process-message.ts` | `processMessage()` | +| Execute | `src/agents/pi-embedded-runner/run.ts` | `runEmbeddedPiAgent()` | +| Deliver | `src/web/auto-reply/deliver-reply.ts` | `deliverWebReply()` | + +### WhatsApp-Specific Features + +- **QR Code Auth**: First-time login requires scanning QR code +- **Group Metadata**: Fetches participant list, subject, description +- **Read Receipts**: Sends read receipts on message reception +- **Location Messages**: Extracts latitude/longitude/address +- **Reply Context**: Includes quoted message in context +- **Presence Updates**: Shows "online" status + +--- + +## Discord Flow + +### Sequence Diagram + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Discord β”‚ β”‚ Carbon β”‚ β”‚ Listener β”‚ β”‚Preflightβ”‚ β”‚ Agent β”‚ β”‚ Send β”‚ +β”‚ Gateway β”‚ β”‚ Client β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ +β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”‚ READY β”‚ β”‚ β”‚ β”‚ β”‚ + │───────────▢│ β”‚ β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”‚ MESSAGE_ β”‚ β”‚ β”‚ β”‚ β”‚ + β”‚ CREATE β”‚ β”‚ β”‚ β”‚ β”‚ + │───────────▢│ β”‚ β”‚ β”‚ β”‚ + β”‚ β”‚ handle β”‚ β”‚ β”‚ β”‚ + β”‚ │──────────▢│ β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚ debounce β”‚ β”‚ β”‚ + β”‚ β”‚ │───┐ β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚β—€β”€β”€β”˜ β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚ preflight β”‚ β”‚ β”‚ + β”‚ β”‚ │──────────▢│ β”‚ β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚ β”‚ validate β”‚ β”‚ + β”‚ β”‚ β”‚ β”‚ - DM policy β”‚ + β”‚ β”‚ β”‚ β”‚ - Guild access β”‚ + β”‚ β”‚ β”‚ β”‚ - Mention req β”‚ + β”‚ β”‚ β”‚ │───┐ β”‚ β”‚ + β”‚ β”‚ β”‚ β”‚β—€β”€β”€β”˜ β”‚ β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚ ok β”‚ β”‚ β”‚ + β”‚ β”‚ │◀──────────│ β”‚ β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚ dispatch β”‚ β”‚ β”‚ + β”‚ β”‚ │─────────────────────▢│ β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚ run Pi β”‚ + β”‚ β”‚ β”‚ β”‚ │───┐ β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚β—€β”€β”€β”˜ β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚ reply β”‚ + β”‚ β”‚ β”‚ β”‚ │──────────▢│ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ chunk + β”‚ β”‚ β”‚ β”‚ β”‚ │──┐ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚β—€β”€β”˜ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”‚ REST β”‚ β”‚ β”‚ β”‚ β”‚ + │◀──────────────────────────────────────────────────────────│ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ +``` + +### Key Files + +| Step | File | Function | +|------|------|----------| +| Connect | `src/discord/monitor/provider.ts` | `monitorDiscordProvider()` | +| Listen | `src/discord/monitor/listeners.ts` | `DiscordMessageListener` | +| Debounce | `src/discord/monitor/message-handler.ts` | `createDiscordDebouncer()` | +| Validate | `src/discord/monitor/message-handler.preflight.ts` | `preflightDiscordMessage()` | +| Process | `src/discord/monitor/message-handler.process.ts` | `processDiscordMessage()` | +| Execute | `src/agents/pi-embedded-runner/run.ts` | `runEmbeddedPiAgent()` | +| Deliver | `src/discord/monitor/reply-delivery.ts` | `deliverDiscordReply()` | +| Send | `src/discord/send.outbound.ts` | `sendMessageDiscord()` | + +### Discord-Specific Features + +- **Guilds & Channels**: Multi-server support with per-channel config +- **Threads**: Native thread support (public, private, forum) +- **Reactions**: Add/remove reactions, ack reactions on processing +- **Slash Commands**: Native Discord commands via `deployDiscordCommands()` +- **Presence**: Optional presence/status monitoring +- **Moderation**: Ban, kick, timeout, role management tools +- **Embeds**: Rich embed support for responses +- **2000 Char Limit**: Auto-chunking for long responses + +--- + +## Gmail Flow + +### Sequence Diagram + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Gmail β”‚ β”‚ gog β”‚ β”‚ Webhook β”‚ β”‚ Hook β”‚ β”‚ Cron β”‚ β”‚Outbound β”‚ +β”‚ Server β”‚ β”‚ watch β”‚ β”‚ Handler β”‚ β”‚Mapping β”‚ β”‚ Agent β”‚ β”‚ Deliver β”‚ +β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”‚ New email β”‚ β”‚ β”‚ β”‚ β”‚ + │───────────▢│ β”‚ β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”‚ β”‚ Pub/Sub β”‚ β”‚ β”‚ β”‚ + β”‚ β”‚ push β”‚ β”‚ β”‚ β”‚ + β”‚ │──────────▢│ β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚ POST β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚ /hooks/ β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚ gmail β”‚ β”‚ β”‚ + β”‚ β”‚ │───┐ β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚β—€β”€β”€β”˜ β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚ validate β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚ token β”‚ β”‚ β”‚ + β”‚ β”‚ │───┐ β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚β—€β”€β”€β”˜ β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚ match β”‚ β”‚ β”‚ + β”‚ β”‚ │──────────▢│ β”‚ β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚ β”‚ template β”‚ β”‚ + β”‚ β”‚ β”‚ β”‚ render β”‚ β”‚ + β”‚ β”‚ β”‚ │───┐ β”‚ β”‚ + β”‚ β”‚ β”‚ β”‚β—€β”€β”€β”˜ β”‚ β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚ β”‚ dispatch β”‚ β”‚ + β”‚ β”‚ β”‚ │─────────▢│ β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚ run Pi β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚ (isolated) + β”‚ β”‚ β”‚ β”‚ │───┐ β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚β—€β”€β”€β”˜ β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚ deliver β”‚ + β”‚ β”‚ β”‚ β”‚ │──────────▢│ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ route to + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ last chan + β”‚ β”‚ β”‚ β”‚ β”‚ │──┐ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚β—€β”€β”˜ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + └─────────────────────────────────────────────────◀─────────│ + (Response via WhatsApp/Discord/Telegram/etc) +``` + +### Key Files + +| Step | File | Function | +|------|------|----------| +| Watch | `src/hooks/gmail-watcher.ts` | `startGmailWatcher()` | +| Receive | `src/gateway/server-http.ts` | `createHooksRequestHandler()` | +| Match | `src/gateway/hooks-mapping.ts` | `applyHookMappings()` | +| Template | `src/gateway/hooks-mapping.ts` | `renderTemplate()` | +| Dispatch | `src/gateway/server/hooks.ts` | `dispatchAgentHook()` | +| Execute | `src/cron/isolated-agent/run.ts` | `runCronIsolatedAgentTurn()` | +| Deliver | `src/infra/outbound/deliver.ts` | `deliverOutboundPayloads()` | + +### Gmail-Specific Features + +- **Pub/Sub Push**: Uses Google Cloud Pub/Sub for real-time notifications +- **gog CLI**: External CLI handles Gmail API, OAuth, watch registration +- **Template Rendering**: `{{messages[0].from}}`, `{{messages[0].subject}}` +- **Isolated Sessions**: Each email creates isolated agent session +- **Cross-Channel Delivery**: Responses go to last-used channel (WhatsApp default) +- **No Direct Reply**: Cannot reply to emails (use gog CLI tool if needed) +- **Webhook Auth**: Token-based authentication for security + +--- + +## Similarities Across Channels + +### 1. Message Reception Pattern + +All channels follow the same pattern: +```typescript +// Pseudocode - common pattern +async function onMessage(rawMessage) { + // 1. Extract normalized message + const msg = extractMessage(rawMessage) + + // 2. Debounce (optional) + await debouncer.enqueue(msg) + + // 3. Validate access + if (!validateAccess(msg)) return + + // 4. Route to agent + const route = await resolveAgentRoute(msg) + + // 5. Dispatch + await dispatchToAgent(msg, route) +} +``` + +### 2. Shared Routing Logic + +All channels use `resolveAgentRoute()` with the same interface: + +```typescript +// src/routing/resolve-route.ts +interface RouteInput { + channel: string // 'whatsapp' | 'discord' | 'gmail' | ... + accountId: string // Account identifier + peer: { + kind: 'dm' | 'group' | 'channel' | 'hook' + id: string // Phone, user ID, channel ID, etc. + } +} + +interface ResolvedRoute { + agentId: string + sessionKey: string + mainSessionKey: string + matchedBy: string +} +``` + +### 3. Shared Agent Execution + +All channels ultimately call the same agent runner: + +```typescript +// src/agents/pi-embedded-runner/run.ts +const result = await runEmbeddedPiAgent({ + sessionKey, + message: userMessage, + model, + tools, + onBlockReply, + onToolResult, +}) +``` + +### 4. Shared Reply Dispatcher + +All channels use the same dispatcher pattern: + +```typescript +// src/auto-reply/reply/reply-dispatcher.ts +const dispatcher = createReplyDispatcher({ + deliver: (payload) => channelSpecificDeliver(payload), + onIdle: () => cleanup(), +}) + +dispatcher.sendBlockReply(chunk) +dispatcher.sendFinalReply(response) +await dispatcher.waitForIdle() +``` + +### 5. Common Configuration Structure + +```json5 +{ + channels: { + whatsapp: { /* channel-specific */ }, + discord: { /* channel-specific */ }, + }, + hooks: { + gmail: { /* webhook-specific */ }, + }, + routing: { + bindings: [ + // Shared routing rules + ], + default: { agentId: 'main' } + }, + agents: { + // Shared agent config + } +} +``` + +--- + +## Key Differences + +### 1. Connection Model + +| Channel | Model | Implication | +|---------|-------|-------------| +| WhatsApp | Persistent WebSocket | Always connected, real-time | +| Discord | Persistent WebSocket | Always connected, real-time | +| Gmail | HTTP Webhooks | Stateless, push-based | + +### 2. Authentication + +| Channel | Auth Type | User Action Required | +|---------|-----------|---------------------| +| WhatsApp | QR Code | Scan with phone | +| Discord | Bot Token | Create bot, copy token | +| Gmail | OAuth2 | Create GCP project, authorize | + +### 3. Message Threading + +| Channel | Threading Model | +|---------|-----------------| +| WhatsApp | Reply-to (quote) | +| Discord | Native threads, forums | +| Gmail | Email threads (not used) | + +### 4. Response Delivery + +| Channel | Direct Reply | Cross-Channel | +|---------|--------------|---------------| +| WhatsApp | Yes | No | +| Discord | Yes | No | +| Gmail | No | Yes (always) | + +### 5. Access Control + +| Channel | Allowlist Granularity | +|---------|-----------------------| +| WhatsApp | Phone numbers, groups | +| Discord | Users, channels, guilds, roles | +| Gmail | N/A (single inbox) | + +### 6. Rich Features + +| Feature | WhatsApp | Discord | Gmail | +|---------|----------|---------|-------| +| Reactions | Limited | Full | No | +| Threads | No | Yes | No | +| Embeds | No | Yes | No | +| Slash Commands | No | Yes | No | +| Typing Indicator | Yes | Yes | No | +| Read Receipts | Yes | No | No | +| Media Upload | Yes | Yes | No | + +--- + +## Data Structure Comparison + +### Inbound Message + +```typescript +// WhatsApp +interface WebInboundMsg { + from: string // JID + body: string + chatType: 'dm' | 'group' + senderE164: string + groupSubject?: string + mediaPath?: string + replyToId?: string + mentionedJids?: string[] +} + +// Discord +interface DiscordInboundMsg { + author: { id: string, username: string } + content: string + channelId: string + guildId?: string + attachments: Attachment[] + referencedMessage?: Message + mentions: User[] +} + +// Gmail (webhook payload) +interface GmailHookPayload { + messages: [{ + id: string + from: string + subject: string + snippet: string + body?: string + }] +} +``` + +### Session Key Format + +| Channel | Format | Example | +|---------|--------|---------| +| WhatsApp | `agent::whatsapp::dm:` | `agent:main:whatsapp:default:dm:+1234567890` | +| WhatsApp Group | `agent::whatsapp::group:` | `agent:main:whatsapp:default:group:123@g.us` | +| Discord DM | `agent::discord::user:` | `agent:main:discord:default:user:123456789` | +| Discord Channel | `agent::discord::channel:` | `agent:main:discord:default:channel:987654321` | +| Gmail | `hook:gmail:` | `hook:gmail:18a1b2c3d4e5f6` | + +--- + +## Adding a New Channel + +To add a new channel, implement these components: + +### 1. Channel Adapter (Required) + +```typescript +// src//monitor.ts +export async function monitorChannel(options: Options) { + // Connect to platform + const client = await connect() + + // Register message handler + client.on('message', async (raw) => { + // Extract normalized message + const msg = extractMessage(raw) + + // Validate + if (!validate(msg)) return + + // Route + const route = await resolveAgentRoute({ + channel: '', + accountId: options.accountId, + peer: { kind: msg.chatType, id: msg.peerId } + }) + + // Dispatch + await dispatchInboundMessage({ + ctx: buildContext(msg, route), + deliver: (reply) => deliverReply(msg, reply), + }) + }) +} +``` + +### 2. Delivery Function (Required) + +```typescript +// src//deliver.ts +export async function deliverReply( + msg: InboundMsg, + reply: ReplyPayload +) { + // Chunk if needed + const chunks = chunkText(reply.text, MAX_LENGTH) + + // Send each chunk + for (const chunk of chunks) { + await client.send(msg.replyTo, chunk) + } +} +``` + +### 3. Channel Plugin (Optional) + +```typescript +// extensions//src/channel.ts +export const Plugin: ChannelPlugin = { + id: '', + name: '', + + async start(runtime) { + await monitorChannel(runtime.options) + }, + + async stop() { + await disconnect() + }, + + async send(to, payload) { + await sendMessage(to, payload) + } +} +``` + +### 4. Configuration Schema + +```typescript +// Add to config schema +channels: { + : { + enabled: Type.Boolean(), + token: Type.String(), + // Channel-specific options + } +} +``` + +--- + +## Related Documentation + +- [WhatsApp Message Flow](./whatsapp-message-flow.md) +- [Gateway Architecture](./gateway.md) +- [Agent Configuration](/configuration) +- [Hooks System](/configuration#hooks) +- [Channel Plugins](/extensions)