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,742 @@
1
+ 'use strict';
2
+
3
+ // WP-2 (docs/cross-machine-session-context-2026-10-31.html §5 R-3/R-4/R-5/R-8/R-11, §6.1, §9 S-14):
4
+ // owner resolution + forwarding for session-memory reads.
5
+ //
6
+ // The defect this module fixes: `GET /api/ctm/session-memory/context` answered from THIS machine
7
+ // only, so a session that lives on another pool member came back as a well-formed EMPTY envelope
8
+ // with no hint that it exists elsewhere (observed M-5/I-5). The hub resolution order (R-3) is:
9
+ // (a) the directory (`getMirrorSessionMachine`) names an owner -> forward ONE GET to that owner;
10
+ // (b) else read locally -> hit: label it and return;
11
+ // (c) else probe every dialable member IN PARALLEL under one deadline; first non-empty answer wins;
12
+ // (d) else found:false + `searched`, naming self and every member with a status.
13
+ //
14
+ // Every forwarded request carries `scope=local` (R-4/R-11): a receiver never forwards again. That is
15
+ // the no-recursion invariant, and this module is its single owner (§7).
16
+ //
17
+ // No new ports and no new trust: the forward leg is the existing peer-bearer HTTP leg
18
+ // (lib/remote-machine-proxy.js `forwardMachineHttp`), and the receiving route admits it with
19
+ // `{ peerControl: true }` (lib/auth-rules.js, §6.2). All external calls go through the injectable
20
+ // `deps` object below; production defaults are loaded lazily via require, so requiring this module
21
+ // from a unit test never wires the machine registry, the proxy or the identity store.
22
+
23
+ const os = require('node:os');
24
+
25
+ const { boolFlag, createEnvFlags } = require('./env-flags');
26
+ // RRF (D-6/P-5): the ONE reciprocal-rank damping constant the repo already shares with the
27
+ // in-process fusion (lib/session-search-fusion.js:6-21) — imported, never re-typed.
28
+ const { DEFAULT_RRF_K } = require('./session-search-fusion');
29
+ // S-6: the one scatter-gather (a leaf module, shared with lib/fleet-session-lists.js).
30
+ const { fanOutMembers } = require('./fleet-fanout');
31
+
32
+ const SESSION_OWNER_FORWARD_TIMEOUT_MS = 10_000; // = remote-machine-proxy DEFAULT_TIMEOUT_MS (D-11)
33
+ const SESSION_PROBE_DEADLINE_MS = 5_000; // D-11
34
+ const POOL_SEARCH_MEMBER_DEADLINE_MS = 5_000; // D-11
35
+
36
+ const SESSION_CONTEXT_PATHNAME = '/api/ctm/session-memory/context';
37
+ const POOL_SEARCH_PATHNAME = '/api/ctm/session-memory/search';
38
+ const POOL_SEARCH_DEFAULT_LIMIT = 10;
39
+ const POOL_FLAG = 'CTM_SESSION_CONTEXT_POOL';
40
+
41
+ // A machine display name is never fabricated: the registry's display name first, then the name the
42
+ // directory reported, then the instance id (which is at least honest).
43
+ function machineLabel(machine) {
44
+ const label = machine && (machine.displayName || machine.machineName);
45
+ return String(label || (machine && machine.instanceId) || '');
46
+ }
47
+
48
+ /**
49
+ * R-11: the pool flag, default ON in source (`!== '0'`), read through the one truthiness parser
50
+ * (lib/env-flags.js — flag-lint forbids raw `process.env.CTM_*` reads outside it). An injected env
51
+ * object (tests) is parsed by the same core, never by a second hand-rolled parser.
52
+ */
53
+ function poolEnabled(env = process.env) {
54
+ if (!env || env === process.env) return boolFlag(POOL_FLAG, true);
55
+ return createEnvFlags({ env, warn: () => {} }).boolFlag(POOL_FLAG, true);
56
+ }
57
+
58
+ /**
59
+ * Who owns `sessionId`, as seen from THIS CTM (P-1: identity -> location, then route).
60
+ *
61
+ * @returns {{owner: 'self'} | {owner: 'machine', machine: object} | {owner: 'unknown'}}
62
+ * deps: { isLocalOwner?(id): boolean, getMirrorSessionMachine?(id), findMachine?(instanceId) }
63
+ */
64
+ function locateSessionOwner(sessionId, deps = {}) {
65
+ const id = String(sessionId == null ? '' : sessionId).trim();
66
+ if (!id) return { owner: 'unknown' };
67
+ if (typeof deps.isLocalOwner === 'function' && deps.isLocalOwner(id)) return { owner: 'self' };
68
+ const getMirror = deps.getMirrorSessionMachine;
69
+ const mirror = typeof getMirror === 'function' ? getMirror(id) : null;
70
+ if (!mirror || !mirror.machineInstanceId) return { owner: 'unknown' };
71
+ const find = typeof deps.findMachine === 'function' ? deps.findMachine(mirror.machineInstanceId) : null;
72
+ // No registry row: still name the owner the directory reported (its forward will fail as
73
+ // `machine_unreachable` — a named failure, never a silent empty answer).
74
+ return {
75
+ owner: 'machine',
76
+ machine: find || {
77
+ instanceId: String(mirror.machineInstanceId),
78
+ displayName: String(mirror.machineName || ''),
79
+ health: String(mirror.health || 'unknown'),
80
+ },
81
+ };
82
+ }
83
+
84
+ /**
85
+ * R-3 hub order. `args` is the same object the context route builds today
86
+ * (lib/ctm-session-context-api.js), plus `scope`.
87
+ *
88
+ * Returns the local/owner context object with `location` (R-5), or
89
+ * `{ ok: true, found: false, sessions: [], messages: [], searched: [{ machine_id, machine_name, status }] }`,
90
+ * or `null` when the local read was deferred (the caller renders today's deferred envelope).
91
+ *
92
+ * deps: { readLocal(args) -> Promise<context|null>, forward(request) -> forwardMachineHttp result,
93
+ * getMirrorSessionMachine(id), machineRows(), findMachine(id), localIdentity(), isLocalOwner(id),
94
+ * now(), setTimeoutFn, clearTimeoutFn, probeDeadlineMs }
95
+ */
96
+ async function readSessionContextAnywhere({ args = {}, excludeMachineId = '', env = process.env } = {}, deps = {}) {
97
+ const d = resolveDeps(deps);
98
+ const exclude = String(excludeMachineId || '').trim();
99
+ const ids = sessionIdsOf(args);
100
+
101
+ // Multi-id (or id-less) requests keep today's LOCAL-ONLY behaviour: WP-2 resolves ONE session id
102
+ // (the single-session read is the case the design measures). The answer is still labelled, so a
103
+ // caller can always see which machine answered.
104
+ if (ids.length !== 1) {
105
+ const local = await d.readLocal(args);
106
+ if (local == null) return null; // deferred off-thread read — today's deferred envelope
107
+ return { ...local, location: selfLocation(d, 'local') };
108
+ }
109
+
110
+ const sessionId = ids[0];
111
+ const pool = poolEnabled(env);
112
+ const scopeLocal = String(args.scope || '').trim() === 'local';
113
+ // R-3(a): the directory names an owner that is not the caller -> forward ONE GET to it.
114
+ if (pool && !scopeLocal) {
115
+ const located = locateSessionOwner(sessionId, {
116
+ isLocalOwner: d.isLocalOwner,
117
+ getMirrorSessionMachine: d.getMirrorSessionMachine,
118
+ findMachine: d.findMachine,
119
+ });
120
+ if (located.owner === 'machine' && located.machine.instanceId !== exclude) {
121
+ return forwardToMachine(located.machine, args, d, 'owner');
122
+ }
123
+ }
124
+
125
+ // R-3(b): read locally.
126
+ const local = await d.readLocal(args);
127
+ if (local == null) return null;
128
+ if (hasSessions(local)) {
129
+ // R-6 (WP-5, S-14): a local hit whose items arrived through transcript replication — the reader
130
+ // stamps `replica_origin` when the session's ONLY ingest cursors are `peer-sync:<origin>:<session>`
131
+ // rows — is not an owner hit. This machine holds a COPY, so the owner is asked instead and the
132
+ // copy is served only when the owner cannot be reached, labelled as the copy it is.
133
+ const origin = replicaOriginOf(local);
134
+ if (!origin) return { ...local, location: selfLocation(d, 'local') };
135
+ // `scope=local` is a caller saying "this machine's copy": served as-is, still labelled a replica.
136
+ if (!pool || scopeLocal) return replicaAnswer(local, d, origin, []);
137
+ const originRow = d.findMachine(origin);
138
+ const owner = replicaOwnerMachine(sessionId, origin, exclude, d, originRow);
139
+ if (!owner) return replicaAnswer(local, d, origin, [ownerEntry(origin, originRow, 'skipped')]);
140
+ const attempt = await attemptForward(owner, args, d);
141
+ if (attempt.error) return replicaAnswer(local, d, origin, [ownerEntry(origin, owner, attempt.status)]);
142
+ return withLocation(attempt.body, owner, 'owner');
143
+ }
144
+
145
+ // R-3(c)/(d). `scope=local` and a disabled pool both stop here: no forward, no probe.
146
+ if (!pool || scopeLocal) return missEnvelope([selfEntry(d, 'miss')]);
147
+ const members = otherMembers(d, exclude);
148
+ const probe = await probeMembers(members.filter(isDialable), args, d);
149
+ if (probe.hit) return probe.hit;
150
+ return missEnvelope(searchedEntries(d, members, probe.statuses));
151
+ }
152
+
153
+ /**
154
+ * R-7 pool search + merge. The LOCAL search always runs (it is the only leg that is always
155
+ * authoritative); every DIALABLE member is asked ONCE, in parallel, with `scope=local` (R-4: the
156
+ * receiver never fans out again), all under ONE deadline — a straggler is reported `timeout`, never
157
+ * waited for (P-2/P-3). Partial results are the contract: a member that fails is a per-machine
158
+ * status, and the merged answer still comes back.
159
+ *
160
+ * Returns `{ ok: true, results, machines }` (machines with self FIRST and every member named with its
161
+ * status), or `null` when the local read was deferred (the caller renders today's deferred envelope).
162
+ * Deliberately carries no wall clock (`ms`) in the result: nothing downstream may branch on latency.
163
+ *
164
+ * deps: { searchLocal(args) -> Promise<{ results }|null>, forward(request), machineRows(), findMachine,
165
+ * localIdentity(), setTimeoutFn, clearTimeoutFn, searchDeadlineMs }
166
+ */
167
+ async function searchSessionsAnywhere({
168
+ query = '', limit, excludeMachineId = '', scope = '', env = process.env,
169
+ } = {}, deps = {}) {
170
+ const d = resolveDeps(deps);
171
+ const cap = clampSearchLimit(limit);
172
+ const local = await d.searchLocal({ query, limit: cap });
173
+ if (local == null) return null; // deferred off-thread search — today's deferred envelope
174
+ const localRows = Array.isArray(local.results) ? local.results : [];
175
+ const self = d.localIdentity();
176
+ const selfEntry = {
177
+ machine_id: String(self.instanceId || ''),
178
+ machine_name: String(self.displayName || ''),
179
+ status: 'ok',
180
+ count: localRows.length,
181
+ };
182
+ const selfList = {
183
+ machine_id: selfEntry.machine_id,
184
+ machine_name: selfEntry.machine_name,
185
+ owner: true,
186
+ rows: localRows,
187
+ };
188
+ const pool = poolEnabled(env);
189
+ const scopeLocal = String(scope || '').trim() === 'local';
190
+ const members = otherMembers(d, excludeMachineId);
191
+ // R-11/`scope=local`: no fan-out. The answer is still labelled, so self is always the first machine.
192
+ if (!pool || scopeLocal) {
193
+ return withLocalEnvelope(local, fusePoolResults([selfList], { limit: cap }), [selfEntry]);
194
+ }
195
+ const fanout = await searchMembers(members.filter(isDialable), { query, limit: cap }, d);
196
+ const machines = [selfEntry];
197
+ for (const row of members) {
198
+ machines.push({
199
+ machine_id: String(row.instanceId || ''),
200
+ machine_name: machineLabel(row),
201
+ // Never dialled (disabled/unreachable) is `skipped`, exactly like the context probe's `searched`.
202
+ status: isDialable(row) ? (fanout.statuses.get(row.instanceId) || 'timeout') : 'skipped',
203
+ count: fanout.counts.get(row.instanceId) || 0,
204
+ });
205
+ }
206
+ return withLocalEnvelope(local, fusePoolResults([selfList, ...fanout.lists], { limit: cap }), machines);
207
+ }
208
+
209
+ // §5 "what does not change": the local search envelope (op, query, db_path, active_source, …) is
210
+ // kept as is; only `results` (now fused) and `count` change, and `machines` is added.
211
+ function withLocalEnvelope(local, results, machines) {
212
+ const envelope = { ...local, ok: local.ok !== false, results, machines };
213
+ if (Object.prototype.hasOwnProperty.call(local, 'count')) envelope.count = results.length;
214
+ return envelope;
215
+ }
216
+
217
+ /**
218
+ * R-7/D-6 rank fusion over per-source rank order. Each list is `{ machine_id, machine_name, owner,
219
+ * rows }` with `rows` ALREADY in that machine's rank order; `score(row) = Σ 1/(k + rank)` (rank
220
+ * 1-based), so a row several machines found scores higher than the same row found once.
221
+ *
222
+ * Dedup key is `${session_id}\u0000${content_hash}` (§6.1): the OWNER's row wins the copy (its text
223
+ * is the one the session's own machine wrote), otherwise the higher-scoring one; scores always add.
224
+ * R-7 (WP-5): a row the reader marked `replica_origin` is a COPY, so it never displaces a machine
225
+ * that holds the item itself, and a surviving replica row carries `stale:true`.
226
+ * Output is sorted by score desc — ties broken deterministically (machine_id, session_id,
227
+ * message_index, content_hash) so a list reorder can never reorder the answer — sliced to `limit`,
228
+ * and every row carries a `machine_id`/`machine_name` (its own attribution when it already has one —
229
+ * a hub's answer already names the machine each row came from — else the list's) plus its 0-based
230
+ * output position as `rank` (order-preserving downstream consumers keep the fusion order).
231
+ * Pure and deterministic: no clock, no I/O, no mutation of the input rows.
232
+ */
233
+ function fusePoolResults(lists, { limit, k = DEFAULT_RRF_K } = {}) {
234
+ const damping = Number.isFinite(Number(k)) && Number(k) >= 0 ? Number(k) : DEFAULT_RRF_K;
235
+ const merged = new Map();
236
+ for (const list of Array.isArray(lists) ? lists : []) {
237
+ if (!list || typeof list !== 'object') continue;
238
+ const machineId = String(list.machine_id == null ? '' : list.machine_id);
239
+ const machineName = String(list.machine_name == null ? '' : list.machine_name);
240
+ const owner = list.owner === true;
241
+ const rows = Array.isArray(list.rows) ? list.rows : [];
242
+ for (let index = 0; index < rows.length; index += 1) {
243
+ const row = rows[index];
244
+ if (!row || typeof row !== 'object' || Array.isArray(row)) continue;
245
+ const contribution = 1 / (damping + (index + 1));
246
+ const replica = isReplicaRow(row);
247
+ const placed = {
248
+ ...row,
249
+ // The row's OWN attribution wins when it has one (a hub's answer already names the machine
250
+ // each row came from); the list's machine is the label for a row that carries none.
251
+ machine_id: rowMachineId(row, machineId),
252
+ machine_name: rowMachineName(row, machineName),
253
+ ...(replica ? { stale: true } : {}),
254
+ };
255
+ const key = fuseKey(row);
256
+ const existing = merged.get(key);
257
+ if (!existing) {
258
+ merged.set(key, {
259
+ row: placed, score: contribution, contribution, owner: owner && !replica, machineId, replica,
260
+ });
261
+ continue;
262
+ }
263
+ existing.score += contribution; // a row several lists carried adds every contribution
264
+ // R-7 (WP-5): a replica copy of an item never beats a machine that holds the item itself —
265
+ // the owner's row wins the duplicate whichever order the two arrived in.
266
+ if (existing.replica !== replica) {
267
+ if (existing.replica) {
268
+ existing.row = placed;
269
+ existing.owner = owner;
270
+ existing.contribution = contribution;
271
+ existing.machineId = machineId;
272
+ existing.replica = false;
273
+ }
274
+ continue;
275
+ }
276
+ if (owner && !replica && !existing.owner) {
277
+ existing.row = placed;
278
+ existing.owner = true;
279
+ existing.contribution = contribution;
280
+ existing.machineId = machineId;
281
+ } else if (!owner && !existing.owner
282
+ && (contribution > existing.contribution
283
+ || (contribution === existing.contribution && machineId < existing.machineId))) {
284
+ existing.row = placed;
285
+ existing.contribution = contribution;
286
+ existing.machineId = machineId;
287
+ }
288
+ }
289
+ }
290
+ const entries = [...merged.values()];
291
+ entries.sort((a, b) => (b.score - a.score)
292
+ || cmpText(a.row.machine_id, b.row.machine_id)
293
+ || cmpText(a.row.session_id, b.row.session_id)
294
+ || (toSortNumber(a.row.message_index) - toSortNumber(b.row.message_index))
295
+ || cmpText(a.row.content_hash, b.row.content_hash));
296
+ const n = Number(limit);
297
+ const capped = Number.isFinite(n) && n >= 0 ? Math.trunc(n) : entries.length;
298
+ return entries.slice(0, capped).map((entry, index) => ({ ...entry.row, rank: index }));
299
+ }
300
+
301
+ /** A row that already names its machine keeps that name; otherwise it inherits the list's. Pure. */
302
+ function rowMachineId(row, listMachineId) {
303
+ const own = row && row.machine_id != null ? String(row.machine_id) : '';
304
+ return own || listMachineId;
305
+ }
306
+
307
+ function rowMachineName(row, listMachineName) {
308
+ const own = row && row.machine_id != null && String(row.machine_id) !== '' ? String(row.machine_name == null ? '' : row.machine_name) : '';
309
+ return own || listMachineName;
310
+ }
311
+
312
+ /** §6.1 dedup key: one row per (session, content). Pure. */
313
+ function fuseKey(row) {
314
+ const sessionId = row && row.session_id != null ? row.session_id : '';
315
+ const contentHash = row && row.content_hash != null ? row.content_hash : '';
316
+ return `${String(sessionId)}\u0000${String(contentHash)}`;
317
+ }
318
+
319
+ /** Locale-independent string order (never `localeCompare`: its result depends on the host locale). */
320
+ function cmpText(a, b) {
321
+ const x = String(a == null ? '' : a);
322
+ const y = String(b == null ? '' : b);
323
+ if (x < y) return -1;
324
+ return x > y ? 1 : 0;
325
+ }
326
+
327
+ function toSortNumber(value) {
328
+ const n = Number(value);
329
+ return Number.isFinite(n) ? n : Number.MAX_SAFE_INTEGER;
330
+ }
331
+
332
+ /**
333
+ * S-6 (docs/fleet-session-lists-hub-search-2026-11-01.html §5 D-5, §8 WP-1): one request per
334
+ * dialable member, all in flight at once, under ONE deadline. The scatter-gather itself now lives
335
+ * in the leaf lib/fleet-fanout.js `fanOutMembers` — this is the caller that gives it the pool-search
336
+ * leg (pathname, `scope=local` query, envelope parser), the search deadline and the member label.
337
+ */
338
+ function searchMembers(machines, args, d) {
339
+ return fanOutMembers(machines, {
340
+ pathname: POOL_SEARCH_PATHNAME,
341
+ search: buildSearchParams(args),
342
+ parse: (result) => parseSearchEnvelope(result, args.limit),
343
+ }, {
344
+ forward: d.forward,
345
+ setTimeoutFn: d.setTimeoutFn,
346
+ clearTimeoutFn: d.clearTimeoutFn,
347
+ deadlineMs: d.searchDeadlineMs,
348
+ label: machineLabel,
349
+ });
350
+ }
351
+
352
+ /**
353
+ * A member's answer -> `{ error: false, rows }` | `{ error: true, status }`. Success is a 2xx JSON
354
+ * object carrying a `results` array; an over-long list is CAPPED (the member answered truthfully, its
355
+ * tail is simply beyond what this caller asked for). Anything else — non-2xx, malformed JSON, a body
356
+ * without `results` — is a named per-machine `error`, never a throw and never a silent empty answer.
357
+ */
358
+ function parseSearchEnvelope(result, limit) {
359
+ if (!result || result.ok !== true) {
360
+ return { error: true, status: result && result.code === 'machine_unreachable' ? 'unreachable' : 'error' };
361
+ }
362
+ if (!(Number(result.status) >= 200 && Number(result.status) < 300)) return { error: true, status: 'error' };
363
+ let body;
364
+ try {
365
+ body = JSON.parse(String(result.text == null ? '' : result.text));
366
+ } catch {
367
+ /* expected: a non-JSON member body is reported as status 'error', never thrown at the caller */
368
+ return { error: true, status: 'error' };
369
+ }
370
+ if (!body || typeof body !== 'object' || Array.isArray(body) || !Array.isArray(body.results)) {
371
+ return { error: true, status: 'error' };
372
+ }
373
+ const cap = clampSearchLimit(limit);
374
+ return { error: false, status: 'ok', rows: body.results.slice(0, cap) };
375
+ }
376
+
377
+ /** R-4: an outbound search re-encodes the read with `scope=local` — the receiver never fans out. */
378
+ function buildSearchParams({ query, limit } = {}) {
379
+ const params = new URLSearchParams();
380
+ params.set('q', String(query == null ? '' : query));
381
+ params.set('limit', String(clampSearchLimit(limit)));
382
+ params.set('scope', 'local');
383
+ return `?${params.toString()}`;
384
+ }
385
+
386
+ /** A search limit is a positive whole row count; junk/absent falls back to the pool default. */
387
+ function clampSearchLimit(value) {
388
+ const n = Number(value);
389
+ if (!Number.isFinite(n) || n <= 0) return POOL_SEARCH_DEFAULT_LIMIT;
390
+ return Math.min(Math.trunc(n), 1000);
391
+ }
392
+
393
+ // ---------------------------------------------------------------------------------------------
394
+ // R-3(a) — forward to the owner
395
+ // ---------------------------------------------------------------------------------------------
396
+
397
+ async function forwardToMachine(machine, args, d, source) {
398
+ const parsed = await attemptForward(machine, args, d);
399
+ if (parsed.error) {
400
+ // R-3(a) forward failure. WP-5's stale-replica fallback lives in readSessionContextAnywhere;
401
+ // here the failure is named (the machine, and why), never a silent empty envelope.
402
+ return missEnvelope([ownerEntry(machine.instanceId, machine, parsed.status)]);
403
+ }
404
+ return withLocation(parsed.body, machine, source);
405
+ }
406
+
407
+ /** One GET to `machine` and the envelope verdict — the R-6 owner attempt reads the failure itself. */
408
+ function attemptForward(machine, args, d) {
409
+ return d.forward({
410
+ machine,
411
+ pathname: SESSION_CONTEXT_PATHNAME,
412
+ search: buildForwardSearch(args),
413
+ method: 'GET',
414
+ }).then((result) => parseForwardedEnvelope(result));
415
+ }
416
+
417
+ // ---------------------------------------------------------------------------------------------
418
+ // R-6 (WP-5) — replica as stale-if-error
419
+ // ---------------------------------------------------------------------------------------------
420
+
421
+ /** R-6: the origin the shared reader stamped on a replicated session ('' = this machine ingested it). */
422
+ function replicaOriginOf(local) {
423
+ const session = Array.isArray(local && local.sessions) ? local.sessions[0] : null;
424
+ return String((session && session.replica_origin) || '').trim();
425
+ }
426
+
427
+ /** R-7: a search row the reader marked as a copy of another machine's record. Pure. */
428
+ function isReplicaRow(row) {
429
+ return String((row && row.replica_origin) || '') !== '';
430
+ }
431
+
432
+ /**
433
+ * R-6: who to ask for the replica's own machine. The directory (`getMirrorSessionMachine`) is the
434
+ * fleet's naming authority and R-3(a) already forwards to what it names without a registry row; the
435
+ * registry row whose `instanceId` IS the replica's origin is the fallback, and only when dialable.
436
+ * `null` means no owner can be reached — the copy is then served, labelled, with that named.
437
+ */
438
+ function replicaOwnerMachine(sessionId, origin, exclude, d, originRow) {
439
+ const located = locateSessionOwner(sessionId, {
440
+ isLocalOwner: d.isLocalOwner,
441
+ getMirrorSessionMachine: d.getMirrorSessionMachine,
442
+ findMachine: d.findMachine,
443
+ });
444
+ if (located.owner === 'machine' && String(located.machine.instanceId) !== exclude) return located.machine;
445
+ const instanceId = String((originRow && originRow.instanceId) || '');
446
+ if (instanceId && instanceId === String(origin) && instanceId !== exclude && isDialable(originRow)) return originRow;
447
+ return null;
448
+ }
449
+
450
+ /** R-5/R-6: the `searched` row for the machine an R-6 read could not reach (named when known). */
451
+ function ownerEntry(origin, row, status) {
452
+ const named = row && String(row.instanceId || '') === String(origin) ? row : null;
453
+ return {
454
+ machine_id: String(origin),
455
+ machine_name: named ? machineLabel(named) : String(origin),
456
+ status,
457
+ };
458
+ }
459
+
460
+ /**
461
+ * R-6: the caller gets THIS machine's copy, labelled as the copy it is. `searched` names the owner
462
+ * that could not be reached, and is absent when there was no owner to name (`scope=local`, where the
463
+ * caller asked for this machine's copy on purpose).
464
+ */
465
+ function replicaAnswer(local, d, origin, searched) {
466
+ const self = d.localIdentity();
467
+ const answer = {
468
+ ...local,
469
+ location: {
470
+ machine_id: String(self.instanceId || ''),
471
+ machine_name: String(self.displayName || ''),
472
+ source: 'replica',
473
+ stale: true,
474
+ origin: String(origin),
475
+ },
476
+ };
477
+ if (Array.isArray(searched) && searched.length) answer.searched = searched;
478
+ return answer;
479
+ }
480
+
481
+ // ---------------------------------------------------------------------------------------------
482
+ // R-3(c) — the deadline-bounded parallel probe
483
+ // ---------------------------------------------------------------------------------------------
484
+
485
+ /** One request per dialable member, all in flight at once; the first non-empty answer wins. */
486
+ function probeMembers(machines, args, d) {
487
+ const statuses = new Map();
488
+ if (!machines.length) return Promise.resolve({ hit: null, statuses });
489
+ const search = buildForwardSearch(args);
490
+ return new Promise((resolve) => {
491
+ let settled = false;
492
+ let pending = machines.length;
493
+ let timer = null;
494
+ const settle = (hit) => {
495
+ if (settled) return;
496
+ settled = true;
497
+ if (timer) d.clearTimeoutFn(timer);
498
+ // Snapshot: a late answer after the deadline must not rewrite what we already reported.
499
+ resolve({ hit, statuses: new Map(statuses) });
500
+ };
501
+ // P-3: bound the whole fan-out, never wait for a straggler.
502
+ timer = d.setTimeoutFn(() => settle(null), d.probeDeadlineMs);
503
+ if (timer && typeof timer.unref === 'function') timer.unref();
504
+ // Every member answering before the deadline ends the probe at once (no gratuitous 5 s wait).
505
+ const note = (machineId, status) => {
506
+ statuses.set(machineId, status);
507
+ pending -= 1;
508
+ if (pending === 0) settle(null);
509
+ };
510
+ for (const machine of machines) {
511
+ statuses.set(machine.instanceId, 'timeout');
512
+ Promise.resolve()
513
+ .then(() => d.forward({
514
+ machine,
515
+ pathname: SESSION_CONTEXT_PATHNAME,
516
+ search,
517
+ method: 'GET',
518
+ }))
519
+ .then((result) => {
520
+ const parsed = parseForwardedEnvelope(result);
521
+ if (parsed.error) {
522
+ note(machine.instanceId, parsed.status);
523
+ return;
524
+ }
525
+ if (hasSessions(parsed.body)) {
526
+ statuses.set(machine.instanceId, 'ok');
527
+ settle(withLocation(parsed.body, machine, 'owner'));
528
+ return;
529
+ }
530
+ note(machine.instanceId, 'miss');
531
+ })
532
+ .catch(() => {
533
+ /* HANDLE: one member's failure is a per-machine status in `searched`, never thrown */
534
+ note(machine.instanceId, 'error');
535
+ });
536
+ }
537
+ });
538
+ }
539
+
540
+ // ---------------------------------------------------------------------------------------------
541
+ // Envelope helpers
542
+ // ---------------------------------------------------------------------------------------------
543
+
544
+ /**
545
+ * A forwarded result -> `{ error: false, body }` | `{ error: true, status }`. Success is a 2xx with
546
+ * a JSON object body (the owner's answer is authoritative, even when it is itself a miss).
547
+ */
548
+ function parseForwardedEnvelope(result) {
549
+ if (!result || result.ok !== true) {
550
+ return { error: true, status: result && result.code === 'machine_unreachable' ? 'unreachable' : 'error' };
551
+ }
552
+ if (!(Number(result.status) >= 200 && Number(result.status) < 300)) return { error: true, status: 'error' };
553
+ let body;
554
+ try {
555
+ body = JSON.parse(String(result.text == null ? '' : result.text));
556
+ } catch {
557
+ /* expected: a non-JSON member body is reported as status 'error', never thrown at the caller */
558
+ return { error: true, status: 'error' };
559
+ }
560
+ if (!body || typeof body !== 'object' || Array.isArray(body)) return { error: true, status: 'error' };
561
+ return { error: false, body };
562
+ }
563
+
564
+ function hasSessions(result) {
565
+ return Array.isArray(result && result.sessions) && result.sessions.length > 0;
566
+ }
567
+
568
+ function withLocation(body, machine, source) {
569
+ return {
570
+ ...body,
571
+ location: {
572
+ machine_id: String(machine.instanceId || ''),
573
+ machine_name: machineLabel(machine),
574
+ source,
575
+ stale: false,
576
+ },
577
+ };
578
+ }
579
+
580
+ function missEnvelope(searched) {
581
+ return { ok: true, found: false, sessions: [], messages: [], searched };
582
+ }
583
+
584
+ /**
585
+ * R-4: the receiver never forwards again — every outbound request re-encodes the read params with
586
+ * `scope=local`. Route-level params (`scope`, and the `ref` alias) are never forwarded.
587
+ */
588
+ function buildForwardSearch(args = {}) {
589
+ const params = new URLSearchParams();
590
+ const set = (key, value) => {
591
+ if (value === undefined || value === null || value === '') return;
592
+ params.set(key, String(value));
593
+ };
594
+ set('session_id', args.session_id);
595
+ if (Array.isArray(args.session_ids)) {
596
+ for (const id of args.session_ids) {
597
+ const value = String(id == null ? '' : id).trim();
598
+ if (value) params.append('session_id', value);
599
+ }
600
+ }
601
+ set('limit', args.limit);
602
+ set('turn_limit', args.turn_limit);
603
+ set('byte_limit', args.byte_limit);
604
+ set('deadline_ms', args.deadline_ms);
605
+ set('direction', args.direction);
606
+ set('cursor', args.cursor);
607
+ set('payload_byte_limit', args.payload_byte_limit);
608
+ if (args.include_raw) params.set('include_raw', '1');
609
+ if (args.dedupe === false) params.set('no_dedupe', '1');
610
+ set('format', args.format);
611
+ set('projection', args.projection);
612
+ params.set('scope', 'local');
613
+ return `?${params.toString()}`;
614
+ }
615
+
616
+ // ---------------------------------------------------------------------------------------------
617
+ // Targets / labels
618
+ // ---------------------------------------------------------------------------------------------
619
+
620
+ /** The mirror rows that are NOT this machine and NOT the caller's (`excludeMachineId`). */
621
+ function otherMembers(d, exclude) {
622
+ const self = String(d.localIdentity().instanceId || '');
623
+ return (d.machineRows() || []).filter((row) => row
624
+ && row.instanceId
625
+ && String(row.instanceId) !== self
626
+ && String(row.instanceId) !== exclude);
627
+ }
628
+
629
+ /** The mesh's dialable rule (lib/hub-bridge-mesh.js `_dialable`): enabled and proved reachable. */
630
+ function isDialable(row) {
631
+ if (!row) return false;
632
+ if (row.enabled === false || row.enabled === 0) return false;
633
+ return String(row.health || '') === 'reachable';
634
+ }
635
+
636
+ function searchedEntries(d, members, statuses) {
637
+ const entries = [selfEntry(d, 'miss')];
638
+ for (const row of members) {
639
+ entries.push({
640
+ machine_id: String(row.instanceId || ''),
641
+ machine_name: machineLabel(row),
642
+ status: statuses.get(row.instanceId) || 'skipped', // never dialled (disabled/unreachable)
643
+ });
644
+ }
645
+ return entries;
646
+ }
647
+
648
+ function selfEntry(d, status) {
649
+ const self = d.localIdentity();
650
+ return { machine_id: String(self.instanceId || ''), machine_name: String(self.displayName || ''), status };
651
+ }
652
+
653
+ function selfLocation(d, source) {
654
+ const self = d.localIdentity();
655
+ return {
656
+ machine_id: String(self.instanceId || ''),
657
+ machine_name: String(self.displayName || ''),
658
+ source,
659
+ stale: false,
660
+ };
661
+ }
662
+
663
+ function sessionIdsOf(args = {}) {
664
+ const out = [];
665
+ const push = (value) => {
666
+ const id = String(value == null ? '' : value).trim();
667
+ if (id && !out.includes(id)) out.push(id);
668
+ };
669
+ if (Array.isArray(args.session_ids)) args.session_ids.forEach(push);
670
+ push(args.session_id);
671
+ return out;
672
+ }
673
+
674
+ // ---------------------------------------------------------------------------------------------
675
+ // deps
676
+ // ---------------------------------------------------------------------------------------------
677
+
678
+ let _localIdentityMemo = null;
679
+
680
+ /** The SAME lazy identity module lib/remote-machine-proxy.js `_localInstanceId` uses (reused). */
681
+ function productionLocalIdentity() {
682
+ if (_localIdentityMemo) return _localIdentityMemo;
683
+ let instanceId = '';
684
+ try {
685
+ const identity = require('./instance-identity-access').getInstanceIdentity({ required: false });
686
+ instanceId = String((identity && identity.instanceId) || '');
687
+ } catch {
688
+ /* expected: identity is not minted before the DB is open — an unknown local id is honest */
689
+ instanceId = '';
690
+ }
691
+ let displayName = '';
692
+ try {
693
+ displayName = String(os.hostname() || '');
694
+ } catch {
695
+ /* expected: os.hostname() is best-effort — an empty display name never blocks a read */
696
+ displayName = '';
697
+ }
698
+ _localIdentityMemo = { instanceId, displayName };
699
+ return _localIdentityMemo;
700
+ }
701
+
702
+ function resolveDeps(deps = {}) {
703
+ return {
704
+ getMirrorSessionMachine: deps.getMirrorSessionMachine
705
+ || ((id) => require('./remote-machine-catalog').getMirrorSessionMachine(id)),
706
+ machineRows: deps.machineRows || (() => require('./remote-machine-catalog').machineRows()),
707
+ findMachine: deps.findMachine || ((instanceId) => (require('./remote-machine-catalog').machineRows() || [])
708
+ .find((row) => row && String(row.instanceId) === String(instanceId)) || null),
709
+ forward: deps.forward || ((request) => require('./remote-machine-proxy').forwardMachineHttp({
710
+ ...request,
711
+ deps: { timeoutMs: SESSION_OWNER_FORWARD_TIMEOUT_MS },
712
+ })),
713
+ readLocal: deps.readLocal || (() => Promise.reject(new Error('session_context_locator: readLocal is not wired'))),
714
+ searchLocal: deps.searchLocal || (() => Promise.reject(new Error('session_context_locator: searchLocal is not wired'))),
715
+ localIdentity: deps.localIdentity || productionLocalIdentity,
716
+ isLocalOwner: deps.isLocalOwner,
717
+ now: deps.now || (() => Date.now()),
718
+ setTimeoutFn: deps.setTimeoutFn || ((fn, ms) => setTimeout(fn, ms)),
719
+ clearTimeoutFn: deps.clearTimeoutFn || ((timer) => clearTimeout(timer)),
720
+ probeDeadlineMs: Number(deps.probeDeadlineMs) > 0 ? Number(deps.probeDeadlineMs) : SESSION_PROBE_DEADLINE_MS,
721
+ searchDeadlineMs: Number(deps.searchDeadlineMs) > 0
722
+ ? Number(deps.searchDeadlineMs)
723
+ : POOL_SEARCH_MEMBER_DEADLINE_MS,
724
+ };
725
+ }
726
+
727
+ module.exports = {
728
+ poolEnabled,
729
+ locateSessionOwner,
730
+ readSessionContextAnywhere,
731
+ searchSessionsAnywhere,
732
+ fusePoolResults,
733
+ // §8 WP-1: shared, never re-typed by lib/fleet-session-lists.js (its fan-out members and its
734
+ // labels must stay exactly this module's rules).
735
+ otherMembers,
736
+ isDialable,
737
+ machineLabel,
738
+ productionLocalIdentity,
739
+ SESSION_OWNER_FORWARD_TIMEOUT_MS,
740
+ SESSION_PROBE_DEADLINE_MS,
741
+ POOL_SEARCH_MEMBER_DEADLINE_MS,
742
+ };