@vellumai/assistant 0.7.1 → 0.7.2

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 (535) hide show
  1. package/ARCHITECTURE.md +32 -49
  2. package/Dockerfile +1 -0
  3. package/README.md +1 -2
  4. package/__tests__/permissions/gateway-threshold-reader.test.ts +9 -3
  5. package/bun.lock +26 -26
  6. package/docs/architecture/security.md +20 -0
  7. package/docs/plugins.md +7 -9
  8. package/knip.json +1 -0
  9. package/node_modules/@vellumai/gateway-client/src/index.ts +1 -0
  10. package/node_modules/@vellumai/gateway-client/src/ipc-client.ts +39 -1
  11. package/node_modules/@vellumai/gateway-client/src/types.ts +11 -0
  12. package/node_modules/@vellumai/service-contracts/package.json +2 -0
  13. package/node_modules/@vellumai/service-contracts/src/__tests__/contracts.test.ts +4 -0
  14. package/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +107 -0
  15. package/node_modules/@vellumai/service-contracts/src/index.ts +5 -1
  16. package/node_modules/@vellumai/service-contracts/src/ingress.ts +24 -0
  17. package/node_modules/@vellumai/service-contracts/src/twilio-ingress.ts +84 -0
  18. package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +9 -0
  19. package/node_modules/@vellumai/twilio-client/bun.lock +24 -0
  20. package/node_modules/@vellumai/twilio-client/package.json +18 -0
  21. package/node_modules/@vellumai/twilio-client/src/__tests__/twilio-client.test.ts +128 -0
  22. package/node_modules/@vellumai/twilio-client/src/index.ts +179 -0
  23. package/node_modules/@vellumai/twilio-client/tsconfig.json +20 -0
  24. package/openapi.yaml +565 -12
  25. package/package.json +6 -3
  26. package/src/__tests__/app-builder-tool-scripts.test.ts +3 -3
  27. package/src/__tests__/app-bundler.test.ts +170 -1
  28. package/src/__tests__/app-control-flow.test.ts +374 -0
  29. package/src/__tests__/app-control-no-global-cgevent.test.ts +98 -0
  30. package/src/__tests__/app-control-tool-schemas.test.ts +621 -0
  31. package/src/__tests__/app-executors.test.ts +30 -43
  32. package/src/__tests__/approval-routes-http.test.ts +23 -6
  33. package/src/__tests__/assistant-event-hub-machine-name.test.ts +146 -0
  34. package/src/__tests__/assistant-event-hub-targeted.test.ts +257 -0
  35. package/src/__tests__/assistant-event-hub.test.ts +109 -2
  36. package/src/__tests__/assistant-event.test.ts +10 -0
  37. package/src/__tests__/assistant-events-sse-hardening.test.ts +7 -2
  38. package/src/__tests__/assistant-feature-flags-integration.test.ts +11 -7
  39. package/src/__tests__/background-shell-host-bash.test.ts +14 -15
  40. package/src/__tests__/bootstrap-turn-cleanup.test.ts +44 -0
  41. package/src/__tests__/btw-routes.test.ts +13 -4
  42. package/src/__tests__/call-controller.test.ts +49 -1
  43. package/src/__tests__/call-domain.test.ts +0 -2
  44. package/src/__tests__/call-routes-http.test.ts +0 -2
  45. package/src/__tests__/channel-readiness-service.test.ts +59 -1
  46. package/src/__tests__/checker.test.ts +3 -4
  47. package/src/__tests__/config-loader-backfill.test.ts +90 -155
  48. package/src/__tests__/config-loader-platform-defaults.test.ts +196 -0
  49. package/src/__tests__/config-schema-cmd.test.ts +0 -1
  50. package/src/__tests__/config-set-platform-guard.test.ts +48 -4
  51. package/src/__tests__/config-watcher-cleanup-throttle.test.ts +2 -2
  52. package/src/__tests__/config-watcher.test.ts +2 -2
  53. package/src/__tests__/conversation-app-control-instantiation.test.ts +392 -0
  54. package/src/__tests__/conversation-app-control-lifecycle.test.ts +237 -0
  55. package/src/__tests__/conversation-init.benchmark.test.ts +0 -2
  56. package/src/__tests__/conversation-lifecycle.test.ts +36 -0
  57. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +283 -0
  58. package/src/__tests__/conversation-routes-disk-view.test.ts +6 -0
  59. package/src/__tests__/conversation-routes-guardian-reply.test.ts +120 -72
  60. package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
  61. package/src/__tests__/conversation-slash-commands.test.ts +0 -4
  62. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +202 -0
  63. package/src/__tests__/conversation-surfaces-app-control.test.ts +317 -0
  64. package/src/__tests__/credential-execution-feature-gates.test.ts +5 -12
  65. package/src/__tests__/credential-execution-managed-contract.test.ts +3 -131
  66. package/src/__tests__/credentials-cli.test.ts +5 -12
  67. package/src/__tests__/cu-unified-flow.test.ts +185 -23
  68. package/src/__tests__/daemon-credential-client.test.ts +101 -19
  69. package/src/__tests__/db-schedule-syntax-migration.test.ts +2 -0
  70. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
  71. package/src/__tests__/gateway-only-enforcement.test.ts +0 -1
  72. package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -2
  73. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +0 -2
  74. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +0 -1
  75. package/src/__tests__/heartbeat-service.test.ts +718 -1
  76. package/src/__tests__/helpers/call-route-handler.ts +7 -1
  77. package/src/__tests__/host-app-control-proxy.test.ts +602 -0
  78. package/src/__tests__/host-app-control-routes.test.ts +263 -0
  79. package/src/__tests__/host-bash-proxy.test.ts +246 -47
  80. package/src/__tests__/host-bash-routes.test.ts +294 -0
  81. package/src/__tests__/host-browser-proxy.test.ts +24 -22
  82. package/src/__tests__/host-browser-routes.test.ts +39 -13
  83. package/src/__tests__/host-cu-proxy.test.ts +41 -52
  84. package/src/__tests__/host-cu-routes-targeted.test.ts +300 -0
  85. package/src/__tests__/host-file-edit-tool.test.ts +47 -1
  86. package/src/__tests__/host-file-proxy-targeted.test.ts +339 -0
  87. package/src/__tests__/host-file-proxy.test.ts +37 -43
  88. package/src/__tests__/host-file-read-tool.test.ts +17 -0
  89. package/src/__tests__/host-file-routes-targeted.test.ts +262 -0
  90. package/src/__tests__/host-file-write-tool.test.ts +42 -1
  91. package/src/__tests__/host-proxy-base.test.ts +312 -0
  92. package/src/__tests__/host-shell-tool.test.ts +22 -4
  93. package/src/__tests__/host-transfer-proxy-targeted.test.ts +583 -0
  94. package/src/__tests__/host-transfer-proxy.test.ts +121 -22
  95. package/src/__tests__/host-transfer-routes-targeted.test.ts +447 -0
  96. package/src/__tests__/http-user-message-parity.test.ts +1 -0
  97. package/src/__tests__/identity-intro-cache.test.ts +29 -0
  98. package/src/__tests__/identity-routes.test.ts +103 -1
  99. package/src/__tests__/init-feature-flag-overrides.test.ts +26 -3
  100. package/src/__tests__/inline-command-runner.test.ts +0 -1
  101. package/src/__tests__/inline-skill-load-permissions.test.ts +5 -11
  102. package/src/__tests__/integration-status.test.ts +85 -5
  103. package/src/__tests__/intent-routing.test.ts +0 -1
  104. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +95 -5
  105. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +17 -0
  106. package/src/__tests__/managed-skill-lifecycle.test.ts +0 -1
  107. package/src/__tests__/mcp-auth-routes.test.ts +197 -0
  108. package/src/__tests__/mcp-cli.test.ts +338 -2
  109. package/src/__tests__/memory-jobs-worker-lanes.test.ts +188 -0
  110. package/src/__tests__/migration-import-commit-http.test.ts +108 -2
  111. package/src/__tests__/mock-gateway-ipc.ts +1 -0
  112. package/src/__tests__/oauth-cli.test.ts +0 -2
  113. package/src/__tests__/oauth2-gateway-transport.test.ts +0 -1
  114. package/src/__tests__/persistence-secret-redaction.test.ts +299 -0
  115. package/src/__tests__/platform-bash-auto-approve.test.ts +5 -9
  116. package/src/__tests__/prechat-onboarding-contract.test.ts +3 -1
  117. package/src/__tests__/process-message-background-slack.test.ts +2 -0
  118. package/src/__tests__/provider-commit-message-generator.test.ts +0 -1
  119. package/src/__tests__/public-ingress-urls.test.ts +97 -0
  120. package/src/__tests__/require-fresh-approval.test.ts +0 -1
  121. package/src/__tests__/retry-backoff.test.ts +87 -0
  122. package/src/__tests__/runtime-events-sse.test.ts +10 -6
  123. package/src/__tests__/sanitize-config-for-transfer.test.ts +24 -2
  124. package/src/__tests__/schedule-retry.test.ts +715 -0
  125. package/src/__tests__/script-proxy-mitm-handler.test.ts +1 -1
  126. package/src/__tests__/secret-ingress-http.test.ts +1 -0
  127. package/src/__tests__/send-endpoint-busy.test.ts +3 -0
  128. package/src/__tests__/shell-tool-proxy-mode.test.ts +0 -1
  129. package/src/__tests__/skill-feature-flags.test.ts +43 -41
  130. package/src/__tests__/skill-load-feature-flag.test.ts +13 -14
  131. package/src/__tests__/skill-load-inline-command.test.ts +0 -51
  132. package/src/__tests__/skill-load-inline-includes.test.ts +0 -43
  133. package/src/__tests__/skill-projection.benchmark.test.ts +0 -1
  134. package/src/__tests__/skill-script-runner-sandbox.test.ts +0 -1
  135. package/src/__tests__/slack-channel-config.test.ts +9 -14
  136. package/src/__tests__/system-prompt-ask-mode.test.ts +0 -1
  137. package/src/__tests__/system-prompt.test.ts +0 -1
  138. package/src/__tests__/telegram-config.test.ts +0 -1
  139. package/src/__tests__/test-preload.ts +8 -0
  140. package/src/__tests__/tool-approval-handler.test.ts +3 -4
  141. package/src/__tests__/tool-audit-listener.test.ts +48 -0
  142. package/src/__tests__/tool-execute-pipeline.test.ts +0 -1
  143. package/src/__tests__/tool-execution-abort-cleanup.test.ts +0 -1
  144. package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -1
  145. package/src/__tests__/tool-executor.test.ts +0 -1
  146. package/src/__tests__/twilio-config.test.ts +3 -16
  147. package/src/__tests__/twilio-routes.test.ts +3 -5
  148. package/src/__tests__/twilio-validation.test.ts +93 -0
  149. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +1 -4
  150. package/src/__tests__/verification-control-plane-policy.test.ts +2 -4
  151. package/src/__tests__/voice-ingress-preflight.test.ts +19 -0
  152. package/src/__tests__/workspace-migration-006-services-config.test.ts +3 -2
  153. package/src/__tests__/workspace-migration-backfill-installation-id.test.ts +1 -5
  154. package/src/__tests__/workspace-migration-down-functions.test.ts +8 -8
  155. package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +10 -6
  156. package/src/backup/__tests__/paths.test.ts +0 -22
  157. package/src/backup/__tests__/restore.test.ts +51 -151
  158. package/src/backup/paths.ts +2 -18
  159. package/src/backup/restore.ts +107 -231
  160. package/src/bundler/app-bundler.ts +51 -3
  161. package/src/calls/relay-server.ts +4 -44
  162. package/src/calls/twilio-config.ts +2 -17
  163. package/src/calls/twilio-rest.ts +33 -105
  164. package/src/calls/twilio-routes.ts +11 -12
  165. package/src/channels/types.ts +8 -7
  166. package/src/cli/commands/__tests__/backup.test.ts +6 -277
  167. package/src/cli/commands/__tests__/gateway.test.ts +288 -0
  168. package/src/cli/commands/__tests__/memory-v2.test.ts +4 -0
  169. package/src/cli/commands/__tests__/webhooks.test.ts +0 -1
  170. package/src/cli/commands/backup.ts +6 -331
  171. package/src/cli/commands/clients.ts +36 -37
  172. package/src/cli/commands/contacts.ts +73 -0
  173. package/src/cli/commands/conversations.ts +2 -5
  174. package/src/cli/commands/credentials.ts +15 -7
  175. package/src/cli/commands/domain.ts +66 -15
  176. package/src/cli/commands/gateway.ts +183 -0
  177. package/src/cli/commands/keys.ts +9 -6
  178. package/src/cli/commands/mcp.ts +116 -156
  179. package/src/cli/commands/memory-v2.ts +296 -1
  180. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -1
  181. package/src/cli/commands/platform/__tests__/connect.test.ts +0 -2
  182. package/src/cli/commands/platform/__tests__/disconnect.test.ts +0 -2
  183. package/src/cli/commands/platform/__tests__/status.test.ts +13 -15
  184. package/src/cli/commands/platform/disconnect.ts +5 -4
  185. package/src/cli/commands/platform/index.ts +0 -18
  186. package/src/cli/lib/daemon-credential-client.ts +110 -28
  187. package/src/cli/program.ts +2 -0
  188. package/src/config/assistant-feature-flags.ts +67 -10
  189. package/src/config/bundled-skills/acp/SKILL.md +6 -0
  190. package/src/config/bundled-skills/acp/TOOLS.json +1 -22
  191. package/src/config/bundled-skills/app-builder/SKILL.md +14 -109
  192. package/src/config/bundled-skills/app-builder/TOOLS.json +1 -28
  193. package/src/config/bundled-skills/app-builder/tools/app-create.ts +1 -10
  194. package/src/config/bundled-skills/app-control/SKILL.md +75 -0
  195. package/src/config/bundled-skills/app-control/TOOLS.json +299 -0
  196. package/src/config/bundled-skills/app-control/tools/app-control-click.ts +12 -0
  197. package/src/config/bundled-skills/app-control/tools/app-control-combo.ts +12 -0
  198. package/src/config/bundled-skills/app-control/tools/app-control-drag.ts +12 -0
  199. package/src/config/bundled-skills/app-control/tools/app-control-observe.ts +12 -0
  200. package/src/config/bundled-skills/app-control/tools/app-control-press.ts +12 -0
  201. package/src/config/bundled-skills/app-control/tools/app-control-sequence.ts +12 -0
  202. package/src/config/bundled-skills/app-control/tools/app-control-start.ts +12 -0
  203. package/src/config/bundled-skills/app-control/tools/app-control-stop.ts +12 -0
  204. package/src/config/bundled-skills/app-control/tools/app-control-type.ts +12 -0
  205. package/src/config/bundled-skills/computer-use/SKILL.md +6 -0
  206. package/src/config/bundled-skills/computer-use/TOOLS.json +67 -43
  207. package/src/config/bundled-skills/contacts/TOOLS.json +0 -16
  208. package/src/config/bundled-skills/document/TOOLS.json +0 -8
  209. package/src/config/bundled-skills/followups/TOOLS.json +0 -12
  210. package/src/config/bundled-skills/image-studio/SKILL.md +4 -0
  211. package/src/config/bundled-skills/image-studio/TOOLS.json +0 -4
  212. package/src/config/bundled-skills/media-processing/TOOLS.json +0 -24
  213. package/src/config/bundled-skills/messaging/TOOLS.json +0 -40
  214. package/src/config/bundled-skills/phone-calls/TOOLS.json +0 -12
  215. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +19 -4
  216. package/src/config/bundled-skills/playbooks/TOOLS.json +0 -16
  217. package/src/config/bundled-skills/schedule/TOOLS.json +14 -14
  218. package/src/config/bundled-skills/sequences/TOOLS.json +0 -36
  219. package/src/config/bundled-skills/settings/SKILL.md +4 -0
  220. package/src/config/bundled-skills/settings/TOOLS.json +0 -12
  221. package/src/config/bundled-skills/skill-management/SKILL.md +6 -0
  222. package/src/config/bundled-skills/skill-management/TOOLS.json +0 -8
  223. package/src/config/bundled-skills/subagent/SKILL.md +6 -2
  224. package/src/config/bundled-skills/subagent/TOOLS.json +0 -20
  225. package/src/config/bundled-skills/transcribe/SKILL.md +4 -0
  226. package/src/config/bundled-skills/transcribe/TOOLS.json +0 -4
  227. package/src/config/bundled-tool-registry.ts +21 -0
  228. package/src/config/env-registry.ts +0 -2
  229. package/src/config/env.ts +19 -12
  230. package/src/config/feature-flag-registry.json +21 -133
  231. package/src/config/loader.ts +73 -99
  232. package/src/config/sanitize-for-transfer.ts +2 -0
  233. package/src/config/schemas/__tests__/memory-lifecycle.test.ts +80 -0
  234. package/src/config/schemas/__tests__/memory-v2.test.ts +7 -4
  235. package/src/config/schemas/calls.ts +0 -9
  236. package/src/config/schemas/heartbeat.ts +63 -0
  237. package/src/config/schemas/ingress.ts +10 -6
  238. package/src/config/schemas/llm.ts +5 -10
  239. package/src/config/schemas/memory-lifecycle.ts +77 -24
  240. package/src/config/schemas/memory-v2.ts +48 -4
  241. package/src/config/schemas/platform.ts +6 -0
  242. package/src/config/schemas/services.ts +1 -15
  243. package/src/config/schemas/skills.ts +0 -6
  244. package/src/config/seed-inference-profiles.ts +1 -1
  245. package/src/contacts/contact-store.ts +0 -30
  246. package/src/contacts/contacts-write.ts +0 -27
  247. package/src/context/window-manager.ts +1 -2
  248. package/src/credential-execution/feature-gates.ts +10 -10
  249. package/src/credential-execution/process-manager.ts +12 -41
  250. package/src/daemon/__tests__/conversation-tool-setup.test.ts +126 -5
  251. package/src/daemon/bootstrap-turn-cleanup.ts +45 -0
  252. package/src/daemon/config-watcher.ts +4 -3
  253. package/src/daemon/conversation-agent-loop-handlers.ts +21 -3
  254. package/src/daemon/conversation-agent-loop.ts +32 -28
  255. package/src/daemon/conversation-lifecycle.ts +8 -1
  256. package/src/daemon/conversation-process.ts +16 -11
  257. package/src/daemon/conversation-runtime-assembly.ts +2 -2
  258. package/src/daemon/conversation-surfaces.ts +125 -4
  259. package/src/daemon/conversation-tool-setup.ts +16 -55
  260. package/src/daemon/conversation.ts +21 -2
  261. package/src/daemon/doordash-steps.ts +1 -1
  262. package/src/daemon/handlers/shared.ts +4 -1
  263. package/src/daemon/host-app-control-proxy.ts +293 -0
  264. package/src/daemon/host-bash-proxy.ts +84 -74
  265. package/src/daemon/host-browser-proxy.ts +67 -82
  266. package/src/daemon/host-cu-proxy.ts +81 -86
  267. package/src/daemon/host-file-proxy.ts +93 -69
  268. package/src/daemon/host-proxy-base.ts +294 -0
  269. package/src/daemon/host-proxy-preactivation.ts +82 -0
  270. package/src/daemon/host-transfer-proxy.ts +247 -129
  271. package/src/daemon/lifecycle.ts +115 -117
  272. package/src/daemon/message-protocol.ts +3 -8
  273. package/src/daemon/message-types/contacts.ts +23 -1
  274. package/src/daemon/message-types/conversations.ts +11 -8
  275. package/src/daemon/message-types/host-app-control.ts +150 -0
  276. package/src/daemon/message-types/host-bash.ts +4 -0
  277. package/src/daemon/message-types/host-cu.ts +2 -0
  278. package/src/daemon/message-types/host-file.ts +4 -0
  279. package/src/daemon/message-types/host-transfer.ts +3 -0
  280. package/src/daemon/message-types/schedules.ts +8 -3
  281. package/src/daemon/message-types/skills.ts +2 -2
  282. package/src/daemon/process-message.ts +18 -1
  283. package/src/daemon/shutdown-handlers.ts +0 -3
  284. package/src/daemon/tool-setup-types.ts +51 -0
  285. package/src/daemon/tool-side-effects.ts +1 -1
  286. package/src/events/tool-audit-listener.ts +2 -1
  287. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +15 -7
  288. package/src/heartbeat/__tests__/heartbeat-run-store.test.ts +216 -0
  289. package/src/heartbeat/heartbeat-run-store.ts +236 -0
  290. package/src/heartbeat/heartbeat-service.ts +280 -49
  291. package/src/home/__tests__/post-connect-feed.test.ts +99 -0
  292. package/src/home/__tests__/relationship-state-writer.test.ts +11 -9
  293. package/src/home/__tests__/suggested-prompts.test.ts +89 -0
  294. package/src/home/post-connect-feed.ts +68 -0
  295. package/src/home/relationship-state-writer.ts +17 -92
  296. package/src/home/suggested-prompts.ts +46 -10
  297. package/src/inbound/public-ingress-urls.ts +32 -34
  298. package/src/ipc/__tests__/route-error-envelope.test.ts +80 -0
  299. package/src/ipc/assistant-server.ts +14 -1
  300. package/src/ipc/cli-client.ts +32 -1
  301. package/src/live-voice/live-voice-metrics.ts +10 -10
  302. package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +304 -0
  303. package/src/mcp/mcp-auth-orchestrator.ts +213 -0
  304. package/src/mcp/mcp-auth-state.ts +133 -0
  305. package/src/mcp/mcp-oauth-provider.ts +19 -0
  306. package/src/memory/__tests__/jobs-store-job-classes.test.ts +24 -0
  307. package/src/memory/__tests__/qdrant-client-sentinel.test.ts +49 -0
  308. package/src/memory/__tests__/sparse-tokenize.test.ts +66 -0
  309. package/src/memory/anisotropy.test.ts +247 -0
  310. package/src/memory/anisotropy.ts +443 -0
  311. package/src/memory/auto-analysis-constants.ts +17 -0
  312. package/src/memory/auto-analysis-guard.ts +5 -15
  313. package/src/memory/canonical-guardian-store.ts +7 -7
  314. package/src/memory/context-search/__tests__/agent-runner-redaction.test.ts +122 -0
  315. package/src/memory/context-search/agent-protocol.ts +6 -6
  316. package/src/memory/context-search/agent-runner.ts +32 -7
  317. package/src/memory/context-search/sources/memory-v2.ts +17 -5
  318. package/src/memory/conversation-crud.ts +1 -1
  319. package/src/memory/conversation-key-store.ts +2 -15
  320. package/src/memory/db-init.ts +4 -0
  321. package/src/memory/embedding-backend.ts +9 -21
  322. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +49 -4
  323. package/src/memory/graph/conversation-graph-memory.ts +1 -24
  324. package/src/memory/graph/graph-search.ts +8 -0
  325. package/src/memory/graph/retriever.ts +28 -0
  326. package/src/memory/graph/tools.ts +1 -1
  327. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +8 -2
  328. package/src/memory/jobs/embed-concept-page.ts +28 -2
  329. package/src/memory/jobs/embed-pkb-file.test.ts +2 -2
  330. package/src/memory/jobs-store.ts +66 -22
  331. package/src/memory/jobs-worker.ts +112 -63
  332. package/src/memory/memory-v2-activation-log-store.ts +1 -1
  333. package/src/memory/migrations/237-heartbeat-runs.ts +45 -0
  334. package/src/memory/migrations/238-schedule-retry-policy.ts +20 -0
  335. package/src/memory/migrations/index.ts +5 -0
  336. package/src/memory/migrations/registry.ts +8 -0
  337. package/src/memory/pkb/pkb-search.ts +7 -0
  338. package/src/memory/qdrant-client.ts +50 -20
  339. package/src/memory/schema/infrastructure.ts +15 -0
  340. package/src/memory/search/semantic.ts +7 -0
  341. package/src/memory/sparse-tokenize.ts +49 -0
  342. package/src/memory/v2/__tests__/activation.test.ts +77 -95
  343. package/src/memory/v2/__tests__/injection.test.ts +43 -21
  344. package/src/memory/v2/__tests__/sim.test.ts +166 -6
  345. package/src/memory/v2/__tests__/sparse-bm25.test.ts +292 -0
  346. package/src/memory/v2/__tests__/static-context.test.ts +0 -1
  347. package/src/memory/v2/activation.ts +69 -88
  348. package/src/memory/v2/consolidation-job.ts +3 -5
  349. package/src/memory/v2/constants.ts +7 -0
  350. package/src/memory/v2/injection.ts +86 -53
  351. package/src/memory/v2/prompts/consolidation.ts +312 -91
  352. package/src/memory/v2/qdrant.ts +99 -1
  353. package/src/memory/v2/sim.ts +126 -16
  354. package/src/memory/v2/skill-qdrant.ts +12 -3
  355. package/src/memory/v2/skill-store.ts +16 -1
  356. package/src/memory/v2/sparse-bm25.ts +245 -0
  357. package/src/memory/v2/static-context.ts +6 -5
  358. package/src/messaging/providers/gmail/types.ts +0 -49
  359. package/src/messaging/providers/slack/adapter.ts +1 -31
  360. package/src/messaging/providers/slack/types.ts +0 -32
  361. package/src/notifications/README.md +10 -10
  362. package/src/notifications/broadcaster.ts +1 -1
  363. package/src/notifications/guardian-question-mode.ts +5 -5
  364. package/src/oauth/connect-orchestrator.ts +4 -0
  365. package/src/oauth/credential-token-resolver.ts +1 -3
  366. package/src/oauth/manual-token-connection.ts +0 -4
  367. package/src/outbound-proxy/index.ts +1 -37
  368. package/src/outbound-proxy/logging.ts +1 -1
  369. package/src/outbound-proxy/policy.ts +6 -5
  370. package/src/outbound-proxy/router.ts +2 -1
  371. package/src/permissions/approval-policy.test.ts +6 -275
  372. package/src/permissions/approval-policy.ts +0 -51
  373. package/src/permissions/checker.test.ts +0 -1
  374. package/src/permissions/checker.ts +3 -17
  375. package/src/permissions/gateway-threshold-reader.ts +2 -0
  376. package/src/permissions/prompter.ts +34 -1
  377. package/src/permissions/secret-prompter.ts +6 -2
  378. package/src/prompts/bootstrap-cleanup.ts +27 -0
  379. package/src/prompts/system-prompt.ts +3 -18
  380. package/src/prompts/templates/SOUL.md +13 -1
  381. package/src/providers/speech-to-text/provider-catalog.ts +7 -8
  382. package/src/runtime/assistant-event-hub.ts +118 -96
  383. package/src/runtime/assistant-event.ts +1 -0
  384. package/src/runtime/auth/__tests__/middleware.test.ts +11 -56
  385. package/src/runtime/auth/middleware.ts +0 -96
  386. package/src/runtime/auth/route-policy.ts +19 -0
  387. package/src/runtime/btw-sidechain.ts +2 -3
  388. package/src/runtime/channel-invite-transport.ts +2 -48
  389. package/src/runtime/channel-invite-transports/email.ts +1 -1
  390. package/src/runtime/channel-invite-transports/slack.ts +1 -1
  391. package/src/runtime/channel-invite-transports/telegram.ts +1 -1
  392. package/src/runtime/channel-invite-transports/voice.ts +1 -1
  393. package/src/runtime/channel-invite-transports/whatsapp.ts +1 -1
  394. package/src/runtime/channel-invite-types.ts +54 -0
  395. package/src/runtime/channel-readiness-service.ts +32 -13
  396. package/src/runtime/http-server.ts +3 -329
  397. package/src/runtime/http-types.ts +0 -5
  398. package/src/runtime/migrations/__tests__/vbundle-import-parity.test.ts +413 -0
  399. package/src/runtime/migrations/__tests__/vbundle-import-policy.test.ts +260 -0
  400. package/src/runtime/migrations/__tests__/vbundle-import-version-compat.test.ts +189 -0
  401. package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +153 -1
  402. package/src/runtime/migrations/__tests__/vbundle-symlink-importer.test.ts +451 -0
  403. package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -0
  404. package/src/runtime/migrations/__tests__/vbundle-symlink-streaming.test.ts +515 -0
  405. package/src/runtime/migrations/__tests__/vbundle-symlink-tar.test.ts +437 -0
  406. package/src/runtime/migrations/__tests__/vbundle-symlink-walker.test.ts +319 -0
  407. package/src/runtime/migrations/__tests__/vbundle-validator-v1-schema.test.ts +51 -1
  408. package/src/runtime/migrations/migration-transport.ts +7 -7
  409. package/src/runtime/migrations/vbundle-builder.ts +327 -60
  410. package/src/runtime/migrations/vbundle-import-analyzer.ts +4 -4
  411. package/src/runtime/migrations/vbundle-import-policy.ts +172 -0
  412. package/src/runtime/migrations/vbundle-importer.ts +245 -68
  413. package/src/runtime/migrations/vbundle-streaming-importer.ts +326 -35
  414. package/src/runtime/migrations/vbundle-streaming-validator.ts +157 -4
  415. package/src/runtime/migrations/vbundle-tar-stream.ts +15 -6
  416. package/src/runtime/migrations/vbundle-validator.ts +114 -0
  417. package/src/runtime/pending-interactions.ts +35 -9
  418. package/src/runtime/routes/__tests__/backup-routes.test.ts +22 -150
  419. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +98 -0
  420. package/src/runtime/routes/__tests__/gateway-log-routes.test.ts +242 -0
  421. package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +112 -0
  422. package/src/runtime/routes/approval-interception-types.ts +13 -0
  423. package/src/runtime/routes/approval-strategies/guardian-text-engine-strategy.ts +1 -1
  424. package/src/runtime/routes/backup-routes.ts +15 -38
  425. package/src/runtime/routes/btw-routes.ts +14 -37
  426. package/src/runtime/routes/client-routes.ts +1 -0
  427. package/src/runtime/routes/contact-prompt-routes.ts +183 -0
  428. package/src/runtime/routes/conversation-query-routes.ts +36 -1
  429. package/src/runtime/routes/conversation-routes.ts +30 -13
  430. package/src/runtime/routes/document-pdf-renderer.ts +165 -0
  431. package/src/runtime/routes/documents-routes.ts +30 -0
  432. package/src/runtime/routes/errors.ts +19 -4
  433. package/src/runtime/routes/events-routes.ts +12 -6
  434. package/src/runtime/routes/gateway-log-routes.ts +79 -0
  435. package/src/runtime/routes/guardian-approval-interception.ts +2 -8
  436. package/src/runtime/routes/heartbeat-routes.ts +103 -38
  437. package/src/runtime/routes/host-app-control-routes.ts +134 -0
  438. package/src/runtime/routes/host-bash-routes.ts +36 -6
  439. package/src/runtime/routes/host-browser-routes.ts +108 -13
  440. package/src/runtime/routes/host-cu-routes.ts +44 -14
  441. package/src/runtime/routes/host-file-routes.ts +33 -10
  442. package/src/runtime/routes/host-transfer-routes.ts +64 -24
  443. package/src/runtime/routes/http-adapter.ts +1 -0
  444. package/src/runtime/routes/identity-intro-cache.ts +30 -0
  445. package/src/runtime/routes/identity-routes.ts +15 -43
  446. package/src/runtime/routes/inbound-message-handler.ts +1 -9
  447. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +0 -7
  448. package/src/runtime/routes/inbound-stages/edit-intercept.ts +0 -8
  449. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +0 -20
  450. package/src/runtime/routes/inbound-stages/transcribe-audio.ts +5 -13
  451. package/src/runtime/routes/index.ts +8 -0
  452. package/src/runtime/routes/mcp-auth-routes.ts +132 -0
  453. package/src/runtime/routes/memory-item-routes.ts +10 -12
  454. package/src/runtime/routes/memory-v2-routes.ts +441 -1
  455. package/src/runtime/routes/migration-routes.ts +96 -0
  456. package/src/runtime/routes/schedule-routes.ts +7 -0
  457. package/src/runtime/verification-templates.ts +4 -7
  458. package/src/schedule/integration-status.ts +66 -2
  459. package/src/schedule/recurrence-engine.ts +4 -1
  460. package/src/schedule/retry-backoff.ts +18 -0
  461. package/src/schedule/retry-policy.ts +82 -0
  462. package/src/schedule/schedule-recovery.ts +64 -0
  463. package/src/schedule/schedule-store.ts +106 -2
  464. package/src/schedule/scheduler-types.ts +25 -0
  465. package/src/schedule/scheduler.ts +63 -38
  466. package/src/security/oauth-callback-registry.ts +8 -0
  467. package/src/sequence/analytics.ts +5 -5
  468. package/src/sequence/engine.ts +1 -1
  469. package/src/skills/catalog-files.ts +2 -8
  470. package/src/skills/include-graph.ts +5 -5
  471. package/src/skills/remote-skill-policy.ts +5 -5
  472. package/src/skills/skill-file-provider.ts +1 -1
  473. package/src/skills/skill-file-types.ts +13 -0
  474. package/src/skills/skillssh-audit-types.ts +28 -0
  475. package/src/skills/skillssh-registry.ts +8 -21
  476. package/src/telemetry/types.ts +2 -0
  477. package/src/telemetry/usage-telemetry-reporter.test.ts +21 -0
  478. package/src/telemetry/usage-telemetry-reporter.ts +1 -0
  479. package/src/tools/app-control/skill-proxy-bridge.ts +28 -0
  480. package/src/tools/apps/executors.ts +56 -69
  481. package/src/tools/browser/__tests__/browser-status.test.ts +21 -18
  482. package/src/tools/browser/browser-execution.ts +2 -2
  483. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +55 -4
  484. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +12 -6
  485. package/src/tools/browser/cdp-client/factory.ts +23 -24
  486. package/src/tools/browser/cdp-client/index.ts +1 -14
  487. package/src/tools/computer-use/definitions.ts +42 -20
  488. package/src/tools/executor.ts +2 -0
  489. package/src/tools/host-filesystem/edit.ts +26 -0
  490. package/src/tools/host-filesystem/read.ts +26 -0
  491. package/src/tools/host-filesystem/transfer.ts +31 -1
  492. package/src/tools/host-filesystem/write.ts +26 -0
  493. package/src/tools/host-terminal/host-shell.ts +58 -0
  494. package/src/tools/schedule/create.ts +6 -0
  495. package/src/tools/schedule/list.ts +2 -0
  496. package/src/tools/schedule/update.ts +10 -0
  497. package/src/tools/shared/filesystem/file-ops-service.ts +2 -0
  498. package/src/tools/shared/filesystem/path-policy.ts +25 -1
  499. package/src/tools/skills/load.ts +0 -32
  500. package/src/tools/tool-approval-handler.ts +1 -5
  501. package/src/tools/types.ts +4 -0
  502. package/src/usage/pricing.ts +1 -1
  503. package/src/workspace/hatched-date.ts +86 -0
  504. package/src/workspace/migrations/003-seed-device-id.ts +1 -1
  505. package/src/workspace/migrations/006-services-config.ts +8 -5
  506. package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +3 -9
  507. package/src/workspace/migrations/021-move-signals-to-workspace.ts +4 -10
  508. package/src/workspace/migrations/022-move-hooks-to-workspace.ts +4 -10
  509. package/src/workspace/migrations/023-move-config-files-to-workspace.ts +4 -11
  510. package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +3 -10
  511. package/src/workspace/migrations/040-seed-latency-callsite-defaults.ts +3 -2
  512. package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +2 -1
  513. package/src/workspace/migrations/059-move-pid-to-workspace.ts +3 -8
  514. package/src/workspace/migrations/061-move-backup-key-to-workspace.ts +3 -8
  515. package/src/workspace/migrations/AGENTS.md +1 -1
  516. package/src/workspace/migrations/migrate-to-workspace-volume.ts +4 -10
  517. package/src/workspace/migrations/utils.ts +21 -0
  518. package/src/__tests__/host-browser-e2e-cloud.test.ts +0 -443
  519. package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +0 -226
  520. package/src/__tests__/host-browser-ws-events-e2e.test.ts +0 -427
  521. package/src/__tests__/twilio-rest.test.ts +0 -34
  522. package/src/backup/__tests__/backup-key.test.ts +0 -152
  523. package/src/backup/__tests__/backup-worker.test.ts +0 -782
  524. package/src/backup/__tests__/offsite-writer.test.ts +0 -641
  525. package/src/backup/__tests__/stream-crypt.test.ts +0 -228
  526. package/src/backup/backup-key.ts +0 -137
  527. package/src/backup/backup-worker.ts +0 -472
  528. package/src/backup/offsite-writer.ts +0 -222
  529. package/src/backup/stream-crypt.ts +0 -263
  530. package/src/daemon/message-types/pairing.ts +0 -58
  531. package/src/outbound-proxy/config.ts +0 -20
  532. package/src/outbound-proxy/health.ts +0 -18
  533. package/src/outbound-proxy/types.ts +0 -150
  534. package/src/runtime/capability-tokens.ts +0 -190
  535. package/src/signals/mcp-reload.ts +0 -18
@@ -292,11 +292,7 @@ export class ToolApprovalHandler {
292
292
  executionTarget,
293
293
  );
294
294
 
295
- if (
296
- isUntrustedTrustClass(context.trustClass) &&
297
- guardianApprovalRequired &&
298
- context.trustClass !== "trusted_contact"
299
- ) {
295
+ if (isUntrustedTrustClass(context.trustClass) && guardianApprovalRequired) {
300
296
  const inputDigest = computeToolApprovalDigest(name, input);
301
297
  needsGrantConsumption = true;
302
298
  deferredConsumeParams = {
@@ -138,6 +138,10 @@ export interface ToolExecutedEvent {
138
138
  riskLevel: string;
139
139
  /** ID of the trust rule that matched this invocation (if any). */
140
140
  matchedTrustRuleId?: string;
141
+ /** How the approval decision was reached. Copied from PermissionDecision for analytics consumers. */
142
+ approvalMode?: string;
143
+ /** Why the approval decision was reached (stable enum). Copied from PermissionDecision for analytics consumers. */
144
+ approvalReason?: string;
141
145
  decision: string;
142
146
  durationMs: number;
143
147
  result: ToolExecutionResult;
@@ -13,7 +13,7 @@ import type {
13
13
 
14
14
  const log = getLogger("usage-pricing");
15
15
 
16
- export function normalizeTokenCount(value: number | null | undefined): number {
16
+ function normalizeTokenCount(value: number | null | undefined): number {
17
17
  if (typeof value !== "number" || !Number.isFinite(value)) return 0;
18
18
  return Math.max(value, 0);
19
19
  }
@@ -0,0 +1,86 @@
1
+ import { mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+
4
+ import { getDataDir } from "../util/platform.js";
5
+
6
+ const HATCHED_SIDECAR_FILENAME = "hatched.json";
7
+
8
+ export function getHatchedSidecarPath(): string {
9
+ return join(getDataDir(), HATCHED_SIDECAR_FILENAME);
10
+ }
11
+
12
+ function normalizeHatchedAt(value: unknown): string | undefined {
13
+ if (typeof value !== "string") return undefined;
14
+
15
+ const parsedTime = Date.parse(value);
16
+ if (isNaN(parsedTime) || parsedTime <= 0) return undefined;
17
+
18
+ return new Date(parsedTime).toISOString();
19
+ }
20
+
21
+ export function readHatchedAtSidecar(): string | undefined {
22
+ try {
23
+ const parsed = JSON.parse(
24
+ readFileSync(getHatchedSidecarPath(), "utf-8"),
25
+ ) as { hatchedAt?: unknown };
26
+ return normalizeHatchedAt(parsed.hatchedAt);
27
+ } catch {
28
+ return undefined;
29
+ }
30
+ }
31
+
32
+ export function writeHatchedAtSidecar(hatchedAt: string): void {
33
+ const normalized = normalizeHatchedAt(hatchedAt);
34
+ if (!normalized) return;
35
+
36
+ try {
37
+ mkdirSync(getDataDir(), { recursive: true });
38
+ writeFileSync(
39
+ getHatchedSidecarPath(),
40
+ JSON.stringify({ hatchedAt: normalized }, null, 2),
41
+ "utf-8",
42
+ );
43
+ } catch {
44
+ // Best-effort stability; callers still return a valid timestamp.
45
+ }
46
+ }
47
+
48
+ export function selectHatchedAtFromStats(stats: {
49
+ birthtime: Date;
50
+ mtime: Date;
51
+ }): Date | undefined {
52
+ const candidates = [stats.birthtime, stats.mtime];
53
+ return candidates.find((candidate) => candidate.getTime() > 0);
54
+ }
55
+
56
+ function readIdentityFileHatchedAt(identityPath: string): string | undefined {
57
+ try {
58
+ return selectHatchedAtFromStats(statSync(identityPath))?.toISOString();
59
+ } catch {
60
+ return undefined;
61
+ }
62
+ }
63
+
64
+ export function resolveHatchedAtReadOnly(
65
+ identityPath: string,
66
+ now: Date = new Date(),
67
+ ): string {
68
+ return (
69
+ readHatchedAtSidecar() ??
70
+ readIdentityFileHatchedAt(identityPath) ??
71
+ now.toISOString()
72
+ );
73
+ }
74
+
75
+ export function resolveAndPersistHatchedAt(
76
+ identityPath: string,
77
+ now: Date = new Date(),
78
+ ): string {
79
+ const sidecarHatchedAt = readHatchedAtSidecar();
80
+ if (sidecarHatchedAt) return sidecarHatchedAt;
81
+
82
+ const hatchedAt =
83
+ readIdentityFileHatchedAt(identityPath) ?? now.toISOString();
84
+ writeHatchedAtSidecar(hatchedAt);
85
+ return hatchedAt;
86
+ }
@@ -45,7 +45,7 @@ export const seedDeviceIdMigration: WorkspaceMigration = {
45
45
 
46
46
  // b. Read the lockfile to find an existing installationId.
47
47
  // The lockfile is always under the user's home directory, never under
48
- // BASE_DATA_DIR. Check both the current and legacy filenames.
48
+ // Check both the current and legacy filenames.
49
49
  const home = homedir();
50
50
  const lockCandidates = [
51
51
  join(home, ".vellum.lock.json"),
@@ -37,8 +37,9 @@ export const servicesConfigMigration: WorkspaceMigration = {
37
37
 
38
38
  // Skip if no legacy fields remain — either already migrated or a fresh install
39
39
  // where schema defaults are correct. We check for legacy fields instead of
40
- // services existence because backfillConfigDefaults may have written a default
41
- // services object before migrations run.
40
+ // services existence because legacy daemons (before defaults were applied
41
+ // only in-memory) may have written a default services object to disk
42
+ // before migrations run.
42
43
  const hasLegacyFields =
43
44
  "provider" in config ||
44
45
  "model" in config ||
@@ -46,7 +47,8 @@ export const servicesConfigMigration: WorkspaceMigration = {
46
47
  "webSearchProvider" in config;
47
48
  if (!hasLegacyFields) return;
48
49
 
49
- // Start from existing services (may have been backfilled by loadConfig defaults)
50
+ // Start from existing services (legacy daemons may have written a
51
+ // schema-default services object to disk before this migration runs)
50
52
  // so we don't discard any non-default values already written there.
51
53
  const existingServices =
52
54
  config.services != null &&
@@ -96,8 +98,9 @@ export const servicesConfigMigration: WorkspaceMigration = {
96
98
  // Legacy top-level fields (provider, model) are the user's actual
97
99
  // configuration from before the services structure existed. If they're
98
100
  // present as strings they take precedence over any `existingServices`
99
- // values, which are just defaults written by backfillConfigDefaults().
100
- // The spread preserves any extra keys that future backfills may add.
101
+ // values, which on legacy daemons may just be schema defaults that an
102
+ // older loader wrote to disk. The spread preserves any extra keys that
103
+ // legacy daemons may have written alongside.
101
104
  services.inference = {
102
105
  ...(existingServices.inference ?? {}),
103
106
  mode: inferenceMode,
@@ -7,16 +7,10 @@ import {
7
7
  unlinkSync,
8
8
  writeFileSync,
9
9
  } from "node:fs";
10
- import { homedir } from "node:os";
11
10
  import { join } from "node:path";
12
11
 
13
12
  import type { WorkspaceMigration } from "./types.js";
14
-
15
- /** Inlined from platform.ts to satisfy migration self-containment rule (AGENTS.md). */
16
- function getRootDir(): string {
17
- const base = process.env.BASE_DATA_DIR?.trim() || homedir();
18
- return join(base, ".vellum");
19
- }
13
+ import { getVellumRoot } from "./utils.js";
20
14
 
21
15
  export const extractFeatureFlagsToProtectedMigration: WorkspaceMigration = {
22
16
  id: "016-extract-feature-flags-to-protected",
@@ -26,7 +20,7 @@ export const extractFeatureFlagsToProtectedMigration: WorkspaceMigration = {
26
20
  down(workspaceDir: string): void {
27
21
  // Reverse: read feature flags from protected directory and write them
28
22
  // back to config.json as assistantFeatureFlagValues.
29
- const protectedDir = join(getRootDir(), "protected");
23
+ const protectedDir = join(getVellumRoot(), "protected");
30
24
  const featureFlagsPath = join(protectedDir, "feature-flags.json");
31
25
 
32
26
  if (!existsSync(featureFlagsPath)) return;
@@ -111,7 +105,7 @@ export const extractFeatureFlagsToProtectedMigration: WorkspaceMigration = {
111
105
  }
112
106
 
113
107
  // Write feature flags to protected directory
114
- const protectedDir = join(getRootDir(), "protected");
108
+ const protectedDir = join(getVellumRoot(), "protected");
115
109
  mkdirSync(protectedDir, { recursive: true });
116
110
 
117
111
  const featureFlagsPath = join(protectedDir, "feature-flags.json");
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Workspace migration 021: Move signals directory from root to workspace.
3
3
  *
4
- * Previously, `~/.vellum/signals/` lived directly under getRootDir(). This
4
+ * Previously, `~/.vellum/signals/` lived directly under the Vellum root. This
5
5
  * migration moves any existing signal files into `~/.vellum/workspace/signals/`
6
6
  * so that getSignalsDir() resolves correctly under the workspace.
7
7
  *
@@ -13,23 +13,17 @@
13
13
  */
14
14
 
15
15
  import { existsSync, mkdirSync, readdirSync, renameSync } from "node:fs";
16
- import { homedir } from "node:os";
17
16
  import { join } from "node:path";
18
17
 
19
18
  import type { WorkspaceMigration } from "./types.js";
20
-
21
- /** Inlined from platform.ts to satisfy migration self-containment rule (AGENTS.md). */
22
- function getRootDir(): string {
23
- const base = process.env.BASE_DATA_DIR?.trim() || homedir();
24
- return join(base, ".vellum");
25
- }
19
+ import { getVellumRoot } from "./utils.js";
26
20
 
27
21
  export const moveSignalsToWorkspaceMigration: WorkspaceMigration = {
28
22
  id: "021-move-signals-to-workspace",
29
23
  description: "Move signals directory from root to workspace",
30
24
 
31
25
  run(workspaceDir: string): void {
32
- const oldSignalsDir = join(getRootDir(), "signals");
26
+ const oldSignalsDir = join(getVellumRoot(), "signals");
33
27
  const newSignalsDir = join(workspaceDir, "signals");
34
28
 
35
29
  mkdirSync(newSignalsDir, { recursive: true });
@@ -56,7 +50,7 @@ export const moveSignalsToWorkspaceMigration: WorkspaceMigration = {
56
50
  },
57
51
 
58
52
  down(workspaceDir: string): void {
59
- const oldSignalsDir = join(getRootDir(), "signals");
53
+ const oldSignalsDir = join(getVellumRoot(), "signals");
60
54
  const newSignalsDir = join(workspaceDir, "signals");
61
55
 
62
56
  mkdirSync(oldSignalsDir, { recursive: true });
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Workspace migration 022: Move hooks directory from root to workspace.
3
3
  *
4
- * Previously, `~/.vellum/hooks/` lived directly under getRootDir(). This
4
+ * Previously, `~/.vellum/hooks/` lived directly under the Vellum root. This
5
5
  * migration moves existing hook directories and files into
6
6
  * `~/.vellum/workspace/hooks/` so that getWorkspaceHooksDir() resolves
7
7
  * correctly under the workspace.
@@ -19,23 +19,17 @@ import {
19
19
  renameSync,
20
20
  rmSync,
21
21
  } from "node:fs";
22
- import { homedir } from "node:os";
23
22
  import { join } from "node:path";
24
23
 
25
24
  import type { WorkspaceMigration } from "./types.js";
26
-
27
- /** Inlined from platform.ts to satisfy migration self-containment rule (AGENTS.md). */
28
- function getRootDir(): string {
29
- const base = process.env.BASE_DATA_DIR?.trim() || homedir();
30
- return join(base, ".vellum");
31
- }
25
+ import { getVellumRoot } from "./utils.js";
32
26
 
33
27
  export const moveHooksToWorkspaceMigration: WorkspaceMigration = {
34
28
  id: "022-move-hooks-to-workspace",
35
29
  description: "Move hooks directory from root to workspace",
36
30
 
37
31
  run(workspaceDir: string): void {
38
- const oldHooksDir = join(getRootDir(), "hooks");
32
+ const oldHooksDir = join(getVellumRoot(), "hooks");
39
33
  const newHooksDir = join(workspaceDir, "hooks");
40
34
 
41
35
  mkdirSync(newHooksDir, { recursive: true });
@@ -65,7 +59,7 @@ export const moveHooksToWorkspaceMigration: WorkspaceMigration = {
65
59
  },
66
60
 
67
61
  down(workspaceDir: string): void {
68
- const oldHooksDir = join(getRootDir(), "hooks");
62
+ const oldHooksDir = join(getVellumRoot(), "hooks");
69
63
  const newHooksDir = join(workspaceDir, "hooks");
70
64
 
71
65
  mkdirSync(oldHooksDir, { recursive: true });
@@ -2,23 +2,16 @@
2
2
  * Workspace migration 023: Move config/state JSON files from root to workspace.
3
3
  *
4
4
  * Previously, dictation-profiles.json, email-guardrails.json, and
5
- * active-call-leases.json lived directly under getRootDir() (~/.vellum/).
5
+ * active-call-leases.json lived directly under the Vellum root (~/.vellum/).
6
6
  * This migration moves them into the workspace directory so they follow
7
7
  * the workspace convention for organizational consistency.
8
8
  */
9
9
 
10
10
  import { existsSync, renameSync, unlinkSync } from "node:fs";
11
- import { homedir } from "node:os";
12
11
  import { join } from "node:path";
13
12
 
14
13
  import type { WorkspaceMigration } from "./types.js";
15
-
16
- /** Inlined from platform.ts to satisfy migration self-containment rule (AGENTS.md). */
17
- function getRootDir(): string {
18
- const base = process.env.BASE_DATA_DIR?.trim() || homedir();
19
- return join(base, ".vellum");
20
- }
21
-
14
+ import { getVellumRoot } from "./utils.js";
22
15
  /** Files to move from root → workspace. */
23
16
  const CONFIG_FILES = [
24
17
  "dictation-profiles.json",
@@ -32,7 +25,7 @@ export const moveConfigFilesToWorkspaceMigration: WorkspaceMigration = {
32
25
  "Move dictation-profiles, email-guardrails, and active-call-leases from root to workspace",
33
26
 
34
27
  run(workspaceDir: string): void {
35
- const rootDir = getRootDir();
28
+ const rootDir = getVellumRoot();
36
29
 
37
30
  for (const file of CONFIG_FILES) {
38
31
  const oldPath = join(rootDir, file);
@@ -60,7 +53,7 @@ export const moveConfigFilesToWorkspaceMigration: WorkspaceMigration = {
60
53
  },
61
54
 
62
55
  down(workspaceDir: string): void {
63
- const rootDir = getRootDir();
56
+ const rootDir = getVellumRoot();
64
57
 
65
58
  for (const file of CONFIG_FILES) {
66
59
  const newPath = join(workspaceDir, file);
@@ -25,17 +25,10 @@ import {
25
25
  renameSync,
26
26
  unlinkSync,
27
27
  } from "node:fs";
28
- import { homedir } from "node:os";
29
28
  import { join } from "node:path";
30
29
 
31
30
  import type { WorkspaceMigration } from "./types.js";
32
-
33
- /** Inlined from platform.ts to satisfy migration self-containment rule (AGENTS.md). */
34
- function getRootDir(): string {
35
- const base = process.env.BASE_DATA_DIR?.trim() || homedir();
36
- return join(base, ".vellum");
37
- }
38
-
31
+ import { getVellumRoot } from "./utils.js";
39
32
  /** Individual files to move from root → workspace (with optional subdirectory). */
40
33
  const FILE_MOVES: Array<{ name: string; subdir?: string }> = [
41
34
  { name: "daemon-stderr.log", subdir: "logs" },
@@ -93,7 +86,7 @@ export const moveRuntimeFilesToWorkspaceMigration: WorkspaceMigration = {
93
86
  "Move daemon-stderr.log, daemon-startup.lock, embed-worker.pid, external/, and bin/ from root to workspace",
94
87
 
95
88
  run(workspaceDir: string): void {
96
- const rootDir = getRootDir();
89
+ const rootDir = getVellumRoot();
97
90
 
98
91
  // Move individual files
99
92
  for (const { name, subdir } of FILE_MOVES) {
@@ -110,7 +103,7 @@ export const moveRuntimeFilesToWorkspaceMigration: WorkspaceMigration = {
110
103
  },
111
104
 
112
105
  down(workspaceDir: string): void {
113
- const rootDir = getRootDir();
106
+ const rootDir = getVellumRoot();
114
107
 
115
108
  // Move individual files back
116
109
  for (const { name, subdir } of FILE_MOVES) {
@@ -22,8 +22,9 @@ import type { WorkspaceMigration } from "./types.js";
22
22
  * 2. **Fresh install** (config.json absent): write a minimal starter
23
23
  * config with just the callSite seeds, using the default provider
24
24
  * (anthropic — same as the schema default). `loadConfig()` runs
25
- * after migrations and backfills the remaining schema defaults via
26
- * `deepMergeMissing`, which preserves our seeded callSites.
25
+ * after migrations and applies schema defaults only to the
26
+ * in-memory config (disk is left untouched), so our seeded
27
+ * callSites are preserved verbatim.
27
28
  *
28
29
  * Without the fresh-install branch, new users permanently fall through
29
30
  * to `llm.default` (opus + max effort) because `LLMSchema.callSites`
@@ -12,7 +12,8 @@ import type { WorkspaceMigration } from "./types.js";
12
12
  * `llm.callSites` without overwriting a user-defined override.
13
13
  * 2. **Fresh install** (config.json absent): write a minimal starter
14
14
  * config with just this seed. `loadConfig()` runs after migrations
15
- * and backfills the remaining schema defaults via `deepMergeMissing`.
15
+ * and applies schema defaults to the in-memory config without
16
+ * rewriting disk, so the seeded callSite is preserved as-is.
16
17
  *
17
18
  * Applied only when:
18
19
  * - the resolved provider is Anthropic (other providers own their
@@ -7,22 +7,17 @@
7
7
  */
8
8
 
9
9
  import { existsSync, renameSync, unlinkSync } from "node:fs";
10
- import { homedir } from "node:os";
11
10
  import { join } from "node:path";
12
11
 
13
12
  import type { WorkspaceMigration } from "./types.js";
14
-
15
- function getRootDir(): string {
16
- const base = process.env.BASE_DATA_DIR?.trim() || homedir();
17
- return join(base, ".vellum");
18
- }
13
+ import { getVellumRoot } from "./utils.js";
19
14
 
20
15
  export const movePidToWorkspaceMigration: WorkspaceMigration = {
21
16
  id: "059-move-pid-to-workspace",
22
17
  description: "Move vellum.pid from root to workspace",
23
18
 
24
19
  run(workspaceDir: string): void {
25
- const oldPath = join(getRootDir(), "vellum.pid");
20
+ const oldPath = join(getVellumRoot(), "vellum.pid");
26
21
  const newPath = join(workspaceDir, "vellum.pid");
27
22
  if (!existsSync(oldPath)) return;
28
23
  if (existsSync(newPath)) {
@@ -37,7 +32,7 @@ export const movePidToWorkspaceMigration: WorkspaceMigration = {
37
32
  },
38
33
 
39
34
  down(workspaceDir: string): void {
40
- const oldPath = join(getRootDir(), "vellum.pid");
35
+ const oldPath = join(getVellumRoot(), "vellum.pid");
41
36
  const newPath = join(workspaceDir, "vellum.pid");
42
37
  if (!existsSync(newPath)) return;
43
38
  if (existsSync(oldPath)) {
@@ -11,22 +11,17 @@
11
11
  */
12
12
 
13
13
  import { copyFileSync, existsSync, unlinkSync } from "node:fs";
14
- import { homedir } from "node:os";
15
14
  import { join } from "node:path";
16
15
 
17
16
  import type { WorkspaceMigration } from "./types.js";
18
-
19
- function getRootDir(): string {
20
- const base = process.env.BASE_DATA_DIR?.trim() || homedir();
21
- return join(base, ".vellum");
22
- }
17
+ import { getVellumRoot } from "./utils.js";
23
18
 
24
19
  export const moveBackupKeyToWorkspaceMigration: WorkspaceMigration = {
25
20
  id: "061-move-backup-key-to-workspace",
26
21
  description: "Move backup.key from protected/ to workspace",
27
22
 
28
23
  run(workspaceDir: string): void {
29
- const oldPath = join(getRootDir(), "protected", "backup.key");
24
+ const oldPath = join(getVellumRoot(), "protected", "backup.key");
30
25
  const newPath = join(workspaceDir, ".backup.key");
31
26
  if (!existsSync(oldPath)) return;
32
27
  if (existsSync(newPath)) {
@@ -42,7 +37,7 @@ export const moveBackupKeyToWorkspaceMigration: WorkspaceMigration = {
42
37
  },
43
38
 
44
39
  down(workspaceDir: string): void {
45
- const oldPath = join(getRootDir(), "protected", "backup.key");
40
+ const oldPath = join(getVellumRoot(), "protected", "backup.key");
46
41
  const newPath = join(workspaceDir, ".backup.key");
47
42
  if (!existsSync(newPath)) return;
48
43
  if (existsSync(oldPath)) {
@@ -6,6 +6,6 @@ Each migration file must be **fully self-contained**. All helper functions, cons
6
6
 
7
7
  - **No external exports.** Migration files must not export anything other than the single `WorkspaceMigration` object. Other code must never import from a migration file.
8
8
  - **Duplicate rather than share.** If two migrations need the same helper, duplicate it in both files. Migrations are write-once code — they run once per assistant and are never modified after release. Duplication is preferable to coupling.
9
- - **Allowed imports:** `./types.js` (for the `WorkspaceMigration` interface), `../../util/logger.js` (for structured logging), and Node/Bun runtime built-ins (`node:fs`, `node:path`, `node:crypto`, `bun:sqlite`, etc.). All other dependencies — both project-internal modules and third-party npm packages — must be inlined. Do not import from shared modules like `../../memory/` or `../../config/`, and do not add npm dependencies.
9
+ - **Allowed imports:** `./types.js` (for the `WorkspaceMigration` interface), `./utils.js` (for shared path-resolution helpers like `getVellumRoot()`), `../../util/logger.js` (for structured logging), and Node/Bun runtime built-ins (`node:fs`, `node:path`, `node:crypto`, `bun:sqlite`, etc.). All other dependencies — both project-internal modules and third-party npm packages — must be inlined. Do not import from shared modules like `../../memory/` or `../../config/`, and do not add npm dependencies.
10
10
  - **Graceful on all platforms.** Migrations run on macOS, Linux, and in Docker. Platform-specific operations must no-op gracefully on unsupported platforms — never throw.
11
11
  - **Idempotent.** Migrations must be safe to re-run if interrupted. The runner checkpoints state as `"started"` before execution and `"completed"` after, so a crash mid-migration triggers a re-run on next startup.
@@ -2,7 +2,7 @@
2
2
  * Workspace migration: Migrate workspace data from /data to /workspace volume.
3
3
  *
4
4
  * In the old Docker volume layout, workspace data lived at
5
- * `$BASE_DATA_DIR/.vellum/workspace`. In the new layout, VELLUM_WORKSPACE_DIR points
5
+ * `<vellumRoot>/workspace`. In the new layout, VELLUM_WORKSPACE_DIR points
6
6
  * to a dedicated volume (e.g. `/workspace`). On first boot with the new layout,
7
7
  * this migration copies existing workspace data from the old location to the
8
8
  * new volume so nothing is lost.
@@ -24,6 +24,7 @@ import {
24
24
  import { join } from "node:path";
25
25
 
26
26
  import type { WorkspaceMigration } from "./types.js";
27
+ import { getVellumRoot } from "./utils.js";
27
28
 
28
29
  const SENTINEL_FILENAME = ".workspace-volume-migrated";
29
30
 
@@ -68,15 +69,8 @@ export const migrateToWorkspaceVolumeMigration: WorkspaceMigration = {
68
69
  return;
69
70
  }
70
71
 
71
- // Resolve the old workspace location: $BASE_DATA_DIR/.vellum/workspace
72
- const baseDataDir = process.env.BASE_DATA_DIR?.trim() || undefined;
73
- if (!baseDataDir) {
74
- // No BASE_DATA_DIR means there's no old location to migrate from
75
- writeSentinel(sentinelPath);
76
- return;
77
- }
78
-
79
- const oldWorkspaceDir = join(baseDataDir, ".vellum", "workspace");
72
+ // Resolve the old workspace location: <vellumRoot>/workspace
73
+ const oldWorkspaceDir = join(getVellumRoot(), "workspace");
80
74
 
81
75
  // If the old workspace doesn't exist or is empty, nothing to migrate
82
76
  if (!existsSync(oldWorkspaceDir)) {
@@ -0,0 +1,21 @@
1
+ import { homedir } from "node:os";
2
+ import { dirname, join } from "node:path";
3
+
4
+ /**
5
+ * Resolve the legacy Vellum root directory (~/.vellum).
6
+ *
7
+ * Resolution order:
8
+ * 1. Parent of VELLUM_WORKSPACE_DIR — e.g. /data/.vellum/workspace → /data/.vellum
9
+ * 2. If that parent is "/" (workspace at top level, e.g. /workspace), fall back
10
+ * to homedir()/.vellum
11
+ *
12
+ * This replaces the old inlined `getRootDir()` pattern used by individual migrations.
13
+ */
14
+ export function getVellumRoot(): string {
15
+ const workspaceDir = process.env.VELLUM_WORKSPACE_DIR?.trim();
16
+ if (workspaceDir) {
17
+ const parent = dirname(workspaceDir);
18
+ if (parent !== "/") return parent;
19
+ }
20
+ return join(homedir(), ".vellum");
21
+ }