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
@@ -20,6 +20,7 @@
20
20
  * §8 WP-2, §9 S-5.
21
21
  */
22
22
 
23
+ const { moveToOwnScope } = require('./systemd-scope');
23
24
  const path = require('node:path');
24
25
  const crypto = require('node:crypto');
25
26
  const { spawn } = require('node:child_process');
@@ -99,7 +100,7 @@ const PROXY_LIVENESS_MS = 10_000;
99
100
  // recovery branch below, exactly as a SIGKILL of a spawned child is.
100
101
  const ADOPTED_PID_GONE = 'ADOPTED_PID_GONE';
101
102
  // The armed probe, or null. Module memory only: it is a live timer, and it is
102
- // dropped on stop()/_resetForTest() and whenever the mode leaves 'adopted'.
103
+ // dropped on stop()/_resetForTest() and whenever the mode leaves 'child'/'adopted'.
103
104
  let _livenessTimer = null;
104
105
  // §6 test seam: the cadence a test can shrink, so AC-3a's 10 s bound is not a
105
106
  // 10 s test. Production never writes it.
@@ -107,6 +108,9 @@ let _livenessProbeMs = PROXY_LIVENESS_MS;
107
108
  // Set by stop() right before it kills the child, cleared by the child's 'exit'
108
109
  // handler. A graceful stop must never trigger the lead-token auto-restart.
109
110
  let _stopping = false;
111
+ // §5 D-1b: whether the last heartbeat renewal failed. Module memory only, so one stderr line marks each
112
+ // transition (ok->failed, failed->ok) rather than one per tick. Reset by _onProxyGone/stop/_resetForTest.
113
+ let _heartbeatFailing = false;
110
114
 
111
115
  // WP-3 (§5 D-6): CTM-side mirror of the proxy's pick table. CTM pushes the full table
112
116
  // on every delegate-pick mutation; start() replays it into a fresh child so the same
@@ -170,11 +174,14 @@ function isRunning() {
170
174
  return _mode === 'adopted' || _mode === 'adopting';
171
175
  }
172
176
 
173
- // ── §5 D-6: liveness in adopted mode ─────────────────────────────────────
177
+ // ── §5 D-6/D-1: one liveness timer in BOTH modes ────────────────────────
174
178
  // A proxy THIS CTM spawned reports its own death: the child handle has an 'exit' event. An ADOPTED proxy
175
179
  // has no handle -- it is somebody else's process that outlived the CTM that started it -- so nothing but
176
- // CTM's own probe can notice the pid going away. That is the whole of D-6: one timer, the same
177
- // _onProxyGone() handler, and the same one rate-limited restart on the other side of it.
180
+ // CTM's own probe can notice the pid going away. D-6 was adopted-only; D-1 arms the SAME single timer in
181
+ // both modes. Every tick is one authenticated admin read, and that read is what renews the proxy's own D-8
182
+ // linger lease (docs/model-proxy-linger-without-ctm-heartbeat-2026-10-27.html D-1): the adopted tick ALSO
183
+ // checks the pid first, and the child tick does not need to (its 'exit' event already owns that). Recovery
184
+ // on the other side is the same _onProxyGone() handler and the same one rate-limited restart.
178
185
 
179
186
  function _clearLivenessProbe() {
180
187
  if (_livenessTimer) clearTimeout(_livenessTimer);
@@ -187,27 +194,58 @@ function _setLivenessProbeMsForTest(ms) {
187
194
  }
188
195
 
189
196
  /**
190
- * §5 D-6: arm the ONE probe for the proxy currently adopted. Re-arming replaces the timer, so a call
191
- * after a WP-2 handoff watches the SUCCESSOR's pid and not the predecessor's.
197
+ * §5 D-1: does `owner` -- the identity captured when the timer was armed -- still own this module? A
198
+ * stop(), a fresh spawn or a newer adoption reassigns the state, and the stale tick must then do nothing.
199
+ */
200
+ function _ownsProbe(owner) {
201
+ return (_mode === 'child' && _child === owner) || (_mode === 'adopted' && _adopted === owner);
202
+ }
203
+
204
+ /**
205
+ * §5 D-1: arm the ONE probe for the proxy currently owned -- the spawned child in 'child' mode, the
206
+ * adopted proxy in 'adopted' mode. Re-arming replaces the timer, so a call after a WP-2 handoff watches
207
+ * the SUCCESSOR's pid and not the predecessor's. No owner means no timer at all.
192
208
  */
193
209
  function _armLivenessProbe({ port, dataDir }) {
194
210
  _clearLivenessProbe();
195
- if (_mode !== 'adopted' || !_adopted) return;
196
- _livenessTimer = setTimeout(() => {
211
+ const owner = _mode === 'child' ? _child : (_mode === 'adopted' ? _adopted : null);
212
+ if (!owner) return;
213
+ _livenessTimer = setTimeout(async () => {
197
214
  _livenessTimer = null;
198
- // Re-checked at fire time: a stop() or a newer adoption in between owns the state now.
199
- if (_mode !== 'adopted' || !_adopted) return;
200
- const pid = _adopted.pid;
201
- if (instanceRecord.isProcessAlive(pid)) {
202
- _armLivenessProbe({ port, dataDir });
215
+ // Re-checked at fire time: a stop(), a fresh spawn or a newer adoption owns the state now.
216
+ if (!_ownsProbe(owner)) return;
217
+ // §5 D-6: only the ADOPTED identity needs a pid check -- it is the one process nothing else reports on.
218
+ if (_mode === 'adopted' && !instanceRecord.isProcessAlive(_adopted.pid)) {
219
+ _onProxyGone({ code: null, signal: ADOPTED_PID_GONE, port, dataDir });
203
220
  return;
204
221
  }
205
- _onProxyGone({ code: null, signal: ADOPTED_PID_GONE, port, dataDir });
222
+ const pid = _ownedPid();
223
+ // §5 D-1: the authenticated admin read IS the lease renewal; a failed read is reported, never fatal.
224
+ const ok = _adminSecret ? await _probeAdminSecretImpl({ port, secret: _adminSecret }) : false;
225
+ _noteHeartbeat({ ok, pid });
226
+ if (_ownsProbe(owner)) _armLivenessProbe({ port, dataDir });
206
227
  }, _livenessProbeMs);
207
228
  // §5 D-6: a probe is never a reason for CTM to stay alive.
208
229
  _livenessTimer.unref();
209
230
  }
210
231
 
232
+ /**
233
+ * §5 D-1b: report a heartbeat that stopped being answered, and one that started again -- each on its own
234
+ * transition only, so a long outage is one line and not one per tick. A failed renewal is NOT a death
235
+ * verdict: death is the child 'exit' event, or the adopted pid check above.
236
+ */
237
+ function _noteHeartbeat({ ok, pid }) {
238
+ if (ok) {
239
+ if (!_heartbeatFailing) return;
240
+ _heartbeatFailing = false;
241
+ process.stderr.write(`[oauth-proxy-supervisor] proxy heartbeat recovered pid=${pid}\n`);
242
+ return;
243
+ }
244
+ if (_heartbeatFailing) return;
245
+ _heartbeatFailing = true;
246
+ process.stderr.write(`[oauth-proxy-supervisor] proxy heartbeat failed pid=${pid}\n`);
247
+ }
248
+
211
249
  /**
212
250
  * §5 D-6: an admin call that cannot reach an ADOPTED proxy is the cheapest probe there is -- run the same
213
251
  * test the timer runs, so a dead proxy is noticed now instead of on the next tick. Only the two codes that
@@ -377,20 +415,42 @@ async function _performUpgradeHandoff({ port }) {
377
415
  handed = await requestHandoff({ port, reason: 'upgrade-pending' });
378
416
  } catch (err) {
379
417
  // REPORT: the old proxy is still serving; the next restart asks again.
380
- process.stderr.write(`[oauth-proxy-supervisor] proxy handoff refused: ${String((err && err.reason) || (err && err.message) || err).slice(0, 200)}\n`);
418
+ const refusal = String((err && err.reason) || (err && err.message) || err).slice(0, 200);
419
+ process.stderr.write(`[oauth-proxy-supervisor] proxy handoff refused: ${refusal}\n`);
420
+ return;
421
+ }
422
+ // §5 D-2 (WP-0 AC-0d): the 202 names the successor the PREDECESSOR proved it was waiting for, and only the
423
+ // process answering on the wire may be published as this adoption's identity or replayed into. A probe that
424
+ // names anybody else -- the draining predecessor still answering a connection it accepted before the handoff,
425
+ // or a foreign listener that took the port -- is a bounded refusal. Nothing is thrown or killed: a throw here
426
+ // would fall through _finishAdoption's catch into a fresh spawn over a successor already serving this port.
427
+ const successorHealth = await _probeHealth(port);
428
+ if (!successorHealth || successorHealth.pid !== handed.successorPid) {
429
+ const answeringPid = successorHealth && Number.isSafeInteger(successorHealth.pid) ? successorHealth.pid : null;
430
+ // The TYPED refusal is `_adopted.decision` plus the line below: `_finishAdoption` is the only caller and
431
+ // ignores a return value, so nothing is returned that could be mistaken for success. The acknowledged
432
+ // successor is still named TRUTHFULLY (it is the process the predecessor handed its sockets to, and the one
433
+ // the liveness probe must watch), but no closure identity is published, no replay is attempted, no retry is
434
+ // scheduled, and no handoff timing is reported (a null handoffMs reads as an adoption, never a handoff).
435
+ // Fail-closed: the adopted proxy keeps serving, and nothing is killed or spawned over it.
436
+ _adopted = {
437
+ ..._adopted, pid: handed.successorPid, generation: handed.generation, handoffMs: null,
438
+ decision: 'upgrade-pending-unverified', closureHash: null,
439
+ };
440
+ process.stderr.write('[oauth-proxy-supervisor] proxy handoff unverified: successor_identity_mismatch'
441
+ + ` expected_pid=${handed.successorPid} answering_pid=${answeringPid === null ? 'none' : answeringPid}\n`);
381
442
  return;
382
443
  }
383
444
  // The successor loaded the code on disk, so the predecessor's closure hash is no longer this adoption's
384
445
  // identity: status() and the restore_complete row must describe the process now answering. (WP-4 live
385
446
  // proof, 2026-10-23: status reported the predecessor's hash after a handoff while /_health had the new one.)
386
- const successorHealth = await _probeHealth(port);
387
447
  _adopted = {
388
448
  ..._adopted,
389
449
  pid: handed.successorPid,
390
450
  generation: handed.generation,
391
451
  handoffMs: handed.handoffMs,
392
452
  decision: 'upgrade-pending',
393
- closureHash: successorHealth && successorHealth.pid === handed.successorPid ? (successorHealth.closure_hash || null) : null,
453
+ closureHash: successorHealth.closure_hash || null,
394
454
  };
395
455
  // The successor starts with the token table the predecessor handed over PLUS whatever CTM replays; the
396
456
  // replay is the same idempotent call the fresh-spawn path makes, so a token cannot be double-registered.
@@ -464,6 +524,13 @@ function _spawnFresh({ port, resolvedDataDir }) {
464
524
  // §5 D-1: CTM is never held open by the proxy it spawned -- the proxy outlives this process and the
465
525
  // next CTM adopts it (D-4), so nothing here may wait on its exit.
466
526
  _child.unref();
527
+ // WP-1 (S-4, session-host-cgroup-survival): on a systemd service host the proxy must leave ctm.service's cgroup
528
+ // or a restart reaps it. Not awaited (the proxy spawns no children); failure is REPORTED, never fatal.
529
+ moveToOwnScope(_child.pid, { label: 'oauth-proxy' }).then((moved) => {
530
+ if (!moved.moved && moved.reason !== 'not-systemd' && moved.reason !== 'disabled') {
531
+ process.stderr.write(`[host-scope] move failed host=oauth-proxy pid=${_child && _child.pid} reason=${moved.reason}\n`);
532
+ }
533
+ });
467
534
  // D-13: capture THIS spawn so a superseded child's late 'exit' (stop() -> start(), or a rapid
468
535
  // restart) cannot null the current child's handle / admin secret out from under a fresh boot.
469
536
  const child = _child;
@@ -477,6 +544,9 @@ function _spawnFresh({ port, resolvedDataDir }) {
477
544
  // below is the authoritative signal, not this write.
478
545
  _lastError = `admin secret handoff failed: ${err.message}`;
479
546
  }
547
+ // §5 D-1a: arm the lease-renewal probe for THIS child, exactly as the adopted path arms its own. Armed
548
+ // after the secret write so the first tick already has a secret to authenticate with.
549
+ _armLivenessProbe({ port, dataDir: resolvedDataDir });
480
550
  _startedAt = Date.now();
481
551
  child.on('error', (err) => {
482
552
  _lastError = err.message;
@@ -509,7 +579,11 @@ function _onProxyGone({ code, signal, port, dataDir }) {
509
579
  // recovery either way; only the reaping differs, because the record a dead adopted pid left behind is
510
580
  // stale truth that start() would otherwise have to discover by ps.
511
581
  const adoptedPid = _mode === 'adopted' || _mode === 'adopting' ? (_adopted ? _adopted.pid : null) : null;
582
+ // §5 D-2: the pid the exit line names, captured before the state reset below.
583
+ const gonePid = _child ? _child.pid : adoptedPid;
512
584
  _clearLivenessProbe();
585
+ // §5 D-1b: the next identity starts with a clean heartbeat verdict.
586
+ _heartbeatFailing = false;
513
587
  if (adoptedPid !== null) {
514
588
  process.stderr.write(`[oauth-proxy-supervisor] adopted proxy pid=${adoptedPid} gone\n`);
515
589
  instanceRecord.unlinkInstanceRecord({ dataDir });
@@ -526,16 +600,21 @@ function _onProxyGone({ code, signal, port, dataDir }) {
526
600
  // The dead child's in-memory token table is gone; any revoke that was still
527
601
  // pending against it no longer has anything to reap.
528
602
  _pendingLeadRevokes.clear();
529
- // WP-0 (§6.2): an unexpected exit (signal, or a non-zero code) with live
530
- // lead tokens must bring the proxy back so those tokens keep working. A
531
- // graceful stop() opts out via _stopping; a crash-loop is rate-limited.
532
- const unexpected = !!signal || code !== 0;
533
603
  const wasStopping = _stopping;
534
604
  _stopping = false;
535
605
  // §9 S-5: the child records every SIGTERM exit, including the one a deliberate stop() sent. A stop
536
606
  // is not a restart, so its record must not become the next start()'s "previous exit".
537
607
  if (wasStopping) clearExitRecord({ dataDir });
538
- if (unexpected && !wasStopping && _leadTokens.size > 0) {
608
+ // §5 D-2: with D-1 keeping the lease renewed, a linger under a live CTM is already a bug -- so a clean
609
+ // code-0 exit nobody asked for is reported as what it is (and recovered below like any other exit).
610
+ if (!wasStopping && code === 0 && !signal) {
611
+ _lastError = 'exited code=0 unrequested';
612
+ process.stderr.write(`[oauth-proxy-supervisor] proxy pid=${gonePid} exited code=0 without stop()\n`);
613
+ }
614
+ // WP-0 (§6.2) + §5 D-2: ANY exit stop() did not ask for -- clean or not -- with live lead tokens must
615
+ // bring the proxy back so those tokens keep working. A graceful stop() opts out via _stopping; a
616
+ // crash-loop is rate-limited.
617
+ if (!wasStopping && _leadTokens.size > 0) {
539
618
  if (Date.now() - _lastLeadAutoRestartAt >= LEAD_RESTART_MIN_INTERVAL_MS) {
540
619
  _lastLeadAutoRestartAt = Date.now();
541
620
  // WP-1 §5 D-3: the restart is bound to the directory the DEAD child was actually running with,
@@ -552,6 +631,8 @@ function stop() {
552
631
  const mode = _mode;
553
632
  // §5 D-6: whatever happens below, this CTM is no longer watching that pid.
554
633
  _clearLivenessProbe();
634
+ // §5 D-1b: a stopped CTM has no heartbeat verdict to carry into the next start().
635
+ _heartbeatFailing = false;
555
636
  if (!isRunning()) {
556
637
  _adminSecret = null;
557
638
  _activeChildDataDir = null;
@@ -615,11 +696,10 @@ async function status({ port = DEFAULT_PORT } = {}) {
615
696
  }
616
697
  let health = null;
617
698
  try {
618
- const resp = await fetch(`http://127.0.0.1:${port}/_health`, {
619
- signal: AbortSignal.timeout(2000),
620
- });
621
- if (resp.ok) health = await resp.json();
699
+ health = await _controlHealth(port, { signal: AbortSignal.timeout(2000) });
622
700
  } catch (err) {
701
+ // §5 D-1: unchanged meaning -- only a network/protocol failure lands here; an unsuccessful status is a
702
+ // null health that leaves `_lastError` alone, and the helper releases its body either way.
623
703
  _lastError = `health probe failed: ${err.message}`;
624
704
  }
625
705
  // Never include the admin secret here — status() is a diagnostics surface
@@ -695,11 +775,41 @@ function hasLiveSurvivor({ port = DEFAULT_PORT, dataDir } = {}) {
695
775
  return !!record && record.port === port && instanceRecord.isProxyProcess(record.pid);
696
776
  }
697
777
 
778
+ // §5 D-1 (WP-0 AC-0b, docs/native-owner-restart-handoff-2026-10-26.html): every supervisor CONTROL request is
779
+ // one-use. Fetch's `keepalive:false` is unrelated; the HTTP `Connection: close` header is what undici honours
780
+ // by giving the request its own closed-when-done connection. Without it the pool can hand a later control
781
+ // request the socket opened against the PREDECESSOR, which stops ACCEPTING on a handoff but keeps answering
782
+ // connections it already accepted (`closeIdleConnections()` cannot close one still serving the handoff
783
+ // response). INFERENCE, not packet-level observation: that reuse is how the native-owner replay lands in the
784
+ // draining process while the successor holding the port never learns the owner -- what the live 00:47:11Z
785
+ // `native_lead_listener_closed` + 401 `native_owner_unknown` sequence looks like outside, reproduced in
786
+ // `.scratch/native-owner/socket-handoff.cjs`. Lifecycle traffic only: no dispatcher, flag or retry.
787
+ const CONTROL_HEADERS = Object.freeze({ connection: 'close' });
788
+
789
+ // §5 D-1: release the body of a control response this call is not going to read, so neither a reader nor
790
+ // its socket outlives the call. Already-consumed bodies and 204/304 (`body` null) are simply nothing to do.
791
+ async function _releaseControlResponse(resp) {
792
+ if (!resp || !resp.body || resp.bodyUsed) return;
793
+ try { await resp.body.cancel(); } catch { /* expected: consumed, already cancelled, or the peer is gone */ }
794
+ }
795
+
796
+ // §5 D-1: the `/_health` control request shared by the two callers wanting its parsed body (`_probeHealth`,
797
+ // `status()`). A non-ok status answers `null`; a dead proxy or malformed body THROWS, so each caller keeps its
798
+ // own meaning for that failure (a refused adoption, or `status()`'s `_lastError`).
799
+ async function _controlHealth(port, init) {
800
+ let resp = null;
801
+ try {
802
+ resp = await fetch(`http://127.0.0.1:${port}/_health`, { headers: CONTROL_HEADERS, ...init });
803
+ return resp.ok ? await resp.json() : null;
804
+ } finally {
805
+ await _releaseControlResponse(resp);
806
+ }
807
+ }
808
+
698
809
  /** §5 D-4 step 4: the health probe the adoption decision reads, with its own 2 s budget. */
699
810
  async function _probeHealth(port, timeoutMs = 2000) {
700
811
  try {
701
- const resp = await fetch(`http://127.0.0.1:${port}/_health`, { signal: AbortSignal.timeout(timeoutMs) });
702
- return resp.ok ? await resp.json() : null;
812
+ return await _controlHealth(port, { signal: AbortSignal.timeout(timeoutMs) });
703
813
  } catch (err) {
704
814
  // expected: an unreachable proxy is a refused adoption, never a thrown boot
705
815
  void err;
@@ -711,9 +821,10 @@ async function _probeHealth(port, timeoutMs = 2000) {
711
821
  async function _probeAdminSecret({ port, secret, timeoutMs = 2000 }) {
712
822
  try {
713
823
  const resp = await fetch(`http://127.0.0.1:${port}/_admin/lead-worker-picks`, {
714
- headers: { [ADMIN_HEADER]: secret },
824
+ headers: { ...CONTROL_HEADERS, [ADMIN_HEADER]: secret },
715
825
  signal: AbortSignal.timeout(timeoutMs),
716
826
  });
827
+ await _releaseControlResponse(resp);
717
828
  return resp.ok;
718
829
  } catch (err) {
719
830
  // expected: an unreachable proxy cannot confirm the secret; the caller falls back to a spawn
@@ -722,6 +833,14 @@ async function _probeAdminSecret({ port, secret, timeoutMs = 2000 }) {
722
833
  }
723
834
  }
724
835
 
836
+ // §6 test seam: the D-1 renewal request itself. Injectable so a case can count heartbeats without touching
837
+ // the proxy. Production never calls the setter; _resetForTest restores it.
838
+ let _probeAdminSecretImpl = _probeAdminSecret;
839
+
840
+ function _setProbeAdminSecretForTest(fn) {
841
+ _probeAdminSecretImpl = typeof fn === 'function' ? fn : _probeAdminSecret;
842
+ }
843
+
725
844
  // ── D-8 readiness ────────────────────────────────────────────────────────
726
845
  // Polls /_health every ~100ms until it answers ok or the deadline passes.
727
846
  // A single fetch is not enough here: during the child's cold start (just
@@ -757,8 +876,10 @@ async function _pollHealth({ port = DEFAULT_PORT, timeoutMs = 2000, signal } = {
757
876
  const probeTimeout = Math.max(1, Math.min(500, remaining));
758
877
  try {
759
878
  const resp = await fetch(`http://127.0.0.1:${port}/_health`, {
879
+ headers: CONTROL_HEADERS,
760
880
  signal: signal ? AbortSignal.any([AbortSignal.timeout(probeTimeout), signal]) : AbortSignal.timeout(probeTimeout),
761
881
  });
882
+ await _releaseControlResponse(resp);
762
883
  if (resp.ok) return true;
763
884
  } catch {
764
885
  // expected: connection refused during cold start, or the probe's own
@@ -787,6 +908,7 @@ async function _adminRequest({ port = DEFAULT_PORT, method, path: adminPath, bod
787
908
  resp = await fetch(`http://127.0.0.1:${port}${adminPath}`, {
788
909
  method,
789
910
  headers: {
911
+ ...CONTROL_HEADERS,
790
912
  'Content-Type': 'application/json',
791
913
  [ADMIN_HEADER]: _adminSecret,
792
914
  },
@@ -802,6 +924,7 @@ async function _adminRequest({ port = DEFAULT_PORT, method, path: adminPath, bod
802
924
  if (returnHttpErrors) {
803
925
  let errorBody = null;
804
926
  try { errorBody = await resp.json(); } catch { /* expected: an empty or non-JSON rejection */ }
927
+ await _releaseControlResponse(resp);
805
928
  return errorBody && typeof errorBody === 'object'
806
929
  ? { ...errorBody, http_status: resp.status }
807
930
  : { error: 'admin_request_failed', http_status: resp.status };
@@ -814,13 +937,18 @@ async function _adminRequest({ port = DEFAULT_PORT, method, path: adminPath, bod
814
937
  // expected: a non-JSON error body (e.g. plain 401 text) — the status-based
815
938
  // reason above is enough.
816
939
  }
940
+ await _releaseControlResponse(resp);
817
941
  throw new ProxyAdminError('admin_request_failed', reason);
818
942
  }
819
943
  const len = resp.headers.get('content-length');
820
- if (resp.status === 204 || len === '0') return null;
944
+ if (resp.status === 204 || len === '0') {
945
+ await _releaseControlResponse(resp);
946
+ return null;
947
+ }
821
948
  try {
822
949
  return await resp.json();
823
950
  } catch {
951
+ await _releaseControlResponse(resp);
824
952
  return null;
825
953
  }
826
954
  }
@@ -1035,7 +1163,7 @@ function _leadEntryKey(entry) {
1035
1163
  nativeCertFingerprint: native ? (entry.nativeCertFingerprint || null) : null,
1036
1164
  // The public settings locator is identity for the same reason: the same token may not silently start
1037
1165
  // pointing at a different reserved directory.
1038
- nativeSettingsRef: native ? (entry.nativeSettingsRef || null) : null,
1166
+ nativeSettingsRef: native ? (entry.nativeSettingsRef || null) : null, usageLog: entry.usageLog || null, // WP-9 L-2: the usage mark is identity
1039
1167
  });
1040
1168
  }
1041
1169
 
@@ -1057,14 +1185,14 @@ async function _flushPendingLeadRevokes({ port = DEFAULT_PORT } = {}) {
1057
1185
 
1058
1186
  function _normalizeLeadRegistration({ ctmSessionId, aliases, bindings, processGeneration, transitionId,
1059
1187
  purpose = 'routed-primary', delegateScope = false, harness, providerThreadId, nativeTransport,
1060
- nativeCertFingerprint, nativeSettingsRef } = {}) {
1188
+ nativeCertFingerprint, nativeSettingsRef, usageLog } = {}) {
1061
1189
  if (typeof ctmSessionId !== 'string' || !SAFE_LEAD_OWNER_ID.test(ctmSessionId)
1062
1190
  || !Array.isArray(aliases) || aliases.length === 0 || aliases.length > 64
1063
1191
  || aliases.some((alias) => !_safeLeadDeploymentId(alias))
1064
1192
  || new Set(aliases).size !== aliases.length
1065
1193
  || !Number.isSafeInteger(processGeneration) || processGeneration < 1
1066
1194
  || typeof delegateScope !== 'boolean') return null;
1067
- if (purpose !== 'routed-primary' && purpose !== 'native-claude-passthrough') return null;
1195
+ if (purpose !== 'routed-primary' && purpose !== 'native-claude-passthrough') return null; if (usageLog != null && (usageLog !== 'dsh' || purpose !== 'routed-primary')) return null; // WP-9 L-2 (D-19): dsh usage mark, routed leads only
1068
1196
  if (nativeTransport != null && !LEAD_NATIVE_TRANSPORTS.includes(nativeTransport)) return null;
1069
1197
  if (nativeCertFingerprint != null
1070
1198
  && (typeof nativeCertFingerprint !== 'string' || !LEAD_NATIVE_CERT_FINGERPRINT_RE.test(nativeCertFingerprint))) return null;
@@ -1116,7 +1244,7 @@ function _normalizeLeadRegistration({ ctmSessionId, aliases, bindings, processGe
1116
1244
  if (providerThreadId != null && (typeof providerThreadId !== 'string' || !SAFE_LEAD_OWNER_ID.test(providerThreadId))) return null;
1117
1245
  return { ctmSessionId, aliases: [...aliases], bindings: normalizedBindings, processGeneration,
1118
1246
  purpose, delegateScope, ...(normalizedHarness ? { harness: normalizedHarness } : {}), ...(providerThreadId ? { providerThreadId } : {}),
1119
- ...(transitionId != null ? { transitionId } : {}) };
1247
+ ...(transitionId != null ? { transitionId } : {}), ...(usageLog ? { usageLog } : {}) };
1120
1248
  }
1121
1249
 
1122
1250
  async function registerLeadToken(registration = {}) {
@@ -1332,14 +1460,14 @@ async function probeRoute(alias, { timeoutMs = 2000, port = DEFAULT_PORT, signal
1332
1460
  // Generation readiness is stronger than probeRoute() reachability: it sends one fixed,
1333
1461
  // minimal inference through the provider's configured wire and returns the proxy's
1334
1462
  // normalized receipt. Admission release remains a caller responsibility.
1335
- async function probeRouteGeneration(alias, { model, timeoutMs = 30_000, port = DEFAULT_PORT } = {}) {
1463
+ async function probeRouteGeneration(alias, { model, timeoutMs = 30_000, port = DEFAULT_PORT, face } = {}) {
1336
1464
  const requestedTimeout = Number(timeoutMs);
1337
1465
  const cappedTimeout = Math.max(1, Math.min(Number.isFinite(requestedTimeout) && requestedTimeout > 0 ? requestedTimeout : 30_000, 30_000));
1338
1466
  return _adminRequest({
1339
1467
  port,
1340
1468
  method: 'POST',
1341
1469
  path: `/_admin/routes/${encodeURIComponent(alias)}/generation-probe`,
1342
- body: { model, timeoutMs: cappedTimeout },
1470
+ body: { model, timeoutMs: cappedTimeout, ...(typeof face === 'string' ? { face } : {}) },
1343
1471
  timeoutMs: cappedTimeout + 1000,
1344
1472
  returnHttpErrors: true,
1345
1473
  });
@@ -1364,8 +1492,10 @@ function _resetForTest() {
1364
1492
  _pendingLeadRevokes.clear();
1365
1493
  _leadWorkerPicks = [];
1366
1494
  _hashClosureFiles = hashClosureFiles;
1495
+ _probeAdminSecretImpl = _probeAdminSecret;
1367
1496
  _clearLivenessProbe();
1368
1497
  _livenessProbeMs = PROXY_LIVENESS_MS;
1498
+ _heartbeatFailing = false;
1369
1499
  }
1370
1500
 
1371
1501
  module.exports = {
@@ -1411,6 +1541,13 @@ module.exports = {
1411
1541
  // §5 D-6/§6: shrink the adopted-mode probe cadence so a liveness case does not take 10 s (and so
1412
1542
  // AC-3a's own bound can be checked against production's constant, which is exported beside it).
1413
1543
  _setLivenessProbeMsForTest,
1544
+ // §5 D-1/D-1b (§6): the armed timer (read-only) and the renewal request the probe sends, so a case can
1545
+ // assert one timer is ever armed and count heartbeats without touching the proxy. Production never
1546
+ // calls the setter; the real implementation is exported beside it so a wrapper can call through.
1547
+ get _livenessTimer() { return _livenessTimer; },
1548
+ get _heartbeatFailing() { return _heartbeatFailing; },
1549
+ _probeAdminSecret,
1550
+ _setProbeAdminSecretForTest,
1414
1551
  // §5 D-7/§6 (AC-2f): make the disk-side closure hash disagree, so the adoption decision reads
1415
1552
  // `upgrade-pending` and the handoff path runs, without editing source. Pass null to restore.
1416
1553
  _setHashClosureFilesForTest,
@@ -13,6 +13,7 @@ function workerError(payload) {
13
13
  function createOnlineBackupRunner(options = {}) {
14
14
  const getDbPath = options.getDbPath;
15
15
  const getBackupDir = options.getBackupDir;
16
+ const getRetentionDays = options.getRetentionDays;
16
17
  const WorkerClass = options.WorkerClass || Worker;
17
18
  const workerPath = options.workerPath || path.join(__dirname, '..', 'workers', 'online-backup-worker.js');
18
19
  const timeoutMs = Math.max(1000, Number(options.timeoutMs || 15 * 60 * 1000));
@@ -46,6 +47,7 @@ function createOnlineBackupRunner(options = {}) {
46
47
  dbPath,
47
48
  backupDir,
48
49
  label: String(meta.label || 'manual'),
50
+ retentionDays: typeof getRetentionDays === 'function' ? Number(getRetentionDays()) : undefined,
49
51
  },
50
52
  env: { ...process.env },
51
53
  });