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,424 @@
1
+ 'use strict';
2
+ // WP-6 (docs/fleet-origin-main-sync-skill-2026-10-31.html §13.4 R-R3/R-R4/R-R6, §13.5, §13.7): the
3
+ // CLI half of the resolver — `land resolve <jobId> --worktree <dir> [--wait] [--json]`.
4
+ //
5
+ // WP-13 (§14) adds the other half: `land resolve <jobId> --claim [--session <id>] [--json]` marks the
6
+ // job's resolver key as owned by the session that is about to resolve it, so the follower's `decide()`
7
+ // skips it instead of opening a second resolver (see `cmdClaim` below).
8
+ //
9
+ // The verb does not resolve anything itself. An agent resolves the merge in its OWN worktree, on the
10
+ // branch `sync/origin-<origin12>` (R-R2/R-R3: only the queue lands, so a hand commit on main in that
11
+ // worktree is never the answer), and this verb VALIDATES that commit and submits it as an ordinary
12
+ // landing job with `kind:"resolve"`. Validation is four refusals, each a typed `landingError` with no
13
+ // job file written:
14
+ //
15
+ // job_not_resolvable the id is unknown, or the job is not `kind:"sync"`, or it is not in
16
+ // `blocked`/`failed` — the two states a resolver session is opened for
17
+ // (§13.4 R-R1).
18
+ // dirty_worktree tracked changes or unmerged paths in `--worktree` (R-R3: the resolution is
19
+ // committed first; a resolution that exists only as a working-tree edit is not
20
+ // a resolution).
21
+ // head_not_merge nothing in `job.base..HEAD` is a merge of EXACTLY the two sides this job
22
+ // conflicted (`{job.base, job.tip}`, order-insensitive) — a hand-built commit,
23
+ // or a rebase that flattened the merge (§13.4 R-R6).
24
+ // conflict_markers a path this job conflicted on still carries `<<<<<<<` / `=======` / `>>>>>>>`
25
+ // in HEAD (R-R3's marker refusal: `git add` of a half-resolved file must not
26
+ // land).
27
+ //
28
+ // Submission reuses the queue primitives `land submit` itself uses — `queue.enqueue` and
29
+ // `syncCli.ensureRunner` — rather than a second copy of the submit path (bin/land.js is under a
30
+ // frozen size ceiling, so the shared internals stay where they already are).
31
+ //
32
+ // The only new state the job carries is `kind:"resolve"`, `resolves:<jobId>` and `gateLane`, which
33
+ // the runner turns into a gate choice: a resolved CONFLICT (`blocked`) is re-gated by the FAST gate
34
+ // (§13.4 R-R4), a resolved GATE FAILURE (`failed`) by the ordinary `landing.gates` (§13.4 R-R6).
35
+ // Publishing is not this module's business: a landed resolve job goes through the existing
36
+ // `afterTrainLanded` auto-publish path like any other landing.
37
+
38
+ const path = require('path');
39
+
40
+ const { landingError, runGitRaw, revParseOrNull } = require('./git');
41
+ const { strFlag } = require('../env-flags');
42
+ const queue = require('./queue');
43
+ const syncCli = require('./sync-cli');
44
+ const { loadLandingConfig, loadTargetLandingConfig } = require('./config');
45
+ // WP-13 (docs/fleet-origin-main-sync-skill-2026-10-31.html §14 WP-13/§14.6): the claim path writes the
46
+ // SAME resolver record `lib/fleet-resolver.js` reads, keyed the same way, through that module's own
47
+ // atomic writer — never a second copy of the state file's shape.
48
+ const resolver = require('../fleet-resolver');
49
+
50
+ // Injected by bin/land.js once, at load (the same handshake sync-cli.js takes). Held as bare
51
+ // bindings so the emitters are the process's own, never a second formatting of the same payload.
52
+ let progress = () => {};
53
+ let emitJsonOrLine = () => {};
54
+ let context = null;
55
+ let branchRef = null;
56
+ let CONTRACT = 1;
57
+ let WAIT_TIMEOUT_MS = 4 * 60 * 60 * 1000;
58
+
59
+ // `git status --porcelain` v1 unmerged codes (`git help status`): both sides of the merge still in
60
+ // the index. Every other non-`??` code is an ordinary tracked change.
61
+ const UNMERGED_CODES = Object.freeze(['DD', 'AU', 'UD', 'UA', 'DU', 'AA', 'UU']);
62
+ // §13.4 R-R3: a marker line is seven identical characters at column 0, then a space or end of line —
63
+ // the same shape `git diff --check` reports.
64
+ const MARKER_RE = new RegExp('^(?:<{7}|={7}|>{7})(?: |$)');
65
+ const RESOLVABLE_STATES = Object.freeze(['blocked', 'failed']);
66
+
67
+ function lines(stdout) {
68
+ return String(stdout || '').split('\n').map((line) => line.trim()).filter(Boolean);
69
+ }
70
+
71
+ // --- validation ------------------------------------------------------------------------------
72
+
73
+ // The refusal R-R3 makes first: the resolution has to be committed in `--worktree`. Untracked files
74
+ // are not an answer either way (they cannot be part of a commit this verb is about to land), so they
75
+ // are ignored exactly as `land submit` ignores them.
76
+ function worktreeDirt(worktree) {
77
+ const res = runGitRaw(worktree, ['status', '--porcelain']);
78
+ if (res.status !== 0) {
79
+ throw landingError('git_failed', `git status failed in ${worktree}`, { cwd: worktree, stderr: String(res.stderr || '') });
80
+ }
81
+ const dirty = [];
82
+ const unmerged = [];
83
+ for (const raw of String(res.stdout || '').split('\n')) {
84
+ if (!raw.trim()) continue;
85
+ const code = raw.slice(0, 2);
86
+ if (code === '??') continue;
87
+ const file = raw.slice(3).trim();
88
+ if (UNMERGED_CODES.includes(code)) unmerged.push(file);
89
+ else dirty.push(file);
90
+ }
91
+ return { dirty, unmerged };
92
+ }
93
+
94
+ // R-R6: the commit that IS the resolution — a merge commit in `job.base..HEAD` whose parents are
95
+ // exactly the two sides this job conflicted. Order-insensitive, because which side git names first
96
+ // depends on which branch was checked out. `null` means the worktree holds no such commit.
97
+ function resolutionCommit(worktree, job) {
98
+ if (!job.base || !job.tip) return null;
99
+ const res = runGitRaw(worktree, ['rev-list', '--merges', '--parents', `${job.base}..HEAD`]);
100
+ if (res.status !== 0) return null;
101
+ for (const line of lines(res.stdout)) {
102
+ const [commit, ...parents] = line.split(/\s+/);
103
+ if (parents.length !== 2 || !commit) continue;
104
+ const forward = parents[0] === job.base && parents[1] === job.tip;
105
+ const reverse = parents[0] === job.tip && parents[1] === job.base;
106
+ if (forward || reverse) return commit;
107
+ }
108
+ return null;
109
+ }
110
+
111
+ // R-R3's marker refusal: HEAD's version of every path this job conflicted on, scanned line by line.
112
+ // A path that does not exist in HEAD cannot hold a marker and is skipped (a resolution may legitimately
113
+ // delete a path).
114
+ function conflictMarkerHits(worktree, paths) {
115
+ const hits = [];
116
+ for (const rel of Array.isArray(paths) ? paths : []) {
117
+ if (typeof rel !== 'string' || !rel) continue;
118
+ const res = runGitRaw(worktree, ['show', `HEAD:${rel}`]);
119
+ if (res.status !== 0) continue;
120
+ const fileLines = String(res.stdout || '').split('\n');
121
+ for (let index = 0; index < fileLines.length; index += 1) {
122
+ if (MARKER_RE.test(fileLines[index])) hits.push(`${rel}:${index + 1}`);
123
+ }
124
+ }
125
+ return hits;
126
+ }
127
+
128
+ // --- submission ---------------------------------------------------------------------------------
129
+
130
+ // §13.5: the resolve job is an ordinary queue job with three extra fields. `gateLane` is decided
131
+ // HERE, from the state the sync job died in, and never re-derived later: `blocked` is a resolved
132
+ // conflict (fast), `failed` is a gate failure (full).
133
+ function resolveJobRecord(ctx, job, { branch, tip, worktree, config }) {
134
+ const session = strFlag('CTM_SESSION_ID') || null;
135
+ return {
136
+ contract: CONTRACT,
137
+ id: queue.newJobId(),
138
+ kind: 'resolve',
139
+ resolves: job.id,
140
+ gateLane: job.state === 'blocked' ? 'fast' : 'full',
141
+ repo: ctx.common,
142
+ branch,
143
+ tip,
144
+ title: `resolve ${job.id} (${branch})`,
145
+ submittedBy: { session, pid: process.pid, cwd: worktree },
146
+ state: 'queued',
147
+ reason: null,
148
+ priority: 'normal',
149
+ priorityHistory: [],
150
+ preemptions: 0,
151
+ base: null,
152
+ train: null,
153
+ tree: null,
154
+ verdict: null,
155
+ conflictPaths: [],
156
+ conflictPreview: null,
157
+ overlapWithTarget: [],
158
+ policySource: config.policySource || null,
159
+ runnerPid: null,
160
+ timestamps: { queued: queue.nowIso() },
161
+ gateLog: null,
162
+ };
163
+ }
164
+
165
+ // --- WP-13 `--claim` ---------------------------------------------------------------------------
166
+
167
+ // §14 WP-13: the two states that mean "something already holds this box's resolver slot" — the same
168
+ // pair `lib/fleet-resolver.js` calls LIVE_STATES (not exported there; `lib/fleet-resolver-tick.js`
169
+ // repeats it the same way rather than widening the module). Keep the copies in step.
170
+ const RESOLVER_LIVE_STATES = Object.freeze(['starting', 'resolving']);
171
+
172
+ /**
173
+ * §14 WP-13: who owns this claim — `--session <id>` wins, then the session this CLI runs inside, then
174
+ * the literal `session:unknown` (the readonly `CTM_SESSION_ID` flag read, never a raw `process.env`).
175
+ */
176
+ function claimOwner(opts) {
177
+ const fromArg = opts && opts.values && typeof opts.values.session === 'string' ? opts.values.session : '';
178
+ const session = fromArg || strFlag('CTM_SESSION_ID') || '';
179
+ return `session:${session || 'unknown'}`;
180
+ }
181
+
182
+ /** §14 WP-13: `resolveMaxMinutes` from the target's committed landing policy, else the module default. */
183
+ function claimMaxMinutes(cwd) {
184
+ let publish = null;
185
+ try {
186
+ publish = loadLandingConfig(cwd).publish;
187
+ } catch (error) {
188
+ /* HANDLE: an unreadable/invalid landing policy is not fatal to a claim — the default deadline
189
+ is the same one lib/fleet-resolver.js would have started the resolver with. */
190
+ void error;
191
+ }
192
+ const minutes = publish && Number.isFinite(publish.resolveMaxMinutes) && publish.resolveMaxMinutes > 0
193
+ ? publish.resolveMaxMinutes
194
+ : resolver.DEFAULT_RESOLVE_MAX_MINUTES;
195
+ return minutes;
196
+ }
197
+
198
+ /**
199
+ * §14 WP-13: the live record a claim writes — the `lib/fleet-resolver.js` record shape (so the
200
+ * follower's own readers, `keep`/`claimed` rule and history all see a record they already understand)
201
+ * plus the two fields that make it a claim: `owner` and `claimedAt`. A record already on disk for the
202
+ * same key keeps whatever it knew (a CTM-started session's id, worktree, model) — the claim takes
203
+ * ownership, it does not pretend that session never existed.
204
+ */
205
+ function claimRecord(job, { owner, claimedAt, deadlineAt, prior }) {
206
+ const before = prior && typeof prior === 'object' ? prior : {};
207
+ return {
208
+ ...before,
209
+ key: resolver.jobKey(job),
210
+ trigger: job.state === 'blocked' ? 'conflict' : 'gate_failed',
211
+ jobId: job.id || job.jobId || null,
212
+ conflictPaths: Array.isArray(job.conflictPaths) ? job.conflictPaths : [],
213
+ gateLog: job.gateLog === undefined ? null : job.gateLog,
214
+ base: job.base,
215
+ tip: job.tip,
216
+ sessionId: before.sessionId === undefined ? null : before.sessionId,
217
+ branch: resolver.branchFor(job.tip),
218
+ worktree: before.worktree === undefined ? null : before.worktree,
219
+ agent: before.agent === undefined ? null : before.agent,
220
+ model: before.model || '',
221
+ modelSource: before.modelSource || '',
222
+ state: 'resolving',
223
+ startedAt: claimedAt,
224
+ deadlineAt,
225
+ updatedAt: claimedAt,
226
+ reason: '',
227
+ owner,
228
+ claimedAt,
229
+ };
230
+ }
231
+
232
+ /**
233
+ * §14 WP-13 AC-13a: `land resolve <jobId> --claim [--session <id>] [--json]`. It runs NO gate and
234
+ * submits nothing: it marks this job's resolver key as owned by the session that is about to resolve
235
+ * it (`/fleet-sync` in the user's own session), so the follower's `decide()` skips the key instead of
236
+ * opening a second resolver for the same conflict.
237
+ */
238
+ async function cmdClaim(opts, cwd) {
239
+ const jobId = opts.positionals[0];
240
+ if (!jobId) throw usageError('land resolve <jobId> --claim [--session <id>] [--json]');
241
+ const ctx = context(cwd);
242
+ const job = queue.readJob(ctx.root, jobId);
243
+ if (!job || job.kind !== 'sync' || !RESOLVABLE_STATES.includes(job.state)) {
244
+ throw landingError(
245
+ 'job_not_resolvable',
246
+ `job ${jobId} is not a resolvable sync job`
247
+ + `${job ? ` (kind ${job.kind || 'none'}, state ${job.state})` : ' (no such job)'}; `
248
+ + 'only a kind:"sync" job in blocked or failed can be claimed',
249
+ { jobId, kind: job ? job.kind || null : null, state: job ? job.state : null },
250
+ );
251
+ }
252
+
253
+ const owner = claimOwner(opts);
254
+ const key = resolver.jobKey(job);
255
+ if (!key) {
256
+ throw landingError('job_not_resolvable', `job ${jobId} has no base/tip pair to key a resolver record on`, { jobId });
257
+ }
258
+
259
+ const prev = await resolver.readState(ctx.common);
260
+ const onDisk = prev.current && typeof prev.current === 'object' ? prev.current : null;
261
+ const live = onDisk && onDisk.key === key && RESOLVER_LIVE_STATES.includes(onDisk.state) ? onDisk : null;
262
+ // §14 WP-13: a session CTM started for this very key stands down — the user's session wins. Say so,
263
+ // then claim anyway: the follower must not start a second resolver, and the record is the only place
264
+ // it reads that from.
265
+ if (live && !resolver.claimedBySession(live)) {
266
+ progress(
267
+ `resolve: ${key} already has a CTM-started resolver session ${live.sessionId || '(unnamed)'}; `
268
+ + `${owner} claims it — CTM will not start a second one`,
269
+ );
270
+ }
271
+
272
+ const claimedAt = queue.nowIso();
273
+ const deadlineAt = new Date(Date.parse(claimedAt) + claimMaxMinutes(cwd) * 60 * 1000).toISOString();
274
+ await resolver.writeState(ctx.common, {
275
+ current: claimRecord(job, { owner, claimedAt, deadlineAt, prior: live }),
276
+ history: prev.history,
277
+ });
278
+
279
+ const payload = { ok: true, key, owner };
280
+ emitJsonOrLine(
281
+ opts,
282
+ payload,
283
+ `resolve: claimed ${key} for ${owner}${live && live.sessionId ? ` (CTM's ${live.sessionId} stands down)` : ''}`,
284
+ );
285
+ return 0;
286
+ }
287
+
288
+ function exitCodeForVerdict(verdict) {
289
+ if (verdict === 'landed' || verdict === 'queued') return 0;
290
+ if (verdict === 'timeout') return 75;
291
+ return 1;
292
+ }
293
+
294
+ async function cmdResolve(opts, cwd) {
295
+ // §14 WP-13: `--claim` is the same verb's second half — mark the key owned and stop, before any of
296
+ // the validate-and-submit work below (no --worktree, no gate, no job file).
297
+ if (opts.flags.has('claim')) return cmdClaim(opts, cwd);
298
+ const jobId = opts.positionals[0];
299
+ if (!jobId) throw usageError('land resolve <jobId> --worktree <dir> [--wait] [--json]');
300
+ const worktreeArg = opts.values.worktree;
301
+ if (!worktreeArg) throw usageError('land resolve <jobId> requires --worktree <dir>');
302
+ const worktree = path.resolve(cwd, worktreeArg);
303
+ // The argument is proved through the git layer this module already uses, not `fs`: a path that is
304
+ // not a worktree at all must be an argument error here, not a `git_failed` stack out of the first
305
+ // `git status` below.
306
+ if (runGitRaw(worktree, ['rev-parse', '--show-toplevel']).status !== 0) {
307
+ throw usageError(`--worktree ${worktree} is not a git worktree`);
308
+ }
309
+
310
+ const ctx = context(cwd);
311
+ const job = queue.readJob(ctx.root, jobId);
312
+ if (!job || job.kind !== 'sync' || !RESOLVABLE_STATES.includes(job.state)) {
313
+ throw landingError(
314
+ 'job_not_resolvable',
315
+ `job ${jobId} is not a resolvable sync job`
316
+ + `${job ? ` (kind ${job.kind || 'none'}, state ${job.state})` : ' (no such job)'}; `
317
+ + 'only a kind:"sync" job in blocked or failed can be resolved',
318
+ { jobId, kind: job ? job.kind || null : null, state: job ? job.state : null },
319
+ );
320
+ }
321
+
322
+ const dirt = worktreeDirt(worktree);
323
+ if (dirt.unmerged.length || dirt.dirty.length) {
324
+ const paths = [...dirt.unmerged, ...dirt.dirty];
325
+ throw landingError(
326
+ 'dirty_worktree',
327
+ `${worktree} has ${dirt.unmerged.length} unmerged path(s) and ${dirt.dirty.length} modified tracked file(s) `
328
+ + `(${paths.join(', ')}); commit the resolution before resolving`,
329
+ { paths, unmerged: dirt.unmerged, dirty: dirt.dirty },
330
+ );
331
+ }
332
+
333
+ const merge = resolutionCommit(worktree, job);
334
+ if (!merge) {
335
+ throw landingError(
336
+ 'head_not_merge',
337
+ `no commit in ${job.base}..HEAD of ${worktree} merges exactly {${job.base}, ${job.tip}}; `
338
+ + `merge origin/${job.branch || 'main'} into ${worktree} and commit the resolution`,
339
+ { base: job.base, tip: job.tip, worktree },
340
+ );
341
+ }
342
+
343
+ const hits = conflictMarkerHits(worktree, job.conflictPaths);
344
+ if (hits.length) {
345
+ throw landingError(
346
+ 'conflict_markers',
347
+ `unresolved conflict markers in ${hits.join(', ')}`,
348
+ { hits, paths: job.conflictPaths || [] },
349
+ );
350
+ }
351
+
352
+ // Gates are read from the TARGET's committed policy, exactly as `land submit` reads them.
353
+ const config = loadTargetLandingConfig(worktree);
354
+ const branch = currentBranch(worktree);
355
+ const tip = revParseOrNull(worktree, branchRef(branch));
356
+ if (!tip) {
357
+ throw landingError('git_failed', `cannot resolve ${branch} in ${worktree}`, { branch, worktree });
358
+ }
359
+ queue.reapDeadRunner(ctx.root);
360
+ const record = resolveJobRecord(ctx, job, { branch, tip, worktree, config });
361
+ progress(`resolve: submitting ${record.id} for ${job.id} (${branch}@${tip.slice(0, 12)}, gate lane ${record.gateLane})`);
362
+ queue.enqueue(ctx.root, record);
363
+ const started = syncCli.ensureRunner(ctx);
364
+ if (started.pid !== null) queue.updateJob(ctx.root, record.id, { runnerPid: started.pid });
365
+
366
+ const payload = { verdict: null, jobId: record.id, resolves: job.id, gateLane: record.gateLane };
367
+ if (opts.flags.has('wait')) {
368
+ // §13.5: the wait is `land sync`'s own (sync-cli.js `waitForSyncJob`) — it already knows how to
369
+ // tell "the runner died" from "still working", which a bare poll of the terminal state does not.
370
+ const final = await syncCli.waitForSyncJob(ctx, record.id);
371
+ payload.verdict = final.state;
372
+ emitJsonOrLine(
373
+ opts,
374
+ payload,
375
+ `resolve: job ${record.id} ${final.state}${final.reason ? ` (${final.reason})` : ''} for ${job.id}${record.gateLane === 'fast' ? ' [fast gate]' : ''}`,
376
+ );
377
+ return exitCodeForVerdict(final.state);
378
+ }
379
+ payload.verdict = 'queued';
380
+ emitJsonOrLine(
381
+ opts,
382
+ payload,
383
+ `resolve: job ${record.id} queued for ${job.id} on ${branch} [${record.gateLane} gate] (runner: ${started.runner})`,
384
+ );
385
+ return 0;
386
+ }
387
+
388
+ function currentBranch(worktree) {
389
+ const res = runGitRaw(worktree, ['rev-parse', '--abbrev-ref', 'HEAD']);
390
+ const name = res.status === 0 ? res.stdout.trim() : '';
391
+ if (!name || name === 'HEAD') {
392
+ throw landingError('detached_head', `${worktree} is on a detached HEAD; check out the resolver branch first`, { worktree });
393
+ }
394
+ return name;
395
+ }
396
+
397
+ function usageError(message) {
398
+ const error = new Error(message);
399
+ error.code = 'usage';
400
+ return error;
401
+ }
402
+
403
+ // bin/land.js calls this once, at load, with its own process-level helpers — the same handshake
404
+ // sync-cli.js takes. A caller that forgets it gets no-op emitters, not a TypeError mid-command.
405
+ function configure(injected) {
406
+ ({
407
+ progress,
408
+ emitJsonOrLine,
409
+ context,
410
+ branchRef,
411
+ CONTRACT,
412
+ WAIT_TIMEOUT_MS,
413
+ } = injected);
414
+ return module.exports;
415
+ }
416
+
417
+ module.exports = {
418
+ configure,
419
+ cmdResolve,
420
+ cmdClaim,
421
+ worktreeDirt,
422
+ resolutionCommit,
423
+ conflictMarkerHits,
424
+ };
@@ -0,0 +1,263 @@
1
+ 'use strict';
2
+ // WP-0 (docs/local-landing-queue-multi-session-integration-2026-10-26.html §5 D-1, §6.3, §8 WP-0,
3
+ // §7 E-5): per-repository state for the landing queue.
4
+ //
5
+ // State lives at `$(git rev-parse --git-common-dir)/landing/` — shared by every worktree of the
6
+ // repo, never inside a worktree, never under os.tmpdir() (D-1). Files are small JSON documents
7
+ // written tmp+rename, and every one of them carries `contract: 1` so an old CLI reading new
8
+ // state refuses with `contract_unsupported` instead of corrupting it (E-5).
9
+ //
10
+ // The queue has no daemon (D-2); the only cross-process serialization primitive is a `.mutex`
11
+ // directory next to the state files, exactly like the broker's `.admission` lock. The mutex is
12
+ // won by renaming a fully-stamped staging directory into place and released the same way, so the
13
+ // path is never observable without an owner stamp and never briefly empty (§5 D-1/D-2).
14
+
15
+ const fs = require('fs');
16
+ const path = require('path');
17
+ const crypto = require('crypto');
18
+ const { commonDir, landingError } = require('./git');
19
+
20
+ const CONTRACT = 1;
21
+ const MUTEX_DIRNAME = '.mutex';
22
+ // Staging directories for a mutex claim live NEXT TO the mutex (same filesystem, so the rename
23
+ // into place is atomic) and are named so they can never be mistaken for the mutex itself.
24
+ const MUTEX_STAGE_PREFIX = '.mutex-claim-';
25
+ const OWNER_FILENAME = 'owner.json';
26
+
27
+ // §6.3 directory layout under landing/.
28
+ const SUBDIRS = Object.freeze(['jobs', 'runner', 'verdicts', 'trains', 'radar', 'claims', 'metrics']);
29
+
30
+ // `$(git rev-parse --git-common-dir)/landing` (absolutized by git itself).
31
+ function landingDir(cwd) {
32
+ return path.join(commonDir(cwd), 'landing');
33
+ }
34
+
35
+ function mutexPath(root) {
36
+ return path.join(root, MUTEX_DIRNAME);
37
+ }
38
+
39
+ function ensureDirs(root) {
40
+ fs.mkdirSync(root, { recursive: true });
41
+ for (const name of SUBDIRS) fs.mkdirSync(path.join(root, name), { recursive: true });
42
+ return root;
43
+ }
44
+
45
+ // Atomic JSON write: `<file>.<pid>.<rand>.tmp` then renameSync (same-filesystem, so the rename
46
+ // is atomic). A plain object without an explicit `contract` is stamped with contract 1 — every
47
+ // state file in §6.3 carries it.
48
+ function writeJsonAtomic(file, obj) {
49
+ let payload = obj;
50
+ if (obj && typeof obj === 'object' && !Array.isArray(obj) && obj.contract === undefined) {
51
+ payload = { ...obj, contract: CONTRACT };
52
+ }
53
+ const tmp = `${file}.${process.pid}.${crypto.randomBytes(6).toString('hex')}.tmp`;
54
+ fs.mkdirSync(path.dirname(file), { recursive: true });
55
+ fs.writeFileSync(tmp, `${JSON.stringify(payload, null, 2)}\n`);
56
+ try {
57
+ fs.renameSync(tmp, file);
58
+ } catch (error) {
59
+ try {
60
+ fs.rmSync(tmp, { force: true });
61
+ } catch (cleanupError) {
62
+ /* expected: best-effort removal of our own tmp file; the rename failure below is the
63
+ error that matters and is rethrown unchanged */
64
+ }
65
+ throw error;
66
+ }
67
+ return file;
68
+ }
69
+
70
+ // object | null (ENOENT) | throws `state_unreadable` (bad JSON or unreadable file). Callers that
71
+ // must never treat a torn stamp as "free" should let the throw propagate, or use readJsonSafe.
72
+ function readJson(file) {
73
+ let raw;
74
+ try {
75
+ raw = fs.readFileSync(file, 'utf8');
76
+ } catch (error) {
77
+ if (error && error.code === 'ENOENT') return null;
78
+ throw landingError('state_unreadable', `cannot read ${file}: ${error.message}`, { file, cause: error });
79
+ }
80
+ try {
81
+ return JSON.parse(raw);
82
+ } catch (error) {
83
+ throw landingError('state_unreadable', `bad JSON in ${file}: ${error.message}`, { file, cause: error });
84
+ }
85
+ }
86
+
87
+ // Same as readJson, but an unreadable file becomes `null` instead of a throw — for callers whose
88
+ // safety property is "unknown holder", never "free" (the broker's UNREADABLE disposition).
89
+ function readJsonSafe(file) {
90
+ try {
91
+ return readJson(file);
92
+ } catch (error) {
93
+ /* expected: unreadable/torn stamp is "unknown", and every caller of readJsonSafe treats
94
+ null as "not ours / not reapable" rather than as an absent record */
95
+ return null;
96
+ }
97
+ }
98
+
99
+ // E-5: refuse state written by a contract this build does not understand.
100
+ function assertContract(obj, meta = {}) {
101
+ if (!obj || typeof obj !== 'object') {
102
+ throw landingError('contract_unsupported', `expected a contract-${CONTRACT} object`, { ...meta, contract: null });
103
+ }
104
+ if (obj.contract !== CONTRACT) {
105
+ throw landingError(
106
+ 'contract_unsupported',
107
+ `state contract ${JSON.stringify(obj.contract)} is not supported (this build speaks ${CONTRACT})`,
108
+ { ...meta, contract: obj.contract, expected: CONTRACT },
109
+ );
110
+ }
111
+ return obj;
112
+ }
113
+
114
+ // Synchronous sleep, used only for the mutex backoff.
115
+ function sleepSync(ms) {
116
+ if (!(ms > 0)) return;
117
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
118
+ }
119
+
120
+ function pidIsAlive(pid, kill) {
121
+ try {
122
+ kill(pid, 0);
123
+ return true;
124
+ } catch (error) {
125
+ // ESRCH = no such process (dead). EPERM = alive but not ours — still alive.
126
+ return !(error && error.code === 'ESRCH');
127
+ }
128
+ }
129
+
130
+ // Reap a mutex whose owner stamp is readable AND names a dead pid. An unreadable stamp is never
131
+ // reaped (we cannot tell a dead owner from one we merely failed to read). Removal is the same
132
+ // verified pattern as the lease: re-read, require the same record, rename to a private tombstone,
133
+ // then remove the tombstone.
134
+ function reapMutexIfDead(mutexDir, { kill }) {
135
+ const stampFile = path.join(mutexDir, OWNER_FILENAME);
136
+ const stamp = readJsonSafe(stampFile);
137
+ if (!stamp || typeof stamp.pid !== 'number') return false;
138
+ if (pidIsAlive(stamp.pid, kill)) return false;
139
+ const again = readJsonSafe(stampFile);
140
+ if (!again || again.pid !== stamp.pid || again.since !== stamp.since) return false;
141
+ const tombstone = `${mutexDir}.dead-${process.pid}-${crypto.randomBytes(4).toString('hex')}`;
142
+ try {
143
+ fs.renameSync(mutexDir, tombstone);
144
+ } catch (error) {
145
+ /* expected: another process won the reap or reclaimed the mutex between the read and the
146
+ rename; its holder is unaffected and we simply retry */
147
+ return false;
148
+ }
149
+ try {
150
+ fs.rmSync(tombstone, { recursive: true, force: true });
151
+ } catch (error) {
152
+ /* expected: the tombstone is now private to this call, so a failed cleanup can only leave a
153
+ stray .dead-* directory behind; the mutex path itself is already free */
154
+ }
155
+ return true;
156
+ }
157
+
158
+ // Claim the mutex the way the lease claims its directory: stage a fully-stamped directory under
159
+ // a private mkdtemp name, then renameSync it ONTO <root>/.mutex. A rename onto a NON-EMPTY
160
+ // directory fails atomically (ENOTEMPTY/EEXIST), so the mutex path is never observable as
161
+ // "exists but has no owner stamp" — a process killed mid-claim leaves a stray `.mutex-claim-*`
162
+ // staging directory, never a lock that every later caller times out on. A rename onto an EMPTY
163
+ // .mutex succeeds, which is why that stray state is not a deadlock either.
164
+ function tryClaimMutex(root, now) {
165
+ const mutexDir = mutexPath(root);
166
+ let staging;
167
+ try {
168
+ staging = fs.mkdtempSync(path.join(root, MUTEX_STAGE_PREFIX));
169
+ } catch (error) {
170
+ throw landingError('mutex_stage_failed', `cannot stage a mutex claim under ${root}: ${error.message}`, { root, cause: error });
171
+ }
172
+ try {
173
+ writeJsonAtomic(path.join(staging, OWNER_FILENAME), { pid: process.pid, since: new Date(now).toISOString() });
174
+ } catch (error) {
175
+ removeDir(staging);
176
+ throw error;
177
+ }
178
+ try {
179
+ fs.renameSync(staging, mutexDir);
180
+ return true;
181
+ } catch (error) {
182
+ removeDir(staging);
183
+ if (error && (error.code === 'ENOTEMPTY' || error.code === 'EEXIST')) return false;
184
+ throw error;
185
+ }
186
+ }
187
+
188
+ function removeDir(dir) {
189
+ try {
190
+ fs.rmSync(dir, { recursive: true, force: true });
191
+ } catch (error) {
192
+ /* expected: best-effort cleanup of a directory this call created (a staging dir, or the
193
+ private tombstone of a release); the caller's own outcome is unaffected */
194
+ }
195
+ }
196
+
197
+ // §5 (file-based state, "queue.json rewritten under a mkdir mutex like .admission").
198
+ // Sync `fn` holds the mutex for its duration; a thenable return value holds it until settlement.
199
+ function withMutex(root, fn, opts = {}) {
200
+ const timeoutMs = Number.isFinite(opts.timeoutMs) ? opts.timeoutMs : 10000;
201
+ const now = typeof opts.now === 'function' ? opts.now : () => (typeof opts.now === 'number' ? opts.now : Date.now());
202
+ const kill = opts.kill || process.kill;
203
+ const mutexDir = mutexPath(root);
204
+ fs.mkdirSync(root, { recursive: true });
205
+ const deadline = now() + timeoutMs;
206
+ let delay = 2;
207
+ for (;;) {
208
+ if (tryClaimMutex(root, now())) break;
209
+ reapMutexIfDead(mutexDir, { kill });
210
+ if (now() >= deadline) {
211
+ throw landingError('mutex_timeout', `timed out after ${timeoutMs}ms waiting for ${mutexDir}`, { mutexDir, timeoutMs });
212
+ }
213
+ sleepSync(delay);
214
+ delay = Math.min(delay * 2, 25);
215
+ }
216
+ let released = false;
217
+ // Release by renaming the whole directory to a private tombstone FIRST and only then removing
218
+ // the tombstone: rmSync(recursive) unlinks owner.json and rmdirs the path in two steps, which
219
+ // leaves a briefly-EMPTY .mutex — exactly the directory a concurrent staged rename would then
220
+ // land in, giving two holders.
221
+ const release = () => {
222
+ if (released) return;
223
+ released = true;
224
+ const tombstone = `${mutexDir}.dead-${process.pid}-${crypto.randomBytes(4).toString('hex')}`;
225
+ try {
226
+ fs.renameSync(mutexDir, tombstone);
227
+ } catch (error) {
228
+ /* expected: the mutex directory is already gone (a reap that raced our own release, or an
229
+ earlier release in this process); there is nothing left to remove */
230
+ return;
231
+ }
232
+ removeDir(tombstone);
233
+ };
234
+ let result;
235
+ try {
236
+ result = fn();
237
+ } catch (error) {
238
+ release();
239
+ throw error;
240
+ }
241
+ if (result && typeof result.then === 'function') return Promise.resolve(result).finally(release);
242
+ release();
243
+ return result;
244
+ }
245
+
246
+ module.exports = {
247
+ CONTRACT,
248
+ MUTEX_DIRNAME,
249
+ MUTEX_STAGE_PREFIX,
250
+ OWNER_FILENAME,
251
+ SUBDIRS,
252
+ landingDir,
253
+ mutexPath,
254
+ ensureDirs,
255
+ writeJsonAtomic,
256
+ readJson,
257
+ readJsonSafe,
258
+ assertContract,
259
+ withMutex,
260
+ reapMutexIfDead,
261
+ pidIsAlive,
262
+ sleepSync,
263
+ };