@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,392 @@
1
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
2
+
3
+ import { SttError } from "../types.js";
4
+
5
+ // ---------------------------------------------------------------------------
6
+ // Module mocks — must precede dynamic imports
7
+ // ---------------------------------------------------------------------------
8
+
9
+ let mockWhisperTranscribeResult: { text: string } = { text: "" };
10
+ let mockWhisperTranscribeError: Error | null = null;
11
+
12
+ mock.module("../../providers/speech-to-text/openai-whisper.js", () => ({
13
+ OpenAIWhisperProvider: class MockWhisperProvider {
14
+ constructor(_apiKey: string) {}
15
+ async transcribe(_audio: Buffer, _mimeType: string, _signal?: AbortSignal) {
16
+ if (mockWhisperTranscribeError) throw mockWhisperTranscribeError;
17
+ return mockWhisperTranscribeResult;
18
+ }
19
+ },
20
+ }));
21
+
22
+ let mockDeepgramTranscribeResult: { text: string } = { text: "" };
23
+ let mockDeepgramTranscribeError: Error | null = null;
24
+
25
+ mock.module("../../providers/speech-to-text/deepgram.js", () => ({
26
+ DeepgramProvider: class MockDeepgramProvider {
27
+ constructor(_apiKey: string) {}
28
+ async transcribe(_audio: Buffer, _mimeType: string, _signal?: AbortSignal) {
29
+ if (mockDeepgramTranscribeError) throw mockDeepgramTranscribeError;
30
+ return mockDeepgramTranscribeResult;
31
+ }
32
+ },
33
+ }));
34
+
35
+ let mockGeminiTranscribeResult: { text: string } = { text: "" };
36
+ let mockGeminiTranscribeError: Error | null = null;
37
+
38
+ mock.module("../../providers/speech-to-text/google-gemini.js", () => ({
39
+ GoogleGeminiProvider: class MockGoogleGeminiProvider {
40
+ constructor(_apiKey: string) {}
41
+ async transcribe(_audio: Buffer, _mimeType: string, _signal?: AbortSignal) {
42
+ if (mockGeminiTranscribeError) throw mockGeminiTranscribeError;
43
+ return mockGeminiTranscribeResult;
44
+ }
45
+ },
46
+ }));
47
+
48
+ // Dynamic import so mocks are active when the module loads.
49
+ const { createDaemonBatchTranscriber, normalizeSttError } =
50
+ await import("../daemon-batch-transcriber.js");
51
+
52
+ // ---------------------------------------------------------------------------
53
+ // Tests
54
+ // ---------------------------------------------------------------------------
55
+
56
+ describe("createDaemonBatchTranscriber", () => {
57
+ beforeEach(() => {
58
+ mockWhisperTranscribeResult = { text: "" };
59
+ mockWhisperTranscribeError = null;
60
+ mockDeepgramTranscribeResult = { text: "" };
61
+ mockDeepgramTranscribeError = null;
62
+ mockGeminiTranscribeResult = { text: "" };
63
+ mockGeminiTranscribeError = null;
64
+ });
65
+
66
+ // -------------------------------------------------------------------------
67
+ // Credential resolution
68
+ // -------------------------------------------------------------------------
69
+
70
+ test("returns null when no API key is provided", () => {
71
+ expect(createDaemonBatchTranscriber(null, "openai-whisper")).toBeNull();
72
+ expect(
73
+ createDaemonBatchTranscriber(undefined, "openai-whisper"),
74
+ ).toBeNull();
75
+ });
76
+
77
+ test("returns a BatchTranscriber when API key is present", () => {
78
+ const transcriber = createDaemonBatchTranscriber(
79
+ "sk-test-key",
80
+ "openai-whisper",
81
+ );
82
+ expect(transcriber).not.toBeNull();
83
+ });
84
+
85
+ // -------------------------------------------------------------------------
86
+ // Provider identity — Whisper
87
+ // -------------------------------------------------------------------------
88
+
89
+ test("reports providerId as openai-whisper when created with openai-whisper", () => {
90
+ const transcriber = createDaemonBatchTranscriber(
91
+ "sk-test-key",
92
+ "openai-whisper",
93
+ );
94
+ expect(transcriber!.providerId).toBe("openai-whisper");
95
+ });
96
+
97
+ test("reports boundaryId as daemon-batch", () => {
98
+ const transcriber = createDaemonBatchTranscriber(
99
+ "sk-test-key",
100
+ "openai-whisper",
101
+ );
102
+ expect(transcriber!.boundaryId).toBe("daemon-batch");
103
+ });
104
+
105
+ // -------------------------------------------------------------------------
106
+ // Successful transcription — Whisper
107
+ // -------------------------------------------------------------------------
108
+
109
+ test("delegates transcription to the Whisper provider", async () => {
110
+ mockWhisperTranscribeResult = { text: "Hello from Whisper" };
111
+
112
+ const transcriber = createDaemonBatchTranscriber(
113
+ "sk-test-key",
114
+ "openai-whisper",
115
+ );
116
+ const result = await transcriber!.transcribe({
117
+ audio: Buffer.from("fake-audio"),
118
+ mimeType: "audio/ogg",
119
+ });
120
+
121
+ expect(result).toEqual({ text: "Hello from Whisper" });
122
+ });
123
+
124
+ // -------------------------------------------------------------------------
125
+ // Error propagation — raw provider errors pass through unchanged so that
126
+ // legacy callers (e.g. transcribe-audio.ts) can still detect AbortError.
127
+ // -------------------------------------------------------------------------
128
+
129
+ test("propagates AbortError unchanged", async () => {
130
+ const original = new DOMException(
131
+ "The operation was aborted",
132
+ "AbortError",
133
+ );
134
+ mockWhisperTranscribeError = original;
135
+
136
+ const transcriber = createDaemonBatchTranscriber(
137
+ "sk-test-key",
138
+ "openai-whisper",
139
+ );
140
+
141
+ try {
142
+ await transcriber!.transcribe({
143
+ audio: Buffer.from("audio"),
144
+ mimeType: "audio/wav",
145
+ });
146
+ expect.unreachable("should have thrown");
147
+ } catch (err) {
148
+ expect(err).toBe(original);
149
+ expect((err as Error).name).toBe("AbortError");
150
+ }
151
+ });
152
+
153
+ test("propagates generic errors unchanged", async () => {
154
+ const original = new Error("Something went wrong");
155
+ mockWhisperTranscribeError = original;
156
+
157
+ const transcriber = createDaemonBatchTranscriber(
158
+ "sk-test-key",
159
+ "openai-whisper",
160
+ );
161
+
162
+ try {
163
+ await transcriber!.transcribe({
164
+ audio: Buffer.from("audio"),
165
+ mimeType: "audio/wav",
166
+ });
167
+ expect.unreachable("should have thrown");
168
+ } catch (err) {
169
+ expect(err).toBe(original);
170
+ }
171
+ });
172
+
173
+ // -------------------------------------------------------------------------
174
+ // Provider identity — Deepgram
175
+ // -------------------------------------------------------------------------
176
+
177
+ test("reports providerId as deepgram when created with deepgram", () => {
178
+ const transcriber = createDaemonBatchTranscriber("dg-test-key", "deepgram");
179
+ expect(transcriber).not.toBeNull();
180
+ expect(transcriber!.providerId).toBe("deepgram");
181
+ });
182
+
183
+ test("reports boundaryId as daemon-batch for deepgram", () => {
184
+ const transcriber = createDaemonBatchTranscriber("dg-test-key", "deepgram");
185
+ expect(transcriber!.boundaryId).toBe("daemon-batch");
186
+ });
187
+
188
+ // -------------------------------------------------------------------------
189
+ // Successful transcription — Deepgram
190
+ // -------------------------------------------------------------------------
191
+
192
+ test("delegates transcription to the Deepgram provider", async () => {
193
+ mockDeepgramTranscribeResult = { text: "Hello from Deepgram" };
194
+
195
+ const transcriber = createDaemonBatchTranscriber("dg-test-key", "deepgram");
196
+ const result = await transcriber!.transcribe({
197
+ audio: Buffer.from("fake-audio"),
198
+ mimeType: "audio/ogg",
199
+ });
200
+
201
+ expect(result).toEqual({ text: "Hello from Deepgram" });
202
+ });
203
+
204
+ // -------------------------------------------------------------------------
205
+ // Error propagation — Deepgram
206
+ // -------------------------------------------------------------------------
207
+
208
+ test("propagates Deepgram errors unchanged", async () => {
209
+ const original = new Error("Deepgram API error (401): Invalid credentials");
210
+ mockDeepgramTranscribeError = original;
211
+
212
+ const transcriber = createDaemonBatchTranscriber("dg-test-key", "deepgram");
213
+
214
+ try {
215
+ await transcriber!.transcribe({
216
+ audio: Buffer.from("audio"),
217
+ mimeType: "audio/wav",
218
+ });
219
+ expect.unreachable("should have thrown");
220
+ } catch (err) {
221
+ expect(err).toBe(original);
222
+ }
223
+ });
224
+
225
+ // -------------------------------------------------------------------------
226
+ // Null on missing key — Deepgram
227
+ // -------------------------------------------------------------------------
228
+
229
+ test("returns null for deepgram when no API key is provided", () => {
230
+ expect(createDaemonBatchTranscriber(null, "deepgram")).toBeNull();
231
+ expect(createDaemonBatchTranscriber(undefined, "deepgram")).toBeNull();
232
+ });
233
+
234
+ // -------------------------------------------------------------------------
235
+ // Provider identity — Google Gemini
236
+ // -------------------------------------------------------------------------
237
+
238
+ test("reports providerId as google-gemini when created with google-gemini", () => {
239
+ const transcriber = createDaemonBatchTranscriber(
240
+ "gemini-test-key",
241
+ "google-gemini",
242
+ );
243
+ expect(transcriber).not.toBeNull();
244
+ expect(transcriber!.providerId).toBe("google-gemini");
245
+ });
246
+
247
+ test("reports boundaryId as daemon-batch for google-gemini", () => {
248
+ const transcriber = createDaemonBatchTranscriber(
249
+ "gemini-test-key",
250
+ "google-gemini",
251
+ );
252
+ expect(transcriber!.boundaryId).toBe("daemon-batch");
253
+ });
254
+
255
+ // -------------------------------------------------------------------------
256
+ // Successful transcription — Google Gemini
257
+ // -------------------------------------------------------------------------
258
+
259
+ test("delegates transcription to the Google Gemini provider", async () => {
260
+ mockGeminiTranscribeResult = { text: "Hello from Gemini" };
261
+
262
+ const transcriber = createDaemonBatchTranscriber(
263
+ "gemini-test-key",
264
+ "google-gemini",
265
+ );
266
+ const result = await transcriber!.transcribe({
267
+ audio: Buffer.from("fake-audio"),
268
+ mimeType: "audio/ogg",
269
+ });
270
+
271
+ expect(result).toEqual({ text: "Hello from Gemini" });
272
+ });
273
+
274
+ // -------------------------------------------------------------------------
275
+ // Error propagation — Google Gemini
276
+ // -------------------------------------------------------------------------
277
+
278
+ test("propagates Google Gemini errors unchanged", async () => {
279
+ const original = new Error(
280
+ "Google Gemini API error (401): Invalid credentials",
281
+ );
282
+ mockGeminiTranscribeError = original;
283
+
284
+ const transcriber = createDaemonBatchTranscriber(
285
+ "gemini-test-key",
286
+ "google-gemini",
287
+ );
288
+
289
+ try {
290
+ await transcriber!.transcribe({
291
+ audio: Buffer.from("audio"),
292
+ mimeType: "audio/wav",
293
+ });
294
+ expect.unreachable("should have thrown");
295
+ } catch (err) {
296
+ expect(err).toBe(original);
297
+ }
298
+ });
299
+
300
+ // -------------------------------------------------------------------------
301
+ // Null on missing key — Google Gemini
302
+ // -------------------------------------------------------------------------
303
+
304
+ test("returns null for google-gemini when no API key is provided", () => {
305
+ expect(createDaemonBatchTranscriber(null, "google-gemini")).toBeNull();
306
+ expect(createDaemonBatchTranscriber(undefined, "google-gemini")).toBeNull();
307
+ });
308
+ });
309
+
310
+ // ---------------------------------------------------------------------------
311
+ // normalizeSttError — callers use this explicitly when they need categories
312
+ // ---------------------------------------------------------------------------
313
+
314
+ describe("normalizeSttError", () => {
315
+ test("normalizes AbortError to timeout category", () => {
316
+ const err = new DOMException("The operation was aborted", "AbortError");
317
+ const result = normalizeSttError(err);
318
+ expect(result).toBeInstanceOf(SttError);
319
+ expect(result.category).toBe("timeout");
320
+ });
321
+
322
+ test("normalizes 401 errors to auth category", () => {
323
+ const result = normalizeSttError(
324
+ new Error("Whisper API error (401): Unauthorized"),
325
+ );
326
+ expect(result).toBeInstanceOf(SttError);
327
+ expect(result.category).toBe("auth");
328
+ });
329
+
330
+ test("normalizes 403 errors to auth category", () => {
331
+ const result = normalizeSttError(
332
+ new Error("Whisper API error (403): Forbidden"),
333
+ );
334
+ expect(result).toBeInstanceOf(SttError);
335
+ expect(result.category).toBe("auth");
336
+ });
337
+
338
+ test("normalizes 429 errors to rate-limit category", () => {
339
+ const result = normalizeSttError(
340
+ new Error("Whisper API error (429): Too Many Requests"),
341
+ );
342
+ expect(result).toBeInstanceOf(SttError);
343
+ expect(result.category).toBe("rate-limit");
344
+ });
345
+
346
+ test("normalizes rate limit text to rate-limit category", () => {
347
+ const result = normalizeSttError(
348
+ new Error("Request rate-limited by provider"),
349
+ );
350
+ expect(result).toBeInstanceOf(SttError);
351
+ expect(result.category).toBe("rate-limit");
352
+ });
353
+
354
+ test("normalizes 400 audio errors to invalid-audio category", () => {
355
+ const result = normalizeSttError(
356
+ new Error("Whisper API error (400): Invalid audio format"),
357
+ );
358
+ expect(result).toBeInstanceOf(SttError);
359
+ expect(result.category).toBe("invalid-audio");
360
+ });
361
+
362
+ test("normalizes unknown errors to provider-error category", () => {
363
+ const result = normalizeSttError(new Error("Something went wrong"));
364
+ expect(result).toBeInstanceOf(SttError);
365
+ expect(result.category).toBe("provider-error");
366
+ });
367
+
368
+ test("passes through SttError instances without re-wrapping", () => {
369
+ const original = new SttError("auth", "Custom auth failure");
370
+ const result = normalizeSttError(original);
371
+ expect(result).toBe(original);
372
+ expect(result.category).toBe("auth");
373
+ });
374
+
375
+ // Deepgram error normalization (same categories apply)
376
+
377
+ test("normalizes Deepgram 401 errors to auth category", () => {
378
+ const result = normalizeSttError(
379
+ new Error("Deepgram API error (401): Invalid credentials"),
380
+ );
381
+ expect(result).toBeInstanceOf(SttError);
382
+ expect(result.category).toBe("auth");
383
+ });
384
+
385
+ test("normalizes Deepgram 429 errors to rate-limit category", () => {
386
+ const result = normalizeSttError(
387
+ new Error("Deepgram API error (429): Rate limited"),
388
+ );
389
+ expect(result).toBeInstanceOf(SttError);
390
+ expect(result.category).toBe("rate-limit");
391
+ });
392
+ });
@@ -0,0 +1,89 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import type {
4
+ SttStreamServerClosedEvent,
5
+ SttStreamServerErrorEvent,
6
+ SttStreamServerEvent,
7
+ SttStreamServerFinalEvent,
8
+ SttStreamServerPartialEvent,
9
+ } from "../types.js";
10
+
11
+ // ---------------------------------------------------------------------------
12
+ // Type-shape assertions for the streaming server event discriminated union.
13
+ //
14
+ // These types are TypeScript interfaces (no runtime Zod schemas), so the
15
+ // "tests" below are primarily structural — they fail to compile if the
16
+ // interfaces change shape in a way that breaks an existing caller. The
17
+ // runtime assertions are intentionally light: they confirm the literal
18
+ // values round-trip unchanged through an assignment to the union type.
19
+ // ---------------------------------------------------------------------------
20
+
21
+ describe("SttStreamServerEvent types", () => {
22
+ test("partial event compiles and round-trips without speakerLabel", () => {
23
+ const event: SttStreamServerPartialEvent = {
24
+ type: "partial",
25
+ text: "hello",
26
+ };
27
+ const asUnion: SttStreamServerEvent = event;
28
+ expect(asUnion.type).toBe("partial");
29
+ expect(event.text).toBe("hello");
30
+ expect(event.speakerLabel).toBeUndefined();
31
+ });
32
+
33
+ test("partial event accepts a speakerLabel when diarization is enabled", () => {
34
+ const event: SttStreamServerPartialEvent = {
35
+ type: "partial",
36
+ text: "hello",
37
+ speakerLabel: "0",
38
+ };
39
+ const asUnion: SttStreamServerEvent = event;
40
+ expect(asUnion.type).toBe("partial");
41
+ expect(event.speakerLabel).toBe("0");
42
+ });
43
+
44
+ test("final event compiles and round-trips without speakerLabel", () => {
45
+ const event: SttStreamServerFinalEvent = {
46
+ type: "final",
47
+ text: "world",
48
+ };
49
+ const asUnion: SttStreamServerEvent = event;
50
+ expect(asUnion.type).toBe("final");
51
+ expect(event.text).toBe("world");
52
+ expect(event.speakerLabel).toBeUndefined();
53
+ });
54
+
55
+ test("final event accepts a speakerLabel when diarization is enabled", () => {
56
+ const event: SttStreamServerFinalEvent = {
57
+ type: "final",
58
+ text: "world",
59
+ speakerLabel: "1",
60
+ };
61
+ const asUnion: SttStreamServerEvent = event;
62
+ expect(asUnion.type).toBe("final");
63
+ expect(event.speakerLabel).toBe("1");
64
+ });
65
+
66
+ test("error event has no speakerLabel field", () => {
67
+ const event: SttStreamServerErrorEvent = {
68
+ type: "error",
69
+ category: "provider-error",
70
+ message: "boom",
71
+ };
72
+ const asUnion: SttStreamServerEvent = event;
73
+ expect(asUnion.type).toBe("error");
74
+ // @ts-expect-error — speakerLabel is not part of SttStreamServerErrorEvent.
75
+ const _label: string | undefined = event.speakerLabel;
76
+ expect(_label).toBeUndefined();
77
+ });
78
+
79
+ test("closed event has no speakerLabel field", () => {
80
+ const event: SttStreamServerClosedEvent = {
81
+ type: "closed",
82
+ };
83
+ const asUnion: SttStreamServerEvent = event;
84
+ expect(asUnion.type).toBe("closed");
85
+ // @ts-expect-error — speakerLabel is not part of SttStreamServerClosedEvent.
86
+ const _label: string | undefined = event.speakerLabel;
87
+ expect(_label).toBeUndefined();
88
+ });
89
+ });
@@ -0,0 +1,195 @@
1
+ /**
2
+ * Daemon batch transcriber facade.
3
+ *
4
+ * Provides a single resolver that returns a `BatchTranscriber` implementation
5
+ * when provider credentials are available, or `null` when no STT backend can
6
+ * be configured. Callers use this instead of constructing provider classes
7
+ * directly.
8
+ *
9
+ * Supported daemon-batch providers:
10
+ * - OpenAI Whisper (`openai-whisper`)
11
+ * - Deepgram (`deepgram`)
12
+ * - Google Gemini (`google-gemini`)
13
+ */
14
+
15
+ import type {
16
+ BatchTranscriber,
17
+ SttProviderId,
18
+ SttTranscribeRequest,
19
+ SttTranscribeResult,
20
+ } from "./types.js";
21
+ import { SttError } from "./types.js";
22
+
23
+ // ---------------------------------------------------------------------------
24
+ // OpenAI Whisper adapter — implements BatchTranscriber on top of the existing
25
+ // OpenAIWhisperProvider low-level class.
26
+ // ---------------------------------------------------------------------------
27
+
28
+ /**
29
+ * Wraps `OpenAIWhisperProvider` behind the `BatchTranscriber` contract.
30
+ *
31
+ * Raw provider errors propagate unchanged so that legacy callers (e.g.
32
+ * `transcribe-audio.ts`) can continue detecting `AbortError` by name.
33
+ * Callers that want normalized categories should wrap calls with
34
+ * {@link normalizeSttError}.
35
+ */
36
+ class WhisperBatchTranscriber implements BatchTranscriber {
37
+ readonly providerId = "openai-whisper" as const;
38
+ readonly boundaryId = "daemon-batch" as const;
39
+
40
+ private readonly apiKey: string;
41
+
42
+ constructor(apiKey: string) {
43
+ this.apiKey = apiKey;
44
+ }
45
+
46
+ async transcribe(
47
+ request: SttTranscribeRequest,
48
+ ): Promise<SttTranscribeResult> {
49
+ // Lazy-import so the module graph stays lightweight for callers that
50
+ // only need the resolver, not the provider.
51
+ const { OpenAIWhisperProvider } =
52
+ await import("../providers/speech-to-text/openai-whisper.js");
53
+ const provider = new OpenAIWhisperProvider(this.apiKey);
54
+
55
+ return provider.transcribe(request.audio, request.mimeType, request.signal);
56
+ }
57
+ }
58
+
59
+ // ---------------------------------------------------------------------------
60
+ // Deepgram adapter — implements BatchTranscriber on top of the Deepgram
61
+ // prerecorded-audio provider.
62
+ // ---------------------------------------------------------------------------
63
+
64
+ /**
65
+ * Wraps `DeepgramProvider` behind the `BatchTranscriber` contract.
66
+ *
67
+ * Same error-propagation semantics as WhisperBatchTranscriber: raw provider
68
+ * errors pass through unchanged.
69
+ */
70
+ class DeepgramBatchTranscriber implements BatchTranscriber {
71
+ readonly providerId = "deepgram" as const;
72
+ readonly boundaryId = "daemon-batch" as const;
73
+
74
+ private readonly apiKey: string;
75
+
76
+ constructor(apiKey: string) {
77
+ this.apiKey = apiKey;
78
+ }
79
+
80
+ async transcribe(
81
+ request: SttTranscribeRequest,
82
+ ): Promise<SttTranscribeResult> {
83
+ const { DeepgramProvider } =
84
+ await import("../providers/speech-to-text/deepgram.js");
85
+ const provider = new DeepgramProvider(this.apiKey);
86
+
87
+ return provider.transcribe(request.audio, request.mimeType, request.signal);
88
+ }
89
+ }
90
+
91
+ // ---------------------------------------------------------------------------
92
+ // Google Gemini adapter — implements BatchTranscriber on top of the Google
93
+ // Gemini multimodal provider.
94
+ // ---------------------------------------------------------------------------
95
+
96
+ /**
97
+ * Wraps `GoogleGeminiProvider` behind the `BatchTranscriber` contract.
98
+ *
99
+ * Same error-propagation semantics as WhisperBatchTranscriber: raw provider
100
+ * errors pass through unchanged.
101
+ */
102
+ class GoogleGeminiBatchTranscriber implements BatchTranscriber {
103
+ readonly providerId = "google-gemini" as const;
104
+ readonly boundaryId = "daemon-batch" as const;
105
+
106
+ private readonly apiKey: string;
107
+
108
+ constructor(apiKey: string) {
109
+ this.apiKey = apiKey;
110
+ }
111
+
112
+ async transcribe(
113
+ request: SttTranscribeRequest,
114
+ ): Promise<SttTranscribeResult> {
115
+ const { GoogleGeminiProvider } =
116
+ await import("../providers/speech-to-text/google-gemini.js");
117
+ const provider = new GoogleGeminiProvider(this.apiKey);
118
+
119
+ return provider.transcribe(request.audio, request.mimeType, request.signal);
120
+ }
121
+ }
122
+
123
+ // ---------------------------------------------------------------------------
124
+ // Error normalization
125
+ // ---------------------------------------------------------------------------
126
+
127
+ /**
128
+ * Map a raw provider error into an {@link SttError} with a normalized category.
129
+ *
130
+ * Callers that need structured error categories should wrap
131
+ * `BatchTranscriber.transcribe()` calls with this utility.
132
+ */
133
+ export function normalizeSttError(err: unknown): SttError {
134
+ if (err instanceof SttError) return err;
135
+
136
+ const message = err instanceof Error ? err.message : String(err);
137
+
138
+ // Abort / timeout
139
+ if (err instanceof Error && err.name === "AbortError") {
140
+ return new SttError("timeout", message);
141
+ }
142
+
143
+ // Auth (401 / 403)
144
+ if (/\b40[13]\b/.test(message)) {
145
+ return new SttError("auth", message);
146
+ }
147
+
148
+ // Rate limit (429)
149
+ if (/\b429\b/.test(message) || /rate.?limit/i.test(message)) {
150
+ return new SttError("rate-limit", message);
151
+ }
152
+
153
+ // Invalid audio (400 with recognisable hints)
154
+ if (/\b400\b/.test(message) && /audio|format|file/i.test(message)) {
155
+ return new SttError("invalid-audio", message);
156
+ }
157
+
158
+ return new SttError("provider-error", message);
159
+ }
160
+
161
+ // ---------------------------------------------------------------------------
162
+ // Public resolver / factory
163
+ // ---------------------------------------------------------------------------
164
+
165
+ /**
166
+ * Create a `BatchTranscriber` for the daemon-batch boundary.
167
+ *
168
+ * Callers provide the API key and provider ID (obtained via the authorized
169
+ * secure-keys importer in `providers/speech-to-text/resolve.ts`) so that
170
+ * this module doesn't need to import secure-keys directly.
171
+ *
172
+ * Returns `null` when `apiKey` is falsy, signalling to the caller that
173
+ * batch transcription is unavailable.
174
+ */
175
+ export function createDaemonBatchTranscriber(
176
+ apiKey: string | null | undefined,
177
+ providerId: SttProviderId,
178
+ ): BatchTranscriber | null {
179
+ if (!apiKey) return null;
180
+
181
+ switch (providerId) {
182
+ case "openai-whisper":
183
+ return new WhisperBatchTranscriber(apiKey);
184
+ case "deepgram":
185
+ return new DeepgramBatchTranscriber(apiKey);
186
+ case "google-gemini":
187
+ return new GoogleGeminiBatchTranscriber(apiKey);
188
+ default: {
189
+ // Exhaustive check — compile error if a new SttProviderId is added
190
+ // without a corresponding case here.
191
+ const _exhaustive: never = providerId;
192
+ return null;
193
+ }
194
+ }
195
+ }