@0xmaxma/claude-gateway 1.8.10 → 1.8.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.md +14 -1
  2. package/config.template.json +2 -1
  3. package/dist/agent/runner.d.ts +72 -0
  4. package/dist/agent/runner.d.ts.map +1 -1
  5. package/dist/agent/runner.js +300 -6
  6. package/dist/agent/runner.js.map +1 -1
  7. package/dist/api/router.d.ts.map +1 -1
  8. package/dist/api/router.js +991 -3
  9. package/dist/api/router.js.map +1 -1
  10. package/dist/api/webhooks-router.d.ts +2 -0
  11. package/dist/api/webhooks-router.d.ts.map +1 -1
  12. package/dist/api/webhooks-router.js +2 -0
  13. package/dist/api/webhooks-router.js.map +1 -1
  14. package/dist/api/whatsapp-access.d.ts +123 -0
  15. package/dist/api/whatsapp-access.d.ts.map +1 -0
  16. package/dist/api/whatsapp-access.js +135 -0
  17. package/dist/api/whatsapp-access.js.map +1 -0
  18. package/dist/api/whatsapp-cloud-access.d.ts +48 -0
  19. package/dist/api/whatsapp-cloud-access.d.ts.map +1 -0
  20. package/dist/api/whatsapp-cloud-access.js +58 -0
  21. package/dist/api/whatsapp-cloud-access.js.map +1 -0
  22. package/dist/api/whatsapp-cloud-client.d.ts +143 -0
  23. package/dist/api/whatsapp-cloud-client.d.ts.map +1 -0
  24. package/dist/api/whatsapp-cloud-client.js +298 -0
  25. package/dist/api/whatsapp-cloud-client.js.map +1 -0
  26. package/dist/api/whatsapp-cloud-webhook-router.d.ts +132 -0
  27. package/dist/api/whatsapp-cloud-webhook-router.d.ts.map +1 -0
  28. package/dist/api/whatsapp-cloud-webhook-router.js +494 -0
  29. package/dist/api/whatsapp-cloud-webhook-router.js.map +1 -0
  30. package/dist/config/loader.d.ts.map +1 -1
  31. package/dist/config/loader.js +7 -0
  32. package/dist/config/loader.js.map +1 -1
  33. package/dist/config/whatsapp-accounts.d.ts +77 -0
  34. package/dist/config/whatsapp-accounts.d.ts.map +1 -0
  35. package/dist/config/whatsapp-accounts.js +218 -0
  36. package/dist/config/whatsapp-accounts.js.map +1 -0
  37. package/dist/history/db.d.ts.map +1 -1
  38. package/dist/history/db.js +43 -11
  39. package/dist/history/db.js.map +1 -1
  40. package/dist/history/types.d.ts +18 -1
  41. package/dist/history/types.d.ts.map +1 -1
  42. package/dist/history/types.js +1 -1
  43. package/dist/history/types.js.map +1 -1
  44. package/dist/index.js +18 -0
  45. package/dist/index.js.map +1 -1
  46. package/dist/session/process.d.ts.map +1 -1
  47. package/dist/session/process.js +37 -0
  48. package/dist/session/process.js.map +1 -1
  49. package/dist/shared/image-optimize.d.ts +32 -0
  50. package/dist/shared/image-optimize.d.ts.map +1 -0
  51. package/dist/shared/image-optimize.js +197 -0
  52. package/dist/shared/image-optimize.js.map +1 -0
  53. package/dist/shared/image-sniff.d.ts +9 -0
  54. package/dist/shared/image-sniff.d.ts.map +1 -1
  55. package/dist/shared/image-sniff.js +13 -0
  56. package/dist/shared/image-sniff.js.map +1 -1
  57. package/dist/shared/text-chunk.d.ts +37 -0
  58. package/dist/shared/text-chunk.d.ts.map +1 -0
  59. package/dist/shared/text-chunk.js +105 -0
  60. package/dist/shared/text-chunk.js.map +1 -0
  61. package/dist/shared/whatsapp-ack.d.ts +13 -0
  62. package/dist/shared/whatsapp-ack.d.ts.map +1 -0
  63. package/dist/shared/whatsapp-ack.js +16 -0
  64. package/dist/shared/whatsapp-ack.js.map +1 -0
  65. package/dist/types.d.ts +158 -0
  66. package/dist/types.d.ts.map +1 -1
  67. package/dist/whatsapp/manager.d.ts +173 -0
  68. package/dist/whatsapp/manager.d.ts.map +1 -0
  69. package/dist/whatsapp/manager.js +838 -0
  70. package/dist/whatsapp/manager.js.map +1 -0
  71. package/mcp/server.ts +4 -0
  72. package/mcp/tools/whatsapp/module.ts +162 -0
  73. package/mcp/tools/whatsapp-cloud/module.ts +536 -0
  74. package/package.json +7 -2
package/README.md CHANGED
@@ -15,7 +15,7 @@ A self-hosted multi-agent gateway for Claude Code — with agents that improve t
15
15
  - 📚 **Knowledge base (two-lane memory)** — per-agent SQLite/FTS5 searchable archive exposed through `memory_search` / `memory_get` MCP tools, so agents recall notes that don't fit the always-injected core; chunks carry fail-closed provenance and the index is refreshed off the gateway event loop. See [`gateway.knowledge`](#gatewayknowledge)
16
16
  - 🌙 **Nightly dreaming** — background consolidation of long-term memory: a print-only reviewer proposes ops that a safe applier writes to `MEMORY.md` / `USER.md` (backup, bounded-loss, net-negative when over budget). Deterministic compaction, budget-scaled pruning, and staleness GC keep memory near budget without forgetting — archived entries stay searchable. See [`gateway.dreaming`](#gatewaydreaming)
17
17
  - 🤖 **Multi-agent** — run multiple bots from a single gateway, each with isolated sessions
18
- - 🔌 **Multi-channel MCP** — modular tool system per channel (Telegram, Discord, LINE, Slack, Cron, Skills, extensible to more)
18
+ - 🔌 **Multi-channel MCP** — modular tool system per channel (Telegram, Discord, LINE, Slack, WhatsApp, Cron, Skills, extensible to more)
19
19
  - 🧩 **Agent skills** — extensible skill system via SKILL.md files; agents can create, delete, and install skills from URLs at runtime with hot-reload
20
20
  - 🎭 **Agent identity** — define personality, tone, and rules via workspace markdown files
21
21
  - 📡 **Live status messages** — real-time status updates showing tool usage, thinking, and progress
@@ -772,6 +772,8 @@ Tokens are stored per-agent at `~/.claude-gateway/agents/<id>/.env` and auto-loa
772
772
 
773
773
  A variable you exported yourself always wins over the `.env` file and is never replaced by a reload. A token the gateway did read from a `.env` is refreshed when that file changes, so **rotating a token takes effect on the next config reload** rather than at the next restart. Note that only `config.json` is watched — editing a `.env` by hand applies on the following reload, while the MCP `agent_create` / `agent_update` tools write both files and so take effect immediately. If a `${VAR}` cannot be resolved from anywhere, that one agent is skipped — the rest of the gateway starts normally — and the skip is logged to `logs/gateway.log` with the name of the missing variable.
774
774
 
775
+ WhatsApp Cloud's credentials (`accessToken`, `phoneNumberId`, `appSecret`, `verifyToken`) are plain fields under the agent's `whatsapp_cloud` config block, not a dedicated "bot token" field — but they resolve through the exact same mechanism as Telegram/Discord bot tokens: reference them as `${VAR}` in `config.json` and put the value in the agent's `.env` (or export it as a shell variable), same as above.
776
+
775
777
  ---
776
778
 
777
779
  ## Architecture
@@ -1383,6 +1385,9 @@ sees a message. If those aren't met the bot looks online but stays silent.
1383
1385
  | | Guild | **Message Content Intent** + **View Channel** + **Read Message History** | `groupPolicy` + `guildAllowlist` (+ optional `channelAllowlist`/`roleAllowlist`) | `requireMention` false, or @mentioned/replied |
1384
1386
  | **LINE** | 1:1 | webhook delivered (valid signature) | `dmPolicy` | — |
1385
1387
  | | Group/Room | webhook delivered + bot is a member | `groupPolicy` + `groupAllowlist` | `requireMention` false, or **native** @mention |
1388
+ | **WhatsApp (Baileys)** | DM | number is linked (QR/pairing-code device link) | `dmPolicy` + `pairing` → `dmAllowlist` (per account) | — |
1389
+ | | Group | number is linked and is a member of the group | `groupPolicy` + `groupAllowlist` (per account) | `requireMention` false, or @mentioned |
1390
+ | **WhatsApp (Cloud API)** | DM | webhook delivered (valid `X-Hub-Signature-256`) | `dmPolicy` + `pairing` → `dmAllowlist` | — (DM-only, no group concept) |
1386
1391
 
1387
1392
  **Telegram limits**
1388
1393
  - Exactly one process may poll a bot token — a second poller causes `409 Conflict`.
@@ -1402,6 +1407,14 @@ sees a message. If those aren't met the bot looks online but stays silent.
1402
1407
  - Delivery is **reply-token-first (free) → push fallback (metered)**. The single-use reply token lives only ~1 min; after that, replies consume the OA's monthly push quota.
1403
1408
  - Max **5 message objects** per reply/push request (the gateway auto-chunks to fit).
1404
1409
 
1410
+ **WhatsApp limits**
1411
+ - Two independent modes, configured separately: the **Baileys** device-link bridge (`whatsapp` config block) and the **Cloud API** (`whatsapp_cloud` config block).
1412
+ - **Baileys** requires linking a device per number — QR code or a text pairing code, the same one-time handshake as WhatsApp Web. Multi-account: an agent can hold several linked numbers at once (`whatsapp.accounts[]`), each with its own DM/group policy and allowlist.
1413
+ - **Cloud API is DM-only** — a WhatsApp Business number has no group concept, so there's no `groupPolicy`/`groupAllowlist`/`requireMention` for it.
1414
+ - **Cloud API's inbound webhook requires a valid `X-Hub-Signature-256`** (HMAC-SHA256 of the raw body against `appSecret`); a bad or missing signature is rejected with `401` before the payload is parsed.
1415
+ - **Cloud API's 24-hour customer-service window**: free-form text replies only work within 24h of the user's last inbound message; outside that window only a pre-approved message template can reach them, and template sending is off by default (`templatesEnabled: false`) since it's the one send that can reach a user outside that window.
1416
+ - Inbound media cap is **20 MB** on both modes (same `MediaStore` cap LINE's file uploads use).
1417
+
1405
1418
  ---
1406
1419
 
1407
1420
  ## Telegram Groups
@@ -39,7 +39,8 @@
39
39
  {
40
40
  "key": "${MY_API_KEY}",
41
41
  "description": "Internal app",
42
- "agents": ["alfred"]
42
+ "agents": ["alfred"],
43
+ "write": true
43
44
  },
44
45
  {
45
46
  "key": "${ADMIN_API_KEY}",
@@ -1,6 +1,7 @@
1
1
  import { EventEmitter } from 'events';
2
2
  import { AgentConfig, CustomConnectorEntry, GatewayConfig, Logger, ModelConfig, ApiAttachment, ImageParams, VideoParams } from '../types';
3
3
  import { SessionProcess } from '../session/process';
4
+ import { type WhatsAppStatus } from '../whatsapp/manager';
4
5
  import { type SkillRegistry } from '../skills';
5
6
  import { type ApiStreamCallbacks, type TurnSink } from './turn-stream';
6
7
  import { type CliChannel } from '../cli-viewer/pairing-store';
@@ -9,6 +10,14 @@ import type { ChatChannel } from '../history/types';
9
10
  import type { SkillLearningManager } from './skill-learning';
10
11
  export declare const MAX_IMAGE_SIZE_BYTES: number;
11
12
  export declare const DEFAULT_MODELS: ModelConfig[];
13
+ /**
14
+ * One-line reminder injected into every `whatsapp_cloud` turn's <channel>
15
+ * block (see buildChannelXml) — the 24h customer-service window is a
16
+ * WhatsApp-Business-only rule the agent cannot infer from the turn itself.
17
+ *
18
+ * Must stay free of `--` so it remains a well-formed XML comment.
19
+ */
20
+ export declare const WHATSAPP_CLOUD_WINDOW_NOTE: string;
12
21
  export declare const CHANNEL_COALESCE_WINDOW_MS = 1200;
13
22
  /**
14
23
  * Convert absolute file paths (e.g. from a reply tool's `files` input) into
@@ -42,6 +51,10 @@ export declare class AgentRunner extends EventEmitter {
42
51
  private discordReceiver;
43
52
  private lineReply;
44
53
  private slackOutbound;
54
+ private whatsAppCloudOutbound;
55
+ private readonly whatsappAccounts;
56
+ private whatsappConfigSnapshot;
57
+ private readonly whatsappAccountForChat;
45
58
  private readonly sessionStore;
46
59
  private readonly idleTimeoutMs;
47
60
  private readonly maxConcurrent;
@@ -475,8 +488,67 @@ export declare class AgentRunner extends EventEmitter {
475
488
  */
476
489
  private sweepStaleSessionDirs;
477
490
  updateAgentConfig(newConfig: AgentConfig): void;
491
+ /**
492
+ * Reconcile `this.whatsappAccounts` with `agentConfig.whatsapp.accounts`.
493
+ *
494
+ * Diffing (rather than teardown+recreate) is load-bearing here: a manager
495
+ * holds a live, authenticated Baileys socket that can take tens of seconds
496
+ * to re-establish, so an edit to account "sales" must not drop account
497
+ * "default"'s connection. Accounts that survive the diff only get the new
498
+ * AgentConfig pushed into them — their socket is untouched.
499
+ *
500
+ * Removal uses stop() (socket teardown), never unlink(): dropping an
501
+ * account from config must not silently wipe its on-disk session. The
502
+ * DELETE .../whatsapp/accounts/:id route unlinks explicitly before it
503
+ * rewrites config.
504
+ */
505
+ private syncWhatsAppAccounts;
506
+ /** Live managers, in configured order. */
507
+ private whatsAppManagers;
508
+ /**
509
+ * Pick the manager a call should use.
510
+ *
511
+ * An EXPLICIT accountId must exist — silently falling back would send a
512
+ * reply from the wrong number, which on WhatsApp is visible to the
513
+ * recipient and unrecoverable. Without one, prefer the account the chat's
514
+ * last inbound message arrived on (so a reply leaves on the number the user
515
+ * actually wrote to), then 'default', then — for an agent whose only
516
+ * account is named something else — its single account.
517
+ */
518
+ private whatsAppManagerFor;
519
+ /**
520
+ * Which account id a send to `chatId` (with an optional explicit
521
+ * `accountId`) would actually use — the same resolution
522
+ * `sendWhatsAppMessage` applies internally, exposed read-only so a caller
523
+ * (the `/whatsapp/send` route) can run access-control/path-confinement
524
+ * checks against the REAL target account without duplicating or
525
+ * second-guessing this resolution logic. Throws the same errors
526
+ * `whatsAppManagerFor` would (unknown account / agent not started).
527
+ */
528
+ resolveWhatsAppAccountId(chatId: string | undefined, accountId?: string): string;
529
+ /** Live link status for one account (defaults to 'default'). */
530
+ getWhatsAppStatus(accountId?: string): WhatsAppStatus | undefined;
531
+ /** Configured account ids that currently have a live manager. */
532
+ getWhatsAppAccountIds(): string[];
533
+ startWhatsAppLinking(accountId?: string): Promise<void>;
534
+ requestWhatsAppPairingCode(phoneNumber: string, accountId?: string): Promise<string>;
535
+ unlinkWhatsApp(accountId?: string): Promise<void>;
536
+ sendWhatsAppMessage(jid: string, text: string, imagePath?: string, accountId?: string,
537
+ /**
538
+ * Phase 2 extras, all optional so existing callers (auto-forward, older
539
+ * MCP builds) are unaffected: quote an inbound message, send the image
540
+ * uncompressed as a document, and clear the ⏳ ack left on the inbound
541
+ * message once the send lands.
542
+ */
543
+ opts?: {
544
+ quotedMessageId?: string;
545
+ asDocument?: boolean;
546
+ ackMessageId?: string;
547
+ }): Promise<void>;
478
548
  startSlackOutbound(): void;
479
549
  stopSlackOutbound(): void;
550
+ startWhatsAppCloudOutbound(): void;
551
+ stopWhatsAppCloudOutbound(): void;
480
552
  startLineReply(): void;
481
553
  stopLineReply(): void;
482
554
  getAgentConfig(): AgentConfig;
@@ -1 +1 @@
1
- {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/agent/runner.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/agent/runner.ts"],"names":[],"mappings":"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"}
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.AgentRunner = exports.CHANNEL_COALESCE_WINDOW_MS = exports.DEFAULT_MODELS = exports.MAX_IMAGE_SIZE_BYTES = void 0;
36
+ exports.AgentRunner = exports.CHANNEL_COALESCE_WINDOW_MS = exports.WHATSAPP_CLOUD_WINDOW_NOTE = exports.DEFAULT_MODELS = exports.MAX_IMAGE_SIZE_BYTES = void 0;
37
37
  exports.toRelMediaFiles = toRelMediaFiles;
38
38
  const crypto_1 = require("crypto");
39
39
  const events_1 = require("events");
@@ -51,6 +51,9 @@ const receiver_1 = require("../telegram/receiver");
51
51
  const receiver_2 = require("../discord/receiver");
52
52
  const line_reply_manager_1 = require("./line-reply-manager");
53
53
  const slack_client_1 = require("../api/slack-client");
54
+ const whatsapp_cloud_client_1 = require("../api/whatsapp-cloud-client");
55
+ const manager_1 = require("../whatsapp/manager");
56
+ const whatsapp_accounts_1 = require("../config/whatsapp-accounts");
54
57
  const markdown_1 = require("../telegram/markdown");
55
58
  const skills_1 = require("../skills");
56
59
  const builtin_commands_1 = require("./builtin-commands");
@@ -125,6 +128,15 @@ const PROTECTED_WORKSPACE_FILES = [
125
128
  'AGENTS.md', 'SOUL.md', 'MEMORY.md', 'CLAUDE.md',
126
129
  'IDENTITY.md', 'USER.md', 'HEARTBEAT.md',
127
130
  ];
131
+ /**
132
+ * One-line reminder injected into every `whatsapp_cloud` turn's <channel>
133
+ * block (see buildChannelXml) — the 24h customer-service window is a
134
+ * WhatsApp-Business-only rule the agent cannot infer from the turn itself.
135
+ *
136
+ * Must stay free of `--` so it remains a well-formed XML comment.
137
+ */
138
+ exports.WHATSAPP_CLOUD_WINDOW_NOTE = '[WhatsApp Business: free-form replies only work within 24h of the user\'s last message. ' +
139
+ 'Outside that window, whatsapp_cloud_reply requires template_name + template_language (see templatesEnabled).]';
128
140
  const MAX_API_IMAGES = 5;
129
141
  /** Extra time an api turn may keep running AFTER its soft timeout already
130
142
  * answered the caller (#75). The soft timeout only abandons the WAIT — the
@@ -236,6 +248,29 @@ class AgentRunner extends events_1.EventEmitter {
236
248
  // it exists only so writeAutoForward's fallback/command-reply path (below) has
237
249
  // somewhere to actually deliver Slack messages instead of silently dropping them.
238
250
  this.slackOutbound = null;
251
+ // WhatsApp Cloud mirrors Slack exactly: webhook-based, real credentials, no
252
+ // reply-token TTL to work around — a plain client so writeAutoForward's
253
+ // fallback/command-reply path has somewhere to deliver messages instead of
254
+ // silently dropping them (see the Slack comment just above).
255
+ this.whatsAppCloudOutbound = null;
256
+ // One live Baileys socket per linked WhatsApp account, keyed by account id
257
+ // (Phase 1 of the WhatsApp feature-parity plan — this used to be a single
258
+ // `whatsapp: WhatsAppManager | null`). Populated in start() (needs
259
+ // this.callbackPort, only resolved once startCallbackServer() runs — same
260
+ // reason DiscordReceiver/TelegramReceiver are built there too) and then
261
+ // DIFFED, never rebuilt wholesale, by syncWhatsAppAccounts(): unlike
262
+ // SlackClient/DiscordReceiver there is no credential to re-read on a config
263
+ // change, since the credential IS the on-disk session (see
264
+ // WhatsAppAccountConfig's doc comment), so tearing a linked socket down on
265
+ // an unrelated config edit would be pure downtime. Each manager decides for
266
+ // itself whether there's anything to do on start (resumeIfLinked() no-ops
267
+ // if that account was never linked).
268
+ this.whatsappAccounts = new Map();
269
+ // chatId → the account id its last inbound message arrived on. Mirrors
270
+ // slackThreadTs above: it lets writeAutoForward (and a whatsapp_reply call
271
+ // that omits account_id) answer on the SAME number the user wrote to
272
+ // instead of guessing 'default' and replying from the wrong line.
273
+ this.whatsappAccountForChat = new Map();
239
274
  this.idleCleanerTimer = null;
240
275
  // Tracks session IDs with an in-flight API request (prevents concurrent turns)
241
276
  this.pendingApiSessions = new Set();
@@ -439,7 +474,11 @@ class AgentRunner extends events_1.EventEmitter {
439
474
  ? 'line'
440
475
  : meta['source'] === 'slack'
441
476
  ? 'slack'
442
- : 'telegram');
477
+ : meta['source'] === 'whatsapp'
478
+ ? 'whatsapp'
479
+ : meta['source'] === 'whatsapp_cloud'
480
+ ? 'whatsapp_cloud'
481
+ : 'telegram');
443
482
  this.channelSourceMap.set(chatId, channelSource);
444
483
  // Slack: remember the current message's thread context so the
445
484
  // auto-forward fallback (writeAutoForward) can stay in-thread. A
@@ -452,6 +491,14 @@ class AgentRunner extends events_1.EventEmitter {
452
491
  else
453
492
  this.slackThreadTs.delete(chatId);
454
493
  }
494
+ // WhatsApp: remember which linked number this chat last came in on,
495
+ // so a reply that doesn't name an account_id (the auto-forward
496
+ // fallback, or a whatsapp_reply call that omitted it) still goes out
497
+ // on the number the user actually wrote to. Same shape as the Slack
498
+ // thread-context stash just above.
499
+ if (channelSource === 'whatsapp' && meta['account_id']) {
500
+ this.whatsappAccountForChat.set(chatId, meta['account_id']);
501
+ }
455
502
  // LINE slow-LLM postback: stash this turn's reply token + arm the
456
503
  // button timer before the token expires. In groups/rooms the shared
457
504
  // button is disabled (armButton:false) — the token is still stashed so
@@ -1027,7 +1074,7 @@ class AgentRunner extends events_1.EventEmitter {
1027
1074
  for (const entry of entries) {
1028
1075
  const meta = entry.meta ?? {};
1029
1076
  const content = entry.content ?? '';
1030
- const userContent = content || (meta['attachment_file_id'] || meta['image_path'] ? '(photo)' : '');
1077
+ const userContent = content || (meta['attachment_file_id'] || meta['image_path'] ? '(photo)' : meta['document_path'] ? '(document)' : meta['sticker_path'] ? '(sticker)' : '');
1031
1078
  const userTs = Date.now();
1032
1079
  await this.sessionStore.appendTelegramMessage(this.agentConfig.id, chatId, sessionId, {
1033
1080
  role: 'user',
@@ -1058,6 +1105,33 @@ class AgentRunner extends events_1.EventEmitter {
1058
1105
  // Non-fatal — leave the original path so host agents still read it
1059
1106
  }
1060
1107
  }
1108
+ // WhatsApp Cloud documents (PDF today — see MediaStore.isAllowedMime):
1109
+ // same MediaStore copy + path-rewrite as image_path above, parallel
1110
+ // key so an inbound document doesn't collide with an inbound image
1111
+ // in the same turn.
1112
+ if (meta['document_path']) {
1113
+ try {
1114
+ const rel = media_store_1.MediaStore.copyToMedia(this.agentsBaseDir, this.agentConfig.id, `${channelSource}-${chatId}`, meta['document_path']);
1115
+ mediaFiles.push(rel);
1116
+ meta['document_path'] = media_store_1.MediaStore.resolvePath(this.agentsBaseDir, this.agentConfig.id, rel);
1117
+ }
1118
+ catch {
1119
+ // Non-fatal — leave the original path so host agents still read it
1120
+ }
1121
+ }
1122
+ // WhatsApp stickers (both channels — image/webp): same MediaStore
1123
+ // copy + path-rewrite as image_path above, on its own key so an
1124
+ // inbound sticker never masquerades as a photo.
1125
+ if (meta['sticker_path']) {
1126
+ try {
1127
+ const rel = media_store_1.MediaStore.copyToMedia(this.agentsBaseDir, this.agentConfig.id, `${channelSource}-${chatId}`, meta['sticker_path']);
1128
+ mediaFiles.push(rel);
1129
+ meta['sticker_path'] = media_store_1.MediaStore.resolvePath(this.agentsBaseDir, this.agentConfig.id, rel);
1130
+ }
1131
+ catch {
1132
+ // Non-fatal — leave the original path so host agents still read it
1133
+ }
1134
+ }
1061
1135
  this.historyDb.insertMessage({
1062
1136
  chatId: `${channelSource}-${chatId}`,
1063
1137
  sessionId,
@@ -1068,6 +1142,14 @@ class AgentRunner extends events_1.EventEmitter {
1068
1142
  senderId: meta['user_id'] ?? meta['chat_id'] ?? undefined,
1069
1143
  platformMessageId: meta['message_id'] ?? undefined,
1070
1144
  mediaFiles: mediaFiles.length > 0 ? mediaFiles : undefined,
1145
+ // Reply context (WhatsApp Phase 2) — the same meta keys buildChannelXml
1146
+ // renders into <replied> for the agent, persisted so the web dashboard
1147
+ // can show "in reply to X" instead of losing it after the turn.
1148
+ // whatsapp_cloud only ever sets replied_message_id (its webhook reports
1149
+ // the quoted id and nothing else), so the other two stay undefined there.
1150
+ repliedToMessageId: meta['replied_message_id'] ?? undefined,
1151
+ repliedToText: meta['replied_text'] ?? undefined,
1152
+ repliedToUser: meta['replied_user'] ?? undefined,
1071
1153
  ts: userTs,
1072
1154
  });
1073
1155
  }
@@ -1330,6 +1412,11 @@ class AgentRunner extends events_1.EventEmitter {
1330
1412
  const meta = params.meta ?? {};
1331
1413
  const optionalAttrs = [
1332
1414
  'image_path',
1415
+ 'document_path', // WhatsApp Cloud: inbound PDF document (see MediaStore.isAllowedMime)
1416
+ 'sticker_path', // WhatsApp (both channels): inbound sticker — kept distinct from image_path so the agent can tell a sticker from a photo
1417
+ 'location_lat', // WhatsApp (both channels): inbound location pin
1418
+ 'location_lng',
1419
+ 'vcard', // WhatsApp: inbound contact card — raw vCard on Baileys, synthesized from Meta's structured payload on Cloud
1333
1420
  'attachment_file_id',
1334
1421
  'attachment_kind',
1335
1422
  'attachment_mime',
@@ -1337,6 +1424,8 @@ class AgentRunner extends events_1.EventEmitter {
1337
1424
  'user_id', // LINE: the userId the session passes back to line_reply
1338
1425
  'reply_token', // LINE: single-use reply token (push is preferred; surfaced for completeness)
1339
1426
  'thread_ts', // Slack: set when the inbound message is inside a thread — pass back as thread_id to slack_reply to reply in-thread
1427
+ 'account_id', // WhatsApp: which linked number this arrived on — pass back to whatsapp_reply to answer on the same one
1428
+ 'interactive_id', // WhatsApp Cloud: the id of the tapped button / picked list row (content already carries its title)
1340
1429
  // NOTE: message_id is NOT listed here — the base <channel> template below
1341
1430
  // already unconditionally emits it; adding it here would duplicate the
1342
1431
  // attribute in the XML whenever meta.message_id is set (any channel).
@@ -1364,9 +1453,18 @@ class AgentRunner extends events_1.EventEmitter {
1364
1453
  `</replied>`;
1365
1454
  }
1366
1455
  const source = meta['source'] ?? 'telegram';
1456
+ // Per-turn channel note (Phase 3). WhatsApp Business enforces a 24h
1457
+ // customer-service window that no other channel here has, and the agent
1458
+ // cannot discover it from the turn itself — it only shows up as an opaque
1459
+ // Meta error at send time. Emitted as an XML COMMENT so it can never be
1460
+ // mistaken for part of the user's message, and kept to ONE short line
1461
+ // because it repeats on every single whatsapp_cloud turn. (A frozen,
1462
+ // system-prompt-level injection would be new infra for one warning; the
1463
+ // per-turn wrapper is the existing seam.)
1464
+ const channelNote = source === 'whatsapp_cloud' ? `<!-- ${exports.WHATSAPP_CLOUD_WINDOW_NOTE} -->` : '';
1367
1465
  return (`<channel source="${source}" chat_id="${meta['chat_id'] ?? ''}" ` +
1368
1466
  `message_id="${meta['message_id'] ?? ''}" user="${AgentRunner.escapeXmlAttr(meta['user'] ?? '')}" ` +
1369
- `ts="${meta['ts'] ?? new Date().toISOString()}"${optionalAttrs}>${repliedBlock}${params.content ?? ''}</channel>`);
1467
+ `ts="${meta['ts'] ?? new Date().toISOString()}"${optionalAttrs}>${channelNote}${repliedBlock}${params.content ?? ''}</channel>`);
1370
1468
  }
1371
1469
  /**
1372
1470
  * Await a SessionProcess's in-flight crash-triggered auto-restart
@@ -1625,7 +1723,11 @@ class AgentRunner extends events_1.EventEmitter {
1625
1723
  ? 'mcp__gateway__line_reply'
1626
1724
  : source === 'slack'
1627
1725
  ? 'mcp__gateway__slack_reply'
1628
- : 'mcp__gateway__telegram_reply';
1726
+ : source === 'whatsapp'
1727
+ ? 'mcp__gateway__whatsapp_reply'
1728
+ : source === 'whatsapp_cloud'
1729
+ ? 'mcp__gateway__whatsapp_cloud_reply'
1730
+ : 'mcp__gateway__telegram_reply';
1629
1731
  proc.on('output', (line) => {
1630
1732
  try {
1631
1733
  const obj = JSON.parse(line);
@@ -1938,10 +2040,15 @@ class AgentRunner extends events_1.EventEmitter {
1938
2040
  }
1939
2041
  else if (channelSrcForResult !== 'discord' &&
1940
2042
  channelSrcForResult !== 'slack' &&
2043
+ channelSrcForResult !== 'whatsapp' &&
2044
+ channelSrcForResult !== 'whatsapp_cloud' &&
1941
2045
  ((0, markdown_1.hasMarkdown)(channelText) || (0, markdown_1.containsTelegramHtml)(channelText))) {
1942
2046
  // Telegram HTML entities — Slack has its own mrkdwn format and
1943
2047
  // would display these tags literally, so Slack skips this and
1944
- // falls through to the plain-text branch below.
2048
+ // falls through to the plain-text branch below. WhatsApp (and
2049
+ // WhatsApp Cloud, same lightweight markup) has
2050
+ // its own lightweight markup (*bold*/_italic_/~strike~, not
2051
+ // HTML), same reasoning.
1945
2052
  this.writeAutoForward(mapKey, (0, markdown_1.toTelegramHtml)(channelText), 'html', replySendFailed);
1946
2053
  }
1947
2054
  else {
@@ -2798,6 +2905,36 @@ class AgentRunner extends events_1.EventEmitter {
2798
2905
  }
2799
2906
  return;
2800
2907
  }
2908
+ // WhatsApp likewise has no .forward consumer, and — unlike Slack/SMS's
2909
+ // outbound REST clients — cannot open a fresh connection per call either
2910
+ // (see WhatsAppManager's doc comment): reach the live socket this
2911
+ // process already holds directly.
2912
+ if (this.channelFor(chatId) === 'whatsapp') {
2913
+ // No account_id to go on here (this is the fallback path, not a tool
2914
+ // call), so whatsAppManagerFor falls back to the account this chat's
2915
+ // last inbound message arrived on — the number the user wrote to.
2916
+ void this.sendWhatsAppMessage(chatId, text).catch((err) => {
2917
+ this.logger.warn('WhatsApp auto-forward failed', {
2918
+ chatId,
2919
+ error: err instanceof Error ? err.message : String(err),
2920
+ });
2921
+ });
2922
+ return;
2923
+ }
2924
+ // WhatsApp Cloud mirrors Slack exactly (no .forward consumer, a real REST
2925
+ // client that can open a fresh connection per call — unlike Baileys'
2926
+ // `whatsapp` above, there's no live socket to reuse or reason to).
2927
+ if (this.channelFor(chatId) === 'whatsapp_cloud') {
2928
+ if (this.whatsAppCloudOutbound) {
2929
+ void this.whatsAppCloudOutbound.sendText(chatId, text).catch((err) => {
2930
+ this.logger.warn('WhatsApp Cloud auto-forward failed', {
2931
+ chatId,
2932
+ error: err instanceof Error ? err.message : String(err),
2933
+ });
2934
+ });
2935
+ }
2936
+ return;
2937
+ }
2801
2938
  const typingDir = this.getTypingDir(chatId);
2802
2939
  const forwardPath = path.join(typingDir, `${chatId}.forward`);
2803
2940
  try {
@@ -2897,6 +3034,15 @@ class AgentRunner extends events_1.EventEmitter {
2897
3034
  // line_reply tool keeps the plain reply-first → push-fallback path).
2898
3035
  this.startLineReply();
2899
3036
  this.startSlackOutbound();
3037
+ this.startWhatsAppCloudOutbound();
3038
+ // Unconditional (unlike every credential-gated channel above) — each
3039
+ // WhatsAppManager checks disk for a previously-linked session itself;
3040
+ // there's no config field to gate on, and an agent with no `whatsapp`
3041
+ // block still gets the implicit 'default' account (see
3042
+ // resolveWhatsAppAccounts). A brand-new agent that's never linked just
3043
+ // no-ops here and stays 'unlinked' until the user starts a QR/pairing
3044
+ // flow via the API.
3045
+ this.syncWhatsAppAccounts();
2900
3046
  this.startIdleCleaner();
2901
3047
  this._startCleanupScheduler();
2902
3048
  this.logger.info('AgentRunner started', { agentId: this.agentConfig.id });
@@ -2950,6 +3096,129 @@ class AgentRunner extends events_1.EventEmitter {
2950
3096
  // it up, same reasoning as LineReplyManager above.
2951
3097
  this.stopSlackOutbound();
2952
3098
  this.startSlackOutbound();
3099
+ // WhatsApp Cloud credentials may have changed (or been cleared) — rebuild
3100
+ // to pick it up, same reasoning as Slack above.
3101
+ this.stopWhatsAppCloudOutbound();
3102
+ this.startWhatsAppCloudOutbound();
3103
+ // WhatsApp has no credential to rebuild on — push the new config
3104
+ // (access-control fields) into every already-running manager, and
3105
+ // add/remove managers only if the set of configured accounts changed.
3106
+ this.syncWhatsAppAccounts();
3107
+ }
3108
+ /**
3109
+ * Reconcile `this.whatsappAccounts` with `agentConfig.whatsapp.accounts`.
3110
+ *
3111
+ * Diffing (rather than teardown+recreate) is load-bearing here: a manager
3112
+ * holds a live, authenticated Baileys socket that can take tens of seconds
3113
+ * to re-establish, so an edit to account "sales" must not drop account
3114
+ * "default"'s connection. Accounts that survive the diff only get the new
3115
+ * AgentConfig pushed into them — their socket is untouched.
3116
+ *
3117
+ * Removal uses stop() (socket teardown), never unlink(): dropping an
3118
+ * account from config must not silently wipe its on-disk session. The
3119
+ * DELETE .../whatsapp/accounts/:id route unlinks explicitly before it
3120
+ * rewrites config.
3121
+ */
3122
+ syncWhatsAppAccounts() {
3123
+ const snapshot = JSON.stringify(this.agentConfig.whatsapp ?? null);
3124
+ const accounts = (0, whatsapp_accounts_1.resolveWhatsAppAccounts)(this.agentConfig.whatsapp);
3125
+ // Always push the fresh config into live managers — access-control fields
3126
+ // are read live off it on every inbound message.
3127
+ for (const manager of this.whatsappAccounts.values()) {
3128
+ manager.updateAgentConfig(this.agentConfig);
3129
+ }
3130
+ // Nothing about the WhatsApp block moved ⇒ the account SET can't have
3131
+ // changed either, so skip the diff entirely.
3132
+ if (this.whatsappConfigSnapshot === snapshot && this.whatsappAccounts.size > 0)
3133
+ return;
3134
+ this.whatsappConfigSnapshot = snapshot;
3135
+ const wanted = new Set(accounts.map((a) => a.id));
3136
+ for (const [accountId, manager] of [...this.whatsappAccounts]) {
3137
+ if (wanted.has(accountId))
3138
+ continue;
3139
+ manager.stop();
3140
+ this.whatsappAccounts.delete(accountId);
3141
+ this.logger.info('WhatsApp account removed', { agentId: this.agentConfig.id, accountId });
3142
+ }
3143
+ for (const account of accounts) {
3144
+ if (this.whatsappAccounts.has(account.id))
3145
+ continue;
3146
+ const manager = new manager_1.WhatsAppManager(this.agentConfig, account.id, this.callbackPort, this.gatewayConfig.gateway.logDir);
3147
+ this.whatsappAccounts.set(account.id, manager);
3148
+ void manager.resumeIfLinked();
3149
+ this.logger.info('WhatsApp account started', { agentId: this.agentConfig.id, accountId: account.id });
3150
+ }
3151
+ }
3152
+ /** Live managers, in configured order. */
3153
+ whatsAppManagers() {
3154
+ return (0, whatsapp_accounts_1.resolveWhatsAppAccounts)(this.agentConfig.whatsapp)
3155
+ .map((a) => this.whatsappAccounts.get(a.id))
3156
+ .filter((m) => !!m);
3157
+ }
3158
+ /**
3159
+ * Pick the manager a call should use.
3160
+ *
3161
+ * An EXPLICIT accountId must exist — silently falling back would send a
3162
+ * reply from the wrong number, which on WhatsApp is visible to the
3163
+ * recipient and unrecoverable. Without one, prefer the account the chat's
3164
+ * last inbound message arrived on (so a reply leaves on the number the user
3165
+ * actually wrote to), then 'default', then — for an agent whose only
3166
+ * account is named something else — its single account.
3167
+ */
3168
+ whatsAppManagerFor(chatId, accountId) {
3169
+ if (accountId) {
3170
+ const explicit = this.whatsappAccounts.get(accountId);
3171
+ if (!explicit)
3172
+ throw new Error(`Unknown WhatsApp account '${accountId}'`);
3173
+ return explicit;
3174
+ }
3175
+ const remembered = chatId ? this.whatsappAccountForChat.get(chatId) : undefined;
3176
+ const managers = this.whatsAppManagers();
3177
+ const picked = (remembered ? this.whatsappAccounts.get(remembered) : undefined) ??
3178
+ this.whatsappAccounts.get(whatsapp_accounts_1.DEFAULT_WHATSAPP_ACCOUNT_ID) ??
3179
+ managers[0];
3180
+ if (!picked)
3181
+ throw new Error('Agent not started');
3182
+ return picked;
3183
+ }
3184
+ /**
3185
+ * Which account id a send to `chatId` (with an optional explicit
3186
+ * `accountId`) would actually use — the same resolution
3187
+ * `sendWhatsAppMessage` applies internally, exposed read-only so a caller
3188
+ * (the `/whatsapp/send` route) can run access-control/path-confinement
3189
+ * checks against the REAL target account without duplicating or
3190
+ * second-guessing this resolution logic. Throws the same errors
3191
+ * `whatsAppManagerFor` would (unknown account / agent not started).
3192
+ */
3193
+ resolveWhatsAppAccountId(chatId, accountId) {
3194
+ return this.whatsAppManagerFor(chatId, accountId).accountId;
3195
+ }
3196
+ /** Live link status for one account (defaults to 'default'). */
3197
+ getWhatsAppStatus(accountId = whatsapp_accounts_1.DEFAULT_WHATSAPP_ACCOUNT_ID) {
3198
+ return this.whatsappAccounts.get(accountId)?.getStatus();
3199
+ }
3200
+ /** Configured account ids that currently have a live manager. */
3201
+ getWhatsAppAccountIds() {
3202
+ return this.whatsAppManagers().map((m) => m.accountId);
3203
+ }
3204
+ async startWhatsAppLinking(accountId) {
3205
+ await this.whatsAppManagerFor(undefined, accountId).startLinking();
3206
+ }
3207
+ async requestWhatsAppPairingCode(phoneNumber, accountId) {
3208
+ return this.whatsAppManagerFor(undefined, accountId).requestPairingCode(phoneNumber);
3209
+ }
3210
+ async unlinkWhatsApp(accountId) {
3211
+ await this.whatsAppManagerFor(undefined, accountId).unlink();
3212
+ }
3213
+ async sendWhatsAppMessage(jid, text, imagePath, accountId,
3214
+ /**
3215
+ * Phase 2 extras, all optional so existing callers (auto-forward, older
3216
+ * MCP builds) are unaffected: quote an inbound message, send the image
3217
+ * uncompressed as a document, and clear the ⏳ ack left on the inbound
3218
+ * message once the send lands.
3219
+ */
3220
+ opts) {
3221
+ await this.whatsAppManagerFor(jid, accountId).sendMessage(jid, text, imagePath, opts);
2953
3222
  }
2954
3223
  startSlackOutbound() {
2955
3224
  if (!this.agentConfig.slack?.botToken || !this.agentConfig.slack?.signingSecret)
@@ -2964,6 +3233,21 @@ class AgentRunner extends events_1.EventEmitter {
2964
3233
  stopSlackOutbound() {
2965
3234
  this.slackOutbound = null;
2966
3235
  }
3236
+ startWhatsAppCloudOutbound() {
3237
+ const cfg = this.agentConfig.whatsapp_cloud;
3238
+ if (!cfg?.accessToken || !cfg?.phoneNumberId || !cfg?.appSecret || !cfg?.verifyToken)
3239
+ return;
3240
+ if (this.whatsAppCloudOutbound)
3241
+ return; // already running
3242
+ this.whatsAppCloudOutbound = new whatsapp_cloud_client_1.WhatsAppCloudClient({
3243
+ accessToken: cfg.accessToken,
3244
+ phoneNumberId: cfg.phoneNumberId,
3245
+ logDir: this.gatewayConfig.gateway.logDir,
3246
+ });
3247
+ }
3248
+ stopWhatsAppCloudOutbound() {
3249
+ this.whatsAppCloudOutbound = null;
3250
+ }
2967
3251
  startLineReply() {
2968
3252
  const lineThreshold = this.agentConfig.line?.slowResponseThreshold ?? 45;
2969
3253
  if (!this.agentConfig.line?.channelSecret || !this.agentConfig.line?.channelAccessToken || lineThreshold <= 0)
@@ -3060,6 +3344,16 @@ class AgentRunner extends events_1.EventEmitter {
3060
3344
  // teardown rather than serially — both are bounded, neither depends on the
3061
3345
  // other, and shutdown latency is user-visible.
3062
3346
  const receiversStopped = [this.receiver?.stop(), this.discordReceiver?.stop()];
3347
+ // stop(), not unlink() — gateway shutdown should NOT wipe a linked
3348
+ // session; resumeIfLinked() picks each account back up on next boot.
3349
+ // Synchronous (no socket-close promise to await), unlike the receivers above.
3350
+ for (const manager of this.whatsappAccounts.values())
3351
+ manager.stop();
3352
+ this.whatsappAccounts.clear();
3353
+ this.whatsappAccountForChat.clear();
3354
+ // Force syncWhatsAppAccounts() to rebuild from scratch if this runner is
3355
+ // started again, rather than short-circuiting on a stale snapshot.
3356
+ this.whatsappConfigSnapshot = undefined;
3063
3357
  this.stopLineReply();
3064
3358
  await Promise.all([
3065
3359
  ...receiversStopped,