@vellumai/assistant 0.6.6 → 0.7.0

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 (1346) hide show
  1. package/AGENTS.md +20 -0
  2. package/ARCHITECTURE.md +45 -36
  3. package/Dockerfile +26 -6
  4. package/README.md +8 -10
  5. package/__tests__/permissions/gateway-threshold-reader.test.ts +14 -20
  6. package/bun.lock +306 -119
  7. package/docs/architecture/memory.md +1 -90
  8. package/docs/architecture/security.md +15 -30
  9. package/docs/credential-execution-service.md +7 -5
  10. package/docs/skills.md +10 -10
  11. package/docs/stt-provider-onboarding.md +17 -45
  12. package/examples/plugins/echo/bun.lock +25 -0
  13. package/knip.json +8 -22
  14. package/node_modules/@vellumai/ces-client/bun.lock +33 -0
  15. package/node_modules/@vellumai/ces-client/package.json +25 -0
  16. package/node_modules/@vellumai/ces-client/src/__tests__/ces-client.test.ts +631 -0
  17. package/node_modules/@vellumai/ces-client/src/__tests__/package-boundary.test.ts +138 -0
  18. package/node_modules/@vellumai/ces-client/src/credential-rpc.ts +13 -0
  19. package/node_modules/@vellumai/ces-client/src/http-credentials.ts +296 -0
  20. package/node_modules/@vellumai/ces-client/src/http-log-export.ts +111 -0
  21. package/node_modules/@vellumai/ces-client/src/index.ts +43 -0
  22. package/node_modules/@vellumai/ces-client/src/rpc-client.ts +445 -0
  23. package/node_modules/@vellumai/credential-storage/src/__tests__/package-boundary.test.ts +32 -6
  24. package/node_modules/@vellumai/egress-proxy/src/__tests__/package-boundary.test.ts +32 -1
  25. package/node_modules/@vellumai/gateway-client/bun.lock +39 -0
  26. package/node_modules/@vellumai/gateway-client/package.json +23 -0
  27. package/node_modules/@vellumai/gateway-client/src/__tests__/gateway-client.test.ts +343 -0
  28. package/node_modules/@vellumai/gateway-client/src/__tests__/package-boundary.test.ts +140 -0
  29. package/node_modules/@vellumai/gateway-client/src/http-delivery.ts +422 -0
  30. package/node_modules/@vellumai/gateway-client/src/index.ts +35 -0
  31. package/node_modules/@vellumai/gateway-client/src/ipc-client.ts +331 -0
  32. package/node_modules/@vellumai/gateway-client/src/types.ts +131 -0
  33. package/node_modules/@vellumai/gateway-client/tsconfig.json +20 -0
  34. package/node_modules/@vellumai/{ces-contracts → service-contracts}/bun.lock +1 -1
  35. package/node_modules/@vellumai/{ces-contracts → service-contracts}/package.json +4 -2
  36. package/node_modules/@vellumai/{ces-contracts → service-contracts}/src/__tests__/contracts.test.ts +5 -1
  37. package/node_modules/@vellumai/service-contracts/src/__tests__/package-boundary.test.ts +155 -0
  38. package/node_modules/@vellumai/service-contracts/src/credential-rpc.ts +23 -0
  39. package/node_modules/@vellumai/service-contracts/src/index.ts +25 -0
  40. package/node_modules/@vellumai/{ces-contracts/src/index.ts → service-contracts/src/transport.ts} +6 -28
  41. package/node_modules/@vellumai/service-contracts/src/trust-rules.ts +116 -0
  42. package/node_modules/@vellumai/service-contracts/tsconfig.json +20 -0
  43. package/node_modules/@vellumai/skill-host-contracts/__tests__/client.test.ts +891 -0
  44. package/node_modules/@vellumai/skill-host-contracts/bun.lock +24 -0
  45. package/node_modules/@vellumai/skill-host-contracts/package.json +18 -0
  46. package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +91 -0
  47. package/node_modules/@vellumai/skill-host-contracts/src/client.ts +1348 -0
  48. package/node_modules/@vellumai/skill-host-contracts/src/index.ts +6 -0
  49. package/node_modules/@vellumai/skill-host-contracts/src/runtime-mode.ts +11 -0
  50. package/node_modules/@vellumai/skill-host-contracts/src/server-message.ts +32 -0
  51. package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +333 -0
  52. package/node_modules/@vellumai/skill-host-contracts/src/tool-types.ts +444 -0
  53. package/node_modules/@vellumai/skill-host-contracts/tsconfig.json +20 -0
  54. package/node_modules/@vellumai/skill-host-contracts/tsconfig.test.json +12 -0
  55. package/openapi.yaml +2855 -556
  56. package/package.json +13 -7
  57. package/scripts/check-circular-deps.ts +80 -0
  58. package/scripts/generate-openapi.ts +24 -7
  59. package/{src/memory/graph/inspect.ts → scripts/memory-inspect.ts} +27 -27
  60. package/src/__tests__/access-request-decision.test.ts +2 -11
  61. package/src/__tests__/acp-session.test.ts +4 -150
  62. package/src/__tests__/actor-token-service.test.ts +17 -678
  63. package/src/__tests__/agent-loop-callsite-precedence.test.ts +2 -6
  64. package/src/__tests__/agent-loop-override-profile.test.ts +404 -0
  65. package/src/__tests__/agent-loop-thinking.test.ts +4 -4
  66. package/src/__tests__/agent-wake-override-profile.test.ts +261 -0
  67. package/src/__tests__/always-loaded-tools-guard.test.ts +2 -1
  68. package/src/__tests__/anthropic-provider.test.ts +127 -15
  69. package/src/__tests__/app-routes-csp.test.ts +106 -55
  70. package/src/__tests__/approval-cascade.test.ts +3 -355
  71. package/src/__tests__/approval-conversation-turn.test.ts +3 -8
  72. package/src/__tests__/approval-hardcoded-copy-guard.test.ts +1 -1
  73. package/src/__tests__/approval-primitive.test.ts +2 -1
  74. package/src/__tests__/approval-routes-http.test.ts +34 -451
  75. package/src/__tests__/assistant-events-sse-hardening.test.ts +73 -80
  76. package/src/__tests__/assistant-id-boundary-guard.test.ts +0 -3
  77. package/src/__tests__/attachment-upload-trusted-source.test.ts +139 -0
  78. package/src/__tests__/attachments-store.test.ts +46 -1
  79. package/src/__tests__/audit-log-rotation.test.ts +2 -1
  80. package/src/__tests__/auto-analysis-end-to-end.test.ts +8 -20
  81. package/src/__tests__/background-shell-bash.test.ts +227 -0
  82. package/src/__tests__/background-shell-host-bash.test.ts +474 -0
  83. package/src/__tests__/background-tool-registry.test.ts +145 -0
  84. package/src/__tests__/background-tool-routes.test.ts +175 -0
  85. package/src/__tests__/btw-routes.test.ts +147 -183
  86. package/src/__tests__/call-controller.test.ts +15 -2
  87. package/src/__tests__/call-conversation-messages.test.ts +2 -1
  88. package/src/__tests__/call-domain.test.ts +2 -2
  89. package/src/__tests__/call-pointer-messages.test.ts +11 -13
  90. package/src/__tests__/call-recovery.test.ts +2 -1
  91. package/src/__tests__/call-routes-http.test.ts +3 -14
  92. package/src/__tests__/call-store.test.ts +2 -1
  93. package/src/__tests__/cancel-resolves-conversation-key.test.ts +31 -62
  94. package/src/__tests__/canonical-guardian-store.test.ts +2 -2
  95. package/src/__tests__/catalog-files.test.ts +0 -26
  96. package/src/__tests__/ces-rpc-credential-backend.test.ts +1 -1
  97. package/src/__tests__/channel-approval-routes.test.ts +79 -49
  98. package/src/__tests__/channel-approval.test.ts +9 -7
  99. package/src/__tests__/channel-approvals.test.ts +9 -180
  100. package/src/__tests__/channel-delivery-store.test.ts +11 -10
  101. package/src/__tests__/channel-guardian.test.ts +14 -25
  102. package/src/__tests__/channel-readiness-service.test.ts +8 -6
  103. package/src/__tests__/channel-reply-delivery.test.ts +3 -19
  104. package/src/__tests__/channel-retry-sweep.test.ts +2 -5
  105. package/src/__tests__/checker.test.ts +274 -3921
  106. package/src/__tests__/circuit-breaker-pipeline.test.ts +1 -1
  107. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +208 -0
  108. package/src/__tests__/cli.test.ts +1 -38
  109. package/src/__tests__/compaction-events.test.ts +0 -1
  110. package/src/__tests__/compaction-pipeline.test.ts +1 -1
  111. package/src/__tests__/compaction-strip-metadata-clear.test.ts +2 -2
  112. package/src/__tests__/compaction-timeout-recovery.test.ts +1 -1
  113. package/src/__tests__/config-managed-gemini-defaults.test.ts +3 -7
  114. package/src/__tests__/config-model-image-provider.test.ts +0 -1
  115. package/src/__tests__/config-schema-cmd.test.ts +1 -1
  116. package/src/__tests__/config-schema.test.ts +30 -221
  117. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +4 -25
  118. package/src/__tests__/contact-store-user-file.test.ts +2 -1
  119. package/src/__tests__/contacts-tools.test.ts +56 -29
  120. package/src/__tests__/contacts-write.test.ts +6 -61
  121. package/src/__tests__/context-search-agent-protocol.test.ts +230 -0
  122. package/src/__tests__/context-search-agent-runner.test.ts +998 -0
  123. package/src/__tests__/context-search-conversations-source.test.ts +320 -0
  124. package/src/__tests__/context-search-fanout.test.ts +380 -0
  125. package/src/__tests__/context-search-memory-source.test.ts +311 -0
  126. package/src/__tests__/context-search-pkb-source.test.ts +444 -0
  127. package/src/__tests__/context-search-types.test.ts +95 -0
  128. package/src/__tests__/context-search-workspace-source.test.ts +545 -0
  129. package/src/__tests__/context-window-manager.test.ts +25 -0
  130. package/src/__tests__/conversation-abort-tool-results.test.ts +10 -1
  131. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +631 -0
  132. package/src/__tests__/conversation-agent-loop-overflow.test.ts +15 -2
  133. package/src/__tests__/conversation-agent-loop.test.ts +24 -2
  134. package/src/__tests__/conversation-analysis-routes.test.ts +60 -82
  135. package/src/__tests__/conversation-attachments.test.ts +9 -20
  136. package/src/__tests__/conversation-attention-store.test.ts +2 -1
  137. package/src/__tests__/conversation-attention-telegram.test.ts +4 -2
  138. package/src/__tests__/conversation-clear-safety.test.ts +53 -95
  139. package/src/__tests__/conversation-confirmation-signals.test.ts +1 -39
  140. package/src/__tests__/conversation-crud-inference-profile.test.ts +54 -0
  141. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +63 -157
  142. package/src/__tests__/conversation-disk-view-integration.test.ts +2 -2
  143. package/src/__tests__/conversation-disk-view.test.ts +5 -4
  144. package/src/__tests__/conversation-fork-crud.test.ts +26 -55
  145. package/src/__tests__/conversation-fork-route.test.ts +5 -74
  146. package/src/__tests__/conversation-inference-profile-list.test.ts +128 -0
  147. package/src/__tests__/conversation-inference-profile-route.test.ts +216 -0
  148. package/src/__tests__/conversation-init.benchmark.test.ts +4 -81
  149. package/src/__tests__/conversation-key-store-disk-view.test.ts +2 -1
  150. package/src/__tests__/conversation-lifecycle.test.ts +0 -1
  151. package/src/__tests__/conversation-list-source.test.ts +2 -2
  152. package/src/__tests__/conversation-load-history-repair.test.ts +0 -1
  153. package/src/__tests__/conversation-pairing.test.ts +0 -1
  154. package/src/__tests__/conversation-pre-run-repair.test.ts +137 -297
  155. package/src/__tests__/conversation-process-callsite.test.ts +0 -1
  156. package/src/__tests__/conversation-provider-retry-repair.test.ts +6 -1
  157. package/src/__tests__/conversation-queue.test.ts +1 -33
  158. package/src/__tests__/conversation-routes-disk-view.test.ts +124 -97
  159. package/src/__tests__/conversation-routes-guardian-reply.test.ts +80 -55
  160. package/src/__tests__/conversation-routes-slash-commands.test.ts +83 -12
  161. package/src/__tests__/conversation-runtime-assembly.test.ts +41 -84
  162. package/src/__tests__/conversation-slash-commands.test.ts +6 -43
  163. package/src/__tests__/conversation-slash-queue.test.ts +0 -1
  164. package/src/__tests__/conversation-slash-unknown.test.ts +0 -1
  165. package/src/__tests__/conversation-speed-override.test.ts +0 -1
  166. package/src/__tests__/conversation-starter-routes.test.ts +177 -55
  167. package/src/__tests__/conversation-starters-cadence.test.ts +2 -2
  168. package/src/__tests__/conversation-store.test.ts +2 -375
  169. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +1 -1
  170. package/src/__tests__/conversation-tool-setup-memory-scope.test.ts +6 -6
  171. package/src/__tests__/conversation-unread-route.test.ts +1 -1
  172. package/src/__tests__/conversation-usage.test.ts +2 -1
  173. package/src/__tests__/conversation-wipe.test.ts +2 -103
  174. package/src/__tests__/conversation-workspace-cache-state.test.ts +0 -1
  175. package/src/__tests__/conversation-workspace-injection.test.ts +0 -1
  176. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -1
  177. package/src/__tests__/conversations-defer-cli.test.ts +150 -0
  178. package/src/__tests__/credential-execution-admin-cli.test.ts +1 -1
  179. package/src/__tests__/credential-execution-api-key-propagation.test.ts +2 -2
  180. package/src/__tests__/credential-execution-approval-bridge.test.ts +22 -289
  181. package/src/__tests__/credential-execution-client.test.ts +1 -1
  182. package/src/__tests__/credential-execution-managed-contract.test.ts +1 -1
  183. package/src/__tests__/credential-security-invariants.test.ts +14 -0
  184. package/src/__tests__/credentials-cli.test.ts +45 -21
  185. package/src/__tests__/daemon-credential-client.test.ts +23 -108
  186. package/src/__tests__/db-acp-history.test.ts +284 -0
  187. package/src/__tests__/db-activation-state.test.ts +240 -0
  188. package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +2 -1
  189. package/src/__tests__/db-conversation-inference-profile-migration.test.ts +248 -0
  190. package/src/__tests__/db-llm-request-log-provider-migration.test.ts +2 -1
  191. package/src/__tests__/db-memory-graph-event-date-repair.test.ts +116 -0
  192. package/src/__tests__/db-rename-inference-profile-snake-case-migration.test.ts +132 -0
  193. package/src/__tests__/db-schedule-syntax-migration.test.ts +1 -0
  194. package/src/__tests__/delete-propagation.test.ts +3 -2
  195. package/src/__tests__/deterministic-verification-control-plane.test.ts +39 -32
  196. package/src/__tests__/dm-backfill.test.ts +3 -2
  197. package/src/__tests__/edit-propagation.test.ts +5 -7
  198. package/src/__tests__/embedding-managed-proxy-selection.test.ts +1 -1
  199. package/src/__tests__/empty-response-pipeline.test.ts +1 -1
  200. package/src/__tests__/events-client-registration.test.ts +297 -0
  201. package/src/__tests__/file-write-tool.test.ts +2 -4
  202. package/src/__tests__/filing-service.test.ts +144 -17
  203. package/src/__tests__/followup-tools.test.ts +2 -1
  204. package/src/__tests__/gateway-client-managed-outbound.test.ts +8 -12
  205. package/src/__tests__/gateway-only-enforcement.test.ts +2 -6
  206. package/src/__tests__/gateway-only-guard.test.ts +4 -3
  207. package/src/__tests__/gemini-provider.test.ts +276 -10
  208. package/src/__tests__/graph-extraction-event-date.test.ts +30 -0
  209. package/src/__tests__/guardian-action-conversation-turn.test.ts +2 -1
  210. package/src/__tests__/guardian-action-followup-executor.test.ts +2 -2
  211. package/src/__tests__/guardian-action-followup-store.test.ts +2 -1
  212. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +9 -9
  213. package/src/__tests__/guardian-action-late-reply.test.ts +2 -1
  214. package/src/__tests__/guardian-action-store.test.ts +2 -1
  215. package/src/__tests__/guardian-action-sweep.test.ts +9 -8
  216. package/src/__tests__/guardian-binding-drift-heal.test.ts +2 -1
  217. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +21 -118
  218. package/src/__tests__/guardian-dispatch.test.ts +14 -11
  219. package/src/__tests__/guardian-grant-minting.test.ts +9 -15
  220. package/src/__tests__/guardian-outbound-http.test.ts +71 -106
  221. package/src/__tests__/guardian-principal-id-roundtrip.test.ts +2 -2
  222. package/src/__tests__/guardian-routing-invariants.test.ts +34 -90
  223. package/src/__tests__/guardian-routing-state.test.ts +14 -22
  224. package/src/__tests__/guardian-verification-voice-binding.test.ts +1 -2
  225. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +253 -0
  226. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +8 -4
  227. package/src/__tests__/heartbeat-service.test.ts +39 -21
  228. package/src/__tests__/helpers/call-route-handler.ts +72 -0
  229. package/src/__tests__/helpers/channel-test-adapter.ts +161 -0
  230. package/src/__tests__/helpers/gateway-classify-mock.ts +67 -0
  231. package/src/__tests__/helpers/mock-logger.ts +36 -0
  232. package/src/__tests__/history-repair-pipeline.test.ts +1 -1
  233. package/src/__tests__/home-state-routes.test.ts +10 -31
  234. package/src/__tests__/host-browser-e2e-cloud.test.ts +2 -1
  235. package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +12 -2
  236. package/src/__tests__/host-browser-routes.test.ts +36 -91
  237. package/src/__tests__/host-browser-ws-events-e2e.test.ts +10 -2
  238. package/src/__tests__/host-proxy-interface.test.ts +3 -3
  239. package/src/__tests__/host-shell-tool.test.ts +2 -4
  240. package/src/__tests__/host-transfer-pending-interactions.test.ts +160 -0
  241. package/src/__tests__/host-transfer-proxy.test.ts +733 -0
  242. package/src/__tests__/http-conversation-lineage.test.ts +3 -2
  243. package/src/__tests__/http-user-message-parity.test.ts +20 -11
  244. package/src/__tests__/inbound-invite-redemption.test.ts +3 -2
  245. package/src/__tests__/injector-chain.test.ts +16 -17
  246. package/src/__tests__/inline-skill-load-permissions.test.ts +41 -206
  247. package/src/__tests__/install-skill-routing.test.ts +1 -1
  248. package/src/__tests__/invite-redemption-service.test.ts +2 -1
  249. package/src/__tests__/invite-routes-http.test.ts +80 -12
  250. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +2 -1
  251. package/src/__tests__/jobs-store-upsert-debounced.test.ts +2 -1
  252. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +157 -0
  253. package/src/__tests__/list-messages-attachments.test.ts +52 -55
  254. package/src/__tests__/list-messages-page-latest.test.ts +283 -0
  255. package/src/__tests__/list-messages-tool-merge.test.ts +16 -17
  256. package/src/__tests__/llm-call-pipeline.test.ts +7 -8
  257. package/src/__tests__/llm-context-normalization.test.ts +69 -4
  258. package/src/__tests__/llm-context-route-provider.test.ts +39 -113
  259. package/src/__tests__/llm-request-log-turn-query.test.ts +2 -1
  260. package/src/__tests__/llm-resolver.test.ts +211 -0
  261. package/src/__tests__/llm-schema.test.ts +57 -1
  262. package/src/__tests__/llm-usage-store.test.ts +2 -1
  263. package/src/__tests__/log-export-workspace.test.ts +28 -17
  264. package/src/__tests__/mcp-abort-signal.test.ts +2 -3
  265. package/src/__tests__/mcp-client-auth.test.ts +2 -3
  266. package/src/__tests__/memory-admin-recall.test.ts +221 -0
  267. package/src/__tests__/memory-recall-log-store.test.ts +2 -1
  268. package/src/__tests__/memory-retrieval-pipeline.test.ts +6 -8
  269. package/src/__tests__/memory-upsert-concurrency.test.ts +2 -1
  270. package/src/__tests__/migration-cross-version-compatibility.test.ts +14 -13
  271. package/src/__tests__/migration-export-http.test.ts +17 -17
  272. package/src/__tests__/migration-export-to-gcs.test.ts +491 -0
  273. package/src/__tests__/migration-import-commit-http.test.ts +16 -16
  274. package/src/__tests__/migration-import-from-gcs.test.ts +533 -0
  275. package/src/__tests__/migration-import-from-url.test.ts +16 -23
  276. package/src/__tests__/migration-import-preflight-http.test.ts +13 -13
  277. package/src/__tests__/migration-jobs-status.test.ts +164 -0
  278. package/src/__tests__/migration-validate-http.test.ts +48 -83
  279. package/src/__tests__/mock-gateway-ipc.ts +32 -62
  280. package/src/__tests__/model-intents.test.ts +15 -2
  281. package/src/__tests__/nl-approval-parser.test.ts +13 -17
  282. package/src/__tests__/non-member-access-request.test.ts +13 -5
  283. package/src/__tests__/notification-guardian-path.test.ts +15 -8
  284. package/src/__tests__/notification-schedule-notify-dedup.test.ts +2 -1
  285. package/src/__tests__/notification-telegram-adapter.test.ts +57 -55
  286. package/src/__tests__/oauth-apps-routes.test.ts +76 -122
  287. package/src/__tests__/oauth-cli.test.ts +14 -1
  288. package/src/__tests__/oauth-provider-profiles.test.ts +1 -1
  289. package/src/__tests__/oauth-provider-visibility.test.ts +3 -1
  290. package/src/__tests__/oauth-providers-routes.test.ts +78 -101
  291. package/src/__tests__/oauth-store.test.ts +3 -1
  292. package/src/__tests__/oauth2-gateway-transport.test.ts +6 -3
  293. package/src/__tests__/openai-provider.test.ts +105 -6
  294. package/src/__tests__/openai-responses-provider.test.ts +146 -4
  295. package/src/__tests__/openrouter-provider-only.test.ts +22 -4
  296. package/src/__tests__/overflow-reduce-pipeline.test.ts +4 -9
  297. package/src/__tests__/permission-types.test.ts +3 -18
  298. package/src/__tests__/persistence-pipeline.test.ts +3 -2
  299. package/src/__tests__/pipeline-runner.test.ts +1 -1
  300. package/src/__tests__/platform-bash-auto-approve.test.ts +27 -20
  301. package/src/__tests__/platform.test.ts +11 -63
  302. package/src/__tests__/playbook-execution.test.ts +2 -1
  303. package/src/__tests__/playbook-tools.test.ts +2 -1
  304. package/src/__tests__/plugin-bootstrap.test.ts +51 -5
  305. package/src/__tests__/plugin-registry.test.ts +30 -0
  306. package/src/__tests__/plugin-route-contribution.test.ts +17 -11
  307. package/src/__tests__/plugin-skill-contribution.test.ts +3 -3
  308. package/src/__tests__/plugin-tool-contribution.test.ts +10 -4
  309. package/src/__tests__/plugin-types.test.ts +1 -1
  310. package/src/__tests__/pricing.test.ts +151 -2
  311. package/src/__tests__/profiler-routes.test.ts +112 -177
  312. package/src/__tests__/provider-send-message-override-profile.test.ts +223 -0
  313. package/src/__tests__/proxy-approval-callback.test.ts +6 -554
  314. package/src/__tests__/qdrant-collection-migration.test.ts +7 -7
  315. package/src/__tests__/reaction-persistence.test.ts +3 -2
  316. package/src/__tests__/rebuild-index-graph-nodes.test.ts +1 -1
  317. package/src/__tests__/recording-handler.test.ts +0 -2
  318. package/src/__tests__/registry.test.ts +1 -0
  319. package/src/__tests__/relay-server.test.ts +19 -4
  320. package/src/__tests__/require-fresh-approval.test.ts +19 -168
  321. package/src/__tests__/resolve-trust-class.test.ts +2 -1
  322. package/src/__tests__/retry-thinking-tool-choice.test.ts +19 -7
  323. package/src/__tests__/retry-verbosity-normalization.test.ts +139 -0
  324. package/src/__tests__/runtime-attachment-metadata.test.ts +26 -6
  325. package/src/__tests__/runtime-events-sse-parity.test.ts +12 -13
  326. package/src/__tests__/runtime-events-sse.test.ts +13 -21
  327. package/src/__tests__/schedule-routes.test.ts +226 -129
  328. package/src/__tests__/schedule-store.test.ts +119 -1
  329. package/src/__tests__/schedule-tools.test.ts +2 -1
  330. package/src/__tests__/scheduler-recurrence.test.ts +2 -1
  331. package/src/__tests__/scheduler-reuse-conversation.test.ts +2 -1
  332. package/src/__tests__/scheduler-wake.test.ts +356 -0
  333. package/src/__tests__/scoped-approval-grants.test.ts +2 -1
  334. package/src/__tests__/scoped-grant-security-matrix.test.ts +2 -1
  335. package/src/__tests__/secret-detection-handler.test.ts +2 -9
  336. package/src/__tests__/secret-ingress-http.test.ts +38 -21
  337. package/src/__tests__/secret-routes-managed-proxy.test.ts +46 -102
  338. package/src/__tests__/secret-scanner-executor.test.ts +1 -2
  339. package/src/__tests__/send-endpoint-busy.test.ts +38 -25
  340. package/src/__tests__/sequence-store.test.ts +2 -1
  341. package/src/__tests__/server-history-render.test.ts +2 -2
  342. package/src/__tests__/service-contracts-import-guard.test.ts +185 -0
  343. package/src/__tests__/set-permission-mode.test.ts +0 -10
  344. package/src/__tests__/settings-routes.test.ts +35 -68
  345. package/src/__tests__/skill-boundary-guard.test.ts +105 -0
  346. package/src/__tests__/skill-load-inline-command.test.ts +2 -2
  347. package/src/__tests__/skill-load-inline-includes.test.ts +2 -2
  348. package/src/__tests__/skill-runtime-path.test.ts +64 -0
  349. package/src/__tests__/skills-file-content-endpoint.test.ts +0 -2
  350. package/src/__tests__/slack-inbound-verification.test.ts +11 -2
  351. package/src/__tests__/slack-messaging-token-resolution.test.ts +1 -3
  352. package/src/__tests__/slack-reaction-approvals.test.ts +4 -4
  353. package/src/__tests__/slack-share-routes.test.ts +37 -72
  354. package/src/__tests__/subagent-call-site-routing.test.ts +79 -0
  355. package/src/__tests__/subagent-fork-spawn.test.ts +20 -28
  356. package/src/__tests__/subagent-notify-parent.test.ts +6 -29
  357. package/src/__tests__/subagent-role-registry.test.ts +3 -3
  358. package/src/__tests__/subagent-spawn-tool-fork.test.ts +52 -104
  359. package/src/__tests__/subagent-tools.test.ts +0 -1
  360. package/src/__tests__/suggestion-routes.test.ts +55 -62
  361. package/src/__tests__/task-compiler.test.ts +2 -1
  362. package/src/__tests__/task-management-tools.test.ts +2 -1
  363. package/src/__tests__/task-memory-cleanup.test.ts +2 -1
  364. package/src/__tests__/task-scheduler.test.ts +2 -1
  365. package/src/__tests__/telegram-config.test.ts +0 -1
  366. package/src/__tests__/terminal-tools.test.ts +5 -314
  367. package/src/__tests__/test-preload.ts +0 -11
  368. package/src/__tests__/thread-backfill.test.ts +3 -2
  369. package/src/__tests__/token-estimate-pipeline.test.ts +68 -15
  370. package/src/__tests__/tool-approval-handler.test.ts +21 -63
  371. package/src/__tests__/tool-audit-listener.test.ts +3 -3
  372. package/src/__tests__/tool-domain-event-publisher.test.ts +3 -3
  373. package/src/__tests__/tool-error-pipeline.test.ts +6 -6
  374. package/src/__tests__/tool-execute-pipeline.test.ts +6 -8
  375. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +64 -1
  376. package/src/__tests__/tool-executor-lifecycle-events.test.ts +28 -56
  377. package/src/__tests__/tool-executor.test.ts +322 -1633
  378. package/src/__tests__/tool-grant-request-escalation.test.ts +90 -311
  379. package/src/__tests__/tool-result-truncate-pipeline.test.ts +1 -1
  380. package/src/__tests__/trust-context-guards.test.ts +1 -1
  381. package/src/__tests__/trusted-contact-approval-notifier.test.ts +7 -15
  382. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +178 -354
  383. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +3 -2
  384. package/src/__tests__/trusted-contact-multichannel.test.ts +3 -2
  385. package/src/__tests__/trusted-contact-verification.test.ts +2 -1
  386. package/src/__tests__/turn-boundary-resolution.test.ts +2 -1
  387. package/src/__tests__/twilio-routes.test.ts +25 -66
  388. package/src/__tests__/usage-cache-backfill-migration.test.ts +3 -7
  389. package/src/__tests__/usage-routes.test.ts +73 -90
  390. package/src/__tests__/user-plugin-loader.test.ts +54 -12
  391. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +2 -2
  392. package/src/__tests__/verification-control-plane-policy.test.ts +95 -14
  393. package/src/__tests__/voice-ingress-preflight.test.ts +5 -5
  394. package/src/__tests__/voice-invite-redemption.test.ts +2 -1
  395. package/src/__tests__/voice-scoped-grant-consumer.test.ts +3 -3
  396. package/src/__tests__/voice-session-bridge.test.ts +285 -106
  397. package/src/__tests__/volume-security-guard.test.ts +0 -2
  398. package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +2 -1
  399. package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +3 -1
  400. package/src/__tests__/workspace-migration-028-recover-conversations-from-disk-view.test.ts +2 -1
  401. package/src/__tests__/workspace-migration-045-release-notes-meet-avatar.test.ts +1 -1
  402. package/src/__tests__/workspace-migration-052-seed-default-inference-profiles.test.ts +260 -0
  403. package/src/__tests__/workspace-migration-053-release-notes-acp-codex.test.ts +225 -0
  404. package/src/__tests__/workspace-migration-054-seed-recall-callsite.test.ts +235 -0
  405. package/src/__tests__/workspace-migration-055-release-notes-agentic-recall.test.ts +128 -0
  406. package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +232 -0
  407. package/src/__tests__/workspace-migration-acp-sessions-ui.test.ts +144 -0
  408. package/src/__tests__/workspace-migration-drop-user-md.test.ts +1 -1
  409. package/src/__tests__/workspace-migration-memory-v2-init.test.ts +274 -0
  410. package/src/acp/__tests__/client-handler.test.ts +64 -0
  411. package/src/acp/__tests__/helpers/acp-config-stub.ts +62 -0
  412. package/src/acp/__tests__/helpers/which-stub.ts +45 -0
  413. package/src/acp/__tests__/session-manager-persistence.test.ts +366 -0
  414. package/src/acp/__tests__/session-manager-startup.test.ts +159 -0
  415. package/src/acp/__tests__/session-manager.test.ts +83 -0
  416. package/src/acp/client-handler.ts +23 -139
  417. package/src/acp/resolve-agent.test.ts +291 -0
  418. package/src/acp/resolve-agent.ts +176 -0
  419. package/src/acp/session-manager.ts +166 -7
  420. package/src/acp/types.ts +2 -50
  421. package/src/agent/loop.ts +37 -14
  422. package/src/agent/message-types.ts +0 -2
  423. package/src/approvals/AGENTS.md +1 -1
  424. package/src/approvals/__tests__/guardian-feed-event.test.ts +1 -9
  425. package/src/approvals/approval-primitive.ts +3 -20
  426. package/src/approvals/guardian-decision-primitive.ts +37 -68
  427. package/src/approvals/guardian-request-resolvers.ts +29 -103
  428. package/src/avatar/character-components.ts +6 -6
  429. package/src/{config/bundled-skills/settings/tools → avatar}/identity-avatar.ts +1 -1
  430. package/src/backup/__tests__/backup-worker.test.ts +0 -2
  431. package/src/backup/__tests__/paths.test.ts +3 -2
  432. package/src/backup/backup-worker.ts +1 -10
  433. package/src/backup/paths.ts +2 -18
  434. package/src/backup/restore.ts +7 -11
  435. package/src/browser/__tests__/operations.test.ts +0 -35
  436. package/src/browser/operations.ts +1 -47
  437. package/src/bundler/package-resolver.ts +2 -6
  438. package/src/calls/active-call-lease.ts +1 -1
  439. package/src/calls/call-constants.ts +1 -1
  440. package/src/calls/call-controller.ts +1 -5
  441. package/src/calls/call-domain.ts +14 -14
  442. package/src/calls/call-pointer-messages.ts +4 -9
  443. package/src/calls/call-store.ts +2 -1
  444. package/src/calls/guardian-action-sweep.ts +9 -25
  445. package/src/calls/guardian-dispatch.ts +1 -20
  446. package/src/calls/media-stream-audio-transcode.ts +2 -41
  447. package/src/calls/media-stream-server.ts +2 -3
  448. package/src/calls/media-stream-stt-session.ts +1 -3
  449. package/src/calls/relay-access-wait.ts +5 -8
  450. package/src/calls/relay-server.ts +15 -18
  451. package/src/calls/relay-setup-router.ts +2 -2
  452. package/src/calls/relay-verification.ts +4 -4
  453. package/src/calls/twilio-rest.ts +1 -1
  454. package/src/calls/twilio-routes.ts +160 -78
  455. package/src/calls/voice-control-protocol.ts +10 -10
  456. package/src/calls/voice-ingress-preflight.ts +2 -2
  457. package/src/calls/voice-session-bridge.ts +137 -42
  458. package/src/channels/__tests__/types.test.ts +25 -3
  459. package/src/channels/permission-profiles.ts +2 -72
  460. package/src/channels/types.ts +42 -26
  461. package/src/cli/AGENTS.md +1 -0
  462. package/src/cli/__tests__/notifications.test.ts +12 -10
  463. package/src/cli/commands/__tests__/attachment.test.ts +14 -8
  464. package/src/cli/commands/__tests__/backup.test.ts +3 -14
  465. package/src/cli/commands/__tests__/browser.test.ts +36 -31
  466. package/src/cli/commands/__tests__/cache.test.ts +23 -18
  467. package/src/cli/commands/__tests__/memory-v2.test.ts +396 -0
  468. package/src/cli/commands/__tests__/task.test.ts +36 -35
  469. package/src/cli/commands/__tests__/trust.test.ts +602 -0
  470. package/src/cli/commands/__tests__/ui-confirm.test.ts +14 -14
  471. package/src/cli/commands/__tests__/ui.test.ts +17 -17
  472. package/src/cli/commands/__tests__/watchers.test.ts +29 -29
  473. package/src/cli/commands/__tests__/webhooks.test.ts +544 -0
  474. package/src/cli/commands/attachment.ts +12 -8
  475. package/src/cli/commands/auth.ts +1 -1
  476. package/src/cli/commands/avatar.ts +192 -9
  477. package/src/cli/commands/backup.ts +14 -44
  478. package/src/cli/commands/browser.ts +52 -4
  479. package/src/cli/commands/cache.ts +7 -5
  480. package/src/cli/commands/channel-verification-sessions.ts +6 -6
  481. package/src/cli/commands/clients.ts +11 -12
  482. package/src/cli/commands/completions.ts +1 -1
  483. package/src/cli/commands/contacts.ts +10 -10
  484. package/src/cli/commands/conversations-defer.ts +364 -0
  485. package/src/cli/commands/conversations-import.ts +2 -3
  486. package/src/cli/commands/conversations.ts +63 -53
  487. package/src/cli/commands/credential-execution.ts +1 -1
  488. package/src/cli/commands/credentials.ts +139 -5
  489. package/src/cli/commands/default-action.ts +1 -1
  490. package/src/cli/commands/domain.ts +2 -2
  491. package/src/cli/commands/email.ts +7 -7
  492. package/src/cli/commands/image-generation.ts +1 -1
  493. package/src/cli/commands/keys.ts +2 -2
  494. package/src/cli/commands/mcp.ts +1 -1
  495. package/src/cli/commands/memory-v2.ts +343 -0
  496. package/src/cli/commands/memory.ts +8 -8
  497. package/src/cli/commands/notifications.ts +21 -20
  498. package/src/cli/commands/oauth/__tests__/connect.test.ts +23 -5
  499. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +1 -1
  500. package/src/cli/commands/oauth/__tests__/mode.test.ts +1 -1
  501. package/src/cli/commands/oauth/__tests__/status.test.ts +1 -1
  502. package/src/cli/commands/oauth/__tests__/token.test.ts +1 -1
  503. package/src/cli/commands/oauth/connect.ts +2 -2
  504. package/src/cli/commands/oauth/shared.ts +29 -2
  505. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -6
  506. package/src/cli/commands/platform/__tests__/connect.test.ts +23 -11
  507. package/src/cli/commands/platform/__tests__/disconnect.test.ts +22 -10
  508. package/src/cli/commands/platform/__tests__/status.test.ts +22 -10
  509. package/src/cli/commands/platform/connect.ts +3 -3
  510. package/src/cli/commands/platform/disconnect.ts +4 -6
  511. package/src/cli/commands/platform/index.ts +12 -10
  512. package/src/cli/commands/routes.ts +7 -1
  513. package/src/cli/commands/sequence.ts +7 -7
  514. package/src/cli/commands/skills.ts +188 -82
  515. package/src/cli/commands/task.ts +12 -10
  516. package/src/cli/commands/trust.ts +460 -162
  517. package/src/cli/commands/ui.ts +3 -3
  518. package/src/cli/commands/usage.ts +10 -5
  519. package/src/cli/commands/watchers.ts +8 -8
  520. package/src/cli/commands/webhooks.ts +270 -0
  521. package/src/cli/lib/daemon-avatar-client.ts +37 -0
  522. package/src/cli/lib/daemon-credential-client.ts +27 -189
  523. package/src/cli/lib/ipc-params.ts +22 -0
  524. package/src/cli/program.ts +4 -0
  525. package/src/cli.ts +1 -61
  526. package/src/config/acp-defaults.test.ts +57 -0
  527. package/src/config/acp-defaults.ts +40 -0
  528. package/src/config/acp-schema.ts +1 -1
  529. package/src/config/assistant-feature-flags.ts +18 -142
  530. package/src/config/bundled-skills/acp/SKILL.md +44 -16
  531. package/src/config/bundled-skills/acp/TOOLS.json +45 -1
  532. package/src/config/bundled-skills/acp/tools/acp-list-agents.ts +12 -0
  533. package/src/config/bundled-skills/acp/tools/acp-steer.ts +12 -0
  534. package/src/config/bundled-skills/contacts/tools/contact-merge.ts +14 -14
  535. package/src/config/bundled-skills/contacts/tools/contact-search.ts +1 -4
  536. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +11 -6
  537. package/src/config/bundled-skills/media-processing/__tests__/cost-tracker.test.ts +6 -6
  538. package/src/config/bundled-skills/media-processing/services/reduce.ts +0 -13
  539. package/src/config/bundled-skills/messaging/tools/gmail-mime-helpers.ts +1 -1
  540. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +1 -1
  541. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +1 -1
  542. package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +1 -1
  543. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +1 -1
  544. package/src/config/bundled-skills/settings/SKILL.md +2 -17
  545. package/src/config/bundled-skills/settings/TOOLS.json +0 -56
  546. package/src/config/bundled-skills/subagent/SKILL.md +2 -0
  547. package/src/config/bundled-tool-registry.ts +4 -6
  548. package/src/config/env.ts +7 -8
  549. package/src/config/feature-flag-registry.json +16 -24
  550. package/src/config/llm-resolver.ts +51 -33
  551. package/src/config/loader.ts +12 -15
  552. package/src/config/schema.ts +5 -72
  553. package/src/config/schemas/__tests__/filing.test.ts +58 -0
  554. package/src/config/schemas/__tests__/memory-v2.test.ts +186 -0
  555. package/src/config/schemas/filing.ts +12 -0
  556. package/src/config/schemas/host-browser.ts +2 -2
  557. package/src/config/schemas/inference.ts +0 -2
  558. package/src/config/schemas/ingress.ts +1 -1
  559. package/src/config/schemas/llm.ts +51 -9
  560. package/src/config/schemas/memory-storage.ts +1 -1
  561. package/src/config/schemas/memory-v2.ts +176 -0
  562. package/src/config/schemas/memory.ts +2 -0
  563. package/src/config/schemas/security.ts +0 -60
  564. package/src/config/schemas/services.ts +46 -7
  565. package/src/config/skills.ts +1 -1
  566. package/src/config/types.ts +0 -41
  567. package/src/contacts/contact-store.ts +2 -2
  568. package/src/contacts/contacts-write.ts +0 -38
  569. package/src/contacts/types.ts +8 -10
  570. package/src/context/token-estimator.ts +1 -1
  571. package/src/context/tool-result-truncation.ts +1 -1
  572. package/src/context/window-manager.ts +1 -1
  573. package/src/credential-execution/approval-bridge.ts +7 -69
  574. package/src/credential-execution/client.ts +17 -422
  575. package/src/credential-execution/feature-gates.ts +1 -2
  576. package/src/credential-execution/managed-catalog.ts +1 -1
  577. package/src/credential-health/credential-health-service.ts +1 -1
  578. package/src/daemon/__tests__/conversation-feed-event.test.ts +0 -13
  579. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +1 -1
  580. package/src/daemon/__tests__/daemon-skill-host.test.ts +272 -0
  581. package/src/daemon/__tests__/meet-host-supervisor.test.ts +587 -0
  582. package/src/daemon/__tests__/meet-manifest-loader.test.ts +463 -0
  583. package/src/daemon/approval-generators.ts +2 -14
  584. package/src/daemon/classifier.ts +0 -106
  585. package/src/daemon/config-watcher.ts +14 -54
  586. package/src/daemon/connection-policy.ts +0 -14
  587. package/src/daemon/conversation-agent-loop-handlers.ts +37 -6
  588. package/src/daemon/conversation-agent-loop.ts +164 -53
  589. package/src/daemon/conversation-attachments.ts +5 -81
  590. package/src/daemon/conversation-error.ts +9 -5
  591. package/src/daemon/conversation-history.ts +1 -1
  592. package/src/daemon/conversation-launch.ts +1 -1
  593. package/src/daemon/conversation-messaging.ts +1 -1
  594. package/src/daemon/conversation-notifiers.ts +1 -1
  595. package/src/daemon/conversation-process.ts +9 -13
  596. package/src/daemon/conversation-runtime-assembly.ts +26 -88
  597. package/src/daemon/conversation-slash.ts +4 -160
  598. package/src/daemon/conversation-store.ts +368 -0
  599. package/src/daemon/conversation-surfaces.ts +5 -4
  600. package/src/daemon/conversation-tool-setup.ts +23 -172
  601. package/src/daemon/conversation.ts +46 -182
  602. package/src/daemon/daemon-control.ts +3 -3
  603. package/src/daemon/daemon-skill-host.ts +262 -0
  604. package/src/daemon/external-plugins-bootstrap.ts +67 -13
  605. package/src/daemon/handlers/config-channels.ts +2 -2
  606. package/src/daemon/handlers/config-embeddings.ts +1 -1
  607. package/src/daemon/handlers/config-ingress.ts +24 -2
  608. package/src/daemon/handlers/config-model.test.ts +17 -0
  609. package/src/daemon/handlers/config-model.ts +7 -52
  610. package/src/daemon/handlers/config-telegram.ts +6 -53
  611. package/src/daemon/handlers/config-voice.ts +1 -1
  612. package/src/daemon/handlers/conversations.ts +22 -156
  613. package/src/daemon/handlers/recording.ts +1 -1
  614. package/src/daemon/handlers/shared.ts +34 -35
  615. package/src/daemon/handlers/skills.ts +15 -23
  616. package/src/daemon/host-transfer-proxy.ts +500 -0
  617. package/src/daemon/lifecycle.ts +23 -258
  618. package/src/daemon/meet-host-startup.ts +51 -0
  619. package/src/daemon/meet-host-supervisor.ts +781 -0
  620. package/src/daemon/meet-manifest-loader.ts +410 -0
  621. package/src/daemon/memory-v2-startup.ts +35 -0
  622. package/src/daemon/message-protocol.ts +4 -7
  623. package/src/daemon/message-types/acp.ts +1 -0
  624. package/src/daemon/message-types/conversations.ts +16 -2
  625. package/src/daemon/message-types/host-transfer.ts +41 -0
  626. package/src/daemon/message-types/integrations.ts +6 -0
  627. package/src/daemon/message-types/messages.ts +14 -14
  628. package/src/daemon/message-types/schedules.ts +1 -0
  629. package/src/daemon/message-types/settings.ts +0 -6
  630. package/src/daemon/message-types/shared.ts +5 -2
  631. package/src/daemon/message-types/subagents.ts +2 -1
  632. package/src/daemon/message-types/workspace.ts +0 -2
  633. package/src/daemon/pkb-reminder-builder.test.ts +13 -12
  634. package/src/daemon/pkb-reminder-builder.ts +8 -16
  635. package/src/daemon/process-message.ts +616 -0
  636. package/src/daemon/providers-setup.ts +14 -6
  637. package/src/daemon/server.ts +79 -1274
  638. package/src/daemon/shutdown-handlers.ts +1 -1
  639. package/src/daemon/startup-error.ts +1 -1
  640. package/src/daemon/trust-context.ts +32 -0
  641. package/src/daemon/wake-target-adapter.ts +223 -0
  642. package/src/email/feature-gate.ts +1 -1
  643. package/src/events/domain-events.ts +1 -8
  644. package/src/events/tool-audit-listener.ts +2 -8
  645. package/src/events/tool-metrics-listener.ts +1 -4
  646. package/src/filing/filing-service.ts +194 -54
  647. package/src/followups/followup-store.ts +3 -71
  648. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +89 -21
  649. package/src/heartbeat/heartbeat-service.ts +32 -11
  650. package/src/home/__tests__/phase5-exit-criteria.test.ts +18 -1
  651. package/src/home/__tests__/rollup-producer.test.ts +67 -2
  652. package/src/home/assistant-feed-authoring.ts +8 -1
  653. package/src/home/feed-types.ts +1 -1
  654. package/src/home/relationship-state-writer.ts +1 -1
  655. package/src/home/rewrite-feed-title.ts +58 -0
  656. package/src/home/rollup-producer.ts +16 -3
  657. package/src/inbound/platform-callback-registration.ts +1 -17
  658. package/src/ipc/__tests__/attachment-ipc.test.ts +128 -66
  659. package/src/ipc/__tests__/browser-ipc.test.ts +75 -51
  660. package/src/ipc/__tests__/cache-ipc.test.ts +52 -107
  661. package/src/ipc/__tests__/cli-ipc.test.ts +9 -6
  662. package/src/ipc/__tests__/skill-server-bidirectional.test.ts +254 -0
  663. package/src/ipc/__tests__/skill-server.test.ts +182 -0
  664. package/src/ipc/__tests__/socket-path.test.ts +69 -23
  665. package/src/ipc/__tests__/ui-request-route.test.ts +241 -216
  666. package/src/ipc/__tests__/watcher-ipc.test.ts +33 -33
  667. package/src/ipc/assistant-server.ts +450 -0
  668. package/src/ipc/cli-client.ts +3 -3
  669. package/src/ipc/gateway-client.test.ts +131 -0
  670. package/src/ipc/gateway-client.ts +98 -123
  671. package/src/ipc/ipc-framing.ts +281 -0
  672. package/src/ipc/routes/__tests__/memory-v2-backfill.test.ts +152 -0
  673. package/src/ipc/routes/__tests__/memory-v2-validate.test.ts +219 -0
  674. package/src/ipc/routes/db-proxy.ts +73 -0
  675. package/src/ipc/routes/route-adapter.ts +32 -0
  676. package/src/ipc/routes/trust-rules.test.ts +218 -0
  677. package/src/ipc/skill-ipc-types.ts +13 -0
  678. package/src/ipc/skill-routes/__tests__/config.test.ts +146 -0
  679. package/src/ipc/skill-routes/__tests__/events-ipc.test.ts +402 -0
  680. package/src/ipc/skill-routes/__tests__/identity.test.ts +81 -0
  681. package/src/ipc/skill-routes/__tests__/log.test.ts +133 -0
  682. package/src/ipc/skill-routes/__tests__/memory.test.ts +178 -0
  683. package/src/ipc/skill-routes/__tests__/platform.test.ts +111 -0
  684. package/src/ipc/skill-routes/__tests__/providers.test.ts +265 -0
  685. package/src/ipc/skill-routes/__tests__/registries.test.ts +361 -0
  686. package/src/ipc/skill-routes/config.ts +47 -0
  687. package/src/ipc/skill-routes/events.ts +131 -0
  688. package/src/ipc/skill-routes/identity.ts +34 -0
  689. package/src/ipc/skill-routes/index.ts +37 -0
  690. package/src/ipc/skill-routes/log.ts +40 -0
  691. package/src/ipc/skill-routes/memory.ts +76 -0
  692. package/src/ipc/skill-routes/platform.ts +39 -0
  693. package/src/ipc/skill-routes/providers.ts +163 -0
  694. package/src/ipc/skill-routes/registries.ts +393 -0
  695. package/src/ipc/skill-server.ts +771 -0
  696. package/src/ipc/skill-socket-path.ts +20 -0
  697. package/src/ipc/socket-cleanup.ts +92 -0
  698. package/src/ipc/socket-path.ts +63 -32
  699. package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +374 -0
  700. package/src/live-voice/__tests__/live-voice-archive.test.ts +525 -0
  701. package/src/live-voice/__tests__/live-voice-events.test.ts +473 -0
  702. package/src/live-voice/__tests__/live-voice-integration.test.ts +359 -0
  703. package/src/live-voice/__tests__/live-voice-metrics.test.ts +179 -0
  704. package/src/live-voice/__tests__/live-voice-session-manager.test.ts +349 -0
  705. package/src/live-voice/__tests__/live-voice-stt.test.ts +244 -0
  706. package/src/live-voice/__tests__/live-voice-tts-session.test.ts +337 -0
  707. package/src/live-voice/__tests__/live-voice-tts.test.ts +337 -0
  708. package/src/live-voice/__tests__/protocol.test.ts +295 -0
  709. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +421 -0
  710. package/src/live-voice/live-voice-archive.ts +758 -0
  711. package/src/live-voice/live-voice-metrics.ts +472 -0
  712. package/src/live-voice/live-voice-session-manager.ts +222 -0
  713. package/src/live-voice/live-voice-session.ts +1144 -0
  714. package/src/live-voice/live-voice-tts.ts +260 -0
  715. package/src/live-voice/protocol.ts +524 -0
  716. package/src/mcp/client.ts +2 -2
  717. package/src/media/types.ts +4 -4
  718. package/src/memory/__tests__/auto-analysis-enqueue.test.ts +4 -28
  719. package/src/memory/__tests__/auto-analysis-guard.test.ts +2 -2
  720. package/src/memory/__tests__/conversation-analyze-job.test.ts +7 -62
  721. package/src/memory/__tests__/conversation-group-migration.test.ts +2 -2
  722. package/src/memory/__tests__/find-analysis-conversation.test.ts +2 -1
  723. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +235 -0
  724. package/src/memory/admin.ts +65 -7
  725. package/src/memory/app-git-service.ts +0 -14
  726. package/src/memory/attachments-store.ts +14 -16
  727. package/src/memory/auto-analysis-enqueue.ts +2 -17
  728. package/src/memory/canonical-guardian-store.ts +2 -1
  729. package/src/memory/channel-verification-sessions.ts +1 -1
  730. package/src/memory/checkpoints.ts +1 -1
  731. package/src/memory/context-search/agent-protocol.ts +424 -0
  732. package/src/memory/context-search/agent-runner.ts +1295 -0
  733. package/src/memory/context-search/format.ts +160 -0
  734. package/src/memory/context-search/limits.ts +106 -0
  735. package/src/memory/context-search/search.ts +387 -0
  736. package/src/memory/context-search/sources/conversations.ts +278 -0
  737. package/src/memory/context-search/sources/memory.ts +90 -0
  738. package/src/memory/context-search/sources/pkb.ts +468 -0
  739. package/src/memory/context-search/sources/workspace.ts +1255 -0
  740. package/src/memory/context-search/types.ts +49 -0
  741. package/src/memory/conversation-analyze-job.ts +3 -24
  742. package/src/memory/conversation-attention-store.ts +1 -1
  743. package/src/memory/conversation-bootstrap.ts +1 -1
  744. package/src/memory/conversation-crud.ts +69 -127
  745. package/src/memory/conversation-directories.ts +1 -11
  746. package/src/memory/conversation-display-order-migration.ts +11 -2
  747. package/src/memory/conversation-group-migration.ts +20 -4
  748. package/src/memory/conversation-key-store.ts +3 -4
  749. package/src/memory/conversation-queries.ts +13 -26
  750. package/src/memory/conversation-starter-validation.ts +88 -0
  751. package/src/memory/conversation-starters-cadence.ts +1 -1
  752. package/src/memory/conversation-title-service.ts +2 -1
  753. package/src/memory/db-init.ts +14 -4
  754. package/src/memory/db-maintenance.ts +1 -1
  755. package/src/memory/delivery-channels.ts +1 -14
  756. package/src/memory/delivery-crud.ts +2 -32
  757. package/src/memory/delivery-status.ts +1 -1
  758. package/src/memory/embedding-gemini.test.ts +4 -4
  759. package/src/memory/external-conversation-store.ts +1 -1
  760. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +412 -0
  761. package/src/memory/graph/__tests__/handle-remember-v2.test.ts +225 -0
  762. package/src/memory/graph/bootstrap.test.ts +2 -7
  763. package/src/memory/graph/bootstrap.ts +2 -1
  764. package/src/memory/graph/capability-seed.ts +3 -3
  765. package/src/memory/graph/compaction.ts +1 -1
  766. package/src/memory/graph/consolidation.ts +13 -10
  767. package/src/memory/graph/conversation-graph-memory.ts +151 -1
  768. package/src/memory/graph/decay.ts +1 -1
  769. package/src/memory/graph/extraction.ts +53 -21
  770. package/src/memory/graph/graph-memory-state-store.ts +1 -1
  771. package/src/memory/graph/graph-search.test.ts +94 -2
  772. package/src/memory/graph/graph-search.ts +22 -7
  773. package/src/memory/graph/image-ref-utils.ts +1 -1
  774. package/src/memory/graph/retriever.test.ts +158 -4
  775. package/src/memory/graph/retriever.ts +17 -5
  776. package/src/memory/graph/store.test.ts +2 -1
  777. package/src/memory/graph/store.ts +1 -1
  778. package/src/memory/graph/tool-handlers.ts +73 -247
  779. package/src/memory/graph/tools.ts +35 -53
  780. package/src/memory/group-crud.ts +1 -2
  781. package/src/memory/guardian-action-store.ts +2 -1
  782. package/src/memory/guardian-approvals.ts +1 -1
  783. package/src/memory/guardian-rate-limits.ts +1 -1
  784. package/src/memory/indexer.ts +43 -17
  785. package/src/memory/invite-store.ts +1 -1
  786. package/src/memory/job-handlers/backfill.ts +1 -1
  787. package/src/memory/job-handlers/cleanup.ts +2 -1
  788. package/src/memory/job-handlers/conversation-starters.ts +18 -10
  789. package/src/memory/job-handlers/embedding.test.ts +2 -1
  790. package/src/memory/job-handlers/embedding.ts +1 -1
  791. package/src/memory/job-handlers/index-maintenance.ts +1 -1
  792. package/src/memory/job-handlers/summarization.ts +3 -3
  793. package/src/memory/job-utils.ts +3 -3
  794. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +362 -0
  795. package/src/memory/jobs/embed-concept-page.ts +210 -0
  796. package/src/memory/jobs/embed-pkb-file.test.ts +2 -1
  797. package/src/memory/jobs-store.ts +10 -2
  798. package/src/memory/jobs-worker.ts +58 -5
  799. package/src/memory/lifecycle-events-store.ts +1 -1
  800. package/src/memory/llm-request-log-store.ts +1 -1
  801. package/src/memory/llm-usage-store.ts +1 -1
  802. package/src/memory/media-store.ts +1 -1
  803. package/src/memory/memory-recall-log-store.ts +1 -1
  804. package/src/memory/migrations/038-actor-token-records.ts +3 -0
  805. package/src/memory/migrations/039-actor-refresh-token-records.ts +3 -0
  806. package/src/memory/migrations/226-schedule-wake-conversation-id.ts +11 -0
  807. package/src/memory/migrations/227-add-conversation-inference-profile.ts +18 -0
  808. package/src/memory/migrations/228-rename-inference-profile-snake-case.ts +27 -0
  809. package/src/memory/migrations/229-delete-private-conversations.test.ts +1087 -0
  810. package/src/memory/migrations/229-delete-private-conversations.ts +210 -0
  811. package/src/memory/migrations/230-acp-session-history.ts +41 -0
  812. package/src/memory/migrations/231-repair-memory-graph-event-dates.ts +128 -0
  813. package/src/memory/migrations/232-activation-state.ts +38 -0
  814. package/src/memory/migrations/index.ts +10 -0
  815. package/src/memory/migrations/registry.ts +7 -0
  816. package/src/memory/pkb/pkb-index.test.ts +4 -5
  817. package/src/memory/pkb/pkb-reconcile.test.ts +4 -5
  818. package/src/memory/pkb/pkb-search.test.ts +83 -3
  819. package/src/memory/pkb/pkb-search.ts +27 -14
  820. package/src/memory/published-pages-store.ts +1 -1
  821. package/src/memory/schema/acp.ts +30 -0
  822. package/src/memory/schema/conversations.ts +1 -1
  823. package/src/memory/schema/index.ts +1 -0
  824. package/src/memory/schema/infrastructure.ts +1 -32
  825. package/src/memory/schema/memory-graph.ts +36 -14
  826. package/src/memory/scoped-approval-grants.ts +2 -1
  827. package/src/memory/search/semantic.ts +2 -2
  828. package/src/memory/shared-app-links-store.ts +2 -1
  829. package/src/memory/tool-usage-store.ts +1 -1
  830. package/src/memory/trace-event-store.ts +2 -1
  831. package/src/memory/turn-events-store.ts +1 -1
  832. package/src/memory/v2/__tests__/activation-store.test.ts +202 -0
  833. package/src/memory/v2/__tests__/activation.test.ts +956 -0
  834. package/src/memory/v2/__tests__/backfill-jobs.test.ts +610 -0
  835. package/src/memory/v2/__tests__/consolidation-job.test.ts +395 -0
  836. package/src/memory/v2/__tests__/edges.test.ts +435 -0
  837. package/src/memory/v2/__tests__/injection.test.ts +792 -0
  838. package/src/memory/v2/__tests__/migration.test.ts +812 -0
  839. package/src/memory/v2/__tests__/page-store.test.ts +334 -0
  840. package/src/memory/v2/__tests__/qdrant.test.ts +438 -0
  841. package/src/memory/v2/__tests__/sim.test.ts +549 -0
  842. package/src/memory/v2/__tests__/skill-content.test.ts +85 -0
  843. package/src/memory/v2/__tests__/skill-qdrant.test.ts +657 -0
  844. package/src/memory/v2/__tests__/skill-store.test.ts +351 -0
  845. package/src/memory/v2/__tests__/sweep-job.test.ts +441 -0
  846. package/src/memory/v2/activation-store.ts +109 -0
  847. package/src/memory/v2/activation.ts +490 -0
  848. package/src/memory/v2/backfill-jobs.ts +442 -0
  849. package/src/memory/v2/consolidation-job.ts +304 -0
  850. package/src/memory/v2/edges.ts +217 -0
  851. package/src/memory/v2/injection.ts +307 -0
  852. package/src/memory/v2/migration.ts +654 -0
  853. package/src/memory/v2/now-text.ts +38 -0
  854. package/src/memory/v2/page-store.ts +245 -0
  855. package/src/memory/v2/prompts/consolidation.ts +185 -0
  856. package/src/memory/v2/prompts/sweep.ts +56 -0
  857. package/src/memory/v2/qdrant.ts +342 -0
  858. package/src/memory/v2/sim.ts +206 -0
  859. package/src/memory/v2/skill-content.ts +42 -0
  860. package/src/memory/v2/skill-qdrant.ts +395 -0
  861. package/src/memory/v2/skill-store.ts +128 -0
  862. package/src/memory/v2/sweep-job.ts +298 -0
  863. package/src/memory/v2/types.ts +116 -0
  864. package/src/memory/validation.ts +1 -1
  865. package/src/messaging/providers/index.ts +262 -0
  866. package/src/messaging/providers/slack/api.ts +242 -0
  867. package/src/messaging/providers/slack/message-metadata.ts +1 -1
  868. package/src/messaging/providers/slack/send.ts +383 -0
  869. package/src/messaging/providers/telegram-bot/adapter.ts +4 -42
  870. package/src/messaging/providers/telegram-bot/api.ts +253 -0
  871. package/src/messaging/providers/telegram-bot/client.ts +17 -58
  872. package/src/messaging/providers/telegram-bot/send.ts +232 -0
  873. package/src/messaging/providers/whatsapp/adapter.ts +4 -36
  874. package/src/messaging/providers/whatsapp/api.ts +319 -0
  875. package/src/messaging/providers/whatsapp/client.ts +4 -48
  876. package/src/messaging/providers/whatsapp/send.ts +209 -0
  877. package/src/notifications/adapters/slack.ts +5 -23
  878. package/src/notifications/adapters/telegram.ts +8 -29
  879. package/src/notifications/conversation-candidates.ts +1 -1
  880. package/src/notifications/conversation-seed-composer.ts +12 -6
  881. package/src/notifications/copy-composer.ts +1 -1
  882. package/src/notifications/decision-engine.ts +1 -1
  883. package/src/notifications/decisions-store.ts +1 -1
  884. package/src/notifications/deliveries-store.ts +2 -1
  885. package/src/notifications/deterministic-checks.ts +1 -1
  886. package/src/notifications/events-store.ts +1 -13
  887. package/src/notifications/preferences-store.ts +1 -1
  888. package/src/notifications/signal.ts +0 -9
  889. package/src/oauth/connection-resolver.ts +11 -2
  890. package/src/oauth/oauth-store.ts +2 -1
  891. package/src/outbound-proxy/index.ts +0 -1
  892. package/src/permissions/approval-policy.test.ts +149 -132
  893. package/src/permissions/approval-policy.ts +65 -91
  894. package/src/permissions/checker.test.ts +632 -0
  895. package/src/permissions/checker.ts +266 -459
  896. package/src/permissions/gateway-threshold-reader.ts +28 -47
  897. package/src/permissions/ipc-risk-types.ts +95 -0
  898. package/src/permissions/prompter.ts +4 -9
  899. package/src/permissions/risk-types.ts +24 -210
  900. package/src/permissions/types.ts +17 -47
  901. package/src/permissions/workspace-policy.ts +2 -4
  902. package/src/playbooks/playbook-compiler.ts +1 -1
  903. package/src/plugins/defaults/index.ts +1 -1
  904. package/src/plugins/defaults/injectors.ts +18 -21
  905. package/src/plugins/defaults/llm-call.ts +6 -9
  906. package/src/plugins/defaults/memory-retrieval.ts +1 -6
  907. package/src/plugins/defaults/overflow-reduce.ts +9 -5
  908. package/src/plugins/defaults/token-estimate.ts +2 -3
  909. package/src/plugins/registry.ts +61 -1
  910. package/src/plugins/types.ts +6 -7
  911. package/src/plugins/user-loader.ts +36 -10
  912. package/src/prompts/__tests__/system-prompt-memory-v2.test.ts +197 -0
  913. package/src/prompts/persona-resolver.ts +2 -4
  914. package/src/prompts/system-prompt.ts +39 -0
  915. package/src/prompts/templates/SOUL.md +3 -1
  916. package/src/providers/__tests__/provider-env-vars.test.ts +0 -21
  917. package/src/providers/__tests__/retry-callsite.test.ts +3 -6
  918. package/src/providers/anthropic/client.ts +71 -19
  919. package/src/providers/call-site-routing.ts +7 -3
  920. package/src/providers/fireworks/client.ts +3 -0
  921. package/src/providers/gemini/client.ts +96 -22
  922. package/src/providers/managed-proxy/context.ts +0 -12
  923. package/src/providers/model-catalog.ts +83 -8
  924. package/src/providers/model-intents.ts +7 -8
  925. package/src/providers/openai/chat-completions-provider.ts +37 -7
  926. package/src/providers/openai/responses-provider.ts +39 -4
  927. package/src/providers/openrouter/client.ts +4 -5
  928. package/src/providers/provider-env-vars.ts +4 -12
  929. package/src/providers/provider-send-message.ts +16 -11
  930. package/src/providers/registry.ts +1 -1
  931. package/src/providers/retry.ts +52 -23
  932. package/src/providers/speech-to-text/openai-whisper-stream.ts +1 -1
  933. package/src/providers/speech-to-text/openai-whisper.ts +3 -6
  934. package/src/providers/speech-to-text/provider-catalog.ts +75 -0
  935. package/src/providers/speech-to-text/xai.ts +5 -5
  936. package/src/providers/thinking-config.ts +34 -0
  937. package/src/providers/types.ts +22 -10
  938. package/src/runtime/AGENTS.md +10 -9
  939. package/src/runtime/__tests__/agent-wake.test.ts +33 -9
  940. package/src/runtime/__tests__/client-registry.test.ts +5 -27
  941. package/src/runtime/__tests__/interactive-ui.test.ts +157 -246
  942. package/src/runtime/access-request-helper.ts +9 -20
  943. package/src/runtime/actor-trust-resolver.ts +2 -2
  944. package/src/runtime/agent-wake.ts +174 -68
  945. package/src/runtime/approval-conversation-turn.ts +2 -15
  946. package/src/runtime/approval-message-composer.ts +11 -60
  947. package/src/runtime/assistant-event.ts +18 -66
  948. package/src/runtime/auth/__tests__/guard-tests.test.ts +6 -30
  949. package/src/runtime/auth/__tests__/middleware.test.ts +10 -10
  950. package/src/runtime/auth/__tests__/route-policy.test.ts +0 -8
  951. package/src/runtime/auth/context.ts +9 -0
  952. package/src/runtime/auth/middleware.ts +4 -4
  953. package/src/runtime/auth/route-policy.ts +195 -4
  954. package/src/runtime/auth/token-service.ts +1 -100
  955. package/src/runtime/capability-tokens.ts +89 -313
  956. package/src/runtime/channel-approval-types.ts +1 -6
  957. package/src/runtime/channel-approvals.ts +7 -79
  958. package/src/runtime/channel-readiness-service.ts +2 -2
  959. package/src/runtime/channel-reply-delivery.ts +2 -8
  960. package/src/runtime/channel-retry-sweep.ts +20 -17
  961. package/src/runtime/client-registry.ts +21 -28
  962. package/src/runtime/confirmation-request-guardian-bridge.ts +2 -7
  963. package/src/runtime/gateway-client.ts +37 -378
  964. package/src/runtime/guardian-action-grant-minter.ts +2 -3
  965. package/src/runtime/guardian-action-message-composer.ts +11 -52
  966. package/src/runtime/guardian-action-service.ts +19 -7
  967. package/src/runtime/guardian-decision-types.ts +4 -65
  968. package/src/runtime/guardian-reply-router.ts +10 -19
  969. package/src/runtime/guardian-vellum-migration.ts +5 -64
  970. package/src/runtime/http-errors.ts +3 -0
  971. package/src/runtime/http-router.ts +50 -7
  972. package/src/runtime/http-server.ts +345 -1110
  973. package/src/runtime/http-types.ts +15 -98
  974. package/src/runtime/interactive-ui-types.ts +145 -0
  975. package/src/runtime/interactive-ui.ts +38 -196
  976. package/src/runtime/invite-redemption-service.ts +1 -1
  977. package/src/runtime/invite-redemption-templates.ts +1 -1
  978. package/src/runtime/local-actor-identity.ts +13 -43
  979. package/src/runtime/message-composer-types.ts +134 -0
  980. package/src/runtime/middleware/rate-limiter.ts +1 -1
  981. package/src/runtime/middleware/request-logger.ts +5 -2
  982. package/src/runtime/migrations/__tests__/job-registry.test.ts +346 -0
  983. package/src/runtime/migrations/__tests__/vbundle-tar-stream.test.ts +16 -0
  984. package/src/runtime/migrations/job-registry.ts +281 -0
  985. package/src/runtime/migrations/vbundle-builder.ts +3 -4
  986. package/src/runtime/migrations/vbundle-importer.ts +1 -1
  987. package/src/runtime/migrations/vbundle-streaming-importer.ts +0 -13
  988. package/src/runtime/migrations/vbundle-tar-stream.ts +11 -3
  989. package/src/runtime/nl-approval-parser.ts +16 -21
  990. package/src/runtime/pending-interactions.ts +29 -12
  991. package/src/runtime/routes/__tests__/acp-routes.test.ts +395 -0
  992. package/src/runtime/routes/__tests__/backup-routes.test.ts +204 -320
  993. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +72 -4
  994. package/src/runtime/routes/__tests__/stt-routes.test.ts +182 -223
  995. package/src/runtime/routes/__tests__/suggest-trust-rule-routes.test.ts +230 -0
  996. package/src/{ipc/__tests__/task-ipc.test.ts → runtime/routes/__tests__/task-routes.test.ts} +116 -96
  997. package/src/runtime/routes/__tests__/tts-routes.test.ts +185 -289
  998. package/src/runtime/routes/access-request-decision.ts +25 -50
  999. package/src/runtime/routes/acp-routes.test.ts +371 -0
  1000. package/src/runtime/routes/acp-routes.ts +392 -166
  1001. package/src/runtime/routes/app-management-routes.ts +464 -660
  1002. package/src/runtime/routes/app-routes.ts +192 -177
  1003. package/src/runtime/routes/approval-routes.ts +116 -434
  1004. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +24 -84
  1005. package/src/runtime/routes/approval-strategies/guardian-text-engine-strategy.ts +3 -10
  1006. package/src/runtime/routes/attachment-routes.ts +409 -253
  1007. package/src/runtime/routes/audio-routes.ts +51 -18
  1008. package/src/runtime/routes/avatar-routes.ts +82 -75
  1009. package/src/runtime/routes/background-tool-routes.ts +94 -0
  1010. package/src/runtime/routes/backup-routes.ts +154 -336
  1011. package/src/runtime/routes/brain-graph-routes.ts +83 -110
  1012. package/src/runtime/routes/browser-routes.ts +141 -0
  1013. package/src/runtime/routes/btw-routes.ts +62 -106
  1014. package/src/runtime/routes/cache-routes.ts +96 -0
  1015. package/src/runtime/routes/call-routes.ts +208 -247
  1016. package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +1 -1
  1017. package/src/runtime/routes/channel-delivery-routes.ts +25 -27
  1018. package/src/runtime/routes/channel-readiness-routes.ts +83 -120
  1019. package/src/runtime/routes/channel-route-definitions.ts +62 -0
  1020. package/src/runtime/routes/channel-route-shared.ts +14 -18
  1021. package/src/runtime/routes/channel-verification-routes.ts +207 -187
  1022. package/src/runtime/routes/client-routes.ts +48 -0
  1023. package/src/runtime/routes/contact-routes.ts +533 -407
  1024. package/src/runtime/routes/conversation-analysis-routes.ts +48 -49
  1025. package/src/runtime/routes/conversation-attention-routes.ts +55 -67
  1026. package/src/runtime/routes/conversation-list-routes.ts +265 -0
  1027. package/src/runtime/routes/conversation-management-routes.ts +626 -715
  1028. package/src/runtime/routes/conversation-query-routes.ts +510 -460
  1029. package/src/runtime/routes/conversation-routes.ts +456 -368
  1030. package/src/runtime/routes/conversation-starter-routes.ts +121 -71
  1031. package/src/runtime/routes/credential-prompt-routes.ts +124 -0
  1032. package/src/runtime/routes/debug-routes.ts +34 -39
  1033. package/src/runtime/routes/defer-routes.ts +230 -0
  1034. package/src/runtime/routes/diagnostics-routes.ts +79 -70
  1035. package/src/runtime/routes/documents-routes.ts +117 -106
  1036. package/src/runtime/routes/errors.ts +132 -0
  1037. package/src/runtime/routes/events-routes.ts +97 -58
  1038. package/src/runtime/routes/filing-routes.ts +65 -78
  1039. package/src/runtime/routes/global-search-routes.ts +51 -57
  1040. package/src/runtime/routes/group-routes.ts +199 -181
  1041. package/src/runtime/routes/guardian-action-routes.ts +103 -169
  1042. package/src/runtime/routes/guardian-approval-interception.ts +27 -58
  1043. package/src/runtime/routes/guardian-approval-prompt.ts +10 -21
  1044. package/src/runtime/routes/guardian-approval-reply-helpers.ts +2 -6
  1045. package/src/runtime/routes/guardian-expiry-sweep.ts +19 -36
  1046. package/src/runtime/routes/heartbeat-routes.ts +194 -209
  1047. package/src/runtime/routes/home-feed-routes.ts +85 -187
  1048. package/src/runtime/routes/home-state-routes.ts +27 -24
  1049. package/src/runtime/routes/host-bash-routes.ts +42 -52
  1050. package/src/runtime/routes/host-browser-routes.ts +38 -69
  1051. package/src/runtime/routes/host-cu-routes.ts +74 -70
  1052. package/src/runtime/routes/host-file-routes.ts +50 -60
  1053. package/src/runtime/routes/host-transfer-routes.ts +220 -0
  1054. package/src/runtime/routes/http-adapter.ts +172 -0
  1055. package/src/runtime/routes/identity-routes.ts +83 -79
  1056. package/src/runtime/routes/inbound-conversation.ts +11 -18
  1057. package/src/runtime/routes/inbound-message-handler.ts +74 -110
  1058. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +79 -138
  1059. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +2 -3
  1060. package/src/runtime/routes/inbound-stages/background-dispatch.ts +54 -90
  1061. package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +25 -50
  1062. package/src/runtime/routes/inbound-stages/edit-intercept.ts +7 -7
  1063. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +5 -5
  1064. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +5 -6
  1065. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +14 -24
  1066. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +3 -10
  1067. package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +4 -4
  1068. package/src/runtime/routes/inbound-stages/transcribe-audio.ts +3 -3
  1069. package/src/runtime/routes/inbound-stages/verification-intercept.ts +19 -26
  1070. package/src/runtime/routes/index.ts +197 -0
  1071. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +25 -32
  1072. package/src/runtime/routes/integrations/slack/__tests__/share.test.ts +22 -31
  1073. package/src/runtime/routes/integrations/slack/channel.ts +69 -66
  1074. package/src/runtime/routes/integrations/slack/share.ts +49 -58
  1075. package/src/runtime/routes/integrations/telegram.ts +91 -74
  1076. package/src/runtime/routes/integrations/twilio.ts +163 -240
  1077. package/src/runtime/routes/integrations/vercel.ts +57 -54
  1078. package/src/runtime/routes/interface-routes.ts +43 -0
  1079. package/src/runtime/routes/internal-oauth-routes.ts +56 -0
  1080. package/src/runtime/routes/internal-twilio-routes.ts +46 -0
  1081. package/src/runtime/routes/llm-context-normalization.ts +4 -2
  1082. package/src/runtime/routes/log-export/workspace-allowlist.ts +1 -1
  1083. package/src/runtime/routes/log-export-routes.ts +90 -100
  1084. package/src/runtime/routes/memory-item-routes.test.ts +152 -175
  1085. package/src/runtime/routes/memory-item-routes.ts +243 -323
  1086. package/src/runtime/routes/memory-v2-routes.ts +193 -0
  1087. package/src/runtime/routes/migration-rollback-routes.ts +167 -212
  1088. package/src/runtime/routes/migration-routes.ts +877 -374
  1089. package/src/runtime/routes/notification-routes.ts +199 -70
  1090. package/src/runtime/routes/oauth-apps.ts +254 -251
  1091. package/src/runtime/routes/oauth-providers.ts +66 -57
  1092. package/src/runtime/routes/playground/__tests__/force-compact.test.ts +60 -120
  1093. package/src/runtime/routes/playground/__tests__/guard.test.ts +34 -54
  1094. package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +107 -151
  1095. package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +41 -117
  1096. package/src/runtime/routes/playground/__tests__/seed-conversation.test.ts +95 -138
  1097. package/src/runtime/routes/playground/__tests__/seeded-conversations.test.ts +115 -217
  1098. package/src/runtime/routes/playground/__tests__/state.test.ts +41 -90
  1099. package/src/runtime/routes/playground/conversation-not-found.ts +9 -11
  1100. package/src/runtime/routes/playground/force-compact.ts +41 -54
  1101. package/src/runtime/routes/playground/guard.ts +18 -19
  1102. package/src/runtime/routes/playground/helpers.ts +103 -0
  1103. package/src/runtime/routes/playground/index.ts +15 -25
  1104. package/src/runtime/routes/playground/inject-failures.ts +48 -64
  1105. package/src/runtime/routes/playground/reset-circuit.ts +31 -57
  1106. package/src/runtime/routes/playground/seed-conversation.ts +66 -92
  1107. package/src/runtime/routes/playground/seeded-conversations.ts +60 -64
  1108. package/src/runtime/routes/playground/state.ts +23 -24
  1109. package/src/runtime/routes/profiler-routes.ts +132 -167
  1110. package/src/runtime/routes/ps-routes.ts +120 -0
  1111. package/src/runtime/routes/recording-routes.ts +197 -258
  1112. package/src/runtime/routes/rename-conversation-routes.ts +89 -0
  1113. package/src/runtime/routes/schedule-routes.ts +238 -242
  1114. package/src/runtime/routes/secret-routes.ts +219 -265
  1115. package/src/runtime/routes/secrets-deps.ts +24 -0
  1116. package/src/runtime/routes/settings-routes.ts +361 -441
  1117. package/src/runtime/routes/skills-routes.ts +434 -469
  1118. package/src/runtime/routes/stt-routes.ts +196 -206
  1119. package/src/runtime/routes/subagents-routes.ts +125 -141
  1120. package/src/runtime/routes/suggest-trust-rule-routes.ts +244 -0
  1121. package/src/runtime/routes/surface-action-routes.ts +135 -190
  1122. package/src/runtime/routes/surface-content-routes.ts +84 -118
  1123. package/src/runtime/routes/task-routes.ts +354 -0
  1124. package/src/runtime/routes/telemetry-routes.ts +33 -49
  1125. package/src/runtime/routes/trace-event-routes.ts +55 -74
  1126. package/src/runtime/routes/trust-rules-routes.ts +147 -239
  1127. package/src/runtime/routes/tts-routes.ts +187 -169
  1128. package/src/runtime/routes/types.ts +139 -0
  1129. package/src/{ipc/routes/ui-request.ts → runtime/routes/ui-request-routes.ts} +23 -17
  1130. package/src/runtime/routes/upgrade-broadcast-routes.ts +156 -197
  1131. package/src/runtime/routes/usage-routes.ts +143 -169
  1132. package/src/runtime/routes/user-routes.ts +102 -18
  1133. package/src/runtime/routes/wake-conversation-routes.ts +49 -0
  1134. package/src/{ipc/routes/watcher.ts → runtime/routes/watcher-routes.ts} +84 -39
  1135. package/src/runtime/routes/wipe-conversation-routes.ts +89 -0
  1136. package/src/runtime/routes/work-items-routes.test.ts +10 -20
  1137. package/src/runtime/routes/work-items-routes.ts +418 -433
  1138. package/src/runtime/routes/workspace-commit-routes.ts +30 -61
  1139. package/src/runtime/routes/workspace-routes.test.ts +254 -381
  1140. package/src/runtime/routes/workspace-routes.ts +238 -246
  1141. package/src/runtime/runtime-mode.ts +8 -1
  1142. package/src/runtime/services/__tests__/analyze-conversation.test.ts +80 -118
  1143. package/src/runtime/services/analyze-conversation.ts +14 -41
  1144. package/src/runtime/services/conversation-serializer.ts +181 -0
  1145. package/src/runtime/trust-context-resolver.ts +3 -2
  1146. package/src/runtime/verification-outbound-actions.ts +13 -49
  1147. package/src/schedule/schedule-store.ts +64 -2
  1148. package/src/schedule/scheduler.ts +101 -0
  1149. package/src/security/ces-credential-client.ts +32 -169
  1150. package/src/security/ces-rpc-credential-backend.ts +1 -1
  1151. package/src/security/credential-backend.ts +6 -6
  1152. package/src/security/oauth-completion-page.ts +1 -1
  1153. package/src/security/oauth2.ts +3 -6
  1154. package/src/sequence/analytics.ts +1 -1
  1155. package/src/sequence/guardrails.ts +3 -3
  1156. package/src/sequence/store.ts +2 -1
  1157. package/src/signals/bash.ts +1 -1
  1158. package/src/signals/event-stream.ts +1 -1
  1159. package/src/skills/catalog-cache.ts +7 -0
  1160. package/src/skills/catalog-files.ts +0 -5
  1161. package/src/skills/catalog-install.ts +28 -18
  1162. package/src/skills/category-inference.ts +0 -11
  1163. package/src/skills/clawhub.ts +2 -2
  1164. package/src/skills/managed-store.ts +2 -2
  1165. package/src/skills/remote-skill-policy.ts +6 -7
  1166. package/src/subagent/index.ts +2 -6
  1167. package/src/subagent/manager.ts +27 -23
  1168. package/src/subagent/types.ts +9 -0
  1169. package/src/tasks/SPEC.md +2 -2
  1170. package/src/tasks/task-compiler.ts +1 -1
  1171. package/src/tasks/task-runner.ts +2 -22
  1172. package/src/tasks/task-store.ts +1 -1
  1173. package/src/tools/acp/list-agents.test.ts +115 -0
  1174. package/src/tools/acp/list-agents.ts +31 -0
  1175. package/src/tools/acp/spawn.test.ts +379 -0
  1176. package/src/tools/acp/spawn.ts +142 -62
  1177. package/src/tools/acp/steer.test.ts +101 -0
  1178. package/src/tools/acp/steer.ts +38 -0
  1179. package/src/tools/background-tool-registry.ts +98 -0
  1180. package/src/tools/browser/browser-execution.ts +34 -7
  1181. package/src/tools/browser/browser-manager.ts +1 -8
  1182. package/src/tools/browser/cdp-client/accessibility-snapshot.ts +1 -1
  1183. package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +3 -1
  1184. package/src/tools/browser/cdp-client/types.ts +4 -1
  1185. package/src/tools/computer-use/definitions.ts +1 -1
  1186. package/src/tools/credential-execution/make-authenticated-request.ts +2 -2
  1187. package/src/tools/credential-execution/manage-secure-command-tool.ts +1 -1
  1188. package/src/tools/credential-execution/run-authenticated-command.ts +2 -2
  1189. package/src/tools/credentials/broker-types.ts +2 -1
  1190. package/src/tools/document/editor-template.ts +1 -1
  1191. package/src/tools/execution-timeout.ts +1 -1
  1192. package/src/tools/executor.ts +10 -15
  1193. package/src/tools/host-filesystem/transfer.test.ts +268 -0
  1194. package/src/tools/host-filesystem/transfer.ts +234 -0
  1195. package/src/tools/host-terminal/host-shell.ts +189 -11
  1196. package/src/tools/mcp/mcp-tool-factory.ts +1 -1
  1197. package/src/tools/memory/register.test.ts +161 -1
  1198. package/src/tools/memory/register.ts +19 -34
  1199. package/src/tools/permission-checker.ts +18 -219
  1200. package/src/tools/policy-context.ts +1 -8
  1201. package/src/tools/registry.ts +16 -1
  1202. package/src/tools/secret-detection-handler.ts +13 -103
  1203. package/src/tools/shared/shell-output.ts +4 -1
  1204. package/src/tools/side-effects.ts +2 -2
  1205. package/src/tools/skills/execute.ts +1 -1
  1206. package/src/tools/subagent/spawn.ts +35 -11
  1207. package/src/tools/terminal/safe-env.ts +9 -1
  1208. package/src/tools/terminal/shell.ts +161 -31
  1209. package/src/tools/tool-approval-handler.ts +4 -70
  1210. package/src/tools/tool-input-summary.ts +10 -0
  1211. package/src/tools/types.ts +143 -163
  1212. package/src/tools/ui-surface/definitions.ts +2 -2
  1213. package/src/util/debounce.ts +0 -21
  1214. package/src/util/errors.ts +0 -8
  1215. package/src/util/log-redact.ts +0 -1
  1216. package/src/util/platform.ts +85 -124
  1217. package/src/util/pricing.ts +109 -6
  1218. package/src/watcher/engine.ts +42 -20
  1219. package/src/watcher/watcher-store.ts +2 -1
  1220. package/src/work-items/work-item-store.ts +1 -1
  1221. package/src/workspace/git-service.ts +1 -6
  1222. package/src/workspace/migrations/006-services-config.ts +10 -1
  1223. package/src/workspace/migrations/017-seed-persona-dirs.ts +1 -1
  1224. package/src/workspace/migrations/019-scope-journal-to-guardian.ts +1 -1
  1225. package/src/workspace/migrations/028-recover-conversations-from-disk-view.ts +1 -1
  1226. package/src/workspace/migrations/031-drop-user-md.ts +1 -1
  1227. package/src/workspace/migrations/045-release-notes-meet-avatar.ts +3 -4
  1228. package/src/workspace/migrations/052-seed-default-inference-profiles.ts +150 -0
  1229. package/src/workspace/migrations/053-release-notes-acp-codex.ts +107 -0
  1230. package/src/workspace/migrations/054-seed-recall-callsite.ts +102 -0
  1231. package/src/workspace/migrations/055-release-notes-agentic-recall.ts +63 -0
  1232. package/src/workspace/migrations/056-release-notes-inference-profile-reordering.ts +65 -0
  1233. package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +98 -0
  1234. package/src/workspace/migrations/058-release-notes-acp-sessions-ui.ts +71 -0
  1235. package/src/workspace/migrations/059-move-pid-to-workspace.ts +53 -0
  1236. package/src/workspace/migrations/060-memory-v2-init.ts +53 -0
  1237. package/src/workspace/migrations/rebuild-conversation-disk-view.ts +1 -1
  1238. package/src/workspace/migrations/registry.ts +18 -0
  1239. package/src/workspace/migrations/runner.ts +2 -2
  1240. package/src/workspace/provider-commit-message-generator.ts +1 -1
  1241. package/node_modules/@vellumai/ces-contracts/src/__tests__/trust-rules.test.ts +0 -471
  1242. package/node_modules/@vellumai/ces-contracts/src/trust-rules.ts +0 -436
  1243. package/src/__tests__/cli-command-risk-guard.test.ts +0 -368
  1244. package/src/__tests__/config-watcher-feature-flags.test.ts +0 -211
  1245. package/src/__tests__/conversation-approval-overrides.test.ts +0 -207
  1246. package/src/__tests__/conversation-host-access-routes.test.ts +0 -229
  1247. package/src/__tests__/conversation-tool-setup-batch-authorized.test.ts +0 -226
  1248. package/src/__tests__/conversation-tool-setup-side-effect-flag.test.ts +0 -167
  1249. package/src/__tests__/ephemeral-permissions.test.ts +0 -474
  1250. package/src/__tests__/extension-id-sync-guard.test.ts +0 -241
  1251. package/src/__tests__/host-browser-e2e-self-hosted.test.ts +0 -374
  1252. package/src/__tests__/native-host-marker-sync-guard.test.ts +0 -157
  1253. package/src/__tests__/pairing-concurrent.test.ts +0 -84
  1254. package/src/__tests__/pairing-routes.test.ts +0 -181
  1255. package/src/__tests__/parser.test.ts +0 -595
  1256. package/src/__tests__/permission-checker-host-gate.test.ts +0 -488
  1257. package/src/__tests__/permission-controls-v2-flag.test.ts +0 -55
  1258. package/src/__tests__/permission-mode.test.ts +0 -89
  1259. package/src/__tests__/provider-env-vars-scope.test.ts +0 -52
  1260. package/src/__tests__/risk-classifier-parity.test.ts +0 -230
  1261. package/src/__tests__/shell-identity.test.ts +0 -236
  1262. package/src/__tests__/shell-parser-fuzz.test.ts +0 -629
  1263. package/src/__tests__/shell-parser-property.test.ts +0 -936
  1264. package/src/__tests__/starter-bundle.test.ts +0 -173
  1265. package/src/__tests__/stt-catalog-parity.test.ts +0 -282
  1266. package/src/__tests__/task-runner.test.ts +0 -224
  1267. package/src/__tests__/tool-executor-shell-integration.test.ts +0 -354
  1268. package/src/__tests__/trust-store-pattern-matches.test.ts +0 -29
  1269. package/src/__tests__/trust-store.test.ts +0 -2013
  1270. package/src/__tests__/v2-consent-policy.test.ts +0 -103
  1271. package/src/browser/identifiers.ts +0 -51
  1272. package/src/cli/db.ts +0 -1
  1273. package/src/config/bundled-skills/settings/tools/avatar-get.ts +0 -40
  1274. package/src/config/bundled-skills/settings/tools/avatar-remove.ts +0 -64
  1275. package/src/config/bundled-skills/settings/tools/avatar-update.ts +0 -88
  1276. package/src/daemon/__tests__/lifecycle-startup-ordering.test.ts +0 -127
  1277. package/src/daemon/approved-devices-store.ts +0 -110
  1278. package/src/daemon/external-skills-bootstrap.ts +0 -41
  1279. package/src/daemon/message-types/trust.ts +0 -71
  1280. package/src/daemon/pairing-store.ts +0 -229
  1281. package/src/ipc/cli-server.ts +0 -252
  1282. package/src/ipc/routes/attachment.ts +0 -114
  1283. package/src/ipc/routes/browser-context.ts +0 -63
  1284. package/src/ipc/routes/browser.ts +0 -97
  1285. package/src/ipc/routes/cache.ts +0 -96
  1286. package/src/ipc/routes/get-contact.ts +0 -16
  1287. package/src/ipc/routes/index.ts +0 -35
  1288. package/src/ipc/routes/list-clients.ts +0 -31
  1289. package/src/ipc/routes/merge-contacts.ts +0 -17
  1290. package/src/ipc/routes/notification.ts +0 -133
  1291. package/src/ipc/routes/rename-conversation.ts +0 -59
  1292. package/src/ipc/routes/search-contacts.ts +0 -19
  1293. package/src/ipc/routes/task-queue.ts +0 -226
  1294. package/src/ipc/routes/task.ts +0 -173
  1295. package/src/ipc/routes/upsert-contact.ts +0 -25
  1296. package/src/ipc/routes/wake-conversation.ts +0 -19
  1297. package/src/memory/db.ts +0 -23
  1298. package/src/permissions/arg-parser.test.ts +0 -161
  1299. package/src/permissions/arg-parser.ts +0 -141
  1300. package/src/permissions/bash-risk-classifier.test.ts +0 -1620
  1301. package/src/permissions/bash-risk-classifier.ts +0 -950
  1302. package/src/permissions/command-registry.test.ts +0 -774
  1303. package/src/permissions/command-registry.ts +0 -1005
  1304. package/src/permissions/defaults.ts +0 -314
  1305. package/src/permissions/file-risk-classifier.test.ts +0 -535
  1306. package/src/permissions/file-risk-classifier.ts +0 -274
  1307. package/src/permissions/permission-mode.ts +0 -24
  1308. package/src/permissions/schedule-risk-classifier.test.ts +0 -129
  1309. package/src/permissions/schedule-risk-classifier.ts +0 -85
  1310. package/src/permissions/shell-identity.ts +0 -297
  1311. package/src/permissions/skill-risk-classifier.test.ts +0 -311
  1312. package/src/permissions/skill-risk-classifier.ts +0 -214
  1313. package/src/permissions/trust-client.ts +0 -359
  1314. package/src/permissions/trust-store-interface.ts +0 -100
  1315. package/src/permissions/trust-store.ts +0 -1330
  1316. package/src/permissions/v2-consent-policy.ts +0 -87
  1317. package/src/permissions/web-risk-classifier.test.ts +0 -170
  1318. package/src/permissions/web-risk-classifier.ts +0 -89
  1319. package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +0 -715
  1320. package/src/runtime/__tests__/capability-tokens.test.ts +0 -258
  1321. package/src/runtime/actor-refresh-token-store.ts +0 -156
  1322. package/src/runtime/actor-token-store.ts +0 -207
  1323. package/src/runtime/auth/__tests__/credential-service.test.ts +0 -264
  1324. package/src/runtime/auth/credential-service.ts +0 -352
  1325. package/src/runtime/conversation-approval-overrides.ts +0 -86
  1326. package/src/runtime/routes/browser-extension-pair-routes.ts +0 -575
  1327. package/src/runtime/routes/channel-routes.ts +0 -112
  1328. package/src/runtime/routes/contact-routes.test.ts +0 -298
  1329. package/src/runtime/routes/guardian-bootstrap-routes.ts +0 -175
  1330. package/src/runtime/routes/guardian-refresh-routes.ts +0 -79
  1331. package/src/runtime/routes/invite-routes.ts +0 -280
  1332. package/src/runtime/routes/pairing-routes.ts +0 -431
  1333. package/src/runtime/routes/playground/deps.ts +0 -56
  1334. package/src/runtime/services/__tests__/analyze-deps-singleton.test.ts +0 -67
  1335. package/src/runtime/services/analyze-deps-singleton.ts +0 -32
  1336. package/src/tasks/ephemeral-permissions.ts +0 -55
  1337. package/src/tools/terminal/parser.ts +0 -623
  1338. package/src/types/qrcode.d.ts +0 -13
  1339. package/src/util/network-info.ts +0 -55
  1340. /package/node_modules/@vellumai/{ces-contracts → ces-client}/tsconfig.json +0 -0
  1341. /package/node_modules/@vellumai/{ces-contracts → service-contracts}/src/__tests__/grants.test.ts +0 -0
  1342. /package/node_modules/@vellumai/{ces-contracts → service-contracts}/src/error.ts +0 -0
  1343. /package/node_modules/@vellumai/{ces-contracts → service-contracts}/src/grants.ts +0 -0
  1344. /package/node_modules/@vellumai/{ces-contracts → service-contracts}/src/handles.ts +0 -0
  1345. /package/node_modules/@vellumai/{ces-contracts → service-contracts}/src/rendering.ts +0 -0
  1346. /package/node_modules/@vellumai/{ces-contracts → service-contracts}/src/rpc.ts +0 -0
@@ -23,7 +23,6 @@ import { z } from "zod";
23
23
  import { invalidateConfigCache } from "../../config/loader.js";
24
24
  import { getDb, resetDb } from "../../memory/db-connection.js";
25
25
  import { validateMigrationState } from "../../memory/migrations/validate-migration-state.js";
26
- import { clearCache as clearTrustCache } from "../../permissions/trust-store.js";
27
26
  import { credentialKey } from "../../security/credential-key.js";
28
27
  import {
29
28
  bulkSetSecureKeysAsync,
@@ -41,12 +40,14 @@ import {
41
40
  getWorkspaceDir,
42
41
  getWorkspaceHooksDir,
43
42
  } from "../../util/platform.js";
44
- import { httpError } from "../http-errors.js";
45
- import type { RouteDefinition } from "../http-router.js";
46
43
  import {
47
44
  validateGcsSignedUrl,
48
45
  type ValidateGcsSignedUrlOptions,
49
46
  } from "../migrations/gcs-signed-url.js";
47
+ import {
48
+ JobAlreadyInProgressError,
49
+ migrationJobs,
50
+ } from "../migrations/job-registry.js";
50
51
  import { streamExportVBundle } from "../migrations/vbundle-builder.js";
51
52
  import {
52
53
  analyzeImport,
@@ -60,6 +61,15 @@ import {
60
61
  } from "../migrations/vbundle-importer.js";
61
62
  import { streamCommitImport } from "../migrations/vbundle-streaming-importer.js";
62
63
  import { validateVBundle } from "../migrations/vbundle-validator.js";
64
+ import {
65
+ BadGatewayError,
66
+ BadRequestError,
67
+ InternalError,
68
+ NotFoundError,
69
+ RouteError,
70
+ } from "./errors.js";
71
+ import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
72
+ import { RouteResponse } from "./types.js";
63
73
 
64
74
  /**
65
75
  * CES account prefix for platform-identity (`vellum:*`) credentials. Entries
@@ -139,60 +149,24 @@ const log = getLogger("migration-routes");
139
149
  * 400: Standard error envelope for missing/empty body
140
150
  * 422: Standard error envelope for completely unparseable input
141
151
  */
142
- export async function handleMigrationValidate(req: Request): Promise<Response> {
143
- let fileData: Uint8Array | null = null;
144
-
145
- const contentType = req.headers.get("content-type") ?? "";
146
-
147
- if (contentType.includes("multipart/form-data")) {
148
- try {
149
- const formData = await req.formData();
150
- const file = formData.get("file");
151
- if (!file || !(file instanceof Blob)) {
152
- return httpError(
153
- "BAD_REQUEST",
154
- 'Multipart upload requires a "file" field',
155
- 400,
156
- );
157
- }
158
- fileData = new Uint8Array(await file.arrayBuffer());
159
- } catch (err) {
160
- log.error({ err }, "Failed to parse multipart form data");
161
- return httpError("BAD_REQUEST", "Invalid multipart form data", 400);
162
- }
163
- } else {
164
- // Treat as raw binary body
165
- try {
166
- const arrayBuffer = await req.arrayBuffer();
167
- fileData = new Uint8Array(arrayBuffer);
168
- } catch (err) {
169
- log.error({ err }, "Failed to read request body");
170
- return httpError("BAD_REQUEST", "Failed to read request body", 400);
171
- }
172
- }
173
-
174
- if (!fileData || fileData.length === 0) {
175
- return httpError(
176
- "BAD_REQUEST",
177
- "Request body is empty — a .vbundle file is required",
178
- 400,
179
- );
180
- }
152
+ export async function handleMigrationValidate({
153
+ rawBody,
154
+ headers,
155
+ }: RouteHandlerArgs) {
156
+ const fileData = await extractFileData(rawBody, headers);
181
157
 
182
158
  try {
183
159
  const result = validateVBundle(fileData);
184
160
 
185
- return Response.json({
161
+ return {
186
162
  is_valid: result.is_valid,
187
163
  errors: result.errors,
188
164
  ...(result.manifest ? { manifest: result.manifest } : {}),
189
- });
165
+ };
190
166
  } catch (err) {
191
167
  log.error({ err }, "Unexpected error during vbundle validation");
192
- return httpError(
193
- "INTERNAL_ERROR",
168
+ throw new InternalError(
194
169
  err instanceof Error ? err.message : "Unexpected validation error",
195
- 500,
196
170
  );
197
171
  }
198
172
  }
@@ -214,21 +188,11 @@ export async function handleMigrationValidate(req: Request): Promise<Response> {
214
188
  *
215
189
  * Auth: Requires settings.write scope. Allowed for actor, svc_gateway, svc_daemon, local.
216
190
  */
217
- export async function handleMigrationExport(req: Request): Promise<Response> {
218
- let description: string | undefined;
219
-
220
- // Parse optional JSON body for export metadata
221
- const contentType = req.headers.get("content-type") ?? "";
222
- if (contentType.includes("application/json")) {
223
- try {
224
- const body = (await req.json()) as Record<string, unknown>;
225
- if (typeof body.description === "string") {
226
- description = body.description;
227
- }
228
- } catch (err) {
229
- log.warn({ err }, "Failed to parse export request body — using defaults");
230
- }
231
- }
191
+ export async function handleMigrationExport({
192
+ body,
193
+ }: RouteHandlerArgs): Promise<RouteResponse> {
194
+ const description =
195
+ typeof body?.description === "string" ? body.description : undefined;
232
196
 
233
197
  let cleanup: (() => Promise<void>) | undefined;
234
198
 
@@ -292,30 +256,282 @@ export async function handleMigrationExport(req: Request): Promise<Response> {
292
256
  cleanup = undefined;
293
257
  });
294
258
 
295
- const body = Readable.toWeb(fileStream) as unknown as ReadableStream;
296
-
297
- return new Response(body, {
298
- status: 200,
299
- headers: {
300
- "Content-Type": "application/octet-stream",
301
- "Content-Disposition": `attachment; filename="${filename}"`,
302
- "Content-Length": String(size),
303
- "X-Vbundle-Schema-Version": manifest.schema_version,
304
- "X-Vbundle-Manifest-Sha256": manifest.manifest_sha256,
305
- "X-Vbundle-Credentials-Included": String(credentials.length),
306
- },
259
+ const streamBody = Readable.toWeb(fileStream) as unknown as ReadableStream;
260
+
261
+ return new RouteResponse(streamBody, {
262
+ "Content-Type": "application/octet-stream",
263
+ "Content-Disposition": `attachment; filename="${filename}"`,
264
+ "Content-Length": String(size),
265
+ "X-Vbundle-Schema-Version": manifest.schema_version,
266
+ "X-Vbundle-Manifest-Sha256": manifest.manifest_sha256,
267
+ "X-Vbundle-Credentials-Included": String(credentials.length),
307
268
  });
308
269
  } catch (err) {
309
270
  await cleanup?.();
310
271
  log.error({ err }, "Failed to build export bundle");
311
- return httpError(
312
- "INTERNAL_ERROR",
272
+ throw new InternalError(
313
273
  err instanceof Error ? err.message : "Unexpected export error",
314
- 500,
315
274
  );
316
275
  }
317
276
  }
318
277
 
278
+ // ---------------------------------------------------------------------------
279
+ // POST /v1/migrations/export-to-gcs — async export streamed to a signed URL
280
+ // ---------------------------------------------------------------------------
281
+
282
+ /** 60 minutes — matches the URL-body import fetch deadline. */
283
+ const EXPORT_TO_GCS_PUT_TIMEOUT_MS = 60 * 60 * 1000;
284
+
285
+ const MigrationExportToGcsBody = z.object({
286
+ upload_url: z.string().url(),
287
+ description: z.string().optional(),
288
+ });
289
+
290
+ /**
291
+ * Collected credentials plus a warning marker if the credential store was
292
+ * unreachable. The caller surfaces the warning in logs; production callers
293
+ * fail closed on errors (a thrown exception → 500) to avoid shipping a
294
+ * bundle with partial credentials. An unreachable store is NOT an error —
295
+ * `handleMigrationExport` treats that case as "export without credentials".
296
+ */
297
+ interface CollectedCredentials {
298
+ credentials: Array<{ account: string; value: string }>;
299
+ unreachable: boolean;
300
+ }
301
+
302
+ /**
303
+ * Mirror of the credential-collection block inside `handleMigrationExport`.
304
+ * Factored out so the new async export-to-gcs handler can share the exact
305
+ * same behavior. Throws if the credential store raises an unexpected error —
306
+ * the caller translates that into a 500 (fail closed on credential errors).
307
+ */
308
+ async function collectExportCredentials(): Promise<CollectedCredentials> {
309
+ const credentialList = await listSecureKeysAsync();
310
+ if (credentialList.unreachable) {
311
+ log.warn(
312
+ "Credential store is unreachable — export will not include credentials",
313
+ );
314
+ return { credentials: [], unreachable: true };
315
+ }
316
+ const credentials: Array<{ account: string; value: string }> = [];
317
+ for (const account of credentialList.accounts) {
318
+ const result = await getSecureKeyResultAsync(account);
319
+ if (result.unreachable) {
320
+ log.warn(
321
+ { account },
322
+ "Credential store unreachable when reading credential — skipping",
323
+ );
324
+ } else if (result.value != null) {
325
+ credentials.push({ account, value: result.value });
326
+ }
327
+ }
328
+ return { credentials, unreachable: false };
329
+ }
330
+
331
+ /**
332
+ * POST /v1/migrations/export-to-gcs
333
+ *
334
+ * Starts an async export job that streams a freshly-built .vbundle archive
335
+ * to a GCS signed PUT URL. Returns `202 Accepted` with a `job_id` the caller
336
+ * can poll via the job-status endpoint; the bundle upload runs in the
337
+ * background via `migrationJobs`.
338
+ *
339
+ * Request body (JSON):
340
+ * { upload_url: string, description?: string }
341
+ *
342
+ * Responses:
343
+ * 202: { job_id, status: "pending", type: "export" }
344
+ * 400: { error: { code: "invalid_upload_url", reason } } — URL failed
345
+ * `validateGcsSignedUrl` (scheme/host/signature/traversal).
346
+ * 409: { error: { code: "export_in_progress", job_id } } — another export
347
+ * job is already pending or running.
348
+ * 500: Standard error envelope for credential-collection failures or
349
+ * other unexpected errors before the job is enqueued.
350
+ *
351
+ * Terminal job state (surfaced via the job-status endpoint once poll lands):
352
+ * result: { size, sha256, schemaVersion, credentialsIncluded }
353
+ * error.code = "upload_failed" with `upstreamStatus` on non-2xx PUT
354
+ * error.code = "fetch_failed" on transport errors from the PUT itself.
355
+ *
356
+ * Auth: settings.write scope (matches `migrations/export`).
357
+ */
358
+ export async function handleMigrationExportToGcs({
359
+ body,
360
+ }: RouteHandlerArgs) {
361
+ // ── 1. Parse JSON body ────────────────────────────────────────────────
362
+ const parsed = MigrationExportToGcsBody.safeParse(body);
363
+ if (!parsed.success) {
364
+ throw new BadRequestError(
365
+ "Request body must be { upload_url: string, description?: string } with a valid URL",
366
+ );
367
+ }
368
+
369
+ // ── 2. Validate the upload URL. Never log `parsed.data.upload_url`.
370
+ const validated = validateGcsSignedUrl(
371
+ parsed.data.upload_url,
372
+ urlValidatorOptions,
373
+ );
374
+ if (!validated.ok) {
375
+ log.warn(
376
+ { reason: validated.reason },
377
+ "Rejected migration export-to-gcs upload URL",
378
+ );
379
+ throw new RouteError(
380
+ `Invalid upload URL: ${validated.reason}`,
381
+ "invalid_upload_url",
382
+ 400,
383
+ );
384
+ }
385
+
386
+ log.info(
387
+ { host: validated.host, path: validated.path },
388
+ "migration export to GCS starting",
389
+ );
390
+
391
+ // ── 3. Collect credentials up front. Fail closed → 500.
392
+ let collected: CollectedCredentials;
393
+ try {
394
+ collected = await collectExportCredentials();
395
+ } catch (err) {
396
+ log.error({ err }, "Failed to collect credentials for export-to-gcs");
397
+ throw new InternalError(
398
+ err instanceof Error ? err.message : "Failed to collect credentials",
399
+ );
400
+ }
401
+
402
+ const description = parsed.data.description;
403
+ const uploadUrl = parsed.data.upload_url;
404
+
405
+ // ── 4. Enqueue the job. The runner captures the collected credentials.
406
+ let job;
407
+ try {
408
+ job = migrationJobs.startJob("export", async () => {
409
+ let cleanup: (() => Promise<void>) | undefined;
410
+ try {
411
+ const result = await streamExportVBundle({
412
+ workspaceDir: getWorkspaceDir(),
413
+ source: "runtime-export",
414
+ description,
415
+ credentials: collected.credentials,
416
+ checkpoint: () => {
417
+ const dbPath = getDbPath();
418
+ try {
419
+ const db = new Database(dbPath);
420
+ try {
421
+ db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
422
+ } finally {
423
+ db.close();
424
+ }
425
+ } catch (err) {
426
+ log.warn(
427
+ { err },
428
+ "WAL checkpoint failed — exporting without checkpoint",
429
+ );
430
+ }
431
+ },
432
+ });
433
+
434
+ cleanup = result.cleanup;
435
+ const { tempPath, size, manifest } = result;
436
+
437
+ // Stream the temp file to GCS via PUT. Using Node's ReadableStream
438
+ // bridge keeps peak memory bounded — we do NOT load the archive
439
+ // into memory.
440
+ const fileStream = createReadStream(tempPath);
441
+ const webBody = Readable.toWeb(
442
+ fileStream,
443
+ ) as unknown as ReadableStream<Uint8Array>;
444
+
445
+ let response: Response;
446
+ try {
447
+ response = await fetch(uploadUrl, {
448
+ method: "PUT",
449
+ body: webBody,
450
+ // `duplex: "half"` is required when sending a streaming body
451
+ // via fetch in Node/Bun — without it the platform rejects the
452
+ // request as "duplex option is required when body is a
453
+ // ReadableStream".
454
+ duplex: "half",
455
+ // `validateGcsSignedUrl` only vets the initial URL. If the
456
+ // upstream responds with a 3xx, default fetch would follow
457
+ // the redirect and PUT bytes to an attacker-controlled host.
458
+ // Refuse redirects so the signed URL's origin is the only
459
+ // destination for the export archive.
460
+ redirect: "error",
461
+ headers: {
462
+ "Content-Type": "application/octet-stream",
463
+ "Content-Length": String(size),
464
+ },
465
+ signal: AbortSignal.timeout(EXPORT_TO_GCS_PUT_TIMEOUT_MS),
466
+ } as RequestInit & { duplex: "half" });
467
+ } catch (err) {
468
+ // Transport-level fetch failures (DNS, reset, abort) — tag them
469
+ // with the fetch-body marker so the registry maps them to
470
+ // `error.code = "fetch_failed"` and logs stay consistent with
471
+ // the import URL path.
472
+ const wrapped = err instanceof Error ? err : new Error(String(err));
473
+ tagFetchBodyError(wrapped as NodeJS.ErrnoException);
474
+ (wrapped as { code?: string }).code = "fetch_failed";
475
+ throw wrapped;
476
+ }
477
+
478
+ if (!response.ok) {
479
+ // Drain so the socket is released promptly. Ignore drain errors.
480
+ try {
481
+ await response.body?.cancel();
482
+ } catch {
483
+ /* best-effort */
484
+ }
485
+ const uploadErr = new Error(
486
+ `Upload to GCS failed with status ${response.status}`,
487
+ );
488
+ (uploadErr as { code?: string }).code = "upload_failed";
489
+ (uploadErr as { upstreamStatus?: number }).upstreamStatus =
490
+ response.status;
491
+ throw uploadErr;
492
+ }
493
+
494
+ return {
495
+ size,
496
+ sha256: manifest.manifest_sha256,
497
+ schemaVersion: manifest.schema_version,
498
+ credentialsIncluded: collected.credentials.length,
499
+ };
500
+ } finally {
501
+ // Mirror the raw-bytes export cleanup pattern: the stream's
502
+ // `close` listener is the happy-path cleanup in that handler, but
503
+ // here we keep everything in the async block, so a finally is the
504
+ // right place to evict the temp file regardless of outcome.
505
+ if (cleanup) {
506
+ try {
507
+ await cleanup();
508
+ } catch (err) {
509
+ log.warn({ err }, "Failed to clean up export-to-gcs temp file");
510
+ }
511
+ }
512
+ }
513
+ });
514
+ } catch (err) {
515
+ if (err instanceof JobAlreadyInProgressError) {
516
+ throw new RouteError(
517
+ `Export already in progress: ${err.existingJobId}`,
518
+ "export_in_progress",
519
+ 409,
520
+ );
521
+ }
522
+ log.error({ err }, "Unexpected error while enqueueing export-to-gcs job");
523
+ throw new InternalError(
524
+ err instanceof Error ? err.message : "Unexpected export-to-gcs error",
525
+ );
526
+ }
527
+
528
+ return {
529
+ job_id: job.id,
530
+ status: "pending" as const,
531
+ type: "export" as const,
532
+ };
533
+ }
534
+
319
535
  /**
320
536
  * Extract file data from a request body, supporting both raw binary
321
537
  * and multipart form data uploads.
@@ -323,42 +539,43 @@ export async function handleMigrationExport(req: Request): Promise<Response> {
323
539
  * Shared between validate and import-preflight handlers.
324
540
  */
325
541
  async function extractFileData(
326
- req: Request,
327
- ): Promise<{ data: Uint8Array } | { error: Response }> {
328
- const contentType = req.headers.get("content-type") ?? "";
542
+ rawBody: Uint8Array | undefined,
543
+ headers: Record<string, string> | undefined,
544
+ ): Promise<Uint8Array> {
545
+ const contentType = headers?.["content-type"] ?? "";
329
546
 
330
547
  if (contentType.includes("multipart/form-data")) {
548
+ if (!rawBody) {
549
+ throw new BadRequestError("Request body is empty");
550
+ }
331
551
  try {
332
- const formData = await req.formData();
552
+ const syntheticReq = new Request("http://localhost", {
553
+ method: "POST",
554
+ headers: { "content-type": contentType },
555
+ body: rawBody.buffer as ArrayBuffer,
556
+ });
557
+ const formData = await syntheticReq.formData();
333
558
  const file = formData.get("file");
334
559
  if (!file || !(file instanceof Blob)) {
335
- return {
336
- error: httpError(
337
- "BAD_REQUEST",
338
- 'Multipart upload requires a "file" field',
339
- 400,
340
- ),
341
- };
560
+ throw new BadRequestError(
561
+ 'Multipart upload requires a "file" field',
562
+ );
342
563
  }
343
- return { data: new Uint8Array(await file.arrayBuffer()) };
564
+ return new Uint8Array(await file.arrayBuffer());
344
565
  } catch (err) {
566
+ if (err instanceof BadRequestError) throw err;
345
567
  log.error({ err }, "Failed to parse multipart form data");
346
- return {
347
- error: httpError("BAD_REQUEST", "Invalid multipart form data", 400),
348
- };
568
+ throw new BadRequestError("Invalid multipart form data");
349
569
  }
350
570
  }
351
571
 
352
- // Treat as raw binary body
353
- try {
354
- const arrayBuffer = await req.arrayBuffer();
355
- return { data: new Uint8Array(arrayBuffer) };
356
- } catch (err) {
357
- log.error({ err }, "Failed to read request body");
358
- return {
359
- error: httpError("BAD_REQUEST", "Failed to read request body", 400),
360
- };
572
+ // Raw binary body — already provided as rawBody by the adapter
573
+ if (!rawBody || rawBody.length === 0) {
574
+ throw new BadRequestError(
575
+ "Request body is empty a .vbundle file is required",
576
+ );
361
577
  }
578
+ return rawBody;
362
579
  }
363
580
 
364
581
  /**
@@ -387,35 +604,23 @@ async function extractFileData(
387
604
  *
388
605
  * Auth: Requires settings.write scope. Allowed for actor, svc_gateway, svc_daemon, local.
389
606
  */
390
- export async function handleMigrationImportPreflight(
391
- req: Request,
392
- ): Promise<Response> {
393
- const extracted = await extractFileData(req);
394
- if ("error" in extracted) {
395
- return extracted.error;
396
- }
397
-
398
- const fileData = extracted.data;
399
- if (fileData.length === 0) {
400
- return httpError(
401
- "BAD_REQUEST",
402
- "Request body is empty — a .vbundle file is required",
403
- 400,
404
- );
405
- }
607
+ export async function handleMigrationImportPreflight({
608
+ rawBody,
609
+ headers,
610
+ }: RouteHandlerArgs) {
611
+ const fileData = await extractFileData(rawBody, headers);
406
612
 
407
613
  try {
408
- // Step 1: Validate the bundle
409
614
  const validationResult = validateVBundle(fileData);
410
615
 
411
616
  if (!validationResult.is_valid || !validationResult.manifest) {
412
- return Response.json({
617
+ return {
413
618
  can_import: false,
414
619
  validation: {
415
620
  is_valid: false,
416
621
  errors: validationResult.errors,
417
622
  },
418
- });
623
+ };
419
624
  }
420
625
 
421
626
  const pathResolver = new DefaultPathResolver(
@@ -423,18 +628,14 @@ export async function handleMigrationImportPreflight(
423
628
  getWorkspaceHooksDir(),
424
629
  );
425
630
 
426
- const report = analyzeImport({
631
+ return analyzeImport({
427
632
  manifest: validationResult.manifest,
428
633
  pathResolver,
429
634
  });
430
-
431
- return Response.json(report);
432
635
  } catch (err) {
433
636
  log.error({ err }, "Unexpected error during import preflight analysis");
434
- return httpError(
435
- "INTERNAL_ERROR",
637
+ throw new InternalError(
436
638
  err instanceof Error ? err.message : "Unexpected import preflight error",
437
- 500,
438
639
  );
439
640
  }
440
641
  }
@@ -477,31 +678,18 @@ export async function handleMigrationImportPreflight(
477
678
  *
478
679
  * Auth: Requires settings.write scope. Allowed for actor, svc_gateway, svc_daemon, local.
479
680
  */
480
- export async function handleMigrationImport(req: Request): Promise<Response> {
681
+ export async function handleMigrationImport(
682
+ args: RouteHandlerArgs,
683
+ ): Promise<unknown> {
684
+ const { body, rawBody, headers } = args;
481
685
  // JSON body means the caller is asking us to fetch the bundle from a
482
- // signed URL and stream it through the importer. This keeps the daemon's
483
- // peak memory bounded by one tar entry instead of bundle size, which is
484
- // the whole point of supporting URL-based imports for large bundles.
485
- //
486
- // Raw-bytes path (octet-stream / multipart) is untouched below.
487
- const contentType = req.headers.get("content-type") ?? "";
686
+ // signed URL and stream it through the importer.
687
+ const contentType = headers?.["content-type"] ?? "";
488
688
  if (contentType.includes("application/json")) {
489
- return handleMigrationImportFromUrl(req);
490
- }
491
-
492
- const extracted = await extractFileData(req);
493
- if ("error" in extracted) {
494
- return extracted.error;
689
+ return handleMigrationImportFromUrl(body);
495
690
  }
496
691
 
497
- const fileData = extracted.data;
498
- if (fileData.length === 0) {
499
- return httpError(
500
- "BAD_REQUEST",
501
- "Request body is empty — a .vbundle file is required",
502
- 400,
503
- );
504
- }
692
+ const fileData = await extractFileData(rawBody, headers);
505
693
 
506
694
  try {
507
695
  // Validate the bundle before closing the DB to avoid an unnecessary
@@ -510,11 +698,11 @@ export async function handleMigrationImport(req: Request): Promise<Response> {
510
698
  // (avoids holding two copies of decompressed data in memory).
511
699
  const validation = validateVBundle(fileData);
512
700
  if (!validation.is_valid) {
513
- return Response.json({
701
+ return {
514
702
  success: false,
515
703
  reason: "validation_failed",
516
704
  errors: validation.errors,
517
- });
705
+ };
518
706
  }
519
707
 
520
708
  const pathResolver = new DefaultPathResolver(
@@ -535,7 +723,7 @@ export async function handleMigrationImport(req: Request): Promise<Response> {
535
723
  });
536
724
 
537
725
  if (!result.ok) {
538
- return importCommitFailureResponse(result);
726
+ throwImportCommitFailure(result);
539
727
  }
540
728
 
541
729
  // Import credentials from the bundle into CES (non-blocking — failures
@@ -558,9 +746,8 @@ export async function handleMigrationImport(req: Request): Promise<Response> {
558
746
  // if Django's post-hatch provisioning raced with the import.
559
747
  await reconcileVellumMetadataFromCes(result.report);
560
748
 
561
- // Invalidate in-process caches so imported settings.json and trust.json take effect
749
+ // Invalidate in-process config cache so imported settings.json takes effect
562
750
  invalidateConfigCache();
563
- clearTrustCache();
564
751
 
565
752
  // Check whether the imported database contains migration checkpoints from
566
753
  // a newer version. This is non-blocking — the import has already
@@ -568,19 +755,18 @@ export async function handleMigrationImport(req: Request): Promise<Response> {
568
755
  // not be fully compatible with this daemon's schema.
569
756
  appendNewerMigrationWarningsIfAny(result.report);
570
757
 
571
- return importCommitSuccessResponse(result.report, credentialsImported);
758
+ return importCommitSuccessResult(result.report, credentialsImported);
572
759
  } catch (err) {
573
760
  log.error({ err }, "Unexpected error during import commit");
574
- return httpError(
575
- "INTERNAL_ERROR",
761
+ throw new InternalError(
576
762
  err instanceof Error ? err.message : "Unexpected import error",
577
- 500,
578
763
  );
579
764
  }
580
765
  }
581
766
 
582
767
  // ---------------------------------------------------------------------------
583
- // URL-body variant of POST /v1/migrations/import
768
+ // GCS URL import pipeline — shared by the URL-body branch of
769
+ // POST /v1/migrations/import and POST /v1/migrations/import-from-gcs.
584
770
  // ---------------------------------------------------------------------------
585
771
 
586
772
  /** 60 minutes — matches the gateway's upstream fetch deadline. */
@@ -588,6 +774,8 @@ const URL_FETCH_TIMEOUT_MS = 60 * 60 * 1000;
588
774
 
589
775
  const MigrationImportUrlBody = z.object({ url: z.string().min(1) });
590
776
 
777
+ const MigrationImportFromGcsBody = z.object({ bundle_url: z.string().url() });
778
+
591
779
  /**
592
780
  * Marker attached to errors that originate from the upstream HTTP body
593
781
  * stream (peer reset, abort mid-stream, DNS/transport failure after
@@ -643,43 +831,98 @@ export function _setUrlImportValidatorOptionsForTests(
643
831
  }
644
832
 
645
833
  /**
646
- * Handle a JSON `{ "url": "..." }` body on POST /v1/migrations/import.
647
- *
648
- * Fetches the signed URL, pipes the response body through the streaming
649
- * importer, and returns the same response shapes as the raw-bytes path.
650
- * The signed URL is never logged or included in error responses — only the
651
- * extracted host and path make it into logs.
834
+ * Successful outcome of `runGcsImport`. Mirrors the wire shape produced by
835
+ * `importCommitSuccessResponse` (report fields spread at the top level, with
836
+ * an optional `credentialsImported` summary alongside) so the same value can
837
+ * be serialized directly as a Response body OR stashed as an async-job
838
+ * `result` both the sync endpoint and the async job-status endpoint then
839
+ * hand the CLI a single, identical `ImportResponse`-compatible shape.
652
840
  */
653
- async function handleMigrationImportFromUrl(req: Request): Promise<Response> {
654
- // ── 1. Parse JSON body ────────────────────────────────────────────────
655
- let rawBody: unknown;
656
- try {
657
- rawBody = await req.json();
658
- } catch (err) {
659
- log.warn(
660
- { err: err instanceof Error ? err.message : String(err) },
661
- "Failed to parse JSON body on migration import URL request",
662
- );
663
- return httpError("BAD_REQUEST", "Invalid JSON body", 400);
664
- }
841
+ export interface ImportSummary extends ImportCommitReport {
842
+ credentialsImported?: CredentialImportSummary;
843
+ }
665
844
 
666
- const parsed = MigrationImportUrlBody.safeParse(rawBody);
667
- if (!parsed.success) {
668
- return httpError(
669
- "BAD_REQUEST",
670
- "Request body must be { url: string } with a non-empty url",
671
- 400,
672
- );
845
+ /**
846
+ * Structured error thrown by `runGcsImport`. Carries the information needed
847
+ * to reconstruct the URL-body handler's legacy Response shapes and for the
848
+ * async-job registry to map to `error.code`/`upstreamStatus`.
849
+ */
850
+ interface GcsImportErrorInit {
851
+ code:
852
+ | "invalid_url"
853
+ | "fetch_failed"
854
+ | "validation_failed"
855
+ | "extraction_failed"
856
+ | "write_failed";
857
+ message: string;
858
+ upstreamStatus?: number;
859
+ reason?: string;
860
+ errors?: Array<{ code: string; message: string; path?: string }>;
861
+ partial_report?: ImportCommitReport;
862
+ }
863
+
864
+ class GcsImportError extends Error {
865
+ public readonly code: GcsImportErrorInit["code"];
866
+ public readonly upstreamStatus?: number;
867
+ public readonly reason?: string;
868
+ public readonly errors?: GcsImportErrorInit["errors"];
869
+ public readonly partial_report?: ImportCommitReport;
870
+
871
+ constructor(init: GcsImportErrorInit) {
872
+ super(init.message);
873
+ this.name = "GcsImportError";
874
+ this.code = init.code;
875
+ if (init.upstreamStatus !== undefined) {
876
+ this.upstreamStatus = init.upstreamStatus;
877
+ }
878
+ if (init.reason !== undefined) {
879
+ this.reason = init.reason;
880
+ }
881
+ if (init.errors !== undefined) {
882
+ this.errors = init.errors;
883
+ }
884
+ if (init.partial_report !== undefined) {
885
+ this.partial_report = init.partial_report;
886
+ }
673
887
  }
888
+ }
674
889
 
675
- // ── 2. Validate the URL (defense-in-depth; never log `parsed.data.url`).
676
- const validated = validateGcsSignedUrl(parsed.data.url, urlValidatorOptions);
890
+ /**
891
+ * Fetch a .vbundle from a signed GCS URL and commit it via the streaming
892
+ * importer. On success, returns an `ImportSummary` the caller can serialize
893
+ * into a Response or stash as an async-job `result`. On failure, throws a
894
+ * `GcsImportError`:
895
+ *
896
+ * - `invalid_url` → URL failed `validateGcsSignedUrl` (pre-fetch).
897
+ * - `fetch_failed` → upstream fetch error, non-2xx response, missing
898
+ * body, OR a mid-stream body teardown tagged via
899
+ * `kFetchBodyError`. `upstreamStatus` is populated
900
+ * for non-2xx responses.
901
+ * - `validation_failed` → the bundle failed schema/structural validation
902
+ * inside `streamCommitImport`; `errors` carries
903
+ * the per-issue list.
904
+ * - `extraction_failed` → bundle extraction threw (malformed archive, hash
905
+ * mismatch, etc.) that was NOT an upstream tear-
906
+ * down. `reason` carries the importer's string.
907
+ * - `write_failed` → post-extraction disk write error; `partial_report`
908
+ * is attached when the importer produced one.
909
+ *
910
+ * The signed URL is never echoed into errors or logs — only the extracted
911
+ * `host`/`path` are.
912
+ */
913
+ export async function runGcsImport(
914
+ url: string,
915
+ _correlationId?: string,
916
+ ): Promise<ImportSummary> {
917
+ // ── 1. Validate the URL (defense-in-depth; never log the raw URL).
918
+ const validated = validateGcsSignedUrl(url, urlValidatorOptions);
677
919
  if (!validated.ok) {
678
- // `reason` is a stable enum string and safe to include. The raw URL is
679
- // not — it may contain a live signature. Callers get the reason so they
680
- // can correct the URL without leaking anything into observability.
681
920
  log.warn({ reason: validated.reason }, "Rejected migration import URL");
682
- return httpError("BAD_REQUEST", `Invalid URL: ${validated.reason}`, 400);
921
+ throw new GcsImportError({
922
+ code: "invalid_url",
923
+ message: `Invalid URL: ${validated.reason}`,
924
+ reason: validated.reason,
925
+ });
683
926
  }
684
927
 
685
928
  log.info(
@@ -689,10 +932,10 @@ async function handleMigrationImportFromUrl(req: Request): Promise<Response> {
689
932
 
690
933
  const startedAt = Date.now();
691
934
 
692
- // ── 3. Fetch the URL ──────────────────────────────────────────────────
935
+ // ── 2. Fetch the URL ──────────────────────────────────────────────────
693
936
  let upstream: Response;
694
937
  try {
695
- upstream = await fetch(parsed.data.url, {
938
+ upstream = await fetch(url, {
696
939
  signal: AbortSignal.timeout(URL_FETCH_TIMEOUT_MS),
697
940
  // SSRF guard: `validateGcsSignedUrl` only vetted the initial URL.
698
941
  // Default fetch behavior follows 3xx responses, which would let a
@@ -710,10 +953,10 @@ async function handleMigrationImportFromUrl(req: Request): Promise<Response> {
710
953
  },
711
954
  "Failed to fetch migration import URL",
712
955
  );
713
- return Response.json(
714
- { success: false, reason: "fetch_failed" },
715
- { status: 502 },
716
- );
956
+ throw new GcsImportError({
957
+ code: "fetch_failed",
958
+ message: err instanceof Error ? err.message : String(err),
959
+ });
717
960
  }
718
961
 
719
962
  if (!upstream.ok) {
@@ -731,14 +974,11 @@ async function handleMigrationImportFromUrl(req: Request): Promise<Response> {
731
974
  } catch {
732
975
  /* best effort */
733
976
  }
734
- return Response.json(
735
- {
736
- success: false,
737
- reason: "fetch_failed",
738
- upstream_status: upstream.status,
739
- },
740
- { status: 502 },
741
- );
977
+ throw new GcsImportError({
978
+ code: "fetch_failed",
979
+ message: `Upstream fetch returned ${upstream.status}`,
980
+ upstreamStatus: upstream.status,
981
+ });
742
982
  }
743
983
 
744
984
  if (!upstream.body) {
@@ -746,13 +986,13 @@ async function handleMigrationImportFromUrl(req: Request): Promise<Response> {
746
986
  { host: validated.host, path: validated.path },
747
987
  "Migration import URL fetch returned no body",
748
988
  );
749
- return Response.json(
750
- { success: false, reason: "fetch_failed" },
751
- { status: 502 },
752
- );
989
+ throw new GcsImportError({
990
+ code: "fetch_failed",
991
+ message: "Upstream fetch returned no body",
992
+ });
753
993
  }
754
994
 
755
- // ── 4. Stream the response through the importer ──────────────────────
995
+ // ── 3. Stream the response through the importer ──────────────────────
756
996
  // Convert the WHATWG ReadableStream from fetch() into a Node Readable so
757
997
  // the tar-stream / gunzip / hash-verifier pipeline inside
758
998
  // streamCommitImport can consume it via `.pipe()`.
@@ -764,8 +1004,8 @@ async function handleMigrationImportFromUrl(req: Request): Promise<Response> {
764
1004
  // from the upstream HTTP body (peer reset, abort mid-stream, etc.) with a
765
1005
  // known symbol. When that tagged error surfaces out of
766
1006
  // streamCommitImport's gunzip/tar pipeline, we can distinguish it from a
767
- // legitimate bundle-format failure and map it to 502 fetch_failed instead
768
- // of 500 extraction_failed — matching the OpenAPI contract for the URL
1007
+ // legitimate bundle-format failure and map it to `fetch_failed` instead
1008
+ // of `extraction_failed` — matching the OpenAPI contract for the URL
769
1009
  // body shape. We also propagate errors from the wrapper back to the
770
1010
  // upstream stream so its underlying connection is torn down cleanly.
771
1011
  //
@@ -781,7 +1021,7 @@ async function handleMigrationImportFromUrl(req: Request): Promise<Response> {
781
1021
  // `taggedSource`. The subsequent `close` on `upstreamNodeStream` is then a
782
1022
  // cascaded effect of our own teardown, NOT a real upstream failure — so
783
1023
  // we must NOT tag it as a fetch-body error, or local validation /
784
- // extraction errors would be masked as 502 fetch_failed.
1024
+ // extraction errors would be masked as fetch_failed.
785
1025
  let localTeardownInitiated = false;
786
1026
  upstreamNodeStream.on("end", () => {
787
1027
  upstreamEnded = true;
@@ -808,6 +1048,19 @@ async function handleMigrationImportFromUrl(req: Request): Promise<Response> {
808
1048
  taggedSource.destroy(err);
809
1049
  });
810
1050
  upstreamNodeStream.pipe(taggedSource);
1051
+ // Absorb stream errors on `taggedSource`. `streamCommitImport` does
1052
+ // several `await`s (workspace recovery, temp-dir mkdir) before
1053
+ // `parseVBundleStream(source)` attaches its own `source.on('error')`
1054
+ // listener. If the upstream socket is destroyed during that window,
1055
+ // the `upstreamNodeStream.on('close')` handler above calls
1056
+ // `taggedSource.destroy(err)` with no listener attached yet and the
1057
+ // error surfaces as unhandled. We don't need to act on it here — the
1058
+ // `kFetchBodyTornDown` flag has already been latched on the stream,
1059
+ // and the post-import branch below (`wasFetchBodyTornDown(taggedSource)`)
1060
+ // maps the failure to `fetch_failed`. Register this absorber
1061
+ // unconditionally so there is always at least one `'error'` listener
1062
+ // on the wrapper for the lifetime of the stream.
1063
+ taggedSource.on("error", () => {});
811
1064
  // Propagate wrapper teardown back to the upstream fetch body. When the
812
1065
  // streaming importer hits a validation/extraction error, it destroys
813
1066
  // `source` (which is `taggedSource`). Without this listener the
@@ -817,8 +1070,8 @@ async function handleMigrationImportFromUrl(req: Request): Promise<Response> {
817
1070
  // (malformed bundle, hash mismatch, size cap, etc.). We set
818
1071
  // `localTeardownInitiated` BEFORE destroying upstream so the resulting
819
1072
  // cascaded `close` on `upstreamNodeStream` isn't misclassified as a real
820
- // upstream failure (which would return 502 fetch_failed and mask the
821
- // actual validation error).
1073
+ // upstream failure (which would return fetch_failed and mask the actual
1074
+ // validation error).
822
1075
  taggedSource.on("close", () => {
823
1076
  if (!upstreamNodeStream.destroyed) {
824
1077
  localTeardownInitiated = true;
@@ -867,10 +1120,10 @@ async function handleMigrationImportFromUrl(req: Request): Promise<Response> {
867
1120
  },
868
1121
  "Upstream body stream failed mid-import",
869
1122
  );
870
- return Response.json(
871
- { success: false, reason: "fetch_failed" },
872
- { status: 502 },
873
- );
1123
+ throw new GcsImportError({
1124
+ code: "fetch_failed",
1125
+ message: err instanceof Error ? err.message : String(err),
1126
+ });
874
1127
  }
875
1128
  log.error(
876
1129
  {
@@ -880,11 +1133,10 @@ async function handleMigrationImportFromUrl(req: Request): Promise<Response> {
880
1133
  },
881
1134
  "streamCommitImport threw during URL-body import",
882
1135
  );
883
- return httpError(
884
- "INTERNAL_ERROR",
885
- err instanceof Error ? err.message : "Unexpected import error",
886
- 500,
887
- );
1136
+ throw new GcsImportError({
1137
+ code: "extraction_failed",
1138
+ message: err instanceof Error ? err.message : "Unexpected import error",
1139
+ });
888
1140
  }
889
1141
 
890
1142
  if (!result.ok) {
@@ -902,10 +1154,10 @@ async function handleMigrationImportFromUrl(req: Request): Promise<Response> {
902
1154
  },
903
1155
  "Upstream body stream failed mid-import (detected via result)",
904
1156
  );
905
- return Response.json(
906
- { success: false, reason: "fetch_failed" },
907
- { status: 502 },
908
- );
1157
+ throw new GcsImportError({
1158
+ code: "fetch_failed",
1159
+ message: "Upstream body stream failed mid-import",
1160
+ });
909
1161
  }
910
1162
  log.warn(
911
1163
  {
@@ -915,7 +1167,28 @@ async function handleMigrationImportFromUrl(req: Request): Promise<Response> {
915
1167
  },
916
1168
  "streamCommitImport returned failure during URL-body import",
917
1169
  );
918
- return importCommitFailureResponse(result);
1170
+ if (result.reason === "validation_failed") {
1171
+ throw new GcsImportError({
1172
+ code: "validation_failed",
1173
+ message: "Bundle validation failed",
1174
+ reason: result.reason,
1175
+ errors: result.errors,
1176
+ });
1177
+ }
1178
+ if (result.reason === "extraction_failed") {
1179
+ throw new GcsImportError({
1180
+ code: "extraction_failed",
1181
+ message: result.message,
1182
+ reason: result.reason,
1183
+ });
1184
+ }
1185
+ // write_failed
1186
+ throw new GcsImportError({
1187
+ code: "write_failed",
1188
+ message: result.message,
1189
+ reason: result.reason,
1190
+ partial_report: result.partial_report,
1191
+ });
919
1192
  }
920
1193
 
921
1194
  // Merge any warnings accumulated by the credential-import callback into
@@ -949,7 +1222,136 @@ async function handleMigrationImportFromUrl(req: Request): Promise<Response> {
949
1222
  "Migration import from URL complete",
950
1223
  );
951
1224
 
952
- return importCommitSuccessResponse(result.report, credentialsImported);
1225
+ return credentialsImported
1226
+ ? { ...result.report, credentialsImported }
1227
+ : { ...result.report };
1228
+ }
1229
+
1230
+ /**
1231
+ * Handle a JSON `{ "url": "..." }` body on POST /v1/migrations/import.
1232
+ *
1233
+ * Thin wrapper around `runGcsImport` that preserves the legacy synchronous
1234
+ * Response shapes. `handleMigrationImportFromGcs` below uses the same helper
1235
+ * asynchronously via the migration-job registry.
1236
+ */
1237
+ async function handleMigrationImportFromUrl(
1238
+ body: Record<string, unknown> | undefined,
1239
+ ): Promise<unknown> {
1240
+ const parsed = MigrationImportUrlBody.safeParse(body);
1241
+ if (!parsed.success) {
1242
+ throw new BadRequestError(
1243
+ "Request body must be { url: string } with a non-empty url",
1244
+ );
1245
+ }
1246
+
1247
+ try {
1248
+ const summary = await runGcsImport(parsed.data.url);
1249
+ const { credentialsImported, ...report } = summary;
1250
+ return importCommitSuccessResult(report, credentialsImported);
1251
+ } catch (err) {
1252
+ throwGcsImportError(err);
1253
+ }
1254
+ }
1255
+
1256
+ /**
1257
+ * Map a `runGcsImport` error (or any other thrown value) to a thrown
1258
+ * RouteError subclass or a plain-object error body. Always throws —
1259
+ * callers should invoke this in a catch block.
1260
+ */
1261
+ function throwGcsImportError(err: unknown): never {
1262
+ if (err instanceof GcsImportError) {
1263
+ if (err.code === "invalid_url") {
1264
+ throw new BadRequestError(err.message);
1265
+ }
1266
+ if (err.code === "fetch_failed") {
1267
+ throw new BadGatewayError(
1268
+ err.upstreamStatus
1269
+ ? `Upstream fetch returned ${err.upstreamStatus}`
1270
+ : err.message,
1271
+ );
1272
+ }
1273
+ if (err.code === "validation_failed") {
1274
+ // Validation failure is not an HTTP error — return structured body
1275
+ // with 200 (same as raw-bytes validate path).
1276
+ throw new BadRequestError(
1277
+ JSON.stringify({
1278
+ success: false,
1279
+ reason: "validation_failed",
1280
+ errors: err.errors ?? [],
1281
+ }),
1282
+ );
1283
+ }
1284
+ if (err.code === "extraction_failed") {
1285
+ throw new InternalError(err.message);
1286
+ }
1287
+ // write_failed
1288
+ throw new InternalError(err.message);
1289
+ }
1290
+
1291
+ log.error({ err }, "Unexpected error from runGcsImport");
1292
+ throw new InternalError(
1293
+ err instanceof Error ? err.message : "Unexpected import error",
1294
+ );
1295
+ }
1296
+
1297
+ /**
1298
+ * POST /v1/migrations/import-from-gcs
1299
+ *
1300
+ * Kick off an async bundle import from a signed GCS URL. Returns 202 with a
1301
+ * `job_id` the caller can poll via `GET /v1/migrations/jobs/:job_id`
1302
+ * (PR 4). 409 if another import is already pending or running.
1303
+ *
1304
+ * Auth: Requires settings.write scope. Allowed for actor, svc_gateway, svc_daemon, local.
1305
+ */
1306
+ export async function handleMigrationImportFromGcs({
1307
+ body,
1308
+ }: RouteHandlerArgs) {
1309
+ const parsed = MigrationImportFromGcsBody.safeParse(body);
1310
+ if (!parsed.success) {
1311
+ throw new BadRequestError(
1312
+ "Request body must be { bundle_url: string } with a valid URL",
1313
+ );
1314
+ }
1315
+
1316
+ const { bundle_url } = parsed.data;
1317
+
1318
+ // Synchronously validate the GCS URL before consuming the single
1319
+ // in-flight import slot.
1320
+ const validated = validateGcsSignedUrl(bundle_url, urlValidatorOptions);
1321
+ if (!validated.ok) {
1322
+ log.warn(
1323
+ { reason: validated.reason },
1324
+ "Rejected migration import-from-gcs bundle URL",
1325
+ );
1326
+ throw new RouteError(
1327
+ `Invalid bundle URL: ${validated.reason}`,
1328
+ "invalid_bundle_url",
1329
+ 400,
1330
+ );
1331
+ }
1332
+
1333
+ try {
1334
+ const job = migrationJobs.startJob("import", async (jobRecord) =>
1335
+ runGcsImport(bundle_url, jobRecord.id),
1336
+ );
1337
+ return {
1338
+ job_id: job.id,
1339
+ status: "pending" as const,
1340
+ type: "import" as const,
1341
+ };
1342
+ } catch (err) {
1343
+ if (err instanceof JobAlreadyInProgressError) {
1344
+ throw new RouteError(
1345
+ `Import already in progress: ${err.existingJobId}`,
1346
+ "import_in_progress",
1347
+ 409,
1348
+ );
1349
+ }
1350
+ log.error({ err }, "Unexpected error scheduling import-from-gcs job");
1351
+ throw new InternalError(
1352
+ err instanceof Error ? err.message : "Unexpected import error",
1353
+ );
1354
+ }
953
1355
  }
954
1356
 
955
1357
  // ---------------------------------------------------------------------------
@@ -1078,178 +1480,279 @@ function appendNewerMigrationWarningsIfAny(report: ImportCommitReport): void {
1078
1480
  }
1079
1481
 
1080
1482
  /**
1081
- * Build a success Response from an ImportCommitReport. The report fields
1082
- * are spread at the top level, with an optional `credentialsImported`
1083
- * summary alongside.
1483
+ * Build a success result from an ImportCommitReport.
1084
1484
  */
1085
- function importCommitSuccessResponse(
1485
+ function importCommitSuccessResult(
1086
1486
  report: ImportCommitReport,
1087
1487
  credentialsImported: CredentialImportSummary | undefined,
1088
- ): Response {
1089
- return Response.json({
1488
+ ): unknown {
1489
+ return {
1090
1490
  ...report,
1091
1491
  ...(credentialsImported ? { credentialsImported } : {}),
1092
- });
1492
+ };
1093
1493
  }
1094
1494
 
1095
1495
  /**
1096
- * Map an `ImportCommitResult` failure to the Response shape callers of
1097
- * `POST /v1/migrations/import` depend on. Status codes and body shapes
1098
- * are part of the public contract and must remain stable.
1496
+ * Map an `ImportCommitResult` failure to a thrown error or a plain-object
1497
+ * error body. Status codes and body shapes are part of the public contract
1498
+ * and must remain stable.
1099
1499
  */
1100
- function importCommitFailureResponse(
1500
+ function throwImportCommitFailure(
1101
1501
  result: Extract<ImportCommitResult, { ok: false }>,
1102
- ): Response {
1502
+ ): never {
1103
1503
  if (result.reason === "validation_failed") {
1104
- return Response.json({
1105
- success: false,
1106
- reason: "validation_failed",
1107
- errors: result.errors,
1108
- });
1504
+ // Validation failure uses 400 — structured body with error details
1505
+ throw new BadRequestError(
1506
+ JSON.stringify({
1507
+ success: false,
1508
+ reason: "validation_failed",
1509
+ errors: result.errors,
1510
+ }),
1511
+ );
1109
1512
  }
1110
1513
 
1111
1514
  if (result.reason === "extraction_failed") {
1112
- return Response.json(
1113
- {
1114
- success: false,
1115
- reason: "extraction_failed",
1116
- message: result.message,
1117
- },
1118
- { status: 500 },
1119
- );
1515
+ throw new InternalError(result.message);
1120
1516
  }
1121
1517
 
1122
1518
  // write_failed
1123
- return Response.json(
1124
- {
1125
- success: false,
1126
- reason: "write_failed",
1127
- message: result.message,
1128
- ...(result.partial_report
1129
- ? { partial_report: result.partial_report }
1130
- : {}),
1131
- },
1132
- { status: 500 },
1133
- );
1519
+ throw new InternalError(result.message);
1520
+ }
1521
+
1522
+ // ---------------------------------------------------------------------------
1523
+ // GET /v1/migrations/jobs/:job_id
1524
+ // ---------------------------------------------------------------------------
1525
+
1526
+ /**
1527
+ * GET /v1/migrations/jobs/:job_id
1528
+ *
1529
+ * Returns the current status of a migration job tracked by
1530
+ * `MigrationJobRegistry`. The response shape is a discriminated union on
1531
+ * `status`:
1532
+ *
1533
+ * - `{ job_id, type, status: "processing" }`
1534
+ * Covers both the internal `pending` and `running` states — collapsed
1535
+ * into a single wire value to match the platform's transport shape used
1536
+ * by `ExportStatusProcessingSerializer` / `ImportStatusProcessingSerializer`.
1537
+ * - `{ job_id, type, status: "complete", result }`
1538
+ * - `{ job_id, type, status: "failed", error, error_code, upstream_status? }`
1539
+ *
1540
+ * 404 `{ error: { code: "job_not_found" } }` when no job matches the id.
1541
+ */
1542
+ export async function handleMigrationJobStatus({
1543
+ pathParams,
1544
+ }: RouteHandlerArgs) {
1545
+ const jobId = pathParams?.job_id;
1546
+ if (!jobId) {
1547
+ throw new BadRequestError("Missing job_id path parameter");
1548
+ }
1549
+
1550
+ const job = migrationJobs.getJob(jobId);
1551
+ if (job === null) {
1552
+ throw new NotFoundError("Job not found");
1553
+ }
1554
+
1555
+ if (job.status === "complete") {
1556
+ return {
1557
+ job_id: job.id,
1558
+ type: job.type,
1559
+ status: "complete",
1560
+ result: job.result,
1561
+ };
1562
+ }
1563
+
1564
+ if (job.status === "failed") {
1565
+ const error = job.error;
1566
+ const result: Record<string, unknown> = {
1567
+ job_id: job.id,
1568
+ type: job.type,
1569
+ status: "failed",
1570
+ error: error?.message ?? "unknown",
1571
+ error_code: error?.code ?? "unknown",
1572
+ };
1573
+ if (error?.upstreamStatus !== undefined) {
1574
+ result.upstream_status = error.upstreamStatus;
1575
+ }
1576
+ return result;
1577
+ }
1578
+
1579
+ // pending or running — collapse to the platform's "processing" wire value.
1580
+ return {
1581
+ job_id: job.id,
1582
+ type: job.type,
1583
+ status: "processing",
1584
+ };
1134
1585
  }
1135
1586
 
1136
1587
  // ---------------------------------------------------------------------------
1137
1588
  // Route definitions
1138
1589
  // ---------------------------------------------------------------------------
1139
1590
 
1140
- export function migrationRouteDefinitions(): RouteDefinition[] {
1141
- return [
1142
- {
1143
- endpoint: "migrations/validate",
1144
- method: "POST",
1145
- summary: "Validate a .vbundle archive",
1146
- description:
1147
- "Upload a .vbundle archive for validation. Accepts raw binary or multipart form data.",
1148
- tags: ["migrations"],
1149
- responseBody: z.object({
1150
- is_valid: z.boolean(),
1151
- errors: z.array(z.unknown()),
1152
- manifest: z.object({}).passthrough(),
1153
- }),
1154
- handler: async ({ req }) => handleMigrationValidate(req),
1591
+ export const ROUTES: RouteDefinition[] = [
1592
+ {
1593
+ operationId: "migrations_validate_post",
1594
+ endpoint: "migrations/validate",
1595
+ method: "POST",
1596
+ summary: "Validate a .vbundle archive",
1597
+ description:
1598
+ "Upload a .vbundle archive for validation. Accepts raw binary or multipart form data.",
1599
+ tags: ["migrations"],
1600
+ responseBody: z.object({
1601
+ is_valid: z.boolean(),
1602
+ errors: z.array(z.unknown()),
1603
+ manifest: z.object({}).passthrough(),
1604
+ }),
1605
+ handler: handleMigrationValidate,
1606
+ },
1607
+ {
1608
+ operationId: "migrations_export_post",
1609
+ endpoint: "migrations/export",
1610
+ method: "POST",
1611
+ summary: "Export a .vbundle archive",
1612
+ description:
1613
+ "Generate and download a .vbundle archive of the assistant's data. Optional JSON body for metadata.",
1614
+ tags: ["migrations"],
1615
+ requestBody: z.object({
1616
+ description: z.string().describe("Human-readable export description"),
1617
+ }),
1618
+ handler: handleMigrationExport,
1619
+ },
1620
+ {
1621
+ operationId: "migrations_importpreflight_post",
1622
+ endpoint: "migrations/import-preflight",
1623
+ method: "POST",
1624
+ summary: "Dry-run import analysis",
1625
+ description:
1626
+ "Validate a .vbundle archive and return a report of what would change on import without modifying data.",
1627
+ tags: ["migrations"],
1628
+ responseBody: z.object({
1629
+ can_import: z.boolean(),
1630
+ summary: z.object({}).passthrough(),
1631
+ files: z.array(z.unknown()),
1632
+ conflicts: z.array(z.unknown()),
1633
+ manifest: z.object({}).passthrough(),
1634
+ }),
1635
+ handler: handleMigrationImportPreflight,
1636
+ },
1637
+ {
1638
+ operationId: "migrations_import_post",
1639
+ endpoint: "migrations/import",
1640
+ method: "POST",
1641
+ summary: "Import a .vbundle archive",
1642
+ description:
1643
+ "Commit a .vbundle archive import to disk — destructive. Accepts the bundle as raw bytes (application/octet-stream), multipart/form-data, or a JSON body with `{ url }` carrying a signed URL the daemon fetches.",
1644
+ tags: ["migrations"],
1645
+ requestBody: z.object({
1646
+ url: z
1647
+ .string()
1648
+ .url()
1649
+ .describe(
1650
+ "A signed GCS URL pointing to the .vbundle archive (JSON body path only).",
1651
+ ),
1652
+ }),
1653
+ additionalResponses: {
1654
+ "502": {
1655
+ description:
1656
+ "Upstream fetch failed (URL body only).",
1657
+ },
1155
1658
  },
1156
- {
1157
- endpoint: "migrations/export",
1158
- method: "POST",
1159
- summary: "Export a .vbundle archive",
1160
- description:
1161
- "Generate and download a .vbundle archive of the assistant's data. Optional JSON body for metadata.",
1162
- tags: ["migrations"],
1163
- requestBody: z.object({
1164
- description: z.string().describe("Human-readable export description"),
1165
- }),
1166
- handler: async ({ req }) => handleMigrationExport(req),
1659
+ responseBody: z.object({
1660
+ success: z.boolean(),
1661
+ summary: z.object({}).passthrough(),
1662
+ files: z.array(z.unknown()),
1663
+ manifest: z.object({}).passthrough(),
1664
+ warnings: z.array(z.unknown()),
1665
+ }),
1666
+ handler: handleMigrationImport,
1667
+ },
1668
+ {
1669
+ operationId: "migrations_exporttogcs_post",
1670
+ endpoint: "migrations/export-to-gcs",
1671
+ method: "POST",
1672
+ summary: "Start an async export streamed to a GCS signed URL",
1673
+ description:
1674
+ "Kick off a background export job that PUTs a freshly-built .vbundle archive to the supplied GCS signed URL. Returns 202 with a job_id the caller can poll via the job-status endpoint. Fails fast with 409 if another export job is already pending or running.",
1675
+ tags: ["migrations"],
1676
+ requestBody: z.object({
1677
+ upload_url: z
1678
+ .string()
1679
+ .url()
1680
+ .describe("Signed GCS PUT URL that receives the exported bundle."),
1681
+ description: z
1682
+ .string()
1683
+ .optional()
1684
+ .describe("Human-readable export description."),
1685
+ }),
1686
+ responseStatus: "202",
1687
+ responseBody: z.object({
1688
+ job_id: z.string(),
1689
+ status: z.literal("pending"),
1690
+ type: z.literal("export"),
1691
+ }),
1692
+ handler: handleMigrationExportToGcs,
1693
+ },
1694
+ {
1695
+ operationId: "migrations_importfromgcs_post",
1696
+ endpoint: "migrations/import-from-gcs",
1697
+ method: "POST",
1698
+ summary: "Start an async .vbundle import from a signed GCS URL",
1699
+ description:
1700
+ "Schedule a background import job that fetches the bundle at `bundle_url` and streams it through the importer. Returns 202 with a `job_id`; poll `GET /v1/migrations/jobs/{job_id}` for status. 409 if another import is already in flight.",
1701
+ tags: ["migrations"],
1702
+ requestBody: z.object({
1703
+ bundle_url: z.string().url(),
1704
+ }),
1705
+ responseStatus: "202",
1706
+ responseBody: z.object({
1707
+ job_id: z.string(),
1708
+ status: z.literal("pending"),
1709
+ type: z.literal("import"),
1710
+ }),
1711
+ additionalResponses: {
1712
+ "409": {
1713
+ description:
1714
+ "Another import job is already pending or running.",
1715
+ },
1167
1716
  },
1168
- {
1169
- endpoint: "migrations/import-preflight",
1170
- method: "POST",
1171
- summary: "Dry-run import analysis",
1172
- description:
1173
- "Validate a .vbundle archive and return a report of what would change on import without modifying data.",
1174
- tags: ["migrations"],
1175
- responseBody: z.object({
1176
- can_import: z.boolean(),
1177
- summary: z.object({}).passthrough(),
1178
- files: z.array(z.unknown()),
1179
- conflicts: z.array(z.unknown()),
1180
- manifest: z.object({}).passthrough(),
1717
+ handler: handleMigrationImportFromGcs,
1718
+ },
1719
+ {
1720
+ operationId: "migrations_jobs_by_job_id_get",
1721
+ endpoint: "migrations/jobs/:job_id",
1722
+ method: "GET",
1723
+ summary: "Get migration job status",
1724
+ description:
1725
+ "Return the current status of an async migration job (export or import). The response discriminates on `status`: `processing` (pending or running), `complete` (with `result`), or `failed` (with `error`, `error_code`, optional `upstream_status`).",
1726
+ tags: ["migrations"],
1727
+ pathParams: [
1728
+ { name: "job_id", description: "The migration job ID to query." },
1729
+ ],
1730
+ responseBody: z.discriminatedUnion("status", [
1731
+ z.object({
1732
+ job_id: z.string(),
1733
+ type: z.enum(["export", "import"]),
1734
+ status: z.literal("processing"),
1181
1735
  }),
1182
- handler: async ({ req }) => handleMigrationImportPreflight(req),
1183
- },
1184
- {
1185
- endpoint: "migrations/import",
1186
- method: "POST",
1187
- summary: "Import a .vbundle archive",
1188
- description:
1189
- "Commit a .vbundle archive import to disk — destructive. Accepts the bundle as raw bytes (application/octet-stream), multipart/form-data, or a JSON body carrying a signed URL the daemon fetches and streams through the importer.",
1190
- tags: ["migrations"],
1191
- requestBodies: [
1192
- {
1193
- contentType: "application/octet-stream",
1194
- schema: {
1195
- type: "string",
1196
- format: "binary",
1197
- description: "Raw .vbundle archive bytes.",
1198
- },
1199
- },
1200
- {
1201
- contentType: "multipart/form-data",
1202
- schema: {
1203
- type: "object",
1204
- properties: {
1205
- file: {
1206
- type: "string",
1207
- format: "binary",
1208
- description: "The .vbundle archive uploaded as a file field.",
1209
- },
1210
- },
1211
- required: ["file"],
1212
- },
1213
- },
1214
- {
1215
- contentType: "application/json",
1216
- schema: {
1217
- type: "object",
1218
- properties: {
1219
- url: {
1220
- type: "string",
1221
- format: "uri",
1222
- description:
1223
- "A signed GCS URL pointing to the .vbundle archive. The daemon fetches the URL and streams the body through the importer.",
1224
- },
1225
- },
1226
- required: ["url"],
1227
- },
1228
- },
1229
- ],
1230
- additionalResponses: {
1231
- "502": {
1232
- description:
1233
- "Upstream fetch failed (URL body only). Body shape: { success: false, reason: 'fetch_failed', upstream_status?: number }.",
1234
- schema: {
1235
- type: "object",
1236
- properties: {
1237
- success: { type: "boolean" },
1238
- reason: { type: "string", enum: ["fetch_failed"] },
1239
- upstream_status: { type: "integer" },
1240
- },
1241
- required: ["success", "reason"],
1242
- },
1243
- },
1244
- },
1245
- responseBody: z.object({
1246
- success: z.boolean(),
1247
- summary: z.object({}).passthrough(),
1248
- files: z.array(z.unknown()),
1249
- manifest: z.object({}).passthrough(),
1250
- warnings: z.array(z.unknown()),
1736
+ z.object({
1737
+ job_id: z.string(),
1738
+ type: z.enum(["export", "import"]),
1739
+ status: z.literal("complete"),
1740
+ result: z.unknown(),
1741
+ }),
1742
+ z.object({
1743
+ job_id: z.string(),
1744
+ type: z.enum(["export", "import"]),
1745
+ status: z.literal("failed"),
1746
+ error: z.string(),
1747
+ error_code: z.string(),
1748
+ upstream_status: z.number().int().optional(),
1251
1749
  }),
1252
- handler: async ({ req }) => handleMigrationImport(req),
1750
+ ]),
1751
+ additionalResponses: {
1752
+ "404": {
1753
+ description: "No job matches the given id.",
1754
+ },
1253
1755
  },
1254
- ];
1255
- }
1756
+ handler: handleMigrationJobStatus,
1757
+ },
1758
+ ];