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,884 @@
1
+ 'use strict';
2
+
3
+ // Landing-throughput WP-6 shard S15 (design doc §5 D-10 "move-only", §6 "Shard module
4
+ // seam"): the 17 setup-wizard HTTP handlers move out of server.js VERBATIM into this module
5
+ // behind the same configure(deps) seam as lib/api/setup.js (S2), lib/api/session-attach.js
6
+ // (S6), lib/walle-history-serve.js (S8), lib/codex-rollout-history.js (S9),
7
+ // lib/terminal-input-prepared.js (S10), lib/idle-notify.js (S11), lib/session-park.js (S12),
8
+ // lib/terminal-draft-operation.js (S13) and lib/conversation-freshness-diagnostics.js (S14).
9
+ // Bodies are identical to the server.js declarations except for the `_deps.` prefix on every
10
+ // identifier that used to resolve to a server.js top-level binding (D-10 rule 1), proven by
11
+ // `node bin/move-only-check.js`. Four inline requires were rebased to this module's own
12
+ // directory (S8 precedent: one move-only DIFF per rebased require -- the remainder is proven
13
+ // by re-running against a copy with the inverse sed applied).
14
+ //
15
+ // The module lives under lib/api/ (and NOT appended to lib/api/setup.js, which is already
16
+ // 1,089 lines: itself plus these 807 would cross the 1,500-line new-file ceiling). It
17
+ // registers no routes of its own: lib/api/setup.js keeps the route table and receives these
18
+ // 17 functions through its own configure() call in server.js.
19
+ //
20
+ // Injected deps, per function:
21
+ // apiSetupStatus: CONFIG_DIR, HOSTNAME, __dirname, _buildHostedRuntimeContext, _controlSnapshot, _effectiveStorageBackends, _runCtmDbWorkerOp, fs, getActiveWalleProviderState, os, path, resolveEnvFilePath, serverConfig, setup, walleClient
22
+ // apiSetupDetectCodexCli: codingAgentAuth, readUserShellEnv
23
+ // apiSetupDetectGeminiCli: CONFIG_DIR, fs, hostIdentityAccessAllowed, path, serverConfig
24
+ // apiSetupDetectClaudeCli: codingAgentAuth, resolveAgentCliCommand
25
+ // apiSetupOauthProxyStart: oauthProxySupervisor
26
+ // apiSetupOauthProxyStop: oauthProxySupervisor
27
+ // apiSetupOauthProxyStatus: oauthProxySupervisor
28
+ // apiSetupProviders: SETUP_PROVIDER_ENV_KEYS, SETUP_PROVIDER_NAMES, collapseSetupProviderInstances, getWalleModelProviders, getWalleStatusData, loggedSetupProviderDuplicateSignatures, normalizeModelOverviewProviderCredentialState, providerRuntimeStateFromRow, sanitizeSetupProviderType, telemetry
29
+ // apiSetupSaveProvider: CONFIG_DIR, SETUP_PROVIDER_ENV_KEYS, SETUP_PROVIDER_NAMES, SETUP_PROVIDER_TYPES, __dirname, atomicWriteFileSync, fs, oauthProxySupervisor, path, resolveEnvFilePath, sanitizeSetupProviderType, serverConfig, setup, setupProviderAnswers, setupProviderTypeList, walleOwnerWrite, walleSupervisor
30
+ // apiSetupTestProvider: claudeCliAccountError, codingAgentAuth, firstModelLabel, getProviderRuntimeStateFromWalle, isSetupProviderType, listWalleModelsViaOwner, probeAnthropicOauthProxy, sanitizeSetupProviderType, setupProviderAnswers, shouldRouteOllamaTestViaOwner
31
+ // apiSetupSetDefault: getWalleDefaultModelSelection, resolveSetupDefaultSelection, sanitizeSetupProviderType, setup, walleOwnerWrite
32
+ // apiSetupCheckDir: fs, path
33
+ // apiSetupEmbeddings: walleClient
34
+ // apiSetupEmbeddingsSwitch: readBody, walleClient
35
+ // apiSetupEmbeddingsPolicy: readBody, walleClient
36
+ // apiSetupEmbeddingsDelete: readBody, walleClient
37
+ // apiSetupSaveDirs: CONFIG_DIR, __dirname, _closeDbForProcessExit, _ctmDbClosingForProcessExit, _dbCloseOutcome, _drainCtmDbWorkers, _exitForLaunchdRestart, atomicWriteFileSync, createWalleBrainBackupForCopy, dbModule, fs, os, path, resolveEnvFilePath, serverConfig
38
+ //
39
+ // Mutable module state (D-10 rule 2 / S9 lesson 7): _ctmDbClosingForProcessExit is a server.js
40
+ // `let` that a moved body WRITES (apiSetupSaveDirs sets it before the launchd restart), so it is
41
+ // injected as a get/set PAIR -- never copied. Every other dep is a `const` binding, a function
42
+ // declaration or the CommonJS wrapper parameter, all passed by value.
43
+ // `__dirname` is injected although it is NOT a server.js top-level binding: it is the CommonJS
44
+ // wrapper parameter, so left bare in this file it would resolve to lib/api/ instead of server.js's
45
+ // own directory and silently change resolveEnvFilePath's repoRoot (D-10 rule 9, "behaviour: none
46
+ // changes"). It is passed by value, same as every immutable binding.
47
+ // Calls between the 17 moved functions stay direct (no `_deps.`); none references another.
48
+
49
+ let _deps = null;
50
+
51
+ function configure(deps = {}) {
52
+ _deps = deps;
53
+ }
54
+
55
+ async function apiSetupStatus(req, res, url) {
56
+ const envPath = _deps.resolveEnvFilePath({ repoRoot: _deps.path.resolve(_deps.__dirname, '..'), dataDir: _deps.CONFIG_DIR });
57
+ let hasApiKey = !!(_deps.serverConfig.ANTHROPIC_API_KEY || _deps.serverConfig.ANTHROPIC_BASE_URL);
58
+ let ownerName = _deps.serverConfig.WALLE_OWNER_NAME || '';
59
+ if (!hasApiKey) {
60
+ try {
61
+ const envContent = await _deps.fs.promises.readFile(envPath, 'utf8');
62
+ hasApiKey = /^ANTHROPIC_API_KEY=\S+/m.test(envContent);
63
+ } catch {}
64
+ }
65
+ let slackConnected = false;
66
+ let slackTeam = '';
67
+ try {
68
+ const tokPath = _deps.path.join(_deps.serverConfig.HOME, '.claude', 'wall-e-slack-token.json');
69
+ const tok = JSON.parse(await _deps.fs.promises.readFile(tokPath, 'utf8'));
70
+ // Check token exists, is non-empty, AND hasn't expired
71
+ if (tok.access_token && tok.access_token.length > 10) {
72
+ const expiresAt = tok.expiresAt || (tok.obtained_at ? tok.obtained_at + (tok.expires_in || 43200) * 1000 : 0);
73
+ slackConnected = !expiresAt || expiresAt > Date.now();
74
+ }
75
+ slackTeam = tok.team_name || '';
76
+ } catch {}
77
+ res.writeHead(200, { 'Content-Type': 'application/json' });
78
+ let version = '';
79
+ try { version = require('../../package.json').version; } catch {}
80
+ const ctmDataDir = require('../data-dir').getDataDir();
81
+ const walleDataDir = _deps.serverConfig.WALL_E_DATA_DIR || _deps.path.join(_deps.os.homedir(), '.walle', 'data');
82
+ let walleProvider = 'anthropic';
83
+ let walleModel = '';
84
+ let serviceAlerts = [];
85
+ let serviceAlertSummary = null;
86
+ try {
87
+ const upstream = await _deps.walleClient.requestJson('/api/wall-e/status');
88
+ const data = upstream.json?.data || {};
89
+ if (data.walle_provider) walleProvider = data.walle_provider;
90
+ if (data.walle_model) walleModel = data.walle_model;
91
+ if (data.owner && !ownerName) ownerName = data.owner;
92
+ if (typeof data.has_api_key === 'boolean') hasApiKey = hasApiKey || data.has_api_key;
93
+ serviceAlerts = Array.isArray(data.service_alerts) ? data.service_alerts : [];
94
+ if (data.service_alert_summary && typeof data.service_alert_summary === 'object') serviceAlertSummary = data.service_alert_summary;
95
+ } catch {}
96
+ // Also check env-based provider keys
97
+ if (!hasApiKey && walleProvider === 'openai' && _deps.serverConfig.OPENAI_API_KEY) hasApiKey = true;
98
+ if (!hasApiKey && walleProvider === 'google' && (_deps.serverConfig.GOOGLE_API_KEY || _deps.serverConfig.GEMINI_API_KEY)) hasApiKey = true;
99
+ if (!hasApiKey && walleProvider === 'deepseek' && _deps.serverConfig.DEEPSEEK_API_KEY) hasApiKey = true;
100
+ if (!hasApiKey && walleProvider === 'moonshot' && _deps.serverConfig.MOONSHOT_API_KEY) hasApiKey = true;
101
+ if (!hasApiKey && walleProvider === 'zai' && _deps.serverConfig.ZAI_API_KEY) hasApiKey = true;
102
+ if (!hasApiKey && (walleProvider === 'ollama' || walleProvider === 'mlx')) hasApiKey = true;
103
+ const providerState = _deps.getActiveWalleProviderState(walleProvider);
104
+ if (!hasApiKey && providerState.hasRuntimeAccess) hasApiKey = true;
105
+ const authMethod = providerState.authMethod || '';
106
+ const codingViaCli = _deps.serverConfig.WALLE_CODING_USE_CLI === 'true';
107
+ const deviceLabel = _deps._controlSnapshot.getSetting('ui_setup_device_label', 'Owner iPhone');
108
+ const {
109
+ instance,
110
+ sync,
111
+ workspaceBindings,
112
+ localEdge,
113
+ security,
114
+ hostedReadiness,
115
+ } = await _deps._buildHostedRuntimeContext();
116
+ const transcriptRetention = await require('../transcript-retention-check').collectTranscriptRetentionCheck({ runOwnerOp: _deps._runCtmDbWorkerOp });
117
+ res.end(JSON.stringify({ owner_name: ownerName, has_api_key: hasApiKey, slack_connected: slackConnected, slack_team: slackTeam, needs_setup: !hasApiKey && _deps.setup.needsSetup(), version, ctm_data_dir: ctmDataDir, walle_data_dir: walleDataDir, hostname: _deps.HOSTNAME, walle_model: walleModel, walle_provider: walleProvider, auth_method: authMethod, coding_via_cli: codingViaCli, service_alerts: serviceAlerts, service_alert_summary: serviceAlertSummary, device_label: deviceLabel, instance, storage_backend: { ..._deps._effectiveStorageBackends }, sync, workspaceBindings, localEdge, security, hostedReadiness, transcript_retention: transcriptRetention }));
118
+ return;
119
+ }
120
+
121
+ async function apiSetupDetectCodexCli(req, res, url) {
122
+ const respond = (data) => { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(data)); };
123
+ // The probe itself (which spawns `codex login status`, matches "logged in" on
124
+ // either stream, and reads plan/email from ~/.codex/auth.json) lives in
125
+ // lib/coding-agent-auth.js (WP-0).
126
+ const account = await _deps.codingAgentAuth.probeAccount('codex');
127
+ const loggedIn = account.signedIn === true;
128
+ // Also surface whether the user has a separate OPENAI_API_KEY (mode-mismatch nudge)
129
+ let alsoHasApiKey = false;
130
+ try {
131
+ const out = _deps.readUserShellEnv('OPENAI_API_KEY', 4);
132
+ alsoHasApiKey = !!(out && out.startsWith('sk-'));
133
+ } catch {}
134
+ respond({
135
+ found: account.reason !== 'cli_missing',
136
+ logged_in: loggedIn,
137
+ version: null, // `codex login status` prints no version; agent-versions owns that
138
+ email: account.email || null,
139
+ plan: account.plan || null,
140
+ also_has_api_key: alsoHasApiKey,
141
+ hint: loggedIn ? null
142
+ : (account.reason === 'cli_missing'
143
+ ? 'codex CLI not on PATH. Install Codex from https://chatgpt.com/codex'
144
+ : (account.reason === 'probe_unavailable'
145
+ ? 'codex login state is only probed by the primary instance'
146
+ : 'codex installed but not logged in — run `codex login`')),
147
+ });
148
+ return;
149
+ }
150
+
151
+ async function apiSetupDetectGeminiCli(req, res, url) {
152
+ const GEMINI_CLI_CREDS = _deps.path.join(_deps.serverConfig.HOME, '.gemini', 'oauth_creds.json');
153
+ const GEMINI_CLIENT_ID = '681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com';
154
+ const GEMINI_CLIENT_SECRET = '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';
155
+ const respond = (data) => { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(data)); };
156
+
157
+ // 1. Read Gemini CLI creds file
158
+ let creds;
159
+ try {
160
+ if (!_deps.hostIdentityAccessAllowed({ configDir: _deps.CONFIG_DIR })) throw new Error('host identity access not allowed');
161
+ creds = JSON.parse(_deps.fs.readFileSync(GEMINI_CLI_CREDS, 'utf8'));
162
+ } catch {
163
+ respond({ found: false, hint: 'Gemini CLI not installed. Get an API key from aistudio.google.com/apikey' });
164
+ return;
165
+ }
166
+ if (!creds.refresh_token) {
167
+ respond({ found: false, hint: 'Gemini CLI creds found but no refresh token. Re-run "gemini auth login"' });
168
+ return;
169
+ }
170
+
171
+ (async () => {
172
+ // 2. Refresh the OAuth access token
173
+ let accessToken;
174
+ try {
175
+ const tokenResp = await fetch('https://oauth2.googleapis.com/token', {
176
+ method: 'POST',
177
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
178
+ body: new URLSearchParams({
179
+ client_id: GEMINI_CLIENT_ID,
180
+ client_secret: GEMINI_CLIENT_SECRET,
181
+ refresh_token: creds.refresh_token,
182
+ grant_type: 'refresh_token',
183
+ }),
184
+ });
185
+ const tokenData = await tokenResp.json();
186
+ if (!tokenData.access_token) throw new Error(tokenData.error_description || 'Token refresh failed');
187
+ accessToken = tokenData.access_token;
188
+ } catch (e) {
189
+ return respond({ found: true, error: 'Token refresh failed: ' + e.message });
190
+ }
191
+
192
+ // 3. Find a GCP project and enable required APIs
193
+ const authHeaders = { 'Authorization': `Bearer ${accessToken}` };
194
+ let projectId;
195
+ try {
196
+ const projResp = await fetch('https://cloudresourcemanager.googleapis.com/v1/projects?filter=lifecycleState%3AACTIVE&pageSize=10', {
197
+ headers: authHeaders,
198
+ });
199
+ const projData = await projResp.json();
200
+ projectId = projData.projects?.[0]?.projectId;
201
+ } catch {}
202
+ if (!projectId) {
203
+ return respond({ found: true, error: 'No active GCP project found. Get an API key from aistudio.google.com/apikey instead.' });
204
+ }
205
+ const projHeaders = { ...authHeaders, 'x-goog-user-project': projectId };
206
+
207
+ // Enable Generative Language API + API Keys API on the project
208
+ for (const svc of ['generativelanguage.googleapis.com', 'apikeys.googleapis.com']) {
209
+ try {
210
+ await fetch(`https://serviceusage.googleapis.com/v1/projects/${projectId}/services/${svc}:enable`, {
211
+ method: 'POST', headers: authHeaders,
212
+ });
213
+ } catch {}
214
+ }
215
+
216
+ // 4. Create a Gemini API key (permanent — no refresh needed)
217
+ let apiKey;
218
+ try {
219
+ // Check for existing Wall-E key first
220
+ const listResp = await fetch(`https://apikeys.googleapis.com/v2/projects/${projectId}/locations/global/keys`, {
221
+ headers: projHeaders,
222
+ });
223
+ const listData = await listResp.json();
224
+ const existing = (listData.keys || []).find(k => k.displayName === 'Wall-E Gemini Key (auto-created)');
225
+ if (existing) {
226
+ // Re-fetch with keyString
227
+ const getResp = await fetch(`https://apikeys.googleapis.com/v2/${existing.name}/keyString`, {
228
+ headers: projHeaders,
229
+ });
230
+ const getData = await getResp.json();
231
+ apiKey = getData.keyString;
232
+ }
233
+ if (!apiKey) {
234
+ // Create new key restricted to Gemini API
235
+ const createResp = await fetch(`https://apikeys.googleapis.com/v2/projects/${projectId}/locations/global/keys`, {
236
+ method: 'POST', headers: { ...projHeaders, 'Content-Type': 'application/json' },
237
+ body: JSON.stringify({
238
+ displayName: 'Wall-E Gemini Key (auto-created)',
239
+ restrictions: { apiTargets: [{ service: 'generativelanguage.googleapis.com' }] },
240
+ }),
241
+ });
242
+ const createData = await createResp.json();
243
+ if (createData.error) throw new Error(createData.error.message);
244
+ // Poll for completion (usually instant)
245
+ const opName = createData.name;
246
+ for (let i = 0; i < 10; i++) {
247
+ const opResp = await fetch(`https://apikeys.googleapis.com/v2/${opName}`, { headers: projHeaders });
248
+ const opData = await opResp.json();
249
+ if (opData.done) {
250
+ // GCP may redact keyString in the LRO response — fetch it via GetKeyString endpoint
251
+ if (opData.response?.keyString) {
252
+ apiKey = opData.response.keyString;
253
+ } else if (opData.response?.name) {
254
+ const ksResp = await fetch(`https://apikeys.googleapis.com/v2/${opData.response.name}/keyString`, { headers: projHeaders });
255
+ const ksData = await ksResp.json();
256
+ apiKey = ksData.keyString;
257
+ }
258
+ break;
259
+ }
260
+ if (opData.error) throw new Error(opData.error.message);
261
+ await new Promise(r => setTimeout(r, 1000));
262
+ }
263
+ }
264
+ } catch (e) {
265
+ return respond({ found: true, error: 'Failed to create API key: ' + e.message + '. Get one manually from aistudio.google.com/apikey' });
266
+ }
267
+ if (!apiKey) {
268
+ return respond({ found: true, error: 'API key creation timed out. Try again or get a key from aistudio.google.com/apikey' });
269
+ }
270
+
271
+ // 5. Test the API key against Gemini API
272
+ try {
273
+ const testResp = await fetch(`https://generativelanguage.googleapis.com/v1beta/models?key=${encodeURIComponent(apiKey)}&pageSize=1`);
274
+ if (testResp.ok) {
275
+ return respond({
276
+ found: true, source: 'Gemini CLI (auto-created API key)',
277
+ api_key: apiKey,
278
+ model: 'gemini-2.5-flash',
279
+ });
280
+ }
281
+ const errBody = await testResp.json().catch(() => ({}));
282
+ const errMsg = errBody?.error?.message || `HTTP ${testResp.status}`;
283
+ return respond({ found: true, error: errMsg });
284
+ } catch (e) {
285
+ return respond({ found: true, error: e.message });
286
+ }
287
+ })().catch(e => respond({ found: false, error: e.message }));
288
+ return;
289
+ }
290
+
291
+ async function apiSetupDetectClaudeCli(req, res, url) {
292
+ const respond = (data) => {
293
+ res.writeHead(200, { 'Content-Type': 'application/json' });
294
+ res.end(JSON.stringify(data));
295
+ };
296
+ const { spawn } = require('child_process');
297
+ let proc;
298
+ try {
299
+ // spawn (not exec) to avoid shell injection. Looks up `claude` on PATH;
300
+ // returns ENOENT if not installed.
301
+ proc = spawn(_deps.resolveAgentCliCommand('claude'), ['--version'], { stdio: ['ignore', 'pipe', 'pipe'], timeout: 5000 });
302
+ } catch (err) {
303
+ return respond({ found: false, hint: 'Could not spawn claude: ' + err.message });
304
+ }
305
+ let stdout = '';
306
+ let stderr = '';
307
+ proc.stdout.on('data', (d) => { stdout += d.toString(); });
308
+ proc.stderr.on('data', (d) => { stderr += d.toString(); });
309
+ proc.on('error', (err) => {
310
+ respond({
311
+ found: false,
312
+ hint: err.code === 'ENOENT'
313
+ ? 'claude CLI not on PATH. Install Claude Code from https://claude.com/claude-code, then run `claude login`.'
314
+ : 'claude failed: ' + err.message,
315
+ });
316
+ });
317
+ proc.on('close', (code) => {
318
+ if (code !== 0) {
319
+ return respond({
320
+ found: false,
321
+ hint: 'claude --version exited ' + code + (stderr ? ': ' + stderr.slice(0, 200) : ''),
322
+ });
323
+ }
324
+ // claude --version prints something like "2.1.119 (Claude Code)" — pull the first version-shaped token.
325
+ const versionMatch = stdout.match(/\d+\.\d+\.\d+/);
326
+ // WP-0: the login facts come from `claude auth status` (lib/coding-agent-auth).
327
+ _deps.codingAgentAuth.probeAccount('claude-code').then((account) => {
328
+ respond({
329
+ found: true,
330
+ version: versionMatch ? versionMatch[0] : stdout.trim().slice(0, 40),
331
+ logged_in: account.signedIn,
332
+ email: account.email || null,
333
+ plan: account.plan || null,
334
+ });
335
+ }, (error) => {
336
+ // HANDLE: probeAccount resolves rather than rejects; this keeps a thrown promise from hanging the response.
337
+ respond({
338
+ found: true,
339
+ version: versionMatch ? versionMatch[0] : stdout.trim().slice(0, 40),
340
+ logged_in: null,
341
+ email: null,
342
+ plan: null,
343
+ hint: 'claude login probe failed: ' + ((error && error.message) || 'unknown error'),
344
+ });
345
+ });
346
+ });
347
+ return;
348
+ }
349
+
350
+ async function apiSetupOauthProxyStart(req, res, url) {
351
+ const out = _deps.oauthProxySupervisor.start();
352
+ res.writeHead(200, { 'Content-Type': 'application/json' });
353
+ res.end(JSON.stringify(out));
354
+ return;
355
+ }
356
+
357
+ async function apiSetupOauthProxyStop(req, res, url) {
358
+ const out = _deps.oauthProxySupervisor.stop();
359
+ res.writeHead(200, { 'Content-Type': 'application/json' });
360
+ res.end(JSON.stringify(out));
361
+ return;
362
+ }
363
+
364
+ async function apiSetupOauthProxyStatus(req, res, url) {
365
+ const out = await _deps.oauthProxySupervisor.status();
366
+ res.writeHead(200, { 'Content-Type': 'application/json' });
367
+ res.end(JSON.stringify(out));
368
+ return;
369
+ }
370
+
371
+ async function apiSetupProviders(req, res, url) {
372
+ try {
373
+ const [providersSettled, statusSettled] = await Promise.allSettled([
374
+ _deps.getWalleModelProviders(2500),
375
+ _deps.getWalleStatusData(2500),
376
+ ]);
377
+ const rows = providersSettled.status === 'fulfilled' ? providersSettled.value : [];
378
+ const status = statusSettled.status === 'fulfilled' ? statusSettled.value : {};
379
+ const defaultProvider = _deps.sanitizeSetupProviderType(status.walle_provider || 'anthropic') || 'anthropic';
380
+ const defaultModel = status.walle_model || '';
381
+ const providerInstances = rows.map(row => {
382
+ const envKey = _deps.SETUP_PROVIDER_ENV_KEYS[row.type];
383
+ const runtime = _deps.providerRuntimeStateFromRow(row.type, row);
384
+ const authMethod = runtime.authMethod || 'api_key';
385
+ const hasStoredKey = runtime.hasStoredKey;
386
+ const hasKey = runtime.hasRuntimeAccess || (envKey ? !!process.env[envKey] : false);
387
+ const credentialState = _deps.normalizeModelOverviewProviderCredentialState({
388
+ ...row,
389
+ auth_method: authMethod,
390
+ has_key: hasStoredKey,
391
+ has_stored_key: hasStoredKey,
392
+ });
393
+ let status = 'unknown', lastTested = null, error = null;
394
+ return {
395
+ id: row.id, type: row.type, name: _deps.SETUP_PROVIDER_NAMES[row.type] || row.name,
396
+ enabled: !!row.enabled, is_default: row.type === defaultProvider,
397
+ has_key: hasKey, model: row.type === defaultProvider ? (defaultModel || null) : null,
398
+ has_stored_key: hasStoredKey,
399
+ has_api_key: hasKey,
400
+ has_runtime_access: hasKey,
401
+ credential_set: hasKey,
402
+ credential_status: hasKey ? credentialState.credential_status : 'missing',
403
+ credential_source: credentialState.credential_source || (hasKey ? 'runtime' : ''),
404
+ base_url: row.base_url || null,
405
+ auth_method: authMethod,
406
+ status, last_tested: lastTested, error,
407
+ created_at: row.created_at,
408
+ updated_at: row.updated_at,
409
+ };
410
+ });
411
+ const { providers, duplicateDiagnostics, invalidProviderCount } = _deps.collapseSetupProviderInstances(providerInstances, { defaultProvider });
412
+ if (duplicateDiagnostics.length || invalidProviderCount) {
413
+ const signature = JSON.stringify({ duplicateDiagnostics, invalidProviderCount });
414
+ if (!_deps.loggedSetupProviderDuplicateSignatures.has(signature)) {
415
+ _deps.loggedSetupProviderDuplicateSignatures.add(signature);
416
+ console.warn('[setup] Collapsed duplicate provider instances for setup health:', {
417
+ duplicateDiagnostics,
418
+ invalidProviderCount,
419
+ });
420
+ }
421
+ try {
422
+ _deps.telemetry.track('setup_provider_instances_collapsed', {
423
+ duplicate_types: duplicateDiagnostics.map((item) => item.type).join(','),
424
+ duplicate_groups: duplicateDiagnostics.length,
425
+ invalid_provider_count: invalidProviderCount,
426
+ });
427
+ } catch {}
428
+ }
429
+ res.writeHead(200, { 'Content-Type': 'application/json' });
430
+ res.end(JSON.stringify({ providers, default_provider: defaultProvider, duplicate_providers: duplicateDiagnostics }));
431
+ } catch (e) {
432
+ res.writeHead(200, { 'Content-Type': 'application/json' });
433
+ res.end(JSON.stringify({ providers: [], default_provider: null, error: e.message }));
434
+ }
435
+ return;
436
+ }
437
+
438
+ async function apiSetupSaveProvider(req, res, url) {
439
+ let body = '';
440
+ let bodyLen = 0;
441
+ req.on('data', c => { bodyLen += c.length; if (bodyLen > 8192) { req.destroy(); return; } body += c; });
442
+ req.on('end', async () => {
443
+ try {
444
+ const data = JSON.parse(body);
445
+ const type = _deps.sanitizeSetupProviderType(data.type);
446
+ if (!_deps.SETUP_PROVIDER_TYPES.includes(type)) {
447
+ res.writeHead(400, { 'Content-Type': 'application/json' });
448
+ res.end(JSON.stringify({ error: 'Invalid provider type. Must be one of: ' + _deps.setupProviderTypeList() }));
449
+ return;
450
+ }
451
+ const apiKey = typeof data.api_key === 'string' ? data.api_key.replace(/[\r\n\s]/g, '').slice(0, 200) : '';
452
+ const hasBaseUrl = Object.prototype.hasOwnProperty.call(data, 'base_url') || Object.prototype.hasOwnProperty.call(data, 'baseUrl');
453
+ const rawBaseUrl = typeof data.base_url === 'string' ? data.base_url : (typeof data.baseUrl === 'string' ? data.baseUrl : '');
454
+ const baseUrl = hasBaseUrl ? rawBaseUrl.replace(/[\r\n]/g, '').trim().slice(0, 500) : undefined;
455
+ const model = typeof data.model === 'string' ? data.model.replace(/[\r\n\s]/g, '').slice(0, 100) : '';
456
+ const enabled = data.enabled !== false;
457
+ const setDefault = !!data.set_default;
458
+ const VALID_AUTH_METHODS = new Set(['api_key', 'claude_cli', 'oauth_proxy', 'codex_cli', 'gemini_cli', 'devbox']);
459
+ const authMethod = typeof data.auth_method === 'string' && VALID_AUTH_METHODS.has(data.auth_method) ? data.auth_method : null;
460
+
461
+ const ownerBody = {
462
+ type,
463
+ name: _deps.SETUP_PROVIDER_NAMES[type] || type,
464
+ api_key: apiKey,
465
+ model,
466
+ enabled,
467
+ set_default: setDefault,
468
+ auth_method: authMethod,
469
+ };
470
+ if (hasBaseUrl) ownerBody.base_url = baseUrl || null;
471
+ if (typeof data.caCert === 'string' && data.caCert.trim()) ownerBody.caCert = data.caCert.trim();
472
+
473
+ const ownerWrite = await _deps.walleOwnerWrite('/api/wall-e/setup/provider', {
474
+ method: 'POST',
475
+ body: ownerBody,
476
+ });
477
+
478
+ // Persist .env updates: API key (if provided), WALLE_AUTH_METHOD (if
479
+ // set for an auth-capable provider), and (set_default is Wall-E-owned, never written here). Note we run this branch even when no apiKey is
480
+ // present — auth_method=oauth_proxy (and similar CLI methods) do
481
+ // not require an API key but still need WALLE_AUTH_METHOD wired
482
+ // into the daemon's env so its provider factory routes correctly.
483
+ const writesEnv = !!(apiKey && _deps.SETUP_PROVIDER_ENV_KEYS[type])
484
+ || (authMethod && (type === 'anthropic' || type === 'openai'));
485
+ if (writesEnv) {
486
+ const envPath = _deps.resolveEnvFilePath({ repoRoot: _deps.path.resolve(_deps.__dirname, '..'), dataDir: _deps.CONFIG_DIR });
487
+ const envVar = _deps.SETUP_PROVIDER_ENV_KEYS[type];
488
+ const lines = [];
489
+ const keysToReplace = new Set();
490
+ if (apiKey && envVar) {
491
+ keysToReplace.add(envVar);
492
+ if (type === 'google') keysToReplace.add('GEMINI_API_KEY');
493
+ }
494
+ if (authMethod && (type === 'anthropic' || type === 'openai')) {
495
+ keysToReplace.add('WALLE_AUTH_METHOD');
496
+ }
497
+ // Auth methods that use keychain/CLI/proxy must NOT leave the old
498
+ // ANTHROPIC_API_KEY / OPENAI_API_KEY in .env — those env vars
499
+ // become a conflict source for child processes (e.g. `claude` CLI
500
+ // emits "Auth conflict: Both a token (claude.ai) and an API key
501
+ // (ANTHROPIC_API_KEY) are set" because both are present after
502
+ // we switch to oauth_proxy or claude_cli). Strip the auth env
503
+ // entirely on those methods so newly-spawned PTY sessions don't
504
+ // inherit them via process.env. Also clears the legacy gateway
505
+ // variables that piggyback on ANTHROPIC_*.
506
+ const KEYCHAIN_AUTH_METHODS = new Set(['oauth_proxy', 'claude_cli']);
507
+ const stripAnthropicEnv = type === 'anthropic' && authMethod && KEYCHAIN_AUTH_METHODS.has(authMethod);
508
+ if (stripAnthropicEnv) {
509
+ keysToReplace.add('ANTHROPIC_API_KEY');
510
+ keysToReplace.add('ANTHROPIC_AUTH_TOKEN');
511
+ keysToReplace.add('ANTHROPIC_BASE_URL');
512
+ keysToReplace.add('ANTHROPIC_CUSTOM_HEADERS_B64');
513
+ }
514
+ const stripOpenaiEnv = type === 'openai' && authMethod === 'codex_cli';
515
+ if (stripOpenaiEnv) {
516
+ keysToReplace.add('OPENAI_API_KEY');
517
+ keysToReplace.add('OPENAI_BASE_URL');
518
+ }
519
+ try {
520
+ const existing = _deps.fs.readFileSync(envPath, 'utf8');
521
+ for (const line of existing.split('\n')) {
522
+ const m = line.match(/^\s*#?\s*([A-Z0-9_]+)\s*=/);
523
+ if (m && keysToReplace.has(m[1])) continue;
524
+ lines.push(line);
525
+ }
526
+ } catch { lines.push('# Wall-E configuration'); lines.push(''); }
527
+ while (lines.length > 0 && lines[lines.length - 1].trim() === '') lines.pop();
528
+ lines.push('');
529
+ if (apiKey && envVar) {
530
+ lines.push(`${envVar}=${apiKey}`);
531
+ _deps.serverConfig.override(envVar, apiKey, 'provider-api-key-save'); // WP-4: seam, never process.env directly
532
+ }
533
+ if (authMethod && (type === 'anthropic' || type === 'openai')) {
534
+ lines.push(`WALLE_AUTH_METHOD=${authMethod}`);
535
+ _deps.serverConfig.override("WALLE_AUTH_METHOD", authMethod, 'ctm-config-seam: migrated write site (WP-4 AC-4c)');
536
+ }
537
+ // Mirror the strip into process.env so newly-spawned PTY children
538
+ // don't inherit the conflicting vars. (The .env edit alone only
539
+ // affects FUTURE CTM boots; live process.env still has them.)
540
+ if (stripAnthropicEnv) {
541
+ delete _deps.serverConfig.ANTHROPIC_API_KEY;
542
+ delete _deps.serverConfig.ANTHROPIC_AUTH_TOKEN;
543
+ delete _deps.serverConfig.ANTHROPIC_BASE_URL;
544
+ delete _deps.serverConfig.ANTHROPIC_CUSTOM_HEADERS_B64;
545
+ }
546
+ if (stripOpenaiEnv) {
547
+ delete _deps.serverConfig.OPENAI_API_KEY;
548
+ delete process.env.OPENAI_BASE_URL;
549
+ }
550
+ _deps.atomicWriteFileSync(envPath, lines.join('\n') + '\n', { mode: 0o600 });
551
+ }
552
+
553
+ // OAuth proxy lifecycle: when the user picks auth_method=oauth_proxy
554
+ // for Anthropic, start the local proxy so the daemon can route
555
+ // through it on its next restart. When they pick anything else AND
556
+ // we previously had it set, stop the proxy. Idempotent on both
557
+ // sides — supervisor returns alreadyRunning/alreadyStopped.
558
+ if (type === 'anthropic') {
559
+ try {
560
+ if (authMethod === 'oauth_proxy') {
561
+ _deps.oauthProxySupervisor.start();
562
+ } else if (authMethod && authMethod !== 'oauth_proxy') {
563
+ if (_deps.oauthProxySupervisor.isRunning()) _deps.oauthProxySupervisor.stop();
564
+ }
565
+ } catch (_) { /* supervisor errors don't block save */ }
566
+ }
567
+
568
+ // WP-2 (delegated-implementation-workers-2026-09-24.html §5 D-4, §9 S-5): re-push
569
+ // the model-proxy route table -- best-effort, never blocks the save response.
570
+ _deps.oauthProxySupervisor.pushRoutesNow().catch(() => { /* expected: best-effort route re-push -- a proxy that is not running yet must never block a provider save */ });
571
+
572
+ // Sync model registry and restart
573
+ _deps.setup.clearSetupCache();
574
+ _deps.walleSupervisor.restartQuiet();
575
+
576
+ res.writeHead(200, { 'Content-Type': 'application/json' });
577
+ res.end(JSON.stringify(_deps.setupProviderAnswers.providerSaveBody(ownerWrite)));
578
+ } catch (e) {
579
+ res.writeHead(400, { 'Content-Type': 'application/json' });
580
+ res.end(JSON.stringify({ error: e.message }));
581
+ }
582
+ });
583
+ return;
584
+ }
585
+
586
+ async function apiSetupTestProvider(req, res, url) {
587
+ let body = '';
588
+ let bodyLen = 0;
589
+ req.on('data', c => { bodyLen += c.length; if (bodyLen > 8192) { req.destroy(); return; } body += c; });
590
+ req.on('end', async () => {
591
+ try {
592
+ const data = JSON.parse(body);
593
+ const type = _deps.sanitizeSetupProviderType(data.type);
594
+ const startMs = Date.now();
595
+ const rawBodyApiKey = typeof data.api_key === 'string' ? data.api_key : (typeof data.apiKey === 'string' ? data.apiKey : '');
596
+ const bodyApiKey = rawBodyApiKey
597
+ ? rawBodyApiKey.replace(/[\r\n\s]/g, '').slice(0, 500)
598
+ : '';
599
+ const rawBaseUrl = typeof data.base_url === 'string' ? data.base_url : (typeof data.baseUrl === 'string' ? data.baseUrl : '');
600
+ const bodyBaseUrl = rawBaseUrl.replace(/[\r\n]/g, '').trim().slice(0, 500);
601
+ const VALID_AUTH_METHODS = new Set(['api_key', 'claude_cli', 'oauth_proxy', 'codex_cli', 'gemini_cli', 'devbox']);
602
+ const bodyAuthMethod = typeof data.auth_method === 'string' && VALID_AUTH_METHODS.has(data.auth_method)
603
+ ? data.auth_method
604
+ : null;
605
+ const bodyCaCert = typeof data.caCert === 'string' ? data.caCert.trim() : ''; // R-2: Test threads caCert like Save does
606
+ const respondOwnerProviderTest = async (providerType) => {
607
+ try {
608
+ const models = await _deps.listWalleModelsViaOwner({ provider: providerType, apiKey: bodyApiKey, baseUrl: bodyBaseUrl, authMethod: bodyAuthMethod, caCert: bodyCaCert || undefined });
609
+ const latency = Date.now() - startMs;
610
+ if (models.length) {
611
+ res.writeHead(200, { 'Content-Type': 'application/json' });
612
+ res.end(JSON.stringify({ ok: true, model: _deps.firstModelLabel(models, providerType), latency_ms: latency }));
613
+ } else {
614
+ res.writeHead(200, { 'Content-Type': 'application/json' });
615
+ res.end(JSON.stringify({ ok: false, error: 'No accessible model found' }));
616
+ }
617
+ } catch (e) {
618
+ res.writeHead(200, { 'Content-Type': 'application/json' });
619
+ res.end(JSON.stringify(_deps.setupProviderAnswers.providerTestFailureBody(e)));
620
+ }
621
+ };
622
+
623
+ if (type === 'anthropic') {
624
+ const runtime = await _deps.getProviderRuntimeStateFromWalle('anthropic');
625
+ const authMethod = bodyAuthMethod
626
+ ? bodyAuthMethod
627
+ : runtime.authMethod || 'api_key';
628
+ if (authMethod === 'claude_cli') {
629
+ const account = await _deps.codingAgentAuth.probeAccount('claude-code');
630
+ const result = account.signedIn === true
631
+ ? {
632
+ ok: true,
633
+ model: [account.email, account.plan].filter(Boolean).join(' · ') || 'claude.ai',
634
+ reply: account.authMethod || 'subscription',
635
+ }
636
+ : { ok: false, error: _deps.claudeCliAccountError(account) };
637
+ const latency = Date.now() - startMs;
638
+ res.writeHead(200, { 'Content-Type': 'application/json' });
639
+ res.end(JSON.stringify({ ...result, latency_ms: latency, auth_method: authMethod }));
640
+ return;
641
+ }
642
+ if (authMethod === 'oauth_proxy') {
643
+ const result = await _deps.probeAnthropicOauthProxy();
644
+ const latency = Date.now() - startMs;
645
+ res.writeHead(200, { 'Content-Type': 'application/json' });
646
+ res.end(JSON.stringify({ ...result, latency_ms: latency, auth_method: authMethod }));
647
+ return;
648
+ }
649
+ await respondOwnerProviderTest('anthropic');
650
+ return;
651
+ }
652
+
653
+ if (type === 'ollama') {
654
+ // R-2: a remote/CA'd row routes via the owner-test path (honors baseUrl+caCert); default local keeps the raw probe below
655
+ if (_deps.shouldRouteOllamaTestViaOwner({ baseUrl: bodyBaseUrl, caCert: bodyCaCert })) {
656
+ await respondOwnerProviderTest('ollama');
657
+ return;
658
+ }
659
+ try {
660
+ const resp = await fetch('http://localhost:11434/api/tags', { signal: AbortSignal.timeout(5000) });
661
+ const latency = Date.now() - startMs;
662
+ if (resp.ok) {
663
+ const result = await resp.json();
664
+ const modelCount = result.models ? result.models.length : 0;
665
+ res.writeHead(200, { 'Content-Type': 'application/json' });
666
+ res.end(JSON.stringify({ ok: true, model: `${modelCount} model(s) available`, latency_ms: latency }));
667
+ } else {
668
+ res.writeHead(200, { 'Content-Type': 'application/json' });
669
+ res.end(JSON.stringify({ ok: false, error: `Ollama returned HTTP ${resp.status}` }));
670
+ }
671
+ } catch (e) {
672
+ res.writeHead(200, { 'Content-Type': 'application/json' });
673
+ res.end(JSON.stringify({ ok: false, error: 'Ollama not reachable at localhost:11434', diagnosis: 'Make sure Ollama is running (ollama serve)' }));
674
+ }
675
+ return;
676
+ }
677
+
678
+ if (_deps.isSetupProviderType(type)) {
679
+ await respondOwnerProviderTest(type);
680
+ return;
681
+ }
682
+
683
+ res.writeHead(400, { 'Content-Type': 'application/json' });
684
+ res.end(JSON.stringify({ error: 'Unknown provider type: ' + type }));
685
+ } catch (e) {
686
+ res.writeHead(400, { 'Content-Type': 'application/json' });
687
+ res.end(JSON.stringify({ error: e.message }));
688
+ }
689
+ });
690
+ return;
691
+ }
692
+
693
+ async function apiSetupSetDefault(req, res, url) {
694
+ let body = '';
695
+ let bodyLen = 0;
696
+ req.on('data', c => { bodyLen += c.length; if (bodyLen > 8192) { req.destroy(); return; } body += c; });
697
+ req.on('end', async () => {
698
+ try {
699
+ const data = JSON.parse(body);
700
+ const type = _deps.sanitizeSetupProviderType(data.type);
701
+ const currentDefault = await _deps.getWalleDefaultModelSelection();
702
+ const storedModel = currentDefault.model_provider === type ? currentDefault.model_id : '';
703
+ const selection = _deps.resolveSetupDefaultSelection({ type, model: data.model, storedModel });
704
+ if (!selection.ok) {
705
+ res.writeHead(400, { 'Content-Type': 'application/json' });
706
+ res.end(JSON.stringify({ error: selection.error }));
707
+ return;
708
+ }
709
+
710
+ const { requestedModel, targetModel } = selection;
711
+ const ownerWrite = await _deps.walleOwnerWrite('/api/wall-e/setup/default-provider', {
712
+ method: 'POST',
713
+ body: { type, requested_model: requestedModel, target_model: targetModel || '' },
714
+ });
715
+
716
+ _deps.setup.clearSetupCache();
717
+ const liveUpdated = ownerWrite.owner_write === 'wall-e-api';
718
+ res.writeHead(200, { 'Content-Type': 'application/json' });
719
+ res.end(JSON.stringify({ ok: true, live_updated: liveUpdated, restart_scheduled: false, owner_write: ownerWrite.owner_write }));
720
+ } catch (e) {
721
+ res.writeHead(400, { 'Content-Type': 'application/json' });
722
+ res.end(JSON.stringify({ error: e.message }));
723
+ }
724
+ });
725
+ return;
726
+ }
727
+
728
+ async function apiSetupCheckDir(req, res, url) {
729
+ let body = '', bodyLen = 0;
730
+ req.on('data', c => { bodyLen += c.length; if (bodyLen > 4096) { req.destroy(); return; } body += c; });
731
+ req.on('end', () => {
732
+ try {
733
+ const data = JSON.parse(body);
734
+ const dir = (data.dir || '').replace(/[\r\n]/g, '').trim();
735
+ if (!dir) { res.writeHead(400, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'No directory' })); return; }
736
+ const exists = _deps.fs.existsSync(dir);
737
+ const hasCtmDb = _deps.fs.existsSync(_deps.path.join(dir, 'task-manager.db'));
738
+ const hasBrainDb = _deps.fs.existsSync(_deps.path.join(dir, 'wall-e-brain.db'));
739
+ const hasBackups = _deps.fs.existsSync(_deps.path.join(dir, 'backups'));
740
+ res.writeHead(200, { 'Content-Type': 'application/json' });
741
+ res.end(JSON.stringify({ exists, has_ctm_db: hasCtmDb, has_brain_db: hasBrainDb, has_backups: hasBackups }));
742
+ } catch (e) { res.writeHead(400, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: e.message })); }
743
+ });
744
+ return;
745
+ }
746
+
747
+ async function apiSetupEmbeddings(req, res, url) {
748
+ try {
749
+ const upstream = await _deps.walleClient.requestJson('/api/wall-e/setup/embeddings');
750
+ res.writeHead(upstream.status || 200, { 'Content-Type': 'application/json' });
751
+ res.end(upstream.body || JSON.stringify({ providers: [], stats: {}, totalMemories: 0 }));
752
+ } catch (e) {
753
+ res.writeHead(200, { 'Content-Type': 'application/json' });
754
+ res.end(JSON.stringify({ providers: [], stats: {}, totalMemories: 0, error: e.message }));
755
+ }
756
+ return;
757
+ }
758
+
759
+ async function apiSetupEmbeddingsSwitch(req, res, url) {
760
+ _deps.readBody(req).then(async (body) => {
761
+ const upstream = await _deps.walleClient.requestJson('/api/wall-e/setup/embeddings/switch', { method: 'POST', body });
762
+ res.writeHead(upstream.status || 200, { 'Content-Type': 'application/json' });
763
+ res.end(upstream.body || JSON.stringify({ ok: false }));
764
+ }).catch((e) => {
765
+ res.writeHead(400, { 'Content-Type': 'application/json' });
766
+ res.end(JSON.stringify({ ok: false, message: e.message }));
767
+ });
768
+ return;
769
+ }
770
+
771
+ async function apiSetupEmbeddingsPolicy(req, res, url) {
772
+ _deps.readBody(req).then(async (body) => {
773
+ const upstream = await _deps.walleClient.requestJson('/api/wall-e/setup/embeddings/policy', { method: 'POST', body });
774
+ res.writeHead(upstream.status || 200, { 'Content-Type': 'application/json' });
775
+ res.end(upstream.body || JSON.stringify({ ok: false }));
776
+ }).catch((e) => {
777
+ res.writeHead(400, { 'Content-Type': 'application/json' });
778
+ res.end(JSON.stringify({ ok: false, message: e.message }));
779
+ });
780
+ return;
781
+ }
782
+
783
+ async function apiSetupEmbeddingsDelete(req, res, url) {
784
+ _deps.readBody(req).then(async (body) => {
785
+ const upstream = await _deps.walleClient.requestJson('/api/wall-e/setup/embeddings/delete', { method: 'POST', body });
786
+ res.writeHead(upstream.status || 200, { 'Content-Type': 'application/json' });
787
+ res.end(upstream.body || JSON.stringify({ ok: false }));
788
+ }).catch((e) => {
789
+ res.writeHead(400, { 'Content-Type': 'application/json' });
790
+ res.end(JSON.stringify({ ok: false, error: e.message }));
791
+ });
792
+ return;
793
+ }
794
+
795
+ async function apiSetupSaveDirs(req, res, url) {
796
+ let body = '', bodyLen = 0;
797
+ req.on('data', c => { bodyLen += c.length; if (bodyLen > 4096) { req.destroy(); return; } body += c; });
798
+ req.on('end', async () => {
799
+ try {
800
+ const data = JSON.parse(body);
801
+ const ctmDir = (data.ctm_data_dir || '').replace(/[\r\n]/g, '').trim();
802
+ const walleDir = (data.walle_data_dir || '').replace(/[\r\n]/g, '').trim();
803
+ const copyData = !!data.copy_data;
804
+ const envPath = _deps.resolveEnvFilePath({ repoRoot: _deps.path.resolve(_deps.__dirname, '..'), dataDir: _deps.CONFIG_DIR });
805
+ // Create directories
806
+ if (ctmDir) try { _deps.fs.mkdirSync(ctmDir, { recursive: true }); } catch {}
807
+ if (walleDir) try { _deps.fs.mkdirSync(walleDir, { recursive: true }); } catch {}
808
+ // Copy current databases if requested
809
+ if (copyData) {
810
+ const curCtmDir = require('../data-dir').getDataDir();
811
+ const curWalleDir = _deps.serverConfig.WALL_E_DATA_DIR || _deps.path.join(_deps.os.homedir(), '.walle', 'data');
812
+ if (ctmDir && ctmDir !== curCtmDir) {
813
+ const dst = _deps.path.join(ctmDir, 'task-manager.db');
814
+ if (!_deps.fs.existsSync(dst)) {
815
+ await _deps._drainCtmDbWorkers('setup-save-dirs-copy', 5000);
816
+ await _deps.dbModule.copyLiveDatabaseTo(dst);
817
+ }
818
+ }
819
+ if (walleDir && walleDir !== curWalleDir) {
820
+ const dst = _deps.path.join(walleDir, 'wall-e-brain.db');
821
+ if (!_deps.fs.existsSync(dst)) {
822
+ const backup = await _deps.createWalleBrainBackupForCopy();
823
+ _deps.fs.copyFileSync(backup.backupPath, dst);
824
+ }
825
+ }
826
+ }
827
+ const lines = [];
828
+ try {
829
+ const existing = _deps.fs.readFileSync(envPath, 'utf8');
830
+ for (const line of existing.split('\n')) {
831
+ const m = line.match(/^\s*#?\s*([A-Z0-9_]+)\s*=/);
832
+ if (m && (m[1] === 'CTM_DATA_DIR' || m[1] === 'WALL_E_DATA_DIR')) continue;
833
+ lines.push(line);
834
+ }
835
+ } catch { lines.push('# Wall-E configuration'); lines.push(''); }
836
+ while (lines.length > 0 && lines[lines.length - 1].trim() === '') lines.pop();
837
+ lines.push('');
838
+ if (ctmDir) lines.push('CTM_DATA_DIR=' + ctmDir);
839
+ if (walleDir) lines.push('WALL_E_DATA_DIR=' + walleDir);
840
+ _deps.atomicWriteFileSync(envPath, lines.join('\n') + '\n', { mode: 0o600 });
841
+ res.writeHead(200, { 'Content-Type': 'application/json' });
842
+ res.end(JSON.stringify({ ok: true }));
843
+ // Restart everything after a brief delay
844
+ setTimeout(async () => {
845
+ // Restart CTM itself. Exit with code 42 — launchd KeepAlive restarts
846
+ // on non-zero exit. No bash watcher needed (EDR flags detached kill-0
847
+ // loops as malware).
848
+ try {
849
+ _deps._ctmDbClosingForProcessExit = true; // nothing on the serving path may write past the door
850
+ const dbExit = await _deps._closeDbForProcessExit('data-dir-change'); _deps._exitForLaunchdRestart('data-dir-change', null, { dbClose: _deps._dbCloseOutcome(dbExit) }); // D-7/D-3
851
+ } catch (e) { // D-3/D-7: as in _scheduleTransportRestart -- a throwing close exits through the door with `post_failed`, never through the fatal/unhandledRejection path
852
+ console.error('[restart] data-dir-change db close threw: ' + e.message); _deps._exitForLaunchdRestart('data-dir-change', null, { dbClose: 'post_failed' });
853
+ }
854
+ }, 1000);
855
+ } catch (e) {
856
+ res.writeHead(400, { 'Content-Type': 'application/json' });
857
+ res.end(JSON.stringify({ error: e.message }));
858
+ }
859
+ });
860
+ return;
861
+ }
862
+ // WP-6 shard S15: the 17 verbatim bodies above are appended here by
863
+ // bin/move-only-check.js's proof (identical to server.js's declarations up to the `_deps.`
864
+ // prefix and the four rebased requires).
865
+ module.exports = {
866
+ configure,
867
+ apiSetupStatus,
868
+ apiSetupDetectCodexCli,
869
+ apiSetupDetectGeminiCli,
870
+ apiSetupDetectClaudeCli,
871
+ apiSetupOauthProxyStart,
872
+ apiSetupOauthProxyStop,
873
+ apiSetupOauthProxyStatus,
874
+ apiSetupProviders,
875
+ apiSetupSaveProvider,
876
+ apiSetupTestProvider,
877
+ apiSetupSetDefault,
878
+ apiSetupCheckDir,
879
+ apiSetupEmbeddings,
880
+ apiSetupEmbeddingsSwitch,
881
+ apiSetupEmbeddingsPolicy,
882
+ apiSetupEmbeddingsDelete,
883
+ apiSetupSaveDirs,
884
+ };