@vellumai/assistant 0.6.4 → 0.6.5

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 (717) hide show
  1. package/.prettierignore +5 -0
  2. package/ARCHITECTURE.md +32 -36
  3. package/Dockerfile +12 -0
  4. package/README.md +3 -4
  5. package/bun.lock +8 -3
  6. package/docs/architecture/integrations.md +1 -20
  7. package/docs/architecture/security.md +16 -16
  8. package/docs/error-handling.md +111 -0
  9. package/docs/skills.md +10 -10
  10. package/docs/stt-provider-onboarding.md +2 -1
  11. package/knip.json +9 -2
  12. package/node_modules/@vellumai/ces-contracts/package.json +2 -1
  13. package/node_modules/@vellumai/ces-contracts/src/__tests__/trust-rules.test.ts +471 -0
  14. package/node_modules/@vellumai/ces-contracts/src/trust-rules.ts +398 -4
  15. package/node_modules/@vellumai/credential-storage/bun.lock +2 -2
  16. package/node_modules/@vellumai/credential-storage/package.json +2 -2
  17. package/node_modules/@vellumai/credential-storage/src/oauth-runtime.ts +20 -2
  18. package/node_modules/@vellumai/egress-proxy/bun.lock +2 -2
  19. package/node_modules/@vellumai/egress-proxy/package.json +2 -2
  20. package/openapi.yaml +123 -11
  21. package/package.json +6 -3
  22. package/scripts/generate-openapi.ts +50 -11
  23. package/src/__tests__/agent-loop-callsite-precedence.test.ts +318 -0
  24. package/src/__tests__/agent-loop-sentry-hygiene.test.ts +137 -0
  25. package/src/__tests__/agent-loop.test.ts +112 -1
  26. package/src/__tests__/anthropic-error-formatting.test.ts +98 -0
  27. package/src/__tests__/anthropic-provider.test.ts +171 -2
  28. package/src/__tests__/approval-cascade.test.ts +31 -10
  29. package/src/__tests__/approval-routes-http.test.ts +134 -10
  30. package/src/__tests__/assistant-attachments.test.ts +44 -0
  31. package/src/__tests__/assistant-feature-flags-integration.test.ts +29 -0
  32. package/src/__tests__/browser-fill-credential.test.ts +1 -1
  33. package/src/__tests__/browser-identifier-parity-guard.test.ts +53 -0
  34. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +23 -33
  35. package/src/__tests__/browser-skill-endstate.test.ts +51 -182
  36. package/src/__tests__/btw-routes.test.ts +47 -1
  37. package/src/__tests__/call-controller.test.ts +1 -2
  38. package/src/__tests__/call-site-routing-provider.test.ts +214 -0
  39. package/src/__tests__/catalog-cache.test.ts +27 -4
  40. package/src/__tests__/channel-approval-routes.test.ts +4 -4
  41. package/src/__tests__/channel-reply-delivery.test.ts +300 -2
  42. package/src/__tests__/checker.test.ts +428 -501
  43. package/src/__tests__/cli-command-risk-guard.test.ts +30 -33
  44. package/src/__tests__/compaction-circuit-breaker.test.ts +336 -0
  45. package/src/__tests__/compaction.benchmark.test.ts +1 -1
  46. package/src/__tests__/config-analysis.test.ts +11 -28
  47. package/src/__tests__/config-loader-backfill.test.ts +174 -0
  48. package/src/__tests__/config-loader-corrupt.test.ts +183 -0
  49. package/src/__tests__/config-loader-quarantine-bulletin.test.ts +202 -0
  50. package/src/__tests__/config-schema-cmd.test.ts +11 -5
  51. package/src/__tests__/config-schema.test.ts +427 -114
  52. package/src/__tests__/config-watcher.test.ts +2 -2
  53. package/src/__tests__/contact-store-user-file.test.ts +72 -73
  54. package/src/__tests__/contacts-write.test.ts +4 -4
  55. package/src/__tests__/context-token-estimator.test.ts +191 -1
  56. package/src/__tests__/context-window-manager.test.ts +530 -2
  57. package/src/__tests__/conversation-abort-tool-results.test.ts +30 -16
  58. package/src/__tests__/conversation-agent-loop-overflow.test.ts +61 -17
  59. package/src/__tests__/conversation-agent-loop.test.ts +412 -82
  60. package/src/__tests__/conversation-attachments.test.ts +1 -1
  61. package/src/__tests__/conversation-confirmation-signals.test.ts +30 -9
  62. package/src/__tests__/conversation-error.test.ts +37 -6
  63. package/src/__tests__/conversation-history-web-search.test.ts +6 -0
  64. package/src/__tests__/conversation-init.benchmark.test.ts +36 -0
  65. package/src/__tests__/conversation-lifecycle.test.ts +336 -0
  66. package/src/__tests__/conversation-load-history-repair.test.ts +27 -10
  67. package/src/__tests__/conversation-pre-run-repair.test.ts +30 -16
  68. package/src/__tests__/conversation-process-callsite.test.ts +306 -0
  69. package/src/__tests__/conversation-provider-retry-repair.test.ts +30 -16
  70. package/src/__tests__/conversation-queue.test.ts +41 -26
  71. package/src/__tests__/conversation-routes-disk-view.test.ts +29 -1
  72. package/src/__tests__/conversation-routes-slash-commands.test.ts +31 -3
  73. package/src/__tests__/conversation-runtime-assembly.test.ts +2735 -55
  74. package/src/__tests__/conversation-runtime-workspace.test.ts +12 -12
  75. package/src/__tests__/conversation-skill-tools.test.ts +12 -146
  76. package/src/__tests__/conversation-slash-queue.test.ts +34 -19
  77. package/src/__tests__/conversation-slash-unknown.test.ts +30 -16
  78. package/src/__tests__/conversation-speed-override.test.ts +30 -11
  79. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +1035 -0
  80. package/src/__tests__/conversation-surfaces-standalone.test.ts +630 -0
  81. package/src/__tests__/conversation-title-service.test.ts +2 -2
  82. package/src/__tests__/conversation-tool-setup-batch-authorized.test.ts +1 -1
  83. package/src/__tests__/conversation-unread-route.test.ts +2 -2
  84. package/src/__tests__/conversation-usage.test.ts +3 -1
  85. package/src/__tests__/conversation-workspace-cache-state.test.ts +31 -10
  86. package/src/__tests__/conversation-workspace-injection.test.ts +43 -15
  87. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +44 -16
  88. package/src/__tests__/credential-broker-browser-fill.test.ts +110 -0
  89. package/src/__tests__/credential-security-invariants.test.ts +3 -0
  90. package/src/__tests__/credential-storage-oauth-compat.test.ts +18 -0
  91. package/src/__tests__/credential-storage-static-compat.test.ts +28 -0
  92. package/src/__tests__/credential-vault-unit.test.ts +135 -19
  93. package/src/__tests__/credentials-cli.test.ts +1 -9
  94. package/src/__tests__/cross-provider-web-search.test.ts +84 -0
  95. package/src/__tests__/daemon-server-persist-and-process-callsite.test.ts +92 -0
  96. package/src/__tests__/delete-propagation.test.ts +437 -0
  97. package/src/__tests__/dm-backfill.test.ts +417 -0
  98. package/src/__tests__/dm-persistence.test.ts +227 -0
  99. package/src/__tests__/edit-propagation.test.ts +280 -0
  100. package/src/__tests__/ephemeral-permissions.test.ts +93 -3
  101. package/src/__tests__/estimator-calibration-integration.test.ts +208 -0
  102. package/src/__tests__/estimator-calibration.test.ts +213 -0
  103. package/src/__tests__/extension-id-sync-guard.test.ts +26 -7
  104. package/src/__tests__/file-write-tool.test.ts +151 -1
  105. package/src/__tests__/filing-service.test.ts +255 -0
  106. package/src/__tests__/gemini-provider.test.ts +0 -3
  107. package/src/__tests__/guardian-grant-minting.test.ts +8 -0
  108. package/src/__tests__/headless-browser-interactions.test.ts +1 -1
  109. package/src/__tests__/heartbeat-service.test.ts +96 -15
  110. package/src/__tests__/host-shell-tool.test.ts +124 -18
  111. package/src/__tests__/http-user-message-parity.test.ts +29 -1
  112. package/src/__tests__/inbound-slack-persistence.test.ts +340 -0
  113. package/src/__tests__/intent-routing.test.ts +1 -40
  114. package/src/__tests__/llm-catalog-parity.test.ts +174 -0
  115. package/src/__tests__/llm-context-normalization.test.ts +121 -0
  116. package/src/__tests__/llm-resolver.test.ts +214 -0
  117. package/src/__tests__/llm-schema.test.ts +223 -0
  118. package/src/__tests__/managed-proxy-context.test.ts +6 -2
  119. package/src/__tests__/messaging-skill-split.test.ts +3 -34
  120. package/src/__tests__/migration-import-from-url.test.ts +684 -0
  121. package/src/__tests__/model-intents.test.ts +9 -83
  122. package/src/__tests__/notification-decision-fallback.test.ts +0 -10
  123. package/src/__tests__/notification-decision-identity.test.ts +0 -9
  124. package/src/__tests__/notification-decision-recipient-context.test.ts +0 -9
  125. package/src/__tests__/oauth-store.test.ts +10 -7
  126. package/src/__tests__/oauth2-gateway-transport.test.ts +8 -3
  127. package/src/__tests__/oauth2-refresh-retry.test.ts +279 -0
  128. package/src/__tests__/openai-provider.test.ts +7 -0
  129. package/src/__tests__/openai-responses-provider.test.ts +396 -0
  130. package/src/__tests__/openrouter-provider-only.test.ts +135 -0
  131. package/src/__tests__/outbound-slack-persistence.test.ts +293 -0
  132. package/src/__tests__/permission-checker-host-gate.test.ts +1 -1
  133. package/src/__tests__/permission-mode.test.ts +16 -0
  134. package/src/__tests__/permission-types.test.ts +0 -1
  135. package/src/__tests__/persona-resolver.test.ts +13 -13
  136. package/src/__tests__/pkb-autoinject.test.ts +37 -1
  137. package/src/__tests__/platform-bash-auto-approve.test.ts +1 -1
  138. package/src/__tests__/pricing.test.ts +50 -3
  139. package/src/__tests__/profiler-routes.test.ts +1 -1
  140. package/src/__tests__/provider-commit-message-generator.test.ts +14 -84
  141. package/src/__tests__/provider-env-vars-scope.test.ts +52 -0
  142. package/src/__tests__/provider-error-scenarios.test.ts +135 -6
  143. package/src/__tests__/provider-managed-proxy-integration.test.ts +42 -11
  144. package/src/__tests__/provider-registry-ollama.test.ts +1 -2
  145. package/src/__tests__/proxy-approval-callback.test.ts +0 -1
  146. package/src/__tests__/reaction-persistence.test.ts +560 -0
  147. package/src/__tests__/relay-server.test.ts +1 -1
  148. package/src/__tests__/require-fresh-approval.test.ts +1 -1
  149. package/src/__tests__/retry-openrouter-only-normalization.test.ts +136 -0
  150. package/src/__tests__/retry-thinking-tool-choice.test.ts +226 -0
  151. package/src/__tests__/risk-classifier-parity.test.ts +230 -0
  152. package/src/__tests__/sanitize-config-for-transfer.test.ts +78 -1
  153. package/src/__tests__/secret-ingress-http.test.ts +28 -0
  154. package/src/__tests__/secret-prompter-channel-fallback.test.ts +125 -0
  155. package/src/__tests__/secret-routes-managed-proxy.test.ts +2 -3
  156. package/src/__tests__/secret-scanner-executor.test.ts +1 -1
  157. package/src/__tests__/send-endpoint-busy.test.ts +29 -1
  158. package/src/__tests__/server-history-render.test.ts +31 -0
  159. package/src/__tests__/shell-parser-property.test.ts +13 -13
  160. package/src/__tests__/skill-cache-store.test.ts +182 -0
  161. package/src/__tests__/skills.test.ts +19 -33
  162. package/src/__tests__/slack-app-setup-skill-regression.test.ts +3 -1
  163. package/src/__tests__/slack-skill.test.ts +3 -8
  164. package/src/__tests__/starter-bundle.test.ts +35 -0
  165. package/src/__tests__/subagent-call-site-routing.test.ts +280 -0
  166. package/src/__tests__/suggestion-routes.test.ts +160 -3
  167. package/src/__tests__/system-prompt.test.ts +22 -35
  168. package/src/__tests__/task-runner.test.ts +3 -1
  169. package/src/__tests__/tcc-sandbox-deny.test.ts +198 -0
  170. package/src/__tests__/terminal-tools.test.ts +8 -0
  171. package/src/__tests__/test-support/browser-skill-harness.ts +2 -52
  172. package/src/__tests__/thread-backfill.test.ts +941 -0
  173. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -2
  174. package/src/__tests__/tool-executor-lifecycle-events.test.ts +2 -2
  175. package/src/__tests__/tool-executor.test.ts +60 -94
  176. package/src/__tests__/trust-store.test.ts +442 -109
  177. package/src/__tests__/update-bulletin-job.test.ts +389 -0
  178. package/src/__tests__/usage-cache-backfill-migration.test.ts +3 -1
  179. package/src/__tests__/verification-control-plane-policy.test.ts +1 -22
  180. package/src/__tests__/voice-session-bridge.test.ts +39 -0
  181. package/src/__tests__/volume-security-guard.test.ts +3 -2
  182. package/src/__tests__/web-search-history.test.ts +337 -0
  183. package/src/__tests__/workspace-migration-039-drop-legacy-llm-keys.test.ts +343 -0
  184. package/src/__tests__/workspace-migration-043-release-notes-latex-rendering.test.ts +202 -0
  185. package/src/__tests__/workspace-migration-045-release-notes-meet-avatar.test.ts +210 -0
  186. package/src/__tests__/workspace-migration-drop-user-md.test.ts +11 -11
  187. package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +841 -0
  188. package/src/__tests__/workspace-policy.test.ts +1 -13
  189. package/src/acp/client-handler.ts +1 -2
  190. package/src/agent/loop.ts +209 -17
  191. package/src/avatar/resvg-lazy.test.ts +136 -0
  192. package/src/avatar/resvg-lazy.ts +82 -9
  193. package/src/avatar/traits-png-sync.ts +21 -1
  194. package/src/browser/__tests__/operations.test.ts +163 -0
  195. package/src/browser/identifiers.ts +51 -0
  196. package/src/browser/operations.ts +660 -0
  197. package/src/browser/types.ts +81 -0
  198. package/src/calls/guardian-question-copy.ts +2 -2
  199. package/src/calls/telephony-stt-routing.ts +1 -1
  200. package/src/calls/voice-session-bridge.ts +1 -0
  201. package/src/cli/AGENTS.md +1 -1
  202. package/src/cli/commands/__tests__/attachment.test.ts +438 -0
  203. package/src/cli/commands/__tests__/browser.test.ts +554 -0
  204. package/src/cli/commands/__tests__/cache.test.ts +623 -0
  205. package/src/cli/commands/__tests__/email-list.test.ts +6 -0
  206. package/src/cli/commands/__tests__/email-send.test.ts +93 -1
  207. package/src/cli/commands/__tests__/image-generation.test.ts +666 -0
  208. package/src/cli/commands/__tests__/inference-send.test.ts +451 -0
  209. package/src/cli/commands/__tests__/stt-transcribe.test.ts +454 -0
  210. package/src/cli/commands/__tests__/task.test.ts +913 -0
  211. package/src/cli/commands/__tests__/tts-synthesize.test.ts +594 -0
  212. package/src/cli/commands/__tests__/ui-confirm.test.ts +650 -0
  213. package/src/cli/commands/__tests__/ui.test.ts +1215 -0
  214. package/src/cli/commands/__tests__/watchers.test.ts +716 -0
  215. package/src/cli/commands/attachment.ts +182 -0
  216. package/src/cli/commands/browser.ts +350 -0
  217. package/src/cli/commands/cache.ts +341 -0
  218. package/src/cli/commands/completions.ts +0 -3
  219. package/src/cli/commands/config.ts +6 -6
  220. package/src/cli/commands/conversations-import.ts +347 -0
  221. package/src/cli/commands/conversations.ts +14 -1
  222. package/src/cli/commands/email.ts +234 -194
  223. package/src/cli/commands/image-generation.ts +300 -0
  224. package/src/cli/commands/inference.ts +200 -0
  225. package/src/cli/commands/memory.ts +127 -17
  226. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -1
  227. package/src/cli/commands/platform/__tests__/connect.test.ts +0 -1
  228. package/src/cli/commands/platform/__tests__/disconnect.test.ts +0 -1
  229. package/src/cli/commands/platform/__tests__/status.test.ts +0 -1
  230. package/src/cli/commands/stt.ts +339 -0
  231. package/src/cli/commands/task.ts +795 -0
  232. package/src/cli/commands/trust.ts +50 -19
  233. package/src/cli/commands/tts.ts +273 -0
  234. package/src/cli/commands/ui.ts +670 -0
  235. package/src/cli/commands/watchers.ts +509 -0
  236. package/src/cli/lib/daemon-credential-client.ts +0 -19
  237. package/src/cli/program.ts +23 -4
  238. package/src/cli.ts +0 -37
  239. package/src/config/bundled-skills/conversations/tools/rename-conversation.ts +23 -1
  240. package/src/config/bundled-skills/media-processing/services/reduce.ts +1 -1
  241. package/src/config/bundled-skills/messaging/SKILL.md +2 -2
  242. package/src/config/bundled-skills/messaging/TOOLS.json +4 -0
  243. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +8 -1
  244. package/src/config/bundled-skills/messaging/tools/messaging-read.ts +15 -1
  245. package/src/config/bundled-skills/messaging/tools/messaging-search.ts +21 -1
  246. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +11 -12
  247. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +9 -8
  248. package/src/config/bundled-skills/settings/TOOLS.json +3 -3
  249. package/src/config/bundled-tool-registry.ts +0 -175
  250. package/src/config/env.ts +7 -2
  251. package/src/config/feature-flag-registry.json +25 -9
  252. package/src/config/llm-resolver.ts +128 -0
  253. package/src/config/loader.ts +194 -10
  254. package/src/config/raw-config-utils.ts +30 -2
  255. package/src/config/sanitize-for-transfer.ts +35 -0
  256. package/src/config/schema.ts +30 -41
  257. package/src/config/schemas/analysis.ts +3 -22
  258. package/src/config/schemas/calls.ts +0 -4
  259. package/src/config/schemas/filing.ts +2 -7
  260. package/src/config/schemas/heartbeat.ts +0 -5
  261. package/src/config/schemas/inference.ts +3 -23
  262. package/src/config/schemas/llm.ts +318 -0
  263. package/src/config/schemas/memory-processing.ts +1 -9
  264. package/src/config/schemas/notifications.ts +4 -11
  265. package/src/config/schemas/platform.ts +3 -9
  266. package/src/config/schemas/security.ts +33 -0
  267. package/src/config/schemas/services.ts +9 -4
  268. package/src/config/schemas/stt.ts +1 -0
  269. package/src/config/schemas/tts.ts +53 -0
  270. package/src/config/schemas/updates.ts +1 -1
  271. package/src/config/schemas/workspace-git.ts +3 -40
  272. package/src/config/skills.ts +2 -2
  273. package/src/context/__tests__/compact-prompt.test.ts +45 -0
  274. package/src/context/__tests__/microcompact.test.ts +805 -0
  275. package/src/context/estimator-calibration.ts +136 -0
  276. package/src/context/microcompact.ts +443 -0
  277. package/src/context/prompts/compact.md +12 -0
  278. package/src/context/token-estimator.ts +61 -3
  279. package/src/context/window-manager.ts +229 -25
  280. package/src/credential-execution/approval-bridge.ts +0 -1
  281. package/src/credential-execution/executable-discovery.ts +19 -8
  282. package/src/credential-execution/process-manager.test.ts +109 -0
  283. package/src/credential-execution/process-manager.ts +65 -2
  284. package/src/daemon/approval-generators.ts +29 -4
  285. package/src/daemon/assistant-attachments.ts +24 -13
  286. package/src/daemon/classifier.ts +2 -2
  287. package/src/daemon/config-watcher.ts +0 -1
  288. package/src/daemon/context-overflow-reducer.ts +4 -1
  289. package/src/daemon/conversation-agent-loop-handlers.ts +79 -12
  290. package/src/daemon/conversation-agent-loop.ts +462 -80
  291. package/src/daemon/conversation-attachments.ts +2 -6
  292. package/src/daemon/conversation-error.ts +36 -1
  293. package/src/daemon/conversation-lifecycle.ts +30 -6
  294. package/src/daemon/conversation-messaging.ts +73 -4
  295. package/src/daemon/conversation-process.ts +10 -4
  296. package/src/daemon/conversation-queue-manager.ts +3 -0
  297. package/src/daemon/conversation-runtime-assembly.ts +760 -29
  298. package/src/daemon/conversation-slash.ts +2 -2
  299. package/src/daemon/conversation-surfaces.ts +389 -1
  300. package/src/daemon/conversation-tool-setup.ts +10 -5
  301. package/src/daemon/conversation-usage.ts +1 -1
  302. package/src/daemon/conversation.ts +118 -30
  303. package/src/daemon/external-skills-bootstrap.ts +41 -0
  304. package/src/daemon/guardian-action-generators.ts +34 -14
  305. package/src/daemon/handlers/config-model.test.ts +86 -0
  306. package/src/daemon/handlers/config-model.ts +54 -12
  307. package/src/daemon/handlers/conversations.ts +9 -2
  308. package/src/daemon/handlers/shared.ts +39 -11
  309. package/src/daemon/handlers/skills.ts +2 -2
  310. package/src/daemon/handlers/slack-channel-oauth-install.ts +197 -0
  311. package/src/daemon/lifecycle.ts +76 -14
  312. package/src/daemon/message-types/conversations.ts +14 -0
  313. package/src/daemon/message-types/messages.ts +9 -1
  314. package/src/daemon/message-types/trust.ts +0 -2
  315. package/src/daemon/parse-actual-tokens-from-error.test.ts +57 -1
  316. package/src/daemon/parse-actual-tokens-from-error.ts +66 -0
  317. package/src/daemon/pkb-context-tracker.test.ts +169 -0
  318. package/src/daemon/pkb-context-tracker.ts +125 -0
  319. package/src/daemon/pkb-reminder-builder.test.ts +70 -0
  320. package/src/daemon/pkb-reminder-builder.ts +31 -0
  321. package/src/daemon/providers-setup.ts +6 -0
  322. package/src/daemon/server.ts +117 -9
  323. package/src/daemon/tool-side-effects.ts +0 -9
  324. package/src/daemon/watch-handler.ts +4 -4
  325. package/src/daemon/web-search-history.ts +126 -0
  326. package/src/events/domain-events.ts +0 -1
  327. package/src/filing/filing-service.ts +9 -10
  328. package/src/heartbeat/heartbeat-service.ts +76 -28
  329. package/src/home/__tests__/feed-scheduler.test.ts +39 -11
  330. package/src/home/__tests__/rollup-producer.test.ts +44 -0
  331. package/src/home/assistant-feed-authoring.ts +4 -0
  332. package/src/home/emit-feed-event.ts +4 -0
  333. package/src/home/feed-scheduler.ts +20 -4
  334. package/src/home/feed-types.ts +56 -2
  335. package/src/home/relationship-state-writer.ts +2 -2
  336. package/src/home/rollup-producer.ts +34 -5
  337. package/src/home/suggested-prompts.ts +101 -0
  338. package/src/ipc/__tests__/attachment-ipc.test.ts +213 -0
  339. package/src/ipc/__tests__/browser-ipc.test.ts +339 -0
  340. package/src/ipc/__tests__/cache-ipc.test.ts +266 -0
  341. package/src/ipc/__tests__/socket-path.test.ts +73 -0
  342. package/src/ipc/__tests__/task-ipc.test.ts +577 -0
  343. package/src/ipc/__tests__/ui-request-route.test.ts +495 -0
  344. package/src/ipc/__tests__/watcher-ipc.test.ts +295 -0
  345. package/src/ipc/cli-client.ts +2 -1
  346. package/src/ipc/cli-server.ts +26 -8
  347. package/src/ipc/gateway-client.ts +4 -4
  348. package/src/ipc/routes/attachment.ts +114 -0
  349. package/src/ipc/routes/browser-context.ts +61 -0
  350. package/src/ipc/routes/browser.ts +96 -0
  351. package/src/ipc/routes/cache.ts +96 -0
  352. package/src/ipc/routes/index.ts +17 -1
  353. package/src/ipc/routes/task-queue.ts +226 -0
  354. package/src/ipc/routes/task.ts +173 -0
  355. package/src/ipc/routes/ui-request.ts +50 -0
  356. package/src/ipc/routes/watcher.ts +203 -0
  357. package/src/ipc/socket-path.ts +100 -0
  358. package/src/memory/__tests__/conversation-analyze-job.test.ts +9 -8
  359. package/src/memory/__tests__/conversation-group-migration.test.ts +99 -0
  360. package/src/memory/admin.ts +18 -0
  361. package/src/memory/conversation-analyze-job.ts +14 -13
  362. package/src/memory/conversation-attention-store.ts +13 -6
  363. package/src/memory/conversation-crud.ts +103 -3
  364. package/src/memory/conversation-group-migration.ts +38 -6
  365. package/src/memory/conversation-title-service.ts +7 -4
  366. package/src/memory/db-init.ts +2 -0
  367. package/src/memory/embedding-backend.ts +1 -1
  368. package/src/memory/graph/compaction.ts +299 -0
  369. package/src/memory/graph/consolidation.ts +4 -4
  370. package/src/memory/graph/conversation-graph-memory.ts +89 -29
  371. package/src/memory/graph/extraction.test.ts +272 -2
  372. package/src/memory/graph/extraction.ts +173 -51
  373. package/src/memory/graph/graph-search.test.ts +92 -0
  374. package/src/memory/graph/graph-search.ts +4 -1
  375. package/src/memory/graph/narrative.ts +2 -2
  376. package/src/memory/graph/pattern-scan.ts +2 -2
  377. package/src/memory/graph/retriever.test.ts +459 -0
  378. package/src/memory/graph/retriever.ts +230 -48
  379. package/src/memory/graph/store.ts +41 -0
  380. package/src/memory/graph/tool-handlers.ts +27 -0
  381. package/src/memory/graph/tools.ts +6 -1
  382. package/src/memory/indexer.ts +5 -5
  383. package/src/memory/job-handlers/conversation-starters.ts +23 -20
  384. package/src/memory/job-handlers/summarization.ts +2 -2
  385. package/src/memory/job-utils.ts +7 -1
  386. package/src/memory/jobs/embed-pkb-file.test.ts +168 -0
  387. package/src/memory/jobs/embed-pkb-file.ts +54 -0
  388. package/src/memory/jobs-store.ts +44 -3
  389. package/src/memory/jobs-worker.ts +4 -0
  390. package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +1 -1
  391. package/src/memory/migrations/220-normalize-user-file-by-principal.ts +2 -2
  392. package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +82 -0
  393. package/src/memory/migrations/index.ts +1 -0
  394. package/src/memory/pkb/pkb-index.test.ts +368 -0
  395. package/src/memory/pkb/pkb-index.ts +255 -0
  396. package/src/memory/pkb/pkb-reconcile.test.ts +251 -0
  397. package/src/memory/pkb/pkb-reconcile.ts +148 -0
  398. package/src/memory/pkb/pkb-search.test.ts +438 -0
  399. package/src/memory/pkb/pkb-search.ts +137 -0
  400. package/src/memory/pkb/types.ts +53 -0
  401. package/src/memory/qdrant-client.ts +122 -1
  402. package/src/memory/slack-thread-store.ts +37 -0
  403. package/src/messaging/providers/gmail/adapter.ts +6 -16
  404. package/src/messaging/providers/gmail/client.ts +22 -0
  405. package/src/messaging/providers/gmail/types.ts +7 -0
  406. package/src/messaging/providers/slack/adapter.ts +14 -2
  407. package/src/messaging/providers/slack/backfill.test.ts +257 -0
  408. package/src/messaging/providers/slack/backfill.ts +101 -0
  409. package/src/messaging/providers/slack/message-metadata.test.ts +316 -0
  410. package/src/messaging/providers/slack/message-metadata.ts +123 -0
  411. package/src/messaging/providers/slack/render-transcript.test.ts +1373 -0
  412. package/src/messaging/providers/slack/render-transcript.ts +443 -0
  413. package/src/messaging/style-analyzer.ts +5 -2
  414. package/src/notifications/README.md +9 -5
  415. package/src/notifications/decision-engine.ts +3 -9
  416. package/src/notifications/preference-extractor.ts +2 -6
  417. package/src/oauth/oauth-store.ts +1 -0
  418. package/src/oauth/platform-connection.test.ts +47 -0
  419. package/src/oauth/platform-connection.ts +15 -5
  420. package/src/oauth/seed-providers.ts +4 -2
  421. package/src/permissions/approval-policy.test.ts +948 -0
  422. package/src/permissions/approval-policy.ts +257 -0
  423. package/src/permissions/bash-risk-classifier.test.ts +1208 -0
  424. package/src/permissions/bash-risk-classifier.ts +707 -0
  425. package/src/permissions/checker.ts +217 -708
  426. package/src/permissions/command-registry.test.ts +535 -0
  427. package/src/permissions/command-registry.ts +825 -0
  428. package/src/permissions/defaults.ts +26 -78
  429. package/src/permissions/file-risk-classifier.test.ts +535 -0
  430. package/src/permissions/file-risk-classifier.ts +274 -0
  431. package/src/permissions/risk-types.ts +205 -0
  432. package/src/permissions/secret-prompter.ts +53 -2
  433. package/src/permissions/skill-risk-classifier.test.ts +311 -0
  434. package/src/permissions/skill-risk-classifier.ts +214 -0
  435. package/src/permissions/trust-client.ts +52 -25
  436. package/src/permissions/trust-store-interface.ts +1 -6
  437. package/src/permissions/trust-store.ts +161 -62
  438. package/src/permissions/types.ts +23 -14
  439. package/src/permissions/web-risk-classifier.test.ts +170 -0
  440. package/src/permissions/web-risk-classifier.ts +89 -0
  441. package/src/permissions/workspace-policy.ts +1 -16
  442. package/src/platform/client.ts +19 -1
  443. package/src/prompts/persona-resolver.ts +3 -3
  444. package/src/prompts/system-prompt.ts +19 -20
  445. package/src/prompts/templates/SOUL.md +2 -2
  446. package/src/prompts/update-bulletin-job.ts +190 -0
  447. package/src/providers/__tests__/context-overflow-error.test.ts +328 -0
  448. package/src/providers/__tests__/provider-env-vars.test.ts +102 -0
  449. package/src/providers/__tests__/retry-callsite.test.ts +424 -0
  450. package/src/providers/anthropic/client.ts +183 -14
  451. package/src/providers/call-site-routing.ts +71 -0
  452. package/src/providers/gemini/client.ts +65 -2
  453. package/src/providers/managed-proxy/constants.ts +2 -1
  454. package/src/providers/model-catalog.ts +501 -33
  455. package/src/providers/model-intents.ts +4 -4
  456. package/src/providers/openai/chat-completions-provider.ts +57 -1
  457. package/src/providers/openai/responses-provider.ts +86 -9
  458. package/src/providers/openrouter/client.ts +76 -9
  459. package/src/providers/provider-env-vars.ts +56 -0
  460. package/src/providers/provider-send-message.ts +22 -5
  461. package/src/providers/ratelimit.ts +4 -0
  462. package/src/providers/registry.ts +19 -8
  463. package/src/providers/retry.ts +174 -39
  464. package/src/providers/speech-to-text/__tests__/resolve.test.ts +55 -0
  465. package/src/providers/speech-to-text/google-gemini-live-stream.ts +4 -4
  466. package/src/providers/speech-to-text/provider-catalog.ts +17 -0
  467. package/src/providers/speech-to-text/resolve.ts +7 -0
  468. package/src/providers/speech-to-text/xai-realtime.test.ts +578 -0
  469. package/src/providers/speech-to-text/xai-realtime.ts +796 -0
  470. package/src/providers/speech-to-text/xai.test.ts +155 -0
  471. package/src/providers/speech-to-text/xai.ts +97 -0
  472. package/src/providers/types.ts +93 -3
  473. package/src/runtime/AGENTS.md +2 -2
  474. package/src/runtime/__tests__/agent-wake.test.ts +43 -2
  475. package/src/runtime/__tests__/interactive-ui.test.ts +673 -0
  476. package/src/runtime/agent-wake.ts +63 -22
  477. package/src/runtime/auth/route-policy.ts +4 -0
  478. package/src/runtime/btw-sidechain.ts +13 -3
  479. package/src/runtime/channel-reply-delivery.ts +106 -2
  480. package/src/runtime/decision-token.ts +116 -0
  481. package/src/runtime/gateway-client.ts +2 -2
  482. package/src/runtime/http-router.ts +32 -0
  483. package/src/runtime/http-server.ts +52 -1
  484. package/src/runtime/http-types.ts +23 -1
  485. package/src/runtime/interactive-ui.ts +362 -0
  486. package/src/runtime/invite-instruction-generator.ts +2 -2
  487. package/src/runtime/migrations/__tests__/gcs-signed-url.test.ts +176 -0
  488. package/src/runtime/migrations/__tests__/vbundle-metadata-merge-integration.test.ts +390 -0
  489. package/src/runtime/migrations/__tests__/vbundle-metadata-merge.test.ts +221 -0
  490. package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +1540 -0
  491. package/src/runtime/migrations/__tests__/vbundle-streaming-validator.test.ts +453 -0
  492. package/src/runtime/migrations/__tests__/vbundle-tar-stream.test.ts +222 -0
  493. package/src/runtime/migrations/gcs-signed-url.ts +162 -0
  494. package/src/runtime/migrations/vbundle-importer.ts +154 -9
  495. package/src/runtime/migrations/vbundle-metadata-merge.ts +124 -0
  496. package/src/runtime/migrations/vbundle-streaming-importer.ts +2522 -0
  497. package/src/runtime/migrations/vbundle-streaming-validator.ts +244 -0
  498. package/src/runtime/migrations/vbundle-tar-stream.ts +217 -0
  499. package/src/runtime/migrations/vbundle-validator.ts +15 -6
  500. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +111 -0
  501. package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +114 -75
  502. package/src/runtime/routes/__tests__/migration-vellum-metadata-reconcile.test.ts +246 -0
  503. package/src/runtime/routes/approval-prompt-ts-tracker.ts +58 -0
  504. package/src/runtime/routes/approval-routes.ts +12 -17
  505. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +9 -0
  506. package/src/runtime/routes/avatar-routes.ts +20 -4
  507. package/src/runtime/routes/btw-routes.ts +1 -4
  508. package/src/runtime/routes/conversation-management-routes.ts +20 -2
  509. package/src/runtime/routes/conversation-routes.ts +133 -27
  510. package/src/runtime/routes/debug-routes.ts +1 -1
  511. package/src/runtime/routes/diagnostics-routes.ts +6 -4
  512. package/src/runtime/routes/events-routes.ts +16 -0
  513. package/src/runtime/routes/guardian-approval-interception.ts +33 -3
  514. package/src/runtime/routes/guardian-approval-prompt.ts +13 -3
  515. package/src/runtime/routes/home-feed-routes.ts +120 -2
  516. package/src/runtime/routes/inbound-message-handler.ts +912 -2
  517. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +113 -2
  518. package/src/runtime/routes/inbound-stages/background-dispatch.ts +61 -3
  519. package/src/runtime/routes/inbound-stages/edit-intercept.ts +129 -6
  520. package/src/runtime/routes/integrations/slack/channel.ts +25 -3
  521. package/src/runtime/routes/llm-context-normalization.ts +23 -1
  522. package/src/runtime/routes/migration-routes.ts +720 -124
  523. package/src/runtime/routes/settings-routes.ts +4 -2
  524. package/src/runtime/routes/trust-rules-routes.ts +30 -14
  525. package/src/runtime/routes/work-items-routes.test.ts +1 -1
  526. package/src/runtime/routes/work-items-routes.ts +3 -2
  527. package/src/runtime/services/__tests__/analyze-conversation.test.ts +25 -43
  528. package/src/runtime/services/analyze-conversation.ts +12 -16
  529. package/src/runtime/skill-route-registry.ts +28 -6
  530. package/src/schedule/scheduler.ts +8 -0
  531. package/src/security/__tests__/provider-key-env-fallback.test.ts +119 -0
  532. package/src/security/__tests__/untrusted-content.test.ts +109 -0
  533. package/src/security/oauth2.ts +98 -35
  534. package/src/security/secure-keys.ts +7 -8
  535. package/src/security/token-manager.ts +27 -13
  536. package/src/security/untrusted-content.ts +102 -0
  537. package/src/skills/catalog-cache.ts +26 -7
  538. package/src/skills/catalog-install.ts +31 -3
  539. package/src/skills/skill-cache-store.ts +97 -0
  540. package/src/stt/__tests__/daemon-batch-transcriber.test.ts +76 -0
  541. package/src/stt/daemon-batch-transcriber.ts +33 -0
  542. package/src/stt/stt-stream-session.ts +8 -1
  543. package/src/stt/types.ts +5 -1
  544. package/src/subagent/manager.ts +41 -13
  545. package/src/tasks/ephemeral-permissions.ts +9 -4
  546. package/src/telemetry/usage-telemetry-reporter.ts +27 -5
  547. package/src/tools/browser/__tests__/browser-status.test.ts +45 -2
  548. package/src/tools/browser/browser-execution.ts +65 -38
  549. package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +22 -0
  550. package/src/tools/credentials/tool-policy.ts +39 -5
  551. package/src/tools/credentials/vault.ts +9 -4
  552. package/src/tools/executor.ts +4 -0
  553. package/src/tools/filesystem/write.ts +52 -0
  554. package/src/tools/host-terminal/host-shell.ts +45 -5
  555. package/src/tools/memory/register.test.ts +185 -0
  556. package/src/tools/memory/register.ts +3 -1
  557. package/src/tools/network/web-fetch.ts +20 -10
  558. package/src/tools/network/web-search.ts +19 -4
  559. package/src/tools/permission-checker.ts +36 -15
  560. package/src/tools/policy-context.ts +25 -8
  561. package/src/tools/registry.ts +55 -3
  562. package/src/tools/side-effects.ts +0 -11
  563. package/src/tools/skills/execute.ts +2 -2
  564. package/src/tools/skills/sandbox-runner.ts +5 -2
  565. package/src/tools/terminal/backends/native.ts +51 -2
  566. package/src/tools/terminal/safe-env.ts +3 -2
  567. package/src/tools/terminal/shell.ts +1 -0
  568. package/src/tools/tool-manifest.ts +6 -21
  569. package/src/tools/types.ts +12 -3
  570. package/src/tools/verification-control-plane-policy.ts +1 -1
  571. package/src/tts/__tests__/provider-adapters.test.ts +240 -13
  572. package/src/tts/provider-catalog.ts +18 -0
  573. package/src/tts/providers/index.ts +2 -0
  574. package/src/tts/providers/xai-provider.ts +224 -0
  575. package/src/tts/types.ts +46 -0
  576. package/src/types/tar-stream.d.ts +66 -0
  577. package/src/util/json.ts +17 -0
  578. package/src/util/platform.ts +2 -2
  579. package/src/util/pricing.ts +15 -5
  580. package/src/watcher/engine.ts +1 -1
  581. package/src/watcher/providers/google-calendar.ts +134 -8
  582. package/src/watcher/providers/outlook-calendar.ts +42 -2
  583. package/src/workspace/git-service.ts +23 -4
  584. package/src/workspace/migrations/038-unify-llm-callsite-configs.ts +516 -0
  585. package/src/workspace/migrations/039-drop-legacy-llm-keys.ts +171 -0
  586. package/src/workspace/migrations/040-seed-latency-callsite-defaults.ts +154 -0
  587. package/src/workspace/migrations/041-backfill-google-gmail-settings-scope.ts +57 -0
  588. package/src/workspace/migrations/042-fix-backfill-google-gmail-settings-scope.ts +70 -0
  589. package/src/workspace/migrations/043-release-notes-latex-rendering.ts +75 -0
  590. package/src/workspace/migrations/044-bump-stale-provider-stream-timeout.ts +51 -0
  591. package/src/workspace/migrations/045-release-notes-meet-avatar.ts +130 -0
  592. package/src/workspace/migrations/AGENTS.md +1 -1
  593. package/src/workspace/migrations/registry.ts +16 -0
  594. package/src/workspace/provider-commit-message-generator.ts +19 -38
  595. package/src/__tests__/gmail-archive-fallback.test.ts +0 -193
  596. package/src/__tests__/gmail-archive-gate.test.ts +0 -246
  597. package/src/__tests__/gmail-preferences.test.ts +0 -117
  598. package/src/__tests__/outlook-attachments.test.ts +0 -301
  599. package/src/__tests__/outlook-automation-tools.test.ts +0 -425
  600. package/src/__tests__/outlook-categories.test.ts +0 -212
  601. package/src/__tests__/outlook-compose-tools.test.ts +0 -325
  602. package/src/__tests__/outlook-declutter-tools.test.ts +0 -585
  603. package/src/__tests__/outlook-follow-up.test.ts +0 -196
  604. package/src/__tests__/outlook-trash.test.ts +0 -77
  605. package/src/__tests__/outlook-unsubscribe.test.ts +0 -279
  606. package/src/__tests__/update-bulletin-format.test.ts +0 -181
  607. package/src/__tests__/update-bulletin-state.test.ts +0 -135
  608. package/src/__tests__/update-bulletin.test.ts +0 -478
  609. package/src/__tests__/update-template-contract.test.ts +0 -29
  610. package/src/cli/commands/doctor.ts +0 -341
  611. package/src/config/bundled-skills/browser/SKILL.md +0 -88
  612. package/src/config/bundled-skills/browser/TOOLS.json +0 -516
  613. package/src/config/bundled-skills/browser/tools/browser-attach.ts +0 -12
  614. package/src/config/bundled-skills/browser/tools/browser-click.ts +0 -12
  615. package/src/config/bundled-skills/browser/tools/browser-close.ts +0 -12
  616. package/src/config/bundled-skills/browser/tools/browser-detach.ts +0 -12
  617. package/src/config/bundled-skills/browser/tools/browser-extract.ts +0 -12
  618. package/src/config/bundled-skills/browser/tools/browser-fill-credential.ts +0 -12
  619. package/src/config/bundled-skills/browser/tools/browser-hover.ts +0 -12
  620. package/src/config/bundled-skills/browser/tools/browser-navigate.ts +0 -12
  621. package/src/config/bundled-skills/browser/tools/browser-press-key.ts +0 -12
  622. package/src/config/bundled-skills/browser/tools/browser-screenshot.ts +0 -12
  623. package/src/config/bundled-skills/browser/tools/browser-scroll.ts +0 -12
  624. package/src/config/bundled-skills/browser/tools/browser-select-option.ts +0 -12
  625. package/src/config/bundled-skills/browser/tools/browser-snapshot.ts +0 -12
  626. package/src/config/bundled-skills/browser/tools/browser-status.ts +0 -12
  627. package/src/config/bundled-skills/browser/tools/browser-type.ts +0 -12
  628. package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +0 -49
  629. package/src/config/bundled-skills/browser/tools/browser-wait-for.ts +0 -12
  630. package/src/config/bundled-skills/chatgpt-import/SKILL.md +0 -27
  631. package/src/config/bundled-skills/chatgpt-import/TOOLS.json +0 -27
  632. package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +0 -378
  633. package/src/config/bundled-skills/gmail/SKILL.md +0 -221
  634. package/src/config/bundled-skills/gmail/TOOLS.json +0 -588
  635. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +0 -256
  636. package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +0 -112
  637. package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +0 -44
  638. package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +0 -81
  639. package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +0 -108
  640. package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +0 -146
  641. package/src/config/bundled-skills/gmail/tools/gmail-label.ts +0 -53
  642. package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +0 -347
  643. package/src/config/bundled-skills/gmail/tools/gmail-preferences-tool.ts +0 -59
  644. package/src/config/bundled-skills/gmail/tools/gmail-preferences.ts +0 -82
  645. package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +0 -26
  646. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +0 -347
  647. package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +0 -29
  648. package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +0 -122
  649. package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +0 -67
  650. package/src/config/bundled-skills/gmail/tools/scan-result-store.ts +0 -100
  651. package/src/config/bundled-skills/gmail/tools/shared.ts +0 -47
  652. package/src/config/bundled-skills/google-calendar/SKILL.md +0 -51
  653. package/src/config/bundled-skills/google-calendar/TOOLS.json +0 -226
  654. package/src/config/bundled-skills/google-calendar/calendar-client.ts +0 -223
  655. package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +0 -27
  656. package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +0 -48
  657. package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +0 -19
  658. package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +0 -36
  659. package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +0 -58
  660. package/src/config/bundled-skills/google-calendar/tools/shared.ts +0 -17
  661. package/src/config/bundled-skills/google-calendar/types.ts +0 -97
  662. package/src/config/bundled-skills/outlook/SKILL.md +0 -196
  663. package/src/config/bundled-skills/outlook/TOOLS.json +0 -530
  664. package/src/config/bundled-skills/outlook/tools/outlook-attachments.ts +0 -85
  665. package/src/config/bundled-skills/outlook/tools/outlook-categories.ts +0 -77
  666. package/src/config/bundled-skills/outlook/tools/outlook-draft.ts +0 -84
  667. package/src/config/bundled-skills/outlook/tools/outlook-follow-up.ts +0 -94
  668. package/src/config/bundled-skills/outlook/tools/outlook-forward.ts +0 -49
  669. package/src/config/bundled-skills/outlook/tools/outlook-outreach-scan.ts +0 -237
  670. package/src/config/bundled-skills/outlook/tools/outlook-rules.ts +0 -161
  671. package/src/config/bundled-skills/outlook/tools/outlook-send-draft.ts +0 -32
  672. package/src/config/bundled-skills/outlook/tools/outlook-sender-digest.ts +0 -272
  673. package/src/config/bundled-skills/outlook/tools/outlook-trash.ts +0 -29
  674. package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +0 -129
  675. package/src/config/bundled-skills/outlook/tools/outlook-vacation.ts +0 -87
  676. package/src/config/bundled-skills/outlook/tools/shared.ts +0 -20
  677. package/src/config/bundled-skills/outlook-calendar/SKILL.md +0 -51
  678. package/src/config/bundled-skills/outlook-calendar/TOOLS.json +0 -221
  679. package/src/config/bundled-skills/outlook-calendar/calendar-client.ts +0 -252
  680. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-check-availability.ts +0 -53
  681. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-create-event.ts +0 -74
  682. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-get-event.ts +0 -18
  683. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-list-events.ts +0 -46
  684. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-rsvp.ts +0 -36
  685. package/src/config/bundled-skills/outlook-calendar/tools/shared.ts +0 -17
  686. package/src/config/bundled-skills/outlook-calendar/types.ts +0 -120
  687. package/src/config/bundled-skills/slack/SKILL.md +0 -108
  688. package/src/config/bundled-skills/tasks/SKILL.md +0 -37
  689. package/src/config/bundled-skills/tasks/TOOLS.json +0 -353
  690. package/src/config/bundled-skills/tasks/icon.svg +0 -34
  691. package/src/config/bundled-skills/tasks/tools/task-delete.ts +0 -12
  692. package/src/config/bundled-skills/tasks/tools/task-list-add.ts +0 -12
  693. package/src/config/bundled-skills/tasks/tools/task-list-remove.ts +0 -12
  694. package/src/config/bundled-skills/tasks/tools/task-list-show.ts +0 -12
  695. package/src/config/bundled-skills/tasks/tools/task-list-update.ts +0 -12
  696. package/src/config/bundled-skills/tasks/tools/task-list.ts +0 -12
  697. package/src/config/bundled-skills/tasks/tools/task-queue-run.ts +0 -12
  698. package/src/config/bundled-skills/tasks/tools/task-run.ts +0 -12
  699. package/src/config/bundled-skills/tasks/tools/task-save.ts +0 -12
  700. package/src/config/bundled-skills/watcher/SKILL.md +0 -31
  701. package/src/config/bundled-skills/watcher/TOOLS.json +0 -167
  702. package/src/config/bundled-skills/watcher/tools/watcher-create.ts +0 -12
  703. package/src/config/bundled-skills/watcher/tools/watcher-delete.ts +0 -12
  704. package/src/config/bundled-skills/watcher/tools/watcher-digest.ts +0 -12
  705. package/src/config/bundled-skills/watcher/tools/watcher-list.ts +0 -12
  706. package/src/config/bundled-skills/watcher/tools/watcher-update.ts +0 -12
  707. package/src/prompts/templates/UPDATES.md +0 -50
  708. package/src/prompts/update-bulletin-format.ts +0 -85
  709. package/src/prompts/update-bulletin-state.ts +0 -58
  710. package/src/prompts/update-bulletin-template-path.ts +0 -13
  711. package/src/prompts/update-bulletin.ts +0 -139
  712. package/src/shared/provider-env-vars.ts +0 -19
  713. package/src/tools/watcher/create.ts +0 -86
  714. package/src/tools/watcher/delete.ts +0 -36
  715. package/src/tools/watcher/digest.ts +0 -54
  716. package/src/tools/watcher/list.ts +0 -83
  717. package/src/tools/watcher/update.ts +0 -71
@@ -0,0 +1,684 @@
1
+ /**
2
+ * Integration tests for the JSON `{url}` body on POST /v1/migrations/import.
3
+ *
4
+ * Covered:
5
+ * - Happy path: a local http server serves a valid .vbundle, the handler
6
+ * fetches it, streams it through `streamCommitImport`, and returns the
7
+ * same success-report shape the raw-bytes path returns.
8
+ * - GCS 500: upstream server returns 500 → handler returns 502 with
9
+ * `{ reason: "fetch_failed", upstream_status: 500 }`.
10
+ * - Malformed body: `{}` and `{ "url": "" }` → 400.
11
+ * - Invalid GCS URL: a real https://evil.com URL is rejected with the
12
+ * redacted `Invalid URL: host` message; the raw URL is not echoed back
13
+ * in the response body.
14
+ * - Memory ceiling: a 100 MB fixture streams through with peak RSS delta
15
+ * bounded at ~128 MB. The ceiling is wider than the direct
16
+ * `streamCommitImport` ceiling because the URL handler stacks a Node
17
+ * HTTP response body + gunzip state + tar-stream state on top of the
18
+ * importer's per-entry working set.
19
+ *
20
+ * The raw-bytes ingress path is exercised by a separate test file,
21
+ * `migration-import-commit-http.test.ts`.
22
+ */
23
+
24
+ import {
25
+ createReadStream,
26
+ existsSync,
27
+ mkdirSync,
28
+ mkdtempSync,
29
+ realpathSync,
30
+ rmSync,
31
+ writeFileSync,
32
+ } from "node:fs";
33
+ import { createServer, type Server } from "node:http";
34
+ import { tmpdir } from "node:os";
35
+ import { join } from "node:path";
36
+ import { Database } from "bun:sqlite";
37
+ import {
38
+ afterAll,
39
+ afterEach,
40
+ beforeAll,
41
+ beforeEach,
42
+ describe,
43
+ expect,
44
+ mock,
45
+ test,
46
+ } from "bun:test";
47
+
48
+ // ---------------------------------------------------------------------------
49
+ // Test isolation: per-file workspace root.
50
+ //
51
+ // The shared test preload points VELLUM_WORKSPACE_DIR at a tmp dir. The
52
+ // streaming importer does an atomic rename of the workspace dir itself,
53
+ // which implicitly invalidates the shared tmp dir for subsequent tests in
54
+ // the same file. Each test below creates its own isolated workspace and
55
+ // re-points getWorkspaceDir() at it via the env var before invoking the
56
+ // handler.
57
+ // ---------------------------------------------------------------------------
58
+
59
+ const originalWorkspaceDir = process.env.VELLUM_WORKSPACE_DIR;
60
+
61
+ function freshWorkspaceRoot(): string {
62
+ const parent = realpathSync(
63
+ mkdtempSync(join(tmpdir(), "migration-import-from-url-")),
64
+ );
65
+ // The streaming importer renames workspaceDir itself, so put the
66
+ // workspace inside a parent dir we own.
67
+ const workspaceDir = join(parent, "workspace");
68
+ mkdirSync(workspaceDir, { recursive: true });
69
+ return workspaceDir;
70
+ }
71
+
72
+ function setWorkspaceDir(dir: string): void {
73
+ process.env.VELLUM_WORKSPACE_DIR = dir;
74
+ }
75
+
76
+ // ---------------------------------------------------------------------------
77
+ // Mocks (mirrors migration-import-commit-http.test.ts)
78
+ // ---------------------------------------------------------------------------
79
+
80
+ mock.module("../util/logger.js", () => ({
81
+ getLogger: () =>
82
+ new Proxy({} as Record<string, unknown>, {
83
+ get: () => () => {},
84
+ }),
85
+ }));
86
+
87
+ mock.module("../permissions/trust-store.js", () => ({
88
+ getAllRules: () => [],
89
+ isStarterBundleAccepted: () => false,
90
+ clearCache: () => {},
91
+ }));
92
+
93
+ mock.module("../config/loader.js", () => ({
94
+ getConfig: () => ({
95
+ ui: {},
96
+ model: "test",
97
+ provider: "test",
98
+ memory: { enabled: false },
99
+ rateLimit: { maxRequestsPerMinute: 0 },
100
+ secretDetection: { enabled: false },
101
+ }),
102
+ invalidateConfigCache: () => {},
103
+ }));
104
+
105
+ mock.module("../config/env.js", () => ({
106
+ isHttpAuthDisabled: () => true,
107
+ hasUngatedHttpAuthDisabled: () => false,
108
+ getGatewayInternalBaseUrl: () => "http://127.0.0.1:7830",
109
+ getGatewayPort: () => 7830,
110
+ getRuntimeHttpPort: () => 7821,
111
+ getRuntimeHttpHost: () => "127.0.0.1",
112
+ getRuntimeGatewayOriginSecret: () => undefined,
113
+ getIngressPublicBaseUrl: () => undefined,
114
+ setIngressPublicBaseUrl: () => {},
115
+ }));
116
+
117
+ // ---------------------------------------------------------------------------
118
+ // Imports (after mocks so module-level code picks up the stubs)
119
+ // ---------------------------------------------------------------------------
120
+
121
+ import { resetDb } from "../memory/db-connection.js";
122
+ import { buildVBundle } from "../runtime/migrations/vbundle-builder.js";
123
+ import {
124
+ _setUrlImportValidatorOptionsForTests,
125
+ handleMigrationImport,
126
+ } from "../runtime/routes/migration-routes.js";
127
+
128
+ // ---------------------------------------------------------------------------
129
+ // Local http fixture server
130
+ // ---------------------------------------------------------------------------
131
+
132
+ interface FixtureServer {
133
+ server: Server;
134
+ port: number;
135
+ close: () => Promise<void>;
136
+ }
137
+
138
+ async function startFixtureServer(
139
+ handler: (
140
+ req: import("node:http").IncomingMessage,
141
+ res: import("node:http").ServerResponse,
142
+ ) => void,
143
+ ): Promise<FixtureServer> {
144
+ const server = createServer(handler);
145
+ await new Promise<void>((resolve) => server.listen(0, "127.0.0.1", resolve));
146
+ const address = server.address();
147
+ if (!address || typeof address === "string") {
148
+ throw new Error("fixture server bound to unexpected address");
149
+ }
150
+ const port = address.port;
151
+ return {
152
+ server,
153
+ port,
154
+ close: async () => {
155
+ await new Promise<void>((resolve) => server.close(() => resolve()));
156
+ },
157
+ };
158
+ }
159
+
160
+ function makeFakeSignedUrl(port: number): string {
161
+ // `validateGcsSignedUrl` requires a signature query param; pin a dummy.
162
+ return `http://127.0.0.1:${port}/bundle?X-Goog-Signature=fake`;
163
+ }
164
+
165
+ // ---------------------------------------------------------------------------
166
+ // Fixture helpers
167
+ // ---------------------------------------------------------------------------
168
+
169
+ function makeSmallValidBundlePath(parent: string): string {
170
+ const { archive } = buildVBundle({
171
+ files: [
172
+ {
173
+ path: "workspace/data/db/assistant.db",
174
+ data: new TextEncoder().encode("SQLite format 3\0"),
175
+ },
176
+ {
177
+ path: "workspace/config.json",
178
+ data: new TextEncoder().encode(
179
+ JSON.stringify({ provider: "anthropic", model: "test-model" }),
180
+ ),
181
+ },
182
+ ],
183
+ });
184
+ const bundlePath = join(parent, "fixture-small.vbundle");
185
+ writeFileSync(bundlePath, archive);
186
+ return bundlePath;
187
+ }
188
+
189
+ // ---------------------------------------------------------------------------
190
+ // Global test-only allowlist: widen the URL validator to accept 127.0.0.1.
191
+ // ---------------------------------------------------------------------------
192
+
193
+ beforeAll(() => {
194
+ _setUrlImportValidatorOptionsForTests({
195
+ allowedHosts: ["127.0.0.1", "storage.googleapis.com"],
196
+ });
197
+ });
198
+
199
+ afterAll(() => {
200
+ _setUrlImportValidatorOptionsForTests(undefined);
201
+ if (originalWorkspaceDir !== undefined) {
202
+ process.env.VELLUM_WORKSPACE_DIR = originalWorkspaceDir;
203
+ }
204
+ });
205
+
206
+ // Each test gets its own workspace dir so the streaming importer's atomic
207
+ // swap doesn't leak state across tests.
208
+ let testWorkspaceRoot: string;
209
+ let testParent: string;
210
+
211
+ beforeEach(() => {
212
+ testWorkspaceRoot = freshWorkspaceRoot();
213
+ testParent = join(testWorkspaceRoot, "..");
214
+ setWorkspaceDir(testWorkspaceRoot);
215
+ });
216
+
217
+ afterEach(() => {
218
+ try {
219
+ rmSync(testParent, { recursive: true, force: true });
220
+ } catch {
221
+ /* best effort */
222
+ }
223
+ });
224
+
225
+ // ---------------------------------------------------------------------------
226
+ // Response shape types
227
+ // ---------------------------------------------------------------------------
228
+
229
+ interface ImportCommitResponse {
230
+ success: boolean;
231
+ summary: {
232
+ total_files: number;
233
+ files_created: number;
234
+ files_overwritten: number;
235
+ files_skipped: number;
236
+ backups_created: number;
237
+ };
238
+ files: Array<{
239
+ path: string;
240
+ disk_path: string;
241
+ action: string;
242
+ size: number;
243
+ sha256: string;
244
+ backup_path: string | null;
245
+ }>;
246
+ manifest: Record<string, unknown>;
247
+ warnings: string[];
248
+ }
249
+
250
+ interface FetchFailedResponse {
251
+ success: false;
252
+ reason: "fetch_failed";
253
+ upstream_status?: number;
254
+ }
255
+
256
+ interface BadRequestResponse {
257
+ error: { code: string; message: string };
258
+ }
259
+
260
+ // ---------------------------------------------------------------------------
261
+ // Tests: JSON URL body
262
+ // ---------------------------------------------------------------------------
263
+
264
+ describe("handleMigrationImport — JSON {url} body", () => {
265
+ test("happy path: fetches fixture bundle from local http server and imports", async () => {
266
+ const bundlePath = makeSmallValidBundlePath(testParent);
267
+
268
+ const fixture = await startFixtureServer((req, res) => {
269
+ // Prove the server itself streams the response body.
270
+ res.writeHead(200, { "Content-Type": "application/octet-stream" });
271
+ createReadStream(bundlePath).pipe(res);
272
+ });
273
+
274
+ try {
275
+ const req = new Request("http://localhost/v1/migrations/import", {
276
+ method: "POST",
277
+ headers: { "Content-Type": "application/json" },
278
+ body: JSON.stringify({ url: makeFakeSignedUrl(fixture.port) }),
279
+ });
280
+
281
+ const res = await handleMigrationImport(req);
282
+ const body = (await res.json()) as ImportCommitResponse;
283
+
284
+ expect(res.status).toBe(200);
285
+ expect(body.success).toBe(true);
286
+ expect(body.summary).toBeDefined();
287
+ expect(body.summary.total_files).toBeGreaterThan(0);
288
+ expect(body.files.length).toBeGreaterThan(0);
289
+ expect(body.manifest).toBeDefined();
290
+
291
+ // Workspace was swapped into place and contains the fixture files.
292
+ expect(
293
+ existsSync(join(testWorkspaceRoot, "data", "db", "assistant.db")),
294
+ ).toBe(true);
295
+ expect(existsSync(join(testWorkspaceRoot, "config.json"))).toBe(true);
296
+ } finally {
297
+ await fixture.close();
298
+ }
299
+ });
300
+
301
+ test("upstream 500 returns 502 with reason: fetch_failed", async () => {
302
+ const fixture = await startFixtureServer((_req, res) => {
303
+ res.writeHead(500);
304
+ res.end("oh no");
305
+ });
306
+
307
+ try {
308
+ const req = new Request("http://localhost/v1/migrations/import", {
309
+ method: "POST",
310
+ headers: { "Content-Type": "application/json" },
311
+ body: JSON.stringify({ url: makeFakeSignedUrl(fixture.port) }),
312
+ });
313
+
314
+ const res = await handleMigrationImport(req);
315
+ const body = (await res.json()) as FetchFailedResponse;
316
+
317
+ expect(res.status).toBe(502);
318
+ expect(body.success).toBe(false);
319
+ expect(body.reason).toBe("fetch_failed");
320
+ expect(body.upstream_status).toBe(500);
321
+ } finally {
322
+ await fixture.close();
323
+ }
324
+ });
325
+
326
+ test('malformed body: {"url": ""} returns 400', async () => {
327
+ const req = new Request("http://localhost/v1/migrations/import", {
328
+ method: "POST",
329
+ headers: { "Content-Type": "application/json" },
330
+ body: JSON.stringify({ url: "" }),
331
+ });
332
+
333
+ const res = await handleMigrationImport(req);
334
+ const body = (await res.json()) as BadRequestResponse;
335
+
336
+ expect(res.status).toBe(400);
337
+ expect(body.error.code).toBe("BAD_REQUEST");
338
+ });
339
+
340
+ test("missing url key: {} returns 400", async () => {
341
+ const req = new Request("http://localhost/v1/migrations/import", {
342
+ method: "POST",
343
+ headers: { "Content-Type": "application/json" },
344
+ body: JSON.stringify({}),
345
+ });
346
+
347
+ const res = await handleMigrationImport(req);
348
+ const body = (await res.json()) as BadRequestResponse;
349
+
350
+ expect(res.status).toBe(400);
351
+ expect(body.error.code).toBe("BAD_REQUEST");
352
+ });
353
+
354
+ test("unparseable JSON body returns 400", async () => {
355
+ const req = new Request("http://localhost/v1/migrations/import", {
356
+ method: "POST",
357
+ headers: { "Content-Type": "application/json" },
358
+ body: "{not-json",
359
+ });
360
+
361
+ const res = await handleMigrationImport(req);
362
+ const body = (await res.json()) as BadRequestResponse;
363
+
364
+ expect(res.status).toBe(400);
365
+ expect(body.error.code).toBe("BAD_REQUEST");
366
+ });
367
+
368
+ test("invalid GCS URL returns 400 and does not leak the URL", async () => {
369
+ const rawUrl = "https://evil.example.com/bucket/obj?X-Goog-Signature=fake";
370
+ const req = new Request("http://localhost/v1/migrations/import", {
371
+ method: "POST",
372
+ headers: { "Content-Type": "application/json" },
373
+ body: JSON.stringify({ url: rawUrl }),
374
+ });
375
+
376
+ const res = await handleMigrationImport(req);
377
+ const rawBody = await res.text();
378
+
379
+ expect(res.status).toBe(400);
380
+
381
+ const parsed = JSON.parse(rawBody) as BadRequestResponse;
382
+ expect(parsed.error.code).toBe("BAD_REQUEST");
383
+ // The message should carry the redacted reason, not the URL itself.
384
+ expect(parsed.error.message.toLowerCase()).toContain("invalid url");
385
+ // Defense-in-depth: the raw URL must not appear anywhere in the response.
386
+ expect(rawBody).not.toContain("evil.example.com");
387
+ expect(rawBody).not.toContain("X-Goog-Signature=fake");
388
+ });
389
+
390
+ test("non-https scheme on default allowlist is rejected", async () => {
391
+ // Temporarily reset to the strict default allowlist so this test
392
+ // exercises the production validator configuration.
393
+ _setUrlImportValidatorOptionsForTests(undefined);
394
+ try {
395
+ const req = new Request("http://localhost/v1/migrations/import", {
396
+ method: "POST",
397
+ headers: { "Content-Type": "application/json" },
398
+ body: JSON.stringify({
399
+ url: "http://storage.googleapis.com/b/o?X-Goog-Signature=x",
400
+ }),
401
+ });
402
+
403
+ const res = await handleMigrationImport(req);
404
+ const body = (await res.json()) as BadRequestResponse;
405
+
406
+ expect(res.status).toBe(400);
407
+ expect(body.error.code).toBe("BAD_REQUEST");
408
+ expect(body.error.message).toContain("scheme");
409
+ } finally {
410
+ _setUrlImportValidatorOptionsForTests({
411
+ allowedHosts: ["127.0.0.1", "storage.googleapis.com"],
412
+ });
413
+ }
414
+ });
415
+ });
416
+
417
+ // ---------------------------------------------------------------------------
418
+ // Memory ceiling — streams a 100 MB fixture through and caps RSS.
419
+ // ---------------------------------------------------------------------------
420
+
421
+ function writeLargeFixtureToDisk(archivePath: string): void {
422
+ const CHUNK = 25 * 1024 * 1024;
423
+ const files = [0, 1, 2, 3].map((i) => ({
424
+ path: `workspace/big-${i}.bin`,
425
+ data: new Uint8Array(CHUNK).fill(0x41 + i),
426
+ }));
427
+ const { archive } = buildVBundle({ files });
428
+ writeFileSync(archivePath, archive);
429
+ }
430
+
431
+ describe("handleMigrationImport — URL body memory ceiling", () => {
432
+ test("100 MB fixture streams in without pushing RSS past ~128 MB over baseline", async () => {
433
+ const archivePath = join(testParent, "fixture-large.vbundle");
434
+ writeLargeFixtureToDisk(archivePath);
435
+
436
+ const fixture = await startFixtureServer((_req, res) => {
437
+ res.writeHead(200, { "Content-Type": "application/octet-stream" });
438
+ createReadStream(archivePath).pipe(res);
439
+ });
440
+
441
+ try {
442
+ // Force a GC opportunity before measuring baseline so stale fixture
443
+ // buffers don't count against the importer's budget. Bun exposes
444
+ // `Bun.gc` but not process.gc; gate on whether it exists.
445
+ const maybeBunGc = (
446
+ globalThis as { Bun?: { gc?: (sync?: boolean) => void } }
447
+ ).Bun?.gc;
448
+ if (typeof maybeBunGc === "function") maybeBunGc(true);
449
+
450
+ const baselineRss = process.memoryUsage().rss;
451
+
452
+ const req = new Request("http://localhost/v1/migrations/import", {
453
+ method: "POST",
454
+ headers: { "Content-Type": "application/json" },
455
+ body: JSON.stringify({ url: makeFakeSignedUrl(fixture.port) }),
456
+ });
457
+
458
+ const res = await handleMigrationImport(req);
459
+ const body = (await res.json()) as ImportCommitResponse;
460
+
461
+ const peakRss = process.memoryUsage().rss;
462
+
463
+ expect(res.status).toBe(200);
464
+ expect(body.success).toBe(true);
465
+
466
+ // 128 MB ceiling: the URL handler pipes the HTTP response body
467
+ // through gunzip + tar-stream on top of the streaming importer's
468
+ // per-entry working set. The extra framing state is bigger than
469
+ // raw-stream importing (which fits in ~64 MB), so 128 MB keeps
470
+ // enough headroom to detect full-bundle buffering without
471
+ // flapping on normal streaming-mode overhead.
472
+ const delta = peakRss - baselineRss;
473
+ expect(delta).toBeLessThan(128 * 1024 * 1024);
474
+ } finally {
475
+ await fixture.close();
476
+ }
477
+ }, 90_000);
478
+ });
479
+
480
+ // ---------------------------------------------------------------------------
481
+ // Gap A regression: no-swap success path must not append a stale
482
+ // "newer migration" warning sourced from the live DB. The warning would
483
+ // wrongly attribute live-DB state to the imported bundle when no bundle
484
+ // files actually land on disk (e.g. credentials-only bundle).
485
+ // ---------------------------------------------------------------------------
486
+
487
+ describe("handleMigrationImport — no-swap path omits newer-migration warning", () => {
488
+ test("credentials-only bundle does not inherit live-DB migration warnings", async () => {
489
+ // Seed the live workspace DB with a migration_* checkpoint that's NOT
490
+ // in the registry. validateMigrationState treats this as a "newer
491
+ // version" and would otherwise push a warning into the report. With
492
+ // the gate in appendNewerMigrationWarningsIfAny the warning must be
493
+ // suppressed when the import didn't modify the workspace.
494
+ const dbDir = join(testWorkspaceRoot, "data", "db");
495
+ mkdirSync(dbDir, { recursive: true });
496
+ const dbPath = join(dbDir, "assistant.db");
497
+ const seed = new Database(dbPath);
498
+ try {
499
+ seed.exec(`
500
+ CREATE TABLE memory_checkpoints (
501
+ key TEXT PRIMARY KEY,
502
+ value TEXT NOT NULL,
503
+ updated_at INTEGER NOT NULL
504
+ );
505
+ `);
506
+ seed
507
+ .query(
508
+ `INSERT INTO memory_checkpoints (key, value, updated_at) VALUES (?, ?, ?)`,
509
+ )
510
+ .run("migration_from_the_future", "1", Date.now());
511
+ } finally {
512
+ seed.close();
513
+ }
514
+
515
+ // Drop any cached Drizzle singleton so getDb() re-opens from the
516
+ // seeded path above when the handler calls it post-import.
517
+ resetDb();
518
+
519
+ // All-`vellum:*` credentials bundle: the streaming importer returns
520
+ // ok=true with zero files_created/overwritten (no-swap success),
521
+ // and the credential-import callback filters every entry as a
522
+ // platform credential so CES is never invoked.
523
+ const { archive } = buildVBundle({
524
+ files: [
525
+ {
526
+ path: "credentials/vellum:device-id",
527
+ data: new TextEncoder().encode("test-device-id"),
528
+ },
529
+ ],
530
+ });
531
+ const bundlePath = join(testParent, "fixture-creds-only.vbundle");
532
+ writeFileSync(bundlePath, archive);
533
+
534
+ const fixture = await startFixtureServer((_req, res) => {
535
+ res.writeHead(200, { "Content-Type": "application/octet-stream" });
536
+ createReadStream(bundlePath).pipe(res);
537
+ });
538
+
539
+ try {
540
+ const req = new Request("http://localhost/v1/migrations/import", {
541
+ method: "POST",
542
+ headers: { "Content-Type": "application/json" },
543
+ body: JSON.stringify({ url: makeFakeSignedUrl(fixture.port) }),
544
+ });
545
+
546
+ const res = await handleMigrationImport(req);
547
+ const body = (await res.json()) as ImportCommitResponse;
548
+
549
+ expect(res.status).toBe(200);
550
+ expect(body.success).toBe(true);
551
+ // Zero files touched — this is the no-swap success path.
552
+ expect(body.summary.files_created).toBe(0);
553
+ expect(body.summary.files_overwritten).toBe(0);
554
+
555
+ // The gate must suppress the newer-migration warning text. The
556
+ // helper's wording starts with "Imported data contains" and ends
557
+ // with "migration(s) from a newer version" — matching either
558
+ // substring is sufficient.
559
+ const stale = body.warnings.filter((w) =>
560
+ w.includes("from a newer version"),
561
+ );
562
+ expect(stale).toEqual([]);
563
+ } finally {
564
+ await fixture.close();
565
+ // Close the cached DB handle before the workspace dir gets rm'd
566
+ // in afterEach so we don't leak WAL/SHM files across tests.
567
+ resetDb();
568
+ }
569
+ });
570
+ });
571
+
572
+ // ---------------------------------------------------------------------------
573
+ // Gap B regression: an upstream body that drops mid-stream (peer reset,
574
+ // socket.destroy() after headers are sent) must surface as 502
575
+ // fetch_failed, not 500 extraction_failed. The tag-at-source plumbing in
576
+ // the URL handler routes importer-rethrown upstream errors to the fetch-
577
+ // failed branch.
578
+ // ---------------------------------------------------------------------------
579
+
580
+ describe("handleMigrationImport — upstream body dropped mid-stream", () => {
581
+ test("socket destroy mid-body returns 502 with reason: fetch_failed", async () => {
582
+ // Build a real gzipped tar prefix, then serve only the first N bytes
583
+ // and tear down the connection. gunzip inside streamCommitImport
584
+ // will surface this as a stream error; we tag it at the source so
585
+ // the handler maps it to 502 fetch_failed. Use random payload bytes
586
+ // so the gzip output is ~incompressible — a compressed all-zeros
587
+ // payload shrinks to <500 bytes total, which doesn't give us enough
588
+ // material to reliably deliver a partial body with gunzip state
589
+ // still alive across the socket teardown.
590
+ const incompressible = new Uint8Array(64 * 1024);
591
+ for (let i = 0; i < incompressible.length; i += 1) {
592
+ incompressible[i] = Math.floor(Math.random() * 256);
593
+ }
594
+ const { archive } = buildVBundle({
595
+ files: [
596
+ {
597
+ path: "workspace/data/db/assistant.db",
598
+ data: incompressible,
599
+ },
600
+ ],
601
+ });
602
+ // Safety net: if someone changes buildVBundle to return very small
603
+ // outputs, drop the test early rather than flaking on a too-short
604
+ // truncation window. 512 bytes is plenty given 64 KB of random data
605
+ // gzips to essentially its original size.
606
+ expect(archive.byteLength).toBeGreaterThan(512);
607
+
608
+ // Truncate to the first 256 bytes so upstream cannot deliver a
609
+ // usable tar stream. gunzip will error on the abrupt close.
610
+ const truncatedPrefix = archive.slice(0, 256);
611
+
612
+ const fixture = await startFixtureServer((_req, res) => {
613
+ // Chunked transfer (no Content-Length) + socket.destroy() mid-body
614
+ // is the cleanest way to force Bun's fetch to surface a
615
+ // post-headers stream error rather than an initial-fetch throw.
616
+ // We write the prefix in a couple of chunks so the client side can
617
+ // return from fetch() and hand the body stream to the importer
618
+ // before we tear the socket down.
619
+ res.writeHead(200, {
620
+ "Content-Type": "application/octet-stream",
621
+ "Transfer-Encoding": "chunked",
622
+ });
623
+ const half = Math.floor(truncatedPrefix.byteLength / 2);
624
+ res.write(truncatedPrefix.slice(0, half), () => {
625
+ // Give the runtime a chance to deliver the first chunk to the
626
+ // consumer and enter the streaming import pipeline, then rip
627
+ // the socket out so the body stream surfaces an abort error.
628
+ setTimeout(() => {
629
+ res.socket?.destroy();
630
+ }, 100);
631
+ });
632
+ });
633
+
634
+ try {
635
+ const req = new Request("http://localhost/v1/migrations/import", {
636
+ method: "POST",
637
+ headers: { "Content-Type": "application/json" },
638
+ body: JSON.stringify({ url: makeFakeSignedUrl(fixture.port) }),
639
+ });
640
+
641
+ const res = await handleMigrationImport(req);
642
+ const body = (await res.json()) as FetchFailedResponse;
643
+
644
+ expect(res.status).toBe(502);
645
+ expect(body.success).toBe(false);
646
+ expect(body.reason).toBe("fetch_failed");
647
+ } finally {
648
+ await fixture.close();
649
+ }
650
+ }, 15_000);
651
+ });
652
+
653
+ // ---------------------------------------------------------------------------
654
+ // Regression: raw-bytes path still works through the same handler.
655
+ // ---------------------------------------------------------------------------
656
+
657
+ describe("handleMigrationImport — raw-bytes regression", () => {
658
+ test("application/octet-stream body still imports successfully", async () => {
659
+ const { archive } = buildVBundle({
660
+ files: [
661
+ {
662
+ path: "workspace/data/db/assistant.db",
663
+ data: new TextEncoder().encode("SQLite format 3\0"),
664
+ },
665
+ ],
666
+ });
667
+
668
+ const req = new Request("http://localhost/v1/migrations/import", {
669
+ method: "POST",
670
+ headers: { "Content-Type": "application/octet-stream" },
671
+ body: archive.buffer.slice(
672
+ archive.byteOffset,
673
+ archive.byteOffset + archive.byteLength,
674
+ ) as ArrayBuffer,
675
+ });
676
+
677
+ const res = await handleMigrationImport(req);
678
+ const body = (await res.json()) as ImportCommitResponse;
679
+
680
+ expect(res.status).toBe(200);
681
+ expect(body.success).toBe(true);
682
+ expect(body.files.length).toBeGreaterThan(0);
683
+ });
684
+ });