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
@@ -8,12 +8,64 @@
8
8
  // legitimately reaches deep into server.js internals (perf tracker, main-DB
9
9
  // census, event-loop lag state, etc.) — the handler body stays put, only the
10
10
  // route registration/dispatch wiring moves here.
11
+ //
12
+ // Landing-throughput WP-5 shard S4 (design doc §5 D-10 "move-only", §6 "Shard
13
+ // module seam", §8 WP-5, §9 S-21..S-27): two more handlers move out of server.js
14
+ // VERBATIM -- apiDiagnosticsClientHealth (POST /api/diagnostics/client-health)
15
+ // and apiDiagnosticsPerfSummary (GET /api/diagnostics/perf-summary). Each body is
16
+ // identical to server.js's declaration except for the `_deps.` prefix on every
17
+ // identifier that used to resolve to a server.js top-level binding (D-10 rule 1),
18
+ // proven by `node bin/move-only-check.js`. Their route-table entries now call the
19
+ // module-local declarations directly (setup.js's apiSetupTestKey precedent) rather
20
+ // than a configured handler. Injected deps, per function:
21
+ // apiDiagnosticsClientHealth: RESUME_CLIENT_BEACON_METRICS, _clientHealthReports,
22
+ // _ctmDbWorker, _ctmReadPool, _ctmWorktreeWorker, _eventLoopIncidents,
23
+ // _mainloopDiagLogger, _providerIdFromCmd, _recentClientHealthIncidents,
24
+ // _recentClientHealthLifecycleEvents, _resumeClientMetricRollup,
25
+ // _stateSyncClientLedger, _typingInteractionTelemetryEnabled,
26
+ // correlateClientHealthIncident, firstMissingStateSyncBoundary,
27
+ // formatInputPerformanceHeadline, readJsonRequestBody, recordSessionDiagnostic,
28
+ // routeKeyboardIncidentToMainloopDiag, sanitizeClientHealthEnvelope,
29
+ // sanitizeClientHealthLifecycleEnvelope, sanitizeClientHealthMemorySample,
30
+ // sanitizeProcessLanes, sanitizeResumeClientMetric, sendJsonResponse, sessions,
31
+ // summarizePerformanceEvidence, telemetry
32
+ // apiDiagnosticsPerfSummary: CTM_BROWSER_QOS_V2_ENABLED,
33
+ // CTM_STREAM_STATUS_PATCH_V2_ENABLED, _controlSnapshot,
34
+ // _conversationUpdateOutboxBackoff, _ctmDbWorker, _ctmReadPool,
35
+ // _degradedOperations, _desktopIndex, _eventLoopIncidents,
36
+ // _exitedSnapshotBudget, _heartbeatDirtyQueue, _historyBreakerSnapshot,
37
+ // _historyWindowBudget, _loadPressure, _mainDbCensus, _msgResponseCache,
38
+ // _perfTracker, _readPerfCertificationLedger, _recentCatalogBuildInFlight,
39
+ // _recentCatalogGeneration, _recentCatalogMetrics, _recentCatalogPublished,
40
+ // _reclaimRecorder, _runtimeEventBuffer, _sessionResourceSampler,
41
+ // _stateSyncClientLedger, _stateSyncUnknownSubscriptionRejected, _syncIoCensus,
42
+ // _terminalSnapshotBudget, _terminalTailState, _transcriptRoots, _trimCadence,
43
+ // _typingInteractionTelemetryEnabled, _typingInteractionTraces,
44
+ // _visibleRevisionWatchdogEnabled, _walCheckpointMetrics, buildMemoryLedger,
45
+ // buildPerfSummary, dbModule, errorSink, fetchMcpGatewayLedgerStatus,
46
+ // getBackgroundLlmRouteStats, getBroadcastReasonStats, getMainThreadWriteEscapeStats, getWorkerLoopLag,
47
+ // headlessWorker, sessionDiagnostics, sessionHostManager
48
+ // Eight of those are MUTABLE server.js bindings, so server.js injects them as
49
+ // getters (D-10 rule 2), never copied at configure() time: _ctmDbWorker,
50
+ // _ctmReadPool, _ctmWorktreeWorker, _recentCatalogBuildInFlight,
51
+ // _recentCatalogGeneration, _recentCatalogPublished,
52
+ // _stateSyncUnknownSubscriptionRejected, _transcriptRoots. Everything else
53
+ // (functions, const objects never reassigned) is passed by value. `configure`
54
+ // therefore keeps the caller's options object by reference instead of
55
+ // destructuring it -- destructuring would invoke each getter once and freeze a
56
+ // null snapshot of the worker clients (session-messages.js's seam is the
57
+ // precedent).
11
58
 
12
59
 
13
60
  const { registerApiRoutes } = require('../api-router');
14
61
  const { buildRestartReceipts, readRestartLedgerEntries } = require('../restart-ledger');
15
62
  const { causalTraceDiagnostics } = require('../../../shared/causal-trace');
16
63
  const { readContextBudgetRecord } = require('../context-budget-record');
64
+ // WP-3 (box-idle-sleep-remote-typing-freezes-2026-11-01.html §8 WP-3): the frame-receipt exit
65
+ // counters live in lib/ws/state-sync.js and are read through its own getter -- this route takes a
66
+ // direct module dependency, never a server.js one (the counters are module-level, so the numbers are
67
+ // process-lifetime regardless of which server instance reads them).
68
+ const { getFrameReceiptExitCounts } = require('../ws/state-sync');
17
69
  const os = require('os');
18
70
 
19
71
  // WP-0b (attributable restarts, round-2 R1 residual): unlike every other handler in this
@@ -63,6 +115,27 @@ async function apiContextBudget(req, res, url) {
63
115
  }
64
116
  }
65
117
 
118
+ // WP-3 (box-idle-sleep-remote-typing-freezes-2026-11-01.html §5 D-7, §6 WP-3, §8 WP-3, §11 A-5):
119
+ // read-only readout of lib/ws/state-sync.js's process-lifetime frame-receipt exit counters. Like
120
+ // apiDiagnosticsRestarts/apiContextBudget above, it owns its FULL body here -- it needs no server.js
121
+ // internals, only the state-sync module's getter, so there is nothing to gain from the usual
122
+ // dispatch-only split. `since` is process start in ms (the counters are module-level and the module
123
+ // loads at boot), so a reader can age the numbers without a second request. Pure observation: this
124
+ // handler neither reads nor touches the per-peer rate limiter or any receipt handling.
125
+ async function apiDiagnosticsReceiptExits(req, res, url) {
126
+ try {
127
+ res.writeHead(200, { 'Content-Type': 'application/json' });
128
+ res.end(JSON.stringify({
129
+ ok: true,
130
+ counts: getFrameReceiptExitCounts(),
131
+ since: Date.now() - Math.round(process.uptime() * 1000),
132
+ }));
133
+ } catch (e) {
134
+ res.writeHead(500, { 'Content-Type': 'application/json' });
135
+ res.end(JSON.stringify({ ok: false, error: e.message }));
136
+ }
137
+ }
138
+
66
139
  function causalTraceDiagnosticPayload(traceIdInput) {
67
140
  const traceId = String(traceIdInput || '');
68
141
  if (!/^[0-9a-f]{32}$/.test(traceId) || /^0+$/.test(traceId)) {
@@ -73,72 +146,8 @@ function causalTraceDiagnosticPayload(traceIdInput) {
73
146
 
74
147
  let _deps = null;
75
148
 
76
- function configure({
77
- apiDiagnosticsClientFreeze,
78
- apiDiagnosticsClientHealthPing,
79
- apiDiagnosticsLifecycle,
80
- apiDiagnosticsSessionResources,
81
- apiDiagnosticsSessionDiagnostics,
82
- apiDiagnosticsClientHealthRecent,
83
- apiDiagnosticsResumeMetrics,
84
- apiDiagnosticsRestartOutcomes,
85
- apiDiagnosticsTestBlockMain,
86
- pairableOrigins,
87
- apiDiagnosticsTestCodexBurst,
88
- apiDiagnosticsClientHealth,
89
- apiDiagnosticsWriteLock,
90
- apiDiagnosticsVersion,
91
- apiDiagnosticsDbOwner,
92
- apiDiagnosticsConversationFreshness,
93
- apiDiagnosticsConversationImport,
94
- apiDiagnosticsDbCensus,
95
- apiDiagnosticsMainDbTop,
96
- apiDiagnosticsUploads,
97
- apiDiagnosticsTestDbOwnerWedge,
98
- apiDiagnosticsPerfSummary,
99
- apiDiagnosticsMainDbBudget,
100
- apiDiagnosticsFsCensus,
101
- apiDiagnosticsEventLoop,
102
- apiDiagnosticsRestoreAdmission,
103
- apiDiagnosticsMainWatchdog,
104
- apiDiagnosticsWatcher,
105
- apiDiagnosticsRenderChurn,
106
- apiDiagnosticsSessionViewers,
107
- apiDiagnosticsSubmissions,
108
- } = {}) {
109
- _deps = {
110
- apiDiagnosticsClientFreeze,
111
- apiDiagnosticsClientHealthPing,
112
- apiDiagnosticsLifecycle,
113
- apiDiagnosticsSessionResources,
114
- apiDiagnosticsSessionDiagnostics,
115
- apiDiagnosticsClientHealthRecent,
116
- apiDiagnosticsResumeMetrics,
117
- apiDiagnosticsRestartOutcomes,
118
- apiDiagnosticsTestBlockMain,
119
- pairableOrigins,
120
- apiDiagnosticsTestCodexBurst,
121
- apiDiagnosticsClientHealth,
122
- apiDiagnosticsWriteLock,
123
- apiDiagnosticsVersion,
124
- apiDiagnosticsDbOwner,
125
- apiDiagnosticsConversationFreshness,
126
- apiDiagnosticsConversationImport,
127
- apiDiagnosticsDbCensus,
128
- apiDiagnosticsMainDbTop,
129
- apiDiagnosticsUploads,
130
- apiDiagnosticsTestDbOwnerWedge,
131
- apiDiagnosticsPerfSummary,
132
- apiDiagnosticsMainDbBudget,
133
- apiDiagnosticsFsCensus,
134
- apiDiagnosticsEventLoop,
135
- apiDiagnosticsRestoreAdmission,
136
- apiDiagnosticsMainWatchdog,
137
- apiDiagnosticsWatcher,
138
- apiDiagnosticsRenderChurn,
139
- apiDiagnosticsSessionViewers,
140
- apiDiagnosticsSubmissions,
141
- };
149
+ function configure(deps = {}) {
150
+ _deps = deps;
142
151
  }
143
152
 
144
153
  // RL-WP-4 (render-lane design D-6): a test harness DECLARES the origins a hermetic server may
@@ -202,7 +211,7 @@ function register() {
202
211
  { method: 'POST', path: '/api/diagnostics/test/pairable-origins', handler: (req, res, url) => apiDiagnosticsTestPairableOriginsSet(req, res, url) },
203
212
  { method: 'DELETE', path: '/api/diagnostics/test/pairable-origins', handler: (req, res, url) => apiDiagnosticsTestPairableOriginsClear(req, res, url) },
204
213
  { method: 'POST', path: '/api/diagnostics/test/codex-burst', handler: (req, res, url) => _deps.apiDiagnosticsTestCodexBurst(req, res, url) },
205
- { method: 'POST', path: '/api/diagnostics/client-health', handler: (req, res, url) => _deps.apiDiagnosticsClientHealth(req, res, url) },
214
+ { method: 'POST', path: '/api/diagnostics/client-health', handler: (req, res, url) => apiDiagnosticsClientHealth(req, res, url) },
206
215
  { method: 'GET', path: '/api/diagnostics/write-lock', handler: (req, res, url) => _deps.apiDiagnosticsWriteLock(req, res, url) },
207
216
  { method: 'GET', path: '/api/diagnostics/version', handler: (req, res, url) => _deps.apiDiagnosticsVersion(req, res, url) },
208
217
  { method: 'GET', path: '/api/diagnostics/db-owner', handler: (req, res, url) => _deps.apiDiagnosticsDbOwner(req, res, url) },
@@ -212,7 +221,7 @@ function register() {
212
221
  { method: 'GET', path: '/api/diagnostics/main-db-top', handler: (req, res, url) => _deps.apiDiagnosticsMainDbTop(req, res, url) },
213
222
  { method: 'GET', path: '/api/diagnostics/uploads', handler: (req, res, url) => _deps.apiDiagnosticsUploads(req, res, url) },
214
223
  { method: 'POST', path: '/api/diagnostics/test/db-owner-wedge', handler: (req, res, url) => _deps.apiDiagnosticsTestDbOwnerWedge(req, res, url) },
215
- { method: 'GET', path: '/api/diagnostics/perf-summary', handler: (req, res, url) => _deps.apiDiagnosticsPerfSummary(req, res, url) },
224
+ { method: 'GET', path: '/api/diagnostics/perf-summary', handler: (req, res, url) => apiDiagnosticsPerfSummary(req, res, url) },
216
225
  { method: 'GET', path: '/api/diagnostics/main-db-budget', handler: (req, res, url) => _deps.apiDiagnosticsMainDbBudget(req, res, url) },
217
226
  { method: 'GET', path: '/api/diagnostics/fs-census', handler: (req, res, url) => _deps.apiDiagnosticsFsCensus(req, res, url) },
218
227
  { method: 'GET', path: '/api/diagnostics/event-loop', handler: (req, res, url) => _deps.apiDiagnosticsEventLoop(req, res, url) },
@@ -223,8 +232,479 @@ function register() {
223
232
  { method: 'GET', path: '/api/diagnostics/session-viewers', handler: (req, res, url) => _deps.apiDiagnosticsSessionViewers(req, res, url) },
224
233
  { method: 'GET', path: '/api/diagnostics/submissions', handler: (req, res, url) => _deps.apiDiagnosticsSubmissions(req, res, url) },
225
234
  { method: 'GET', path: '/api/diagnostics/restarts', handler: (req, res, url) => apiDiagnosticsRestarts(req, res, url) },
235
+ // WP-3 (box-idle-sleep-remote-typing-freezes-2026-11-01.html §8 WP-3): process-lifetime
236
+ // frame-receipt exit counters (read-only; the next live measurement names where a hub-typed
237
+ // keystroke's `painted` receipt dies on the box).
238
+ { method: 'GET', path: '/api/diagnostics/receipt-exits', handler: (req, res, url) => apiDiagnosticsReceiptExits(req, res, url) },
226
239
  { method: 'GET', path: '/api/context-budget', handler: (req, res, url) => apiContextBudget(req, res, url) },
227
240
  ]);
228
241
  }
229
242
 
230
- module.exports = { apiDiagnosticsTestPairableOriginsSet, apiDiagnosticsTestPairableOriginsClear, causalTraceDiagnosticPayload, apiContextBudget, configure, register };
243
+ async function apiDiagnosticsClientHealth(req, res, url) {
244
+ try {
245
+ // Fresh-load LoAF evidence can contain up to 20 frames x 8 bounded script
246
+ // descriptors. Admit that bounded pre-sanitization shape, then let
247
+ // sanitizeClientHealthEnvelope enforce the retained field and string caps.
248
+ const raw = await _deps.readJsonRequestBody(req, { limitBytes: 128 * 1024 });
249
+ // Layered-resume and switch beacons use a compact metric shape on the existing endpoint.
250
+ if (raw && typeof raw === 'object' && _deps.RESUME_CLIENT_BEACON_METRICS.has(raw.metric)) {
251
+ const metric = _deps.sanitizeResumeClientMetric(raw);
252
+ if (!metric) {
253
+ return _deps.sendJsonResponse(res, 400, { ok: false, error: 'sessionId and ms are required' });
254
+ }
255
+ const { sessionId, ms } = metric;
256
+ const rateKey = `resume-metric:${sessionId}`;
257
+ const now = Date.now();
258
+ const prior = _deps._clientHealthReports.get(rateKey) || { windowStartedAt: now, count: 0, events: new Set() };
259
+ if (now - prior.windowStartedAt >= 60_000) {
260
+ prior.windowStartedAt = now;
261
+ prior.count = 0;
262
+ prior.events.clear();
263
+ }
264
+ if (prior.count >= 240) return _deps.sendJsonResponse(res, 429, { ok: false, error: 'client-health rate limit exceeded' });
265
+ prior.count += 1;
266
+ _deps._clientHealthReports.set(rateKey, prior);
267
+ if (_deps._clientHealthReports.size > 500) _deps._clientHealthReports.delete(_deps._clientHealthReports.keys().next().value);
268
+ _deps._resumeClientMetricRollup.add(metric, now);
269
+ _deps.recordSessionDiagnostic(sessionId, 'resume-client-metric', metric);
270
+ try {
271
+ _deps.telemetry.track(metric.metric, {
272
+ duration_ms: ms,
273
+ l0_outcome: metric.l0Outcome,
274
+ switch_path: metric.switchPath,
275
+ hot_reject_reason: metric.hotRejectReason,
276
+ kind: metric.kind,
277
+ frozen_frame_path: metric.path,
278
+ frozen_frame_font_faithful: metric.fontFaithful,
279
+ frozen_frame_cover_ms: metric.coverMs,
280
+ frozen_frame_max_age_hit: metric.maxAgeHit,
281
+ frozen_frame_activation_epoch: metric.activationEpoch,
282
+ });
283
+ } catch {}
284
+ return _deps.sendJsonResponse(res, 200, { ok: true });
285
+ }
286
+ // WP-1 (design/ctm-memory-footprint-round2-2026-08-14 §6): Poisson-sampled renderer
287
+ // memory beacon riding this same channel. Unprotected 'client-memory' event — the
288
+ // default 120/session ring + DB caps are fine for a trend metric (no db.js change).
289
+ // Kept ahead of the lifecycle branch below (own recordType tag, own rate key) so
290
+ // neither route's dedupe/count bookkeeping observes the other's traffic.
291
+ if (raw?.recordType === 'client-memory') {
292
+ const memorySample = _deps.sanitizeClientHealthMemorySample(raw);
293
+ if (!memorySample) {
294
+ return _deps.sendJsonResponse(res, 400, { ok: false, error: 'invalid client-memory sample' });
295
+ }
296
+ const rateKey = `client-memory:${memorySample.sessionId}`;
297
+ const now = Date.now();
298
+ const prior = _deps._clientHealthReports.get(rateKey) || { windowStartedAt: now, count: 0, events: new Set() };
299
+ if (now - prior.windowStartedAt >= 60_000) {
300
+ prior.windowStartedAt = now;
301
+ prior.count = 0;
302
+ prior.events.clear();
303
+ }
304
+ if (prior.count >= 20) return _deps.sendJsonResponse(res, 429, { ok: false, error: 'client-health rate limit exceeded' });
305
+ prior.count += 1;
306
+ _deps._clientHealthReports.set(rateKey, prior);
307
+ if (_deps._clientHealthReports.size > 500) _deps._clientHealthReports.delete(_deps._clientHealthReports.keys().next().value);
308
+ _deps.recordSessionDiagnostic(memorySample.sessionId, 'client-memory', memorySample.sample);
309
+ return _deps.sendJsonResponse(res, 200, { ok: true, recordType: 'client-memory' });
310
+ }
311
+ if (raw?.recordType === 'lifecycle') {
312
+ const lifecycle = _deps.sanitizeClientHealthLifecycleEnvelope(raw);
313
+ if (!lifecycle) {
314
+ return _deps.sendJsonResponse(res, 400, { ok: false, error: 'invalid lifecycle envelope' });
315
+ }
316
+ const rateKey = `lifecycle:${lifecycle.tabIdHash}:${lifecycle.runIdHash}`;
317
+ const now = Date.now();
318
+ const prior = _deps._clientHealthReports.get(rateKey) || { windowStartedAt: now, count: 0, events: new Set() };
319
+ if (now - prior.windowStartedAt >= 60_000) {
320
+ prior.windowStartedAt = now;
321
+ prior.count = 0;
322
+ prior.events.clear();
323
+ }
324
+ const duplicate = prior.events.has(lifecycle.eventId)
325
+ || _deps._recentClientHealthLifecycleEvents.has(lifecycle.eventId);
326
+ if (!duplicate && prior.count >= 20) {
327
+ return _deps.sendJsonResponse(res, 429, { ok: false, error: 'client-health rate limit exceeded' });
328
+ }
329
+ if (!duplicate) prior.count += 1;
330
+ prior.events.add(lifecycle.eventId);
331
+ _deps._clientHealthReports.set(rateKey, prior);
332
+ if (_deps._clientHealthReports.size > 500) _deps._clientHealthReports.delete(_deps._clientHealthReports.keys().next().value);
333
+ _deps._recentClientHealthLifecycleEvents.upsert(lifecycle);
334
+ return _deps.sendJsonResponse(res, 200, { ok: true, duplicate, recordType: 'lifecycle' });
335
+ }
336
+ const envelope = _deps.sanitizeClientHealthEnvelope(raw);
337
+ if (!envelope.tabId || !envelope.runId || !envelope.incidentId) {
338
+ return _deps.sendJsonResponse(res, 400, { ok: false, error: 'tabId, runId, and incidentId are required' });
339
+ }
340
+ const rateKey = `${envelope.tabId}:${envelope.runId}`;
341
+ const now = Date.now();
342
+ const prior = _deps._clientHealthReports.get(rateKey) || { windowStartedAt: now, count: 0, events: new Set() };
343
+ if (now - prior.windowStartedAt >= 60_000) {
344
+ prior.windowStartedAt = now;
345
+ prior.count = 0;
346
+ prior.events.clear();
347
+ }
348
+ const eventKey = `${envelope.incidentId}:${envelope.phase}`;
349
+ if (prior.events.has(eventKey)) return _deps.sendJsonResponse(res, 200, { ok: true, duplicate: true });
350
+ if (prior.count >= 20) return _deps.sendJsonResponse(res, 429, { ok: false, error: 'client-health rate limit exceeded' });
351
+ prior.count += 1;
352
+ prior.events.add(eventKey);
353
+ _deps._clientHealthReports.set(rateKey, prior);
354
+ if (_deps._clientHealthReports.size > 500) _deps._clientHealthReports.delete(_deps._clientHealthReports.keys().next().value);
355
+
356
+ for (const row of envelope.stateSync) _deps._stateSyncClientLedger.mergeClientSnapshot(row);
357
+ const correlated = _deps.correlateClientHealthIncident(envelope.incident, _deps._eventLoopIncidents, envelope.clock);
358
+ // S-30: `latestForSession` now throws on an empty clientId -- an untrusted client-health
359
+ // payload can omit both `row.clientId` and `websocket.clientId`; treat that as "no row"
360
+ // rather than let a malformed beat 500 this endpoint.
361
+ const mergedRows = envelope.stateSync
362
+ .map((row) => {
363
+ const beatClientId = String(row.clientId || envelope.websocket.clientId || '');
364
+ return beatClientId ? _deps._stateSyncClientLedger.latestForSession(row.sessionId, beatClientId) : null;
365
+ })
366
+ .filter(Boolean);
367
+ const firstMissingBoundary = mergedRows.map(row => ({
368
+ sessionId: row.sessionId,
369
+ clientId: row.clientId,
370
+ socketGeneration: row.socketGeneration,
371
+ boundary: row.firstMissingBoundary || _deps.firstMissingStateSyncBoundary(row),
372
+ })).find(row => row.boundary) || null;
373
+ const processLanes = _deps.sanitizeProcessLanes({
374
+ readPool: _deps._ctmReadPool?.getStatus?.() || null,
375
+ worktreeWorker: _deps._ctmWorktreeWorker?.getStatus?.() || null,
376
+ dbOwner: _deps._ctmDbWorker?.getStatus?.() || null,
377
+ });
378
+ const performanceSummary = _deps.summarizePerformanceEvidence(envelope.performance);
379
+ if (envelope.incident.kind === 'interaction_stall' && envelope.phase === 'detected') {
380
+ const diagnosticSessionId = envelope.activeSessionId
381
+ || envelope.deliveryGap?.sessionId
382
+ || envelope.stateSync[0]?.sessionId
383
+ || '';
384
+ const diagnosticSession = diagnosticSessionId ? _deps.sessions.get(diagnosticSessionId) : null;
385
+ const interactionStallMs = Math.max(
386
+ Number(envelope.incident.mainGapMs || 0),
387
+ Number(performanceSummary.inputDelayMs || 0),
388
+ );
389
+ if (diagnosticSessionId) _deps.recordSessionDiagnostic(diagnosticSessionId, 'interaction-stall', {
390
+ durationMs: interactionStallMs,
391
+ provider: diagnosticSession?._providerId || _deps._providerIdFromCmd(diagnosticSession?.cmd || ''),
392
+ activeTab: diagnosticSessionId === envelope.activeSessionId,
393
+ kind: envelope.incident.kind,
394
+ });
395
+ // WP-3 (docs/mainloop-composer-freeze-2026-08-20.html §5 D-6, §6, §8 WP-3): the client
396
+ // floor dropped 500ms -> 200ms (public/js/client-health-core.js); a keyboard incident
397
+ // that clears the NEW 200ms bar but not the old 500ms one is exactly the class this WP
398
+ // exists to make visible, so it gets its own [mainloop-diag] line independent of the
399
+ // per-session recordSessionDiagnostic row above.
400
+ _deps.routeKeyboardIncidentToMainloopDiag(_deps._mainloopDiagLogger, {
401
+ kind: envelope.incident.kind,
402
+ ms: interactionStallMs,
403
+ });
404
+ }
405
+ const inputPerformanceHeadline = _deps.formatInputPerformanceHeadline(performanceSummary, {
406
+ enabled: _deps._typingInteractionTelemetryEnabled,
407
+ });
408
+ // S-6: renderCount/sincePaint above only count terminal render spans — indistinguishable
409
+ // from "just painted" when a tab never rendered a span at all. paint.count is the true
410
+ // rAF-presented counter the paint-drought classifier itself uses (client-health-core.js).
411
+ const paintSnapshot = envelope.paint || { count: 0, lastPresentedAtMs: 0 };
412
+ const paintSincePresentedMs = paintSnapshot.lastPresentedAtMs > 0
413
+ ? Math.max(0, Math.round(now - paintSnapshot.lastPresentedAtMs))
414
+ : 0;
415
+ console.warn(
416
+ `[client-health] incident=${envelope.incidentId} phase=${envelope.phase} domain=${correlated.domain} kind=${correlated.kind} `
417
+ + `serverIncident=${correlated.serverIncidentId || 'none'} tab=${envelope.tabId.slice(0, 16)} `
418
+ + `run=${envelope.runId.slice(0, 16)} boundary=${firstMissingBoundary?.boundary || 'none'} `
419
+ + `clientBoundary=${envelope.deliveryGap?.boundary || 'none'} `
420
+ + `wsBuffered=${envelope.websocket.bufferedAmount} worktreeQueued=${processLanes.worktreeWorker?.queued || 0} `
421
+ + `clientBuild=${String(envelope.clientBuild?.loadedAsset || envelope.clientBuild?.assetEpoch || 'unknown').slice(0, 16)} `
422
+ + `renderer=${envelope.renderer.webglContexts}webgl/${envelope.renderer.activeTerminals}active/`
423
+ + `${envelope.renderer.hiddenTerminals}hidden/${envelope.renderer.sessions}sess `
424
+ + inputPerformanceHeadline
425
+ + `loaf=${performanceSummary.loafMs}/${performanceSummary.blockingMs}/${performanceSummary.pauseMs}ms `
426
+ + `loafScript=${performanceSummary.scriptSource || 'none'}:${performanceSummary.scriptFunction || 'anonymous'}`
427
+ + `@${performanceSummary.scriptCharPos}(offset-advisory)/${performanceSummary.scriptMs}ms `
428
+ + `renderSpan=${performanceSummary.renderSpan || 'none'}/${performanceSummary.renderSpanMs}ms `
429
+ + `renderCount=${envelope.performance.renderSpanCount} sincePaint=${Math.round(envelope.performance.msSinceLastPaint)}ms `
430
+ + `paint=${paintSnapshot.count}/${paintSincePresentedMs}ms `
431
+ + `wake=${Number(envelope.incident.wake || 0)}`,
432
+ );
433
+ _deps._recentClientHealthIncidents.push(Object.freeze({
434
+ receivedAt: now,
435
+ incidentId: envelope.incidentId,
436
+ tabId: envelope.tabId,
437
+ runId: envelope.runId,
438
+ phase: envelope.phase,
439
+ classification: correlated.kind,
440
+ incidentDomain: correlated.domain,
441
+ serverIncidentId: correlated.serverIncidentId || '',
442
+ firstMissingBoundary,
443
+ clientDeliveryGap: envelope.deliveryGap,
444
+ mainGapMs: envelope.incident.mainGapMs,
445
+ workerTickDeltaMs: envelope.incident.workerTickDeltaMs,
446
+ lifecycle: envelope.lifecycle,
447
+ wake: envelope.wake,
448
+ websocket: envelope.websocket,
449
+ clientBuild: envelope.clientBuild,
450
+ collector: envelope.performance.collector,
451
+ performance: envelope.performance,
452
+ renderer: envelope.renderer,
453
+ stateSync: mergedRows,
454
+ processLanes,
455
+ }));
456
+ if (_deps._recentClientHealthIncidents.length > 100) {
457
+ _deps._recentClientHealthIncidents.splice(0, _deps._recentClientHealthIncidents.length - 100);
458
+ }
459
+ return _deps.sendJsonResponse(res, 200, {
460
+ ok: true,
461
+ schemaVersion: 1,
462
+ classification: correlated.kind,
463
+ incidentDomain: correlated.domain,
464
+ serverIncidentId: correlated.serverIncidentId || '',
465
+ firstMissingBoundary,
466
+ clientDeliveryGap: envelope.deliveryGap,
467
+ });
468
+ } catch (error) {
469
+ return _deps.sendJsonResponse(res, 400, { ok: false, error: error.message });
470
+ }
471
+ }
472
+
473
+ async function apiDiagnosticsPerfSummary(req, res, url) {
474
+ try {
475
+ const censusLimit = Math.max(1, Math.min(256, Number(url?.searchParams?.get('censusLimit')) || 10));
476
+ const eventLoop = _deps._perfTracker.getEventLoopStats();
477
+ const pressure = _deps._loadPressure.getStatus();
478
+ const readPool = _deps._ctmReadPool?.getStatus?.() || null;
479
+ const dbOwner = _deps._ctmDbWorker?.getStatus?.() || null;
480
+ const providerIndex = _deps._desktopIndex.status();
481
+ const processTreeRssBytes = Math.round(
482
+ (Number(_deps._sessionResourceSampler.snapshot?.processTree?.rssMB) || 0) * 1024 * 1024,
483
+ );
484
+ // (a) physical footprint + (b) mirror visibility (design doc §6a/§6b). Both are
485
+ // async and spawned off the main thread (footprint via child_process; the mirrors
486
+ // report via IPC round-trip to the pooled host process) — AC-0d never blocks the
487
+ // main thread synchronously for these. Both resolve to an empty/null value on
488
+ // any failure so perf-summary never 500s because a diagnostic sample was slow.
489
+ //
490
+ // D-1 (§8 WP-0): prefer the 60s sweep's own main-process footprint sample
491
+ // (_sampleLifecycleResources -> _sessionResourceSampler.snapshot.platform.main) --
492
+ // it is already fresh (sampled at most one sweep interval ago) whenever the sweep has
493
+ // run at least once recently, so most perf-summary polls no longer need a second
494
+ // on-demand footprint round trip for the same pid. Falls back to sampling on demand only
495
+ // when the cached sample is missing or older than 2 sweep intervals (sweep stalled,
496
+ // or this is the very first request before any sweep has run).
497
+ let physFootprintBytes = null;
498
+ const sweepSnapshot = _deps._sessionResourceSampler.snapshot;
499
+ const sweepIntervalMs = Math.max(1, Number(_deps._sessionResourceSampler.intervalMs) || 60_000);
500
+ const sweepAgeMs = Date.now() - (Number(sweepSnapshot?.sampledAt) || 0);
501
+ const sweepFootprintMB = sweepSnapshot?.platform?.main?.footprintMB;
502
+ if (sweepFootprintMB != null && sweepAgeMs <= sweepIntervalMs * 2) {
503
+ physFootprintBytes = Math.round(sweepFootprintMB * 1024 * 1024);
504
+ } else {
505
+ try {
506
+ const footprintMB = await _deps._sessionResourceSampler.sampleFootprintMB(process.pid);
507
+ physFootprintBytes = footprintMB == null ? null : Math.round(footprintMB * 1024 * 1024);
508
+ } catch (error) {
509
+ _deps.errorSink.report('diagnostics:physical-footprint-sample', error);
510
+ }
511
+ }
512
+ let mirrors = [];
513
+ try {
514
+ mirrors = await _deps.sessionHostManager.requestPoolMirrorsReport();
515
+ } catch (error) {
516
+ console.warn(`[diagnostics] mirrors report failed: ${error.message}`);
517
+ }
518
+ // WP-3/S5: pool gauges the pooled-host process alone can answer (frame-emitter
519
+ // session count, archive-store pending backlog) -- same IPC round trip shape as the
520
+ // mirrors report above, same zeroed-not-thrown failure posture.
521
+ // No try/catch: requestPoolPoolsReport rides _poolRequest, which RESOLVES to its
522
+ // zeroed emptyResult on timeout / no pool / send failure (lib/session-host-manager.js
523
+ // "never rejects" contract) -- a catch here would be a new bare-console tail for a
524
+ // path that cannot reject (G-2 count-ceiling, tests/db-owner-log-discipline.test.js).
525
+ const poolsFromHost = await _deps.sessionHostManager.requestPoolPoolsReport();
526
+ // WP-6 (§7 "Ledger visibility", §8 WP-6/AC-6a): the gateway's own
527
+ // /_status, normalized for the ledger by fetchMcpGatewayLedgerStatus()
528
+ // above. Same shape whether the gateway is up or down (zeroed, never
529
+ // thrown) -- there is nothing to catch here, matching the
530
+ // poolsFromHost "never rejects" contract just above.
531
+ const mcpGatewayStatus = await _deps.fetchMcpGatewayLedgerStatus();
532
+ // D-1 (§8 WP-0): sum of every footprint the sweep actually sampled this pass (main,
533
+ // pooled session-host, desktop index, and every live session) -- `null`, not 0, when
534
+ // the sweep has not sampled anything yet (first request before any sweep, or
535
+ // FOOTPRINT_TRUTH disabled) so the ledger cannot mistake "unsampled" for "0 bytes".
536
+ // There is no true tree-wide footprint measurement (`treeFootprintBytes`) to compare it
537
+ // against, so that field is left for a caller with that data; the RSS-based
538
+ // unattributedTreeRssBytes estimate keeps its current meaning until one exists.
539
+ let knownFootprintMB = 0;
540
+ let knownFootprintSamples = 0;
541
+ for (const entry of Object.values(sweepSnapshot?.platform || {})) {
542
+ if (entry?.footprintMB != null) { knownFootprintMB += Number(entry.footprintMB); knownFootprintSamples += 1; }
543
+ }
544
+ for (const session of sweepSnapshot?.sessions || []) {
545
+ if (session?.footprintMB != null) { knownFootprintMB += Number(session.footprintMB); knownFootprintSamples += 1; }
546
+ }
547
+ const knownFootprintBytes = knownFootprintSamples > 0 ? Math.round(knownFootprintMB * 1024 * 1024) : null;
548
+ // WP-4 (§8 WP-4): the same sweep-sampled pool-host footprint D-1 already folds into
549
+ // knownFootprintBytes above, handed to the ledger a second time so it can compute
550
+ // poolHostBaseMB (footprint minus every mirror's attributed bytes) -- null, not 0,
551
+ // when the sweep has not sampled the pool host this pass.
552
+ const poolHostFootprintMB = sweepSnapshot?.platform?.['session-host-pool']?.footprintMB ?? null;
553
+ const memory = _deps.buildMemoryLedger({
554
+ processMemory: process.memoryUsage(),
555
+ processTreeRssBytes,
556
+ physFootprintBytes,
557
+ footprints: { knownFootprintBytes, treeFootprintBytes: null },
558
+ mirrors,
559
+ poolHostFootprintMB,
560
+ caches: {
561
+ messageResponses: _deps._msgResponseCache.status(),
562
+ terminalSnapshots: _deps._terminalSnapshotBudget.status(),
563
+ exitedTerminalSnapshots: _deps._exitedSnapshotBudget.status(),
564
+ // WP-3/S5: same budget primitive as the two above, previously absent from the
565
+ // ledger entirely (§1d: "8 MB, same primitive as the ledgered budgets").
566
+ historyWindow: _deps._historyWindowBudget.status(),
567
+ },
568
+ // WP-3/S5: pools the ledger previously could not see at all. archivePending* comes
569
+ // from the pooled host process (main process holds no archive-store instance of its
570
+ // own -- the store lives in lib/headless-term-service.js, WP-1B). frameEmitterSessions
571
+ // rides the same round trip. sessionDiagnosticsSessions/terminalTailSessions are
572
+ // in-heap and read directly.
573
+ pools: {
574
+ archivePending: {
575
+ rows: poolsFromHost.archivePendingRows,
576
+ bytes: poolsFromHost.archivePendingBytes,
577
+ dropped: poolsFromHost.archiveDroppedRows,
578
+ },
579
+ frameEmitterSessions: poolsFromHost.frameEmitterSessions,
580
+ sessionDiagnosticsSessions: _deps.sessionDiagnostics.size(),
581
+ terminalTailSessions: _deps._terminalTailState.size,
582
+ // WP-6/AC-6a: {reachable, servers: {<name>: {state, pid, rssBytes,
583
+ // toolCalls, sessions, adopted}}, totalRssBytes, warmChildren} --
584
+ // zeroed (never thrown) when the gateway is unreachable.
585
+ mcpGateway: mcpGatewayStatus,
586
+ },
587
+ workers: {
588
+ readPool: (readPool?.workers || []).map((worker) => ({
589
+ kind: 'thread',
590
+ ...(worker.memory || {}),
591
+ openConnections: worker.openConnections,
592
+ })),
593
+ dbOwner: dbOwner?.memory ? {
594
+ kind: 'thread',
595
+ ...dbOwner.memory,
596
+ heapStatistics: dbOwner.heapStatistics || null,
597
+ openConnections: Number(dbOwner.openConnections) || 0,
598
+ } : null,
599
+ providerIndex: providerIndex.memory ? {
600
+ ...providerIndex.memory,
601
+ openConnections: 0,
602
+ } : null,
603
+ bulkService: readPool?.bulkService?.memory ? {
604
+ ...readPool.bulkService.memory,
605
+ openConnections: Number(readPool.bulkService.worker?.openConnections) || 0,
606
+ } : null,
607
+ },
608
+ });
609
+ const payload = _deps.buildPerfSummary({
610
+ eventLoop,
611
+ pressure,
612
+ headlessWorker: _deps.getWorkerLoopLag(), // WP-0 D-4
613
+ perfCertificationLedger: _deps._readPerfCertificationLedger(),
614
+ backgroundLlmRouteStats: _deps.getBackgroundLlmRouteStats(),
615
+ // `?censusLimit=N` widens both tripwire tables. The default of 10 is right for the
616
+ // dashboard, but it hid the real volume offender during the 2026-09-12 main-loop
617
+ // investigation: the fs-watcher scan's existence-check entry (tens of thousands of
618
+ // calls, but few ms) ranks by sumMs and fell off the list between two reads, so the
619
+ // biggest call-COUNT source on the main thread was invisible. Capped at 256,
620
+ // matching the tripwire's own TRIPWIRE_MAX_KEYS.
621
+ mainDb: _deps._mainDbCensus.getTripwireTop({ limit: censusLimit }),
622
+ syncIo: _deps._syncIoCensus.getTripwireTop({ limit: censusLimit }),
623
+ transcriptRoots: _deps._transcriptRoots,
624
+ wal: {
625
+ ..._deps._walCheckpointMetrics,
626
+ truncateSuccessRatio: _deps._walCheckpointMetrics.truncateAttempts > 0
627
+ ? _deps._walCheckpointMetrics.truncateSuccesses / _deps._walCheckpointMetrics.truncateAttempts
628
+ : null,
629
+ },
630
+ dbOwner,
631
+ readPool, mainTimers: { cpuByTimer: _deps._perfTracker.getTimerCpuStats?.() || {} }, broadcast: { byReason: _deps.getBroadcastReasonStats?.()?.byReason || {} }, // WP-0 M-1 (hub-local-cpu-memory-burn §7) + WP-0 M-2 (§3.2/§7): folded onto this line, not a new one, so the moved-body line budget is unchanged
632
+ writeLock: _deps.dbModule.getWriteLockStats?.() || null,
633
+ mainWriteEscapes: _deps.getMainThreadWriteEscapeStats(),
634
+ degraded: _deps._degradedOperations.getStatus(),
635
+ memory: { ...memory, retireNoReclaim: _deps._reclaimRecorder.getRetireNoReclaim(), reclaimRing: _deps._reclaimRecorder.getReclaimRing(), trimCadence: _deps._trimCadence.getStatus() }, // WP-6 D-9/AC-6a + WP-3 §6.3: folded onto this line -- handleApi is a frozen-length function (lint/file-size.config.json)
636
+ providerIndex: _deps._desktopIndex.status(),
637
+ typingInteractions: _deps._typingInteractionTelemetryEnabled
638
+ ? _deps._typingInteractionTraces.snapshot()
639
+ : { interactions: [], providers: {} },
640
+ codexInputTraces: _deps._typingInteractionTelemetryEnabled
641
+ ? _deps._typingInteractionTraces.publicSnapshot({
642
+ contextFor: ({ startedAt, endedAt }) => ({
643
+ eventLoopMaxMs: Number(_deps._eventLoopIncidents.correlate({ startedAt, endedAt })?.blockedMs || 0),
644
+ }),
645
+ })
646
+ : { interactions: [], providers: { codex: { count: 0 } } },
647
+ typingLatencyFlags: {
648
+ typingE2eTelemetry: _deps._typingInteractionTelemetryEnabled,
649
+ codexInputTrace: _deps._typingInteractionTelemetryEnabled,
650
+ streamStatusPatchV2: _deps.CTM_STREAM_STATUS_PATCH_V2_ENABLED,
651
+ stateSyncClientLedgerV2: true,
652
+ stateSyncInteractiveSupersede: true,
653
+ browserQosV2: _deps.CTM_BROWSER_QOS_V2_ENABLED,
654
+ stateSyncAtomicHandoffLease: true,
655
+ visibleRevisionWatchdog: _deps._visibleRevisionWatchdogEnabled,
656
+ },
657
+ });
658
+ res.writeHead(200, { 'Content-Type': 'application/json' });
659
+ res.end(JSON.stringify({
660
+ ok: true,
661
+ ...payload,
662
+ recentCatalog: {
663
+ ..._deps._recentCatalogMetrics,
664
+ generation: _deps._recentCatalogGeneration,
665
+ publishedGeneration: _deps._recentCatalogPublished.generation,
666
+ publishedBytes: _deps._recentCatalogPublished.bytes,
667
+ variants: _deps._recentCatalogPublished.variants.size,
668
+ buildInFlight: _deps._recentCatalogBuildInFlight,
669
+ },
670
+ heartbeatProjection: _deps._heartbeatDirtyQueue.status(),
671
+ mainDbRequestGuard: _deps._mainDbCensus.getRequestGuard(),
672
+ controlSnapshot: _deps._controlSnapshot.status(),
673
+ runtimeEvents: _deps._runtimeEventBuffer.getStatus(),
674
+ conversationPublication: _deps._conversationUpdateOutboxBackoff.getStatus(),
675
+ historyBreakers: _deps._historyBreakerSnapshot(),
676
+ stateSyncLifecycle: {
677
+ unknownSessionSubscriptionRejections: _deps._stateSyncUnknownSubscriptionRejected,
678
+ ledgerCausalityRepairs: Number(_deps._stateSyncClientLedger.stats?.().causalityRepairs || 0),
679
+ pendingClients: (() => {
680
+ const stats = _deps.headlessWorker.getStatus?.().legacy || {};
681
+ return {
682
+ sessions: Number(stats.pendingStateSyncSessions || 0),
683
+ clients: Number(stats.pendingStateSyncClients || 0),
684
+ oldestAgeMs: Number(stats.pendingStateSyncOldestAgeMs || 0),
685
+ counters: {
686
+ ...(stats.pendingStateSyncCounters || {}),
687
+ unknownServerRejected: _deps._stateSyncUnknownSubscriptionRejected,
688
+ },
689
+ };
690
+ })(),
691
+ stallWarnings: (() => {
692
+ const stats = _deps.headlessWorker.getStatus?.().legacy || {};
693
+ return {
694
+ identities: Number(stats.stalledWarningIdentities || 0),
695
+ counters: stats.stalledWarningCounters || {},
696
+ };
697
+ })(),
698
+ },
699
+ }));
700
+ } catch (e) {
701
+ res.writeHead(500, { 'Content-Type': 'application/json' });
702
+ res.end(JSON.stringify({ error: e.message }));
703
+ }
704
+ return;
705
+ }
706
+
707
+ // WP-5 shard S4: the two verbatim bodies below are appended here by
708
+ // bin/move-only-check.js's proof (identical to server.js's declarations up to
709
+ // the `_deps.` prefix).
710
+ module.exports = { apiDiagnosticsTestPairableOriginsSet, apiDiagnosticsTestPairableOriginsClear, causalTraceDiagnosticPayload, apiContextBudget, apiDiagnosticsClientHealth, apiDiagnosticsPerfSummary, configure, register };