@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.
- package/dist/__tests__/brand-port-uniqueness.test.js +74 -0
- package/dist/known-brands.js +1 -0
- package/package.json +1 -1
- package/payload/platform/config/brand-registry.json +8 -0
- package/payload/platform/lib/active-rules/dist/index.d.ts +35 -0
- package/payload/platform/lib/active-rules/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/active-rules/dist/index.js +94 -0
- package/payload/platform/lib/active-rules/dist/index.js.map +1 -0
- package/payload/platform/lib/active-rules/package.json +7 -0
- package/payload/platform/lib/active-rules/src/index.test.ts +89 -0
- package/payload/platform/lib/active-rules/src/index.ts +133 -0
- package/payload/platform/lib/active-rules/tsconfig.json +9 -0
- package/payload/platform/package-lock.json +5158 -2922
- package/payload/platform/package.json +4 -3
- package/payload/platform/plugins/admin/hooks/__tests__/prompt-optimiser-compliance.test.sh +22 -0
- package/payload/platform/plugins/admin/hooks/prompt-optimiser-compliance.sh +14 -1
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +16 -2
- package/payload/platform/plugins/docs/references/internals.md +10 -0
- package/payload/platform/plugins/docs/references/memory-guide.md +4 -0
- package/payload/platform/plugins/docs/references/outlook-guide.md +1 -1
- package/payload/platform/plugins/memory/PLUGIN.md +3 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-read-active-rules.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-read-active-rules.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-read-active-rules.test.js +49 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-read-active-rules.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-reconcile.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-reconcile.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-reconcile.test.js +99 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-reconcile.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.d.ts +3 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.js +11 -3
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.d.ts +21 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js +106 -14
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js.map +1 -1
- package/payload/platform/plugins/memory/skills/conversational-memory/SKILL.md +30 -1
- package/payload/platform/plugins/memory/skills/preference-audit/SKILL.md +50 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/calendar-write.test.js +24 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/calendar-write.test.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-create.d.ts +15 -2
- package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-create.d.ts.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-create.js +16 -7
- package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-create.js.map +1 -1
- package/payload/platform/plugins/whatsapp/PLUGIN.md +3 -1
- package/payload/platform/plugins/whatsapp/mcp/dist/__tests__/resolve-read-account.test.d.ts +2 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/__tests__/resolve-read-account.test.d.ts.map +1 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/__tests__/resolve-read-account.test.js +15 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/__tests__/resolve-read-account.test.js.map +1 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/index.js +14 -8
- package/payload/platform/plugins/whatsapp/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/whatsapp/mcp/dist/resolve-read-account.d.ts +7 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/resolve-read-account.d.ts.map +1 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/resolve-read-account.js +13 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/resolve-read-account.js.map +1 -0
- package/payload/platform/plugins/whatsapp/references/channels-whatsapp.md +9 -1
- package/payload/platform/plugins/work/mcp/dist/__tests__/work-create-raised-by-env.test.d.ts +2 -0
- package/payload/platform/plugins/work/mcp/dist/__tests__/work-create-raised-by-env.test.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/__tests__/work-create-raised-by-env.test.js +66 -0
- package/payload/platform/plugins/work/mcp/dist/__tests__/work-create-raised-by-env.test.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-create.d.ts.map +1 -1
- package/payload/platform/plugins/work/mcp/dist/tools/work-create.js +8 -1
- package/payload/platform/plugins/work/mcp/dist/tools/work-create.js.map +1 -1
- package/payload/platform/scripts/__tests__/provision-role-stamp.test.sh +3 -0
- package/payload/platform/scripts/lib/provision-account-dir.sh +9 -1
- package/payload/platform/services/claude-session-manager/dist/agent-identity-locator.d.ts +1 -1
- package/payload/platform/services/claude-session-manager/dist/agent-identity-locator.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/agent-identity-locator.js +5 -1
- package/payload/platform/services/claude-session-manager/dist/agent-identity-locator.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts +1 -0
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js +65 -4
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/index.js +53 -0
- package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/passive-dispatch-prune.d.ts +37 -0
- package/payload/platform/services/claude-session-manager/dist/passive-dispatch-prune.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/passive-dispatch-prune.js +80 -0
- package/payload/platform/services/claude-session-manager/dist/passive-dispatch-prune.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/passive-intake-audit.d.ts +70 -0
- package/payload/platform/services/claude-session-manager/dist/passive-intake-audit.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/passive-intake-audit.js +129 -0
- package/payload/platform/services/claude-session-manager/dist/passive-intake-audit.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/preference-reconciliation-audit.d.ts +46 -0
- package/payload/platform/services/claude-session-manager/dist/preference-reconciliation-audit.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/preference-reconciliation-audit.js +96 -0
- package/payload/platform/services/claude-session-manager/dist/preference-reconciliation-audit.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +39 -0
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +66 -5
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.js +5 -1
- package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts +6 -0
- package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/system-prompt.js +20 -0
- package/payload/platform/services/claude-session-manager/dist/system-prompt.js.map +1 -1
- package/payload/platform/services/telegram-channel/dist/instructions.d.ts +9 -0
- package/payload/platform/services/telegram-channel/dist/instructions.d.ts.map +1 -0
- package/payload/platform/services/telegram-channel/dist/instructions.js +80 -0
- package/payload/platform/services/telegram-channel/dist/instructions.js.map +1 -0
- package/payload/platform/services/telegram-channel/dist/notification.d.ts +93 -0
- package/payload/platform/services/telegram-channel/dist/notification.d.ts.map +1 -0
- package/payload/platform/services/telegram-channel/dist/notification.js +119 -0
- package/payload/platform/services/telegram-channel/dist/notification.js.map +1 -0
- package/payload/platform/services/telegram-channel/dist/server.d.ts +40 -0
- package/payload/platform/services/telegram-channel/dist/server.d.ts.map +1 -0
- package/payload/platform/services/telegram-channel/dist/server.js +291 -0
- package/payload/platform/services/telegram-channel/dist/server.js.map +1 -0
- package/payload/platform/services/telegram-channel/dist/turn-follow.d.ts +28 -0
- package/payload/platform/services/telegram-channel/dist/turn-follow.d.ts.map +1 -0
- package/payload/platform/services/telegram-channel/dist/turn-follow.js +188 -0
- package/payload/platform/services/telegram-channel/dist/turn-follow.js.map +1 -0
- package/payload/platform/services/webchat-channel/dist/instructions.d.ts.map +1 -1
- package/payload/platform/services/webchat-channel/dist/instructions.js +37 -78
- package/payload/platform/services/webchat-channel/dist/instructions.js.map +1 -1
- package/payload/platform/services/webchat-channel/dist/notification.d.ts +5 -0
- package/payload/platform/services/webchat-channel/dist/notification.d.ts.map +1 -1
- package/payload/platform/services/webchat-channel/dist/notification.js +6 -1
- package/payload/platform/services/webchat-channel/dist/notification.js.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/notification.d.ts +20 -36
- package/payload/platform/services/whatsapp-channel/dist/notification.d.ts.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/notification.js +43 -81
- package/payload/platform/services/whatsapp-channel/dist/notification.js.map +1 -1
- package/payload/platform/templates/agents/admin/IDENTITY.md +2 -1
- package/payload/platform/templates/agents/passive/IDENTITY.md +21 -0
- package/payload/platform/templates/specialists/agents/content-producer.md +4 -0
- package/payload/server/public/assets/{AdminLoginScreens-CBTx7ZPK.js → AdminLoginScreens-B1P6PrZ3.js} +1 -1
- package/payload/server/public/assets/AdminShell-Y1fHQApK.js +2 -0
- package/payload/server/public/assets/{Checkbox-BHTxekRB.js → Checkbox-CgCqOslz.js} +1 -1
- package/payload/server/public/assets/{Transcript-5kLbBInN.js → Transcript-DZzFjhoq.js} +1 -1
- package/payload/server/public/assets/{admin-BWCLE32N.js → admin-BRBjJEci.js} +1 -1
- package/payload/server/public/assets/{browser-CRvCPsvV.js → browser-ID7iqIi4.js} +1 -1
- package/payload/server/public/assets/{calendar-BI-kd-QP.js → calendar-BEQPMTtd.js} +1 -1
- package/payload/server/public/assets/chat-D0GDSWM-.js +1 -0
- package/payload/server/public/assets/chevron-left-A0HYKwGY.js +1 -0
- package/payload/server/public/assets/data-CV38dClT.js +1 -0
- package/payload/server/public/assets/{graph-BFSyW_5i.js → graph-BK0MjKRy.js} +1 -1
- package/payload/server/public/assets/{graph-labels-DEjD_qJh.js → graph-labels-BfG6FO25.js} +1 -1
- package/payload/server/public/assets/{maximize-2-BcMJNeCB.js → maximize-2-DrBaKD3z.js} +1 -1
- package/payload/server/public/assets/{operator-Dtvm9ero.js → operator-CnpNyIJ8.js} +1 -1
- package/payload/server/public/assets/page-DC_L-IS1.js +32 -0
- package/payload/server/public/assets/{page-TXi9dmaB.js → page-DKHLflh7.js} +1 -1
- package/payload/server/public/assets/{public-DBx3U0r8.js → public-ByUJr48E.js} +1 -1
- package/payload/server/public/assets/{rotate-ccw-8ti3QkeY.js → rotate-ccw-D45TbeAI.js} +1 -1
- package/payload/server/public/assets/useSubAccountSwitcher-CCn22f98.css +1 -0
- package/payload/server/public/assets/useSubAccountSwitcher-CprHkBnZ.js +9 -0
- package/payload/server/public/browser.html +4 -4
- package/payload/server/public/calendar.html +5 -5
- package/payload/server/public/chat.html +9 -9
- package/payload/server/public/data.html +8 -8
- package/payload/server/public/graph.html +9 -9
- package/payload/server/public/index.html +11 -11
- package/payload/server/public/operator.html +11 -11
- package/payload/server/public/public.html +9 -9
- package/payload/server/server.js +406 -99
- package/payload/server/public/assets/AdminShell-DPHUeXea.js +0 -2
- package/payload/server/public/assets/chat-DFy8hb1y.js +0 -1
- package/payload/server/public/assets/chevron-left-Bggm5B5U.js +0 -1
- package/payload/server/public/assets/data-HvZ69Ya1.js +0 -1
- package/payload/server/public/assets/page-C_pm3hl3.js +0 -32
- package/payload/server/public/assets/useSubAccountSwitcher-BUvlETHN.css +0 -1
- 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"}
|