@vellumai/assistant 0.6.4 → 0.6.6

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 (1008) hide show
  1. package/.prettierignore +5 -0
  2. package/AGENTS.md +9 -1
  3. package/ARCHITECTURE.md +43 -49
  4. package/Dockerfile +17 -3
  5. package/README.md +3 -4
  6. package/__tests__/permissions/gateway-threshold-reader.test.ts +283 -0
  7. package/bun.lock +8 -3
  8. package/docs/architecture/integrations.md +33 -59
  9. package/docs/architecture/memory.md +25 -30
  10. package/docs/architecture/security.md +19 -18
  11. package/docs/browser-use-architecture-phase2.md +63 -20
  12. package/docs/error-handling.md +111 -0
  13. package/docs/plugins.md +761 -0
  14. package/docs/skills.md +10 -10
  15. package/docs/stt-provider-onboarding.md +2 -1
  16. package/examples/plugins/echo/README.md +132 -0
  17. package/examples/plugins/echo/package.json +17 -0
  18. package/examples/plugins/echo/register.ts +187 -0
  19. package/knip.json +9 -2
  20. package/node_modules/@vellumai/ces-contracts/package.json +2 -1
  21. package/node_modules/@vellumai/ces-contracts/src/__tests__/trust-rules.test.ts +471 -0
  22. package/node_modules/@vellumai/ces-contracts/src/trust-rules.ts +398 -4
  23. package/node_modules/@vellumai/credential-storage/bun.lock +2 -2
  24. package/node_modules/@vellumai/credential-storage/package.json +2 -2
  25. package/node_modules/@vellumai/credential-storage/src/oauth-runtime.ts +20 -2
  26. package/node_modules/@vellumai/egress-proxy/bun.lock +2 -2
  27. package/node_modules/@vellumai/egress-proxy/package.json +2 -2
  28. package/node_modules/@vellumai/egress-proxy/src/types.ts +19 -0
  29. package/openapi.yaml +334 -78
  30. package/package.json +6 -3
  31. package/scripts/generate-openapi.ts +50 -11
  32. package/src/__tests__/agent-loop-callsite-precedence.test.ts +318 -0
  33. package/src/__tests__/agent-loop-sentry-hygiene.test.ts +137 -0
  34. package/src/__tests__/agent-loop.test.ts +112 -1
  35. package/src/__tests__/anthropic-error-formatting.test.ts +98 -0
  36. package/src/__tests__/anthropic-provider.test.ts +171 -2
  37. package/src/__tests__/app-compiler.test.ts +57 -0
  38. package/src/__tests__/approval-cascade.test.ts +36 -10
  39. package/src/__tests__/approval-routes-http.test.ts +134 -10
  40. package/src/__tests__/assistant-attachments.test.ts +44 -0
  41. package/src/__tests__/assistant-feature-flags-integration.test.ts +29 -0
  42. package/src/__tests__/auto-analysis-end-to-end.test.ts +1 -0
  43. package/src/__tests__/avatar-generator.test.ts +4 -2
  44. package/src/__tests__/browser-fill-credential.test.ts +1 -1
  45. package/src/__tests__/browser-identifier-parity-guard.test.ts +53 -0
  46. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +23 -33
  47. package/src/__tests__/browser-skill-endstate.test.ts +51 -182
  48. package/src/__tests__/btw-routes.test.ts +47 -1
  49. package/src/__tests__/bundled-asset.test.ts +6 -6
  50. package/src/__tests__/call-controller.test.ts +1 -2
  51. package/src/__tests__/call-site-routing-provider.test.ts +214 -0
  52. package/src/__tests__/catalog-cache.test.ts +96 -4
  53. package/src/__tests__/channel-approval-routes.test.ts +4 -4
  54. package/src/__tests__/channel-reply-delivery.test.ts +300 -2
  55. package/src/__tests__/checker.test.ts +870 -655
  56. package/src/__tests__/circuit-breaker-pipeline.test.ts +406 -0
  57. package/src/__tests__/cli-command-risk-guard.test.ts +30 -33
  58. package/src/__tests__/compaction-events.test.ts +501 -0
  59. package/src/__tests__/compaction-pipeline.test.ts +210 -0
  60. package/src/__tests__/compaction-strip-metadata-clear.test.ts +181 -0
  61. package/src/__tests__/compaction-timeout-recovery.test.ts +262 -0
  62. package/src/__tests__/compaction.benchmark.test.ts +1 -1
  63. package/src/__tests__/config-analysis.test.ts +11 -28
  64. package/src/__tests__/config-loader-backfill.test.ts +174 -0
  65. package/src/__tests__/config-loader-corrupt.test.ts +183 -0
  66. package/src/__tests__/config-loader-quarantine-bulletin.test.ts +202 -0
  67. package/src/__tests__/config-model-image-provider.test.ts +110 -0
  68. package/src/__tests__/config-schema-cmd.test.ts +11 -5
  69. package/src/__tests__/config-schema.test.ts +440 -114
  70. package/src/__tests__/config-watcher-cleanup-throttle.test.ts +0 -4
  71. package/src/__tests__/config-watcher.test.ts +2 -2
  72. package/src/__tests__/contact-store-user-file.test.ts +72 -73
  73. package/src/__tests__/contacts-tools.test.ts +26 -0
  74. package/src/__tests__/contacts-write.test.ts +4 -4
  75. package/src/__tests__/context-overflow-policy.test.ts +7 -7
  76. package/src/__tests__/context-token-estimator.test.ts +191 -1
  77. package/src/__tests__/context-window-manager.test.ts +883 -4
  78. package/src/__tests__/conversation-abort-tool-results.test.ts +32 -15
  79. package/src/__tests__/conversation-agent-loop-overflow.test.ts +86 -46
  80. package/src/__tests__/conversation-agent-loop.test.ts +435 -216
  81. package/src/__tests__/conversation-attachments.test.ts +1 -1
  82. package/src/__tests__/conversation-confirmation-signals.test.ts +36 -10
  83. package/src/__tests__/conversation-error.test.ts +37 -6
  84. package/src/__tests__/conversation-history-web-search.test.ts +7 -0
  85. package/src/__tests__/conversation-init.benchmark.test.ts +34 -12
  86. package/src/__tests__/conversation-lifecycle.test.ts +336 -0
  87. package/src/__tests__/conversation-load-history-repair.test.ts +27 -10
  88. package/src/__tests__/conversation-pairing.test.ts +174 -10
  89. package/src/__tests__/conversation-pre-run-repair.test.ts +32 -15
  90. package/src/__tests__/conversation-process-callsite.test.ts +309 -0
  91. package/src/__tests__/conversation-provider-retry-repair.test.ts +44 -21
  92. package/src/__tests__/conversation-queue.test.ts +68 -38
  93. package/src/__tests__/conversation-routes-disk-view.test.ts +36 -7
  94. package/src/__tests__/conversation-routes-slash-commands.test.ts +31 -3
  95. package/src/__tests__/conversation-runtime-assembly.test.ts +2877 -152
  96. package/src/__tests__/conversation-runtime-workspace.test.ts +35 -50
  97. package/src/__tests__/conversation-seed-composer.test.ts +2 -2
  98. package/src/__tests__/conversation-skill-tools.test.ts +12 -146
  99. package/src/__tests__/conversation-slash-queue.test.ts +39 -19
  100. package/src/__tests__/conversation-slash-unknown.test.ts +53 -16
  101. package/src/__tests__/conversation-speed-override.test.ts +36 -12
  102. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +1035 -0
  103. package/src/__tests__/conversation-surfaces-standalone.test.ts +630 -0
  104. package/src/__tests__/conversation-title-service.test.ts +118 -2
  105. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +41 -2
  106. package/src/__tests__/conversation-tool-setup-batch-authorized.test.ts +1 -1
  107. package/src/__tests__/conversation-unread-route.test.ts +2 -2
  108. package/src/__tests__/conversation-usage.test.ts +4 -2
  109. package/src/__tests__/conversation-workspace-cache-state.test.ts +33 -9
  110. package/src/__tests__/conversation-workspace-injection.test.ts +46 -15
  111. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +46 -15
  112. package/src/__tests__/credential-broker-browser-fill.test.ts +110 -0
  113. package/src/__tests__/credential-health-service.test.ts +78 -9
  114. package/src/__tests__/credential-security-invariants.test.ts +5 -2
  115. package/src/__tests__/credential-storage-oauth-compat.test.ts +18 -0
  116. package/src/__tests__/credential-storage-static-compat.test.ts +28 -0
  117. package/src/__tests__/credential-vault-unit.test.ts +135 -19
  118. package/src/__tests__/credentials-cli.test.ts +1 -9
  119. package/src/__tests__/cross-provider-web-search.test.ts +84 -0
  120. package/src/__tests__/daemon-server-persist-and-process-callsite.test.ts +92 -0
  121. package/src/__tests__/db-schedule-syntax-migration.test.ts +1 -0
  122. package/src/__tests__/delete-propagation.test.ts +437 -0
  123. package/src/__tests__/dm-backfill.test.ts +417 -0
  124. package/src/__tests__/dm-persistence.test.ts +227 -0
  125. package/src/__tests__/edit-propagation.test.ts +280 -0
  126. package/src/__tests__/empty-response-pipeline.test.ts +305 -0
  127. package/src/__tests__/ephemeral-permissions.test.ts +93 -3
  128. package/src/__tests__/estimator-calibration-integration.test.ts +208 -0
  129. package/src/__tests__/estimator-calibration.test.ts +213 -0
  130. package/src/__tests__/extension-id-sync-guard.test.ts +29 -10
  131. package/src/__tests__/file-write-tool.test.ts +151 -1
  132. package/src/__tests__/filing-service.test.ts +255 -0
  133. package/src/__tests__/first-greeting.test.ts +247 -5
  134. package/src/__tests__/gemini-provider.test.ts +0 -3
  135. package/src/__tests__/guardian-grant-minting.test.ts +8 -0
  136. package/src/__tests__/headless-browser-interactions.test.ts +1 -1
  137. package/src/__tests__/headless-browser-mode.test.ts +57 -0
  138. package/src/__tests__/heartbeat-service.test.ts +96 -15
  139. package/src/__tests__/history-repair-pipeline.test.ts +399 -0
  140. package/src/__tests__/host-browser-e2e-cloud.test.ts +307 -0
  141. package/src/__tests__/host-browser-e2e-self-hosted.test.ts +3 -3
  142. package/src/__tests__/host-proxy-interface.test.ts +36 -2
  143. package/src/__tests__/host-shell-tool.test.ts +124 -18
  144. package/src/__tests__/http-user-message-parity.test.ts +29 -1
  145. package/src/__tests__/image-credentials.test.ts +137 -0
  146. package/src/__tests__/image-service-dispatcher.test.ts +186 -0
  147. package/src/__tests__/inbound-slack-persistence.test.ts +340 -0
  148. package/src/__tests__/injector-chain.test.ts +526 -0
  149. package/src/__tests__/intent-routing.test.ts +1 -66
  150. package/src/__tests__/llm-call-pipeline.test.ts +285 -0
  151. package/src/__tests__/llm-catalog-parity.test.ts +174 -0
  152. package/src/__tests__/llm-context-normalization.test.ts +121 -0
  153. package/src/__tests__/llm-resolver.test.ts +214 -0
  154. package/src/__tests__/llm-schema.test.ts +223 -0
  155. package/src/__tests__/managed-proxy-context.test.ts +6 -2
  156. package/src/__tests__/media-generate-image.test.ts +119 -13
  157. package/src/__tests__/memory-retrieval-pipeline.test.ts +401 -0
  158. package/src/__tests__/memory-upsert-concurrency.test.ts +1 -0
  159. package/src/__tests__/messaging-skill-split.test.ts +3 -34
  160. package/src/__tests__/migration-import-from-url.test.ts +621 -0
  161. package/src/__tests__/model-intents.test.ts +11 -83
  162. package/src/__tests__/notification-broadcaster.test.ts +3 -3
  163. package/src/__tests__/notification-decision-fallback.test.ts +0 -10
  164. package/src/__tests__/notification-decision-identity.test.ts +0 -9
  165. package/src/__tests__/notification-decision-recipient-context.test.ts +0 -9
  166. package/src/__tests__/notification-decision-strategy.test.ts +0 -11
  167. package/src/__tests__/notification-schedule-notify-dedup.test.ts +108 -0
  168. package/src/__tests__/oauth-apps-routes.test.ts +1 -1
  169. package/src/__tests__/oauth-cli.test.ts +14 -12
  170. package/src/__tests__/oauth-connect-orchestrator.test.ts +4 -13
  171. package/src/__tests__/oauth-provider-serializer.test.ts +6 -4
  172. package/src/__tests__/oauth-provider-visibility.test.ts +3 -5
  173. package/src/__tests__/oauth-providers-routes.test.ts +3 -2
  174. package/src/__tests__/oauth-store.test.ts +46 -78
  175. package/src/__tests__/oauth2-gateway-transport.test.ts +8 -3
  176. package/src/__tests__/oauth2-refresh-retry.test.ts +279 -0
  177. package/src/__tests__/onboarding-template-contract.test.ts +16 -64
  178. package/src/__tests__/openai-image-service.test.ts +368 -0
  179. package/src/__tests__/openai-provider.test.ts +7 -0
  180. package/src/__tests__/openai-responses-provider.test.ts +396 -0
  181. package/src/__tests__/openrouter-provider-only.test.ts +135 -0
  182. package/src/__tests__/outbound-slack-persistence.test.ts +293 -0
  183. package/src/__tests__/overflow-reduce-pipeline.test.ts +676 -0
  184. package/src/__tests__/permission-checker-host-gate.test.ts +1 -25
  185. package/src/__tests__/permission-mode.test.ts +16 -0
  186. package/src/__tests__/permission-types.test.ts +0 -1
  187. package/src/__tests__/persist-onboarding-artifacts.test.ts +266 -0
  188. package/src/__tests__/persistence-pipeline.test.ts +377 -0
  189. package/src/__tests__/persona-resolver.test.ts +13 -13
  190. package/src/__tests__/pipeline-runner.test.ts +565 -0
  191. package/src/__tests__/pkb-autoinject.test.ts +37 -1
  192. package/src/__tests__/platform-bash-auto-approve.test.ts +1 -1
  193. package/src/__tests__/platform.test.ts +5 -2
  194. package/src/__tests__/plugin-bootstrap.test.ts +483 -0
  195. package/src/__tests__/plugin-registry.test.ts +273 -0
  196. package/src/__tests__/plugin-route-contribution.test.ts +288 -0
  197. package/src/__tests__/plugin-skill-contribution.test.ts +367 -0
  198. package/src/__tests__/plugin-tool-contribution.test.ts +286 -0
  199. package/src/__tests__/plugin-types.test.ts +320 -0
  200. package/src/__tests__/pricing.test.ts +93 -14
  201. package/src/__tests__/profiler-routes.test.ts +1 -1
  202. package/src/__tests__/provider-commit-message-generator.test.ts +14 -84
  203. package/src/__tests__/provider-env-vars-scope.test.ts +52 -0
  204. package/src/__tests__/provider-error-scenarios.test.ts +135 -6
  205. package/src/__tests__/provider-managed-proxy-integration.test.ts +42 -11
  206. package/src/__tests__/provider-registry-ollama.test.ts +1 -2
  207. package/src/__tests__/proxy-approval-callback.test.ts +69 -9
  208. package/src/__tests__/reaction-persistence.test.ts +561 -0
  209. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +1 -0
  210. package/src/__tests__/registry.test.ts +0 -2
  211. package/src/__tests__/relay-server.test.ts +1 -1
  212. package/src/__tests__/require-fresh-approval.test.ts +1 -1
  213. package/src/__tests__/retry-openrouter-only-normalization.test.ts +136 -0
  214. package/src/__tests__/retry-thinking-tool-choice.test.ts +226 -0
  215. package/src/__tests__/risk-classifier-parity.test.ts +230 -0
  216. package/src/__tests__/sanitize-config-for-transfer.test.ts +78 -1
  217. package/src/__tests__/schedule-routes.test.ts +131 -1
  218. package/src/__tests__/scheduler-recurrence.test.ts +14 -70
  219. package/src/__tests__/scheduler-reuse-conversation.test.ts +10 -50
  220. package/src/__tests__/secret-detection-handler.test.ts +0 -10
  221. package/src/__tests__/secret-ingress-http.test.ts +28 -0
  222. package/src/__tests__/secret-prompter-channel-fallback.test.ts +125 -0
  223. package/src/__tests__/secret-routes-managed-proxy.test.ts +2 -3
  224. package/src/__tests__/secret-scanner-executor.test.ts +1 -1
  225. package/src/__tests__/send-endpoint-busy.test.ts +29 -1
  226. package/src/__tests__/server-history-render.test.ts +31 -0
  227. package/src/__tests__/shell-identity.test.ts +0 -134
  228. package/src/__tests__/shell-parser-property.test.ts +13 -13
  229. package/src/__tests__/skill-cache-store.test.ts +182 -0
  230. package/src/__tests__/skills.test.ts +19 -33
  231. package/src/__tests__/slack-app-setup-skill-regression.test.ts +3 -1
  232. package/src/__tests__/slack-skill.test.ts +3 -8
  233. package/src/__tests__/starter-bundle.test.ts +35 -0
  234. package/src/__tests__/subagent-call-site-routing.test.ts +280 -0
  235. package/src/__tests__/suggestion-routes.test.ts +259 -3
  236. package/src/__tests__/system-prompt.test.ts +22 -35
  237. package/src/__tests__/task-memory-cleanup.test.ts +1 -0
  238. package/src/__tests__/task-runner.test.ts +3 -1
  239. package/src/__tests__/task-scheduler.test.ts +3 -15
  240. package/src/__tests__/tcc-sandbox-deny.test.ts +198 -0
  241. package/src/__tests__/terminal-tools.test.ts +8 -0
  242. package/src/__tests__/test-preload.ts +11 -0
  243. package/src/__tests__/test-support/browser-skill-harness.ts +2 -52
  244. package/src/__tests__/thread-backfill.test.ts +941 -0
  245. package/src/__tests__/title-generate-pipeline.test.ts +224 -0
  246. package/src/__tests__/token-estimate-pipeline.test.ts +431 -0
  247. package/src/__tests__/tool-error-pipeline.test.ts +244 -0
  248. package/src/__tests__/tool-execute-pipeline.test.ts +431 -0
  249. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -8
  250. package/src/__tests__/tool-executor-lifecycle-events.test.ts +2 -2
  251. package/src/__tests__/tool-executor-shell-integration.test.ts +7 -10
  252. package/src/__tests__/tool-executor.test.ts +201 -94
  253. package/src/__tests__/tool-result-truncate-pipeline.test.ts +356 -0
  254. package/src/__tests__/tool-result-truncation.test.ts +0 -110
  255. package/src/__tests__/trust-store.test.ts +442 -109
  256. package/src/__tests__/update-bulletin-job.test.ts +389 -0
  257. package/src/__tests__/usage-cache-backfill-migration.test.ts +3 -1
  258. package/src/__tests__/user-plugin-loader.test.ts +191 -0
  259. package/src/__tests__/verification-control-plane-policy.test.ts +1 -22
  260. package/src/__tests__/voice-session-bridge.test.ts +39 -0
  261. package/src/__tests__/volume-security-guard.test.ts +3 -2
  262. package/src/__tests__/web-search-history.test.ts +337 -0
  263. package/src/__tests__/workspace-migration-039-drop-legacy-llm-keys.test.ts +343 -0
  264. package/src/__tests__/workspace-migration-043-release-notes-latex-rendering.test.ts +202 -0
  265. package/src/__tests__/workspace-migration-045-release-notes-meet-avatar.test.ts +210 -0
  266. package/src/__tests__/workspace-migration-046-seed-conversation-starters-callsite.test.ts +185 -0
  267. package/src/__tests__/workspace-migration-049-release-notes-default-sonnet.test.ts +100 -0
  268. package/src/__tests__/workspace-migration-050-seed-main-agent-opus-callsite.test.ts +171 -0
  269. package/src/__tests__/workspace-migration-051-seed-conversation-summarization-callsite.test.ts +252 -0
  270. package/src/__tests__/workspace-migration-drop-user-md.test.ts +11 -11
  271. package/src/__tests__/workspace-migration-remove-hooks.test.ts +99 -0
  272. package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +841 -0
  273. package/src/__tests__/workspace-policy.test.ts +22 -16
  274. package/src/acp/client-handler.ts +1 -2
  275. package/src/agent/loop.ts +545 -115
  276. package/src/approvals/__tests__/guardian-feed-event.test.ts +304 -0
  277. package/src/approvals/guardian-request-resolvers.ts +80 -0
  278. package/src/avatar/resvg-lazy.test.ts +136 -0
  279. package/src/avatar/resvg-lazy.ts +82 -9
  280. package/src/avatar/traits-png-sync.ts +21 -1
  281. package/src/backup/__tests__/backup-worker.test.ts +2 -13
  282. package/src/backup/backup-worker.ts +3 -15
  283. package/src/browser/__tests__/operations.test.ts +163 -0
  284. package/src/browser/identifiers.ts +51 -0
  285. package/src/browser/operations.ts +660 -0
  286. package/src/browser/types.ts +81 -0
  287. package/src/bundler/app-compiler.ts +84 -1
  288. package/src/calls/call-state.ts +2 -2
  289. package/src/calls/guardian-question-copy.ts +2 -2
  290. package/src/calls/telephony-stt-routing.ts +1 -1
  291. package/src/calls/voice-session-bridge.ts +1 -0
  292. package/src/channels/__tests__/types.test.ts +3 -3
  293. package/src/channels/types.ts +6 -4
  294. package/src/cli/AGENTS.md +1 -1
  295. package/src/cli/__tests__/notifications.test.ts +87 -211
  296. package/src/cli/commands/__tests__/attachment.test.ts +438 -0
  297. package/src/cli/commands/__tests__/backup.test.ts +1 -1
  298. package/src/cli/commands/__tests__/browser.test.ts +554 -0
  299. package/src/cli/commands/__tests__/cache.test.ts +623 -0
  300. package/src/cli/commands/__tests__/email-list.test.ts +6 -0
  301. package/src/cli/commands/__tests__/email-send.test.ts +93 -1
  302. package/src/cli/commands/__tests__/image-generation.test.ts +886 -0
  303. package/src/cli/commands/__tests__/inference-send.test.ts +463 -0
  304. package/src/cli/commands/__tests__/stt-transcribe.test.ts +454 -0
  305. package/src/cli/commands/__tests__/task.test.ts +913 -0
  306. package/src/cli/commands/__tests__/tts-synthesize.test.ts +606 -0
  307. package/src/cli/commands/__tests__/ui-confirm.test.ts +650 -0
  308. package/src/cli/commands/__tests__/ui.test.ts +1215 -0
  309. package/src/cli/commands/__tests__/watchers.test.ts +716 -0
  310. package/src/cli/commands/attachment.ts +182 -0
  311. package/src/cli/commands/backup.ts +2 -2
  312. package/src/cli/commands/browser.ts +350 -0
  313. package/src/cli/commands/cache.ts +341 -0
  314. package/src/cli/commands/clients.ts +138 -0
  315. package/src/cli/commands/completions.ts +2 -12
  316. package/src/cli/commands/config.ts +6 -6
  317. package/src/cli/commands/conversations-import.ts +347 -0
  318. package/src/cli/commands/conversations.ts +69 -8
  319. package/src/cli/commands/email.ts +234 -194
  320. package/src/cli/commands/image-generation.ts +299 -0
  321. package/src/cli/commands/inference.ts +200 -0
  322. package/src/cli/commands/memory.ts +127 -17
  323. package/src/cli/commands/notifications.ts +68 -103
  324. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +1 -1
  325. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -1
  326. package/src/cli/commands/oauth/connect.ts +2 -2
  327. package/src/cli/commands/oauth/providers.ts +176 -8
  328. package/src/cli/commands/oauth/status.ts +46 -36
  329. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -1
  330. package/src/cli/commands/platform/__tests__/connect.test.ts +0 -1
  331. package/src/cli/commands/platform/__tests__/disconnect.test.ts +0 -1
  332. package/src/cli/commands/platform/__tests__/status.test.ts +0 -1
  333. package/src/cli/commands/skills.ts +3 -4
  334. package/src/cli/commands/stt.ts +339 -0
  335. package/src/cli/commands/task.ts +795 -0
  336. package/src/cli/commands/trust.ts +50 -19
  337. package/src/cli/commands/tts.ts +273 -0
  338. package/src/cli/commands/ui.ts +670 -0
  339. package/src/cli/commands/watchers.ts +509 -0
  340. package/src/cli/lib/daemon-credential-client.ts +0 -19
  341. package/src/cli/program.ts +39 -24
  342. package/src/cli.ts +0 -37
  343. package/src/config/__tests__/backup-schema.test.ts +7 -2
  344. package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
  345. package/src/config/bundled-skills/app-builder/references/WIDGETS.md +10 -10
  346. package/src/config/bundled-skills/contacts/tools/contact-merge.ts +66 -87
  347. package/src/config/bundled-skills/contacts/tools/contact-search.ts +28 -51
  348. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +22 -40
  349. package/src/config/bundled-skills/image-studio/SKILL.md +2 -1
  350. package/src/config/bundled-skills/image-studio/TOOLS.json +2 -1
  351. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +23 -39
  352. package/src/config/bundled-skills/media-processing/services/reduce.ts +1 -1
  353. package/src/config/bundled-skills/messaging/SKILL.md +5 -5
  354. package/src/config/bundled-skills/messaging/TOOLS.json +4 -0
  355. package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +207 -0
  356. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +20 -1
  357. package/src/config/bundled-skills/messaging/tools/messaging-read.ts +15 -1
  358. package/src/config/bundled-skills/messaging/tools/messaging-search.ts +21 -1
  359. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +69 -12
  360. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +9 -8
  361. package/src/config/bundled-skills/schedule/SKILL.md +8 -3
  362. package/src/config/bundled-skills/schedule/TOOLS.json +15 -7
  363. package/src/config/bundled-skills/schedule/references/SCRIPT_MODE_PATTERNS.md +59 -0
  364. package/src/config/bundled-skills/settings/TOOLS.json +3 -3
  365. package/src/config/bundled-tool-registry.ts +0 -190
  366. package/src/config/env.ts +7 -2
  367. package/src/config/feature-flag-registry.json +42 -10
  368. package/src/config/llm-resolver.ts +128 -0
  369. package/src/config/loader.ts +194 -10
  370. package/src/config/raw-config-utils.ts +30 -2
  371. package/src/config/sanitize-for-transfer.ts +35 -0
  372. package/src/config/schema.ts +49 -41
  373. package/src/config/schemas/analysis.ts +3 -22
  374. package/src/config/schemas/backup.ts +1 -1
  375. package/src/config/schemas/calls.ts +0 -4
  376. package/src/config/schemas/conversations.ts +16 -0
  377. package/src/config/schemas/filing.ts +2 -7
  378. package/src/config/schemas/heartbeat.ts +0 -5
  379. package/src/config/schemas/inference.ts +3 -23
  380. package/src/config/schemas/llm.ts +317 -0
  381. package/src/config/schemas/memory-processing.ts +1 -9
  382. package/src/config/schemas/notifications.ts +4 -11
  383. package/src/config/schemas/platform.ts +3 -9
  384. package/src/config/schemas/security.ts +33 -0
  385. package/src/config/schemas/services.ts +9 -4
  386. package/src/config/schemas/stt.ts +1 -0
  387. package/src/config/schemas/tts.ts +64 -0
  388. package/src/config/schemas/updates.ts +1 -1
  389. package/src/config/schemas/workspace-git.ts +3 -40
  390. package/src/config/skill-state.ts +6 -2
  391. package/src/config/skills.ts +96 -7
  392. package/src/context/__tests__/compact-prompt.test.ts +63 -0
  393. package/src/context/__tests__/microcompact.test.ts +805 -0
  394. package/src/context/estimator-calibration.ts +136 -0
  395. package/src/context/microcompact.ts +443 -0
  396. package/src/context/prompts/compact.md +26 -0
  397. package/src/context/token-estimator.ts +61 -3
  398. package/src/context/tool-result-truncation.ts +3 -63
  399. package/src/context/window-manager.ts +417 -39
  400. package/src/credential-execution/approval-bridge.ts +0 -1
  401. package/src/credential-execution/executable-discovery.ts +19 -8
  402. package/src/credential-execution/process-manager.test.ts +109 -0
  403. package/src/credential-execution/process-manager.ts +65 -2
  404. package/src/credential-health/credential-health-service.ts +19 -6
  405. package/src/daemon/__tests__/conversation-feed-event.test.ts +317 -0
  406. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +4 -12
  407. package/src/daemon/__tests__/conversation-tool-setup.test.ts +14 -15
  408. package/src/daemon/approval-generators.ts +29 -4
  409. package/src/daemon/assistant-attachments.ts +24 -13
  410. package/src/daemon/classifier.ts +2 -2
  411. package/src/daemon/config-watcher.ts +0 -3
  412. package/src/daemon/context-overflow-policy.ts +4 -13
  413. package/src/daemon/context-overflow-reducer.ts +4 -1
  414. package/src/daemon/conversation-agent-loop-handlers.ts +162 -34
  415. package/src/daemon/conversation-agent-loop.ts +1282 -599
  416. package/src/daemon/conversation-attachments.ts +2 -6
  417. package/src/daemon/conversation-error.ts +36 -1
  418. package/src/daemon/conversation-history.ts +10 -19
  419. package/src/daemon/conversation-lifecycle.ts +59 -17
  420. package/src/daemon/conversation-messaging.ts +73 -4
  421. package/src/daemon/conversation-notifiers.ts +2 -110
  422. package/src/daemon/conversation-process.ts +24 -11
  423. package/src/daemon/conversation-queue-manager.ts +3 -0
  424. package/src/daemon/conversation-runtime-assembly.ts +1063 -211
  425. package/src/daemon/conversation-slash.ts +2 -2
  426. package/src/daemon/conversation-surfaces.ts +389 -1
  427. package/src/daemon/conversation-tool-setup.ts +51 -9
  428. package/src/daemon/conversation-usage.ts +1 -1
  429. package/src/daemon/conversation.ts +197 -64
  430. package/src/daemon/external-plugins-bootstrap.ts +478 -0
  431. package/src/daemon/external-skills-bootstrap.ts +41 -0
  432. package/src/daemon/first-greeting.ts +191 -14
  433. package/src/daemon/guardian-action-generators.ts +34 -14
  434. package/src/daemon/handlers/config-model.test.ts +86 -0
  435. package/src/daemon/handlers/config-model.ts +65 -12
  436. package/src/daemon/handlers/conversations.ts +9 -2
  437. package/src/daemon/handlers/shared.ts +39 -11
  438. package/src/daemon/handlers/skills.ts +7 -3
  439. package/src/daemon/handlers/slack-channel-oauth-install.ts +197 -0
  440. package/src/daemon/lifecycle.ts +109 -82
  441. package/src/daemon/message-types/computer-use.ts +2 -34
  442. package/src/daemon/message-types/conversations.ts +63 -0
  443. package/src/daemon/message-types/messages.ts +21 -1
  444. package/src/daemon/message-types/trust.ts +0 -2
  445. package/src/daemon/parse-actual-tokens-from-error.test.ts +57 -1
  446. package/src/daemon/parse-actual-tokens-from-error.ts +66 -0
  447. package/src/daemon/pkb-context-tracker.test.ts +169 -0
  448. package/src/daemon/pkb-context-tracker.ts +125 -0
  449. package/src/daemon/pkb-reminder-builder.test.ts +70 -0
  450. package/src/daemon/pkb-reminder-builder.ts +31 -0
  451. package/src/daemon/providers-setup.ts +6 -0
  452. package/src/daemon/server.ts +122 -12
  453. package/src/daemon/shutdown-handlers.ts +2 -12
  454. package/src/daemon/tool-side-effects.ts +14 -65
  455. package/src/daemon/web-search-history.ts +126 -0
  456. package/src/events/domain-events.ts +0 -1
  457. package/src/filing/filing-service.ts +9 -10
  458. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +160 -0
  459. package/src/heartbeat/heartbeat-service.ts +99 -28
  460. package/src/home/__tests__/feed-population-integration.test.ts +312 -0
  461. package/src/home/__tests__/feed-scheduler.test.ts +39 -11
  462. package/src/home/__tests__/rollup-producer.test.ts +44 -0
  463. package/src/home/assistant-feed-authoring.ts +4 -0
  464. package/src/home/emit-feed-event.ts +11 -0
  465. package/src/home/feed-scheduler.ts +20 -4
  466. package/src/home/feed-types.ts +97 -4
  467. package/src/home/relationship-state-writer.ts +2 -2
  468. package/src/home/rewrite-command-preview.ts +66 -0
  469. package/src/home/rollup-producer.ts +34 -5
  470. package/src/home/suggested-prompts.ts +101 -0
  471. package/src/ipc/__tests__/attachment-ipc.test.ts +213 -0
  472. package/src/ipc/__tests__/browser-ipc.test.ts +339 -0
  473. package/src/ipc/__tests__/cache-ipc.test.ts +266 -0
  474. package/src/ipc/__tests__/socket-path.test.ts +34 -0
  475. package/src/ipc/__tests__/task-ipc.test.ts +577 -0
  476. package/src/ipc/__tests__/ui-request-route.test.ts +495 -0
  477. package/src/ipc/__tests__/watcher-ipc.test.ts +295 -0
  478. package/src/ipc/cli-client.ts +2 -1
  479. package/src/ipc/cli-server.ts +26 -8
  480. package/src/ipc/gateway-client.ts +6 -3
  481. package/src/ipc/routes/attachment.ts +114 -0
  482. package/src/ipc/routes/browser-context.ts +63 -0
  483. package/src/ipc/routes/browser.ts +97 -0
  484. package/src/ipc/routes/cache.ts +96 -0
  485. package/src/ipc/routes/get-contact.ts +16 -0
  486. package/src/ipc/routes/index.ts +31 -1
  487. package/src/ipc/routes/list-clients.ts +31 -0
  488. package/src/ipc/routes/merge-contacts.ts +17 -0
  489. package/src/ipc/routes/notification.ts +133 -0
  490. package/src/ipc/routes/rename-conversation.ts +59 -0
  491. package/src/ipc/routes/search-contacts.ts +19 -0
  492. package/src/ipc/routes/task-queue.ts +226 -0
  493. package/src/ipc/routes/task.ts +173 -0
  494. package/src/ipc/routes/ui-request.ts +50 -0
  495. package/src/ipc/routes/upsert-contact.ts +25 -0
  496. package/src/ipc/routes/watcher.ts +203 -0
  497. package/src/ipc/socket-path.ts +76 -0
  498. package/src/media/app-icon-generator.ts +23 -46
  499. package/src/media/avatar-router.ts +26 -41
  500. package/src/media/gemini-image-service.ts +8 -41
  501. package/src/media/image-credentials.ts +73 -0
  502. package/src/media/image-service.ts +85 -0
  503. package/src/media/openai-image-service.ts +131 -0
  504. package/src/media/types.ts +46 -0
  505. package/src/memory/__tests__/conversation-analyze-job.test.ts +9 -8
  506. package/src/memory/__tests__/conversation-group-migration.test.ts +99 -0
  507. package/src/memory/admin.ts +18 -0
  508. package/src/memory/conversation-analyze-job.ts +14 -13
  509. package/src/memory/conversation-attention-store.ts +13 -6
  510. package/src/memory/conversation-crud.ts +133 -3
  511. package/src/memory/conversation-group-migration.ts +38 -6
  512. package/src/memory/conversation-queries.ts +57 -4
  513. package/src/memory/conversation-title-service.ts +32 -4
  514. package/src/memory/db-init.ts +10 -0
  515. package/src/memory/embedding-backend.ts +1 -1
  516. package/src/memory/embedding-gemini.test.ts +41 -2
  517. package/src/memory/embedding-gemini.ts +6 -1
  518. package/src/memory/graph/bootstrap.test.ts +282 -0
  519. package/src/memory/graph/bootstrap.ts +8 -5
  520. package/src/memory/graph/compaction.ts +299 -0
  521. package/src/memory/graph/consolidation.ts +4 -4
  522. package/src/memory/graph/conversation-graph-memory.ts +89 -29
  523. package/src/memory/graph/extraction.test.ts +272 -2
  524. package/src/memory/graph/extraction.ts +183 -53
  525. package/src/memory/graph/graph-search.test.ts +93 -0
  526. package/src/memory/graph/graph-search.ts +4 -1
  527. package/src/memory/graph/inspect.ts +2 -2
  528. package/src/memory/graph/narrative.ts +2 -2
  529. package/src/memory/graph/pattern-scan.ts +2 -2
  530. package/src/memory/graph/retriever.test.ts +459 -0
  531. package/src/memory/graph/retriever.ts +237 -48
  532. package/src/memory/graph/store.ts +41 -0
  533. package/src/memory/graph/tool-handlers.ts +27 -0
  534. package/src/memory/graph/tools.ts +6 -1
  535. package/src/memory/indexer.ts +5 -5
  536. package/src/memory/job-handlers/conversation-starters.ts +23 -20
  537. package/src/memory/job-handlers/summarization.ts +2 -2
  538. package/src/memory/job-utils.ts +7 -1
  539. package/src/memory/jobs/embed-pkb-file.test.ts +168 -0
  540. package/src/memory/jobs/embed-pkb-file.ts +54 -0
  541. package/src/memory/jobs-store.ts +44 -3
  542. package/src/memory/jobs-worker.ts +4 -0
  543. package/src/memory/migrations/041-approval-prompt-ts-tracker.ts +26 -0
  544. package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +1 -1
  545. package/src/memory/migrations/149-oauth-tables.ts +1 -0
  546. package/src/memory/migrations/220-normalize-user-file-by-principal.ts +2 -2
  547. package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +82 -0
  548. package/src/memory/migrations/223-schedule-script-column.ts +11 -0
  549. package/src/memory/migrations/224-oauth-providers-managed-service-is-paid.ts +24 -0
  550. package/src/memory/migrations/225-oauth-providers-available-scopes.ts +13 -0
  551. package/src/memory/migrations/index.ts +5 -0
  552. package/src/memory/pkb/pkb-index.test.ts +369 -0
  553. package/src/memory/pkb/pkb-index.ts +255 -0
  554. package/src/memory/pkb/pkb-reconcile.test.ts +252 -0
  555. package/src/memory/pkb/pkb-reconcile.ts +148 -0
  556. package/src/memory/pkb/pkb-search.test.ts +499 -0
  557. package/src/memory/pkb/pkb-search.ts +159 -0
  558. package/src/memory/pkb/types.ts +53 -0
  559. package/src/memory/qdrant-client.test.ts +60 -0
  560. package/src/memory/qdrant-client.ts +147 -1
  561. package/src/memory/schema/infrastructure.ts +1 -0
  562. package/src/memory/schema/oauth.ts +4 -1
  563. package/src/memory/slack-thread-store.ts +37 -0
  564. package/src/messaging/providers/gmail/adapter.ts +6 -16
  565. package/src/messaging/providers/gmail/client.ts +22 -0
  566. package/src/messaging/providers/gmail/types.ts +7 -0
  567. package/src/messaging/providers/slack/adapter.ts +14 -2
  568. package/src/messaging/providers/slack/backfill.test.ts +257 -0
  569. package/src/messaging/providers/slack/backfill.ts +101 -0
  570. package/src/messaging/providers/slack/message-metadata.test.ts +316 -0
  571. package/src/messaging/providers/slack/message-metadata.ts +123 -0
  572. package/src/messaging/providers/slack/render-transcript.test.ts +1421 -0
  573. package/src/messaging/providers/slack/render-transcript.ts +501 -0
  574. package/src/messaging/style-analyzer.ts +5 -2
  575. package/src/notifications/README.md +9 -5
  576. package/src/notifications/conversation-pairing.ts +78 -19
  577. package/src/notifications/copy-composer.ts +0 -5
  578. package/src/notifications/decision-engine.ts +3 -9
  579. package/src/notifications/emit-signal.ts +1 -1
  580. package/src/notifications/preference-extractor.ts +2 -6
  581. package/src/notifications/signal.ts +1 -2
  582. package/src/oauth/AGENTS.md +1 -1
  583. package/src/oauth/__tests__/identity-verifier.test.ts +2 -1
  584. package/src/oauth/connect-orchestrator.ts +8 -34
  585. package/src/oauth/connect-types.ts +6 -10
  586. package/src/oauth/manual-token-connection.ts +23 -0
  587. package/src/oauth/oauth-store.ts +31 -14
  588. package/src/oauth/platform-connection.test.ts +47 -0
  589. package/src/oauth/platform-connection.ts +15 -5
  590. package/src/oauth/provider-serializer.ts +6 -1
  591. package/src/oauth/seed-providers.ts +56 -106
  592. package/src/outbound-proxy/http-forwarder.ts +9 -0
  593. package/src/permissions/approval-policy.test.ts +1223 -0
  594. package/src/permissions/approval-policy.ts +309 -0
  595. package/src/permissions/arg-parser.test.ts +161 -0
  596. package/src/permissions/arg-parser.ts +141 -0
  597. package/src/permissions/bash-risk-classifier.test.ts +1620 -0
  598. package/src/permissions/bash-risk-classifier.ts +950 -0
  599. package/src/permissions/checker.ts +348 -711
  600. package/src/permissions/command-registry.test.ts +774 -0
  601. package/src/permissions/command-registry.ts +1005 -0
  602. package/src/permissions/defaults.ts +28 -79
  603. package/src/permissions/file-risk-classifier.test.ts +535 -0
  604. package/src/permissions/file-risk-classifier.ts +274 -0
  605. package/src/permissions/gateway-threshold-reader.ts +196 -0
  606. package/src/permissions/prompter.ts +4 -0
  607. package/src/permissions/risk-types.ts +262 -0
  608. package/src/permissions/schedule-risk-classifier.test.ts +129 -0
  609. package/src/permissions/schedule-risk-classifier.ts +85 -0
  610. package/src/permissions/secret-prompter.ts +53 -2
  611. package/src/permissions/shell-identity.ts +2 -42
  612. package/src/permissions/skill-risk-classifier.test.ts +311 -0
  613. package/src/permissions/skill-risk-classifier.ts +214 -0
  614. package/src/permissions/trust-client.ts +52 -25
  615. package/src/permissions/trust-store-interface.ts +1 -6
  616. package/src/permissions/trust-store.ts +161 -62
  617. package/src/permissions/types.ts +25 -14
  618. package/src/permissions/web-risk-classifier.test.ts +170 -0
  619. package/src/permissions/web-risk-classifier.ts +89 -0
  620. package/src/permissions/workspace-policy.ts +9 -19
  621. package/src/platform/client.ts +19 -1
  622. package/src/plugins/defaults/circuit-breaker.ts +146 -0
  623. package/src/plugins/defaults/compaction.ts +145 -0
  624. package/src/plugins/defaults/empty-response.ts +126 -0
  625. package/src/plugins/defaults/history-repair.ts +85 -0
  626. package/src/plugins/defaults/index.ts +116 -0
  627. package/src/plugins/defaults/injectors.ts +491 -0
  628. package/src/plugins/defaults/llm-call.ts +82 -0
  629. package/src/plugins/defaults/memory-retrieval.ts +226 -0
  630. package/src/plugins/defaults/overflow-reduce.ts +181 -0
  631. package/src/plugins/defaults/persistence.ts +129 -0
  632. package/src/plugins/defaults/title-generate.ts +95 -0
  633. package/src/plugins/defaults/token-estimate.ts +104 -0
  634. package/src/plugins/defaults/tool-error.ts +126 -0
  635. package/src/plugins/defaults/tool-execute.ts +89 -0
  636. package/src/plugins/defaults/tool-result-truncate.ts +88 -0
  637. package/src/plugins/pipeline.ts +316 -0
  638. package/src/plugins/plugin-skill-contributions.ts +292 -0
  639. package/src/plugins/registry.ts +241 -0
  640. package/src/plugins/types.ts +1134 -0
  641. package/src/plugins/user-loader.ts +177 -0
  642. package/src/prompts/persona-resolver.ts +3 -3
  643. package/src/prompts/system-prompt.ts +19 -20
  644. package/src/prompts/templates/BOOTSTRAP.md +27 -77
  645. package/src/prompts/templates/SOUL.md +2 -2
  646. package/src/prompts/update-bulletin-job.ts +190 -0
  647. package/src/providers/__tests__/context-overflow-error.test.ts +328 -0
  648. package/src/providers/__tests__/provider-env-vars.test.ts +102 -0
  649. package/src/providers/__tests__/retry-callsite.test.ts +424 -0
  650. package/src/providers/anthropic/client.ts +183 -14
  651. package/src/providers/call-site-routing.ts +71 -0
  652. package/src/providers/gemini/client.ts +65 -2
  653. package/src/providers/managed-proxy/constants.ts +2 -1
  654. package/src/providers/model-catalog.ts +524 -33
  655. package/src/providers/model-intents.ts +4 -4
  656. package/src/providers/openai/chat-completions-provider.ts +57 -1
  657. package/src/providers/openai/responses-provider.ts +86 -9
  658. package/src/providers/openrouter/client.ts +80 -9
  659. package/src/providers/provider-env-vars.ts +56 -0
  660. package/src/providers/provider-send-message.ts +22 -5
  661. package/src/providers/ratelimit.ts +4 -0
  662. package/src/providers/registry.ts +19 -8
  663. package/src/providers/retry.ts +174 -39
  664. package/src/providers/speech-to-text/__tests__/resolve.test.ts +55 -0
  665. package/src/providers/speech-to-text/deepgram-realtime.test.ts +61 -0
  666. package/src/providers/speech-to-text/deepgram-realtime.ts +57 -0
  667. package/src/providers/speech-to-text/google-gemini-live-stream.ts +4 -4
  668. package/src/providers/speech-to-text/provider-catalog.ts +17 -0
  669. package/src/providers/speech-to-text/resolve.ts +7 -0
  670. package/src/providers/speech-to-text/xai-realtime.test.ts +646 -0
  671. package/src/providers/speech-to-text/xai-realtime.ts +821 -0
  672. package/src/providers/speech-to-text/xai.test.ts +155 -0
  673. package/src/providers/speech-to-text/xai.ts +97 -0
  674. package/src/providers/types.ts +93 -3
  675. package/src/runtime/AGENTS.md +27 -18
  676. package/src/runtime/__tests__/agent-wake.test.ts +43 -2
  677. package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +3 -3
  678. package/src/runtime/__tests__/client-registry.test.ts +293 -0
  679. package/src/runtime/__tests__/interactive-ui.test.ts +673 -0
  680. package/src/runtime/agent-wake.ts +63 -22
  681. package/src/runtime/auth/route-policy.ts +4 -0
  682. package/src/runtime/btw-sidechain.ts +13 -3
  683. package/src/runtime/channel-reply-delivery.ts +106 -2
  684. package/src/runtime/client-registry.ts +261 -0
  685. package/src/runtime/decision-token.ts +116 -0
  686. package/src/runtime/gateway-client.ts +2 -2
  687. package/src/runtime/http-router.ts +32 -0
  688. package/src/runtime/http-server.ts +129 -9
  689. package/src/runtime/http-types.ts +23 -3
  690. package/src/runtime/interactive-ui.ts +362 -0
  691. package/src/runtime/invite-instruction-generator.ts +2 -2
  692. package/src/runtime/migrations/__tests__/gcs-signed-url.test.ts +176 -0
  693. package/src/runtime/migrations/__tests__/vbundle-metadata-merge-integration.test.ts +390 -0
  694. package/src/runtime/migrations/__tests__/vbundle-metadata-merge.test.ts +221 -0
  695. package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +1540 -0
  696. package/src/runtime/migrations/__tests__/vbundle-streaming-validator.test.ts +453 -0
  697. package/src/runtime/migrations/__tests__/vbundle-tar-stream.test.ts +222 -0
  698. package/src/runtime/migrations/gcs-signed-url.ts +162 -0
  699. package/src/runtime/migrations/vbundle-builder.ts +1 -22
  700. package/src/runtime/migrations/vbundle-importer.ts +154 -9
  701. package/src/runtime/migrations/vbundle-metadata-merge.ts +124 -0
  702. package/src/runtime/migrations/vbundle-streaming-importer.ts +2522 -0
  703. package/src/runtime/migrations/vbundle-streaming-validator.ts +244 -0
  704. package/src/runtime/migrations/vbundle-tar-stream.ts +217 -0
  705. package/src/runtime/migrations/vbundle-validator.ts +15 -6
  706. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +111 -0
  707. package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +114 -75
  708. package/src/runtime/routes/__tests__/migration-vellum-metadata-reconcile.test.ts +246 -0
  709. package/src/runtime/routes/approval-prompt-ts-tracker.ts +78 -0
  710. package/src/runtime/routes/approval-routes.ts +29 -17
  711. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +9 -0
  712. package/src/runtime/routes/avatar-routes.ts +20 -4
  713. package/src/runtime/routes/browser-extension-pair-routes.ts +27 -8
  714. package/src/runtime/routes/btw-routes.ts +1 -4
  715. package/src/runtime/routes/conversation-management-routes.ts +20 -2
  716. package/src/runtime/routes/conversation-routes.ts +351 -138
  717. package/src/runtime/routes/debug-routes.ts +1 -1
  718. package/src/runtime/routes/diagnostics-routes.ts +6 -4
  719. package/src/runtime/routes/events-routes.ts +16 -0
  720. package/src/runtime/routes/guardian-approval-interception.ts +33 -3
  721. package/src/runtime/routes/guardian-approval-prompt.ts +13 -3
  722. package/src/runtime/routes/home-feed-routes.ts +120 -2
  723. package/src/runtime/routes/inbound-message-handler.ts +987 -2
  724. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +113 -2
  725. package/src/runtime/routes/inbound-stages/background-dispatch.ts +61 -3
  726. package/src/runtime/routes/inbound-stages/edit-intercept.ts +129 -6
  727. package/src/runtime/routes/integrations/slack/channel.ts +25 -3
  728. package/src/runtime/routes/llm-context-normalization.ts +23 -1
  729. package/src/runtime/routes/memory-item-routes.test.ts +1 -0
  730. package/src/runtime/routes/migration-routes.ts +720 -127
  731. package/src/runtime/routes/playground/__tests__/force-compact.test.ts +284 -0
  732. package/src/runtime/routes/playground/__tests__/guard.test.ts +80 -0
  733. package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +294 -0
  734. package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +271 -0
  735. package/src/runtime/routes/playground/__tests__/seed-conversation.test.ts +202 -0
  736. package/src/runtime/routes/playground/__tests__/seeded-conversations.test.ts +309 -0
  737. package/src/runtime/routes/playground/__tests__/state.test.ts +224 -0
  738. package/src/runtime/routes/playground/conversation-not-found.ts +29 -0
  739. package/src/runtime/routes/playground/deps.ts +56 -0
  740. package/src/runtime/routes/playground/force-compact.ts +73 -0
  741. package/src/runtime/routes/playground/guard.ts +37 -0
  742. package/src/runtime/routes/playground/index.ts +28 -0
  743. package/src/runtime/routes/playground/inject-failures.ts +159 -0
  744. package/src/runtime/routes/playground/reset-circuit.ts +115 -0
  745. package/src/runtime/routes/playground/seed-conversation.ts +139 -0
  746. package/src/runtime/routes/playground/seeded-conversations.ts +78 -0
  747. package/src/runtime/routes/playground/state.ts +78 -0
  748. package/src/runtime/routes/schedule-routes.ts +89 -8
  749. package/src/runtime/routes/settings-routes.ts +4 -2
  750. package/src/runtime/routes/trust-rules-routes.ts +30 -14
  751. package/src/runtime/routes/work-items-routes.test.ts +1 -1
  752. package/src/runtime/routes/work-items-routes.ts +3 -2
  753. package/src/runtime/services/__tests__/analyze-conversation.test.ts +25 -43
  754. package/src/runtime/services/analyze-conversation.ts +12 -16
  755. package/src/runtime/skill-route-registry.ts +97 -15
  756. package/src/schedule/run-script.ts +68 -0
  757. package/src/schedule/schedule-store.ts +7 -1
  758. package/src/schedule/scheduler.ts +56 -8
  759. package/src/security/__tests__/provider-key-env-fallback.test.ts +119 -0
  760. package/src/security/__tests__/untrusted-content.test.ts +109 -0
  761. package/src/security/oauth2.ts +98 -35
  762. package/src/security/secure-keys.ts +7 -8
  763. package/src/security/token-manager.ts +27 -13
  764. package/src/security/untrusted-content.ts +102 -0
  765. package/src/skills/catalog-cache.ts +35 -9
  766. package/src/skills/catalog-install.ts +31 -3
  767. package/src/skills/skill-cache-store.ts +97 -0
  768. package/src/stt/__tests__/daemon-batch-transcriber.test.ts +76 -0
  769. package/src/stt/daemon-batch-transcriber.ts +33 -0
  770. package/src/stt/stt-stream-session.ts +8 -1
  771. package/src/stt/types.ts +5 -1
  772. package/src/subagent/manager.ts +41 -13
  773. package/src/tasks/ephemeral-permissions.ts +9 -4
  774. package/src/telemetry/usage-telemetry-reporter.ts +27 -5
  775. package/src/tools/browser/__tests__/browser-status.test.ts +234 -2
  776. package/src/tools/browser/browser-execution.ts +150 -54
  777. package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +230 -0
  778. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +146 -3
  779. package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +22 -0
  780. package/src/tools/browser/cdp-client/extension-cdp-client.ts +54 -3
  781. package/src/tools/browser/cdp-client/factory.ts +15 -4
  782. package/src/tools/credentials/tool-policy.ts +39 -5
  783. package/src/tools/credentials/vault.ts +9 -4
  784. package/src/tools/executor.ts +129 -73
  785. package/src/tools/filesystem/write.ts +52 -0
  786. package/src/tools/host-terminal/host-shell.ts +45 -5
  787. package/src/tools/memory/register.test.ts +185 -0
  788. package/src/tools/memory/register.ts +3 -1
  789. package/src/tools/network/script-proxy/session-manager.ts +37 -1
  790. package/src/tools/network/web-fetch.ts +20 -10
  791. package/src/tools/network/web-search.ts +19 -4
  792. package/src/tools/permission-checker.ts +116 -46
  793. package/src/tools/policy-context.ts +29 -8
  794. package/src/tools/registry.ts +195 -6
  795. package/src/tools/schedule/create.ts +23 -8
  796. package/src/tools/schedule/update.ts +3 -1
  797. package/src/tools/secret-detection-handler.ts +0 -51
  798. package/src/tools/side-effects.ts +0 -11
  799. package/src/tools/skills/execute.ts +2 -2
  800. package/src/tools/skills/sandbox-runner.ts +5 -2
  801. package/src/tools/system/avatar-generator.ts +6 -2
  802. package/src/tools/terminal/backends/native.ts +51 -2
  803. package/src/tools/terminal/safe-env.ts +3 -2
  804. package/src/tools/terminal/shell.ts +1 -0
  805. package/src/tools/tool-manifest.ts +6 -21
  806. package/src/tools/types.ts +40 -5
  807. package/src/tools/verification-control-plane-policy.ts +1 -1
  808. package/src/tts/__tests__/provider-adapters.test.ts +240 -13
  809. package/src/tts/provider-catalog.ts +18 -0
  810. package/src/tts/providers/index.ts +2 -0
  811. package/src/tts/providers/xai-provider.ts +224 -0
  812. package/src/tts/types.ts +46 -0
  813. package/src/types/tar-stream.d.ts +66 -0
  814. package/src/util/json.ts +17 -0
  815. package/src/util/platform.ts +9 -4
  816. package/src/util/pricing.ts +41 -8
  817. package/src/watcher/engine.ts +1 -1
  818. package/src/watcher/providers/google-calendar.ts +134 -8
  819. package/src/watcher/providers/outlook-calendar.ts +42 -2
  820. package/src/workspace/git-service.ts +23 -4
  821. package/src/workspace/migrations/006-services-config.ts +2 -4
  822. package/src/workspace/migrations/022-move-hooks-to-workspace.ts +2 -3
  823. package/src/workspace/migrations/038-unify-llm-callsite-configs.ts +516 -0
  824. package/src/workspace/migrations/039-drop-legacy-llm-keys.ts +171 -0
  825. package/src/workspace/migrations/040-seed-latency-callsite-defaults.ts +154 -0
  826. package/src/workspace/migrations/041-backfill-google-gmail-settings-scope.ts +56 -0
  827. package/src/workspace/migrations/042-fix-backfill-google-gmail-settings-scope.ts +70 -0
  828. package/src/workspace/migrations/043-release-notes-latex-rendering.ts +75 -0
  829. package/src/workspace/migrations/044-bump-stale-provider-stream-timeout.ts +51 -0
  830. package/src/workspace/migrations/045-release-notes-meet-avatar.ts +130 -0
  831. package/src/workspace/migrations/046-seed-conversation-starters-callsite.ts +108 -0
  832. package/src/workspace/migrations/047-remove-watch-callsites.ts +54 -0
  833. package/src/workspace/migrations/048-remove-workspace-hooks.ts +81 -0
  834. package/src/workspace/migrations/049-release-notes-default-sonnet.ts +80 -0
  835. package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +86 -0
  836. package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +128 -0
  837. package/src/workspace/migrations/AGENTS.md +1 -1
  838. package/src/workspace/migrations/registry.ts +28 -0
  839. package/src/workspace/provider-commit-message-generator.ts +19 -38
  840. package/tsconfig.json +1 -1
  841. package/hook-templates/debug-prompt-logger/hook.json +0 -7
  842. package/hook-templates/debug-prompt-logger/run.sh +0 -66
  843. package/src/__tests__/context-overflow-approval.test.ts +0 -156
  844. package/src/__tests__/gmail-archive-fallback.test.ts +0 -193
  845. package/src/__tests__/gmail-archive-gate.test.ts +0 -246
  846. package/src/__tests__/gmail-preferences.test.ts +0 -117
  847. package/src/__tests__/hooks-blocking.test.ts +0 -178
  848. package/src/__tests__/hooks-cli.test.ts +0 -182
  849. package/src/__tests__/hooks-config.test.ts +0 -108
  850. package/src/__tests__/hooks-discovery.test.ts +0 -211
  851. package/src/__tests__/hooks-integration.test.ts +0 -196
  852. package/src/__tests__/hooks-manager.test.ts +0 -226
  853. package/src/__tests__/hooks-runner.test.ts +0 -175
  854. package/src/__tests__/hooks-settings.test.ts +0 -160
  855. package/src/__tests__/hooks-templates.test.ts +0 -169
  856. package/src/__tests__/hooks-ts-runner.test.ts +0 -170
  857. package/src/__tests__/hooks-watch.test.ts +0 -112
  858. package/src/__tests__/notification-schedule-dedup.test.ts +0 -213
  859. package/src/__tests__/oauth-scope-policy.test.ts +0 -180
  860. package/src/__tests__/outlook-attachments.test.ts +0 -301
  861. package/src/__tests__/outlook-automation-tools.test.ts +0 -425
  862. package/src/__tests__/outlook-categories.test.ts +0 -212
  863. package/src/__tests__/outlook-compose-tools.test.ts +0 -325
  864. package/src/__tests__/outlook-declutter-tools.test.ts +0 -585
  865. package/src/__tests__/outlook-follow-up.test.ts +0 -196
  866. package/src/__tests__/outlook-trash.test.ts +0 -77
  867. package/src/__tests__/outlook-unsubscribe.test.ts +0 -279
  868. package/src/__tests__/send-notification-tool.test.ts +0 -83
  869. package/src/__tests__/update-bulletin-format.test.ts +0 -181
  870. package/src/__tests__/update-bulletin-state.test.ts +0 -135
  871. package/src/__tests__/update-bulletin.test.ts +0 -478
  872. package/src/__tests__/update-template-contract.test.ts +0 -29
  873. package/src/cli/commands/doctor.ts +0 -341
  874. package/src/cli/commands/shotgun.ts +0 -266
  875. package/src/config/bundled-skills/browser/SKILL.md +0 -88
  876. package/src/config/bundled-skills/browser/TOOLS.json +0 -516
  877. package/src/config/bundled-skills/browser/tools/browser-attach.ts +0 -12
  878. package/src/config/bundled-skills/browser/tools/browser-click.ts +0 -12
  879. package/src/config/bundled-skills/browser/tools/browser-close.ts +0 -12
  880. package/src/config/bundled-skills/browser/tools/browser-detach.ts +0 -12
  881. package/src/config/bundled-skills/browser/tools/browser-extract.ts +0 -12
  882. package/src/config/bundled-skills/browser/tools/browser-fill-credential.ts +0 -12
  883. package/src/config/bundled-skills/browser/tools/browser-hover.ts +0 -12
  884. package/src/config/bundled-skills/browser/tools/browser-navigate.ts +0 -12
  885. package/src/config/bundled-skills/browser/tools/browser-press-key.ts +0 -12
  886. package/src/config/bundled-skills/browser/tools/browser-screenshot.ts +0 -12
  887. package/src/config/bundled-skills/browser/tools/browser-scroll.ts +0 -12
  888. package/src/config/bundled-skills/browser/tools/browser-select-option.ts +0 -12
  889. package/src/config/bundled-skills/browser/tools/browser-snapshot.ts +0 -12
  890. package/src/config/bundled-skills/browser/tools/browser-status.ts +0 -12
  891. package/src/config/bundled-skills/browser/tools/browser-type.ts +0 -12
  892. package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +0 -49
  893. package/src/config/bundled-skills/browser/tools/browser-wait-for.ts +0 -12
  894. package/src/config/bundled-skills/chatgpt-import/SKILL.md +0 -27
  895. package/src/config/bundled-skills/chatgpt-import/TOOLS.json +0 -27
  896. package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +0 -378
  897. package/src/config/bundled-skills/conversations/SKILL.md +0 -20
  898. package/src/config/bundled-skills/conversations/TOOLS.json +0 -23
  899. package/src/config/bundled-skills/conversations/tools/rename-conversation.ts +0 -66
  900. package/src/config/bundled-skills/gmail/SKILL.md +0 -221
  901. package/src/config/bundled-skills/gmail/TOOLS.json +0 -588
  902. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +0 -256
  903. package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +0 -112
  904. package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +0 -44
  905. package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +0 -81
  906. package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +0 -108
  907. package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +0 -146
  908. package/src/config/bundled-skills/gmail/tools/gmail-label.ts +0 -53
  909. package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +0 -347
  910. package/src/config/bundled-skills/gmail/tools/gmail-preferences-tool.ts +0 -59
  911. package/src/config/bundled-skills/gmail/tools/gmail-preferences.ts +0 -82
  912. package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +0 -26
  913. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +0 -347
  914. package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +0 -29
  915. package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +0 -122
  916. package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +0 -67
  917. package/src/config/bundled-skills/gmail/tools/scan-result-store.ts +0 -100
  918. package/src/config/bundled-skills/gmail/tools/shared.ts +0 -47
  919. package/src/config/bundled-skills/google-calendar/SKILL.md +0 -51
  920. package/src/config/bundled-skills/google-calendar/TOOLS.json +0 -226
  921. package/src/config/bundled-skills/google-calendar/calendar-client.ts +0 -223
  922. package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +0 -27
  923. package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +0 -48
  924. package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +0 -19
  925. package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +0 -36
  926. package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +0 -58
  927. package/src/config/bundled-skills/google-calendar/tools/shared.ts +0 -17
  928. package/src/config/bundled-skills/google-calendar/types.ts +0 -97
  929. package/src/config/bundled-skills/heartbeat/SKILL.md +0 -43
  930. package/src/config/bundled-skills/notifications/SKILL.md +0 -40
  931. package/src/config/bundled-skills/notifications/TOOLS.json +0 -80
  932. package/src/config/bundled-skills/notifications/tools/send-notification.ts +0 -152
  933. package/src/config/bundled-skills/notifications/tools/shared.ts +0 -13
  934. package/src/config/bundled-skills/outlook/SKILL.md +0 -196
  935. package/src/config/bundled-skills/outlook/TOOLS.json +0 -530
  936. package/src/config/bundled-skills/outlook/tools/outlook-attachments.ts +0 -85
  937. package/src/config/bundled-skills/outlook/tools/outlook-categories.ts +0 -77
  938. package/src/config/bundled-skills/outlook/tools/outlook-draft.ts +0 -84
  939. package/src/config/bundled-skills/outlook/tools/outlook-follow-up.ts +0 -94
  940. package/src/config/bundled-skills/outlook/tools/outlook-forward.ts +0 -49
  941. package/src/config/bundled-skills/outlook/tools/outlook-outreach-scan.ts +0 -237
  942. package/src/config/bundled-skills/outlook/tools/outlook-rules.ts +0 -161
  943. package/src/config/bundled-skills/outlook/tools/outlook-send-draft.ts +0 -32
  944. package/src/config/bundled-skills/outlook/tools/outlook-sender-digest.ts +0 -272
  945. package/src/config/bundled-skills/outlook/tools/outlook-trash.ts +0 -29
  946. package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +0 -129
  947. package/src/config/bundled-skills/outlook/tools/outlook-vacation.ts +0 -87
  948. package/src/config/bundled-skills/outlook/tools/shared.ts +0 -20
  949. package/src/config/bundled-skills/outlook-calendar/SKILL.md +0 -51
  950. package/src/config/bundled-skills/outlook-calendar/TOOLS.json +0 -221
  951. package/src/config/bundled-skills/outlook-calendar/calendar-client.ts +0 -252
  952. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-check-availability.ts +0 -53
  953. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-create-event.ts +0 -74
  954. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-get-event.ts +0 -18
  955. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-list-events.ts +0 -46
  956. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-rsvp.ts +0 -36
  957. package/src/config/bundled-skills/outlook-calendar/tools/shared.ts +0 -17
  958. package/src/config/bundled-skills/outlook-calendar/types.ts +0 -120
  959. package/src/config/bundled-skills/screen-watch/SKILL.md +0 -27
  960. package/src/config/bundled-skills/screen-watch/TOOLS.json +0 -35
  961. package/src/config/bundled-skills/screen-watch/tools/start-screen-watch.ts +0 -12
  962. package/src/config/bundled-skills/skills-catalog/SKILL.md +0 -84
  963. package/src/config/bundled-skills/slack/SKILL.md +0 -108
  964. package/src/config/bundled-skills/tasks/SKILL.md +0 -37
  965. package/src/config/bundled-skills/tasks/TOOLS.json +0 -353
  966. package/src/config/bundled-skills/tasks/icon.svg +0 -34
  967. package/src/config/bundled-skills/tasks/tools/task-delete.ts +0 -12
  968. package/src/config/bundled-skills/tasks/tools/task-list-add.ts +0 -12
  969. package/src/config/bundled-skills/tasks/tools/task-list-remove.ts +0 -12
  970. package/src/config/bundled-skills/tasks/tools/task-list-show.ts +0 -12
  971. package/src/config/bundled-skills/tasks/tools/task-list-update.ts +0 -12
  972. package/src/config/bundled-skills/tasks/tools/task-list.ts +0 -12
  973. package/src/config/bundled-skills/tasks/tools/task-queue-run.ts +0 -12
  974. package/src/config/bundled-skills/tasks/tools/task-run.ts +0 -12
  975. package/src/config/bundled-skills/tasks/tools/task-save.ts +0 -12
  976. package/src/config/bundled-skills/watcher/SKILL.md +0 -31
  977. package/src/config/bundled-skills/watcher/TOOLS.json +0 -167
  978. package/src/config/bundled-skills/watcher/tools/watcher-create.ts +0 -12
  979. package/src/config/bundled-skills/watcher/tools/watcher-delete.ts +0 -12
  980. package/src/config/bundled-skills/watcher/tools/watcher-digest.ts +0 -12
  981. package/src/config/bundled-skills/watcher/tools/watcher-list.ts +0 -12
  982. package/src/config/bundled-skills/watcher/tools/watcher-update.ts +0 -12
  983. package/src/daemon/context-overflow-approval.ts +0 -52
  984. package/src/daemon/watch-handler.ts +0 -399
  985. package/src/hooks/cli.ts +0 -253
  986. package/src/hooks/config.ts +0 -100
  987. package/src/hooks/discovery.ts +0 -135
  988. package/src/hooks/manager.ts +0 -179
  989. package/src/hooks/runner.ts +0 -117
  990. package/src/hooks/templates.ts +0 -77
  991. package/src/hooks/types.ts +0 -75
  992. package/src/oauth/scope-policy.ts +0 -89
  993. package/src/prompts/templates/UPDATES.md +0 -50
  994. package/src/prompts/update-bulletin-format.ts +0 -85
  995. package/src/prompts/update-bulletin-state.ts +0 -58
  996. package/src/prompts/update-bulletin-template-path.ts +0 -13
  997. package/src/prompts/update-bulletin.ts +0 -139
  998. package/src/runtime/gateway-internal-client.ts +0 -94
  999. package/src/runtime/routes/watch-routes.ts +0 -156
  1000. package/src/shared/provider-env-vars.ts +0 -19
  1001. package/src/signals/shotgun.ts +0 -203
  1002. package/src/tools/watch/screen-watch.ts +0 -144
  1003. package/src/tools/watch/watch-state.ts +0 -142
  1004. package/src/tools/watcher/create.ts +0 -86
  1005. package/src/tools/watcher/delete.ts +0 -36
  1006. package/src/tools/watcher/digest.ts +0 -54
  1007. package/src/tools/watcher/list.ts +0 -83
  1008. package/src/tools/watcher/update.ts +0 -71
@@ -6,13 +6,16 @@
6
6
  * POST /v1/migrations/import-preflight — dry-run import analysis of a .vbundle archive.
7
7
  * POST /v1/migrations/import — commit a .vbundle archive import to disk.
8
8
  *
9
- * Accepts raw binary body (Content-Type: application/octet-stream) or
10
- * multipart form data with a "file" field. Returns structured validation
11
- * results with is_valid flag and detailed error descriptions.
9
+ * Accepts raw binary body (Content-Type: application/octet-stream),
10
+ * multipart form data with a "file" field, or on /import only — a JSON
11
+ * body of shape `{ "url": "<signed-gcs-url>" }` that causes the daemon to
12
+ * fetch the bundle from GCS and stream it through `streamCommitImport`.
13
+ * Returns structured validation results with is_valid flag and detailed
14
+ * error descriptions.
12
15
  */
13
16
 
14
17
  import { createReadStream } from "node:fs";
15
- import { Readable } from "node:stream";
18
+ import { PassThrough, Readable } from "node:stream";
16
19
  import { Database } from "bun:sqlite";
17
20
 
18
21
  import { z } from "zod";
@@ -21,11 +24,17 @@ import { invalidateConfigCache } from "../../config/loader.js";
21
24
  import { getDb, resetDb } from "../../memory/db-connection.js";
22
25
  import { validateMigrationState } from "../../memory/migrations/validate-migration-state.js";
23
26
  import { clearCache as clearTrustCache } from "../../permissions/trust-store.js";
27
+ import { credentialKey } from "../../security/credential-key.js";
24
28
  import {
25
29
  bulkSetSecureKeysAsync,
30
+ getSecureKeyAsync,
26
31
  getSecureKeyResultAsync,
27
32
  listSecureKeysAsync,
28
33
  } from "../../security/secure-keys.js";
34
+ import {
35
+ getCredentialMetadata,
36
+ upsertCredentialMetadata,
37
+ } from "../../tools/credentials/metadata-store.js";
29
38
  import { getLogger } from "../../util/logger.js";
30
39
  import {
31
40
  getDbPath,
@@ -34,6 +43,10 @@ import {
34
43
  } from "../../util/platform.js";
35
44
  import { httpError } from "../http-errors.js";
36
45
  import type { RouteDefinition } from "../http-router.js";
46
+ import {
47
+ validateGcsSignedUrl,
48
+ type ValidateGcsSignedUrlOptions,
49
+ } from "../migrations/gcs-signed-url.js";
37
50
  import { streamExportVBundle } from "../migrations/vbundle-builder.js";
38
51
  import {
39
52
  analyzeImport,
@@ -42,11 +55,74 @@ import {
42
55
  import {
43
56
  commitImport,
44
57
  extractCredentialsFromBundle,
58
+ type ImportCommitReport,
59
+ type ImportCommitResult,
45
60
  } from "../migrations/vbundle-importer.js";
61
+ import { streamCommitImport } from "../migrations/vbundle-streaming-importer.js";
46
62
  import { validateVBundle } from "../migrations/vbundle-validator.js";
47
63
 
48
- /** Credentials with this prefix are platform-identity keys and must not be imported. */
49
- const PLATFORM_CREDENTIAL_PREFIX = "vellum:";
64
+ /**
65
+ * CES account prefix for platform-identity (`vellum:*`) credentials. Entries
66
+ * with an account that starts with this string are filtered out of any
67
+ * imported bundle so they don't overwrite the target's own Django-provisioned
68
+ * platform identity (most notably `assistant_api_key`).
69
+ *
70
+ * Derived from `credentialKey("vellum", "")` so the prefix automatically
71
+ * tracks the real CES account format — the literal string `"credential/vellum/"`.
72
+ */
73
+ const PLATFORM_CREDENTIAL_PREFIX = credentialKey("vellum", "");
74
+
75
+ /**
76
+ * Platform-identity fields that the managed runtime expects to see in CES.
77
+ * Django's post-hatch provisioning populates the first four via
78
+ * `POST /v1/secrets`; `platform_organization_id` and `platform_user_id` are
79
+ * populated by the signed-in client after hatch (onboarding, teleport,
80
+ * local→managed transfer) because Django has no signed-in user session to
81
+ * resolve them. Either set of writes can race with the import — the CES
82
+ * write survives (separate volume), but the metadata upsert may be
83
+ * clobbered by the in-place clear / atomic swap. After every import we
84
+ * reconcile metadata.json against CES so any field CES already holds a
85
+ * value for gets a matching metadata entry.
86
+ */
87
+ const VELLUM_PLATFORM_IDENTITY_FIELDS = [
88
+ "platform_base_url",
89
+ "assistant_api_key",
90
+ "platform_assistant_id",
91
+ "platform_organization_id",
92
+ "platform_user_id",
93
+ "webhook_secret",
94
+ ] as const;
95
+
96
+ /**
97
+ * Idempotent post-import reconciliation: for each vellum:* field, if CES
98
+ * has a value but metadata.json doesn't list it, upsert the entry. Pure
99
+ * add-only — never deletes anything. Safe to run whether or not Django's
100
+ * post-hatch provisioning has completed (missing CES values are skipped).
101
+ *
102
+ * Exported for direct unit-testing.
103
+ */
104
+ export async function reconcileVellumMetadataFromCes(warningSink: {
105
+ warnings: string[];
106
+ }): Promise<void> {
107
+ for (const field of VELLUM_PLATFORM_IDENTITY_FIELDS) {
108
+ try {
109
+ const value = await getSecureKeyAsync(credentialKey("vellum", field));
110
+ if (!value) continue;
111
+ if (getCredentialMetadata("vellum", field)) continue;
112
+ upsertCredentialMetadata("vellum", field, {});
113
+ log.info(
114
+ { field },
115
+ "Reconciled vellum:* metadata entry from CES after import",
116
+ );
117
+ } catch (err) {
118
+ warningSink.warnings.push(
119
+ `Failed to reconcile vellum:${field} metadata: ${
120
+ err instanceof Error ? err.message : String(err)
121
+ }`,
122
+ );
123
+ }
124
+ }
125
+ }
50
126
 
51
127
  const log = getLogger("migration-routes");
52
128
 
@@ -179,9 +255,6 @@ export async function handleMigrationExport(req: Request): Promise<Response> {
179
255
  }
180
256
 
181
257
  const result = await streamExportVBundle({
182
- // hooksDir is intentionally omitted — hooks now live under workspace/hooks/
183
- // and are included in the workspace walk. Passing hooksDir separately would
184
- // export them twice (once as workspace/hooks/... and again as hooks/...).
185
258
  workspaceDir: getWorkspaceDir(),
186
259
  source: "runtime-export",
187
260
  description,
@@ -380,11 +453,15 @@ export async function handleMigrationImportPreflight(
380
453
  * 5. Verifies post-write integrity (SHA-256 check)
381
454
  * 6. Returns a detailed report of what was imported
382
455
  *
383
- * The file can be sent as:
456
+ * The bundle can be supplied in any of three ways:
384
457
  * - Raw binary body with Content-Type: application/octet-stream
385
458
  * - Multipart form data with a "file" field
459
+ * - JSON body `{ "url": "<signed-gcs-url>" }` (Content-Type:
460
+ * application/json). The daemon fetches and streams the archive
461
+ * through `streamCommitImport`, so peak memory stays bounded by a
462
+ * single tar entry rather than bundle size.
386
463
  *
387
- * Returns:
464
+ * Returns (all three paths):
388
465
  * 200: {
389
466
  * success: true,
390
467
  * summary: { total_files, files_created, files_overwritten, files_skipped, backups_created },
@@ -393,12 +470,25 @@ export async function handleMigrationImportPreflight(
393
470
  * warnings: [...]
394
471
  * }
395
472
  * 200: { success: false, reason: "validation_failed", errors: [...] }
396
- * 400: Standard error envelope for missing/empty body
473
+ * 400: Standard error envelope for missing/empty body or malformed URL
397
474
  * 500: Standard error envelope for unexpected failures
475
+ * 502: { success: false, reason: "fetch_failed", upstream_status?: number }
476
+ * (URL path only — upstream GCS fetch failed)
398
477
  *
399
478
  * Auth: Requires settings.write scope. Allowed for actor, svc_gateway, svc_daemon, local.
400
479
  */
401
480
  export async function handleMigrationImport(req: Request): Promise<Response> {
481
+ // 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") ?? "";
488
+ if (contentType.includes("application/json")) {
489
+ return handleMigrationImportFromUrl(req);
490
+ }
491
+
402
492
  const extracted = await extractFileData(req);
403
493
  if ("error" in extracted) {
404
494
  return extracted.error;
@@ -445,122 +535,29 @@ export async function handleMigrationImport(req: Request): Promise<Response> {
445
535
  });
446
536
 
447
537
  if (!result.ok) {
448
- if (result.reason === "validation_failed") {
449
- return Response.json({
450
- success: false,
451
- reason: "validation_failed",
452
- errors: result.errors,
453
- });
454
- }
455
-
456
- if (result.reason === "extraction_failed") {
457
- return Response.json(
458
- {
459
- success: false,
460
- reason: "extraction_failed",
461
- message: result.message,
462
- },
463
- { status: 500 },
464
- );
465
- }
466
-
467
- // write_failed
468
- return Response.json(
469
- {
470
- success: false,
471
- reason: "write_failed",
472
- message: result.message,
473
- ...(result.partial_report
474
- ? { partial_report: result.partial_report }
475
- : {}),
476
- },
477
- { status: 500 },
478
- );
538
+ return importCommitFailureResponse(result);
479
539
  }
480
540
 
481
541
  // Import credentials from the bundle into CES (non-blocking — failures
482
542
  // are logged as warnings but do not fail the overall import).
483
- let credentialsImported:
484
- | {
485
- total: number;
486
- succeeded: number;
487
- failed: number;
488
- failedAccounts: string[];
489
- skippedPlatform: number;
490
- }
491
- | undefined;
543
+ let credentialsImported: CredentialImportSummary | undefined;
492
544
 
493
545
  if (validation.entries) {
494
546
  const bundleCredentials = extractCredentialsFromBundle(
495
547
  validation.entries,
496
548
  validation.manifest!,
497
549
  );
498
-
499
- // Filter out platform-identity credentials (vellum:*) — these are
500
- // environment-specific and must not overwrite the target's own identity.
501
- const userCredentials = bundleCredentials.filter(
502
- (c) => !c.account.startsWith(PLATFORM_CREDENTIAL_PREFIX),
550
+ credentialsImported = await importBundleCredentialsIntoCes(
551
+ bundleCredentials,
552
+ result.report,
503
553
  );
504
- const skippedPlatform = bundleCredentials.length - userCredentials.length;
505
- if (skippedPlatform > 0) {
506
- log.info(
507
- `Skipped ${skippedPlatform} platform credential(s) from import`,
508
- );
509
- }
510
-
511
- if (userCredentials.length > 0) {
512
- try {
513
- const credResults = await bulkSetSecureKeysAsync(userCredentials);
514
- const failedResults = credResults.filter((r) => !r.ok);
515
- if (failedResults.length > 0) {
516
- log.warn(
517
- { failed: failedResults.map((f) => f.account) },
518
- "Some credentials failed to import",
519
- );
520
- }
521
- log.info(
522
- { total: userCredentials.length, failed: failedResults.length },
523
- "Credential import complete",
524
- );
525
- const succeeded = userCredentials.length - failedResults.length;
526
- credentialsImported = {
527
- total: userCredentials.length,
528
- succeeded,
529
- failed: failedResults.length,
530
- failedAccounts: failedResults.map((f) => f.account),
531
- skippedPlatform,
532
- };
533
- if (failedResults.length > 0) {
534
- result.report.warnings.push(
535
- `Imported ${succeeded} credential(s), ${failedResults.length} failed`,
536
- );
537
- }
538
- } catch (err) {
539
- log.warn({ err }, "Credential import failed entirely");
540
- result.report.warnings.push(
541
- `Credential import failed: ${err instanceof Error ? err.message : String(err)}`,
542
- );
543
- credentialsImported = {
544
- total: userCredentials.length,
545
- succeeded: 0,
546
- failed: userCredentials.length,
547
- failedAccounts: userCredentials.map((c) => c.account),
548
- skippedPlatform,
549
- };
550
- }
551
- } else if (skippedPlatform > 0) {
552
- // All credentials in the bundle were platform credentials — report
553
- // the skip count even though nothing was sent to CES.
554
- credentialsImported = {
555
- total: userCredentials.length,
556
- succeeded: 0,
557
- failed: 0,
558
- failedAccounts: [],
559
- skippedPlatform,
560
- };
561
- }
562
554
  }
563
555
 
556
+ // Reconcile vellum:* metadata against CES so the gateway's
557
+ // readServiceCredentials can still find platform identity values even
558
+ // if Django's post-hatch provisioning raced with the import.
559
+ await reconcileVellumMetadataFromCes(result.report);
560
+
564
561
  // Invalidate in-process caches so imported settings.json and trust.json take effect
565
562
  invalidateConfigCache();
566
563
  clearTrustCache();
@@ -569,29 +566,571 @@ export async function handleMigrationImport(req: Request): Promise<Response> {
569
566
  // a newer version. This is non-blocking — the import has already
570
567
  // succeeded — but we surface a warning so the caller knows some data may
571
568
  // not be fully compatible with this daemon's schema.
569
+ appendNewerMigrationWarningsIfAny(result.report);
570
+
571
+ return importCommitSuccessResponse(result.report, credentialsImported);
572
+ } catch (err) {
573
+ log.error({ err }, "Unexpected error during import commit");
574
+ return httpError(
575
+ "INTERNAL_ERROR",
576
+ err instanceof Error ? err.message : "Unexpected import error",
577
+ 500,
578
+ );
579
+ }
580
+ }
581
+
582
+ // ---------------------------------------------------------------------------
583
+ // URL-body variant of POST /v1/migrations/import
584
+ // ---------------------------------------------------------------------------
585
+
586
+ /** 60 minutes — matches the gateway's upstream fetch deadline. */
587
+ const URL_FETCH_TIMEOUT_MS = 60 * 60 * 1000;
588
+
589
+ const MigrationImportUrlBody = z.object({ url: z.string().min(1) });
590
+
591
+ /**
592
+ * Marker attached to errors that originate from the upstream HTTP body
593
+ * stream (peer reset, abort mid-stream, DNS/transport failure after
594
+ * headers were received). The handler's catch/result-mapping path looks
595
+ * for this tag to return 502 `fetch_failed` instead of 500
596
+ * `extraction_failed` for truncated bodies, matching the OpenAPI
597
+ * contract.
598
+ */
599
+ const kFetchBodyError = Symbol.for("vellum.migrationImport.fetchBodyError");
600
+
601
+ /**
602
+ * Sidecar flag on the wrapper PassThrough indicating that its upstream
603
+ * was torn down by a tagged fetch-body error. Checked after
604
+ * streamCommitImport returns — the importer preserves the error message
605
+ * in `result.reason = "extraction_failed"` but strips the tag.
606
+ */
607
+ const kFetchBodyTornDown = Symbol.for(
608
+ "vellum.migrationImport.fetchBodyTornDown",
609
+ );
610
+
611
+ function tagFetchBodyError(err: NodeJS.ErrnoException): void {
612
+ (err as unknown as Record<symbol, boolean>)[kFetchBodyError] = true;
613
+ }
614
+
615
+ function isFetchBodyError(err: unknown): boolean {
616
+ if (!err || typeof err !== "object") return false;
617
+ return (err as unknown as Record<symbol, boolean>)[kFetchBodyError] === true;
618
+ }
619
+
620
+ function wasFetchBodyTornDown(stream: PassThrough): boolean {
621
+ return (
622
+ (stream as unknown as Record<symbol, boolean>)[kFetchBodyTornDown] === true
623
+ );
624
+ }
625
+
626
+ /**
627
+ * Test seam: the integration test needs to point the validator at a local
628
+ * HTTP server fixture. Production callers never pass this — the default
629
+ * keeps the validator strict (GCS host, HTTPS only, no explicit port).
630
+ */
631
+ let urlValidatorOptions: ValidateGcsSignedUrlOptions | undefined;
632
+
633
+ /**
634
+ * Test-only: override the allowed-host list used by the URL-body import
635
+ * handler. Call with `undefined` (or no arguments) to reset to production
636
+ * defaults. This is intentionally not exported from the module's public
637
+ * surface — tests import it directly from this file.
638
+ */
639
+ export function _setUrlImportValidatorOptionsForTests(
640
+ options: ValidateGcsSignedUrlOptions | undefined,
641
+ ): void {
642
+ urlValidatorOptions = options;
643
+ }
644
+
645
+ /**
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.
652
+ */
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
+ }
665
+
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
+ );
673
+ }
674
+
675
+ // ── 2. Validate the URL (defense-in-depth; never log `parsed.data.url`).
676
+ const validated = validateGcsSignedUrl(parsed.data.url, urlValidatorOptions);
677
+ 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
+ log.warn({ reason: validated.reason }, "Rejected migration import URL");
682
+ return httpError("BAD_REQUEST", `Invalid URL: ${validated.reason}`, 400);
683
+ }
684
+
685
+ log.info(
686
+ { host: validated.host, path: validated.path },
687
+ "migration import from URL",
688
+ );
689
+
690
+ const startedAt = Date.now();
691
+
692
+ // ── 3. Fetch the URL ──────────────────────────────────────────────────
693
+ let upstream: Response;
694
+ try {
695
+ upstream = await fetch(parsed.data.url, {
696
+ signal: AbortSignal.timeout(URL_FETCH_TIMEOUT_MS),
697
+ // SSRF guard: `validateGcsSignedUrl` only vetted the initial URL.
698
+ // Default fetch behavior follows 3xx responses, which would let a
699
+ // validated `storage.googleapis.com` URL redirect to an arbitrary
700
+ // host and bypass the allowlist. Reject redirects so we only ever
701
+ // read bytes from the URL the caller handed us.
702
+ redirect: "error",
703
+ });
704
+ } catch (err) {
705
+ log.error(
706
+ {
707
+ host: validated.host,
708
+ path: validated.path,
709
+ err: err instanceof Error ? err.message : String(err),
710
+ },
711
+ "Failed to fetch migration import URL",
712
+ );
713
+ return Response.json(
714
+ { success: false, reason: "fetch_failed" },
715
+ { status: 502 },
716
+ );
717
+ }
718
+
719
+ if (!upstream.ok) {
720
+ log.error(
721
+ {
722
+ host: validated.host,
723
+ path: validated.path,
724
+ upstream_status: upstream.status,
725
+ },
726
+ "Migration import URL fetch returned non-2xx",
727
+ );
728
+ // Drain the body so the underlying socket can be released promptly.
572
729
  try {
573
- const migrationValidation = validateMigrationState(getDb());
574
- if (migrationValidation.unknownCheckpoints.length > 0) {
575
- result.report.warnings.push(
576
- `Imported data contains ${migrationValidation.unknownCheckpoints.length} migration(s) from a newer version. Some data may not be fully compatible.`,
577
- );
578
- }
730
+ await upstream.body?.cancel();
579
731
  } catch {
580
- // Don't fail the import if validation itself errors
732
+ /* best effort */
733
+ }
734
+ return Response.json(
735
+ {
736
+ success: false,
737
+ reason: "fetch_failed",
738
+ upstream_status: upstream.status,
739
+ },
740
+ { status: 502 },
741
+ );
742
+ }
743
+
744
+ if (!upstream.body) {
745
+ log.error(
746
+ { host: validated.host, path: validated.path },
747
+ "Migration import URL fetch returned no body",
748
+ );
749
+ return Response.json(
750
+ { success: false, reason: "fetch_failed" },
751
+ { status: 502 },
752
+ );
753
+ }
754
+
755
+ // ── 4. Stream the response through the importer ──────────────────────
756
+ // Convert the WHATWG ReadableStream from fetch() into a Node Readable so
757
+ // the tar-stream / gunzip / hash-verifier pipeline inside
758
+ // streamCommitImport can consume it via `.pipe()`.
759
+ const upstreamNodeStream = Readable.fromWeb(
760
+ upstream.body as unknown as import("node:stream/web").ReadableStream<Uint8Array>,
761
+ );
762
+
763
+ // Wrap the upstream stream in a PassThrough that tags any error bubbling
764
+ // from the upstream HTTP body (peer reset, abort mid-stream, etc.) with a
765
+ // known symbol. When that tagged error surfaces out of
766
+ // 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
769
+ // body shape. We also propagate errors from the wrapper back to the
770
+ // upstream stream so its underlying connection is torn down cleanly.
771
+ //
772
+ // Bun's `Readable.fromWeb(fetchBody)` does NOT emit `'error'` when the
773
+ // TCP socket is torn down mid-response — it just emits `'close'` with
774
+ // no final `'end'`. We therefore track BOTH signals:
775
+ // • explicit `'error'` → tag the error, destroy the wrapper.
776
+ // • premature `'close'` → synthesize an error, tag it, destroy the
777
+ // wrapper. "Premature" = close fired without end first.
778
+ const taggedSource = new PassThrough();
779
+ let upstreamEnded = false;
780
+ // True once the importer (or any local consumer) initiates a teardown of
781
+ // `taggedSource`. The subsequent `close` on `upstreamNodeStream` is then a
782
+ // cascaded effect of our own teardown, NOT a real upstream failure — so
783
+ // we must NOT tag it as a fetch-body error, or local validation /
784
+ // extraction errors would be masked as 502 fetch_failed.
785
+ let localTeardownInitiated = false;
786
+ upstreamNodeStream.on("end", () => {
787
+ upstreamEnded = true;
788
+ });
789
+ upstreamNodeStream.on("error", (err: NodeJS.ErrnoException) => {
790
+ tagFetchBodyError(err);
791
+ (taggedSource as unknown as Record<symbol, boolean>)[kFetchBodyTornDown] =
792
+ true;
793
+ taggedSource.destroy(err);
794
+ });
795
+ upstreamNodeStream.on("close", () => {
796
+ if (upstreamEnded) return;
797
+ // A local teardown path closed us; don't treat this as an upstream
798
+ // failure. The real error (validation / extraction / hash mismatch) is
799
+ // already propagating through `streamCommitImport`'s result.
800
+ if (localTeardownInitiated) return;
801
+ const err = new Error(
802
+ "Upstream body stream closed before end",
803
+ ) as NodeJS.ErrnoException;
804
+ err.code = "ERR_UPSTREAM_BODY_CLOSED";
805
+ tagFetchBodyError(err);
806
+ (taggedSource as unknown as Record<symbol, boolean>)[kFetchBodyTornDown] =
807
+ true;
808
+ taggedSource.destroy(err);
809
+ });
810
+ upstreamNodeStream.pipe(taggedSource);
811
+ // Propagate wrapper teardown back to the upstream fetch body. When the
812
+ // streaming importer hits a validation/extraction error, it destroys
813
+ // `source` (which is `taggedSource`). Without this listener the
814
+ // `Readable.fromWeb(fetchBody)` stream would stay alive and continue
815
+ // buffering the remote response in the background until GC or the
816
+ // 60-minute timeout — a socket/bandwidth leak for any non-upstream error
817
+ // (malformed bundle, hash mismatch, size cap, etc.). We set
818
+ // `localTeardownInitiated` BEFORE destroying upstream so the resulting
819
+ // 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).
822
+ taggedSource.on("close", () => {
823
+ if (!upstreamNodeStream.destroyed) {
824
+ localTeardownInitiated = true;
825
+ upstreamNodeStream.destroy();
581
826
  }
827
+ });
582
828
 
583
- return Response.json({
584
- ...result.report,
585
- ...(credentialsImported ? { credentialsImported } : {}),
829
+ const pathResolver = new DefaultPathResolver(
830
+ getWorkspaceDir(),
831
+ getWorkspaceHooksDir(),
832
+ );
833
+
834
+ // streamCommitImport does its own resetDb() internally before the atomic
835
+ // swap, so we don't need to call it here.
836
+ let result: ImportCommitResult;
837
+ // Track credential-import outcome for inclusion in the success response.
838
+ // The streaming importer invokes our callback only after the atomic swap,
839
+ // so filling this in here is safe.
840
+ let credentialsImported: CredentialImportSummary | undefined;
841
+ // Per-invocation warning collector — scoped to this request so concurrent
842
+ // URL imports can't trample each other's warnings.
843
+ const credentialImportWarningSink: CredentialWarningSink = { warnings: [] };
844
+
845
+ try {
846
+ result = await streamCommitImport({
847
+ source: taggedSource,
848
+ pathResolver,
849
+ workspaceDir: getWorkspaceDir(),
850
+ importCredentials: async (bundleCredentials) => {
851
+ // We can't mutate `result.report.warnings` in place here — the
852
+ // streaming importer hasn't returned its report yet. Accumulate
853
+ // into a sidecar and merge into the final report below.
854
+ credentialsImported = await importBundleCredentialsIntoCes(
855
+ bundleCredentials,
856
+ credentialImportWarningSink,
857
+ );
858
+ },
586
859
  });
587
860
  } catch (err) {
588
- log.error({ err }, "Unexpected error during import commit");
861
+ if (isFetchBodyError(err)) {
862
+ log.error(
863
+ {
864
+ host: validated.host,
865
+ path: validated.path,
866
+ err: err instanceof Error ? err.message : String(err),
867
+ },
868
+ "Upstream body stream failed mid-import",
869
+ );
870
+ return Response.json(
871
+ { success: false, reason: "fetch_failed" },
872
+ { status: 502 },
873
+ );
874
+ }
875
+ log.error(
876
+ {
877
+ host: validated.host,
878
+ path: validated.path,
879
+ err: err instanceof Error ? err.message : String(err),
880
+ },
881
+ "streamCommitImport threw during URL-body import",
882
+ );
589
883
  return httpError(
590
884
  "INTERNAL_ERROR",
591
885
  err instanceof Error ? err.message : "Unexpected import error",
592
886
  500,
593
887
  );
594
888
  }
889
+
890
+ if (!result.ok) {
891
+ // streamCommitImport swallows the raw cause and maps any
892
+ // non-validation throw to `extraction_failed`. If the cause was an
893
+ // upstream body failure that we tagged at the source, surface the
894
+ // tag through the result (the importer preserves the message) by
895
+ // detecting the latched flag on the wrapper stream.
896
+ if (wasFetchBodyTornDown(taggedSource)) {
897
+ log.error(
898
+ {
899
+ host: validated.host,
900
+ path: validated.path,
901
+ reason: result.reason,
902
+ },
903
+ "Upstream body stream failed mid-import (detected via result)",
904
+ );
905
+ return Response.json(
906
+ { success: false, reason: "fetch_failed" },
907
+ { status: 502 },
908
+ );
909
+ }
910
+ log.warn(
911
+ {
912
+ host: validated.host,
913
+ path: validated.path,
914
+ reason: result.reason,
915
+ },
916
+ "streamCommitImport returned failure during URL-body import",
917
+ );
918
+ return importCommitFailureResponse(result);
919
+ }
920
+
921
+ // Merge any warnings accumulated by the credential-import callback into
922
+ // the final report.
923
+ if (credentialImportWarningSink.warnings.length > 0) {
924
+ result.report.warnings.push(...credentialImportWarningSink.warnings);
925
+ }
926
+
927
+ // Reconcile vellum:* metadata against CES so the gateway's
928
+ // readServiceCredentials can still find platform identity values even
929
+ // if Django's post-hatch provisioning raced with the streaming import
930
+ // (its metadata upsert may have landed in the backup-dir copy that the
931
+ // swap pushed aside, while its CES write survived on the separate
932
+ // volume).
933
+ await reconcileVellumMetadataFromCes(result.report);
934
+
935
+ // streamCommitImport already invalidated config + trust caches inside its
936
+ // post-swap cleanup. We only need to check whether the newly-imported DB
937
+ // carries migration checkpoints from a newer daemon version.
938
+ appendNewerMigrationWarningsIfAny(result.report);
939
+
940
+ const elapsedMs = Date.now() - startedAt;
941
+ log.info(
942
+ {
943
+ host: validated.host,
944
+ path: validated.path,
945
+ files_written: result.report.summary.files_created,
946
+ bytes_written: result.report.files.reduce((n, f) => n + f.size, 0),
947
+ elapsed_ms: elapsedMs,
948
+ },
949
+ "Migration import from URL complete",
950
+ );
951
+
952
+ return importCommitSuccessResponse(result.report, credentialsImported);
953
+ }
954
+
955
+ // ---------------------------------------------------------------------------
956
+ // Shared helpers for raw-bytes and URL paths
957
+ // ---------------------------------------------------------------------------
958
+
959
+ interface CredentialImportSummary {
960
+ total: number;
961
+ succeeded: number;
962
+ failed: number;
963
+ failedAccounts: string[];
964
+ skippedPlatform: number;
965
+ }
966
+
967
+ /**
968
+ * Minimal surface the credential-import helper needs to stash warnings —
969
+ * either a full `ImportCommitReport` (raw-bytes path, after commitImport
970
+ * returns) or an ephemeral per-request collector (streaming path, where the
971
+ * report doesn't exist yet when the callback fires).
972
+ */
973
+ interface CredentialWarningSink {
974
+ warnings: string[];
975
+ }
976
+
977
+ /**
978
+ * Filter platform-identity (vellum:*) credentials out of the bundle, push
979
+ * user credentials into CES via `bulkSetSecureKeysAsync`, and return a
980
+ * structured summary. Never throws — CES failures become report warnings.
981
+ */
982
+ async function importBundleCredentialsIntoCes(
983
+ bundleCredentials: Array<{ account: string; value: string }>,
984
+ warningSink: CredentialWarningSink,
985
+ ): Promise<CredentialImportSummary | undefined> {
986
+ // Filter out platform-identity credentials (vellum:*) — these are
987
+ // environment-specific and must not overwrite the target's own identity.
988
+ const userCredentials = bundleCredentials.filter(
989
+ (c) => !c.account.startsWith(PLATFORM_CREDENTIAL_PREFIX),
990
+ );
991
+ const skippedPlatform = bundleCredentials.length - userCredentials.length;
992
+ if (skippedPlatform > 0) {
993
+ log.info(`Skipped ${skippedPlatform} platform credential(s) from import`);
994
+ }
995
+
996
+ if (userCredentials.length === 0) {
997
+ if (skippedPlatform > 0) {
998
+ // All credentials in the bundle were platform credentials — report
999
+ // the skip count even though nothing was sent to CES.
1000
+ return {
1001
+ total: 0,
1002
+ succeeded: 0,
1003
+ failed: 0,
1004
+ failedAccounts: [],
1005
+ skippedPlatform,
1006
+ };
1007
+ }
1008
+ return undefined;
1009
+ }
1010
+
1011
+ try {
1012
+ const credResults = await bulkSetSecureKeysAsync(userCredentials);
1013
+ const failedResults = credResults.filter((r) => !r.ok);
1014
+ if (failedResults.length > 0) {
1015
+ log.warn(
1016
+ { failed: failedResults.map((f) => f.account) },
1017
+ "Some credentials failed to import",
1018
+ );
1019
+ }
1020
+ log.info(
1021
+ { total: userCredentials.length, failed: failedResults.length },
1022
+ "Credential import complete",
1023
+ );
1024
+ const succeeded = userCredentials.length - failedResults.length;
1025
+ if (failedResults.length > 0) {
1026
+ warningSink.warnings.push(
1027
+ `Imported ${succeeded} credential(s), ${failedResults.length} failed`,
1028
+ );
1029
+ }
1030
+ return {
1031
+ total: userCredentials.length,
1032
+ succeeded,
1033
+ failed: failedResults.length,
1034
+ failedAccounts: failedResults.map((f) => f.account),
1035
+ skippedPlatform,
1036
+ };
1037
+ } catch (err) {
1038
+ log.warn({ err }, "Credential import failed entirely");
1039
+ warningSink.warnings.push(
1040
+ `Credential import failed: ${err instanceof Error ? err.message : String(err)}`,
1041
+ );
1042
+ return {
1043
+ total: userCredentials.length,
1044
+ succeeded: 0,
1045
+ failed: userCredentials.length,
1046
+ failedAccounts: userCredentials.map((c) => c.account),
1047
+ skippedPlatform,
1048
+ };
1049
+ }
1050
+ }
1051
+
1052
+ /**
1053
+ * Append a warning to `report` when the newly-imported database contains
1054
+ * migration checkpoints from a daemon version newer than this one. Silent
1055
+ * on any validation error — the import has already succeeded.
1056
+ *
1057
+ * Gated on the report's own file counts: if the import didn't create or
1058
+ * overwrite any workspace files (no-swap success — e.g. credentials-only
1059
+ * bundle, all-skipped legacy bundle), the live DB is unchanged and any
1060
+ * "newer migrations" detected there came from the existing workspace,
1061
+ * NOT from the imported bundle. Attributing them to the bundle would be a
1062
+ * false positive, so skip the check entirely in that case.
1063
+ */
1064
+ function appendNewerMigrationWarningsIfAny(report: ImportCommitReport): void {
1065
+ if (report.summary.files_created + report.summary.files_overwritten === 0) {
1066
+ return;
1067
+ }
1068
+ try {
1069
+ const migrationValidation = validateMigrationState(getDb());
1070
+ if (migrationValidation.unknownCheckpoints.length > 0) {
1071
+ report.warnings.push(
1072
+ `Imported data contains ${migrationValidation.unknownCheckpoints.length} migration(s) from a newer version. Some data may not be fully compatible.`,
1073
+ );
1074
+ }
1075
+ } catch {
1076
+ // Don't fail the import if validation itself errors
1077
+ }
1078
+ }
1079
+
1080
+ /**
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.
1084
+ */
1085
+ function importCommitSuccessResponse(
1086
+ report: ImportCommitReport,
1087
+ credentialsImported: CredentialImportSummary | undefined,
1088
+ ): Response {
1089
+ return Response.json({
1090
+ ...report,
1091
+ ...(credentialsImported ? { credentialsImported } : {}),
1092
+ });
1093
+ }
1094
+
1095
+ /**
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.
1099
+ */
1100
+ function importCommitFailureResponse(
1101
+ result: Extract<ImportCommitResult, { ok: false }>,
1102
+ ): Response {
1103
+ if (result.reason === "validation_failed") {
1104
+ return Response.json({
1105
+ success: false,
1106
+ reason: "validation_failed",
1107
+ errors: result.errors,
1108
+ });
1109
+ }
1110
+
1111
+ 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
+ );
1120
+ }
1121
+
1122
+ // 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
+ );
595
1134
  }
596
1135
 
597
1136
  // ---------------------------------------------------------------------------
@@ -647,8 +1186,62 @@ export function migrationRouteDefinitions(): RouteDefinition[] {
647
1186
  method: "POST",
648
1187
  summary: "Import a .vbundle archive",
649
1188
  description:
650
- "Commit a .vbundle archive import to disk — destructive. Backs up existing files before overwriting.",
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.",
651
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
+ },
652
1245
  responseBody: z.object({
653
1246
  success: z.boolean(),
654
1247
  summary: z.object({}).passthrough(),