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,355 @@
1
+ 'use strict';
2
+
3
+ // WP-5 (docs/shared-memory-every-machine-2026-10-31.html §5 D-3, D-11, §6.8): the storage half of
4
+ // the conversation-history change feed — a version-vector addressed feed over
5
+ // `conversation_item_clock` plus the one transaction that applies a page of it. Deliberately the
6
+ // same shape as wall-e's brain replica (the Wall-E brain replica's brain-sync-v2 module), because D-3 is one
7
+ // protocol over two stores.
8
+ //
9
+ // readConversationVector(db) -> { instanceId, vector, seedDone }
10
+ // conversationChangesSince(db, { since, limit }) -> { entries, more, seedDone }
11
+ // applyConversationChanges(db, { sourceInstanceId, entries }) -> { ok, applied, skipped, vector }
12
+ //
13
+ // The entries are the SENDER's clock rows: the receiver keeps the sender's (origin, origin_seq)
14
+ // verbatim, so a box-1 row relayed by the hub to box-2 still reads as box-1's, and a node never
15
+ // takes its own rows back (it asks with its own maximum, and `origin === local` is skipped).
16
+ //
17
+ // The item half is committed through the store's OWN writer (commitConversationBatchV2) with
18
+ // `replication: { origin, originSeqByKey }` — so the FTS triggers, the placement table, the
19
+ // `conversation_projection_outbox_v2` event and the session generation bookkeeping all run exactly
20
+ // as they do for a locally ingested item, which is what makes Wall-E's existing subscriber project
21
+ // a replicated item with no subscriber change (AC-5b). Tombstones instead go through
22
+ // deleteConversationItemsByStableKeyV2, which records the conversation_projection_deletes_v2 row
23
+ // the subscriber's delete feed reads.
24
+
25
+ const {
26
+ commitConversationBatchV2,
27
+ ensureConversationStoreV2,
28
+ } = require('./conversation-store-v2');
29
+ // WP-5 §6.8: the clock's read/stamp API lives in lib/conversation-item-clock.js (no DDL there).
30
+ const {
31
+ localConversationInstanceId,
32
+ readConversationClockVector,
33
+ bumpConversationVv,
34
+ stampIncomingConversationClockV2,
35
+ conversationSeedState,
36
+ } = require('./conversation-item-clock');
37
+ // The receiver's delete lives beside purgeConversationV2 (the pinned item-table writer).
38
+ const { deleteConversationItemsByStableKeyV2 } = require('./prompt-purge-v2');
39
+ const { admitDurableText } = require('../../shared/conversation-admission');
40
+
41
+ const DEFAULT_CHANGES_LIMIT = 2_000; // §6.8: one page is at most 2000 clock rows
42
+ const MAX_CHANGES_LIMIT = 2_000;
43
+ const MAX_APPLY_ENTRIES = 2_000;
44
+ // The synthetic origin of the receiver path's ingest cursor. §6.8 names it `peer-sync:<origin>:<session>`.
45
+ const PEER_SYNC_SOURCE_PREFIX = 'peer-sync:';
46
+ const PEER_SYNC_SOURCE_FINGERPRINT = 'peer-sync';
47
+ const PEER_SYNC_PARSER_VERSION = 1;
48
+
49
+ function _string(value, max = 400) {
50
+ if (typeof value === 'string') return value.slice(0, max);
51
+ return value == null ? '' : String(value).slice(0, max);
52
+ }
53
+
54
+ function _parseSeq(value) {
55
+ const n = Number(value);
56
+ return Number.isFinite(n) && n > 0 ? Math.floor(n) : 0;
57
+ }
58
+
59
+ function _ensure(db) {
60
+ ensureConversationStoreV2(db);
61
+ }
62
+
63
+ function _itemExists(db, sessionId, stableKey) {
64
+ return db.prepare(
65
+ 'SELECT turn_id, kind, text, provenance, provider, model, created_at_ms, content_version, updated_at_ms,'
66
+ + ' source_event_digest, provider_message_digest FROM conversation_text_items_v2 WHERE session_id=? AND stable_key=?',
67
+ ).get(sessionId, stableKey) || null;
68
+ }
69
+
70
+ // A hidden placement (a superseded/tombstoned incumbent) is a delete on the wire: shipping its
71
+ // tombstone position key would make the receiver store a live row at a position it never owned.
72
+ function _positionOf(db, sessionId, stableKey) {
73
+ const row = db.prepare(
74
+ 'SELECT position_key, visible FROM conversation_item_placement_v2 WHERE session_id=? AND stable_key=?',
75
+ ).get(sessionId, stableKey);
76
+ if (!row || Number(row.visible) !== 1) return null;
77
+ return row.position_key;
78
+ }
79
+
80
+ /** GET /api/sync/conversation/v2/vector — this node's id, its vector, and its seed verdict. */
81
+ function readConversationVector(db) {
82
+ _ensure(db);
83
+ return {
84
+ instanceId: localConversationInstanceId(db),
85
+ vector: readConversationClockVector(db),
86
+ seedDone: conversationSeedState(db).done,
87
+ };
88
+ }
89
+
90
+ function _feedEntry(db, row) {
91
+ const origin = _string(row.origin_instance_id, 160);
92
+ const deleted = Number(row.deleted) ? 1 : 0;
93
+ const stableKey = _string(row.stable_key, 200);
94
+ const sessionId = _string(row.session_id, 200);
95
+ const item = deleted ? null : _itemExists(db, sessionId, stableKey);
96
+ // A clock that says "live" for a row that is gone is a race with a local delete: ship it as a
97
+ // delete. The peer's apply is idempotent, so the tombstone is harmless when it is already gone.
98
+ const isDeleted = deleted === 1 || !item;
99
+ return {
100
+ stableKey,
101
+ sessionId,
102
+ origin,
103
+ originSeq: Number(row.origin_seq) || 0,
104
+ deleted: isDeleted ? 1 : 0,
105
+ updatedAtMs: Number(row.updated_at_ms) || 0,
106
+ item: isDeleted ? null : {
107
+ turnId: item.turn_id,
108
+ kind: item.kind,
109
+ text: item.text,
110
+ provenance: item.provenance == null ? '' : item.provenance,
111
+ provider: item.provider,
112
+ model: item.model,
113
+ createdAtMs: Number(item.created_at_ms),
114
+ contentVersion: Number(item.content_version) || 1,
115
+ updatedAtMs: item.updated_at_ms == null ? null : Number(item.updated_at_ms),
116
+ sourceEventDigest: item.source_event_digest || '',
117
+ providerMessageDigest: item.provider_message_digest || '',
118
+ positionKey: _positionOf(db, sessionId, stableKey),
119
+ },
120
+ };
121
+ }
122
+
123
+ // Every clock row whose origin_seq is beyond `since`, ascending by (origin, origin_seq), filling one
124
+ // page. Origin '' (a row this host could not address) is never shipped. `more` is true when ANY
125
+ // origin still holds rows past this page.
126
+ function conversationChangesSince(db, { since = {}, limit = DEFAULT_CHANGES_LIMIT } = {}) {
127
+ _ensure(db);
128
+ const requested = Math.floor(Number(limit) || 0) || DEFAULT_CHANGES_LIMIT;
129
+ const budget = Math.max(1, Math.min(MAX_CHANGES_LIMIT, requested));
130
+ const sinceMap = since && typeof since === 'object' && !Array.isArray(since) ? since : {};
131
+ const origins = db.prepare('SELECT origin_instance_id FROM conversation_vv ORDER BY origin_instance_id ASC').all()
132
+ .map((row) => String(row.origin_instance_id))
133
+ .filter((origin) => origin !== '');
134
+ const select = db.prepare(`
135
+ SELECT stable_key, session_id, origin_instance_id, origin_seq, updated_at_ms, deleted
136
+ FROM conversation_item_clock
137
+ WHERE origin_instance_id = ? AND origin_seq > ?
138
+ ORDER BY origin_seq ASC
139
+ LIMIT ?
140
+ `);
141
+ const probe = db.prepare('SELECT 1 AS hit FROM conversation_item_clock WHERE origin_instance_id = ? AND origin_seq > ? LIMIT 1');
142
+ const entries = [];
143
+ let pending = null;
144
+ for (const origin of origins) {
145
+ const after = Math.max(0, _parseSeq(sinceMap[origin]));
146
+ const remaining = budget - entries.length;
147
+ if (remaining <= 0) { pending = { origin, after }; break; }
148
+ const rows = select.all(origin, after, remaining);
149
+ for (const row of rows) entries.push(_feedEntry(db, row));
150
+ if (rows.length < remaining) continue; // this origin drained
151
+ const lastSeq = rows.length ? Number(rows[rows.length - 1].origin_seq) : after;
152
+ if (probe.get(origin, lastSeq)) { pending = { origin, after: lastSeq }; break; }
153
+ }
154
+ let more = false;
155
+ if (pending) {
156
+ const startAt = origins.indexOf(pending.origin);
157
+ for (let index = startAt; index < origins.length && !more; index += 1) {
158
+ const origin = origins[index];
159
+ const after = index === startAt ? pending.after : Math.max(0, _parseSeq(sinceMap[origin]));
160
+ more = !!probe.get(origin, after);
161
+ }
162
+ }
163
+ return { entries, more, seedDone: conversationSeedState(db).done };
164
+ }
165
+
166
+ // The wire item, reduced to exactly the keys admission accepts. `contentVersion`/`updatedAtMs`
167
+ // travel on the entry (below) for observability; admitDurableText rejects unknown item fields.
168
+ function _commitItem(entry) {
169
+ const item = entry.item || {};
170
+ return {
171
+ stableKey: _string(entry.stableKey, 200),
172
+ sessionId: _string(entry.sessionId, 200),
173
+ turnId: _string(item.turnId, 400),
174
+ kind: item.kind,
175
+ text: typeof item.text === 'string' ? item.text : String(item.text == null ? '' : item.text),
176
+ provenance: item.provenance == null ? '' : String(item.provenance),
177
+ provider: item.provider,
178
+ model: item.model == null ? null : String(item.model),
179
+ createdAtMs: Number(item.createdAtMs),
180
+ positionKey: _string(item.positionKey, 400),
181
+ sourceEventDigest: _string(item.sourceEventDigest, 64),
182
+ providerMessageDigest: _string(item.providerMessageDigest, 64),
183
+ };
184
+ }
185
+
186
+ function _isAdmissible(item) {
187
+ // Mirror the store's own pre-admission strip (conversation-store-v2 `_normalizeItems`): the two
188
+ // digests are recomputed/kept by the store and are NOT part of admission's item shape.
189
+ const probe = { ...item };
190
+ delete probe.sourceEventDigest;
191
+ delete probe.providerMessageDigest;
192
+ return admitDurableText(probe).action === 'persist_text';
193
+ }
194
+
195
+ /**
196
+ * POST /api/sync/conversation/v2/apply — one IMMEDIATE transaction for the whole page. Entries the
197
+ * vector already covers, this node's own origin, and malformed entries are skipped (and counted as
198
+ * "considered" so the sender stops re-offering them). Live entries are grouped by session and
199
+ * committed through the store's writer with `replication` set; tombstones are deleted with the
200
+ * Wall-E delete-feed row. The vector advances only inside this transaction.
201
+ */
202
+ function applyConversationChanges(db, { sourceInstanceId, entries } = {}) {
203
+ _ensure(db);
204
+ const source = _string(sourceInstanceId, 160).trim();
205
+ if (!source) {
206
+ const error = new Error('conversation_sync_source_instance_required');
207
+ error.code = 'CONVERSATION_SYNC_SOURCE_REQUIRED';
208
+ throw error;
209
+ }
210
+ const list = Array.isArray(entries) ? entries : [];
211
+ if (list.length > MAX_APPLY_ENTRIES) {
212
+ const error = new Error('conversation_sync_entries_too_large');
213
+ error.code = 'CONVERSATION_SYNC_ENTRIES_TOO_LARGE';
214
+ throw error;
215
+ }
216
+ const localId = localConversationInstanceId(db);
217
+ const nowMs = Date.now();
218
+ let applied = 0;
219
+ let skipped = 0;
220
+ db.transaction(() => {
221
+ const seenThrough = db.prepare('SELECT max_seq FROM conversation_vv WHERE origin_instance_id = ?');
222
+ // Dedupe by stable key keeping the highest seq: a page is a compacted log, so one row per key.
223
+ const byKey = new Map();
224
+ for (const entry of list) {
225
+ const stableKey = _string(entry?.stableKey, 200).trim();
226
+ const sessionId = _string(entry?.sessionId, 200).trim();
227
+ const origin = _string(entry?.origin, 160).trim();
228
+ const originSeq = _parseSeq(entry?.originSeq);
229
+ if (!stableKey || !sessionId || !origin || !originSeq) { skipped += 1; continue; }
230
+ if (origin === localId) { skipped += 1; continue; } // echo guard (D-3): never take our own rows back
231
+ const seen = seenThrough.get(origin);
232
+ if (seen && Number(seen.max_seq) >= originSeq) { skipped += 1; continue; }
233
+ const prior = byKey.get(stableKey);
234
+ if (prior && prior.originSeq >= originSeq) { skipped += 1; continue; }
235
+ byKey.set(stableKey, { stableKey, sessionId, origin, originSeq, deleted: Number(entry?.deleted) ? 1 : 0, item: entry?.item || null });
236
+ }
237
+
238
+ const liveBySession = new Map(); // `${origin}\0${sessionId}` -> { origin, sessionId, items[], originSeqByKey }
239
+ const tombstonesBySession = new Map(); // `${origin}\0${sessionId}` -> { sessionId, keys[] }
240
+ const considered = new Map(); // origin -> highest seq considered
241
+ for (const entry of byKey.values()) {
242
+ considered.set(entry.origin, Math.max(considered.get(entry.origin) || 0, entry.originSeq));
243
+ if (entry.deleted) {
244
+ const key = `${entry.origin}\0${entry.sessionId}`;
245
+ if (!tombstonesBySession.has(key)) tombstonesBySession.set(key, { sessionId: entry.sessionId, keys: [] });
246
+ tombstonesBySession.get(key).keys.push(entry.stableKey);
247
+ continue;
248
+ }
249
+ const item = _commitItem(entry);
250
+ if (!_isAdmissible(item)) { skipped += 1; continue; }
251
+ const key = `${entry.origin}\0${entry.sessionId}`;
252
+ if (!liveBySession.has(key)) {
253
+ liveBySession.set(key, { origin: entry.origin, sessionId: entry.sessionId, provider: item.provider, items: [], originSeqByKey: {}, maxSeq: 0 });
254
+ }
255
+ const group = liveBySession.get(key);
256
+ group.items.push(item);
257
+ group.originSeqByKey[item.stableKey] = entry.originSeq;
258
+ group.maxSeq = Math.max(group.maxSeq, entry.originSeq);
259
+ }
260
+
261
+ for (const group of liveBySession.values()) {
262
+ const cursor = {
263
+ sourceId: `${PEER_SYNC_SOURCE_PREFIX}${group.origin}:${group.sessionId}`,
264
+ provider: group.provider,
265
+ sourceFingerprint: PEER_SYNC_SOURCE_FINGERPRINT,
266
+ byteOffset: group.maxSeq,
267
+ sourceSize: group.maxSeq,
268
+ parserVersion: PEER_SYNC_PARSER_VERSION,
269
+ };
270
+ const result = commitConversationBatchV2(db, {
271
+ items: group.items,
272
+ cursor,
273
+ replication: { origin: group.origin, originSeqByKey: group.originSeqByKey },
274
+ nowMs,
275
+ });
276
+ applied += Number(result.inserted || 0) + Number(result.corrected || 0);
277
+ }
278
+ for (const group of tombstonesBySession.values()) {
279
+ applied += deleteConversationItemsByStableKeyV2(db, { sessionId: group.sessionId, stableKeys: group.keys, nowMs });
280
+ }
281
+ // A tombstone keeps its SENDER's (origin, seq) so it stays addressable in that origin's stream
282
+ // and relays onward — including in the case where the receiver never held the item, which is
283
+ // exactly when compaction must still record "origin X has moved past this key".
284
+ for (const entry of byKey.values()) {
285
+ if (!entry.deleted) continue;
286
+ stampIncomingConversationClockV2(db, {
287
+ stableKey: entry.stableKey, sessionId: entry.sessionId, origin: entry.origin,
288
+ originSeq: entry.originSeq, nowMs,
289
+ });
290
+ }
291
+ // D-3 "seen through": every entry this call considered is now behind the vector, whether it was
292
+ // written, skipped as stale, or refused as malformed — re-offering it next tick would only grow
293
+ // the page budget.
294
+ for (const [origin, seq] of considered) bumpConversationVv(db, origin, seq);
295
+ }).immediate();
296
+ return { ok: true, applied, skipped, vector: readConversationClockVector(db) };
297
+ }
298
+
299
+ // ── WP-3 (docs/join-sync-followups-2-2026-11-01.html §5 R-10, §8 WP-3) ──────────────────────
300
+ // The OWNER-side entry points for the three /api/sync/conversation/v2/* handlers. Every one of
301
+ // them is a pure function of a DB handle: the route calls it via db.js's closed
302
+ // DB_OWNER_MUTATION_ACTIONS map (runMutation -> workers/db-owner-worker.js), so the seed page,
303
+ // the store DDL (`_ensure`) and the reads/writes all run on the owner worker's own connection
304
+ // instead of the request thread — which the request-zone guard forbids
305
+ // (lib/main-db-census.js, CTM_MAIN_DB_REQUEST_FORBIDDEN). That is the whole reason these exist:
306
+ // /vector and /changes seeded synchronously on the main handle and 500'd on every member (I-2).
307
+ // The payload shapes are unchanged (R-11); only where the work runs changed.
308
+
309
+ // One bounded seed page per call, on the owner's handle. Never throws: a seed fault must not
310
+ // take the feed down (the next call retries from the same cursor).
311
+ function _seedOwnerPage(db, seedPageLimit) {
312
+ try {
313
+ return require('./conversation-store-v2').seedConversationClockPage(db, { limit: seedPageLimit });
314
+ } catch (error) {
315
+ // REPORT: seed fault — the feed still answers, and the next call retries from the same cursor.
316
+ console.warn('[conversation-sync] seed page failed: ' + (error && error.message ? error.message : error));
317
+ return { seeded: 0, done: false, afterItemId: 0 };
318
+ }
319
+ }
320
+
321
+ /** GET /api/sync/conversation/v2/vector owner half — seed one page, then this node's vector. */
322
+ function ownerVector(db, { seedPageLimit } = {}) {
323
+ _seedOwnerPage(db, seedPageLimit);
324
+ return readConversationVector(db);
325
+ }
326
+
327
+ /** POST /api/sync/conversation/v2/changes owner half — seed one page, then one feed page. */
328
+ function ownerChanges(db, { since, limit, seedPageLimit } = {}) {
329
+ _seedOwnerPage(db, seedPageLimit);
330
+ return conversationChangesSince(db, { since, limit });
331
+ }
332
+
333
+ /**
334
+ * POST /api/sync/conversation/v2/apply owner half. The over-cap case still throws with
335
+ * `code = 'CONVERSATION_SYNC_ENTRIES_TOO_LARGE'` (applyConversationChanges' own contract): the
336
+ * DB-owner transport carries `error.code` back verbatim (workers/db-owner-worker.js's
337
+ * _serializeError -> lib/db-owner-worker-client.js's _finishRequest), so the route can keep
338
+ * mapping it to 413 without a second in-band flag.
339
+ */
340
+ function ownerApply(db, { sourceInstanceId, entries } = {}) {
341
+ return applyConversationChanges(db, { sourceInstanceId, entries });
342
+ }
343
+
344
+ module.exports = {
345
+ DEFAULT_CHANGES_LIMIT,
346
+ MAX_CHANGES_LIMIT,
347
+ MAX_APPLY_ENTRIES,
348
+ PEER_SYNC_SOURCE_PREFIX,
349
+ readConversationVector,
350
+ conversationChangesSince,
351
+ applyConversationChanges,
352
+ ownerVector,
353
+ ownerChanges,
354
+ ownerApply,
355
+ };
@@ -51,17 +51,19 @@ const { locateClaudeSettingsArg } = require('../native-lead-settings-argv');
51
51
  const codexSandboxArgs = require('../codex-sandbox-args');
52
52
  const codexLaunchHealth = require('../codex-launch-health');
53
53
  const dbModule = require('../../db-owner-client');
54
- const { buildTelemetryEnv, sanitizeInheritedAgentEnv } = require('../agent-presets');
54
+ const { buildTelemetryEnv, sanitizeInheritedAgentEnv, buildClaudeRendererLaunchEnv } = require('../agent-presets');
55
55
  const { sanitizeAnthropicAuth } = require('../sanitize-anthropic-auth');
56
56
  const { sanitizeOpenAiAuth } = require('../sanitize-openai-auth');
57
57
  // WP-2 (S-6): the D-1/D-2 sandbox env builder for a non-primary instance's launch env.
58
58
  const { buildSandboxCliEnv } = require('../../../shared/sandbox-cli-env');
59
59
  const { getPrivateSourceRecoveryEnvironment } = require('../lead-model-restore-reconciliation');
60
+ // WP-2b: the ONE Claude `--session-id` reader, shared with the source runtime (D-5/D-6).
61
+ const { claudeSessionIdArg } = require('../lead-switch-history-mode');
60
62
  const { getSession } = require('../../server-state');
61
63
  const { claudeMcpConfigArgs } = require('../lifecycle-wake-experiment');
62
64
  const { buildWorkerRoleArgs, WORKER_PROFILES } = require('./worker-role-profile');
63
65
  const {
64
- workerDeniedToolsArgs, codexWorkerDenyArgs, workerClaudeSettingsArgs, workerSandboxArgs,
66
+ workerDeniedToolsArgs, codexWorkerDenyArgs, workerClaudeSettingsArgs, workerSandboxArgs, dshWorkerSandbox,
65
67
  } = require('../session-create/worker-launch-sandbox');
66
68
 
67
69
  function resolveCreateLaunchParams(ctx) {
@@ -174,6 +176,35 @@ function resolveCreateLaunchParams(ctx) {
174
176
  console.warn('[native-model-args] skipped:', e.message);
175
177
  }
176
178
  }
179
+
180
+ // --- OpenCode 2.x launch shape (R-1, R-2) ---
181
+ // docs/opencode-v2-model-switch-standalone-plugin-2026-10-30.html §5.1: a plain 2.x TUI attaches to a
182
+ // shared background service that ignores the tab's env/OPENCODE_CONFIG_CONTENT, so EVERY 2.x
183
+ // launch (fresh AND restore — a restore's argv comes from preparedLaunchPlan.args, built by
184
+ // lib/agent-capabilities.js's resumeSpec) runs its own private server via `--standalone`.
185
+ // R-2: CTM also chooses the 2.x session id, `ses_<CTM session UUID without dashes>`, the twin of
186
+ // Claude's deterministic `--session-id` injection below, so a restore has a precise `--session`.
187
+ // The line is the same peek rule as the model-args gate above (R-6): server.js's handleCreate
188
+ // has already awaited primeOpencodeLine() for an OpenCode launch before this argv is built, so
189
+ // this read is sync and fs-free. A v1 or null line leaves `args` byte-identical to before.
190
+ let _injectedOpencodeSessionId = null;
191
+ if (normalizeAgentType(createAgentType) === 'opencode') {
192
+ try {
193
+ const cli = require('../opencode-cli');
194
+ if (cli.peekOpencodeLine() === 'v2') {
195
+ if (!args.includes('--standalone')) args = ['--standalone', ...args];
196
+ const hasSessionArg = args.some(a => a === '--session' || a === '-s' || a === '--continue' || a === '-c'
197
+ || (typeof a === 'string' && a.startsWith('--session=')));
198
+ if (!hasSessionArg && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id)) {
199
+ const minted = `ses_${id.replace(/-/g, '')}`;
200
+ args = [...args, '--session', minted];
201
+ _injectedOpencodeSessionId = minted;
202
+ }
203
+ }
204
+ } catch (e) {
205
+ console.warn('[opencode-v2-launch] skipped:', e.message);
206
+ }
207
+ }
177
208
  if (createAgentType === 'codex' && codexResumeCwdGuardEnabled) {
178
209
  args = withCodexResumeCd(args, cwd);
179
210
  }
@@ -352,10 +383,23 @@ function resolveCreateLaunchParams(ctx) {
352
383
  const resume = preparedLaunchPlan?.resume || {};
353
384
  const providerThreadId = resume.providerResumeId || resume.providerResumeID || resume.sessionId || resume.agentSessionId || '';
354
385
  const routeId = msg._launchLeadRoute?.routeId || null;
386
+ // WP-2b (§5 D-5/D-6): a FRESH rollback of a never-prompted source is the one shape that carries
387
+ // neither a resume selector nor a model override. `freshRelaunch` is the record's own fact, so an
388
+ // older record (absent -> false) keeps today's exact resume-shaped rule -- including the empty
389
+ // provider thread it could never have matched.
390
+ const freshRelaunch = sourceRecoveryEnvironment.freshRelaunch === true;
391
+ // The launch's resolved thread: for a fresh rollback the plan has no resume selector at all, so ''
392
+ // is acceptable -- but the argv must then name the SOURCE's own thread through `--session-id`.
393
+ const threadMatches = freshRelaunch
394
+ ? providerThreadId === '' || providerThreadId === sourceRecoveryEnvironment.providerThreadId
395
+ : providerThreadId === sourceRecoveryEnvironment.providerThreadId;
396
+ const argvThreadMatches = !freshRelaunch || createAgentType !== 'claude'
397
+ || claudeSessionIdArg(args) === sourceRecoveryEnvironment.providerThreadId;
355
398
  if (!msg._isRestore || msg._leadModelTransition || sourceRecoveryEnvironment.ctmSessionId !== id
356
399
  || sourceRecoveryEnvironment.agent !== createAgentType
357
- || sourceRecoveryEnvironment.providerThreadId !== providerThreadId
358
- || sourceRecoveryEnvironment.modelId !== msg._launchModelOverride
400
+ || !threadMatches
401
+ || sourceRecoveryEnvironment.modelId !== String(msg._launchModelOverride || '')
402
+ || !argvThreadMatches
359
403
  || sourceRecoveryEnvironment.routeId !== routeId) {
360
404
  throw Object.assign(new Error('Captured source environment does not match the prepared recovery owner.'), {
361
405
  code: 'source_recovery_environment_owner_mismatch',
@@ -396,6 +440,12 @@ function resolveCreateLaunchParams(ctx) {
396
440
  // This adds OTEL_EXPORTER_OTLP_ENDPOINT → http://localhost:<PORT> for agents
397
441
  // that emit OTLP logs, plus CTM_SESSION_ID for our hook scripts to read.
398
442
  Object.assign(env, buildTelemetryEnv(id, cmd, PORT, env));
443
+ // WP-2 (docs/claude-restore-seed-alt-screen-trap-2026-10-27.html R-4, §8 WP-2): restate the
444
+ // user's own Claude renderer choice through Claude's own override, so a CTM crash-restore
445
+ // storm cannot silently flip it via the fullscreen boot canary. Runs AFTER the non-primary
446
+ // sandbox env rebuild above, so a dev/test instance reads the sandbox HOME's settings, never
447
+ // the real user's.
448
+ Object.assign(env, buildClaudeRendererLaunchEnv(cmd, env));
399
449
  if (createAgentType === 'codex') {
400
450
  try {
401
451
  const launchHealth = preparedLaunchPlan.maintenance?.codexHealth || { args, mcpReport: { disabled: [] } };
@@ -466,13 +516,10 @@ function resolveCreateLaunchParams(ctx) {
466
516
  && msg.titleIntent !== 'default' && !!requestedTitle;
467
517
  let title = requestedTitle;
468
518
  if (!title) {
469
- const agentName = cmd.includes('claude') ? 'Claude Code'
470
- : cmd.includes('codex') ? 'Codex'
471
- : cmd.includes('gemini') ? 'Gemini'
472
- : cmd.includes('opencode') ? 'OpenCode'
473
- : cmd.includes('cursor') ? 'Cursor'
474
- : cmd.includes('aider') ? 'Aider'
475
- : null;
519
+ // S-3 (D-5): the product-facing title comes from the ONE detector; 'aider' stays a local fallback
520
+ // ('walle' and 'shell' still title as `Shell: <cwd>`, unchanged).
521
+ const AGENT_TITLE = { dsh: 'DeepSeek Harness', claude: 'Claude Code', codex: 'Codex', gemini: 'Gemini', opencode: 'OpenCode', cursor: 'Cursor' };
522
+ const agentName = AGENT_TITLE[detectAgentType(cmd)] || (cmd.includes('aider') ? 'Aider' : null);
476
523
  if (agentName) {
477
524
  const shortCwd = cwd.replace(serverConfig.HOME || '', '~');
478
525
  title = `${agentName}: ${shortCwd}`;
@@ -506,6 +553,9 @@ function resolveCreateLaunchParams(ctx) {
506
553
  claudeResumeId,
507
554
  isResumeLaunch,
508
555
  _injectedSessionId,
556
+ // R-2: deliberately NOT `_injectedSessionId` — that one drives Claude-only JSONL/project-dir
557
+ // side effects downstream in lib/session-create/provider-process.js. Only one can ever be set.
558
+ _injectedOpencodeSessionId,
509
559
  _approvalHookEnabled,
510
560
  _approvalHookToken,
511
561
  restoreSeedSnapshot,
@@ -696,7 +746,7 @@ function resolveWorkerLaunchParams(opts = {}) {
696
746
  agent, alias, model, token, proxyPort,
697
747
  briefPath, cwd, reportPath,
698
748
  } = opts;
699
- if (agent !== 'claude' && agent !== 'codex') {
749
+ if (agent !== 'claude' && agent !== 'codex' && agent !== 'dsh') {
700
750
  throw new TypeError(`resolveWorkerLaunchParams: unsupported agent "${agent}"`);
701
751
  }
702
752
  if (!alias) throw new TypeError('resolveWorkerLaunchParams: alias required');
@@ -715,6 +765,40 @@ function resolveWorkerLaunchParams(opts = {}) {
715
765
  throw new TypeError(`resolveWorkerLaunchParams: unsupported worker profile "${workerProfile}"`);
716
766
  }
717
767
  const prompt = typeof opts.prompt === 'string' ? opts.prompt : buildWorkerPrompt({ briefPath, cwd, reportPath, profile: workerProfile });
768
+ // WP-14 (docs/deepseek-harness-acp-agent-2026-10-28.html §9b W-2): a dsh worker has NO argv. It is
769
+ // ONE ACP child (lib/acp/dsh-session-runtime.js) started from the create request's own resolved
770
+ // launch env (server.js `buildLaunchEnv` -> lib/session-create/dsh-session.js) and spoken to over
771
+ // ACP, so this branch returns the ACP START PARAMS instead of an argv:
772
+ // * `env` — the worker's own proxy credential (ANTHROPIC_AUTH_TOKEN, the one key pi-ai's built-in
773
+ // anthropic provider turns into `Authorization: Bearer …`, §6.6 OQ-1) plus the worker mark the
774
+ // env strip reads (agent-presets.js) and the child's permission mode (W-9). The routed baseURL
775
+ // is deliberately NOT here: the dsh route patch declares it (D-21/§6.6) and the runtime strips
776
+ // the inherited provider keys, so the credential and the mark are all that travel.
777
+ // * `acp` — the brief as the FIRST PROMPT, the report path, the routed pick and the MCP set
778
+ // (WP-10 already materializes `mcpServers`; the dsh create path converts them to ACP entries).
779
+ // Sits BEFORE `workerSandboxArgs` precisely because that table's harness ternary is claude/codex only.
780
+ if (agent === 'dsh') {
781
+ const mcpServers = (opts.mcpServers && typeof opts.mcpServers === 'object' && !Array.isArray(opts.mcpServers))
782
+ ? opts.mcpServers
783
+ : {};
784
+ const { permissionMode } = dshWorkerSandbox({ profile: workerProfile });
785
+ return {
786
+ cmd: 'dsh',
787
+ args: [],
788
+ env: {
789
+ CTM_WORKER_AGENT: agent,
790
+ CTM_WORKER_TOKEN: token,
791
+ ANTHROPIC_AUTH_TOKEN: token,
792
+ DSH_PERMISSION_MODE: permissionMode,
793
+ },
794
+ acp: {
795
+ prompt, briefPath, cwd, reportPath,
796
+ routeAlias: alias, model,
797
+ mcpServers,
798
+ permissionMode,
799
+ },
800
+ };
801
+ }
718
802
  const port = Number(proxyPort);
719
803
  // WP-4 (delegate-codex-lessons §5 D-29): the lead posture's launch half. Absent posture =
720
804
  // today's argv exactly (no sandbox key, no extra args); a resolved posture adds the
@@ -21,12 +21,12 @@ function normalizeWorkerRequest(opts = {}) {
21
21
  const invalid = (code, message) => { const error = new Error(message); error.code = code; throw error; };
22
22
  const agentInput = opts.agent;
23
23
  const harnessInput = opts.harness;
24
- if (agentInput !== undefined && agentInput !== 'claude' && agentInput !== 'codex') invalid('agent_invalid', 'agent must be claude or codex');
25
- if (harnessInput !== undefined && harnessInput !== 'claude-code' && harnessInput !== 'codex-cli') invalid('harness_invalid', 'harness must be claude-code or codex-cli');
26
- const harnessAgent = harnessInput === 'codex-cli' ? 'codex' : harnessInput === 'claude-code' ? 'claude' : null;
24
+ if (agentInput !== undefined && agentInput !== 'claude' && agentInput !== 'codex' && agentInput !== 'dsh') invalid('agent_invalid', 'agent must be claude, codex or dsh');
25
+ if (harnessInput !== undefined && harnessInput !== 'claude-code' && harnessInput !== 'codex-cli' && harnessInput !== 'dsh-acp') invalid('harness_invalid', 'harness must be claude-code, codex-cli or dsh-acp');
26
+ const harnessAgent = harnessInput === 'codex-cli' ? 'codex' : harnessInput === 'claude-code' ? 'claude' : harnessInput === 'dsh-acp' ? 'dsh' : null;
27
27
  if (agentInput && harnessAgent && agentInput !== harnessAgent) invalid('harness_conflict', 'agent and harness identify different coding CLIs');
28
28
  const agent = harnessAgent || agentInput || 'claude';
29
- const harness = harnessInput || (agent === 'codex' ? 'codex-cli' : 'claude-code');
29
+ const harness = harnessInput || (agent === 'codex' ? 'codex-cli' : agent === 'dsh' ? 'dsh-acp' : 'claude-code');
30
30
  const workerProfile = opts.workerProfile === undefined ? 'implementation' : opts.workerProfile;
31
31
  if (!WORKER_PROFILES.includes(workerProfile)) invalid('worker_profile_invalid', 'worker_profile must be legacy, implementation or review');
32
32
  return { agent, harness, workerProfile };
@@ -0,0 +1,51 @@
1
+ 'use strict';
2
+
3
+ // WP-3 (docs/session-create-box-restart-and-git-lane-starvation-2026-10-30.html §5 D-4,
4
+ // §6.3, §8, §9 S-9..S-11, S-17): a "create in flight" is invisible to the restart guard.
5
+ // On 2026-10-30 at 02:20:31Z the MBP box's repo-root fleet-apply script restarted CTM 5 s
6
+ // after the operator opened the new-session modal for that box, and the create failed.
7
+ // fleet-apply polls GET /api/instance/update-gate, and the non-force POST /api/restart/ctm
8
+ // refusal both read _restartGuardSnapshot, which summarized LIVE SESSIONS ONLY.
9
+ //
10
+ // This module is the missing signal: a short, in-process lease noted by the LOCAL create
11
+ // handlers (the machine that would restart is the one that notes it -- a machine-routed
12
+ // proxy branch must never note, or a hub would block its own restart for a box's create).
13
+ // It is deliberately tiny: a clock, an expiry, and the last route/hop diagnostic.
14
+ //
15
+ // The lease is advisory only -- the guard turns it into a 409 (or a blocker row), and
16
+ // ?force=true still overrides it exactly as it overrides a live session.
17
+
18
+ const CREATE_INTENT_LEASE_MS = 120000;
19
+
20
+ function createIntentLease({ now = Date.now, leaseMs = CREATE_INTENT_LEASE_MS } = {}) {
21
+ let until = 0;
22
+ let last = null; // { route, forwardedBy, at }
23
+ return {
24
+ note(route, forwardedBy = '') {
25
+ const t = now();
26
+ until = t + leaseMs;
27
+ last = { route: String(route || ''), forwardedBy: String(forwardedBy || ''), at: t };
28
+ },
29
+ active() {
30
+ return now() < until ? { ...last, untilMs: until } : null;
31
+ },
32
+ };
33
+ }
34
+
35
+ const processLease = createIntentLease();
36
+
37
+ /** Note an in-flight create on THIS machine. `ctxOrReq` may be an HTTP req or a WS ctx. */
38
+ function noteCreateIntent(route, ctxOrReq) {
39
+ let forwardedBy = '';
40
+ try {
41
+ forwardedBy = require('./remote-machine-create').forwardedByOf(ctxOrReq) || '';
42
+ } catch { /* expected: a hop-header read fault only loses the diagnostic field */ }
43
+ processLease.note(route, forwardedBy);
44
+ }
45
+
46
+ module.exports = {
47
+ CREATE_INTENT_LEASE_MS,
48
+ createIntentLease,
49
+ noteCreateIntent,
50
+ activeCreateIntent: () => processLease.active(),
51
+ };