@rubytech/create-maxy-code 0.1.591 → 0.1.593

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 (126) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/lib/routine-templates/dist/index.d.ts +2 -2
  3. package/payload/platform/lib/routine-templates/dist/index.d.ts.map +1 -1
  4. package/payload/platform/lib/routine-templates/dist/index.js +2 -9
  5. package/payload/platform/lib/routine-templates/dist/index.js.map +1 -1
  6. package/payload/platform/lib/routine-templates/dist/roster.d.ts +1 -11
  7. package/payload/platform/lib/routine-templates/dist/roster.d.ts.map +1 -1
  8. package/payload/platform/lib/routine-templates/dist/roster.js +11 -15
  9. package/payload/platform/lib/routine-templates/dist/roster.js.map +1 -1
  10. package/payload/platform/lib/routine-templates/src/__tests__/roster.test.ts +0 -0
  11. package/payload/platform/lib/routine-templates/src/__tests__/seed.test.ts +8 -3
  12. package/payload/platform/lib/routine-templates/src/index.ts +1 -8
  13. package/payload/platform/lib/routine-templates/src/roster.ts +10 -14
  14. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +7 -3
  15. package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +10 -0
  16. package/payload/platform/plugins/docs/references/internals.md +4 -2
  17. package/payload/platform/plugins/docs/references/telegram-guide.md +2 -0
  18. package/payload/platform/plugins/scheduling/PLUGIN.md +6 -6
  19. package/payload/platform/plugins/scheduling/mcp/dist/index.js +4 -12
  20. package/payload/platform/plugins/scheduling/mcp/dist/index.js.map +1 -1
  21. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate-audits.test.js +85 -1
  22. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate-audits.test.js.map +1 -1
  23. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate.test.js +55 -1
  24. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate.test.js.map +1 -1
  25. package/payload/platform/plugins/scheduling/mcp/dist/lib/gate.d.ts +10 -1
  26. package/payload/platform/plugins/scheduling/mcp/dist/lib/gate.d.ts.map +1 -1
  27. package/payload/platform/plugins/scheduling/mcp/dist/lib/gate.js +42 -6
  28. package/payload/platform/plugins/scheduling/mcp/dist/lib/gate.js.map +1 -1
  29. package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.d.ts +22 -0
  30. package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.d.ts.map +1 -1
  31. package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.js +73 -0
  32. package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.js.map +1 -1
  33. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/agent-turn-dispatch.test.js +93 -1
  34. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/agent-turn-dispatch.test.js.map +1 -1
  35. package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.d.ts +2 -1
  36. package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.d.ts.map +1 -1
  37. package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.js +45 -1
  38. package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.js.map +1 -1
  39. package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js +19 -2
  40. package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js.map +1 -1
  41. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-gate-write.test.js +27 -280
  42. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-gate-write.test.js.map +1 -1
  43. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/scheduling-gate.test.js +315 -208
  44. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/scheduling-gate.test.js.map +1 -1
  45. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.d.ts +1 -31
  46. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.d.ts.map +1 -1
  47. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.js +15 -95
  48. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.js.map +1 -1
  49. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.d.ts.map +1 -1
  50. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.js +5 -36
  51. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.js.map +1 -1
  52. package/payload/platform/plugins/scheduling/mcp/dist/tools/scheduling-gate.d.ts +83 -51
  53. package/payload/platform/plugins/scheduling/mcp/dist/tools/scheduling-gate.d.ts.map +1 -1
  54. package/payload/platform/plugins/scheduling/mcp/dist/tools/scheduling-gate.js +152 -105
  55. package/payload/platform/plugins/scheduling/mcp/dist/tools/scheduling-gate.js.map +1 -1
  56. package/payload/platform/plugins/telegram/PLUGIN.md +12 -5
  57. package/payload/platform/plugins/telegram/mcp/dist/__tests__/ask-tool.test.js +31 -0
  58. package/payload/platform/plugins/telegram/mcp/dist/__tests__/ask-tool.test.js.map +1 -1
  59. package/payload/platform/plugins/telegram/mcp/dist/__tests__/card-tool.test.js +30 -0
  60. package/payload/platform/plugins/telegram/mcp/dist/__tests__/card-tool.test.js.map +1 -1
  61. package/payload/platform/plugins/telegram/mcp/dist/__tests__/media-fetch-tool.test.js +24 -0
  62. package/payload/platform/plugins/telegram/mcp/dist/__tests__/media-fetch-tool.test.js.map +1 -1
  63. package/payload/platform/plugins/telegram/mcp/dist/__tests__/media-send-tool.test.js +31 -0
  64. package/payload/platform/plugins/telegram/mcp/dist/__tests__/media-send-tool.test.js.map +1 -1
  65. package/payload/platform/plugins/telegram/mcp/dist/__tests__/public-pin-registry.test.d.ts +2 -0
  66. package/payload/platform/plugins/telegram/mcp/dist/__tests__/public-pin-registry.test.d.ts.map +1 -0
  67. package/payload/platform/plugins/telegram/mcp/dist/__tests__/public-pin-registry.test.js +55 -0
  68. package/payload/platform/plugins/telegram/mcp/dist/__tests__/public-pin-registry.test.js.map +1 -0
  69. package/payload/platform/plugins/telegram/mcp/dist/__tests__/public-pin.test.d.ts +2 -0
  70. package/payload/platform/plugins/telegram/mcp/dist/__tests__/public-pin.test.d.ts.map +1 -0
  71. package/payload/platform/plugins/telegram/mcp/dist/__tests__/public-pin.test.js +89 -0
  72. package/payload/platform/plugins/telegram/mcp/dist/__tests__/public-pin.test.js.map +1 -0
  73. package/payload/platform/plugins/telegram/mcp/dist/index.js +13 -42
  74. package/payload/platform/plugins/telegram/mcp/dist/index.js.map +1 -1
  75. package/payload/platform/plugins/telegram/mcp/dist/lib/public-pin.d.ts +80 -0
  76. package/payload/platform/plugins/telegram/mcp/dist/lib/public-pin.d.ts.map +1 -0
  77. package/payload/platform/plugins/telegram/mcp/dist/lib/public-pin.js +96 -0
  78. package/payload/platform/plugins/telegram/mcp/dist/lib/public-pin.js.map +1 -0
  79. package/payload/platform/plugins/telegram/mcp/dist/tools/ask.d.ts +5 -0
  80. package/payload/platform/plugins/telegram/mcp/dist/tools/ask.d.ts.map +1 -1
  81. package/payload/platform/plugins/telegram/mcp/dist/tools/ask.js.map +1 -1
  82. package/payload/platform/plugins/telegram/mcp/dist/tools/card.d.ts +5 -0
  83. package/payload/platform/plugins/telegram/mcp/dist/tools/card.d.ts.map +1 -1
  84. package/payload/platform/plugins/telegram/mcp/dist/tools/card.js.map +1 -1
  85. package/payload/platform/plugins/telegram/mcp/dist/tools/media-fetch.d.ts +5 -0
  86. package/payload/platform/plugins/telegram/mcp/dist/tools/media-fetch.d.ts.map +1 -1
  87. package/payload/platform/plugins/telegram/mcp/dist/tools/media-fetch.js.map +1 -1
  88. package/payload/platform/plugins/telegram/mcp/dist/tools/media-send.d.ts +5 -0
  89. package/payload/platform/plugins/telegram/mcp/dist/tools/media-send.d.ts.map +1 -1
  90. package/payload/platform/plugins/telegram/mcp/dist/tools/media-send.js.map +1 -1
  91. package/payload/platform/plugins/telegram/mcp/dist/tools/thread.d.ts +5 -0
  92. package/payload/platform/plugins/telegram/mcp/dist/tools/thread.d.ts.map +1 -1
  93. package/payload/platform/plugins/telegram/mcp/dist/tools/thread.js.map +1 -1
  94. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts +37 -0
  95. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  96. package/payload/platform/services/claude-session-manager/dist/http-server.js +178 -13
  97. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  98. package/payload/platform/services/claude-session-manager/dist/rc-public-lockdown.d.ts +13 -5
  99. package/payload/platform/services/claude-session-manager/dist/rc-public-lockdown.d.ts.map +1 -1
  100. package/payload/platform/services/claude-session-manager/dist/rc-public-lockdown.js +23 -9
  101. package/payload/platform/services/claude-session-manager/dist/rc-public-lockdown.js.map +1 -1
  102. package/payload/platform/services/claude-session-manager/dist/telegram-channel-mcp.d.ts +7 -4
  103. package/payload/platform/services/claude-session-manager/dist/telegram-channel-mcp.d.ts.map +1 -1
  104. package/payload/platform/services/claude-session-manager/dist/telegram-channel-mcp.js +5 -3
  105. package/payload/platform/services/claude-session-manager/dist/telegram-channel-mcp.js.map +1 -1
  106. package/payload/platform/services/claude-session-manager/dist/telegram-channel-store.d.ts +15 -1
  107. package/payload/platform/services/claude-session-manager/dist/telegram-channel-store.d.ts.map +1 -1
  108. package/payload/platform/services/claude-session-manager/dist/telegram-channel-store.js +16 -7
  109. package/payload/platform/services/claude-session-manager/dist/telegram-channel-store.js.map +1 -1
  110. package/payload/platform/services/claude-session-manager/dist/telegram-plugin-mcp.d.ts +28 -0
  111. package/payload/platform/services/claude-session-manager/dist/telegram-plugin-mcp.d.ts.map +1 -0
  112. package/payload/platform/services/claude-session-manager/dist/telegram-plugin-mcp.js +53 -0
  113. package/payload/platform/services/claude-session-manager/dist/telegram-plugin-mcp.js.map +1 -0
  114. package/payload/platform/services/claude-session-manager/dist/wa-channel-mcp.d.ts +9 -5
  115. package/payload/platform/services/claude-session-manager/dist/wa-channel-mcp.d.ts.map +1 -1
  116. package/payload/platform/services/claude-session-manager/dist/wa-channel-mcp.js +12 -6
  117. package/payload/platform/services/claude-session-manager/dist/wa-channel-mcp.js.map +1 -1
  118. package/payload/platform/services/claude-session-manager/dist/webchat-channel-mcp.d.ts +7 -3
  119. package/payload/platform/services/claude-session-manager/dist/webchat-channel-mcp.d.ts.map +1 -1
  120. package/payload/platform/services/claude-session-manager/dist/webchat-channel-mcp.js +5 -1
  121. package/payload/platform/services/claude-session-manager/dist/webchat-channel-mcp.js.map +1 -1
  122. package/payload/platform/services/telegram-channel/dist/instructions.d.ts +14 -2
  123. package/payload/platform/services/telegram-channel/dist/instructions.d.ts.map +1 -1
  124. package/payload/platform/services/telegram-channel/dist/instructions.js +34 -3
  125. package/payload/platform/services/telegram-channel/dist/instructions.js.map +1 -1
  126. package/payload/server/server.js +74 -9
@@ -23,6 +23,43 @@ import { TelegramChannelStore } from './telegram-channel-store.js';
23
23
  * named agent's IDENTITY/SOUL/KNOWLEDGE via composeAppendSystemPrompt, the same
24
24
  * composer /public-spawn uses. An admin spawn is unchanged and still composes
25
25
  * only reseat / voice / specialist-domains / authenticated-identity fragments. */
26
+ /** Task 2666 — the framing a channel server may be given, or null when the
27
+ * spawn does not carry one.
28
+ *
29
+ * A channel server has exactly two framings, and `admin` is the
30
+ * higher-privilege one: its instruction block tells the agent the message "is
31
+ * a message the operator has typed to you directly" and to act on it as their
32
+ * instruction. Every site that reached it by exclusion — anything not
33
+ * `'public'` becomes admin — turned each way of getting the role wrong into a
34
+ * privilege elevation that emitted nothing, because a spawn that silently
35
+ * became admin is byte-identical to a correct admin spawn.
36
+ *
37
+ * So this answers the question and never invents an answer. `passive` is not a
38
+ * channel framing and null means the role was never resolved; both return null
39
+ * and the caller refuses the spawn. */
40
+ export declare function channelFramingRole(role: AgentRole | null): 'admin' | 'public' | null;
41
+ /** Task 2666 — the same value at the point of use, where null is unreachable
42
+ * because the caller refused the spawn already.
43
+ *
44
+ * A throw rather than a `!` assertion or a default. The compiler cannot narrow
45
+ * through the `channelBound && framing === null` conjunction that guards the
46
+ * attach blocks, and the two ways of silencing it are both worse than a crash:
47
+ * `!` asserts an invariant it cannot check, and a default reintroduces exactly
48
+ * the admin-by-exclusion this task removed. If the guard is ever conditioned
49
+ * away, this is loud on the first spawn instead of quiet for ever. */
50
+ export declare function requireChannelFraming(role: 'admin' | 'public' | null): 'admin' | 'public';
51
+ /** Task 2666 — the telegram plugin's own public tools, out of the install-wide
52
+ * public surface `loadToolSurface` built from every PLUGIN.md at boot.
53
+ *
54
+ * Filtered to this one plugin rather than passed whole: a pinned public
55
+ * telegram spawn registers only the telegram servers, so a public tool
56
+ * declared on any other plugin would name a server that is not there — the
57
+ * granted-but-uncallable failure this task's `op=mcp-registered` line exists
58
+ * to make visible. Filtering states that boundary instead of relying on no
59
+ * other plugin ever declaring one.
60
+ *
61
+ * Sorted so the emitted argv is stable and the audit test can compare. */
62
+ export declare function telegramPublicToolsFrom(publicSurface: ReadonlySet<string>): string[];
26
63
  export declare function parseRcSpawnRole(body: {
27
64
  role?: unknown;
28
65
  }): Extract<AgentRole, 'admin' | 'public'> | null;
@@ -1 +1 @@
1
- {"version":3,"file":"http-server.d.ts","sourceRoot":"","sources":["../src/http-server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"http-server.d.ts","sourceRoot":"","sources":["../src/http-server.ts"],"names":[],"mappings":"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"}
@@ -27,6 +27,7 @@ import { requirePlatformPort } from './channel-spawn-tee.js';
27
27
  import { parseWaChannelBody, sessionIdFieldsFor } from './public-spawn-session.js';
28
28
  import { buildWebchatChannelMcpServers, buildWebchatChannelArgv, webchatChannelMcpConfigPath, WEBCHAT_CHANNEL_SERVER_NAME } from './webchat-channel-mcp.js';
29
29
  import { buildTelegramChannelMcpServers, buildTelegramChannelArgv, telegramChannelMcpConfigPath, TELEGRAM_CHANNEL_SERVER_NAME } from './telegram-channel-mcp.js';
30
+ import { buildTelegramPluginMcpServers, TELEGRAM_PLUGIN_SERVER_NAME } from './telegram-plugin-mcp.js';
30
31
  import { buildChannelMcpServers, buildChannelArgv, channelMcpConfigPath, CHANNEL_SERVER_NAME } from './channel-mcp.js';
31
32
  import { buildRcPublicLockdownArgv, formatToolAllowlistLine, PUBLIC_ALLOWED_TOOLS_ENV, allowedToolsFromArgv, formatRoleResolutionLine, } from './rc-public-lockdown.js';
32
33
  import { resolvePublicSpawnModel } from './public-spawn-model.js';
@@ -78,6 +79,53 @@ const ROLES = ['admin', 'public'];
78
79
  * named agent's IDENTITY/SOUL/KNOWLEDGE via composeAppendSystemPrompt, the same
79
80
  * composer /public-spawn uses. An admin spawn is unchanged and still composes
80
81
  * only reseat / voice / specialist-domains / authenticated-identity fragments. */
82
+ /** Task 2666 — the framing a channel server may be given, or null when the
83
+ * spawn does not carry one.
84
+ *
85
+ * A channel server has exactly two framings, and `admin` is the
86
+ * higher-privilege one: its instruction block tells the agent the message "is
87
+ * a message the operator has typed to you directly" and to act on it as their
88
+ * instruction. Every site that reached it by exclusion — anything not
89
+ * `'public'` becomes admin — turned each way of getting the role wrong into a
90
+ * privilege elevation that emitted nothing, because a spawn that silently
91
+ * became admin is byte-identical to a correct admin spawn.
92
+ *
93
+ * So this answers the question and never invents an answer. `passive` is not a
94
+ * channel framing and null means the role was never resolved; both return null
95
+ * and the caller refuses the spawn. */
96
+ export function channelFramingRole(role) {
97
+ return role === 'admin' || role === 'public' ? role : null;
98
+ }
99
+ /** Task 2666 — the same value at the point of use, where null is unreachable
100
+ * because the caller refused the spawn already.
101
+ *
102
+ * A throw rather than a `!` assertion or a default. The compiler cannot narrow
103
+ * through the `channelBound && framing === null` conjunction that guards the
104
+ * attach blocks, and the two ways of silencing it are both worse than a crash:
105
+ * `!` asserts an invariant it cannot check, and a default reintroduces exactly
106
+ * the admin-by-exclusion this task removed. If the guard is ever conditioned
107
+ * away, this is loud on the first spawn instead of quiet for ever. */
108
+ export function requireChannelFraming(role) {
109
+ if (role === null) {
110
+ throw new Error('rc-spawn: channel framing unresolved at attach — the guard above must refuse first');
111
+ }
112
+ return role;
113
+ }
114
+ /** Task 2666 — the telegram plugin's own public tools, out of the install-wide
115
+ * public surface `loadToolSurface` built from every PLUGIN.md at boot.
116
+ *
117
+ * Filtered to this one plugin rather than passed whole: a pinned public
118
+ * telegram spawn registers only the telegram servers, so a public tool
119
+ * declared on any other plugin would name a server that is not there — the
120
+ * granted-but-uncallable failure this task's `op=mcp-registered` line exists
121
+ * to make visible. Filtering states that boundary instead of relying on no
122
+ * other plugin ever declaring one.
123
+ *
124
+ * Sorted so the emitted argv is stable and the audit test can compare. */
125
+ export function telegramPublicToolsFrom(publicSurface) {
126
+ const prefix = `mcp__${TELEGRAM_PLUGIN_SERVER_NAME}__`;
127
+ return [...publicSurface].filter((n) => n.startsWith(prefix)).sort();
128
+ }
81
129
  export function parseRcSpawnRole(body) {
82
130
  return body.role === 'admin' || body.role === 'public' ? body.role : null;
83
131
  }
@@ -2859,7 +2907,23 @@ export function buildHttpApp(deps) {
2859
2907
  typeof telegramChannelRaw.senderId === 'string' &&
2860
2908
  typeof telegramChannelRaw.gatewayUrl === 'string' &&
2861
2909
  typeof telegramChannelRaw.serverPath === 'string'
2862
- ? telegramChannelRaw
2910
+ ? (() => {
2911
+ const o = telegramChannelRaw;
2912
+ // Task 2666 — the pin is admitted only as a matched PAIR of
2913
+ // non-empty strings, and is OPTIONAL so the admin and specialist
2914
+ // telegram spawns that come through this same door still validate.
2915
+ // A half pin would confine one axis and leave the other free,
2916
+ // which reads as protection while granting exactly what the pin
2917
+ // exists to prevent.
2918
+ const pinned = typeof o.botId === 'string' && o.botId.length > 0 &&
2919
+ typeof o.chatId === 'string' && o.chatId.length > 0;
2920
+ return {
2921
+ senderId: o.senderId,
2922
+ gatewayUrl: o.gatewayUrl,
2923
+ serverPath: o.serverPath,
2924
+ ...(pinned ? { botId: o.botId, chatId: o.chatId } : {}),
2925
+ };
2926
+ })()
2863
2927
  : undefined;
2864
2928
  // Task-962 — recover an already-live but unresponsive webchat session. When
2865
2929
  // set, the already-live branch classifies the child busy vs wedged and
@@ -3308,8 +3372,17 @@ export function buildHttpApp(deps) {
3308
3372
  else {
3309
3373
  const stored = deps.telegramChannelStore?.get(sessionId);
3310
3374
  if (stored) {
3311
- effectiveTelegramChannel = { senderId: stored.senderId, gatewayUrl: stored.gatewayUrl, serverPath: stored.serverPath };
3312
- deps.logger(`[rc-spawn] op=telegram-channel-rebind sessionId=${sessionId.slice(0, 8)} senderId=${stored.senderId} source=store`);
3375
+ // Task 2666 the pin comes back with the binding, so the second and
3376
+ // every later message of a public conversation keeps the rich
3377
+ // surface. Spread as a pair: the store admits both or neither.
3378
+ effectiveTelegramChannel = {
3379
+ senderId: stored.senderId,
3380
+ gatewayUrl: stored.gatewayUrl,
3381
+ serverPath: stored.serverPath,
3382
+ ...(stored.botId && stored.chatId ? { botId: stored.botId, chatId: stored.chatId } : {}),
3383
+ };
3384
+ deps.logger(`[rc-spawn] op=telegram-channel-rebind sessionId=${sessionId.slice(0, 8)} senderId=${stored.senderId} ` +
3385
+ `pinned=${stored.botId && stored.chatId ? 'yes' : 'no'} source=store`);
3313
3386
  }
3314
3387
  }
3315
3388
  }
@@ -3395,9 +3468,35 @@ export function buildHttpApp(deps) {
3395
3468
  targetSet = mergeTarget(targetSet, { kind: 'whatsapp', ...effectiveWaChannel, accountId: effectiveAccountId });
3396
3469
  if (effectiveWebchatChannel)
3397
3470
  targetSet = mergeTarget(targetSet, { kind: 'webchat', ...effectiveWebchatChannel });
3471
+ // Task 2666 — resolved ONCE for all three channel builders, and refused
3472
+ // rather than defaulted. Before this, each builder took an optional role
3473
+ // and filled an absent one in as 'admin' at the stamp, so a resume that
3474
+ // carried no role framed a stranger's conversation as the operator's and
3475
+ // logged nothing. Two of the three channel servers already threw on an
3476
+ // absent value; the builders' own defaults meant they never saw one, so
3477
+ // that strictness was unreachable.
3478
+ //
3479
+ // The cost is stated rather than hidden, as Task 2267 stated its own. That
3480
+ // task chose 200-plus-lockdown for a channel-bound resume whose role will
3481
+ // not resolve, which fails closed on TOOLS. It could not fail closed on
3482
+ // framing, because the framing default was admin. Refusing covers both, and
3483
+ // an admin whose sidecar is damaged now gets a named error instead of a
3484
+ // session that silently comes back holding one tool and never repairs
3485
+ // itself.
3486
+ const channelFraming = channelFramingRole(effectiveRcRole);
3487
+ if (channelBound && channelFraming === null) {
3488
+ deps.logger(`[rc-spawn] op=channel-role-unresolved sessionId=${sessionId?.slice(0, 8) ?? 'none'} ` +
3489
+ `role=${effectiveRcRole ?? 'none'} outcome=${priorSidecarRead?.outcome ?? 'missing'}`);
3490
+ return c.json({ error: 'spawn-failed', reason: 'channel-role-unresolved' }, 503);
3491
+ }
3398
3492
  let waPath = null;
3399
3493
  let webchatPath = null;
3400
3494
  let telegramPath = null;
3495
+ // Task 2666 — the telegram tools this spawn is granted. Non-empty only when
3496
+ // a pinned public telegram channel attached AND its config write succeeded,
3497
+ // so a failed write cannot leave the allowlist naming a server that was
3498
+ // never registered.
3499
+ let telegramPublicExtraTools = [];
3401
3500
  // Task 2260 — the channel server this spawn actually registered, set inside
3402
3501
  // each branch below that succeeded in writing its --mcp-config (a write
3403
3502
  // failure leaves it null). The public lockdown reads it to name the one
@@ -3439,11 +3538,15 @@ export function buildHttpApp(deps) {
3439
3538
  nodeBin: deps.nodeBin,
3440
3539
  // Task 2290 — the account the gateway keys this session's hub entry on.
3441
3540
  accountId: effectiveAccountId,
3442
- // Task 2458 — the body role, same value and same bridging as the
3443
- // telegram sibling below. The gateway branches this session's turn-end
3444
- // delivery on it, so a public visitor's answer goes back down the
3445
- // posting key instead of into the admin identity fan-out.
3446
- role: rcRole ?? undefined,
3541
+ // Task 2458 — the gateway branches this session's turn-end delivery
3542
+ // on it, so a public visitor's answer goes back down the posting key
3543
+ // instead of into the admin identity fan-out.
3544
+ //
3545
+ // Task 2666 — the RESOLVED framing, not the body role. The body role
3546
+ // is null on a bare resume, which used to fall through the builder's
3547
+ // default to admin. Refused above when it will not resolve, so this
3548
+ // is non-null here.
3549
+ role: requireChannelFraming(channelFraming),
3447
3550
  // Task 2356 — the spawn-tee inputs, so this server's stderr lands in
3448
3551
  // logs/mcp-whatsapp-channel-<sessionId>.log.
3449
3552
  platformRoot: deps.platformRoot,
@@ -3470,6 +3573,10 @@ export function buildHttpApp(deps) {
3470
3573
  gatewayUrl: effectiveWebchatChannel.gatewayUrl,
3471
3574
  serverPath: effectiveWebchatChannel.serverPath,
3472
3575
  nodeBin: deps.nodeBin,
3576
+ // Task 2666 — this call site named NO role at all, so every webchat
3577
+ // spawn through this door took the builder's 'admin' default,
3578
+ // including a public one. The resolved framing is passed now.
3579
+ role: requireChannelFraming(channelFraming),
3473
3580
  // Task 2356 — the spawn-tee inputs, so this server's stderr lands in
3474
3581
  // logs/mcp-webchat-channel-<sessionId>.log.
3475
3582
  platformRoot: deps.platformRoot,
@@ -3500,25 +3607,83 @@ export function buildHttpApp(deps) {
3500
3607
  gatewayUrl: effectiveTelegramChannel.gatewayUrl,
3501
3608
  serverPath: effectiveTelegramChannel.serverPath,
3502
3609
  nodeBin: deps.nodeBin,
3503
- // Task 2325 — the body role, unchanged in meaning; `?? undefined`
3504
- // only bridges null to the optional parameter this builder declares.
3505
- role: rcRole ?? undefined,
3610
+ // Task 2325 — `?? undefined` only bridges null to the optional
3611
+ // parameter this builder declares.
3612
+ //
3613
+ // Task 2666 — `effectiveRcRole`, not the body role. A resume omits
3614
+ // `role` (the wedged-session recovery and the re-seat send both post
3615
+ // `{ sessionId }` alone), so the body value resolved null and this
3616
+ // server booted with ADMIN framing on a public conversation: the
3617
+ // admin block tells the agent the message "is a message the operator
3618
+ // has typed to you directly" and to act on it as their instruction,
3619
+ // which handed a stranger's message operator authority. The lockdown
3620
+ // below and this task's pin gate already read `effectiveRcRole`, so
3621
+ // the framing was also the one axis that disagreed with the tool
3622
+ // grant on the same spawn. Resolved once above, and refused there
3623
+ // when it will not resolve, rather than mapped to a default here.
3624
+ role: requireChannelFraming(channelFraming),
3506
3625
  // Task 2356 — the spawn-tee inputs, so this server's stderr lands in
3507
3626
  // logs/mcp-telegram-channel-<sessionId>.log.
3508
3627
  platformRoot: deps.platformRoot,
3509
3628
  logDir: deps.logDir,
3510
3629
  platformPort: requirePlatformPort(),
3511
3630
  });
3631
+ // Task 2666 — the pinned rich surface. Gated on the PAIR: without both
3632
+ // a bot and a chat there is nothing to pin the tools to, and an
3633
+ // unpinned grant would let a visitor's turn name any chat. Gated on
3634
+ // role:'public' too, because an admin telegram spawn already holds this
3635
+ // same plugin under the long prefix from the installed-plugin channel;
3636
+ // registering it again here would give one session two live copies of
3637
+ // the same MCP server.
3638
+ const telegramPin = effectiveRcRole === 'public' && effectiveTelegramChannel.botId && effectiveTelegramChannel.chatId
3639
+ ? { botId: effectiveTelegramChannel.botId, chatId: effectiveTelegramChannel.chatId }
3640
+ : null;
3641
+ const telegramPluginServers = telegramPin
3642
+ ? buildTelegramPluginMcpServers({
3643
+ sessionId,
3644
+ botId: telegramPin.botId,
3645
+ chatId: telegramPin.chatId,
3646
+ accountId: effectiveAccountId,
3647
+ nodeBin: deps.nodeBin,
3648
+ platformRoot: deps.platformRoot,
3649
+ logDir: deps.logDir,
3650
+ platformPort: requirePlatformPort(),
3651
+ })
3652
+ : {};
3653
+ if (!telegramPin && effectiveRcRole === 'public') {
3654
+ // A public telegram spawn that reached here with no pin. Emitted
3655
+ // rather than left silent: the surface it gets is the pre-2666 single
3656
+ // entry, and without this line that is indistinguishable from a spawn
3657
+ // this feature was never meant to widen.
3658
+ deps.logger(`[telegram-public] op=pin-absent sessionId=${sessionId.slice(0, 8)} ` +
3659
+ `botId=${effectiveTelegramChannel.botId ?? 'none'} chatId=${effectiveTelegramChannel.chatId ?? 'none'}`);
3660
+ }
3512
3661
  telegramPath = telegramChannelMcpConfigPath(sessionId, tmpdir());
3513
3662
  try {
3514
- writeFileSync(telegramPath, JSON.stringify({ mcpServers: telegramServers }, null, 2), 'utf8');
3663
+ // Both servers in ONE config file, so the existing single
3664
+ // `--mcp-config` argv carries them. The dev-channels flag still names
3665
+ // `telegram-channel` alone: the plugin server is an ordinary MCP
3666
+ // server, not a development channel.
3667
+ writeFileSync(telegramPath, JSON.stringify({ mcpServers: { ...telegramServers, ...telegramPluginServers } }, null, 2), 'utf8');
3515
3668
  argv.push(...buildTelegramChannelArgv(telegramPath));
3516
3669
  channelServerName = TELEGRAM_CHANNEL_SERVER_NAME;
3670
+ if (telegramPin) {
3671
+ telegramPublicExtraTools = telegramPublicToolsFrom(deps.toolSurface.public);
3672
+ // The config write, with the count actually in the file. `count=N`
3673
+ // on the tool-allowlist line with no line here is the
3674
+ // argv-names-an-unregistered-server signature, and it is visible at
3675
+ // spawn with no visitor turn.
3676
+ deps.logger(`[telegram-public] op=mcp-registered sessionId=${sessionId.slice(0, 8)} ` +
3677
+ `server=${TELEGRAM_PLUGIN_SERVER_NAME} path=${telegramPath} tools=${telegramPublicExtraTools.length}`);
3678
+ }
3517
3679
  deps.logger(`[rc-spawn] op=telegram-channel senderId=${effectiveTelegramChannel.senderId} gateway=${effectiveTelegramChannel.gatewayUrl} path=${telegramPath}`);
3518
3680
  deps.logger(`[rc-spawn] op=channel-mcp-spawn channel=telegram node=${deps.nodeBin} exists=${existsSync(deps.nodeBin)}`);
3519
3681
  }
3520
3682
  catch (err) {
3521
3683
  telegramPath = null;
3684
+ // Reset with it: granting tools whose server was never registered is
3685
+ // the exact failure the op=mcp-registered line exists to catch.
3686
+ telegramPublicExtraTools = [];
3522
3687
  deps.logger(`[rc-spawn] op=telegram-channel-write-failed err=${JSON.stringify(err instanceof Error ? err.message : String(err))}`);
3523
3688
  }
3524
3689
  }
@@ -3549,7 +3714,7 @@ export function buildHttpApp(deps) {
3549
3714
  // itself gated on a non-null role and so never repairs the file. The
3550
3715
  // `op=role-resolution` line above is what makes that state greppable.
3551
3716
  const publicLockdown = effectiveRcRole === 'public' || (effectiveRcRole === null && channelBound)
3552
- ? buildRcPublicLockdownArgv(channelServerName)
3717
+ ? buildRcPublicLockdownArgv(channelServerName, telegramPublicExtraTools)
3553
3718
  : null;
3554
3719
  if (publicLockdown) {
3555
3720
  // A re-seat fork pushes its own caller-picked --permission-mode above,