create-walle 0.10.2 → 0.10.3

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 (488) hide show
  1. package/README.md +3 -1
  2. package/lib/release-schema.json +2 -2
  3. package/lib/runtime-closure-policy.json +8 -7
  4. package/package.json +1 -1
  5. package/template/coding-task-manager/api-prompts.js +45 -7
  6. package/template/coding-task-manager/bin/claude-hook.js +55 -1
  7. package/template/coding-task-manager/bin/ctm-relay-client.js +35 -12
  8. package/template/coding-task-manager/bin/land.js +2069 -0
  9. package/template/coding-task-manager/bin/memory-census.js +4 -2
  10. package/template/coding-task-manager/bin/memory-sync-verify.js +151 -0
  11. package/template/coding-task-manager/bin/probe-refusal-scope.js +530 -0
  12. package/template/coding-task-manager/db.js +156 -32
  13. package/template/coding-task-manager/lib/acp/acp-client.js +383 -0
  14. package/template/coding-task-manager/lib/acp/dsh-context-truth.js +176 -0
  15. package/template/coding-task-manager/lib/acp/dsh-launch-model.js +302 -0
  16. package/template/coding-task-manager/lib/acp/dsh-mcp-servers.js +194 -0
  17. package/template/coding-task-manager/lib/acp/dsh-prompt-lane.js +616 -0
  18. package/template/coding-task-manager/lib/acp/dsh-route-patch.js +308 -0
  19. package/template/coding-task-manager/lib/acp/dsh-session-runtime.js +522 -0
  20. package/template/coding-task-manager/lib/acp/dsh-transcript.js +395 -0
  21. package/template/coding-task-manager/lib/acp/dsh-usage-log.js +162 -0
  22. package/template/coding-task-manager/lib/acp/ndjson-stdio-transport.js +225 -0
  23. package/template/coding-task-manager/lib/adopted-host-redial.js +47 -11
  24. package/template/coding-task-manager/lib/adopted-launch-environment.js +123 -0
  25. package/template/coding-task-manager/lib/agent-auto-update-runtime.js +0 -3
  26. package/template/coding-task-manager/lib/agent-auto-update.js +26 -10
  27. package/template/coding-task-manager/lib/agent-bundle/build.js +657 -0
  28. package/template/coding-task-manager/lib/agent-bundle/codex-install.js +558 -0
  29. package/template/coding-task-manager/lib/agent-bundle/drift.js +120 -0
  30. package/template/coding-task-manager/lib/agent-bundle/gc.js +522 -0
  31. package/template/coding-task-manager/lib/agent-bundle/inventory.js +346 -0
  32. package/template/coding-task-manager/lib/agent-bundle/push.js +717 -0
  33. package/template/coding-task-manager/lib/agent-bundle/skill-transfer.js +724 -0
  34. package/template/coding-task-manager/lib/agent-bundle/spawn.js +119 -0
  35. package/template/coding-task-manager/lib/agent-bundle/store.js +568 -0
  36. package/template/coding-task-manager/lib/agent-capabilities.js +142 -12
  37. package/template/coding-task-manager/lib/agent-cli-cache.js +30 -5
  38. package/template/coding-task-manager/lib/agent-definitions.js +19 -0
  39. package/template/coding-task-manager/lib/agent-hooks-codex-toml.js +28 -16
  40. package/template/coding-task-manager/lib/agent-hooks-codex-workspace.js +9 -1
  41. package/template/coding-task-manager/lib/agent-hooks-installer.js +23 -8
  42. package/template/coding-task-manager/lib/agent-hooks-prune.js +37 -11
  43. package/template/coding-task-manager/lib/agent-hooks-shim.js +105 -0
  44. package/template/coding-task-manager/lib/agent-presets.js +84 -1
  45. package/template/coding-task-manager/lib/agent-upgrade-jobs.js +12 -1
  46. package/template/coding-task-manager/lib/agent-version-service.js +74 -11
  47. package/template/coding-task-manager/lib/all-sessions-query.js +6 -2
  48. package/template/coding-task-manager/lib/api/agent-bundles.js +436 -0
  49. package/template/coding-task-manager/lib/api/backup-retention.js +83 -0
  50. package/template/coding-task-manager/lib/api/conversation-sync-routes.js +124 -0
  51. package/template/coding-task-manager/lib/api/diagnostics.js +549 -69
  52. package/template/coding-task-manager/lib/api/fleet.js +118 -0
  53. package/template/coding-task-manager/lib/api/hub-bridge.js +243 -0
  54. package/template/coding-task-manager/lib/api/ingress.js +9 -1
  55. package/template/coding-task-manager/lib/api/landing.js +403 -0
  56. package/template/coding-task-manager/lib/api/machines.js +691 -2
  57. package/template/coding-task-manager/lib/api/memory-sync-routes.js +335 -0
  58. package/template/coding-task-manager/lib/api/misc-ops.js +8 -3
  59. package/template/coding-task-manager/lib/api/remote-sessions.js +1326 -0
  60. package/template/coding-task-manager/lib/api/session-attach.js +623 -0
  61. package/template/coding-task-manager/lib/api/session-messages.js +1384 -0
  62. package/template/coding-task-manager/lib/api/sessions.js +77 -51
  63. package/template/coding-task-manager/lib/api/setup-network.js +544 -16
  64. package/template/coding-task-manager/lib/api/setup-wizard-handlers.js +884 -0
  65. package/template/coding-task-manager/lib/api/setup.js +829 -10
  66. package/template/coding-task-manager/lib/api/walle-bridge.js +9 -1
  67. package/template/coding-task-manager/lib/attachment-reconciler-wiring.js +5 -2
  68. package/template/coding-task-manager/lib/auth-rate-limit.js +23 -0
  69. package/template/coding-task-manager/lib/auth-rules.js +292 -21
  70. package/template/coding-task-manager/lib/background-stop.js +369 -0
  71. package/template/coding-task-manager/lib/best-of-attempts.js +4 -1
  72. package/template/coding-task-manager/lib/best-of-launch-plan.js +52 -0
  73. package/template/coding-task-manager/lib/box-keep-awake.js +89 -0
  74. package/template/coding-task-manager/lib/broadcast-reason-stats.js +152 -0
  75. package/template/coding-task-manager/lib/broadcast-serialize.js +5 -0
  76. package/template/coding-task-manager/lib/build-info.js +4 -2
  77. package/template/coding-task-manager/lib/claude-model-truth.js +13 -2
  78. package/template/coding-task-manager/lib/claude-transcript-terminal-renderer.js +17 -13
  79. package/template/coding-task-manager/lib/client-diag-handler.js +64 -2
  80. package/template/coding-task-manager/lib/codex-app-server-client.js +14 -16
  81. package/template/coding-task-manager/lib/codex-configuration-history.js +55 -0
  82. package/template/coding-task-manager/lib/codex-harness-qualification.js +43 -20
  83. package/template/coding-task-manager/lib/codex-rollout-history.js +743 -0
  84. package/template/coding-task-manager/lib/coding-agent-models.js +27 -12
  85. package/template/coding-task-manager/lib/coding-cli-skill-installer.js +44 -14
  86. package/template/coding-task-manager/lib/coding-session-model-adapters.js +43 -2
  87. package/template/coding-task-manager/lib/coding-session-model-owner.js +1 -1
  88. package/template/coding-task-manager/lib/coding-session-model-public.js +7 -1
  89. package/template/coding-task-manager/lib/coding-session-model-runtime.js +23 -4
  90. package/template/coding-task-manager/lib/coding-session-model-scan.js +31 -0
  91. package/template/coding-task-manager/lib/coding-session-model-state.js +7 -0
  92. package/template/coding-task-manager/lib/conversation-freshness-diagnostics.js +280 -0
  93. package/template/coding-task-manager/lib/conversation-item-clock.js +261 -0
  94. package/template/coding-task-manager/lib/conversation-source-registry.js +5 -0
  95. package/template/coding-task-manager/lib/conversation-store-v2.js +44 -0
  96. package/template/coding-task-manager/lib/conversation-sync.js +355 -0
  97. package/template/coding-task-manager/lib/create/resolve-launch-params.js +96 -12
  98. package/template/coding-task-manager/lib/create/worker-role-profile.js +4 -4
  99. package/template/coding-task-manager/lib/create-intent-lease.js +51 -0
  100. package/template/coding-task-manager/lib/ctm-session-context-api.js +97 -6
  101. package/template/coding-task-manager/lib/db-domain-tables.js +1 -0
  102. package/template/coding-task-manager/lib/db-domains.js +1 -0
  103. package/template/coding-task-manager/lib/db-owner-dispatch-probe.js +32 -0
  104. package/template/coding-task-manager/lib/db-owner-request-counts.js +178 -0
  105. package/template/coding-task-manager/lib/db-owner-worker-client.js +68 -14
  106. package/template/coding-task-manager/lib/db-schema-source-files.js +5 -0
  107. package/template/coding-task-manager/lib/deferred-restore-attempt.js +6 -2
  108. package/template/coding-task-manager/lib/feature-manifest.js +4 -0
  109. package/template/coding-task-manager/lib/fleet-conversation-route.js +97 -0
  110. package/template/coding-task-manager/lib/fleet-fanout.js +78 -0
  111. package/template/coding-task-manager/lib/fleet-hub.js +762 -0
  112. package/template/coding-task-manager/lib/fleet-origin-follower.js +252 -0
  113. package/template/coding-task-manager/lib/fleet-resolver-effects.js +147 -0
  114. package/template/coding-task-manager/lib/fleet-resolver-tick.js +148 -0
  115. package/template/coding-task-manager/lib/fleet-resolver.js +454 -0
  116. package/template/coding-task-manager/lib/fleet-session-list-routes.js +114 -0
  117. package/template/coding-task-manager/lib/fleet-session-lists.js +672 -0
  118. package/template/coding-task-manager/lib/fleet-update.js +798 -0
  119. package/template/coding-task-manager/lib/fs-watcher-root-watch.js +141 -0
  120. package/template/coding-task-manager/lib/fs-watcher.js +58 -42
  121. package/template/coding-task-manager/lib/harness-history-transition.js +50 -9
  122. package/template/coding-task-manager/lib/headless-term-service.js +84 -8
  123. package/template/coding-task-manager/lib/hermetic-agent-launch-guard.js +10 -0
  124. package/template/coding-task-manager/lib/history-transition-read-op.js +175 -8
  125. package/template/coding-task-manager/lib/host-cpu-categories.js +310 -0
  126. package/template/coding-task-manager/lib/host-fseventsd-sampler.js +399 -0
  127. package/template/coding-task-manager/lib/host-handoff.js +16 -19
  128. package/template/coding-task-manager/lib/host-stats.js +150 -3
  129. package/template/coding-task-manager/lib/hosted-sync-worker.js +43 -320
  130. package/template/coding-task-manager/lib/hub-bridge-access.js +798 -0
  131. package/template/coding-task-manager/lib/hub-bridge-box.js +409 -0
  132. package/template/coding-task-manager/lib/hub-bridge-mcp.js +380 -0
  133. package/template/coding-task-manager/lib/hub-bridge-mesh.js +313 -0
  134. package/template/coding-task-manager/lib/hub-bridge-ops.js +721 -0
  135. package/template/coding-task-manager/lib/hub-bridge.js +398 -0
  136. package/template/coding-task-manager/lib/idle-notify.js +491 -0
  137. package/template/coding-task-manager/lib/instance-id-ownership-manifest.js +8 -0
  138. package/template/coding-task-manager/lib/instance-manifest.js +5 -1
  139. package/template/coding-task-manager/lib/instance-sync-status.js +18 -8
  140. package/template/coding-task-manager/lib/landing/agent-rule.txt +1 -0
  141. package/template/coding-task-manager/lib/landing/batch-runner.js +366 -0
  142. package/template/coding-task-manager/lib/landing/config.js +536 -0
  143. package/template/coding-task-manager/lib/landing/ctm-view.js +256 -0
  144. package/template/coding-task-manager/lib/landing/gate.js +360 -0
  145. package/template/coding-task-manager/lib/landing/git.js +344 -0
  146. package/template/coding-task-manager/lib/landing/lease.js +247 -0
  147. package/template/coding-task-manager/lib/landing/merge-delta.js +380 -0
  148. package/template/coding-task-manager/lib/landing/proof.js +223 -0
  149. package/template/coding-task-manager/lib/landing/queue.js +998 -0
  150. package/template/coding-task-manager/lib/landing/radar.js +489 -0
  151. package/template/coding-task-manager/lib/landing/resolve-cli.js +424 -0
  152. package/template/coding-task-manager/lib/landing/state.js +263 -0
  153. package/template/coding-task-manager/lib/landing/stop.js +49 -0
  154. package/template/coding-task-manager/lib/landing/sync-cli.js +475 -0
  155. package/template/coding-task-manager/lib/landing/sync.js +456 -0
  156. package/template/coding-task-manager/lib/landing/train.js +885 -0
  157. package/template/coding-task-manager/lib/landing/window.js +57 -0
  158. package/template/coding-task-manager/lib/launch-environment.js +121 -0
  159. package/template/coding-task-manager/lib/launch-model-override.js +36 -10
  160. package/template/coding-task-manager/lib/launch-plan-pool.js +2 -1
  161. package/template/coding-task-manager/lib/launch-plan-warm-cache.js +6 -0
  162. package/template/coding-task-manager/lib/lead-model-observer.js +57 -1
  163. package/template/coding-task-manager/lib/lead-model-options.js +160 -3
  164. package/template/coding-task-manager/lib/lead-model-restore-reconciliation.js +106 -24
  165. package/template/coding-task-manager/lib/lead-model-switch-runtime.js +352 -68
  166. package/template/coding-task-manager/lib/lead-model-switch.js +114 -10
  167. package/template/coding-task-manager/lib/lead-model-transition-runtime.js +14 -2
  168. package/template/coding-task-manager/lib/lead-model-transition-store.js +24 -6
  169. package/template/coding-task-manager/lib/lead-route-preflight.js +179 -0
  170. package/template/coding-task-manager/lib/lead-switch-history-mode.js +110 -0
  171. package/template/coding-task-manager/lib/lead-wire-agent.js +7 -0
  172. package/template/coding-task-manager/lib/lifecycle-wake-experiment.js +9 -2
  173. package/template/coding-task-manager/lib/live-defer-announce.js +45 -0
  174. package/template/coding-task-manager/lib/machine-registry.js +750 -5
  175. package/template/coding-task-manager/lib/macos-capabilities.js +32 -15
  176. package/template/coding-task-manager/lib/main-thread-lint-exemptions.json +433 -53
  177. package/template/coding-task-manager/lib/mainloop-observability.js +14 -9
  178. package/template/coding-task-manager/lib/media-draft-owner.js +16 -3
  179. package/template/coding-task-manager/lib/memory-join.js +337 -0
  180. package/template/coding-task-manager/lib/memory-sync-verify.js +393 -0
  181. package/template/coding-task-manager/lib/memory-sync.js +565 -0
  182. package/template/coding-task-manager/lib/migrations/v57-delegate-pref-mirror-opencode.js +68 -0
  183. package/template/coding-task-manager/lib/migrations/v58-subagent-launch-native-subagents.js +42 -0
  184. package/template/coding-task-manager/lib/migrations/v59-coding-session-model-state-dsh.js +73 -0
  185. package/template/coding-task-manager/lib/migrations/v60-delegate-pref-mirror-dsh.js +78 -0
  186. package/template/coding-task-manager/lib/migrations/v61-conversation-item-clock.js +71 -0
  187. package/template/coding-task-manager/lib/model-availability.js +727 -0
  188. package/template/coding-task-manager/lib/model-execution-recipe-metadata.js +4 -6
  189. package/template/coding-task-manager/lib/model-host-receipt.js +6 -3
  190. package/template/coding-task-manager/lib/model-id-rule.js +19 -0
  191. package/template/coding-task-manager/lib/model-proxy/anthropic-face.js +4 -0
  192. package/template/coding-task-manager/lib/model-proxy/auto-mode-review.js +110 -23
  193. package/template/coding-task-manager/lib/model-proxy/chat-response-translators.js +3 -0
  194. package/template/coding-task-manager/lib/model-proxy/generation-probe.js +60 -17
  195. package/template/coding-task-manager/lib/model-proxy/historical-tool-declarations.js +37 -0
  196. package/template/coding-task-manager/lib/model-proxy/lead-request-validation.js +1 -0
  197. package/template/coding-task-manager/lib/model-proxy/lead-token-admin.js +130 -0
  198. package/template/coding-task-manager/lib/model-proxy/refusal-scope.js +151 -0
  199. package/template/coding-task-manager/lib/model-proxy/request-budget-store.js +11 -2
  200. package/template/coding-task-manager/lib/model-proxy/tokens.js +69 -5
  201. package/template/coding-task-manager/lib/model-proxy/upstream-outcome.js +9 -0
  202. package/template/coding-task-manager/lib/native-agent-model-args.js +25 -22
  203. package/template/coding-task-manager/lib/native-history-control.js +6 -4
  204. package/template/coding-task-manager/lib/native-lead-settings-client.js +44 -4
  205. package/template/coding-task-manager/lib/oauth-proxy-supervisor.js +173 -36
  206. package/template/coding-task-manager/lib/online-backup-worker-client.js +2 -0
  207. package/template/coding-task-manager/lib/opencode-cli.js +480 -0
  208. package/template/coding-task-manager/lib/opencode-launch-model.js +471 -0
  209. package/template/coding-task-manager/lib/opencode-v1-plugin/model-carrier.mjs +35 -0
  210. package/template/coding-task-manager/lib/opencode-v2-plugin/server.js +118 -0
  211. package/template/coding-task-manager/lib/owner-action-manifest-data.json +1 -1
  212. package/template/coding-task-manager/lib/park-composer-draft.js +54 -0
  213. package/template/coding-task-manager/lib/park-fresh-relaunch.js +41 -10
  214. package/template/coding-task-manager/lib/perf-phase-trace.js +72 -0
  215. package/template/coding-task-manager/lib/perf-summary.js +7 -0
  216. package/template/coding-task-manager/lib/perf-tracker.js +63 -2
  217. package/template/coding-task-manager/lib/project-config.js +7 -0
  218. package/template/coding-task-manager/lib/prompt-purge-v2.js +52 -1
  219. package/template/coding-task-manager/lib/provider-login-broker.js +72 -15
  220. package/template/coding-task-manager/lib/provider-login-tab.js +10 -2
  221. package/template/coding-task-manager/lib/read-pool-client.js +89 -2
  222. package/template/coding-task-manager/lib/read-pool-worker-handle.js +29 -4
  223. package/template/coding-task-manager/lib/recent-catalog-query.js +15 -0
  224. package/template/coding-task-manager/lib/recent-session-catalog-response.js +67 -10
  225. package/template/coding-task-manager/lib/recovery-evidence.js +26 -11
  226. package/template/coding-task-manager/lib/relay-client.js +57 -3
  227. package/template/coding-task-manager/lib/relink-audit.js +34 -5
  228. package/template/coding-task-manager/lib/remote-machine-catalog.js +321 -26
  229. package/template/coding-task-manager/lib/remote-machine-create.js +576 -36
  230. package/template/coding-task-manager/lib/remote-machine-link.js +546 -59
  231. package/template/coding-task-manager/lib/remote-machine-proxy.js +143 -9
  232. package/template/coding-task-manager/lib/remote-session-forward.js +65 -7
  233. package/template/coding-task-manager/lib/remote-terminal-target.js +9 -2
  234. package/template/coding-task-manager/lib/restart-guard.js +18 -6
  235. package/template/coding-task-manager/lib/restore-seed-modes.js +87 -0
  236. package/template/coding-task-manager/lib/restored-scrollback-hydration-runtime.js +22 -2
  237. package/template/coding-task-manager/lib/resume-admission.js +17 -3
  238. package/template/coding-task-manager/lib/review-repo-registry.js +58 -19
  239. package/template/coding-task-manager/lib/root-delivery-runtime.js +2 -2
  240. package/template/coding-task-manager/lib/root-prompt-flow.js +1 -1
  241. package/template/coding-task-manager/lib/runtime-contract.js +3 -1
  242. package/template/coding-task-manager/lib/schema-indexes.js +1 -0
  243. package/template/coding-task-manager/lib/session-context-locator.js +742 -0
  244. package/template/coding-task-manager/lib/session-create/attachment.js +2 -2
  245. package/template/coding-task-manager/lib/session-create/create-phases.js +118 -0
  246. package/template/coding-task-manager/lib/session-create/delegate-worker.js +86 -16
  247. package/template/coding-task-manager/lib/session-create/dsh-session.js +930 -0
  248. package/template/coding-task-manager/lib/session-create/lead-route-launch.js +186 -13
  249. package/template/coding-task-manager/lib/session-create/native-lead-lifetime.js +55 -3
  250. package/template/coding-task-manager/lib/session-create/opencode-lead-provider.js +50 -0
  251. package/template/coding-task-manager/lib/session-create/prepared.js +162 -38
  252. package/template/coding-task-manager/lib/session-create/provider-process.js +160 -28
  253. package/template/coding-task-manager/lib/session-create/response.js +7 -7
  254. package/template/coding-task-manager/lib/session-create/structured-session.js +27 -0
  255. package/template/coding-task-manager/lib/session-create/subagent-pick-table.js +3 -1
  256. package/template/coding-task-manager/lib/session-create/subagent-pick.js +458 -9
  257. package/template/coding-task-manager/lib/session-create/transcript.js +1 -1
  258. package/template/coding-task-manager/lib/session-create/worker-budget-setup.js +21 -2
  259. package/template/coding-task-manager/lib/session-create/worker-continuation.js +7 -1
  260. package/template/coding-task-manager/lib/session-create/worker-launch-sandbox.js +39 -10
  261. package/template/coding-task-manager/lib/session-create/worker-observability.js +2 -2
  262. package/template/coding-task-manager/lib/session-empty.js +13 -10
  263. package/template/coding-task-manager/lib/session-host-manager.js +60 -0
  264. package/template/coding-task-manager/lib/session-jobs.js +3 -1
  265. package/template/coding-task-manager/lib/session-lifecycle.js +10 -4
  266. package/template/coding-task-manager/lib/session-model-action-preconditions.js +6 -2
  267. package/template/coding-task-manager/lib/session-model-intent-contract.js +11 -8
  268. package/template/coding-task-manager/lib/session-park.js +738 -0
  269. package/template/coding-task-manager/lib/session-path-grant.js +13 -4
  270. package/template/coding-task-manager/lib/session-status-ledger.js +106 -4
  271. package/template/coding-task-manager/lib/session-transport.js +17 -0
  272. package/template/coding-task-manager/lib/session-workspace-projection.js +54 -5
  273. package/template/coding-task-manager/lib/session-workspace-tracking-runtime.js +213 -1
  274. package/template/coding-task-manager/lib/skill-autocomplete-query.js +120 -3
  275. package/template/coding-task-manager/lib/skill-autocomplete.js +15 -1
  276. package/template/coding-task-manager/lib/sqlite-file-set.js +39 -0
  277. package/template/coding-task-manager/lib/standup-search-adapter.js +16 -2
  278. package/template/coding-task-manager/lib/state-sync/client-ledger.js +12 -0
  279. package/template/coding-task-manager/lib/state-sync/frame-emitter.js +319 -100
  280. package/template/coding-task-manager/lib/storage/coding-session-model-store.js +4 -1
  281. package/template/coding-task-manager/lib/storage/delegate-pref-mirror-store.js +32 -9
  282. package/template/coding-task-manager/lib/structured-session-boot-sweep.js +235 -0
  283. package/template/coding-task-manager/lib/submission-ack.js +90 -2
  284. package/template/coding-task-manager/lib/systemd-scope.js +115 -0
  285. package/template/coding-task-manager/lib/telemetry-receiver.js +39 -0
  286. package/template/coding-task-manager/lib/terminal-attach-geometry.js +985 -0
  287. package/template/coding-task-manager/lib/terminal-bootstrap.js +30 -0
  288. package/template/coding-task-manager/lib/terminal-draft-operation.js +357 -0
  289. package/template/coding-task-manager/lib/terminal-input-admission.js +17 -1
  290. package/template/coding-task-manager/lib/terminal-input-prepared.js +563 -0
  291. package/template/coding-task-manager/lib/transcript-cache-temps.js +171 -0
  292. package/template/coding-task-manager/lib/transcript-frame-cache.js +8 -3
  293. package/template/coding-task-manager/lib/transcript-source.js +36 -4
  294. package/template/coding-task-manager/lib/transcript-store.js +9 -0
  295. package/template/coding-task-manager/lib/typing-interaction-trace.js +106 -3
  296. package/template/coding-task-manager/lib/usage-ledger/backfill.js +40 -5
  297. package/template/coding-task-manager/lib/usage-ledger/client.js +4 -1
  298. package/template/coding-task-manager/lib/usage-ledger/dsh-parse.js +152 -0
  299. package/template/coding-task-manager/lib/usage-ledger/dsh-read.js +73 -0
  300. package/template/coding-task-manager/lib/usage-ledger/ingest.js +34 -6
  301. package/template/coding-task-manager/lib/usage-ledger/live.js +35 -8
  302. package/template/coding-task-manager/lib/usage-ledger/read.js +29 -20
  303. package/template/coding-task-manager/lib/usage-ledger/schema.js +61 -1
  304. package/template/coding-task-manager/lib/usage-ledger/task-coordinator.js +4 -2
  305. package/template/coding-task-manager/lib/wake-skip-reasons.js +1 -0
  306. package/template/coding-task-manager/lib/walle/turn.js +1 -1
  307. package/template/coding-task-manager/lib/walle-history-serve.js +838 -0
  308. package/template/coding-task-manager/lib/walle-kill-authority.js +2 -2
  309. package/template/coding-task-manager/lib/walle-log-rotation.js +164 -1
  310. package/template/coding-task-manager/lib/walle-mcp-auto-config.js +56 -2
  311. package/template/coding-task-manager/lib/walle-row-history.js +495 -0
  312. package/template/coding-task-manager/lib/walle-supervisor.js +33 -14
  313. package/template/coding-task-manager/lib/watch-worker-client.js +203 -0
  314. package/template/coding-task-manager/lib/worker-terminal-output.js +5 -2
  315. package/template/coding-task-manager/lib/ws/hub-bridge.js +35 -0
  316. package/template/coding-task-manager/lib/ws/lifecycle.js +6 -11
  317. package/template/coding-task-manager/lib/ws/misc.js +1 -1
  318. package/template/coding-task-manager/lib/ws/state-sync.js +106 -4
  319. package/template/coding-task-manager/lib/ws/terminal-attach.js +91 -0
  320. package/template/coding-task-manager/oauth-proxy.js +112 -115
  321. package/template/coding-task-manager/package-lock.json +10 -94
  322. package/template/coding-task-manager/package.json +4 -3
  323. package/template/coding-task-manager/protocol/message-table.json +10 -0
  324. package/template/coding-task-manager/protocol/messages.js +10 -0
  325. package/template/coding-task-manager/protocol/ws-schema.json +20 -0
  326. package/template/coding-task-manager/protocol/ws-schema.md +2 -0
  327. package/template/coding-task-manager/public/css/app.css +170 -0
  328. package/template/coding-task-manager/public/css/walle-session.css +4 -0
  329. package/template/coding-task-manager/public/index.html +473 -243
  330. package/template/coding-task-manager/public/js/agent-command.js +86 -0
  331. package/template/coding-task-manager/public/js/app/backups-panel.js +111 -5
  332. package/template/coding-task-manager/public/js/app/coding-agent-upgrade.js +103 -4
  333. package/template/coding-task-manager/public/js/app/machines-page.js +1328 -8
  334. package/template/coding-task-manager/public/js/app/models-tab.js +113 -38
  335. package/template/coding-task-manager/public/js/app/restart-banner.js +53 -0
  336. package/template/coding-task-manager/public/js/app/worktrees-panel.js +184 -2
  337. package/template/coding-task-manager/public/js/background-work.js +196 -27
  338. package/template/coding-task-manager/public/js/coding-session-model-presentation.js +138 -10
  339. package/template/coding-task-manager/public/js/core/active-session-groups.js +179 -0
  340. package/template/coding-task-manager/public/js/core/pending-create.js +238 -0
  341. package/template/coding-task-manager/public/js/core/recent-machine-source.js +228 -0
  342. package/template/coding-task-manager/public/js/core/session-commands.js +180 -14
  343. package/template/coding-task-manager/public/js/dashboard-client-function-order.json +8 -0
  344. package/template/coding-task-manager/public/js/document-review-links.js +9 -1
  345. package/template/coding-task-manager/public/js/file-resolution-client.js +52 -1
  346. package/template/coding-task-manager/public/js/history-app-seam.js +22 -2
  347. package/template/coding-task-manager/public/js/machine-pool-shared.js +558 -3
  348. package/template/coding-task-manager/public/js/message-renderer.js +4 -3
  349. package/template/coding-task-manager/public/js/model-picker.js +3 -1
  350. package/template/coding-task-manager/public/js/native-subagent-review.js +15 -2
  351. package/template/coding-task-manager/public/js/new-session-agent-grid.js +346 -0
  352. package/template/coding-task-manager/public/js/pasted-content.js +236 -0
  353. package/template/coding-task-manager/public/js/pe-private-content-lifetime.js +39 -7
  354. package/template/coding-task-manager/public/js/private-model-lifetime.js +6 -2
  355. package/template/coding-task-manager/public/js/prompts.js +11 -10
  356. package/template/coding-task-manager/public/js/protocol.js +10 -0
  357. package/template/coding-task-manager/public/js/restart-banner-policy.js +29 -1
  358. package/template/coding-task-manager/public/js/session-model-picker.js +447 -57
  359. package/template/coding-task-manager/public/js/session-search-utils.js +18 -2
  360. package/template/coding-task-manager/public/js/session-usage-view.js +2 -1
  361. package/template/coding-task-manager/public/js/session-workspace-tracking.js +51 -1
  362. package/template/coding-task-manager/public/js/state-sync-client.js +10 -1
  363. package/template/coding-task-manager/public/js/stream-view.js +6 -5
  364. package/template/coding-task-manager/public/js/terminal/conv-only-session.js +287 -0
  365. package/template/coding-task-manager/public/js/terminal/runtime-09.js +2 -0
  366. package/template/coding-task-manager/public/js/terminal/runtime-10.js +5 -5
  367. package/template/coding-task-manager/public/js/terminal/runtime-12.js +8 -1
  368. package/template/coding-task-manager/public/js/terminal/runtime-16.js +4 -0
  369. package/template/coding-task-manager/public/js/terminal/runtime-20.js +1 -1
  370. package/template/coding-task-manager/public/js/terminal-attachment-draft.js +67 -13
  371. package/template/coding-task-manager/public/js/terminal-image-orchestration.js +52 -6
  372. package/template/coding-task-manager/public/js/terminal-wheel.js +23 -3
  373. package/template/coding-task-manager/public/js/walle/chat-conversation.js +4 -4
  374. package/template/coding-task-manager/public/js/walle.js +6 -0
  375. package/template/coding-task-manager/public/js/ws/live-dispatch.js +10 -0
  376. package/template/coding-task-manager/public/m/app.css +140 -0
  377. package/template/coding-task-manager/public/m/app.js +507 -68
  378. package/template/coding-task-manager/public/m/index.html +17 -0
  379. package/template/coding-task-manager/public/m/machines.js +838 -4
  380. package/template/coding-task-manager/public/m/private-api.js +31 -1
  381. package/template/coding-task-manager/public/usage.html +1 -0
  382. package/template/coding-task-manager/scripts/dev-route-fixture/synthetic-route-upstream.js +104 -0
  383. package/template/coding-task-manager/scripts/dev-route-fixture.js +339 -0
  384. package/template/coding-task-manager/scripts/opencode-lead-route-probe.js +195 -0
  385. package/template/coding-task-manager/scripts/opencode-v2-contract-probe/fake-anthropic.js +168 -0
  386. package/template/coding-task-manager/scripts/opencode-v2-contract-probe/probe-plugin/server.js +95 -0
  387. package/template/coding-task-manager/scripts/opencode-v2-contract-probe.js +673 -0
  388. package/template/coding-task-manager/server.js +1950 -11679
  389. package/template/coding-task-manager/session-integrity.js +64 -4
  390. package/template/coding-task-manager/workers/db-owner-worker.js +19 -2
  391. package/template/coding-task-manager/workers/online-backup-worker.js +1 -0
  392. package/template/coding-task-manager/workers/read-pool-model-collector.js +51 -3
  393. package/template/coding-task-manager/workers/read-pool-worker.js +76 -6
  394. package/template/coding-task-manager/workers/session-host-pool-process.js +6 -6
  395. package/template/coding-task-manager/workers/session-host-process.js +5 -5
  396. package/template/coding-task-manager/workers/watch-worker.js +105 -0
  397. package/template/coding-task-manager/workers/worktree-worker.js +168 -33
  398. package/template/ctm-relay/lib/protocol.js +14 -1
  399. package/template/package-lock.json +2 -2
  400. package/template/package.json +1 -1
  401. package/template/shared/backup-retention.js +82 -0
  402. package/template/shared/conversation-admission.js +2 -0
  403. package/template/shared/conversation-provenance.js +3 -7
  404. package/template/shared/conversation-storage-v2-contract.js +5 -0
  405. package/template/shared/linked-worktree.js +51 -0
  406. package/template/shared/macos-fda-target.js +57 -0
  407. package/template/shared/orphan-parents.js +55 -0
  408. package/template/shared/release-schema.json +2 -2
  409. package/template/shared/storage/contract.js +3 -0
  410. package/template/wall-e/adapters/imessage.js +12 -0
  411. package/template/wall-e/agent.js +71 -4
  412. package/template/wall-e/api-walle.js +21 -1
  413. package/template/wall-e/bin/train-gemma-e4b-tooluse.js +2 -2
  414. package/template/wall-e/bin/walle-mcp-stdio.js +5 -5
  415. package/template/wall-e/brain/brain-join.js +859 -0
  416. package/template/wall-e/brain/brain-sync-membership.js +20 -0
  417. package/template/wall-e/brain/brain-sync-store.js +131 -77
  418. package/template/wall-e/brain/brain-sync-v2-schema.js +203 -0
  419. package/template/wall-e/brain/brain-sync-v2.js +578 -0
  420. package/template/wall-e/brain/delegate-model-prefs-store.js +13 -4
  421. package/template/wall-e/brain/deterministic-id.js +51 -0
  422. package/template/wall-e/brain/memories.js +2 -2
  423. package/template/wall-e/brain/migrations.js +40 -6
  424. package/template/wall-e/brain/model-identity-migration.js +1 -1
  425. package/template/wall-e/brain/owner-call-whitelist.js +23 -7
  426. package/template/wall-e/brain/schema.js +45 -85
  427. package/template/wall-e/brain.js +171 -35
  428. package/template/wall-e/chat.js +3 -3
  429. package/template/wall-e/coding/agent-shell-env.js +7 -2
  430. package/template/wall-e/coding/prompt-bundle.js +66 -21
  431. package/template/wall-e/config/main-loop-exemptions.json +2 -2
  432. package/template/wall-e/http/admin-api.js +37 -0
  433. package/template/wall-e/http/gws-api.js +4 -0
  434. package/template/wall-e/http/helper-rules-api.js +7 -1
  435. package/template/wall-e/http/model-admin.js +8 -1
  436. package/template/wall-e/http/model-delegate-pref-admin.js +3 -3
  437. package/template/wall-e/http/model-proxy-providers.js +174 -1
  438. package/template/wall-e/http/model-route-health.js +157 -0
  439. package/template/wall-e/http/sync-api.js +278 -464
  440. package/template/wall-e/jobs/job-runner.js +28 -1
  441. package/template/wall-e/lib/brain-owner-op-priority.js +9 -1
  442. package/template/wall-e/lib/ctm-data-dir.js +26 -0
  443. package/template/wall-e/lib/derivation-home.js +147 -0
  444. package/template/wall-e/lib/helper-rules/evaluate.js +50 -9
  445. package/template/wall-e/lib/helper-rules/grade.js +51 -8
  446. package/template/wall-e/lib/helper-rules/lead-call-hook.js +38 -5
  447. package/template/wall-e/lib/helper-rules/store.js +4 -2
  448. package/template/wall-e/lib/landing-mcp-tools.js +170 -0
  449. package/template/wall-e/lib/mcp-client-config.js +133 -14
  450. package/template/wall-e/lib/mcp-gateway-migrate.js +28 -64
  451. package/template/wall-e/lib/mcp-integration.js +58 -13
  452. package/template/wall-e/lib/parent-death.js +7 -1
  453. package/template/wall-e/lib/scheduler-worker-jobs.js +6 -4
  454. package/template/wall-e/llm/delegate-lead-prefs.js +16 -1
  455. package/template/wall-e/llm/delegate-vendor-default.js +12 -5
  456. package/template/wall-e/llm/model-roles.js +1 -1
  457. package/template/wall-e/llm/native-delegate-selection.js +56 -8
  458. package/template/wall-e/llm/worker-delegate-args.js +72 -7
  459. package/template/wall-e/llm/worker-mcp-routing.js +66 -17
  460. package/template/wall-e/loops/brain-custom-writes.js +19 -0
  461. package/template/wall-e/loops/ingest.js +17 -1
  462. package/template/wall-e/loops/think.js +19 -4
  463. package/template/wall-e/mcp-contract.js +222 -6
  464. package/template/wall-e/mcp-protocol-contract.js +40 -5
  465. package/template/wall-e/mcp-server.js +128 -29
  466. package/template/wall-e/mcp-worker-schemas.js +5 -5
  467. package/template/wall-e/memory/ctm-context-client.js +85 -0
  468. package/template/wall-e/memory/ctm-session-context.js +9 -3
  469. package/template/wall-e/memory/hub-bridge-client.js +388 -0
  470. package/template/wall-e/memory/replica-origin.js +56 -0
  471. package/template/wall-e/memory/session-pool.js +135 -0
  472. package/template/wall-e/runtime/live-capabilities.js +8 -3
  473. package/template/wall-e/server.js +4 -2
  474. package/template/wall-e/shared/release-schema.json +2 -2
  475. package/template/wall-e/shared/sqlite-file-set.js +39 -0
  476. package/template/wall-e/shared/storage/contract.js +3 -0
  477. package/template/wall-e/skills/_templates/delegate-worker/VERSION +1 -1
  478. package/template/wall-e/skills/_templates/delegate-worker/released-sha256.json +7 -1
  479. package/template/wall-e/skills/_templates/delegate-worker/templates/claude/SKILL.md +4 -4
  480. package/template/wall-e/skills/_templates/delegate-worker/templates/codex/SKILL.md +6 -6
  481. package/template/wall-e/skills/_templates/fleet-sync/VERSION +1 -0
  482. package/template/wall-e/skills/_templates/fleet-sync/released-sha256.json +16 -0
  483. package/template/wall-e/skills/_templates/fleet-sync/templates/claude/SKILL.md +64 -0
  484. package/template/wall-e/skills/_templates/fleet-sync/templates/codex/SKILL.md +64 -0
  485. package/template/wall-e/tools/chrome-automation.js +6 -0
  486. package/template/wall-e/training/setup-cuda-train-env.sh +2 -2
  487. package/template/wall-e/workers/brain-owner-worker.js +18 -4
  488. package/template/wall-e/workers/mcp-gateway-process.js +5 -5
@@ -0,0 +1,388 @@
1
+ 'use strict';
2
+
3
+ // WP-1 (docs/remote-session-hub-bridge-2026-10-30.html §5 R-10, §6.4, §8 WP-1): the Wall-E half of
4
+ // the CTM hub-bridge lane.
5
+ //
6
+ // GET /api/hub-bridge/status -> { role, hubs, poolMembers } (R-10's pool probe)
7
+ // POST /api/hub-bridge/call <- { callerSessionId, op, args } (`session.read`)
8
+ //
9
+ // Base-URL/token resolution is the SAME one every other CTM call in this package uses
10
+ // (memory/ctm-context-client.js:12-17 + `_ctmToken`) — never a second hand-rolled client, and CTM
11
+ // stays the authority: this module runs nothing itself.
12
+ //
13
+ // R-1/R-2/R-2b (design-hub-bridge-box-session-awareness-2026-10-31.html §5, §6.3, §8 WP-0): the
14
+ // visibility gate is per SESSION, not per pool. Every `hub_*` tool is listed when this session has a
15
+ // creator-hub record on the box CTM (`status.session.hubInstanceId`) or the CTM reports
16
+ // `role === 'box'`; it is hidden only when CTM answers, reports another role, and reports
17
+ // `session: null`. `poolMembers` gates NOTHING (a real box reports poolMembers: 0 while connected).
18
+ // An unreachable or older CTM lists the tools (R-2): a call then answers the named `hub_unreachable`,
19
+ // so the served contract stays independent of whether a CTM happens to be running (hermetic tests).
20
+ //
21
+ // A "visible" answer is final for the process; a "hidden" one is re-checked at most once per
22
+ // HIDE_RECHECK_MS (R-2b), so a hub link that opens later is seen within one mesh tick.
23
+ //
24
+ // WP-2/WP-3: the same memoised probe feeds `boxContext()` (the "where you run" block) and, on stdio
25
+ // only, the re-probe timer that emits notifications/tools/list_changed once hidden -> visible.
26
+
27
+ // The ONE base-URL resolution (ctm-context-client.js:12-17). Reused, not re-implemented: a second
28
+ // copy is exactly how CTM_PORT/DEV_CTM_PORT drift apart.
29
+ const { resolveApiBaseUrl } = require('./ctm-context-client');
30
+
31
+ const HUB_TOOL_PREFIX = 'hub_';
32
+ const DEFAULT_STATUS_TIMEOUT_MS = 1500;
33
+ const DEFAULT_DEADLINE_MS = 30000;
34
+ // R-2b: the re-check interval equals CTM's mesh reconcile interval (coding-task-manager
35
+ // lib/hub-bridge-mesh.js MESH_INTERVAL_MS), so a hub link that just opened is seen within one tick.
36
+ const HIDE_RECHECK_MS = 30000;
37
+ // R-8: the box-side deadline bounds the bridge call; the HTTP client waits a fixed 2 s longer so a
38
+ // deadline exhaustion is reported by the BOX (`hub_unreachable`, message 'deadline') rather than by
39
+ // a client-side abort that cannot say what happened.
40
+ const DEADLINE_GRACE_MS = 2000;
41
+ // R-8: exec may run 120 s on the hub; its bridge deadline is that plus the 2 s grace, so the box
42
+ // never gives up on a command CTM is still allowed to finish (EXEC_MAX_TIMEOUT_MS + 2000).
43
+ const EXEC_MAX_TIMEOUT_MS = 120000;
44
+ const EXEC_DEFAULT_TIMEOUT_MS = 30000;
45
+ const MAX_DEADLINE_MS = EXEC_MAX_TIMEOUT_MS + DEADLINE_GRACE_MS;
46
+ // WP-3 (§6.4/§8): a hub MCP call may spawn a server and do a browser round-trip, so its bridge
47
+ // deadline is longer than the read family's 20 s — but 25 s + the client's 2 s grace stays under the
48
+ // stdio proxy's 30 s default (bin/walle-mcp-stdio.js, not edited).
49
+ const MCP_DEADLINE_MS = 25000;
50
+
51
+ // R-2b (WP-0): the probe memo, keyed by session id (a stdio proxy has exactly one). Each entry is
52
+ // `{ promise, at, visible, hide, status }`; `_resetForTests()` clears the whole map. A separate map
53
+ // holds the stdio-only re-probe timers (WP-3) so a second initialize cannot arm a second interval.
54
+ const _memos = new Map();
55
+ const _listChangedTimers = new Map();
56
+
57
+ function _token(env) {
58
+ return env.CTM_TOKEN || env.WALLE_CTM_TOKEN || env.CTM_API_TOKEN;
59
+ }
60
+
61
+ async function requestJson(url, { method = 'GET', body, timeoutMs = DEFAULT_STATUS_TIMEOUT_MS, token } = {}) {
62
+ const controller = new AbortController();
63
+ const timer = setTimeout(() => controller.abort(), Math.max(1, Number(timeoutMs) || DEFAULT_STATUS_TIMEOUT_MS));
64
+ try {
65
+ const headers = { Accept: 'application/json' };
66
+ let payload;
67
+ if (body !== undefined) {
68
+ headers['Content-Type'] = 'application/json';
69
+ payload = JSON.stringify(body);
70
+ }
71
+ if (token) headers.Authorization = `Bearer ${token}`;
72
+ const response = await fetch(url, { method, headers, body: payload, signal: controller.signal });
73
+ const text = await response.text();
74
+ return text ? JSON.parse(text) : {};
75
+ } finally {
76
+ clearTimeout(timer);
77
+ }
78
+ }
79
+
80
+ /**
81
+ * `GET /api/hub-bridge/status[?session=<id>]`, or null when CTM is unreachable. Never throws.
82
+ * R-1: the optional `session` query asks CTM for THIS session's creator-hub record, which is the
83
+ * per-session fact the visibility gate reads.
84
+ */
85
+ async function fetchStatus({ env = process.env, timeoutMs = DEFAULT_STATUS_TIMEOUT_MS, sessionId = '' } = {}) {
86
+ const baseUrl = resolveApiBaseUrl(env);
87
+ if (!baseUrl) return null;
88
+ const id = String(sessionId == null ? '' : sessionId).trim();
89
+ const query = id ? `?session=${encodeURIComponent(id)}` : '';
90
+ try {
91
+ return await requestJson(`${baseUrl}/api/hub-bridge/status${query}`, { timeoutMs, token: _token(env) });
92
+ } catch {
93
+ // REPORT: an unreachable or older (404) CTM answers null; R-2 then LISTS the hub_* tools (see
94
+ // resolveHideTools) and a call answers the named hub_unreachable instead.
95
+ return null;
96
+ }
97
+ }
98
+
99
+ /**
100
+ * `POST /api/hub-bridge/call`. Resolves the bridge body verbatim — `{ok:true, result}` or
101
+ * `{ok:false, error:{code,message}}` — and NEVER throws: a transport failure is the same named
102
+ * `hub_unreachable` a bridge refusal is, so the caller has exactly one error shape to handle.
103
+ */
104
+ async function callHubBridge({ callerSessionId, op, args, machine, deadlineMs, env = process.env } = {}) {
105
+ const baseUrl = resolveApiBaseUrl(env);
106
+ if (!baseUrl) {
107
+ return { ok: false, error: { code: 'hub_unreachable', message: 'ctm_api_unavailable: CTM base URL not resolvable' } };
108
+ }
109
+ const bounded = Math.min(Math.max(1, Number(deadlineMs) || DEFAULT_DEADLINE_MS), MAX_DEADLINE_MS);
110
+ const wanted = String(machine == null ? '' : machine).trim();
111
+ try {
112
+ const body = await requestJson(`${baseUrl}/api/hub-bridge/call`, {
113
+ method: 'POST',
114
+ body: {
115
+ callerSessionId: String(callerSessionId || ''),
116
+ op: String(op || ''),
117
+ args: args === undefined ? {} : args,
118
+ // WP-5 (R-12): the target machine is a TOP-LEVEL field of the bridge body (an instance id or
119
+ // a display name) — never part of `op`'s args. Omitted entirely when unnamed, so the bridge's
120
+ // own default (the machine driving this session) applies.
121
+ ...(wanted ? { machine: wanted } : {}),
122
+ deadlineMs: bounded,
123
+ },
124
+ timeoutMs: bounded + DEADLINE_GRACE_MS,
125
+ token: _token(env),
126
+ });
127
+ if (!body || typeof body !== 'object' || typeof body.ok !== 'boolean') {
128
+ return { ok: false, error: { code: 'hub_unreachable', message: 'hub bridge returned an unexpected body' } };
129
+ }
130
+ return body;
131
+ } catch (error) {
132
+ return { ok: false, error: { code: 'hub_unreachable', message: String((error && error.message) || error) } };
133
+ }
134
+ }
135
+
136
+ /**
137
+ * R-1/R-2: is this session a BOX session (so every `hub_*` tool is listed)? True when CTM reports a
138
+ * creator-hub record for the session (`session.hubInstanceId`) or `role === 'box'`; also true for the
139
+ * UNKNOWN case (status null/non-object or with no `role` key, R-2), because listing is the safe
140
+ * failure — a call on a machine that cannot reach a hub answers the named `hub_unreachable`.
141
+ * `poolMembers` is never consulted.
142
+ */
143
+ function isBoxSession(status) {
144
+ if (!status || typeof status !== 'object') return true;
145
+ if (!Object.prototype.hasOwnProperty.call(status, 'role')) return true;
146
+ const sessionHub = status.session && status.session.hubInstanceId;
147
+ return Boolean(sessionHub) || status.role === 'box';
148
+ }
149
+
150
+ /**
151
+ * R-1/R-2: `{ hide, status }` for one probe. `hide` names every `hub_*` tool iff this is NOT a box
152
+ * session; `status` is the raw answer (null when CTM is unreachable), so `boxContext()` can read the
153
+ * display names from the SAME probe. Never throws (fetchStatus already answers null on any fault).
154
+ */
155
+ async function resolveHideTools({ tools = [], env = process.env, timeoutMs = DEFAULT_STATUS_TIMEOUT_MS, sessionId = '' } = {}) {
156
+ const status = await fetchStatus({ env, timeoutMs, sessionId });
157
+ if (isBoxSession(status)) return { hide: new Set(), status };
158
+ const hide = new Set((Array.isArray(tools) ? tools : [])
159
+ .map((tool) => String((tool && tool.name) || ''))
160
+ .filter((name) => name.startsWith(HUB_TOOL_PREFIX)));
161
+ return { hide, status };
162
+ }
163
+
164
+ /** The memoised probe for one session: shared in-flight probe, one per HIDE_RECHECK_MS while hidden. */
165
+ function _probe({ sessionId = '', tools, env = process.env, now = Date.now, timeoutMs = DEFAULT_STATUS_TIMEOUT_MS } = {}) {
166
+ const key = String(sessionId == null ? '' : sessionId);
167
+ const clock = typeof now === 'function' ? now : () => now;
168
+ const existing = _memos.get(key);
169
+ if (existing) {
170
+ // R-2b: a visible result is FINAL; a hidden one is reused until HIDE_RECHECK_MS has passed
171
+ // (concurrent callers therefore share the one in-flight probe — T-7).
172
+ if (existing.visible || clock() - existing.at < HIDE_RECHECK_MS) return existing.promise;
173
+ }
174
+ const entry = { at: clock(), visible: false, hide: new Set(), status: null, promise: null };
175
+ entry.promise = resolveHideTools({ tools: tools || require('../mcp-contract').MCP_TOOLS, env, timeoutMs, sessionId: key })
176
+ .then(({ hide, status }) => {
177
+ entry.hide = hide;
178
+ entry.status = status;
179
+ entry.visible = hide.size === 0;
180
+ return entry;
181
+ })
182
+ .catch(() => entry); // expected: resolveHideTools never rejects; a fault lists the tools
183
+ _memos.set(key, entry);
184
+ return entry.promise;
185
+ }
186
+
187
+ /** The set of tool names to hide from tools/list for this session (R-1/R-2, memoised per R-2b). */
188
+ function hideTools(options = {}) {
189
+ return _probe(options).then((entry) => entry.hide);
190
+ }
191
+
192
+ /**
193
+ * R-4 (WP-2): `null | { boxName, hubName, hubConnected }` read from the SAME memoised probe as
194
+ * `hideTools` — non-null only for a box session whose hub name CTM actually reported.
195
+ */
196
+ async function boxContext(options = {}) {
197
+ const entry = await _probe(options);
198
+ const status = entry.status;
199
+ // R-2: isBoxSession is TRUE for the unknown case (null/no `role`), but an unknown status carries
200
+ // no names to put in the block, so the context is null there as well.
201
+ if (!status || typeof status !== 'object') return null;
202
+ if (!isBoxSession(status)) return null;
203
+ const session = status.session && status.session.hubInstanceId ? status.session : null;
204
+ const hubs = Array.isArray(status.hubs) ? status.hubs : [];
205
+ const hub = session || (hubs.length === 1 ? hubs[0] : null);
206
+ const hubName = String((hub && hub.hubDisplayName) || '').trim();
207
+ if (!hubName) return null;
208
+ const self = status.self && typeof status.self === 'object' ? status.self : null;
209
+ return {
210
+ boxName: String((self && (self.displayName || self.instanceId)) || '').trim(),
211
+ hubName,
212
+ hubConnected: hub.connected === undefined ? undefined : Boolean(hub.connected),
213
+ };
214
+ }
215
+
216
+ /** The stdio proxy's session key (R-1): the CTM session this proxy was spawned for, if any. */
217
+ function proxySessionId(env = process.env) {
218
+ return String(env.CTM_SESSION_ID || '').trim();
219
+ }
220
+
221
+ function _clearListChangedWatcher(key) {
222
+ const entry = _listChangedTimers.get(key);
223
+ if (!entry) return;
224
+ (entry.clearIntervalFn || clearInterval)(entry.timer);
225
+ _listChangedTimers.delete(key);
226
+ }
227
+
228
+ /**
229
+ * R-6 (WP-3, stdio only): while this session's set is hidden, re-probe on ONE unref'd
230
+ * HIDE_RECHECK_MS timer. On hidden -> visible, write exactly one
231
+ * `notifications/tools/list_changed` through the proxy's own `writeMessage` and stop. It never
232
+ * emits visible -> hidden (a visible answer is final, so the timer is gone by then).
233
+ */
234
+ function _armListChangedWatcher({ sessionId = '', env = process.env, writeMessage, now = Date.now, setIntervalFn = setInterval, clearIntervalFn = clearInterval } = {}) {
235
+ const key = String(sessionId == null ? '' : sessionId);
236
+ if (typeof writeMessage !== 'function' || _listChangedTimers.has(key)) return;
237
+ const timer = setIntervalFn(async () => {
238
+ if (!_listChangedTimers.has(key)) return; // a stale fire of a cleared interval writes nothing
239
+ const hide = await hideTools({ sessionId: key, env, now });
240
+ if (!hide || hide.size > 0) return;
241
+ _clearListChangedWatcher(key);
242
+ writeMessage({ jsonrpc: '2.0', method: 'notifications/tools/list_changed' });
243
+ }, HIDE_RECHECK_MS);
244
+ if (typeof timer === 'object' && timer && typeof timer.unref === 'function') timer.unref();
245
+ _listChangedTimers.set(key, { timer, clearIntervalFn });
246
+ }
247
+
248
+ /**
249
+ * The proxy's initialize options (R-4/R-6): the "where you run" block from the SAME probe as the
250
+ * visibility gate, the listChanged capability, and — only while hidden — the re-probe timer. Kept
251
+ * here (not in bin/walle-mcp-stdio.js) because that file is at a frozen file-size ceiling.
252
+ */
253
+ async function proxyInitializeOptions({ writeMessage, env = process.env, sessionId = proxySessionId(env), now = Date.now, setIntervalFn = setInterval, clearIntervalFn = clearInterval } = {}) {
254
+ const [hubContext, hide] = await Promise.all([
255
+ boxContext({ sessionId, env, now }),
256
+ hideTools({ sessionId, env, now }),
257
+ ]);
258
+ if (hide && hide.size > 0) _armListChangedWatcher({ sessionId, env, writeMessage, now, setIntervalFn, clearIntervalFn });
259
+ return { hubContext, listChanged: true };
260
+ }
261
+
262
+ /**
263
+ * The ONE helper bin/walle-mcp-stdio.js calls per control message (its own option lines are at a
264
+ * frozen ceiling): undefined for anything it does not own, `{hideTools}` for tools/list, and
265
+ * `{leadGuidance, hubContext, listChanged}` for initialize. `leadGuidance` is a lazy thunk so a
266
+ * tools/list never performs the daemon guidance read.
267
+ */
268
+ async function controlPlaneOptions(message, { writeMessage, leadGuidance, env = process.env, sessionId = proxySessionId(env), now = Date.now, setIntervalFn = setInterval, clearIntervalFn = clearInterval } = {}) {
269
+ if (!message || Array.isArray(message)) return undefined;
270
+ if (message.method === 'initialize') {
271
+ const guidance = typeof leadGuidance === 'function' ? await leadGuidance() : (leadGuidance || null);
272
+ const init = await proxyInitializeOptions({ writeMessage, env, sessionId, now, setIntervalFn, clearIntervalFn });
273
+ return guidance ? { ...init, leadGuidance: guidance } : init;
274
+ }
275
+ if (message.method === 'tools/list') return { hideTools: await hideTools({ sessionId, env, now }) };
276
+ return undefined;
277
+ }
278
+
279
+ /** §6.4: `hub_exec`'s own timeout — default 30 s, hard max 120 s (CTM's exec ceiling). */
280
+ function clampExecTimeoutMs(value) {
281
+ const n = Number(value);
282
+ if (!Number.isFinite(n) || n <= 0) return EXEC_DEFAULT_TIMEOUT_MS;
283
+ return Math.min(EXEC_MAX_TIMEOUT_MS, Math.max(1, Math.trunc(n)));
284
+ }
285
+
286
+ // tool name -> (tool args) => { op, opArgs, deadlineMs }. `session.read`'s 20 s deadline + the 2 s
287
+ // grace stays inside the stdio proxy's 30 s default; exec's deadline outlasts CTM's own exec
288
+ // timeout by the grace, so a command that hits it is reported by CTM (`timed_out`), never by a
289
+ // local abort.
290
+ //
291
+ // WP-5 (R-12): `machine` is NOT an op arg — it is a top-level field of the bridge POST body (see
292
+ // callHubBridge) and picks which connected hub answers. The op's args describe the WORK, the machine
293
+ // describes the TARGET, and CTM never has to dig a routing field out of an op's payload.
294
+ const HUB_TOOL_OPS = Object.freeze({
295
+ hub_session_read: (a) => ({
296
+ op: 'session.read',
297
+ opArgs: { ref: a.ref, query: a.query, max_messages: a.max_messages },
298
+ deadlineMs: Number(a.deadline_ms) || 20000,
299
+ }),
300
+ hub_read_file: (a) => ({
301
+ op: 'fs.read',
302
+ opArgs: { path: a.path, offset: a.offset, limit: a.limit },
303
+ deadlineMs: Number(a.deadline_ms) || DEFAULT_DEADLINE_MS,
304
+ }),
305
+ hub_list: (a) => ({
306
+ op: 'fs.list',
307
+ opArgs: { path: a.path, glob: a.glob },
308
+ deadlineMs: Number(a.deadline_ms) || DEFAULT_DEADLINE_MS,
309
+ }),
310
+ hub_exec: (a) => {
311
+ const timeoutMs = clampExecTimeoutMs(a.timeout_ms);
312
+ return {
313
+ op: 'exec',
314
+ opArgs: { command: a.command, cwd: a.cwd, timeout_ms: timeoutMs },
315
+ deadlineMs: timeoutMs + DEADLINE_GRACE_MS,
316
+ };
317
+ },
318
+ // WP-3 (§3 P-2, D-4, §6.4, §8 WP-3): drive a HUB-side browser MCP server (tier `browser`). The
319
+ // op names are CTM's (`mcp.list` / `mcp.call`); CTM owns the allowlist, the client lifecycle and
320
+ // every refusal code, and this table stays the ONE tool -> op mapping.
321
+ hub_mcp_list_tools: (a) => ({
322
+ op: 'mcp.list',
323
+ opArgs: { server: a.server },
324
+ deadlineMs: MCP_DEADLINE_MS,
325
+ }),
326
+ hub_mcp_call: (a) => ({
327
+ op: 'mcp.call',
328
+ opArgs: { server: a.server, tool: a.tool, arguments: a.arguments },
329
+ deadlineMs: MCP_DEADLINE_MS,
330
+ }),
331
+ });
332
+
333
+ /**
334
+ * Run one `hub_*` tool: map it to its op, call the bridge, and either return the bridge body or
335
+ * throw the NAMED refusal (R-7: never a local fallback). A `hub_grant_required` refusal tells the
336
+ * agent exactly which tier to ask the user for (R-4/R-5), and to retry once, not in a loop.
337
+ */
338
+ async function runHubTool(name, args = {}, callerSessionId = '') {
339
+ const toOp = HUB_TOOL_OPS[name];
340
+ if (!toOp) throw Object.assign(new Error(`unknown hub tool: ${name}`), { code: 'unknown_hub_tool' });
341
+ if (!callerSessionId) {
342
+ throw Object.assign(
343
+ new Error('caller_unresolved: no caller session id — pass `session`, or run under a CTM session'),
344
+ { code: 'caller_unresolved' },
345
+ );
346
+ }
347
+ const { op, opArgs, deadlineMs } = toOp(args || {});
348
+ // Looked up on module.exports so a test can stub the transport (tests/hub-bridge-tool.test.js).
349
+ // WP-5 (R-12): the tool's `machine` rides the bridge body top-level, not the op args.
350
+ const bridge = await module.exports.callHubBridge({ callerSessionId, op, args: opArgs, machine: args && args.machine, deadlineMs });
351
+ if (!bridge.ok) {
352
+ const code = String((bridge.error && bridge.error.code) || 'hub_unreachable');
353
+ const message = String((bridge.error && bridge.error.message) || code);
354
+ const hint = code === 'hub_grant_required'
355
+ ? ` Ask the user to approve '${String((bridge.error && bridge.error.tier) || 'exec')}' for this session`
356
+ + ' in CTM (session toolbar or Machines page), then retry once.'
357
+ : '';
358
+ throw Object.assign(new Error(`${code}: ${message}${hint}`), { code, body: bridge });
359
+ }
360
+ return bridge;
361
+ }
362
+
363
+ function _resetForTests() {
364
+ _memos.clear();
365
+ for (const key of [..._listChangedTimers.keys()]) _clearListChangedWatcher(key);
366
+ }
367
+
368
+ module.exports = {
369
+ HUB_TOOL_PREFIX,
370
+ DEFAULT_STATUS_TIMEOUT_MS,
371
+ HIDE_RECHECK_MS,
372
+ HUB_TOOL_OPS,
373
+ MAX_DEADLINE_MS,
374
+ MCP_DEADLINE_MS,
375
+ callHubBridge,
376
+ clampExecTimeoutMs,
377
+ runHubTool,
378
+ fetchStatus,
379
+ isBoxSession,
380
+ hideTools,
381
+ boxContext,
382
+ proxySessionId,
383
+ proxyInitializeOptions,
384
+ controlPlaneOptions,
385
+ requestJson,
386
+ resolveHideTools,
387
+ _resetForTests,
388
+ };
@@ -0,0 +1,56 @@
1
+ 'use strict';
2
+
3
+ // WP-5 (docs/cross-machine-session-context-2026-10-31.html §5 R-2/R-6, §8 WP-5): transcript
4
+ // replication (shared-memory WP-5) writes a peer's items into the CTM conversation tables under a
5
+ // synthetic ingest cursor `peer-sync:<origin instance id>:<session id>`
6
+ // (coding-task-manager/lib/conversation-sync.js:45, :262-263). The prefix is re-typed here rather than
7
+ // required, because that module pulls the whole CTM store graph into Wall-E. A session whose cursors
8
+ // are ONLY such rows is a REPLICA of another machine's session: memory/ctm-session-context.js labels
9
+ // it `replica_origin` and the pool serves it only when its owner is unreachable
10
+ // (coding-task-manager/lib/session-context-locator.js). Kept out of ctm-session-context.js, which sits
11
+ // at its file-size ceiling.
12
+ const REPLICA_CURSOR_PREFIX = 'peer-sync:';
13
+ const REPLICA_CURSOR_TABLE = 'conversation_ingest_cursor_v2';
14
+
15
+ /**
16
+ * WP-5/R-6: the machine a replicated session came from, or '' when this machine ingested the session
17
+ * itself. ONE query per call (`conversation_ingest_cursor_v2` holds one row per source — thousands,
18
+ * not millions), then both checks in JS: the origin named by its `peer-sync:<origin>:<session>` rows,
19
+ * and whether ANY non-peer cursor ends in `:<session>` (which proves local ingestion, whatever else is
20
+ * present). '' for an unknown id, for a session with no cursor at all, and when the table is absent —
21
+ * today's state on every machine, where the answer is byte-identical to pre-replication behaviour.
22
+ */
23
+ function createReplicaOriginLookup(db, hasTable) {
24
+ let sources = null;
25
+ const load = () => {
26
+ if (sources) return sources;
27
+ if (!hasTable(db, REPLICA_CURSOR_TABLE)) return null;
28
+ try {
29
+ sources = db.prepare(`SELECT source_id FROM ${REPLICA_CURSOR_TABLE}`).all() || [];
30
+ } catch {
31
+ /* expected: the cursor table is CTM-owned schema this module only reads — a renamed column
32
+ throws SQLITE_ERROR past the hasTable() guard; the session then reads as non-replica */
33
+ sources = [];
34
+ }
35
+ return sources;
36
+ };
37
+ return function replicaOriginOf(ids) {
38
+ const wanted = (Array.isArray(ids) ? ids : [ids])
39
+ .map((id) => String(id == null ? '' : id).trim())
40
+ .filter(Boolean);
41
+ const rows = wanted.length ? load() : null;
42
+ if (!rows) return '';
43
+ let origin = '';
44
+ for (const row of rows) {
45
+ const sourceId = String((row && row.source_id) || '');
46
+ const split = sourceId.lastIndexOf(':');
47
+ if (split <= 0) continue;
48
+ if (!wanted.includes(sourceId.slice(split + 1))) continue;
49
+ if (!sourceId.startsWith(REPLICA_CURSOR_PREFIX)) return ''; // a cursor of ours: not a replica
50
+ origin = sourceId.slice(REPLICA_CURSOR_PREFIX.length, split);
51
+ }
52
+ return origin;
53
+ };
54
+ }
55
+
56
+ module.exports = { createReplicaOriginLookup, REPLICA_CURSOR_PREFIX, REPLICA_CURSOR_TABLE };
@@ -0,0 +1,135 @@
1
+ 'use strict';
2
+
3
+ // WP-4 (docs/cross-machine-session-context-2026-10-31.html §5 R-7/R-8, §6.4, §8 WP-4) — the two
4
+ // POOL legs a Wall-E tool takes when the caller session runs on a BOX driven by a hub:
5
+ //
6
+ // sessionContextViaHub (WP-3) a `walle_get_session` miss asks the hub over the bridge; the
7
+ // hub's answer (with its own `location`/`searched`) becomes the result.
8
+ // searchSessionsAcrossPool (WP-4) `walle_search_sessions` runs the LOCAL CTM search (`scope=pool`,
9
+ // so CTM itself fans out across its own pool) and, when the caller is a box
10
+ // session, the hub's `session.search` op IN PARALLEL; the two lists are
11
+ // merged by the ONE RRF implementation (`fusePoolResults`) and the answer
12
+ // names every machine it heard from.
13
+ //
14
+ // Both legs share the same box test (`fetchStatus` -> `isBoxSession`) and the same bridge transport
15
+ // (`hubBridgeClient.callHubBridge`, looked up on the module object at call time so a test can stub
16
+ // it). This module lives beside wall-e/mcp-server.js rather than inside it: that file is under a
17
+ // file-size ratchet and keeps only the call sites.
18
+
19
+ const ctmContextClient = require('./ctm-context-client');
20
+ const hubBridgeClient = require('./hub-bridge-client');
21
+ const { callerLandingSessionId } = require('../lib/landing-mcp-tools');
22
+ // R-7/D-6: the ONE reciprocal-rank-fusion implementation, shared with CTM's locator (the precedent
23
+ // for a Wall-E -> CTM lib require is wall-e/session-files.js requiring lib/transcript-source).
24
+ const { fusePoolResults } = require('../../coding-task-manager/lib/session-context-locator');
25
+
26
+ // R-8: the same bounded bridge deadline the WP-3 context leg uses (lib/hub-bridge-client MAX_DEADLINE_MS).
27
+ const HUB_DEADLINE_MS = 20000;
28
+
29
+ /** The hub that drives this session, by display name — the `searched`/`machines` label (R-5). */
30
+ function hubDisplayName(status) {
31
+ return String((((status || {}).session || ((status || {}).hubs || [])[0] || {}).hubDisplayName) || 'hub');
32
+ }
33
+
34
+ /** THIS box, as CTM reports it in the bridge status (`status.self`). */
35
+ function selfMachine(status) {
36
+ const self = (status && status.self) || {};
37
+ return { machine_id: String(self.instanceId || ''), machine_name: String(self.displayName || '') };
38
+ }
39
+
40
+ /**
41
+ * R-7: the LOCAL CTM search, run with `scope=pool` so CTM fans its own pool out and merges by RRF.
42
+ * Never throws — a search that could not run is a named `ok:false` result, exactly like the other
43
+ * `safeCtm*` readers beside the tool dispatch.
44
+ */
45
+ async function safeSearchLocal({ query, limit }) {
46
+ try {
47
+ return await ctmContextClient.searchSessions({ query, limit, scope: 'pool' });
48
+ } catch (err) {
49
+ return { ok: false, source: 'ctm-db', results: [], reason: err.message };
50
+ }
51
+ }
52
+
53
+ /**
54
+ * WP-3 (design §6.4, R-1/R-3): a session this machine cannot read may live on the hub that drives
55
+ * this box session — ONE bridge call asks it, and its answer (with `location`/`searched`) is returned;
56
+ * a dead hub keeps the local empty envelope and names the hub in `searched`.
57
+ */
58
+ async function sessionContextViaHub(local, args, context) {
59
+ const callerSessionId = local && local.found === false ? callerLandingSessionId(args, context) : '';
60
+ if (!callerSessionId) return local;
61
+ const status = await hubBridgeClient.fetchStatus({ sessionId: callerSessionId });
62
+ if (!hubBridgeClient.isBoxSession(status)) return local;
63
+ const hubName = hubDisplayName(status);
64
+ const hub = await hubBridgeClient.callHubBridge({
65
+ callerSessionId, op: 'session.context', deadlineMs: HUB_DEADLINE_MS,
66
+ args: { ref: String(args.ref || args.session_id || args.source_id || ''), turn_limit: args.turn_limit, byte_limit: args.byte_limit, cursor: args.cursor, direction: args.direction },
67
+ });
68
+ const miss = Array.isArray(local.searched) ? local.searched : [];
69
+ if (!hub.ok) return { ...local, searched: [...miss, { machine_name: hubName, status: String((hub.error && hub.error.code) || 'hub_unreachable') }] };
70
+ const result = hub.result && typeof hub.result === 'object' ? hub.result : {};
71
+ return { ...result, searched: [...(Array.isArray(result.searched) ? result.searched : []), ...miss] };
72
+ }
73
+
74
+ /**
75
+ * R-7 (design §6.4): `walle_search_sessions`'s CTM leg. The local CTM search starts immediately
76
+ * (`scope=pool`); when the caller session is a BOX session, the hub's `session.search` op runs IN
77
+ * PARALLEL and the two ranked lists are merged by `fusePoolResults`. The local result object is the
78
+ * base (`ok`/`source`/`reason`/… feed the tool's `sources` block); only `results` and `machines` are
79
+ * replaced. Rows keep the `machine_id`/`machine_name` they were ranked with — a row that came from
80
+ * another box through the hub is still tagged with THAT box, never with the hub.
81
+ */
82
+ async function searchSessionsAcrossPool({ query, limit, args = {}, context } = {}) {
83
+ const callerSessionId = callerLandingSessionId(args, context);
84
+ const localPromise = safeSearchLocal({ query, limit });
85
+ // R-2: the box test is the same probe the WP-3 context leg makes — no caller session, no probe.
86
+ const status = callerSessionId ? await hubBridgeClient.fetchStatus({ sessionId: callerSessionId }) : null;
87
+ const isBox = Boolean(callerSessionId) && hubBridgeClient.isBoxSession(status);
88
+ const hubPromise = isBox
89
+ ? hubBridgeClient.callHubBridge({
90
+ callerSessionId, op: 'session.search', args: { query, limit }, deadlineMs: HUB_DEADLINE_MS,
91
+ })
92
+ : null;
93
+ const local = await localPromise;
94
+ const localRows = Array.isArray(local && local.results) ? local.results : [];
95
+ if (!isBox) {
96
+ // A hub caller's CTM already fanned out (its own route merged the pool and labelled every row),
97
+ // so the answer is relayed as is — `machines` included when CTM sent one.
98
+ return { ...local, machines: Array.isArray(local && local.machines) ? local.machines : [] };
99
+ }
100
+
101
+ const self = selfMachine(status);
102
+ const hubName = hubDisplayName(status);
103
+ const selfEntry = { ...self, status: 'ok', count: localRows.length };
104
+ const lists = [{ ...self, owner: true, rows: localRows }];
105
+ const hub = await hubPromise;
106
+ let machines = [selfEntry];
107
+ if (!hub || hub.ok !== true) {
108
+ // R-7 partial results: the hub's failure is a NAMED per-machine status, never a lost local answer.
109
+ machines = [
110
+ selfEntry,
111
+ { machine_name: hubName, status: String((hub && hub.error && hub.error.code) || 'hub_unreachable') },
112
+ ];
113
+ } else {
114
+ const result = hub.result && typeof hub.result === 'object' ? hub.result : {};
115
+ const hubMachines = Array.isArray(result.machines) ? result.machines : [];
116
+ // The hub's list is the hub's own view (its self entry first); rows it carries already name the
117
+ // machine each came from, so the hub's id is only the fallback label for an unlabelled row.
118
+ const hubSelf = hubMachines[0] || {};
119
+ lists.push({
120
+ machine_id: String(hubSelf.machine_id || ''),
121
+ machine_name: String(hubSelf.machine_name || hubName),
122
+ owner: false,
123
+ rows: Array.isArray(result.results) ? result.results : [],
124
+ });
125
+ machines = [...hubMachines, selfEntry];
126
+ }
127
+ return { ...local, results: fusePoolResults(lists, { limit }), machines };
128
+ }
129
+
130
+ module.exports = {
131
+ HUB_DEADLINE_MS,
132
+ sessionContextViaHub,
133
+ searchSessionsAcrossPool,
134
+ safeSearchLocal,
135
+ };
@@ -2,6 +2,8 @@
2
2
 
3
3
  const os = require('node:os');
4
4
  const { getSlackReadToolNames, slackReadToolsEnabled, getIMessageReadToolNames, imessageReadToolsEnabled } = require('../mcp-tool-registry');
5
+ // WP-2 (D-8): name the exec an FDA grant must actually carry, not a fixed bundle path.
6
+ const { fdaTargetForExec } = require('../../shared/macos-fda-target');
5
7
 
6
8
  // surface-redesign WP-5a (D-12): the `connections` record — not the presence of a
7
9
  // token file — is the authority for "is this connected?". The reader comes from
@@ -222,6 +224,9 @@ function inspectIMessage(opts = {}, warnings, errors, lane = 'chat') {
222
224
  const fs = opts.fs || require('fs');
223
225
  const reader = require('../lib/imessage/chat-db-reader');
224
226
  const dbPath = opts.imessageDbPath || reader.DEFAULT_DB_PATH;
227
+ // D-8: the FDA grant is charged to the exec that runs (launchd's job exec / its children),
228
+ // e.g. `~/.walle/notarized-node/bin/node` on a host with no Wall-E.app bundle.
229
+ const fdaTarget = fdaTargetForExec(opts.execPath || process.execPath, opts.fdaDeps);
225
230
  const coding = lane === 'coding';
226
231
  const readTools = coding ? getIMessageReadToolNames(env) : ['imessage_search', 'imessage_read_chat'];
227
232
  try {
@@ -248,14 +253,14 @@ function inspectIMessage(opts = {}, warnings, errors, lane = 'chat') {
248
253
  const available = connected && flagOn && lastIngestAt !== null;
249
254
  const note = platform !== 'darwin' ? 'iMessage is macOS-only'
250
255
  : !dbPresent ? 'Messages database not found at ~/Library/Messages/chat.db'
251
- : !fullDiskAccess ? 'Grant Full Disk Access to the Wall-E node binary in System Settings → Privacy & Security → Full Disk Access, then restart Wall-E'
256
+ : !fullDiskAccess ? `Grant Full Disk Access to ${fdaTarget ? fdaTarget.path : 'the Wall-E process binary'} in System Settings → Privacy & Security → Full Disk Access, then restart Wall-E`
252
257
  : !flagOn ? 'iMessage read tools disabled in the coding lane (WALLE_MCP_IMESSAGE_READ=0)'
253
258
  : lastIngestAt === null ? 'iMessage ingest has not run yet'
254
259
  : `iMessage mirror current as of ${lastIngestAt}`;
255
- return { available, connected, dbPresent, fullDiskAccess, lastIngestAt, maxRowid, readTools, writeTools: [], note };
260
+ return { available, connected, dbPresent, fullDiskAccess, fdaTarget, lastIngestAt, maxRowid, readTools, writeTools: [], note };
256
261
  } catch (err) {
257
262
  errors.push({ capability: 'imessage', message: err.message });
258
- return { available: false, connected: false, dbPresent: false, fullDiskAccess: false, lastIngestAt: null, maxRowid: null, readTools, writeTools: [], note: err.message };
263
+ return { available: false, connected: false, dbPresent: false, fullDiskAccess: false, fdaTarget: null, lastIngestAt: null, maxRowid: null, readTools, writeTools: [], note: err.message };
259
264
  } // HANDLE: capability report must never throw
260
265
  }
261
266
 
@@ -110,8 +110,10 @@ function isMcpAutoConfigDisabled() {
110
110
 
111
111
  async function autoConfigureWallEMcp() {
112
112
  if (isMcpAutoConfigDisabled()) return;
113
+ let failureMessage = (err) => err.message;
113
114
  try {
114
- const { ensureMcpIntegrations, getMcpAutoConfigSkipReason } = require('./lib/mcp-integration');
115
+ const { ensureMcpIntegrations, getMcpAutoConfigSkipReason, ensureFailureMessage } = require('./lib/mcp-integration');
116
+ failureMessage = ensureFailureMessage;
115
117
  if (getMcpAutoConfigSkipReason(process.env)) return;
116
118
  const results = await ensureMcpIntegrations(PORT);
117
119
  const changed = results.filter(r => r.kind === 'mcp_config' && (r.action === 'added' || r.action === 'updated'));
@@ -127,7 +129,7 @@ async function autoConfigureWallEMcp() {
127
129
  console.warn(`[wall-e] Wall-E MCP auto-config skipped ${errors.length} invalid config file(s): ${errors.map(r => r.tool).join(', ')}`);
128
130
  }
129
131
  } catch (err) {
130
- console.warn(`[wall-e] Wall-E MCP auto-config failed: ${err.message}`);
132
+ console.warn(`[wall-e] Wall-E MCP auto-config failed: ${failureMessage(err)}`);
131
133
  }
132
134
  }
133
135
 
@@ -9,7 +9,7 @@
9
9
  21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
10
10
  31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
11
11
  41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
12
- 51, 52, 53, 54, 55, 56
12
+ 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61
13
13
  ]
14
14
  },
15
15
  "walle": {
@@ -25,7 +25,7 @@
25
25
  71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
26
26
  81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96,
27
27
  97, 98, 99, 100,
28
- 101
28
+ 101, 102, 103, 104, 105
29
29
  ]
30
30
  }
31
31
  }