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,838 @@
1
+ 'use strict';
2
+
3
+ // Landing-throughput WP-6 shard S8 (design doc §5 D-10 "move-only", §6 "Shard
4
+ // module seam"): the nine Wall-E history-serving functions move out of server.js
5
+ // VERBATIM into this module behind the same configure(deps) seam as
6
+ // lib/api/session-messages.js (S3), lib/api/session-attach.js and
7
+ // lib/terminal-attach-geometry.js (S6). It registers NO route and NO socket
8
+ // handler: lib/ws/walle-control.js (wsWalleControlApi) and lib/ws/state-sync.js
9
+ // (wsStateSyncApi) keep their tables and now receive this module's exports, the two
10
+ // session-create configure() closures (identity/transcript) likewise, and server.js
11
+ // keeps no wrapper -- the only surviving reference there is the by-value
12
+ // walleHistoryServe.handleWalleHistoryRequest handed to wsWalleControlApi. Bodies
13
+ // are identical to server.js's declarations except for the `_deps.` prefix on every
14
+ // identifier that used to resolve to a server.js top-level binding (D-10 rule 1),
15
+ // proven by `node bin/move-only-check.js`. Seven inline `require('./lib/...')` calls
16
+ // are rebased to this module's own sibling paths and one
17
+ // `require('./public/js/...')` to `../public/js/...` (S1/S3/S6 precedent: injecting
18
+ // server.js's `require` is a loader-value transfer the public runtime closure guard
19
+ // refuses) -- the one deviation from a pure move-only diff.
20
+ //
21
+ // The module deliberately lives at lib/walle-history-serve.js and NOT under lib/ws/:
22
+ // this is not a router handler (two of the functions take the socket as their first
23
+ // parameter, the other seven are payload/lifecycle helpers), and lib/ws/*.js is the
24
+ // ws-router handler directory whose contract (tests/ws-router-parity) allows socket
25
+ // access only through ctx.
26
+ //
27
+ // The episode/cursor lifecycles SPLIT across this seam on purpose: the bodies that
28
+ // OWN the state stay in server.js -- `_walleHistoryCatchUpEpisodes`,
29
+ // `_walleHistoryCursorExpiredDiagnosticAt`, `_resetWalleHistoryCatchUpEpisode`,
30
+ // `_sweepWalleHistoryCatchUpEpisodes` and the two `_scheduleWalle*` schedulers --
31
+ // and are injected here by reference, so the maps keep one identity.
32
+ //
33
+ // Injected deps, per function:
34
+ // _walleHistoryForSessionUi: _capToolCallsForDisplay, normalizeWalleDisplayContent
35
+ // _walleHistoryKeyRange: (none)
36
+ // _walleHistoryPayload: CTM_CONV_ORDER_AUTHORITY_ENABLED, _publicTimelineIdentity,
37
+ // _resolveSessionTimelineIdentity, _resolveWalleHistorySource,
38
+ // _walleHistoryCorrelationFields, _wallePromptEntriesForSessionUi, publicHistoryMessage
39
+ // _noteWalleHistoryCatchUp: _scheduleWalleSessionRowsCatchUp,
40
+ // _walleHistoryCatchUpEpisodes, recordSessionDiagnostic
41
+ // _noteWalleHistoryCursorExpired: _walleHistoryCursorExpiredDiagnosticAt,
42
+ // recordSessionDiagnostic
43
+ // _sendWalleHistoryReply: ADMISSION_RESULTS, CTM_WS_SOLICITED_REPLY_INTERACTIVE_ENABLED,
44
+ // _WS_ADMISSION_COUNTER_KEY, _wsSend, _wsSolicitedReplyCounters, errorSink,
45
+ // recordSessionDiagnostic
46
+ // _sendWalleHistoryErrorFrame: ADMISSION_RESULTS, _walleHistoryCorrelationFields
47
+ // _sendWalleSessionHistory: WALLE_HISTORY_SERIALIZE_MAX_MESSAGES, _perfTracker,
48
+ // _resetWalleHistoryCatchUpEpisode, _resolveWalleHistorySourceOffThread,
49
+ // _scheduleWalleProjectionRepair, _scheduleWalleSessionRowsCatchUp,
50
+ // _walleHistoryCorrelationFields, _walleHistoryForReview,
51
+ // _walleOpenTurnActivityForNewestPage, _wallePromptEntriesForSessionUi,
52
+ // _walleRowHistoryForCtmId, _walleSessionRowsFreshness, createHistoryLaneBudget,
53
+ // recordSessionDiagnostic, sessions
54
+ // handleWalleHistoryRequest: _hydrateRuntimeGoalProjectionForSocket,
55
+ // _sendRuntimeWorkStateToSocket, _subscribeStateSyncClient,
56
+ // _walleHistoryCorrelationFields, isWalleLikeSession, recordSessionDiagnostic,
57
+ // sessionEvents, sessions, subscribeWalleHistoryRequestClient
58
+ //
59
+ // No moved body assigns server.js state (D-10 rule 2), so nothing is injected as a
60
+ // getter: every dep is a by-value reference. The two per-session Maps, the
61
+ // admission counters and the sessions map are mutated in place, never rebound.
62
+
63
+ let _deps = null;
64
+
65
+ function configure(deps = {}) {
66
+ _deps = deps;
67
+ }
68
+
69
+ function _walleHistoryForSessionUi(history, baseIndex = 0) {
70
+ const offset = Number.isFinite(Number(baseIndex)) ? Number(baseIndex) : 0;
71
+ return (Array.isArray(history) ? history : []).map((m, index) => {
72
+ const role = m.role;
73
+ const content = m.content || m.text || '';
74
+ const projected = {
75
+ role,
76
+ content: role === 'assistant' ? _deps.normalizeWalleDisplayContent(content) : content,
77
+ model: m.model_id || m.model || '',
78
+ model_id: m.model_id || m.model || '',
79
+ latency_ms: m.latency_ms || m.latencyMs || 0,
80
+ timestamp: m.timestamp || m.created_at || m.createdAt || 0,
81
+ created_at: m.created_at || m.timestamp || m.createdAt || 0,
82
+ toolCalls: _deps._capToolCallsForDisplay(m.toolCalls),
83
+ codingSummary: m.codingSummary || undefined,
84
+ metadata: m.metadata || undefined,
85
+ runtimeDiagnostic: m.runtimeDiagnostic || undefined,
86
+ liveActivity: !!m.liveActivity,
87
+ agentLabel: m.agentLabel || undefined,
88
+ attachments: m.attachments || undefined,
89
+ // Append-only tree edges for the session view's flag-gated branch projection.
90
+ // displayParentUuid bridges the intervening tool_call/tool_result records so the
91
+ // client tree's edges land assistant->user (computed server-side in readWalleCtmHistory).
92
+ uuid: m.uuid || undefined,
93
+ historyRowUuid: m.historyRowUuid || m.uuid || undefined,
94
+ parentUuid: m.parentUuid != null ? m.parentUuid : undefined,
95
+ historyIndex: Number.isFinite(Number(m.historyIndex)) ? Number(m.historyIndex) : offset + index,
96
+ messageIndex: Number.isFinite(Number(m.messageIndex)) ? Number(m.messageIndex) : offset + index,
97
+ orderKey: m.orderKey || m.order_key || undefined, kind: m.kind || undefined, provenance: m.provenance || undefined, turnId: m.turnId != null ? String(m.turnId) : undefined,
98
+ };
99
+ if (m.displayParentUuid !== undefined) projected.displayParentUuid = m.displayParentUuid;
100
+ return projected;
101
+ });
102
+ }
103
+
104
+ function _walleHistoryKeyRange(messages) {
105
+ let first = null;
106
+ let last = null;
107
+ for (const message of Array.isArray(messages) ? messages : []) {
108
+ const key = message && (message.orderKey != null ? message.orderKey : message.order_key);
109
+ if (typeof key !== 'string' || key === '') continue;
110
+ if (first === null || key < first) first = key;
111
+ if (last === null || key > last) last = key;
112
+ }
113
+ return first === null ? null : { first, last };
114
+ }
115
+
116
+ function _walleHistoryPayload(sessionOrId, history, options = {}, extra = {}) {
117
+ const source = _deps._resolveWalleHistorySource(sessionOrId);
118
+ const id = source?.ctmSessionId || (typeof sessionOrId === 'object' ? sessionOrId?.id : sessionOrId);
119
+ const windowStart = Number.isFinite(extra.windowStart) ? Number(extra.windowStart) : 0;
120
+ const messages = _walleHistoryForSessionUi(history, windowStart).map(_deps.publicHistoryMessage);
121
+ const promptEntries = Array.isArray(extra.promptEntries)
122
+ ? extra.promptEntries
123
+ : _deps._wallePromptEntriesForSessionUi(history, windowStart);
124
+ const turnFields = require('./walle-turn-projection').walleTurnResponseFields(messages, {
125
+ scope: extra.mode || options.mode ? 'page' : 'full',
126
+ });
127
+ const historyIntegrity = require('../public/js/walle-history-integrity')
128
+ .buildWalleHistoryState(messages, {
129
+ headUuid: extra.headUuid || '',
130
+ hasOlder: extra.hasMore === true,
131
+ projectionValid: extra.projection?.valid === true,
132
+ });
133
+ const correlation = _deps._walleHistoryCorrelationFields({
134
+ ...options,
135
+ mode: extra.mode || options.mode,
136
+ });
137
+ const payload = {
138
+ contractVersion: 2,
139
+ type: 'walle-history',
140
+ id,
141
+ messages,
142
+ promptEntries,
143
+ promptCount: Number.isFinite(extra.promptCount) ? Number(extra.promptCount) : promptEntries.length,
144
+ integrity: {
145
+ schemaVersion: 1,
146
+ counts: historyIntegrity.counts,
147
+ activeHeadUuid: historyIntegrity.activeHeadUuid || '',
148
+ diagnosticEventIds: historyIntegrity.diagnosticEvents
149
+ .map((message) => message.uuid || message.id || '')
150
+ .filter(Boolean),
151
+ },
152
+ ...turnFields,
153
+ ...correlation,
154
+ request: {
155
+ mode: correlation.historyMode,
156
+ requestedCursor: correlation.requestedCursor,
157
+ requestedGeneration: correlation.requestedGeneration,
158
+ requestedOrderingGeneration: correlation.requestedOrderingGeneration,
159
+ },
160
+ source: extra.source || options.source || 'walle-history',
161
+ cacheFresh: extra.cacheFresh,
162
+ stale: extra.stale,
163
+ // Windowing metadata (OpenCode-style): the client renders this page and lazy-loads older on
164
+ // scroll-up. `mode` 'replace' (initial/newest) vs 'prepend' (older page). Absent total ⇒ a
165
+ // legacy (non-windowed) serve; the client treats it as the full set.
166
+ total: Number.isFinite(extra.total) ? extra.total : undefined,
167
+ hasMore: extra.hasMore === true ? true : (extra.total != null ? false : undefined),
168
+ offset: Number.isFinite(extra.offset) ? extra.offset : undefined,
169
+ mode: extra.mode || undefined,
170
+ ...require('./session-messages-page').walleHistoryPageFields(extra),
171
+ };
172
+ const projection = require('./session-messages-projection');
173
+ const coverage = projection.conversationPageCoverage({
174
+ offset: payload.offset,
175
+ has_more: payload.hasMore,
176
+ olderCursor: payload.olderCursor,
177
+ newerCursor: payload.newerCursor,
178
+ projectionEpoch: payload.projection?.epoch,
179
+ orderingEpoch: payload.projection?.orderingEpoch,
180
+ settled: extra.degraded === true ? false : extra.settled,
181
+ partial: extra.partial,
182
+ stale: payload.stale,
183
+ catchingUp: extra.catchingUp === true,
184
+ // D-2/S-4 (WP-0): a lag-only catch-up page carries its measured lag so WP-1's
185
+ // conversationPageCoverage can render the catch-up lifecycle from a declared lag instead
186
+ // of a synthesized gap.
187
+ lag: extra.lag || null,
188
+ degraded: extra.degraded,
189
+ degradedReason: extra.degradedReason,
190
+ readiness: extra.readiness,
191
+ }, messages);
192
+ const identity = _deps._publicTimelineIdentity(_deps._resolveSessionTimelineIdentity(id));
193
+ payload.coverage = coverage;
194
+ payload.timelineIdentity = identity;
195
+ Object.assign(payload, projection.conversationLifecycleEnvelope({
196
+ ...payload,
197
+ coverage,
198
+ timelineIdentity: identity,
199
+ catchingUp: extra.catchingUp,
200
+ degraded: extra.degraded,
201
+ degradedReason: extra.degradedReason,
202
+ settled: extra.degraded === true ? false : extra.settled,
203
+ readiness: extra.readiness,
204
+ }));
205
+ // WP-3 (D-5, §6): the envelope gains keyRange so the client can render the gap pill
206
+ // from a DECLARED range instead of inferring it. Flag-gated: kill-switch OFF keeps the
207
+ // envelope byte-shape unchanged (no keyRange field at all).
208
+ if (_deps.CTM_CONV_ORDER_AUTHORITY_ENABLED) payload.keyRange = _walleHistoryKeyRange(messages);
209
+ return payload;
210
+ }
211
+
212
+ function _noteWalleHistoryCatchUp(source, lag, turnOpen) {
213
+ const id = source?.ctmSessionId;
214
+ if (!id) return;
215
+ const now = Date.now();
216
+ let episode = _deps._walleHistoryCatchUpEpisodes.get(id);
217
+ if (!episode) {
218
+ episode = { firstSeenMs: now, lastRecordedAt: 0, lastStallLoggedAt: 0, lastTouchedMs: now };
219
+ _deps._walleHistoryCatchUpEpisodes.set(id, episode);
220
+ }
221
+ episode.lastTouchedMs = now;
222
+ if (now - episode.lastRecordedAt >= 30_000) {
223
+ episode.lastRecordedAt = now;
224
+ _deps.recordSessionDiagnostic(id, 'walle-history-catchup', {
225
+ lagBytes: Number(lag?.bytes) || 0,
226
+ lagSeconds: Number(lag?.seconds) || 0,
227
+ turnOpen: !!turnOpen,
228
+ firstSeenMs: episode.firstSeenMs,
229
+ });
230
+ }
231
+ if (turnOpen) return;
232
+ _deps._scheduleWalleSessionRowsCatchUp(source, 'walle-history-catchup');
233
+ if (now - episode.firstSeenMs >= 60_000 && now - episode.lastStallLoggedAt >= 300_000) {
234
+ episode.lastStallLoggedAt = now;
235
+ console.error(`[walle-history] catch-up stalled session=${String(id).slice(0, 8)} `
236
+ + `lagBytes=${Number(lag?.bytes) || 0} lagSeconds=${Number(lag?.seconds) || 0} `
237
+ + `sinceMs=${now - episode.firstSeenMs}`);
238
+ _deps._scheduleWalleSessionRowsCatchUp(source, 'walle-history-catchup-stalled');
239
+ }
240
+ }
241
+
242
+ function _noteWalleHistoryCursorExpired(sessionId, details) {
243
+ if (!sessionId) return;
244
+ const now = Date.now();
245
+ const lastAt = _deps._walleHistoryCursorExpiredDiagnosticAt.get(sessionId) || 0;
246
+ if (now - lastAt < 30_000) return;
247
+ _deps._walleHistoryCursorExpiredDiagnosticAt.set(sessionId, now);
248
+ _deps.recordSessionDiagnostic(sessionId, 'walle-history-cursor-expired', details);
249
+ }
250
+
251
+ function _sendWalleHistoryReply(ws, payload, sessionId = '') {
252
+ const id = String(sessionId || payload?.id || '').trim();
253
+ const result = _deps._wsSend(ws, payload, _deps.CTM_WS_SOLICITED_REPLY_INTERACTIVE_ENABLED ? {
254
+ class: 'interactive',
255
+ channel: 'walle-history',
256
+ sessionId: id,
257
+ } : { channel: 'walle-history', sessionId: id });
258
+ const counterKey = _deps._WS_ADMISSION_COUNTER_KEY[result];
259
+ if (counterKey) _deps._wsSolicitedReplyCounters[counterKey] += 1;
260
+ if (result !== _deps.ADMISSION_RESULTS.SENT && id) {
261
+ _deps.recordSessionDiagnostic(id, 'walle-history-reply-dropped', {
262
+ result,
263
+ channel: 'walle-history',
264
+ });
265
+ _deps.errorSink.report('walle-history:reply-dropped', Object.assign(new Error(result), {
266
+ code: 'CTM_WALLE_HISTORY_REPLY_DROPPED',
267
+ }), { sessionId: id, result });
268
+ }
269
+ return result;
270
+ }
271
+
272
+ function _sendWalleHistoryErrorFrame(ws, sessionId, options = {}, error = null) {
273
+ if (!ws || ws.readyState !== 1) return _deps.ADMISSION_RESULTS.DROPPED_BACKPRESSURE;
274
+ const id = String(sessionId || '').trim();
275
+ const code = String(error?.code || error?.message || 'walle_history_failed');
276
+ const correlation = _deps._walleHistoryCorrelationFields(options);
277
+ return _sendWalleHistoryReply(ws, JSON.stringify({
278
+ type: 'walle-history',
279
+ id,
280
+ messages: [],
281
+ ...correlation,
282
+ mode: correlation.historyMode,
283
+ request: {
284
+ mode: correlation.historyMode,
285
+ requestedCursor: correlation.requestedCursor,
286
+ requestedGeneration: correlation.requestedGeneration,
287
+ requestedOrderingGeneration: correlation.requestedOrderingGeneration,
288
+ },
289
+ source: options.source || 'walle-history',
290
+ error: code,
291
+ degraded: true,
292
+ degradedReason: 'internal_error',
293
+ projection: {
294
+ valid: true,
295
+ revalidating: true,
296
+ status: 'missing',
297
+ error: '',
298
+ staleReason: 'no_verdict',
299
+ epoch: 0,
300
+ },
301
+ }), id);
302
+ }
303
+
304
+ async function _sendWalleSessionHistory(ws, session, options = {}) {
305
+ if (!ws || ws.readyState !== 1 || !session) return false;
306
+ const budget = _deps.createHistoryLaneBudget(options.deadlineMs);
307
+ const source = await _deps._resolveWalleHistorySourceOffThread(session);
308
+ if (!source) {
309
+ const correlation = _deps._walleHistoryCorrelationFields(options);
310
+ _sendWalleHistoryReply(ws, JSON.stringify({
311
+ type: 'walle-history',
312
+ id: typeof session === 'object' ? session.id : session,
313
+ messages: [],
314
+ ...correlation,
315
+ request: {
316
+ mode: correlation.historyMode,
317
+ requestedCursor: correlation.requestedCursor,
318
+ requestedGeneration: correlation.requestedGeneration,
319
+ requestedOrderingGeneration: correlation.requestedOrderingGeneration,
320
+ },
321
+ source: options.source || 'walle-history',
322
+ error: 'not_walle_session',
323
+ degraded: true,
324
+ degradedReason: 'not_walle_session',
325
+ projection: {
326
+ valid: true,
327
+ revalidating: true,
328
+ status: 'missing',
329
+ error: '',
330
+ staleReason: 'no_verdict',
331
+ epoch: 0,
332
+ },
333
+ }), typeof session === 'object' ? session.id : session);
334
+ return false;
335
+ }
336
+
337
+ // OpenCode-style windowing: the client requests a small newest page (offset 0, limit ~60) and
338
+ // lazy-loads older pages (offset bumps, mode 'prepend') on scroll-up. Serializing just the page
339
+ // — not the whole ~23MB history — is what removes the main-loop JSON.stringify block measured in
340
+ // the /ctm-dev E2E. Missing or oversized limits are clamped to the bounded server page.
341
+ const offset = Math.max(0, Number(options.offset) || 0);
342
+ const historyParentAdoptionEnabled = process.env.WALLE_HISTORY_REPAIR !== '0';
343
+ const beforeRaw = String(options.before || '').trim();
344
+ // Preserve an over-limit value as invalid while bounding what crosses the
345
+ // read-pool boundary; decodeWalleHistoryCursor rejects anything over 2048.
346
+ const before = beforeRaw.length > 2048 ? beforeRaw.slice(0, 2049) : beforeRaw;
347
+ const afterCursorRaw = historyParentAdoptionEnabled ? String(options.afterCursor || '').trim() : '';
348
+ const afterCursor = afterCursorRaw.length > 2048 ? afterCursorRaw.slice(0, 2049) : afterCursorRaw;
349
+ const around = historyParentAdoptionEnabled ? String(options.around || '').trim().slice(0, 200) : '';
350
+ const afterRaw = String(options.historyWatermark || options.after || '').trim();
351
+ const after = afterRaw.length > 200 ? afterRaw.slice(0, 200) : afterRaw;
352
+ const projectionEpoch = Math.max(0, Number(options.projectionEpoch) || 0);
353
+ // Thread orderingEpoch end-to-end (D-1 ledger row 3): additive-only, same clamp as
354
+ // projectionEpoch, so getWalleHistoryDelta can fence an ordering-epoch change without
355
+ // refusing on a plain projection-epoch bump.
356
+ const orderingEpoch = Math.max(0, Number(options.orderingEpoch) || 0);
357
+ const requestedLimit = Number.isFinite(Number(options.limit)) ? Number(options.limit) : 0;
358
+ const limit = Math.min(_deps.WALLE_HISTORY_SERIALIZE_MAX_MESSAGES,
359
+ requestedLimit > 0 ? requestedLimit : _deps.WALLE_HISTORY_SERIALIZE_MAX_MESSAGES);
360
+ const requestedMode = options.mode === 'prepend'
361
+ ? 'prepend'
362
+ : (options.mode === 'around' && around
363
+ ? 'around'
364
+ : (options.mode === 'newer' && afterCursor
365
+ ? 'newer'
366
+ : (options.mode === 'append' && after && projectionEpoch > 0 ? 'append' : 'replace')));
367
+ const projectionLag = _deps._walleSessionRowsFreshness(source);
368
+ const sendHistory = (history, src, extra = {}) => {
369
+ if (ws.readyState !== 1) return;
370
+ const payloadExtra = { ...extra };
371
+ const prewindowed = payloadExtra.prewindowed === true;
372
+ delete payloadExtra.prewindowed;
373
+ const suppliedPromptEntries = payloadExtra.promptEntries;
374
+ delete payloadExtra.promptEntries;
375
+ const suppliedPromptCount = payloadExtra.promptCount;
376
+ delete payloadExtra.promptCount;
377
+ const responseMode = payloadExtra.responseMode || requestedMode;
378
+ delete payloadExtra.responseMode;
379
+ const promptEntries = Array.isArray(suppliedPromptEntries)
380
+ ? suppliedPromptEntries
381
+ : _deps._wallePromptEntriesForSessionUi(history);
382
+ let win;
383
+ if (prewindowed) {
384
+ win = {
385
+ window: Array.isArray(history) ? history : [],
386
+ total: Math.max(0, Number(payloadExtra.total) || 0),
387
+ hasMore: payloadExtra.hasMore === true,
388
+ offset: Math.max(0, Number(payloadExtra.offset) || 0),
389
+ start: Math.max(0, Number(payloadExtra.windowStart) || 0),
390
+ };
391
+ } else {
392
+ const pageHelpers = require('./session-messages-page');
393
+ const { buildProjectionManifest, WALLE_HISTORY_PROJECTOR_VERSION } = require('./walle-history-projection');
394
+ const sourceRows = Array.isArray(history) ? history : [];
395
+ const sourceManifest = buildProjectionManifest(_deps._walleHistoryForReview(sourceRows));
396
+ payloadExtra.projection = {
397
+ valid: sourceManifest.valid,
398
+ revalidating: false,
399
+ status: 'source',
400
+ version: WALLE_HISTORY_PROJECTOR_VERSION,
401
+ epoch: 0,
402
+ rowCount: sourceManifest.rowCount,
403
+ identityDigest: sourceManifest.identityDigest,
404
+ error: sourceManifest.error || '',
405
+ staleReason: '',
406
+ };
407
+ if (sourceManifest.valid === false) {
408
+ _deps._scheduleWalleProjectionRepair(source.ctmSessionId, 'walle-history-source-structural');
409
+ }
410
+ // Invalid topology is still paginated: the projection metadata forces the
411
+ // client into audit mode, while the cursor lets a large damaged transcript
412
+ // reveal every event without one unbounded main-loop serialization.
413
+ const sourceWindow = pageHelpers.getWalleSourceHistoryWindow(sourceRows, {
414
+ sessionId: source.ctmSessionId,
415
+ offset,
416
+ limit,
417
+ before,
418
+ afterCursor,
419
+ around,
420
+ });
421
+ win = {
422
+ window: sourceWindow.messages,
423
+ total: sourceWindow.total,
424
+ hasMore: sourceWindow.hasMore,
425
+ offset: sourceWindow.offset,
426
+ start: sourceWindow.windowStart,
427
+ };
428
+ Object.assign(payloadExtra, pageHelpers.walleHistoryPageFields(sourceWindow));
429
+ }
430
+ const openActivity = _deps._walleOpenTurnActivityForNewestPage(
431
+ typeof session === 'object' && session ? session : _deps.sessions.get(String(session || '')),
432
+ source,
433
+ { mode: responseMode, offset: win.offset, before, around, hasMoreNewer: payloadExtra.hasMoreNewer },
434
+ );
435
+ if (openActivity) win = { ...win, window: [...win.window, openActivity] };
436
+ let serializedChars = 0;
437
+ const serialized = _deps._perfTracker.runSyncProbed('walleWsHistory:serialize', () => {
438
+ const json = JSON.stringify(_walleHistoryPayload(session, win.window, options, {
439
+ source: options.source || src,
440
+ cacheFresh: payloadExtra.cacheFresh !== undefined
441
+ ? payloadExtra.cacheFresh
442
+ : payloadExtra.settled === true,
443
+ total: win.total,
444
+ hasMore: win.hasMore,
445
+ offset: win.offset,
446
+ windowStart: win.start,
447
+ promptEntries,
448
+ promptCount: Number.isFinite(Number(suppliedPromptCount))
449
+ ? Number(suppliedPromptCount)
450
+ : promptEntries.length,
451
+ mode: responseMode,
452
+ historyParentAdoptionEnabled,
453
+ // One-release compatibility for clients predating the terminology fix.
454
+ historyRepairEnabled: historyParentAdoptionEnabled,
455
+ projectionLagBytes: Math.max(0, Number(projectionLag.lagBytes || 0)),
456
+ projectionLagSeconds: Math.max(0, Number(projectionLag.lagSeconds || 0)),
457
+ ...payloadExtra,
458
+ }));
459
+ serializedChars = json.length;
460
+ return json;
461
+ }, { context: () => ({
462
+ msgs: Array.isArray(win.window) ? win.window.length : 0,
463
+ total: win.total,
464
+ src,
465
+ serializedChars,
466
+ }) });
467
+ return _sendWalleHistoryReply(ws, serialized, source.ctmSessionId);
468
+ };
469
+
470
+ // (1) Durable row store under the canonical ctm id — fresh ⇒ serve without touching the JSONL.
471
+ // D-12: degradedReason is honesty about what actually happened during THIS
472
+ // read, not a precomputed guess seeded from options.noCache — a client
473
+ // passing noCache:true (WP-4's auto-retry does exactly this) is asking to
474
+ // skip the fresh row-store lane below, not asserting that the read is
475
+ // degraded. Only a real miss (rowHistory.__miss / stale.__miss) sets this now.
476
+ let degradedReason = '';
477
+ if (options.noCache !== true) {
478
+ // D-4 (WP-0): the P11 byte-identical retry that used to sit here (a second, identical
479
+ // off-thread read whenever the first came back watermarkStale) is deleted -- D-1 makes
480
+ // getWalleHistoryDelta resolve the anchor before ever refusing, so a repeat read with the
481
+ // SAME inputs could only ever reproduce the SAME reply. It never healed anything; it only
482
+ // doubled read cost and hid the true cause behind a generic degraded page.
483
+ const rowHistory = await _deps._walleRowHistoryForCtmId(source, {
484
+ offset,
485
+ limit,
486
+ before,
487
+ afterCursor: requestedMode === 'newer' ? afterCursor : '',
488
+ around: requestedMode === 'around' ? around : '',
489
+ after: requestedMode === 'append' ? after : '',
490
+ projectionEpoch,
491
+ orderingEpoch: requestedMode === 'append' ? orderingEpoch : 0,
492
+ budget,
493
+ });
494
+ if (rowHistory?.__miss) degradedReason = rowHistory.__miss;
495
+ if (limit > 0 && rowHistory && Array.isArray(rowHistory.messages)) {
496
+ // S-8 (WP-0): a cursor/watermark expiry is a THIRD lane, distinct from both catching-up
497
+ // and complete -- it must not enter the catch-up lifecycle (no _noteWalleHistoryCatchUp,
498
+ // no _resetWalleHistoryCatchUpEpisode: an expiry says nothing about lag) and must not
499
+ // report settled:true (nothing was actually served).
500
+ const expired = rowHistory.cursorStale === true || rowHistory.error === 'cursor_expired';
501
+ // D-2/S-4 (WP-0): rowHistory.catchingUp === true means catchUpKind classified this v2
502
+ // window as 'lag' -- renderable, complete-to-its-cursor history that is not a miss. It
503
+ // gets the SAME extras as a complete page except for the catch-up lifecycle markers
504
+ // below; a fresh (non-catching-up) page closes out any open lag episode for the session.
505
+ const catchingUp = !expired && rowHistory.catchingUp === true;
506
+ if (expired) {
507
+ _noteWalleHistoryCursorExpired(source.ctmSessionId, {
508
+ expiredReason: rowHistory.expiredReason,
509
+ requestedEpoch: projectionEpoch,
510
+ servedEpoch: rowHistory.projection?.epoch,
511
+ });
512
+ } else if (catchingUp) {
513
+ // D-3 review fix: the production call site (handleWalleHistoryRequest) passes a bare
514
+ // session id STRING here, not the session object -- this function already branches on
515
+ // `typeof session === 'object'` above for exactly that reason. Resolve the LIVE session
516
+ // from the session map so turnOpen reflects reality instead of always reading undefined
517
+ // off a string.
518
+ const liveSession = typeof session === 'object' && session
519
+ ? session
520
+ : _deps.sessions.get(String(session || ''));
521
+ _noteWalleHistoryCatchUp(source, rowHistory.lag, !!liveSession?.abortController);
522
+ } else {
523
+ _deps._resetWalleHistoryCatchUpEpisode(source.ctmSessionId);
524
+ }
525
+ sendHistory(rowHistory.messages, 'walle-session-rows', {
526
+ prewindowed: true,
527
+ total: rowHistory.total,
528
+ hasMore: rowHistory.hasMore,
529
+ offset: rowHistory.offset,
530
+ windowStart: rowHistory.windowStart,
531
+ promptEntries: rowHistory.promptEntries,
532
+ promptCount: rowHistory.promptCount,
533
+ olderCursor: rowHistory.olderCursor,
534
+ newerCursor: rowHistory.newerCursor,
535
+ hasMoreNewer: rowHistory.hasMoreNewer === true,
536
+ windowEnd: rowHistory.windowEnd,
537
+ firstMessageIndex: rowHistory.firstMessageIndex,
538
+ lastMessageIndex: rowHistory.lastMessageIndex,
539
+ // S-32b: getWalleHistoryDelta's watermark-miss reply (append/newer mode) sets
540
+ // watermarkStale/error:'watermark_stale' — a DIFFERENT field than the cursor-miss
541
+ // cursorStale/error:'cursor_stale' the client's stale-recovery branches already key off
542
+ // (handleHistoryPrepend, and WP-6's handleHistoryAppend). Fold it into the same signal
543
+ // the client understands instead of silently shipping an empty "loaded" page.
544
+ cursorStale: expired || require('./session-messages-page').walleReplyCursorStale(rowHistory),
545
+ expiredReason: rowHistory.expiredReason || undefined,
546
+ resume: rowHistory.resume || undefined,
547
+ epochAdvanced: rowHistory.epochAdvanced === true || undefined,
548
+ newerRemaining: Math.max(0, Number(rowHistory.newerRemaining) || 0),
549
+ projection: rowHistory.projection,
550
+ // WP-5 (S-29): honest fallback marker — set when the served store's cutover is not
551
+ // 'active' (getSelectedWalleHistoryWindow), null/undefined otherwise.
552
+ importProgress: rowHistory.importProgress || undefined,
553
+ error: rowHistory.error || undefined,
554
+ // H1b-v2 (§10.22): forward ONLY the producer's own conditional observation properties.
555
+ ...(Object.hasOwn(rowHistory, 'historyViewProof') ? { historyViewProof: rowHistory.historyViewProof } : {}),
556
+ ...(Object.hasOwn(rowHistory, 'historyViewProofError') ? { historyViewProofError: rowHistory.historyViewProofError } : {}),
557
+ ...(Object.hasOwn(rowHistory, 'resumeHistoryViewProof') ? { resumeHistoryViewProof: rowHistory.resumeHistoryViewProof } : {}),
558
+ ...(Object.hasOwn(rowHistory, 'resumeHistoryViewProofError') ? { resumeHistoryViewProofError: rowHistory.resumeHistoryViewProofError } : {}),
559
+ responseMode: rowHistory.mode === 'append' ? 'append' : requestedMode,
560
+ promptMode: rowHistory.mode === 'append' ? 'append' : requestedMode,
561
+ ...(expired
562
+ ? {
563
+ settled: false,
564
+ cacheFresh: false,
565
+ stale: false,
566
+ catchingUp: false,
567
+ degraded: false,
568
+ readiness: { reason: 'cursor_expired' },
569
+ }
570
+ : catchingUp
571
+ ? {
572
+ settled: false,
573
+ cacheFresh: false,
574
+ stale: true,
575
+ catchingUp: true,
576
+ degraded: false,
577
+ readiness: { reason: 'rows_behind' },
578
+ lag: { bytes: rowHistory.lag.bytes, seconds: rowHistory.lag.seconds },
579
+ }
580
+ : {
581
+ settled: true,
582
+ readiness: { reason: 'complete' },
583
+ }),
584
+ });
585
+ return true;
586
+ }
587
+ if (Array.isArray(rowHistory) && rowHistory.length > 0) {
588
+ sendHistory(rowHistory, 'walle-session-rows', {
589
+ settled: true,
590
+ readiness: { reason: 'complete' },
591
+ });
592
+ return true;
593
+ }
594
+ }
595
+
596
+ // (2) Stale/missing rows: render the last committed page and schedule the
597
+ // uniform incremental source importer. A request path never parses an
598
+ // enrolled append log or writes a reconstructed snapshot.
599
+ try {
600
+ _deps._scheduleWalleSessionRowsCatchUp(source, 'walle-history-attach');
601
+ const stale = await _deps._walleRowHistoryForCtmId(source, {
602
+ offset,
603
+ limit,
604
+ before,
605
+ afterCursor: requestedMode === 'newer' ? afterCursor : '',
606
+ around: requestedMode === 'around' ? around : '',
607
+ after: requestedMode === 'append' ? after : '',
608
+ projectionEpoch,
609
+ orderingEpoch: requestedMode === 'append' ? orderingEpoch : 0,
610
+ allowStale: true,
611
+ budget,
612
+ budgetRung: 'stale',
613
+ });
614
+ if (stale?.__miss) degradedReason = stale.__miss; if (stale?.__miss === 'projection_empty' && Array.isArray(stale.fallbackRows) && stale.fallbackRows.length > 0) { sendHistory(stale.fallbackRows, 'walle-session-rows-syncing', { cacheFresh: false, settled: false, stale: true, catchingUp: true, degraded: true, degradedReason: 'projection_empty', readiness: { reason: 'rows_behind' } }); return true; } // P1-1: an empty v2 projection over a non-empty transcript serves the raw fold instead of the source_unavailable page below
615
+ if (limit > 0 && stale && Array.isArray(stale.messages)) {
616
+ // D-12: an honest label for a miss with no classified cause, not a
617
+ // borrowed 'freshness_gate' — that reason belongs to the freshness
618
+ // predicates in _walleRowHistoryForCtmId, which set degradedReason
619
+ // themselves (via __miss) when they actually fire.
620
+ const reason = degradedReason || 'unknown_miss';
621
+ // Fix round 1 (WP-0 review): the stale lane can ALSO serve an expired cursor/watermark
622
+ // (allowStale:true does not bypass D-1's own refusal reasons) -- a zero-row expiry here
623
+ // is exactly as much "not a lag frame" as it is in the fresh lane (D-6), so it must not
624
+ // be stamped catchingUp:true, and must carry expiredReason/resume for WP-1.
625
+ const staleExpired = stale.cursorStale === true || stale.error === 'cursor_expired';
626
+ sendHistory(stale.messages, 'walle-session-rows-syncing', {
627
+ prewindowed: true,
628
+ total: stale.total,
629
+ hasMore: stale.hasMore,
630
+ offset: stale.offset,
631
+ windowStart: stale.windowStart,
632
+ promptEntries: stale.promptEntries,
633
+ promptCount: stale.promptCount,
634
+ olderCursor: stale.olderCursor,
635
+ newerCursor: stale.newerCursor,
636
+ hasMoreNewer: stale.hasMoreNewer === true,
637
+ firstMessageIndex: stale.firstMessageIndex,
638
+ lastMessageIndex: stale.lastMessageIndex,
639
+ windowEnd: stale.windowEnd,
640
+ // S-32b: same watermarkStale->cursorStale fold as the fresh-row reply above.
641
+ cursorStale: staleExpired || require('./session-messages-page').walleReplyCursorStale(stale),
642
+ expiredReason: stale.expiredReason || undefined,
643
+ resume: stale.resume || undefined,
644
+ projection: stale.projection,
645
+ importProgress: stale.importProgress || undefined,
646
+ error: stale.error || undefined,
647
+ // H1b-v2 (§10.22): the same four OWN observation properties, forwarded from this lane's
648
+ // own page object. An unobserved page adds nothing here (no absent identity is minted).
649
+ ...(Object.hasOwn(stale, 'historyViewProof') ? { historyViewProof: stale.historyViewProof } : {}),
650
+ ...(Object.hasOwn(stale, 'historyViewProofError') ? { historyViewProofError: stale.historyViewProofError } : {}),
651
+ ...(Object.hasOwn(stale, 'resumeHistoryViewProof') ? { resumeHistoryViewProof: stale.resumeHistoryViewProof } : {}),
652
+ ...(Object.hasOwn(stale, 'resumeHistoryViewProofError') ? { resumeHistoryViewProofError: stale.resumeHistoryViewProofError } : {}),
653
+ cacheFresh: false,
654
+ settled: false,
655
+ // The source is being revalidated, but these committed rows are still
656
+ // a usable durable snapshot. Reserve source_unavailable for the branch
657
+ // below where no rows can be served at all.
658
+ readiness: { reason: staleExpired ? 'cursor_expired' : 'rows_behind' },
659
+ stale: true,
660
+ catchingUp: !staleExpired,
661
+ // WP-3 (D-5): degradedReason is honesty, not a feature — a stale/degraded page
662
+ // always carries the reason it's degraded so the client renders the gap pill
663
+ // from the declared range instead of guessing. Never flag-gated (the flag that
664
+ // used to gate this, CTM_WALLE_HISTORY_DEGRADED_HONESTY, is retired).
665
+ // I-2 (final review): an expiry is a THIRD lane, not a fault (D-2) — the stale
666
+ // lane must ship the same degraded:false/no-degradedReason contract the fresh
667
+ // lane already does, or the live degraded-rate metric this design is gated on
668
+ // (AC-1e/AC-4c) goes blind to every expiry that lands on this lane.
669
+ degraded: !staleExpired,
670
+ ...(staleExpired ? {} : { degradedReason: reason }),
671
+ responseMode: requestedMode,
672
+ });
673
+ if (staleExpired) {
674
+ _noteWalleHistoryCursorExpired(source.ctmSessionId, {
675
+ expiredReason: stale.expiredReason,
676
+ requestedEpoch: projectionEpoch,
677
+ servedEpoch: stale.projection?.epoch,
678
+ });
679
+ } else {
680
+ _deps.recordSessionDiagnostic(source.ctmSessionId, 'walle-history-degraded', { reason });
681
+ }
682
+ return true;
683
+ }
684
+ const reason = stale?.__miss || degradedReason || budget.reason() || 'meta_unavailable';
685
+ sendHistory(Array.isArray(stale) ? stale : [], 'walle-session-rows-syncing', {
686
+ cacheFresh: false,
687
+ settled: false,
688
+ readiness: { reason: 'source_unavailable' },
689
+ stale: true,
690
+ catchingUp: true,
691
+ // WP-3 (D-5): see the matching comment on the rows_behind branch above — always on.
692
+ degraded: true,
693
+ degradedReason: reason,
694
+ responseMode: 'replace',
695
+ });
696
+ _deps.recordSessionDiagnostic(source.ctmSessionId, 'walle-history-degraded', { reason });
697
+ return true;
698
+ } catch (e) {
699
+ console.error('[walle-history] rehydrate error:', e.message);
700
+ _sendWalleHistoryErrorFrame(ws, source.ctmSessionId, options, e);
701
+ return false;
702
+ }
703
+ }
704
+
705
+ function handleWalleHistoryRequest(ws, msg = {}) {
706
+ const id = String(msg.id || '').trim();
707
+ const correlation = _deps._walleHistoryCorrelationFields(msg);
708
+ if (!id) {
709
+ if (ws?.readyState === 1) {
710
+ _sendWalleHistoryReply(ws, JSON.stringify({
711
+ type: 'walle-history',
712
+ id,
713
+ messages: [],
714
+ ...correlation,
715
+ request: {
716
+ mode: correlation.historyMode,
717
+ requestedCursor: correlation.requestedCursor,
718
+ requestedGeneration: correlation.requestedGeneration,
719
+ requestedOrderingGeneration: correlation.requestedOrderingGeneration,
720
+ },
721
+ error: 'missing_session_id',
722
+ degraded: true,
723
+ degradedReason: 'internal_error',
724
+ }), id);
725
+ }
726
+ return;
727
+ }
728
+ const hasOwn = (field) => Object.prototype.hasOwnProperty.call(msg, field);
729
+ // H2 controller incarnation: the marker is OPTIONAL (a markerless request stays accepted --
730
+ // unfenced compatibility with old/qualifying callers), but an explicitly supplied malformed
731
+ // one -- own undefined/null/zero/fraction/non-canonical text -- is a contract fault that keeps its own null
732
+ // sentinel, whatever contract version the caller declares. It is never newly REQUIRED.
733
+ if (hasOwn('historyControllerInstanceId')
734
+ && correlation.historyControllerInstanceId === null) {
735
+ const error = Object.assign(new Error('invalid_history_contract'), {
736
+ code: 'invalid_history_contract',
737
+ });
738
+ _sendWalleHistoryErrorFrame(ws, id, msg, error);
739
+ return;
740
+ }
741
+ if (correlation.historyContractVersion >= 3
742
+ && ((!hasOwn('historyRequestId')
743
+ || !hasOwn('historyCycleId')
744
+ || !hasOwn('historyMode')
745
+ || !hasOwn('historyCursor')
746
+ || !hasOwn('requestedGeneration')
747
+ || !hasOwn('requestedOrderingGeneration'))
748
+ || !correlation.historyRequestId
749
+ || !correlation.historyCycleId
750
+ || !['replace', 'prepend', 'append', 'newer', 'around'].includes(correlation.historyMode))) {
751
+ const error = Object.assign(new Error('invalid_history_contract'), {
752
+ code: 'invalid_history_contract',
753
+ });
754
+ _sendWalleHistoryErrorFrame(ws, id, msg, error);
755
+ return;
756
+ }
757
+
758
+ const session = _deps.sessions.get(id);
759
+ const isWalleLike = !!session && _deps.isWalleLikeSession(session);
760
+ // WI-3 (RC-2 fix): subscribe the ws as a client of ANY existing walle-like session —
761
+ // INCLUDING one still `_restoreDeferred` after a restart (see subscribeWalleHistoryRequestClient
762
+ // for why). work-state/`session:attached` stay gated on !_restoreDeferred below since they
763
+ // read state that isn't populated yet for a deferred session.
764
+ // I-6: no PTY/terminal to attach a worker to or reset a reconnect ladder for.
765
+ if (session && isWalleLike && _deps._subscribeStateSyncClient(ws, id, 'walle-history-request', { skipWorkerAttach: true })) {
766
+ _deps.subscribeWalleHistoryRequestClient(session, ws);
767
+ }
768
+ if (session && !session._restoreDeferred && session.type === 'walle') {
769
+ try { _deps._sendRuntimeWorkStateToSocket(ws, id, 'history'); } catch {}
770
+ _deps._hydrateRuntimeGoalProjectionForSocket(ws, id, 'history-goal-hydrated');
771
+ _deps.sessionEvents.emit('session:attached', { id, clientCount: session.clients.length, walle: true, historyOnly: true });
772
+ } else if (session?._restoreDeferred && isWalleLike) {
773
+ _deps.recordSessionDiagnostic(id, 'walle-history-request-deferred', {
774
+ source: msg.reason || msg.source || 'walle-history-request',
775
+ clients: Array.isArray(session.clients) ? session.clients.length : 0,
776
+ });
777
+ }
778
+
779
+ // H2 controller incarnation: forwarded ONLY when the caller supplied it own, and then VERBATIM
780
+ // (a valid number and the explicit null sentinel both survive; never truthiness, never a
781
+ // default of 0). The absence case carries no key at all -- an old internal caller stays
782
+ // unfenced compatibility rather than being given a fabricated identity.
783
+ const historyControllerMarker = hasOwn('historyControllerInstanceId')
784
+ ? { historyControllerInstanceId: msg.historyControllerInstanceId }
785
+ : {};
786
+ _sendWalleSessionHistory(ws, session?.id || id, {
787
+ ...historyControllerMarker,
788
+ limit: msg.limit,
789
+ offset: msg.offset,
790
+ before: msg.before,
791
+ afterCursor: msg.afterCursor,
792
+ around: msg.around,
793
+ mode: msg.mode,
794
+ historyWatermark: msg.historyWatermark,
795
+ projectionEpoch: msg.projectionEpoch,
796
+ orderingEpoch: msg.orderingEpoch,
797
+ noCache: msg.noCache === true,
798
+ deadlineMs: msg.deadlineMs,
799
+ historyRequestId: msg.historyRequestId || null,
800
+ historyContractVersion: msg.historyContractVersion,
801
+ historyCycleId: msg.historyCycleId,
802
+ historyMode: msg.historyMode || msg.mode,
803
+ historyCursor: msg.historyCursor,
804
+ requestedGeneration: msg.requestedGeneration,
805
+ requestedOrderingGeneration: msg.requestedOrderingGeneration,
806
+ source: msg.reason || msg.source || 'walle-history-request',
807
+ }).catch((error) => _sendWalleHistoryErrorFrame(ws, id, {
808
+ ...historyControllerMarker,
809
+ historyRequestId: msg.historyRequestId || null,
810
+ historyContractVersion: msg.historyContractVersion,
811
+ historyCycleId: msg.historyCycleId,
812
+ historyMode: msg.historyMode || msg.mode,
813
+ historyCursor: msg.historyCursor,
814
+ requestedGeneration: msg.requestedGeneration,
815
+ requestedOrderingGeneration: msg.requestedOrderingGeneration,
816
+ source: msg.reason || msg.source || 'walle-history-request',
817
+ mode: msg.mode,
818
+ before: msg.before,
819
+ afterCursor: msg.afterCursor,
820
+ around: msg.around,
821
+ historyWatermark: msg.historyWatermark,
822
+ projectionEpoch: msg.projectionEpoch,
823
+ orderingEpoch: msg.orderingEpoch,
824
+ }, error));
825
+ }
826
+
827
+ module.exports = {
828
+ configure,
829
+ _walleHistoryForSessionUi,
830
+ _walleHistoryKeyRange,
831
+ _walleHistoryPayload,
832
+ _noteWalleHistoryCatchUp,
833
+ _noteWalleHistoryCursorExpired,
834
+ _sendWalleHistoryReply,
835
+ _sendWalleHistoryErrorFrame,
836
+ _sendWalleSessionHistory,
837
+ handleWalleHistoryRequest,
838
+ };