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
@@ -25,6 +25,9 @@
25
25
  const { createLeadModelSwitch } = require('./lead-model-switch');
26
26
  const { writeCodexDefaultModel, writeCodexDefaultEffort, listCodexModels } = require('./codex-default-model');
27
27
  const { createLeadModelOptions, claudeEfforts } = require('./lead-model-options');
28
+ const { createOpencodeLaunchModel } = require('./opencode-launch-model');
29
+ const { createDshLaunchModel } = require('./acp/dsh-launch-model');
30
+ const opencodeCli = require('./opencode-cli');
28
31
  const { requestWalleAllPages } = require('./walle-paged-read');
29
32
  const { modelsMatch, CLAUDE_ALIASES } = require('./lead-model-switch');
30
33
  const leadModelObserver = require('./lead-model-observer');
@@ -33,15 +36,28 @@ const { ADAPTER_VERSION } = require('./harness-model-profile');
33
36
  const { CODEX_CONTROL_ADAPTER } = require('./codex-harness-qualification');
34
37
  const { summarizeRestartGuard } = require('./restart-guard');
35
38
  const { getLeadRouteReadiness } = require('./session-create/proxy-route-readiness');
39
+ // WP-1 (docs/lead-route-model-invocability-preflight-2026-10-28.html §5 D-1/D-2, §6.2): the routed
40
+ // switch proves the pair can generate BEFORE the session is parked.
41
+ const { createLeadRoutePreflight } = require('./lead-route-preflight');
36
42
  const { workerCapabilityForModel } = require('./model-proxy/routes');
37
43
  const { effectiveOutputCap } = require('./model-proxy/output-cap');
38
44
  const { modelExecutionRecipeMetadata } = require('./model-execution-recipe-metadata');
39
45
  const { capturePrivateSourceEnvironment, attachPrivateSourceRecoveryEnvironment } = require('./lead-model-restore-reconciliation');
40
46
  const { createLeadModelTransitionRuntime } = require('./lead-model-transition-runtime');
47
+ // R-1: the ONE wire mapping. An OpenCode lead (and its routed workers) speaks the claude wire, so
48
+ // route servability and the harness profile are asked of 'claude' -- never of 'opencode'.
49
+ const { leadWireAgent } = require('./lead-wire-agent');
41
50
  const {
42
51
  readSessionModelActionIntent, projectSessionModelConflictContext, sameCoreOwner, coreOwnerTuple,
43
52
  } = require('./session-model-action-preconditions');
44
53
  const { validateRecipe } = require('./lead-model-transition-store');
54
+ // WP-0/WP-1 (§6/§7): the ONE history-mode module. The planner's pre-park thread check and the
55
+ // restore receipt call the SAME clause, and the recipes carry the SAME mode the controller resolved.
56
+ const { resolveSourceHistoryMode, recipeHistoryMode, sourceModelOrigin, claudeSessionIdArg,
57
+ targetThreadClause } = require('./lead-switch-history-mode');
58
+ const { freshEvidenceInputs, freshRelaunchArgs } = require('./park-fresh-relaunch');
59
+ // WP-2 S-9: the ONE versioned proxy-filter receipt this launch path stamps onto its own recipe.
60
+ const { CODEX_HISTORICAL_TOOLS_FILTER_RECEIPT } = require('./model-proxy/historical-tool-declarations');
45
61
  const { validateSessionModelIntentRequest } = require('./session-model-intent-contract');
46
62
  const { canonicalJson } = require('../../shared/coding-model-deployment');
47
63
  const { findCatalogRoute } = require('./coding-catalog-route');
@@ -306,12 +322,49 @@ function createLeadTransitionTargetPlanner(deps = {}) {
306
322
  return { ok: false, reason: 'lead_target_resume_changed' };
307
323
  }
308
324
  }
325
+ // WP-1 D-4 (§7): the SAME thread clause the restore receipt will run, evaluated here so a plan the
326
+ // receipt would refuse is never allowed to park the source. The recipe pair this planner's
327
+ // transition will be written with is exactly (source thread, resolved mode), so the clause is
328
+ // asked of that pair. A 'present' source REQUIRES the plan to resume its own thread -- a
329
+ // both-absent plan under 'present' is the drift this refuses (the never-prompted source is
330
+ // declared 'none' by the controller instead); a 'none' source requires a plan with no resume
331
+ // target at all.
332
+ const historyMode = recipeHistoryMode(source);
333
+ if (!targetThreadClause({ source: { historyMode }, target: { historyMode, providerThreadId: canonicalThreadId },
334
+ launchPlan: result.plan })) {
335
+ return { ok: false, reason: 'lead_target_resume_inconsistent' };
336
+ }
309
337
  noteLaunchPlanReadinessGeneration(result.plan);
310
338
  return { ok: true, generation, launchPlan: result.plan, sourceGeneration };
311
339
  };
312
340
 
313
341
  }
314
342
 
343
+ /**
344
+ * WP-0 D-1/D-2 (§6): the SOURCE history-mode resolver the switch asks BEFORE it claims a transition.
345
+ * It composes the SAME inputs park already injects (`prepareLifecycleTranscript` + the off-thread
346
+ * resume ladder), so the switch and park can never disagree about whether a source has history. The
347
+ * composition lives here; server.js only supplies the two inputs.
348
+ */
349
+ function createSourceHistoryModeResolver({ getSession, detectAgentType, lifecycleResumeTarget, prepareLifecycleTranscript, resolveRung } = {}) {
350
+ return async function resolveSourceHistoryModeForSession(sessionId) {
351
+ try {
352
+ const session = getSession(String(sessionId || ''));
353
+ if (!session) return 'present';
354
+ const agentType = detectAgentType(String(session.cmd || ''));
355
+ const resumeId = lifecycleResumeTarget(session, agentType) || '';
356
+ const prepared = typeof prepareLifecycleTranscript === 'function' ? await prepareLifecycleTranscript(session) : null;
357
+ const { resumeEvidence, jsonlPath } = freshEvidenceInputs({ agentType,
358
+ transcript: prepared?.transcript || null, codexRolloutPath: prepared?.codexRolloutPath || '' });
359
+ return await resolveSourceHistoryMode({ agentType, ctmSessionId: String(sessionId), jsonlPath, resumeId, resumeEvidence, resolveRung });
360
+ } catch {
361
+ // HANDLE: D-2 -- a missing session, an unreadable transcript or a failed ladder read is never
362
+ // evidence that the conversation is gone, so the switch keeps today's 'present' path.
363
+ return 'present';
364
+ }
365
+ };
366
+ }
367
+
315
368
  function createLeadModelSwitchRuntime(host) {
316
369
  const {
317
370
  getSession, detectAgentType, lifecycleResumeTarget, extractResumeTarget,
@@ -335,6 +388,41 @@ function createLeadModelSwitchRuntime(host) {
335
388
  finalizeHistoryHandoff: typeof host.finalizeHistoryHandoff === 'function'
336
389
  ? host.finalizeHistoryHandoff : async () => ({ ok: false, reason: 'history_runtime_unavailable' }) };
337
390
 
391
+ // WP-0 D-1: an INJECTED resolver wins (tests drive the mode directly); otherwise it is composed
392
+ // here from the two park inputs the server already injects. A host with neither keeps the
393
+ // controller's own 'present' default rather than failing the switch.
394
+ const resolveSourceHistoryModeForSession = typeof host.resolveSourceHistoryMode === 'function'
395
+ ? host.resolveSourceHistoryMode
396
+ : createSourceHistoryModeResolver({ getSession, detectAgentType, lifecycleResumeTarget,
397
+ prepareLifecycleTranscript: host.prepareLifecycleTranscript, resolveRung: host.resolveSourceRung });
398
+
399
+ // WP-2 D-6 (§5/§6): the captured launch argv a source lifetime was started with. `getSessionInfo`
400
+ // already exposes it as `launchContext`; a session with no capture has no argv to read.
401
+ function capturedLaunchArgs(session) {
402
+ const args = session?._modelTransitionLaunchContext?.args;
403
+ return Array.isArray(args) ? args : null;
404
+ }
405
+
406
+ // D-6: an argv that names a model of its own is never a CLI default. `--model=x` is the same flag
407
+ // as `--model x` for the Claude CLI, so both spellings are matched here (and `-m`).
408
+ function argvNamesModel(args) {
409
+ return args.some((arg) => {
410
+ const text = String(arg);
411
+ return text === '--model' || text === '-m' || text.startsWith('--model=');
412
+ });
413
+ }
414
+
415
+ // D-6: CTM's own record of an explicit lead-model choice for this session (`session_lead_model`).
416
+ // The read is deliberately three-valued: a missing getter or a THROWN read is 'unknown', never
417
+ // 'absent' — "an unreadable store is not evidence that the conversation is gone" applied to the pin,
418
+ // so an unreadable row can never be read as proof of a CLI default (AC-2a: a pin READ ERROR records
419
+ // modelOrigin 'observed').
420
+ function leadModelPinEvidence(sessionId) {
421
+ if (!dbModule || typeof dbModule.getSessionLeadModel !== 'function') return 'unknown';
422
+ try { return dbModule.getSessionLeadModel(sessionId) ? 'present' : 'absent'; }
423
+ catch { return 'unknown'; } // REPORT: the caller reads 'unknown' as "a pin may exist", not as absence
424
+ }
425
+
338
426
  function getSessionInfo(sessionId) {
339
427
  const session = getSession(sessionId);
340
428
  if (!session) return null;
@@ -350,17 +438,27 @@ function createLeadModelSwitchRuntime(host) {
350
438
  ? session._modelProcessGeneration : Number(session._launchPlanGeneration || 0);
351
439
  const executionRecipe = modelExecutionRecipeMetadata(session._modelExecutionRecipe,
352
440
  { sessionId, processGeneration: actualGeneration }).modelExecutionRecipe || null;
441
+ // §5 D-6: the projector names WHY an uncapturable environment is uncapturable, so the gate's refusal
442
+ // (WP-3) and the client sentence can say it. 'no_capture' is the only reason 'unknown' can mean.
353
443
  const launchEnvironment = session._launchEnvOverridesCaptureState === 'unavailable'
354
- ? Object.freeze({ state: 'unavailable', values: null })
444
+ ? Object.freeze({ state: 'unavailable', values: null, reason: session._launchEnvUnavailableReason || null })
355
445
  : session._launchEnvOverridesCaptureState === 'unknown' || !Object.hasOwn(session, '_launchEnvOverrides')
356
- ? Object.freeze({ state: 'unknown', values: null })
446
+ ? Object.freeze({ state: 'unknown', values: null, reason: 'no_capture' })
357
447
  : session._launchEnvOverridesCaptureState === 'captured-empty'
358
- ? Object.freeze({ state: 'captured', values: Object.freeze({}) })
359
- : capturePrivateSourceEnvironment(session._launchEnvOverrides);
448
+ ? Object.freeze({ state: 'captured', values: Object.freeze({}), reason: null })
449
+ : Object.freeze({ ...capturePrivateSourceEnvironment(session._launchEnvOverrides), reason: null });
450
+ // D-6: the source model's ORIGIN, captured where the facts are. True only when every proof of a
451
+ // real model choice is absent: no session model_id, a captured argv that names no `--model`/`-m`
452
+ // (and must exist at all to be evidence), and no readable `session_lead_model` pin row.
453
+ const launchArgs = capturedLaunchArgs(session);
454
+ const cliDefaultModel = (typeof session.model_id !== 'string' || session.model_id === '')
455
+ && launchArgs !== null && !argvNamesModel(launchArgs)
456
+ && leadModelPinEvidence(sessionId) === 'absent';
360
457
  return {
361
458
  type: session.type || 'pty',
362
459
  agent,
363
460
  modelId: typeof session.model_id === 'string' ? session.model_id : '',
461
+ cliDefaultModel,
364
462
  resumeId,
365
463
  processGeneration: executionRecipe?.processGeneration || actualGeneration || 0,
366
464
  providerThreadId: executionRecipe?.providerThreadId || resumeId || session.agentSessionId || session._claudeSessionId || session._providerResumeId || '',
@@ -488,6 +586,22 @@ function createLeadModelSwitchRuntime(host) {
488
586
  return await requestOwner('getSessionModelIntent', { ctmSessionId: String(sessionId || '') });
489
587
  }
490
588
 
589
+ // WP-1 (§6.2): the ONE preflight, built once per runtime. It reuses the SAME readiness the
590
+ // resolver already reads (`host.leadRouteDeps?.readiness`, else the configured singleton) and the
591
+ // SAME proxy supervisor the launch path uses; `host.leadRouteDeps.preflight` is the test seam.
592
+ // The report is a fire-and-forget health hint for Wall-E's per-pair circuit — it never changes
593
+ // the switch (REPORT disposition inside the preflight).
594
+ const leadRoutePreflight = typeof host.leadRouteDeps?.preflight === 'function'
595
+ ? host.leadRouteDeps.preflight
596
+ : createLeadRoutePreflight({
597
+ // Production injects no leadRouteDeps and uses the real supervisor. An injected leadRouteDeps
598
+ // (tests) without an `oauthProxySupervisor` probes nothing, so no test can start a real proxy.
599
+ getSupervisor: () => (host.leadRouteDeps ? host.leadRouteDeps.oauthProxySupervisor || null : require('./oauth-proxy-supervisor')),
600
+ getReadiness: () => host.leadRouteDeps?.readiness || getLeadRouteReadiness(),
601
+ report: (body) => requestWalleData('/api/models/route-health', { method: 'POST', body }, 5000),
602
+ diagnostic: recordSessionDiagnostic,
603
+ });
604
+
491
605
  // D-2 (§6.5): the routed-lead resolver. Every refusal carries a SPECIFIC reason AND a specific
492
606
  // sentence (never the generic "The target route is unavailable." that pointed at a healthy route).
493
607
  // The service entry below is the ONE refusal-logging seam, so a refused switch is diagnosable from
@@ -508,9 +622,13 @@ function createLeadModelSwitchRuntime(host) {
508
622
  }
509
623
  const readiness = host.leadRouteDeps?.readiness || getLeadRouteReadiness();
510
624
  if (!readiness) return refusal('proxy_unconfigured');
625
+ // R-1/S-8: an OpenCode lead dials the claude wire -- the route must be servable to 'claude', and
626
+ // the harness profile below is the claude-code one. `agent` itself still decides the native
627
+ // shortcut above (isNativeLeadModel, codex's model/list), so Claude/Codex stay byte-identical.
628
+ const wire = leadWireAgent(agent);
511
629
  let readinessResult;
512
630
  try {
513
- readinessResult = await readiness.resolveRoute({ route: routeId, model: modelId, agent, signal });
631
+ readinessResult = await readiness.resolveRoute({ route: routeId, model: modelId, agent: wire, signal });
514
632
  } catch (err) {
515
633
  return refusal(err && err.code ? err.code : 'route_invalid', String(err && err.message || err).slice(0, 200));
516
634
  }
@@ -527,12 +645,21 @@ function createLeadModelSwitchRuntime(host) {
527
645
  const deployment = readinessResult?.deployment || null;
528
646
  if (!deployment || readinessResult.metadataUnavailable) return refusal('model_metadata_unavailable');
529
647
  if (!Number.isSafeInteger(deployment.contextTokens) || deployment.contextTokens <= 0) return refusal('model_context_unknown');
648
+ // WP-1 D-1: prove the pair can generate BEFORE anything touches the session. This runs after
649
+ // every existence/metadata check (so a probe is never spent on a pair that cannot be prepared
650
+ // anyway) and before `prepareLeadProfile`/park. Its refusal already has the
651
+ // `{ ok:false, reason, message }` shape the resolver returns everywhere else.
652
+ // §12a rule 1: probe the wire THIS harness will dial. Codex reads the Responses face, Claude (and
653
+ // an OpenCode lead, R-1) the Anthropic Messages face; without it the probe would dial its own
654
+ // preferred wire and could refuse a pair the lead's harness runs perfectly well.
655
+ const pre = await leadRoutePreflight({ routeId, modelId, sessionId, signal, face: wire === 'codex' ? 'responses' : 'anthropic' });
656
+ if (pre && pre.refusal) return pre.refusal;
530
657
  let preparedProfile;
531
658
  try {
532
659
  if (typeof prepareLeadProfile !== 'function' || typeof getDataDir !== 'function') return refusal('model_profile_unavailable');
533
660
  const session = getSession(sessionId);
534
661
  preparedProfile = await prepareLeadProfile({
535
- deployment, harness: agent === 'codex' ? 'codex-cli' : 'claude-code',
662
+ deployment, harness: wire === 'codex' ? 'codex-cli' : 'claude-code',
536
663
  binaryPath: session?.cmd || '', adapterVersion: ADAPTER_VERSION,
537
664
  routeId, providerId: readinessResult.providerId || routeId, wireModelId: modelId,
538
665
  artifactRoot: path.join(getDataDir(), 'model-profiles'), signal,
@@ -561,40 +688,71 @@ function createLeadModelSwitchRuntime(host) {
561
688
  };
562
689
  }
563
690
 
691
+ // The ONE park and the ONE restore, defined once. The journaled Claude/Codex machine and the
692
+ // OpenCode launch controller (WP-1) receive these EXACT lambdas: a relaunch has one implementation,
693
+ // so the private restore intent, the one-shot override and the deferred-restore handoff cannot
694
+ // drift between the two controllers.
695
+ const parkForLeadSwitch = (sessionId, opts) => parkSession(sessionId, opts);
696
+ const restoreForLeadSwitch = async (sessionId, opts) => {
697
+ const placeholder = getSession(sessionId);
698
+ // A wake already in flight (or a session that already woke) started WITHOUT the override:
699
+ // starting again would dedupe onto it and lose the flag, so report "not restarted on the
700
+ // requested model" instead of pretending.
701
+ if (!placeholder || placeholder._restoreDeferred !== true || placeholder._activationStartPromise) return false;
702
+ const restoreOptions = {
703
+ // Preserve the controller's trusted private restore intent across this wrapper. The
704
+ // server later normalizes activation priority but uses the original options to forward
705
+ // the non-enumerable transition claim into its exact target-owner authorization boundary.
706
+ source: opts.source === 'lead-model' && opts.leadModelTransition ? 'lead-model' : 'remote-activate',
707
+ clientKind: 'api',
708
+ launchModelOverride: opts.launchModelOverride,
709
+ launchEffortOverride: opts.launchEffortOverride,
710
+ // D-6: the routed launch plan rides this ONE restore (an object = routed, null = explicitly
711
+ // native so the stored route row does not re-apply). Absent = the stored row applies.
712
+ ...(Object.hasOwn(opts, 'launchLeadRoute') ? { launchLeadRoute: opts.launchLeadRoute } : {}),
713
+ transitionSignal: opts.signal,
714
+ preparedLaunchPlan: opts.preparedLaunchPlan,
715
+ expectedProviderThreadId: opts.expectedProviderThreadId || null,
716
+ expectedSourceGeneration: opts.expectedSourceGeneration || null,
717
+ };
718
+ if (opts.leadModelTransition) Object.defineProperty(restoreOptions, 'leadModelTransition', {
719
+ value: opts.leadModelTransition, enumerable: false,
720
+ });
721
+ const started = await startDeferredRestoreSession(placeholder, restoreOptions);
722
+ return started === true;
723
+ };
724
+
725
+ // A routed lead switch is validated against the SAME readiness the delegate path uses (WP-1b):
726
+ // the route must resolve (exist/enabled/routable/servable) for this agent, and the model must be
727
+ // in Wall-E's coding catalog (that is where its display name and context window come from).
728
+ // D-2: the ONE refusal-logging seam. The specific reason reaches ctm.log, and the message it
729
+ // returns (never generic) is what the caller stores as the request's detail. It is a named lambda
730
+ // because the OpenCode launch adapter (WP-3) is handed the SAME one -- no second resolver.
731
+ const resolveLeadRoute = async (agent, routeId, modelId, sessionId, opts) => {
732
+ const v = await resolveLeadRouteChecked(agent, routeId, modelId, sessionId, opts);
733
+ if (!v.ok) {
734
+ process.stderr.write(`[lead-model] route refused session=${String(sessionId).slice(0, 8)} route=${routeId} model=${modelId} reason=${v.reason}\n`);
735
+ }
736
+ return v;
737
+ };
738
+ // WP-3: the session_lead_route reader/writer the journaled machine already uses. Named lambdas so
739
+ // the OpenCode launch adapter gets the EXACT same persistence -- one row, one agent encoding.
740
+ const getLeadRoute = (sessionId) => {
741
+ try { return dbModule.getSessionLeadRoute(sessionId); }
742
+ catch (error) { recordSessionDiagnostic(sessionId, 'lead-route-read-failed', { message: String(error && error.message || error).slice(0, 200) }); /* REPORT: treated as no stored route, so a native switch adds no explicit null */ return null; }
743
+ };
744
+ const persistLeadRoute = async (sessionId, agent, route) => {
745
+ if (route) return await dbModule.setSessionLeadRoute({ ctmSessionId: sessionId, agent, routeId: route.routeId, model: route.model, display: route.display, contextWindow: route.contextWindow, profileFingerprint: route.profileFingerprint, factsRevision: route.factsRevision, processGeneration: route.processGeneration, transitionId: route.transitionId });
746
+ return await dbModule.deleteSessionLeadRoute(sessionId);
747
+ };
748
+
564
749
  const service = createLeadModelSwitch({
565
750
  getSessionInfo,
566
751
  isIdle,
567
752
  captureActionContext,
568
753
  readModelIntent,
569
- park: (sessionId, opts) => parkSession(sessionId, opts),
570
- restore: async (sessionId, opts) => {
571
- const placeholder = getSession(sessionId);
572
- // A wake already in flight (or a session that already woke) started WITHOUT the override:
573
- // starting again would dedupe onto it and lose the flag, so report "not restarted on the
574
- // requested model" instead of pretending.
575
- if (!placeholder || placeholder._restoreDeferred !== true || placeholder._activationStartPromise) return false;
576
- const restoreOptions = {
577
- // Preserve the controller's trusted private restore intent across this wrapper. The
578
- // server later normalizes activation priority but uses the original options to forward
579
- // the non-enumerable transition claim into its exact target-owner authorization boundary.
580
- source: opts.source === 'lead-model' && opts.leadModelTransition ? 'lead-model' : 'remote-activate',
581
- clientKind: 'api',
582
- launchModelOverride: opts.launchModelOverride,
583
- launchEffortOverride: opts.launchEffortOverride,
584
- // D-6: the routed launch plan rides this ONE restore (an object = routed, null = explicitly
585
- // native so the stored route row does not re-apply). Absent = the stored row applies.
586
- ...(Object.hasOwn(opts, 'launchLeadRoute') ? { launchLeadRoute: opts.launchLeadRoute } : {}),
587
- transitionSignal: opts.signal,
588
- preparedLaunchPlan: opts.preparedLaunchPlan,
589
- expectedProviderThreadId: opts.expectedProviderThreadId || null,
590
- expectedSourceGeneration: opts.expectedSourceGeneration || null,
591
- };
592
- if (opts.leadModelTransition) Object.defineProperty(restoreOptions, 'leadModelTransition', {
593
- value: opts.leadModelTransition, enumerable: false,
594
- });
595
- const started = await startDeferredRestoreSession(placeholder, restoreOptions);
596
- return started === true;
597
- },
754
+ park: parkForLeadSwitch,
755
+ restore: restoreForLeadSwitch,
598
756
  writeCodexDefault: (model) => writeCodexDefaultModel({ model }),
599
757
  writeCodexDefaultEffort: (effort) => writeCodexDefaultEffort({ effort }),
600
758
  // The efforts the Lead options row of the target model supports (a Claude model the rows do not
@@ -611,22 +769,13 @@ function createLeadModelSwitchRuntime(host) {
611
769
  if (effort) return await dbModule.setSessionLeadEffort(sessionId, agent, effort);
612
770
  return await dbModule.deleteSessionLeadEffort(sessionId);
613
771
  },
614
- // A routed lead switch is validated against the SAME readiness the delegate path uses (WP-1b):
615
- // the route must resolve (exist/enabled/routable/servable) for this agent, and the model must be
616
- // in Wall-E's coding catalog (that is where its display name and context window come from).
617
- // D-2: the ONE refusal-logging seam. The specific reason reaches ctm.log, and the message it
618
- // returns (never generic) is what the caller stores as the request's detail.
619
- resolveLeadRoute: async (agent, routeId, modelId, sessionId, opts) => {
620
- const v = await resolveLeadRouteChecked(agent, routeId, modelId, sessionId, opts);
621
- if (!v.ok) {
622
- process.stderr.write(`[lead-model] route refused session=${String(sessionId).slice(0, 8)} route=${routeId} model=${modelId} reason=${v.reason}\n`);
623
- }
624
- return v;
625
- },
772
+ resolveLeadRoute,
626
773
  prepareHistory: historyRuntime.prepareHistoryTransition,
627
774
  revalidateHistoryBeforePark: historyRuntime.revalidateHistoryBeforePark,
628
775
  finalizeHistoryHandoff: historyRuntime.finalizeHistoryHandoff,
629
776
  prepareTargetPlan: host.prepareTargetPlan,
777
+ // WP-0 D-1: resolved ONCE per request, before the claim, and journaled on both recipes.
778
+ resolveSourceHistoryMode: resolveSourceHistoryModeForSession,
630
779
  stageTarget: host.stageLeadTarget || (async ({ sessionId, transitionId, journal, expectedRevision, source, targetPlan, route, signal }) => {
631
780
  if (!journal || !Number.isSafeInteger(targetPlan?.generation) || !route?.preparedProfile) return { ok: false, reason: 'lead_transition_owner_unavailable' };
632
781
  const session = getSession(sessionId);
@@ -641,14 +790,8 @@ function createLeadModelSwitchRuntime(host) {
641
790
  }, { ...(leadRouteDeps || {}), profileLoader: async () => route.preparedProfile });
642
791
  return result.ok ? { ok: true, preparedLeadRoute: result } : result;
643
792
  }),
644
- getLeadRoute: (sessionId) => {
645
- try { return dbModule.getSessionLeadRoute(sessionId); }
646
- catch (error) { recordSessionDiagnostic(sessionId, 'lead-route-read-failed', { message: String(error && error.message || error).slice(0, 200) }); /* REPORT: treated as no stored route, so a native switch adds no explicit null */ return null; }
647
- },
648
- persistLeadRoute: async (sessionId, agent, route) => {
649
- if (route) return await dbModule.setSessionLeadRoute({ ctmSessionId: sessionId, agent, routeId: route.routeId, model: route.model, display: route.display, contextWindow: route.contextWindow, profileFingerprint: route.profileFingerprint, factsRevision: route.factsRevision, processGeneration: route.processGeneration, transitionId: route.transitionId });
650
- return await dbModule.deleteSessionLeadRoute(sessionId);
651
- },
793
+ getLeadRoute,
794
+ persistLeadRoute,
652
795
  beginTransition: async ({ transitionId, sessionId, source, target, targetPlan, request, actionContext = null }) => {
653
796
  // WP-2A: a NEW action always needs the durable journal. A runtime whose db module has none
654
797
  // refuses the action outright rather than falling back to a direct effort/route write.
@@ -687,8 +830,16 @@ function createLeadModelSwitchRuntime(host) {
687
830
  const targetProfile = target.preparedProfile || null;
688
831
  const sourceRecipeId = `${transitionId}:source`;
689
832
  const targetRecipeId = `${transitionId}:target`;
833
+ // WP-0 D-1/S-2-S-3: ONE mode per transition, resolved by the controller BEFORE this claim and
834
+ // written on BOTH recipes. Absent reads as 'present' (the pre-change behaviour), so only a
835
+ // deliberately resolved 'none' ever takes the resume-less path.
836
+ const historyMode = recipeHistoryMode(source);
837
+ // WP-2 D-6: beside the mode, the ONE other fact rollback needs — where the source model came
838
+ // from. 'cli-default' is the only spelling that lets a resume-less recovery omit the one-shot
839
+ // model override; everything else is 'observed'.
840
+ const modelOrigin = sourceModelOrigin(source);
690
841
  const sourceRecipe = {
691
- id: sourceRecipeId, sessionId, providerThreadId: source.providerThreadId, harness,
842
+ id: sourceRecipeId, sessionId, providerThreadId: source.providerThreadId, harness, historyMode, modelOrigin,
692
843
  providerId: source.routeId || source.modelExecutionRecipe?.routeId || source.modelProvider || 'unknown',
693
844
  origin: source.routeId ? 'routed-proxy' : (source.agent === 'codex' ? 'native-codex' : 'native-claude'),
694
845
  controlAdapterVersion: source.agent === 'codex' ? CODEX_CONTROL_ADAPTER : null,
@@ -707,12 +858,18 @@ function createLeadModelSwitchRuntime(host) {
707
858
  approvalPolicyReference: 'host:session-policy', compactHash: null,
708
859
  };
709
860
  const targetRecipe = {
710
- id: targetRecipeId, sessionId, providerThreadId: source.providerThreadId, harness,
861
+ id: targetRecipeId, sessionId, providerThreadId: source.providerThreadId, harness, historyMode,
711
862
  providerId: target.routeId || 'unknown', origin: target.routeId ? 'routed-proxy' : 'unknown',
712
863
  // The marker names the Codex app-server conversion adapter. A Claude target -- routed or
713
864
  // native -- has no conversion adapter at all, so recording the Codex one there would claim a
714
865
  // conversion this operation never performs (same-harness native resume).
715
866
  controlAdapterVersion: harness === 'codex-cli' && target.routeId ? CODEX_CONTROL_ADAPTER : null,
867
+ // WP-2 D-4: the versioned historical-tool filter receipt is a claim about THIS launch path
868
+ // only -- a server-built Codex target launched through the local authenticated CTM proxy
869
+ // route. A native/no-route target, a non-Codex target, a source recipe and any client input
870
+ // never carry it (the store validator rejects every value but null or the exact constant).
871
+ historyToolFilterReceipt: harness === 'codex-cli' && target.routeId
872
+ ? CODEX_HISTORICAL_TOOLS_FILTER_RECEIPT : null,
716
873
  programVersion: null, cyberAccessProgram: null,
717
874
  routeId: target.routeId || null,
718
875
  wireModelId: target.routeId ? (target.modelId || null) : null, processGeneration: targetPlan.generation,
@@ -778,7 +935,12 @@ function createLeadModelSwitchRuntime(host) {
778
935
  || (source.agent === 'codex' && (!sourceContext.homeDir || !sourceContext.codexHome))) {
779
936
  return { ok: false, reason: 'source_launch_context_unavailable' };
780
937
  }
781
- if (!source?.modelId) return { ok: false, reason: 'source_model_unknown' };
938
+ // WP-2 D-6 (S-7): WHERE this source's model came from decides how it is restored. An empty
939
+ // model_id is only legitimate for a source whose recipe recorded the explicit 'cli-default'
940
+ // origin (nothing was ever chosen, so the original launch argv IS the model); every other empty
941
+ // model stays today's `source_model_unknown` refusal.
942
+ const sourceOrigin = sourceModelOrigin(source);
943
+ if (!source?.modelId && sourceOrigin !== 'cli-default') return { ok: false, reason: 'source_model_unknown' };
782
944
  let route = source?.leadRoute || null;
783
945
  if (!route) {
784
946
  try { route = dbModule.getSessionLeadRoute(sessionId) || null; } catch { route = null; }
@@ -795,8 +957,37 @@ function createLeadModelSwitchRuntime(host) {
795
957
  factsRevision: route.facts_revision || route.factsRevision || null,
796
958
  providerThreadId: source.providerThreadId,
797
959
  } : null;
960
+ // WP-2 D-5 (§5/§6, S-7): a 'none' source has nothing to resume, so a rollback relaunches the
961
+ // ORIGINAL captured argv instead of forcing `--resume` on a conversation that was never written.
962
+ // The mode is RE-READ here through the same host function WP-0 added, because the user may have
963
+ // typed while the switch was in flight (R-1): the moment a transcript exists, today's `--resume`
964
+ // path is the right one again. Any other answer -- including a thrown or non-committal one --
965
+ // keeps that path (D-2: an unreadable store is never evidence of absence).
966
+ let freshRelaunch = false;
967
+ if (recipeHistoryMode(source) === 'none') {
968
+ let modeNow = 'present';
969
+ try { modeNow = await resolveSourceHistoryModeForSession(sessionId); }
970
+ catch { modeNow = 'present'; } // HANDLE: D-2 -- a broken re-check is not proof the transcript is gone
971
+ freshRelaunch = modeNow === 'none';
972
+ }
798
973
  let recoveryArgs = sourceContext.args;
799
- if (source.agent === 'codex') {
974
+ if (freshRelaunch) {
975
+ // The fresh argv is the launch args MINUS resume forms (lib/park-fresh-relaunch.js): the
976
+ // captured startup argv names its own thread through `--session-id`, which is exactly the
977
+ // identity this source owns, so no `--resume <thread>` is ever issued for it.
978
+ const fresh = freshRelaunchArgs({ agentType: source.agent,
979
+ storedTask: { cmd: sourceContext.executablePath, args: sourceContext.args },
980
+ sessionCmd: sourceContext.executablePath, sessionId, resumeId: source.providerThreadId });
981
+ if (!fresh) return { ok: false, reason: 'source_launch_context_unavailable' };
982
+ recoveryArgs = fresh.args;
983
+ // Asserted, not assumed: the fresh relaunch must hand the process the SOURCE's own thread. The
984
+ // captured argv normally already carries it from launch; when it does not, it is added here.
985
+ if (source.agent === 'claude') {
986
+ const argvThread = claudeSessionIdArg(recoveryArgs);
987
+ if (!argvThread) recoveryArgs = [...recoveryArgs, '--session-id', source.providerThreadId];
988
+ else if (argvThread !== sourceThreadId) return { ok: false, reason: 'source_launch_context_unavailable' };
989
+ }
990
+ } else if (source.agent === 'codex') {
800
991
  const rebound = require('./launch-model-override').ensureCodexResumeTarget(recoveryArgs, source.providerThreadId);
801
992
  if (!rebound.ok) return { ok: false, reason: rebound.reason };
802
993
  recoveryArgs = rebound.args;
@@ -813,10 +1004,17 @@ function createLeadModelSwitchRuntime(host) {
813
1004
  recoveryArgs = rebound.args;
814
1005
  }
815
1006
  const selectedSourceThread = extractResumeTarget(source.agent, recoveryArgs)?.sessionId || '';
816
- if (selectedSourceThread !== sourceThreadId) return { ok: false, reason: 'source_launch_context_unavailable' };
1007
+ // The fresh branch carries no resume selector AT ALL, so '' is its only acceptable answer; the
1008
+ // resume path keeps today's exact-source-thread requirement.
1009
+ if (selectedSourceThread !== (freshRelaunch ? '' : sourceThreadId)) {
1010
+ return { ok: false, reason: 'source_launch_context_unavailable' };
1011
+ }
817
1012
  const restoreOptions = {
818
1013
  source: 'remote-activate', clientKind: 'api',
819
- launchModelOverride: source.modelId,
1014
+ // D-5/D-6: a 'cli-default' source's rollback passes NO `launchModelOverride` key at all, so
1015
+ // the relaunched process lands on the CLI's own default by construction; every other source
1016
+ // keeps today's one-shot override.
1017
+ ...(sourceOrigin === 'cli-default' ? {} : { launchModelOverride: source.modelId }),
820
1018
  ...(typeof source.effort === 'string' ? { launchEffortOverride: source.effort } : {}),
821
1019
  launchLeadRoute,
822
1020
  expectedProviderThreadId: source.providerThreadId,
@@ -834,7 +1032,8 @@ function createLeadModelSwitchRuntime(host) {
834
1032
  };
835
1033
  if (!attachPrivateSourceRecoveryEnvironment(restoreOptions, { ctmSessionId: sessionId,
836
1034
  agent: source.agent, modelId: source.modelId, providerThreadId: source.providerThreadId,
837
- routeId: source.routeId || null, environment: source.launchEnvironment })) {
1035
+ routeId: source.routeId || null, environment: source.launchEnvironment,
1036
+ modelOrigin: sourceOrigin, freshRelaunch })) {
838
1037
  return { ok: false, reason: 'source_launch_environment_unavailable' };
839
1038
  }
840
1039
  const currentOwner = () => {
@@ -876,7 +1075,9 @@ function createLeadModelSwitchRuntime(host) {
876
1075
  const parkedFrom = session;
877
1076
  const parked = await parkSession(sessionId, { manual: true, reason: 'model-transition-source-recovery',
878
1077
  sourceGeneration: expectedAbandoned.processGeneration });
879
- if (!parked || (parked.ok !== true && parked.alreadyParked !== true)) return { ok: false, reason: parked?.code || 'source_recovery_park_failed' };
1078
+ if (!parked || (parked.ok !== true && parked.alreadyParked !== true)) {
1079
+ return { ok: false, reason: parked?.code || 'source_recovery_park_failed', protections: parked?.protections || [], retryAt: parked?.retryAt ?? null };
1080
+ }
880
1081
  const placeholder = getSession(sessionId);
881
1082
  if (!placeholder || placeholder._restoreDeferred !== true || placeholder.ptyProcess) {
882
1083
  return { ok: false, reason: 'recovery_owner_changed_after_park' };
@@ -1022,14 +1223,90 @@ function createLeadModelSwitchRuntime(host) {
1022
1223
  diagnostic: recordSessionDiagnostic,
1023
1224
  });
1024
1225
 
1226
+ // WP-1 (docs/opencode-lead-and-delegate-model-selection-2026-10-28.html §5 R-2..R-9, §6.3):
1227
+ // the never-prompted OpenCode 1.x switch is its own small controller (no owner tuple, no provider
1228
+ // thread, no journal). It is handed the SAME park/restore lambdas the journaled machine uses, so a
1229
+ // relaunch has one implementation. `opencodeVersion`/`listOpencodeModels` are overridable host DI in
1230
+ // the same way `listCodexModels` is (a test never spawns the real CLI).
1231
+ const opencodeLaunchModel = createOpencodeLaunchModel({
1232
+ getSessionInfo,
1233
+ getSession,
1234
+ opencodeVersion: typeof host.opencodeVersion === 'function' ? host.opencodeVersion : opencodeCli.opencodeVersion,
1235
+ listModels: typeof host.listOpencodeModels === 'function' ? host.listOpencodeModels : opencodeCli.opencodeCatalog,
1236
+ // WP-3 (S-9): the SAME shared pieces the journaled machine uses -- the one route resolver (D-2,
1237
+ // which logs refusals) and the one session_lead_route row reader/writer. A routed OpenCode target
1238
+ // is validated, persisted and re-adopted exactly like a Claude/Codex one; only the relaunch step
1239
+ // differs (R-3).
1240
+ resolveLeadRoute,
1241
+ getLeadRoute,
1242
+ persistLeadRoute,
1243
+ park: parkForLeadSwitch,
1244
+ restore: restoreForLeadSwitch,
1245
+ isIdle,
1246
+ broadcast,
1247
+ diagnostic: recordSessionDiagnostic,
1248
+ });
1249
+
1250
+ // WP-8 step 3 (docs/deepseek-harness-acp-agent-2026-10-28.html §6.7, §9b M-15): the DeepSeek
1251
+ // Harness live switch. dsh is an ACP child, so the switch is `session/set_config_option` on the
1252
+ // SAME child (one route) or one intentional stop + the existing restore path (another route) — no
1253
+ // park/argv machinery and no journal. Its public request shape is OpenCode's byte for byte, so the
1254
+ // picker renders both with one code path; `dshRuntime`/`dshSession` are the server's own instances.
1255
+ const dshLaunchModel = createDshLaunchModel({
1256
+ getSessionInfo,
1257
+ getSession,
1258
+ dshRuntime: host.dshRuntime,
1259
+ dshSession: host.dshSession,
1260
+ broadcast,
1261
+ diagnostic: recordSessionDiagnostic,
1262
+ });
1263
+
1264
+ // WP-6a: warm the spawn-free OpenCode line (package.json of the resolved binary) at boot, so the
1265
+ // synchronous launch path's peekOpencodeLine() already knows whether `--model` is safe to pass.
1266
+ if (host.warmOpencodeLine !== false) {
1267
+ void opencodeCli.opencodeLine().catch(() => { /* expected: an undetected line keeps today's launch args */ });
1268
+ }
1269
+
1270
+ // One owner per agent: an OpenCode tab is answered by the launch controller, a dsh tab by the ACP
1271
+ // controller, everything else by the journaled machine, byte-for-byte unchanged.
1272
+ const isOpencodeSession = (sessionId) => {
1273
+ const info = getSessionInfo(String(sessionId || ''));
1274
+ return !!info && info.agent === 'opencode';
1275
+ };
1276
+ const isDshSession = (sessionId) => {
1277
+ const info = getSessionInfo(String(sessionId || ''));
1278
+ return !!info && info.agent === 'dsh';
1279
+ };
1280
+ const routeLeadModelCall = (name) => {
1281
+ const base = service[name].bind(service);
1282
+ return (sessionId, ...rest) => {
1283
+ if (isOpencodeSession(sessionId)) return opencodeLaunchModel[name](sessionId, ...rest);
1284
+ if (isDshSession(sessionId)) return dshLaunchModel[name](sessionId, ...rest);
1285
+ return base(sessionId, ...rest);
1286
+ };
1287
+ };
1288
+ service.request = routeLeadModelCall('request');
1289
+ service.get = routeLeadModelCall('get');
1290
+ service.cancel = routeLeadModelCall('cancel');
1291
+
1025
1292
  leadModelObserver.attach({
1026
1293
  wants: (sessionId) => service.wantsObservation(sessionId),
1027
- observe: (sessionId, obs) => service.observe(sessionId, obs),
1294
+ observe: (sessionId, obs) => {
1295
+ const verdict = service.observe(sessionId, obs);
1296
+ // OpenCode settles from the session's own `opencode:message` observation fields (R-7); the
1297
+ // lead-model observer knows only Claude/Codex turn sources, so the controller is told directly.
1298
+ opencodeLaunchModel.notifyObservation(sessionId);
1299
+ return verdict;
1300
+ },
1028
1301
  observeSelection: (sessionId, obs) => service.observeSelection(sessionId, obs),
1029
1302
  });
1030
1303
 
1031
1304
  statusBus.on('verdict', (ev) => {
1032
- if (ev && ev.sessionId && ev.status !== 'running' && ev.status !== 'stalled') void service.notifyStatus(ev.sessionId);
1305
+ if (ev && ev.sessionId && ev.status !== 'running' && ev.status !== 'stalled') {
1306
+ void service.notifyStatus(ev.sessionId);
1307
+ // A turn that just ended is when an OpenCode observation lands: settle without waiting for a poll.
1308
+ opencodeLaunchModel.notifyObservation(ev.sessionId);
1309
+ }
1033
1310
  });
1034
1311
 
1035
1312
  // The Lead tab's rows (GET /api/sessions/:id/lead-model/options): what this CLI can switch to.
@@ -1041,8 +1318,11 @@ function createLeadModelSwitchRuntime(host) {
1041
1318
  getSessionInfo,
1042
1319
  loadCatalog: () => loadCatalogItems(),
1043
1320
  listCodexModels: typeof host.listCodexModels === 'function' ? host.listCodexModels : () => listCodexModels(),
1321
+ ...(typeof host.listOpencodeModels === 'function' ? { listOpencodeModels: host.listOpencodeModels } : {}),
1044
1322
  ...(typeof host.readClaudeSettings === 'function' ? { readClaudeSettings: host.readClaudeSettings } : {}),
1045
1323
  ...(typeof host.readCodexConfigToml === 'function' ? { readCodexConfigToml: host.readCodexConfigToml } : {}),
1324
+ // WP-8 step 4: a dsh tab's in-route rows and efforts come from its live ACP child's configOptions.
1325
+ getDshConfigOptions: (id) => { const live = host.dshRuntime && host.dshRuntime.get(id); return live && Array.isArray(live.configOptions) ? live.configOptions : null; },
1046
1326
  });
1047
1327
  // D-7 (picker design WP-4): Wall-E's registry-changed notify routes here, so the Lead tab never
1048
1328
  // serves a catalog that predates the change.
@@ -1096,9 +1376,13 @@ function createLeadModelSwitchRuntime(host) {
1096
1376
  return superseded();
1097
1377
  }
1098
1378
  if (!intent.ok) return { status: intent.status, body: { ok: false, code: intent.code } };
1099
- return { status: out.status, body: { ...out.body, stored_effort: stored, lead_route: leadRoute,
1379
+ const body = { ...out.body, stored_effort: stored, lead_route: leadRoute,
1100
1380
  expected_owner: capture.owner, intent_revision: intent.revision, intent: intent.intent,
1101
- action_state: capture.owner ? 'ready' : 'owner_not_ready' } };
1381
+ action_state: capture.owner ? 'ready' : 'owner_not_ready' };
1382
+ // WP-1 (§6.2): ONE added field, for an OpenCode tab only. Every existing field stays exactly as
1383
+ // it is for Claude/Codex, so no client has to learn a second response shape.
1384
+ if (isOpencodeSession(sessionId)) body.opencode = await opencodeLaunchModel.sessionMode(sessionId);
1385
+ return { status: out.status, body };
1102
1386
  }
1103
1387
  };
1104
1388
 
@@ -1107,4 +1391,4 @@ function createLeadModelSwitchRuntime(host) {
1107
1391
 
1108
1392
  // WP-5 (F-1): `refusal` is exported so its own fallback contract (never an inherited
1109
1393
  // Object.prototype member) has a direct unit test; it is the same function every route refusal uses.
1110
- module.exports = { createLeadModelSwitchRuntime, createLeadTransitionTargetPlanner, ROUTE_REFUSAL_MESSAGES, refusal };
1394
+ module.exports = { createLeadModelSwitchRuntime, createLeadTransitionTargetPlanner, createSourceHistoryModeResolver, ROUTE_REFUSAL_MESSAGES, refusal };