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
@@ -27,6 +27,8 @@ const {
27
27
  ClientConfigError,
28
28
  OPERATIONS,
29
29
  applySourceEdits,
30
+ asClient,
31
+ attributionOptions,
30
32
  buildOwnershipManifest,
31
33
  emptyJsonDocumentText,
32
34
  creationBoundaryEvidence,
@@ -59,6 +61,38 @@ const MCP_TARGETS = Object.freeze([
59
61
  { id: 'claude-desktop', tool: 'Claude Desktop', configPath: 'Library/Application Support/Claude/claude_desktop_config.json', detectPath: 'Library/Application Support/Claude', kind: 'json' },
60
62
  ]);
61
63
 
64
+ // D-3: every ensure TARGET is worth NAMING when it refuses, so each MCP_TARGETS
65
+ // id maps onto exactly one token of the leaf's fixed client vocabulary
66
+ // (mcp-client-config.js CLIENT_CONFIG_CLIENTS); `asClient` carries that token
67
+ // onto whatever owned refusal the target's capture/parse/plan raises. The
68
+ // mapping is TOTAL over MCP_TARGETS on purpose — a new target id with no token
69
+ // here fails the coverage test in
70
+ // tests/client-config-error-attribution.test.js instead of inventing one, and
71
+ // `_targetClient` refuses at runtime rather than returning an unmapped value.
72
+ const MCP_TARGET_CLIENTS = Object.freeze({
73
+ 'claude-code-user': 'claude-code',
74
+ 'claude-code-project': 'claude-code',
75
+ codex: 'codex',
76
+ cursor: 'cursor',
77
+ windsurf: 'windsurf',
78
+ 'claude-desktop': 'claude-desktop',
79
+ });
80
+
81
+ function _targetClient(target) {
82
+ const token = target ? MCP_TARGET_CLIENTS[target.id] : undefined;
83
+ // No token is a programming error, never a silently anonymous refusal.
84
+ if (!token) throw new TypeError('mcp-integration: unmapped MCP target id');
85
+ return token;
86
+ }
87
+
88
+ // D-3: the ensure boot lines (wall-e/server.js, CTM walle-mcp-auto-config.js)
89
+ // print the message plus the bounded attribution an owned refusal carries.
90
+ function ensureFailureMessage(err) {
91
+ const { client, refusal } = err && typeof err === 'object' ? attributionOptions(err) : {};
92
+ const parts = [client && `client=${client}`, refusal && `refusal=${refusal}`].filter(Boolean);
93
+ return `${err && err.message}${parts.length ? ` (${parts.join(', ')})` : ''}`;
94
+ }
95
+
62
96
  const AGENT_INSTRUCTION_TARGETS = Object.freeze([
63
97
  {
64
98
  id: 'claude-code-instructions',
@@ -1588,10 +1622,10 @@ async function ensureMcpIntegrations(wallePort, opts = {}) {
1588
1622
  let primary = null;
1589
1623
  let failurePresent = false;
1590
1624
 
1591
- const jobFor = (physicalPath, logicalPath, source) => {
1625
+ const jobFor = (physicalPath, logicalPath, source, client) => {
1592
1626
  let job = jobsByPhysical.get(physicalPath);
1593
1627
  if (!job) {
1594
- job = { physicalPath, logicalPath, source, members: [], plugin: null, pluginPlan: null, emitted: null, action: null };
1628
+ job = { physicalPath, logicalPath, source, client, members: [], plugin: null, pluginPlan: null, emitted: null, action: null };
1595
1629
  jobsByPhysical.set(physicalPath, job);
1596
1630
  jobs.push(job);
1597
1631
  sources.push(source);
@@ -1606,10 +1640,13 @@ async function ensureMcpIntegrations(wallePort, opts = {}) {
1606
1640
  order.push({ kind: 'row', row: { kind: 'mcp_config', tool: target.tool, action: 'not_installed' } });
1607
1641
  continue;
1608
1642
  }
1643
+ // D-3: this target's client token, so its capture/parse/plan refusal
1644
+ // names the client instead of the anonymous boot line.
1645
+ const client = _targetClient(target);
1609
1646
  if (target.kind === 'codex-toml') {
1610
- const plan = _planCodexTarget({
1647
+ const plan = asClient(client, () => _planCodexTarget({
1611
1648
  target, logicalPath: paths.configPath, wallePort, opts: scopedOpts, context, allowMissing: true, sources,
1612
- });
1649
+ }));
1613
1650
  if (plan.source) { jobs.push(Object.assign({ kind: 'toml', logicalPath: paths.configPath }, plan)); sources.push(plan.source); }
1614
1651
  order.push({ kind: 'single', plan });
1615
1652
  continue;
@@ -1618,12 +1655,12 @@ async function ensureMcpIntegrations(wallePort, opts = {}) {
1618
1655
  // logical aliases of one file share ONE captured original: no second
1619
1656
  // unregistered capture, and never two plans derived from two originals.
1620
1657
  // `assertNotReserved` is also the reserved pre-FD admission.
1621
- const groupingKey = assertNotReserved(paths.configPath, context);
1622
- const member = { target, logicalPath: paths.configPath, tombstoned: _isTombstoned(target, scopedOpts) };
1658
+ const groupingKey = asClient(client, () => assertNotReserved(paths.configPath, context));
1659
+ const member = { target, client, logicalPath: paths.configPath, tombstoned: _isTombstoned(target, scopedOpts) };
1623
1660
  let job = jobsByPhysical.get(groupingKey);
1624
1661
  if (!job) {
1625
- const source = captureClientConfigFile(paths.configPath, { mutexContext: context, allowMissing: true });
1626
- job = jobFor(source.targetPath, paths.configPath, source);
1662
+ const source = asClient(client, () => captureClientConfigFile(paths.configPath, { mutexContext: context, allowMissing: true }));
1663
+ job = jobFor(source.targetPath, paths.configPath, source, client);
1627
1664
  }
1628
1665
  job.members.push(member);
1629
1666
  order.push({ kind: 'json', job, target, logicalPath: paths.configPath });
@@ -1635,12 +1672,15 @@ async function ensureMcpIntegrations(wallePort, opts = {}) {
1635
1672
  const routing = _planSessionPluginRouting(scopedOpts);
1636
1673
  if (routing && routing.qualifiedNames.length) {
1637
1674
  const claudeJsonPath = path.join(homeDir, '.claude.json');
1675
+ // …and it IS the Claude Code user config, so its refusals name that
1676
+ // client too.
1677
+ const pluginClient = MCP_TARGET_CLIENTS['claude-code-user'];
1638
1678
  let physical = claudeJsonPath;
1639
1679
  try { physical = fs.realpathSync(claudeJsonPath); } catch { physical = claudeJsonPath; }
1640
1680
  let job = jobsByPhysical.get(physical);
1641
1681
  if (!job) {
1642
- const source = captureClientConfigFile(claudeJsonPath, { mutexContext: context, allowMissing: true });
1643
- job = jobFor(source.targetPath, claudeJsonPath, source);
1682
+ const source = asClient(pluginClient, () => captureClientConfigFile(claudeJsonPath, { mutexContext: context, allowMissing: true }));
1683
+ job = jobFor(source.targetPath, claudeJsonPath, source, pluginClient);
1644
1684
  }
1645
1685
  job.plugin = routing;
1646
1686
  order.push({ kind: 'plugin', job, tool: 'Claude Code Plugin Routing', configPath: claudeJsonPath });
@@ -1654,7 +1694,10 @@ async function ensureMcpIntegrations(wallePort, opts = {}) {
1654
1694
  // _planCodexTarget and has no session-plugin stage to compose.
1655
1695
  if (!job.source || job.kind === 'toml') continue;
1656
1696
  let current = job.source.text === null ? emptyJsonDocumentText() : job.source.text;
1657
- let doc = parseClientConfig(current, { format: 'json' });
1697
+ // D-3: the job's JSON parse and each member's plan are attributed to the
1698
+ // client whose file this is (aliases of one physical document share the
1699
+ // token, and its members must agree).
1700
+ let doc = asClient(job.client, () => parseClientConfig(current, { format: 'json' }));
1658
1701
  // GEN8-F: same-physical aliases must AGREE. Each member's own opt-out is
1659
1702
  // evaluated separately (a tombstone for one client is never reported for
1660
1703
  // another), and a disagreement — one alias tombstoned while another wants
@@ -1687,13 +1730,13 @@ async function ensureMcpIntegrations(wallePort, opts = {}) {
1687
1730
  );
1688
1731
  const perMember = job.members.map((member) => ({
1689
1732
  member,
1690
- plan: _planOwnJsonEntryEdits(doc, current, {
1733
+ plan: asClient(member.client, () => _planOwnJsonEntryEdits(doc, current, {
1691
1734
  expected,
1692
1735
  authority,
1693
1736
  ownHttpUrl,
1694
1737
  tombstoned: member.tombstoned === true,
1695
1738
  allowCommandTransition,
1696
- }),
1739
+ })),
1697
1740
  }));
1698
1741
  // GEN9: aliases of one physical document must agree on the FULL desired
1699
1742
  // output (action AND the exact edits), not merely on the action name.
@@ -2000,6 +2043,8 @@ module.exports = {
2000
2043
  AGENT_INSTRUCTIONS_BEGIN,
2001
2044
  AGENT_INSTRUCTIONS_END,
2002
2045
  MCP_TARGETS,
2046
+ MCP_TARGET_CLIENTS,
2047
+ ensureFailureMessage,
2003
2048
  WALLE_SERVER_NAME,
2004
2049
  detectAgentMemoryInstructions,
2005
2050
  detectMcpIntegrations,
@@ -156,10 +156,16 @@ function installParentDeathWatch(options = {}) {
156
156
  try { logger?.error?.(`[parent-death:${label}] failed to register disconnect listener`, err); } catch { /* expected: logger itself is best-effort here; nothing downstream depends on this line succeeding */ }
157
157
  }
158
158
 
159
+ // Reparenting is "ppid changed since install", not "ppid is 1": a macOS orphan goes to
160
+ // launchd (pid 1), but a Linux orphan goes to the nearest subreaper (`systemd --user`), so
161
+ // `=== 1` alone never fired there (design nightly-linux-host-portability-2026-10-27, WP-4b).
162
+ // A process launched with ppid 1 (under launchd) keeps it, so it is never "changed".
163
+ let initialPpid = null;
164
+ try { initialPpid = getPpid(); } catch { /* expected: unknown at install -- only === 1 can fire */ }
159
165
  interval = setIntervalImpl(() => {
160
166
  let ppid;
161
167
  try { ppid = getPpid(); } catch { return; }
162
- if (ppid === 1) shutdown('ppid-reparented');
168
+ if (ppid === 1 || (initialPpid !== null && ppid !== initialPpid)) shutdown('ppid-reparented');
163
169
  }, pollMs);
164
170
  interval.unref?.();
165
171
 
@@ -151,8 +151,10 @@ async function runSchedulerJob(jobName, payload = {}) {
151
151
  ));
152
152
  break;
153
153
  case 'memory-consolidation':
154
- result = await _measurePhase(phases, 'memoryConsolidation.run', () => (
155
- require('../memory/consolidation').runMemoryConsolidation({ brain })
154
+ // shared-memory D-5: derived from replicated rows — the derivation home only. This worker path
155
+ // is what production runs (agent.js's run wrapper is bypassed when the job is worker-routed).
156
+ result = await _measurePhase(phases, 'memoryConsolidation.run', () => require('./derivation-home').runIfHome(
157
+ 'memory-consolidation', () => require('../memory/consolidation').runMemoryConsolidation({ brain }),
156
158
  ));
157
159
  break;
158
160
  case 'think':
@@ -183,12 +185,12 @@ async function runSchedulerJob(jobName, payload = {}) {
183
185
  const generateFn = reflectFallbackOnly
184
186
  ? null
185
187
  : createDailySummaryGenerateFn({ model: modelOverride?.model || undefined });
186
- result = await _measurePhase(phases, 'reflect.runOnce', () => require('../loops/reflect').runOnce({
188
+ result = await _measurePhase(phases, 'reflect.runOnce', () => require('./derivation-home').runIfHome('reflect', () => require('../loops/reflect').runOnce({
187
189
  generateFn,
188
190
  allowProviderFallback: true,
189
191
  fallbackReason: reflectFallbackOnly ? 'provider_backpressure' : '',
190
192
  forceSummary: payload.forceSummary === true,
191
- }));
193
+ }))); // shared-memory D-5: derivation home only (see memory-consolidation above)
192
194
  break;
193
195
  }
194
196
  case 'backfill':
@@ -35,10 +35,19 @@ function parentLookupUnavailable() {
35
35
  })), { code: LOOKUP_UNAVAILABLE_CODE });
36
36
  }
37
37
 
38
+ // §9 S-16 (opencode-lead-and-delegate-model-selection-2026-10-28.html §8 WP-5): the ladder is keyed
39
+ // by the LEAD's OWN agent -- the agent of the session row the parent lookup resolved -- never by the
40
+ // worker harness the caller happens to ask for. The store is the ONE owner of the agent list, so a
41
+ // lead agent joins this derivation by joining store.AGENTS (dsh did, WP-10/P-6).
42
+ const STORE_AGENTS = new Set(store.AGENTS);
43
+
38
44
  // `needSession: false` (the call named its own route/model) skips the CTM lookup: only the agent
39
45
  // default is still read, for the route tie-break.
40
46
  async function readLeadDelegatePrefs({ db, agent, parentAgentSessionId, lookupParent, timeoutMs, needSession = true } = {}) {
41
- const leadAgent = agent === 'codex' ? 'codex' : 'claude';
47
+ // The historical derivation from the caller's worker harness: a Codex worker means a Codex lead
48
+ // asked, anything else a Claude one. It STAYS the fallback -- it is the only signal available when
49
+ // the parent lookup did not answer (no lookup, an unreachable CTM, or an unresolved lead).
50
+ let leadAgent = agent === 'codex' ? 'codex' : 'claude';
42
51
  const raw = String(parentAgentSessionId || '').trim();
43
52
  let ctmSessionId = '';
44
53
  let parentResolution = 'unresolved';
@@ -68,6 +77,12 @@ async function readLeadDelegatePrefs({ db, agent, parentAgentSessionId, lookupPa
68
77
  // (an older CTM, or a lead that is not live) stays unknown rather than being inferred, and a
69
78
  // lookup that resolved NO session has no running lead to describe -- unknown either way.
70
79
  nativeSubagentLaunch = ctmSessionId ? nativeLaunchConstraints(found && found.native_subagent_launch) : UNKNOWN_NATIVE_LAUNCH;
80
+ // S-16: the resolved LIVE lead's own harness is the ladder's agent whenever it is one the store
81
+ // keys on -- an OpenCode lead reads default:opencode and a DeepSeek Harness lead default:dsh,
82
+ // exactly as they wrote them. A value this module cannot classify (an older CTM, or a lead
83
+ // whose agent is not projected) leaves the derivation above in place rather than inventing an
84
+ // agent -- which is why CTM must PROJECT `dsh` in native_subagent_launch.agent (D-24).
85
+ if (nativeSubagentLaunch && STORE_AGENTS.has(nativeSubagentLaunch.agent)) leadAgent = nativeSubagentLaunch.agent;
71
86
  }
72
87
  }
73
88
  // The session row is read exactly ONCE, and the raw-id fallback above shares this one read: the
@@ -5,16 +5,23 @@
5
5
  // a route or model outranks all three and is handled by resolveDelegateArgs itself.
6
6
  //
7
7
  // VENDOR_DEFAULT is the bottom rung: what a plain delegate uses when nothing is stored.
8
- // These two model literals live HERE and nowhere else in server code; the skill templates
9
- // ask Wall-E (walle_delegate_implementation answers lane:'native') instead of carrying
10
- // their own copy.
8
+ // The claude/codex model literals live HERE and nowhere else in server code; the skill
9
+ // templates ask Wall-E (walle_delegate_implementation answers lane:'native') instead of
10
+ // carrying their own copy. OpenCode carries no literal at all -- its rung is `inherit`.
11
11
  const VENDOR_DEFAULT = Object.freeze({
12
12
  claude: Object.freeze({ lane: 'native', model: 'sonnet' }),
13
13
  codex: Object.freeze({ lane: 'native', model: 'gpt-5.6-terra' }),
14
+ // OpenCode's own built-in subagents (`general`, `explore`) run on the model of the primary agent
15
+ // that invoked them unless their own config names one (https://opencode.ai/docs/agents/), so the
16
+ // bottom rung is INHERIT -- CTM names no model literal here. opencode-lead-and-delegate-model-
17
+ // selection-2026-10-28.html §6.5: the projection turns an inherit rung into no `model` key at all.
18
+ opencode: Object.freeze({ lane: 'inherit', model: '' }),
14
19
  });
15
20
 
21
+ // An unknown/absent agent keeps the historical claude fallback: every existing caller (a bare
22
+ // delegate, a Codex lead with no stored row) must fold exactly as it did before opencode existed.
16
23
  function vendorDefaultFor(agent) {
17
- return VENDOR_DEFAULT[agent === 'codex' ? 'codex' : 'claude'];
24
+ return VENDOR_DEFAULT[agent] || VENDOR_DEFAULT.claude;
18
25
  }
19
26
 
20
27
  function rungValue(pref) {
@@ -26,7 +33,7 @@ function rungValue(pref) {
26
33
 
27
34
  /**
28
35
  * @param {{session?:object|null, default?:object|null}} prefs stored rows (null/absent = unset)
29
- * @param {'claude'|'codex'} agent
36
+ * @param {'claude'|'codex'|'opencode'} agent
30
37
  * @returns {{effective:{lane,model,route_id,identity_id,display,effort,source}, ladder:Array<{rung,value}>}}
31
38
  */
32
39
  function foldDelegateLadder(prefs, agent) {
@@ -289,6 +289,6 @@ async function recordRoleUsage({ task, response, model, providerId = null, provi
289
289
  });
290
290
  }
291
291
 
292
- module.exports = { ROLES, TASK_ROLE, roleForTask, resolveRoleModel, completeForRole, recordRoleUsage };
292
+ module.exports = { ROLES, TASK_ROLE, MAX_COMPLETION_TOKENS, roleForTask, resolveRoleModel, completeForRole, recordRoleUsage };
293
293
  // Test seam (non-enumerable, so the public surface stays the seven names above).
294
294
  Object.defineProperty(module.exports, '_detectFallbackFrom', { value: _detectFallbackFrom, enumerable: false });
@@ -37,6 +37,10 @@
37
37
  // `kind:'routed'` model is the launch-time CONFIGURED TARGET snapshot: that launch's native role
38
38
  // forces the reserved worker alias and the live target may have moved since, so it describes what
39
39
  // was configured at launch, never the model a subagent actually consumed.
40
+ //
41
+ // ONE LEAD HARNESS HAS NO NATIVE LANE AT ALL: a DeepSeek Harness lead (D-24). CTM cannot pin the
42
+ // model of dsh's own sub-agent tool, so its native answer is refused by the lead's own harness
43
+ // before any pin is compared -- see DSH_LEAD_AGENT below.
40
44
 
41
45
  // Reason codes (the `error` field of a typed refusal; the MCP layer renders them as text).
42
46
  const NATIVE_SELECTION_REQUIRES_RELAUNCH = 'native_selection_requires_relaunch';
@@ -48,7 +52,24 @@ const RELAUNCH_NEXT_STEP = 'Relaunch the lead to apply its native pick, or expli
48
52
  // pick / vendor rung) and `unservable` (a pick CTM refused to install) are deliberately absent:
49
53
  // they pin nothing, so a requested model is not overridden by them.
50
54
  const PINNING_KINDS = new Set(['native', 'routed', 'inherit']);
51
- const KNOWN_AGENTS = new Set(['codex', 'claude']);
55
+ // WP-5 (§9 S-20, opencode-lead-and-delegate-model-selection-2026-10-28.html §8): an OpenCode lead is
56
+ // a live lead too, and its native helper runs on OpenCode, so its harness and its pin are reported
57
+ // through exactly this sanitizer. The tool does NOT accept `opencode` as a worker harness (that
58
+ // anti-goal is unchanged) -- see the harness comparison below for what that means. dsh joins the
59
+ // same way (WP-10/P-6): its harness is reported here so D-24's refusal below can key on it.
60
+ const KNOWN_AGENTS = new Set(['codex', 'claude', 'opencode', 'dsh']);
61
+ // The harnesses a caller can actually REQUEST through walle_delegate_implementation
62
+ // (normalizeWorkerExecutionOptions). A lead harness outside this set (OpenCode) can never be equal
63
+ // to a request, so comparing against it would report every native answer as a cross-harness
64
+ // mismatch and hide the model/effort pin conflict this module exists to surface (AC-5c).
65
+ const REQUESTABLE_HARNESSES = new Set(['codex', 'claude']);
66
+ // D-24 (deepseek-harness-acp-agent-2026-10-28.html §5b, WP-10): a DeepSeek Harness lead delegates
67
+ // through ROUTED WORKERS ONLY. CTM cannot pin the model of dsh's own sub-agent tool, so no native
68
+ // answer describes anything a dsh lead could spawn: every native answer for it is the typed mismatch
69
+ // refusal below -- never a Claude model, never a silent substitution, and never a fallback to
70
+ // another lane. Unlike the OpenCode case there is no "same harness" native answer to preserve.
71
+ const DSH_LEAD_AGENT = 'dsh';
72
+ const DSH_ROUTED_ONLY_NEXT_STEP = 'A DeepSeek Harness lead delegates through routed workers only: name route/model (or route alone) to run an explicitly routed claude/codex worker.';
52
73
  // 'inherit' is the Claude native lane's own literal for "same model as the lead" (CTM's
53
74
  // resolveFromEffectivePref); a request for it matches an already-inheriting launch, so it is
54
75
  // never treated as a specific model a routed alias/FORCE would override.
@@ -67,9 +88,10 @@ function _token(value, max = MAX_TOKEN_LENGTH) {
67
88
  /**
68
89
  * The sanitized descriptor of a lead's applied native launch constraints. PURE.
69
90
  * @param {unknown} launch whatever arrived as native_subagent_launch (or nothing at all)
70
- * @returns {{state:'known',agent:'codex'|'claude',kind:string,model:string,effort:string|null}
71
- * | {state:'unknown',agent?:'codex'|'claude'}} exactly these keys, nothing else: an unknown pin
72
- * keeps the lead's own harness (`agent`) when it is known, and only `state` when it is not
91
+ * @returns {{state:'known',agent:'codex'|'claude'|'opencode'|'dsh',kind:string,model:string,effort:string|null}
92
+ * | {state:'unknown',agent?:'codex'|'claude'|'opencode'|'dsh'}} exactly these keys, nothing else:
93
+ * an unknown pin keeps the lead's own harness (`agent`) when it is known, and only `state` when it
94
+ * is not
73
95
  */
74
96
  function nativeLaunchConstraints(launch) {
75
97
  if (!launch || typeof launch !== 'object' || Array.isArray(launch)) return { state: 'unknown' };
@@ -120,11 +142,36 @@ function checkNativeDelegateSelection({ launch, resolved, agent, effort } = {})
120
142
  const constraints = nativeLaunchConstraints(launch);
121
143
  const requestedAgent = agent === 'codex' || agent === 'claude' ? agent : '';
122
144
 
145
+ // D-24 FIRST, for EVERY native answer whose lead harness is dsh -- a known pin, an unknown one, or
146
+ // bare launch metadata alike (this module is only ever consulted for a native answer). There is no
147
+ // native configuration a DeepSeek Harness lead could run, so no pin comparison could ever make the
148
+ // answer acceptable: the refusal is the lead's own harness, not a conflict with what was asked
149
+ // for. Nothing is substituted -- the remedy is an explicitly routed worker.
150
+ if (constraints.agent === DSH_LEAD_AGENT) {
151
+ return {
152
+ ok: false,
153
+ constraints,
154
+ refusal: {
155
+ error: NATIVE_DELEGATE_HARNESS_MISMATCH,
156
+ // Only reported when the call actually named a worker harness: the refusal does not depend
157
+ // on one (a bare call is refused the same way), and an empty string would name nothing.
158
+ ...(requestedAgent ? { requested_agent: requestedAgent } : {}),
159
+ configured_agent: DSH_LEAD_AGENT,
160
+ reason: 'lead_routed_only',
161
+ next_step: DSH_ROUTED_ONLY_NEXT_STEP,
162
+ },
163
+ };
164
+ }
165
+
123
166
  // HARNESS FIRST, whenever the ACTUAL lead harness is known -- including an unknown pin, where
124
167
  // the harness is still a known fact. A native helper runs on the lead's own harness, so a
125
168
  // cross-harness request is the one refusal a relaunch cannot fix: the remedy is an explicitly
126
169
  // routed worker. Nothing about the pin is claimed by this refusal.
127
- if (requestedAgent && constraints.agent && requestedAgent !== constraints.agent) {
170
+ //
171
+ // Only compared when the lead's harness is one the caller could have requested at all
172
+ // (REQUESTABLE_HARNESSES): an OpenCode lead's harness can never equal a request, and refusing
173
+ // there would mask the pin conflict with an unfixable-by-relaunch harness error (AC-5c).
174
+ if (requestedAgent && REQUESTABLE_HARNESSES.has(constraints.agent) && requestedAgent !== constraints.agent) {
128
175
  return {
129
176
  ok: false,
130
177
  constraints,
@@ -178,9 +225,10 @@ function checkNativeDelegateSelection({ launch, resolved, agent, effort } = {})
178
225
  // Codex native children get a role-file effort; a known NON-EMPTY effort the caller does not
179
226
  // ask for cannot be changed without a relaunch. An EMPTY configured effort is a known "no role
180
227
  // override", and an EMPTY requested effort means the call named none -- the CLI's own
181
- // inheritance/default decides it, so neither is a claim about the other. Claude applies no
182
- // native role effort at all, so Claude is never refused on effort; a NULL configured effort is
183
- // unknown and never claimed equal to the requested one.
228
+ // inheritance/default decides it, so neither is a claim about the other. Only Codex installs a
229
+ // native role effort (Claude does not, and an OpenCode or DeepSeek Harness lead was answered
230
+ // above), so no other harness is refused on effort; a NULL configured effort is unknown and never
231
+ // claimed equal to the requested one.
184
232
  if (constraints.agent === 'codex' && (constraints.kind === 'native' || constraints.kind === 'routed')
185
233
  && constraints.effort && constraints.effort !== requestedEffort) {
186
234
  return {
@@ -31,6 +31,15 @@ function isExactModelOnRoute(model, routes, route) {
31
31
  return Boolean(row && Array.isArray(row.models) && row.models.includes(model));
32
32
  }
33
33
 
34
+ // WP-2 fix round 1 (§5 D-6): the one origin-refusal sentence tail, shared by the stored-pick
35
+ // and default-rung branches so both read identically -- the route IS configured; this machine
36
+ // just cannot dial its origin -- and both name the origin and when it was probed.
37
+ function originRefusalClause(ex) {
38
+ const origin = ex.origin || 'unknown';
39
+ const checked = ex.checked_at ? `, checked ${ex.checked_at}` : '';
40
+ return `cannot be reached from this machine (origin ${origin}${checked}) -- name a route this machine can reach, or pick one in the model picker`;
41
+ }
42
+
34
43
  // WP-1 §6.2, §9 S-2: renders a resolver failure ({ok:false, code, candidates?|tried?})
35
44
  // as a structured, readable tool error -- route ids and model ids only, never a
36
45
  // provider name or credential. The caller throws this so handleMcpMessage's generic
@@ -55,9 +64,16 @@ function delegateArgsError(code, payload) {
55
64
  // worker route feed at all (gone/not configured or carrying no enabled
56
65
  // models) -- say that, not "cannot serve agent X" (which reads as a
57
66
  // serve-time refusal of a route that exists).
58
- detail = safe.cause === 'route_unavailable'
59
- ? `The default route ${route} is unavailable right now (not in the worker route feed: no enabled models or not configured) -- name one of eligible[] as route/model`
60
- : `The default route ${route} cannot serve agent ${safe.agent || ''} -- name one of eligible[] as route/model`;
67
+ // WP-2 fix round 1 (§5 D-6): an ORIGIN refusal is a third, different truth --
68
+ // the route IS configured and served, this machine just cannot dial its
69
+ // origin -- so it gets its own sentence rather than the "not in the feed" one.
70
+ const ex = Array.isArray(safe.excluded) ? safe.excluded[0] : null;
71
+ const originClause = ex && ex.reason === 'origin_unreachable' ? originRefusalClause(ex) : null;
72
+ detail = originClause
73
+ ? `The default route ${route} ${originClause}`
74
+ : safe.cause === 'route_unavailable'
75
+ ? `The default route ${route} is unavailable right now (not in the worker route feed: no enabled models or not configured) -- name one of eligible[] as route/model`
76
+ : `The default route ${route} cannot serve agent ${safe.agent || ''} -- name one of eligible[] as route/model`;
61
77
  } else if (code === 'delegate_pref_unresolvable') {
62
78
  const pref = safe.pref || {};
63
79
  // WP-0 (§5 D-1/D-2): the stored pick hit the health gate -- the sentence
@@ -68,6 +84,11 @@ function delegateArgsError(code, payload) {
68
84
  const ex = Array.isArray(safe.excluded) ? safe.excluded[0] : null;
69
85
  if (safe.cause === 'route_unavailable' && pref.route_id && ex && (ex.reason === 'circuit_open' || ex.reason === 'throttled')) {
70
86
  detail = `The stored ${safe.source || ''} delegate route "${pref.route_id}" ${ex.reason === 'throttled' ? 'is throttled' : 'has an open circuit'} (${retryPhrase(ex.retry_after_ms)}) -- wait, pick another route in the model picker, or name a model in the call`;
87
+ } else if (safe.cause === 'route_unavailable' && pref.route_id && ex && ex.reason === 'origin_unreachable') {
88
+ // WP-2 fix round 1 (§5 D-6): the route IS configured -- this machine cannot dial
89
+ // its origin. The "no longer configured" sentence below would be a lie here, and
90
+ // the lead needs to know WHICH origin and when it was probed.
91
+ detail = `The stored ${safe.source || ''} delegate route "${pref.route_id}" ${originRefusalClause(ex)}`;
71
92
  } else if (safe.cause === 'route_unavailable' && pref.route_id) {
72
93
  // D-7 (§6.4): a gone pref route_id names the route, not a model phrase -- the sentence
73
94
  // points at the picker where that id was written, never at a re-resolved model.
@@ -159,6 +180,16 @@ function routeHealthRefusal(route, model, identityCtx) {
159
180
  return null;
160
181
  }
161
182
 
183
+ // WP-2 (§5 D-6): the second input to the health gate -- the box's OWN origin verdict for the stored
184
+ // pick's route. Only an explicit `unreachable`/`no_server` refuses; `unknown`, a missing row, or no
185
+ // availability at all is "no gate" (the delegation may still fail later as upstream_unready, which
186
+ // is the pre-WP-2 behaviour). Explicit `model`/`route` arguments never pass through here (D-7).
187
+ function originReachabilityRefusal(route, availability) {
188
+ const row = availability && Array.isArray(availability.routes) ? availability.routes.find((r) => r && r.id === route) : null;
189
+ if (!row || (row.reach !== 'unreachable' && row.reach !== 'no_server')) return null;
190
+ return { registry_id: route, reason: 'origin_unreachable', origin: row.origin || null, reach: row.reach, checked_at: availability.checkedAt || null };
191
+ }
192
+
162
193
  // L-3: one offer per ROUTE (never one per model), at most MAX_ELIGIBLE_ROUTES, ordered so a lead
163
194
  // can pick from the top: keyed before keyless, direct providers before gateway integration rows,
164
195
  // then route id. A route has no per-route default model (only the per-agent default delegate
@@ -173,7 +204,10 @@ function eligibleRouteOffers(listing, agent, identityCtx = null) {
173
204
  if (seen.has(row.route) || row.models.length === 0) return false;
174
205
  seen.add(row.route);
175
206
  return true;
176
- }).filter((row) => routeHealthRefusal(row.route, row.model || (row.models && row.models[0]) || '', identityCtx) === null);
207
+ }).filter((row) => routeHealthRefusal(row.route, row.model || (row.models && row.models[0]) || '', identityCtx) === null)
208
+ // WP-2 (§5 D-6): an offer the box's own origin probe calls unreachable is not an
209
+ // offer -- naming it back would round-trip to the same refusal.
210
+ .filter((row) => originReachabilityRefusal(row.route, identityCtx && identityCtx.availability) === null);
177
211
  const keyed = (row) => row.has_credential !== false;
178
212
  const rank = (row) => (keyed(row) ? 0 : 2) + (row.gateway === true ? 1 : 0);
179
213
  rows.sort((a, b) => rank(a) - rank(b) || (a.route < b.route ? -1 : a.route > b.route ? 1 : 0));
@@ -311,6 +345,26 @@ function resolveFromEffectivePref(effective, agent, listing, registryModels, ide
311
345
  }
312
346
  return unresolvablePref(effective, 'route_unavailable', { excluded, offers });
313
347
  }
348
+ // WP-2 (§5 D-6/D-7): the stored pick's route exists and serves the agent, but this
349
+ // machine's own origin probe says it cannot be reached -- refuse NOW with the same
350
+ // route_unavailable shape (and offers[] filtered by the same verdict), instead of
351
+ // letting CTM die later as a raw 503 upstream_unready, after launch work. The
352
+ // explicit path (resolveRoutedPair with caller-given route/model) never reaches here.
353
+ const originRefusal = originReachabilityRefusal(routeId, identityCtx && identityCtx.availability);
354
+ if (originRefusal) {
355
+ if (source === 'default') {
356
+ return {
357
+ ok: false, code: 'default_route_not_servable', source, agent, cause: 'route_unavailable',
358
+ default: { route: routeId, model: effective.model, agents: [] },
359
+ // WP-2 fix round 1: carry the WHY (this machine cannot reach the origin) and the
360
+ // origin itself -- without it the sentence could only say "unavailable right now",
361
+ // and the lead could not tell an unreachable origin from a route gone from the feed.
362
+ excluded: [originRefusal],
363
+ eligible: eligibleRouteOffers(listing, agent, identityCtx),
364
+ };
365
+ }
366
+ return unresolvablePref(effective, 'route_unavailable', { excluded: [originRefusal], offers: eligibleRouteOffers(listing, agent, identityCtx) });
367
+ }
314
368
  const resolved = resolveRoutedPair(agent, routeId, effective.model, listing, registryModels, identityCtx);
315
369
  if (!resolved.ok) {
316
370
  const { ok: _ok, code, ...detail } = resolved;
@@ -324,25 +378,36 @@ function resolveFromEffectivePref(effective, agent, listing, registryModels, ide
324
378
  * Precedence (lead-and-delegate-model-picker-ux-2026-10-08.html D-9): a route/model named in the
325
379
  * call > the lead's session pick > the default for its agent > the vendor default. Every success
326
380
  * names its rung as `source` ('explicit' | 'session' | 'default' | 'vendor') and its `lane`.
381
+ *
382
+ * §9 S-16 (opencode-lead-and-delegate-model-selection-2026-10-28.html §8 WP-5): `prefs.agent` is the
383
+ * LEAD's own agent (what readLeadDelegatePrefs resolved the parent session row to). It keys the
384
+ * LADDER; `args.agent` stays the worker HARNESS every resolved pair is checked against, so an
385
+ * OpenCode lead's stored default:opencode/session pick still launches a claude-code or codex-cli
386
+ * worker. An absent/unrecognized prefs.agent keeps today's derivation from args.agent exactly.
327
387
  * @param {{route?:string, model?:string, agent?:'claude'|'codex'}} args caller-given delegate args
328
388
  * @param {{default:{route,model}, routes:Array}} listing buildWorkerRoutesResult() shape
329
389
  * @param {Array} [registryModels] GET /api/models/registry rows, forwarded to resolveWorkerModel
330
390
  * @param {object} [identityCtx] resolveWorkerModel's identity-tier ctx
331
- * @param {{session?:object|null, default?:object|null}} [prefs] stored delegate_model_prefs rows
391
+ * @param {{session?:object|null, default?:object|null, agent?:'claude'|'codex'|'opencode'}} [prefs]
392
+ * stored delegate_model_prefs rows (+ the lead agent that keys them)
332
393
  * @returns {{ok:true, lane:'routed', route, model, source} | {ok:true, lane:'native', model, source}
333
394
  * | {ok:false, code:'model_unknown'|'model_ambiguous'|'route_unavailable'|'default_route_not_servable'|'delegate_pref_unresolvable', ...}}
334
395
  */
335
396
  function resolveDelegateArgs(args, listing, registryModels, identityCtx, prefs) {
336
397
  const agent = args && args.agent === 'codex' ? 'codex' : 'claude';
398
+ // S-16: the ladder is the LEAD's; the harness is the WORKER's. An OpenCode lead folds
399
+ // default:opencode (or its own session rung) while the resolved worker harness stays claude/codex.
400
+ const ladderAgent = prefs && (prefs.agent === 'codex' || prefs.agent === 'claude' || prefs.agent === 'opencode')
401
+ ? prefs.agent : agent;
337
402
  const route = args && args.route;
338
403
  const model = args && args.model;
339
404
 
340
405
  if (!route && !model) {
341
- const { effective } = foldDelegateLadder(prefs, agent);
406
+ const { effective } = foldDelegateLadder(prefs, ladderAgent);
342
407
  return resolveFromEffectivePref(effective, agent, listing, registryModels, identityCtx);
343
408
  }
344
409
  const resolved = resolveRoutedPair(agent, route, model, listing, registryModels, identityCtx);
345
410
  return resolved.ok ? { ...resolved, lane: 'routed', source: 'explicit' } : resolved;
346
411
  }
347
412
 
348
- module.exports = { resolveDelegateArgs, isExactModelOnRoute, delegateArgsError, routeThrottledError, briefRefusalError };
413
+ module.exports = { resolveDelegateArgs, isExactModelOnRoute, delegateArgsError, routeThrottledError, briefRefusalError, originReachabilityRefusal };