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,417 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ // D-9 (WP-0 of docs/codex-lead-routed-switch-history-handoff-2026-10-21.html): cut real records
5
+ // out of real Codex rollouts into sanitized fixtures, one file per Codex CLI version, so the
6
+ // scanner is exercised against what Codex actually serializes instead of a hand-written copy.
7
+ //
8
+ // node bin/capture-codex-rollout-records.js --out tests/fixtures/codex-rollout-records [--roots <dir,dir>]
9
+ //
10
+ // Default roots are ~/.codex/sessions and ~/.codex-local/sessions. For every distinct
11
+ // session_meta.cli_version found the tool writes <version>.jsonl holding one real example of the
12
+ // session_meta, thread_settings_applied (the one with the most optional keys), turn_context,
13
+ // task_started, task_complete, token_count, world_state (full:true) and, when the version produced
14
+ // one, a compacted record. Adding a new Codex version's fixture file is part of qualifying it.
15
+
16
+ const fs = require('node:fs');
17
+ const os = require('node:os');
18
+ const path = require('node:path');
19
+
20
+ const DEFAULT_ROOTS = [path.join(os.homedir(), '.codex', 'sessions'), path.join(os.homedir(), '.codex-local', 'sessions')];
21
+ // Kinds captured per version, written in this order. The optional kind is only present when the
22
+ // version wrote one; a missing optional record never fails the capture.
23
+ const REQUIRED_KINDS = Object.freeze(['thread_settings_applied', 'turn_context', 'task_started', 'task_complete', 'token_count', 'world_state']);
24
+ const OPTIONAL_KINDS = Object.freeze(['compacted']);
25
+ const MAX_FILES_PER_VERSION = 40;
26
+ const MAX_BYTES_PER_FILE = 24 * 1024 * 1024;
27
+
28
+ const THREAD_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
29
+ const FIXTURE_ID_PREFIX = 'a0000000-0000-4000-8000-';
30
+ // The one thread a fixture file describes. Every record is cut from a rollout of that thread, so
31
+ // the payload `thread_id`/`session_id` fields all name it (the scanner checks them against the
32
+ // session_meta lineage).
33
+ const FIXTURE_THREAD_ID = `${FIXTURE_ID_PREFIX}000000000001`;
34
+ const FIXTURE_PATH_ROOT = '/tmp/fixture';
35
+ const FIXTURE_TEXT = 'fixture text';
36
+ const FIXTURE_IMAGE_URL = 'data:image/png;base64,AAAA';
37
+ const FIXTURE_CIPHERTEXT = `gAAAA${'x'.repeat(40)}`;
38
+ // Every string under these keys is a filesystem location, whatever its depth in the record.
39
+ const PATH_KEYS = new Set(['cwd', 'path', 'paths', 'runtime_workspace_roots', 'workspace_roots',
40
+ 'writable_roots', 'roots', 'dir', 'directory', 'home', 'jsonl_path', 'rollout_path']);
41
+ // Every string under these keys names the thread the fixture describes.
42
+ const THREAD_KEYS = new Set(['thread_id', 'session_id', 'threadid']);
43
+ // The replacement-item types `classifyReplacement` accepts in lib/history-transition-read-op.js.
44
+ // A compacted record outside this set is refused by the scanner, so it is not a usable fixture.
45
+ const REPLACEMENT_CALL_TYPES = new Set(['function_call', 'custom_tool_call', 'computer_call', 'local_shell_call']);
46
+ const REPLACEMENT_OUTPUT_TYPES = new Set(['function_call_output', 'custom_tool_call_output',
47
+ 'computer_call_output', 'local_shell_call_output']);
48
+ const REPLACEMENT_ITEM_TYPES = new Set(['text', 'summary', 'compaction', 'message']);
49
+ // Every string under these keys is prose the person wrote or Codex prompted with.
50
+ const TEXT_KEYS = new Set(['text', 'message', 'last_agent_message', 'developer_instructions',
51
+ 'base_instructions', 'instructions', 'summary', 'prompt', 'description']);
52
+ // Prose and tool arguments arrive as one long string under keys this tool cannot enumerate
53
+ // (`arguments`, `plugins_instructions`, ...). Anything that long is text, not a protocol value.
54
+ const MAX_TEXT_LENGTH = 200;
55
+ // Default-deny: a short string survives only under a key whose values are protocol enums or
56
+ // identifiers of the protocol itself. Everything else -- approved command prefixes, network
57
+ // domains, MCP server and tool names, search queries, document and channel ids -- is local data
58
+ // and becomes FIXTURE_WORD (a token that also satisfies the scanner's identifier patterns).
59
+ const ENUM_KEYS = new Set(['type', 'role', 'model', 'model_provider_id', 'approval_policy',
60
+ 'approvals_reviewer', 'mode', 'network', 'service_tier', 'reasoning_effort', 'reasoning_summary',
61
+ 'effort', 'personality', 'status', 'phase', 'turn_trigger', 'sandbox', 'sandbox_mode',
62
+ 'sandbox_policy', 'cli_version', 'originator', 'source', 'detail', 'timestamp', 'model_family',
63
+ 'collaboration_mode', 'collaboration_mode_kind', 'kind', 'trigger', 'reason', 'stop_reason', 'end_turn', 'level']);
64
+ const ENUM_VALUE = /^[A-Za-z0-9@][A-Za-z0-9@._:+-]{0,63}$/;
65
+ const FIXTURE_WORD = 'fixture';
66
+ // The local account name, matched anywhere (including inside an ssh remote "<account>@host:path").
67
+ const ACCOUNT_NAME = new RegExp(os.userInfo().username.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'i');
68
+ const ENVELOPE_KEYS = Object.freeze(['timestamp', 'ordinal', 'type', 'payload']);
69
+
70
+ function parseArgs(argv) {
71
+ const args = { out: '', roots: [] };
72
+ for (let index = 0; index < argv.length; index += 1) {
73
+ const flag = argv[index];
74
+ if (flag === '--out') { args.out = String(argv[index + 1] || ''); index += 1; }
75
+ else if (flag === '--roots') {
76
+ args.roots = String(argv[index + 1] || '').split(',').map((value) => value.trim()).filter(Boolean);
77
+ index += 1;
78
+ } else if (flag === '--help' || flag === '-h') { args.help = true; }
79
+ else throw new Error(`unknown argument: ${flag}`);
80
+ }
81
+ return args;
82
+ }
83
+
84
+ function walkRollouts(root) {
85
+ const files = [];
86
+ const pending = [root];
87
+ while (pending.length) {
88
+ const dir = pending.pop();
89
+ let entries;
90
+ try { entries = fs.readdirSync(dir, { withFileTypes: true }); }
91
+ // expected: an absent or unreadable configured Codex home simply contributes no rollouts.
92
+ catch { continue; }
93
+ for (const entry of entries) {
94
+ const full = path.join(dir, entry.name);
95
+ if (entry.isDirectory()) pending.push(full);
96
+ else if (entry.isFile() && entry.name.startsWith('rollout-') && entry.name.endsWith('.jsonl')) files.push(full);
97
+ }
98
+ }
99
+ return files;
100
+ }
101
+
102
+ function readFirstLine(file, maxBytes = 256 * 1024) {
103
+ let fd;
104
+ try { fd = fs.openSync(file, 'r'); }
105
+ // expected: a rollout that vanished between listing and reading is skipped.
106
+ catch { return null; }
107
+ try {
108
+ const buffer = Buffer.allocUnsafe(maxBytes);
109
+ const read = fs.readSync(fd, buffer, 0, maxBytes, 0);
110
+ const text = buffer.toString('utf8', 0, read);
111
+ const newline = text.indexOf('\n');
112
+ return newline < 0 ? text : text.slice(0, newline);
113
+ } catch { return null; }
114
+ finally { fs.closeSync(fd); }
115
+ }
116
+
117
+ function parseRecord(line) {
118
+ if (!line.trim()) return null;
119
+ try { return JSON.parse(line); }
120
+ // expected: a rollout captured while a turn was running can end in a partial line.
121
+ catch { return null; }
122
+ }
123
+
124
+ function recordKind(record) {
125
+ if (!record || typeof record.type !== 'string') return '';
126
+ if (record.type === 'event_msg') return String(record.payload?.type || '');
127
+ return record.type;
128
+ }
129
+
130
+ function threadSettingsScore(record) {
131
+ const settings = record?.payload?.thread_settings;
132
+ return settings && typeof settings === 'object' ? Object.keys(settings).length : 0;
133
+ }
134
+
135
+ // Streams one rollout from the start, up to `maxBytes`, reporting the first (or best) example of
136
+ // every wanted kind and stopping as soon as all of them have been seen.
137
+ function collectKinds(file, { kinds, maxBytes = MAX_BYTES_PER_FILE, accept = null } = {}) {
138
+ const hits = new Map();
139
+ const scores = new Map();
140
+ const rejected = new Map();
141
+ let sessionMeta = null;
142
+ let fd;
143
+ try { fd = fs.openSync(file, 'r'); }
144
+ // expected: an unreadable rollout contributes nothing.
145
+ catch { return { hits, sessionMeta, rejected }; }
146
+ const buffer = Buffer.allocUnsafe(1 << 20);
147
+ let carry = '';
148
+ let bytes = 0;
149
+ try {
150
+ while (bytes < maxBytes) {
151
+ const read = fs.readSync(fd, buffer, 0, buffer.length, null);
152
+ if (!read) break;
153
+ bytes += read;
154
+ carry += buffer.toString('utf8', 0, read);
155
+ const lines = carry.split('\n');
156
+ carry = lines.pop();
157
+ for (const line of lines) {
158
+ const record = parseRecord(line);
159
+ if (!record) continue;
160
+ if (record.type === 'session_meta' && !sessionMeta) sessionMeta = record;
161
+ const kind = recordKind(record);
162
+ if (!kinds.has(kind)) continue;
163
+ const refusal = accept ? accept(kind, record) : '';
164
+ if (refusal) { if (!rejected.has(kind)) rejected.set(kind, refusal); continue; }
165
+ const score = kind === 'thread_settings_applied' ? threadSettingsScore(record) : 0;
166
+ if (!hits.has(kind) || score > (scores.get(kind) || 0)) { hits.set(kind, record); scores.set(kind, score); }
167
+ }
168
+ if ([...kinds].every((kind) => hits.has(kind))) break;
169
+ }
170
+ } catch { /* expected: a truncated read of an active rollout keeps what was parsed so far */ }
171
+ finally { fs.closeSync(fd); }
172
+ return { hits, sessionMeta, rejected };
173
+ }
174
+
175
+ function pickVersionRecords(files) {
176
+ const candidates = files
177
+ .map((file) => ({ file, size: statSize(file) }))
178
+ .sort((a, b) => b.size - a.size || a.file.localeCompare(b.file))
179
+ .slice(0, MAX_FILES_PER_VERSION);
180
+ const wanted = new Set([...REQUIRED_KINDS, ...OPTIONAL_KINDS]);
181
+ const accept = (kind, record) => (kind === 'compacted' ? compactedRefusal(record) : '');
182
+ const picked = { sessionMeta: null, records: new Map(), skipped: new Map() };
183
+ for (const { file } of candidates) {
184
+ const remaining = new Set([...wanted].filter((kind) => !picked.records.has(kind)));
185
+ if (!remaining.size) break;
186
+ // The largest rollout for a version carries the most record types; after it, only the kinds
187
+ // still missing are searched for, so a version costs at most one full pass.
188
+ const found = collectKinds(file, { kinds: remaining, accept });
189
+ if (!picked.sessionMeta) picked.sessionMeta = found.sessionMeta;
190
+ for (const [kind, reason] of found.rejected) if (!picked.skipped.has(kind)) picked.skipped.set(kind, reason);
191
+ for (const [kind, record] of found.hits) {
192
+ const previous = picked.records.get(kind);
193
+ if (!previous) { picked.records.set(kind, record); continue; }
194
+ if (kind === 'thread_settings_applied' && threadSettingsScore(record) > threadSettingsScore(previous)) {
195
+ picked.records.set(kind, record);
196
+ }
197
+ }
198
+ }
199
+ return picked;
200
+ }
201
+
202
+ // A compacted record is only a fixture when the scanner can classify its replacement window; the
203
+ // item types below mirror `classifyReplacement` (lib/history-transition-read-op.js). Codex writes
204
+ // `agent_message` items into some windows, which the scanner still refuses -- such a record is
205
+ // reported as skipped instead of being written into a fixture that cannot pass.
206
+ function compactedRefusal(record) {
207
+ const payload = record?.payload;
208
+ if (!payload || typeof payload !== 'object') return 'no payload';
209
+ const replacement = payload.replacement_history ?? payload.replacementHistory ?? payload.summary;
210
+ if (replacement === undefined) return '';
211
+ if (typeof replacement === 'string') return '';
212
+ if (!Array.isArray(replacement)) return 'replacement is not an array';
213
+ const calls = new Set();
214
+ const outputs = new Set();
215
+ for (const item of replacement) {
216
+ if (typeof item === 'string') continue;
217
+ if (!item || typeof item !== 'object' || Array.isArray(item)) return 'replacement item is not an object';
218
+ const type = String(item.type || item.kind || '');
219
+ const id = String(item.call_id || item.id || '');
220
+ if (REPLACEMENT_ITEM_TYPES.has(type)) continue;
221
+ if (REPLACEMENT_CALL_TYPES.has(type)) {
222
+ if (!id || calls.has(id)) return 'replacement call id is missing or repeated';
223
+ calls.add(id);
224
+ continue;
225
+ }
226
+ if (REPLACEMENT_OUTPUT_TYPES.has(type)) {
227
+ if (!id || !calls.has(id) || outputs.has(id)) return 'replacement output does not pair with a call';
228
+ outputs.add(id);
229
+ continue;
230
+ }
231
+ return `replacement item type ${type || '(none)'}`;
232
+ }
233
+ return calls.size === outputs.size ? '' : 'replacement tool pair is unclosed';
234
+ }
235
+
236
+ function statSize(file) {
237
+ try { return fs.statSync(file).size; }
238
+ // expected: a rollout deleted mid-run is ranked last instead of aborting the capture.
239
+ catch { return 0; }
240
+ }
241
+
242
+ // `threadRealId` is the session_meta id of the fixture: it is the fixture's thread id, so every
243
+ // other record naming that thread keeps naming it.
244
+ function createSanitizer(threadRealId = '') {
245
+ const ids = new Map();
246
+ if (THREAD_ID.test(threadRealId)) ids.set(threadRealId, FIXTURE_THREAD_ID);
247
+ const paths = new Map();
248
+ let nextIdIndex = 2;
249
+ const idFor = (value) => {
250
+ if (!ids.has(value)) {
251
+ ids.set(value, `${FIXTURE_ID_PREFIX}${nextIdIndex.toString(16).padStart(12, '0')}`);
252
+ nextIdIndex += 1;
253
+ }
254
+ return ids.get(value);
255
+ };
256
+ const pathFor = (value) => {
257
+ if (!paths.has(value)) paths.set(value, `${FIXTURE_PATH_ROOT}/${paths.size + 1}`);
258
+ return paths.get(value);
259
+ };
260
+ return { idFor, pathFor, threadId: () => FIXTURE_THREAD_ID };
261
+ }
262
+
263
+ function sanitizeValue(key, value, sanitizer, depth) {
264
+ if (typeof value === 'string') return sanitizeString(key, value, sanitizer);
265
+ if (depth > 64) return value;
266
+ if (Array.isArray(value)) return value.map((item) => sanitizeValue(key, item, sanitizer, depth + 1));
267
+ if (value && typeof value === 'object') {
268
+ return Object.fromEntries(Object.entries(value).map(([childKey, child]) => [childKey, sanitizeValue(childKey, child, sanitizer, depth + 1)]));
269
+ }
270
+ return value;
271
+ }
272
+
273
+ function sanitizeString(key, value, sanitizer) {
274
+ const name = key.toLowerCase();
275
+ if (name === 'encrypted_content') return FIXTURE_CIPHERTEXT;
276
+ if (name === 'image_url') return FIXTURE_IMAGE_URL;
277
+ if (PATH_KEYS.has(name)) return sanitizer.pathFor(value);
278
+ // Every record in the file belongs to the one fixture thread, exactly as in the rollout it was
279
+ // cut from, so the scanner's lineage check holds across records captured from different rollouts.
280
+ if (THREAD_KEYS.has(name)) return sanitizer.threadId();
281
+ if (THREAD_ID.test(value)) return sanitizer.idFor(value);
282
+ // A path or an account name can appear anywhere, including inside prose, tool arguments and an
283
+ // ssh remote ("<account>@host:path"). The substring test is deliberate: the account name is a
284
+ // substring of other local accounts too.
285
+ if (value.includes('/Users/') || value.includes('/home/') || ACCOUNT_NAME.test(value)) return FIXTURE_TEXT;
286
+ if (TEXT_KEYS.has(name) || value.length > MAX_TEXT_LENGTH) return FIXTURE_TEXT;
287
+ if (value.startsWith('/')) return sanitizer.pathFor(value);
288
+ if (ENUM_KEYS.has(name) && (ENUM_VALUE.test(value) || /^\d{4}-\d\d-\d\dT[\d:.]+Z$/.test(value))) return value;
289
+ // An id the scanner pairs or compares (call_id, item id, ...) stays consistent within the file.
290
+ if (name === 'id' || name.endsWith('_id')) return sanitizer.idFor(value);
291
+ return FIXTURE_WORD;
292
+ }
293
+
294
+ function sanitizeRecord(record, sanitizer) {
295
+ const envelope = {};
296
+ for (const key of ENVELOPE_KEYS) {
297
+ if (!Object.hasOwn(record, key)) continue;
298
+ envelope[key] = key === 'payload' ? sanitizeValue(key, record.payload, sanitizer, 0) : record[key];
299
+ }
300
+ // Any envelope key Codex adds later is carried through, never dropped.
301
+ for (const [key, value] of Object.entries(record)) {
302
+ if (Object.hasOwn(envelope, key)) continue;
303
+ envelope[key] = sanitizeValue(key, value, sanitizer, 0);
304
+ }
305
+ return envelope;
306
+ }
307
+
308
+ function fixtureRecords(picked) {
309
+ const sanitizer = createSanitizer(String(picked.sessionMeta?.payload?.id || ''));
310
+ const ordered = [];
311
+ if (picked.sessionMeta) ordered.push(picked.sessionMeta);
312
+ for (const kind of [...OPTIONAL_KINDS, ...REQUIRED_KINDS]) {
313
+ const record = picked.records.get(kind);
314
+ if (record) ordered.push(record);
315
+ }
316
+ return ordered.map((record, index) => sanitizeRecord(record, sanitizer));
317
+ }
318
+
319
+ function compareVersions(a, b) {
320
+ const parse = (value) => String(value).split('.').map((part) => Number.parseInt(part, 10) || 0);
321
+ const left = parse(a), right = parse(b);
322
+ for (let index = 0; index < Math.max(left.length, right.length); index += 1) {
323
+ if ((left[index] || 0) !== (right[index] || 0)) return (left[index] || 0) - (right[index] || 0);
324
+ }
325
+ return String(a).localeCompare(String(b));
326
+ }
327
+
328
+ function groupByVersion(files) {
329
+ const byVersion = new Map();
330
+ for (const file of files) {
331
+ const first = parseRecord(readFirstLine(file) || '');
332
+ if (!first || first.type !== 'session_meta') continue;
333
+ const version = String(first.payload?.cli_version || first.payload?.version || '').trim();
334
+ if (!version) continue;
335
+ if (!byVersion.has(version)) byVersion.set(version, []);
336
+ byVersion.get(version).push(file);
337
+ }
338
+ return byVersion;
339
+ }
340
+
341
+ function capture({ outDir, roots }) {
342
+ const files = roots.flatMap((root) => walkRollouts(root));
343
+ const byVersion = groupByVersion(files);
344
+ fs.mkdirSync(outDir, { recursive: true });
345
+ const captured = [];
346
+ for (const version of [...byVersion.keys()].sort(compareVersions)) {
347
+ const picked = pickVersionRecords(byVersion.get(version));
348
+ const records = fixtureRecords(picked);
349
+ const missing = REQUIRED_KINDS.filter((kind) => !picked.records.has(kind));
350
+ fs.writeFileSync(path.join(outDir, `${version}.jsonl`), `${records.map((record) => JSON.stringify(record)).join('\n')}\n`);
351
+ captured.push({ version, records: records.length, files: byVersion.get(version).length,
352
+ compacted: picked.records.has('compacted'), missing, skipped: picked.skipped });
353
+ }
354
+ return captured;
355
+ }
356
+
357
+ const README = `# Codex rollout record fixtures
358
+
359
+ One file per Codex CLI version (\`<cli_version>.jsonl\`), holding real rollout records cut from real
360
+ sessions and sanitized. These are what the scanner in \`lib/history-transition-read-op.js\` is
361
+ validated against, so schema drift shows up as a test failure instead of a refused provider switch.
362
+
363
+ Each file holds, in order: the session_meta that names the fixture thread, and then one record of
364
+ each kind Codex writes — \`compacted\` when the version produced one, \`thread_settings_applied\`
365
+ (the record with the most optional keys), \`turn_context\`, \`task_started\`, \`task_complete\`,
366
+ \`token_count\`, \`world_state\` (\`full: true\`).
367
+
368
+ Sanitization (bin/capture-codex-rollout-records.js): every string under a path key
369
+ (\`cwd\`, \`path\`, \`runtime_workspace_roots\`, \`workspace_roots\`, \`writable_roots\`, ...) and every
370
+ other absolute path becomes \`/tmp/fixture/<n>\`; every id-like uuid (thread, turn, response,
371
+ window) becomes a fixture uuid that is consistent within a file; prose keys (\`text\`, \`message\`,
372
+ \`last_agent_message\`, \`developer_instructions\`, \`base_instructions\`, ...) and any string that
373
+ carries a home path or the account name become \`fixture text\`; \`encrypted_content\` becomes
374
+ \`gAAAA\` + 40 x's; \`image_url\` becomes a tiny data uri. Every other short string is
375
+ default-deny: it survives only under a protocol-enum key (\`ENUM_KEYS\`: type, role, model, status,
376
+ approval_policy, ...); an \`id\`/\`*_id\` value becomes a consistent fixture uuid and anything
377
+ else -- approved command prefixes, network domains, MCP names, queries -- becomes \`fixture\`.
378
+ Key sets and value types are unchanged.
379
+
380
+ ## Regenerate
381
+
382
+ \`\`\`
383
+ cd coding-task-manager
384
+ node bin/capture-codex-rollout-records.js --out tests/fixtures/codex-rollout-records
385
+ \`\`\`
386
+
387
+ Add \`--roots <dir,dir>\` to read other Codex homes (default \`~/.codex/sessions\`,
388
+ \`~/.codex-local/sessions\`). After a Codex upgrade, capture the new version's file and review the
389
+ diff: **adding a new Codex version's fixture file is part of qualifying that version** (see
390
+ \`docs/codex-harness-version-qualification-2026-10-19.html\`).
391
+
392
+ Verify no real path or account name survives: \`tests/codex-rollout-record-fixtures.test.js\` greps
393
+ every fixture for the home-directory prefix and for the account name, and fails if either appears.
394
+ `;
395
+
396
+ function main(argv) {
397
+ const args = parseArgs(argv);
398
+ if (args.help || !args.out) {
399
+ process.stdout.write('usage: node bin/capture-codex-rollout-records.js --out <dir> [--roots <dir,dir>]\n');
400
+ return args.help ? 0 : 2;
401
+ }
402
+ const roots = (args.roots.length ? args.roots : DEFAULT_ROOTS).map((root) => path.resolve(root));
403
+ const outDir = path.resolve(process.cwd(), args.out);
404
+ const captured = capture({ outDir, roots });
405
+ fs.writeFileSync(path.join(outDir, 'README.md'), README);
406
+ for (const entry of captured) {
407
+ const compactedNote = entry.compacted ? 'compacted'
408
+ : `no-compacted${entry.skipped.has('compacted') ? ` (${entry.skipped.get('compacted')})` : ''}`;
409
+ const notes = [entry.missing.length ? `missing ${entry.missing.join(',')}` : 'complete', compactedNote];
410
+ process.stdout.write(`${entry.version}: ${entry.records} records from ${entry.files} rollout(s) (${notes.join(', ')})\n`);
411
+ }
412
+ return 0;
413
+ }
414
+
415
+ if (require.main === module) process.exitCode = main(process.argv.slice(2));
416
+
417
+ module.exports = { capture, sanitizeValue, createSanitizer, walkRollouts, groupByVersion, fixtureRecords };