@vellumai/assistant 0.6.3 → 0.6.4

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 (667) hide show
  1. package/ARCHITECTURE.md +273 -10
  2. package/Dockerfile +2 -3
  3. package/bun.lock +5 -13
  4. package/docs/backup-troubleshooting.md +52 -0
  5. package/docs/browser-use-architecture-phase2.md +174 -0
  6. package/docs/stt-provider-onboarding.md +120 -0
  7. package/knip.json +12 -2
  8. package/node_modules/@vellumai/ces-contracts/bun.lock +8 -6
  9. package/node_modules/@vellumai/ces-contracts/package.json +3 -3
  10. package/openapi.yaml +982 -72
  11. package/package.json +4 -6
  12. package/scripts/generate-openapi.ts +0 -1
  13. package/scripts/test.sh +73 -18
  14. package/src/__tests__/agent-image-optimize.test.ts +28 -0
  15. package/src/__tests__/agent-loop.test.ts +123 -0
  16. package/src/__tests__/anthropic-provider.test.ts +263 -10
  17. package/src/__tests__/auto-analysis-end-to-end.test.ts +550 -0
  18. package/src/__tests__/auto-analysis-prompt.test.ts +50 -0
  19. package/src/__tests__/browser-fill-credential.test.ts +11 -0
  20. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +2 -2
  21. package/src/__tests__/browser-skill-endstate.test.ts +31 -7
  22. package/src/__tests__/btw-routes.test.ts +7 -0
  23. package/src/__tests__/call-controller.test.ts +581 -20
  24. package/src/__tests__/catalog-files.test.ts +138 -0
  25. package/src/__tests__/channel-invite-transport.test.ts +2 -2
  26. package/src/__tests__/channel-readiness-routes.test.ts +16 -20
  27. package/src/__tests__/channel-readiness-service.test.ts +12 -7
  28. package/src/__tests__/checker.test.ts +157 -10
  29. package/src/__tests__/clawhub-files.test.ts +347 -0
  30. package/src/__tests__/commit-message-enrichment-service.test.ts +36 -19
  31. package/src/__tests__/config-analysis.test.ts +100 -0
  32. package/src/__tests__/config-schema.test.ts +1013 -66
  33. package/src/__tests__/config-watcher-cleanup-throttle.test.ts +339 -0
  34. package/src/__tests__/config-watcher.test.ts +43 -8
  35. package/src/__tests__/contact-store-user-file.test.ts +512 -0
  36. package/src/__tests__/contacts-write.test.ts +197 -0
  37. package/src/__tests__/context-window-manager.test.ts +88 -0
  38. package/src/__tests__/conversation-abort-tool-results.test.ts +2 -0
  39. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
  40. package/src/__tests__/conversation-agent-loop.test.ts +98 -2
  41. package/src/__tests__/conversation-confirmation-signals.test.ts +135 -0
  42. package/src/__tests__/conversation-error.test.ts +70 -0
  43. package/src/__tests__/conversation-history-web-search.test.ts +11 -4
  44. package/src/__tests__/conversation-init.benchmark.test.ts +6 -1
  45. package/src/__tests__/conversation-launcher-skill-regression.test.ts +51 -0
  46. package/src/__tests__/conversation-list-source.test.ts +145 -0
  47. package/src/__tests__/conversation-pre-run-repair.test.ts +2 -0
  48. package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -0
  49. package/src/__tests__/conversation-queue.test.ts +901 -60
  50. package/src/__tests__/conversation-routes-disk-view.test.ts +270 -0
  51. package/src/__tests__/conversation-runtime-assembly.test.ts +55 -0
  52. package/src/__tests__/conversation-skill-tools.test.ts +7 -4
  53. package/src/__tests__/conversation-slash-commands.test.ts +33 -0
  54. package/src/__tests__/conversation-slash-queue.test.ts +89 -18
  55. package/src/__tests__/conversation-slash-unknown.test.ts +2 -0
  56. package/src/__tests__/conversation-tool-setup-batch-authorized.test.ts +226 -0
  57. package/src/__tests__/conversation-workspace-injection.test.ts +2 -0
  58. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +2 -0
  59. package/src/__tests__/credential-health-service.test.ts +352 -0
  60. package/src/__tests__/credential-security-invariants.test.ts +5 -3
  61. package/src/__tests__/credential-vault-unit.test.ts +379 -3
  62. package/src/__tests__/credentials-cli.test.ts +40 -16
  63. package/src/__tests__/cross-provider-web-search.test.ts +146 -35
  64. package/src/__tests__/deterministic-verification-control-plane.test.ts +10 -1
  65. package/src/__tests__/device-id.test.ts +112 -0
  66. package/src/__tests__/docker-signing-key-bootstrap.test.ts +167 -4
  67. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +1 -3
  68. package/src/__tests__/email-html-renderer.test.ts +71 -0
  69. package/src/__tests__/email-invite-adapter.test.ts +36 -32
  70. package/src/__tests__/emit-event-signal.test.ts +71 -0
  71. package/src/__tests__/extension-id-sync-guard.test.ts +75 -8
  72. package/src/__tests__/fixtures/mock-chrome-extension.ts +11 -0
  73. package/src/__tests__/gateway-only-enforcement.test.ts +206 -1
  74. package/src/__tests__/gateway-only-guard.test.ts +0 -1
  75. package/src/__tests__/gemini-provider.test.ts +64 -0
  76. package/src/__tests__/get-skill-detail-audit.test.ts +325 -0
  77. package/src/__tests__/gmail-archive-fallback.test.ts +193 -0
  78. package/src/__tests__/gmail-archive-gate.test.ts +246 -0
  79. package/src/__tests__/gmail-preferences.test.ts +117 -0
  80. package/src/__tests__/headless-browser-interactions.test.ts +43 -0
  81. package/src/__tests__/headless-browser-mode.test.ts +614 -0
  82. package/src/__tests__/headless-browser-navigate.test.ts +142 -5
  83. package/src/__tests__/headless-browser-read-tools.test.ts +11 -0
  84. package/src/__tests__/headless-browser-snapshot.test.ts +10 -0
  85. package/src/__tests__/heartbeat-service.test.ts +70 -17
  86. package/src/__tests__/home-state-routes.test.ts +162 -0
  87. package/src/__tests__/host-bash-proxy.test.ts +0 -5
  88. package/src/__tests__/host-browser-e2e-cloud.test.ts +138 -4
  89. package/src/__tests__/host-browser-e2e-self-hosted.test.ts +4 -4
  90. package/src/__tests__/host-browser-ws-events-e2e.test.ts +103 -0
  91. package/src/__tests__/host-cu-proxy.test.ts +0 -5
  92. package/src/__tests__/identity-intro-cache.test.ts +40 -10
  93. package/src/__tests__/init-feature-flag-overrides.test.ts +38 -112
  94. package/src/__tests__/jobs-store-upsert-debounced.test.ts +141 -0
  95. package/src/__tests__/llm-context-normalization.test.ts +488 -0
  96. package/src/__tests__/llm-context-route-provider.test.ts +86 -5
  97. package/src/__tests__/llm-usage-store.test.ts +363 -0
  98. package/src/__tests__/media-stream-output.test.ts +555 -0
  99. package/src/__tests__/media-stream-parser.test.ts +374 -0
  100. package/src/__tests__/media-stream-server-integration.test.ts +1234 -0
  101. package/src/__tests__/media-stream-stt-session.test.ts +588 -0
  102. package/src/__tests__/media-turn-detector.test.ts +440 -0
  103. package/src/__tests__/message-queue.test.ts +125 -0
  104. package/src/__tests__/migration-export-http.test.ts +6 -6
  105. package/src/__tests__/migration-import-commit-http.test.ts +8 -6
  106. package/src/__tests__/migration-import-preflight-http.test.ts +6 -5
  107. package/src/__tests__/migration-validate-http.test.ts +3 -3
  108. package/src/__tests__/mock-gateway-ipc.ts +151 -0
  109. package/src/__tests__/model-intents.test.ts +2 -2
  110. package/src/__tests__/oauth-apps-routes.test.ts +1 -0
  111. package/src/__tests__/oauth-cli.test.ts +2 -0
  112. package/src/__tests__/oauth-connect-orchestrator.test.ts +2 -0
  113. package/src/__tests__/oauth-provider-serializer.test.ts +1 -0
  114. package/src/__tests__/oauth-providers-routes.test.ts +2 -0
  115. package/src/__tests__/oauth-store.test.ts +85 -0
  116. package/src/__tests__/oauth2-gateway-transport.test.ts +249 -6
  117. package/src/__tests__/onboarding-template-contract.test.ts +6 -13
  118. package/src/__tests__/openai-provider.test.ts +176 -0
  119. package/src/__tests__/openai-responses-cutover-guard.test.ts +184 -0
  120. package/src/__tests__/openai-responses-provider.test.ts +1105 -0
  121. package/src/__tests__/openrouter-token-estimation.test.ts +100 -0
  122. package/src/__tests__/outlook-unsubscribe.test.ts +31 -2
  123. package/src/__tests__/persona-resolver.test.ts +251 -0
  124. package/src/__tests__/platform-bash-auto-approve.test.ts +4 -0
  125. package/src/__tests__/platform.test.ts +92 -1
  126. package/src/__tests__/post-turn-tool-result-truncation.test.ts +47 -0
  127. package/src/__tests__/prechat-onboarding-contract.test.ts +267 -0
  128. package/src/__tests__/pricing.test.ts +174 -0
  129. package/src/__tests__/qdrant-manager.test.ts +29 -8
  130. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +194 -0
  131. package/src/__tests__/relationship-state-contract.test.ts +175 -0
  132. package/src/__tests__/relay-server.test.ts +423 -5
  133. package/src/__tests__/search-skills-unified.test.ts +118 -0
  134. package/src/__tests__/secret-scanner-executor.test.ts +4 -0
  135. package/src/__tests__/secure-keys.test.ts +107 -0
  136. package/src/__tests__/send-endpoint-busy.test.ts +5 -1
  137. package/src/__tests__/sequence-store.test.ts +1 -1
  138. package/src/__tests__/server-history-render.test.ts +49 -0
  139. package/src/__tests__/settings-routes.test.ts +201 -0
  140. package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
  141. package/src/__tests__/skills-file-content-endpoint.test.ts +276 -145
  142. package/src/__tests__/skills-files-catalog-fallback.test.ts +381 -93
  143. package/src/__tests__/skills.test.ts +5 -2
  144. package/src/__tests__/skillssh-files.test.ts +446 -0
  145. package/src/__tests__/slack-block-formatting.test.ts +110 -0
  146. package/src/__tests__/slack-channel-config.test.ts +564 -1
  147. package/src/__tests__/stt-catalog-parity.test.ts +282 -0
  148. package/src/__tests__/stt-stream-session.test.ts +535 -0
  149. package/src/__tests__/system-prompt.test.ts +112 -26
  150. package/src/__tests__/telephony-stt-routing.test.ts +329 -0
  151. package/src/__tests__/terminal-tools.test.ts +18 -7
  152. package/src/__tests__/test-preload.ts +18 -0
  153. package/src/__tests__/test-support/browser-skill-harness.ts +4 -1
  154. package/src/__tests__/tool-executor-lifecycle-events.test.ts +9 -5
  155. package/src/__tests__/tool-executor-shell-integration.test.ts +4 -0
  156. package/src/__tests__/tool-executor.test.ts +33 -24
  157. package/src/__tests__/tool-result-truncation.test.ts +36 -0
  158. package/src/__tests__/trust-store.test.ts +7 -1
  159. package/src/__tests__/trusted-contact-approval-notifier.test.ts +1 -1
  160. package/src/__tests__/tts-catalog-parity.test.ts +345 -0
  161. package/src/__tests__/twilio-routes-twiml.test.ts +512 -114
  162. package/src/__tests__/twilio-routes.test.ts +376 -0
  163. package/src/__tests__/unicode.test.ts +293 -0
  164. package/src/__tests__/update-bulletin-format.test.ts +59 -0
  165. package/src/__tests__/update-bulletin.test.ts +206 -5
  166. package/src/__tests__/usage-routes.test.ts +25 -4
  167. package/src/__tests__/user-reference.test.ts +46 -61
  168. package/src/__tests__/verification-control-plane-policy.test.ts +4 -0
  169. package/src/__tests__/voice-config-update.test.ts +403 -0
  170. package/src/__tests__/voice-quality.test.ts +434 -19
  171. package/src/__tests__/workspace-heartbeat-service.test.ts +7 -0
  172. package/src/__tests__/workspace-migration-033-stt-service-explicit-config.test.ts +547 -0
  173. package/src/__tests__/workspace-migration-034-remove-calls-voice-transcription-provider.test.ts +596 -0
  174. package/src/__tests__/workspace-migration-drop-user-md.test.ts +368 -0
  175. package/src/__tests__/workspace-migration-meets.test.ts +244 -0
  176. package/src/__tests__/workspace-migration-seed-device-id.test.ts +14 -20
  177. package/src/__tests__/workspace-policy.test.ts +2 -0
  178. package/src/agent/image-optimize.ts +24 -12
  179. package/src/agent/loop.ts +43 -3
  180. package/src/backup/__tests__/backup-key.test.ts +152 -0
  181. package/src/backup/__tests__/backup-worker.test.ts +767 -0
  182. package/src/backup/__tests__/list-snapshots.test.ts +87 -0
  183. package/src/backup/__tests__/local-writer.test.ts +218 -0
  184. package/src/backup/__tests__/offsite-writer.test.ts +641 -0
  185. package/src/backup/__tests__/paths.test.ts +300 -0
  186. package/src/backup/__tests__/restore.test.ts +498 -0
  187. package/src/backup/__tests__/snapshot-lock.test.ts +352 -0
  188. package/src/backup/__tests__/stream-crypt.test.ts +228 -0
  189. package/src/backup/backup-key.ts +137 -0
  190. package/src/backup/backup-worker.ts +459 -0
  191. package/src/backup/list-snapshots.ts +147 -0
  192. package/src/backup/local-writer.ts +133 -0
  193. package/src/backup/offsite-writer.ts +222 -0
  194. package/src/backup/paths.ts +226 -0
  195. package/src/backup/restore.ts +322 -0
  196. package/src/backup/snapshot-lock.ts +431 -0
  197. package/src/backup/stream-crypt.ts +263 -0
  198. package/src/bundler/package-resolver.ts +4 -0
  199. package/src/calls/audio-store.ts +11 -5
  200. package/src/calls/call-controller.ts +226 -71
  201. package/src/calls/call-domain.ts +9 -0
  202. package/src/calls/call-speech-output.ts +190 -0
  203. package/src/calls/call-transport.ts +77 -0
  204. package/src/calls/media-stream-audio-transcode.ts +173 -0
  205. package/src/calls/media-stream-output.ts +660 -0
  206. package/src/calls/media-stream-parser.ts +300 -0
  207. package/src/calls/media-stream-protocol.ts +166 -0
  208. package/src/calls/media-stream-server.ts +592 -0
  209. package/src/calls/media-stream-stt-session.ts +460 -0
  210. package/src/calls/media-turn-detector.ts +230 -0
  211. package/src/calls/relay-server.ts +90 -75
  212. package/src/calls/resolve-call-tts-provider.ts +136 -0
  213. package/src/calls/telephony-stt-routing.ts +145 -0
  214. package/src/calls/tts-call-strategy.ts +161 -0
  215. package/src/calls/tts-text-sanitizer.ts +32 -16
  216. package/src/calls/twilio-routes.ts +281 -17
  217. package/src/calls/voice-quality.ts +78 -35
  218. package/src/calls/voice-session-bridge.ts +8 -1
  219. package/src/channels/types.ts +16 -0
  220. package/src/cli/__tests__/run-assistant-command.ts +11 -1
  221. package/src/cli/commands/__tests__/backup.test.ts +1165 -0
  222. package/src/cli/commands/__tests__/domain-register.test.ts +234 -0
  223. package/src/cli/commands/__tests__/domain-status.test.ts +132 -0
  224. package/src/cli/commands/__tests__/email-attachment.test.ts +422 -0
  225. package/src/cli/commands/__tests__/email-download.test.ts +16 -1
  226. package/src/cli/commands/__tests__/email-list.test.ts +22 -4
  227. package/src/cli/commands/__tests__/email-register.test.ts +4 -4
  228. package/src/cli/commands/__tests__/email-send.test.ts +37 -4
  229. package/src/cli/commands/__tests__/email-status.test.ts +5 -1
  230. package/src/cli/commands/__tests__/email-unregister.test.ts +34 -5
  231. package/src/cli/commands/backup.ts +993 -0
  232. package/src/cli/commands/conversations.ts +77 -0
  233. package/src/cli/commands/credentials.ts +0 -1
  234. package/src/cli/commands/domain.ts +210 -0
  235. package/src/cli/commands/email.ts +255 -3
  236. package/src/cli/commands/oauth/__tests__/connect.test.ts +12 -0
  237. package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +1 -0
  238. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +1 -0
  239. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -0
  240. package/src/cli/commands/oauth/mode.ts +12 -3
  241. package/src/cli/commands/oauth/providers.ts +15 -0
  242. package/src/cli/commands/oauth/shared.ts +2 -1
  243. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +4 -9
  244. package/src/cli/commands/platform/__tests__/connect.test.ts +6 -0
  245. package/src/cli/commands/platform/__tests__/disconnect.test.ts +7 -1
  246. package/src/cli/commands/platform/__tests__/status.test.ts +6 -0
  247. package/src/cli/program.ts +30 -4
  248. package/src/config/__tests__/backup-schema.test.ts +134 -0
  249. package/src/config/assistant-feature-flags.ts +61 -62
  250. package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +37 -1
  251. package/src/config/bundled-skills/browser/SKILL.md +30 -5
  252. package/src/config/bundled-skills/browser/TOOLS.json +123 -0
  253. package/src/config/bundled-skills/browser/tools/browser-attach.ts +12 -0
  254. package/src/config/bundled-skills/browser/tools/browser-detach.ts +12 -0
  255. package/src/config/bundled-skills/browser/tools/browser-status.ts +12 -0
  256. package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +17 -0
  257. package/src/config/bundled-skills/contacts/SKILL.md +2 -2
  258. package/src/config/bundled-skills/gmail/SKILL.md +53 -7
  259. package/src/config/bundled-skills/gmail/TOOLS.json +33 -3
  260. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +116 -9
  261. package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +138 -11
  262. package/src/config/bundled-skills/gmail/tools/gmail-preferences-tool.ts +59 -0
  263. package/src/config/bundled-skills/gmail/tools/gmail-preferences.ts +82 -0
  264. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +113 -17
  265. package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +2 -2
  266. package/src/config/bundled-skills/media-processing/SKILL.md +3 -9
  267. package/src/config/bundled-skills/media-processing/TOOLS.json +1 -6
  268. package/src/config/bundled-skills/media-processing/__tests__/audio-transcribe.test.ts +125 -0
  269. package/src/config/bundled-skills/media-processing/__tests__/extract-keyframes.test.ts +181 -0
  270. package/src/config/bundled-skills/media-processing/__tests__/preprocess-audio.test.ts +141 -0
  271. package/src/config/bundled-skills/media-processing/services/audio-transcribe.ts +32 -87
  272. package/src/config/bundled-skills/media-processing/services/preprocess.ts +8 -4
  273. package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +0 -10
  274. package/src/config/bundled-skills/messaging/SKILL.md +3 -3
  275. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +2 -2
  276. package/src/config/bundled-skills/outlook/SKILL.md +2 -2
  277. package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +2 -2
  278. package/src/config/bundled-skills/phone-calls/SKILL.md +2 -2
  279. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +27 -18
  280. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +3 -3
  281. package/src/config/bundled-skills/settings/TOOLS.json +3 -3
  282. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +26 -22
  283. package/src/config/bundled-skills/slack/SKILL.md +1 -0
  284. package/src/config/bundled-skills/transcribe/SKILL.md +9 -14
  285. package/src/config/bundled-skills/transcribe/TOOLS.json +2 -7
  286. package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +256 -0
  287. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +38 -188
  288. package/src/config/bundled-tool-registry.ts +8 -0
  289. package/src/config/env-registry.ts +24 -0
  290. package/src/config/env.ts +34 -10
  291. package/src/config/feature-flag-registry.json +46 -14
  292. package/src/config/loader.ts +26 -12
  293. package/src/config/schema.ts +35 -10
  294. package/src/config/schemas/__tests__/stt.test.ts +43 -0
  295. package/src/config/schemas/analysis.ts +51 -0
  296. package/src/config/schemas/backup.ts +72 -0
  297. package/src/config/schemas/calls.ts +1 -26
  298. package/src/config/schemas/elevenlabs.ts +0 -59
  299. package/src/config/schemas/filing.ts +47 -7
  300. package/src/config/schemas/heartbeat.ts +27 -5
  301. package/src/config/schemas/host-browser.ts +47 -1
  302. package/src/config/schemas/inference.ts +1 -1
  303. package/src/config/schemas/memory-lifecycle.ts +14 -2
  304. package/src/config/schemas/services.ts +44 -0
  305. package/src/config/schemas/stt.ts +59 -0
  306. package/src/config/schemas/tts.ts +230 -0
  307. package/src/config/schemas/updates.ts +14 -0
  308. package/src/config/skills.ts +4 -0
  309. package/src/config/types.ts +4 -0
  310. package/src/contacts/contact-store.ts +56 -11
  311. package/src/contacts/contacts-write.ts +38 -1
  312. package/src/context/post-turn-tool-result-truncation.ts +3 -2
  313. package/src/context/tool-result-truncation.ts +2 -1
  314. package/src/context/window-manager.ts +45 -12
  315. package/src/credential-execution/executable-discovery.ts +12 -2
  316. package/src/credential-execution/process-manager.ts +33 -2
  317. package/src/credential-health/credential-health-service.ts +366 -0
  318. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +324 -0
  319. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +497 -0
  320. package/src/daemon/__tests__/conversation-tool-setup.test.ts +17 -8
  321. package/src/daemon/__tests__/lifecycle-startup-ordering.test.ts +127 -0
  322. package/src/daemon/config-watcher.ts +99 -5
  323. package/src/daemon/conversation-agent-loop-handlers.ts +6 -0
  324. package/src/daemon/conversation-agent-loop.ts +101 -24
  325. package/src/daemon/conversation-error.ts +11 -0
  326. package/src/daemon/conversation-history.ts +40 -6
  327. package/src/daemon/conversation-launch.ts +220 -0
  328. package/src/daemon/conversation-lifecycle.ts +59 -9
  329. package/src/daemon/conversation-messaging.ts +37 -3
  330. package/src/daemon/conversation-notifiers.ts +5 -0
  331. package/src/daemon/conversation-process.ts +581 -19
  332. package/src/daemon/conversation-queue-manager.ts +24 -0
  333. package/src/daemon/conversation-runtime-assembly.ts +11 -1
  334. package/src/daemon/conversation-slash.ts +36 -0
  335. package/src/daemon/conversation-surfaces.ts +94 -4
  336. package/src/daemon/conversation-tool-setup.ts +25 -0
  337. package/src/daemon/conversation-usage.ts +7 -4
  338. package/src/daemon/conversation.ts +86 -28
  339. package/src/daemon/handlers/config-slack-channel.ts +269 -94
  340. package/src/daemon/handlers/conversations.ts +4 -1
  341. package/src/daemon/handlers/shared.ts +22 -0
  342. package/src/daemon/handlers/skills.ts +321 -77
  343. package/src/daemon/host-browser-proxy.ts +2 -1
  344. package/src/daemon/lifecycle.ts +122 -25
  345. package/src/daemon/message-protocol.ts +6 -0
  346. package/src/daemon/message-types/conversations.ts +34 -1
  347. package/src/daemon/message-types/home.ts +40 -0
  348. package/src/daemon/message-types/meet.ts +143 -0
  349. package/src/daemon/message-types/messages.ts +14 -0
  350. package/src/daemon/message-types/schedules.ts +34 -2
  351. package/src/daemon/message-types/skills.ts +16 -0
  352. package/src/daemon/message-types/surfaces.ts +2 -0
  353. package/src/daemon/server.ts +347 -2
  354. package/src/daemon/shutdown-handlers.ts +32 -4
  355. package/src/daemon/shutdown-registry.ts +40 -0
  356. package/src/daemon/tool-side-effects.ts +9 -0
  357. package/src/email/html-renderer.ts +76 -0
  358. package/src/heartbeat/heartbeat-service.ts +93 -7
  359. package/src/home/__tests__/assistant-feed-authoring.test.ts +156 -0
  360. package/src/home/__tests__/emit-feed-event.test.ts +169 -0
  361. package/src/home/__tests__/feed-scheduler.test.ts +194 -0
  362. package/src/home/__tests__/feed-types.test.ts +275 -0
  363. package/src/home/__tests__/feed-writer.test.ts +688 -0
  364. package/src/home/__tests__/phase5-exit-criteria.test.ts +212 -0
  365. package/src/home/__tests__/platform-gmail-digest.test.ts +222 -0
  366. package/src/home/__tests__/progress-formula.test.ts +213 -0
  367. package/src/home/__tests__/relationship-state-writer.test.ts +740 -0
  368. package/src/home/__tests__/rollup-producer.test.ts +398 -0
  369. package/src/home/assistant-feed-authoring.ts +124 -0
  370. package/src/home/emit-feed-event.ts +158 -0
  371. package/src/home/feed-scheduler.ts +247 -0
  372. package/src/home/feed-types.ts +181 -0
  373. package/src/home/feed-writer.ts +469 -0
  374. package/src/home/platform-gmail-digest.ts +163 -0
  375. package/src/home/progress-formula.ts +86 -0
  376. package/src/home/relationship-state-writer.ts +824 -0
  377. package/src/home/relationship-state.ts +143 -0
  378. package/src/home/rollup-producer.ts +384 -0
  379. package/src/hooks/runner.ts +7 -0
  380. package/src/inbound/platform-callback-registration.ts +12 -3
  381. package/src/inbound/public-ingress-urls.ts +12 -0
  382. package/src/instrument.ts +1 -1
  383. package/src/ipc/__tests__/cli-ipc.test.ts +200 -0
  384. package/src/ipc/cli-client.ts +151 -0
  385. package/src/ipc/cli-server.ts +234 -0
  386. package/src/ipc/gateway-client.ts +180 -0
  387. package/src/ipc/routes/index.ts +5 -0
  388. package/src/ipc/routes/wake-conversation.ts +19 -0
  389. package/src/memory/__tests__/auto-analysis-enqueue.test.ts +356 -0
  390. package/src/memory/__tests__/auto-analysis-guard.test.ts +57 -0
  391. package/src/memory/__tests__/conversation-analyze-job.test.ts +232 -0
  392. package/src/memory/__tests__/find-analysis-conversation.test.ts +196 -0
  393. package/src/memory/app-store.ts +1 -1
  394. package/src/memory/attachments-store.ts +70 -0
  395. package/src/memory/auto-analysis-enqueue.ts +127 -0
  396. package/src/memory/auto-analysis-guard.ts +27 -0
  397. package/src/memory/cleanup-schedule-state.ts +37 -0
  398. package/src/memory/conversation-analyze-job.ts +73 -0
  399. package/src/memory/conversation-crud.ts +99 -0
  400. package/src/memory/conversation-disk-view.ts +7 -0
  401. package/src/memory/conversation-group-migration.ts +34 -2
  402. package/src/memory/conversation-queries.ts +6 -5
  403. package/src/memory/db-init.ts +6 -0
  404. package/src/memory/db-maintenance.ts +108 -0
  405. package/src/memory/db.ts +1 -0
  406. package/src/memory/graph/conversation-graph-memory.ts +15 -0
  407. package/src/memory/graph/extraction.test.ts +23 -0
  408. package/src/memory/graph/extraction.ts +8 -0
  409. package/src/memory/graph/retriever.ts +27 -18
  410. package/src/memory/graph/scoring.test.ts +186 -0
  411. package/src/memory/graph/scoring.ts +31 -1
  412. package/src/memory/graph/tools.ts +1 -1
  413. package/src/memory/group-crud.ts +6 -1
  414. package/src/memory/indexer.ts +95 -16
  415. package/src/memory/job-handlers/cleanup.ts +11 -8
  416. package/src/memory/job-handlers/conversation-starters.ts +16 -10
  417. package/src/memory/jobs-store.ts +64 -4
  418. package/src/memory/jobs-worker.ts +22 -9
  419. package/src/memory/llm-usage-store.ts +92 -56
  420. package/src/memory/migrations/219-oauth-providers-token-exchange-body-format.ts +15 -0
  421. package/src/memory/migrations/220-normalize-user-file-by-principal.ts +190 -0
  422. package/src/memory/migrations/221-conversations-archived-at.ts +16 -0
  423. package/src/memory/migrations/index.ts +6 -0
  424. package/src/memory/migrations/registry.ts +8 -0
  425. package/src/memory/qdrant-manager.ts +43 -16
  426. package/src/memory/schema/conversations.ts +2 -0
  427. package/src/memory/schema/oauth.ts +3 -0
  428. package/src/memory/usage-buckets.ts +396 -0
  429. package/src/messaging/providers/gmail/client.ts +57 -6
  430. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +282 -0
  431. package/src/messaging/providers/slack/adapter.ts +143 -38
  432. package/src/messaging/providers/slack/client.ts +16 -0
  433. package/src/messaging/providers/slack/types.ts +4 -0
  434. package/src/notifications/decision-engine.ts +3 -3
  435. package/src/notifications/signal.ts +5 -0
  436. package/src/oauth/__tests__/identity-verifier.test.ts +1 -0
  437. package/src/oauth/byo-connection.test.ts +18 -1
  438. package/src/oauth/byo-connection.ts +3 -1
  439. package/src/oauth/connect-orchestrator.ts +2 -0
  440. package/src/oauth/connection-resolver.ts +6 -2
  441. package/src/oauth/connection.ts +2 -0
  442. package/src/oauth/oauth-store.ts +9 -0
  443. package/src/oauth/platform-connection.test.ts +98 -0
  444. package/src/oauth/platform-connection.ts +52 -31
  445. package/src/oauth/seed-providers.ts +7 -0
  446. package/src/permissions/checker.ts +16 -6
  447. package/src/permissions/defaults.ts +49 -1
  448. package/src/permissions/trust-store.ts +3 -3
  449. package/src/permissions/workspace-policy.ts +3 -0
  450. package/src/platform/client.test.ts +10 -0
  451. package/src/platform/sync-identity.ts +129 -0
  452. package/src/prompts/persona-resolver.ts +126 -2
  453. package/src/prompts/system-prompt.ts +59 -18
  454. package/src/prompts/templates/BOOTSTRAP.md +5 -5
  455. package/src/prompts/templates/SOUL.md +3 -1
  456. package/src/prompts/templates/UPDATES.md +12 -0
  457. package/src/prompts/templates/channels/slack.md +20 -0
  458. package/src/prompts/update-bulletin-format.ts +26 -9
  459. package/src/prompts/update-bulletin.ts +34 -23
  460. package/src/prompts/user-reference.ts +20 -17
  461. package/src/providers/__tests__/provider-secret-catalog.test.ts +42 -0
  462. package/src/providers/anthropic/client.ts +157 -61
  463. package/src/providers/fireworks/client.ts +2 -2
  464. package/src/providers/gemini/client.ts +9 -1
  465. package/src/providers/model-catalog.ts +6 -0
  466. package/src/providers/model-intents.ts +4 -4
  467. package/src/providers/ollama/client.ts +2 -2
  468. package/src/providers/openai/chat-completions-provider.ts +474 -0
  469. package/src/providers/openai/client.ts +25 -440
  470. package/src/providers/openai/responses-provider.ts +502 -0
  471. package/src/providers/openrouter/client.ts +101 -4
  472. package/src/providers/provider-secret-catalog.ts +139 -0
  473. package/src/providers/registry.ts +2 -2
  474. package/src/providers/retry.ts +14 -3
  475. package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +251 -0
  476. package/src/providers/speech-to-text/__tests__/resolve.test.ts +828 -0
  477. package/src/providers/speech-to-text/deepgram-realtime.test.ts +980 -0
  478. package/src/providers/speech-to-text/deepgram-realtime.ts +767 -0
  479. package/src/providers/speech-to-text/deepgram.test.ts +332 -0
  480. package/src/providers/speech-to-text/deepgram.ts +115 -0
  481. package/src/providers/speech-to-text/google-gemini-live-stream.test.ts +743 -0
  482. package/src/providers/speech-to-text/google-gemini-live-stream.ts +625 -0
  483. package/src/providers/speech-to-text/google-gemini.test.ts +226 -0
  484. package/src/providers/speech-to-text/google-gemini.ts +101 -0
  485. package/src/providers/speech-to-text/openai-whisper-stream.test.ts +564 -0
  486. package/src/providers/speech-to-text/openai-whisper-stream.ts +381 -0
  487. package/src/providers/speech-to-text/openai-whisper.test.ts +1 -37
  488. package/src/providers/speech-to-text/openai-whisper.ts +63 -33
  489. package/src/providers/speech-to-text/provider-catalog.ts +306 -0
  490. package/src/providers/speech-to-text/resolve.ts +386 -6
  491. package/src/providers/types.ts +9 -0
  492. package/src/runtime/AGENTS.md +43 -1
  493. package/src/runtime/__tests__/agent-wake.test.ts +831 -0
  494. package/src/runtime/__tests__/runtime-mode.test.ts +62 -0
  495. package/src/runtime/__tests__/slack-block-formatting.test.ts +481 -0
  496. package/src/runtime/agent-wake.ts +512 -0
  497. package/src/runtime/auth/__tests__/route-policy.test.ts +40 -0
  498. package/src/runtime/auth/route-policy.ts +30 -5
  499. package/src/runtime/auth/token-service.ts +56 -1
  500. package/src/runtime/btw-sidechain.ts +2 -0
  501. package/src/runtime/capability-tokens.ts +10 -10
  502. package/src/runtime/channel-invite-transport.ts +1 -1
  503. package/src/runtime/channel-invite-transports/email.ts +14 -6
  504. package/src/runtime/channel-readiness-service.ts +12 -22
  505. package/src/runtime/chrome-extension-registry.ts +38 -2
  506. package/src/runtime/http-server.ts +395 -10
  507. package/src/runtime/http-types.ts +6 -2
  508. package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +36 -0
  509. package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +360 -0
  510. package/src/runtime/migrations/migration-transport.ts +1 -0
  511. package/src/runtime/migrations/migration-wizard.ts +1 -0
  512. package/src/runtime/migrations/vbundle-import-analyzer.ts +77 -1
  513. package/src/runtime/migrations/vbundle-importer.ts +34 -0
  514. package/src/runtime/pending-interactions.ts +0 -11
  515. package/src/runtime/routes/__tests__/backup-routes.test.ts +967 -0
  516. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +507 -0
  517. package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +208 -0
  518. package/src/runtime/routes/__tests__/stt-routes.test.ts +406 -0
  519. package/src/runtime/routes/__tests__/tts-routes.test.ts +474 -0
  520. package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +148 -17
  521. package/src/runtime/routes/app-management-routes.ts +12 -18
  522. package/src/runtime/routes/attachment-routes.test.ts +9 -3
  523. package/src/runtime/routes/attachment-routes.ts +216 -17
  524. package/src/runtime/routes/backup-routes.ts +519 -0
  525. package/src/runtime/routes/browser-extension-pair-routes.ts +82 -23
  526. package/src/runtime/routes/btw-routes.ts +8 -6
  527. package/src/runtime/routes/contact-routes.test.ts +298 -0
  528. package/src/runtime/routes/contact-routes.ts +132 -5
  529. package/src/runtime/routes/conversation-analysis-routes.ts +22 -142
  530. package/src/runtime/routes/conversation-management-routes.ts +115 -0
  531. package/src/runtime/routes/conversation-routes.ts +367 -146
  532. package/src/runtime/routes/filing-routes.ts +93 -0
  533. package/src/runtime/routes/home-feed-routes.ts +334 -0
  534. package/src/runtime/routes/home-state-routes.ts +138 -0
  535. package/src/runtime/routes/host-browser-routes.ts +3 -14
  536. package/src/runtime/routes/identity-intro-cache.ts +7 -3
  537. package/src/runtime/routes/identity-routes.ts +3 -17
  538. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +46 -39
  539. package/src/runtime/routes/inbound-stages/transcribe-audio.ts +15 -15
  540. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +137 -0
  541. package/src/runtime/routes/integrations/slack/__tests__/share.test.ts +179 -0
  542. package/src/runtime/routes/integrations/slack/channel.ts +11 -3
  543. package/src/runtime/routes/integrations/slack/share.ts +45 -7
  544. package/src/runtime/routes/llm-context-normalization.ts +303 -0
  545. package/src/runtime/routes/memory-item-routes.test.ts +3 -2
  546. package/src/runtime/routes/migration-routes.ts +40 -5
  547. package/src/runtime/routes/settings-routes.ts +22 -5
  548. package/src/runtime/routes/skills-routes.ts +76 -7
  549. package/src/runtime/routes/stt-routes.ts +233 -0
  550. package/src/runtime/routes/surface-action-routes.ts +41 -2
  551. package/src/runtime/routes/tts-routes.ts +108 -24
  552. package/src/runtime/routes/usage-routes.ts +30 -2
  553. package/src/runtime/routes/user-route-dispatcher.ts +50 -5
  554. package/src/runtime/routes/user-routes.ts +13 -1
  555. package/src/runtime/routes/work-items-routes.ts +8 -1
  556. package/src/runtime/runtime-mode.ts +33 -0
  557. package/src/runtime/services/__tests__/analyze-conversation.test.ts +444 -0
  558. package/src/runtime/services/__tests__/analyze-deps-singleton.test.ts +67 -0
  559. package/src/runtime/services/__tests__/auto-analysis-prompt.test.ts +53 -0
  560. package/src/runtime/services/__tests__/manual-analysis-prompt.test.ts +41 -0
  561. package/src/runtime/services/analyze-conversation.ts +344 -0
  562. package/src/runtime/services/analyze-deps-singleton.ts +32 -0
  563. package/src/runtime/services/auto-analysis-prompt.ts +55 -0
  564. package/src/runtime/skill-route-registry.ts +49 -0
  565. package/src/runtime/slack-block-formatting.ts +437 -10
  566. package/src/schedule/scheduler.ts +50 -0
  567. package/src/security/oauth2.ts +26 -4
  568. package/src/security/secure-keys.ts +25 -2
  569. package/src/security/token-manager.ts +8 -0
  570. package/src/sequence/engine.ts +23 -0
  571. package/src/sequence/types.ts +1 -1
  572. package/src/skills/catalog-files.ts +64 -2
  573. package/src/skills/category-inference.ts +122 -0
  574. package/src/skills/clawhub-files.ts +213 -0
  575. package/src/skills/clawhub.ts +84 -23
  576. package/src/skills/skill-file-provider.ts +40 -0
  577. package/src/skills/skillssh-files.ts +395 -0
  578. package/src/skills/skillssh-registry.ts +4 -4
  579. package/src/stt/__tests__/daemon-batch-transcriber.test.ts +392 -0
  580. package/src/stt/__tests__/types.test.ts +89 -0
  581. package/src/stt/daemon-batch-transcriber.ts +195 -0
  582. package/src/stt/stt-stream-session.ts +499 -0
  583. package/src/stt/types.ts +330 -0
  584. package/src/stt/wav-encoder.test.ts +373 -0
  585. package/src/stt/wav-encoder.ts +175 -0
  586. package/src/subagent/manager.ts +38 -14
  587. package/src/tools/browser/__tests__/browser-mode.test.ts +119 -0
  588. package/src/tools/browser/__tests__/browser-status.test.ts +123 -0
  589. package/src/tools/browser/browser-execution.ts +1163 -23
  590. package/src/tools/browser/browser-manager.ts +45 -0
  591. package/src/tools/browser/browser-mode-constants.ts +12 -0
  592. package/src/tools/browser/browser-mode.ts +92 -0
  593. package/src/tools/browser/browser-status-constants.ts +33 -0
  594. package/src/tools/browser/cdp-client/__tests__/cdp-inspect-client.test.ts +393 -0
  595. package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +29 -0
  596. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +1648 -32
  597. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/discovery.test.ts +264 -0
  598. package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +183 -17
  599. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +254 -21
  600. package/src/tools/browser/cdp-client/errors.ts +15 -0
  601. package/src/tools/browser/cdp-client/extension-cdp-client.ts +39 -16
  602. package/src/tools/browser/cdp-client/factory.ts +797 -87
  603. package/src/tools/browser/cdp-client/index.ts +16 -2
  604. package/src/tools/browser/cdp-client/types.ts +68 -0
  605. package/src/tools/credentials/vault.ts +35 -6
  606. package/src/tools/network/web-fetch.ts +5 -2
  607. package/src/tools/network/web-search.ts +5 -2
  608. package/src/tools/shared/shell-output.ts +3 -1
  609. package/src/tools/side-effects.ts +2 -0
  610. package/src/tools/skills/sandbox-runner.ts +3 -2
  611. package/src/tools/terminal/safe-env.ts +10 -2
  612. package/src/tools/terminal/shell.ts +15 -4
  613. package/src/tools/tool-manifest.ts +21 -0
  614. package/src/tools/types.ts +17 -0
  615. package/src/tools/ui-surface/definitions.ts +6 -1
  616. package/src/tts/__tests__/provider-adapters.test.ts +834 -0
  617. package/src/tts/__tests__/provider-catalog-consistency.test.ts +196 -0
  618. package/src/tts/__tests__/provider-catalog.test.ts +183 -0
  619. package/src/tts/__tests__/provider-registry.test.ts +90 -0
  620. package/src/tts/provider-catalog.ts +201 -0
  621. package/src/tts/provider-registry.ts +73 -0
  622. package/src/tts/providers/deepgram-provider.ts +219 -0
  623. package/src/tts/providers/elevenlabs-provider.ts +211 -0
  624. package/src/tts/providers/fish-audio-provider.ts +183 -0
  625. package/src/tts/providers/index.ts +42 -0
  626. package/src/tts/providers/register-builtins.ts +130 -0
  627. package/src/tts/synthesize-text.ts +110 -0
  628. package/src/tts/tts-config-resolver.ts +78 -0
  629. package/src/tts/types.ts +153 -0
  630. package/src/types/onboarding-context.ts +7 -0
  631. package/src/util/abort-reasons.ts +58 -0
  632. package/src/util/device-id.ts +32 -16
  633. package/src/util/errors.ts +9 -1
  634. package/src/util/platform.ts +54 -10
  635. package/src/util/pricing.ts +66 -3
  636. package/src/util/spawn.ts +1 -1
  637. package/src/util/truncate.ts +4 -2
  638. package/src/util/unicode.ts +201 -0
  639. package/src/version.ts +19 -24
  640. package/src/watcher/engine.ts +23 -0
  641. package/src/watcher/watcher-store.ts +31 -0
  642. package/src/workspace/migrations/003-seed-device-id.ts +9 -3
  643. package/src/workspace/migrations/017-seed-persona-dirs.ts +68 -4
  644. package/src/workspace/migrations/029-seed-pkb.ts +1 -1
  645. package/src/workspace/migrations/031-drop-user-md.ts +317 -0
  646. package/src/workspace/migrations/031-llm-log-retention-zero-to-null.ts +73 -0
  647. package/src/workspace/migrations/032-tts-provider-unification.ts +227 -0
  648. package/src/workspace/migrations/033-stt-service-explicit-config.ts +122 -0
  649. package/src/workspace/migrations/034-remove-calls-voice-transcription-provider.ts +215 -0
  650. package/src/workspace/migrations/035-seed-slack-channel-persona.ts +50 -0
  651. package/src/workspace/migrations/036-update-pkb-index-bar.ts +37 -0
  652. package/src/workspace/migrations/037-create-meets-dir.ts +61 -0
  653. package/src/workspace/migrations/registry.ts +16 -0
  654. package/src/workspace/top-level-renderer.ts +13 -1
  655. package/src/workspace/turn-commit.ts +31 -0
  656. package/src/__tests__/email-cli.test.ts +0 -297
  657. package/src/__tests__/email-service-config-fallback.test.ts +0 -102
  658. package/src/cli/commands/browser-relay.ts +0 -466
  659. package/src/email/guardrails.ts +0 -221
  660. package/src/email/provider.ts +0 -117
  661. package/src/email/providers/agentmail.ts +0 -361
  662. package/src/email/providers/index.ts +0 -65
  663. package/src/email/service.ts +0 -384
  664. package/src/email/types.ts +0 -126
  665. package/src/prompts/templates/USER.md +0 -13
  666. package/src/providers/speech-to-text/types.ts +0 -17
  667. package/src/runtime/routes/browser-cdp-routes.ts +0 -229
@@ -102,16 +102,24 @@ export class AssistantError extends VellumError {
102
102
  export class ProviderError extends AssistantError {
103
103
  /** Delay (in ms) suggested by the server's Retry-After header, if present. */
104
104
  public readonly retryAfterMs?: number;
105
+ /**
106
+ * Tagged daemon-owned abort reason carried over from the AbortSignal that
107
+ * triggered this error. Untyped here to avoid a daemon→util import cycle;
108
+ * `AbortReason` from `util/abort-reasons.ts` is the only producer and
109
+ * `isAbortReason` is the canonical type guard for consumers.
110
+ */
111
+ public readonly abortReason?: unknown;
105
112
 
106
113
  constructor(
107
114
  message: string,
108
115
  public readonly provider: string,
109
116
  public readonly statusCode?: number,
110
- options?: { cause?: unknown; retryAfterMs?: number },
117
+ options?: { cause?: unknown; retryAfterMs?: number; abortReason?: unknown },
111
118
  ) {
112
119
  super(message, ErrorCode.PROVIDER_ERROR, options);
113
120
  this.name = "ProviderError";
114
121
  this.retryAfterMs = options?.retryAfterMs;
122
+ this.abortReason = options?.abortReason;
115
123
  }
116
124
  }
117
125
 
@@ -45,13 +45,24 @@ export function normalizeAssistantId(assistantId: string): string {
45
45
  }
46
46
 
47
47
  /**
48
- * Compute the root ~/.vellum directory path.
48
+ * The daemon's per-instance root data directory. Resolution order:
49
49
  *
50
- * This is a simple inline computation not a shared function — so each
51
- * helper is self-contained and the module has no hidden coupling to
52
- * env-var indirection.
50
+ * 1. `BASE_DATA_DIR` env var set by the CLI when spawning the daemon
51
+ * for a specific assistant (see `cli/src/lib/local.ts`). Points at the
52
+ * per-assistant instanceDir; we append `.vellum` to get the daemon's
53
+ * root so root-level state (PID, credentials, runtime-port, workspace)
54
+ * is isolated per instance.
55
+ * 2. Fallback: `join(homedir(), ".vellum")`. Used when the daemon runs
56
+ * outside the CLI-spawn lifecycle (containerized deployments, manual
57
+ * test invocations).
58
+ *
59
+ * Docker mode relocates the workspace via `VELLUM_WORKSPACE_DIR` rather
60
+ * than `BASE_DATA_DIR`, so honoring `BASE_DATA_DIR` here does not affect
61
+ * containerized deployments.
53
62
  */
54
63
  function vellumRoot(): string {
64
+ const baseDataDir = process.env.BASE_DATA_DIR?.trim();
65
+ if (baseDataDir) return join(baseDataDir, ".vellum");
55
66
  return join(homedir(), ".vellum");
56
67
  }
57
68
 
@@ -143,15 +154,47 @@ export function getTCPHost(): string {
143
154
  return "127.0.0.1";
144
155
  }
145
156
 
157
+ // Kept in sync with `cli/src/lib/environments/seeds.ts` and
158
+ // `clients/chrome-extension/native-host/src/lockfile.ts`. Drift between
159
+ // these three sites is caught at test time by
160
+ // `cli/src/__tests__/env-drift.test.ts`. Fast follow: hoist the shared
161
+ // list into a `packages/environments` package so all three sites import
162
+ // from one place.
163
+ const KNOWN_ENVIRONMENTS: ReadonlySet<string> = new Set([
164
+ "production",
165
+ "staging",
166
+ "test",
167
+ "dev",
168
+ "local",
169
+ ]);
170
+
171
+ /**
172
+ * Returns the env-scoped XDG config subdirectory name for Vellum
173
+ * (`vellum` in production, `vellum-<env>` otherwise). Mirrors the Swift
174
+ * side's `VellumPaths.configDir` and the CLI's
175
+ * `environments/paths.ts:getConfigDir` so the daemon resolves to the
176
+ * same on-disk location as every other writer of these files.
177
+ *
178
+ * Unknown environment names fall back to production to preserve the
179
+ * legacy path for any unrecognized value.
180
+ */
181
+ export function getXdgVellumConfigDirName(): string {
182
+ const raw = process.env.VELLUM_ENVIRONMENT?.trim();
183
+ if (!raw || raw === "production") return "vellum";
184
+ if (!KNOWN_ENVIRONMENTS.has(raw)) return "vellum";
185
+ return `vellum-${raw}`;
186
+ }
187
+
146
188
  /**
147
- * Returns the XDG-compliant path for the platform API token
148
- * (~/.config/vellum/platform-token). This is the canonical location
149
- * shared by the CLI and desktop app.
189
+ * Returns the XDG-compliant path for the platform API token. Resolves to
190
+ * `$XDG_CONFIG_HOME/vellum/platform-token` in production and
191
+ * `$XDG_CONFIG_HOME/vellum-<env>/platform-token` otherwise, matching the
192
+ * Swift client and CLI.
150
193
  */
151
- function getXdgPlatformTokenPath(): string {
194
+ export function getXdgPlatformTokenPath(): string {
152
195
  const configHome =
153
196
  process.env.XDG_CONFIG_HOME?.trim() || join(homedir(), ".config");
154
- return join(configHome, "vellum", "platform-token");
197
+ return join(configHome, getXdgVellumConfigDirName(), "platform-token");
155
198
  }
156
199
 
157
200
  /**
@@ -323,7 +366,7 @@ export function getConversationsDir(): string {
323
366
  return join(getWorkspaceDir(), "conversations");
324
367
  }
325
368
 
326
- /** Returns the workspace path for a prompt file (e.g. IDENTITY.md, SOUL.md, USER.md). */
369
+ /** Returns the workspace path for a prompt file (e.g. IDENTITY.md, SOUL.md). */
327
370
  export function getWorkspacePromptPath(file: string): string {
328
371
  return join(getWorkspaceDir(), file);
329
372
  }
@@ -383,6 +426,7 @@ export function ensureDataDir(): void {
383
426
  join(wsData, "memory"),
384
427
  join(wsData, "memory", "knowledge"),
385
428
  join(wsData, "apps"),
429
+ join(wsData, "attachments"),
386
430
  join(wsData, "interfaces"),
387
431
  join(wsData, "sounds"),
388
432
  ];
@@ -21,6 +21,7 @@ const ANTHROPIC_FAST_MODE_MULTIPLIER = 6;
21
21
  */
22
22
  const PROVIDER_PRICING: Record<string, Record<string, ModelPricing>> = {
23
23
  anthropic: {
24
+ "claude-opus-4-7": { inputPer1M: 5, outputPer1M: 25 },
24
25
  "claude-opus-4-6": { inputPer1M: 5, outputPer1M: 25 },
25
26
  "claude-opus-4": { inputPer1M: 15, outputPer1M: 75 },
26
27
  "claude-sonnet-4": { inputPer1M: 3, outputPer1M: 15 },
@@ -55,6 +56,44 @@ const PROVIDER_PRICING: Record<string, Record<string, ModelPricing>> = {
55
56
  },
56
57
  };
57
58
 
59
+ /**
60
+ * Identify a model ID as an Anthropic model — accepts both the OpenRouter
61
+ * prefix form (`anthropic/claude-opus-4.6`) and the bare Anthropic slug
62
+ * (`claude-opus-4-6`) that `response.model` sometimes carries.
63
+ */
64
+ function isAnthropicModelId(model: string): boolean {
65
+ return model.startsWith("anthropic/") || model.startsWith("claude-");
66
+ }
67
+
68
+ /**
69
+ * Normalize an OpenRouter-style Anthropic model ID for lookup against the
70
+ * Anthropic catalog: strip the `anthropic/` prefix and convert OpenRouter's
71
+ * dot-separated version tokens (`claude-opus-4.6`) to the dash form Anthropic
72
+ * uses natively (`claude-opus-4-6`).
73
+ */
74
+ function normalizeAnthropicModelId(model: string): string {
75
+ const bare = model.startsWith("anthropic/")
76
+ ? model.slice("anthropic/".length)
77
+ : model;
78
+ return bare.replace(/\./g, "-");
79
+ }
80
+
81
+ /**
82
+ * Whether Anthropic's pricing rules (cache-read/write multipliers, fast-mode
83
+ * surcharge) apply for the given provider/model. True for direct Anthropic
84
+ * calls and for Anthropic models routed through OpenRouter — OpenRouter
85
+ * proxies to Anthropic's Messages API, so the usage response carries the
86
+ * same cache and speed fields and is charged at Anthropic's rates.
87
+ */
88
+ export function usesAnthropicPricingRules(
89
+ provider: string,
90
+ model: string,
91
+ ): boolean {
92
+ if (provider === "anthropic") return true;
93
+ if (provider === "openrouter" && isAnthropicModelId(model)) return true;
94
+ return false;
95
+ }
96
+
58
97
  /**
59
98
  * Look up pricing for a model within a provider's catalog.
60
99
  * Tries exact match first, then longest prefix match.
@@ -147,12 +186,14 @@ function getAnthropicCacheWriteTokens(usage: PricingUsage): {
147
186
  */
148
187
  function calculateUsageCost(
149
188
  provider: string,
189
+ model: string,
150
190
  pricing: ModelPricing,
151
191
  usage: PricingUsage,
152
192
  ): number {
193
+ const useAnthropicRules = usesAnthropicPricingRules(provider, model);
153
194
  // Anthropic fast mode: 6x multiplier on base rates (cache multipliers stack on top)
154
195
  const speedMultiplier =
155
- provider === "anthropic" && usage.speed === "fast"
196
+ useAnthropicRules && usage.speed === "fast"
156
197
  ? ANTHROPIC_FAST_MODE_MULTIPLIER
157
198
  : 1;
158
199
  const effectivePricing: ModelPricing = {
@@ -169,7 +210,7 @@ function calculateUsageCost(
169
210
  usage.outputTokens,
170
211
  );
171
212
 
172
- if (provider !== "anthropic") {
213
+ if (!useAnthropicRules) {
173
214
  return (
174
215
  directInputCost +
175
216
  outputCost +
@@ -223,6 +264,27 @@ export function resolvePricingForUsage(
223
264
  model: string,
224
265
  usage: PricingUsage,
225
266
  ): PricingResult {
267
+ // Anthropic models routed through OpenRouter: look up against the Anthropic
268
+ // catalog using the normalized bare slug. OpenRouter bills these calls at
269
+ // Anthropic's rates and the underlying Messages API response includes
270
+ // Anthropic's cache- and speed-metadata fields.
271
+ if (provider === "openrouter" && isAnthropicModelId(model)) {
272
+ const anthropicCatalog = PROVIDER_PRICING.anthropic;
273
+ if (anthropicCatalog) {
274
+ const pricing = findPricing(
275
+ anthropicCatalog,
276
+ normalizeAnthropicModelId(model),
277
+ );
278
+ if (pricing) {
279
+ return {
280
+ estimatedCostUsd: calculateUsageCost(provider, model, pricing, usage),
281
+ pricingStatus: "priced",
282
+ };
283
+ }
284
+ }
285
+ return { estimatedCostUsd: null, pricingStatus: "unpriced" };
286
+ }
287
+
226
288
  const providerCatalog = PROVIDER_PRICING[provider];
227
289
  if (!providerCatalog) {
228
290
  return { estimatedCostUsd: null, pricingStatus: "unpriced" };
@@ -234,7 +296,7 @@ export function resolvePricingForUsage(
234
296
  }
235
297
 
236
298
  return {
237
- estimatedCostUsd: calculateUsageCost(provider, pricing, usage),
299
+ estimatedCostUsd: calculateUsageCost(provider, model, pricing, usage),
238
300
  pricingStatus: "priced",
239
301
  };
240
302
  }
@@ -253,6 +315,7 @@ export function resolvePricingForUsageWithOverrides(
253
315
  return {
254
316
  estimatedCostUsd: calculateUsageCost(
255
317
  provider,
318
+ model,
256
319
  {
257
320
  inputPer1M: bestOverride.inputPer1M,
258
321
  outputPer1M: bestOverride.outputPer1M,
package/src/util/spawn.ts CHANGED
@@ -22,7 +22,7 @@ export function spawnWithTimeout(
22
22
  timeoutMs: number,
23
23
  ): Promise<{ exitCode: number; stdout: string; stderr: string }> {
24
24
  return new Promise((resolve, reject) => {
25
- // Augment PATH so Homebrew-installed tools (ffmpeg, ffprobe, whisper-cpp)
25
+ // Augment PATH so Homebrew-installed tools (ffmpeg, ffprobe)
26
26
  // are found even when the daemon runs as a bundled binary with minimal PATH.
27
27
  const proc = Bun.spawn(cmd, {
28
28
  stdout: "pipe",
@@ -1,6 +1,8 @@
1
+ import { safeStringSlice } from "./unicode.js";
2
+
1
3
  /** Truncate a string to `maxLen` characters, appending `suffix` if truncated. */
2
4
  export function truncate(str: string, maxLen: number, suffix = "..."): string {
3
5
  if (str.length <= maxLen) return str;
4
- if (maxLen < suffix.length) return str.slice(0, maxLen);
5
- return str.slice(0, maxLen - suffix.length) + suffix;
6
+ if (maxLen < suffix.length) return safeStringSlice(str, 0, maxLen);
7
+ return safeStringSlice(str, 0, maxLen - suffix.length) + suffix;
6
8
  }
@@ -0,0 +1,201 @@
1
+ /**
2
+ * UTF-16 surrogate-pair-safe string utilities.
3
+ *
4
+ * JavaScript strings are UTF-16. Code points above the BMP (emoji, many CJK
5
+ * characters, mathematical symbols, etc.) are stored as surrogate pairs — a
6
+ * high surrogate (U+D800–U+DBFF) followed by a low surrogate (U+DC00–U+DFFF).
7
+ * `String.prototype.slice` operates on code units, not code points, so naive
8
+ * slicing can split a pair and leave an orphaned surrogate. Orphaned
9
+ * surrogates are invalid UTF-16 and cause `JSON.stringify` output to be
10
+ * rejected by strict JSON parsers (including Anthropic's API).
11
+ */
12
+
13
+ const HIGH_SURROGATE_START = 0xd800;
14
+ const HIGH_SURROGATE_END = 0xdbff;
15
+ const LOW_SURROGATE_START = 0xdc00;
16
+ const LOW_SURROGATE_END = 0xdfff;
17
+ const REPLACEMENT_CHAR = "\ufffd";
18
+
19
+ function isHighSurrogate(code: number): boolean {
20
+ return code >= HIGH_SURROGATE_START && code <= HIGH_SURROGATE_END;
21
+ }
22
+
23
+ function isLowSurrogate(code: number): boolean {
24
+ return code >= LOW_SURROGATE_START && code <= LOW_SURROGATE_END;
25
+ }
26
+
27
+ /**
28
+ * Slice a string like `String.prototype.slice`, but never cut a UTF-16
29
+ * surrogate pair in half.
30
+ *
31
+ * If the character at `end - 1` is a high surrogate *and* there is more of
32
+ * the string beyond `end` (so cutting would actually orphan it), `end` is
33
+ * decremented by one.
34
+ *
35
+ * If the character at `start` is a low surrogate *and* `start > 0` (so we
36
+ * would be starting mid-pair), `start` is incremented by one.
37
+ *
38
+ * When `end === str.length` we do not touch a trailing high surrogate: if it
39
+ * was already orphaned upstream, repairing it here would silently mutate
40
+ * content. That is the sanitizer's job, not this function's.
41
+ */
42
+ export function safeStringSlice(
43
+ str: string,
44
+ start = 0,
45
+ end: number = str.length,
46
+ ): string {
47
+ let safeStart = Math.max(0, Math.min(str.length, start));
48
+ let safeEnd = Math.max(safeStart, Math.min(str.length, end));
49
+
50
+ if (safeEnd < str.length && safeEnd > safeStart) {
51
+ const lastCode = str.charCodeAt(safeEnd - 1);
52
+ if (isHighSurrogate(lastCode)) {
53
+ safeEnd--;
54
+ }
55
+ }
56
+
57
+ if (safeStart > 0 && safeStart < str.length) {
58
+ const firstCode = str.charCodeAt(safeStart);
59
+ if (isLowSurrogate(firstCode)) {
60
+ safeStart++;
61
+ if (safeStart > safeEnd) safeEnd = safeStart;
62
+ }
63
+ }
64
+
65
+ return str.slice(safeStart, safeEnd);
66
+ }
67
+
68
+ /**
69
+ * Replace every orphaned UTF-16 surrogate in `str` with U+FFFD
70
+ * (REPLACEMENT CHARACTER).
71
+ *
72
+ * Returns the original reference if no changes were needed, so callers can
73
+ * check `result === input` as a cheap "nothing was stripped" signal.
74
+ */
75
+ export function stripOrphanedSurrogates(str: string): string {
76
+ let needsFix = false;
77
+ for (let i = 0; i < str.length; i++) {
78
+ const code = str.charCodeAt(i);
79
+ if (isHighSurrogate(code)) {
80
+ const next = i + 1 < str.length ? str.charCodeAt(i + 1) : 0;
81
+ if (!isLowSurrogate(next)) {
82
+ needsFix = true;
83
+ break;
84
+ }
85
+ i++;
86
+ } else if (isLowSurrogate(code)) {
87
+ needsFix = true;
88
+ break;
89
+ }
90
+ }
91
+ if (!needsFix) return str;
92
+
93
+ let out = "";
94
+ for (let i = 0; i < str.length; i++) {
95
+ const code = str.charCodeAt(i);
96
+ if (isHighSurrogate(code)) {
97
+ const next = i + 1 < str.length ? str.charCodeAt(i + 1) : 0;
98
+ if (isLowSurrogate(next)) {
99
+ out += str[i] + str[i + 1];
100
+ i++;
101
+ } else {
102
+ out += REPLACEMENT_CHAR;
103
+ }
104
+ } else if (isLowSurrogate(code)) {
105
+ out += REPLACEMENT_CHAR;
106
+ } else {
107
+ out += str[i];
108
+ }
109
+ }
110
+ return out;
111
+ }
112
+
113
+ /**
114
+ * Result of a deep sanitization walk. If `changed` is false the caller should
115
+ * use the original reference it passed in; `value` is only meaningful when
116
+ * `changed` is true.
117
+ */
118
+ export interface DeepSanitizeResult<T> {
119
+ value: T;
120
+ changed: boolean;
121
+ /** Count of string values that had at least one orphan replaced. */
122
+ fixedStringCount: number;
123
+ }
124
+
125
+ /**
126
+ * Recursively walk arrays and plain objects, replacing orphaned surrogates in
127
+ * every string value. Non-plain objects (class instances, Date, Buffer, Map,
128
+ * Set, etc.) and non-string primitives are returned unchanged.
129
+ *
130
+ * On the happy path (no orphans found anywhere in the tree) the original
131
+ * reference is returned verbatim — no copies are made.
132
+ */
133
+ export function stripOrphanedSurrogatesDeep<T>(input: T): DeepSanitizeResult<T> {
134
+ let fixedStringCount = 0;
135
+
136
+ const walk = (value: unknown): { value: unknown; changed: boolean } => {
137
+ if (typeof value === "string") {
138
+ const cleaned = stripOrphanedSurrogates(value);
139
+ if (cleaned !== value) {
140
+ fixedStringCount++;
141
+ return { value: cleaned, changed: true };
142
+ }
143
+ return { value, changed: false };
144
+ }
145
+
146
+ if (Array.isArray(value)) {
147
+ let next: unknown[] | null = null;
148
+ for (let i = 0; i < value.length; i++) {
149
+ const result = walk(value[i]);
150
+ if (result.changed && next === null) {
151
+ next = [];
152
+ for (let j = 0; j < i; j++) {
153
+ next.push(value[j]);
154
+ }
155
+ }
156
+ if (next !== null) {
157
+ next.push(result.value);
158
+ }
159
+ }
160
+ return next !== null
161
+ ? { value: next, changed: true }
162
+ : { value, changed: false };
163
+ }
164
+
165
+ if (value != null && typeof value === "object") {
166
+ const proto = Object.getPrototypeOf(value);
167
+ if (proto !== Object.prototype && proto !== null) {
168
+ return { value, changed: false };
169
+ }
170
+ const source = value as Record<string, unknown>;
171
+ const keys = Object.keys(source);
172
+ let next: Record<string, unknown> | null = null;
173
+ for (let i = 0; i < keys.length; i++) {
174
+ const key = keys[i]!;
175
+ const result = walk(source[key]);
176
+ if (result.changed && next === null) {
177
+ next = {};
178
+ for (let j = 0; j < i; j++) {
179
+ const priorKey = keys[j]!;
180
+ next[priorKey] = source[priorKey];
181
+ }
182
+ }
183
+ if (next !== null) {
184
+ next[key] = result.value;
185
+ }
186
+ }
187
+ return next !== null
188
+ ? { value: next, changed: true }
189
+ : { value, changed: false };
190
+ }
191
+
192
+ return { value, changed: false };
193
+ };
194
+
195
+ const result = walk(input);
196
+ return {
197
+ value: result.value as T,
198
+ changed: result.changed,
199
+ fixedStringCount,
200
+ };
201
+ }
package/src/version.ts CHANGED
@@ -1,36 +1,31 @@
1
1
  import { readFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
 
4
+ const DEV_VERSION_SENTINEL = "0.0.0-dev";
5
+
6
+ function readPackageVersion(): string | undefined {
7
+ try {
8
+ const pkgPath = join(
9
+ import.meta.dirname ?? __dirname,
10
+ "..",
11
+ "package.json",
12
+ );
13
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
14
+ if (pkg.version && typeof pkg.version === "string") return pkg.version;
15
+ } catch {
16
+ // package.json missing or unreadable
17
+ }
18
+ return undefined;
19
+ }
20
+
4
21
  function resolveVersion(): string {
5
22
  const envVersion = process.env.APP_VERSION;
6
23
 
7
- // When APP_VERSION is not set, we're in local development — return the dev
8
- // sentinel so Sentry (and similar) classify the session as "development".
9
- if (!envVersion) return "0.0.0-dev";
10
-
11
- // CI sets APP_VERSION to the dev placeholder during builds; resolve it to
12
- // the package.json release version so Sentry gets a meaningful release tag.
13
- if (envVersion === "0.0.0-dev") {
14
- try {
15
- const pkgPath = join(
16
- import.meta.dirname ?? __dirname,
17
- "..",
18
- "package.json",
19
- );
20
- const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
21
- if (pkg.version && typeof pkg.version === "string") return pkg.version;
22
- } catch {
23
- // package.json missing or unreadable
24
- }
25
- return "0.0.0-dev";
26
- }
24
+ if (envVersion && envVersion !== DEV_VERSION_SENTINEL) return envVersion;
27
25
 
28
- return envVersion;
26
+ return readPackageVersion() ?? DEV_VERSION_SENTINEL;
29
27
  }
30
28
 
31
- // Version is embedded at compile time via --define in CI.
32
- // Falls back to "0.0.0-dev" for local development, or resolves the dev
33
- // placeholder to package.json version when explicitly set in CI.
34
29
  export const APP_VERSION: string = resolveVersion();
35
30
 
36
31
  // Commit SHA is embedded at compile time via --define in CI.
@@ -19,6 +19,7 @@ import {
19
19
  insertWatcherEvent,
20
20
  resetStuckWatchers,
21
21
  setWatcherConversationId,
22
+ skipWatcherPoll,
22
23
  updateEventDisposition,
23
24
  } from "./watcher-store.js";
24
25
 
@@ -76,6 +77,28 @@ export async function runWatchersOnce(
76
77
  continue;
77
78
  }
78
79
 
80
+ // Pre-poll credential gate: skip if token is irrecoverably broken.
81
+ // Prevents wasting API calls and burning through circuit breaker
82
+ // attempts on credentials that need manual reauthorization.
83
+ try {
84
+ const { checkCredentialForProvider } =
85
+ await import("../credential-health/credential-health-service.js");
86
+ const health = await checkCredentialForProvider(
87
+ watcher.credentialService,
88
+ );
89
+ if (
90
+ health &&
91
+ (health.status === "revoked" ||
92
+ health.status === "missing_token" ||
93
+ (health.status === "expired" && !health.canAutoRecover))
94
+ ) {
95
+ skipWatcherPoll(watcher.id, `Credential unhealthy: ${health.details}`);
96
+ continue;
97
+ }
98
+ } catch {
99
+ // Non-fatal: proceed with normal poll if health check fails
100
+ }
101
+
79
102
  try {
80
103
  const config = watcher.configJson ? JSON.parse(watcher.configJson) : {};
81
104
 
@@ -221,6 +221,37 @@ export function completeWatcherPoll(
221
221
  db.update(watchers).set(set).where(eq(watchers.id, id)).run();
222
222
  }
223
223
 
224
+ /**
225
+ * Skip a watcher poll: apply backoff to nextPollAt without incrementing
226
+ * consecutiveErrors. Used when a poll is skipped for a recoverable reason
227
+ * (e.g. credential health gate) that should NOT count toward the circuit
228
+ * breaker threshold.
229
+ */
230
+ export function skipWatcherPoll(id: string, reason: string): void {
231
+ const db = getDb();
232
+ const watcher = db.select().from(watchers).where(eq(watchers.id, id)).get();
233
+ if (!watcher) return;
234
+
235
+ const now = Date.now();
236
+ // Use the same backoff formula but based on existing consecutiveErrors
237
+ // (which stays unchanged). Minimum backoff of 30s.
238
+ const backoff = Math.min(
239
+ 30_000 * Math.pow(2, watcher.consecutiveErrors),
240
+ 60 * 60 * 1000,
241
+ );
242
+
243
+ db.update(watchers)
244
+ .set({
245
+ status: "idle",
246
+ lastError: truncate(reason, 2000, ""),
247
+ lastPollAt: now,
248
+ nextPollAt: now + backoff,
249
+ updatedAt: now,
250
+ })
251
+ .where(eq(watchers.id, id))
252
+ .run();
253
+ }
254
+
224
255
  /**
225
256
  * Record a poll error: increment consecutive errors, apply backoff.
226
257
  */
@@ -8,15 +8,21 @@ import {
8
8
  import { homedir } from "node:os";
9
9
  import { join } from "node:path";
10
10
 
11
- import { getDeviceIdBaseDir } from "../../util/device-id.js";
12
11
  import type { WorkspaceMigration } from "./types.js";
13
12
 
13
+ function deviceIdBaseDir(): string {
14
+ const containerized =
15
+ process.env.IS_CONTAINERIZED === "true" ||
16
+ process.env.IS_CONTAINERIZED === "1";
17
+ return containerized ? "/home/assistant" : homedir();
18
+ }
19
+
14
20
  export const seedDeviceIdMigration: WorkspaceMigration = {
15
21
  id: "003-seed-device-id",
16
22
  description:
17
23
  "Seed device.json deviceId from the most recent lockfile installationId for continuity",
18
24
  run(_workspaceDir: string): void {
19
- const base = getDeviceIdBaseDir();
25
+ const base = deviceIdBaseDir();
20
26
  const vellumDir = join(base, ".vellum");
21
27
  const devicePath = join(vellumDir, "device.json");
22
28
 
@@ -109,7 +115,7 @@ export const seedDeviceIdMigration: WorkspaceMigration = {
109
115
  // The forward migration seeds deviceId in ~/.vellum/device.json from the
110
116
  // lockfile. Reverse by removing device.json entirely — getDeviceId() will
111
117
  // generate a fresh one on next startup if needed.
112
- const base = getDeviceIdBaseDir();
118
+ const base = deviceIdBaseDir();
113
119
  const devicePath = join(base, ".vellum", "device.json");
114
120
  if (existsSync(devicePath)) {
115
121
  unlinkSync(devicePath);