@rubytech/create-maxy-code 0.1.417 → 0.1.423

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 (165) hide show
  1. package/dist/__tests__/brand-port-uniqueness.test.js +74 -0
  2. package/dist/known-brands.js +1 -0
  3. package/package.json +1 -1
  4. package/payload/platform/config/brand-registry.json +8 -0
  5. package/payload/platform/lib/active-rules/dist/index.d.ts +35 -0
  6. package/payload/platform/lib/active-rules/dist/index.d.ts.map +1 -0
  7. package/payload/platform/lib/active-rules/dist/index.js +94 -0
  8. package/payload/platform/lib/active-rules/dist/index.js.map +1 -0
  9. package/payload/platform/lib/active-rules/package.json +7 -0
  10. package/payload/platform/lib/active-rules/src/index.test.ts +89 -0
  11. package/payload/platform/lib/active-rules/src/index.ts +133 -0
  12. package/payload/platform/lib/active-rules/tsconfig.json +9 -0
  13. package/payload/platform/package-lock.json +5158 -2922
  14. package/payload/platform/package.json +4 -3
  15. package/payload/platform/plugins/admin/hooks/__tests__/prompt-optimiser-compliance.test.sh +22 -0
  16. package/payload/platform/plugins/admin/hooks/prompt-optimiser-compliance.sh +14 -1
  17. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +16 -2
  18. package/payload/platform/plugins/docs/references/internals.md +10 -0
  19. package/payload/platform/plugins/docs/references/memory-guide.md +4 -0
  20. package/payload/platform/plugins/docs/references/outlook-guide.md +1 -1
  21. package/payload/platform/plugins/memory/PLUGIN.md +3 -0
  22. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-read-active-rules.test.d.ts +2 -0
  23. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-read-active-rules.test.d.ts.map +1 -0
  24. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-read-active-rules.test.js +49 -0
  25. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-read-active-rules.test.js.map +1 -0
  26. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-reconcile.test.d.ts +2 -0
  27. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-reconcile.test.d.ts.map +1 -0
  28. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-reconcile.test.js +99 -0
  29. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-reconcile.test.js.map +1 -0
  30. package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.d.ts +3 -0
  31. package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.d.ts.map +1 -1
  32. package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.js +11 -3
  33. package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.js.map +1 -1
  34. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.d.ts +21 -0
  35. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.d.ts.map +1 -1
  36. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js +106 -14
  37. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js.map +1 -1
  38. package/payload/platform/plugins/memory/skills/conversational-memory/SKILL.md +30 -1
  39. package/payload/platform/plugins/memory/skills/preference-audit/SKILL.md +50 -0
  40. package/payload/platform/plugins/outlook/mcp/dist/__tests__/calendar-write.test.js +24 -0
  41. package/payload/platform/plugins/outlook/mcp/dist/__tests__/calendar-write.test.js.map +1 -1
  42. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-create.d.ts +15 -2
  43. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-create.d.ts.map +1 -1
  44. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-create.js +16 -7
  45. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-create.js.map +1 -1
  46. package/payload/platform/plugins/whatsapp/PLUGIN.md +3 -1
  47. package/payload/platform/plugins/whatsapp/mcp/dist/__tests__/resolve-read-account.test.d.ts +2 -0
  48. package/payload/platform/plugins/whatsapp/mcp/dist/__tests__/resolve-read-account.test.d.ts.map +1 -0
  49. package/payload/platform/plugins/whatsapp/mcp/dist/__tests__/resolve-read-account.test.js +15 -0
  50. package/payload/platform/plugins/whatsapp/mcp/dist/__tests__/resolve-read-account.test.js.map +1 -0
  51. package/payload/platform/plugins/whatsapp/mcp/dist/index.js +14 -8
  52. package/payload/platform/plugins/whatsapp/mcp/dist/index.js.map +1 -1
  53. package/payload/platform/plugins/whatsapp/mcp/dist/resolve-read-account.d.ts +7 -0
  54. package/payload/platform/plugins/whatsapp/mcp/dist/resolve-read-account.d.ts.map +1 -0
  55. package/payload/platform/plugins/whatsapp/mcp/dist/resolve-read-account.js +13 -0
  56. package/payload/platform/plugins/whatsapp/mcp/dist/resolve-read-account.js.map +1 -0
  57. package/payload/platform/plugins/whatsapp/references/channels-whatsapp.md +9 -1
  58. package/payload/platform/plugins/work/mcp/dist/__tests__/work-create-raised-by-env.test.d.ts +2 -0
  59. package/payload/platform/plugins/work/mcp/dist/__tests__/work-create-raised-by-env.test.d.ts.map +1 -0
  60. package/payload/platform/plugins/work/mcp/dist/__tests__/work-create-raised-by-env.test.js +66 -0
  61. package/payload/platform/plugins/work/mcp/dist/__tests__/work-create-raised-by-env.test.js.map +1 -0
  62. package/payload/platform/plugins/work/mcp/dist/tools/work-create.d.ts.map +1 -1
  63. package/payload/platform/plugins/work/mcp/dist/tools/work-create.js +8 -1
  64. package/payload/platform/plugins/work/mcp/dist/tools/work-create.js.map +1 -1
  65. package/payload/platform/scripts/__tests__/provision-role-stamp.test.sh +3 -0
  66. package/payload/platform/scripts/lib/provision-account-dir.sh +9 -1
  67. package/payload/platform/services/claude-session-manager/dist/agent-identity-locator.d.ts +1 -1
  68. package/payload/platform/services/claude-session-manager/dist/agent-identity-locator.d.ts.map +1 -1
  69. package/payload/platform/services/claude-session-manager/dist/agent-identity-locator.js +5 -1
  70. package/payload/platform/services/claude-session-manager/dist/agent-identity-locator.js.map +1 -1
  71. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts +1 -0
  72. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  73. package/payload/platform/services/claude-session-manager/dist/http-server.js +65 -4
  74. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  75. package/payload/platform/services/claude-session-manager/dist/index.js +53 -0
  76. package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
  77. package/payload/platform/services/claude-session-manager/dist/passive-dispatch-prune.d.ts +37 -0
  78. package/payload/platform/services/claude-session-manager/dist/passive-dispatch-prune.d.ts.map +1 -0
  79. package/payload/platform/services/claude-session-manager/dist/passive-dispatch-prune.js +80 -0
  80. package/payload/platform/services/claude-session-manager/dist/passive-dispatch-prune.js.map +1 -0
  81. package/payload/platform/services/claude-session-manager/dist/passive-intake-audit.d.ts +70 -0
  82. package/payload/platform/services/claude-session-manager/dist/passive-intake-audit.d.ts.map +1 -0
  83. package/payload/platform/services/claude-session-manager/dist/passive-intake-audit.js +129 -0
  84. package/payload/platform/services/claude-session-manager/dist/passive-intake-audit.js.map +1 -0
  85. package/payload/platform/services/claude-session-manager/dist/preference-reconciliation-audit.d.ts +46 -0
  86. package/payload/platform/services/claude-session-manager/dist/preference-reconciliation-audit.d.ts.map +1 -0
  87. package/payload/platform/services/claude-session-manager/dist/preference-reconciliation-audit.js +96 -0
  88. package/payload/platform/services/claude-session-manager/dist/preference-reconciliation-audit.js.map +1 -0
  89. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +39 -0
  90. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
  91. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +66 -5
  92. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
  93. package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.d.ts.map +1 -1
  94. package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.js +5 -1
  95. package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.js.map +1 -1
  96. package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts +6 -0
  97. package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts.map +1 -1
  98. package/payload/platform/services/claude-session-manager/dist/system-prompt.js +20 -0
  99. package/payload/platform/services/claude-session-manager/dist/system-prompt.js.map +1 -1
  100. package/payload/platform/services/telegram-channel/dist/instructions.d.ts +9 -0
  101. package/payload/platform/services/telegram-channel/dist/instructions.d.ts.map +1 -0
  102. package/payload/platform/services/telegram-channel/dist/instructions.js +80 -0
  103. package/payload/platform/services/telegram-channel/dist/instructions.js.map +1 -0
  104. package/payload/platform/services/telegram-channel/dist/notification.d.ts +93 -0
  105. package/payload/platform/services/telegram-channel/dist/notification.d.ts.map +1 -0
  106. package/payload/platform/services/telegram-channel/dist/notification.js +119 -0
  107. package/payload/platform/services/telegram-channel/dist/notification.js.map +1 -0
  108. package/payload/platform/services/telegram-channel/dist/server.d.ts +40 -0
  109. package/payload/platform/services/telegram-channel/dist/server.d.ts.map +1 -0
  110. package/payload/platform/services/telegram-channel/dist/server.js +291 -0
  111. package/payload/platform/services/telegram-channel/dist/server.js.map +1 -0
  112. package/payload/platform/services/telegram-channel/dist/turn-follow.d.ts +28 -0
  113. package/payload/platform/services/telegram-channel/dist/turn-follow.d.ts.map +1 -0
  114. package/payload/platform/services/telegram-channel/dist/turn-follow.js +188 -0
  115. package/payload/platform/services/telegram-channel/dist/turn-follow.js.map +1 -0
  116. package/payload/platform/services/webchat-channel/dist/instructions.d.ts.map +1 -1
  117. package/payload/platform/services/webchat-channel/dist/instructions.js +37 -78
  118. package/payload/platform/services/webchat-channel/dist/instructions.js.map +1 -1
  119. package/payload/platform/services/webchat-channel/dist/notification.d.ts +5 -0
  120. package/payload/platform/services/webchat-channel/dist/notification.d.ts.map +1 -1
  121. package/payload/platform/services/webchat-channel/dist/notification.js +6 -1
  122. package/payload/platform/services/webchat-channel/dist/notification.js.map +1 -1
  123. package/payload/platform/services/whatsapp-channel/dist/notification.d.ts +20 -36
  124. package/payload/platform/services/whatsapp-channel/dist/notification.d.ts.map +1 -1
  125. package/payload/platform/services/whatsapp-channel/dist/notification.js +43 -81
  126. package/payload/platform/services/whatsapp-channel/dist/notification.js.map +1 -1
  127. package/payload/platform/templates/agents/admin/IDENTITY.md +2 -1
  128. package/payload/platform/templates/agents/passive/IDENTITY.md +21 -0
  129. package/payload/platform/templates/specialists/agents/content-producer.md +4 -0
  130. package/payload/server/public/assets/{AdminLoginScreens-CBTx7ZPK.js → AdminLoginScreens-B1P6PrZ3.js} +1 -1
  131. package/payload/server/public/assets/AdminShell-Y1fHQApK.js +2 -0
  132. package/payload/server/public/assets/{Checkbox-BHTxekRB.js → Checkbox-CgCqOslz.js} +1 -1
  133. package/payload/server/public/assets/{Transcript-5kLbBInN.js → Transcript-DZzFjhoq.js} +1 -1
  134. package/payload/server/public/assets/{admin-BWCLE32N.js → admin-BRBjJEci.js} +1 -1
  135. package/payload/server/public/assets/{browser-CRvCPsvV.js → browser-ID7iqIi4.js} +1 -1
  136. package/payload/server/public/assets/{calendar-BI-kd-QP.js → calendar-BEQPMTtd.js} +1 -1
  137. package/payload/server/public/assets/chat-D0GDSWM-.js +1 -0
  138. package/payload/server/public/assets/chevron-left-A0HYKwGY.js +1 -0
  139. package/payload/server/public/assets/data-CV38dClT.js +1 -0
  140. package/payload/server/public/assets/{graph-BFSyW_5i.js → graph-BK0MjKRy.js} +1 -1
  141. package/payload/server/public/assets/{graph-labels-DEjD_qJh.js → graph-labels-BfG6FO25.js} +1 -1
  142. package/payload/server/public/assets/{maximize-2-BcMJNeCB.js → maximize-2-DrBaKD3z.js} +1 -1
  143. package/payload/server/public/assets/{operator-Dtvm9ero.js → operator-CnpNyIJ8.js} +1 -1
  144. package/payload/server/public/assets/page-DC_L-IS1.js +32 -0
  145. package/payload/server/public/assets/{page-TXi9dmaB.js → page-DKHLflh7.js} +1 -1
  146. package/payload/server/public/assets/{public-DBx3U0r8.js → public-ByUJr48E.js} +1 -1
  147. package/payload/server/public/assets/{rotate-ccw-8ti3QkeY.js → rotate-ccw-D45TbeAI.js} +1 -1
  148. package/payload/server/public/assets/useSubAccountSwitcher-CCn22f98.css +1 -0
  149. package/payload/server/public/assets/useSubAccountSwitcher-CprHkBnZ.js +9 -0
  150. package/payload/server/public/browser.html +4 -4
  151. package/payload/server/public/calendar.html +5 -5
  152. package/payload/server/public/chat.html +9 -9
  153. package/payload/server/public/data.html +8 -8
  154. package/payload/server/public/graph.html +9 -9
  155. package/payload/server/public/index.html +11 -11
  156. package/payload/server/public/operator.html +11 -11
  157. package/payload/server/public/public.html +9 -9
  158. package/payload/server/server.js +406 -99
  159. package/payload/server/public/assets/AdminShell-DPHUeXea.js +0 -2
  160. package/payload/server/public/assets/chat-DFy8hb1y.js +0 -1
  161. package/payload/server/public/assets/chevron-left-Bggm5B5U.js +0 -1
  162. package/payload/server/public/assets/data-HvZ69Ya1.js +0 -1
  163. package/payload/server/public/assets/page-C_pm3hl3.js +0 -32
  164. package/payload/server/public/assets/useSubAccountSwitcher-BUvlETHN.css +0 -1
  165. package/payload/server/public/assets/useSubAccountSwitcher-Bc5dOBdI.js +0 -9
@@ -0,0 +1,93 @@
1
+ import { type TelegramChannelRole } from './instructions.js';
2
+ export interface InboundPayload {
3
+ /** The telegram conversation key this payload is for. */
4
+ key: string;
5
+ /** The operator's typed message, verbatim — multi-line content is preserved. */
6
+ text: string;
7
+ /** Per-message id, for dedup/observability. */
8
+ messageId: string;
9
+ /** Task 1378 — the inbound's origin: 'user' (a real telegram message) or
10
+ * 'schedule' (a synthetic inbound injected by the scheduler). Carried only
11
+ * into the turn's structured log line. Absent on legacy frames = 'user'. */
12
+ source?: 'user' | 'schedule';
13
+ /** Task 1486 — the account's active standing-rules block (already formatted as
14
+ * `## Standing rules`), fetched and attached by the gateway. Prepended above
15
+ * `## Context` for ADMIN turns only; a public visitor never receives it.
16
+ * Absent/blank when the account has no active rules. */
17
+ standingRules?: string;
18
+ }
19
+ /** The composed content the agent receives, plus the inner payload's byte
20
+ * length for the observability line. */
21
+ export interface ComposedInbound {
22
+ content: string;
23
+ payloadBytes: number;
24
+ }
25
+ /** Frame an ADMIN inbound as `## Context` (the typed message) then
26
+ * `## Instruction` (the routing + capability-denial directive), so the
27
+ * directive rides every turn. PUBLIC stays plain: the public reader echoes the
28
+ * visitor's own inbound back to them, so wrapping it would leak the internal
29
+ * directive to the visitor. `payloadBytes` is the inner message length,
30
+ * excluding any wrapper — matching the WhatsApp `[reframe]` semantics. */
31
+ export declare function composeContent(p: InboundPayload, role: TelegramChannelRole): ComposedInbound;
32
+ /** Defence in depth: this server serves exactly one key. Even though the
33
+ * gateway only streams that key's messages, refuse anything else before it
34
+ * reaches the session. */
35
+ export declare function acceptsKey(myKey: string, p: {
36
+ key: string;
37
+ }): boolean;
38
+ /** Build the body for POST /tg-channel/reply. The reply tool exposes only
39
+ * `text` to the model; the key is the server's fixed key (from spawn env),
40
+ * never trusted from the model, so a compromised turn cannot redirect a reply
41
+ * to a different conversation. */
42
+ export declare function buildReplyPostBody(myKey: string, text: string): {
43
+ key: string;
44
+ text: string;
45
+ };
46
+ /** Build the body for POST /tg-channel/received — the receipt ack the channel
47
+ * server sends after it has emitted a drained inbound to the agent, so the
48
+ * gateway marks it delivered instead of sweeping it as drain-undelivered. The
49
+ * key is the server's fixed key, never model-supplied. */
50
+ export declare function buildReceivedPostBody(myKey: string, messageId: string): {
51
+ key: string;
52
+ messageId: string;
53
+ };
54
+ /**
55
+ * Build the body for POST /tg-channel/turn-end — the turn-end decision the
56
+ * channel server posts once per turn. `key` + `sessionId` are the server's fixed
57
+ * values (spawn env), never model-supplied. `replied` is whether a telegram
58
+ * reply tool_use occurred in the turn. Delivery is reply-tool-only (Task 1464):
59
+ * no `finalText` travels, so a turn that ended without a reply-tool call delivers
60
+ * nothing — mirroring the WhatsApp twin, the gateway only stamps the lifeline
61
+ * (delivered=reply | delivered=none). */
62
+ export declare function buildTurnEndPostBody(myKey: string, sessionId: string, replied: boolean): {
63
+ key: string;
64
+ sessionId: string;
65
+ replied: boolean;
66
+ };
67
+ /** Retry an idempotent POST until it is accepted or the attempt cap is
68
+ * reached. `attempt` returns whether the POST was accepted; the helper
69
+ * returns on the first acceptance, otherwise sleeps `delayMs` and retries,
70
+ * up to `attempts` total. `sleep` is injected so tests run without real
71
+ * timers. */
72
+ export declare function postWithRetry(attempt: () => Promise<boolean>, opts: {
73
+ attempts: number;
74
+ delayMs: number;
75
+ sleep: (ms: number) => Promise<void>;
76
+ }): Promise<boolean>;
77
+ /** Map a non-OK /tg-channel/reply response into an actionable MCP error string.
78
+ * Falls back to the bare status when the body carries no usable reason. (No
79
+ * terminal/logged-out branch — there is no external transport to log out of.) */
80
+ export declare function buildReplyError(status: number, body: unknown): string;
81
+ export declare const INTERNAL_LEAK_PATTERNS: {
82
+ name: string;
83
+ re: RegExp;
84
+ }[];
85
+ export interface Redaction {
86
+ pattern: string;
87
+ match: string;
88
+ }
89
+ export declare function redactInternalLeaks(text: string): {
90
+ clean: string;
91
+ redactions: Redaction[];
92
+ };
93
+ //# sourceMappingURL=notification.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notification.d.ts","sourceRoot":"","sources":["../src/notification.ts"],"names":[],"mappings":"AAcA,OAAO,EAA6B,KAAK,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AAEvF,MAAM,WAAW,cAAc;IAC7B,yDAAyD;IACzD,GAAG,EAAE,MAAM,CAAA;IACX,gFAAgF;IAChF,IAAI,EAAE,MAAM,CAAA;IACZ,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAA;IACjB;;iFAE6E;IAC7E,MAAM,CAAC,EAAE,MAAM,GAAG,UAAU,CAAA;IAC5B;;;6DAGyD;IACzD,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED;yCACyC;AACzC,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAA;IACf,YAAY,EAAE,MAAM,CAAA;CACrB;AAED;;;;;2EAK2E;AAC3E,wBAAgB,cAAc,CAAC,CAAC,EAAE,cAAc,EAAE,IAAI,EAAE,mBAAmB,GAAG,eAAe,CAgB5F;AAED;;2BAE2B;AAC3B,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAErE;AAED;;;mCAGmC;AACnC,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAE7F;AAED;;;2DAG2D;AAC3D,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,GAChB;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAEpC;AAED;;;;;;;yCAOyC;AACzC,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,OAAO,GACf;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAEtD;AAED;;;;cAIc;AACd,wBAAsB,aAAa,CACjC,OAAO,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,EAC/B,IAAI,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,GAChF,OAAO,CAAC,OAAO,CAAC,CAMlB;AAED;;kFAEkF;AAClF,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,CAIrE;AAYD,eAAO,MAAM,sBAAsB,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,EAOhE,CAAA;AAED,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;CACd;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,SAAS,EAAE,CAAA;CAAE,CAU5F"}
@@ -0,0 +1,119 @@
1
+ // Pure helpers for the telegram channel server. No MCP SDK import here, so these
2
+ // unit-test without the SDK and keep server.ts's transport wiring separable from
3
+ // its message shaping. Mirrors whatsapp-channel/notification.ts with the
4
+ // deliberate divergences below.
5
+ //
6
+ // Every ADMIN inbound is framed with `## Context / ## Instruction`, the same
7
+ // shape the WhatsApp channel uses, so the routing + capability-denial directive
8
+ // rides EVERY turn (the standing UserPromptSubmit directive hook suppresses
9
+ // itself on `<channel source=` turns, so the channel must carry the directive
10
+ // itself). This overrides the plain-delivery decision for the admin role. PUBLIC
11
+ // stays plain — see composeContent. Still no media weaving (the gateway POST
12
+ // carries text only) and no reply-document — the reader renders agent replies
13
+ // from the session JSONL.
14
+ import { ADMIN_REFRAME_INSTRUCTION } from './instructions.js';
15
+ /** Frame an ADMIN inbound as `## Context` (the typed message) then
16
+ * `## Instruction` (the routing + capability-denial directive), so the
17
+ * directive rides every turn. PUBLIC stays plain: the public reader echoes the
18
+ * visitor's own inbound back to them, so wrapping it would leak the internal
19
+ * directive to the visitor. `payloadBytes` is the inner message length,
20
+ * excluding any wrapper — matching the WhatsApp `[reframe]` semantics. */
21
+ export function composeContent(p, role) {
22
+ const payload = p.text;
23
+ const payloadBytes = Buffer.byteLength(payload, 'utf8');
24
+ if (role !== 'admin') {
25
+ return { content: payload, payloadBytes };
26
+ }
27
+ // Task 1486 — prepend the account's active standing rules above ## Context for
28
+ // admin turns only (a public visitor must never see operator rules).
29
+ const rulesPrefix = typeof p.standingRules === 'string' && p.standingRules.trim().length > 0
30
+ ? `${p.standingRules.trim()}\n\n`
31
+ : '';
32
+ return {
33
+ content: `${rulesPrefix}## Context\n${payload}\n\n## Instruction\n${ADMIN_REFRAME_INSTRUCTION}`,
34
+ payloadBytes,
35
+ };
36
+ }
37
+ /** Defence in depth: this server serves exactly one key. Even though the
38
+ * gateway only streams that key's messages, refuse anything else before it
39
+ * reaches the session. */
40
+ export function acceptsKey(myKey, p) {
41
+ return p.key === myKey;
42
+ }
43
+ /** Build the body for POST /tg-channel/reply. The reply tool exposes only
44
+ * `text` to the model; the key is the server's fixed key (from spawn env),
45
+ * never trusted from the model, so a compromised turn cannot redirect a reply
46
+ * to a different conversation. */
47
+ export function buildReplyPostBody(myKey, text) {
48
+ return { key: myKey, text };
49
+ }
50
+ /** Build the body for POST /tg-channel/received — the receipt ack the channel
51
+ * server sends after it has emitted a drained inbound to the agent, so the
52
+ * gateway marks it delivered instead of sweeping it as drain-undelivered. The
53
+ * key is the server's fixed key, never model-supplied. */
54
+ export function buildReceivedPostBody(myKey, messageId) {
55
+ return { key: myKey, messageId };
56
+ }
57
+ /**
58
+ * Build the body for POST /tg-channel/turn-end — the turn-end decision the
59
+ * channel server posts once per turn. `key` + `sessionId` are the server's fixed
60
+ * values (spawn env), never model-supplied. `replied` is whether a telegram
61
+ * reply tool_use occurred in the turn. Delivery is reply-tool-only (Task 1464):
62
+ * no `finalText` travels, so a turn that ended without a reply-tool call delivers
63
+ * nothing — mirroring the WhatsApp twin, the gateway only stamps the lifeline
64
+ * (delivered=reply | delivered=none). */
65
+ export function buildTurnEndPostBody(myKey, sessionId, replied) {
66
+ return { key: myKey, sessionId, replied };
67
+ }
68
+ /** Retry an idempotent POST until it is accepted or the attempt cap is
69
+ * reached. `attempt` returns whether the POST was accepted; the helper
70
+ * returns on the first acceptance, otherwise sleeps `delayMs` and retries,
71
+ * up to `attempts` total. `sleep` is injected so tests run without real
72
+ * timers. */
73
+ export async function postWithRetry(attempt, opts) {
74
+ for (let i = 0; i < opts.attempts; i++) {
75
+ if (await attempt())
76
+ return true;
77
+ if (i < opts.attempts - 1)
78
+ await opts.sleep(opts.delayMs);
79
+ }
80
+ return false;
81
+ }
82
+ /** Map a non-OK /tg-channel/reply response into an actionable MCP error string.
83
+ * Falls back to the bare status when the body carries no usable reason. (No
84
+ * terminal/logged-out branch — there is no external transport to log out of.) */
85
+ export function buildReplyError(status, body) {
86
+ const b = body;
87
+ const reason = typeof b?.error === 'string' && b.error.length > 0 ? b.error : null;
88
+ return reason ? `reply: ${reason}` : `reply: gateway returned ${status}`;
89
+ }
90
+ // Runtime internal-leak guard. The build-time gate check-no-task-id-leaks.mjs
91
+ // scans source strings; it cannot catch a task number, repo path, or log tag the
92
+ // model generates at runtime inside a reply. This is its runtime complement: it
93
+ // redacts those patterns from outbound operator text before the reply leaves the
94
+ // box. Precision — the 2-digit floor on task numbers keeps "Task 3 on your list"
95
+ // and a price "£829" out; the hyphen requirement on bracket tags keeps
96
+ // "[urgent]" out; `.js` is not a source-file match (only `.ts`/`.tsx`), and the
97
+ // built bundle server.js is matched explicitly. repo-path is ordered before
98
+ // source-file so a full path like platform/ui/app/page.tsx redacts as ONE match,
99
+ // not path + filename.
100
+ export const INTERNAL_LEAK_PATTERNS = [
101
+ { name: 'task-id', re: /\bTasks?[\s-]+\d{2,4}\b/g },
102
+ { name: 'repo-path', re: /\bplatform\/[A-Za-z0-9_./-]+/g },
103
+ { name: 'bundle', re: /\bserver\.(?:js|cjs|mjs)\b/g },
104
+ { name: 'source-file', re: /\b[A-Za-z0-9_-]+\.tsx?\b/g },
105
+ { name: 'log-tag', re: /\[[a-z]+(?:-[a-z]+)+\]/g },
106
+ { name: 'op-tag', re: /\bop=[a-z][a-z-]*\b/g },
107
+ ];
108
+ export function redactInternalLeaks(text) {
109
+ const redactions = [];
110
+ let clean = text;
111
+ for (const { name, re } of INTERNAL_LEAK_PATTERNS) {
112
+ clean = clean.replace(re, (m) => {
113
+ redactions.push({ pattern: name, match: m });
114
+ return '[redacted]';
115
+ });
116
+ }
117
+ return { clean, redactions };
118
+ }
119
+ //# sourceMappingURL=notification.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notification.js","sourceRoot":"","sources":["../src/notification.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env node
2
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
+ export declare const INSTRUCTIONS: string;
4
+ export declare const mcp: Server<{
5
+ method: string;
6
+ params?: {
7
+ [x: string]: unknown;
8
+ _meta?: {
9
+ [x: string]: unknown;
10
+ progressToken?: string | number | undefined;
11
+ "io.modelcontextprotocol/related-task"?: {
12
+ taskId: string;
13
+ } | undefined;
14
+ } | undefined;
15
+ } | undefined;
16
+ }, {
17
+ method: string;
18
+ params?: {
19
+ [x: string]: unknown;
20
+ _meta?: {
21
+ [x: string]: unknown;
22
+ progressToken?: string | number | undefined;
23
+ "io.modelcontextprotocol/related-task"?: {
24
+ taskId: string;
25
+ } | undefined;
26
+ } | undefined;
27
+ } | undefined;
28
+ }, {
29
+ [x: string]: unknown;
30
+ _meta?: {
31
+ [x: string]: unknown;
32
+ progressToken?: string | number | undefined;
33
+ "io.modelcontextprotocol/related-task"?: {
34
+ taskId: string;
35
+ } | undefined;
36
+ } | undefined;
37
+ }>;
38
+ export declare function subscribeInbound(): Promise<void>;
39
+ export declare function main(): Promise<void>;
40
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":";AAmBA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAA;AAsDlE,eAAO,MAAM,YAAY,QAA4B,CAAA;AAErD,eAAO,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAWf,CAAA;AA0ED,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAqItD;AAED,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAO1C"}
@@ -0,0 +1,291 @@
1
+ #!/usr/bin/env node
2
+ // Per-conversation telegram channel MCP server. Spawned as a stdio child of one
3
+ // `claude` session (so it dies when that session dies — no bound port, no
4
+ // orphan). It:
5
+ // - subscribes to the gateway for ITS key's inbound (SSE) and emits each
6
+ // message as a notifications/claude/channel event;
7
+ // - exposes a `reply` tool. The tool POSTs the text to the gateway, which
8
+ // relays it to the telegram chat via the Bot API. Delivery is reply-tool-only
9
+ // (Task 1464): a turn that ends without a reply-tool call delivers nothing —
10
+ // the gateway's turn-end handler only stamps the lifeline, it does not send.
11
+ //
12
+ // Transport glue only — message shaping and the post bodies are the
13
+ // unit-tested pure helpers in ./notification. Mirrors webchat-channel/server.ts
14
+ // for the inbound/cold-start path and the WhatsApp twin for the outbound side
15
+ // (reply relay only). One text-only reply tool; telegram document
16
+ // delivery is handled by the gateway's file follower, not a second tool here.
17
+ import { realpathSync, statSync } from 'node:fs';
18
+ import { pathToFileURL } from 'node:url';
19
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
20
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
21
+ import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';
22
+ import { acceptsKey, composeContent, buildReplyPostBody, buildReceivedPostBody, buildTurnEndPostBody, buildReplyError, redactInternalLeaks, postWithRetry, } from './notification.js';
23
+ import { findJsonlForSessionId, followTurnEnds } from './turn-follow.js';
24
+ import { channelInstructions, replyToolDescription, replyTextDescription, resolveChannelRole } from './instructions.js';
25
+ function requireEnv(name) {
26
+ const v = process.env[name];
27
+ if (!v)
28
+ throw new Error(`telegram-channel: missing required env ${name}`);
29
+ return v;
30
+ }
31
+ const KEY = requireEnv('TG_CHANNEL_SENDER_ID');
32
+ const GATEWAY_URL = requireEnv('TG_CHANNEL_GATEWAY_URL').replace(/\/$/, '');
33
+ // Turn-undelivered detection. The session id is carried on the spawn env so the
34
+ // channel server can tail this session's JSONL and reconcile, per turn, whether
35
+ // a reply tool was dispatched. Resolved lazily: a brand-new session has no
36
+ // transcript at startup, so the path is re-resolved on the first inbound.
37
+ // SESSION_ID is empty in unit tests (the module imports without the env), so
38
+ // findJsonlForSessionId is never called and the follower never starts.
39
+ const SESSION_ID = process.env.TG_CHANNEL_SESSION_ID ?? '';
40
+ let jsonlPath = SESSION_ID ? findJsonlForSessionId(SESSION_ID) : null;
41
+ // Count inbound this server emitted but whose turn-end the follower has not yet
42
+ // reported, and whether the session-long follower is running. Mirrors the
43
+ // WhatsApp twin: a turn queued mid-turn is covered by the next end_turn the
44
+ // follower reports, and a claude.ai-driven turn (pendingTurns === 0) reports
45
+ // nothing.
46
+ let pendingTurns = 0;
47
+ let followerStarted = false;
48
+ // Receipt-ack retry budget. 5 attempts × 2000ms = up to ~8s of sleeping,
49
+ // comfortably inside the gateway's 30s ack window (ACK_THRESHOLD_MS), so a
50
+ // recovered ack lands before the sweep would emit a false drain-undelivered.
51
+ const RECEIPT_ACK_ATTEMPTS = 5;
52
+ const RECEIPT_ACK_DELAY_MS = 2000;
53
+ // The channel server serves admin (the operator) OR public (an anonymous
54
+ // visitor); the framing differs so a public agent never treats a visitor message
55
+ // as a trusted operator instruction. The role is carried on the spawn env. The
56
+ // reply tool is the delivery path back to the telegram chat (the reader renders
57
+ // the tool call's text), so every turn must end with it.
58
+ const ROLE = resolveChannelRole(process.env.TG_CHANNEL_ROLE);
59
+ export const INSTRUCTIONS = channelInstructions(ROLE);
60
+ export const mcp = new Server({ name: 'telegram-channel', version: '0.1.0' }, {
61
+ capabilities: {
62
+ experimental: {
63
+ 'claude/channel': {},
64
+ },
65
+ tools: {},
66
+ },
67
+ instructions: INSTRUCTIONS,
68
+ });
69
+ // Readiness gate. The stdio transport "connecting" only means the pipes exist;
70
+ // the harness can only receive notifications after it completes MCP
71
+ // initialization (client sends notifications/initialized). Gate the /ready POST
72
+ // — and therefore the gateway's cold-start drain and every subsequent emission —
73
+ // on this promise. `op=initialized` is the lifeline anchor: its absence after
74
+ // channel-attached means the harness never became ready.
75
+ let markInitialized;
76
+ const initialized = new Promise((resolve) => {
77
+ markInitialized = resolve;
78
+ });
79
+ mcp.oninitialized = () => {
80
+ console.error('[telegram-channel] op=initialized');
81
+ markInitialized();
82
+ };
83
+ mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
84
+ tools: [
85
+ {
86
+ name: 'reply',
87
+ description: replyToolDescription(ROLE),
88
+ inputSchema: {
89
+ type: 'object',
90
+ properties: { text: { type: 'string', description: replyTextDescription(ROLE) } },
91
+ required: ['text'],
92
+ },
93
+ },
94
+ ],
95
+ }));
96
+ // Redact internal-leak patterns from outbound operator text and log each
97
+ // redaction before it leaves the box.
98
+ function guardOutbound(text) {
99
+ const { clean, redactions } = redactInternalLeaks(text);
100
+ for (const r of redactions) {
101
+ console.error(`[channel-guard] op=redact senderId=${KEY} pattern=${r.pattern} bytes=${Buffer.byteLength(r.match, 'utf8')}`);
102
+ }
103
+ return clean;
104
+ }
105
+ mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
106
+ if (req.params.name === 'reply') {
107
+ const rawText = req.params.arguments?.text;
108
+ if (typeof rawText !== 'string')
109
+ throw new Error('reply: text must be a string');
110
+ const clean = guardOutbound(rawText);
111
+ const res = await fetch(`${GATEWAY_URL}/tg-channel/reply`, {
112
+ method: 'POST',
113
+ headers: { 'content-type': 'application/json' },
114
+ body: JSON.stringify(buildReplyPostBody(KEY, clean)),
115
+ });
116
+ if (!res.ok) {
117
+ const body = await res.json().catch(() => null);
118
+ throw new Error(buildReplyError(res.status, body));
119
+ }
120
+ return { content: [{ type: 'text', text: 'sent' }] };
121
+ }
122
+ throw new Error(`unknown tool: ${req.params.name}`);
123
+ });
124
+ // Subscribe to this key's inbound stream and emit each message as an event.
125
+ // Reconnect loop: the channel server must stay alive — and its reply tool
126
+ // available — for the whole claude session. If the SSE link to the gateway
127
+ // drops (gateway restart, idle close, transient blip), reconnect instead of
128
+ // returning. The process still dies with the session because claude SIGTERMs
129
+ // its stdio child.
130
+ async function postReady() {
131
+ await fetch(`${GATEWAY_URL}/tg-channel/ready`, {
132
+ method: 'POST',
133
+ headers: { 'content-type': 'application/json' },
134
+ body: JSON.stringify({ key: KEY }),
135
+ }).catch(() => { });
136
+ }
137
+ export async function subscribeInbound() {
138
+ const url = `${GATEWAY_URL}/tg-channel/inbound?key=${encodeURIComponent(KEY)}`;
139
+ for (;;) {
140
+ try {
141
+ const res = await fetch(url, { headers: { accept: 'text/event-stream' } });
142
+ if (!res.ok || !res.body)
143
+ throw new Error(`subscribe failed: ${res.status}`);
144
+ // The SSE link is established AND the harness has completed MCP
145
+ // initialization — only NOW announce readiness, so the gateway drains
146
+ // the cold-start queue onto a live subscription whose emissions the
147
+ // harness can actually receive. Frames that arrive while we wait
148
+ // buffer in the SSE body; the read loop below starts strictly after
149
+ // this point, so every emission (and its /received ack) is
150
+ // post-initialization. (SSE-link-up alone raced the harness's
151
+ // channel-handler registration and lost the first message.)
152
+ await initialized;
153
+ await postReady();
154
+ const reader = res.body.getReader();
155
+ const decoder = new TextDecoder();
156
+ let buffer = '';
157
+ for (;;) {
158
+ const { value, done } = await reader.read();
159
+ if (done)
160
+ break;
161
+ buffer += decoder.decode(value, { stream: true });
162
+ const frames = buffer.split('\n\n');
163
+ buffer = frames.pop() ?? '';
164
+ for (const frame of frames) {
165
+ const dataLine = frame.split('\n').find((l) => l.startsWith('data:'));
166
+ if (!dataLine)
167
+ continue;
168
+ const data = dataLine.slice('data:'.length).trim();
169
+ if (!data)
170
+ continue; // ping/keepalive
171
+ let payload;
172
+ try {
173
+ payload = JSON.parse(data);
174
+ }
175
+ catch {
176
+ continue;
177
+ }
178
+ if (!acceptsKey(KEY, payload))
179
+ continue;
180
+ // composeContent frames an ADMIN inbound with the routing +
181
+ // capability-denial directive (`## Context / ## Instruction`) so it
182
+ // rides every turn the way the WhatsApp reframe does; PUBLIC stays
183
+ // plain (echo-leak).
184
+ const composed = composeContent(payload, ROLE);
185
+ console.error(`[telegram:inbound] op=delivered key=${KEY.slice(0, 8)} role=${ROLE} source=${payload.source ?? 'user'} payloadBytes=${composed.payloadBytes}`);
186
+ // JSONL byte offset at turn start, captured before the emit so the
187
+ // follower catches every line this turn produces. Resilient: a
188
+ // missing/odd file yields 0 and the follow bails fast.
189
+ let turnStartOffset = 0;
190
+ if (jsonlPath) {
191
+ try {
192
+ turnStartOffset = statSync(jsonlPath).size;
193
+ }
194
+ catch {
195
+ turnStartOffset = 0;
196
+ }
197
+ }
198
+ await mcp.notification({
199
+ method: 'notifications/claude/channel',
200
+ params: { content: composed.content },
201
+ });
202
+ // Confirm receipt so the gateway marks this message delivered
203
+ // instead of timing out as drain-undelivered. Retry within the
204
+ // gateway's ack window; log only on final give-up.
205
+ const acked = await postWithRetry(async () => {
206
+ try {
207
+ const r = await fetch(`${GATEWAY_URL}/tg-channel/received`, {
208
+ method: 'POST',
209
+ headers: { 'content-type': 'application/json' },
210
+ body: JSON.stringify(buildReceivedPostBody(KEY, payload.messageId)),
211
+ });
212
+ return r.ok;
213
+ }
214
+ catch {
215
+ return false;
216
+ }
217
+ }, {
218
+ attempts: RECEIPT_ACK_ATTEMPTS,
219
+ delayMs: RECEIPT_ACK_DELAY_MS,
220
+ sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
221
+ });
222
+ if (!acked) {
223
+ console.error(`[telegram:inbound] op=receipt-ack-failed key=${KEY} messageId=${payload.messageId} attempts=${RECEIPT_ACK_ATTEMPTS}`);
224
+ }
225
+ // Count this inbound and ensure the session-long follower is running.
226
+ // Each end_turn the follower reports posts a turn-end decision (and
227
+ // decrements) while an inbound is outstanding. Delivery is reply-tool-
228
+ // only (Task 1464): the gateway stamps the lifeline and never sends, so
229
+ // only the `replied` decision travels — no answer text leaves this
230
+ // server. Re-resolve the path lazily when it was absent at startup
231
+ // (brand-new session whose JSONL appears on the first turn). The
232
+ // follower is detached so the SSE read loop keeps receiving.
233
+ if (!jsonlPath && SESSION_ID)
234
+ jsonlPath = findJsonlForSessionId(SESSION_ID);
235
+ if (jsonlPath) {
236
+ pendingTurns++;
237
+ if (!followerStarted) {
238
+ followerStarted = true;
239
+ const followPath = jsonlPath;
240
+ const followStart = turnStartOffset;
241
+ void followTurnEnds(followPath, followStart, (outcome) => {
242
+ if (pendingTurns <= 0)
243
+ return;
244
+ pendingTurns--;
245
+ // Task 1464 — delivery is reply-tool-only: the gateway sends
246
+ // nothing at turn-end, so only the `replied` decision travels.
247
+ // `outcome.finalText` is not shipped (no fallback delivery, no
248
+ // outbound guard needed here).
249
+ void fetch(`${GATEWAY_URL}/tg-channel/turn-end`, {
250
+ method: 'POST',
251
+ headers: { 'content-type': 'application/json' },
252
+ body: JSON.stringify(buildTurnEndPostBody(KEY, SESSION_ID, outcome.replied)),
253
+ }).catch(() => { });
254
+ }).finally(() => {
255
+ // The follower settles only on a stat failure (file unreadable —
256
+ // the session is ending). Clear any outstanding count so a
257
+ // replacement follower started by a later inbound begins clean.
258
+ pendingTurns = 0;
259
+ followerStarted = false;
260
+ });
261
+ }
262
+ }
263
+ }
264
+ }
265
+ console.error('[telegram-channel] inbound stream closed by gateway; reconnecting');
266
+ }
267
+ catch (err) {
268
+ console.error(`[telegram-channel] inbound stream error: ${err instanceof Error ? err.message : String(err)}; reconnecting`);
269
+ }
270
+ await new Promise((resolve) => setTimeout(resolve, 2000));
271
+ }
272
+ }
273
+ export async function main() {
274
+ await mcp.connect(new StdioServerTransport());
275
+ // Readiness is announced inside subscribeInbound, once the SSE link is up
276
+ // AND the MCP client has completed initialization — never before, or the
277
+ // gateway would drain into a stream whose emissions the harness cannot
278
+ // receive yet.
279
+ await subscribeInbound();
280
+ }
281
+ // Run only when executed directly (node dist/server.js); importable for tests.
282
+ // realpath the argv side: Node's ESM loader resolves the entry to its real
283
+ // path for import.meta.url, so a symlinked invocation path would otherwise
284
+ // silently skip main().
285
+ if (process.argv[1] && import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href) {
286
+ main().catch((err) => {
287
+ console.error(`[telegram-channel] fatal: ${err instanceof Error ? err.message : String(err)}`);
288
+ process.exit(1);
289
+ });
290
+ }
291
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ export interface TurnOutcome {
2
+ /** A telegram reply tool_use appeared in the turn. */
3
+ replied: boolean;
4
+ /** Text of the end_turn assistant message ('' when none / not yet reached). */
5
+ finalText: string;
6
+ /** The turn reached an assistant end_turn. */
7
+ reachedEndTurn: boolean;
8
+ }
9
+ /** Fold one turn's JSONL line strings into the delivery decision. Pure. */
10
+ export declare function reduceTurnFromLines(lines: string[]): TurnOutcome;
11
+ export type OnTurnEnd = (outcome: TurnOutcome) => void;
12
+ /**
13
+ * Walk `${CLAUDE_CONFIG_DIR}/projects/<slug>/` for `${sessionId}.jsonl` (live
14
+ * and archived). Returns the first match or null. Inlined from
15
+ * whatsapp-channel/jsonl-watch.ts (itself inlined from
16
+ * claude-session-manager/src/jsonl-path.ts) to avoid a cross-package dep.
17
+ */
18
+ export declare function findJsonlForSessionId(sessionId: string, env?: NodeJS.ProcessEnv): string | null;
19
+ /**
20
+ * Tail `jsonlPath` from `startOffset` for the life of the session, invoking
21
+ * `onTurnEnd` once for every assistant end_turn with that turn's outcome. It
22
+ * segments the JSONL at each end_turn so a follow-on turn produced from a
23
+ * mid-turn-queued message is reported too. The process dies with the session,
24
+ * so there is no per-turn timeout; the promise resolves only when the file
25
+ * becomes unreadable. Never rejects. (Mirror of the WhatsApp twin.)
26
+ */
27
+ export declare function followTurnEnds(jsonlPath: string, startOffset: number, onTurnEnd: OnTurnEnd): Promise<void>;
28
+ //# sourceMappingURL=turn-follow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"turn-follow.d.ts","sourceRoot":"","sources":["../src/turn-follow.ts"],"names":[],"mappings":"AAwBA,MAAM,WAAW,WAAW;IAC1B,sDAAsD;IACtD,OAAO,EAAE,OAAO,CAAA;IAChB,+EAA+E;IAC/E,SAAS,EAAE,MAAM,CAAA;IACjB,8CAA8C;IAC9C,cAAc,EAAE,OAAO,CAAA;CACxB;AAUD,2EAA2E;AAC3E,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,WAAW,CAoChE;AAED,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAA;AAkBtD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,MAAM,EACjB,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,GAAG,IAAI,CAmBf;AAED;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAClC,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,SAAS,GACnB,OAAO,CAAC,IAAI,CAAC,CAgEf"}