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,344 @@
1
+ 'use strict';
2
+ // WP-0 (docs/local-landing-queue-multi-session-integration-2026-10-26.html §5 D-3/D-5/D-11,
3
+ // §6.2, §8 WP-0): the git plumbing the landing queue is built on. Zero runtime deps.
4
+ //
5
+ // Three hard rules this module exists to enforce:
6
+ //
7
+ // 1. Every git child runs with `--no-optional-locks` (D-11 / I-7): a `git status`-style
8
+ // probe must never leave `index.lock` in a session worktree.
9
+ // 2. Every git child env is `process.env` MINUS the §4 GIT_* list (same seven names as
10
+ // bin/git-test-env.js; the two lists are asserted equal by
11
+ // tests/landing-git-primitives.test.js). I-8 is the incident: a leaked GIT_DIR made a
12
+ // gate/test process operate on the wrong repository and wipe real refs.
13
+ // 3. Nothing here writes into a session worktree. The train commit is built from
14
+ // `merge-tree`/`commit-tree` objects only; the one exception is `snapshotCommit`, which
15
+ // uses a TEMP INDEX outside the worktree so the real index (and its lock) is untouched.
16
+ //
17
+ // This file is the lowest-level module of lib/landing/ (no local requires), so it also hosts
18
+ // the shared typed-error helper `landingError` used by state.js/lease.js/config.js — that
19
+ // keeps the directory to the four files WP-0 owns instead of adding a fifth just for 5 lines.
20
+ //
21
+ // Error contract: every throw is an Error with `.code` set to a typed string
22
+ // (`git_failed`, `cas_lost`, `target_dirty`, ...), per §6.2's exit-code/typed-error contract.
23
+
24
+ const { spawnSync } = require('child_process');
25
+ const fs = require('fs');
26
+ const os = require('os');
27
+ const crypto = require('crypto');
28
+ const path = require('path');
29
+
30
+ const MAX_BUFFER = 64 * 1024 * 1024;
31
+
32
+ // §4 / bin/git-test-env.js: GIT_CONTEXT_ENV_KEYS. Copied (not required: lib/ must not depend on
33
+ // bin/, whose files are pinned by bin/test-broker-protocol.manifest.json, I-6); a unit test
34
+ // asserts the two arrays stay equal so drift is caught.
35
+ const SCRUBBED_ENV = Object.freeze([
36
+ 'GIT_DIR',
37
+ 'GIT_WORK_TREE',
38
+ 'GIT_INDEX_FILE',
39
+ 'GIT_PREFIX',
40
+ 'GIT_OBJECT_DIRECTORY',
41
+ 'GIT_COMMON_DIR',
42
+ 'GIT_ALTERNATE_OBJECT_DIRECTORIES',
43
+ ]);
44
+
45
+ const OID_RE = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/;
46
+
47
+ // Git's own refusal wording when a fast-forward would clobber local state (§1.6 V-4).
48
+ const OVERWRITE_HEADERS = [
49
+ 'error: Your local changes to the following files would be overwritten by merge:',
50
+ 'error: The following untracked working tree files would be overwritten by merge:',
51
+ ];
52
+
53
+ function landingError(code, message, extra) {
54
+ const err = new Error(message);
55
+ err.code = code;
56
+ if (extra) Object.assign(err, extra);
57
+ return err;
58
+ }
59
+
60
+ // process.env minus the §4 GIT_* list, plus `extra`. `extra` is applied AFTER the scrub, which
61
+ // is what lets snapshotCommit pass GIT_INDEX_FILE=<tmp> on purpose.
62
+ function childEnv(extra) {
63
+ const env = { ...process.env };
64
+ for (const key of SCRUBBED_ENV) delete env[key];
65
+ if (extra) Object.assign(env, extra);
66
+ return env;
67
+ }
68
+
69
+ // Raw spawn: never throws on a non-zero exit (the wrappers below need to inspect it).
70
+ function runGitRaw(cwd, args, opts = {}) {
71
+ const res = spawnSync('git', ['--no-optional-locks', ...args], {
72
+ cwd,
73
+ env: childEnv(opts.env),
74
+ encoding: 'utf8',
75
+ maxBuffer: opts.maxBuffer || MAX_BUFFER,
76
+ input: opts.input === undefined ? undefined : opts.input,
77
+ });
78
+ if (res.error) {
79
+ throw landingError('git_failed', `git ${args.join(' ')}: ${res.error.message}`, { args, cwd, cause: res.error });
80
+ }
81
+ return { status: res.status, stdout: res.stdout || '', stderr: res.stderr || '' };
82
+ }
83
+
84
+ function gitFailed(cwd, args, res) {
85
+ const detail = String(res.stderr || res.stdout || '').trim();
86
+ return landingError('git_failed', `git ${args.join(' ')} exited ${res.status}${detail ? `: ${detail}` : ''}`, {
87
+ args, cwd, status: res.status, stderr: res.stderr, stdout: res.stdout,
88
+ });
89
+ }
90
+
91
+ // Sync `git` that throws `git_failed` on any non-zero exit and returns stdout.
92
+ function git(cwd, args, opts = {}) {
93
+ const res = runGitRaw(cwd, args, opts);
94
+ if (res.status !== 0) throw gitFailed(cwd, args, res);
95
+ return res.stdout;
96
+ }
97
+
98
+ // --- read-only helpers ---------------------------------------------------------------------
99
+
100
+ function revParseOrNull(cwd, rev, opts = {}) {
101
+ const res = runGitRaw(cwd, ['rev-parse', '--verify', '--quiet', rev], opts);
102
+ if (res.status !== 0) return null;
103
+ const value = res.stdout.trim();
104
+ return value || null;
105
+ }
106
+
107
+ function revParse(cwd, rev, opts = {}) {
108
+ const value = revParseOrNull(cwd, rev, opts);
109
+ if (value === null) {
110
+ throw landingError('git_failed', `cannot resolve revision ${JSON.stringify(rev)} in ${cwd}`, { rev, cwd });
111
+ }
112
+ return value;
113
+ }
114
+
115
+ // Absolute path of the repository's common git dir (shared by every worktree of the repo).
116
+ // Memoized per resolved cwd: it cannot change while this process runs, and every state path
117
+ // (state.landingDir) and every radar cache read goes through it -- un-memoized, a plain
118
+ // `land status` on a 413-worktree repo spent 46 s of its 128 s spawning this one rev-parse.
119
+ // Only successes are kept, so a cwd that is not a repo yet still fails every time.
120
+ const COMMON_DIR_CACHE = new Map();
121
+ function commonDir(cwd) {
122
+ const key = path.resolve(cwd);
123
+ let value = COMMON_DIR_CACHE.get(key);
124
+ if (value === undefined) {
125
+ value = git(cwd, ['rev-parse', '--path-format=absolute', '--git-common-dir']).trim();
126
+ COMMON_DIR_CACHE.set(key, value);
127
+ }
128
+ return value;
129
+ }
130
+
131
+ // Absolute path of the git dir for THIS worktree (`.git/worktrees/<n>` for a linked one).
132
+ function gitDir(cwd) {
133
+ return git(cwd, ['rev-parse', '--path-format=absolute', '--git-dir']).trim();
134
+ }
135
+
136
+ function isAncestor(cwd, ancestor, descendant, opts = {}) {
137
+ const res = runGitRaw(cwd, ['merge-base', '--is-ancestor', ancestor, descendant], opts);
138
+ if (res.status === 0) return true;
139
+ if (res.status === 1) return false;
140
+ throw gitFailed(cwd, ['merge-base', '--is-ancestor', ancestor, descendant], res);
141
+ }
142
+
143
+ function isGitRepo(cwd) {
144
+ const res = runGitRaw(cwd, ['rev-parse', '--git-dir']);
145
+ return res.status === 0;
146
+ }
147
+
148
+ // Absolute, symlink-resolved path of the worktree root (the directory the queue's `.ctm.json`
149
+ // may live in — a worktree's own root, not the common dir). Outside a repository there is no
150
+ // toplevel, and that is a typed refusal rather than a silent fallback (§6.1).
151
+ function toplevel(cwd) {
152
+ const res = runGitRaw(cwd, ['rev-parse', '--show-toplevel']);
153
+ if (res.status !== 0) {
154
+ throw landingError('not_a_repo', `not inside a git repository: ${cwd}`, {
155
+ cwd, status: res.status, stderr: String(res.stderr || '').trim(),
156
+ });
157
+ }
158
+ const top = res.stdout.trim();
159
+ if (!top) {
160
+ throw landingError('not_a_repo', `git reported no toplevel for ${cwd}`, { cwd, status: res.status });
161
+ }
162
+ return fs.realpathSync(top);
163
+ }
164
+
165
+ // --- D-3: worktree-free train commit -------------------------------------------------------
166
+
167
+ // `git merge-tree --write-tree --name-only <base> <tip>` (observed §1.6 V-1/V-2).
168
+ // exit 0 -> { tree, conflicts: [] }
169
+ // exit 1 -> { tree, conflicts: [paths...] } (first line is the tree oid, then the conflicted
170
+ // paths up to the first blank line, then git's informational block)
171
+ // other -> throw git_failed
172
+ // A merge driver is free to print to stdout, so the tree oid is located by shape rather than by
173
+ // assuming it is line 1.
174
+ function mergeTree(cwd, base, tip, opts = {}) {
175
+ const args = ['merge-tree', '--write-tree', '--name-only', base, tip];
176
+ const res = runGitRaw(cwd, args, opts);
177
+ if (res.status !== 0 && res.status !== 1) throw gitFailed(cwd, args, res);
178
+ const lines = res.stdout.split('\n').map((line) => line.replace(/\r$/, ''));
179
+ let index = 0;
180
+ while (index < lines.length && !OID_RE.test(lines[index].trim())) index += 1;
181
+ if (index >= lines.length) {
182
+ throw landingError('git_failed', `git merge-tree produced no tree oid for ${base}..${tip}`, {
183
+ base, tip, cwd, status: res.status, stdout: res.stdout,
184
+ });
185
+ }
186
+ const tree = lines[index].trim();
187
+ const conflicts = [];
188
+ for (let i = index + 1; i < lines.length; i += 1) {
189
+ if (lines[i] === '') break;
190
+ conflicts.push(lines[i]);
191
+ }
192
+ return { tree, conflicts };
193
+ }
194
+
195
+ function commitTree(cwd, tree, parents = [], message = '', opts = {}) {
196
+ const args = ['commit-tree', tree];
197
+ for (const parent of parents) args.push('-p', parent);
198
+ args.push('-m', message);
199
+ return git(cwd, args, opts).trim();
200
+ }
201
+
202
+ // --- D-5: landing the train ----------------------------------------------------------------
203
+
204
+ // `git update-ref <ref> <new> <old>` is a compare-and-swap (observed §1.6 V-3). On failure the
205
+ // ref is re-read: only a genuinely different current value is `cas_lost`; anything else is a
206
+ // real git failure.
207
+ function casUpdateRef(cwd, ref, newSha, oldSha, opts = {}) {
208
+ const args = ['update-ref', ref, newSha, oldSha];
209
+ const res = runGitRaw(cwd, args, opts);
210
+ if (res.status === 0) return true;
211
+ const current = revParseOrNull(cwd, ref);
212
+ if (current !== oldSha) {
213
+ throw landingError('cas_lost', `ref ${ref} is at ${current || '(absent)'}, expected ${oldSha}`, {
214
+ ref, expected: oldSha, current, cwd, stderr: res.stderr,
215
+ });
216
+ }
217
+ throw gitFailed(cwd, args, res);
218
+ }
219
+
220
+ // Paths git lists under an "…would be overwritten" header (tab-indented, one per line).
221
+ function parseOverwritePaths(stderr) {
222
+ const lines = String(stderr || '').split('\n');
223
+ const paths = [];
224
+ let collecting = false;
225
+ for (const line of lines) {
226
+ if (!collecting) {
227
+ if (OVERWRITE_HEADERS.includes(line.trim())) collecting = true;
228
+ continue;
229
+ }
230
+ if (!line.startsWith('\t')) break;
231
+ let rel = line.slice(1).trim();
232
+ if (rel.startsWith('"') && rel.endsWith('"') && rel.length > 1) rel = rel.slice(1, -1);
233
+ if (rel) paths.push(rel);
234
+ }
235
+ return paths;
236
+ }
237
+
238
+ // `git -C <worktree> merge --ff-only <sha>` (observed §1.6 V-4): unrelated dirt is tolerated,
239
+ // an overlapping dirty file refuses WITHOUT modifying the worktree -> `target_dirty` + .paths.
240
+ function ffCheckedOut(worktree, sha, opts = {}) {
241
+ const args = ['merge', '--ff-only', sha];
242
+ const res = runGitRaw(worktree, args, opts);
243
+ if (res.status === 0) return { sha: revParseOrNull(worktree, 'HEAD'), stdout: res.stdout };
244
+ const paths = parseOverwritePaths(res.stderr);
245
+ if (paths.length) {
246
+ throw landingError('target_dirty', `fast-forward into ${worktree} would overwrite local changes: ${paths.join(', ')}`, {
247
+ paths, sha, worktree, stderr: res.stderr,
248
+ });
249
+ }
250
+ throw gitFailed(worktree, args, res);
251
+ }
252
+
253
+ // WP-2 (D-7): the radar snapshots a dirty worktree on EVERY sweep, so the snapshot commit must be a
254
+ // pure function of (HEAD, content) — otherwise the pair cache key (`<snapshotSha>-<otherSha>.json`)
255
+ // would change on every run and never hit. With `deterministic: true` the identity and both dates are
256
+ // fixed (the date is HEAD's own committer date), so the commit object bytes — and therefore the sha —
257
+ // depend only on the tree, the parent and this message.
258
+ const SNAPSHOT_MESSAGE = 'land radar snapshot';
259
+ const SNAPSHOT_IDENTITY = Object.freeze({ name: 'ctm-landing-radar', email: 'radar@example.invalid' });
260
+
261
+ // Fixed author/committer identity + HEAD's committer date, as GIT_* env for commit-tree (which
262
+ // otherwise requires a configured identity it must not depend on).
263
+ function snapshotIdentityEnv(worktree, head) {
264
+ const env = {
265
+ GIT_AUTHOR_NAME: SNAPSHOT_IDENTITY.name,
266
+ GIT_AUTHOR_EMAIL: SNAPSHOT_IDENTITY.email,
267
+ GIT_COMMITTER_NAME: SNAPSHOT_IDENTITY.name,
268
+ GIT_COMMITTER_EMAIL: SNAPSHOT_IDENTITY.email,
269
+ };
270
+ const res = runGitRaw(worktree, ['show', '-s', '--format=%cI', head]);
271
+ const date = res.status === 0 ? res.stdout.trim() : '';
272
+ // No date means the commit could not be read, which cannot happen for a resolved HEAD; leaving the
273
+ // dates unset then makes the commit non-deterministic rather than silently wrong-dated.
274
+ if (date) {
275
+ env.GIT_AUTHOR_DATE = date;
276
+ env.GIT_COMMITTER_DATE = date;
277
+ }
278
+ return env;
279
+ }
280
+
281
+ // Snapshot commit of a worktree's CURRENT state (tracked edits + untracked-not-ignored files),
282
+ // built through a temp index so the real index is never touched and no `index.lock` appears in
283
+ // the worktree (observed §1.6 V-5: 0.186 s on a 10k-file repo).
284
+ //
285
+ // `opts.deterministic` (WP-2) additionally pins the identity, the dates and the message, so the same
286
+ // HEAD and the same content always produce the same sha. Absent, behaviour is unchanged.
287
+ function snapshotCommit(worktree, opts = {}) {
288
+ const head = revParseOrNull(worktree, 'HEAD');
289
+ if (!head) {
290
+ throw landingError('git_failed', `snapshotCommit: ${worktree} has no HEAD commit`, { cwd: worktree });
291
+ }
292
+ const tmpIndex = path.join(os.tmpdir(), `ctm-landing-index-${process.pid}-${crypto.randomBytes(6).toString('hex')}`);
293
+ const env = { GIT_INDEX_FILE: tmpIndex };
294
+ if (opts.deterministic) Object.assign(env, snapshotIdentityEnv(worktree, head));
295
+ try {
296
+ const realIndex = git(worktree, ['rev-parse', '--path-format=absolute', '--git-path', 'index']).trim();
297
+ let seeded = false;
298
+ try {
299
+ fs.copyFileSync(realIndex, tmpIndex);
300
+ seeded = true;
301
+ } catch (error) {
302
+ /* expected: no index file yet (fresh/odd worktree) -> seed the temp index from HEAD instead */
303
+ }
304
+ if (!seeded) git(worktree, ['read-tree', head], { env });
305
+ git(worktree, ['add', '-A'], { env });
306
+ const tree = git(worktree, ['write-tree'], { env }).trim();
307
+ const message = opts.message || (opts.deterministic ? SNAPSHOT_MESSAGE : `snapshot ${new Date().toISOString()}`);
308
+ return commitTree(worktree, tree, [head], message, { env });
309
+ } finally {
310
+ for (const file of [tmpIndex, `${tmpIndex}.lock`]) {
311
+ try {
312
+ fs.rmSync(file, { force: true });
313
+ } catch (error) {
314
+ /* expected: temp index cleanup is best-effort; a leftover file under os.tmpdir() never
315
+ affects the repo (the real index and its lock are untouched by construction) */
316
+ }
317
+ }
318
+ }
319
+ }
320
+
321
+ module.exports = {
322
+ SCRUBBED_ENV,
323
+ MAX_BUFFER,
324
+ SNAPSHOT_MESSAGE,
325
+ SNAPSHOT_IDENTITY,
326
+ landingError,
327
+ childEnv,
328
+ runGitRaw,
329
+ git,
330
+ gitFailed,
331
+ revParse,
332
+ revParseOrNull,
333
+ commonDir,
334
+ gitDir,
335
+ isAncestor,
336
+ isGitRepo,
337
+ toplevel,
338
+ mergeTree,
339
+ commitTree,
340
+ casUpdateRef,
341
+ ffCheckedOut,
342
+ parseOverwritePaths,
343
+ snapshotCommit,
344
+ };
@@ -0,0 +1,247 @@
1
+ 'use strict';
2
+ // WP-0 (docs/local-landing-queue-multi-session-integration-2026-10-26.html §5 D-2, §4/§9.1, §8
3
+ // WP-0): the single per-repository runner lease — one writer, no daemon.
4
+ //
5
+ // Ported (copied with attribution, never imported — bin/ files are pinned by
6
+ // bin/test-broker-protocol.manifest.json, I-6) from bin/test-slot-broker.js:
7
+ // _claimDir (:600-620) mkdtemp stage + renameSync into place
8
+ // _verifiedRemove (:388-440) re-read the stamp, require record equality, rename to a private
9
+ // tombstone, then remove the tombstone (never rm -rf a live claim)
10
+ // _reapIfDead (:530-547) kill(pid, 0) -> ESRCH, plus a lease-age + registry check; an
11
+ // unreadable stamp is never reaped
12
+ //
13
+ // Layout: `landing/runner/lease/owner.json` (the DIRECTORY is what gets renamed). Use
14
+ // leaseDir()/leasePath() — nothing else should hardcode these paths.
15
+
16
+ const fs = require('fs');
17
+ const os = require('os');
18
+ const path = require('path');
19
+ const crypto = require('crypto');
20
+ const { spawnSync } = require('child_process');
21
+ const { landingError } = require('./git');
22
+ const { readJsonSafe, writeJsonAtomic } = require('./state');
23
+
24
+ // D-2: heartbeat freshness window. A constant on purpose — the queue has no flags (D-1).
25
+ const LAND_HEARTBEAT_MS = 10000;
26
+ // D-2 pid-reuse clause: a stale lease whose pid is alive is only dead when that pid is not a
27
+ // live `land` process. Matches `land`, `land.js`, `node .../bin/land.js` — and nothing else.
28
+ const LAND_PROCESS_RE = /\bland(?:\.js)?\b/;
29
+
30
+ const RUNNER_DIRNAME = 'runner';
31
+ const LEASE_DIRNAME = 'lease';
32
+ const OWNER_FILENAME = 'owner.json';
33
+
34
+ function runnerDir(root) {
35
+ return path.join(root, RUNNER_DIRNAME);
36
+ }
37
+
38
+ function leaseDir(root) {
39
+ return path.join(runnerDir(root), LEASE_DIRNAME);
40
+ }
41
+
42
+ function leasePath(root) {
43
+ return path.join(leaseDir(root), OWNER_FILENAME);
44
+ }
45
+
46
+ function isLeaseRecord(record) {
47
+ return Boolean(record) && typeof record === 'object' && typeof record.pid === 'number' && typeof record.token === 'string';
48
+ }
49
+
50
+ // D-10: the runner advertises what it can do. A runner spawned from an older `land.js` has no
51
+ // `caps`, which is how `land cancel --force` (and, in WP-3, `urgent`) tells "this runner will
52
+ // honour a stop request" from "this runner will overwrite the state I am about to write".
53
+ function hasCap(record, cap) {
54
+ return Boolean(record) && Array.isArray(record.caps) && record.caps.includes(cap);
55
+ }
56
+
57
+ function isProcessAlive(pid, kill) {
58
+ try {
59
+ kill(pid, 0);
60
+ return true;
61
+ } catch (error) {
62
+ // ESRCH = gone; EPERM = exists but not ours, i.e. alive.
63
+ return !(error && error.code === 'ESRCH');
64
+ }
65
+ }
66
+
67
+ // `opts.now` is a number or a function, so a caller can freeze or advance the clock.
68
+ function nowValue(opts = {}) {
69
+ const now = opts.now;
70
+ if (typeof now === 'function') return now();
71
+ if (typeof now === 'number') return now;
72
+ return Date.now();
73
+ }
74
+
75
+ function isStale(record, opts = {}) {
76
+ if (!isLeaseRecord(record)) return false;
77
+ const heartbeat = Date.parse(record.heartbeatAt);
78
+ if (!Number.isFinite(heartbeat)) return false;
79
+ return nowValue(opts) - heartbeat > 3 * LAND_HEARTBEAT_MS;
80
+ }
81
+
82
+ // D-2 pid-reuse probe: the command line of a live pid, or null when it cannot be read (the
83
+ // process exited between the liveness check and this call, ps is missing, ...). null is
84
+ // "unknown", which is never treated as dead.
85
+ function processCommand(pid) {
86
+ const res = spawnSync('ps', ['-o', 'command=', '-p', String(pid)], { encoding: 'utf8' });
87
+ if (!res || res.error || res.status !== 0) return null;
88
+ const command = String(res.stdout || '').trim();
89
+ return command === '' ? null : command;
90
+ }
91
+
92
+ // D-2 liveness. Dead iff `kill(pid, 0)` -> ESRCH, OR the heartbeat is older than
93
+ // 3 x LAND_HEARTBEAT_MS AND the pid is not a live `land` process (a pid the OS reused after the
94
+ // runner died, which is exactly what the ESRCH test alone cannot see). A live `land` process is
95
+ // never dead, however old its heartbeat. A missing/unreadable stamp is never dead: we cannot
96
+ // tell a dead owner from one we merely failed to read.
97
+ function isDead(record, opts = {}) {
98
+ if (!isLeaseRecord(record)) return false;
99
+ const kill = opts.kill || process.kill;
100
+ if (!isProcessAlive(record.pid, kill)) return true;
101
+ if (!isStale(record, opts)) return false;
102
+ const probe = opts.processCommand || processCommand;
103
+ const command = probe(record.pid);
104
+ if (typeof command !== 'string') return false;
105
+ return !LAND_PROCESS_RE.test(command);
106
+ }
107
+
108
+ function read(root) {
109
+ const record = readJsonSafe(leasePath(root));
110
+ return isLeaseRecord(record) ? record : null;
111
+ }
112
+
113
+ // Verified removal: re-read IMMEDIATELY before removing and require the stamp to still be the one
114
+ // the decision was made on. The removal itself renames the whole directory to a private
115
+ // tombstone FIRST (a single syscall, so the path is never briefly empty — rename onto an EMPTY
116
+ // directory would otherwise succeed and a concurrent claimant could slip in) and only then
117
+ // removes the tombstone.
118
+ function verifiedRemove(dir, expected) {
119
+ const current = readJsonSafe(path.join(dir, OWNER_FILENAME));
120
+ if (!isLeaseRecord(current) || !expected || current.token !== expected.token) return false;
121
+ const tombstone = `${dir}.dead-${process.pid}-${crypto.randomBytes(4).toString('hex')}`;
122
+ try {
123
+ fs.renameSync(dir, tombstone);
124
+ } catch (error) {
125
+ /* expected: someone else already reaped or reclaimed this lease; there is nothing of ours to
126
+ remove, so report "not removed" rather than deleting a live claim */
127
+ return false;
128
+ }
129
+ try {
130
+ fs.rmSync(tombstone, { recursive: true, force: true });
131
+ } catch (error) {
132
+ /* expected: the tombstone is private to this call, so a failed cleanup leaves only a stray
133
+ .dead-* directory; the live lease path is already free */
134
+ }
135
+ return true;
136
+ }
137
+
138
+ function reapIfDead(root, opts = {}) {
139
+ const record = read(root);
140
+ if (!record) return false;
141
+ if (!isDead(record, opts)) return false;
142
+ return verifiedRemove(leaseDir(root), record);
143
+ }
144
+
145
+ // Atomic claim (broker `_claimDir`): stage owner.json inside a private mkdtemp'd directory, then
146
+ // renameSync it into place. rename onto a non-empty directory fails atomically, so the lease path
147
+ // is either fully-formed-and-stamped or it was never claimed by this call.
148
+ // Returns the record on success, null when a live holder owns it.
149
+ function claim(root, opts = {}) {
150
+ const host = opts.host || os.hostname();
151
+ const jobId = opts.jobId === undefined ? null : opts.jobId;
152
+ const dir = leaseDir(root);
153
+ const staging = runnerDir(root);
154
+ fs.mkdirSync(staging, { recursive: true });
155
+ // Bounded retry: a reap that really removed a dead holder frees the path for THIS call, so the
156
+ // next submitter becomes the runner without an outer wait loop (D-2: "a dead runner's job
157
+ // returns to the head of the queue"). A live holder still yields null after the first attempt.
158
+ for (let attempt = 0; attempt < 3; attempt += 1) {
159
+ const token = crypto.randomBytes(16).toString('hex');
160
+ const stamp = new Date(nowValue(opts)).toISOString();
161
+ // D-10: `caps` rides along with the record. `heartbeat` spreads `{...current}`, so it survives
162
+ // every refresh without being re-stated there.
163
+ const record = { contract: 1, pid: process.pid, startedAt: stamp, heartbeatAt: stamp, token, jobId, host, caps: ['stop'] };
164
+ let tmp;
165
+ try {
166
+ tmp = fs.mkdtempSync(path.join(staging, '.claim-'));
167
+ } catch (error) {
168
+ throw landingError('lease_stage_failed', `cannot stage a claim under ${staging}: ${error.message}`, { root, cause: error });
169
+ }
170
+ try {
171
+ fs.writeFileSync(path.join(tmp, OWNER_FILENAME), `${JSON.stringify(record)}\n`);
172
+ } catch (error) {
173
+ rmrf(tmp);
174
+ throw landingError('lease_stage_failed', `cannot write the staged owner stamp in ${tmp}: ${error.message}`, { root, cause: error });
175
+ }
176
+ try {
177
+ fs.renameSync(tmp, dir);
178
+ return record;
179
+ } catch (error) {
180
+ rmrf(tmp);
181
+ // Lost the race (or the path is a non-empty directory). Reap only a genuinely dead holder.
182
+ if (!reapIfDead(root, opts)) return null;
183
+ }
184
+ }
185
+ return null;
186
+ }
187
+
188
+ function rmrf(dir) {
189
+ try {
190
+ fs.rmSync(dir, { recursive: true, force: true });
191
+ } catch (error) {
192
+ /* expected: best-effort cleanup of our own staging directory under landing/runner/ */
193
+ }
194
+ }
195
+
196
+ // Refresh heartbeatAt (and jobId, when given) only while the on-disk token is still ours.
197
+ function heartbeat(root, token, opts = {}) {
198
+ const file = leasePath(root);
199
+ const current = readJsonSafe(file);
200
+ if (!isLeaseRecord(current) || current.token !== token) {
201
+ throw landingError('lease_lost', `lease token ${token} is no longer held in ${root}`, { root, token });
202
+ }
203
+ const next = { ...current, heartbeatAt: new Date(nowValue(opts)).toISOString() };
204
+ if (opts.jobId !== undefined) next.jobId = opts.jobId;
205
+ // §9 S-6/D-6 (WP-1): the train in flight, so `land cancel` can tell a batch member from a singleton
206
+ // without reading a second file. Additive (R-3) — an older reader ignores it, and a lease written by
207
+ // an older runner has no `jobIds` at all, which `stop.isBatchMemberInFlight` reads as "not a member".
208
+ if (opts.jobIds !== undefined) next.jobIds = opts.jobIds;
209
+ writeJsonAtomic(file, next);
210
+ // The write is a rename into the same path; re-read to make sure we did not overwrite a holder
211
+ // that replaced the lease in between (the same "verified" discipline as removal).
212
+ const after = readJsonSafe(file);
213
+ if (!isLeaseRecord(after) || after.token !== token) {
214
+ throw landingError('lease_lost', `lease token ${token} was replaced while heartbeating ${root}`, { root, token });
215
+ }
216
+ return after;
217
+ }
218
+
219
+ // Verified release: true only when this call removed OUR lease.
220
+ function release(root, token) {
221
+ const current = read(root);
222
+ if (!current || current.token !== token) return false;
223
+ return verifiedRemove(leaseDir(root), current);
224
+ }
225
+
226
+ module.exports = {
227
+ LAND_HEARTBEAT_MS,
228
+ LAND_PROCESS_RE,
229
+ RUNNER_DIRNAME,
230
+ LEASE_DIRNAME,
231
+ OWNER_FILENAME,
232
+ runnerDir,
233
+ leaseDir,
234
+ leasePath,
235
+ hasCap,
236
+ claim,
237
+ heartbeat,
238
+ release,
239
+ read,
240
+ isDead,
241
+ isStale,
242
+ isProcessAlive,
243
+ processCommand,
244
+ nowValue,
245
+ reapIfDead,
246
+ verifiedRemove,
247
+ };