@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
@@ -33,6 +33,10 @@ const inflight = new Map<string, Promise<string | null>>();
33
33
 
34
34
  /** Where all cached packages live on disk. */
35
35
  export function getCacheDir(): string {
36
+ // Package cache is intentionally shared across all local assistants to
37
+ // avoid re-downloading the same bundler deps per instance. Resolved
38
+ // against the real home directory so the cache is a single host-wide
39
+ // location regardless of BASE_DATA_DIR / per-instance containers.
36
40
  return join(homedir(), ".vellum", "package-cache");
37
41
  }
38
42
 
@@ -24,7 +24,7 @@ let currentBytes = 0;
24
24
 
25
25
  export function storeAudio(
26
26
  buffer: Buffer,
27
- format: "mp3" | "wav" | "opus",
27
+ format: "mp3" | "wav" | "opus" | "pcm",
28
28
  ): string {
29
29
  evictExpired();
30
30
  // Evict oldest if over capacity
@@ -50,7 +50,7 @@ export interface StreamingAudioHandle {
50
50
  }
51
51
 
52
52
  export function createStreamingEntry(
53
- format: "mp3" | "wav" | "opus",
53
+ format: "mp3" | "wav" | "opus" | "pcm",
54
54
  ): StreamingAudioHandle {
55
55
  evictExpired();
56
56
  const id = randomUUID();
@@ -151,19 +151,25 @@ export function getAudio(id: string): AudioResult | null {
151
151
  removeEntry(id);
152
152
  return null;
153
153
  }
154
- return { type: "buffer", buffer: entry.buffer, contentType: entry.contentType };
154
+ return {
155
+ type: "buffer",
156
+ buffer: entry.buffer,
157
+ contentType: entry.contentType,
158
+ };
155
159
  }
156
160
 
157
161
  // ---------------------------------------------------------------------------
158
162
  // Internal helpers
159
163
  // ---------------------------------------------------------------------------
160
164
 
161
- function contentTypeForFormat(format: "mp3" | "wav" | "opus"): string {
165
+ function contentTypeForFormat(format: "mp3" | "wav" | "opus" | "pcm"): string {
162
166
  return format === "mp3"
163
167
  ? "audio/mpeg"
164
168
  : format === "wav"
165
169
  ? "audio/wav"
166
- : "audio/opus";
170
+ : format === "pcm"
171
+ ? "audio/pcm"
172
+ : "audio/opus";
167
173
  }
168
174
 
169
175
  function mergeChunks(chunks: Uint8Array[]): Uint8Array {
@@ -23,6 +23,8 @@ import { revokeScopedApprovalGrantsForContext } from "../memory/scoped-approval-
23
23
  import { DAEMON_INTERNAL_ASSISTANT_ID } from "../runtime/assistant-scope.js";
24
24
  import { mintDaemonDeliveryToken } from "../runtime/auth/token-service.js";
25
25
  import { computeToolApprovalDigest } from "../security/tool-approval-digest.js";
26
+ import { getCatalogProvider } from "../tts/provider-catalog.js";
27
+ import type { TtsProvider, TtsProviderId } from "../tts/types.js";
26
28
  import { getLogger } from "../util/logger.js";
27
29
  import { createStreamingEntry } from "./audio-store.js";
28
30
  import {
@@ -44,11 +46,11 @@ import {
44
46
  recordCallEvent,
45
47
  updateCallSession,
46
48
  } from "./call-store.js";
49
+ import type { CallTransport } from "./call-transport.js";
47
50
  import { finalizeCall } from "./finalize-call.js";
48
- import { synthesizeWithFishAudio } from "./fish-audio-client.js";
49
51
  import { sendGuardianExpiryNotices } from "./guardian-action-sweep.js";
50
52
  import { dispatchGuardianQuestion } from "./guardian-dispatch.js";
51
- import type { RelayConnection } from "./relay-server.js";
53
+ import { resolveCallTtsProvider } from "./resolve-call-tts-provider.js";
52
54
  import type { PromptSpeakerContext } from "./speaker-identification.js";
53
55
  import { sanitizeForTts } from "./tts-text-sanitizer.js";
54
56
  import {
@@ -61,7 +63,6 @@ import {
61
63
  extractBalancedJson,
62
64
  stripInternalSpeechMarkers,
63
65
  } from "./voice-control-protocol.js";
64
- import { isFishAudioTts } from "./voice-quality.js";
65
66
  import {
66
67
  startVoiceTurn,
67
68
  type VoiceTurnHandle,
@@ -87,7 +88,7 @@ interface PendingGuardianInput {
87
88
 
88
89
  export class CallController {
89
90
  private callSessionId: string;
90
- private relay: RelayConnection;
91
+ private transport: CallTransport;
91
92
  private state: ControllerState = "idle";
92
93
  private abortController: AbortController = new AbortController();
93
94
  private currentTurnHandle: VoiceTurnHandle | null = null;
@@ -139,12 +140,12 @@ export class CallController {
139
140
  * without blocking the caller.
140
141
  */
141
142
  private guardianUnavailableForCall = false;
142
- /** Active Fish Audio session — tracked so interrupt handling can close it. */
143
- private activeFishAbort: AbortController | null = null;
143
+ /** Active synthesized-TTS session — tracked so interrupt handling can close it. */
144
+ private activeSynthesisAbort: AbortController | null = null;
144
145
 
145
146
  constructor(
146
147
  callSessionId: string,
147
- relay: RelayConnection,
148
+ transport: CallTransport,
148
149
  task: string | null,
149
150
  opts?: {
150
151
  broadcast?: (msg: ServerMessage) => void;
@@ -153,7 +154,7 @@ export class CallController {
153
154
  },
154
155
  ) {
155
156
  this.callSessionId = callSessionId;
156
- this.relay = relay;
157
+ this.transport = transport;
157
158
  this.task = task;
158
159
  this.isInbound = !task;
159
160
  this.broadcast = opts?.broadcast;
@@ -344,22 +345,58 @@ export class CallController {
344
345
  await this.runTurn(`[USER_INSTRUCTION: ${instructionText}]`);
345
346
  }
346
347
 
348
+ /**
349
+ * Handle a barge-in attempt from inbound caller audio.
350
+ *
351
+ * Only interrupts the in-flight turn when the assistant is actively
352
+ * speaking. When the controller is idle or still processing (no TTS
353
+ * output yet), the barge-in is ignored — this prevents false
354
+ * interruption on initial inbound media frames that arrive before
355
+ * the assistant has had a chance to produce its first response.
356
+ *
357
+ * @returns `true` if the barge-in was accepted (assistant was speaking),
358
+ * `false` if it was ignored (assistant idle or processing).
359
+ */
360
+ handleBargeIn(): boolean {
361
+ if (this.state !== "speaking") {
362
+ log.debug(
363
+ {
364
+ callSessionId: this.callSessionId,
365
+ state: this.state,
366
+ },
367
+ "Barge-in ignored — assistant not speaking",
368
+ );
369
+ return false;
370
+ }
371
+
372
+ log.info(
373
+ { callSessionId: this.callSessionId },
374
+ "Barge-in accepted — interrupting assistant speech",
375
+ );
376
+ this.handleInterrupt();
377
+ return true;
378
+ }
379
+
347
380
  /**
348
381
  * Handle caller interrupting the assistant's speech.
382
+ *
383
+ * This is the hard interrupt path used for explicit teardown and
384
+ * internal abort scenarios. For barge-in from inbound audio, prefer
385
+ * {@link handleBargeIn} which gates on the speaking state.
349
386
  */
350
387
  handleInterrupt(): void {
351
388
  const wasSpeaking = this.state === "speaking";
352
389
  this.abortCurrentTurn();
353
390
  this.llmRunVersion++;
354
- // Cancel in-flight Fish Audio synthesis on barge-in
355
- if (this.activeFishAbort) {
356
- this.activeFishAbort.abort();
357
- this.activeFishAbort = null;
391
+ // Cancel in-flight synthesized TTS on barge-in
392
+ if (this.activeSynthesisAbort) {
393
+ this.activeSynthesisAbort.abort();
394
+ this.activeSynthesisAbort = null;
358
395
  }
359
396
  // Explicitly terminate the in-progress TTS turn so the relay can
360
397
  // immediately hand control back to the caller after barge-in.
361
398
  if (wasSpeaking) {
362
- this.relay.sendTextToken("", true);
399
+ this.transport.sendTextToken("", true);
363
400
  }
364
401
  this.state = "idle";
365
402
  // Restart silence detection so a barge-in that never yields a
@@ -386,9 +423,9 @@ export class CallController {
386
423
  this.pendingInstructions = [];
387
424
  this.llmRunVersion++;
388
425
  this.abortCurrentTurn();
389
- if (this.activeFishAbort) {
390
- this.activeFishAbort.abort();
391
- this.activeFishAbort = null;
426
+ if (this.activeSynthesisAbort) {
427
+ this.activeSynthesisAbort.abort();
428
+ this.activeSynthesisAbort = null;
392
429
  }
393
430
  this.currentTurnPromise = null;
394
431
  unregisterCallController(this.callSessionId);
@@ -516,7 +553,7 @@ export class CallController {
516
553
  return;
517
554
  }
518
555
  log.error({ err, callSessionId: this.callSessionId }, "Voice turn error");
519
- this.relay.sendTextToken(
556
+ this.transport.sendTextToken(
520
557
  "I'm sorry, I encountered a technical issue. Could you repeat that?",
521
558
  true,
522
559
  );
@@ -536,11 +573,19 @@ export class CallController {
536
573
  runVersion: number,
537
574
  runSignal: AbortSignal,
538
575
  ): Promise<string> {
539
- // Fish Audio TTS routing: when configured, buffer text by sentence
540
- // boundaries and synthesize via Fish Audio instead of streaming text
541
- // tokens for ElevenLabs TTS.
542
- const config = loadConfig();
543
- const useFishAudio = isFishAudioTts(config);
576
+ // Resolve the active TTS provider through the global abstraction.
577
+ // The catalog's callMode determines the call path: synthesized-play
578
+ // providers buffer text, synthesize via provider API, and stream
579
+ // audio chunks to Twilio via play-URL. Native-twilio providers
580
+ // stream text tokens to the relay for Twilio's built-in TTS.
581
+ //
582
+ // When the transport requires WAV (media-stream), request WAV so
583
+ // the audio store entry and any downstream fetch/transcode receives
584
+ // PCM that audioBufferToFrames can convert to mu-law.
585
+ const { provider, useSynthesizedPath, audioFormat } =
586
+ resolveCallTtsProvider({
587
+ preferWav: this.transport.requiresWavAudio,
588
+ });
544
589
 
545
590
  // Buffer incoming tokens so we can strip control markers ([ASK_GUARDIAN:...], [END_CALL])
546
591
  // before they reach TTS. We hold text whenever an unmatched '[' appears, since it
@@ -548,18 +593,18 @@ export class CallController {
548
593
  let ttsBuffer = "";
549
594
  let fullResponseText = "";
550
595
 
551
- // When using Fish Audio, we accumulate all text and synthesize
596
+ // When using the synthesized path, we accumulate all text and synthesize
552
597
  // the complete response at the end of the turn (better prosody).
553
- let fishAudioTextBuffer = "";
598
+ let synthesizedTextBuffer = "";
554
599
 
555
600
  /** Emit a chunk of safe text to the appropriate TTS backend. */
556
601
  const emitSafeChunk = (safeText: string): void => {
557
602
  const cleaned = sanitizeForTts(safeText);
558
603
  if (cleaned.length === 0) return;
559
- if (useFishAudio) {
560
- fishAudioTextBuffer += cleaned;
604
+ if (useSynthesizedPath) {
605
+ synthesizedTextBuffer += cleaned;
561
606
  } else {
562
- this.relay.sendTextToken(cleaned, false);
607
+ this.transport.sendTextToken(cleaned, false);
563
608
  }
564
609
  };
565
610
 
@@ -670,48 +715,29 @@ export class CallController {
670
715
  emitSafeChunk(ttsBuffer);
671
716
  }
672
717
 
673
- // When using Fish Audio, synthesize the complete response text in a
674
- // single REST API call. The full text gives Fish Audio better context
675
- // for prosody and intonation. Audio streams back via chunked transfer
676
- // encoding and is forwarded to Twilio as it arrives.
677
- const sanitizedFishText = sanitizeForTts(fishAudioTextBuffer.trim());
678
- if (useFishAudio && sanitizedFishText.length > 0) {
718
+ // Synthesized-play path: when the active provider supports streaming,
719
+ // synthesize the complete response text via the provider's streaming
720
+ // API. The full text gives the provider better context for prosody
721
+ // and intonation. Audio streams back via chunked transfer encoding
722
+ // and is forwarded to Twilio as it arrives.
723
+ const sanitizedSynthText = sanitizeForTts(synthesizedTextBuffer.trim());
724
+ if (useSynthesizedPath && provider && sanitizedSynthText.length > 0) {
679
725
  if (!this.isCurrentRun(runVersion)) return fullResponseText;
680
- let handle: ReturnType<typeof createStreamingEntry> | null = null;
681
- try {
682
- const format = config.fishAudio.format ?? "mp3";
683
- handle = createStreamingEntry(format as "mp3" | "wav" | "opus");
684
- const baseUrl = getPublicBaseUrl(config);
685
- const url = `${baseUrl}/v1/audio/${handle.audioId}`;
686
- this.relay.sendPlayUrl(url);
687
- const abortController = new AbortController();
688
- this.activeFishAbort = abortController;
689
- await synthesizeWithFishAudio(
690
- sanitizedFishText,
691
- config.fishAudio,
692
- {
693
- onChunk: (chunk) => handle!.push(chunk),
694
- signal: abortController.signal,
695
- },
696
- );
697
- } catch (err) {
698
- if (err instanceof DOMException && err.name === "AbortError") {
699
- log.debug("Fish Audio synthesis aborted (barge-in)");
700
- } else {
701
- log.error({ err }, "Fish Audio synthesis failed — skipping");
702
- }
703
- } finally {
704
- this.activeFishAbort = null;
705
- handle?.finalize();
706
- }
726
+ await this.synthesizeAndStreamAudio(
727
+ provider,
728
+ sanitizedSynthText,
729
+ runVersion,
730
+ audioFormat,
731
+ );
707
732
  }
708
733
 
709
734
  // Signal end of this turn's speech. An empty token with `last: true`
710
735
  // tells ConversationRelay to start listening — it does NOT trigger TTS
711
- // synthesis. This is required even when Fish Audio handled all audio
712
- // playback, because ConversationRelay still needs the end-of-turn signal
713
- // to transition from "assistant speaking" to "caller speaking" state.
714
- this.relay.sendTextToken("", true);
736
+ // synthesis. This is required even when a synthesized provider handled
737
+ // all audio playback, because ConversationRelay still needs the
738
+ // end-of-turn signal to transition from "assistant speaking" to
739
+ // "caller speaking" state.
740
+ this.transport.sendTextToken("", true);
715
741
 
716
742
  // Mark the greeting's first response as awaiting ack
717
743
  if (this.lastSentWasOpener && fullResponseText.length > 0) {
@@ -722,6 +748,133 @@ export class CallController {
722
748
  return fullResponseText;
723
749
  }
724
750
 
751
+ /**
752
+ * Synthesize text via a streaming TTS provider and forward audio chunks
753
+ * to Twilio through the audio store / play-URL mechanism.
754
+ */
755
+ private async synthesizeAndStreamAudio(
756
+ provider: TtsProvider,
757
+ text: string,
758
+ _runVersion: number,
759
+ format: "mp3" | "wav" | "opus" = "mp3",
760
+ ): Promise<void> {
761
+ let handle: ReturnType<typeof createStreamingEntry> | null = null;
762
+ let playUrlSent = false;
763
+ try {
764
+ // When format is WAV (media-stream transport), request raw PCM from
765
+ // the provider so the audio bytes match the store's content-type.
766
+ // Without this, providers like Fish Audio still return mp3 and the
767
+ // downstream mu-law transcoder fails on the format mismatch.
768
+ const outputFormat = format === "wav" ? ("pcm" as const) : undefined;
769
+
770
+ // Use "pcm" as the store format when requesting PCM output so the
771
+ // audio store entry's content-type (audio/pcm) matches the raw PCM
772
+ // bytes providers return. Without this, the store says "audio/wav"
773
+ // but the bytes have no RIFF header, causing audioBufferToFrames to
774
+ // fall through to the wrong decode path.
775
+ const storeFormat = outputFormat ? "pcm" : format;
776
+ handle = createStreamingEntry(storeFormat);
777
+ const config = loadConfig();
778
+ const baseUrl = getPublicBaseUrl(config);
779
+ const url = `${baseUrl}/v1/audio/${handle.audioId}`;
780
+ const sendPlayUrlOnce = (): void => {
781
+ if (playUrlSent) return;
782
+ this.transport.sendPlayUrl(url);
783
+ playUrlSent = true;
784
+ };
785
+
786
+ const abortController = new AbortController();
787
+ this.activeSynthesisAbort = abortController;
788
+
789
+ if (provider.synthesizeStream) {
790
+ let streamedChunk = false;
791
+ await provider.synthesizeStream(
792
+ {
793
+ text,
794
+ useCase: "phone-call",
795
+ outputFormat,
796
+ signal: abortController.signal,
797
+ },
798
+ (chunk) => {
799
+ if (chunk.byteLength === 0) return;
800
+ if (!streamedChunk) {
801
+ sendPlayUrlOnce();
802
+ streamedChunk = true;
803
+ }
804
+ handle!.push(chunk);
805
+ },
806
+ );
807
+
808
+ // Some provider adapters may return a buffer without invoking
809
+ // onChunk. If that happens, do not leave a dangling unspeakable
810
+ // turn; degrade to native token TTS below by treating it as no-audio.
811
+ if (!streamedChunk) {
812
+ throw new Error("Streaming TTS returned no audio chunks");
813
+ }
814
+ } else {
815
+ // Fallback: buffer-oriented synthesis for providers that don't
816
+ // implement streaming (shouldn't normally reach here since
817
+ // useSynthesizedPath is gated on catalog callMode).
818
+ const result = await provider.synthesize({
819
+ text,
820
+ useCase: "phone-call",
821
+ outputFormat,
822
+ signal: abortController.signal,
823
+ });
824
+ if (result.audio.byteLength === 0) {
825
+ throw new Error("Buffer TTS returned an empty audio payload");
826
+ }
827
+ sendPlayUrlOnce();
828
+ handle.push(result.audio);
829
+ }
830
+ } catch (err) {
831
+ if (err instanceof DOMException && err.name === "AbortError") {
832
+ log.debug(
833
+ { provider: provider.id },
834
+ "TTS synthesis aborted (barge-in)",
835
+ );
836
+ } else {
837
+ // Extract error class and code for diagnosable log entries.
838
+ const errName = err instanceof Error ? err.name : String(err);
839
+ const errCode =
840
+ err instanceof Error && "code" in err
841
+ ? (err as Error & { code?: string }).code
842
+ : undefined;
843
+
844
+ // `allowNativeFallback` controls whether the LLM's original
845
+ // response text should be sent via native Twilio token-based
846
+ // TTS when synthesis fails. When false (e.g. Deepgram), the
847
+ // error is re-thrown so the outer catch handler sends a
848
+ // generic recovery message via native TTS instead — the
849
+ // caller still hears *something*, but not the LLM's text
850
+ // rendered in a mismatched voice.
851
+ const catalogEntry = getCatalogProvider(provider.id as TtsProviderId);
852
+ if (!catalogEntry.allowNativeFallback) {
853
+ log.error(
854
+ { err, provider: provider.id, errName, errCode },
855
+ "TTS synthesis failed — native fallback disabled for this provider",
856
+ );
857
+ throw err;
858
+ }
859
+
860
+ log.error(
861
+ { err, provider: provider.id, errName, errCode },
862
+ "TTS synthesis failed — falling back to native token TTS",
863
+ );
864
+ // If synthesis fails before any audio has started, degrade to
865
+ // token-based speech on ConversationRelay so the caller still
866
+ // hears a response instead of silence. This fallback is only
867
+ // used for providers whose catalog entry allows native fallback.
868
+ if (!playUrlSent && !this.transport.requiresWavAudio) {
869
+ this.transport.sendTextToken(text, false);
870
+ }
871
+ }
872
+ } finally {
873
+ this.activeSynthesisAbort = null;
874
+ handle?.finalize();
875
+ }
876
+ }
877
+
725
878
  /**
726
879
  * Handle post-turn marker detection and dispatch: guardian consultation
727
880
  * (ASK_GUARDIAN_APPROVAL / ASK_GUARDIAN), call finalization (END_CALL),
@@ -734,7 +887,9 @@ export class CallController {
734
887
  recordCallEvent(this.callSessionId, "assistant_spoke", {
735
888
  text: responseText,
736
889
  });
737
- const spokenText = sanitizeForTts(stripInternalSpeechMarkers(responseText)).trim();
890
+ const spokenText = sanitizeForTts(
891
+ stripInternalSpeechMarkers(responseText),
892
+ ).trim();
738
893
  if (spokenText.length > 0) {
739
894
  const session = getCallSession(this.callSessionId);
740
895
  if (session) {
@@ -952,7 +1107,7 @@ export class CallController {
952
1107
  currentSession.status !== "cancelled"
953
1108
  : false;
954
1109
 
955
- this.relay.endSession("Call completed");
1110
+ this.transport.endSession("Call completed");
956
1111
  updateCallSession(this.callSessionId, {
957
1112
  status: "completed",
958
1113
  endedAt: Date.now(),
@@ -1200,7 +1355,7 @@ export class CallController {
1200
1355
  { callSessionId: this.callSessionId },
1201
1356
  "Call duration warning",
1202
1357
  );
1203
- this.relay.sendTextToken(
1358
+ this.transport.sendTextToken(
1204
1359
  "Just to let you know, we're running low on time for this call.",
1205
1360
  true,
1206
1361
  );
@@ -1212,7 +1367,7 @@ export class CallController {
1212
1367
  { callSessionId: this.callSessionId },
1213
1368
  "Call duration limit reached",
1214
1369
  );
1215
- this.relay.sendTextToken(
1370
+ this.transport.sendTextToken(
1216
1371
  "I'm sorry, but we've reached the maximum time for this call. Thank you for your time. Goodbye!",
1217
1372
  true,
1218
1373
  );
@@ -1225,7 +1380,7 @@ export class CallController {
1225
1380
  currentSession.status !== "cancelled"
1226
1381
  : false;
1227
1382
 
1228
- this.relay.endSession("Maximum call duration reached");
1383
+ this.transport.endSession("Maximum call duration reached");
1229
1384
  updateCallSession(this.callSessionId, {
1230
1385
  status: "completed",
1231
1386
  endedAt: Date.now(),
@@ -1274,7 +1429,7 @@ export class CallController {
1274
1429
  // inbound access-request wait (relay state).
1275
1430
  if (
1276
1431
  this.pendingGuardianInput ||
1277
- this.relay.getConnectionState() === "awaiting_guardian_decision"
1432
+ this.transport.getConnectionState() === "awaiting_guardian_decision"
1278
1433
  ) {
1279
1434
  log.debug(
1280
1435
  { callSessionId: this.callSessionId },
@@ -1286,7 +1441,7 @@ export class CallController {
1286
1441
  { callSessionId: this.callSessionId },
1287
1442
  "Silence timeout triggered",
1288
1443
  );
1289
- this.relay.sendTextToken("Are you still there?", true);
1444
+ this.transport.sendTextToken("Are you still there?", true);
1290
1445
  }, getSilenceTimeoutMs());
1291
1446
  }
1292
1447
  }
@@ -38,6 +38,7 @@ import {
38
38
  getPendingQuestion,
39
39
  updateCallSession,
40
40
  } from "./call-store.js";
41
+ import { activeMediaStreamSessions } from "./media-stream-server.js";
41
42
  import { activeRelayConnections } from "./relay-server.js";
42
43
  import { getTwilioConfig } from "./twilio-config.js";
43
44
  import { TwilioConversationRelayProvider } from "./twilio-provider.js";
@@ -679,6 +680,14 @@ export async function cancelCall(
679
680
  activeRelayConnections.delete(callSessionId);
680
681
  }
681
682
 
683
+ // End the media-stream session if active
684
+ const mediaStreamSession = activeMediaStreamSessions.get(callSessionId);
685
+ if (mediaStreamSession) {
686
+ mediaStreamSession.getOutput().endSession(reason);
687
+ mediaStreamSession.destroy();
688
+ activeMediaStreamSessions.delete(callSessionId);
689
+ }
690
+
682
691
  // Clean up controller
683
692
  const controller = getCallController(callSessionId);
684
693
  if (controller) {