create-walle 0.10.2 → 0.10.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (488) hide show
  1. package/README.md +3 -1
  2. package/lib/release-schema.json +2 -2
  3. package/lib/runtime-closure-policy.json +8 -7
  4. package/package.json +1 -1
  5. package/template/coding-task-manager/api-prompts.js +45 -7
  6. package/template/coding-task-manager/bin/claude-hook.js +55 -1
  7. package/template/coding-task-manager/bin/ctm-relay-client.js +35 -12
  8. package/template/coding-task-manager/bin/land.js +2069 -0
  9. package/template/coding-task-manager/bin/memory-census.js +4 -2
  10. package/template/coding-task-manager/bin/memory-sync-verify.js +151 -0
  11. package/template/coding-task-manager/bin/probe-refusal-scope.js +530 -0
  12. package/template/coding-task-manager/db.js +156 -32
  13. package/template/coding-task-manager/lib/acp/acp-client.js +383 -0
  14. package/template/coding-task-manager/lib/acp/dsh-context-truth.js +176 -0
  15. package/template/coding-task-manager/lib/acp/dsh-launch-model.js +302 -0
  16. package/template/coding-task-manager/lib/acp/dsh-mcp-servers.js +194 -0
  17. package/template/coding-task-manager/lib/acp/dsh-prompt-lane.js +616 -0
  18. package/template/coding-task-manager/lib/acp/dsh-route-patch.js +308 -0
  19. package/template/coding-task-manager/lib/acp/dsh-session-runtime.js +522 -0
  20. package/template/coding-task-manager/lib/acp/dsh-transcript.js +395 -0
  21. package/template/coding-task-manager/lib/acp/dsh-usage-log.js +162 -0
  22. package/template/coding-task-manager/lib/acp/ndjson-stdio-transport.js +225 -0
  23. package/template/coding-task-manager/lib/adopted-host-redial.js +47 -11
  24. package/template/coding-task-manager/lib/adopted-launch-environment.js +123 -0
  25. package/template/coding-task-manager/lib/agent-auto-update-runtime.js +0 -3
  26. package/template/coding-task-manager/lib/agent-auto-update.js +26 -10
  27. package/template/coding-task-manager/lib/agent-bundle/build.js +657 -0
  28. package/template/coding-task-manager/lib/agent-bundle/codex-install.js +558 -0
  29. package/template/coding-task-manager/lib/agent-bundle/drift.js +120 -0
  30. package/template/coding-task-manager/lib/agent-bundle/gc.js +522 -0
  31. package/template/coding-task-manager/lib/agent-bundle/inventory.js +346 -0
  32. package/template/coding-task-manager/lib/agent-bundle/push.js +717 -0
  33. package/template/coding-task-manager/lib/agent-bundle/skill-transfer.js +724 -0
  34. package/template/coding-task-manager/lib/agent-bundle/spawn.js +119 -0
  35. package/template/coding-task-manager/lib/agent-bundle/store.js +568 -0
  36. package/template/coding-task-manager/lib/agent-capabilities.js +142 -12
  37. package/template/coding-task-manager/lib/agent-cli-cache.js +30 -5
  38. package/template/coding-task-manager/lib/agent-definitions.js +19 -0
  39. package/template/coding-task-manager/lib/agent-hooks-codex-toml.js +28 -16
  40. package/template/coding-task-manager/lib/agent-hooks-codex-workspace.js +9 -1
  41. package/template/coding-task-manager/lib/agent-hooks-installer.js +23 -8
  42. package/template/coding-task-manager/lib/agent-hooks-prune.js +37 -11
  43. package/template/coding-task-manager/lib/agent-hooks-shim.js +105 -0
  44. package/template/coding-task-manager/lib/agent-presets.js +84 -1
  45. package/template/coding-task-manager/lib/agent-upgrade-jobs.js +12 -1
  46. package/template/coding-task-manager/lib/agent-version-service.js +74 -11
  47. package/template/coding-task-manager/lib/all-sessions-query.js +6 -2
  48. package/template/coding-task-manager/lib/api/agent-bundles.js +436 -0
  49. package/template/coding-task-manager/lib/api/backup-retention.js +83 -0
  50. package/template/coding-task-manager/lib/api/conversation-sync-routes.js +124 -0
  51. package/template/coding-task-manager/lib/api/diagnostics.js +549 -69
  52. package/template/coding-task-manager/lib/api/fleet.js +118 -0
  53. package/template/coding-task-manager/lib/api/hub-bridge.js +243 -0
  54. package/template/coding-task-manager/lib/api/ingress.js +9 -1
  55. package/template/coding-task-manager/lib/api/landing.js +403 -0
  56. package/template/coding-task-manager/lib/api/machines.js +691 -2
  57. package/template/coding-task-manager/lib/api/memory-sync-routes.js +335 -0
  58. package/template/coding-task-manager/lib/api/misc-ops.js +8 -3
  59. package/template/coding-task-manager/lib/api/remote-sessions.js +1326 -0
  60. package/template/coding-task-manager/lib/api/session-attach.js +623 -0
  61. package/template/coding-task-manager/lib/api/session-messages.js +1384 -0
  62. package/template/coding-task-manager/lib/api/sessions.js +77 -51
  63. package/template/coding-task-manager/lib/api/setup-network.js +544 -16
  64. package/template/coding-task-manager/lib/api/setup-wizard-handlers.js +884 -0
  65. package/template/coding-task-manager/lib/api/setup.js +829 -10
  66. package/template/coding-task-manager/lib/api/walle-bridge.js +9 -1
  67. package/template/coding-task-manager/lib/attachment-reconciler-wiring.js +5 -2
  68. package/template/coding-task-manager/lib/auth-rate-limit.js +23 -0
  69. package/template/coding-task-manager/lib/auth-rules.js +292 -21
  70. package/template/coding-task-manager/lib/background-stop.js +369 -0
  71. package/template/coding-task-manager/lib/best-of-attempts.js +4 -1
  72. package/template/coding-task-manager/lib/best-of-launch-plan.js +52 -0
  73. package/template/coding-task-manager/lib/box-keep-awake.js +89 -0
  74. package/template/coding-task-manager/lib/broadcast-reason-stats.js +152 -0
  75. package/template/coding-task-manager/lib/broadcast-serialize.js +5 -0
  76. package/template/coding-task-manager/lib/build-info.js +4 -2
  77. package/template/coding-task-manager/lib/claude-model-truth.js +13 -2
  78. package/template/coding-task-manager/lib/claude-transcript-terminal-renderer.js +17 -13
  79. package/template/coding-task-manager/lib/client-diag-handler.js +64 -2
  80. package/template/coding-task-manager/lib/codex-app-server-client.js +14 -16
  81. package/template/coding-task-manager/lib/codex-configuration-history.js +55 -0
  82. package/template/coding-task-manager/lib/codex-harness-qualification.js +43 -20
  83. package/template/coding-task-manager/lib/codex-rollout-history.js +743 -0
  84. package/template/coding-task-manager/lib/coding-agent-models.js +27 -12
  85. package/template/coding-task-manager/lib/coding-cli-skill-installer.js +44 -14
  86. package/template/coding-task-manager/lib/coding-session-model-adapters.js +43 -2
  87. package/template/coding-task-manager/lib/coding-session-model-owner.js +1 -1
  88. package/template/coding-task-manager/lib/coding-session-model-public.js +7 -1
  89. package/template/coding-task-manager/lib/coding-session-model-runtime.js +23 -4
  90. package/template/coding-task-manager/lib/coding-session-model-scan.js +31 -0
  91. package/template/coding-task-manager/lib/coding-session-model-state.js +7 -0
  92. package/template/coding-task-manager/lib/conversation-freshness-diagnostics.js +280 -0
  93. package/template/coding-task-manager/lib/conversation-item-clock.js +261 -0
  94. package/template/coding-task-manager/lib/conversation-source-registry.js +5 -0
  95. package/template/coding-task-manager/lib/conversation-store-v2.js +44 -0
  96. package/template/coding-task-manager/lib/conversation-sync.js +355 -0
  97. package/template/coding-task-manager/lib/create/resolve-launch-params.js +96 -12
  98. package/template/coding-task-manager/lib/create/worker-role-profile.js +4 -4
  99. package/template/coding-task-manager/lib/create-intent-lease.js +51 -0
  100. package/template/coding-task-manager/lib/ctm-session-context-api.js +97 -6
  101. package/template/coding-task-manager/lib/db-domain-tables.js +1 -0
  102. package/template/coding-task-manager/lib/db-domains.js +1 -0
  103. package/template/coding-task-manager/lib/db-owner-dispatch-probe.js +32 -0
  104. package/template/coding-task-manager/lib/db-owner-request-counts.js +178 -0
  105. package/template/coding-task-manager/lib/db-owner-worker-client.js +68 -14
  106. package/template/coding-task-manager/lib/db-schema-source-files.js +5 -0
  107. package/template/coding-task-manager/lib/deferred-restore-attempt.js +6 -2
  108. package/template/coding-task-manager/lib/feature-manifest.js +4 -0
  109. package/template/coding-task-manager/lib/fleet-conversation-route.js +97 -0
  110. package/template/coding-task-manager/lib/fleet-fanout.js +78 -0
  111. package/template/coding-task-manager/lib/fleet-hub.js +762 -0
  112. package/template/coding-task-manager/lib/fleet-origin-follower.js +252 -0
  113. package/template/coding-task-manager/lib/fleet-resolver-effects.js +147 -0
  114. package/template/coding-task-manager/lib/fleet-resolver-tick.js +148 -0
  115. package/template/coding-task-manager/lib/fleet-resolver.js +454 -0
  116. package/template/coding-task-manager/lib/fleet-session-list-routes.js +114 -0
  117. package/template/coding-task-manager/lib/fleet-session-lists.js +672 -0
  118. package/template/coding-task-manager/lib/fleet-update.js +798 -0
  119. package/template/coding-task-manager/lib/fs-watcher-root-watch.js +141 -0
  120. package/template/coding-task-manager/lib/fs-watcher.js +58 -42
  121. package/template/coding-task-manager/lib/harness-history-transition.js +50 -9
  122. package/template/coding-task-manager/lib/headless-term-service.js +84 -8
  123. package/template/coding-task-manager/lib/hermetic-agent-launch-guard.js +10 -0
  124. package/template/coding-task-manager/lib/history-transition-read-op.js +175 -8
  125. package/template/coding-task-manager/lib/host-cpu-categories.js +310 -0
  126. package/template/coding-task-manager/lib/host-fseventsd-sampler.js +399 -0
  127. package/template/coding-task-manager/lib/host-handoff.js +16 -19
  128. package/template/coding-task-manager/lib/host-stats.js +150 -3
  129. package/template/coding-task-manager/lib/hosted-sync-worker.js +43 -320
  130. package/template/coding-task-manager/lib/hub-bridge-access.js +798 -0
  131. package/template/coding-task-manager/lib/hub-bridge-box.js +409 -0
  132. package/template/coding-task-manager/lib/hub-bridge-mcp.js +380 -0
  133. package/template/coding-task-manager/lib/hub-bridge-mesh.js +313 -0
  134. package/template/coding-task-manager/lib/hub-bridge-ops.js +721 -0
  135. package/template/coding-task-manager/lib/hub-bridge.js +398 -0
  136. package/template/coding-task-manager/lib/idle-notify.js +491 -0
  137. package/template/coding-task-manager/lib/instance-id-ownership-manifest.js +8 -0
  138. package/template/coding-task-manager/lib/instance-manifest.js +5 -1
  139. package/template/coding-task-manager/lib/instance-sync-status.js +18 -8
  140. package/template/coding-task-manager/lib/landing/agent-rule.txt +1 -0
  141. package/template/coding-task-manager/lib/landing/batch-runner.js +366 -0
  142. package/template/coding-task-manager/lib/landing/config.js +536 -0
  143. package/template/coding-task-manager/lib/landing/ctm-view.js +256 -0
  144. package/template/coding-task-manager/lib/landing/gate.js +360 -0
  145. package/template/coding-task-manager/lib/landing/git.js +344 -0
  146. package/template/coding-task-manager/lib/landing/lease.js +247 -0
  147. package/template/coding-task-manager/lib/landing/merge-delta.js +380 -0
  148. package/template/coding-task-manager/lib/landing/proof.js +223 -0
  149. package/template/coding-task-manager/lib/landing/queue.js +998 -0
  150. package/template/coding-task-manager/lib/landing/radar.js +489 -0
  151. package/template/coding-task-manager/lib/landing/resolve-cli.js +424 -0
  152. package/template/coding-task-manager/lib/landing/state.js +263 -0
  153. package/template/coding-task-manager/lib/landing/stop.js +49 -0
  154. package/template/coding-task-manager/lib/landing/sync-cli.js +475 -0
  155. package/template/coding-task-manager/lib/landing/sync.js +456 -0
  156. package/template/coding-task-manager/lib/landing/train.js +885 -0
  157. package/template/coding-task-manager/lib/landing/window.js +57 -0
  158. package/template/coding-task-manager/lib/launch-environment.js +121 -0
  159. package/template/coding-task-manager/lib/launch-model-override.js +36 -10
  160. package/template/coding-task-manager/lib/launch-plan-pool.js +2 -1
  161. package/template/coding-task-manager/lib/launch-plan-warm-cache.js +6 -0
  162. package/template/coding-task-manager/lib/lead-model-observer.js +57 -1
  163. package/template/coding-task-manager/lib/lead-model-options.js +160 -3
  164. package/template/coding-task-manager/lib/lead-model-restore-reconciliation.js +106 -24
  165. package/template/coding-task-manager/lib/lead-model-switch-runtime.js +352 -68
  166. package/template/coding-task-manager/lib/lead-model-switch.js +114 -10
  167. package/template/coding-task-manager/lib/lead-model-transition-runtime.js +14 -2
  168. package/template/coding-task-manager/lib/lead-model-transition-store.js +24 -6
  169. package/template/coding-task-manager/lib/lead-route-preflight.js +179 -0
  170. package/template/coding-task-manager/lib/lead-switch-history-mode.js +110 -0
  171. package/template/coding-task-manager/lib/lead-wire-agent.js +7 -0
  172. package/template/coding-task-manager/lib/lifecycle-wake-experiment.js +9 -2
  173. package/template/coding-task-manager/lib/live-defer-announce.js +45 -0
  174. package/template/coding-task-manager/lib/machine-registry.js +750 -5
  175. package/template/coding-task-manager/lib/macos-capabilities.js +32 -15
  176. package/template/coding-task-manager/lib/main-thread-lint-exemptions.json +433 -53
  177. package/template/coding-task-manager/lib/mainloop-observability.js +14 -9
  178. package/template/coding-task-manager/lib/media-draft-owner.js +16 -3
  179. package/template/coding-task-manager/lib/memory-join.js +337 -0
  180. package/template/coding-task-manager/lib/memory-sync-verify.js +393 -0
  181. package/template/coding-task-manager/lib/memory-sync.js +565 -0
  182. package/template/coding-task-manager/lib/migrations/v57-delegate-pref-mirror-opencode.js +68 -0
  183. package/template/coding-task-manager/lib/migrations/v58-subagent-launch-native-subagents.js +42 -0
  184. package/template/coding-task-manager/lib/migrations/v59-coding-session-model-state-dsh.js +73 -0
  185. package/template/coding-task-manager/lib/migrations/v60-delegate-pref-mirror-dsh.js +78 -0
  186. package/template/coding-task-manager/lib/migrations/v61-conversation-item-clock.js +71 -0
  187. package/template/coding-task-manager/lib/model-availability.js +727 -0
  188. package/template/coding-task-manager/lib/model-execution-recipe-metadata.js +4 -6
  189. package/template/coding-task-manager/lib/model-host-receipt.js +6 -3
  190. package/template/coding-task-manager/lib/model-id-rule.js +19 -0
  191. package/template/coding-task-manager/lib/model-proxy/anthropic-face.js +4 -0
  192. package/template/coding-task-manager/lib/model-proxy/auto-mode-review.js +110 -23
  193. package/template/coding-task-manager/lib/model-proxy/chat-response-translators.js +3 -0
  194. package/template/coding-task-manager/lib/model-proxy/generation-probe.js +60 -17
  195. package/template/coding-task-manager/lib/model-proxy/historical-tool-declarations.js +37 -0
  196. package/template/coding-task-manager/lib/model-proxy/lead-request-validation.js +1 -0
  197. package/template/coding-task-manager/lib/model-proxy/lead-token-admin.js +130 -0
  198. package/template/coding-task-manager/lib/model-proxy/refusal-scope.js +151 -0
  199. package/template/coding-task-manager/lib/model-proxy/request-budget-store.js +11 -2
  200. package/template/coding-task-manager/lib/model-proxy/tokens.js +69 -5
  201. package/template/coding-task-manager/lib/model-proxy/upstream-outcome.js +9 -0
  202. package/template/coding-task-manager/lib/native-agent-model-args.js +25 -22
  203. package/template/coding-task-manager/lib/native-history-control.js +6 -4
  204. package/template/coding-task-manager/lib/native-lead-settings-client.js +44 -4
  205. package/template/coding-task-manager/lib/oauth-proxy-supervisor.js +173 -36
  206. package/template/coding-task-manager/lib/online-backup-worker-client.js +2 -0
  207. package/template/coding-task-manager/lib/opencode-cli.js +480 -0
  208. package/template/coding-task-manager/lib/opencode-launch-model.js +471 -0
  209. package/template/coding-task-manager/lib/opencode-v1-plugin/model-carrier.mjs +35 -0
  210. package/template/coding-task-manager/lib/opencode-v2-plugin/server.js +118 -0
  211. package/template/coding-task-manager/lib/owner-action-manifest-data.json +1 -1
  212. package/template/coding-task-manager/lib/park-composer-draft.js +54 -0
  213. package/template/coding-task-manager/lib/park-fresh-relaunch.js +41 -10
  214. package/template/coding-task-manager/lib/perf-phase-trace.js +72 -0
  215. package/template/coding-task-manager/lib/perf-summary.js +7 -0
  216. package/template/coding-task-manager/lib/perf-tracker.js +63 -2
  217. package/template/coding-task-manager/lib/project-config.js +7 -0
  218. package/template/coding-task-manager/lib/prompt-purge-v2.js +52 -1
  219. package/template/coding-task-manager/lib/provider-login-broker.js +72 -15
  220. package/template/coding-task-manager/lib/provider-login-tab.js +10 -2
  221. package/template/coding-task-manager/lib/read-pool-client.js +89 -2
  222. package/template/coding-task-manager/lib/read-pool-worker-handle.js +29 -4
  223. package/template/coding-task-manager/lib/recent-catalog-query.js +15 -0
  224. package/template/coding-task-manager/lib/recent-session-catalog-response.js +67 -10
  225. package/template/coding-task-manager/lib/recovery-evidence.js +26 -11
  226. package/template/coding-task-manager/lib/relay-client.js +57 -3
  227. package/template/coding-task-manager/lib/relink-audit.js +34 -5
  228. package/template/coding-task-manager/lib/remote-machine-catalog.js +321 -26
  229. package/template/coding-task-manager/lib/remote-machine-create.js +576 -36
  230. package/template/coding-task-manager/lib/remote-machine-link.js +546 -59
  231. package/template/coding-task-manager/lib/remote-machine-proxy.js +143 -9
  232. package/template/coding-task-manager/lib/remote-session-forward.js +65 -7
  233. package/template/coding-task-manager/lib/remote-terminal-target.js +9 -2
  234. package/template/coding-task-manager/lib/restart-guard.js +18 -6
  235. package/template/coding-task-manager/lib/restore-seed-modes.js +87 -0
  236. package/template/coding-task-manager/lib/restored-scrollback-hydration-runtime.js +22 -2
  237. package/template/coding-task-manager/lib/resume-admission.js +17 -3
  238. package/template/coding-task-manager/lib/review-repo-registry.js +58 -19
  239. package/template/coding-task-manager/lib/root-delivery-runtime.js +2 -2
  240. package/template/coding-task-manager/lib/root-prompt-flow.js +1 -1
  241. package/template/coding-task-manager/lib/runtime-contract.js +3 -1
  242. package/template/coding-task-manager/lib/schema-indexes.js +1 -0
  243. package/template/coding-task-manager/lib/session-context-locator.js +742 -0
  244. package/template/coding-task-manager/lib/session-create/attachment.js +2 -2
  245. package/template/coding-task-manager/lib/session-create/create-phases.js +118 -0
  246. package/template/coding-task-manager/lib/session-create/delegate-worker.js +86 -16
  247. package/template/coding-task-manager/lib/session-create/dsh-session.js +930 -0
  248. package/template/coding-task-manager/lib/session-create/lead-route-launch.js +186 -13
  249. package/template/coding-task-manager/lib/session-create/native-lead-lifetime.js +55 -3
  250. package/template/coding-task-manager/lib/session-create/opencode-lead-provider.js +50 -0
  251. package/template/coding-task-manager/lib/session-create/prepared.js +162 -38
  252. package/template/coding-task-manager/lib/session-create/provider-process.js +160 -28
  253. package/template/coding-task-manager/lib/session-create/response.js +7 -7
  254. package/template/coding-task-manager/lib/session-create/structured-session.js +27 -0
  255. package/template/coding-task-manager/lib/session-create/subagent-pick-table.js +3 -1
  256. package/template/coding-task-manager/lib/session-create/subagent-pick.js +458 -9
  257. package/template/coding-task-manager/lib/session-create/transcript.js +1 -1
  258. package/template/coding-task-manager/lib/session-create/worker-budget-setup.js +21 -2
  259. package/template/coding-task-manager/lib/session-create/worker-continuation.js +7 -1
  260. package/template/coding-task-manager/lib/session-create/worker-launch-sandbox.js +39 -10
  261. package/template/coding-task-manager/lib/session-create/worker-observability.js +2 -2
  262. package/template/coding-task-manager/lib/session-empty.js +13 -10
  263. package/template/coding-task-manager/lib/session-host-manager.js +60 -0
  264. package/template/coding-task-manager/lib/session-jobs.js +3 -1
  265. package/template/coding-task-manager/lib/session-lifecycle.js +10 -4
  266. package/template/coding-task-manager/lib/session-model-action-preconditions.js +6 -2
  267. package/template/coding-task-manager/lib/session-model-intent-contract.js +11 -8
  268. package/template/coding-task-manager/lib/session-park.js +738 -0
  269. package/template/coding-task-manager/lib/session-path-grant.js +13 -4
  270. package/template/coding-task-manager/lib/session-status-ledger.js +106 -4
  271. package/template/coding-task-manager/lib/session-transport.js +17 -0
  272. package/template/coding-task-manager/lib/session-workspace-projection.js +54 -5
  273. package/template/coding-task-manager/lib/session-workspace-tracking-runtime.js +213 -1
  274. package/template/coding-task-manager/lib/skill-autocomplete-query.js +120 -3
  275. package/template/coding-task-manager/lib/skill-autocomplete.js +15 -1
  276. package/template/coding-task-manager/lib/sqlite-file-set.js +39 -0
  277. package/template/coding-task-manager/lib/standup-search-adapter.js +16 -2
  278. package/template/coding-task-manager/lib/state-sync/client-ledger.js +12 -0
  279. package/template/coding-task-manager/lib/state-sync/frame-emitter.js +319 -100
  280. package/template/coding-task-manager/lib/storage/coding-session-model-store.js +4 -1
  281. package/template/coding-task-manager/lib/storage/delegate-pref-mirror-store.js +32 -9
  282. package/template/coding-task-manager/lib/structured-session-boot-sweep.js +235 -0
  283. package/template/coding-task-manager/lib/submission-ack.js +90 -2
  284. package/template/coding-task-manager/lib/systemd-scope.js +115 -0
  285. package/template/coding-task-manager/lib/telemetry-receiver.js +39 -0
  286. package/template/coding-task-manager/lib/terminal-attach-geometry.js +985 -0
  287. package/template/coding-task-manager/lib/terminal-bootstrap.js +30 -0
  288. package/template/coding-task-manager/lib/terminal-draft-operation.js +357 -0
  289. package/template/coding-task-manager/lib/terminal-input-admission.js +17 -1
  290. package/template/coding-task-manager/lib/terminal-input-prepared.js +563 -0
  291. package/template/coding-task-manager/lib/transcript-cache-temps.js +171 -0
  292. package/template/coding-task-manager/lib/transcript-frame-cache.js +8 -3
  293. package/template/coding-task-manager/lib/transcript-source.js +36 -4
  294. package/template/coding-task-manager/lib/transcript-store.js +9 -0
  295. package/template/coding-task-manager/lib/typing-interaction-trace.js +106 -3
  296. package/template/coding-task-manager/lib/usage-ledger/backfill.js +40 -5
  297. package/template/coding-task-manager/lib/usage-ledger/client.js +4 -1
  298. package/template/coding-task-manager/lib/usage-ledger/dsh-parse.js +152 -0
  299. package/template/coding-task-manager/lib/usage-ledger/dsh-read.js +73 -0
  300. package/template/coding-task-manager/lib/usage-ledger/ingest.js +34 -6
  301. package/template/coding-task-manager/lib/usage-ledger/live.js +35 -8
  302. package/template/coding-task-manager/lib/usage-ledger/read.js +29 -20
  303. package/template/coding-task-manager/lib/usage-ledger/schema.js +61 -1
  304. package/template/coding-task-manager/lib/usage-ledger/task-coordinator.js +4 -2
  305. package/template/coding-task-manager/lib/wake-skip-reasons.js +1 -0
  306. package/template/coding-task-manager/lib/walle/turn.js +1 -1
  307. package/template/coding-task-manager/lib/walle-history-serve.js +838 -0
  308. package/template/coding-task-manager/lib/walle-kill-authority.js +2 -2
  309. package/template/coding-task-manager/lib/walle-log-rotation.js +164 -1
  310. package/template/coding-task-manager/lib/walle-mcp-auto-config.js +56 -2
  311. package/template/coding-task-manager/lib/walle-row-history.js +495 -0
  312. package/template/coding-task-manager/lib/walle-supervisor.js +33 -14
  313. package/template/coding-task-manager/lib/watch-worker-client.js +203 -0
  314. package/template/coding-task-manager/lib/worker-terminal-output.js +5 -2
  315. package/template/coding-task-manager/lib/ws/hub-bridge.js +35 -0
  316. package/template/coding-task-manager/lib/ws/lifecycle.js +6 -11
  317. package/template/coding-task-manager/lib/ws/misc.js +1 -1
  318. package/template/coding-task-manager/lib/ws/state-sync.js +106 -4
  319. package/template/coding-task-manager/lib/ws/terminal-attach.js +91 -0
  320. package/template/coding-task-manager/oauth-proxy.js +112 -115
  321. package/template/coding-task-manager/package-lock.json +10 -94
  322. package/template/coding-task-manager/package.json +4 -3
  323. package/template/coding-task-manager/protocol/message-table.json +10 -0
  324. package/template/coding-task-manager/protocol/messages.js +10 -0
  325. package/template/coding-task-manager/protocol/ws-schema.json +20 -0
  326. package/template/coding-task-manager/protocol/ws-schema.md +2 -0
  327. package/template/coding-task-manager/public/css/app.css +170 -0
  328. package/template/coding-task-manager/public/css/walle-session.css +4 -0
  329. package/template/coding-task-manager/public/index.html +473 -243
  330. package/template/coding-task-manager/public/js/agent-command.js +86 -0
  331. package/template/coding-task-manager/public/js/app/backups-panel.js +111 -5
  332. package/template/coding-task-manager/public/js/app/coding-agent-upgrade.js +103 -4
  333. package/template/coding-task-manager/public/js/app/machines-page.js +1328 -8
  334. package/template/coding-task-manager/public/js/app/models-tab.js +113 -38
  335. package/template/coding-task-manager/public/js/app/restart-banner.js +53 -0
  336. package/template/coding-task-manager/public/js/app/worktrees-panel.js +184 -2
  337. package/template/coding-task-manager/public/js/background-work.js +196 -27
  338. package/template/coding-task-manager/public/js/coding-session-model-presentation.js +138 -10
  339. package/template/coding-task-manager/public/js/core/active-session-groups.js +179 -0
  340. package/template/coding-task-manager/public/js/core/pending-create.js +238 -0
  341. package/template/coding-task-manager/public/js/core/recent-machine-source.js +228 -0
  342. package/template/coding-task-manager/public/js/core/session-commands.js +180 -14
  343. package/template/coding-task-manager/public/js/dashboard-client-function-order.json +8 -0
  344. package/template/coding-task-manager/public/js/document-review-links.js +9 -1
  345. package/template/coding-task-manager/public/js/file-resolution-client.js +52 -1
  346. package/template/coding-task-manager/public/js/history-app-seam.js +22 -2
  347. package/template/coding-task-manager/public/js/machine-pool-shared.js +558 -3
  348. package/template/coding-task-manager/public/js/message-renderer.js +4 -3
  349. package/template/coding-task-manager/public/js/model-picker.js +3 -1
  350. package/template/coding-task-manager/public/js/native-subagent-review.js +15 -2
  351. package/template/coding-task-manager/public/js/new-session-agent-grid.js +346 -0
  352. package/template/coding-task-manager/public/js/pasted-content.js +236 -0
  353. package/template/coding-task-manager/public/js/pe-private-content-lifetime.js +39 -7
  354. package/template/coding-task-manager/public/js/private-model-lifetime.js +6 -2
  355. package/template/coding-task-manager/public/js/prompts.js +11 -10
  356. package/template/coding-task-manager/public/js/protocol.js +10 -0
  357. package/template/coding-task-manager/public/js/restart-banner-policy.js +29 -1
  358. package/template/coding-task-manager/public/js/session-model-picker.js +447 -57
  359. package/template/coding-task-manager/public/js/session-search-utils.js +18 -2
  360. package/template/coding-task-manager/public/js/session-usage-view.js +2 -1
  361. package/template/coding-task-manager/public/js/session-workspace-tracking.js +51 -1
  362. package/template/coding-task-manager/public/js/state-sync-client.js +10 -1
  363. package/template/coding-task-manager/public/js/stream-view.js +6 -5
  364. package/template/coding-task-manager/public/js/terminal/conv-only-session.js +287 -0
  365. package/template/coding-task-manager/public/js/terminal/runtime-09.js +2 -0
  366. package/template/coding-task-manager/public/js/terminal/runtime-10.js +5 -5
  367. package/template/coding-task-manager/public/js/terminal/runtime-12.js +8 -1
  368. package/template/coding-task-manager/public/js/terminal/runtime-16.js +4 -0
  369. package/template/coding-task-manager/public/js/terminal/runtime-20.js +1 -1
  370. package/template/coding-task-manager/public/js/terminal-attachment-draft.js +67 -13
  371. package/template/coding-task-manager/public/js/terminal-image-orchestration.js +52 -6
  372. package/template/coding-task-manager/public/js/terminal-wheel.js +23 -3
  373. package/template/coding-task-manager/public/js/walle/chat-conversation.js +4 -4
  374. package/template/coding-task-manager/public/js/walle.js +6 -0
  375. package/template/coding-task-manager/public/js/ws/live-dispatch.js +10 -0
  376. package/template/coding-task-manager/public/m/app.css +140 -0
  377. package/template/coding-task-manager/public/m/app.js +507 -68
  378. package/template/coding-task-manager/public/m/index.html +17 -0
  379. package/template/coding-task-manager/public/m/machines.js +838 -4
  380. package/template/coding-task-manager/public/m/private-api.js +31 -1
  381. package/template/coding-task-manager/public/usage.html +1 -0
  382. package/template/coding-task-manager/scripts/dev-route-fixture/synthetic-route-upstream.js +104 -0
  383. package/template/coding-task-manager/scripts/dev-route-fixture.js +339 -0
  384. package/template/coding-task-manager/scripts/opencode-lead-route-probe.js +195 -0
  385. package/template/coding-task-manager/scripts/opencode-v2-contract-probe/fake-anthropic.js +168 -0
  386. package/template/coding-task-manager/scripts/opencode-v2-contract-probe/probe-plugin/server.js +95 -0
  387. package/template/coding-task-manager/scripts/opencode-v2-contract-probe.js +673 -0
  388. package/template/coding-task-manager/server.js +1950 -11679
  389. package/template/coding-task-manager/session-integrity.js +64 -4
  390. package/template/coding-task-manager/workers/db-owner-worker.js +19 -2
  391. package/template/coding-task-manager/workers/online-backup-worker.js +1 -0
  392. package/template/coding-task-manager/workers/read-pool-model-collector.js +51 -3
  393. package/template/coding-task-manager/workers/read-pool-worker.js +76 -6
  394. package/template/coding-task-manager/workers/session-host-pool-process.js +6 -6
  395. package/template/coding-task-manager/workers/session-host-process.js +5 -5
  396. package/template/coding-task-manager/workers/watch-worker.js +105 -0
  397. package/template/coding-task-manager/workers/worktree-worker.js +168 -33
  398. package/template/ctm-relay/lib/protocol.js +14 -1
  399. package/template/package-lock.json +2 -2
  400. package/template/package.json +1 -1
  401. package/template/shared/backup-retention.js +82 -0
  402. package/template/shared/conversation-admission.js +2 -0
  403. package/template/shared/conversation-provenance.js +3 -7
  404. package/template/shared/conversation-storage-v2-contract.js +5 -0
  405. package/template/shared/linked-worktree.js +51 -0
  406. package/template/shared/macos-fda-target.js +57 -0
  407. package/template/shared/orphan-parents.js +55 -0
  408. package/template/shared/release-schema.json +2 -2
  409. package/template/shared/storage/contract.js +3 -0
  410. package/template/wall-e/adapters/imessage.js +12 -0
  411. package/template/wall-e/agent.js +71 -4
  412. package/template/wall-e/api-walle.js +21 -1
  413. package/template/wall-e/bin/train-gemma-e4b-tooluse.js +2 -2
  414. package/template/wall-e/bin/walle-mcp-stdio.js +5 -5
  415. package/template/wall-e/brain/brain-join.js +859 -0
  416. package/template/wall-e/brain/brain-sync-membership.js +20 -0
  417. package/template/wall-e/brain/brain-sync-store.js +131 -77
  418. package/template/wall-e/brain/brain-sync-v2-schema.js +203 -0
  419. package/template/wall-e/brain/brain-sync-v2.js +578 -0
  420. package/template/wall-e/brain/delegate-model-prefs-store.js +13 -4
  421. package/template/wall-e/brain/deterministic-id.js +51 -0
  422. package/template/wall-e/brain/memories.js +2 -2
  423. package/template/wall-e/brain/migrations.js +40 -6
  424. package/template/wall-e/brain/model-identity-migration.js +1 -1
  425. package/template/wall-e/brain/owner-call-whitelist.js +23 -7
  426. package/template/wall-e/brain/schema.js +45 -85
  427. package/template/wall-e/brain.js +171 -35
  428. package/template/wall-e/chat.js +3 -3
  429. package/template/wall-e/coding/agent-shell-env.js +7 -2
  430. package/template/wall-e/coding/prompt-bundle.js +66 -21
  431. package/template/wall-e/config/main-loop-exemptions.json +2 -2
  432. package/template/wall-e/http/admin-api.js +37 -0
  433. package/template/wall-e/http/gws-api.js +4 -0
  434. package/template/wall-e/http/helper-rules-api.js +7 -1
  435. package/template/wall-e/http/model-admin.js +8 -1
  436. package/template/wall-e/http/model-delegate-pref-admin.js +3 -3
  437. package/template/wall-e/http/model-proxy-providers.js +174 -1
  438. package/template/wall-e/http/model-route-health.js +157 -0
  439. package/template/wall-e/http/sync-api.js +278 -464
  440. package/template/wall-e/jobs/job-runner.js +28 -1
  441. package/template/wall-e/lib/brain-owner-op-priority.js +9 -1
  442. package/template/wall-e/lib/ctm-data-dir.js +26 -0
  443. package/template/wall-e/lib/derivation-home.js +147 -0
  444. package/template/wall-e/lib/helper-rules/evaluate.js +50 -9
  445. package/template/wall-e/lib/helper-rules/grade.js +51 -8
  446. package/template/wall-e/lib/helper-rules/lead-call-hook.js +38 -5
  447. package/template/wall-e/lib/helper-rules/store.js +4 -2
  448. package/template/wall-e/lib/landing-mcp-tools.js +170 -0
  449. package/template/wall-e/lib/mcp-client-config.js +133 -14
  450. package/template/wall-e/lib/mcp-gateway-migrate.js +28 -64
  451. package/template/wall-e/lib/mcp-integration.js +58 -13
  452. package/template/wall-e/lib/parent-death.js +7 -1
  453. package/template/wall-e/lib/scheduler-worker-jobs.js +6 -4
  454. package/template/wall-e/llm/delegate-lead-prefs.js +16 -1
  455. package/template/wall-e/llm/delegate-vendor-default.js +12 -5
  456. package/template/wall-e/llm/model-roles.js +1 -1
  457. package/template/wall-e/llm/native-delegate-selection.js +56 -8
  458. package/template/wall-e/llm/worker-delegate-args.js +72 -7
  459. package/template/wall-e/llm/worker-mcp-routing.js +66 -17
  460. package/template/wall-e/loops/brain-custom-writes.js +19 -0
  461. package/template/wall-e/loops/ingest.js +17 -1
  462. package/template/wall-e/loops/think.js +19 -4
  463. package/template/wall-e/mcp-contract.js +222 -6
  464. package/template/wall-e/mcp-protocol-contract.js +40 -5
  465. package/template/wall-e/mcp-server.js +128 -29
  466. package/template/wall-e/mcp-worker-schemas.js +5 -5
  467. package/template/wall-e/memory/ctm-context-client.js +85 -0
  468. package/template/wall-e/memory/ctm-session-context.js +9 -3
  469. package/template/wall-e/memory/hub-bridge-client.js +388 -0
  470. package/template/wall-e/memory/replica-origin.js +56 -0
  471. package/template/wall-e/memory/session-pool.js +135 -0
  472. package/template/wall-e/runtime/live-capabilities.js +8 -3
  473. package/template/wall-e/server.js +4 -2
  474. package/template/wall-e/shared/release-schema.json +2 -2
  475. package/template/wall-e/shared/sqlite-file-set.js +39 -0
  476. package/template/wall-e/shared/storage/contract.js +3 -0
  477. package/template/wall-e/skills/_templates/delegate-worker/VERSION +1 -1
  478. package/template/wall-e/skills/_templates/delegate-worker/released-sha256.json +7 -1
  479. package/template/wall-e/skills/_templates/delegate-worker/templates/claude/SKILL.md +4 -4
  480. package/template/wall-e/skills/_templates/delegate-worker/templates/codex/SKILL.md +6 -6
  481. package/template/wall-e/skills/_templates/fleet-sync/VERSION +1 -0
  482. package/template/wall-e/skills/_templates/fleet-sync/released-sha256.json +16 -0
  483. package/template/wall-e/skills/_templates/fleet-sync/templates/claude/SKILL.md +64 -0
  484. package/template/wall-e/skills/_templates/fleet-sync/templates/codex/SKILL.md +64 -0
  485. package/template/wall-e/tools/chrome-automation.js +6 -0
  486. package/template/wall-e/training/setup-cuda-train-env.sh +2 -2
  487. package/template/wall-e/workers/brain-owner-worker.js +18 -4
  488. package/template/wall-e/workers/mcp-gateway-process.js +5 -5
@@ -0,0 +1,171 @@
1
+ 'use strict';
2
+
3
+ // Cache temp names and their reclaimer (design doc temp-file-leaks §5 D-1/D-3, §8 WP-1).
4
+ //
5
+ // A decoded frame is written to `<final>.tmp-<pid>-<threadId>-<hex>` and published with a rename, so a
6
+ // crash between the two strands a temp. The pid+THREAD pair is what makes one attributable: the read
7
+ // pool's worker threads share process.pid, and pool `worker.terminate()` stops a thread after its write
8
+ // and before its rename -- a dead-pid sweep alone cannot see that (this is the 63 GB incident, §1 L-1).
9
+ // Threads get their own module instance, so `threadId` here is this isolate's own id (0 = main thread).
10
+ // CACHE_TEMP_RE also matches the legacy `<final>.tmp-<pid>-<hex>` shape, which has no thread to blame.
11
+ //
12
+ // Extracted from lib/transcript-source.js (which was over its 1500-line new-file ceiling) and kept
13
+ // deliberately dependency-free: this module knows NOTHING about the data dir. Every entry point takes
14
+ // the cache root in `opts.cacheDir`, and transcript-source -- the one owner of the default root -- passes
15
+ // it. The only external dependency is the liveness probe, loaded lazily on first sweep.
16
+
17
+ const crypto = require('node:crypto');
18
+ const fs = require('node:fs');
19
+ const path = require('node:path');
20
+ const { threadId } = require('node:worker_threads');
21
+
22
+ const CACHE_TEMP_RE = /\.bin\.tmp-(\d+)-(?:(\d+)-)?[0-9a-f]{8,16}$/;
23
+ const CACHE_TEMP_STALE_MS = 30 * 60_000;
24
+ const CACHE_TEMP_SWEEP_INTERVAL_MS = 5 * 60_000;
25
+ let _cacheTempSweptAt = 0; // per-isolate sweep throttle; 0 = never swept
26
+
27
+ function cacheTempPath(finalPath) {
28
+ return `${finalPath}.tmp-${process.pid}-${threadId}-${crypto.randomBytes(8).toString('hex')}`;
29
+ }
30
+
31
+ /** The one cache-root rule here: the caller passes it. A missing root is a programming error, not a silent no-op. */
32
+ function _cacheRootOf(opts) {
33
+ const root = opts && opts.cacheDir;
34
+ if (typeof root !== 'string' || root === '') throw new TypeError('transcript-cache-temps: opts.cacheDir is required');
35
+ return root;
36
+ }
37
+
38
+ function _cacheKeyDirPaths(root, dirs) {
39
+ return dirs.filter((d) => d.isDirectory()).map((d) => path.join(root, d.name));
40
+ }
41
+
42
+ /** The `<key>` subdirectories of a cache root, or [] when the root does not exist yet. */
43
+ async function _cacheKeyDirsAsync(root) {
44
+ let dirs;
45
+ try {
46
+ dirs = await fs.promises.readdir(root, { withFileTypes: true });
47
+ } catch (err) {
48
+ if (err && err.code === 'ENOENT') return [];
49
+ throw err;
50
+ }
51
+ return _cacheKeyDirPaths(root, dirs);
52
+ }
53
+
54
+ /** Every `*.bin.tmp-*` cache temp in `dir`: the one temp-shape rule (a published `.bin` never matches). */
55
+ function _cacheTempEntry(dir, name, match, st) {
56
+ return {
57
+ file: path.join(dir, name), pid: Number(match[1]),
58
+ threadId: match[2] === undefined ? null : Number(match[2]), size: st.size, mtimeMs: st.mtimeMs,
59
+ };
60
+ }
61
+
62
+ async function _cacheTempsInDirAsync(dir) {
63
+ let names;
64
+ try {
65
+ names = await fs.promises.readdir(dir);
66
+ } catch (err) {
67
+ if (err && err.code === 'ENOENT') return []; // expected: a concurrent prune took the whole key dir
68
+ throw err;
69
+ }
70
+ const temps = [];
71
+ for (const name of names) {
72
+ const match = CACHE_TEMP_RE.exec(name);
73
+ if (!match) continue;
74
+ let st;
75
+ try {
76
+ st = await fs.promises.stat(path.join(dir, name));
77
+ } catch (err) {
78
+ if (err && err.code === 'ENOENT') continue; // expected: another reclaimer unlinked this temp first
79
+ throw err;
80
+ }
81
+ temps.push(_cacheTempEntry(dir, name, match, st));
82
+ }
83
+ return temps;
84
+ }
85
+
86
+ async function _unlinkCacheTempAsync(file) {
87
+ try {
88
+ await fs.promises.unlink(file);
89
+ return true;
90
+ } catch (err) {
91
+ if (err && err.code === 'ENOENT') return false; // expected: a concurrent reclaimer won the race
92
+ throw err;
93
+ }
94
+ }
95
+
96
+ /** The per-temp sweep decision: a provably-gone OWNER, or simply age. The sweep calls only this. */
97
+ function _cacheTempIsReclaimable(temp, now, isProcessAlive) {
98
+ const ownerGone = temp.pid !== process.pid && !isProcessAlive(temp.pid);
99
+ return ownerGone || now - temp.mtimeMs >= CACHE_TEMP_STALE_MS;
100
+ }
101
+
102
+ /** The per-temp thread claim (D-2): a legacy name has no threadId, so no thread can claim it. */
103
+ function _cacheTempIsOwnedByThread(temp, pid, threadId_) {
104
+ return temp.pid === pid && temp.threadId === threadId_;
105
+ }
106
+
107
+ /** The sweep throttle + its inputs: null when this isolate already swept inside the interval. */
108
+ function _cacheTempSweepState(opts) {
109
+ const now = typeof opts.now === 'function' ? Number(opts.now())
110
+ : (Number.isFinite(opts.now) ? Number(opts.now) : Date.now());
111
+ if (!opts.force && now - _cacheTempSweptAt < CACHE_TEMP_SWEEP_INTERVAL_MS) return null;
112
+ _cacheTempSweptAt = now;
113
+ return {
114
+ now,
115
+ root: _cacheRootOf(opts),
116
+ isProcessAlive: typeof opts.isProcessAlive === 'function'
117
+ ? opts.isProcessAlive
118
+ : require('./host-registry').isProcessAlive, // lazy: the probe is not needed on the decode path
119
+ };
120
+ }
121
+
122
+ /**
123
+ * The one reclaimer for stranded decode temps (design §5 D-3): a temp goes when its OWNER is provably
124
+ * gone (dead pid -- the only evidence a terminated worker thread leaves behind) or when it is simply
125
+ * old, whatever wrote it. Never touches a `.bin`; tolerates ENOENT races; throttled per isolate.
126
+ *
127
+ * ASYNC-only on purpose: a live cache holds thousands of `<key>` dirs (measured 479 ms for one walk),
128
+ * so a synchronous form would stall whatever loop called it. Every caller -- server.js's interval and
129
+ * the read pool's exit listener -- can await, so no sync twin exists to drift from these rules.
130
+ */
131
+ async function sweepStaleCacheTempsAsync(opts = {}) {
132
+ const result = { removed: 0, bytes: 0 };
133
+ const state = _cacheTempSweepState(opts);
134
+ if (!state) return result;
135
+ for (const dir of await _cacheKeyDirsAsync(state.root)) {
136
+ for (const temp of await _cacheTempsInDirAsync(dir)) {
137
+ if (!_cacheTempIsReclaimable(temp, state.now, state.isProcessAlive)) continue;
138
+ if (await _unlinkCacheTempAsync(temp.file)) {
139
+ result.removed += 1;
140
+ result.bytes += temp.size;
141
+ }
142
+ }
143
+ }
144
+ return result;
145
+ }
146
+
147
+ /**
148
+ * D-2: reclaims what ONE dead thread left behind, by ownership (pid + threadId) instead of by age --
149
+ * the thread is gone, so no grace period is needed. Async for the same reason as the sweep above.
150
+ */
151
+ async function removeCacheTempsForThreadAsync(pid, threadId_, opts = {}) {
152
+ const wantPid = Number(pid);
153
+ const wantThreadId = Number(threadId_);
154
+ let removed = 0;
155
+ for (const dir of await _cacheKeyDirsAsync(_cacheRootOf(opts))) {
156
+ for (const temp of await _cacheTempsInDirAsync(dir)) {
157
+ if (!_cacheTempIsOwnedByThread(temp, wantPid, wantThreadId)) continue;
158
+ if (await _unlinkCacheTempAsync(temp.file)) removed += 1;
159
+ }
160
+ }
161
+ return removed;
162
+ }
163
+
164
+ module.exports = {
165
+ CACHE_TEMP_RE,
166
+ CACHE_TEMP_STALE_MS,
167
+ CACHE_TEMP_SWEEP_INTERVAL_MS,
168
+ cacheTempPath,
169
+ sweepStaleCacheTempsAsync,
170
+ removeCacheTempsForThreadAsync,
171
+ };
@@ -3,7 +3,6 @@
3
3
  // Async full-frame cache ownership. A pathname is only the publication/eviction name: readers own
4
4
  // the opened inode through a refcounted lease, so a prune in another isolate cannot invalidate them.
5
5
  // Admission is injected by transcript-source, sharing its two slots with bounded prefix decoders.
6
- const crypto = require('node:crypto');
7
6
  const fs = require('node:fs');
8
7
  const path = require('node:path');
9
8
  const { Transform } = require('node:stream');
@@ -27,8 +26,12 @@ function closedReceipt(stream) {
27
26
  // the eviction order of multi-GB entries unchanged (fsevents-churn D-10b).
28
27
  const TOUCH_MIN_INTERVAL_MS = 60_000;
29
28
 
30
- function createDecodedFrameCache({ withDecoder, cacheRoot, cacheMax, now, frameFile, pruneCache, codeError }) {
29
+ function createDecodedFrameCache({ withDecoder, cacheRoot, cacheMax, now, frameFile, pruneCache, codeError, cacheTempPath }) {
31
30
  const entries = new Map();
31
+ // D-1: the temp-naming rule lives in transcript-cache-temps.js (the sync writer in transcript-source
32
+ // shares it). It is injected at construction so the module graph stays acyclic; the lazy fallback is
33
+ // for direct constructors (tests) that call this factory without transcript-source in the loop.
34
+ const tempPathFor = cacheTempPath || require('./transcript-cache-temps').cacheTempPath;
32
35
 
33
36
  function removeEntry(entry) {
34
37
  if (entries.get(entry.key) === entry) entries.delete(entry.key);
@@ -96,7 +99,9 @@ function createDecodedFrameCache({ withDecoder, cacheRoot, cacheMax, now, frameF
96
99
  entry.state = 'filling';
97
100
  await fs.promises.mkdir(path.dirname(entry.file), { recursive: true });
98
101
  throwIfAborted(signal);
99
- entry.tmp = `${entry.file}.tmp-${process.pid}-${crypto.randomBytes(8).toString('hex')}`;
102
+ // D-1: one naming rule for both writers, owned by transcript-cache-temps.js (pid + threadId), so a
103
+ // temp stranded by a terminated read-pool thread is attributable to the thread that wrote it.
104
+ entry.tmp = tempPathFor(entry.file);
100
105
  const decoder = (entry.opts.decoderFactory || decodeFrame)(entry.archive.path, entry.frame);
101
106
  const decoderClosed = closedReceipt(decoder);
102
107
  let total = 0;
@@ -23,6 +23,7 @@ const zlib = require('node:zlib');
23
23
  const { decodeFrame, waitForDecodedFrameClose, decompressParams } = require('./transcript-archive-codec');
24
24
  const { getDataDir } = require('./data-dir');
25
25
  const { createDecodedFrameCache, TOUCH_MIN_INTERVAL_MS } = require('./transcript-frame-cache');
26
+ const cacheTemps = require('./transcript-cache-temps');
26
27
 
27
28
  const CACHE_DIR_NAME = 'transcript-archive-cache';
28
29
  const DEFAULT_CACHE_MAX_BYTES = 4 * 1024 * 1024 * 1024;
@@ -91,6 +92,16 @@ function _frameFile(root, key, index) {
91
92
  return path.join(root, key, `${index}.bin`);
92
93
  }
93
94
 
95
+ // The cache root is this module's to know (CACHE_DIR_NAME under getDataDir()); every reclaimer entry
96
+ // point takes it as `cacheDir`. Delegated so the sweep's rules live in exactly one module.
97
+ const _cacheTempsOpts = (opts) => ({ ...opts, cacheDir: (opts && opts.cacheDir) || _cacheRoot(opts) });
98
+ const cacheTempPath = cacheTemps.cacheTempPath;
99
+ const CACHE_TEMP_RE = cacheTemps.CACHE_TEMP_RE;
100
+ const CACHE_TEMP_STALE_MS = cacheTemps.CACHE_TEMP_STALE_MS;
101
+ const CACHE_TEMP_SWEEP_INTERVAL_MS = cacheTemps.CACHE_TEMP_SWEEP_INTERVAL_MS;
102
+ const sweepStaleCacheTempsAsync = (opts = {}) => cacheTemps.sweepStaleCacheTempsAsync(_cacheTempsOpts(opts));
103
+ const removeCacheTempsForThreadAsync = (pid, tid, opts = {}) => cacheTemps.removeCacheTempsForThreadAsync(pid, tid, _cacheTempsOpts(opts));
104
+
94
105
  // ---------------------------------------------------------------------------------------------
95
106
  // Manifest lookup (the db handle sees the base database; guarded like the identity guard).
96
107
 
@@ -336,6 +347,7 @@ function _evictionList(files, max, keepFile) {
336
347
  }
337
348
 
338
349
  async function _pruneCache(opts, keepFile) {
350
+ await sweepStaleCacheTempsAsync(opts);
339
351
  const doomed = _evictionList(await _listCache(_cacheRoot(opts)), _cacheMax(opts), keepFile);
340
352
  for (const file of doomed) {
341
353
  await fs.promises.unlink(file).catch((err) => {
@@ -391,7 +403,7 @@ async function _withDecoder(fn, signal) {
391
403
 
392
404
  const { acquireDecodedFrame } = createDecodedFrameCache({
393
405
  withDecoder: _withDecoder, cacheRoot: _cacheRoot, cacheMax: _cacheMax,
394
- now: _now, frameFile: _frameFile, pruneCache: _pruneCache, codeError: _codeError,
406
+ now: _now, frameFile: _frameFile, pruneCache: _pruneCache, codeError: _codeError, cacheTempPath,
395
407
  });
396
408
 
397
409
  // ---------------------------------------------------------------------------------------------
@@ -1146,9 +1158,23 @@ function _decodeFrameSync(archive, index, opts, learnLength) {
1146
1158
  if (!learnLength && bytes.length !== uLen) throw _codeError('ARCHIVE_VERIFY_FAILED', `frame ${index} decoded to ${bytes.length} bytes, expected ${uLen}`);
1147
1159
  if (ledger && learnLength) _chargeLearnedDecoded(ledger, `${archive.cacheKey}:${index}`, bytes.length);
1148
1160
  fs.mkdirSync(path.dirname(file), { recursive: true });
1149
- const tmp = `${file}.tmp-${process.pid}-${crypto.randomBytes(4).toString('hex')}`;
1150
- fs.writeFileSync(tmp, bytes, { flag: 'wx' });
1151
- fs.renameSync(tmp, file);
1161
+ const tmp = cacheTempPath(file);
1162
+ try {
1163
+ fs.writeFileSync(tmp, bytes, { flag: 'wx' });
1164
+ fs.renameSync(tmp, file);
1165
+ } catch (err) {
1166
+ // D-4: this write is synchronous inside whatever isolate called it, so nothing else can clean up
1167
+ // after a failed publish -- and a caller killed right here (read pool terminate) never gets to.
1168
+ // Unlink the temp, then rethrow the ORIGINAL error unchanged.
1169
+ try {
1170
+ fs.unlinkSync(tmp);
1171
+ } catch (cleanupErr) {
1172
+ if (!cleanupErr || cleanupErr.code !== 'ENOENT') {
1173
+ console.warn(`[transcript-cache] temp cleanup failed for ${tmp}: ${cleanupErr?.message || cleanupErr}`);
1174
+ } // else: the write failed before creating the temp -- nothing to remove
1175
+ }
1176
+ throw err;
1177
+ }
1152
1178
  const when = new Date(_now(opts));
1153
1179
  fs.utimesSync(file, when, when);
1154
1180
  _pruneCacheSync(opts, file);
@@ -1423,4 +1449,10 @@ module.exports = {
1423
1449
  zstdContentSize,
1424
1450
  MAX_RANGE_BYTES,
1425
1451
  DEFAULT_CACHE_MAX_BYTES,
1452
+ cacheTempPath,
1453
+ CACHE_TEMP_RE,
1454
+ CACHE_TEMP_STALE_MS,
1455
+ CACHE_TEMP_SWEEP_INTERVAL_MS,
1456
+ sweepStaleCacheTempsAsync,
1457
+ removeCacheTempsForThreadAsync,
1426
1458
  };
@@ -4314,6 +4314,11 @@ function catchUpTranscriptFiles(db, options = {}) {
4314
4314
  for (;;) {
4315
4315
  assertCatchUpLive();
4316
4316
  if (Date.now() - startedAt > catchUpBudgetMs) { budgetExceeded = true; break; }
4317
+ // S-16 (D-11): a wall-clock budget alone lets this whole op (which blocks the serial
4318
+ // writer) run to its 5 s slice while an interactive request waits. The caller's
4319
+ // predicates are the same ones drainTranscriptByteBackfill already honours; a set
4320
+ // predicate ends the run as a partial result, exactly like budget expiry.
4321
+ if (options.shouldYieldToUrgent?.() || options.shouldYieldToPending?.()) { budgetExceeded = true; break; }
4317
4322
  const promoted = backfillSessionRowsFromTranscriptEvents(db, {
4318
4323
  limit: backfillLimit,
4319
4324
  ctmSessionId: sessionId,
@@ -4334,6 +4339,10 @@ function catchUpTranscriptFiles(db, options = {}) {
4334
4339
  for (const rawPath of filePaths) {
4335
4340
  assertCatchUpLive();
4336
4341
  if (Date.now() - startedAt > catchUpBudgetMs) { budgetExceeded = true; break; }
4342
+ // S-16 (D-11): same yield contract as the backfill loop above — one file per check, so a
4343
+ // pending interactive/urgent request is honoured within a single file's ingest here, not
4344
+ // only at the 5 s op budget.
4345
+ if (options.shouldYieldToUrgent?.() || options.shouldYieldToPending?.()) { budgetExceeded = true; break; }
4337
4346
  if (filesProcessed >= maxFilesPerRun) { budgetExceeded = true; break; }
4338
4347
  const filePath = path.resolve(String(rawPath || ''));
4339
4348
  if (!filePath || seenPaths.has(filePath)) continue;
@@ -72,10 +72,13 @@ const STAGES = Object.freeze([
72
72
  // new gate -- Gate 1 (§7) pins it.
73
73
  // WP-2 (state-sync-delivery-path-reset-2026-09-18.html §5 D-9): the suppression/no-audience/
74
74
  // client-fault gates were deleted from the emitter along with their latches -- the emitter now
75
- // only refuses on an outstanding in-flight frame or the rate floor. `hidden`/`nudge`/`sync-hold`/
76
- // `settle` are stamped by headless-term-service.js itself, not by the emitter's onRefused.
75
+ // only refuses on a full unacked window or the rate floor. `hidden`/`nudge`/`sync-hold`/`settle`
76
+ // are stamped by headless-term-service.js itself, not by the emitter's onRefused.
77
+ // WP-1 (docs/state-sync-pipelined-deltas-echo-never-waits-for-ack-2026-10-31.html §5 D-2, §6, §9
78
+ // S-9): the one-slot gates are replaced by `window-full`; the OLD names are kept at the end so rows
79
+ // written before WP-1 still parse.
77
80
  const GATES = Object.freeze([
78
- 'in-flight-keyframe', 'in-flight-delta', 'rate', 'hidden', 'nudge', 'sync-hold', 'settle',
81
+ 'window-full', 'in-flight-keyframe', 'in-flight-delta', 'rate', 'hidden', 'nudge', 'sync-hold', 'settle',
79
82
  ]);
80
83
  const GATE_SET = new Set(GATES);
81
84
 
@@ -95,12 +98,23 @@ const OPTIONAL_STAGES = new Set([
95
98
  // coalescer delivered before any keystroke was pending), so neither may end the boundary walk.
96
99
  'frame-arrived', 'host-pty-data',
97
100
  'echo-bytes', 'echo-cell',
101
+ // WP-0 (docs/state-sync-pipelined-deltas-echo-never-waits-for-ack-2026-10-31.html §5 D-5,
102
+ // §9 S-9): 'inflight-acked' is a REPORT-ONLY stamp on the interaction a matched ack unblocked --
103
+ // it is not a step in any interaction's sequence (it is not in STAGES at all), so it must never
104
+ // move `lastCompletedBoundary`/`missingBoundary`. Its record carries the acked frame's seq and
105
+ // both measured legs (see note()'s branch below); the interaction itself gains `heldBehind`.
106
+ 'inflight-acked',
98
107
  ]);
99
108
  const WORKLOAD_FIELDS = Object.freeze([
100
109
  'rawBatchClass', 'synchronizedFrameCount', 'mirrorChangedRows', 'frameKind',
101
110
  'frameBytes', 'queuedBytes', 'queueDepth', 'codexRepairActions',
102
111
  'statusRenderSource', 'visibility', 'focused', 'activeLoafMs', 'gridRevision',
103
112
  'frameSeq',
113
+ // WP-2 (docs/typing-delay-session-list-broadcast-storm-2026-11-01.html §6 WP-2, §5 OQ-3): the
114
+ // socket's bufferedAmount at the instant the frame carrying this key's echo was handed to it.
115
+ // Read on the `frame-sent` note (server.js's 'frame' fan-out); 0 at a slow frame is what rules
116
+ // socket backlog out and leaves browser task-queue time (OQ-3).
117
+ 'socketBufferedBytes',
104
118
  ]);
105
119
 
106
120
  function finite(value, fallback = 0) {
@@ -139,6 +153,15 @@ function percentile(values, pct) {
139
153
  return sorted[Math.max(0, Math.ceil(sorted.length * pct) - 1)];
140
154
  }
141
155
 
156
+ // WP-0 (S-9): one leg of an in-flight frame's round trip. A leg is a non-negative millisecond
157
+ // count, or the explicit "no evidence" sentinel -1 -- never a silent 0, which would read as an
158
+ // instant round trip and quietly flatter the very measurement this WP exists to make honest.
159
+ function rttMs(value, fallback = -1) {
160
+ const number = Number(value);
161
+ if (!Number.isFinite(number) || number < 0) return fallback;
162
+ return Math.floor(number);
163
+ }
164
+
142
165
  function sanitizePublicContext(context = {}, fallback = {}) {
143
166
  const source = { ...fallback, ...context };
144
167
  return {
@@ -212,6 +235,10 @@ function buildSlowInteractionRow(row, ktpMs, lastStage) {
212
235
  inFlightAgeMs: refused ? refused.inFlightAgeMs : 0,
213
236
  rejectedSurfaceEpoch: refused ? refused.rejectedSurfaceEpoch : 0,
214
237
  sweepSkipReason: refused ? refused.sweepSkipReason : '',
238
+ // WP-0 (S-9, §6 API specs): the in-flight frame this keystroke was held behind, with both
239
+ // measured round-trip legs and the relay remainder -- null for a keystroke that was never
240
+ // refused, or whose held frame's ack never landed.
241
+ heldBehind: row.heldBehind || null,
215
242
  browserKeydownMs: stageMsFromKeydown(row, 'browser-keydown'),
216
243
  serverReceivedMs: stageMsFromKeydown(row, 'server-received'),
217
244
  ptyWrittenMs: stageMsFromKeydown(row, 'pty-written'),
@@ -310,6 +337,9 @@ function createTypingInteractionTraceStore(options = {}) {
310
337
  const slowThrottledBySession = new Map();
311
338
  const worstByGroup = new Map();
312
339
  const worstUnpaintedByGroup = new Map();
340
+ // WP-0 (S-9): the frame WINDOW size in force when the minute's acks landed (1 today, the gate's
341
+ // size after WP-1) -- keyed like the two maps above, last non-zero value wins, cleared per flush.
342
+ const windowFramesByGroup = new Map();
313
343
 
314
344
  // Both new per-key maps are bounded/evicted exactly like lastSlowEmitAtBySession above: re-set
315
345
  // an existing key at the tail, then drop the oldest keys past the shared cap.
@@ -378,6 +408,9 @@ function createTypingInteractionTraceStore(options = {}) {
378
408
  gate,
379
409
  gateHoldMs,
380
410
  clientId: row.clientId,
411
+ // WP-0 (S-9): this interaction's held-frame evidence (null when it was never refused, or the
412
+ // ack for its held seq never landed) -- flushRollups turns it into the three held*P95Ms fields.
413
+ heldBehind: row.heldBehind || null,
381
414
  });
382
415
  windowClosedHistory.push({ at: paintedAt, ktpMs, gate });
383
416
  if (windowClosedHistory.length > WINDOW_HISTORY_CAP) windowClosedHistory.shift();
@@ -468,6 +501,9 @@ function createTypingInteractionTraceStore(options = {}) {
468
501
  lastCompletedBoundary: 'browser-keydown',
469
502
  stages: Object.assign(Object.create(null), { 'browser-keydown': { at: startedAt } }),
470
503
  workload: Object.create(null),
504
+ // WP-0 (S-9): set by note()'s 'inflight-acked' branch when this interaction's own refusal
505
+ // named the seq that ack retired (see buildSlowInteractionRow / pendingClosed).
506
+ heldBehind: null,
471
507
  });
472
508
  prune(startedAt);
473
509
  return interactionId;
@@ -528,10 +564,46 @@ function createTypingInteractionTraceStore(options = {}) {
528
564
  row.stages[stage] = Array.isArray(event.mainSpans)
529
565
  ? { at, mainSpans: sanitizeMainSpans(event.mainSpans) }
530
566
  : { at };
567
+ } else if (stage === 'inflight-acked') {
568
+ // WP-0 (S-9): the acked frame's seq plus both measured legs (host = the emitter's own
569
+ // send->ack span; main = the server ledger's sentAt delta; windowFrames = the gate's window
570
+ // size when the ack landed). Report-only -- see OPTIONAL_STAGES above.
571
+ row.stages[stage] = {
572
+ at,
573
+ inFlightSeq: Math.max(0, Math.floor(finite(event.inFlightSeq))),
574
+ hostRttMs: rttMs(event.hostRttMs, 0),
575
+ mainRttMs: rttMs(event.mainRttMs, -1),
576
+ windowFrames: Math.max(0, Math.floor(finite(event.windowFrames))),
577
+ };
531
578
  } else {
532
579
  row.stages[stage] = { at };
533
580
  }
534
581
  }
582
+ // WP-0 (S-9, §5 D-5): if THIS interaction was refused behind the very seq this ack retired,
583
+ // remember the held frame's two round-trip legs on the interaction itself. FIRST match wins --
584
+ // a later ack, or one for another seq, never overwrites the evidence for the frame that
585
+ // actually held this keystroke, and a never-refused interaction keeps `heldBehind: null`.
586
+ if (stage === 'inflight-acked') {
587
+ const windowFrames = Math.max(0, Math.floor(finite(event.windowFrames)));
588
+ if (windowFrames > 0) {
589
+ setBoundedByKey(windowFramesByGroup, rollupGroupKey(row.sessionId, row.agentType), windowFrames);
590
+ }
591
+ if (!row.heldBehind) {
592
+ const refused = row.stages['emit-refused'];
593
+ const retiredSeq = Math.max(0, Math.floor(finite(event.inFlightSeq)));
594
+ if (refused && retiredSeq > 0 && Math.max(0, Math.floor(finite(refused.inFlightSeq))) === retiredSeq) {
595
+ const hostRttMs = rttMs(event.hostRttMs, 0);
596
+ const mainRttMs = rttMs(event.mainRttMs, -1);
597
+ row.heldBehind = {
598
+ seq: retiredSeq,
599
+ hostRttMs,
600
+ mainRttMs,
601
+ // The relay leg is the part of the host leg main's own measurement cannot see.
602
+ relayMs: mainRttMs >= 0 ? Math.max(0, hostRttMs - mainRttMs) : -1,
603
+ };
604
+ }
605
+ }
606
+ }
535
607
  // R-8, order-independent: the worker posts `frame-posted` as its own 'typing-stage' message
536
608
  // AFTER the frame shipped (headless-term-service.js emitSettled -> noteFramePostedForEchoed),
537
609
  // so main usually stamps `frame-arrived` BEFORE it knows `frame-posted`. Compute the spans when
@@ -724,6 +796,20 @@ function createTypingInteractionTraceStore(options = {}) {
724
796
  return values.length ? percentile(values, 0.95) : 0;
725
797
  }
726
798
 
799
+ // WP-0 (S-9, §6 API specs): nearest-rank p95 over the CLOSED interactions of the minute that
800
+ // carried held-frame evidence, for one leg (`hostRttMs` | `mainRttMs` | `relayMs`). The -1
801
+ // "no evidence" sentinel is not a sample: an all-unknown minute reports 0, not -1.
802
+ function heldPercentileMs(closedList, field) {
803
+ const values = [];
804
+ for (const entry of closedList) {
805
+ const held = entry.heldBehind;
806
+ if (!held) continue;
807
+ const value = Number(held[field]);
808
+ if (Number.isFinite(value) && value >= 0) values.push(value);
809
+ }
810
+ return values.length ? percentile(values, 0.95) : 0;
811
+ }
812
+
727
813
  // D-1: groups every interaction that CLOSED (reached `painted`) or EXPIRED (pruned by TTL
728
814
  // without `painted`) since the previous flush, by (sessionId, agentType||'unknown'), and
729
815
  // emits one flat rollup object per group via `onRollup`. Returns the number emitted.
@@ -793,6 +879,14 @@ function createTypingInteractionTraceStore(options = {}) {
793
879
  worstUnpainted: worstUnpaintedByGroup.get(key) || null,
794
880
  slowEmitted: slowEmittedBySession.get(sessionId) || 0,
795
881
  slowThrottled: slowThrottledBySession.get(sessionId) || 0,
882
+ // WP-0 (S-9, §6 API specs): the frame WINDOW size in force this minute (1 today -- the gate
883
+ // is still one frame in flight; WP-1 widens it) and the round trip of every keystroke this
884
+ // minute held behind an in-flight frame, split into its two measured legs plus the relay
885
+ // remainder (host - main). 0 = nothing held / no evidence in this minute.
886
+ windowFrames: windowFramesByGroup.get(key) || 1,
887
+ heldHostRttP95Ms: heldPercentileMs(closedList, 'hostRttMs'),
888
+ heldMainRttP95Ms: heldPercentileMs(closedList, 'mainRttMs'),
889
+ heldRelayP95Ms: heldPercentileMs(closedList, 'relayMs'),
796
890
  workerLoopMaxMs: Math.max(0, finite(extras.workerLoopMaxMs, 0)),
797
891
  host: {
798
892
  loadPerCore: Math.max(0, finite(extras.host?.loadPerCore, 0)),
@@ -810,6 +904,9 @@ function createTypingInteractionTraceStore(options = {}) {
810
904
  // minute starts from 0 rather than re-reporting the previous minute's worst).
811
905
  worstByGroup.clear();
812
906
  worstUnpaintedByGroup.clear();
907
+ // WP-0 (S-9): the minute's observed window size is per-flush-window state, cleared with the
908
+ // buckets it describes -- a later minute starts from "no evidence" (reported as 1) again.
909
+ windowFramesByGroup.clear();
813
910
  slowEmittedBySession.clear();
814
911
  slowThrottledBySession.clear();
815
912
  lastFlushAt = at;
@@ -880,6 +977,12 @@ function noteWorkerStageMessage(store, msg = {}) {
880
977
  inFlightAgeMs: msg.inFlightAgeMs,
881
978
  rejectedSurfaceEpoch: msg.rejectedSurfaceEpoch,
882
979
  sweepSkipReason: msg.sweepSkipReason,
980
+ // WP-0 (S-11): the matched ack's two measured round-trip legs and the gate's window size ride
981
+ // the same 'typing-stage' message as the `inflight-acked` stage stamp (the store bounds and
982
+ // validates each one itself -- see note()'s branch).
983
+ hostRttMs: msg.hostRttMs,
984
+ mainRttMs: msg.mainRttMs,
985
+ windowFrames: msg.windowFrames,
883
986
  });
884
987
  }
885
988
  return true;
@@ -22,9 +22,11 @@ const path = require('node:path');
22
22
 
23
23
  const { statTranscriptSync } = require('../transcript-source');
24
24
  const { claudeFileIdentity } = require('./claude-parse');
25
+ const { DSH_USAGE_DIR_NAME } = require('./dsh-parse');
25
26
 
26
27
  const CODEX_ROLLOUT_RE = /^rollout-.*\.jsonl$/;
27
28
  const CLAUDE_TRANSCRIPT_RE = /\.jsonl(\.bak)?$/i;
29
+ const DSH_USAGE_FILE_RE = /\.jsonl$/i;
28
30
  // Directories that hold no usage facts (memory notes, tool results).
29
31
  const SKIP_DIR_NAMES = new Set(['memory', 'tool-results']);
30
32
 
@@ -74,16 +76,39 @@ function codexCorpusFiles(root, { listDir, realpath, onError = (_path, error) =>
74
76
  return out;
75
77
  }
76
78
 
79
+ // Every `*.jsonl` directly under the dsh usage directory (WP-9). FLAT on purpose: the directory
80
+ // holds one file per CTM session, named by the session id, and nothing else -- no project nesting, no
81
+ // `subagents/` tree, no `workflows/`. A missing directory is normal (no dsh session has been routed
82
+ // through the proxy yet).
83
+ function dshCorpusFiles(root, { listDir, onError = (_path, error) => { throw error; } }) {
84
+ const out = [];
85
+ if (!root) return out;
86
+ let entries;
87
+ try { entries = listDir(root); } catch (err) { onError(root, err); return out; }
88
+ for (const entry of entries) {
89
+ if (!entry || !entry.isFile) continue;
90
+ if (!DSH_USAGE_FILE_RE.test(entry.name)) continue;
91
+ out.push(path.join(root, entry.name));
92
+ }
93
+ return out;
94
+ }
95
+
77
96
  /**
78
97
  * The whole local corpus, newest first.
79
98
  *
80
- * @param {{claudeProjectsDir?: string, codexSessionsDir?: string}} roots
99
+ * @param {{claudeProjectsDir?: string, codexSessionsDir?: string, dshUsageDir?: string}} roots
100
+ * `dshUsageDir` is `<dataDir>/dsh-usage`; `createBackfill` derives it from the ledger's own
101
+ * database file when the caller does not name it.
81
102
  * @param {{listDir: (dir: string) => Array<{name: string, isFile: boolean, isDirectory: boolean}>,
82
103
  * realpath: (p: string) => string|null}} fsOps
83
104
  * @returns {Array<{path: string, size: number, mtimeMs: number}>}
84
105
  */
85
- function listCorpusFiles({ claudeProjectsDir, codexSessionsDir } = {}, fsOps) {
86
- const paths = [...claudeCorpusFiles(claudeProjectsDir, fsOps), ...codexCorpusFiles(codexSessionsDir, fsOps)];
106
+ function listCorpusFiles({ claudeProjectsDir, codexSessionsDir, dshUsageDir } = {}, fsOps) {
107
+ const paths = [
108
+ ...claudeCorpusFiles(claudeProjectsDir, fsOps),
109
+ ...codexCorpusFiles(codexSessionsDir, fsOps),
110
+ ...dshCorpusFiles(dshUsageDir, fsOps),
111
+ ];
87
112
  const out = [];
88
113
  for (const filePath of paths) {
89
114
  try {
@@ -105,6 +130,7 @@ function createBackfill({
105
130
  store,
106
131
  ingestor,
107
132
  roots,
133
+ dshUsageDir = null,
108
134
  parserVersion,
109
135
  isShedding = () => false,
110
136
  isLiveClaimed = () => false,
@@ -117,6 +143,15 @@ function createBackfill({
117
143
  } = {}) {
118
144
  const STEP = Math.max(1, Math.floor(Number(stepBytes) || DEFAULT_STEP_BYTES));
119
145
  const CHECKPOINT_EVERY = Math.max(1, Math.floor(Number(checkpointEveryCalls) || DEFAULT_CHECKPOINT_EVERY_CALLS));
146
+ // The dsh corpus root (WP-9). The Claude/Codex roots are HOME-derived and handed in by the worker;
147
+ // dsh's is NOT (it lives under the CTM data dir), and the ledger's own database file is the one
148
+ // thing in this process that already names that directory -- `<dataDir>/usage.db`. Deriving it here
149
+ // keeps the worker entrypoint free of a second path source, and a caller that knows better (a test)
150
+ // can still pass `dshUsageDir` explicitly.
151
+ const DSH_USAGE_DIR = dshUsageDir
152
+ || (store && store.db && typeof store.db.name === 'string' && store.db.name !== ''
153
+ ? path.join(path.dirname(store.db.name), DSH_USAGE_DIR_NAME)
154
+ : null);
120
155
  let callsSinceCheckpoint = 0;
121
156
  let lastError = null;
122
157
  // Calls upserted so far for a file that takes several steps, so the queue row is
@@ -138,7 +173,7 @@ function createBackfill({
138
173
  lastError = { path: sourcePath, message, at: now() };
139
174
  if (scanErrors.length < 20) scanErrors.push(message);
140
175
  };
141
- const files = listCorpusFiles(roots, { listDir, realpath, onError, statTranscript }).map((f) => ({ path: f.path, size: f.size }));
176
+ const files = listCorpusFiles({ ...(roots || {}), dshUsageDir: DSH_USAGE_DIR }, { listDir, realpath, onError, statTranscript }).map((f) => ({ path: f.path, size: f.size }));
142
177
  const runId = store.createRun({ parserVersion, reason, files });
143
178
  if (scanErrors.length) store.setRunStatus(runId, 'queued', { error: `scan incomplete: ${scanErrors.join('; ')}`.slice(0, 500) });
144
179
  return runId;
@@ -244,4 +279,4 @@ function createBackfill({
244
279
  return { ensureRun, step, status, lastFailure };
245
280
  }
246
281
 
247
- module.exports = { listCorpusFiles, createBackfill };
282
+ module.exports = { listCorpusFiles, dshCorpusFiles, createBackfill };
@@ -221,7 +221,10 @@ function startUsageLedger({
221
221
 
222
222
  function requestSources(sources) {
223
223
  if (stopping || !Array.isArray(sources) || sources.length > PRIORITY_REQUEST_LIMIT) return { accepted: false, backlog: priority.size };
224
- const valid = sources.filter((s) => s && typeof s.path === 'string' && path.isAbsolute(s.path) && ['claude', 'codex'].includes(s.provider));
224
+ // The main thread is the FIRST gate on a source batch; the live lane re-checks the same set
225
+ // (lib/usage-ledger/live.js `LIVE_PROVIDERS`). dsh was added by WP-9: its roots are the
226
+ // `<dataDir>/dsh-usage/<ctmSessionId>.jsonl` files the model proxy writes.
227
+ const valid = sources.filter((s) => s && typeof s.path === 'string' && path.isAbsolute(s.path) && ['claude', 'codex', 'dsh'].includes(s.provider));
225
228
  if (valid.length !== sources.length || priority.size + valid.filter((s) => !priority.has(s.path)).length > PRIORITY_QUEUE_CAP) return { accepted: false, backlog: priority.size };
226
229
  for (const source of valid) priority.set(source.path, { provider: source.provider, path: source.path });
227
230
  deliverSources();