@rubytech/create-maxy-code 0.1.566 → 0.1.568

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 (176) hide show
  1. package/dist/__tests__/brand-agent-root.test.js +91 -0
  2. package/dist/brand-agent-root.js +58 -0
  3. package/dist/index.js +25 -28
  4. package/package.json +1 -1
  5. package/payload/platform/neo4j/schema.cypher +9 -0
  6. package/payload/platform/package-lock.json +66 -1
  7. package/payload/platform/plugins/.claude-plugin/marketplace.json +5 -0
  8. package/payload/platform/plugins/admin/hooks/lib/maxy-mcp-plugins.txt +1 -0
  9. package/payload/platform/plugins/admin/mcp/dist/index.js +7 -1
  10. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  11. package/payload/platform/plugins/admin/mcp/dist/lib/roster-census.d.ts +10 -1
  12. package/payload/platform/plugins/admin/mcp/dist/lib/roster-census.d.ts.map +1 -1
  13. package/payload/platform/plugins/admin/mcp/dist/lib/roster-census.js +6 -1
  14. package/payload/platform/plugins/admin/mcp/dist/lib/roster-census.js.map +1 -1
  15. package/payload/platform/plugins/admin/mcp/dist/tools/capabilities-here.d.ts +9 -0
  16. package/payload/platform/plugins/admin/mcp/dist/tools/capabilities-here.d.ts.map +1 -1
  17. package/payload/platform/plugins/admin/mcp/dist/tools/capabilities-here.js +1 -1
  18. package/payload/platform/plugins/admin/mcp/dist/tools/capabilities-here.js.map +1 -1
  19. package/payload/platform/plugins/admin/skills/capabilities-here/SKILL.md +1 -1
  20. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +91 -1
  21. package/payload/platform/plugins/admin/skills/public-agent-manager/SKILL.md +5 -1
  22. package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +13 -0
  23. package/payload/platform/plugins/docs/references/channel-wake-and-prompt.md +90 -0
  24. package/payload/platform/plugins/inventory/.claude-plugin/plugin.json +21 -0
  25. package/payload/platform/plugins/inventory/PLUGIN.md +75 -0
  26. package/payload/platform/plugins/inventory/lib/mcp-spawn-tee/index.js +204 -0
  27. package/payload/platform/plugins/inventory/lib/mcp-spawn-tee/package.json +3 -0
  28. package/payload/platform/plugins/inventory/mcp/dist/index.d.ts +2 -0
  29. package/payload/platform/plugins/inventory/mcp/dist/index.d.ts.map +1 -0
  30. package/payload/platform/plugins/inventory/mcp/dist/index.js +241 -0
  31. package/payload/platform/plugins/inventory/mcp/dist/index.js.map +1 -0
  32. package/payload/platform/plugins/inventory/mcp/dist/lib/forecast.d.ts +20 -0
  33. package/payload/platform/plugins/inventory/mcp/dist/lib/forecast.d.ts.map +1 -0
  34. package/payload/platform/plugins/inventory/mcp/dist/lib/forecast.js +29 -0
  35. package/payload/platform/plugins/inventory/mcp/dist/lib/forecast.js.map +1 -0
  36. package/payload/platform/plugins/inventory/mcp/dist/lib/neo4j.d.ts +5 -0
  37. package/payload/platform/plugins/inventory/mcp/dist/lib/neo4j.d.ts.map +1 -0
  38. package/payload/platform/plugins/inventory/mcp/dist/lib/neo4j.js +40 -0
  39. package/payload/platform/plugins/inventory/mcp/dist/lib/neo4j.js.map +1 -0
  40. package/payload/platform/plugins/inventory/mcp/dist/lib/read.d.ts +28 -0
  41. package/payload/platform/plugins/inventory/mcp/dist/lib/read.d.ts.map +1 -0
  42. package/payload/platform/plugins/inventory/mcp/dist/lib/read.js +91 -0
  43. package/payload/platform/plugins/inventory/mcp/dist/lib/read.js.map +1 -0
  44. package/payload/platform/plugins/inventory/mcp/dist/lib/replenish.d.ts +56 -0
  45. package/payload/platform/plugins/inventory/mcp/dist/lib/replenish.d.ts.map +1 -0
  46. package/payload/platform/plugins/inventory/mcp/dist/lib/replenish.js +107 -0
  47. package/payload/platform/plugins/inventory/mcp/dist/lib/replenish.js.map +1 -0
  48. package/payload/platform/plugins/inventory/mcp/dist/lib/types.d.ts +98 -0
  49. package/payload/platform/plugins/inventory/mcp/dist/lib/types.d.ts.map +1 -0
  50. package/payload/platform/plugins/inventory/mcp/dist/lib/types.js +7 -0
  51. package/payload/platform/plugins/inventory/mcp/dist/lib/types.js.map +1 -0
  52. package/payload/platform/plugins/inventory/mcp/dist/lib/write-rejects.d.ts +12 -0
  53. package/payload/platform/plugins/inventory/mcp/dist/lib/write-rejects.d.ts.map +1 -0
  54. package/payload/platform/plugins/inventory/mcp/dist/lib/write-rejects.js +49 -0
  55. package/payload/platform/plugins/inventory/mcp/dist/lib/write-rejects.js.map +1 -0
  56. package/payload/platform/plugins/inventory/mcp/dist/tools/demand-record.d.ts +20 -0
  57. package/payload/platform/plugins/inventory/mcp/dist/tools/demand-record.d.ts.map +1 -0
  58. package/payload/platform/plugins/inventory/mcp/dist/tools/demand-record.js +53 -0
  59. package/payload/platform/plugins/inventory/mcp/dist/tools/demand-record.js.map +1 -0
  60. package/payload/platform/plugins/inventory/mcp/dist/tools/forecast.d.ts +23 -0
  61. package/payload/platform/plugins/inventory/mcp/dist/tools/forecast.d.ts.map +1 -0
  62. package/payload/platform/plugins/inventory/mcp/dist/tools/forecast.js +18 -0
  63. package/payload/platform/plugins/inventory/mcp/dist/tools/forecast.js.map +1 -0
  64. package/payload/platform/plugins/inventory/mcp/dist/tools/policy-set.d.ts +25 -0
  65. package/payload/platform/plugins/inventory/mcp/dist/tools/policy-set.d.ts.map +1 -0
  66. package/payload/platform/plugins/inventory/mcp/dist/tools/policy-set.js +37 -0
  67. package/payload/platform/plugins/inventory/mcp/dist/tools/policy-set.js.map +1 -0
  68. package/payload/platform/plugins/inventory/mcp/dist/tools/purchase-order-record.d.ts +25 -0
  69. package/payload/platform/plugins/inventory/mcp/dist/tools/purchase-order-record.d.ts.map +1 -0
  70. package/payload/platform/plugins/inventory/mcp/dist/tools/purchase-order-record.js +43 -0
  71. package/payload/platform/plugins/inventory/mcp/dist/tools/purchase-order-record.js.map +1 -0
  72. package/payload/platform/plugins/inventory/mcp/dist/tools/replenish-plan.d.ts +29 -0
  73. package/payload/platform/plugins/inventory/mcp/dist/tools/replenish-plan.d.ts.map +1 -0
  74. package/payload/platform/plugins/inventory/mcp/dist/tools/replenish-plan.js +95 -0
  75. package/payload/platform/plugins/inventory/mcp/dist/tools/replenish-plan.js.map +1 -0
  76. package/payload/platform/plugins/inventory/mcp/dist/tools/stock-record.d.ts +21 -0
  77. package/payload/platform/plugins/inventory/mcp/dist/tools/stock-record.d.ts.map +1 -0
  78. package/payload/platform/plugins/inventory/mcp/dist/tools/stock-record.js +46 -0
  79. package/payload/platform/plugins/inventory/mcp/dist/tools/stock-record.js.map +1 -0
  80. package/payload/platform/plugins/inventory/mcp/dist/tools/supplier-terms-set.d.ts +24 -0
  81. package/payload/platform/plugins/inventory/mcp/dist/tools/supplier-terms-set.d.ts.map +1 -0
  82. package/payload/platform/plugins/inventory/mcp/dist/tools/supplier-terms-set.js +29 -0
  83. package/payload/platform/plugins/inventory/mcp/dist/tools/supplier-terms-set.js.map +1 -0
  84. package/payload/platform/plugins/inventory/mcp/package.json +21 -0
  85. package/payload/platform/plugins/memory/references/schema-retail.md +20 -0
  86. package/payload/platform/scripts/lib/provision-account-dir.sh +0 -2
  87. package/payload/platform/services/claude-session-manager/dist/account-agent-registry.d.ts +19 -0
  88. package/payload/platform/services/claude-session-manager/dist/account-agent-registry.d.ts.map +1 -0
  89. package/payload/platform/services/claude-session-manager/dist/account-agent-registry.js +146 -0
  90. package/payload/platform/services/claude-session-manager/dist/account-agent-registry.js.map +1 -0
  91. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
  92. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +8 -0
  93. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
  94. package/payload/platform/services/claude-session-manager/dist/fs-watcher.d.ts +5 -0
  95. package/payload/platform/services/claude-session-manager/dist/fs-watcher.d.ts.map +1 -1
  96. package/payload/platform/services/claude-session-manager/dist/fs-watcher.js +5 -5
  97. package/payload/platform/services/claude-session-manager/dist/fs-watcher.js.map +1 -1
  98. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts +11 -1
  99. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  100. package/payload/platform/services/claude-session-manager/dist/http-server.js +124 -17
  101. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  102. package/payload/platform/services/claude-session-manager/dist/index.js +25 -13
  103. package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
  104. package/payload/platform/services/claude-session-manager/dist/pty-census.d.ts +6 -1
  105. package/payload/platform/services/claude-session-manager/dist/pty-census.d.ts.map +1 -1
  106. package/payload/platform/services/claude-session-manager/dist/pty-census.js +6 -3
  107. package/payload/platform/services/claude-session-manager/dist/pty-census.js.map +1 -1
  108. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +21 -8
  109. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
  110. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +38 -15
  111. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
  112. package/payload/platform/services/claude-session-manager/dist/public-spawn-model.d.ts +28 -0
  113. package/payload/platform/services/claude-session-manager/dist/public-spawn-model.d.ts.map +1 -0
  114. package/payload/platform/services/claude-session-manager/dist/public-spawn-model.js +58 -0
  115. package/payload/platform/services/claude-session-manager/dist/public-spawn-model.js.map +1 -0
  116. package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts +2 -0
  117. package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts.map +1 -1
  118. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js +4 -1
  119. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -1
  120. package/payload/platform/services/claude-session-manager/dist/reaper.d.ts +3 -0
  121. package/payload/platform/services/claude-session-manager/dist/reaper.d.ts.map +1 -1
  122. package/payload/platform/services/claude-session-manager/dist/reaper.js +2 -1
  123. package/payload/platform/services/claude-session-manager/dist/reaper.js.map +1 -1
  124. package/payload/platform/services/claude-session-manager/dist/session-cap-audit.d.ts +2 -0
  125. package/payload/platform/services/claude-session-manager/dist/session-cap-audit.d.ts.map +1 -1
  126. package/payload/platform/services/claude-session-manager/dist/session-cap-audit.js +5 -2
  127. package/payload/platform/services/claude-session-manager/dist/session-cap-audit.js.map +1 -1
  128. package/payload/platform/services/claude-session-manager/dist/slice-memory-policy.d.ts +2 -0
  129. package/payload/platform/services/claude-session-manager/dist/slice-memory-policy.d.ts.map +1 -1
  130. package/payload/platform/services/claude-session-manager/dist/slice-memory-policy.js +5 -3
  131. package/payload/platform/services/claude-session-manager/dist/slice-memory-policy.js.map +1 -1
  132. package/payload/platform/services/claude-session-manager/dist/systemd-scope.d.ts +26 -16
  133. package/payload/platform/services/claude-session-manager/dist/systemd-scope.d.ts.map +1 -1
  134. package/payload/platform/services/claude-session-manager/dist/systemd-scope.js +84 -31
  135. package/payload/platform/services/claude-session-manager/dist/systemd-scope.js.map +1 -1
  136. package/payload/platform/services/claude-session-manager/dist/wa-channel-mcp.d.ts +7 -0
  137. package/payload/platform/services/claude-session-manager/dist/wa-channel-mcp.d.ts.map +1 -1
  138. package/payload/platform/services/claude-session-manager/dist/wa-channel-mcp.js +6 -0
  139. package/payload/platform/services/claude-session-manager/dist/wa-channel-mcp.js.map +1 -1
  140. package/payload/platform/services/telegram-channel/dist/notification.d.ts +10 -2
  141. package/payload/platform/services/telegram-channel/dist/notification.d.ts.map +1 -1
  142. package/payload/platform/services/telegram-channel/dist/notification.js +15 -3
  143. package/payload/platform/services/telegram-channel/dist/notification.js.map +1 -1
  144. package/payload/platform/services/telegram-channel/dist/server.d.ts +5 -0
  145. package/payload/platform/services/telegram-channel/dist/server.d.ts.map +1 -1
  146. package/payload/platform/services/telegram-channel/dist/server.js +57 -5
  147. package/payload/platform/services/telegram-channel/dist/server.js.map +1 -1
  148. package/payload/platform/services/telegram-channel/dist/turn-follow.d.ts +44 -2
  149. package/payload/platform/services/telegram-channel/dist/turn-follow.d.ts.map +1 -1
  150. package/payload/platform/services/telegram-channel/dist/turn-follow.js +188 -15
  151. package/payload/platform/services/telegram-channel/dist/turn-follow.js.map +1 -1
  152. package/payload/platform/services/webchat-channel/dist/server.d.ts +5 -0
  153. package/payload/platform/services/webchat-channel/dist/server.d.ts.map +1 -1
  154. package/payload/platform/services/webchat-channel/dist/server.js +53 -4
  155. package/payload/platform/services/webchat-channel/dist/server.js.map +1 -1
  156. package/payload/platform/services/webchat-channel/dist/turn-follow.d.ts +44 -2
  157. package/payload/platform/services/webchat-channel/dist/turn-follow.d.ts.map +1 -1
  158. package/payload/platform/services/webchat-channel/dist/turn-follow.js +188 -15
  159. package/payload/platform/services/webchat-channel/dist/turn-follow.js.map +1 -1
  160. package/payload/platform/services/whatsapp-channel/dist/instructions.d.ts +9 -0
  161. package/payload/platform/services/whatsapp-channel/dist/instructions.d.ts.map +1 -0
  162. package/payload/platform/services/whatsapp-channel/dist/instructions.js +22 -0
  163. package/payload/platform/services/whatsapp-channel/dist/instructions.js.map +1 -0
  164. package/payload/platform/services/whatsapp-channel/dist/notification.d.ts +3 -1
  165. package/payload/platform/services/whatsapp-channel/dist/notification.d.ts.map +1 -1
  166. package/payload/platform/services/whatsapp-channel/dist/notification.js +10 -2
  167. package/payload/platform/services/whatsapp-channel/dist/notification.js.map +1 -1
  168. package/payload/platform/services/whatsapp-channel/dist/server.d.ts +10 -2
  169. package/payload/platform/services/whatsapp-channel/dist/server.d.ts.map +1 -1
  170. package/payload/platform/services/whatsapp-channel/dist/server.js +71 -8
  171. package/payload/platform/services/whatsapp-channel/dist/server.js.map +1 -1
  172. package/payload/platform/services/whatsapp-channel/dist/turn-follow.d.ts +34 -2
  173. package/payload/platform/services/whatsapp-channel/dist/turn-follow.d.ts.map +1 -1
  174. package/payload/platform/services/whatsapp-channel/dist/turn-follow.js +153 -15
  175. package/payload/platform/services/whatsapp-channel/dist/turn-follow.js.map +1 -1
  176. package/payload/server/server.js +254 -96
@@ -29,10 +29,12 @@ import { buildWebchatChannelMcpServers, buildWebchatChannelArgv, webchatChannelM
29
29
  import { buildTelegramChannelMcpServers, buildTelegramChannelArgv, telegramChannelMcpConfigPath, TELEGRAM_CHANNEL_SERVER_NAME } from './telegram-channel-mcp.js';
30
30
  import { buildChannelMcpServers, buildChannelArgv, channelMcpConfigPath, CHANNEL_SERVER_NAME } from './channel-mcp.js';
31
31
  import { buildRcPublicLockdownArgv, formatToolAllowlistLine, PUBLIC_ALLOWED_TOOLS_ENV, allowedToolsFromArgv, formatRoleResolutionLine, } from './rc-public-lockdown.js';
32
+ import { resolvePublicSpawnModel } from './public-spawn-model.js';
32
33
  import { mergeTarget, isMultiTarget, targetKinds } from './channel-targets.js';
33
34
  import { randomUUID, createHash } from 'node:crypto';
34
35
  import { execFile, execFileSync } from 'node:child_process';
35
36
  import { buildRcChildEnv, reapplyAdminLevers } from './rc-daemon.js';
37
+ import { syncAccountAgentRegistry } from './account-agent-registry.js';
36
38
  import { resolveSpawnUserId, formatMcpUserBindingLine } from './mcp-user-binding.js';
37
39
  import { runConnectorAuthLogin } from './connector-auth.js';
38
40
  import { isEffortLevel, isNewSessionEffortLevel, isPermissionMode, SONNET_MODEL } from '../../../lib/models/dist/index.js';
@@ -41,7 +43,7 @@ import { parseWorkServerRegistration, formatSpawnToolsLine } from './passive-spa
41
43
  import { parsePrintSurface } from './passive-print-surface.js';
42
44
  import { writeScopeRecord, removeScopeRecord, readAllScopeRecords } from './scope-record.js';
43
45
  import { writeReseatMarker, resolveReseatChain, clearReseatMarker } from './reseat-ledger.js';
44
- import { getScopeMainPid, defaultProcessSignals, defaultSystemctlRunner } from './systemd-scope.js';
46
+ import { getScopeMainPid, scopeUnitName, defaultProcessSignals, defaultSystemctlRunner } from './systemd-scope.js';
45
47
  import { readSidecar, updateSidecar, appendBridgeId, writeSidecar } from './session-sidecar.js';
46
48
  import { houseAdminScopeFor, isHouseAccount, formatHouseScopeInjectLine } from './config.js';
47
49
  import { resolvePassiveAssignee, readHouseAdminUserIds } from './passive-admin-assignment.js';
@@ -280,10 +282,17 @@ function tailHasDanglingToolUse(jsonlPath) {
280
282
  * record so the synthetic record matches claude's own shape; uuid and
281
283
  * timestamp are minted. Idempotent: returns false when nothing dangles or on
282
284
  * any error. */
283
- export function appendTurnAbortTerminator(jsonlPath, sessionId) {
285
+ export function appendTurnAbortTerminator(jsonlPath, sessionId,
286
+ /** Task 2457 — supplied only when the child was terminated by a signal rather
287
+ * than stopped by the operator. With no dangling tool_use there is nothing to
288
+ * repair for a later --resume, but the transcript may still hold the CLI's
289
+ * `[Request interrupted by user for tool use]` line, which is false in that
290
+ * case: the CLI writes it for ANY mid-tool teardown, and it resolves the
291
+ * dangle, so this function used to return false and leave the lie standing. */
292
+ termination) {
284
293
  try {
285
294
  const ids = danglingToolUseIds(jsonlPath);
286
- if (ids.length === 0)
295
+ if (ids.length === 0 && !termination)
287
296
  return false;
288
297
  const raw = readFileSync(jsonlPath, 'utf8').trimEnd().split('\n');
289
298
  let envelope = {};
@@ -307,12 +316,22 @@ export function appendTurnAbortTerminator(jsonlPath, sessionId) {
307
316
  timestamp: new Date().toISOString(),
308
317
  message: {
309
318
  role: 'user',
310
- content: ids.map((id) => ({
311
- tool_use_id: id,
312
- type: 'tool_result',
313
- content: 'Turn aborted: the agent process exited before this tool finished.',
314
- is_error: true,
315
- })),
319
+ // Task 2457 — the termination text names no culprit. This manager
320
+ // observes only that its own child took a signal; the process that sent
321
+ // it belongs to another brand and writes to another log.
322
+ content: ids.length > 0
323
+ ? ids.map((id) => ({
324
+ tool_use_id: id,
325
+ type: 'tool_result',
326
+ content: 'Turn aborted: the agent process exited before this tool finished.',
327
+ is_error: true,
328
+ }))
329
+ : [{
330
+ type: 'text',
331
+ text: 'Turn ended by process termination, not by the operator. The agent process exited '
332
+ + `with code ${termination.exitCode ?? 'unknown'} while this turn was streaming `
333
+ + `(scope ${termination.unit}).`,
334
+ }],
316
335
  },
317
336
  };
318
337
  appendFileSync(jsonlPath, JSON.stringify(record) + '\n');
@@ -571,6 +590,7 @@ function attachEndTurnAutoClose(deps, sessionId, label) {
571
590
  closed = true;
572
591
  deps.logger(`auto-archive sessionId=${sessionId} ${label} reason=end-turn archivedAt=${new Date().toISOString()}`);
573
592
  void stopSession({
593
+ brandSlug: deps.brandSlug,
574
594
  killGraceMs: deps.killGraceMs,
575
595
  logger: deps.logger,
576
596
  runSystemctl: deps.runSystemctl,
@@ -2053,6 +2073,7 @@ export function buildHttpApp(deps) {
2053
2073
  recycleSession: async (sid) => {
2054
2074
  const row = resolveRow(deps.watcher, sid);
2055
2075
  await stopSession({
2076
+ brandSlug: deps.brandSlug,
2056
2077
  killGraceMs: deps.killGraceMs,
2057
2078
  logger: deps.logger,
2058
2079
  runSystemctl: deps.runSystemctl,
@@ -2107,6 +2128,7 @@ export function buildHttpApp(deps) {
2107
2128
  deps.logger(`[stop-session] resolved sessionId=${sessionId.slice(0, 8)} pid=${liveBeforeStop ?? 'none'} prior-exited-count=${priorExitedCount}`);
2108
2129
  }
2109
2130
  const outcome = await stopSession({
2131
+ brandSlug: deps.brandSlug,
2110
2132
  killGraceMs: deps.killGraceMs,
2111
2133
  logger: deps.logger,
2112
2134
  runSystemctl: deps.runSystemctl,
@@ -2633,7 +2655,7 @@ export function buildHttpApp(deps) {
2633
2655
  for (const row of deps.watcher.all()) {
2634
2656
  if (row.status !== 'detached' || seen.has(row.sessionId) || !row.scopeUnitToken)
2635
2657
  continue;
2636
- const mainPid = getScopeMainPid(runSystemctl, row.scopeUnitToken);
2658
+ const mainPid = getScopeMainPid(runSystemctl, deps.brandSlug, row.scopeUnitToken);
2637
2659
  if (mainPid !== null && defaultProcessSignals.isAlive(mainPid))
2638
2660
  seen.set(row.sessionId, mainPid);
2639
2661
  }
@@ -2950,6 +2972,7 @@ export function buildHttpApp(deps) {
2950
2972
  // idempotent branch returns without recoverOutcome).
2951
2973
  deps.logger(`[rc-spawn] op=recover-respawn sessionId=${sessionId.slice(0, 8)} reason=wedged`);
2952
2974
  await stopSession({
2975
+ brandSlug: deps.brandSlug,
2953
2976
  killGraceMs: deps.killGraceMs,
2954
2977
  logger: deps.logger,
2955
2978
  runSystemctl: deps.runSystemctl,
@@ -2976,6 +2999,7 @@ export function buildHttpApp(deps) {
2976
2999
  if (detached && detached.status === 'detached' && detached.scopeUnitToken) {
2977
3000
  deps.logger(`[rc-spawn] op=reconcile-stop-survivor sessionId=${sessionId.slice(0, 8)} unitToken=${detached.scopeUnitToken}`);
2978
3001
  await stopSession({
3002
+ brandSlug: deps.brandSlug,
2979
3003
  killGraceMs: deps.killGraceMs,
2980
3004
  logger: deps.logger,
2981
3005
  runSystemctl: deps.runSystemctl,
@@ -2993,9 +3017,47 @@ export function buildHttpApp(deps) {
2993
3017
  // Task 969 — the new-session picker's model/effort apply only to a genuinely
2994
3018
  // fresh ADMIN spawn (the /chat popover posts role:'admin'). Gating on the
2995
3019
  // admin role as well as freshness keeps any other fresh spawn (LinkedIn
2996
- // ingest, public, specialist, channel cold-start) on the settings defaults
2997
- // even if a future caller were to put model/effort on its body.
3020
+ // ingest, specialist, channel cold-start) on the settings defaults even if
3021
+ // a future caller were to put model/effort on its body.
3022
+ // Task 2452 — `public` left that list. A public spawn no longer takes the
3023
+ // settings default model; it resolves one from its own agent below. Its
3024
+ // EFFORT is still the settings default, so this gate is unchanged.
2998
3025
  const freshAdminLevers = mode === 'fresh' && rcRole === 'admin';
3026
+ // Task 2452 — the model a public spawn runs under. Commit 725731df9 routed
3027
+ // public Telegram to this door, which pushes --model only for the two admin
3028
+ // shapes below, so a visitor's session silently took settings.json — pinned
3029
+ // to Opus for the ADMIN session by the rc-daemon.
3030
+ //
3031
+ // Gated on the BODY role, not `effectiveRcRole`: that resolves ~180 lines
3032
+ // below, after the argv push this feeds. Both callers that reach this door
3033
+ // with role:'public' — Telegram (telegram-ensure-session.ts) and a slugless
3034
+ // WhatsApp public inbound (server/index.ts, the no-enabled-agent
3035
+ // fall-through) — put `role` in the body on every spawn, fresh and resume
3036
+ // alike, so the body role covers every public spawn that arrives here. A
3037
+ // channel-bound resume carrying no role at all is not covered; no public
3038
+ // caller produces that shape.
3039
+ //
3040
+ // A re-seat fork is excluded, which Task 2452 states as a scope boundary
3041
+ // and the argv shape enforces anyway: a fork takes the `forkArgvSegment`
3042
+ // branch below, which pushes its own caller-picked `--model`, so the public
3043
+ // push never runs for one. Resolving here regardless would make
3044
+ // `effectiveRcModel` and `op=public-model-assert` report the agent's
3045
+ // config.json model for a session whose command carries the re-seat model —
3046
+ // a false green on the surface this task added to catch false greens.
3047
+ const publicModel = rcRole === 'public' && forkFromSessionId === null
3048
+ ? resolvePublicSpawnModel(effectiveAccountDir, rcAgentSlug)
3049
+ : null;
3050
+ if (publicModel?.rejected) {
3051
+ // An operator wrote a model id that is not selectable. Emitted before the
3052
+ // resolution line so the pair reads as cause then consequence.
3053
+ deps.logger(`[rc-spawn] op=public-model-rejected sessionId=${sessionId ? sessionId.slice(0, 8) : 'new'} value=${publicModel.rejected.value} from=${publicModel.rejected.from} reason=not-selectable`);
3054
+ }
3055
+ if (publicModel) {
3056
+ // Emitted on EVERY public spawn, including the one that resolves nothing:
3057
+ // `source=settings` is a positive line rather than a silence, which is
3058
+ // exactly what this failure had for a day.
3059
+ deps.logger(`[rc-spawn] op=public-model sessionId=${sessionId ? sessionId.slice(0, 8) : 'new'} slug=${rcAgentSlug ?? 'none'} resolved=${publicModel.model ?? 'none'} source=${publicModel.source}`);
3060
+ }
2999
3061
  // Task 1392 — resolve whether this spawn runs under the WhatsApp Sonnet-5/max
3000
3062
  // override, for BOTH the argv and the reported launch params below. Only
3001
3063
  // sessions BORN under the override adopt it, so an existing Opus-born WhatsApp
@@ -3052,6 +3114,12 @@ export function buildHttpApp(deps) {
3052
3114
  // (reseatModel null) leaves the settings.json default model in force.
3053
3115
  if (whatsappSonnetSpawn)
3054
3116
  argv.push('--model', WHATSAPP_ADMIN_MODEL);
3117
+ // Task 2452 — a public spawn carries its agent's own model. The three
3118
+ // gates are mutually exclusive: both admin arms require rcRole==='admin'
3119
+ // (whatsappSonnetSpawn on the fresh branch, freshAdminLevers always), so
3120
+ // ordering here carries no precedence decision.
3121
+ else if (publicModel?.model)
3122
+ argv.push('--model', publicModel.model);
3055
3123
  else if (freshAdminLevers && reseatModel)
3056
3124
  argv.push('--model', reseatModel);
3057
3125
  }
@@ -3269,6 +3337,11 @@ export function buildHttpApp(deps) {
3269
3337
  nodeBin: deps.nodeBin,
3270
3338
  // Task 2290 — the account the gateway keys this session's hub entry on.
3271
3339
  accountId: effectiveAccountId,
3340
+ // Task 2458 — the body role, same value and same bridging as the
3341
+ // telegram sibling below. The gateway branches this session's turn-end
3342
+ // delivery on it, so a public visitor's answer goes back down the
3343
+ // posting key instead of into the admin identity fan-out.
3344
+ role: rcRole ?? undefined,
3272
3345
  // Task 2356 — the spawn-tee inputs, so this server's stderr lands in
3273
3346
  // logs/mcp-whatsapp-channel-<sessionId>.log.
3274
3347
  platformRoot: deps.platformRoot,
@@ -3661,11 +3734,18 @@ export function buildHttpApp(deps) {
3661
3734
  // Task 969 — a fresh new-session spawn carrying picked levers runs under
3662
3735
  // them too (not just a fork), so the sidecar + lifeline + the /chat params
3663
3736
  // row report the model/effort the session actually launched on.
3737
+ // Task 2452 — a public spawn's resolved model, so the op=argv line, the
3738
+ // classification sidecar and the lifeline name the model the session
3739
+ // actually launched on instead of the settings default it no longer uses.
3740
+ // Null model (nothing declared) falls through to the settings read, which
3741
+ // is what that spawn genuinely runs under.
3664
3742
  const effectiveRcModel = whatsappSonnetSpawn
3665
3743
  ? WHATSAPP_ADMIN_MODEL
3666
- : (forkFromSessionId !== null || freshAdminLevers) && reseatModel
3667
- ? reseatModel
3668
- : resolveSettingsModel(deps.claudeConfigDir);
3744
+ : publicModel?.model
3745
+ ? publicModel.model
3746
+ : (forkFromSessionId !== null || freshAdminLevers) && reseatModel
3747
+ ? reseatModel
3748
+ : resolveSettingsModel(deps.claudeConfigDir);
3669
3749
  // Task 970 — the fork path reports its 4-level effort; the fresh path reports
3670
3750
  // its 5-level pick (incl. `max`). Falls back to the settings-resolved effort
3671
3751
  // when neither path carries one (absent on a fork, invalid/absent on a fresh).
@@ -3723,6 +3803,15 @@ export function buildHttpApp(deps) {
3723
3803
  .split(',')
3724
3804
  .filter((t) => t.length > 0).length,
3725
3805
  }));
3806
+ // Task 2452 — argv is final here. A resolved model that never reached the
3807
+ // command is the 725731df9 regression class, and it is invisible on every
3808
+ // other surface: the resolution line still reads healthy, the spawn still
3809
+ // succeeds, and only the session's own JSONL says which model it ran. This
3810
+ // pair (`resolved` non-none with `argvHasModel=false`) is that state,
3811
+ // greppable without a repro.
3812
+ if (publicModel) {
3813
+ deps.logger(`[rc-spawn] op=public-model-assert sessionId=${sessionId ? sessionId.slice(0, 8) : 'new'} resolved=${publicModel.model ?? 'none'} argvHasModel=${argv.includes('--model')}`);
3814
+ }
3726
3815
  deps.logger(`[rc-spawn] op=argv unitToken=${unitToken} cwd=${effectiveAccountDir} permissionMode=${effectiveRcPermissionMode} model=${effectiveRcModel ?? 'unset'} effort=${effectiveRcEffort ?? 'unset'} positionalBytes=${positionalBytes} voiceBytes=${voiceBytes} argv=${JSON.stringify([deps.claudeBin, ...argv])}`);
3727
3816
  emitRcLife(deps.logger, 'argv', 'rc-spawn', 'node-pty', rcLifeMode, unitToken, {
3728
3817
  cwd: effectiveAccountDir,
@@ -3730,6 +3819,12 @@ export function buildHttpApp(deps) {
3730
3819
  });
3731
3820
  let pty;
3732
3821
  try {
3822
+ // Task 2460 — this door passed the account's specialists nowhere at all,
3823
+ // and it is the door the reported dispatch failure came through. Sync
3824
+ // against the request's own account dir, which /public-spawn re-scopes
3825
+ // per request, so a re-scoped spawn never writes the boot account's
3826
+ // registry.
3827
+ syncAccountAgentRegistry(effectiveAccountDir, deps.logger);
3733
3828
  pty = deps.spawnPty(deps.claudeBin, argv, env, effectiveAccountDir, unitToken);
3734
3829
  }
3735
3830
  catch (err) {
@@ -4067,10 +4162,21 @@ export function buildHttpApp(deps) {
4067
4162
  const jsonlPath = jsonlPathForSessionId(effectiveAccountDir, sessionId);
4068
4163
  if (tailIsOpenTurn(jsonlPath)) {
4069
4164
  deps.logger(`[rc-life] op=turn-aborted sessionId=${sessionId.slice(0, 8)} reason=child-exit-mid-turn`);
4070
- appendTurnAbortTerminator(jsonlPath, sessionId);
4165
+ // Task 2457 — the child was terminated, not interrupted. Record that
4166
+ // in the transcript: the CLI's own line blames the operator, and it
4167
+ // is the only thing a later --resume would otherwise read.
4168
+ appendTurnAbortTerminator(jsonlPath, sessionId, {
4169
+ exitCode: e.exitCode ?? null,
4170
+ unit: scopeUnitName(deps.brandSlug, unitToken),
4171
+ });
4071
4172
  }
4072
4173
  }
4073
- catch { /* exit path must never throw */ }
4174
+ catch (err) {
4175
+ // Task 2457 — the exit path still must never throw, but a swallowed
4176
+ // error here means the transcript keeps the CLI's operator-interrupt
4177
+ // line as its only account of the kill. Say why.
4178
+ deps.logger(`[rc-life] op=turn-abort-record-failed sessionId=${sessionId.slice(0, 8)} err=${JSON.stringify(err instanceof Error ? err.message : String(err))}`);
4179
+ }
4074
4180
  }
4075
4181
  // Task 977 — close the per-session child log on exit.
4076
4182
  childLog.close();
@@ -4240,6 +4346,7 @@ export function buildHttpApp(deps) {
4240
4346
  const source = deps.watcher.getBySessionId(forkFromSessionId);
4241
4347
  if (source && source.scopeUnitToken) {
4242
4348
  await stopSession({
4349
+ brandSlug: deps.brandSlug,
4243
4350
  killGraceMs: deps.killGraceMs,
4244
4351
  logger: deps.logger,
4245
4352
  runSystemctl: deps.runSystemctl,