@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,330 @@
1
+ /**
2
+ * Provider-agnostic speech-to-text domain types for daemon transcription.
3
+ *
4
+ * These types define the boundary between callers that need audio transcription
5
+ * and the concrete STT provider implementations. The goal is to let daemon
6
+ * callsites program against a single typed interface so that provider swaps are
7
+ * localized to the adapter layer.
8
+ *
9
+ * Two execution modes are supported:
10
+ * - **Batch** — a single audio buffer is sent and a final transcript returned.
11
+ * - **Streaming** — audio chunks are sent over a persistent session, and the
12
+ * provider emits partial/final transcript events in real time.
13
+ */
14
+
15
+ // ---------------------------------------------------------------------------
16
+ // Provider identity
17
+ // ---------------------------------------------------------------------------
18
+
19
+ /**
20
+ * Canonical provider identifiers for daemon-hosted STT backends.
21
+ * Extend this union as new providers are integrated.
22
+ */
23
+ export type SttProviderId = "openai-whisper" | "deepgram" | "google-gemini";
24
+
25
+ /**
26
+ * Telephony-specific STT capability class.
27
+ *
28
+ * Describes the provider's native audio-ingestion capability when used in
29
+ * a telephony context. This is a **capability classification**, not a direct
30
+ * Twilio strategy selector — the telephony routing resolver
31
+ * (`telephony-stt-routing.ts`) maps capability classes plus catalog routing
32
+ * metadata to concrete Twilio setup strategies (conversation-relay-native
33
+ * vs media-stream-custom).
34
+ *
35
+ * - `"realtime-ws"` — provider offers a WebSocket streaming endpoint suitable
36
+ * for low-latency telephony audio (e.g. Deepgram live transcription).
37
+ * - `"batch-only"` — provider supports only REST batch transcription as its
38
+ * native capability. A `batch-only` provider may still participate in
39
+ * telephony via Twilio-native ConversationRelay (e.g. Google Gemini) or
40
+ * via the media-stream custom path — the routing strategy is determined
41
+ * by the catalog's telephony routing metadata, not this field alone.
42
+ * - `"none"` — provider has no telephony support.
43
+ */
44
+ export type TelephonySttMode = "realtime-ws" | "batch-only" | "none";
45
+
46
+ /**
47
+ * Conversation streaming STT support mode.
48
+ *
49
+ * Describes how a provider can participate in real-time conversation
50
+ * streaming when used for chat message capture (chat composer and iOS
51
+ * input bar).
52
+ *
53
+ * - `"realtime-ws"` — provider offers a native WebSocket streaming endpoint
54
+ * that accepts audio chunks and emits partial/final transcript events
55
+ * with low latency (e.g. Deepgram live transcription).
56
+ * - `"incremental-batch"` — provider does not offer true streaming but can
57
+ * be polled with incremental audio batches to approximate streaming
58
+ * behaviour (e.g. Google Gemini multimodal).
59
+ * - `"none"` — provider has no conversation streaming support; callers
60
+ * should fall back to batch transcription.
61
+ */
62
+ export type ConversationStreamingMode =
63
+ | "realtime-ws"
64
+ | "incremental-batch"
65
+ | "none";
66
+
67
+ // ---------------------------------------------------------------------------
68
+ // Boundary identifier
69
+ // ---------------------------------------------------------------------------
70
+
71
+ /**
72
+ * Runtime boundary through which STT is executed.
73
+ * - `daemon-batch` — transcription runs in the daemon process via a REST API
74
+ * call to the provider (e.g. OpenAI Whisper).
75
+ * - `daemon-streaming` — transcription runs in the daemon process over a
76
+ * persistent streaming session (e.g. WebSocket or incremental-batch loop).
77
+ */
78
+ export type SttBoundaryId = "daemon-batch" | "daemon-streaming";
79
+
80
+ // ---------------------------------------------------------------------------
81
+ // Call-context hints
82
+ // ---------------------------------------------------------------------------
83
+
84
+ /**
85
+ * Optional metadata hints that a caller can supply when the transcription
86
+ * originates from a phone-call context. These are advisory — providers may
87
+ * ignore hints they do not support.
88
+ *
89
+ * This type is intentionally separate from the batch request so that
90
+ * call-context awareness can be added incrementally without changing the
91
+ * shape for non-call callers.
92
+ */
93
+ export interface SttCallContextHints {
94
+ /** BCP-47 language code for the expected speech (e.g. "en-US"). */
95
+ language?: string;
96
+ /** Static vocabulary hints (proper nouns, domain terms) the ASR should prioritize. */
97
+ vocabularyHints?: string[];
98
+ /** Short natural-language prompt to bias the transcription model. */
99
+ prompt?: string;
100
+ }
101
+
102
+ // ---------------------------------------------------------------------------
103
+ // Request / result
104
+ // ---------------------------------------------------------------------------
105
+
106
+ /** Input to a batch transcription call. */
107
+ export interface SttTranscribeRequest {
108
+ /** Raw audio data (WAV, OGG, MP3, etc.). */
109
+ audio: Buffer;
110
+ /** MIME type of the audio data (e.g. "audio/ogg", "audio/wav"). */
111
+ mimeType: string;
112
+ /** Optional abort signal for cancellation / timeout. */
113
+ signal?: AbortSignal;
114
+ /**
115
+ * Optional call-context hints. Present when the transcription request
116
+ * originates from a telephony call. Providers may use these to improve
117
+ * recognition accuracy.
118
+ */
119
+ callContext?: SttCallContextHints;
120
+ }
121
+
122
+ /** Successful transcription output. */
123
+ export interface SttTranscribeResult {
124
+ /** The transcribed text, trimmed. Empty string for silence. */
125
+ text: string;
126
+ }
127
+
128
+ // ---------------------------------------------------------------------------
129
+ // Normalized error categories
130
+ // ---------------------------------------------------------------------------
131
+
132
+ /**
133
+ * Normalized error categories that callers can branch on without coupling to
134
+ * provider-specific error shapes or HTTP status codes.
135
+ */
136
+ export type SttErrorCategory =
137
+ /** The provider rejected the request due to invalid or missing credentials. */
138
+ | "auth"
139
+ /** The provider rate-limited the request. */
140
+ | "rate-limit"
141
+ /** The request or response timed out. */
142
+ | "timeout"
143
+ /** The audio payload was rejected (unsupported format, too large, etc.). */
144
+ | "invalid-audio"
145
+ /** Any other provider-side or network failure. */
146
+ | "provider-error";
147
+
148
+ /** A transcription error enriched with a normalized category. */
149
+ export class SttError extends Error {
150
+ readonly category: SttErrorCategory;
151
+
152
+ constructor(category: SttErrorCategory, message: string) {
153
+ super(message);
154
+ this.name = "SttError";
155
+ this.category = category;
156
+ }
157
+ }
158
+
159
+ // ---------------------------------------------------------------------------
160
+ // Batch transcriber interface
161
+ // ---------------------------------------------------------------------------
162
+
163
+ /**
164
+ * Daemon-hosted batch transcriber contract.
165
+ *
166
+ * Implementations accept a buffer of audio data and return a transcription
167
+ * result. Errors propagate as raw provider errors (not wrapped in
168
+ * {@link SttError}) so that callers relying on specific error identities
169
+ * (e.g. `AbortError` for cancellation) continue to work. Callers that need
170
+ * normalized error categories should wrap calls with `normalizeSttError()`
171
+ * from `daemon-batch-transcriber.ts`.
172
+ */
173
+ export interface BatchTranscriber {
174
+ /** Which provider backs this transcriber. */
175
+ readonly providerId: SttProviderId;
176
+ /** Which runtime boundary this transcriber operates in. */
177
+ readonly boundaryId: SttBoundaryId;
178
+
179
+ /**
180
+ * Transcribe a chunk of audio.
181
+ *
182
+ * Rejects with the raw provider error on failure. Use
183
+ * `normalizeSttError()` to convert to an {@link SttError} with a
184
+ * structured {@link SttErrorCategory}.
185
+ */
186
+ transcribe(request: SttTranscribeRequest): Promise<SttTranscribeResult>;
187
+ }
188
+
189
+ // ---------------------------------------------------------------------------
190
+ // Streaming client events (client -> daemon)
191
+ // ---------------------------------------------------------------------------
192
+
193
+ /**
194
+ * Events that a client sends to the daemon streaming session.
195
+ *
196
+ * The discriminated `type` field lets the runtime session handler
197
+ * dispatch to the correct streaming adapter method without coupling
198
+ * to transport-level framing (WebSocket opcodes, HTTP/2 frames, etc.).
199
+ */
200
+ export type SttStreamClientEvent =
201
+ | SttStreamClientAudioEvent
202
+ | SttStreamClientStopEvent;
203
+
204
+ /** A chunk of audio data to be transcribed. */
205
+ export interface SttStreamClientAudioEvent {
206
+ readonly type: "audio";
207
+ /** Raw audio data for the current chunk. */
208
+ readonly audio: Buffer;
209
+ /** MIME type of the audio data (e.g. "audio/webm", "audio/pcm"). */
210
+ readonly mimeType: string;
211
+ }
212
+
213
+ /** Signals that the client has finished sending audio. */
214
+ export interface SttStreamClientStopEvent {
215
+ readonly type: "stop";
216
+ }
217
+
218
+ // ---------------------------------------------------------------------------
219
+ // Streaming server events (daemon -> client)
220
+ // ---------------------------------------------------------------------------
221
+
222
+ /**
223
+ * Events that the daemon streaming session emits to the client.
224
+ *
225
+ * The discriminated `type` field allows clients to handle partial
226
+ * and final transcripts, errors, and session lifecycle signals in a
227
+ * type-safe manner.
228
+ */
229
+ export type SttStreamServerEvent =
230
+ | SttStreamServerPartialEvent
231
+ | SttStreamServerFinalEvent
232
+ | SttStreamServerErrorEvent
233
+ | SttStreamServerClosedEvent;
234
+
235
+ /**
236
+ * A partial (interim) transcript — may be revised by subsequent
237
+ * partial or final events.
238
+ */
239
+ export interface SttStreamServerPartialEvent {
240
+ readonly type: "partial";
241
+ /** Interim transcript text. May change with subsequent events. */
242
+ readonly text: string;
243
+ // Provider-emitted speaker label; undefined when diarization is disabled or unsupported. Consumers cross-check with channel-specific signals (e.g., Meet DOM).
244
+ readonly speakerLabel?: string;
245
+ /**
246
+ * Provider-emitted confidence score in [0, 1]. Undefined when the
247
+ * provider does not surface confidence on interim chunks.
248
+ */
249
+ readonly confidence?: number;
250
+ }
251
+
252
+ /**
253
+ * A final (committed) transcript — this segment will not be revised.
254
+ */
255
+ export interface SttStreamServerFinalEvent {
256
+ readonly type: "final";
257
+ /** Committed transcript text for a completed speech segment. */
258
+ readonly text: string;
259
+ // Provider-emitted speaker label; undefined when diarization is disabled or unsupported. Consumers cross-check with channel-specific signals (e.g., Meet DOM).
260
+ readonly speakerLabel?: string;
261
+ /**
262
+ * Provider-emitted confidence score in [0, 1]. Undefined when the
263
+ * provider does not surface confidence on this chunk.
264
+ */
265
+ readonly confidence?: number;
266
+ }
267
+
268
+ /** An error occurred during streaming transcription. */
269
+ export interface SttStreamServerErrorEvent {
270
+ readonly type: "error";
271
+ /** Normalized error category for caller branching. */
272
+ readonly category: SttErrorCategory;
273
+ /** Human-readable error description. */
274
+ readonly message: string;
275
+ }
276
+
277
+ /** The streaming session has closed (no more events will be emitted). */
278
+ export interface SttStreamServerClosedEvent {
279
+ readonly type: "closed";
280
+ }
281
+
282
+ // ---------------------------------------------------------------------------
283
+ // Streaming transcriber interface
284
+ // ---------------------------------------------------------------------------
285
+
286
+ /**
287
+ * Daemon-hosted streaming transcriber contract.
288
+ *
289
+ * Implementations manage a persistent session that accepts audio chunks
290
+ * and emits partial/final transcript events. The runtime session
291
+ * orchestrator (PR 5) drives this interface from the gateway WebSocket
292
+ * path.
293
+ *
294
+ * Lifecycle:
295
+ * 1. Call {@link start} to open the provider session.
296
+ * 2. Feed audio chunks via {@link sendAudio}.
297
+ * 3. Call {@link stop} when the client finishes recording.
298
+ * 4. The `onEvent` callback receives server events until `closed`.
299
+ */
300
+ export interface StreamingTranscriber {
301
+ /** Which provider backs this transcriber. */
302
+ readonly providerId: SttProviderId;
303
+ /** Which runtime boundary this transcriber operates in. */
304
+ readonly boundaryId: "daemon-streaming";
305
+
306
+ /**
307
+ * Open the streaming session with the provider.
308
+ *
309
+ * Must be called once before {@link sendAudio}. Rejects if the
310
+ * provider session cannot be established.
311
+ */
312
+ start(onEvent: (event: SttStreamServerEvent) => void): Promise<void>;
313
+
314
+ /**
315
+ * Feed a chunk of audio into the streaming session.
316
+ *
317
+ * Callers must not call this before {@link start} resolves or after
318
+ * {@link stop} has been called.
319
+ */
320
+ sendAudio(audio: Buffer, mimeType: string): void;
321
+
322
+ /**
323
+ * Signal that the client has finished sending audio.
324
+ *
325
+ * The provider may emit additional final events after stop is called.
326
+ * The session is fully closed when the `onEvent` callback receives a
327
+ * `closed` event.
328
+ */
329
+ stop(): void;
330
+ }
@@ -0,0 +1,373 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import { encodePcm16LeToWav } from "./wav-encoder.js";
4
+
5
+ // ---------------------------------------------------------------------------
6
+ // Helpers
7
+ // ---------------------------------------------------------------------------
8
+
9
+ /** Default mono 16 kHz PCM16LE options used by most tests. */
10
+ const MONO_16K = { sampleRate: 16000, channels: 1 } as const;
11
+
12
+ /**
13
+ * Read a 4-byte ASCII string from a buffer at the given offset.
14
+ */
15
+ function readTag(buf: Buffer, offset: number): string {
16
+ return buf.toString("ascii", offset, offset + 4);
17
+ }
18
+
19
+ // ---------------------------------------------------------------------------
20
+ // RIFF / WAVE structure
21
+ // ---------------------------------------------------------------------------
22
+
23
+ describe("encodePcm16LeToWav", () => {
24
+ describe("RIFF/WAVE markers", () => {
25
+ test("starts with RIFF chunk ID", () => {
26
+ const wav = encodePcm16LeToWav(Buffer.alloc(0), MONO_16K);
27
+ expect(readTag(wav, 0)).toBe("RIFF");
28
+ });
29
+
30
+ test("contains WAVE format identifier at offset 8", () => {
31
+ const wav = encodePcm16LeToWav(Buffer.alloc(0), MONO_16K);
32
+ expect(readTag(wav, 8)).toBe("WAVE");
33
+ });
34
+
35
+ test("contains fmt sub-chunk ID at offset 12", () => {
36
+ const wav = encodePcm16LeToWav(Buffer.alloc(0), MONO_16K);
37
+ expect(readTag(wav, 12)).toBe("fmt ");
38
+ });
39
+
40
+ test("contains data sub-chunk ID at offset 36", () => {
41
+ const wav = encodePcm16LeToWav(Buffer.alloc(0), MONO_16K);
42
+ expect(readTag(wav, 36)).toBe("data");
43
+ });
44
+ });
45
+
46
+ // -------------------------------------------------------------------------
47
+ // RIFF chunk size
48
+ // -------------------------------------------------------------------------
49
+
50
+ describe("RIFF chunk size", () => {
51
+ test("ChunkSize equals fileSize - 8 for non-empty payload", () => {
52
+ const pcm = Buffer.alloc(1024);
53
+ const wav = encodePcm16LeToWav(pcm, MONO_16K);
54
+ const chunkSize = wav.readUInt32LE(4);
55
+ // Total file size = 44 (header) + 1024 (data) = 1068
56
+ // ChunkSize = 1068 - 8 = 1060
57
+ expect(chunkSize).toBe(1068 - 8);
58
+ });
59
+
60
+ test("ChunkSize equals 36 for empty payload (header-only)", () => {
61
+ const wav = encodePcm16LeToWav(Buffer.alloc(0), MONO_16K);
62
+ const chunkSize = wav.readUInt32LE(4);
63
+ // Total file size = 44, ChunkSize = 44 - 8 = 36
64
+ expect(chunkSize).toBe(36);
65
+ });
66
+ });
67
+
68
+ // -------------------------------------------------------------------------
69
+ // fmt sub-chunk fields
70
+ // -------------------------------------------------------------------------
71
+
72
+ describe("fmt sub-chunk", () => {
73
+ test("fmt sub-chunk size is 16 (PCM)", () => {
74
+ const wav = encodePcm16LeToWav(Buffer.alloc(0), MONO_16K);
75
+ expect(wav.readUInt32LE(16)).toBe(16);
76
+ });
77
+
78
+ test("AudioFormat is 1 (PCM)", () => {
79
+ const wav = encodePcm16LeToWav(Buffer.alloc(0), MONO_16K);
80
+ expect(wav.readUInt16LE(20)).toBe(1);
81
+ });
82
+
83
+ test("NumChannels matches mono input", () => {
84
+ const wav = encodePcm16LeToWav(Buffer.alloc(0), {
85
+ sampleRate: 16000,
86
+ channels: 1,
87
+ });
88
+ expect(wav.readUInt16LE(22)).toBe(1);
89
+ });
90
+
91
+ test("NumChannels matches stereo input", () => {
92
+ const wav = encodePcm16LeToWav(Buffer.alloc(0), {
93
+ sampleRate: 44100,
94
+ channels: 2,
95
+ });
96
+ expect(wav.readUInt16LE(22)).toBe(2);
97
+ });
98
+
99
+ test("SampleRate is written correctly for 16 kHz", () => {
100
+ const wav = encodePcm16LeToWav(Buffer.alloc(0), MONO_16K);
101
+ expect(wav.readUInt32LE(24)).toBe(16000);
102
+ });
103
+
104
+ test("SampleRate is written correctly for 44.1 kHz", () => {
105
+ const wav = encodePcm16LeToWav(Buffer.alloc(0), {
106
+ sampleRate: 44100,
107
+ channels: 1,
108
+ });
109
+ expect(wav.readUInt32LE(24)).toBe(44100);
110
+ });
111
+
112
+ test("SampleRate is written correctly for 48 kHz", () => {
113
+ const wav = encodePcm16LeToWav(Buffer.alloc(0), {
114
+ sampleRate: 48000,
115
+ channels: 2,
116
+ });
117
+ expect(wav.readUInt32LE(24)).toBe(48000);
118
+ });
119
+
120
+ test("ByteRate = sampleRate * channels * bitsPerSample / 8 for mono 16-bit", () => {
121
+ const wav = encodePcm16LeToWav(Buffer.alloc(0), MONO_16K);
122
+ // 16000 * 1 * 16 / 8 = 32000
123
+ expect(wav.readUInt32LE(28)).toBe(32000);
124
+ });
125
+
126
+ test("ByteRate = sampleRate * channels * bitsPerSample / 8 for stereo 16-bit", () => {
127
+ const wav = encodePcm16LeToWav(Buffer.alloc(0), {
128
+ sampleRate: 48000,
129
+ channels: 2,
130
+ });
131
+ // 48000 * 2 * 16 / 8 = 192000
132
+ expect(wav.readUInt32LE(28)).toBe(192000);
133
+ });
134
+
135
+ test("BlockAlign = channels * bitsPerSample / 8 for mono 16-bit", () => {
136
+ const wav = encodePcm16LeToWav(Buffer.alloc(0), MONO_16K);
137
+ // 1 * 16 / 8 = 2
138
+ expect(wav.readUInt16LE(32)).toBe(2);
139
+ });
140
+
141
+ test("BlockAlign = channels * bitsPerSample / 8 for stereo 16-bit", () => {
142
+ const wav = encodePcm16LeToWav(Buffer.alloc(0), {
143
+ sampleRate: 44100,
144
+ channels: 2,
145
+ });
146
+ // 2 * 16 / 8 = 4
147
+ expect(wav.readUInt16LE(32)).toBe(4);
148
+ });
149
+
150
+ test("BitsPerSample defaults to 16", () => {
151
+ const wav = encodePcm16LeToWav(Buffer.alloc(0), MONO_16K);
152
+ expect(wav.readUInt16LE(34)).toBe(16);
153
+ });
154
+
155
+ test("BitsPerSample respects explicit override", () => {
156
+ const wav = encodePcm16LeToWav(Buffer.alloc(0), {
157
+ sampleRate: 16000,
158
+ channels: 1,
159
+ bitsPerSample: 24,
160
+ });
161
+ expect(wav.readUInt16LE(34)).toBe(24);
162
+ });
163
+ });
164
+
165
+ // -------------------------------------------------------------------------
166
+ // data sub-chunk
167
+ // -------------------------------------------------------------------------
168
+
169
+ describe("data sub-chunk", () => {
170
+ test("data sub-chunk size matches PCM payload length", () => {
171
+ const pcm = Buffer.alloc(512);
172
+ const wav = encodePcm16LeToWav(pcm, MONO_16K);
173
+ expect(wav.readUInt32LE(40)).toBe(512);
174
+ });
175
+
176
+ test("data sub-chunk size is 0 for empty payload", () => {
177
+ const wav = encodePcm16LeToWav(Buffer.alloc(0), MONO_16K);
178
+ expect(wav.readUInt32LE(40)).toBe(0);
179
+ });
180
+ });
181
+
182
+ // -------------------------------------------------------------------------
183
+ // Payload passthrough
184
+ // -------------------------------------------------------------------------
185
+
186
+ describe("payload passthrough", () => {
187
+ test("PCM data is copied verbatim after the 44-byte header", () => {
188
+ const pcm = Buffer.from([0x01, 0x02, 0x03, 0x04, 0xff, 0xfe]);
189
+ const wav = encodePcm16LeToWav(pcm, MONO_16K);
190
+
191
+ const payload = wav.subarray(44);
192
+ expect(payload).toEqual(pcm);
193
+ });
194
+
195
+ test("large payload is preserved byte-for-byte", () => {
196
+ // 1 second of mono 16 kHz 16-bit audio = 32000 bytes
197
+ const pcm = Buffer.alloc(32000);
198
+ for (let i = 0; i < pcm.length; i++) {
199
+ pcm[i] = i % 256;
200
+ }
201
+
202
+ const wav = encodePcm16LeToWav(pcm, MONO_16K);
203
+ const payload = wav.subarray(44);
204
+ expect(payload).toEqual(pcm);
205
+ });
206
+
207
+ test("total file length is header + payload", () => {
208
+ const pcm = Buffer.alloc(256);
209
+ const wav = encodePcm16LeToWav(pcm, MONO_16K);
210
+ expect(wav.length).toBe(44 + 256);
211
+ });
212
+ });
213
+
214
+ // -------------------------------------------------------------------------
215
+ // Empty audio (silence / final flush edge case)
216
+ // -------------------------------------------------------------------------
217
+
218
+ describe("empty audio", () => {
219
+ test("produces a deterministic 44-byte header-only output", () => {
220
+ const wav = encodePcm16LeToWav(Buffer.alloc(0), MONO_16K);
221
+ expect(wav.length).toBe(44);
222
+ });
223
+
224
+ test("header-only output has valid RIFF structure", () => {
225
+ const wav = encodePcm16LeToWav(Buffer.alloc(0), MONO_16K);
226
+ expect(readTag(wav, 0)).toBe("RIFF");
227
+ expect(readTag(wav, 8)).toBe("WAVE");
228
+ expect(readTag(wav, 12)).toBe("fmt ");
229
+ expect(readTag(wav, 36)).toBe("data");
230
+ expect(wav.readUInt32LE(40)).toBe(0);
231
+ });
232
+
233
+ test("two calls with empty audio produce identical output", () => {
234
+ const a = encodePcm16LeToWav(Buffer.alloc(0), MONO_16K);
235
+ const b = encodePcm16LeToWav(Buffer.alloc(0), MONO_16K);
236
+ expect(a).toEqual(b);
237
+ });
238
+ });
239
+
240
+ // -------------------------------------------------------------------------
241
+ // Custom bit depths
242
+ // -------------------------------------------------------------------------
243
+
244
+ describe("custom bit depths", () => {
245
+ test("accepts 8-bit depth and computes correct byte rate", () => {
246
+ const wav = encodePcm16LeToWav(Buffer.alloc(0), {
247
+ sampleRate: 8000,
248
+ channels: 1,
249
+ bitsPerSample: 8,
250
+ });
251
+ // ByteRate = 8000 * 1 * 8 / 8 = 8000
252
+ expect(wav.readUInt32LE(28)).toBe(8000);
253
+ // BlockAlign = 1 * 8 / 8 = 1
254
+ expect(wav.readUInt16LE(32)).toBe(1);
255
+ expect(wav.readUInt16LE(34)).toBe(8);
256
+ });
257
+
258
+ test("accepts 24-bit depth and computes correct byte rate", () => {
259
+ const wav = encodePcm16LeToWav(Buffer.alloc(0), {
260
+ sampleRate: 48000,
261
+ channels: 2,
262
+ bitsPerSample: 24,
263
+ });
264
+ // ByteRate = 48000 * 2 * 24 / 8 = 288000
265
+ expect(wav.readUInt32LE(28)).toBe(288000);
266
+ // BlockAlign = 2 * 24 / 8 = 6
267
+ expect(wav.readUInt16LE(32)).toBe(6);
268
+ expect(wav.readUInt16LE(34)).toBe(24);
269
+ });
270
+
271
+ test("accepts 32-bit depth and computes correct byte rate", () => {
272
+ const wav = encodePcm16LeToWav(Buffer.alloc(0), {
273
+ sampleRate: 44100,
274
+ channels: 1,
275
+ bitsPerSample: 32,
276
+ });
277
+ // ByteRate = 44100 * 1 * 32 / 8 = 176400
278
+ expect(wav.readUInt32LE(28)).toBe(176400);
279
+ // BlockAlign = 1 * 32 / 8 = 4
280
+ expect(wav.readUInt16LE(32)).toBe(4);
281
+ expect(wav.readUInt16LE(34)).toBe(32);
282
+ });
283
+ });
284
+
285
+ // -------------------------------------------------------------------------
286
+ // Input validation
287
+ // -------------------------------------------------------------------------
288
+
289
+ describe("input validation", () => {
290
+ test("throws for zero sample rate", () => {
291
+ expect(() =>
292
+ encodePcm16LeToWav(Buffer.alloc(0), {
293
+ sampleRate: 0,
294
+ channels: 1,
295
+ }),
296
+ ).toThrow("Invalid sampleRate");
297
+ });
298
+
299
+ test("throws for negative sample rate", () => {
300
+ expect(() =>
301
+ encodePcm16LeToWav(Buffer.alloc(0), {
302
+ sampleRate: -16000,
303
+ channels: 1,
304
+ }),
305
+ ).toThrow("Invalid sampleRate");
306
+ });
307
+
308
+ test("throws for NaN sample rate", () => {
309
+ expect(() =>
310
+ encodePcm16LeToWav(Buffer.alloc(0), {
311
+ sampleRate: NaN,
312
+ channels: 1,
313
+ }),
314
+ ).toThrow("Invalid sampleRate");
315
+ });
316
+
317
+ test("throws for Infinity sample rate", () => {
318
+ expect(() =>
319
+ encodePcm16LeToWav(Buffer.alloc(0), {
320
+ sampleRate: Infinity,
321
+ channels: 1,
322
+ }),
323
+ ).toThrow("Invalid sampleRate");
324
+ });
325
+
326
+ test("throws for zero channels", () => {
327
+ expect(() =>
328
+ encodePcm16LeToWav(Buffer.alloc(0), {
329
+ sampleRate: 16000,
330
+ channels: 0,
331
+ }),
332
+ ).toThrow("Invalid channels");
333
+ });
334
+
335
+ test("throws for negative channels", () => {
336
+ expect(() =>
337
+ encodePcm16LeToWav(Buffer.alloc(0), {
338
+ sampleRate: 16000,
339
+ channels: -1,
340
+ }),
341
+ ).toThrow("Invalid channels");
342
+ });
343
+
344
+ test("throws for fractional channels", () => {
345
+ expect(() =>
346
+ encodePcm16LeToWav(Buffer.alloc(0), {
347
+ sampleRate: 16000,
348
+ channels: 1.5,
349
+ }),
350
+ ).toThrow("Invalid channels");
351
+ });
352
+
353
+ test("throws for unsupported bit depth", () => {
354
+ expect(() =>
355
+ encodePcm16LeToWav(Buffer.alloc(0), {
356
+ sampleRate: 16000,
357
+ channels: 1,
358
+ bitsPerSample: 12,
359
+ }),
360
+ ).toThrow("Unsupported bitsPerSample");
361
+ });
362
+
363
+ test("throws for zero bit depth", () => {
364
+ expect(() =>
365
+ encodePcm16LeToWav(Buffer.alloc(0), {
366
+ sampleRate: 16000,
367
+ channels: 1,
368
+ bitsPerSample: 0,
369
+ }),
370
+ ).toThrow("Unsupported bitsPerSample");
371
+ });
372
+ });
373
+ });