@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
@@ -0,0 +1,196 @@
1
+ /**
2
+ * Consistency guard: assistant catalog vs client artifact.
3
+ *
4
+ * The assistant-side canonical catalog (`provider-catalog.ts`) and the
5
+ * client-facing artifact (`meta/tts-provider-catalog.json`) must agree on
6
+ * provider IDs, ordering, and display names. This test fails when the two
7
+ * sources drift — for example, if a new provider is added to the assistant
8
+ * catalog but forgotten in the client artifact, or if display names diverge.
9
+ *
10
+ * These checks complement the full parity guard in
11
+ * `assistant/src/__tests__/tts-catalog-parity.test.ts`, which validates
12
+ * credential metadata and deeper structural invariants. This file focuses
13
+ * on fast, local assertions that live next to the catalog source.
14
+ */
15
+
16
+ import { readFileSync } from "node:fs";
17
+ import { resolve } from "node:path";
18
+ import { describe, expect, test } from "bun:test";
19
+
20
+ import {
21
+ getCatalogProvider,
22
+ listCatalogProviderIds,
23
+ listCatalogProviders,
24
+ } from "../provider-catalog.js";
25
+
26
+ // ---------------------------------------------------------------------------
27
+ // Load the client artifact
28
+ // ---------------------------------------------------------------------------
29
+
30
+ interface ClientCatalogProvider {
31
+ id: string;
32
+ displayName: string;
33
+ credentialMode?: string;
34
+ credentialsGuide?: { url: string };
35
+ }
36
+
37
+ interface ClientCatalog {
38
+ version: number;
39
+ providers: ClientCatalogProvider[];
40
+ }
41
+
42
+ /**
43
+ * Resolve the path to `meta/tts-provider-catalog.json` relative to the
44
+ * repo root. The test file lives at
45
+ * `assistant/src/tts/__tests__/provider-catalog-consistency.test.ts`,
46
+ * so the repo root is four directories up.
47
+ */
48
+ const CLIENT_ARTIFACT_PATH = resolve(
49
+ __dirname,
50
+ "../../../../meta/tts-provider-catalog.json",
51
+ );
52
+
53
+ function loadClientArtifact(): ClientCatalog {
54
+ const raw = readFileSync(CLIENT_ARTIFACT_PATH, "utf-8");
55
+ return JSON.parse(raw) as ClientCatalog;
56
+ }
57
+
58
+ // ---------------------------------------------------------------------------
59
+ // Tests
60
+ // ---------------------------------------------------------------------------
61
+
62
+ describe("TTS provider catalog / client artifact consistency", () => {
63
+ const assistantIds = listCatalogProviderIds();
64
+ const clientCatalog = loadClientArtifact();
65
+ const clientIds = clientCatalog.providers.map((p) => p.id);
66
+
67
+ // -- Loadability ----------------------------------------------------------
68
+
69
+ test("client artifact file is loadable and has providers", () => {
70
+ expect(clientCatalog.providers.length).toBeGreaterThan(0);
71
+ });
72
+
73
+ // -- Provider ID parity ---------------------------------------------------
74
+
75
+ test("assistant catalog and client artifact have the same provider IDs (sorted)", () => {
76
+ expect(assistantIds.slice().sort()).toEqual(clientIds.slice().sort());
77
+ });
78
+
79
+ test("no provider IDs in assistant catalog are missing from client artifact", () => {
80
+ const missingFromClient = assistantIds.filter(
81
+ (id) => !clientIds.includes(id),
82
+ );
83
+ if (missingFromClient.length > 0) {
84
+ const message = [
85
+ "Assistant catalog has provider IDs not present in meta/tts-provider-catalog.json.",
86
+ "",
87
+ "Missing from client artifact:",
88
+ ...missingFromClient.map((id) => ` - ${id}`),
89
+ "",
90
+ "Add entries for these providers to meta/tts-provider-catalog.json.",
91
+ ].join("\n");
92
+ expect(missingFromClient, message).toEqual([]);
93
+ }
94
+ });
95
+
96
+ test("no provider IDs in client artifact are missing from assistant catalog", () => {
97
+ const missingFromAssistant = clientIds.filter(
98
+ (id) => !assistantIds.includes(id),
99
+ );
100
+ if (missingFromAssistant.length > 0) {
101
+ const message = [
102
+ "Client artifact (meta/tts-provider-catalog.json) has provider IDs not present in assistant catalog.",
103
+ "",
104
+ "Missing from assistant catalog:",
105
+ ...missingFromAssistant.map((id) => ` - ${id}`),
106
+ "",
107
+ "Add entries for these providers to assistant/src/tts/provider-catalog.ts.",
108
+ ].join("\n");
109
+ expect(missingFromAssistant, message).toEqual([]);
110
+ }
111
+ });
112
+
113
+ // -- Ordering parity ------------------------------------------------------
114
+
115
+ test("assistant catalog and client artifact list providers in the same order", () => {
116
+ expect(clientIds).toEqual([...assistantIds]);
117
+ });
118
+
119
+ // -- Display name parity --------------------------------------------------
120
+
121
+ test("display names match between assistant catalog and client artifact", () => {
122
+ const violations: string[] = [];
123
+ for (const clientEntry of clientCatalog.providers) {
124
+ try {
125
+ const assistantEntry = getCatalogProvider(clientEntry.id as any);
126
+ if (clientEntry.displayName !== assistantEntry.displayName) {
127
+ violations.push(
128
+ `"${clientEntry.id}": client="${clientEntry.displayName}" vs assistant="${assistantEntry.displayName}"`,
129
+ );
130
+ }
131
+ } catch {
132
+ // Unknown ID — covered by provider ID parity tests above.
133
+ }
134
+ }
135
+
136
+ if (violations.length > 0) {
137
+ const message = [
138
+ "Display name mismatch between assistant catalog and client artifact.",
139
+ "",
140
+ "Violations:",
141
+ ...violations.map((v) => ` - ${v}`),
142
+ ].join("\n");
143
+ expect(violations, message).toEqual([]);
144
+ }
145
+ });
146
+
147
+ // -- Structural sanity ----------------------------------------------------
148
+
149
+ test("client artifact version is a positive integer", () => {
150
+ expect(Number.isInteger(clientCatalog.version)).toBe(true);
151
+ expect(clientCatalog.version).toBeGreaterThan(0);
152
+ });
153
+
154
+ test("every client artifact entry has a non-empty id and displayName", () => {
155
+ for (const entry of clientCatalog.providers) {
156
+ expect(entry.id.length).toBeGreaterThan(0);
157
+ expect(entry.displayName.length).toBeGreaterThan(0);
158
+ }
159
+ });
160
+
161
+ test("every client artifact entry has a credentialMode and credentialsGuide", () => {
162
+ const violations: string[] = [];
163
+ for (const entry of clientCatalog.providers) {
164
+ if (
165
+ !entry.credentialMode ||
166
+ !["api-key", "credential"].includes(entry.credentialMode)
167
+ ) {
168
+ violations.push(
169
+ `${entry.id}: missing or invalid credentialMode (got "${entry.credentialMode}")`,
170
+ );
171
+ }
172
+ if (!entry.credentialsGuide?.url) {
173
+ violations.push(`${entry.id}: missing credentialsGuide.url`);
174
+ }
175
+ }
176
+
177
+ if (violations.length > 0) {
178
+ const message = [
179
+ "Client artifact entries have missing required fields.",
180
+ "",
181
+ "Violations:",
182
+ ...violations.map((v) => ` - ${v}`),
183
+ ].join("\n");
184
+ expect(violations, message).toEqual([]);
185
+ }
186
+ });
187
+
188
+ // -- Catalog size guard ---------------------------------------------------
189
+
190
+ test("assistant catalog has at least as many providers as expected", () => {
191
+ const providers = listCatalogProviders();
192
+ // Guard: adding a provider to the catalog without updating this
193
+ // lower-bound forces the developer to acknowledge the growth.
194
+ expect(providers.length).toBeGreaterThanOrEqual(3);
195
+ });
196
+ });
@@ -0,0 +1,183 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import {
4
+ getCatalogProvider,
5
+ listCatalogProviderIds,
6
+ listCatalogProviders,
7
+ } from "../provider-catalog.js";
8
+
9
+ // ---------------------------------------------------------------------------
10
+ // Catalog invariants
11
+ // ---------------------------------------------------------------------------
12
+
13
+ describe("TTS provider catalog", () => {
14
+ const entries = listCatalogProviders();
15
+ const ids = listCatalogProviderIds();
16
+
17
+ // -- Uniqueness -----------------------------------------------------------
18
+
19
+ test("all provider IDs are unique", () => {
20
+ const seen = new Set<string>();
21
+ for (const entry of entries) {
22
+ expect(seen.has(entry.id)).toBe(false);
23
+ seen.add(entry.id);
24
+ }
25
+ });
26
+
27
+ // -- Required fields ------------------------------------------------------
28
+
29
+ test("every entry has a non-empty id", () => {
30
+ for (const entry of entries) {
31
+ expect(entry.id.length).toBeGreaterThan(0);
32
+ }
33
+ });
34
+
35
+ test("every entry has a non-empty displayName", () => {
36
+ for (const entry of entries) {
37
+ expect(entry.displayName.length).toBeGreaterThan(0);
38
+ }
39
+ });
40
+
41
+ test("every entry has a valid callMode", () => {
42
+ const validModes = new Set(["native-twilio", "synthesized-play"]);
43
+ for (const entry of entries) {
44
+ expect(validModes.has(entry.callMode)).toBe(true);
45
+ }
46
+ });
47
+
48
+ test("every entry has a capabilities object with supportedFormats", () => {
49
+ for (const entry of entries) {
50
+ expect(entry.capabilities).toBeDefined();
51
+ expect(Array.isArray(entry.capabilities.supportedFormats)).toBe(true);
52
+ expect(entry.capabilities.supportedFormats.length).toBeGreaterThan(0);
53
+ }
54
+ });
55
+
56
+ test("every entry has at least one secret requirement", () => {
57
+ for (const entry of entries) {
58
+ expect(entry.secretRequirements.length).toBeGreaterThan(0);
59
+ }
60
+ });
61
+
62
+ test("every secret requirement has non-empty fields", () => {
63
+ for (const entry of entries) {
64
+ for (const secret of entry.secretRequirements) {
65
+ expect(secret.credentialStoreKey.length).toBeGreaterThan(0);
66
+ expect(secret.displayName.length).toBeGreaterThan(0);
67
+ expect(secret.setCommand.length).toBeGreaterThan(0);
68
+ }
69
+ }
70
+ });
71
+
72
+ // -- Lookup helpers -------------------------------------------------------
73
+
74
+ test("listCatalogProviderIds returns IDs matching listCatalogProviders", () => {
75
+ expect(ids).toEqual(entries.map((e) => e.id));
76
+ });
77
+
78
+ test("getCatalogProvider returns the correct entry for each known ID", () => {
79
+ for (const entry of entries) {
80
+ const resolved = getCatalogProvider(entry.id);
81
+ expect(resolved).toBe(entry);
82
+ }
83
+ });
84
+
85
+ test("getCatalogProvider throws for unknown provider ID", () => {
86
+ expect(() => getCatalogProvider("nonexistent-provider")).toThrow(
87
+ /Unknown TTS provider "nonexistent-provider"/,
88
+ );
89
+ });
90
+
91
+ test("getCatalogProvider error message includes known provider IDs", () => {
92
+ try {
93
+ getCatalogProvider("nonexistent-provider");
94
+ throw new Error("Expected getCatalogProvider to throw");
95
+ } catch (err) {
96
+ const msg = (err as Error).message;
97
+ for (const id of ids) {
98
+ expect(msg).toContain(id);
99
+ }
100
+ }
101
+ });
102
+ });
103
+
104
+ // ---------------------------------------------------------------------------
105
+ // Provider-specific assertions
106
+ // ---------------------------------------------------------------------------
107
+
108
+ describe("ElevenLabs catalog entry", () => {
109
+ const entry = getCatalogProvider("elevenlabs");
110
+
111
+ test("uses native-twilio call mode", () => {
112
+ expect(entry.callMode).toBe("native-twilio");
113
+ });
114
+
115
+ test("does not support streaming", () => {
116
+ expect(entry.capabilities.supportsStreaming).toBe(false);
117
+ });
118
+
119
+ test("supports mp3 format", () => {
120
+ expect(entry.capabilities.supportedFormats).toContain("mp3");
121
+ });
122
+
123
+ test("requires a credential stored under 'credential/elevenlabs/api_key'", () => {
124
+ const apiKeySecret = entry.secretRequirements.find(
125
+ (s) => s.credentialStoreKey === "credential/elevenlabs/api_key",
126
+ );
127
+ expect(apiKeySecret).toBeDefined();
128
+ expect(apiKeySecret!.displayName).toContain("ElevenLabs");
129
+ });
130
+ });
131
+
132
+ describe("Fish Audio catalog entry", () => {
133
+ const entry = getCatalogProvider("fish-audio");
134
+
135
+ test("uses synthesized-play call mode", () => {
136
+ expect(entry.callMode).toBe("synthesized-play");
137
+ });
138
+
139
+ test("supports streaming", () => {
140
+ expect(entry.capabilities.supportsStreaming).toBe(true);
141
+ });
142
+
143
+ test("supports mp3, wav, and opus formats", () => {
144
+ expect(entry.capabilities.supportedFormats).toContain("mp3");
145
+ expect(entry.capabilities.supportedFormats).toContain("wav");
146
+ expect(entry.capabilities.supportedFormats).toContain("opus");
147
+ });
148
+
149
+ test("requires an API key stored under 'credential/fish-audio/api_key'", () => {
150
+ const apiKeySecret = entry.secretRequirements.find(
151
+ (s) => s.credentialStoreKey === "credential/fish-audio/api_key",
152
+ );
153
+ expect(apiKeySecret).toBeDefined();
154
+ expect(apiKeySecret!.displayName).toContain("Fish Audio");
155
+ });
156
+ });
157
+
158
+ describe("Deepgram catalog entry", () => {
159
+ const entry = getCatalogProvider("deepgram");
160
+
161
+ test("uses synthesized-play call mode", () => {
162
+ expect(entry.callMode).toBe("synthesized-play");
163
+ });
164
+
165
+ test("does not support streaming", () => {
166
+ expect(entry.capabilities.supportsStreaming).toBe(false);
167
+ });
168
+
169
+ test("supports mp3, wav, and opus formats", () => {
170
+ expect(entry.capabilities.supportedFormats).toContain("mp3");
171
+ expect(entry.capabilities.supportedFormats).toContain("wav");
172
+ expect(entry.capabilities.supportedFormats).toContain("opus");
173
+ });
174
+
175
+ test("requires a bare API key stored under 'deepgram'", () => {
176
+ const apiKeySecret = entry.secretRequirements.find(
177
+ (s) => s.credentialStoreKey === "deepgram",
178
+ );
179
+ expect(apiKeySecret).toBeDefined();
180
+ expect(apiKeySecret!.displayName).toContain("Deepgram");
181
+ expect(apiKeySecret!.setCommand).toContain("assistant keys set deepgram");
182
+ });
183
+ });
@@ -0,0 +1,90 @@
1
+ import { afterEach, describe, expect, test } from "bun:test";
2
+
3
+ import {
4
+ _resetTtsProviderRegistry,
5
+ getTtsProvider,
6
+ listTtsProviders,
7
+ registerTtsProvider,
8
+ } from "../provider-registry.js";
9
+ import type { TtsProvider } from "../types.js";
10
+
11
+ // ---------------------------------------------------------------------------
12
+ // Helpers
13
+ // ---------------------------------------------------------------------------
14
+
15
+ function stubProvider(id: string): TtsProvider {
16
+ return {
17
+ id,
18
+ capabilities: {
19
+ supportsStreaming: false,
20
+ supportedFormats: ["mp3"],
21
+ },
22
+ async synthesize() {
23
+ return { audio: Buffer.alloc(0), contentType: "audio/mpeg" };
24
+ },
25
+ };
26
+ }
27
+
28
+ // ---------------------------------------------------------------------------
29
+ // Tests
30
+ // ---------------------------------------------------------------------------
31
+
32
+ describe("TTS provider registry", () => {
33
+ afterEach(() => {
34
+ _resetTtsProviderRegistry();
35
+ });
36
+
37
+ // -- Registration & lookup ------------------------------------------------
38
+
39
+ test("registers and resolves a provider by ID", () => {
40
+ const provider = stubProvider("test-provider");
41
+ registerTtsProvider(provider);
42
+
43
+ const resolved = getTtsProvider("test-provider");
44
+ expect(resolved).toBe(provider);
45
+ });
46
+
47
+ test("rejects duplicate registration with an explicit error", () => {
48
+ registerTtsProvider(stubProvider("dup"));
49
+
50
+ expect(() => registerTtsProvider(stubProvider("dup"))).toThrow(
51
+ /already registered/,
52
+ );
53
+ });
54
+
55
+ // -- Unknown provider errors ----------------------------------------------
56
+
57
+ test("throws for unknown provider ID when registry is empty", () => {
58
+ expect(() => getTtsProvider("nope")).toThrow(/Unknown TTS provider "nope"/);
59
+ });
60
+
61
+ test("throws for unknown provider ID and lists known providers", () => {
62
+ registerTtsProvider(stubProvider("alpha"));
63
+ registerTtsProvider(stubProvider("beta"));
64
+
65
+ try {
66
+ getTtsProvider("gamma");
67
+ throw new Error("Expected getTtsProvider to throw");
68
+ } catch (err) {
69
+ const msg = (err as Error).message;
70
+ expect(msg).toMatch(/Unknown TTS provider "gamma"/);
71
+ expect(msg).toMatch(/alpha/);
72
+ expect(msg).toMatch(/beta/);
73
+ }
74
+ });
75
+
76
+ // -- Listing order --------------------------------------------------------
77
+
78
+ test("listTtsProviders returns providers in registration order", () => {
79
+ registerTtsProvider(stubProvider("charlie"));
80
+ registerTtsProvider(stubProvider("alpha"));
81
+ registerTtsProvider(stubProvider("bravo"));
82
+
83
+ const ids = listTtsProviders().map((p) => p.id);
84
+ expect(ids).toEqual(["charlie", "alpha", "bravo"]);
85
+ });
86
+
87
+ test("listTtsProviders returns empty array when nothing is registered", () => {
88
+ expect(listTtsProviders()).toEqual([]);
89
+ });
90
+ });
@@ -0,0 +1,201 @@
1
+ /**
2
+ * Canonical TTS provider catalog.
3
+ *
4
+ * This module is the **single source of truth** for provider IDs and
5
+ * provider-level metadata on the assistant side. Every TTS provider that
6
+ * the system knows about is declared here — downstream modules query the
7
+ * catalog via {@link getCatalogProvider}, {@link listCatalogProviders}, or
8
+ * {@link listCatalogProviderIds} instead of hardcoding provider IDs.
9
+ *
10
+ * Adding a new TTS provider starts here: create a new
11
+ * {@link TtsProviderCatalogEntry} and append it to {@link CATALOG}.
12
+ */
13
+
14
+ import type { TtsCallMode, TtsProviderId } from "./types.js";
15
+
16
+ export type { TtsCallMode } from "./types.js";
17
+
18
+ // ---------------------------------------------------------------------------
19
+ // Catalog entry model
20
+ // ---------------------------------------------------------------------------
21
+
22
+ /**
23
+ * Metadata about a secret (API key / credential) required by a provider.
24
+ */
25
+ export interface TtsProviderSecretRequirement {
26
+ /**
27
+ * The key used to retrieve this secret from the secure credential store.
28
+ *
29
+ * For simple keys this is a bare name (e.g. `"elevenlabs"`).
30
+ * For namespaced keys this follows the `credential/{service}/{field}`
31
+ * convention (e.g. `"credential/fish-audio/api_key"`).
32
+ */
33
+ readonly credentialStoreKey: string;
34
+
35
+ /** Human-readable label shown in settings UI and error messages. */
36
+ readonly displayName: string;
37
+
38
+ /**
39
+ * CLI command the user can run to store this secret.
40
+ *
41
+ * Shown in error messages when the key is missing.
42
+ */
43
+ readonly setCommand: string;
44
+ }
45
+
46
+ /**
47
+ * Provider-level capabilities metadata surfaced by the catalog.
48
+ *
49
+ * These describe static, provider-wide traits — they do not change based
50
+ * on runtime configuration or per-request parameters.
51
+ */
52
+ export interface TtsProviderCatalogCapabilities {
53
+ /** Whether the provider supports chunk-level streaming synthesis. */
54
+ readonly supportsStreaming: boolean;
55
+
56
+ /** Audio formats the provider can produce (e.g. `["mp3"]`). */
57
+ readonly supportedFormats: readonly string[];
58
+ }
59
+
60
+ /**
61
+ * A single entry in the TTS provider catalog.
62
+ *
63
+ * Captures everything the system needs to know about a provider at a
64
+ * metadata level — identity, display name, telephony call mode,
65
+ * capabilities, and secret requirements.
66
+ */
67
+ export interface TtsProviderCatalogEntry {
68
+ /** Unique provider identifier matching {@link TtsProviderId}. */
69
+ readonly id: TtsProviderId;
70
+
71
+ /** Human-readable name for display in settings UI and logs. */
72
+ readonly displayName: string;
73
+
74
+ /** How this provider integrates with the telephony call path. */
75
+ readonly callMode: TtsCallMode;
76
+
77
+ /**
78
+ * Whether the call path may fall back to native Twilio token-based
79
+ * TTS when synthesized audio fails.
80
+ *
81
+ * Providers with `callMode: "native-twilio"` always set this to `true`.
82
+ * Synthesized-play providers that also work through Twilio's built-in
83
+ * TTS (e.g. Fish Audio) set this to `true` so callers still hear
84
+ * a response if synthesis fails. Providers that have **no** native
85
+ * Twilio integration (e.g. Deepgram) set this to `false` — a synthesis
86
+ * failure must propagate so the outer error handler can surface a
87
+ * user-facing recovery message.
88
+ */
89
+ readonly allowNativeFallback: boolean;
90
+
91
+ /** Static provider-level capabilities. */
92
+ readonly capabilities: Readonly<TtsProviderCatalogCapabilities>;
93
+
94
+ /** Secrets the provider requires to function. */
95
+ readonly secretRequirements: readonly Readonly<TtsProviderSecretRequirement>[];
96
+ }
97
+
98
+ // ---------------------------------------------------------------------------
99
+ // Catalog data
100
+ // ---------------------------------------------------------------------------
101
+
102
+ /**
103
+ * The authoritative list of TTS providers.
104
+ *
105
+ * Order is significant only for display purposes (e.g. settings dropdowns).
106
+ */
107
+ const CATALOG: readonly TtsProviderCatalogEntry[] = [
108
+ {
109
+ id: "elevenlabs",
110
+ displayName: "ElevenLabs",
111
+ callMode: "native-twilio",
112
+ allowNativeFallback: true,
113
+ capabilities: {
114
+ supportsStreaming: false,
115
+ supportedFormats: ["mp3"],
116
+ },
117
+ secretRequirements: [
118
+ {
119
+ credentialStoreKey: "credential/elevenlabs/api_key",
120
+ displayName: "ElevenLabs API Key",
121
+ setCommand:
122
+ "assistant credentials set --service elevenlabs --field api_key <key>",
123
+ },
124
+ ],
125
+ },
126
+ {
127
+ id: "fish-audio",
128
+ displayName: "Fish Audio",
129
+ callMode: "synthesized-play",
130
+ allowNativeFallback: true,
131
+ capabilities: {
132
+ supportsStreaming: true,
133
+ supportedFormats: ["mp3", "wav", "opus"],
134
+ },
135
+ secretRequirements: [
136
+ {
137
+ credentialStoreKey: "credential/fish-audio/api_key",
138
+ displayName: "Fish Audio API Key",
139
+ setCommand:
140
+ "assistant credentials set --service fish-audio --field api_key <key>",
141
+ },
142
+ ],
143
+ },
144
+ {
145
+ id: "deepgram",
146
+ displayName: "Deepgram",
147
+ callMode: "synthesized-play",
148
+ allowNativeFallback: false,
149
+ capabilities: {
150
+ supportsStreaming: false,
151
+ supportedFormats: ["mp3", "wav", "opus"],
152
+ },
153
+ secretRequirements: [
154
+ {
155
+ credentialStoreKey: "deepgram",
156
+ displayName: "Deepgram API Key",
157
+ setCommand: "assistant keys set deepgram <key>",
158
+ },
159
+ ],
160
+ },
161
+ ] as const;
162
+
163
+ /** Index for O(1) lookup by provider ID. */
164
+ const catalogById = new Map<TtsProviderId, TtsProviderCatalogEntry>(
165
+ CATALOG.map((entry) => [entry.id, entry]),
166
+ );
167
+
168
+ // ---------------------------------------------------------------------------
169
+ // Public API
170
+ // ---------------------------------------------------------------------------
171
+
172
+ /**
173
+ * List all catalog providers in display order.
174
+ */
175
+ export function listCatalogProviders(): readonly TtsProviderCatalogEntry[] {
176
+ return CATALOG;
177
+ }
178
+
179
+ /**
180
+ * List all known provider IDs in display order.
181
+ */
182
+ export function listCatalogProviderIds(): TtsProviderId[] {
183
+ return CATALOG.map((entry) => entry.id);
184
+ }
185
+
186
+ /**
187
+ * Look up a catalog entry by provider ID.
188
+ *
189
+ * @throws if the ID is not in the catalog.
190
+ */
191
+ export function getCatalogProvider(id: TtsProviderId): TtsProviderCatalogEntry {
192
+ const entry = catalogById.get(id);
193
+ if (!entry) {
194
+ const known = listCatalogProviderIds();
195
+ throw new Error(
196
+ `Unknown TTS provider "${id}" is not in the catalog. ` +
197
+ `Known providers: ${known.join(", ")}`,
198
+ );
199
+ }
200
+ return entry;
201
+ }