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
@@ -265,6 +265,11 @@ function _renderResumeLoadingOverlay(s) {
265
265
  if (!s || !s.container) return false;
266
266
  const overlay = s.container.querySelector('.snapshot-loading-overlay');
267
267
  if (!overlay) return false;
268
+ // WP-4c (§5 OQ-4 = H-B, §8 WP-4c): the resume-blocked card shares this shell class (index.html
269
+ // ~3825); its copy is the CARD's, and a loading label written here is what made a stranded card
270
+ // read as a stuck resume. The card's renderer owns the shell until the server row's
271
+ // resumeBlocked:false clears the store lifecycle in onSessionsList.
272
+ if (_presentationStore.getSessionState(_resumeClientSessionId(s))?.lifecycle === 'resume-blocked') return false;
268
273
  const lbl = overlay.querySelector('.snapshot-loading-text');
269
274
  if (lbl) lbl.textContent = _resumeLoadingText(s);
270
275
  const rs = _resumeStateForSession(s);
@@ -770,6 +775,32 @@ function _resumeBlockedAgentLabel(s, msg) {
770
775
  || 'The agent';
771
776
  }
772
777
 
778
+ // D-14: the session a card is about. The label/cwd expressions are the ones the store rebuild already
779
+ // used (WP-6), lifted here so the identity line and the rebuild cannot drift apart.
780
+ function _resumeBlockedSessionLabel(s) {
781
+ return (s && s.meta && s.meta.title) || (s && s.title) || 'Session';
782
+ }
783
+
784
+ function _resumeBlockedSessionCwd(s) {
785
+ return (s && s.meta && (s.meta.cwd || s.meta.projectPath || s.meta.project_path)) || (s && s.cwd) || '';
786
+ }
787
+
788
+ // D-14/D-12: a folder is shown with the home prefix as `~`, so no absolute path reaches the card.
789
+ function _resumeBlockedFolder(cwd) {
790
+ return String(cwd || '').replace(/^\/(?:Users|home)\/[^/]+/, '~');
791
+ }
792
+
793
+ // D-14: `{label} · {agent} · {folder} · last active {date}` — the folder and the date segments are
794
+ // dropped when CTM has nothing to put in them, never rendered empty.
795
+ function _resumeBlockedIdentity(s, msg) {
796
+ const parts = [_resumeBlockedSessionLabel(s), _resumeBlockedAgentLabel(s, msg)];
797
+ const folder = _resumeBlockedFolder(_resumeBlockedSessionCwd(s));
798
+ if (folder) parts.push(folder);
799
+ const seen = msg && msg.lastSeenAt ? new Date(msg.lastSeenAt) : null;
800
+ if (seen && !Number.isNaN(seen.getTime())) parts.push(`last active ${seen.toLocaleDateString()}`);
801
+ return parts.join(' · ');
802
+ }
803
+
773
804
  // Leave the blocked card for the resuming cover (shared by Retry now and Resume from stored history).
774
805
  function _enterResumingFromBlocked(s) {
775
806
  s._resumeBlocked = false;
@@ -830,8 +861,31 @@ function _formatResumeBlockedBytes(bytes) {
830
861
  return `${rounded} ${units[i]}`;
831
862
  }
832
863
 
864
+ // D-13: ONE "why" sentence, built only from recorded facts, first match wins. It says what CTM knows
865
+ // happened; it never guesses, and it never asks the user to touch a file. Empty when nothing is known.
866
+ function _resumeBlockedWhy(msg, agent) {
867
+ const m = msg || {};
868
+ // ` on <date>` only when the recorded date parses: an older manifest without the key never renders "Invalid Date".
869
+ const on = (x) => { const t = x ? new Date(x) : null; return t && !Number.isNaN(t.getTime()) ? ` on ${t.toLocaleDateString()}` : ''; };
870
+ if (m.trashReason === 'archived') return `CTM's archiver moved it${on(m.trashedAt)}. CTM no longer does this.`;
871
+ if (m.trashReason === 'delete-session') return `It was moved to CTM's trash when this session was deleted${on(m.trashedAt)}.`;
872
+ if (m.localHistory === 'removed') {
873
+ const size = _formatResumeBlockedBytes(m.lastSeenBytes);
874
+ const seen = on(m.lastSeenAt);
875
+ return `CTM last saw it${seen || ' here'}${size ? ` (${size})` : ''}; it was removed outside CTM.`;
876
+ }
877
+ if (m.localHistory === 'never_seen') {
878
+ return 'CTM never saw it on this machine.'
879
+ + (m.code === 'missing_codex_rollout' ? '' : ' Claude Code deletes transcripts older than its retention period, 30 days by default.');
880
+ }
881
+ return '';
882
+ }
883
+
833
884
  // The missing-transcript card is a pure function of the facts on the message (origin,
834
885
  // localHistory, resumeRung, storeTurns); see docs/resume-blocked-card-truth-transcript-gone-2026-10-07.html D-2.
886
+ // WP-3 (D-10..D-15): the RUNG picks the headline (localHistory and the trash reason only pick the "why"
887
+ // sentence), every missing-transcript card opens with an identity line, and no path ever reaches the
888
+ // title or the body — the paths live in the collapsed Details row the renderer builds.
835
889
  function _resumeBlockedCopyForMsg(s, msg) {
836
890
  const code = (msg && msg.code) || '';
837
891
  const base = RESUME_BLOCKED_COPY[code];
@@ -839,8 +893,8 @@ function _resumeBlockedCopyForMsg(s, msg) {
839
893
  if (!_resumeBlockedIsMissingTranscript(msg)) return base;
840
894
  const agent = _resumeBlockedAgentLabel(s, msg);
841
895
  const origin = msg.origin || 'unknown';
842
- const lh = msg.localHistory;
843
896
  const rung = msg.resumeRung || '';
897
+ const identity = _resumeBlockedIdentity(s, msg);
844
898
  // WP-2 (multi-machine R-15): where the transcript lives, keyed on the server's `transcriptState` — a file
845
899
  // deleted HERE and a file living on machine X are different truths and must not share a sentence. An
846
900
  // older server (no transcriptState on the message) keeps the origin-only copy below, unchanged.
@@ -848,6 +902,7 @@ function _resumeBlockedCopyForMsg(s, msg) {
848
902
  if (msg.transcriptState === 'on_other_machine') {
849
903
  return {
850
904
  tone: 'info',
905
+ identity,
851
906
  title: `This session was created on ${host}.`,
852
907
  body: msg.originIsPeer
853
908
  ? `Its ${agent} transcript is there. CTM copies it here once the session has exited there.`
@@ -855,55 +910,41 @@ function _resumeBlockedCopyForMsg(s, msg) {
855
910
  };
856
911
  }
857
912
  if (msg.transcriptState === 'copy_pending') {
858
- return { tone: 'info', title: `Copying this session's transcript from ${host}…`, body: 'Resume will work once the copy completes.' };
913
+ return { tone: 'info', identity, title: `Copying this session's transcript from ${host}…`, body: 'Resume will work once the copy completes.' };
859
914
  }
860
915
  if (msg.transcriptState === 'deleted_after_copy') {
861
916
  return {
862
917
  tone: 'info',
918
+ identity,
863
919
  title: "This session's transcript was deleted from this machine.",
864
920
  body: `It was copied here from ${host}; CTM does not know whether ${host} still has it.`,
865
921
  };
866
922
  }
867
923
  if (!msg.transcriptState && origin === 'other_instance') {
868
- return { tone: 'info', title: 'This session was created on another CTM instance.', body: `Its ${agent} transcript lives there.` };
869
- }
870
- if (lh === 'removed') {
871
- const sizeText = _formatResumeBlockedBytes(msg.lastSeenBytes);
872
- const dateText = msg.lastSeenAt ? new Date(msg.lastSeenAt).toLocaleString() : 'an earlier time';
873
- const seenText = sizeText ? `${sizeText}, last seen ${dateText}` : `last seen ${dateText}`;
874
- return {
875
- tone: 'info',
876
- title: "This session's transcript was deleted from this machine.",
877
- body: `CTM read it here (${seenText}) and the file is gone, so ${agent} can't resume it natively.`,
878
- };
924
+ return { tone: 'info', identity, title: 'This session was created on another CTM instance.', body: `Its ${agent} transcript lives there.` };
879
925
  }
926
+ const why = _resumeBlockedWhy(msg, agent);
927
+ const st = msg.storeTurns || { human: 0, total: 0 };
880
928
  if (rung === 'restore' || rung === 'trash') {
881
- return {
882
- tone: 'info',
883
- title: "This session's transcript file is missing.",
884
- body: rung === 'restore' ? 'An archived copy exists here.' : "A copy is in CTM's transcript trash.",
885
- };
929
+ let body = `Restore puts it back where ${agent} looks for it and resumes the session.`;
930
+ if (why) body += ` ${why}`;
931
+ if (msg.restoreError && msg.restoreError !== 'not_implemented') body += ` The last restore did not finish (${msg.restoreError}).`;
932
+ return { tone: 'info', identity, title: `${agent} can't find this session's transcript, but CTM kept a copy.`, body };
886
933
  }
887
934
  if (rung === 'store') {
888
- return { tone: 'info', title: "This session's transcript file is missing.", body: `${agent} can't resume it natively.` };
935
+ const cc = String((msg.coverage && msg.coverage.storeComplete) || '');
936
+ const coverage = cc === 'yes' || cc === 'no' ? `, ${_resumeStoreCompleteText(cc)}` : '';
937
+ let body = `CTM saved ${Number(st.human) || 0} prompts (${Number(st.total) || 0} turns) of the conversation; ${agent} can start from them${coverage}.`;
938
+ if (why) body += ` ${why}`;
939
+ return { tone: 'info', identity, title: `${agent} can't find this session's transcript, and CTM has no copy of the file.`, body };
889
940
  }
890
941
  if (rung === '') {
891
- // Unknown rung (the ladder could not answer): never the "nothing to resume" copy. Keep the code's title and say what is unknown.
892
- const removed = lh === 'removed';
893
- const sizeText = _formatResumeBlockedBytes(msg.lastSeenBytes);
894
- const dateText = msg.lastSeenAt ? new Date(msg.lastSeenAt).toLocaleString() : 'an earlier time';
895
- return {
896
- tone: 'info',
897
- title: removed ? "This session's transcript was deleted from this machine." : "This session's transcript is gone from this machine.",
898
- body: 'CTM could not check what it has stored for this session.' + (removed ? ` CTM read it here (${sizeText ? `${sizeText}, last seen ${dateText}` : `last seen ${dateText}`}) and the file is gone, so ${agent} can't resume it natively.` : ''),
899
- };
942
+ // Unknown rung (the ladder could not answer): never the "nothing to resume" copy.
943
+ return { tone: 'info', identity, title: "CTM couldn't check this session's history.", body: 'The check did not finish.' };
900
944
  }
901
- const retention = code === 'missing_codex_rollout' ? '' : ' (Claude Code deletes transcripts older than its retention period, 30 days by default)';
902
- return {
903
- tone: 'info',
904
- title: "This session's transcript is gone from this machine.",
905
- body: `The file was removed before CTM stored any of its prompts${retention}. There is nothing to resume; start a fresh session in the same folder.`,
906
- };
945
+ if (rung === 'native') return { tone: 'info', identity, title: "This session's transcript is back.", body: '' };
946
+ // rung 'fresh' (and any rung CTM does not recognise): nothing was stored, so there is nothing to resume.
947
+ return { tone: 'info', identity, title: `${agent} can't find this session's transcript, and CTM saved none of it.`, body: why || 'There is nothing to resume.' };
907
948
  }
908
949
 
909
950
  // Session-transcript-storage WP-6 (D-6, D-7, D-13): the missing-transcript card names the rung the
@@ -930,7 +971,7 @@ function _resumeBlockedStoredLine(msg) {
930
971
  const tail = coverage && Number.isFinite(Number(coverage.turnsTotal))
931
972
  ? `, ${_resumeStoreCompleteText(coverage.storeComplete)}.`
932
973
  : '.';
933
- return `CTM has ${Number(st.human) || 0} prompts (${total} turns) of this conversation${tail} Open Conversation shows CTM's copy.`;
974
+ return `CTM has ${Number(st.human) || 0} prompts (${total} turns) of this conversation${tail}`;
934
975
  }
935
976
 
936
977
  function _resumeCheckedRungWord(rung) {
@@ -944,15 +985,55 @@ function _resumeCheckedLine(checkedAt, rung) {
944
985
  return `Checked ${time}: ${_resumeCheckedRungWord(rung)}`;
945
986
  }
946
987
 
988
+ // D-12: the file facts live in ONE collapsed Details row under the buttons, never in the card's title,
989
+ // body or identity line (a path is not something the user should have to read, let alone act on). The
990
+ // row is rendered only when it would hold at least one value; lines are written with textContent only.
991
+ function _resumeBlockedDetailLines(msg, checked) {
992
+ const m = msg || {};
993
+ const rung = String(m.resumeRung || '');
994
+ const lines = [];
995
+ const transcript = String(m.originalPath || m.storedPath || '');
996
+ if (transcript) lines.push({ key: 'transcript', text: `Transcript: ${transcript}` });
997
+ const copyPath = String(m.trashPath || (rung === 'restore' ? 'CTM archive' : ''));
998
+ if (copyPath) lines.push({ key: 'copy', text: `Copy: ${copyPath}` });
999
+ if (checked) lines.push({ key: 'checked', text: _resumeCheckedLine(m.checkedAt, rung) });
1000
+ return lines;
1001
+ }
1002
+
1003
+ function _renderResumeBlockedDetails(overlay, msg, checked) {
1004
+ const lines = _resumeBlockedIsMissingTranscript(msg) ? _resumeBlockedDetailLines(msg, checked) : [];
1005
+ let details = overlay.querySelector('.resume-blocked-details');
1006
+ if (!lines.length) {
1007
+ if (details) details.remove();
1008
+ return null;
1009
+ }
1010
+ if (!details) {
1011
+ details = document.createElement('details');
1012
+ details.className = 'resume-blocked-details';
1013
+ const summary = document.createElement('summary');
1014
+ summary.textContent = 'Details';
1015
+ details.appendChild(summary);
1016
+ overlay.appendChild(details);
1017
+ }
1018
+ for (const child of Array.from(details.children)) if (String(child.tagName || '').toLowerCase() !== 'summary') child.remove(); // HTML tagName is upper-case
1019
+ for (const line of lines) {
1020
+ const el = document.createElement('div');
1021
+ el.className = `resume-blocked-detail-line resume-blocked-detail-${line.key}`;
1022
+ el.textContent = line.text;
1023
+ details.appendChild(el);
1024
+ }
1025
+ return details;
1026
+ }
1027
+
947
1028
  function _resumeBlockedBodyWithStore(body, msg) {
948
1029
  // D-2/D-4: the stored line rides on any card carrying store-turn facts, not only the missing codes.
949
1030
  // A card with no ladder facts (storeTurns absent) keeps its body exactly as the code's copy wrote it.
950
1031
  if (!msg || !msg.storeTurns || msg.storeTurns.known === undefined) return body;
1032
+ // WP-3 (D-10/D-15): on a missing-transcript card the stored coverage is the RUNG's own sentence (the
1033
+ // card copy already wrote it), so nothing is appended here. No card ever names a path or tells the user
1034
+ // to move a file: paths live in the Details row, and "Restore and resume" does the restore itself.
1035
+ if (_resumeBlockedIsMissingTranscript(msg)) return body;
951
1036
  let text = [body, _resumeBlockedStoredLine(msg)].filter(Boolean).join(' ');
952
- if (msg.trashPath) {
953
- text += ` A copy is in CTM's transcript trash at ${msg.trashPath} \u2014 move it back to `
954
- + `${msg.originalPath || 'its original location'} and press Check again.`;
955
- }
956
1037
  if (msg.restoreError && msg.restoreError !== 'not_implemented') text += '\nRestore failed: ' + msg.restoreError;
957
1038
  return text;
958
1039
  }
@@ -993,8 +1074,8 @@ async function _resumeBlockedFromStore(s, msg, btn) {
993
1074
  const sessionId = _resumeClientSessionId(s);
994
1075
  if (!s || !sessionId) return;
995
1076
  const provider = _clientNormalizeAgentType(msg.agentType || _clientAgentTypeForSession(s)) === 'codex' ? 'codex' : 'claude';
996
- const cwd = (s.meta && (s.meta.cwd || s.meta.projectPath || s.meta.project_path)) || s.cwd || '';
997
- const label = (s.meta && s.meta.title) || s.title || 'Session';
1077
+ const cwd = _resumeBlockedSessionCwd(s);
1078
+ const label = _resumeBlockedSessionLabel(s);
998
1079
  const idle = btn.textContent;
999
1080
  btn.disabled = true;
1000
1081
  btn.textContent = 'Starting…';
@@ -1053,43 +1134,50 @@ function _resumeBlockedButton(actions, label, onclick, primary) {
1053
1134
  return btn;
1054
1135
  }
1055
1136
 
1137
+ // D-11: the third recovery option is a text link, not a fourth equal button. Same handler map.
1138
+ function _resumeBlockedLinkButton(actions, label, onclick) {
1139
+ const btn = document.createElement('button');
1140
+ btn.className = 'resume-blocked-link';
1141
+ btn.textContent = label;
1142
+ btn.onclick = () => onclick(btn);
1143
+ actions.appendChild(btn);
1144
+ return btn;
1145
+ }
1146
+
1056
1147
  // Buttons of the card as data: a pure function of the message facts, no DOM (D-3). Missing-transcript
1057
1148
  // cards offer only the actions their rung can honor; D-7 makes the store primary for an oversized Codex rollout.
1058
- // D-4: "Resume from stored history" appears on ANY blocked card whose store holds a human prompt, as a
1059
- // secondary action after the code's own primary.
1149
+ // D-4 (session-exit-close-truth): the store rebuild asks the ladder whether the native transcript is
1150
+ // gone. It is offered ONLY on a missing-transcript code whose rung is store, fresh or '' (unknown), or
1151
+ // when offerStoreFirst is true -- never on a non-missing code and never on rung native, restore or trash.
1060
1152
  function _resumeBlockedActionsFor(msg) {
1061
1153
  const missing = _resumeBlockedIsMissingTranscript(msg);
1154
+ const m = msg || {};
1062
1155
  const fresh = { id: 'fresh', label: 'Start fresh here', primary: false };
1156
+ // D-11: at most two buttons and one text link, exactly one button primary. `link: true` marks the
1157
+ // action the renderer draws AFTER the buttons as a text-styled link (same handler map).
1063
1158
  const conversation = { id: 'conversation', label: 'Open Conversation', primary: false };
1159
+ const conversationLink = { ...conversation, link: true };
1064
1160
  const store = (primary) => ({ id: 'store', label: 'Resume from stored history', primary });
1065
- const st = msg.storeTurns || { human: 0, total: 0 };
1066
- const human = Number(st.human) > 0;
1067
- if (!missing) return [{ id: 'retry', label: 'Retry now', primary: true }, ...(human ? [store(false)] : []), fresh, conversation];
1068
- if (msg.offerStoreFirst === true) return [store(true), { id: 'retry', label: 'Retry now', primary: false }, fresh, conversation];
1069
- const rung = String(msg.resumeRung || '');
1070
- const hasStore = Number(st.total) > 0 || st.known === false;
1071
1161
  const check = { id: 'check', label: 'Check again', primary: false };
1162
+ const st = m.storeTurns || { human: 0, total: 0 };
1163
+ const hasStore = Number(st.total) > 0;
1164
+ // Non-missing codes keep today's list exactly.
1165
+ if (!missing) return [{ id: 'retry', label: 'Retry now', primary: true }, fresh, conversation];
1166
+ if (m.offerStoreFirst === true) return [store(true), { id: 'retry', label: 'Retry now', primary: false }, conversationLink];
1167
+ const rung = String(m.resumeRung || '');
1072
1168
  if (rung === 'restore' || rung === 'trash') {
1073
- const list = [{ id: 'restore', label: 'Restore and resume', primary: true }];
1074
- if (Number(st.human) > 0) list.push(store(false));
1075
- list.push(fresh);
1076
- if (hasStore) list.push(conversation);
1077
- list.push(check);
1169
+ // The rung can put the original back itself, so it leads and "Check again" is gone (D-11/I-5).
1170
+ const list = [{ id: 'restore', label: 'Restore and resume', primary: true }, fresh];
1171
+ if (hasStore) list.push(conversationLink);
1078
1172
  return list;
1079
1173
  }
1080
- if (rung === 'store') return [store(true), fresh, conversation, check];
1081
- if (rung === '') { // unknown: the answer is a re-check, never "Start fresh" as the lead action
1082
- const unknown = [{ ...check, primary: true }];
1083
- if (Number(st.total) > 0) unknown.push(conversation);
1084
- if (human) unknown.push(store(false));
1085
- unknown.push(fresh);
1086
- return unknown;
1174
+ if (rung === 'store') return [store(true), fresh, conversationLink];
1175
+ if (rung === '') { // unknown: the answer is a re-check -- the check itself is what failed
1176
+ return hasStore ? [{ ...check, primary: true }, fresh, conversationLink] : [{ ...check, primary: true }, fresh];
1087
1177
  }
1088
- const list = [{ ...fresh, primary: true }];
1089
- if (human) list.push(store(false));
1090
- if (hasStore) list.push(conversation);
1091
- list.push(check);
1092
- return list;
1178
+ // rung native with a missing transcript: the file is back, so resuming leads and the store is gone.
1179
+ if (rung === 'native') return [{ id: 'retry', label: 'Resume', primary: true }, fresh];
1180
+ return [{ ...fresh, primary: true }];
1093
1181
  }
1094
1182
 
1095
1183
  function _appendResumeBlockedActions(actions, s, msg) {
@@ -1103,8 +1191,24 @@ function _appendResumeBlockedActions(actions, s, msg) {
1103
1191
  conversation: () => _openBlockedResumeConversation(s),
1104
1192
  check: (b) => _checkBlockedResumeAgain(s, b),
1105
1193
  };
1106
- for (const action of _resumeBlockedActionsFor(msg)) {
1107
- _resumeBlockedButton(actions, action.label, handlers[action.id], action.primary);
1194
+ const list = _resumeBlockedActionsFor(msg);
1195
+ for (const action of list.filter((a) => !a.link)) {
1196
+ const handler = handlers[action.id];
1197
+ // WP-0 (S-9/D-8): every click on a blocked card's button records which button it was before the
1198
+ // handler runs, so a card's outcome is attributable to the action the user actually pressed.
1199
+ const traced = typeof handler !== 'function' ? handler : (...args) => {
1200
+ send({ type: 'client-diag', id: _resumeClientSessionId(s), event: 'resume-blocked-action', details: { action: action.id, code: String(msg.code || '') } });
1201
+ return handler(...args);
1202
+ };
1203
+ _resumeBlockedButton(actions, action.label, traced, action.primary);
1204
+ }
1205
+ for (const action of list.filter((a) => a.link)) {
1206
+ const handler = handlers[action.id];
1207
+ const traced = typeof handler !== 'function' ? handler : (...args) => {
1208
+ send({ type: 'client-diag', id: _resumeClientSessionId(s), event: 'resume-blocked-action', details: { action: action.id, code: String(msg.code || '') } });
1209
+ return handler(...args);
1210
+ };
1211
+ _resumeBlockedLinkButton(actions, action.label, traced);
1108
1212
  }
1109
1213
  if (_resumeBlockedIsMissingTranscript(msg) && msg.restoreError === 'not_implemented') {
1110
1214
  _resumeBlockedNote(s, 'Restore lands with the archive work');
@@ -1144,9 +1248,15 @@ function onResumeRetryNoop(msg) {
1144
1248
  if (!s || !s._resumeCheckPending) return;
1145
1249
  _clearResumeCheckPending(s);
1146
1250
  const overlay = s.container && s.container.querySelector('.snapshot-loading-overlay');
1147
- const lbl = overlay && overlay.querySelector('.snapshot-loading-text');
1148
- const line = _resumeCheckedLine(msg.checkedAt, (s._resumeBlockedFacts && s._resumeBlockedFacts.resumeRung) || '');
1149
- if (lbl) lbl.textContent = String(lbl.textContent || '').replace(/\nChecked [^\n]*$/, '') + '\n' + line;
1251
+ // D-12: the stamp goes in the Details row (written once, replaced in place) -- never into the body.
1252
+ const rendered = s._resumeBlockedMsg || msg;
1253
+ if (overlay) {
1254
+ _renderResumeBlockedDetails(overlay, {
1255
+ ...rendered,
1256
+ resumeRung: rendered.resumeRung || s._resumeBlockedRung || '',
1257
+ checkedAt: msg.checkedAt,
1258
+ }, true);
1259
+ }
1150
1260
  const actions = overlay && overlay.querySelector('.snapshot-loading-actions');
1151
1261
  for (const b of (actions ? Array.from(actions.children) : [])) if (b.textContent === 'Checking\u2026') b.textContent = 'Check again';
1152
1262
  }
@@ -1175,6 +1285,16 @@ function _renderResumeBlockedOverlay(s, msg, opts) {
1175
1285
  clearTimeout(s._loadingOverlayTimer);
1176
1286
  s._loadingOverlayTimer = null;
1177
1287
  _projectSessionLifecycle(s, 'resume-blocked'); // the renderer mounts (or keeps) the shell
1288
+ // WP-0 (S-9/D-8): the card the user actually saw is recorded once per render, with the action list it
1289
+ // offered -- the server side alone cannot say which of the three cards for one code was painted.
1290
+ send({
1291
+ type: 'client-diag', id: _resumeClientSessionId(s), event: 'resume-blocked-card',
1292
+ details: {
1293
+ code: String(msg.code || ''), rung: String(msg.resumeRung || ''),
1294
+ storeHuman: Number(msg.storeTurns && msg.storeTurns.human) || 0,
1295
+ actions: _resumeBlockedActionsFor(msg).map((a) => a.id).join(','),
1296
+ },
1297
+ });
1178
1298
  const overlay = s.container.querySelector('.snapshot-loading-overlay');
1179
1299
  // Off the terminal the renderer withholds the element on purpose: the rail line represents the card.
1180
1300
  if (!overlay) return typeof _terminalLifecycleOverlaySuppressed === 'function' && _terminalLifecycleOverlaySuppressed(s);
@@ -1189,6 +1309,18 @@ function _renderResumeBlockedOverlay(s, msg, opts) {
1189
1309
  overlay.insertBefore(lbl, overlay.firstChild);
1190
1310
  }
1191
1311
  lbl.classList.add('is-static');
1312
+ // D-14: every missing-transcript card names its subject first, in its own dim line above the headline.
1313
+ let ident = overlay.querySelector('.resume-blocked-identity');
1314
+ if (copy.identity) {
1315
+ if (!ident) {
1316
+ ident = document.createElement('div');
1317
+ ident.className = 'resume-blocked-identity';
1318
+ overlay.insertBefore(ident, lbl);
1319
+ }
1320
+ ident.textContent = copy.identity;
1321
+ } else if (ident) {
1322
+ ident.remove();
1323
+ }
1192
1324
  const agentLabel = _resumeBlockedAgentLabel(s, msg);
1193
1325
  const attempts = Math.max(0, Number(msg?.attempts || 0));
1194
1326
  let body = _resumeBlockedBodyWithStore(copy.body, msg);
@@ -1198,10 +1330,11 @@ function _renderResumeBlockedOverlay(s, msg, opts) {
1198
1330
  body = `${agentLabel} is producing output but never painted its screen.`;
1199
1331
  }
1200
1332
  const remount = !!(opts && opts.remount);
1333
+ let checked = false;
1201
1334
  if (!remount && s._resumeCheckPending && _resumeBlockedIsMissingTranscript(msg)) {
1202
- // A Check again was in flight: this re-render is its answer.
1335
+ // A Check again was in flight: this re-render is its answer. D-12: the stamp lives in Details.
1203
1336
  _clearResumeCheckPending(s);
1204
- body += '\n' + _resumeCheckedLine(msg.checkedAt, msg.resumeRung);
1337
+ checked = true;
1205
1338
  }
1206
1339
  lbl.textContent = copy.title + (body ? '\n' + body : '');
1207
1340
  let detail = overlay.querySelector('.snapshot-loading-detail');
@@ -1224,10 +1357,14 @@ function _renderResumeBlockedOverlay(s, msg, opts) {
1224
1357
  }
1225
1358
  actions.innerHTML = '';
1226
1359
  _appendResumeBlockedActions(actions, s, msg);
1360
+ if (typeof resumeBlockedOpenOnMachineAction === 'function') resumeBlockedOpenOnMachineAction(msg, actions, s); // hub WP-5
1227
1361
  if (remount && s._resumeCheckPending) {
1228
1362
  // A Check again is still in flight: a surface round trip is not its answer.
1229
1363
  for (const b of Array.from(actions.children)) if (b.textContent === 'Check again') b.textContent = 'Checking\u2026';
1230
1364
  }
1365
+ // D-12: the file facts are the LAST thing on the card, under the buttons -- a collapsed disclosure, not
1366
+ // something the user has to read or act on. Rendered after the actions so the DOM order is the visual order.
1367
+ _renderResumeBlockedDetails(overlay, msg, checked);
1231
1368
  return true;
1232
1369
  }
1233
1370
 
@@ -222,12 +222,23 @@ function _fitTerminalPreservingViewport(s, sessionId, opts) {
222
222
  return;
223
223
  }
224
224
  if (!opts.force && _terminalInputDisplayHoldActive(s)) {
225
- clearTimeout(s._terminalInputDeferredFitTimer);
226
- s._terminalInputDeferredFitTimer = setTimeout(() => {
227
- s._terminalInputDeferredFitTimer = null;
228
- _fitTerminalPreservingViewport(s, sessionId, opts);
229
- }, TERMINAL_ACTIVE_INPUT_HOLD_MS);
230
- return;
225
+ // The input display hold keeps a focused draft's repaint stable; it does not pin a
226
+ // grid the pane no longer has. A local fit proposal whose rows are strictly below the
227
+ // current term.rows is positive evidence the rendered rows are already clipped, so that
228
+ // case continues through the normal fit pipeline below (which still owes its own
229
+ // geometry acceptance). Optional, equal-row, row-growth and invalid proposals keep the
230
+ // old defer-and-rearm behavior untouched.
231
+ const proposal = _terminalLocalFitProposal(s);
232
+ const clippedRows = !!proposal && Number.isFinite(proposal.cols) && proposal.cols > 0
233
+ && Number.isFinite(proposal.rows) && proposal.rows > 0 && proposal.rows < s.term.rows;
234
+ if (!clippedRows) {
235
+ clearTimeout(s._terminalInputDeferredFitTimer);
236
+ s._terminalInputDeferredFitTimer = setTimeout(() => {
237
+ s._terminalInputDeferredFitTimer = null;
238
+ _fitTerminalPreservingViewport(s, sessionId, opts);
239
+ }, TERMINAL_ACTIVE_INPUT_HOLD_MS);
240
+ return;
241
+ }
231
242
  }
232
243
  if (!opts.force && _claudeTuiResizeHoldActive(s)) {
233
244
  _scheduleClaudeDeferredFit(s, sessionId, opts, opts.resizeReason || 'fit-preserving-viewport');
@@ -600,6 +611,8 @@ function _launchResumedAgentSession(provider, agentSessionId, cwd, label, ctmSes
600
611
  args: spec.args,
601
612
  cwd: cwd || undefined,
602
613
  label: (label || 'Resumed session').replace(/^Resume:\s*/i, '').slice(0, 60),
614
+ // D-13: the desktop fork's name is composed by CTM (or echoed from the row), never user intent.
615
+ titleIntent: 'default',
603
616
  });
604
617
  }
605
618
 
@@ -24,3 +24,89 @@ function _focusModalPanel(modalEl) {
24
24
  modalEl.focus();
25
25
  return true;
26
26
  }
27
+
28
+ // WP-1 (session-exit-close-truth-2026-10-24.html §5 D-3, §8 WP-1): this table moved here verbatim
29
+ // from public/index.html, which is exactly at its file-size ceiling. The freed lines are intentional
30
+ // headroom for WP-3/WP-4. Loaded as a classic script before the dashboard's inline script, so the
31
+ // top-level `const` is a shared global; every reader uses it inside a function that runs later.
32
+ // WI-5: cause-specific, non-alarming copy for the enumerated deterministic resume_blocked reason
33
+ // codes the server can send (see server.js markSessionResumeBlocked / _notifyResumePreflightFailure).
34
+ // Branch ONLY on `msg.code` here — never on message text — so this stays correct even if the
35
+ // human-readable message wording changes server-side. An unrecognized code (contract drift, or a
36
+ // code we haven't special-cased yet) falls back to the same honest red failure banner
37
+ // onSessionRestoreFailed already renders, rather than inventing false calm.
38
+ const RESUME_BLOCKED_COPY = {
39
+ missing_claude_transcript: {
40
+ tone: 'info',
41
+ title: "This session's transcript is gone from this machine.",
42
+ body: 'The file was removed before CTM stored any of its prompts (Claude Code deletes transcripts older than '
43
+ + 'its retention period, 30 days by default). There is nothing to resume; start a fresh session in the same folder.',
44
+ },
45
+ missing_codex_rollout: {
46
+ tone: 'info',
47
+ title: "This session's transcript is gone from this machine.",
48
+ body: 'The file was removed before CTM stored any of its prompts. There is nothing to resume; '
49
+ + 'start a fresh session in the same folder.',
50
+ },
51
+ agent_cli_not_found: {
52
+ tone: 'warn',
53
+ title: 'The agent CLI isn’t available on this host.',
54
+ body: 'CTM could not find the CLI needed to resume this session on this machine. '
55
+ + 'Install it (or fix PATH), then reload — or start fresh here.',
56
+ },
57
+ resume_exit_loop: {
58
+ tone: 'warn',
59
+ title: 'This session could not finish resuming.',
60
+ body: '',
61
+ },
62
+ // WP-1 (D-3): the create path's defensive name for a resume that never became ready but carried
63
+ // no explicit code (lib/session-create/response.js defer -> deferBlockForExit rule 1). Same truth
64
+ // as the resume_* entries around it; without this entry the enforcement test in
65
+ // tests/restore-failed-presentation.test.js found the block had no renderer at all.
66
+ resume_failed: {
67
+ tone: 'warn',
68
+ title: 'This session could not finish resuming.',
69
+ body: '',
70
+ },
71
+ resume_no_frame: {
72
+ tone: 'warn',
73
+ title: 'The session never painted a usable screen.',
74
+ body: '',
75
+ },
76
+ resume_no_output: {
77
+ tone: 'warn',
78
+ title: 'The agent did not respond while resuming.',
79
+ body: 'It may be waiting on authentication, blocked by configuration, or unavailable on this host.',
80
+ },
81
+ // Mixed-version compatibility: current servers normalize this internal
82
+ // lifecycle reason to resume_no_output before it reaches the browser.
83
+ no_provider_output: {
84
+ tone: 'warn',
85
+ title: 'The agent did not respond while resuming.',
86
+ body: 'It may be waiting on authentication, blocked by configuration, or unavailable on this host.',
87
+ },
88
+ // Compatibility for a server from before the resume lifecycle became provider-neutral.
89
+ codex_no_output: {
90
+ tone: 'warn',
91
+ title: 'Codex did not respond while resuming.',
92
+ body: 'It may be waiting on authentication, blocked by configuration, or unavailable on this host.',
93
+ },
94
+ restore_retries_exhausted: {
95
+ tone: 'warn',
96
+ title: 'CTM could not prepare this session after several tries.',
97
+ body: 'The launch planner kept timing out. Retry now, or start fresh here.',
98
+ },
99
+ // WP-1 (D-3, S-12): the two exit-truth codes `deferBlockForExit` (lib/resume-blocked-wire.js) can
100
+ // return. Without these the create-path defer fell through to the retryable "Preparing session…"
101
+ // cover with nothing the user could resolve.
102
+ session_crash_loop: {
103
+ tone: 'warn',
104
+ title: 'This session kept exiting and CTM stopped restarting it.',
105
+ body: 'The agent exited with an error after automatic restarts. Retry now, or start fresh here.',
106
+ },
107
+ session_crashed: {
108
+ tone: 'warn',
109
+ title: 'This session exited with an error.',
110
+ body: 'Retry now, or start fresh here.',
111
+ },
112
+ };
@@ -648,7 +648,7 @@
648
648
  errorCode: String((error && error.code) || message || 'unknown'),
649
649
  httpStatus,
650
650
  ms: Date.now() - (Number(startedAt) || Date.now()),
651
- attempts: 1,
651
+ attempts: 1, reason: String((error && (error.reason || error.coverageReason)) || '').slice(0, 64) || null, // source-missing diagnosis: the SERVER's own cause (`coverage.reason`/`meta.reason`, e.g. 'no-source'/'source-unreadable'), which err.reason does not carry for the raw-Codex lane. On this line (not its own): the file is at its size ceiling.
652
652
  });
653
653
  } catch { /* expected: the entry-failure beacon is best-effort and must never block the failure path */ }
654
654
  };