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,152 @@
1
+ 'use strict';
2
+
3
+ // DeepSeek Harness (`dsh`) usage-row parser for the usage ledger (design
4
+ // docs/deepseek-harness-acp-agent-2026-10-28.html §5 D-19 and §9b L-3..L-8).
5
+ //
6
+ // The ONE source of dsh spend is the model proxy's per-request file,
7
+ // `<dataDir>/dsh-usage/<ctmSessionId>.jsonl` (written by lib/acp/dsh-usage-log.js; that contract is
8
+ // FROZEN -- this parser reads it, it never redefines it). One JSON object per line:
9
+ //
10
+ // {"ts":"<ISO-8601 UTC>","requestId":"<id>","model":"<wire model id>","input_tokens":N,
11
+ // "output_tokens":N,"cache_read_input_tokens":N,"cache_creation_input_tokens":N}
12
+ //
13
+ // A line that is not JSON, or is JSON that is not an object, carries no call: the reader counts it
14
+ // (usage_files.skipped_lines) and keeps going. Nothing a malformed line says is ever guessed at.
15
+ //
16
+ // Pure by construction: it takes an already-parsed JSON object per line and returns facts. No fs, no
17
+ // DB, no try/catch (the caller owns the read loop and the JSON.parse), exactly like codex-parse.js.
18
+ //
19
+ // Unlike a Claude/Codex transcript this file is APPEND-ONLY and holds nothing but calls, so there is
20
+ // no turn, prompt, spawn, delegation or agent fact to derive from it:
21
+ // * `prompt_id` is NULL. The file states spend, not conversation; inventing a prompt identity from
22
+ // a request id would claim an attribution the source cannot support.
23
+ // * `agent_id` is '' -- the ledger's convention for the SESSION's main agent (the same spelling
24
+ // claude-parse.js uses for a root transcript). dsh has no sub-agents of its own.
25
+ //
26
+ // Identity: `requestId` IS the call's identity (the contract says so), so the call key is
27
+ // `dsh:<requestId>` and the same id written twice is ONE call. The id is also stored beside the key
28
+ // as `request_id`, the evidence column every provider's call row carries.
29
+
30
+ // The directory whose `*.jsonl` files are dsh usage rows (ingest.js decides the provider by it).
31
+ const DSH_USAGE_DIR_NAME = 'dsh-usage';
32
+ const DSH_CALL_KEY_PREFIX = 'dsh:';
33
+
34
+ /**
35
+ * The CTM session id a dsh usage file belongs to: its own file name without the extension (the
36
+ * frozen contract of lib/acp/dsh-usage-log.js -- the name IS the id, never a directory lookup).
37
+ *
38
+ * @param {string} filePath
39
+ * @returns {string|null}
40
+ */
41
+ function dshFileSessionId(filePath) {
42
+ const clean = String(filePath || '').replace(/\\/g, '/');
43
+ const base = clean.slice(clean.lastIndexOf('/') + 1);
44
+ const match = /^(.+)\.jsonl$/.exec(base);
45
+ return match ? match[1] : null;
46
+ }
47
+
48
+ // Non-negative integer token count. A missing, malformed or negative value is 0 -- the columns are
49
+ // NOT NULL integers where 0 already means "the provider reported nothing", never a separate state.
50
+ function count(value) {
51
+ const n = Number(value);
52
+ if (!Number.isFinite(n) || n <= 0) return 0;
53
+ return Math.round(n);
54
+ }
55
+
56
+ // The call's identity. An absent or non-string `requestId` names no call the proxy could have
57
+ // written twice under one id, so the LINE OFFSET stands in: it is stable for a given append-only
58
+ // file, so a re-read of the same bytes keys identically instead of minting a second call.
59
+ function callKeyFor(requestId, lineOffset) {
60
+ const id = typeof requestId === 'string' ? requestId : (typeof requestId === 'number' && Number.isFinite(requestId) ? String(requestId) : '');
61
+ return id === '' ? `${DSH_CALL_KEY_PREFIX}line:${lineOffset}` : `${DSH_CALL_KEY_PREFIX}${id}`;
62
+ }
63
+
64
+ // The call-row shape the store accepts, field for field the shape codex-parse.js hands it.
65
+ // * `input` is the UNCACHED input: the Anthropic Messages API (what dsh speaks) reports
66
+ // `input_tokens` WITHOUT the cache read/creation halves, so nothing is subtracted here (Codex's
67
+ // OpenAI-shaped totals, which DO include the cached prefix, are the case that subtracts).
68
+ // * `cache_write_1h` is 0: the contract carries one cache-creation count, and the 5-minute bucket
69
+ // is the only one that backs it.
70
+ function callRowFromRow(record, { callKey, requestId, sessionId, lineOffset }) {
71
+ return {
72
+ call_key: callKey,
73
+ provider: 'dsh',
74
+ session_id: sessionId,
75
+ // '' is the session's MAIN agent (the ledger convention, see claude-parse.js).
76
+ agent_id: '',
77
+ // No prompt attribution is derivable from a usage row alone (see the header).
78
+ prompt_id: null,
79
+ ts: Date.parse(record.ts) || 0,
80
+ model: typeof record.model === 'string' && record.model !== '' ? record.model : null,
81
+ input: count(record.input_tokens),
82
+ output: count(record.output_tokens),
83
+ cache_read: count(record.cache_read_input_tokens),
84
+ cache_write_5m: count(record.cache_creation_input_tokens),
85
+ cache_write_1h: 0,
86
+ reasoning: 0,
87
+ // A dsh row IS the final reading of its own request: the proxy writes it once, when the request
88
+ // completed, so the counts are never a lower bound on a response still being streamed.
89
+ output_final: 1,
90
+ basis: 'record',
91
+ request_id: requestId,
92
+ line_offset: lineOffset,
93
+ };
94
+ }
95
+
96
+ /**
97
+ * @param {{sessionId?: string|null, state?: object|null}} [options]
98
+ * `sessionId` is the CTM session id every call of this file belongs to (its file name).
99
+ * `state` is the checkpoint's parse state. This parser derives nothing across lines, so the state
100
+ * is carried only to keep the checkpoint shape identical to the other parsers'.
101
+ */
102
+ function createDshFileParser({ sessionId = null, state = null } = {}) {
103
+ const persisted = state && typeof state === 'object' ? state : {};
104
+ let calls = [];
105
+ // The ids this pass has already emitted: a repeated requestId is ONE call, not two rows the store
106
+ // would then have to collapse. Cleared per drain (the batch is the dedup window); a duplicate that
107
+ // straddles a slice boundary still collapses in the store, whose call_key is the durable identity.
108
+ let seen = new Set();
109
+
110
+ function line(record, lineOffset) {
111
+ if (!record || typeof record !== 'object' || Array.isArray(record)) return;
112
+ const offset = Number(lineOffset) || 0;
113
+ const callKey = callKeyFor(record.requestId, offset);
114
+ if (seen.has(callKey)) return;
115
+ seen.add(callKey);
116
+ calls.push(callRowFromRow(record, {
117
+ callKey,
118
+ requestId: typeof record.requestId === 'string' ? record.requestId : (typeof record.requestId === 'number' && Number.isFinite(record.requestId) ? String(record.requestId) : null),
119
+ sessionId,
120
+ lineOffset: offset,
121
+ }));
122
+ }
123
+
124
+ function drain() {
125
+ const out = {
126
+ calls,
127
+ // dsh states spend only: no prompt, spawn, alias, delegation, attempt or agent fact exists in
128
+ // this source, and an empty array is the honest answer for each (never a fabricated one).
129
+ prompts: [],
130
+ aliases: [],
131
+ spawns: [],
132
+ delegations: [],
133
+ attempts: [],
134
+ agents: [],
135
+ reported: null,
136
+ state: persisted,
137
+ };
138
+ calls = [];
139
+ seen = new Set();
140
+ return out;
141
+ }
142
+
143
+ return { line, drain };
144
+ }
145
+
146
+ module.exports = {
147
+ createDshFileParser,
148
+ dshFileSessionId,
149
+ callKeyFor,
150
+ DSH_USAGE_DIR_NAME,
151
+ DSH_CALL_KEY_PREFIX,
152
+ };
@@ -0,0 +1,73 @@
1
+ 'use strict';
2
+
3
+ // The read model's dsh rules (design docs/deepseek-harness-acp-agent-2026-10-28.html §5 D-19, §9b L-8).
4
+ // Path arithmetic only: this module opens no file, so lib/usage-ledger/read.js can load it in the read pool.
5
+ //
6
+ // dsh is the ONE provider whose ledger rows are not keyed by an `agent_sessions.agent_session_id`: the
7
+ // model proxy names a session's usage file `<dataDir>/dsh-usage/<ctmSessionId>.jsonl`
8
+ // (lib/acp/dsh-usage-log.js), so the ledger's `session_id` IS the CTM session id.
9
+ //
10
+ // The usage file is NOT the session's `agent_sessions.jsonl_path`. That column names the CTM-owned
11
+ // TRANSCRIPT (`<dataDir>/dsh-transcripts/<acpSessionId>.jsonl`, design D-8), which carries no token usage
12
+ // AND whose `{type:'user'}` rows the Claude parser would admit as PROMPTS if it were ever handed to the
13
+ // writer as a source. The usage file lives in the sibling directory, named by the CTM session id; it is
14
+ // derived from the transcript path so the SCOPE and the CATALOG cannot disagree about where a session's
15
+ // dsh rows are. tests-integration/usage-ledger-dsh.test.js proves this path equals the ingested one
16
+ // (`coverage.complete` is true only then).
17
+
18
+ const path = require('node:path');
19
+ const { DSH_USAGE_DIR_NAME } = require('./dsh-parse');
20
+
21
+ const DSH_TRANSCRIPT_DIR_NAME = 'dsh-transcripts';
22
+
23
+ /**
24
+ * The usage file the ledger stores a dsh session's rows under, or null when the transcript path does not
25
+ * name a `<dataDir>/dsh-transcripts/<acp>.jsonl` file.
26
+ *
27
+ * @param {string|null|undefined} transcriptPath `agent_sessions.jsonl_path` of a dsh row
28
+ * @param {string|null|undefined} ctmSessionId the CTM session id the usage file is named after
29
+ * @returns {string|null}
30
+ */
31
+ function dshUsageSourcePath(transcriptPath, ctmSessionId) {
32
+ const id = String(ctmSessionId == null ? '' : ctmSessionId);
33
+ const file = String(transcriptPath == null ? '' : transcriptPath);
34
+ if (!id || !file) return null;
35
+ const transcriptDir = path.dirname(file);
36
+ if (path.basename(transcriptDir) !== DSH_TRANSCRIPT_DIR_NAME) return null;
37
+ const dataDir = path.dirname(transcriptDir);
38
+ if (!dataDir || dataDir === '.' || dataDir === path.sep) return null;
39
+ return path.join(dataDir, DSH_USAGE_DIR_NAME, `${id}.jsonl`);
40
+ }
41
+
42
+ /**
43
+ * A scope entry for a dsh `agent_sessions` row: re-keyed to the CTM session it belongs to, its source the
44
+ * usage file. The core db's size/mtime describe the TRANSCRIPT, so they are reported unknown rather than
45
+ * compared against a different file (which would call every dsh session stale). A session the proxy never
46
+ * wrote a file for reads as no rows, no error.
47
+ */
48
+ function dshScopeEntry(row, role, ctmId) {
49
+ const sessionId = row.ctm_session_id == null || row.ctm_session_id === '' ? String(ctmId) : String(row.ctm_session_id);
50
+ return {
51
+ session_id: sessionId,
52
+ provider: 'dsh',
53
+ ctm_session_id: sessionId,
54
+ role,
55
+ source_path: dshUsageSourcePath(row.jsonl_path, sessionId),
56
+ expected_size: null,
57
+ expected_modified_at: null,
58
+ expected_compact_bak_size: null,
59
+ };
60
+ }
61
+
62
+ /**
63
+ * A source-catalog entry for a dsh row, or null when its usage file cannot be named (an empty path is not
64
+ * ingestable). `session_id` stays the row's `agent_session_id` -- the catalog page's cursor key.
65
+ */
66
+ function dshCatalogSource(row) {
67
+ const ctmSessionId = row.ctm_session_id == null ? '' : String(row.ctm_session_id);
68
+ const sourcePath = dshUsageSourcePath(row.path, ctmSessionId);
69
+ if (!sourcePath) return null;
70
+ return { provider: 'dsh', session_id: row.session_id == null ? '' : String(row.session_id), path: sourcePath, size: null, modified_at: null };
71
+ }
72
+
73
+ module.exports = { DSH_TRANSCRIPT_DIR_NAME, dshUsageSourcePath, dshScopeEntry, dshCatalogSource };
@@ -26,6 +26,7 @@ const {
26
26
  CLAUDE_MARKERS,
27
27
  } = require('./claude-parse');
28
28
  const { createCodexFileParser, CODEX_MARKERS } = require('./codex-parse');
29
+ const { createDshFileParser, dshFileSessionId, DSH_USAGE_DIR_NAME } = require('./dsh-parse');
29
30
 
30
31
  // Bump when a parser's READING of a record changes: a checkpoint stamped with another
31
32
  // version is discarded and the file is re-read from 0 (D-3). Upserts are idempotent,
@@ -49,7 +50,11 @@ const { createCodexFileParser, CODEX_MARKERS } = require('./codex-parse');
49
50
  // promptId of the prompt it continues no longer relabels that row's kind, and a `delegate` result
50
51
  // with no `generation` key resolves to generation 1 instead of dropping its edge. Both are
51
52
  // corrections to facts already stored, so the re-read this version forces is what repairs them.
52
- const PARSER_VERSION = 7;
53
+ // v8 (deepseek-harness-acp-agent WP-9, design D-19): the dsh provider exists. Its source --
54
+ // `<dataDir>/dsh-usage/<ctmSessionId>.jsonl` -- is a NEW file this reading discovers, so nothing
55
+ // already stored changes shape; the bump is what makes every stored dsh file (should one ever be
56
+ // written before the provider was known) get read through the parser below.
57
+ const PARSER_VERSION = 8;
53
58
  // The head fingerprint is the first min(size, 4096) bytes (D-3).
54
59
  const HEAD_BYTES = 4096;
55
60
  const DEFAULT_SLICE_BYTES = 8 * 1024 * 1024;
@@ -66,16 +71,22 @@ function sha256Hex(buf) {
66
71
  }
67
72
 
68
73
  /**
69
- * Which provider owns `sourcePath`: 'codex' for a rollout file, 'claude' for a
70
- * Claude Code transcript, null for anything else.
74
+ * Which provider owns `sourcePath`: 'codex' for a rollout file, 'dsh' for a DeepSeek Harness usage
75
+ * file (`<dataDir>/dsh-usage/<ctmSessionId>.jsonl`), 'claude' for a Claude Code transcript, null for
76
+ * anything else.
77
+ *
78
+ * The dsh test is the PARENT DIRECTORY NAME, and it is evaluated BEFORE the Claude identity: a dsh
79
+ * file is `<session-id>.jsonl`, which `claudeFileIdentity` would otherwise accept as a Claude root
80
+ * transcript (its rule is any `<name>.jsonl`). Only the directory separates the two providers.
71
81
  *
72
82
  * @param {string} sourcePath
73
- * @returns {'claude'|'codex'|null}
83
+ * @returns {'claude'|'codex'|'dsh'|null}
74
84
  */
75
85
  function fileProvider(sourcePath) {
76
86
  const p = String(sourcePath || '');
77
87
  if (!p) return null;
78
88
  if (CODEX_ROLLOUT_RE.test(path.basename(p))) return 'codex';
89
+ if (/\.jsonl$/i.test(path.basename(p)) && path.basename(path.dirname(p)) === DSH_USAGE_DIR_NAME) return 'dsh';
79
90
  if (claudeFileIdentity(p)) return 'claude';
80
91
  return null;
81
92
  }
@@ -154,14 +165,21 @@ function createUsageIngestor({ store, sliceBytes = DEFAULT_SLICE_BYTES, pricer =
154
165
  }
155
166
 
156
167
  const identity = provider === 'claude' ? claudeFileIdentity(sourcePath) : null;
157
- const markers = provider === 'claude' ? CLAUDE_MARKERS : CODEX_MARKERS;
168
+ // A dsh file's session id IS its file name (the frozen contract of lib/acp/dsh-usage-log.js), so
169
+ // it is known before a single byte is read -- unlike Codex, whose thread ids appear in the file.
170
+ const dshSessionId = provider === 'dsh' ? dshFileSessionId(sourcePath) : null;
171
+ // Every dsh line is a call, so there is no marker to prefilter on (null = deliver every complete
172
+ // line; a line that is not JSON is counted by the read loop, never guessed at).
173
+ const markers = provider === 'claude' ? CLAUDE_MARKERS : (provider === 'codex' ? CODEX_MARKERS : null);
158
174
  const metaAgent = provider === 'claude' && identity.agentId && offset === 0
159
175
  ? readMetaAgent(sourcePath, identity)
160
176
  : null;
161
177
 
162
178
  const parser = provider === 'claude'
163
179
  ? createClaudeFileParser({ sessionId: identity.sessionId, agentId: identity.agentId, state })
164
- : createCodexFileParser({ state, sourcePath });
180
+ : (provider === 'dsh'
181
+ ? createDshFileParser({ sessionId: dshSessionId, state })
182
+ : createCodexFileParser({ state, sourcePath }));
165
183
 
166
184
  // The file identity of a batch. For Codex the root session id and the thread id only
167
185
  // appear once session_meta / token_usage_record has been read, so it is recomputed
@@ -176,6 +194,16 @@ function createUsageIngestor({ store, sliceBytes = DEFAULT_SLICE_BYTES, pricer =
176
194
  agent_id: identity.agentId,
177
195
  };
178
196
  }
197
+ if (provider === 'dsh') {
198
+ // A dsh file is ONE session (its name): the id never arrives from a line, so it is the same
199
+ // on every pass and `agent_id` '' names that session's main agent.
200
+ return {
201
+ source_path: sourcePath,
202
+ provider,
203
+ session_id: dshSessionId,
204
+ agent_id: '',
205
+ };
206
+ }
179
207
  const s = parserState || {};
180
208
  return {
181
209
  source_path: sourcePath,
@@ -29,18 +29,33 @@ const DEFAULT_MAX_BYTES_PER_FILE = 4 * 1024 * 1024;
29
29
  const MAX_ERRORS = 20;
30
30
  const CODEX_ROLLOUT_RE = /^rollout-.*\.jsonl$/;
31
31
  const CLAUDE_AGENT_RE = /^agent-.*\.jsonl$/;
32
+ const DSH_USAGE_FILE_RE = /\.jsonl$/i;
33
+ // Every provider this lane can be handed a root for. A root naming anything else is defaulted to
34
+ // 'claude' (the historical spelling) rather than silently admitted as itself.
35
+ const LIVE_PROVIDERS = new Set(['claude', 'codex', 'dsh']);
32
36
 
33
37
  // `<...>/<sid>.jsonl` or `<...>/<sid>.jsonl.bak` -> `<...>/<sid>/subagents`.
34
38
  function claudeSubagentsDir(rootPath) {
35
39
  return path.join(String(rootPath).replace(/\.jsonl(?:\.bak)?$/i, ''), 'subagents');
36
40
  }
37
41
 
42
+ // The directory a source's SIBLING files live in, and the key that identifies it as a discovery root.
43
+ // Codex and dsh both scan one FLAT directory (Codex `sessions/`, dsh `<dataDir>/dsh-usage/`, whose
44
+ // file names are CTM session ids); Claude's siblings are the `<sid>/subagents` tree beside a root
45
+ // transcript. The key is prefixed per provider so a Codex sessions dir and a dsh dir can never
46
+ // collide.
47
+ function discoveryDirKey(provider, rootPath) {
48
+ if (provider === 'codex') return `codex:${path.dirname(rootPath)}`;
49
+ if (provider === 'dsh') return `dsh:${path.dirname(rootPath)}`;
50
+ return claudeSubagentsDir(rootPath);
51
+ }
52
+
38
53
  // A root list normalized to `{provider, path}` with the provider defaulted. Pure and
39
54
  // exported so both the lane and the worker's change detector agree on one shape.
40
55
  function normalizeRoots(next) {
41
56
  return (Array.isArray(next) ? next : [])
42
57
  .filter((root) => root && typeof root.path === 'string' && root.path)
43
- .map((root) => ({ provider: root.provider === 'codex' ? 'codex' : 'claude', path: root.path }));
58
+ .map((root) => ({ provider: LIVE_PROVIDERS.has(root.provider) ? root.provider : 'claude', path: root.path }));
44
59
  }
45
60
 
46
61
  // The identity of a root set: sorted `provider:path`. The worker fires its prompt
@@ -80,11 +95,16 @@ function createLiveLane({
80
95
  const priority = new Map();
81
96
  function discoveryRoot(source) {
82
97
  if (source.provider === 'codex') return null; // inactive Codex sources are supplied by the native catalog
98
+ // dsh has no catalog of its own: one flat directory holds every session's file, so ANY dsh root
99
+ // (the source itself, or a file the caller requested) makes the whole directory discoverable.
100
+ if (source.provider === 'dsh') return { ...source, key: discoveryDirKey('dsh', source.path) };
83
101
  const identity = claudeFileIdentity(source.path);
84
102
  return identity && !identity.agentId ? { ...source, key: claudeSubagentsDir(source.path) } : null;
85
103
  }
86
104
  function enqueueDiscovery(source, { active = false, urgent = false } = {}) {
87
- const root = source.provider === 'codex' && active ? { ...source, key: `codex:${path.dirname(source.path)}` } : discoveryRoot(source);
105
+ const root = (source.provider === 'codex' || source.provider === 'dsh') && active
106
+ ? { ...source, key: discoveryDirKey(source.provider, source.path) }
107
+ : discoveryRoot(source);
88
108
  if (!root) return true;
89
109
  const previous = discoveryRoots.get(root.key);
90
110
  if (previous) { previous.active ||= active; previous.urgent ||= urgent; return true; }
@@ -97,7 +117,7 @@ function createLiveLane({
97
117
  const cap = urgent ? PRIORITY_SOURCE_CAP : SOURCE_QUEUE_CAP;
98
118
  const result = (accepted) => ({ accepted, backlog: pending.size + priority.size, priorityBacklog: priority.size, catalogBacklog: pending.size, discoveryBacklog: discoveryRoots.size });
99
119
  if (!Array.isArray(sources) || sources.length > SOURCE_BATCH_CAP) return result(false);
100
- if (sources.some((source) => !source || typeof source.path !== 'string' || !path.isAbsolute(source.path) || !['claude', 'codex'].includes(source.provider))) return result(false);
120
+ if (sources.some((source) => !source || typeof source.path !== 'string' || !path.isAbsolute(source.path) || !LIVE_PROVIDERS.has(source.provider))) return result(false);
101
121
  const normalized = normalizeRoots(sources);
102
122
  const fresh = normalized.filter((source) => !queue.has(source.path) && (urgent || !priority.has(source.path)));
103
123
  if (queue.size + new Set(fresh.map((source) => source.path)).size > cap) return result(false);
@@ -117,7 +137,7 @@ function createLiveLane({
117
137
 
118
138
  function setRoots(next) {
119
139
  roots = normalizeRoots(next);
120
- const activeDirs = new Set(roots.map((root) => root.provider === 'codex' ? `codex:${path.dirname(root.path)}` : claudeSubagentsDir(root.path)));
140
+ const activeDirs = new Set(roots.map((root) => discoveryDirKey(root.provider, root.path)));
121
141
  for (const entry of discoveryRoots.values()) entry.active = activeDirs.has(entry.key);
122
142
  for (const root of roots) enqueueDiscovery(root, { active: true });
123
143
  }
@@ -136,12 +156,19 @@ function createLiveLane({
136
156
  }
137
157
  {
138
158
  yield null; // one root-work unit even when its directories do not exist
139
- const dir = root.provider === 'codex' ? path.dirname(root.path) : claudeSubagentsDir(root.path);
159
+ // Codex and dsh both list ONE flat directory; Claude's siblings live in `<sid>/subagents`.
160
+ // `recentOnly` keeps the Codex sessions tree bounded to recently touched rollouts -- a dsh
161
+ // usage directory holds one small file per session, so every file in it is worth a stat.
162
+ const dir = root.provider === 'codex' || root.provider === 'dsh'
163
+ ? path.dirname(root.path)
164
+ : claudeSubagentsDir(root.path);
165
+ const fileRe = root.provider === 'codex' ? CODEX_ROLLOUT_RE
166
+ : (root.provider === 'dsh' ? DSH_USAGE_FILE_RE : CLAUDE_AGENT_RE);
140
167
  for (const entry of entries(dir)) {
141
- yield entry.isFile && (root.provider === 'codex' ? CODEX_ROLLOUT_RE : CLAUDE_AGENT_RE).test(entry.name)
168
+ yield entry.isFile && fileRe.test(entry.name)
142
169
  ? { provider: root.provider, path: path.join(dir, entry.name), recentOnly: root.provider === 'codex' } : null;
143
170
  }
144
- if (root.provider === 'codex') return;
171
+ if (root.provider === 'codex' || root.provider === 'dsh') return; // no `workflows/` walk: flat only
145
172
  const workflows = path.join(dir, 'workflows');
146
173
  for (const workflow of entries(workflows)) {
147
174
  yield null;
@@ -273,4 +300,4 @@ function createLiveLane({
273
300
  return { setRoots, requestSources, tick, close, currentFiles: () => current };
274
301
  }
275
302
 
276
- module.exports = { SOURCE_QUEUE_CAP, SOURCE_BATCH_CAP, PRIORITY_SOURCE_CAP, DISCOVERY_ROOT_CAP, OPEN_DISCOVERY_CAP, KNOWN_SWEEP_LIMIT, BACKGROUND_BYTES, createLiveLane, normalizeRoots, rootsKey, claudeSubagentsDir };
303
+ module.exports = { SOURCE_QUEUE_CAP, SOURCE_BATCH_CAP, PRIORITY_SOURCE_CAP, DISCOVERY_ROOT_CAP, OPEN_DISCOVERY_CAP, KNOWN_SWEEP_LIMIT, BACKGROUND_BYTES, createLiveLane, normalizeRoots, rootsKey, claudeSubagentsDir, discoveryDirKey, LIVE_PROVIDERS };
@@ -7,11 +7,13 @@
7
7
  // (workers/read-pool-worker.js), the same way store.js is loaded only inside the writer worker.
8
8
  //
9
9
  // Entry points:
10
- // resolveScope(coreDb, ctmSessionId) -- CTM id -> the PROVIDER session ids whose
10
+ // resolveScope(coreDb, ctmSessionId) -- CTM id -> the PROVIDER session ids whose
11
11
  // ledger rows belong to it (the ledger's
12
12
  // session_id is the provider session id: the
13
- // Claude root transcript uuid, or the Codex
14
- // root thread id), plus the durable
13
+ // Claude root transcript uuid, the Codex
14
+ // root thread id, or -- for dsh -- the CTM
15
+ // session id itself, which is what its usage
16
+ // file is NAMED), plus the durable
15
17
  // source/mapping coverage of that scope.
16
18
  // sourceHints(scope) -- the internal (path-bearing) work hints the
17
19
  // runtime WP feeds to the writer. Never public.
@@ -31,7 +33,9 @@
31
33
  // `agent_session_id` equals it while a `ctm_sessions` row carries that id
32
34
  // (`LEFT JOIN ctm_sessions c ON c.id = a.agent_session_id`) -- role 'self';
33
35
  // * the CTM sessions in `worker_sessions WHERE parent_ctm_session_id = <ctm id>`
34
- // recurse the same mapping with role 'delegated' (depth <= 4, cycle-safe).
36
+ // recurse the same mapping with role 'delegated' (depth <= 4, cycle-safe);
37
+ // * dsh (WP-9, D-19): the ledger's session_id IS the CTM session id (the usage file is named by it);
38
+ // lib/usage-ledger/dsh-read.js owns that mapping.
35
39
  //
36
40
  // D-1 (durable membership): a worker's UI lifecycle never changes what it COST. A hidden,
37
41
  // completed or resumed worker keeps every provider root mapping it ever had, so the
@@ -78,12 +82,15 @@ const CALLS_MAX_LIMIT = 500;
78
82
  const SOURCE_CATALOG_DEFAULT_LIMIT = 200;
79
83
  const SOURCE_CATALOG_MAX_LIMIT = 200;
80
84
 
85
+ const { dshScopeEntry, dshCatalogSource } = require('./dsh-read');
86
+
81
87
  // The parser version the live ingest lane stamps (lib/usage-ledger/ingest.js PARSER_VERSION).
82
88
  // read.js keeps its own copy so the read model stays loadable without the transcript parsers;
83
89
  // a checkpoint stamped with any other version was written by an older READING of the record,
84
90
  // so its source is stale until the re-read lands. tests/usage-ledger-read.test.js pins the two
85
91
  // constants equal, so a parser bump cannot drift silently out of this comparison.
86
- const CURRENT_PARSER_VERSION = 7;
92
+ // v8 (deepseek-harness-acp-agent WP-9): the dsh provider's usage files are a new reading.
93
+ const CURRENT_PARSER_VERSION = 8;
87
94
 
88
95
  // usage_calls columns the public Call carries -- everything except source_file_id and
89
96
  // line_offset (file-storage detail, not part of the read model).
@@ -392,7 +399,7 @@ function resolveScope(coreDb, ctmSessionId) {
392
399
  COALESCE(${owner}${hasCtmSessions ? ', c.id' : ''}) AS ctm_session_id${sourceSelect}${tokenSelect}
393
400
  FROM agent_sessions a
394
401
  ${join}
395
- WHERE lower(COALESCE(a.provider, '')) IN ('claude', 'codex')`;
402
+ WHERE lower(COALESCE(a.provider, '')) IN ('claude', 'codex', 'dsh')`;
396
403
 
397
404
  // One probe row past the cap: the walk stops at exactly MAX_PROVIDER_SESSIONS and says so.
398
405
  const providersFor = coreDb.prepare(`${rootSelect}
@@ -429,7 +436,7 @@ function resolveScope(coreDb, ctmSessionId) {
429
436
  ORDER BY ctm_session_id LIMIT @limit`)
430
437
  : null;
431
438
 
432
- const entryFor = (row, role, ctmId) => ({
439
+ const entryFor = (row, role, ctmId) => (row.provider === 'dsh' ? dshScopeEntry(row, role, ctmId) : {
433
440
  session_id: String(row.session_id),
434
441
  provider: row.provider,
435
442
  ctm_session_id: row.ctm_session_id == null ? ctmId : row.ctm_session_id,
@@ -520,7 +527,8 @@ function resolveScope(coreDb, ctmSessionId) {
520
527
  }
521
528
 
522
529
  for (const row of providerSessions) {
523
- if (!hasPathColumn) {
530
+ // A dsh source path is derived (lib/usage-ledger/dsh-read.js), never read from a path column.
531
+ if (!hasPathColumn && row.provider !== 'dsh') {
524
532
  missingMappings.push({
525
533
  ctm_session_id: row.ctm_session_id,
526
534
  role: row.role,
@@ -633,11 +641,13 @@ function sourceHints(scope) {
633
641
  }
634
642
 
635
643
  /**
636
- * One keyset page of every supported (Claude/Codex) source the core db knows about, ordered by
644
+ * One keyset page of every supported (Claude/Codex/dsh) source the core db knows about, ordered by
637
645
  * agent_session_id (design D-2). Discovery is INDEPENDENT of terminal state: hidden workers and
638
646
  * native subagents are sources like any other. No prompt text, no transcript bytes -- metadata
639
647
  * only. `next` is the agent_session_id to resume AFTER, or null at the end of the catalog.
640
648
  *
649
+ * A dsh row's `path` is its USAGE file, not its transcript (lib/usage-ledger/dsh-read.js).
650
+ *
641
651
  * @param {object} coreDb an open READONLY core handle (the read pool's flip-aware routed handle)
642
652
  * @param {{after?: string, limit?: number}} [options]
643
653
  * @returns {{sources: Array<{provider: string, session_id: string, path: string, size: number|null,
@@ -657,10 +667,11 @@ function readSourceCatalog(coreDb, { after = '', limit = SOURCE_CATALOG_DEFAULT_
657
667
  SELECT a.agent_session_id AS session_id,
658
668
  lower(COALESCE(a.provider, '')) AS provider,
659
669
  a.jsonl_path AS path,
670
+ ${cols.has('ctm_session_id') ? 'a.ctm_session_id' : 'NULL'} AS ctm_session_id,
660
671
  ${cols.has('file_size') ? 'a.file_size' : 'NULL'} AS size,
661
672
  ${cols.has('modified_at') ? 'a.modified_at' : 'NULL'} AS modified_at
662
673
  FROM agent_sessions a
663
- WHERE lower(COALESCE(a.provider, '')) IN ('claude', 'codex')
674
+ WHERE lower(COALESCE(a.provider, '')) IN ('claude', 'codex', 'dsh')
664
675
  AND COALESCE(a.jsonl_path, '') != ''
665
676
  AND a.agent_session_id > @after
666
677
  ORDER BY a.agent_session_id
@@ -669,16 +680,14 @@ function readSourceCatalog(coreDb, { after = '', limit = SOURCE_CATALOG_DEFAULT_
669
680
  const hasMore = rows.length > effective;
670
681
  const kept = hasMore ? rows.slice(0, effective) : rows;
671
682
  const last = kept[kept.length - 1];
672
- return {
673
- sources: kept.map((row) => ({
674
- provider: row.provider,
675
- session_id: row.session_id == null ? '' : String(row.session_id),
676
- path: row.path == null ? '' : String(row.path),
677
- size: numberOrNull(row.size),
678
- modified_at: textOrNull(row.modified_at),
679
- })),
680
- next: hasMore && last ? String(last.session_id) : null,
681
- };
683
+ const sources = kept.map((row) => (row.provider === 'dsh' ? dshCatalogSource(row) : {
684
+ provider: row.provider,
685
+ session_id: row.session_id == null ? '' : String(row.session_id),
686
+ path: row.path == null ? '' : String(row.path),
687
+ size: numberOrNull(row.size),
688
+ modified_at: textOrNull(row.modified_at),
689
+ })).filter(Boolean);
690
+ return { sources, next: hasMore && last ? String(last.session_id) : null };
682
691
  }
683
692
 
684
693
  // A reported row's model_usage_json is written by the ingester; a torn or absent value reads
@@ -15,7 +15,7 @@
15
15
 
16
16
  const { createMigrationRunner, pragmaVersionStore } = require('../../../shared/db-migration-runner');
17
17
 
18
- const USAGE_LEDGER_SCHEMA_VERSION = 8;
18
+ const USAGE_LEDGER_SCHEMA_VERSION = 9;
19
19
 
20
20
  // The complete v1 schema, in one place. Each statement is idempotent
21
21
  // (IF NOT EXISTS) so a re-run of the baseline against a partially-created file
@@ -348,6 +348,62 @@ function createV8(db) {
348
348
  `);
349
349
  }
350
350
 
351
+ // v9 (deepseek-harness-acp-agent WP-9, design D-19 / §9b L-3): `usage_files.provider` must admit
352
+ // 'dsh'. D-19 gives dsh spend a source of its own -- the model proxy appends one row per completed
353
+ // lead request to `<dataDir>/dsh-usage/<ctmSessionId>.jsonl` -- and the ledger ingests that file
354
+ // under `provider='dsh'`, which the v1 CHECK `provider IN ('claude','codex')` refuses by SQLite
355
+ // itself. SQLite cannot ALTER a CHECK, so the table is REBUILT: rename, recreate with the widened
356
+ // CHECK, copy every row and every column, drop the staging table. v1 is left untouched: it stays the
357
+ // published record of the schema IT created.
358
+ //
359
+ // Idempotence is decided by the SCHEMA, never by the version counter: a table whose sqlite_master.sql
360
+ // already admits 'dsh' is left byte-for-byte alone. So a fresh file (the runner's isFresh path runs
361
+ // the whole baseline, v9 included), a restored snapshot, and a file that already took this step all
362
+ // converge on the same shape, and a re-run of the ladder body writes nothing.
363
+ //
364
+ // The recreate text is the table's OWN stored DDL with only the provider CHECK widened, so no column,
365
+ // default, ordinal position or other constraint can drift from what the database already has. Copying
366
+ // by position is what makes that mandatory, and `usage_files.file_id` is an INTEGER PRIMARY KEY
367
+ // (rowid alias), so the copy preserves every id `usage_calls.source_file_id` already points at.
368
+ //
369
+ // The v9 step writes DDL only: nothing here inserts a 'dsh' row.
370
+ const DSH_USAGE_FILES_TABLE = 'usage_files';
371
+ // The exact v1 text of the provider CHECK. v1 is frozen, so this literal is stable; a table whose DDL
372
+ // does not match it is refused rather than silently rebuilt with a guessed shape.
373
+ const PROVIDER_CHECK = "CHECK(provider IN ('claude','codex'))";
374
+ const PROVIDER_CHECK_DSH = "CHECK(provider IN ('claude','codex','dsh'))";
375
+
376
+ function tableSql(db, table) {
377
+ const row = db.prepare("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = ?").get(table);
378
+ return row && typeof row.sql === 'string' ? row.sql : null;
379
+ }
380
+
381
+ // The EXPLICIT indexes of a table, in creation order. A PRIMARY KEY's implicit autoindex has sql =
382
+ // NULL and must never be recreated by name, so it is filtered out. Captured BEFORE the rename, while
383
+ // each index's SQL still names the real table: ALTER TABLE RENAME would rewrite that reference to the
384
+ // staging name, and the DROP of the staging table takes the indexes with it.
385
+ function tableIndexes(db, table) {
386
+ return db.prepare("SELECT name, sql FROM sqlite_master WHERE type = 'index' AND tbl_name = ? AND sql IS NOT NULL").all(table)
387
+ .map((row) => ({ name: row.name, sql: row.sql }));
388
+ }
389
+
390
+ function createV9(db) {
391
+ const sql = tableSql(db, DSH_USAGE_FILES_TABLE);
392
+ if (!sql) return; // no such table: nothing to widen
393
+ if (sql.includes("'dsh'")) return; // already widened: idempotent on schema
394
+ const widened = sql.split(PROVIDER_CHECK).join(PROVIDER_CHECK_DSH);
395
+ if (widened === sql) {
396
+ throw new Error(`v9: ${DSH_USAGE_FILES_TABLE} has no claude/codex provider CHECK to widen`);
397
+ }
398
+ const staging = `${DSH_USAGE_FILES_TABLE}__v9`;
399
+ const indexes = tableIndexes(db, DSH_USAGE_FILES_TABLE);
400
+ db.exec(`ALTER TABLE ${DSH_USAGE_FILES_TABLE} RENAME TO ${staging}`);
401
+ db.exec(widened);
402
+ db.exec(`INSERT INTO ${DSH_USAGE_FILES_TABLE} SELECT * FROM ${staging}`);
403
+ db.exec(`DROP TABLE ${staging}`);
404
+ for (const index of indexes) db.exec(index.sql);
405
+ }
406
+
351
407
  // Built lazily on first use so merely requiring this module has no side effects
352
408
  // (no module-level runner construction, no DDL, no env reads).
353
409
  let _runner = null;
@@ -370,6 +426,7 @@ function runner() {
370
426
  6: { up: createV6, label: 'v6 delegation edges' },
371
427
  7: { up: createV7, label: 'v7 delegation launch attempts' },
372
428
  8: { up: createV8, label: 'v8 claude re-read snapshot' },
429
+ 9: { up: createV9, label: 'v9 dsh provider' },
373
430
  },
374
431
  baseline: (db) => {
375
432
  createV1(db);
@@ -380,6 +437,9 @@ function runner() {
380
437
  createV6(db);
381
438
  createV7(db);
382
439
  createV8(db);
440
+ // The baseline must produce the FULL current shape: for a fresh file this widens the v1
441
+ // provider CHECK in place (the table is empty, so the rebuild is a no-op on data).
442
+ createV9(db);
383
443
  },
384
444
  versionStore: pragmaVersionStore(),
385
445
  });
@@ -3,6 +3,8 @@
3
3
  // One lifecycle-owned background round, driven by the existing client's hint tick. No interval,
4
4
  // transcript reads or model call on the configuration/status path.
5
5
  const { applyJudgeAnswer, JUDGE_QUESTIONS } = require('./task-grouping');
6
+ // D-2: the ONE model-id rule (docs/lead-model-id-rule-at-tilde-prefix-2026-10-28.html §5).
7
+ const { isValidOverrideModel } = require('../model-id-rule');
6
8
  const PROBE_INTERVAL_MS = 5000;
7
9
  const STATUS_TIMEOUT_MS = 2000;
8
10
  const DECISION_TIMEOUT_MS = 5000;
@@ -12,12 +14,12 @@ const BACKOFF_MS = 60000;
12
14
  // maker policy. Validated here (the ONE place) so the coordinator snapshot and the
13
15
  // /api/usage/task-grouping/status reader cannot disagree about what is safe to publish.
14
16
  const ROUTE_PROVIDER_ID_RE = /^[A-Za-z0-9._:@+-]{1,160}$/;
15
- const ROUTE_MODEL_ID_RE = /^[~A-Za-z0-9._:\/@+-]{1,160}$/;
16
17
  const ROUTE_POLICIES = new Set(['auto', 'direct', 'portkey']);
17
18
  function routeDetail(json) {
18
19
  return {
19
20
  provider_id: typeof json?.provider_id === 'string' && ROUTE_PROVIDER_ID_RE.test(json.provider_id) ? json.provider_id : null,
20
- model_id: typeof json?.model_id === 'string' && ROUTE_MODEL_ID_RE.test(json.model_id) ? json.model_id : null,
21
+ // D-2: the ONE model-id rule (it also refuses a leading dash, which the forked rule admitted).
22
+ model_id: typeof json?.model_id === 'string' && isValidOverrideModel(json.model_id) ? json.model_id : null,
21
23
  policy: ROUTE_POLICIES.has(json?.policy) ? json.policy : null,
22
24
  };
23
25
  }