create-walle 0.10.0 → 0.10.2

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 (381) hide show
  1. package/README.md +88 -41
  2. package/lib/owner-write-safety.js +135 -0
  3. package/lib/public-runtime.js +13 -5
  4. package/lib/release-schema.json +3 -2
  5. package/lib/runtime-closure-policy.json +7 -5
  6. package/package.json +10 -4
  7. package/template/bin/deps-doctor.js +1 -1
  8. package/template/bin/node-bin.sh +11 -0
  9. package/template/coding-task-manager/api-prompts.js +10 -22
  10. package/template/coding-task-manager/bin/capture-codex-rollout-records.js +417 -0
  11. package/template/coding-task-manager/bin/census-codex-lead-history.js +325 -0
  12. package/template/coding-task-manager/bin/claude-hook.js +7 -3
  13. package/template/coding-task-manager/bin/claude-table-parity.js +307 -0
  14. package/template/coding-task-manager/bin/conversation-storage-auto-migration-lib.js +16 -1
  15. package/template/coding-task-manager/bin/conversation-storage-generation-evidence.js +9 -2
  16. package/template/coding-task-manager/bin/conversation-storage-service-entry-lib.js +16 -1
  17. package/template/coding-task-manager/bin/conversation-storage-walle-owner.js +247 -0
  18. package/template/coding-task-manager/bin/ctm-machine-pair.js +160 -0
  19. package/template/coding-task-manager/bin/lead-wait-census.js +1348 -0
  20. package/template/coding-task-manager/bin/repair-foreign-clone.js +564 -0
  21. package/template/coding-task-manager/bin/repair-resume-echo-titles.js +248 -0
  22. package/template/coding-task-manager/bin/seam-replay-codex.js +301 -0
  23. package/template/coding-task-manager/bin/write-lock-bench.js +111 -0
  24. package/template/coding-task-manager/db.js +705 -286
  25. package/template/coding-task-manager/git-utils.js +44 -0
  26. package/template/coding-task-manager/lib/adopted-host-redial.js +4 -0
  27. package/template/coding-task-manager/lib/agent-presets.js +15 -0
  28. package/template/coding-task-manager/lib/agent-title-apply.js +88 -0
  29. package/template/coding-task-manager/lib/agent-title-backfill-read.js +92 -0
  30. package/template/coding-task-manager/lib/api/host-stats.js +98 -0
  31. package/template/coding-task-manager/lib/api/ingress.js +35 -12
  32. package/template/coding-task-manager/lib/api/machine-pairing.js +285 -0
  33. package/template/coding-task-manager/lib/api/machine-pool.js +307 -0
  34. package/template/coding-task-manager/lib/api/machines.js +461 -0
  35. package/template/coding-task-manager/lib/api/misc-ops.js +20 -5
  36. package/template/coding-task-manager/lib/api/providers.js +53 -6
  37. package/template/coding-task-manager/lib/api/resume-from-store.js +25 -0
  38. package/template/coding-task-manager/lib/api/sessions.js +160 -8
  39. package/template/coding-task-manager/lib/api/transcript-history.js +17 -4
  40. package/template/coding-task-manager/lib/api/worktrees.js +38 -37
  41. package/template/coding-task-manager/lib/approval-drift.js +16 -1
  42. package/template/coding-task-manager/lib/approval-logical-frame.js +5 -1
  43. package/template/coding-task-manager/lib/approval-scope.js +68 -4
  44. package/template/coding-task-manager/lib/auth-context.js +32 -2
  45. package/template/coding-task-manager/lib/auth-rate-limit.js +41 -1
  46. package/template/coding-task-manager/lib/auth-rules.js +186 -45
  47. package/template/coding-task-manager/lib/broadcast-serialize.js +2 -1
  48. package/template/coding-task-manager/lib/browser-session-broker.js +9 -1
  49. package/template/coding-task-manager/lib/browser-use-adapter.js +33 -2
  50. package/template/coding-task-manager/lib/claude-markdown-ansi.js +27 -85
  51. package/template/coding-task-manager/lib/claude-session-title.js +6 -30
  52. package/template/coding-task-manager/lib/claude-table-layout.js +322 -0
  53. package/template/coding-task-manager/lib/client-diag-handler.js +25 -0
  54. package/template/coding-task-manager/lib/codex-config-guard.js +33 -3
  55. package/template/coding-task-manager/lib/codex-harness-qualification.js +12 -0
  56. package/template/coding-task-manager/lib/codex-history-terminal-renderer.js +33 -33
  57. package/template/coding-task-manager/lib/codex-launch-health.js +29 -2
  58. package/template/coding-task-manager/lib/codex-link-preparation.js +9 -8
  59. package/template/coding-task-manager/lib/codex-local-links.js +267 -0
  60. package/template/coding-task-manager/lib/codex-markdown-lists.js +160 -0
  61. package/template/coding-task-manager/lib/codex-native-history-prefix.js +4 -0
  62. package/template/coding-task-manager/lib/codex-paths.js +2 -1
  63. package/template/coding-task-manager/lib/codex-source-handoff.js +186 -0
  64. package/template/coding-task-manager/lib/coding-agent-models.js +21 -30
  65. package/template/coding-task-manager/lib/coding-session-model-adapters.js +27 -2
  66. package/template/coding-task-manager/lib/coding-session-model-projection.js +5 -0
  67. package/template/coding-task-manager/lib/coding-session-model-runtime.js +1 -2
  68. package/template/coding-task-manager/lib/coding-session-model-state.js +10 -0
  69. package/template/coding-task-manager/lib/coding-session-model-sync-state.js +0 -1
  70. package/template/coding-task-manager/lib/conversation-import/cursor-guard-owner.js +10 -0
  71. package/template/coding-task-manager/lib/create/resolve-launch-params.js +8 -1
  72. package/template/coding-task-manager/lib/ctm-boot-lifecycle.js +17 -1
  73. package/template/coding-task-manager/lib/db-domain-tables.js +7 -0
  74. package/template/coding-task-manager/lib/db-domains.js +21 -2
  75. package/template/coding-task-manager/lib/db-owner-op-registry.js +7 -0
  76. package/template/coding-task-manager/lib/db-owner-worker-client.js +22 -0
  77. package/template/coding-task-manager/lib/db-schema-source-files.js +1 -0
  78. package/template/coding-task-manager/lib/feature-manifest.js +1 -3
  79. package/template/coding-task-manager/lib/fs-watcher.js +3 -2
  80. package/template/coding-task-manager/lib/harness-history-transition.js +41 -14
  81. package/template/coding-task-manager/lib/headless-inflight-ops.js +203 -0
  82. package/template/coding-task-manager/lib/headless-missing-reply.js +42 -0
  83. package/template/coding-task-manager/lib/headless-term-service.js +60 -23
  84. package/template/coding-task-manager/lib/history-transition-read-op.js +179 -23
  85. package/template/coding-task-manager/lib/host-control-dial.js +167 -0
  86. package/template/coding-task-manager/lib/host-handoff-orchestrator.js +16 -2
  87. package/template/coding-task-manager/lib/host-handoff.js +51 -13
  88. package/template/coding-task-manager/lib/host-health-ledger.js +164 -12
  89. package/template/coding-task-manager/lib/host-self-truth-watchdog.js +4 -0
  90. package/template/coding-task-manager/lib/host-stats.js +328 -0
  91. package/template/coding-task-manager/lib/host-threadpool-evacuation.js +160 -0
  92. package/template/coding-task-manager/lib/host-threadpool-monitor.js +14 -1
  93. package/template/coding-task-manager/lib/hosted-input-dispatch.js +203 -0
  94. package/template/coding-task-manager/lib/hosted-sync-worker.js +113 -5
  95. package/template/coding-task-manager/lib/instance-id-ownership-manifest.js +4 -0
  96. package/template/coding-task-manager/lib/instance-manifest-runtime.js +3 -0
  97. package/template/coding-task-manager/lib/instance-manifest.js +13 -1
  98. package/template/coding-task-manager/lib/instance-sync-status.js +15 -0
  99. package/template/coding-task-manager/lib/kernel/diag-log.js +12 -0
  100. package/template/coding-task-manager/lib/launch-model-override.js +160 -1
  101. package/template/coding-task-manager/lib/launch-plan-worker.js +59 -2
  102. package/template/coding-task-manager/lib/lead-model-observer.js +17 -2
  103. package/template/coding-task-manager/lib/lead-model-switch-runtime.js +57 -4
  104. package/template/coding-task-manager/lib/lead-model-switch.js +11 -2
  105. package/template/coding-task-manager/lib/lead-model-transition-runtime.js +42 -4
  106. package/template/coding-task-manager/lib/lead-model-transition-store.js +61 -3
  107. package/template/coding-task-manager/lib/legacy-draft-cancellation-recovery.js +111 -0
  108. package/template/coding-task-manager/lib/login-tripwire.js +18 -0
  109. package/template/coding-task-manager/lib/machine-pairing.js +238 -0
  110. package/template/coding-task-manager/lib/machine-pool.js +409 -0
  111. package/template/coding-task-manager/lib/machine-registry.js +749 -0
  112. package/template/coding-task-manager/lib/machine-self.js +66 -0
  113. package/template/coding-task-manager/lib/main-thread-lint-exemptions.json +56 -4
  114. package/template/coding-task-manager/lib/media-journal-control-reader.js +473 -0
  115. package/template/coding-task-manager/lib/media-journal.js +84 -27
  116. package/template/coding-task-manager/lib/media-store-options.js +43 -15
  117. package/template/coding-task-manager/lib/migrations/v56-host-samples-and-machine-pool.js +76 -0
  118. package/template/coding-task-manager/lib/mirror-feed-sanitize.js +12 -3
  119. package/template/coding-task-manager/lib/model-execution-recipe-metadata.js +16 -3
  120. package/template/coding-task-manager/lib/model-proxy/anthropic-face.js +4 -0
  121. package/template/coding-task-manager/lib/model-proxy/chat-response-translators.js +3 -0
  122. package/template/coding-task-manager/lib/model-proxy/collaboration-codec.js +94 -0
  123. package/template/coding-task-manager/lib/model-proxy/handoff.js +494 -0
  124. package/template/coding-task-manager/lib/model-proxy/lead-worker-picks.js +25 -0
  125. package/template/coding-task-manager/lib/model-proxy/native-lead-listener.js +61 -7
  126. package/template/coding-task-manager/lib/model-proxy/proxy-cli.js +200 -0
  127. package/template/coding-task-manager/lib/model-proxy/proxy-instance-record.js +257 -0
  128. package/template/coding-task-manager/lib/model-proxy/responses-face.js +24 -2
  129. package/template/coding-task-manager/lib/model-proxy/stream-accounting.js +205 -0
  130. package/template/coding-task-manager/lib/model-proxy/tokens.js +78 -0
  131. package/template/coding-task-manager/lib/model-proxy/upstream-outcome.js +9 -0
  132. package/template/coding-task-manager/lib/native-history-control.js +62 -14
  133. package/template/coding-task-manager/lib/native-lead-settings-client.js +2 -1
  134. package/template/coding-task-manager/lib/native-lead-tls.js +18 -4
  135. package/template/coding-task-manager/lib/oauth-proxy-supervisor.js +628 -64
  136. package/template/coding-task-manager/lib/owner-action-manifest-data.json +14 -0
  137. package/template/coding-task-manager/lib/painted-row-recorder.js +9 -1
  138. package/template/coding-task-manager/lib/prompt-admission-trace.js +69 -0
  139. package/template/coding-task-manager/lib/prompt-submission-contract.js +21 -1
  140. package/template/coding-task-manager/lib/provider-login-broker.js +192 -9
  141. package/template/coding-task-manager/lib/provider-login-capture.js +153 -0
  142. package/template/coding-task-manager/lib/queue-draft-status-overlay.js +36 -0
  143. package/template/coding-task-manager/lib/raw-codex-history-window.js +5 -3
  144. package/template/coding-task-manager/lib/raw-history-block.js +3 -1
  145. package/template/coding-task-manager/lib/read-pool-client.js +36 -24
  146. package/template/coding-task-manager/lib/read-pool-op-registry.js +10 -1
  147. package/template/coding-task-manager/lib/read-pool-telemetry.js +14 -5
  148. package/template/coding-task-manager/lib/recent-catalog-query.js +26 -0
  149. package/template/coding-task-manager/lib/recent-session-catalog-response.js +9 -0
  150. package/template/coding-task-manager/lib/remote-command-context.js +12 -0
  151. package/template/coding-task-manager/lib/remote-command-runtime.js +18 -5
  152. package/template/coding-task-manager/lib/remote-machine-catalog.js +1062 -0
  153. package/template/coding-task-manager/lib/remote-machine-create.js +693 -0
  154. package/template/coding-task-manager/lib/remote-machine-link.js +939 -0
  155. package/template/coding-task-manager/lib/remote-machine-proxy.js +520 -0
  156. package/template/coding-task-manager/lib/remote-session-forward.js +306 -0
  157. package/template/coding-task-manager/lib/remote-submission-status.js +6 -0
  158. package/template/coding-task-manager/lib/restart-outcome-truth.js +20 -3
  159. package/template/coding-task-manager/lib/restart-preflight.js +9 -2
  160. package/template/coding-task-manager/lib/restore-backoff-gate.js +10 -2
  161. package/template/coding-task-manager/lib/restore-prepare-retry.js +12 -5
  162. package/template/coding-task-manager/lib/restore-resume-batch.js +22 -1
  163. package/template/coding-task-manager/lib/resume-blocked-wire.js +62 -1
  164. package/template/coding-task-manager/lib/resume-cwd.js +13 -0
  165. package/template/coding-task-manager/lib/resume-from-store.js +12 -1
  166. package/template/coding-task-manager/lib/resume-origin-facts.js +38 -1
  167. package/template/coding-task-manager/lib/resume-preflight.js +21 -16
  168. package/template/coding-task-manager/lib/review-repo-admission.js +39 -6
  169. package/template/coding-task-manager/lib/review-repo-registry.js +17 -0
  170. package/template/coding-task-manager/lib/root-delivery-runtime.js +72 -15
  171. package/template/coding-task-manager/lib/root-delivery-selection.js +37 -1
  172. package/template/coding-task-manager/lib/root-prompt-admission.js +43 -11
  173. package/template/coding-task-manager/lib/root-prompt-flow.js +175 -38
  174. package/template/coding-task-manager/lib/scrollback-archive-store.js +7 -4
  175. package/template/coding-task-manager/lib/server-memory-sampler-runtime.js +10 -0
  176. package/template/coding-task-manager/lib/server-memory-sampler.js +41 -2
  177. package/template/coding-task-manager/lib/session-analysis-memory.js +2 -0
  178. package/template/coding-task-manager/lib/session-create/codex-native-provider-gate.js +218 -0
  179. package/template/coding-task-manager/lib/session-create/codex-resume-exit-classify.js +52 -0
  180. package/template/coding-task-manager/lib/session-create/delegate-worker.js +95 -6
  181. package/template/coding-task-manager/lib/session-create/identity.js +20 -1
  182. package/template/coding-task-manager/lib/session-create/prepared.js +69 -1
  183. package/template/coding-task-manager/lib/session-create/provider-launch-context.js +77 -0
  184. package/template/coding-task-manager/lib/session-create/provider-process.js +38 -39
  185. package/template/coding-task-manager/lib/session-create/response.js +30 -12
  186. package/template/coding-task-manager/lib/session-create/transcript.js +13 -4
  187. package/template/coding-task-manager/lib/session-create/worker-continuation.js +43 -1
  188. package/template/coding-task-manager/lib/session-create/worker-notices.js +94 -5
  189. package/template/coding-task-manager/lib/session-create/worker-observability.js +99 -7
  190. package/template/coding-task-manager/lib/session-create/worker-stop.js +11 -3
  191. package/template/coding-task-manager/lib/session-create/worker-wait-watcher.js +315 -0
  192. package/template/coding-task-manager/lib/session-geometry-authority-handler.js +10 -1
  193. package/template/coding-task-manager/lib/session-host-manager.js +152 -206
  194. package/template/coding-task-manager/lib/session-jobs.js +17 -9
  195. package/template/coding-task-manager/lib/session-lifecycle.js +20 -50
  196. package/template/coding-task-manager/lib/session-model-action-preconditions.js +68 -3
  197. package/template/coding-task-manager/lib/session-resource-sampler.js +67 -9
  198. package/template/coding-task-manager/lib/session-standup.js +67 -4
  199. package/template/coding-task-manager/lib/session-status-ledger.js +42 -3
  200. package/template/coding-task-manager/lib/session-status-runtime.js +45 -1
  201. package/template/coding-task-manager/lib/session-status.js +2 -2
  202. package/template/coding-task-manager/lib/session-title-mirror.js +25 -57
  203. package/template/coding-task-manager/lib/session-transcript-trash.js +8 -1
  204. package/template/coding-task-manager/lib/startup-pointer-write.js +57 -0
  205. package/template/coding-task-manager/lib/state-sync/painted-frame-field.js +11 -0
  206. package/template/coding-task-manager/lib/storage/coding-session-model-store.js +21 -22
  207. package/template/coding-task-manager/lib/surviving-owner-bootstrap.js +7 -2
  208. package/template/coding-task-manager/lib/surviving-owner-recovery.js +2 -1
  209. package/template/coding-task-manager/lib/terminal-draft-http-status.js +25 -0
  210. package/template/coding-task-manager/lib/transcript-archiver.js +229 -229
  211. package/template/coding-task-manager/lib/transcript-frame-cache.js +12 -2
  212. package/template/coding-task-manager/lib/transcript-guard-job.js +55 -4
  213. package/template/coding-task-manager/lib/transcript-link.js +77 -0
  214. package/template/coding-task-manager/lib/transcript-manifest-store.js +8 -4
  215. package/template/coding-task-manager/lib/transcript-source.js +8 -3
  216. package/template/coding-task-manager/lib/transcript-store.js +40 -0
  217. package/template/coding-task-manager/lib/tunnel-lane-governor.js +6 -3
  218. package/template/coding-task-manager/lib/typing-interaction-trace.js +136 -2
  219. package/template/coding-task-manager/lib/typing-worker-stages.js +208 -1
  220. package/template/coding-task-manager/lib/usage-ledger/claude-parse.js +53 -2
  221. package/template/coding-task-manager/lib/usage-ledger/ingest.js +13 -4
  222. package/template/coding-task-manager/lib/usage-ledger/read.js +14 -0
  223. package/template/coding-task-manager/lib/usage-ledger/schema.js +49 -1
  224. package/template/coding-task-manager/lib/usage-ledger/store.js +337 -6
  225. package/template/coding-task-manager/lib/usage-ledger/task-read.js +123 -5
  226. package/template/coding-task-manager/lib/visible-revision-watchdog.js +8 -12
  227. package/template/coding-task-manager/lib/wake-skip-reasons.js +46 -0
  228. package/template/coding-task-manager/lib/walle-supervisor.js +98 -14
  229. package/template/coding-task-manager/lib/worker-failure-classify.js +13 -1
  230. package/template/coding-task-manager/lib/ws/input.js +1 -0
  231. package/template/coding-task-manager/lib/ws/state-sync.js +19 -2
  232. package/template/coding-task-manager/lib/ws/terminal-attach.js +32 -1
  233. package/template/coding-task-manager/lib/ws-router.js +68 -1
  234. package/template/coding-task-manager/lib/ws-send-admission.js +22 -4
  235. package/template/coding-task-manager/oauth-proxy.js +99 -55
  236. package/template/coding-task-manager/package-lock.json +24 -0
  237. package/template/coding-task-manager/package.json +3 -0
  238. package/template/coding-task-manager/protocol/message-table.json +0 -10
  239. package/template/coding-task-manager/protocol/messages.js +0 -10
  240. package/template/coding-task-manager/protocol/ws-schema.json +0 -20
  241. package/template/coding-task-manager/protocol/ws-schema.md +0 -2
  242. package/template/coding-task-manager/providers/codex-mcp-extract.js +121 -19
  243. package/template/coding-task-manager/providers/codex-mcp.js +14 -5
  244. package/template/coding-task-manager/public/css/app.css +279 -2
  245. package/template/coding-task-manager/public/css/reviews.css +4 -0
  246. package/template/coding-task-manager/public/index.html +297 -240
  247. package/template/coding-task-manager/public/js/app/machines-page.js +1252 -0
  248. package/template/coding-task-manager/public/js/app/models-tab.js +546 -36
  249. package/template/coding-task-manager/public/js/app/worktrees-panel.js +7 -5
  250. package/template/coding-task-manager/public/js/background-work.js +13 -7
  251. package/template/coding-task-manager/public/js/client-health-core.js +81 -0
  252. package/template/coding-task-manager/public/js/client-health-main.js +61 -0
  253. package/template/coding-task-manager/public/js/coding-session-model-presentation.js +44 -4
  254. package/template/coding-task-manager/public/js/core/session-commands.js +676 -0
  255. package/template/coding-task-manager/public/js/core/session-selectors.js +5 -1
  256. package/template/coding-task-manager/public/js/history-app-seam.js +26 -2
  257. package/template/coding-task-manager/public/js/history-painted-rows.js +17 -2
  258. package/template/coding-task-manager/public/js/history-reader-park.js +13 -1
  259. package/template/coding-task-manager/public/js/history-scroller.js +187 -9
  260. package/template/coding-task-manager/public/js/history-transcript-pages.js +5 -5
  261. package/template/coding-task-manager/public/js/machine-pool-shared.js +436 -0
  262. package/template/coding-task-manager/public/js/private-state-scope.js +31 -3
  263. package/template/coding-task-manager/public/js/prompt-submission-client.js +173 -4
  264. package/template/coding-task-manager/public/js/prompt-submission-tray.js +281 -0
  265. package/template/coding-task-manager/public/js/protocol.js +0 -10
  266. package/template/coding-task-manager/public/js/queue-panel-submission-reconcile.js +174 -1
  267. package/template/coding-task-manager/public/js/reviews.js +7 -3
  268. package/template/coding-task-manager/public/js/root-draft-lifetime.js +139 -2
  269. package/template/coding-task-manager/public/js/root-queue-builder-lifetime.js +16 -0
  270. package/template/coding-task-manager/public/js/row-join.js +18 -3
  271. package/template/coding-task-manager/public/js/session-model-picker.js +1 -1
  272. package/template/coding-task-manager/public/js/session-search-utils.js +46 -19
  273. package/template/coding-task-manager/public/js/session-usage-view.js +1 -0
  274. package/template/coding-task-manager/public/js/state-sync-client.js +4 -0
  275. package/template/coding-task-manager/public/js/terminal/runtime-02.js +16 -7
  276. package/template/coding-task-manager/public/js/terminal/runtime-03.js +41 -0
  277. package/template/coding-task-manager/public/js/terminal/runtime-04.js +41 -4
  278. package/template/coding-task-manager/public/js/terminal/runtime-12.js +52 -2
  279. package/template/coding-task-manager/public/js/terminal/runtime-16.js +12 -12
  280. package/template/coding-task-manager/public/js/terminal/runtime-18.js +14 -0
  281. package/template/coding-task-manager/public/js/terminal/runtime-19.js +8 -4
  282. package/template/coding-task-manager/public/js/terminal/runtime-20.js +210 -73
  283. package/template/coding-task-manager/public/js/terminal/runtime-21.js +19 -6
  284. package/template/coding-task-manager/public/js/terminal/runtime-22.js +86 -0
  285. package/template/coding-task-manager/public/js/terminal-history-viewport.js +1 -3
  286. package/template/coding-task-manager/public/js/terminal-wheel.js +39 -2
  287. package/template/coding-task-manager/public/js/walle/chat-conversation.js +11 -3
  288. package/template/coding-task-manager/public/js/walle-session.js +30 -7
  289. package/template/coding-task-manager/public/m/app.css +226 -8
  290. package/template/coding-task-manager/public/m/app.js +502 -9
  291. package/template/coding-task-manager/public/m/index.html +38 -1
  292. package/template/coding-task-manager/public/m/machines.js +1313 -0
  293. package/template/coding-task-manager/public/m/pool-failover.js +391 -0
  294. package/template/coding-task-manager/public/m/private-queue-actions.js +26 -7
  295. package/template/coding-task-manager/public/m/sw.js +5 -0
  296. package/template/coding-task-manager/queue-engine.js +13 -0
  297. package/template/coding-task-manager/server/approvals.js +5 -6
  298. package/template/coding-task-manager/server/boot.js +136 -17
  299. package/template/coding-task-manager/server.js +964 -425
  300. package/template/coding-task-manager/session-integrity.js +7 -9
  301. package/template/coding-task-manager/workers/db-owner-worker.js +53 -7
  302. package/template/coding-task-manager/workers/read-pool-worker.js +17 -6
  303. package/template/coding-task-manager/workers/session-host-pool-process.js +18 -4
  304. package/template/coding-task-manager/workers/session-host-process.js +14 -3
  305. package/template/coding-task-manager/workers/transcript-archive-worker.js +1 -1
  306. package/template/coding-task-manager/workers/worktree-worker.js +34 -0
  307. package/template/package-lock.json +2 -2
  308. package/template/package.json +1 -1
  309. package/template/scripts/conversation-storage-v2-migration.js +78 -11
  310. package/template/shared/conversation-storage-v2-contract.js +42 -6
  311. package/template/shared/fixtures/worker-status-payloads.json +392 -0
  312. package/template/shared/git-remote-key.js +83 -0
  313. package/template/shared/host-identity.js +98 -2
  314. package/template/shared/owner-write-safety.js +135 -0
  315. package/template/shared/public-runtime.js +13 -5
  316. package/template/shared/release-schema.json +3 -2
  317. package/template/shared/report-citations.js +14 -0
  318. package/template/shared/sqlite-write-lock.js +361 -223
  319. package/template/shared/storage/contract.js +35 -3
  320. package/template/wall-e/agent.js +21 -0
  321. package/template/wall-e/api-walle.js +5 -0
  322. package/template/wall-e/bin/brain-purge-legacy-ctm.js +3 -2
  323. package/template/wall-e/bin/walle-mcp-stdio.js +12 -6
  324. package/template/wall-e/brain/helper-rules-schema.js +129 -0
  325. package/template/wall-e/brain/migrations.js +51 -22
  326. package/template/wall-e/brain/model-deployment-facts.js +31 -1
  327. package/template/wall-e/brain/owner-call-whitelist.js +16 -0
  328. package/template/wall-e/brain/schema-ensures.js +22 -0
  329. package/template/wall-e/brain.js +1 -1
  330. package/template/wall-e/coding/host-capabilities.js +2 -2
  331. package/template/wall-e/coding/journal-dispatcher.js +51 -0
  332. package/template/wall-e/coding/session-journal.js +28 -40
  333. package/template/wall-e/coding-orchestrator.js +23 -26
  334. package/template/wall-e/fly.toml +1 -1
  335. package/template/wall-e/http/auth.js +18 -1
  336. package/template/wall-e/http/blog-views-api.js +318 -0
  337. package/template/wall-e/http/helper-rules-api.js +321 -0
  338. package/template/wall-e/http/model-proxy-providers.js +3 -1
  339. package/template/wall-e/http/telemetry-api.js +12 -0
  340. package/template/wall-e/lib/blog-view-policy.js +88 -0
  341. package/template/wall-e/lib/blog-view-store.js +169 -0
  342. package/template/wall-e/lib/client-config-lock.js +2 -1
  343. package/template/wall-e/lib/coding-journal-writer-child-client.js +67 -18
  344. package/template/wall-e/lib/error-kinds.js +10 -1
  345. package/template/wall-e/lib/feature-manifest.js +1 -1
  346. package/template/wall-e/lib/helper-rules/cell-state.js +123 -0
  347. package/template/wall-e/lib/helper-rules/evaluate.js +357 -0
  348. package/template/wall-e/lib/helper-rules/explore-rubric.md +12 -0
  349. package/template/wall-e/lib/helper-rules/grade.js +277 -0
  350. package/template/wall-e/lib/helper-rules/label.js +247 -0
  351. package/template/wall-e/lib/helper-rules/lead-call-hook.js +522 -0
  352. package/template/wall-e/lib/helper-rules/store.js +667 -0
  353. package/template/wall-e/lib/mcp-integration.js +5 -6
  354. package/template/wall-e/lib/runtime-worker-pool.js +1 -0
  355. package/template/wall-e/lib/scheduler-worker-jobs.js +6 -0
  356. package/template/wall-e/lib/telemetry-db-schema.js +37 -0
  357. package/template/wall-e/lib/telemetry-process-client.js +76 -1
  358. package/template/wall-e/llm/default-fallback.js +4 -0
  359. package/template/wall-e/llm/model-catalog.js +13 -4
  360. package/template/wall-e/llm/model-roles.js +6 -0
  361. package/template/wall-e/llm/portkey-http.js +150 -0
  362. package/template/wall-e/llm/portkey-sync.js +15 -145
  363. package/template/wall-e/llm/worker-mcp-routing.js +61 -0
  364. package/template/wall-e/llm/worker-route.js +43 -1
  365. package/template/wall-e/loops/brain-custom-writes.js +45 -0
  366. package/template/wall-e/mcp-protocol-contract.js +41 -1
  367. package/template/wall-e/mcp-server.js +13 -2
  368. package/template/wall-e/mcp-worker-schemas.js +20 -2
  369. package/template/wall-e/memory/ctm-context-client.js +25 -5
  370. package/template/wall-e/shared/release-schema.json +3 -2
  371. package/template/wall-e/shared/sqlite-write-lock.js +361 -223
  372. package/template/wall-e/shared/storage/contract.js +35 -3
  373. package/template/wall-e/skills/_templates/delegate-worker/VERSION +1 -1
  374. package/template/wall-e/skills/_templates/delegate-worker/released-sha256.json +6 -0
  375. package/template/wall-e/skills/_templates/delegate-worker/templates/claude/SKILL.md +7 -2
  376. package/template/wall-e/skills/_templates/delegate-worker/templates/codex/SKILL.md +7 -2
  377. package/template/wall-e/tools/local-tools.js +12 -5
  378. package/template/wall-e/workers/brain-owner-worker.js +9 -0
  379. package/template/wall-e/workers/coding-journal-writer-child.js +8 -1
  380. package/template/wall-e/workers/telemetry-worker.js +8 -0
  381. package/template/coding-task-manager/lib/giant-freeze-park.js +0 -31
@@ -0,0 +1,200 @@
1
+ 'use strict';
2
+
3
+ // §5 D-7 / §6 (docs/model-proxy-survives-ctm-restart-2026-10-22.html): the CLI half of oauth-proxy.js.
4
+ // It lives here so oauth-proxy.js stays inside its hard 1500-line ceiling, and nothing in this file is
5
+ // reachable from an embedded start() -- only from `node oauth-proxy.js`.
6
+ //
7
+ // Two ways in, distinguished by ONE thing: whether there is an IPC channel.
8
+ // * the supervisor spawns the proxy with the admin secret on STDIN (WP-0 §5 D-4): read one line, start
9
+ // serving, and the secret is only ever used for the loopback admin calls above;
10
+ // * a PREDECESSOR performing an upgrade handoff (§5 D-7) spawns it with an `ipc` channel and no stdin
11
+ // at all (`stdio: ['ignore', logFd, logFd, 'ipc']`): the three `handoff/v1*` messages carry the two
12
+ // listening sockets, the admin secret and the memory-only token state, and this process is the
13
+ // SUCCESSOR. It publishes its own identity and answers `ready` -- the predecessor stops accepting
14
+ // only after that answer, which is why the answer is sent from start(), not from here.
15
+
16
+ const readline = require('node:readline');
17
+
18
+ const LOOPBACK_HOST = '127.0.0.1';
19
+ const LOOPBACK_REFUSAL_EXIT_CODE = 78;
20
+ const STARTUP_FAILURE_EXIT_CODE = 1;
21
+ const SIGTERM_EXIT_CODE = 143;
22
+
23
+ // Test seam (§6, AC-2b): truthy => the successor does everything a successor does EXCEPT send `ready`, so
24
+ // a test can observe the predecessor's bounded wait from outside both processes. It is read through a
25
+ // local `env` binding -- the shape lib/hermetic-agent-launch-guard.js uses for its own test-only
26
+ // switches -- and it reaches a successor because the supervisor's `childEnv = { ...process.env }` and the
27
+ // predecessor's `env: process.env` both pass it through. There is no operator flag here.
28
+ function _holdReadyFlag(env = process.env) {
29
+ return String(env.CTM_TEST_HANDOFF_SUCCESSOR_HOLD_READY || '') === '1';
30
+ }
31
+
32
+ /**
33
+ * §5 D-8 test seam: the CLI is the ONLY caller that arms the proxy's own linger timer, and this is its
34
+ * idle window. It is read through a local `env` binding, exactly like `_holdReadyFlag` above, so a child
35
+ * spawned by a test can be observed leaving its window in about a second instead of fifteen minutes; the
36
+ * production value comes from handoff.js (`PROXY_LINGER_MS`, === HOST_LINGER_MS). No operator flag, and
37
+ * no production path ever sets this.
38
+ */
39
+ function _lingerMsFlag(env = process.env, handoff) {
40
+ const raw = Number(env.CTM_TEST_PROXY_LINGER_MS);
41
+ return Number.isSafeInteger(raw) && raw > 0 ? raw : handoff.PROXY_LINGER_MS;
42
+ }
43
+
44
+ /** D-1: the CLI's bind host, resolved once from the environment exactly as it always was. */
45
+ function _resolveHost(env = process.env) {
46
+ const raw = env.OAUTH_PROXY_HOST;
47
+ return typeof raw === 'string' && raw.length > 0 ? raw : LOOPBACK_HOST;
48
+ }
49
+
50
+ // ── D-4 stdin secret (CLI mode only) ────────────────────────────────────
51
+ // Reads exactly one line from stdin at process start. Never env, never argv.
52
+ // Resolves null (no admin secret; admin endpoints stay locked) if stdin is a
53
+ // TTY or closes without producing a line.
54
+ function _readAdminSecretFromStdin() {
55
+ return new Promise((resolve) => {
56
+ if (!process.stdin || process.stdin.isTTY) {
57
+ resolve(null);
58
+ return;
59
+ }
60
+ const rl = readline.createInterface({ input: process.stdin, terminal: false });
61
+ let resolved = false;
62
+ const finish = (value) => {
63
+ if (resolved) return;
64
+ resolved = true;
65
+ rl.close();
66
+ resolve(value);
67
+ };
68
+ rl.once('line', (line) => finish((line || '').trim() || null));
69
+ rl.once('close', () => finish(null));
70
+ });
71
+ }
72
+
73
+ /**
74
+ * §9 S-5: record the exit, say it once, then leave. Semantics are unchanged -- nothing drains, and
75
+ * 128+15 keeps the supervisor's `code !== 0` classification of a SIGTERM exactly as it is today.
76
+ */
77
+ function _installSigtermHandler({ startedContext, streamAccountingMod, instanceRecord, handoff }) {
78
+ process.on('SIGTERM', () => {
79
+ const ctx = startedContext();
80
+ if (!ctx) {
81
+ process.exit(SIGTERM_EXIT_CODE);
82
+ return;
83
+ }
84
+ const openStreams = ctx.streamAccounting.count();
85
+ // §5 D-5: an explicit stop leaves no record, so the next boot spawns rather than adopts. A
86
+ // PREDECESSOR that is draining a handoff is the one exception: the record on disk already names its
87
+ // successor, and removing it would take the live proxy out of the next boot's reach.
88
+ if (!handoff.isDraining()) instanceRecord.unlinkInstanceRecord({ dataDir: ctx.dataDir });
89
+ streamAccountingMod.writeExitRecord({ dataDir: ctx.dataDir, reason: 'SIGTERM', openStreams })
90
+ .catch((err) => {
91
+ // REPORT: the exit code below still classifies this stop, so a failed write is not fatal.
92
+ console.error(`[model-proxy] exit record not written: ${(err && err.code) || 'unknown'}`);
93
+ })
94
+ .then(() => {
95
+ console.log(`[model-proxy] exit reason=SIGTERM open_streams=${openStreams}`);
96
+ process.exit(SIGTERM_EXIT_CODE);
97
+ });
98
+ });
99
+ }
100
+
101
+ /**
102
+ * The CLI entry point, called by oauth-proxy.js's `require.main === module` block with its own
103
+ * dependencies injected (so this module never requires oauth-proxy.js back).
104
+ *
105
+ * @param {object} input
106
+ * @param {Function} input.start oauth-proxy.js's start()
107
+ * @param {object} input.streamAccountingMod lib/model-proxy/stream-accounting.js
108
+ * @param {object} input.instanceRecord lib/model-proxy/proxy-instance-record.js
109
+ * @param {object} input.handoff lib/model-proxy/handoff.js
110
+ * @param {Function} [input.startedContext] getter for the context of the server this process started
111
+ * @param {string} [input.loopbackErrorCode] the named refusal code start() rejects with
112
+ */
113
+ function run({
114
+ start,
115
+ streamAccountingMod,
116
+ instanceRecord,
117
+ handoff,
118
+ startedContext = () => null,
119
+ loopbackErrorCode = 'EBIND_NOT_LOOPBACK',
120
+ } = {}) {
121
+ const host = _resolveHost();
122
+ if (host !== LOOPBACK_HOST) {
123
+ // D-1: refuse before ever touching stdin, so a bad --host/env doesn't hang waiting for an admin
124
+ // secret that will never matter.
125
+ console.error(`[oauth-proxy] ${loopbackErrorCode}: refusing to bind to "${host}" — must be 127.0.0.1 (D-1)`);
126
+ process.exit(LOOPBACK_REFUSAL_EXIT_CODE);
127
+ return;
128
+ }
129
+ const successor = typeof process.send === 'function';
130
+ const startup = successor
131
+ // §5 D-7: the successor never reads stdin -- its secret and its data directory arrive in the
132
+ // handoff message, and its listener is a socket it did not open.
133
+ ? handoff.receiveInheritance().then((received) => ({
134
+ host,
135
+ adminSecret: received.inherited.adminSecret,
136
+ dataDir: received.inherited.dataDir,
137
+ inherited: { ...received.inherited, state: received.state, holdReady: _holdReadyFlag() },
138
+ }))
139
+ // A TTY, or stdin closing before a line arrived, is not a failure: the process simply serves with no
140
+ // admin secret, and its admin surface stays locked. The read itself already resolves null in both of
141
+ // those cases, so this tail exists only for an unexpected readline error.
142
+ : _readAdminSecretFromStdin()
143
+ .catch((/* expected: no line on stdin -- start with no admin secret rather than not at all */) => null)
144
+ .then((adminSecret) => ({ host, adminSecret }));
145
+
146
+ startup
147
+ // §5 D-8: `lingerMs` is what makes THIS process (and only this one: an embedded start() passes none)
148
+ // leave on its own once CTM has been gone for a full window.
149
+ .then((args) => start({ ...args, lingerMs: _lingerMsFlag(process.env, handoff) }))
150
+ .then(() => {
151
+ if (successor) {
152
+ // §5 D-7: the successor outlives the predecessor that spawned it, so the channel it was born
153
+ // with must never be what keeps it alive -- nor may its closure end it. `ready` is already sent.
154
+ try {
155
+ process.channel.unref();
156
+ } catch (err) {
157
+ // expected: nothing to detach when this process has no channel state left to manage.
158
+ void err;
159
+ }
160
+ }
161
+ _installSigtermHandler({ startedContext, streamAccountingMod, instanceRecord, handoff });
162
+ })
163
+ .catch((err) => {
164
+ if (err && err.code === loopbackErrorCode) {
165
+ console.error(`[oauth-proxy] ${err.code}: ${err.message}`);
166
+ process.exit(LOOPBACK_REFUSAL_EXIT_CODE);
167
+ return;
168
+ }
169
+ console.error('[oauth-proxy] failed to start:', err.message);
170
+ process.exit(STARTUP_FAILURE_EXIT_CODE);
171
+ });
172
+ }
173
+
174
+ /**
175
+ * The `require.main === module` entry: oauth-proxy.js calls exactly this, so its own CLI seams
176
+ * (start, the accounting/record modules, the SIGTERM context) do not have to be spelled out at the bottom
177
+ * of a file that is at its hard line ceiling. The require is deferred to call time, so loading THIS module
178
+ * never loads oauth-proxy.js back -- and when the proxy is its own main, this module is loaded from
179
+ * inside that same evaluation, by which point oauth-proxy.js's exports are complete.
180
+ */
181
+ function runCli() {
182
+ const proxy = require('../../oauth-proxy.js');
183
+ return run({
184
+ start: proxy.start,
185
+ streamAccountingMod: require('./stream-accounting'),
186
+ instanceRecord: require('./proxy-instance-record'),
187
+ handoff: require('./handoff'),
188
+ startedContext: proxy._startedContext,
189
+ loopbackErrorCode: proxy.EBIND_NOT_LOOPBACK,
190
+ });
191
+ }
192
+
193
+ module.exports = {
194
+ run,
195
+ runCli,
196
+ _readAdminSecretFromStdin,
197
+ _holdReadyFlag,
198
+ _lingerMsFlag,
199
+ _resolveHost,
200
+ };
@@ -0,0 +1,257 @@
1
+ 'use strict';
2
+
3
+ // §5 D-1/D-2/D-3 + §6 (docs/model-proxy-survives-ctm-restart-2026-10-22.html): the durable identity of a
4
+ // LIVE model proxy, and the four paths it lives on. The supervisor binds the child's stdout/stderr to
5
+ // proxyLogPath() (the proxy never writes into a CTM-owned pipe -- incident 7: an orphaned child dies on
6
+ // its second write with EPIPE); the child publishes instance.json after listen and removes it on an
7
+ // explicit stop; CTM writes the per-spawn admin secret BEFORE the spawn, so a CTM that dies between the
8
+ // spawn and its own exit still leaves a readable secret for the next boot -- which is also the CTM that
9
+ // adopts the survivor (§5 D-4). Both processes must agree on these paths byte-for-byte, so they are
10
+ // defined once, here.
11
+ //
12
+ // Publications are atomic in the shape lib/host-registry.js uses (a nonce-scoped tmp file created with
13
+ // flag 'wx' and mode 0600, renamed over the final path), and the directory is 0700 -- the same tree the
14
+ // native mTLS authority already lives in. The same 0600 secret authenticates loopback admin calls only;
15
+ // nothing here is a transport credential.
16
+
17
+ const crypto = require('node:crypto');
18
+ const fs = require('node:fs');
19
+ const path = require('node:path');
20
+ const { execFileSync } = require('node:child_process');
21
+ const { computeClosure } = require('../host-closure-hash');
22
+
23
+ // lib/host-closure-hash.js resolves its own default repo root from ITS __dirname (coding-task-manager/lib
24
+ // -> the repo root). This module sits one level deeper, so the same root is re-stated explicitly: the
25
+ // file list the PROXY hashes and the file list CTM hashes from disk must name the same files.
26
+ const REPO_ROOT = path.resolve(__dirname, '..', '..', '..');
27
+
28
+ const RECORD_VERSION = 1;
29
+ const PROXY_PROTOCOL = 'proxy-v1';
30
+ const PROXY_FLOOR_VERSION = 0;
31
+ const PROXY_SCRIPT_NAME = 'oauth-proxy.js';
32
+ // §5 D-1: one generation of log is kept. 203,900 request lines in 26 days is ~1.5 MB/day, so ~40 days
33
+ // per generation; the rotation is a rename, not a truncation, so the previous generation stays readable.
34
+ const LOG_ROTATE_BYTES = 64 * 1024 * 1024;
35
+ const LOG_ROTATE_SUFFIX = '.1';
36
+ const PS_TIMEOUT_MS = 2000;
37
+
38
+ function proxyDir(dataDir) {
39
+ return path.join(dataDir, 'oauth-proxy');
40
+ }
41
+
42
+ function instanceRecordPath(dataDir) {
43
+ return path.join(proxyDir(dataDir), 'instance.json');
44
+ }
45
+
46
+ function adminSecretPath(dataDir) {
47
+ return path.join(proxyDir(dataDir), 'admin-secret');
48
+ }
49
+
50
+ function proxyLogPath(dataDir) {
51
+ return path.join(proxyDir(dataDir), 'oauth-proxy.log');
52
+ }
53
+
54
+ // 0700, created on demand. Synchronous because the spawn-time log descriptor below needs the directory to
55
+ // exist before the child does; one call per spawn and one per publication, never a request path.
56
+ function ensureProxyDir(dataDir) {
57
+ const dir = proxyDir(dataDir);
58
+ fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
59
+ return dir;
60
+ }
61
+
62
+ function _writeAtomic0600Sync(target, text) {
63
+ const tmp = `${target}.${process.pid}.${crypto.randomUUID()}.tmp`;
64
+ fs.writeFileSync(tmp, text, { mode: 0o600, flag: 'wx' });
65
+ fs.renameSync(tmp, target);
66
+ return target;
67
+ }
68
+
69
+ function _unlinkQuiet(target) {
70
+ try {
71
+ fs.unlinkSync(target);
72
+ return true;
73
+ } catch (err) {
74
+ if (err && err.code === 'ENOENT') return false;
75
+ // REPORT: a file that cannot be removed is stale truth on disk; the caller's decision still stands.
76
+ process.stderr.write(`[model-proxy] record not removed: ${path.basename(target)} ${(err && err.code) || 'unknown'}\n`);
77
+ return false;
78
+ }
79
+ }
80
+
81
+ /**
82
+ * §5 D-2: the record the proxy publishes after listen. Written before the first request is answered, so
83
+ * a CTM that restarts immediately afterwards still finds the exact identity of what is listening.
84
+ * @returns {object} the record written
85
+ */
86
+ function writeInstanceRecord({ dataDir, record }) {
87
+ ensureProxyDir(dataDir);
88
+ _writeAtomic0600Sync(instanceRecordPath(dataDir), `${JSON.stringify(record)}\n`);
89
+ return record;
90
+ }
91
+
92
+ /**
93
+ * §5 D-3/D-2: compute this process's loaded-code hash ONCE, publish the record, and hand the same two
94
+ * fields to the accounting the /_health answer reports from -- so identity and the published record can
95
+ * never describe different code. §5 D-7 (WP-2): a handoff successor publishes `generation: n+1` and the
96
+ * predecessor it took over from, so the record names the LIVE socket owner and who handed it over.
97
+ */
98
+ function publishInstanceRecord({ dataDir, port, nativePort = null, accounting = null, generation = 1, predecessorPid = null }) {
99
+ const { closureHash, closureFiles } = computeClosure({ repoRoot: REPO_ROOT });
100
+ const record = writeInstanceRecord({
101
+ dataDir,
102
+ record: {
103
+ v: RECORD_VERSION,
104
+ pid: process.pid,
105
+ port,
106
+ nativePort,
107
+ protocol: PROXY_PROTOCOL,
108
+ floorVersion: PROXY_FLOOR_VERSION,
109
+ closureHash,
110
+ closureFiles,
111
+ generation,
112
+ predecessorPid,
113
+ startedAt: new Date().toISOString(),
114
+ draining: false,
115
+ },
116
+ });
117
+ if (accounting && typeof accounting.setIdentity === 'function') {
118
+ accounting.setIdentity({ closureHash, closureFiles, generation, draining: false });
119
+ }
120
+ return record;
121
+ }
122
+
123
+ /**
124
+ * §5 D-7 step 2: rewrite the published record in place with one patch (today: `draining`). Written
125
+ * BEFORE the successor is spawned, so a predecessor that dies mid-handoff leaves a truthful record --
126
+ * and rewritten with `draining:false` on any abort, because the predecessor kept serving.
127
+ * @returns {object|null} the rewritten record, or null when this process published none
128
+ */
129
+ function updateInstanceRecord({ dataDir, patch }) {
130
+ const current = readInstanceRecord({ dataDir });
131
+ if (!current) return null;
132
+ return writeInstanceRecord({ dataDir, record: { ...current, ...patch } });
133
+ }
134
+
135
+ /**
136
+ * @returns {object|null} the published record, or null when there is none. A truncated, hand-edited or
137
+ * unusable record is reported as absent AND removed: writes are atomic via rename, so it can only be
138
+ * leftover garbage, and leaving it behind would make the next boot re-read the same lie.
139
+ */
140
+ function readInstanceRecord({ dataDir }) {
141
+ let raw;
142
+ try {
143
+ raw = fs.readFileSync(instanceRecordPath(dataDir), 'utf8');
144
+ } catch (err) {
145
+ if (err && err.code === 'ENOENT') return null; // expected: no proxy has published one
146
+ throw err;
147
+ }
148
+ try {
149
+ const record = JSON.parse(raw);
150
+ if (record && typeof record === 'object' && Number.isSafeInteger(record.pid) && record.pid > 0) return record;
151
+ } catch {
152
+ // expected: an unparseable record is not evidence -- fall through and remove it
153
+ }
154
+ unlinkInstanceRecord({ dataDir });
155
+ return null;
156
+ }
157
+
158
+ function unlinkInstanceRecord({ dataDir }) {
159
+ return _unlinkQuiet(instanceRecordPath(dataDir));
160
+ }
161
+
162
+ /** §5 D-2: written before each fresh spawn, read back when CTM adopts a proxy it did not spawn. */
163
+ function writeAdminSecret({ dataDir, secret }) {
164
+ ensureProxyDir(dataDir);
165
+ _writeAtomic0600Sync(adminSecretPath(dataDir), `${String(secret)}\n`);
166
+ return secret;
167
+ }
168
+
169
+ /** @returns {string|null} the current secret, or null when there is none to authenticate with. */
170
+ function readAdminSecret({ dataDir }) {
171
+ let raw;
172
+ try {
173
+ raw = fs.readFileSync(adminSecretPath(dataDir), 'utf8');
174
+ } catch (err) {
175
+ if (err && err.code === 'ENOENT') return null; // expected: this CTM never spawned a proxy here
176
+ throw err;
177
+ }
178
+ const secret = raw.trim();
179
+ return secret || null;
180
+ }
181
+
182
+ function unlinkAdminSecret({ dataDir }) {
183
+ return _unlinkQuiet(adminSecretPath(dataDir));
184
+ }
185
+
186
+ /**
187
+ * §5 D-1: the descriptor the child's stdout and stderr are bound to. One synchronous open per spawn
188
+ * (never a request path), rotating one generation at 64 MiB first: every proxy generation passes through
189
+ * this spawn, including WP-2's handoff successor, so this is the one place rotation has to happen.
190
+ */
191
+ function openProxyLogFd(dataDir) {
192
+ ensureProxyDir(dataDir);
193
+ const target = proxyLogPath(dataDir);
194
+ const fd = fs.openSync(target, 'a');
195
+ if (fs.fstatSync(fd).size <= LOG_ROTATE_BYTES) return fd;
196
+ fs.closeSync(fd);
197
+ fs.renameSync(target, `${target}${LOG_ROTATE_SUFFIX}`);
198
+ return fs.openSync(target, 'a');
199
+ }
200
+
201
+ /** The supervisor's own copy of the descriptor; the child holds its own. */
202
+ function closeProxyLogFd(fd) {
203
+ try {
204
+ fs.closeSync(fd);
205
+ } catch (err) {
206
+ // expected: a descriptor that is already closed costs nothing -- the child has its own copy
207
+ void err;
208
+ }
209
+ }
210
+
211
+ /** §5 D-4: is this pid a live proxy? `ps` is the authority -- a recycled pid must never be signalled. */
212
+ function isProxyProcess(pid) {
213
+ if (!Number.isSafeInteger(pid) || pid < 1) return false;
214
+ try {
215
+ return execFileSync('ps', ['-o', 'command=', '-p', String(pid)], { encoding: 'utf8', timeout: PS_TIMEOUT_MS })
216
+ .includes(PROXY_SCRIPT_NAME);
217
+ } catch (err) {
218
+ // expected: ps exits non-zero for a pid that is gone, and a failed probe is never a proxy
219
+ void err;
220
+ return false;
221
+ }
222
+ }
223
+
224
+ /** D-4 step 1: `process.kill(pid, 0)` -- EPERM means alive but not ours, which is still alive. */
225
+ function isProcessAlive(pid) {
226
+ if (!Number.isSafeInteger(pid) || pid < 1) return false;
227
+ try {
228
+ process.kill(pid, 0);
229
+ return true;
230
+ } catch (err) {
231
+ return err && err.code === 'EPERM';
232
+ }
233
+ }
234
+
235
+ module.exports = {
236
+ RECORD_VERSION,
237
+ PROXY_PROTOCOL,
238
+ PROXY_FLOOR_VERSION,
239
+ LOG_ROTATE_BYTES,
240
+ proxyDir,
241
+ instanceRecordPath,
242
+ adminSecretPath,
243
+ proxyLogPath,
244
+ ensureProxyDir,
245
+ publishInstanceRecord,
246
+ updateInstanceRecord,
247
+ writeInstanceRecord,
248
+ readInstanceRecord,
249
+ unlinkInstanceRecord,
250
+ writeAdminSecret,
251
+ readAdminSecret,
252
+ unlinkAdminSecret,
253
+ openProxyLogFd,
254
+ closeProxyLogFd,
255
+ isProxyProcess,
256
+ isProcessAlive,
257
+ };
@@ -18,6 +18,7 @@
18
18
  const crypto = require('node:crypto');
19
19
  const chatUpstream = require('./chat-upstream');
20
20
  const routes = require('./routes');
21
+ const { dropHandedOffCompactions } = require('./collaboration-codec');
21
22
  const { effectiveOutputCap } = require('./output-cap');
22
23
  const { upstreamRequest, readBodyBuffer } = require('./upstream-http');
23
24
  const { parseRetryAfterMs } = require('./retry-after');
@@ -39,6 +40,10 @@ function errorEnvelope(type, message) {
39
40
  return { error: { type: String(type), code: String(type), message: String(message) } };
40
41
  }
41
42
 
43
+ function isPlainObject(value) {
44
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
45
+ }
46
+
42
47
  function writeJson(res, status, obj) {
43
48
  res.writeHead(status, { 'content-type': 'application/json' });
44
49
  res.end(JSON.stringify(obj));
@@ -355,10 +360,27 @@ async function handle(req, res, ctx) {
355
360
  writeJson(res, 502, errorEnvelope('route_not_servable', `route "${route.alias}" (${route.upstreamApis.join(',')}) is not servable from the Responses face`));
356
361
  return;
357
362
  }
363
+ // WP-3 (docs/codex-lead-routed-switch-history-handoff-2026-10-21.html §5 D-7): a `compaction`
364
+ // item whose plaintext handoff replacement travels in the SAME request is omitted
365
+ // -- the replacement IS the readable form of that turn, so both faces get the
366
+ // rewritten body (chat-translate translates the summary; passthrough relays it).
367
+ // An UNMATCHED compaction item is untouched, so chat-translate still refuses it
368
+ // through the existing preflight and passthrough still forwards it verbatim.
369
+ const handedOff = isPlainObject(ctx.body) ? dropHandedOffCompactions(ctx.body) : null;
370
+ let requestCtx = ctx;
371
+ if (handedOff && handedOff.droppedHandedOffCompactions > 0) {
372
+ const body = handedOff.body;
373
+ // Same tag/wording policy as chat-upstream.js's reasoning-ciphertext line: one
374
+ // line, once per request, counts only -- never provider text.
375
+ process.stderr.write(`[model-proxy] dropped ${handedOff.droppedHandedOffCompactions} handed-off compaction item(s)\n`);
376
+ // ctx.body was JSON-parsed from the request, so it always serializes; passthrough sends
377
+ // this buffer (the caller's content-length is in CALLER_HEADER_DENYLIST).
378
+ requestCtx = { ...ctx, body, rawBody: Buffer.from(JSON.stringify(body)) };
379
+ }
358
380
  if (sel.mode === 'passthrough') {
359
- return _passthrough(req, res, ctx);
381
+ return _passthrough(req, res, requestCtx);
360
382
  }
361
- return _handleChatTranslate(req, res, ctx);
383
+ return _handleChatTranslate(req, res, requestCtx);
362
384
  }
363
385
 
364
386
  module.exports = { handle, errorEnvelope, notFound, writeJson, recordTranslatedNonstreamTelemetry };