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
@@ -42,6 +42,9 @@ const MACHINE_HEADER = 'x-ctm-machine';
42
42
  const MACHINE_QUERY_PARAM = 'machine';
43
43
  const DEFAULT_TIMEOUT_MS = 10_000;
44
44
  const BODY_LIMIT_BYTES = 1024 * 1024;
45
+ // D-5 (§6.4): a box mid fleet-update is silent for a minute or two. A bundle the hub sent inside
46
+ // this window is what lets a 502 name the update instead of leaving the user with "unreachable".
47
+ const FLEET_UPDATE_MAX_AGE_MS = 10 * 60 * 1000;
45
48
 
46
49
  // The four routes this WP proxies (§9 S-9/S-10). Keyed `METHOD /pathname`, the same spelling the
47
50
  // lead passes as `{ route }`.
@@ -72,6 +75,39 @@ const PROXIED_ROUTES = Object.freeze([
72
75
  'GET /api/providers/login-state',
73
76
  'POST /api/providers/:id/login',
74
77
  'POST /api/providers/:id/login/code',
78
+ // WP-2 (docs/remote-box-model-access-2026-10-26.html §6.3, §5.1 R-4, §5.3 D-4): the picker's four
79
+ // lead-model calls for a MIRROR session are answered by the BOX that owns the session, through
80
+ // lib/remote-machine-catalog.js `forwardLeadModelRequest`, which names the REAL box path. Declared
81
+ // here so the box-routed route list stays the ONE inventory of what crosses the peer leg (the same
82
+ // `:id` spelling as the templated rows above).
83
+ 'GET /api/sessions/:id/lead-model/options',
84
+ 'POST /api/sessions/:id/lead-model',
85
+ 'GET /api/sessions/:id/lead-model/request',
86
+ 'DELETE /api/sessions/:id/lead-model/request',
87
+ // WP-3 (docs/remote-session-child-transcript-model-truth-and-restart-2026-10-27.html §5 D-4, §8
88
+ // WP-3): the Delegate pane's ladder read for a BOX-owned session is answered by the box that owns
89
+ // it, through lib/api/walle-bridge.js `handleProxy` → lib/remote-machine-catalog.js
90
+ // `forwardDelegatePrefsRequest` (a MIRROR lookup on the `ctm_session_id` query param, not a path
91
+ // id). Declared here so this list stays the ONE inventory of what crosses the peer leg.
92
+ 'GET /api/models/delegate-prefs',
93
+ // WP-1 (docs/remote-session-delegate-pick-write-forward-2026-10-30.html D-1/D-2/D-7): the same
94
+ // forward carries the two WRITES that set a rung of that box's own ladder — a pick saved on the
95
+ // hub's Wall-E is one the box never reads. Same peer leg, same `ctm_session_id` ownership lookup.
96
+ 'PUT /api/models/delegate-prefs',
97
+ 'DELETE /api/models/delegate-prefs/:scope',
98
+ // WP-0 (docs/recent-sessions-follow-computer-filter-2026-10-31.html §5 D-2, §6): with a box
99
+ // selected the Recent list, its server-side search and the single-click review transcript all
100
+ // come from that box — the same ?machine= + peer-bearer leg as every row above, so the hub never
101
+ // has to mirror a box's sessions. The box already answers /api/recent-sessions and admits the
102
+ // peer bearer for it (lib/auth-rules.js); the other two rows gain the flag in the same change.
103
+ 'GET /api/recent-sessions',
104
+ 'GET /api/sessions/search',
105
+ 'GET /api/session/messages',
106
+ // WP-4 (docs/fleet-session-lists-hub-search-2026-11-01.html §6.3, §9 S-15): the PE conversations
107
+ // list is fleet-wide (api-prompts.js wraps it with fleetRouted), so a stamped row's single-click
108
+ // view is answered by the BOX that owns the conversation — same `?machine=` + peer-bearer leg as
109
+ // the rows above, with lib/auth-rules.js admitting the peer bearer for both shapes.
110
+ 'GET /api/conversations/:id',
75
111
  ]);
76
112
 
77
113
  // S-10 mixed-request refusal: a create that names a REMOTE machine must carry the BOX-owned source
@@ -178,6 +214,63 @@ function _recencyScore(row, index) {
178
214
  return index ? -index : 0;
179
215
  }
180
216
 
217
+ // ---------------------------------------------------------------------------------------------
218
+ // WP-4 (D-5, §6.4): the failure trace and the "box is restarting for an update" name.
219
+ // ---------------------------------------------------------------------------------------------
220
+
221
+ /**
222
+ * D-5: ONE line per failed forward, so the next 02:20 has a record on the hub. `route`, `machine`
223
+ * and `status` are the only identifiers; `detail` is the refusal's own short reason. Headers,
224
+ * bodies and tokens are NEVER logged.
225
+ */
226
+ function _logProxyFailure(route, machine, status, detail, startedAt) {
227
+ const elapsedMs = Math.max(0, Date.now() - (Number(startedAt) || Date.now()));
228
+ console.warn(`[remote-proxy] ${route} machine=${machine} status=${status} detail=${_string(detail, 300)} elapsedMs=${elapsedMs}`);
229
+ }
230
+
231
+ /**
232
+ * D-5: the hub's persisted per-box fleet record -> `{ to, sentAt }` for a box the hub sent a
233
+ * bundle to inside the last 10 minutes, else null. Reads the normalized shape
234
+ * lib/fleet-hub.js `_normalizeHubState` produces: `{ inFlight: {instanceId, target, sentAt}|null,
235
+ * boxes: { [id]: { lastSentAt, attempts: { [target]: {sentAt, verdict, reason} } } } }`.
236
+ */
237
+ function _fleetUpdateFromHubState(state, instanceId, now = Date.now()) {
238
+ const id = _string(instanceId, 160);
239
+ if (!state || !id) return null;
240
+ const fresh = (sentAt) => Number.isFinite(sentAt) && sentAt > 0 && now - sentAt < FLEET_UPDATE_MAX_AGE_MS;
241
+ const inFlight = state.inFlight;
242
+ if (inFlight && _string(inFlight.instanceId, 160) === id && fresh(Number(inFlight.sentAt))) {
243
+ return { to: _string(inFlight.target, 200), sentAt: Number(inFlight.sentAt) };
244
+ }
245
+ const box = state.boxes && typeof state.boxes === 'object' ? state.boxes[id] : null;
246
+ const attempts = box && box.attempts && typeof box.attempts === 'object' ? box.attempts : null;
247
+ if (!attempts) return null;
248
+ let best = null;
249
+ for (const [target, attempt] of Object.entries(attempts)) {
250
+ if (!attempt || typeof attempt !== 'object') continue;
251
+ if (String(attempt.verdict || '') !== 'sent') continue;
252
+ const sentAt = Number(attempt.sentAt);
253
+ if (!fresh(sentAt)) continue;
254
+ if (!best || sentAt > best.sentAt) best = { to: _string(target, 200), sentAt };
255
+ }
256
+ return best;
257
+ }
258
+
259
+ /**
260
+ * D-5: the injected seam (`deps.fleetUpdateFor(instanceId)`), defaulting to the hub's own
261
+ * `<dataDir>/fleet-hub.json`. A lookup failure — no file, unreadable data dir, a throwing stub —
262
+ * is null and NOTHING else: naming an update may never change the proxy's own outcome.
263
+ */
264
+ async function _fleetUpdateFor(deps, instanceId) {
265
+ try {
266
+ if (typeof deps.fleetUpdateFor === 'function') return await deps.fleetUpdateFor(instanceId);
267
+ const state = await require('./fleet-hub').readHubState(require('./data-dir').getDataDir());
268
+ return _fleetUpdateFromHubState(state, instanceId, Date.now());
269
+ } catch {
270
+ return null; /* expected: no fleet record yet (or an unreadable data dir) — the proxy still answers 502 */
271
+ }
272
+ }
273
+
181
274
  /**
182
275
  * The routing machine id, or '' when this request is not machine-routed at all.
183
276
  * Query/header are consulted first so the common GET path never touches the request body.
@@ -197,6 +290,26 @@ function isMachineRouted(req, url) {
197
290
  return !!_machineFromRequest(req, _resolveUrl(req, url));
198
291
  }
199
292
 
293
+ /**
294
+ * WP-0 (docs/recent-sessions-follow-computer-filter-2026-10-31.html §5 D-2, §6): wrap a route
295
+ * handler so a picker request naming another machine (?machine= / X-CTM-Machine) is answered by
296
+ * that box and every other request is the unchanged local handler — the same shape
297
+ * lib/api/misc-ops.js's `_machineRouted` keeps for its own rows. Exported so lib/api/sessions.js
298
+ * can wrap its three rows without a fourth private copy (and without growing past its file-size
299
+ * ceiling). `local` runs exactly when the request is not machine-routed, or when the hook declines
300
+ * (a route it does not own) — the `handled === true` case never touches it.
301
+ *
302
+ * @param {string} route `'METHOD /pathname'`, the key PROXIED_ROUTES spells
303
+ * @param {(req: any, res: any, url: URL) => any} local
304
+ * @returns {(req: any, res: any, url: URL) => any}
305
+ */
306
+ function machineRouted(route, local) {
307
+ return (req, res, url) => (isMachineRouted(req, url)
308
+ ? maybeProxyMachineRequest(req, res, url, { route })
309
+ .then((handled) => (handled ? undefined : local(req, res, url)))
310
+ : local(req, res, url));
311
+ }
312
+
200
313
  function _localInstanceId(deps) {
201
314
  if (typeof deps.localInstanceId === 'string') return _string(deps.localInstanceId, 160);
202
315
  try {
@@ -273,6 +386,9 @@ async function maybeProxyMachineRequest(req, res, url, options = {}) {
273
386
  const isPost = route.startsWith('POST ');
274
387
  const machine = _machineFromRequest(req, parsed);
275
388
  if (!machine) return false;
389
+ // D-5: the clock every `[remote-proxy]` line reports against — the whole machine-routed
390
+ // handling, from the registry read to the box's answer.
391
+ const startedAt = Date.now();
276
392
  let rawBody = null;
277
393
  let parsedBody = null;
278
394
 
@@ -306,14 +422,22 @@ async function maybeProxyMachineRequest(req, res, url, options = {}) {
306
422
  }
307
423
 
308
424
  const machineName = _string(target.displayName, 200) || machine;
309
- const unreachable = (extra = {}) => _sendJson(res, 502, {
310
- ok: false,
311
- code: 'machine_unreachable',
312
- error: `Machine ${machineName} is unreachable`,
313
- machine,
314
- machineName,
315
- ...extra,
316
- });
425
+ // D-5: every 502 is logged once, and names the update the box is applying when the hub's fleet
426
+ // record says a bundle went to it inside the last 10 minutes.
427
+ const unreachable = async (extra = {}) => {
428
+ const status = 502;
429
+ _logProxyFailure(route, machine, status, extra.detail || extra.error || '', startedAt);
430
+ const update = await _fleetUpdateFor(deps, machine);
431
+ return _sendJson(res, status, {
432
+ ok: false,
433
+ code: 'machine_unreachable',
434
+ error: `Machine ${machineName} is unreachable`,
435
+ machine,
436
+ machineName,
437
+ ...extra,
438
+ ...(update && typeof update === 'object' ? { box_updating: { to: _string(update.to, 200) } } : {}),
439
+ });
440
+ };
317
441
 
318
442
  // S-10: a create that mixes a hub-local worktree source with a remote machine is refused rather
319
443
  // than silently creating a worktree on the hub (whose path the caller would then hand to the box).
@@ -358,6 +482,7 @@ async function maybeProxyMachineRequest(req, res, url, options = {}) {
358
482
  // what it says: the box was dialled and did not answer.
359
483
  if (!peerHeaders) {
360
484
  const missing = _peerCredentialMissing(machineName);
485
+ _logProxyFailure(route, machine, 503, missing.code, startedAt);
361
486
  return _sendJson(res, 503, {
362
487
  ok: false,
363
488
  code: missing.code,
@@ -430,7 +555,13 @@ async function maybeProxyMachineRequest(req, res, url, options = {}) {
430
555
  if (res.headersSent) return true;
431
556
  // The box's status and body are returned VERBATIM (§6.5): the picker parses the box's own
432
557
  // payload shape, and a hub that re-shaped it would drift from the local case.
433
- res.writeHead(Number(response.status) || 502, {
558
+ const upstreamStatus = Number(response.status) || 502;
559
+ // D-5: a non-2xx answer from the box is a failed forward like any other. The body is never
560
+ // logged — only "upstream", because the box's own payload may carry anything.
561
+ if (upstreamStatus < 200 || upstreamStatus >= 300) {
562
+ _logProxyFailure(route, machine, upstreamStatus, 'upstream', startedAt);
563
+ }
564
+ res.writeHead(upstreamStatus, {
434
565
  'Content-Type': _string(response.headers && response.headers.get && response.headers.get('content-type'), 200)
435
566
  || 'application/json',
436
567
  });
@@ -509,12 +640,15 @@ module.exports = {
509
640
  MACHINE_HEADER,
510
641
  MACHINE_QUERY_PARAM,
511
642
  DEFAULT_TIMEOUT_MS,
643
+ FLEET_UPDATE_MAX_AGE_MS,
512
644
  PROXIED_ROUTES,
513
645
  configure,
514
646
  isMachineRouted,
647
+ machineRouted,
515
648
  maybeProxyMachineRequest,
516
649
  forwardMachineHttp,
517
650
  matchBoxProject,
518
651
  normalizeGitRemote,
519
652
  _recencyScore,
653
+ _fleetUpdateFromHubState,
520
654
  };
@@ -12,8 +12,9 @@
12
12
  // that owns the session — the bytes must land on the PTY host, because the CLI opens the delivered
13
13
  // draft path on its own filesystem (§1 "Where the bytes must live").
14
14
  //
15
- // It is deliberately NOT a general reverse proxy, and it is NOT wired into any route here (WP-3
16
- // adds the one dispatch line in api-prompts.js):
15
+ // It is deliberately NOT a general reverse proxy. Two dispatch lines use it today: WP-3's image-draft
16
+ // line in api-prompts.js, and WP-2's three terminal-history reads in lib/api/sessions.js (through
17
+ // `serveMirrorOrLocal` below):
17
18
  //
18
19
  // D-1 ownership is decided ONCE, by `catalog.getMirrorSessionMachine` — local sessions Map
19
20
  // first (the 2026-10-23 relay-hijack fix), then the forwarded creates, the live catalog and
@@ -66,11 +67,24 @@ const FORWARDED_REQUEST_HEADERS = Object.freeze(['content-type', 'content-length
66
67
  // keep its own retry hint; everything else (set-cookie, server, date) stays at the hop that owns it.
67
68
  const FORWARDED_RESPONSE_HEADERS = Object.freeze(['content-type', 'content-length', 'etag', 'cache-control', 'retry-after', 'x-content-type-options']);
68
69
 
69
- // D-3: the closed forwarded set. Each entry is [METHOD, RegExp on pathname]; it mirrors the ten
70
- // image-draft peer-control routes on the box (lib/auth-rules.js:164-179) exactly, and the WP-1
71
- // pairing meta-test (tests/peer-control-auth.test.js §7) fails when a member here has no flagged
72
- // row on the box. A member added here without that flag turns the hub's honest 503 into the box's
73
- // 403 insufficient_scope (the incident-2/incident-3 class).
70
+ // D-3: the closed forwarded set — EIGHTEEN members. Each entry is [METHOD, RegExp on pathname]. The
71
+ // first ten mirror the image-draft peer-control routes on the box (lib/auth-rules.js:164-179)
72
+ // exactly; WP-1 of docs/composer-image-chip-stuck-submission-recovery-2026-10-30.html §5 R-1 adds the
73
+ // eleventh, `POST /api/sessions/<id>/image-refs` (image-ref recording, session-scoped so it can be
74
+ // forwarded); the next three are WP-2's box-owned terminal-history reads — the SINGULAR
75
+ // `/api/session/<id>/(history-window|scrollback|painted-rows)` GETs
76
+ // (docs/remote-session-history-reads-and-box-status-hooks-2026-10-26.html WP-2 / D-3), whose matching
77
+ // `peerControl` row is lib/auth-rules.js's single anchor regex; the fourteenth is WP-13's canonical
78
+ // conversation read for a BOX-OWNED session — the SINGULAR `/api/session/messages` GET, whose session
79
+ // id rides the `?id=` query (see the member's own comment below); the last three are WP-1's box-owned
80
+ // child-transcript routes — the native/worker review GETs and the native clear cutoff
81
+ // (docs/remote-session-child-transcript-model-truth-and-restart-2026-10-27.html §5 D-2/D-3), whose
82
+ // `peerControl` rows are lib/auth-rules.js:181/187/485. The
83
+ // pairing meta-test is tests/peer-control-auth.test.js → `forwarded-route pairing: every
84
+ // FORWARDED_SESSION_ROUTES member resolves to a peerControl HTTP rule with the same method`: it
85
+ // iterates this set and fails when a member here has no flagged row on the box. A member added here
86
+ // without that flag turns the hub's honest 503 into the box's 403 insufficient_scope (the
87
+ // incident-2/incident-3 class).
74
88
  const FORWARDED_SESSION_ROUTES = Object.freeze([
75
89
  ['GET', /^\/api\/sessions\/[^/]+\/image-drafts$/],
76
90
  ['GET', /^\/api\/sessions\/[^/]+\/image-drafts\/[^/]+\/content$/],
@@ -82,6 +96,31 @@ const FORWARDED_SESSION_ROUTES = Object.freeze([
82
96
  ['POST', /^\/api\/sessions\/[^/]+\/image-drafts\/[^/]+\/freeze$/],
83
97
  ['POST', /^\/api\/sessions\/[^/]+\/image-drafts\/[^/]+\/submit$/],
84
98
  ['POST', /^\/api\/sessions\/[^/]+\/image-drafts\/[^/]+\/retry$/],
99
+ // WP-1 (§5 R-1, §6, §8 WP-1): image-ref RECORDING is session-scoped so it can be forwarded too —
100
+ // the draft it resolves lives on the box that owns a MIRROR session, and the hub's local resolver
101
+ // cannot see it (it answered CTM_SESSION_IMAGE_REF_UNRESOLVED until this row existed). The singular
102
+ // `/api/session/image-refs` path is REMOVED, not aliased, and lib/auth-rules.js carries the paired
103
+ // peerControl row.
104
+ ['POST', /^\/api\/sessions\/[^/]+\/image-refs$/],
105
+ // WP-2/D-3: the box's terminal-history reads — SINGULAR `/api/session/`, not `/api/sessions/`.
106
+ ['GET', /^\/api\/session\/[^/]+\/history-window$/],
107
+ ['GET', /^\/api\/session\/[^/]+\/scrollback$/],
108
+ ['GET', /^\/api\/session\/[^/]+\/painted-rows$/],
109
+ // WP-13 (docs/deepseek-harness-acp-agent-2026-10-28.html §5 D-25, §8 WP-13, §9 R-1, §10 T-23): the
110
+ // CANONICAL conversation read — the one the Conv view polls. It was local-only, so a box-owned
111
+ // session's Conv showed nothing on the hub for EVERY agent (Claude and Codex included, not just
112
+ // dsh): the hub holds no session row for a mirror id, so its own reader answered empty. There is
113
+ // ONE generic forward — no dsh-only route and no hub mirror file. The session id is the `?id=`
114
+ // QUERY parameter here (server.js's apiSessionMessages reads `url.searchParams.get('id')`), so the
115
+ // dispatch line in lib/api/sessions.js passes that value as serveMirrorOrLocal's encodedSessionId.
116
+ // The pathname is fixed, which is all this predicate needs (D-3 is purely lexical).
117
+ ['GET', /^\/api\/session\/messages$/],
118
+ // WP-1 (docs/remote-session-child-transcript-model-truth-and-restart-2026-10-27.html §5 D-2):
119
+ // child-transcript reads and the clear cutoff are box-owned — the transcript and the native
120
+ // runtime live on the box; the hub has no row for a mirror id.
121
+ ['GET', /^\/api\/sessions\/[^/]+\/native-subagents\/[^/]+\/messages$/],
122
+ ['GET', /^\/api\/sessions\/[^/]+\/worker-transcript\/messages$/],
123
+ ['POST', /^\/api\/sessions\/[^/]+\/native-subagents\/clear$/],
85
124
  ]);
86
125
 
87
126
  function _string(value, max = 200) {
@@ -296,6 +335,24 @@ async function maybeForwardSessionRequest(req, res, url, sessionId, deps = {}) {
296
335
  return true;
297
336
  }
298
337
 
338
+ /**
339
+ * D-1/D-4: serve `localHandler` for a local or unclaimed id (same tick, request untouched), or forward a mirror id.
340
+ * @param {import('http').IncomingMessage} req
341
+ * @param {import('http').ServerResponse} res
342
+ * @param {URL} url
343
+ * @param {string} encodedSessionId the raw path segment (route param '1')
344
+ * @param {(req, res, url, encodedSessionId) => any} localHandler
345
+ * @param {object} [deps] passed through to isMirrorSessionRequest / maybeForwardSessionRequest
346
+ */
347
+ function serveMirrorOrLocal(req, res, url, encodedSessionId, localHandler, deps = {}) {
348
+ let sessionId = '';
349
+ try { sessionId = decodeURIComponent(String(encodedSessionId || '')); }
350
+ catch { return localHandler(req, res, url, encodedSessionId); } // expected: a malformed id is the local handler's 4xx to give
351
+ if (!isMirrorSessionRequest(req, url, sessionId, deps)) return localHandler(req, res, url, encodedSessionId);
352
+ return maybeForwardSessionRequest(req, res, url, sessionId, deps)
353
+ .then((forwarded) => (forwarded ? undefined : localHandler(req, res, url, encodedSessionId)));
354
+ }
355
+
299
356
  module.exports = {
300
357
  FORWARDED_SESSION_ROUTES,
301
358
  FORWARD_DEADLINE_MS,
@@ -303,4 +360,5 @@ module.exports = {
303
360
  maybeForwardSessionRequest,
304
361
  isForwardedSessionRoute,
305
362
  isMirrorSessionRequest,
363
+ serveMirrorOrLocal,
306
364
  };
@@ -2,10 +2,17 @@
2
2
 
3
3
  const attemptOwners = new WeakMap();
4
4
 
5
+ // The delivery target's process identity: a PTY session's ptyProcess, or an ACP (DeepSeek Harness)
6
+ // session's `_acpProcess` token — set once per child by lib/session-create/dsh-session.js and cleared when
7
+ // the child exits — so a replaced or dead ACP child retires the target exactly like a replaced PTY.
8
+ function targetProcessOf(session) {
9
+ return session?.ptyProcess || session?._acpProcess || null;
10
+ }
11
+
5
12
  function captureRemoteTerminalTarget(sessionId, session, sessionSource, getProcessGeneration) {
6
13
  const getSession = typeof sessionSource === 'function' ? sessionSource : id => sessionSource?.get(id);
7
14
  const originalSession = session && typeof session === 'object' ? session : null;
8
- const originalPtyProcess = originalSession?.ptyProcess || null;
15
+ const originalPtyProcess = targetProcessOf(originalSession);
9
16
  const originalGeneration = originalSession && typeof getProcessGeneration === 'function'
10
17
  ? getProcessGeneration(originalSession)
11
18
  : null;
@@ -19,7 +26,7 @@ function captureRemoteTerminalTarget(sessionId, session, sessionSource, getProce
19
26
  }
20
27
  const current = getSession(sessionId);
21
28
  const matches = current === originalSession
22
- && current.ptyProcess === originalPtyProcess
29
+ && targetProcessOf(current) === originalPtyProcess
23
30
  && typeof getProcessGeneration === 'function'
24
31
  && getProcessGeneration(current) === originalGeneration;
25
32
  if (!matches) targetRetired = true;
@@ -81,13 +81,17 @@ function summarizeRestartGuard(sessions = [], options = {}) {
81
81
  ? normalizeWaitingReason(options.waitingReasonForSession(session))
82
82
  : defaultWaitingReasonForSession(session);
83
83
  const summary = restartSessionSummary(session, rawStatus, waitingForInput, waitingReason);
84
- // WP-4 (pty-survival design doc, S-16): a restart no longer kills a HOSTED
85
- // session's live process (WP-1/2 linger + WP-3 boot adoption) -- a
86
- // running/busy hosted session survives the restart itself, so it must stop
87
- // counting as "would be interrupted". Approval/choice waits still block:
88
- // those are a human decision left hanging mid-turn, not process survival.
84
+ // WP-4 (pty-survival design doc, S-16) + WP-2 (session-host-cgroup-survival design doc, D-5): a HOSTED
85
+ // session's live process survives a restart ONLY IF its host actually survives CTM's exit -- true under
86
+ // launchd (proven), and under systemd only when the host was moved out of ctm.service's cgroup
87
+ // (KillMode=control-group reaps everything else). `options.hostSurvivesRestart(session)` answers that at
88
+ // snapshot time; when absent it defaults to true (callers/tests that never knew about cgroups). A running or
89
+ // busy session whose host would NOT survive blocks like a non-hosted one. Approval/choice waits still
90
+ // block: those are a human decision left hanging mid-turn, not process survival.
89
91
  const isHosted = typeof options.isHostedSession === 'function' ? !!options.isHostedSession(session) : false;
90
- const runningBlocked = BLOCKING_STATUSES.has(summary.status) && !isHosted;
92
+ const survives = isHosted && (typeof options.hostSurvivesRestart === 'function' ? !!options.hostSurvivesRestart(session) : true);
93
+ summary.hostSurvives = isHosted ? survives : null;
94
+ const runningBlocked = BLOCKING_STATUSES.has(summary.status) && !survives;
91
95
  const waitingBlocked = summary.status === 'waiting_input' &&
92
96
  isBlockingWaitingSession(session, summary, waitingForInput, waitingReason, options);
93
97
  if (runningBlocked || waitingBlocked) blockers.push(summary);
@@ -107,6 +111,14 @@ function summarizeRestartGuard(sessions = [], options = {}) {
107
111
  blockers.push(summary);
108
112
  }
109
113
 
114
+ // WP-3 (docs/session-create-box-restart-and-git-lane-starvation-2026-10-30.html §6.3/§8):
115
+ // an in-flight CREATE has no session row yet, so the loop above cannot see it — but a restart
116
+ // would still abort it. Each entry is appended VERBATIM (the caller owns the shape:
117
+ // `{ id, kind: 'create_in_progress', ... }`), and counts as a blocker. Absent/empty => unchanged.
118
+ for (const entry of options.pendingCreates || []) {
119
+ if (entry) blockers.push(entry);
120
+ }
121
+
110
122
  return {
111
123
  preflight,
112
124
  totalCount: items.length,
@@ -0,0 +1,87 @@
1
+ 'use strict';
2
+
3
+ // WP-1 (docs/claude-restore-seed-alt-screen-trap-2026-10-27.html §5 R-1/R-2/R-5, §6, §8, §9):
4
+ // a restore seed fed to a NEW process's headless mirror must be CONTENT-ONLY. The persisted
5
+ // snapshot of a fullscreen Claude begins with `ESC[?1049h` (xterm SerializeAddon serializes an
6
+ // active alternate buffer that way). Fed to a newly-started Claude that renders INLINE -- which
7
+ // never emits `ESC[?1049l` because it never entered -- the mirror stays on the alternate buffer
8
+ // forever: DECSET 1049 disables scrollback while active, CTM strips mouse enables from snapshots,
9
+ // so the tab has no mouse and no history and the UI shows "Scrolling is off until this app
10
+ // reports its mouse mode". This module rewrites the seed so the NEW process alone decides the
11
+ // terminal modes:
12
+ // * every alternate-screen ENTER (`?1049h`/`?1047h`/`?47h`, including inside a multi-param
13
+ // DECSET such as `ESC[?2004;1049h`) is replaced by a push-out into normal-buffer history --
14
+ // the same shape lib/seed-push-out.js:17 (`seedPushOutSequence`) emits, with n = rows -- so
15
+ // the picture that followed the enter survives as scrollback instead of being covered;
16
+ // * every alternate-screen LEAVE (`?1049l`/`?1047l`/`?47l`, incl. inside a multi-param DECRST)
17
+ // is dropped (the new process never entered, so a leave is meaningless);
18
+ // * every mouse-tracking ENABLE (MOUSE_PARAMS with `h`) is dropped -- mouse truth must come only
19
+ // from the live process (R-2 / the read-only R-10 contract); mouse DISABLES (`l`) are kept.
20
+ // Pure: no I/O, no shared state. Applied to the WHOLE seed before slicing (R-5) so a DECSET is
21
+ // never torn across feedSliceChars chunks.
22
+ //
23
+ // NOT used by lib/host-handoff.js (R-3): a same-process handoff keeps the real alternate screen.
24
+ // The push-out is emitted once per DECSET that named at least one alt-screen param; the three
25
+ // counters each report the number of individual params removed.
26
+
27
+ const ALT_SCREEN_PARAMS = [1049, 1047, 47];
28
+ const MOUSE_PARAMS = [1000, 1002, 1003, 1005, 1006, 1015, 1016];
29
+
30
+ const ALT_SET = new Set(ALT_SCREEN_PARAMS);
31
+ const MOUSE_SET = new Set(MOUSE_PARAMS);
32
+
33
+ // A private DECSET/DECRST: `ESC [ ? <;params> h|l`. Params are decimal.
34
+ const DEC_MODE_RE = /\x1b\[\?([0-9;]*)([hl])/g;
35
+
36
+ // The push-out shape -- byte-identical to lib/seed-push-out.js:17 seedPushOutSequence with
37
+ // cursorY >= rows (n = rows): move to the bottom row, emit one LF per row (scrolling each seed
38
+ // row into history), then home the cursor.
39
+ function pushOutSequence(rows) {
40
+ return '\x1b[' + rows + ';1H' + '\n'.repeat(rows) + '\x1b[H';
41
+ }
42
+
43
+ function contentOnlyRestoreSeed(data, rows) {
44
+ if (typeof data !== 'string') {
45
+ return { data, altEntersConverted: 0, altLeavesDropped: 0, mouseEnablesDropped: 0 };
46
+ }
47
+ // Never guess a row count: anything that is not an integer >= 1 drops alt enters outright.
48
+ const validRows = Number.isInteger(rows) && rows >= 1;
49
+ let altEntersConverted = 0;
50
+ let altLeavesDropped = 0;
51
+ let mouseEnablesDropped = 0;
52
+
53
+ const out = data.replace(DEC_MODE_RE, (match, paramsStr, final) => {
54
+ if (!paramsStr) return match; // `ESC[?h` / `ESC[?l` name no mode -> leave untouched
55
+ const params = paramsStr.split(';');
56
+ const kept = [];
57
+ let altCount = 0;
58
+ let mouseCount = 0;
59
+ for (const param of params) {
60
+ const n = Number(param);
61
+ if (final === 'h' && ALT_SET.has(n)) { altCount += 1; continue; }
62
+ if (final === 'l' && ALT_SET.has(n)) { altLeavesDropped += 1; continue; }
63
+ if (final === 'h' && MOUSE_SET.has(n)) { mouseCount += 1; continue; }
64
+ kept.push(param);
65
+ }
66
+ if (final === 'l') {
67
+ // Alt leaves already dropped; keep the other params verbatim (or remove the DECRST).
68
+ return kept.length ? '\x1b[?' + kept.join(';') + 'l' : '';
69
+ }
70
+ // final === 'h'
71
+ mouseEnablesDropped += mouseCount;
72
+ if (altCount > 0) {
73
+ altEntersConverted += altCount;
74
+ const head = kept.length ? '\x1b[?' + kept.join(';') + 'h' : '';
75
+ return head + (validRows ? pushOutSequence(rows) : '');
76
+ }
77
+ if (mouseCount === 0) return match; // nothing matched -> preserve the exact bytes
78
+ return kept.length ? '\x1b[?' + kept.join(';') + 'h' : '';
79
+ });
80
+
81
+ if (altEntersConverted === 0 && altLeavesDropped === 0 && mouseEnablesDropped === 0) {
82
+ return { data, altEntersConverted, altLeavesDropped, mouseEnablesDropped };
83
+ }
84
+ return { data: out, altEntersConverted, altLeavesDropped, mouseEnablesDropped };
85
+ }
86
+
87
+ module.exports = { contentOnlyRestoreSeed, ALT_SCREEN_PARAMS, MOUSE_PARAMS };
@@ -1,5 +1,7 @@
1
1
  'use strict';
2
2
 
3
+ const { contentOnlyRestoreSeed } = require('./restore-seed-modes');
4
+
3
5
  // Owns the restored-scrollback probe/feed transaction. The host supplies archive,
4
6
  // worker, session, and diagnostic boundaries; this module owns ordering and policy.
5
7
  function createRestoredScrollbackHydrationRuntime(deps = {}) {
@@ -80,6 +82,24 @@ function createRestoredScrollbackHydrationRuntime(deps = {}) {
80
82
  return tail;
81
83
  }
82
84
 
85
+ // WP-1 (docs/claude-restore-seed-alt-screen-trap-2026-10-27.html §5 R-1/R-2/R-5): strip the
86
+ // dead process's terminal modes from the seed so the NEW process alone decides the buffer and
87
+ // the mouse. Applied to the whole (already bounded) string so a DECSET is never split across
88
+ // feed slices. Idempotent, so hydrateInBackground (prepareSeed -> feed) records exactly one
89
+ // 'restore-seed-modes' diagnostic -- at whichever stage actually removed a mode.
90
+ function contentOnlySeed(sessionId, scrollData, seedRows) {
91
+ const cleaned = contentOnlyRestoreSeed(scrollData, seedRows);
92
+ if (cleaned.altEntersConverted || cleaned.altLeavesDropped || cleaned.mouseEnablesDropped) {
93
+ recordDiagnostic(sessionId, 'restore-seed-modes', {
94
+ altEntersConverted: cleaned.altEntersConverted,
95
+ altLeavesDropped: cleaned.altLeavesDropped,
96
+ mouseEnablesDropped: cleaned.mouseEnablesDropped,
97
+ seedRows: seedRows === undefined ? null : seedRows,
98
+ });
99
+ }
100
+ return cleaned.data;
101
+ }
102
+
83
103
  async function prepareSeed(sessionId, scrollDataIn, opts = {}) {
84
104
  let probe = { aligned: false, reason: 'flag-off', archiveLastRow: null };
85
105
  if (alignedEnabled) {
@@ -103,7 +123,7 @@ function createRestoredScrollbackHydrationRuntime(deps = {}) {
103
123
  drift: seam.drift,
104
124
  });
105
125
  return {
106
- data: boundInput(sessionId, scrollDataIn, probe.aligned === true),
126
+ data: contentOnlySeed(sessionId, boundInput(sessionId, scrollDataIn, probe.aligned === true), opts.seedRows),
107
127
  seedAligned: probe.aligned === true,
108
128
  forwardDriftSeamRow: probe.mode === 'forward-drift' ? seam.recordedSeamRow : null,
109
129
  };
@@ -114,7 +134,7 @@ function createRestoredScrollbackHydrationRuntime(deps = {}) {
114
134
  const forwardDriftSeamRow = Number.isFinite(opts.forwardDriftSeamRow)
115
135
  ? opts.forwardDriftSeamRow
116
136
  : null;
117
- const scrollData = boundInput(sessionId, scrollDataIn, seedAligned);
137
+ const scrollData = contentOnlySeed(sessionId, boundInput(sessionId, scrollDataIn, seedAligned), opts.seedRows);
118
138
  if (!sessionId || !scrollData) {
119
139
  opts.onDone?.('empty');
120
140
  return;
@@ -703,7 +703,7 @@ function classifyStartupTaskRestore(task, restoredOrdinal, fastSessionCount, ali
703
703
  ) {
704
704
  return 'adopt';
705
705
  }
706
- if (task?.type === 'walle') return 'eager';
706
+ if (task?.type === 'walle') return 'eager'; if (task?.type === 'dsh') return 'defer'; // D-A (WP-5): dsh NEVER spawns at boot -- it is a childless deferred placeholder, started on attach/input by lib/session-create/dsh-session.js's restoreDshSession.
707
707
  if (primary && task?._restoreInterruption?.interrupted) return 'eager'; // WP-2: resume an interrupted turn now
708
708
  const ordinal = Number(restoredOrdinal) || 0;
709
709
  const fastCount = Number(fastSessionCount) || 0;
@@ -790,12 +790,24 @@ function orderAutoResumeDeferredIds(deferredIds, priorityIds) {
790
790
 
791
791
  function selectAutoResumeDeferredIds(deferredIds, priorityIds, policy) {
792
792
  const ids = Array.isArray(deferredIds) ? deferredIds.filter(Boolean).map(String) : [];
793
- if (!ids.length || !policy || !policy.enabled || policy.limit <= 0) return [];
793
+ // WP-2 (docs/external-causality-kill-session-loss-2026-10-27.html §6): the ONE escape from the
794
+ // boot cap, used by the explicit "Resume N sessions" control (POST /api/sessions/resume-deferred).
795
+ // `all` is read ONLY when present; with it absent every arm below is byte-identical to before.
796
+ const all = !!(policy && policy.all === true);
797
+ if (!ids.length) return [];
798
+ if (!all && (!policy || !policy.enabled || policy.limit <= 0)) return [];
794
799
  const ordered = orderAutoResumeDeferredIds(ids, priorityIds);
795
- if (!Number.isFinite(policy.limit)) return ordered;
800
+ if (all || !Number.isFinite(policy.limit)) return ordered;
796
801
  return ordered.slice(0, Math.max(0, Math.floor(policy.limit)));
797
802
  }
798
803
 
804
+ // WP-2 (§5 D-5, §6): the boot restore selector's cap as ONE object, so the adopted-host-death
805
+ // drain reuses the exact boot policy instead of inventing a second one. Same CTM_AUTO_RESUME_DEFERRED
806
+ // semantics resolveAutoResumeDeferredPolicy already applies at boot (mode 'off' ⇒ nothing drains).
807
+ function bootAutoResumePolicy(env = process.env) {
808
+ return resolveAutoResumeDeferredPolicy(env);
809
+ }
810
+
799
811
  // WP-3 (design doc §5 D-4, §8 WP-3, §9 S-17/S-18): explicit all/wide numeric
800
812
  // drains can still admit many eligible sessions, most-recent-first. The 2026-08-22
801
813
  // incident measured 8 sessions (~2.5 GB) auto-resumed on a host already at 88%
@@ -914,6 +926,7 @@ const resumeAdmission = {
914
926
  selectAutoResumeDeferredIds,
915
927
  orderAutoResumeDeferredIds,
916
928
  resolveAutoResumePolicy,
929
+ bootAutoResumePolicy,
917
930
  DEFAULT_SESSION_FOOTPRINT_MB,
918
931
  },
919
932
  };
@@ -949,5 +962,6 @@ module.exports = {
949
962
  selectAutoResumeDeferredIds,
950
963
  orderAutoResumeDeferredIds,
951
964
  resolveAutoResumePolicy,
965
+ bootAutoResumePolicy,
952
966
  DEFAULT_SESSION_FOOTPRINT_MB,
953
967
  };