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,454 @@
1
+ 'use strict';
2
+ // WP-7 (coding-task-manager/docs/fleet-origin-main-sync-skill-2026-10-31.html §13.4 R-R1/R-R5..R-R10,
3
+ // §13.5, §13.6): the fleet-sync resolver — pure decisions, an injected-effect start sequence, the
4
+ // resolver state file, and the session's first prompt.
5
+ //
6
+ // R-R1: the trigger is a `land sync` verdict. CTM's origin follower (WP-8) reads
7
+ // `<common>/landing/sync-state.json`#lastSync after every tick; when that verdict is `conflict` or
8
+ // `gate_failed` for a `kind:"sync"` job, this module decides whether a resolver session must start,
9
+ // starts it through injected effects, and writes `<common>/landing/resolver.json`.
10
+ //
11
+ // Everything decision-shaped here is PURE: `decide()` reads no fs and no clock (the caller passes
12
+ // `now`), so the whole rule table is a table test. The effects (`prepareWorkspace`, `resolvePick`,
13
+ // `createSession`, `notify`) are injected by the caller — this module never touches server.js, never
14
+ // creates a session on its own, and never pushes or moves `main` (R-R6 anti-goal: the resolver's
15
+ // text names `git push` exactly once, on its Never line).
16
+ //
17
+ // R-R7 dedupe: one resolver per box, keyed per (local main tip, origin tip) — `key = base..tip`.
18
+ // A key that is live is never started twice; a key that already ended (in `history`, or a `current`
19
+ // record with an ended state) never restarts itself. A live record whose origin moved is kept as-is
20
+ // (R-14.4: no queue nudge — the brief's own step 4 re-runs `land sync`), and a resolver with no
21
+ // progress past its deadline follows the existing give-up path.
22
+ //
23
+ // §14 WP-13: the SAME `key` can also be owned from outside CTM. The `/fleet-sync` skill resolves the
24
+ // sync in the USER's own session and marks the key first (`land resolve <job> --claim`, which writes
25
+ // `owner: "session:<id>"` into this file); `decide()` then returns the no-op `keep`/`claimed` for it
26
+ // so no second resolver is ever started for that conflict.
27
+
28
+ const fsp = require('node:fs/promises');
29
+ const path = require('node:path');
30
+
31
+ const CONTRACT = 1;
32
+ // §13.5: `resolveMaxMinutes` default (R-R8 deadline) — the caller's normalized publish block wins.
33
+ const DEFAULT_RESOLVE_MAX_MINUTES = 120;
34
+ // §13.5: default resolver agent when the config omits one.
35
+ const DEFAULT_RESOLVE_AGENT = 'claude';
36
+ // §13.4 R-R7: history keeps the last 20 ended records, newest first.
37
+ const HISTORY_LIMIT = 20;
38
+ // A `current` record in one of these states owns the box's resolver slot (R-R7).
39
+ const LIVE_STATES = Object.freeze(['starting', 'resolving']);
40
+ // §13.4 R-R1: the two verdicts that make a sync job a resolver candidate.
41
+ const CANDIDATE_VERDICTS = Object.freeze(['conflict', 'gate_failed']);
42
+ // A verdict in this set, recorded after the resolver started, means the sync finished under it.
43
+ const SETTLED_VERDICTS = Object.freeze(['in_sync', 'pulled', 'pushed', 'published']);
44
+
45
+ // §14.3 R-14.4: the rev-3 R-R7 queue nudge is gone — a live record whose origin moved is simply
46
+ // KEPT (the `origin_moved` rule in `decide()`); there is no second prompt to queue and no per-key
47
+ // bookkeeping on the record.
48
+
49
+ // R-R4/R-R5 fast gate for a resolver's own merge commit, and the verify gate it runs afterwards.
50
+ const RESOLVER_BRANCH_PREFIX = 'sync/origin-';
51
+ const WORKTREE_NAME_PREFIX = 'sync-origin-';
52
+
53
+ // §14 WP-13: the `owner` prefix that marks a record as claimed by a USER session (the `/fleet-sync`
54
+ // skill's own session, via `land resolve <job> --claim`) rather than one CTM started. A CTM-started
55
+ // record either has no `owner` at all or carries one without this prefix.
56
+ const SESSION_OWNER_PREFIX = 'session:';
57
+
58
+ /** §14 WP-13: does this record belong to a claiming user session (`owner: "session:<id>"`)? */
59
+ function claimedBySession(record) {
60
+ return Boolean(record) && typeof record.owner === 'string' && record.owner.startsWith(SESSION_OWNER_PREFIX);
61
+ }
62
+
63
+ /** `<common>/landing/resolver.json` — the one resolver state file (R-R7). */
64
+ function resolverPath(commonDir) {
65
+ return path.join(String(commonDir || ''), 'landing', 'resolver.json');
66
+ }
67
+
68
+ /** The empty state every reader gets for a missing or corrupt file (never a throw). */
69
+ function emptyState(warning) {
70
+ const state = { contract: CONTRACT, current: null, history: [] };
71
+ if (warning) state.warning = warning;
72
+ return state;
73
+ }
74
+
75
+ /** The 12-char short form used in branch names and the session title (§13.5/R-R10). */
76
+ function short12(sha) {
77
+ return String(sha == null ? '' : sha).slice(0, 12);
78
+ }
79
+
80
+ /** R-R7: the dedupe key of a (local main tip, origin tip) pair. */
81
+ function jobKey(job) {
82
+ if (!job || !job.base || !job.tip) return '';
83
+ return `${job.base}..${job.tip}`;
84
+ }
85
+
86
+ /** The branch a resolver for this tip runs on; R-R10, and the pre-commit hook (WP-6) reads it. */
87
+ function branchFor(tip) {
88
+ return `${RESOLVER_BRANCH_PREFIX}${short12(tip)}`;
89
+ }
90
+
91
+ /** The worktree NAME (not path) a resolver runs in; R-R10. */
92
+ function worktreeNameFor(tip) {
93
+ return `${WORKTREE_NAME_PREFIX}${short12(tip)}`;
94
+ }
95
+
96
+ /** `now` is the only clock `decide()` may read; accept ms epoch, Date, or ISO string. */
97
+ function nowToMs(now) {
98
+ if (typeof now === 'number') return now;
99
+ if (now instanceof Date) return now.getTime();
100
+ const t = Date.parse(String(now));
101
+ if (!Number.isFinite(t)) throw new TypeError('fleet-resolver: `now` must be a ms epoch, a Date, or an ISO string');
102
+ return t;
103
+ }
104
+
105
+ function isoOf(ms) {
106
+ return new Date(ms).toISOString();
107
+ }
108
+
109
+ // --- state file (atomic write, tolerant read) -----------------------------------------------------
110
+
111
+ /**
112
+ * Read `<common>/landing/resolver.json`. A missing file is the empty state with no warning; a file
113
+ * that cannot be parsed (or is not an object) is the empty state plus a `warning` — REPORTED, never
114
+ * thrown, so one torn file can never stop the follower's tick.
115
+ */
116
+ async function readState(commonDir) {
117
+ const file = resolverPath(commonDir);
118
+ let raw;
119
+ try {
120
+ raw = await fsp.readFile(file, 'utf8');
121
+ } catch (error) {
122
+ if (error && error.code === 'ENOENT') return emptyState();
123
+ return emptyState(`resolver state unreadable at ${file}: ${error && error.message ? error.message : String(error)}`);
124
+ }
125
+ let parsed;
126
+ try {
127
+ parsed = JSON.parse(raw);
128
+ } catch (error) {
129
+ return emptyState(`resolver state corrupt at ${file}: ${error && error.message ? error.message : String(error)}`);
130
+ }
131
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
132
+ return emptyState(`resolver state corrupt at ${file}: not a JSON object`);
133
+ }
134
+ return {
135
+ contract: CONTRACT,
136
+ current: parsed.current && typeof parsed.current === 'object' ? parsed.current : null,
137
+ history: Array.isArray(parsed.history) ? parsed.history : [],
138
+ };
139
+ }
140
+
141
+ /**
142
+ * Write the state atomically: `resolver.json.tmp-<pid>` then `fs.rename` (same directory, so
143
+ * the rename is atomic — a reader never sees a half-written file). `history` is trimmed to the last
144
+ * 20 ended records, newest first. A `warning` read off disk is never persisted.
145
+ */
146
+ async function writeState(commonDir, state) {
147
+ const file = resolverPath(commonDir);
148
+ const payload = {
149
+ contract: CONTRACT,
150
+ current: state && state.current ? state.current : null,
151
+ history: state && Array.isArray(state.history) ? state.history.slice(0, HISTORY_LIMIT) : [],
152
+ };
153
+ await fsp.mkdir(path.dirname(file), { recursive: true });
154
+ const tmp = `${file}.tmp-${process.pid}`;
155
+ await fsp.writeFile(tmp, `${JSON.stringify(payload, null, 2)}\n`);
156
+ await fsp.rename(tmp, file);
157
+ return payload;
158
+ }
159
+
160
+ /** The full on-disk state object for a write of one `current` record plus the history we read. */
161
+ function stateWith(prev, current) {
162
+ return { contract: CONTRACT, current, history: prev && Array.isArray(prev.history) ? prev.history : [] };
163
+ }
164
+
165
+ // --- decide() -------------------------------------------------------------------------------------
166
+
167
+ function none(reason, record) {
168
+ return { action: 'none', reason, record: record || null };
169
+ }
170
+
171
+ /** The normalized publish block: missing `resolve` means a session, missing keys take the defaults. */
172
+ function publishPolicy(publish) {
173
+ const p = publish && typeof publish === 'object' ? publish : null;
174
+ if (!p) return null;
175
+ const resolve = p.resolve === undefined || p.resolve === null ? 'session' : p.resolve;
176
+ const maxMinutes = Number.isFinite(p.resolveMaxMinutes) && p.resolveMaxMinutes > 0
177
+ ? p.resolveMaxMinutes
178
+ : DEFAULT_RESOLVE_MAX_MINUTES;
179
+ const agent = typeof p.resolveAgent === 'string' && p.resolveAgent ? p.resolveAgent : DEFAULT_RESOLVE_AGENT;
180
+ return { resolve, maxMinutes, agent };
181
+ }
182
+
183
+ /** R-R1: is this sync job a resolver candidate at all? */
184
+ function candidateJob(syncState, job) {
185
+ const verdict = syncState && syncState.lastSync ? syncState.lastSync.verdict : null;
186
+ if (!CANDIDATE_VERDICTS.includes(verdict)) return null;
187
+ if (!job || job.kind !== 'sync') return null;
188
+ if (job.state !== 'blocked' && job.state !== 'failed') return null;
189
+ if (!job.base || !job.tip) return null;
190
+ return { verdict, key: jobKey(job) };
191
+ }
192
+
193
+ /** R-R7: has this key already been handled — live, ended, or in the history? */
194
+ function alreadyHandled(key, current, history) {
195
+ if (current && current.key === key) return true;
196
+ return (history || []).some((row) => row && row.key === key);
197
+ }
198
+
199
+ /** The fresh `current` record a `start` begins with. */
200
+ function newRecord({ job, verdict, nowMs, maxMinutes, agent }) {
201
+ const startedAt = isoOf(nowMs);
202
+ return {
203
+ key: jobKey(job),
204
+ trigger: verdict,
205
+ jobId: job.id || job.jobId || null,
206
+ conflictPaths: Array.isArray(job.conflictPaths) ? job.conflictPaths : [],
207
+ gateLog: job.gateLog === undefined ? null : job.gateLog,
208
+ base: job.base,
209
+ tip: job.tip,
210
+ sessionId: null,
211
+ branch: branchFor(job.tip),
212
+ worktree: null,
213
+ agent,
214
+ model: '',
215
+ modelSource: '',
216
+ state: 'starting',
217
+ startedAt,
218
+ deadlineAt: isoOf(nowMs + maxMinutes * 60 * 1000),
219
+ updatedAt: startedAt,
220
+ reason: '',
221
+ };
222
+ }
223
+
224
+ /**
225
+ * §13.4 R-R1/R-R7: the whole decision table, first match wins.
226
+ *
227
+ * `syncState` parsed `<common>/landing/sync-state.json` (`{ lastSync: { verdict, at, jobId } }`);
228
+ * `job` the job record for `lastSync.jobId` (or null);
229
+ * `publish` normalized `config.publish` (`resolve`, `resolveAgent`, `resolveMaxMinutes`);
230
+ * `now` ms epoch | Date | ISO string — the ONLY clock read here;
231
+ * `sessionAlive(id)` → boolean, for a live record's session.
232
+ *
233
+ * Returns `{ action, reason, record }`. A live record whose `owner` is `session:<id>` (§14 WP-13, a
234
+ * claim written by `land resolve <job> --claim`) is `keep`/`claimed`: the follower starts nothing.
235
+ * PURE — no fs, no `Date.now()`.
236
+ */
237
+ function decide({ syncState, resolverState, job, publish, now, sessionAlive } = {}) {
238
+ const policy = publishPolicy(publish);
239
+ if (!policy || policy.resolve === 'off') return none('resolve_off');
240
+
241
+ const nowMs = nowToMs(now);
242
+ const state = resolverState && typeof resolverState === 'object' ? resolverState : emptyState();
243
+ const current = state.current && typeof state.current === 'object' ? state.current : null;
244
+ const history = Array.isArray(state.history) ? state.history : [];
245
+ const lastSync = syncState && syncState.lastSync ? syncState.lastSync : null;
246
+
247
+ // A live record owns the slot: finish it, end it, keep it, or leave it alone (R-R7/R-14.4).
248
+ if (current && LIVE_STATES.includes(current.state)) {
249
+ if (lastSync && SETTLED_VERDICTS.includes(lastSync.verdict)
250
+ && lastSync.at && current.startedAt
251
+ && Date.parse(lastSync.at) > Date.parse(current.startedAt)) {
252
+ return {
253
+ action: 'finish',
254
+ reason: 'sync_settled',
255
+ record: { ...current, state: 'done', reason: '', updatedAt: isoOf(nowMs) },
256
+ };
257
+ }
258
+ // §14 WP-13: a claimed record is never ended by session liveness — the claiming session's id is
259
+ // not necessarily in CTM's map at all (a shell/other-provider session can claim), so
260
+ // `session_closed` would drop a live claim on the very next tick and start the second resolver
261
+ // this rule exists to prevent. Its deadline below still retires it (AC-13c).
262
+ if (!claimedBySession(current) && typeof sessionAlive === 'function' && !sessionAlive(current.sessionId)) {
263
+ return {
264
+ action: 'give_up',
265
+ reason: 'session_closed',
266
+ record: { ...current, state: 'gave_up', reason: 'session_closed', updatedAt: isoOf(nowMs) },
267
+ };
268
+ }
269
+ if (current.deadlineAt && nowMs >= Date.parse(current.deadlineAt)) {
270
+ return {
271
+ action: 'give_up',
272
+ reason: 'deadline',
273
+ record: { ...current, state: 'gave_up', reason: 'deadline', updatedAt: isoOf(nowMs) },
274
+ };
275
+ }
276
+ // §14 WP-13 AC-13b: the key is owned by the user's own session — the follower starts nothing. The
277
+ // no-op action is the existing `keep` (R-14.4), so `apply()` writes nothing and `start()` is never
278
+ // reached; only the reason distinguishes a claim from a live resolver whose origin moved.
279
+ if (claimedBySession(current)) return { action: 'keep', reason: 'claimed', record: current };
280
+ const moved = candidateJob(syncState, job);
281
+ if (moved && moved.key && moved.key !== current.key) {
282
+ // §14.3 R-14.4: the record is LEFT in its live state and nothing is written — no queue nudge
283
+ // (the rev-3 R-R7 path had the same Enter defect as the start prompt, E-5). The brief's own
284
+ // step 4 re-runs `land sync` after `land resolve`, which picks up an origin that moved.
285
+ return { action: 'keep', reason: 'origin_moved', record: current };
286
+ }
287
+ return none('resolving', current);
288
+ }
289
+
290
+ // No live record: is this a brand-new candidate?
291
+ const candidate = candidateJob(syncState, job);
292
+ if (!candidate) return none('no_candidate', current);
293
+ if (alreadyHandled(candidate.key, current, history)) return none('already_handled', current);
294
+
295
+ return {
296
+ action: 'start',
297
+ reason: 'candidate',
298
+ record: newRecord({ job, verdict: candidate.verdict, nowMs, maxMinutes: policy.maxMinutes, agent: policy.agent }),
299
+ };
300
+ }
301
+
302
+ // --- briefFor() -----------------------------------------------------------------------------------
303
+
304
+ /**
305
+ * §13.6: the resolver session's first prompt — deterministic from the record alone. `git push`
306
+ * appears exactly once, on the Never line (bin/landing-rule-lint.js fixture, S-R13).
307
+ */
308
+ function briefFor(record) {
309
+ const r = record || {};
310
+ const base12 = short12(r.base);
311
+ const tip12 = short12(r.tip);
312
+ const lines = [];
313
+ lines.push(`You are this box's fleet-sync resolver. Job ${r.jobId}: local main ${base12} and origin/main ${tip12}`);
314
+ if (r.trigger === 'gate_failed') {
315
+ lines.push(`merge is clean but the gate failed; log: ${r.gateLog == null ? 'unknown' : r.gateLog}`);
316
+ } else {
317
+ lines.push(`conflict in: ${(Array.isArray(r.conflictPaths) ? r.conflictPaths : []).join(', ')}`);
318
+ }
319
+ lines.push(`1. In this worktree run \`git merge --no-ff ${r.tip}\` (ledger merge drivers and the .ctm.json landing.refresh commands run; never hand-edit a ledger — regenerate it with the command .ctm.json names).`);
320
+ lines.push('2. Resolve keeping BOTH sides\' intent; never drop either side\'s change; for append-only registries keep every row.');
321
+ lines.push('3. `git commit` (for a conflict the hook runs the fast resolver lane automatically). Never `--no-verify`.');
322
+ lines.push(`4. \`node coding-task-manager/bin/land.js resolve ${r.jobId} --worktree "$PWD" --wait --json\`, then \`node coding-task-manager/bin/land.js sync --wait --json\`. If either reports conflict/publish_contended: run \`land sync\` again and repeat from 1 on the new tips.`);
323
+ if (r.trigger === 'conflict') {
324
+ lines.push(`5. After it is published, verify with the full gate on what landed: \`node bin/bounded.js --deadline-ms 5400000 -- bash bin/test-gate.sh --commit --plan-base ${r.base}\`. On FAIL: fix on this branch, commit (normal gate), \`land submit\`, then \`land sync\`.`);
325
+ }
326
+ lines.push('Never: git push, git merge into main, update-ref main, --force, --no-verify, restarting CTM.');
327
+ lines.push('Report each verdict in one line.');
328
+ return `${lines.join('\n')}\n`;
329
+ }
330
+
331
+ // --- start() / apply() — injected effects ---------------------------------------------------------
332
+
333
+ function reasonOf(error) {
334
+ if (!error) return 'unknown';
335
+ return error.code || error.message || String(error);
336
+ }
337
+
338
+ /**
339
+ * §13.5/R-R10 + §14.3 R-14.3: start one resolver session, in this exact order —
340
+ * prepareWorkspace(baseRef = record.base) → resolvePick → briefFor(record) →
341
+ * createSession(title `fleet-sync: resolve <tip12>`, argv, prompt = the brief) →
342
+ * writeState(state `resolving`).
343
+ * The brief is built BEFORE the session exists and rides the launch argv as the CLI's positional
344
+ * prompt (R-14.3); there is no queue, no PTY typing and no Enter, so nothing can sit unsubmitted.
345
+ * Any step throwing ends the record `failed` with the error's code/message, writes it, notifies
346
+ * once, and returns the record — `start` NEVER throws (a failed resolver cannot kill the tick).
347
+ */
348
+ async function start({ record, repoRoot, commonDir, deps }) {
349
+ const effects = deps || {};
350
+ const tip12 = short12(record && record.tip);
351
+ const prev = await readState(commonDir);
352
+ let current = { ...record, state: 'starting', updatedAt: isoOf(Date.now()) };
353
+ try {
354
+ const workspace = await effects.prepareWorkspace({
355
+ repoRoot,
356
+ baseRef: current.base,
357
+ worktreeName: worktreeNameFor(current.tip),
358
+ branchName: current.branch,
359
+ });
360
+ current = { ...current, worktree: workspace && workspace.cwd ? workspace.cwd : null };
361
+ if (workspace && workspace.branch) current.branch = workspace.branch;
362
+
363
+ const pick = await effects.resolvePick({ agent: current.agent });
364
+ current = {
365
+ ...current,
366
+ model: pick && pick.model ? pick.model : '',
367
+ modelSource: pick && pick.source ? pick.source : '',
368
+ };
369
+
370
+ // Built from the record alone, so it is known before the session exists (R-14.3).
371
+ const brief = briefFor(current);
372
+ const session = await effects.createSession({
373
+ cwd: current.worktree,
374
+ agent: current.agent,
375
+ model: current.model,
376
+ title: `fleet-sync: resolve ${tip12}`,
377
+ argv: pick && Array.isArray(pick.argv) ? pick.argv : [],
378
+ prompt: brief,
379
+ });
380
+ current = { ...current, sessionId: session && session.sessionId ? session.sessionId : null };
381
+
382
+ current = { ...current, state: 'resolving', updatedAt: isoOf(Date.now()) };
383
+ await writeState(commonDir, stateWith(prev, current));
384
+ return current;
385
+ } catch (error) {
386
+ const failed = { ...current, state: 'failed', reason: reasonOf(error), updatedAt: isoOf(Date.now()) };
387
+ await writeState(commonDir, stateWith(prev, failed));
388
+ try {
389
+ if (typeof effects.notify === 'function') {
390
+ await effects.notify({
391
+ title: 'fleet sync resolver failed',
392
+ body: `could not start the resolver for job ${failed.jobId}: ${failed.reason}`,
393
+ sessionId: failed.sessionId || null,
394
+ });
395
+ }
396
+ } catch { /* expected: the failure notice is itself best-effort — start() never throws */ }
397
+ return failed;
398
+ }
399
+ }
400
+
401
+ /**
402
+ * §13.5/R-R7: execute one `decide()` result.
403
+ * start → start(); the new record.
404
+ * finish/give_up → move `current` to `history` with that state, writeState;
405
+ * give_up also notifies once (R-R8).
406
+ * keep/none → nothing (R-14.4: the nudge path is gone, so a live `origin_moved` record is a
407
+ * pure no-op — no prompt, no state write).
408
+ */
409
+ async function apply({ decision, repoRoot, commonDir, deps } = {}) {
410
+ const effects = deps || {};
411
+ const result = decision || { action: 'none' };
412
+ const prev = await readState(commonDir);
413
+
414
+ if (result.action === 'start') {
415
+ return start({ record: result.record, repoRoot, commonDir, deps: effects });
416
+ }
417
+
418
+ if (result.action === 'finish' || result.action === 'give_up') {
419
+ const ended = result.record || {};
420
+ const record = { ...ended, state: result.action === 'finish' ? 'done' : 'gave_up', updatedAt: isoOf(Date.now()) };
421
+ const history = [record, ...(prev.history || [])].slice(0, HISTORY_LIMIT);
422
+ await writeState(commonDir, { contract: CONTRACT, current: null, history });
423
+ if (result.action === 'give_up' && typeof effects.notify === 'function') {
424
+ const paths = (Array.isArray(record.conflictPaths) ? record.conflictPaths : []).join(', ');
425
+ await effects.notify({
426
+ title: 'fleet sync needs you',
427
+ body: `conflict in ${paths} on ${short12(record.tip)} — resolver ${result.reason}`,
428
+ sessionId: record.sessionId || null,
429
+ });
430
+ }
431
+ return record;
432
+ }
433
+
434
+ // `keep` (R-14.4 origin_moved) and `none` write nothing: the live record stays exactly as it is.
435
+ return null;
436
+ }
437
+
438
+ module.exports = {
439
+ CONTRACT,
440
+ DEFAULT_RESOLVE_MAX_MINUTES,
441
+ DEFAULT_RESOLVE_AGENT,
442
+ HISTORY_LIMIT,
443
+ resolverPath,
444
+ jobKey,
445
+ branchFor,
446
+ worktreeNameFor,
447
+ claimedBySession,
448
+ readState,
449
+ writeState,
450
+ decide,
451
+ start,
452
+ apply,
453
+ briefFor,
454
+ };
@@ -0,0 +1,114 @@
1
+ 'use strict';
2
+
3
+ // WP-2 (docs/fleet-session-lists-hub-search-2026-11-01.html §5 D-9/D-11, §6.2-§6.3, §8 WP-2,
4
+ // §9 S-7/S-8): the two session-list route SPECS, and their registration into WP-1's
5
+ // FLEET_LIST_ROUTES map.
6
+ //
7
+ // The specs are deliberately plain data. lib/fleet-session-lists.js `fleetRouted()` owns every
8
+ // rule that is hard to get right (D-2 fan-out, D-4 dedup, D-6 envelope, D-8 stale-if-error,
9
+ // D-10 provenance) — this module only says, per route:
10
+ // - which key of the local body holds the row array (`rowsKey`);
11
+ // - how a row is keyed for dedup (`rowId`);
12
+ // - how the caller's `limit` is read and clamped (`limitOf`);
13
+ // - which ORDER the merged rows keep — the route's own existing order, re-applied over the
14
+ // union of every machine's rows (D-9: one comparator, never a cross-machine RRF);
15
+ // - whether D-8 stale-if-error applies (`lastGood`), and what "unfiltered" means (`unfiltered`);
16
+ // - `select` = the local route's own selection rule, run over the deduped union.
17
+ //
18
+ // Registering is a module-load side effect (`registerFleetListRoutes()` at the bottom) so
19
+ // lib/api/sessions.js only has to `require()` this file before it reads the map.
20
+
21
+ const { FLEET_LIST_ROUTES } = require('./fleet-session-lists');
22
+ const {
23
+ normalizeRecentCatalogShape,
24
+ selectRecentCatalogRows,
25
+ } = require('./recent-session-catalog-response');
26
+ // The ONE search-hit comparator, required exactly the way the server requires it
27
+ // (server.js:2179) so a merged fleet order equals single-DB order (D-9) by construction.
28
+ const SessionSearchUtils = require('../public/js/session-search-utils.js');
29
+
30
+ const RECENT_SESSIONS_ROUTE = 'GET /api/recent-sessions';
31
+ const SESSION_SEARCH_ROUTE = 'GET /api/sessions/search';
32
+
33
+ /** The same clamp `buildSessionSearchResponse` applies (lib/api/session-messages.js:1013). */
34
+ const MAX_SESSION_SEARCH_LIMIT = 100;
35
+ const DEFAULT_SESSION_SEARCH_LIMIT = 50;
36
+
37
+ /**
38
+ * D-4 (`sessionId || id`): both list shapes carry a session id under one of these two names, and
39
+ * a row with neither is left unkeyed (kept, never merged) — same rule as
40
+ * lib/fleet-session-lists.js `defaultRowId`, narrowed to the two keys these routes actually emit.
41
+ */
42
+ function listRowId(row) {
43
+ return String((row && (row.sessionId || row.id)) || '');
44
+ }
45
+
46
+ /**
47
+ * §6.2/§6.3: GET /api/recent-sessions.
48
+ *
49
+ * `limitOf` is the route's own limit rule — `normalizeRecentCatalogShape` (1..500, default 50) —
50
+ * so the merged list is sliced exactly where the single-machine list is.
51
+ *
52
+ * `select` is `selectRecentCatalogRows` (WP-0, extracted from `buildRecentSessionCatalogResponse`):
53
+ * activityMs desc, then starred/pinned rows kept past the limit (D-9). The owner filter is NOT
54
+ * re-applied here — the hub's own leg already ran with no `owner`, and D-4 already dropped the
55
+ * copies of our sessions at the box boundary.
56
+ *
57
+ * `lastGood: true` + `unfiltered` (no `q`) = D-8's cache eligibility: an unfiltered Recent list is
58
+ * the same query for every caller, so a down box's last good rows may be re-served as `stale`.
59
+ */
60
+ const recentSessionsSpec = {
61
+ rowsKey: 'sessions',
62
+ rowId: listRowId,
63
+ limitOf: (url) => normalizeRecentCatalogShape({ limit: url.searchParams.get('limit') || 50 }).limit,
64
+ select: (rows, { limit }) => selectRecentCatalogRows(rows, { limit }),
65
+ lastGood: true,
66
+ unfiltered: (url) => !url.searchParams.get('q'),
67
+ };
68
+
69
+ /**
70
+ * §6.2/§6.3: GET /api/sessions/search.
71
+ *
72
+ * `limitOf` is the same 1..100 clamp `buildSessionSearchResponse` uses, so a forwarded leg and the
73
+ * local leg slice identically.
74
+ *
75
+ * `select` is `SessionSearchUtils.compareSearchHits` over the deduped union (D-9), then the limit.
76
+ * `lastGood: false`: search hits are query-keyed with a low hit rate, so D-8 never caches them.
77
+ */
78
+ const sessionSearchSpec = {
79
+ rowsKey: 'results',
80
+ rowId: listRowId,
81
+ limitOf: (url) => {
82
+ const requested = parseInt(url.searchParams.get('limit') || String(DEFAULT_SESSION_SEARCH_LIMIT), 10);
83
+ return Math.min(Math.max(requested || DEFAULT_SESSION_SEARCH_LIMIT, 1), MAX_SESSION_SEARCH_LIMIT);
84
+ },
85
+ select: (rows, { limit }) => rows.slice().sort(SessionSearchUtils.compareSearchHits).slice(0, limit),
86
+ lastGood: false,
87
+ };
88
+
89
+ /**
90
+ * Fill FLEET_LIST_ROUTES (§6.2/§7). Idempotent — assigning the same two spec objects again is a
91
+ * no-op — and called once at module load, so a plain `require()` of this file is enough for the
92
+ * router wiring and for the §7 census gate (tests-integration/fleet-session-lists-census.test.js).
93
+ */
94
+ function registerFleetListRoutes() {
95
+ FLEET_LIST_ROUTES[RECENT_SESSIONS_ROUTE] = recentSessionsSpec;
96
+ FLEET_LIST_ROUTES[SESSION_SEARCH_ROUTE] = sessionSearchSpec;
97
+ return FLEET_LIST_ROUTES;
98
+ }
99
+
100
+ registerFleetListRoutes();
101
+
102
+ module.exports = {
103
+ RECENT_SESSIONS_ROUTE,
104
+ SESSION_SEARCH_ROUTE,
105
+ // The same object lib/fleet-session-lists.js exports (identity matters: the router and the §7
106
+ // census read THIS map), re-exported so a caller needs one require to wire the routes.
107
+ FLEET_LIST_ROUTES,
108
+ // Re-exported so lib/api/sessions.js wires both rows with one require (its file-size ceiling).
109
+ fleetRouted: require('./fleet-session-lists').fleetRouted,
110
+ listRowId,
111
+ recentSessionsSpec,
112
+ sessionSearchSpec,
113
+ registerFleetListRoutes,
114
+ };