create-walle 0.10.1 → 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 (341) hide show
  1. package/README.md +4 -0
  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 +6 -4
  6. package/package.json +1 -1
  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/claude-hook.js +7 -3
  11. package/template/coding-task-manager/bin/claude-table-parity.js +307 -0
  12. package/template/coding-task-manager/bin/conversation-storage-auto-migration-lib.js +16 -1
  13. package/template/coding-task-manager/bin/conversation-storage-generation-evidence.js +9 -2
  14. package/template/coding-task-manager/bin/conversation-storage-service-entry-lib.js +16 -1
  15. package/template/coding-task-manager/bin/conversation-storage-walle-owner.js +247 -0
  16. package/template/coding-task-manager/bin/ctm-machine-pair.js +160 -0
  17. package/template/coding-task-manager/bin/lead-wait-census.js +1348 -0
  18. package/template/coding-task-manager/bin/repair-foreign-clone.js +564 -0
  19. package/template/coding-task-manager/bin/repair-resume-echo-titles.js +248 -0
  20. package/template/coding-task-manager/bin/write-lock-bench.js +111 -0
  21. package/template/coding-task-manager/db.js +705 -286
  22. package/template/coding-task-manager/git-utils.js +44 -0
  23. package/template/coding-task-manager/lib/adopted-host-redial.js +4 -0
  24. package/template/coding-task-manager/lib/agent-presets.js +15 -0
  25. package/template/coding-task-manager/lib/agent-title-apply.js +88 -0
  26. package/template/coding-task-manager/lib/agent-title-backfill-read.js +92 -0
  27. package/template/coding-task-manager/lib/api/host-stats.js +98 -0
  28. package/template/coding-task-manager/lib/api/ingress.js +35 -12
  29. package/template/coding-task-manager/lib/api/machine-pairing.js +285 -0
  30. package/template/coding-task-manager/lib/api/machine-pool.js +307 -0
  31. package/template/coding-task-manager/lib/api/machines.js +461 -0
  32. package/template/coding-task-manager/lib/api/misc-ops.js +20 -5
  33. package/template/coding-task-manager/lib/api/providers.js +53 -6
  34. package/template/coding-task-manager/lib/api/resume-from-store.js +25 -0
  35. package/template/coding-task-manager/lib/api/sessions.js +160 -8
  36. package/template/coding-task-manager/lib/api/transcript-history.js +17 -4
  37. package/template/coding-task-manager/lib/api/worktrees.js +38 -37
  38. package/template/coding-task-manager/lib/approval-drift.js +16 -1
  39. package/template/coding-task-manager/lib/approval-logical-frame.js +5 -1
  40. package/template/coding-task-manager/lib/approval-scope.js +68 -4
  41. package/template/coding-task-manager/lib/auth-context.js +32 -2
  42. package/template/coding-task-manager/lib/auth-rate-limit.js +41 -1
  43. package/template/coding-task-manager/lib/auth-rules.js +186 -45
  44. package/template/coding-task-manager/lib/broadcast-serialize.js +2 -1
  45. package/template/coding-task-manager/lib/browser-session-broker.js +9 -1
  46. package/template/coding-task-manager/lib/browser-use-adapter.js +33 -2
  47. package/template/coding-task-manager/lib/claude-markdown-ansi.js +27 -85
  48. package/template/coding-task-manager/lib/claude-session-title.js +6 -30
  49. package/template/coding-task-manager/lib/claude-table-layout.js +322 -0
  50. package/template/coding-task-manager/lib/client-diag-handler.js +5 -0
  51. package/template/coding-task-manager/lib/codex-config-guard.js +33 -3
  52. package/template/coding-task-manager/lib/codex-harness-qualification.js +12 -0
  53. package/template/coding-task-manager/lib/codex-launch-health.js +29 -2
  54. package/template/coding-task-manager/lib/codex-link-preparation.js +9 -8
  55. package/template/coding-task-manager/lib/codex-paths.js +2 -1
  56. package/template/coding-task-manager/lib/coding-agent-models.js +21 -30
  57. package/template/coding-task-manager/lib/coding-session-model-adapters.js +27 -2
  58. package/template/coding-task-manager/lib/coding-session-model-projection.js +5 -0
  59. package/template/coding-task-manager/lib/coding-session-model-runtime.js +1 -2
  60. package/template/coding-task-manager/lib/coding-session-model-state.js +10 -0
  61. package/template/coding-task-manager/lib/coding-session-model-sync-state.js +0 -1
  62. package/template/coding-task-manager/lib/conversation-import/cursor-guard-owner.js +10 -0
  63. package/template/coding-task-manager/lib/create/resolve-launch-params.js +8 -1
  64. package/template/coding-task-manager/lib/ctm-boot-lifecycle.js +17 -1
  65. package/template/coding-task-manager/lib/db-domain-tables.js +7 -0
  66. package/template/coding-task-manager/lib/db-domains.js +21 -2
  67. package/template/coding-task-manager/lib/db-owner-op-registry.js +7 -0
  68. package/template/coding-task-manager/lib/db-schema-source-files.js +1 -0
  69. package/template/coding-task-manager/lib/feature-manifest.js +1 -1
  70. package/template/coding-task-manager/lib/fs-watcher.js +3 -2
  71. package/template/coding-task-manager/lib/harness-history-transition.js +3 -3
  72. package/template/coding-task-manager/lib/headless-inflight-ops.js +203 -0
  73. package/template/coding-task-manager/lib/headless-missing-reply.js +42 -0
  74. package/template/coding-task-manager/lib/headless-term-service.js +55 -17
  75. package/template/coding-task-manager/lib/host-control-dial.js +167 -0
  76. package/template/coding-task-manager/lib/host-handoff-orchestrator.js +16 -2
  77. package/template/coding-task-manager/lib/host-handoff.js +51 -13
  78. package/template/coding-task-manager/lib/host-health-ledger.js +164 -12
  79. package/template/coding-task-manager/lib/host-self-truth-watchdog.js +4 -0
  80. package/template/coding-task-manager/lib/host-stats.js +328 -0
  81. package/template/coding-task-manager/lib/host-threadpool-evacuation.js +160 -0
  82. package/template/coding-task-manager/lib/host-threadpool-monitor.js +14 -1
  83. package/template/coding-task-manager/lib/hosted-input-dispatch.js +203 -0
  84. package/template/coding-task-manager/lib/hosted-sync-worker.js +113 -5
  85. package/template/coding-task-manager/lib/instance-id-ownership-manifest.js +4 -0
  86. package/template/coding-task-manager/lib/instance-manifest-runtime.js +3 -0
  87. package/template/coding-task-manager/lib/instance-manifest.js +13 -1
  88. package/template/coding-task-manager/lib/instance-sync-status.js +15 -0
  89. package/template/coding-task-manager/lib/launch-model-override.js +160 -1
  90. package/template/coding-task-manager/lib/launch-plan-worker.js +59 -2
  91. package/template/coding-task-manager/lib/lead-model-switch-runtime.js +57 -4
  92. package/template/coding-task-manager/lib/lead-model-switch.js +3 -2
  93. package/template/coding-task-manager/lib/lead-model-transition-store.js +61 -3
  94. package/template/coding-task-manager/lib/legacy-draft-cancellation-recovery.js +111 -0
  95. package/template/coding-task-manager/lib/login-tripwire.js +1 -0
  96. package/template/coding-task-manager/lib/machine-pairing.js +238 -0
  97. package/template/coding-task-manager/lib/machine-pool.js +409 -0
  98. package/template/coding-task-manager/lib/machine-registry.js +749 -0
  99. package/template/coding-task-manager/lib/machine-self.js +66 -0
  100. package/template/coding-task-manager/lib/main-thread-lint-exemptions.json +52 -4
  101. package/template/coding-task-manager/lib/media-journal-control-reader.js +473 -0
  102. package/template/coding-task-manager/lib/media-journal.js +84 -27
  103. package/template/coding-task-manager/lib/media-store-options.js +43 -15
  104. package/template/coding-task-manager/lib/migrations/v56-host-samples-and-machine-pool.js +76 -0
  105. package/template/coding-task-manager/lib/mirror-feed-sanitize.js +12 -3
  106. package/template/coding-task-manager/lib/model-proxy/anthropic-face.js +4 -0
  107. package/template/coding-task-manager/lib/model-proxy/chat-response-translators.js +3 -0
  108. package/template/coding-task-manager/lib/model-proxy/handoff.js +494 -0
  109. package/template/coding-task-manager/lib/model-proxy/lead-worker-picks.js +25 -0
  110. package/template/coding-task-manager/lib/model-proxy/native-lead-listener.js +61 -7
  111. package/template/coding-task-manager/lib/model-proxy/proxy-cli.js +200 -0
  112. package/template/coding-task-manager/lib/model-proxy/proxy-instance-record.js +257 -0
  113. package/template/coding-task-manager/lib/model-proxy/stream-accounting.js +205 -0
  114. package/template/coding-task-manager/lib/model-proxy/tokens.js +78 -0
  115. package/template/coding-task-manager/lib/model-proxy/upstream-outcome.js +9 -0
  116. package/template/coding-task-manager/lib/native-history-control.js +2 -2
  117. package/template/coding-task-manager/lib/native-lead-settings-client.js +2 -1
  118. package/template/coding-task-manager/lib/native-lead-tls.js +18 -4
  119. package/template/coding-task-manager/lib/oauth-proxy-supervisor.js +628 -64
  120. package/template/coding-task-manager/lib/owner-action-manifest-data.json +14 -0
  121. package/template/coding-task-manager/lib/painted-row-recorder.js +9 -1
  122. package/template/coding-task-manager/lib/prompt-submission-contract.js +21 -1
  123. package/template/coding-task-manager/lib/provider-login-broker.js +192 -9
  124. package/template/coding-task-manager/lib/provider-login-capture.js +153 -0
  125. package/template/coding-task-manager/lib/queue-draft-status-overlay.js +36 -0
  126. package/template/coding-task-manager/lib/raw-codex-history-window.js +5 -3
  127. package/template/coding-task-manager/lib/read-pool-client.js +5 -2
  128. package/template/coding-task-manager/lib/read-pool-op-registry.js +10 -1
  129. package/template/coding-task-manager/lib/recent-catalog-query.js +26 -0
  130. package/template/coding-task-manager/lib/recent-session-catalog-response.js +9 -0
  131. package/template/coding-task-manager/lib/remote-command-context.js +12 -0
  132. package/template/coding-task-manager/lib/remote-command-runtime.js +13 -1
  133. package/template/coding-task-manager/lib/remote-machine-catalog.js +1062 -0
  134. package/template/coding-task-manager/lib/remote-machine-create.js +693 -0
  135. package/template/coding-task-manager/lib/remote-machine-link.js +939 -0
  136. package/template/coding-task-manager/lib/remote-machine-proxy.js +520 -0
  137. package/template/coding-task-manager/lib/remote-session-forward.js +306 -0
  138. package/template/coding-task-manager/lib/remote-submission-status.js +6 -0
  139. package/template/coding-task-manager/lib/restart-outcome-truth.js +20 -3
  140. package/template/coding-task-manager/lib/restart-preflight.js +9 -2
  141. package/template/coding-task-manager/lib/restore-backoff-gate.js +10 -2
  142. package/template/coding-task-manager/lib/restore-prepare-retry.js +12 -5
  143. package/template/coding-task-manager/lib/restore-resume-batch.js +22 -1
  144. package/template/coding-task-manager/lib/resume-blocked-wire.js +62 -1
  145. package/template/coding-task-manager/lib/resume-cwd.js +13 -0
  146. package/template/coding-task-manager/lib/resume-from-store.js +12 -1
  147. package/template/coding-task-manager/lib/resume-origin-facts.js +38 -1
  148. package/template/coding-task-manager/lib/resume-preflight.js +21 -16
  149. package/template/coding-task-manager/lib/review-repo-admission.js +39 -6
  150. package/template/coding-task-manager/lib/review-repo-registry.js +17 -0
  151. package/template/coding-task-manager/lib/root-delivery-runtime.js +72 -15
  152. package/template/coding-task-manager/lib/root-delivery-selection.js +37 -1
  153. package/template/coding-task-manager/lib/root-prompt-admission.js +33 -2
  154. package/template/coding-task-manager/lib/root-prompt-flow.js +167 -33
  155. package/template/coding-task-manager/lib/scrollback-archive-store.js +7 -4
  156. package/template/coding-task-manager/lib/session-analysis-memory.js +2 -0
  157. package/template/coding-task-manager/lib/session-create/codex-native-provider-gate.js +218 -0
  158. package/template/coding-task-manager/lib/session-create/codex-resume-exit-classify.js +52 -0
  159. package/template/coding-task-manager/lib/session-create/delegate-worker.js +95 -6
  160. package/template/coding-task-manager/lib/session-create/identity.js +20 -1
  161. package/template/coding-task-manager/lib/session-create/prepared.js +69 -1
  162. package/template/coding-task-manager/lib/session-create/provider-launch-context.js +77 -0
  163. package/template/coding-task-manager/lib/session-create/provider-process.js +34 -37
  164. package/template/coding-task-manager/lib/session-create/response.js +30 -12
  165. package/template/coding-task-manager/lib/session-create/transcript.js +13 -4
  166. package/template/coding-task-manager/lib/session-create/worker-continuation.js +43 -1
  167. package/template/coding-task-manager/lib/session-create/worker-notices.js +94 -5
  168. package/template/coding-task-manager/lib/session-create/worker-observability.js +99 -7
  169. package/template/coding-task-manager/lib/session-create/worker-stop.js +11 -3
  170. package/template/coding-task-manager/lib/session-create/worker-wait-watcher.js +315 -0
  171. package/template/coding-task-manager/lib/session-geometry-authority-handler.js +10 -1
  172. package/template/coding-task-manager/lib/session-host-manager.js +152 -206
  173. package/template/coding-task-manager/lib/session-jobs.js +17 -9
  174. package/template/coding-task-manager/lib/session-lifecycle.js +18 -0
  175. package/template/coding-task-manager/lib/session-model-action-preconditions.js +9 -0
  176. package/template/coding-task-manager/lib/session-resource-sampler.js +19 -9
  177. package/template/coding-task-manager/lib/session-standup.js +67 -4
  178. package/template/coding-task-manager/lib/session-status-ledger.js +42 -3
  179. package/template/coding-task-manager/lib/session-status-runtime.js +45 -1
  180. package/template/coding-task-manager/lib/session-title-mirror.js +25 -57
  181. package/template/coding-task-manager/lib/session-transcript-trash.js +8 -1
  182. package/template/coding-task-manager/lib/startup-pointer-write.js +57 -0
  183. package/template/coding-task-manager/lib/state-sync/painted-frame-field.js +11 -0
  184. package/template/coding-task-manager/lib/storage/coding-session-model-store.js +21 -22
  185. package/template/coding-task-manager/lib/surviving-owner-bootstrap.js +7 -2
  186. package/template/coding-task-manager/lib/surviving-owner-recovery.js +2 -1
  187. package/template/coding-task-manager/lib/terminal-draft-http-status.js +25 -0
  188. package/template/coding-task-manager/lib/transcript-archiver.js +229 -229
  189. package/template/coding-task-manager/lib/transcript-frame-cache.js +12 -2
  190. package/template/coding-task-manager/lib/transcript-guard-job.js +55 -4
  191. package/template/coding-task-manager/lib/transcript-link.js +77 -0
  192. package/template/coding-task-manager/lib/transcript-manifest-store.js +8 -4
  193. package/template/coding-task-manager/lib/transcript-source.js +8 -3
  194. package/template/coding-task-manager/lib/transcript-store.js +40 -0
  195. package/template/coding-task-manager/lib/tunnel-lane-governor.js +6 -3
  196. package/template/coding-task-manager/lib/typing-interaction-trace.js +92 -0
  197. package/template/coding-task-manager/lib/typing-worker-stages.js +60 -0
  198. package/template/coding-task-manager/lib/visible-revision-watchdog.js +8 -12
  199. package/template/coding-task-manager/lib/wake-skip-reasons.js +46 -0
  200. package/template/coding-task-manager/lib/walle-supervisor.js +98 -14
  201. package/template/coding-task-manager/lib/worker-failure-classify.js +13 -1
  202. package/template/coding-task-manager/lib/ws/state-sync.js +19 -2
  203. package/template/coding-task-manager/lib/ws/terminal-attach.js +32 -1
  204. package/template/coding-task-manager/lib/ws-router.js +68 -1
  205. package/template/coding-task-manager/lib/ws-send-admission.js +22 -4
  206. package/template/coding-task-manager/oauth-proxy.js +99 -55
  207. package/template/coding-task-manager/package-lock.json +24 -0
  208. package/template/coding-task-manager/package.json +3 -0
  209. package/template/coding-task-manager/providers/codex-mcp-extract.js +121 -19
  210. package/template/coding-task-manager/providers/codex-mcp.js +14 -5
  211. package/template/coding-task-manager/public/css/app.css +279 -1
  212. package/template/coding-task-manager/public/css/reviews.css +4 -0
  213. package/template/coding-task-manager/public/index.html +294 -223
  214. package/template/coding-task-manager/public/js/app/machines-page.js +1252 -0
  215. package/template/coding-task-manager/public/js/app/models-tab.js +546 -36
  216. package/template/coding-task-manager/public/js/app/worktrees-panel.js +7 -5
  217. package/template/coding-task-manager/public/js/background-work.js +13 -7
  218. package/template/coding-task-manager/public/js/coding-session-model-presentation.js +44 -4
  219. package/template/coding-task-manager/public/js/core/session-commands.js +676 -0
  220. package/template/coding-task-manager/public/js/core/session-selectors.js +5 -1
  221. package/template/coding-task-manager/public/js/history-app-seam.js +26 -2
  222. package/template/coding-task-manager/public/js/history-painted-rows.js +17 -2
  223. package/template/coding-task-manager/public/js/history-reader-park.js +13 -1
  224. package/template/coding-task-manager/public/js/history-scroller.js +187 -9
  225. package/template/coding-task-manager/public/js/history-transcript-pages.js +5 -5
  226. package/template/coding-task-manager/public/js/machine-pool-shared.js +436 -0
  227. package/template/coding-task-manager/public/js/private-state-scope.js +31 -3
  228. package/template/coding-task-manager/public/js/prompt-submission-client.js +173 -8
  229. package/template/coding-task-manager/public/js/prompt-submission-tray.js +281 -0
  230. package/template/coding-task-manager/public/js/queue-panel-submission-reconcile.js +174 -1
  231. package/template/coding-task-manager/public/js/reviews.js +7 -3
  232. package/template/coding-task-manager/public/js/root-draft-lifetime.js +139 -2
  233. package/template/coding-task-manager/public/js/root-queue-builder-lifetime.js +16 -0
  234. package/template/coding-task-manager/public/js/row-join.js +4 -2
  235. package/template/coding-task-manager/public/js/session-model-picker.js +1 -1
  236. package/template/coding-task-manager/public/js/session-search-utils.js +46 -19
  237. package/template/coding-task-manager/public/js/state-sync-client.js +4 -0
  238. package/template/coding-task-manager/public/js/terminal/runtime-02.js +16 -7
  239. package/template/coding-task-manager/public/js/terminal/runtime-03.js +41 -0
  240. package/template/coding-task-manager/public/js/terminal/runtime-04.js +41 -4
  241. package/template/coding-task-manager/public/js/terminal/runtime-12.js +52 -2
  242. package/template/coding-task-manager/public/js/terminal/runtime-16.js +12 -12
  243. package/template/coding-task-manager/public/js/terminal/runtime-18.js +14 -0
  244. package/template/coding-task-manager/public/js/terminal/runtime-19.js +8 -4
  245. package/template/coding-task-manager/public/js/terminal/runtime-20.js +210 -73
  246. package/template/coding-task-manager/public/js/terminal/runtime-21.js +19 -6
  247. package/template/coding-task-manager/public/js/terminal/runtime-22.js +86 -0
  248. package/template/coding-task-manager/public/js/terminal-history-viewport.js +1 -1
  249. package/template/coding-task-manager/public/js/terminal-wheel.js +39 -2
  250. package/template/coding-task-manager/public/js/walle/chat-conversation.js +11 -3
  251. package/template/coding-task-manager/public/js/walle-session.js +30 -7
  252. package/template/coding-task-manager/public/m/app.css +226 -8
  253. package/template/coding-task-manager/public/m/app.js +502 -9
  254. package/template/coding-task-manager/public/m/index.html +38 -1
  255. package/template/coding-task-manager/public/m/machines.js +1313 -0
  256. package/template/coding-task-manager/public/m/pool-failover.js +391 -0
  257. package/template/coding-task-manager/public/m/private-queue-actions.js +26 -7
  258. package/template/coding-task-manager/public/m/sw.js +5 -0
  259. package/template/coding-task-manager/queue-engine.js +13 -0
  260. package/template/coding-task-manager/server/approvals.js +5 -6
  261. package/template/coding-task-manager/server/boot.js +136 -17
  262. package/template/coding-task-manager/server.js +902 -239
  263. package/template/coding-task-manager/session-integrity.js +7 -9
  264. package/template/coding-task-manager/workers/db-owner-worker.js +53 -7
  265. package/template/coding-task-manager/workers/read-pool-worker.js +17 -6
  266. package/template/coding-task-manager/workers/session-host-pool-process.js +18 -4
  267. package/template/coding-task-manager/workers/session-host-process.js +14 -3
  268. package/template/coding-task-manager/workers/transcript-archive-worker.js +1 -1
  269. package/template/coding-task-manager/workers/worktree-worker.js +34 -0
  270. package/template/package-lock.json +2 -2
  271. package/template/package.json +1 -1
  272. package/template/scripts/conversation-storage-v2-migration.js +78 -11
  273. package/template/shared/conversation-storage-v2-contract.js +42 -6
  274. package/template/shared/fixtures/worker-status-payloads.json +392 -0
  275. package/template/shared/git-remote-key.js +83 -0
  276. package/template/shared/host-identity.js +98 -2
  277. package/template/shared/owner-write-safety.js +135 -0
  278. package/template/shared/public-runtime.js +13 -5
  279. package/template/shared/release-schema.json +3 -2
  280. package/template/shared/report-citations.js +14 -0
  281. package/template/shared/sqlite-write-lock.js +361 -223
  282. package/template/shared/storage/contract.js +35 -3
  283. package/template/wall-e/agent.js +21 -0
  284. package/template/wall-e/api-walle.js +5 -0
  285. package/template/wall-e/bin/brain-purge-legacy-ctm.js +3 -2
  286. package/template/wall-e/bin/walle-mcp-stdio.js +12 -6
  287. package/template/wall-e/brain/helper-rules-schema.js +129 -0
  288. package/template/wall-e/brain/migrations.js +51 -22
  289. package/template/wall-e/brain/model-deployment-facts.js +31 -1
  290. package/template/wall-e/brain/owner-call-whitelist.js +16 -0
  291. package/template/wall-e/brain/schema-ensures.js +22 -0
  292. package/template/wall-e/brain.js +1 -1
  293. package/template/wall-e/coding/journal-dispatcher.js +51 -0
  294. package/template/wall-e/coding/session-journal.js +28 -40
  295. package/template/wall-e/coding-orchestrator.js +23 -26
  296. package/template/wall-e/http/auth.js +18 -1
  297. package/template/wall-e/http/blog-views-api.js +318 -0
  298. package/template/wall-e/http/helper-rules-api.js +321 -0
  299. package/template/wall-e/http/model-proxy-providers.js +3 -1
  300. package/template/wall-e/http/telemetry-api.js +12 -0
  301. package/template/wall-e/lib/blog-view-policy.js +88 -0
  302. package/template/wall-e/lib/blog-view-store.js +169 -0
  303. package/template/wall-e/lib/client-config-lock.js +2 -1
  304. package/template/wall-e/lib/coding-journal-writer-child-client.js +67 -18
  305. package/template/wall-e/lib/error-kinds.js +10 -1
  306. package/template/wall-e/lib/feature-manifest.js +1 -1
  307. package/template/wall-e/lib/helper-rules/cell-state.js +123 -0
  308. package/template/wall-e/lib/helper-rules/evaluate.js +357 -0
  309. package/template/wall-e/lib/helper-rules/explore-rubric.md +12 -0
  310. package/template/wall-e/lib/helper-rules/grade.js +277 -0
  311. package/template/wall-e/lib/helper-rules/label.js +247 -0
  312. package/template/wall-e/lib/helper-rules/lead-call-hook.js +522 -0
  313. package/template/wall-e/lib/helper-rules/store.js +667 -0
  314. package/template/wall-e/lib/mcp-integration.js +5 -6
  315. package/template/wall-e/lib/runtime-worker-pool.js +1 -0
  316. package/template/wall-e/lib/scheduler-worker-jobs.js +6 -0
  317. package/template/wall-e/lib/telemetry-db-schema.js +37 -0
  318. package/template/wall-e/lib/telemetry-process-client.js +76 -1
  319. package/template/wall-e/llm/default-fallback.js +4 -0
  320. package/template/wall-e/llm/model-catalog.js +13 -4
  321. package/template/wall-e/llm/model-roles.js +6 -0
  322. package/template/wall-e/llm/portkey-http.js +150 -0
  323. package/template/wall-e/llm/portkey-sync.js +15 -145
  324. package/template/wall-e/llm/worker-mcp-routing.js +61 -0
  325. package/template/wall-e/llm/worker-route.js +43 -1
  326. package/template/wall-e/loops/brain-custom-writes.js +45 -0
  327. package/template/wall-e/mcp-protocol-contract.js +41 -1
  328. package/template/wall-e/mcp-server.js +13 -2
  329. package/template/wall-e/mcp-worker-schemas.js +20 -2
  330. package/template/wall-e/memory/ctm-context-client.js +25 -5
  331. package/template/wall-e/shared/release-schema.json +3 -2
  332. package/template/wall-e/shared/sqlite-write-lock.js +361 -223
  333. package/template/wall-e/shared/storage/contract.js +35 -3
  334. package/template/wall-e/skills/_templates/delegate-worker/VERSION +1 -1
  335. package/template/wall-e/skills/_templates/delegate-worker/released-sha256.json +6 -0
  336. package/template/wall-e/skills/_templates/delegate-worker/templates/claude/SKILL.md +7 -2
  337. package/template/wall-e/skills/_templates/delegate-worker/templates/codex/SKILL.md +7 -2
  338. package/template/wall-e/tools/local-tools.js +6 -4
  339. package/template/wall-e/workers/brain-owner-worker.js +9 -0
  340. package/template/wall-e/workers/coding-journal-writer-child.js +8 -1
  341. package/template/wall-e/workers/telemetry-worker.js +8 -0
@@ -7,6 +7,12 @@ const admissionDefault = require('../worker-admission');
7
7
  const { restoreSucceeded } = require('../resume-rung-enrich');
8
8
  const transcriptRetention = require('../transcript-retention-check');
9
9
  const transcriptSource = require('../transcript-source');
10
+ // WP-2 (D-4/D-7): the derived-transcript-path helper and its verifier (see the preflight at the
11
+ // transcript link below). serverConfig is the same live-read env seam production uses for the
12
+ // Claude config dir (server.js:6743 getClaudeConfigDir).
13
+ const serverConfig = require('../server-config');
14
+ const { deriveClaudeTranscriptPath } = require('../resume-cwd');
15
+ const { verifyClaudeTranscriptAt } = require('../transcript-link');
10
16
  const { isColdDeferredSession, notifyWorkerGeneration } = require('./worker-observability');
11
17
  const { budgetSentence } = require('./worker-brief');
12
18
  const { attachProgressGuard } = require('./worker-progress-guard');
@@ -533,7 +539,43 @@ function createWorkerContinuation({ getDeps, admission = admissionDefault, helpe
533
539
  catch {
534
540
  return failReceipt('worker_transcript_unavailable', 'The provider transcript link could not be read.', { retryable: true });
535
541
  }
536
- const transcriptPath = String(agentRow && agentRow.jsonl_path || '').trim();
542
+ // WP-2 (D-4, worker-transcript-link-derived-not-discovered-2026-10-25.html §5 D-4/D-7): an
543
+ // empty jsonl_path is "we never recorded the link", not "the transcript is gone". Claude Code
544
+ // writes a session's transcript at a path derivable from (cwd, session id) alone, so derive
545
+ // it, VERIFY it through the one transcript reader (lib/transcript-link.js -- the file's own
546
+ // first line must name this session), link it, and continue. A derived path that verifies
547
+ // false, or a row with no cwd to derive from, refuses exactly as before -- now naming what
548
+ // was checked. Codex rows never enter this branch (their resume identity is the rollout
549
+ // thread, and a missing link there is a different failure).
550
+ let transcriptPath = String(agentRow && agentRow.jsonl_path || '').trim();
551
+ if (!transcriptPath && agentRow && recipe.agent === 'claude') {
552
+ const cwd = String(recipe.cwd || agentRow.project_path || agentRow.cwd || '').trim();
553
+ const providerSessionId = String(agentRow.agent_session_id || id);
554
+ let derived = '';
555
+ try {
556
+ derived = deriveClaudeTranscriptPath({
557
+ cwd,
558
+ agentSessionId: providerSessionId,
559
+ // The house coercion for this dep (lib/session-create/worker-mcp-servers.js:45): production's
560
+ // deps carries no `homedir` at all, and the test corpus injects both a getter and a string.
561
+ homeDir: typeof deps.homedir === 'function' ? deps.homedir() : deps.homedir,
562
+ claudeConfigDir: deps.claudeConfigDir || serverConfig.CLAUDE_CONFIG_DIR || '',
563
+ });
564
+ } catch { /* expected: a row with no cwd (or no agent_session_id) has nothing to derive -- fall through to the unchanged refusal */ }
565
+ if (derived) {
566
+ const verified = await verifyClaudeTranscriptAt(derived, providerSessionId, {
567
+ ...(typeof deps.resolveTranscriptSource === 'function' ? { resolveTranscriptSource: deps.resolveTranscriptSource } : {}),
568
+ ...(typeof deps.readTranscriptHead === 'function' ? { readTranscriptHead: deps.readTranscriptHead } : {}),
569
+ });
570
+ if (!verified.ok) {
571
+ return failReceipt('worker_transcript_missing', 'The provider transcript is not linked to this worker.', {
572
+ extra: { derived_path: derived, derived_state: verified.state },
573
+ });
574
+ }
575
+ if (typeof db.updateAgentSessionTranscriptLink === 'function') db.updateAgentSessionTranscriptLink(providerSessionId, derived);
576
+ transcriptPath = derived;
577
+ }
578
+ }
537
579
  if (!transcriptPath) return failReceipt('worker_transcript_missing', 'The provider transcript is not linked to this worker.');
538
580
 
539
581
  let providerId = '';
@@ -1,9 +1,12 @@
1
1
  'use strict';
2
2
 
3
3
  // A lead hears that its delegated worker stopped through its next PostToolUse / UserPromptSubmit
4
- // hook (lib/api/ingress.js -> bin/claude-hook.js). The queue lives here; the durable truth is the
5
- // terminal receipt's `delivered_at_ms` in worker_sessions.runtime_json, so a CTM restart rebuilds
6
- // the undelivered notices once per parent. Never types into the lead's terminal.
4
+ // hook, and -- RC-1, docs/session-stops-mid-job-2026-10-24.html D-1..D-3 -- through its Stop hook
5
+ // too, which is answered synchronously by CTM with the drained notices plus, when the lead still
6
+ // owns a live worker and nothing pending will wake it, a nudge naming walle_worker_wait /
7
+ // walle_stop_worker. The queue lives here; the durable truth is the terminal receipt's
8
+ // `delivered_at_ms` in worker_sessions.runtime_json, so a CTM restart rebuilds the undelivered
9
+ // notices once per parent. Never types into the lead's terminal (D-5).
7
10
 
8
11
  const mainDbCensus = require('../main-db-census');
9
12
  const { nextStepFor } = require('../worker-failure-classify');
@@ -13,6 +16,88 @@ const MAX_TEXT = 400;
13
16
 
14
17
  const _queues = new Map(); // parent ctm id -> [{ receipt_id, worker_id, parent, text, at_ms }]
15
18
 
19
+ // WP-0 (D-2/D-7): the live-worker index, maintained from the receipts CTM already publishes.
20
+ // Memory only -- the hook path touches no DB, and a CTM restart retires every live worker, so the
21
+ // index starts empty and needs no rebuild.
22
+ const _live = new Map(); // parent ctm id -> Map(worker id -> { generation, at_ms })
23
+
24
+ // WP-1 (D-4): one nudge per tool epoch. `nudgedAtEpoch` starts at `null` so the first Stop
25
+ // (toolEpoch 0) nudges; a second Stop with no PostToolUse between sees toolEpoch === nudgedAtEpoch.
26
+ const _epochs = new Map(); // parent ctm id -> { toolEpoch, nudgedAtEpoch }
27
+
28
+ function _epochFor(parent) {
29
+ let epoch = _epochs.get(parent);
30
+ if (!epoch) { epoch = { toolEpoch: 0, nudgedAtEpoch: null }; _epochs.set(parent, epoch); }
31
+ return epoch;
32
+ }
33
+
34
+ // D-2: a `started` receipt says this worker is live for its parent until a terminal receipt for the
35
+ // same or a newer generation arrives. No parent or no worker id -> nothing to index.
36
+ function markWorkerLive(receipt) {
37
+ if (!receipt || receipt.kind !== 'started') return null;
38
+ const parent = String(receipt.parent_ctm_session_id || '');
39
+ const workerId = String(receipt.worker_id || '');
40
+ if (!parent || !workerId) return null;
41
+ const generation = Number(receipt.generation) || 1;
42
+ const atMs = Number(receipt.at_ms) || Date.now();
43
+ const map = _live.get(parent) || new Map();
44
+ map.set(workerId, { generation, at_ms: atMs });
45
+ _live.set(parent, map);
46
+ return { worker_id: workerId, generation, at_ms: atMs };
47
+ }
48
+
49
+ // The terminal receipt retires the worker, but an OLDER generation's terminal must never erase a
50
+ // newer generation's `started` (AC-0c): the remove happens only while stored generation <= receipt
51
+ // generation.
52
+ function markWorkerGone(receipt) {
53
+ const parent = String(receipt?.parent_ctm_session_id || '');
54
+ const workerId = String(receipt?.worker_id || '');
55
+ if (!parent || !workerId) return false;
56
+ const map = _live.get(parent);
57
+ if (!map) return false;
58
+ const entry = map.get(workerId);
59
+ if (entry && Number(entry.generation) > (Number(receipt.generation) || 1)) return false;
60
+ map.delete(workerId);
61
+ if (!map.size) _live.delete(parent);
62
+ return true;
63
+ }
64
+
65
+ function liveWorkers(parentCtmSessionId) {
66
+ const parent = String(parentCtmSessionId || '');
67
+ const map = _live.get(parent);
68
+ if (!map || !map.size) return [];
69
+ return [...map.entries()]
70
+ .map(([worker_id, entry]) => ({ worker_id, generation: entry.generation, at_ms: entry.at_ms }))
71
+ .sort((a, b) => a.at_ms - b.at_ms);
72
+ }
73
+
74
+ function noteLeadToolUse(parentCtmSessionId) {
75
+ const parent = String(parentCtmSessionId || '');
76
+ if (!parent) return null;
77
+ const epoch = _epochFor(parent);
78
+ epoch.toolEpoch += 1;
79
+ return epoch.toolEpoch;
80
+ }
81
+
82
+ // D-3/D-4: the Stop nudge. Returns null when no worker is live, when a pending background task or
83
+ // session cron will wake the lead anyway, when the field is absent (Claude did not send it --
84
+ // unknown state fails open, never a nudge), or when this tool epoch was already nudged.
85
+ function stopNudge(parentCtmSessionId, { backgroundTasks = null, sessionCrons = null } = {}) {
86
+ const parent = String(parentCtmSessionId || '');
87
+ if (!parent) return null;
88
+ const workers = liveWorkers(parent);
89
+ if (!workers.length) return null;
90
+ if (Array.isArray(backgroundTasks) && backgroundTasks.some((task) => task && task.status === 'running')) return null;
91
+ if (Array.isArray(sessionCrons) && sessionCrons.length) return null;
92
+ if (!Array.isArray(backgroundTasks)) return null;
93
+ const epoch = _epochFor(parent);
94
+ if (epoch.toolEpoch === epoch.nudgedAtEpoch) return null;
95
+ epoch.nudgedAtEpoch = epoch.toolEpoch;
96
+ const firstId = workers[0].worker_id;
97
+ const text = `Delegated worker ${firstId} is still working (${workers.length} live) and nothing pending will wake you when it finishes. Call walle_worker_wait to keep waiting, or walle_stop_worker if you no longer need it. If you are deliberately handing back to your user with the worker still running, say so and end your turn again.`;
98
+ return { text, workers: workers.map((worker) => worker.worker_id) };
99
+ }
100
+
16
101
  // D-25: the notice ends with the failure's next_step. The evidence carries it when the
17
102
  // publisher stamped it; a receipt stored before that (or filtered by the evidence
18
103
  // whitelist) still names the code/class/reason, so the SAME table derives the identical
@@ -51,6 +136,7 @@ function noticeText(receipt) {
51
136
  function enqueueNotice(receipt) {
52
137
  const parent = String(receipt?.parent_ctm_session_id || '');
53
138
  if (!parent || receipt.kind !== 'terminal' || !receipt.receipt_id) return null;
139
+ markWorkerGone(receipt);
54
140
  const notice = { receipt_id: receipt.receipt_id, worker_id: String(receipt.worker_id || ''), parent, text: noticeText(receipt), at_ms: Number(receipt.at_ms) || Date.now() };
55
141
  const queue = _queues.get(parent) || [];
56
142
  if (!queue.some((n) => n.receipt_id === notice.receipt_id)) queue.push(notice);
@@ -132,6 +218,9 @@ function drainNotices(parentCtmSessionId, { now = Date.now(), dbModule = null, d
132
218
  };
133
219
  }
134
220
 
135
- function _resetForTest() { _queues.clear(); }
221
+ function _resetForTest() { _queues.clear(); _live.clear(); _epochs.clear(); }
136
222
 
137
- module.exports = { enqueueNotice, rebuildFromDb, drainNotices, noticeText, _resetForTest };
223
+ module.exports = {
224
+ enqueueNotice, rebuildFromDb, drainNotices, noticeText,
225
+ markWorkerLive, liveWorkers, noteLeadToolUse, stopNudge, _resetForTest,
226
+ };
@@ -4,7 +4,8 @@ const path = require('path');
4
4
  const { captureWorkerProgress } = require('./worker-progress');
5
5
  const { classifyFailure } = require('../worker-failure-classify');
6
6
  const { terminalBudgetFailure, budgetEvidence } = require('../model-proxy/request-budget');
7
- const { enqueueNotice } = require('./worker-notices');
7
+ const { enqueueNotice, markWorkerLive } = require('./worker-notices');
8
+ const { startWaitWatcher } = require('./worker-wait-watcher');
8
9
 
9
10
  const PROGRESS_CACHE_MS = 5000;
10
11
  const EVENT_RECEIPT_LIMIT = 32;
@@ -13,6 +14,10 @@ const NO_WRITE_ADVISORY_TURN_THRESHOLD = 20;
13
14
  const NO_WRITE_TERMINAL_TURN_THRESHOLD = 40;
14
15
  const EVENT_PRECEDENCE = { started: 0, progress: 1, advisory: 2, terminal: 3 };
15
16
  const WORKER_FINAL_RESULT_CAP_BYTES = 64 * 1024;
17
+ // Lead-cost routing round 2 D-3 (§6.2): the ONE report-text byte cap, shared by the status reader
18
+ // (delegate-worker.js counts citations from the bounded text) and the ?include_report=1 reader
19
+ // (lib/api/sessions.js). Same 64 KiB as the final-result cap.
20
+ const WORKER_REPORT_TEXT_CAP_BYTES = 64 * 1024;
16
21
  const FILE_PROBE_TTL_MS = 2000;
17
22
  const THROTTLED_WINDOW_MS = 60000;
18
23
 
@@ -89,10 +94,17 @@ function _removeWaiterRegistration(waiter, ctmSessionId) {
89
94
  if (!set.size) _waiters.delete(ctmSessionId);
90
95
  }
91
96
  const _liveWaiters = new Set(); // every not-yet-resolved waiter, once each (waiterCount counts waiters, not registrations)
92
- function _finishWaiter(waiter, why, workerId) {
97
+ function _finishWaiter(waiter, why, workerId, detail) {
93
98
  if (waiter.done) return;
94
99
  waiter.done = true;
95
100
  clearTimeout(waiter.timer);
101
+ // WP-1 (D-3): a resolved waiter stops its watcher first, so a late model reply is dropped
102
+ // (stop() marks the watcher dead) and no interval timer outlives the wait.
103
+ if (waiter.watch) {
104
+ try { waiter.watch.stop(); }
105
+ catch { /* expected: a watcher stop failure never blocks the resolve */ }
106
+ waiter.watch = null;
107
+ }
96
108
  for (const id of waiter.watchedIds) _removeWaiterRegistration(waiter, id);
97
109
  _liveWaiters.delete(waiter);
98
110
  if (waiter.onHumanSubmit) {
@@ -100,9 +112,16 @@ function _finishWaiter(waiter, why, workerId) {
100
112
  humanInputEvents.removeListener('humanSubmit', waiter.onHumanSubmit);
101
113
  waiter.onHumanSubmit = null;
102
114
  }
103
- waiter.resolve({ why, workerId: workerId != null ? workerId : null });
115
+ const result = { why, workerId: workerId != null ? workerId : null };
116
+ // WP-1: the watcher's own result fields exist only on a waiter that ran one -- a caller with
117
+ // no watcher sees exactly today's `{ why, workerId }` shape.
118
+ if (waiter.decisions) {
119
+ result.detail = detail || null;
120
+ result.decisions = waiter.decisions;
121
+ }
122
+ waiter.resolve(result);
104
123
  }
105
- function waitForWorkers({ workerIds, leadId, waitMs, generationById } = {}) {
124
+ function waitForWorkers({ workerIds, leadId, waitMs, generationById, watchMode, callModel, getStatus, readTail, workerRoute } = {}) {
106
125
  const ids = Array.isArray(workerIds) ? workerIds.map((id) => String(id || '')).filter(Boolean) : [];
107
126
  if (!ids.length) {
108
127
  // No watched workers: the wait is still meaningful for a lead (a user_input wake with no
@@ -111,7 +130,7 @@ function waitForWorkers({ workerIds, leadId, waitMs, generationById } = {}) {
111
130
  const empty = { generationById: {} };
112
131
  return waitForWorkersWithListener([], leadId, waitMs, empty);
113
132
  }
114
- return waitForWorkersWithListener(ids, leadId, waitMs, { generationById });
133
+ return waitForWorkersWithListener(ids, leadId, waitMs, { generationById, watchMode, callModel, getStatus, readTail, workerRoute });
115
134
  }
116
135
  function waitForWorkersWithListener(ids, leadId, waitMs, state) {
117
136
  let release = () => {};
@@ -124,7 +143,7 @@ function waitForWorkersWithListener(ids, leadId, waitMs, state) {
124
143
  done: false,
125
144
  resolve,
126
145
  };
127
- const finish = (why, workerId) => _finishWaiter(waiter, why, workerId);
146
+ const finish = (why, workerId, detail) => _finishWaiter(waiter, why, workerId, detail);
128
147
  waiter.finish = finish;
129
148
  waiter.timer = setTimeout(() => finish('timeout'), Math.max(0, Number(waitMs) || 0));
130
149
  if (typeof waiter.timer.unref === 'function') waiter.timer.unref();
@@ -142,12 +161,64 @@ function waitForWorkersWithListener(ids, leadId, waitMs, state) {
142
161
  humanInputEvents.on('humanSubmit', waiter.onHumanSubmit);
143
162
  }
144
163
  release = () => finish('timeout');
164
+ // WP-1 (D-3/D-4): the watcher parks BESIDE the waiter -- one tick per watched worker, the
165
+ // worker's own pinned model, and a `watcher`/`watcher_unavailable` wake through the same
166
+ // finish gate as every code wake. It is started only when the caller wired one (a caller
167
+ // with no watchMode/callModel keeps today's behaviour untouched).
168
+ if (ids.length && state.watchMode !== 'off' && typeof state.callModel === 'function') {
169
+ waiter.decisions = [];
170
+ waiter.watch = startWaitWatcher({
171
+ workerIds: ids,
172
+ mode: state.watchMode || 'on',
173
+ wake: (why, detail) => finish(why, detail && detail.worker_id, detail),
174
+ onDecision: (d) => waiter.decisions.push(d),
175
+ callModel: state.callModel,
176
+ getStatus: state.getStatus,
177
+ readTail: state.readTail,
178
+ workerRoute: state.workerRoute,
179
+ }) || null;
180
+ }
145
181
  // F1: a waiter registered before the caller knows a worker's current generation (undefined
146
182
  // wakes on any generation notice) is narrowed per worker once the first status read lands.
147
183
  setGeneration = (workerId, value) => { if (!waiter.done) waiter.generationByCtmId.set(String(workerId), value); };
148
184
  });
149
185
  return { promise, release, setGeneration };
150
186
  }
187
+
188
+ // WP-1 (D-3): the watcher's PRODUCTION model call. It rides the lane every other CTM
189
+ // background completion uses (lib/background-llm.js -> Wall-E's background-completion
190
+ // endpoint), so the resolved provider, the fallback chain and the circuit breakers stay the
191
+ // routing authority's and CTM builds no provider client of its own. The pin is the caller's
192
+ // `model` (the worker row's own route_alias + ':' + model, never a second route).
193
+ // A failure -- transport, an unroutable pin (route_throttled / route_unavailable /
194
+ // model_not_found), or a non-2xx decision -- answers `{ ok: false, error }`, which is exactly
195
+ // what the watcher's D-4 consecutive-failure counter reads.
196
+ function createWaitWatcherCallModel() {
197
+ return async function callWaitWatcherModel({ feature, model, system, messages, maxTokens, timeoutMs, sessionId }) {
198
+ let llm;
199
+ try {
200
+ llm = require('../background-llm');
201
+ // D-4: the watcher never takes a second route. background-llm's last-ditch Ollama hop
202
+ // fires on a Wall-E transport error unless the fallback setting is off, so this call
203
+ // carries an env copy with it off -- a transport failure is then `{ ok: false }` and the
204
+ // consecutive-failure counter ends the watcher with `watcher_unavailable`.
205
+ const env = { ...process.env, CTM_BACKGROUND_LLM_FALLBACK_PROVIDER: 'off' };
206
+ // WP-4a (D-9): the tick's `sessionId` is the watched worker's ctm_session_id (WP-1's
207
+ // per-worker tick). background-llm.js forwards it into the POST body only when it is a
208
+ // non-empty string, and Wall-E's ledger then keys the row by it -- so the cell's watcher
209
+ // spend is exactly the ledger rows of that cell's own workers.
210
+ const reply = await llm.callBackgroundLlm(messages, {
211
+ task: feature, model, system, maxTokens, timeoutMs, env,
212
+ ...(typeof sessionId === 'string' && sessionId ? { sessionId } : {}),
213
+ });
214
+ const text = typeof reply?.text === 'string' ? reply.text : llm.extractBackgroundLlmText(reply);
215
+ return { ok: true, text };
216
+ } catch (error) {
217
+ return { ok: false, error: String((error && (error.code || error.message)) || 'watch_call_failed').slice(0, 200) };
218
+ }
219
+ };
220
+ }
221
+
151
222
  // D-7: a route waiter is the same record shape in the same `_waiters` registry, under the key
152
223
  // `route:<alias>`. Timer = min(waitMs, remaining hold) when the snapshot has a horizon, else waitMs.
153
224
  // The registration/wake checks read snapshot() (never admit()) so a wait is never counted as a refusal.
@@ -634,6 +705,10 @@ function createWorkerObservability({ getDeps, readWorkerRuntime, getBaseStatus }
634
705
  try { enqueueNotice(receipt); } catch { /* expected: the lead's hook notice is best-effort; walle_worker_wait and the sentinel still report */ }
635
706
  _wakeWaiters(ctmSessionId, 'terminal');
636
707
  auditScopeAtTerminal(ctmSessionId).catch((error) => reportObservationFailure(deps, ctmSessionId, error));
708
+ } else if (kind === 'started') {
709
+ // WP-0 (D-2): the live-worker index is maintained beside the notice queue from the same
710
+ // receipts. Memory only, so a failure here can never affect the receipt's persistence.
711
+ try { markWorkerLive(receipt); } catch { /* expected: the live index is best-effort; the notice queue and walle_worker_wait still report */ }
637
712
  }
638
713
  return receipt;
639
714
  }
@@ -911,8 +986,22 @@ function withIncompleteStreams(runtime, requestBudget) {
911
986
  return { ...requestBudget, incomplete_streams: count, incomplete_stream_ms: ms };
912
987
  }
913
988
 
989
+ // D-1 (session-stops-followups-2026-10-24.html): the terminal statuses a LIVE payload can carry.
990
+ // The DB-row readers in lib/api/sessions.js keep their own copy (_WORKER_TERMINAL_STATUSES):
991
+ // a row's terminal_status is written only at a real exit, so it can never be provisional.
992
+ const WORKER_TERMINAL_STATUSES = new Set(['report_ready', 'failed', 'exited']);
993
+
994
+ /** D-1: a status payload is final when it is terminal AND not a report written while the process
995
+ * is still alive (delegate-worker.js report_provisional). An absent field is final, so DB rows
996
+ * and fixtures keep today's meaning.
997
+ * @param {{status?: string, report_provisional?: boolean}|null|undefined} status
998
+ * @returns {boolean} */
999
+ function isFinalWorkerStatus(status) {
1000
+ return !!status && WORKER_TERMINAL_STATUSES.has(status.status) && status.report_provisional !== true;
1001
+ }
1002
+
914
1003
  function pollAfterMs({ status, requestBudget } = {}) {
915
- if (['report_ready', 'failed', 'exited'].includes(status?.status)
1004
+ if (isFinalWorkerStatus(status)
916
1005
  || (status?.exitCode !== null && status?.exitCode !== undefined)) return 0;
917
1006
  const p50 = requestBudget?.requestDurationP50Ms;
918
1007
  if (!Number.isInteger(p50) || p50 <= 0) return 30000;
@@ -921,6 +1010,8 @@ function pollAfterMs({ status, requestBudget } = {}) {
921
1010
 
922
1011
  module.exports = {
923
1012
  waitForWorkerChange, waitForWorkerChangeHandle, waitForWorkers, notifyWorkerGeneration, notifyWorkerAdvisory, waiterCount, waitForRouteClear, _resetRouteSubscriptionsForTest,
1013
+ createWaitWatcherCallModel,
1014
+ isFinalWorkerStatus,
924
1015
  pollAfterMs,
925
1016
  withIncompleteStreams,
926
1017
  createWorkerObservability,
@@ -940,6 +1031,7 @@ module.exports = {
940
1031
  computeExitFailure,
941
1032
  boundedWorkerText,
942
1033
  isColdDeferredSession,
1034
+ WORKER_REPORT_TEXT_CAP_BYTES,
943
1035
  THROTTLED_WINDOW_MS,
944
1036
  EVENT_RECEIPT_LIMIT,
945
1037
  NO_WRITE_ADVISORY_TURN_THRESHOLD,
@@ -65,9 +65,17 @@ async function stopWorkerSession(ctmSessionId, { mode = 'after_turn', reason = '
65
65
  return refuse('worker_not_owned', 403, id, { message: 'Only the lead that created this worker can stop it.', next_step: nextStepFor({ code: 'worker_not_owned' }) });
66
66
  }
67
67
 
68
- // 4. idempotent on a terminal worker
69
- if (row.terminal_at || TERMINAL_STATUSES.includes(String(row.terminal_status || ''))) {
70
- return refuse('already_terminal', 200, id, { terminal_status: row.terminal_status || '', generation: Number(row.generation || 1), status: 'terminal' });
68
+ // WP-2 (worker-terminal-stamp-durable 2026-10-26 D-2/D-3): a dead process with an unstamped row is repaired first,
69
+ // so the verb below answers the truth (already_terminal), never 'stopping' on a process that no longer exists.
70
+ let repaired = null;
71
+ if (typeof helpers.repairExitedWorkerRow === 'function') {
72
+ try { repaired = helpers.repairExitedWorkerRow(id) || null; } catch { repaired = null; /* expected: a repair that throws leaves the row as read; the stop proceeds on the row's own truth */ }
73
+ if (repaired) { try { row = deps.dbModule.getWorkerSession(id) || row; } catch { /* expected: keep the row already read */ } }
74
+ }
75
+
76
+ // 4. idempotent on a terminal worker -- or on a repaired one whose stamp is still riding the WP-1 retry (D-3: never 'stopping' for a dead process)
77
+ if (repaired || row.terminal_at || TERMINAL_STATUSES.includes(String(row.terminal_status || ''))) {
78
+ return refuse('already_terminal', 200, id, { terminal_status: row.terminal_status || '', generation: Number(row.generation || 1), status: 'terminal', repaired });
71
79
  }
72
80
 
73
81
  // 5. the stop record: journaled by the proxy for a proxied worker, on the row for a proxyless one