@vellumai/assistant 0.11.3 → 0.11.4-staging.1

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 (324) hide show
  1. package/ARCHITECTURE.md +11 -6
  2. package/docs/architecture/memory.md +11 -0
  3. package/docs/architecture/turn-actor.md +70 -0
  4. package/docs/flux-turn-detection-spike.md +243 -0
  5. package/docs/stt-provider-onboarding.md +3 -1
  6. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
  7. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
  8. package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +34 -0
  9. package/node_modules/@vellumai/gateway-client/src/index.ts +2 -0
  10. package/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
  11. package/openapi.yaml +140 -38
  12. package/package.json +1 -1
  13. package/scripts/voice-ttft-spike.ts +3 -3
  14. package/src/__tests__/app-compiler.test.ts +38 -3
  15. package/src/__tests__/attachments-store.test.ts +21 -12
  16. package/src/__tests__/byok-default-profile-ensure.test.ts +2 -0
  17. package/src/__tests__/call-setup-flow-name-capture.test.ts +0 -1
  18. package/src/__tests__/call-site-routing-provider.test.ts +1 -1
  19. package/src/__tests__/channel-availability-routes.test.ts +14 -1
  20. package/src/__tests__/channel-capabilities-dedupe.test.ts +214 -0
  21. package/src/__tests__/channel-delivery-store.test.ts +14 -14
  22. package/src/__tests__/config-loader-backfill.test.ts +3 -3
  23. package/src/__tests__/config-schema.test.ts +25 -10
  24. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +8 -11
  25. package/src/__tests__/conversation-agent-loop-overflow.test.ts +8 -11
  26. package/src/__tests__/conversation-agent-loop.test.ts +28 -20
  27. package/src/__tests__/conversation-attention-store.test.ts +63 -0
  28. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +0 -4
  29. package/src/__tests__/conversation-fork-crud.test.ts +69 -0
  30. package/src/__tests__/conversation-fork-referential.test.ts +67 -0
  31. package/src/__tests__/conversation-fork-retrospective.test.ts +24 -0
  32. package/src/__tests__/conversation-notifiers-provenance.test.ts +59 -0
  33. package/src/__tests__/conversation-queue.test.ts +55 -4
  34. package/src/__tests__/conversation-runtime-assembly.test.ts +134 -102
  35. package/src/__tests__/conversation-runtime-workspace.test.ts +14 -10
  36. package/src/__tests__/credential-prompt-route.test.ts +7 -10
  37. package/src/__tests__/custom-profile-ensure.test.ts +5 -1
  38. package/src/__tests__/discord-access-request-privacy.test.ts +5 -1
  39. package/src/__tests__/discord-requester-notice-privacy.test.ts +3 -3
  40. package/src/__tests__/document-append-idempotency.test.ts +233 -0
  41. package/src/__tests__/edit-propagation.test.ts +0 -7
  42. package/src/__tests__/helpers/mock-actor-context.ts +49 -0
  43. package/src/__tests__/helpers/mock-conversation.ts +13 -1
  44. package/src/__tests__/injector-chain.test.ts +63 -41
  45. package/src/__tests__/injector-disk-pressure.test.ts +11 -23
  46. package/src/__tests__/llm-context-resolution.test.ts +73 -1
  47. package/src/__tests__/llm-schema.test.ts +5 -2
  48. package/src/__tests__/mcp-list-plugin-servers.test.ts +250 -0
  49. package/src/__tests__/memory-retrieval-hook.test.ts +6 -5
  50. package/src/__tests__/messages-read-boundary-guard.test.ts +134 -0
  51. package/src/__tests__/mtime-cache.test.ts +1 -1
  52. package/src/__tests__/non-member-access-request.test.ts +0 -20
  53. package/src/__tests__/outbound-slack-persistence.test.ts +40 -1
  54. package/src/__tests__/plugin-import-boundary-guard.test.ts +5 -0
  55. package/src/__tests__/plugin-secret-pattern-contribution.test.ts +1 -1
  56. package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +14 -7
  57. package/src/__tests__/provider-commit-message-generator.test.ts +20 -0
  58. package/src/__tests__/run-conversation-turn-persistence.test.ts +434 -105
  59. package/src/__tests__/scoped-approval-grants.test.ts +11 -6
  60. package/src/__tests__/secret-ingress-channel.test.ts +0 -1
  61. package/src/__tests__/skills.test.ts +32 -0
  62. package/src/__tests__/slack-edit-ordering-characterization.test.ts +0 -1
  63. package/src/__tests__/subagent-call-site-routing.test.ts +31 -19
  64. package/src/__tests__/subagent-spawn-and-await.test.ts +14 -10
  65. package/src/__tests__/turn-events-store.test.ts +43 -0
  66. package/src/__tests__/user-plugin-loader.test.ts +1 -1
  67. package/src/__tests__/visible-app-context.test.ts +16 -9
  68. package/src/__tests__/worker-entrypoint-guards.test.ts +54 -0
  69. package/src/__tests__/worker-plugin-surface.test.ts +77 -0
  70. package/src/__tests__/workspace-migration-142-consolidate-voice-front-door.test.ts +158 -0
  71. package/src/__tests__/workspace-migration-143-repair-deprecated-codex-model-id.test.ts +133 -0
  72. package/src/__tests__/workspace-migration-144-convert-stranded-subscription-openai-profiles.test.ts +316 -0
  73. package/src/__tests__/workspace-migration-145-collapse-profile-bindings-to-entries.test.ts +325 -0
  74. package/src/acp/__tests__/acp-claude-oauth.test.ts +10 -2
  75. package/src/acp/__tests__/auth-required.test.ts +161 -0
  76. package/src/acp/acp-claude-oauth.ts +19 -2
  77. package/src/acp/agent-process.test.ts +100 -0
  78. package/src/acp/agent-process.ts +29 -26
  79. package/src/acp/auth-required.ts +102 -0
  80. package/src/acp/session-manager.test.ts +119 -0
  81. package/src/acp/session-manager.ts +68 -2
  82. package/src/api/events/acp-auth-required.ts +55 -0
  83. package/src/api/index.ts +7 -0
  84. package/src/apps/app-store.ts +3 -0
  85. package/src/bundler/package-resolver.ts +2 -30
  86. package/src/calls/__tests__/voice-session-bridge.test.ts +173 -1
  87. package/src/calls/__tests__/voice-triage-escalate.test.ts +94 -0
  88. package/src/calls/call-controller.ts +9 -2
  89. package/src/calls/call-setup-flow.ts +0 -1
  90. package/src/calls/media-stream-stt-session.ts +15 -0
  91. package/src/calls/voice-session-bridge.ts +71 -16
  92. package/src/calls/voice-triage-escalate.ts +104 -2
  93. package/src/channels/__tests__/plugin-channel-declarations.test.ts +161 -0
  94. package/src/channels/config.ts +13 -0
  95. package/src/channels/plugin-channel-declarations.ts +108 -0
  96. package/src/channels/types.ts +30 -0
  97. package/src/cli/AGENTS.md +5 -2
  98. package/src/cli/commands/credentials.help.ts +2 -2
  99. package/src/cli/commands/inference-providers.ts +1 -1
  100. package/src/cli/commands/mcp.help.ts +13 -4
  101. package/src/cli/commands/mcp.ts +9 -0
  102. package/src/cli/commands/memory/__tests__/memory-v3.test.ts +128 -5
  103. package/src/cli/commands/memory/index.help.ts +43 -1
  104. package/src/cli/commands/memory/memory-v3.ts +64 -0
  105. package/src/cli/commands/stt.help.ts +27 -2
  106. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +39 -0
  107. package/src/cli/lib/bundled-marketplace.json +13 -0
  108. package/src/cli/lib/upgrade-plugin.ts +42 -0
  109. package/src/config/__tests__/default-profile-catalog.test.ts +34 -2
  110. package/src/config/__tests__/default-provider.test.ts +6 -1
  111. package/src/config/__tests__/profile-materialization.test.ts +75 -19
  112. package/src/config/bundled-skills/acp/SKILL.md +6 -7
  113. package/src/config/bundled-skills/document-editor/SKILL.md +2 -2
  114. package/src/config/bundled-skills/document-editor/TOOLS.json +2 -2
  115. package/src/config/bundled-skills/media-processing/services/preprocess.ts +14 -4
  116. package/src/config/bundled-skills/settings/TOOLS.json +3 -3
  117. package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +22 -1
  118. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +9 -2
  119. package/src/config/call-site-defaults.ts +4 -5
  120. package/src/config/default-profile-catalog.ts +82 -11
  121. package/src/config/default-profile-names.ts +4 -1
  122. package/src/config/default-provider-resolution.ts +4 -0
  123. package/src/config/llm-context-resolution.ts +11 -3
  124. package/src/config/llm-resolver.ts +28 -1
  125. package/src/config/profile-materialization.ts +70 -22
  126. package/src/config/schemas/__tests__/live-voice.test.ts +107 -4
  127. package/src/config/schemas/call-site-catalog.ts +4 -4
  128. package/src/config/schemas/live-voice.ts +57 -23
  129. package/src/config/schemas/llm.ts +59 -32
  130. package/src/config/schemas/mcp.ts +23 -0
  131. package/src/config/schemas/plugin-updates.ts +6 -2
  132. package/src/config/schemas/stt.ts +1 -0
  133. package/src/context/outbound-sanitize.ts +96 -1
  134. package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +82 -0
  135. package/src/daemon/conversation-agent-loop-handlers.ts +15 -10
  136. package/src/daemon/conversation-agent-loop.ts +17 -6
  137. package/src/daemon/conversation-messaging.ts +5 -1
  138. package/src/daemon/conversation-notifiers.ts +9 -1
  139. package/src/daemon/conversation-process.ts +9 -6
  140. package/src/daemon/conversation-runtime-assembly.ts +3 -4
  141. package/src/daemon/conversation-tool-setup.ts +1 -2
  142. package/src/daemon/conversation.ts +48 -0
  143. package/src/daemon/mcp-reload-service.ts +36 -6
  144. package/src/daemon/process-message.ts +13 -3
  145. package/src/daemon/providers-setup.ts +6 -3
  146. package/src/daemon/trust-context-types.ts +29 -0
  147. package/src/daemon/wake-conversation-ops.ts +3 -2
  148. package/src/documents/document-store.ts +138 -5
  149. package/src/hooks/hook-loader.ts +3 -3
  150. package/src/hooks/registry.ts +50 -6
  151. package/src/inbound/__tests__/oauth-callback-url.test.ts +83 -0
  152. package/src/inbound/oauth-callback-url.ts +61 -0
  153. package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +1 -104
  154. package/src/live-voice/__tests__/live-voice-events.test.ts +7 -8
  155. package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +932 -0
  156. package/src/live-voice/__tests__/live-voice-metrics.test.ts +115 -8
  157. package/src/live-voice/__tests__/live-voice-photo.test.ts +100 -0
  158. package/src/live-voice/__tests__/live-voice-progress.test.ts +60 -194
  159. package/src/live-voice/__tests__/live-voice-stt.test.ts +14 -0
  160. package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +29 -0
  161. package/src/live-voice/__tests__/live-voice-tts-session.test.ts +0 -483
  162. package/src/live-voice/__tests__/live-voice-vad.test.ts +0 -16
  163. package/src/live-voice/__tests__/progress-narration.test.ts +214 -0
  164. package/src/live-voice/live-voice-archive.ts +2 -0
  165. package/src/live-voice/live-voice-metrics.ts +57 -32
  166. package/src/live-voice/live-voice-photo.ts +1 -2
  167. package/src/live-voice/live-voice-session.ts +535 -314
  168. package/src/live-voice/progress-narration.ts +277 -0
  169. package/src/live-voice/protocol.ts +21 -1
  170. package/src/mcp/__tests__/effective-config.test.ts +238 -0
  171. package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +0 -1
  172. package/src/mcp/__tests__/mcp-oauth-client-registration.test.ts +200 -0
  173. package/src/mcp/__tests__/mcp-oauth-provider.test.ts +9 -9
  174. package/src/mcp/__tests__/plugin-server-credential-isolation.test.ts +95 -0
  175. package/src/mcp/client.ts +16 -11
  176. package/src/mcp/effective-config.ts +113 -0
  177. package/src/mcp/manager.ts +11 -6
  178. package/src/mcp/mcp-auth-orchestrator.ts +13 -22
  179. package/src/mcp/mcp-oauth-provider.ts +205 -240
  180. package/src/monitoring/__tests__/plugin-auto-update.test.ts +166 -3
  181. package/src/monitoring/plugin-auto-update.ts +128 -24
  182. package/src/notifications/signal.ts +1 -0
  183. package/src/permissions/confirmation-guardian-request.test.ts +15 -11
  184. package/src/permissions/confirmation-guardian-request.ts +2 -2
  185. package/src/permissions/question-guardian-request.test.ts +14 -6
  186. package/src/permissions/question-guardian-request.ts +1 -2
  187. package/src/persistence/attachments-store.ts +8 -1
  188. package/src/persistence/bookmark-crud.ts +3 -7
  189. package/src/persistence/conversation-attention-store.ts +16 -45
  190. package/src/persistence/conversation-crud.ts +33 -4
  191. package/src/persistence/conversation-lineage.ts +9 -0
  192. package/src/persistence/conversation-queries.ts +108 -41
  193. package/src/persistence/delivery-crud.ts +38 -29
  194. package/src/persistence/external-conversation-store.ts +32 -4
  195. package/src/persistence/llm-request-log-store.ts +4 -10
  196. package/src/persistence/llm-usage-store.ts +8 -3
  197. package/src/persistence/message-reads.test.ts +197 -0
  198. package/src/persistence/message-reads.ts +211 -0
  199. package/src/persistence/migrations/366-chatgpt-subscription-row-identity.test.ts +120 -0
  200. package/src/persistence/migrations/366-chatgpt-subscription-row-identity.ts +62 -0
  201. package/src/persistence/real-user-turn-filter.ts +27 -3
  202. package/src/persistence/steps.ts +9 -0
  203. package/src/plugin-api/__tests__/oauth-callback-url-export.test.ts +29 -0
  204. package/src/plugin-api/conversation-turn.ts +168 -5
  205. package/src/plugin-api/index.ts +21 -5
  206. package/src/plugins/__tests__/mcp-servers.test.ts +371 -0
  207. package/src/plugins/defaults/memory/AGENTS.md +4 -0
  208. package/src/plugins/defaults/memory/__tests__/buffer-format.test.ts +204 -0
  209. package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +72 -0
  210. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +4 -1
  211. package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +4 -1
  212. package/src/plugins/defaults/memory/buffer-format.ts +165 -0
  213. package/src/plugins/defaults/memory/context-search/sources/conversations.ts +6 -0
  214. package/src/plugins/defaults/memory/graph/image-ref-utils.ts +3 -0
  215. package/src/plugins/defaults/memory/graph/tool-handlers.ts +1 -30
  216. package/src/plugins/defaults/memory/graph-topology/pending-buffer.test.ts +34 -0
  217. package/src/plugins/defaults/memory/graph-topology/pending-buffer.ts +8 -12
  218. package/src/plugins/defaults/memory/hooks/post-compact.ts +1 -4
  219. package/src/plugins/defaults/memory/indexer.ts +3 -1
  220. package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +19 -7
  221. package/src/plugins/defaults/memory/src/__tests__/memory-v3-gate-stats.test.ts +281 -0
  222. package/src/plugins/defaults/memory/src/memory-v3-routes.ts +207 -0
  223. package/src/plugins/defaults/memory/substrate/__tests__/consolidation-job.test.ts +33 -0
  224. package/src/plugins/defaults/memory/substrate/__tests__/static-context.test.ts +199 -2
  225. package/src/plugins/defaults/memory/substrate/consolidation-job.ts +25 -18
  226. package/src/plugins/defaults/memory/substrate/skill-content.ts +8 -1
  227. package/src/plugins/defaults/memory/substrate/static-context.ts +160 -4
  228. package/src/plugins/defaults/memory/substrate/sweep-job.ts +2 -4
  229. package/src/plugins/defaults/memory/v1/graph/extraction.ts +3 -1
  230. package/src/plugins/defaults/memory/v3/prune.ts +2 -0
  231. package/src/plugins/defaults/memory/v3/selection-log-store.ts +2 -0
  232. package/src/plugins/defaults/memory/worker.ts +6 -3
  233. package/src/plugins/external-plugin-loader.ts +47 -0
  234. package/src/plugins/mcp-servers.ts +361 -0
  235. package/src/plugins/mtime-cache.ts +23 -49
  236. package/src/plugins/worker-plugin-surface.ts +33 -0
  237. package/src/providers/__tests__/connection-model-compat.test.ts +1 -1
  238. package/src/providers/__tests__/dispatch-connection-routing.test.ts +214 -2
  239. package/src/providers/__tests__/preflight-resolved-config.test.ts +57 -0
  240. package/src/providers/__tests__/retry-callsite.test.ts +5 -2
  241. package/src/providers/call-site-routing.ts +30 -3
  242. package/src/providers/connection-resolution.ts +194 -11
  243. package/src/providers/inference/auth.ts +6 -0
  244. package/src/providers/inference/connection-availability.ts +24 -2
  245. package/src/providers/inference/connections.ts +2 -0
  246. package/src/providers/model-intents.ts +26 -6
  247. package/src/providers/openai/codex-models.ts +2 -1
  248. package/src/providers/provider-send-message.ts +32 -3
  249. package/src/providers/speech-to-text/__tests__/deepgram-flux-frames.test.ts +433 -0
  250. package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +620 -0
  251. package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +34 -0
  252. package/src/providers/speech-to-text/__tests__/resolve.test.ts +285 -6
  253. package/src/providers/speech-to-text/deepgram-flux-frames.ts +395 -0
  254. package/src/providers/speech-to-text/deepgram-flux-realtime.ts +719 -0
  255. package/src/providers/speech-to-text/provider-catalog.ts +99 -8
  256. package/src/providers/speech-to-text/resolve.ts +25 -2
  257. package/src/routes/worker.ts +17 -5
  258. package/src/runtime/access-request-helper.ts +9 -12
  259. package/src/runtime/agent-wake.ts +3 -3
  260. package/src/runtime/pre-first-message-gate.ts +4 -0
  261. package/src/runtime/routes/__tests__/acp-claude-auth-routes.test.ts +12 -4
  262. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +219 -1
  263. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +52 -0
  264. package/src/runtime/routes/__tests__/default-provider-routes.test.ts +61 -0
  265. package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +44 -0
  266. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +102 -1
  267. package/src/runtime/routes/__tests__/plugins-routes.test.ts +44 -0
  268. package/src/runtime/routes/__tests__/stt-routes.test.ts +25 -0
  269. package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +62 -1
  270. package/src/runtime/routes/channel-availability-routes.ts +32 -14
  271. package/src/runtime/routes/channel-route-shared.ts +0 -6
  272. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +6 -6
  273. package/src/runtime/routes/conversation-list-routes.ts +112 -1
  274. package/src/runtime/routes/conversation-query-routes.ts +40 -27
  275. package/src/runtime/routes/credential-prompt-routes.ts +4 -7
  276. package/src/runtime/routes/default-provider-routes.ts +15 -0
  277. package/src/runtime/routes/inbound-message-handler.ts +17 -41
  278. package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +0 -1
  279. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +0 -9
  280. package/src/runtime/routes/inbound-stages/admission-policy.ts +1 -17
  281. package/src/runtime/routes/inbound-stages/bootstrap-intercept.test.ts +0 -1
  282. package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +2 -3
  283. package/src/runtime/routes/inbound-stages/edit-intercept.ts +1 -3
  284. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +0 -1
  285. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +3 -4
  286. package/src/runtime/routes/inbound-stages/reaction-intercept.test.ts +0 -1
  287. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +11 -20
  288. package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +2 -3
  289. package/src/runtime/routes/inference-profiles-routes.ts +20 -11
  290. package/src/runtime/routes/inference-provider-connection-routes.ts +77 -15
  291. package/src/runtime/routes/log-export-routes.ts +3 -0
  292. package/src/runtime/routes/mcp-auth-routes.ts +148 -57
  293. package/src/runtime/routes/plugins-routes.ts +21 -3
  294. package/src/runtime/routes/stt-routes.ts +31 -25
  295. package/src/runtime/routes/surface-conversation-resolver.ts +3 -0
  296. package/src/runtime/routes/user-route-dispatcher.ts +39 -14
  297. package/src/runtime/routes/user-route-import.ts +108 -0
  298. package/src/schedule/worker.ts +6 -0
  299. package/src/security/oauth2.ts +6 -22
  300. package/src/stt/__tests__/daemon-batch-transcriber.test.ts +22 -0
  301. package/src/stt/__tests__/types.test.ts +94 -0
  302. package/src/stt/daemon-batch-transcriber.ts +10 -0
  303. package/src/stt/stt-stream-session.ts +8 -4
  304. package/src/stt/types.ts +103 -0
  305. package/src/subagent/manager.ts +1 -3
  306. package/src/subagent/types.ts +7 -6
  307. package/src/tools/acp/spawn.test.ts +97 -0
  308. package/src/tools/acp/spawn.ts +32 -0
  309. package/src/tools/document/document-tool.ts +12 -3
  310. package/src/tools/registry.ts +2 -1
  311. package/src/tools/workflows/run-workflow.ts +1 -2
  312. package/src/tts/__tests__/reasoning-tag-filter.test.ts +63 -0
  313. package/src/tts/reasoning-tag-filter.ts +110 -0
  314. package/src/workspace/byok-default-profile-ensure.ts +61 -18
  315. package/src/workspace/custom-profile-ensure.ts +4 -24
  316. package/src/workspace/migrations/142-consolidate-voice-front-door.ts +70 -0
  317. package/src/workspace/migrations/143-repair-deprecated-codex-model-id.ts +134 -0
  318. package/src/workspace/migrations/144-convert-stranded-subscription-openai-profiles.ts +265 -0
  319. package/src/workspace/migrations/145-collapse-profile-bindings-to-entries.ts +328 -0
  320. package/src/workspace/migrations/__tests__/141-stt-english-default-to-multilingual.test.ts +0 -10
  321. package/src/workspace/migrations/registry.ts +8 -0
  322. package/src/workspace/provider-commit-message-generator.ts +7 -5
  323. package/src/live-voice/__tests__/front-decision.test.ts +0 -645
  324. package/src/live-voice/front-decision.ts +0 -476
@@ -2,6 +2,7 @@ import { beforeEach, describe, expect, mock, test } from "bun:test";
2
2
 
3
3
  import { setConfig } from "../../../__tests__/helpers/set-config.js";
4
4
  import { getConfig } from "../../../config/loader.js";
5
+ import { SttError } from "../../../stt/types.js";
5
6
 
6
7
  // ---------------------------------------------------------------------------
7
8
  // Mocks — must be declared before any subject imports
@@ -491,7 +492,12 @@ describe("resolveTelephonySttCapability", () => {
491
492
  // Tests — telephony capability alignment with provider catalog
492
493
  // ---------------------------------------------------------------------------
493
494
 
494
- import { getProviderEntry, listProviderIds } from "../provider-catalog.js";
495
+ import type { SttProviderId } from "../../../stt/types.js";
496
+ import {
497
+ getProviderEntry,
498
+ listProviderIds,
499
+ supportsBoundary,
500
+ } from "../provider-catalog.js";
495
501
 
496
502
  describe("telephony capability catalog alignment", () => {
497
503
  /**
@@ -502,6 +508,16 @@ describe("telephony capability catalog alignment", () => {
502
508
  * misalignment early.
503
509
  */
504
510
 
511
+ /**
512
+ * Providers that deliberately sit out telephony (`telephonyMode: "none"`).
513
+ * `deepgram-flux` is a streaming-only spike, and telephony is out of its
514
+ * scope. Nothing reroutes a call to another provider, so opting out here
515
+ * means a Flux-configured assistant does not transcribe calls.
516
+ */
517
+ const TELEPHONY_OPT_OUT: ReadonlySet<SttProviderId> = new Set([
518
+ "deepgram-flux",
519
+ ]);
520
+
505
521
  test("deepgram catalog entry has realtime-ws telephonyMode", () => {
506
522
  const entry = getProviderEntry("deepgram");
507
523
  expect(entry).toBeDefined();
@@ -535,18 +551,34 @@ describe("telephony capability catalog alignment", () => {
535
551
  expect(entry!.credentialProvider).toBe("openai");
536
552
  });
537
553
 
538
- test("every catalog provider has a non-none telephonyMode", () => {
539
- // The telephony capability resolver assumes all known providers
540
- // participate in telephony over the media-stream transport. If a
541
- // provider with telephonyMode: "none" is added, the resolver reports
542
- // it as unsupported.
554
+ test("every catalog provider has a non-none telephonyMode unless it opts out", () => {
555
+ // The telephony capability resolver assumes known providers participate
556
+ // in telephony over the media-stream transport. A provider with
557
+ // telephonyMode: "none" is reported as unsupported, so opting out has to
558
+ // be a deliberate, listed choice rather than an oversight.
543
559
  for (const id of listProviderIds()) {
544
560
  const entry = getProviderEntry(id);
545
561
  expect(entry).toBeDefined();
562
+ if (TELEPHONY_OPT_OUT.has(id)) {
563
+ expect(entry!.telephonyMode).toBe("none");
564
+ continue;
565
+ }
546
566
  expect(entry!.telephonyMode).not.toBe("none");
547
567
  }
548
568
  });
549
569
 
570
+ test("telephony opt-out providers resolve as unsupported", async () => {
571
+ for (const id of TELEPHONY_OPT_OUT) {
572
+ const entry = getProviderEntry(id);
573
+ mockVellumAvailable = false;
574
+ mockProviderKeys = { [entry!.credentialProvider]: `test-key-${id}` };
575
+ applyConfig({ provider: id });
576
+
577
+ const result = await resolveTelephonySttCapability();
578
+ expect(result.status).toBe("unsupported");
579
+ }
580
+ });
581
+
550
582
  // -----------------------------------------------------------------------
551
583
  // Stable provider identity
552
584
  // -----------------------------------------------------------------------
@@ -574,6 +606,9 @@ describe("telephony capability catalog alignment", () => {
574
606
  };
575
607
 
576
608
  for (const id of listProviderIds()) {
609
+ if (TELEPHONY_OPT_OUT.has(id)) {
610
+ continue;
611
+ }
577
612
  const credKey = credentialMap[id];
578
613
  expect(credKey).toBeDefined();
579
614
 
@@ -1383,6 +1418,250 @@ describe("resolveStreamingTranscriber language plumbing", () => {
1383
1418
  });
1384
1419
  });
1385
1420
 
1421
+ // ---------------------------------------------------------------------------
1422
+ // Tests: advertised streaming capability vs. what the factory can build
1423
+ // ---------------------------------------------------------------------------
1424
+
1425
+ describe("streaming capability matches the streaming factory", () => {
1426
+ /**
1427
+ * The catalog's `daemon-streaming` boundary is load-bearing in three
1428
+ * places that must never disagree:
1429
+ *
1430
+ * 1. `resolveConversationStreamingSttCapability`, which preflight reports
1431
+ * as `supported`.
1432
+ * 2. `resolveStreamingTranscriber`, which has to actually build an adapter.
1433
+ * 3. The two user-facing "streaming-capable providers" strings, which list
1434
+ * exactly `listProviderIds().filter(id => supportsBoundary(id,
1435
+ * "daemon-streaming"))`: `stt/stt-stream-session.ts` and
1436
+ * `live-voice/live-voice-session.ts`.
1437
+ *
1438
+ * A provider that is advertised by (1) and (3) but resolves to `null` in
1439
+ * (2) makes preflight lie and sends the session down the provider-error
1440
+ * path, and the error message it lands on names the very provider that
1441
+ * just failed as a supported alternative. This suite is the guard: every
1442
+ * advertised provider must build.
1443
+ *
1444
+ * `deepgram-flux-realtime.js` is deliberately NOT mocked in this file, so
1445
+ * the Flux case constructs the real adapter.
1446
+ */
1447
+ const STREAMING_CREDENTIAL: Record<string, string> = {
1448
+ deepgram: "deepgram",
1449
+ "deepgram-flux": "deepgram",
1450
+ "google-gemini": "gemini",
1451
+ "openai-whisper": "openai",
1452
+ xai: "xai",
1453
+ // vellum's credential is the platform connection, mocked below.
1454
+ vellum: "vellum",
1455
+ };
1456
+
1457
+ /** The exact list both user-facing error strings advertise. */
1458
+ function advertisedStreamingProviders(): readonly SttProviderId[] {
1459
+ return listProviderIds().filter((id) =>
1460
+ supportsBoundary(id, "daemon-streaming"),
1461
+ );
1462
+ }
1463
+
1464
+ function seedCredentialsFor(id: SttProviderId): void {
1465
+ const credential = STREAMING_CREDENTIAL[id];
1466
+ expect(credential).toBeDefined();
1467
+ mockVellumAvailable = id === "vellum";
1468
+ mockVelayConnection =
1469
+ id === "vellum"
1470
+ ? {
1471
+ wsBaseUrl: "ws://gateway.test",
1472
+ httpBaseUrl: "http://gateway.test",
1473
+ mintServiceToken: () => "vk-test",
1474
+ }
1475
+ : null;
1476
+ mockProviderKeys = id === "vellum" ? {} : { [credential!]: `key-${id}` };
1477
+ applyConfig({ provider: id });
1478
+ }
1479
+
1480
+ beforeEach(() => {
1481
+ mockVellumAvailable = false;
1482
+ mockVelayConnection = null;
1483
+ mockProviderKeys = {};
1484
+ loggerWarnings.length = 0;
1485
+ });
1486
+
1487
+ test("the advertised list includes deepgram-flux", () => {
1488
+ // Guards the suite below against passing vacuously: if Flux dropped out of
1489
+ // the advertised list, those loops would assert nothing about it.
1490
+ expect(advertisedStreamingProviders()).toContain("deepgram-flux");
1491
+ });
1492
+
1493
+ test("every advertised provider reports supported AND builds a transcriber", async () => {
1494
+ for (const id of advertisedStreamingProviders()) {
1495
+ seedCredentialsFor(id);
1496
+
1497
+ const capability = await resolveConversationStreamingSttCapability();
1498
+ expect(capability.status).toBe("supported");
1499
+
1500
+ const transcriber = await resolveStreamingTranscriber();
1501
+ expect(transcriber).not.toBeNull();
1502
+ expect(transcriber!.boundaryId).toBe("daemon-streaming");
1503
+ }
1504
+ });
1505
+
1506
+ test("every advertised provider reports missing credentials AND builds nothing without them", async () => {
1507
+ for (const id of advertisedStreamingProviders()) {
1508
+ mockVellumAvailable = false;
1509
+ mockVelayConnection = null;
1510
+ mockProviderKeys = {};
1511
+ applyConfig({ provider: id });
1512
+
1513
+ const capability = await resolveConversationStreamingSttCapability();
1514
+ expect(capability.status).toBe("missing-credentials");
1515
+ expect(await resolveStreamingTranscriber()).toBeNull();
1516
+ }
1517
+ });
1518
+ });
1519
+
1520
+ // ---------------------------------------------------------------------------
1521
+ // Tests: deepgram-flux streaming resolution
1522
+ // ---------------------------------------------------------------------------
1523
+
1524
+ describe("deepgram-flux streaming resolution", () => {
1525
+ beforeEach(() => {
1526
+ mockVellumAvailable = false;
1527
+ mockVelayConnection = null;
1528
+ mockProviderKeys = {};
1529
+ loggerWarnings.length = 0;
1530
+ });
1531
+
1532
+ test("resolves a real Flux transcriber on the shared Deepgram key", async () => {
1533
+ mockProviderKeys = { deepgram: "dg-key" };
1534
+ applyConfig({ provider: "deepgram-flux" });
1535
+
1536
+ const transcriber = await resolveStreamingTranscriber({
1537
+ sampleRate: 16_000,
1538
+ });
1539
+
1540
+ expect(transcriber).not.toBeNull();
1541
+ expect(transcriber!.providerId).toBe("deepgram-flux");
1542
+ expect(transcriber!.boundaryId).toBe("daemon-streaming");
1543
+ // Flux owns turn boundaries, so callers feature-detecting this method
1544
+ // must fall back to stop().
1545
+ expect(transcriber!.finalizeUtterance).toBeUndefined();
1546
+ });
1547
+
1548
+ test("resolves from an explicit providerId, the way live voice asks", async () => {
1549
+ // Live voice derives its provider itself and passes it in rather than
1550
+ // letting the resolver re-read config.
1551
+ mockProviderKeys = { deepgram: "dg-key" };
1552
+ applyConfig({ provider: "openai-whisper" });
1553
+
1554
+ const transcriber = await resolveStreamingTranscriber({
1555
+ providerId: "deepgram-flux",
1556
+ });
1557
+
1558
+ expect(transcriber?.providerId).toBe("deepgram-flux");
1559
+ });
1560
+
1561
+ test("resolves null without a Deepgram key", async () => {
1562
+ mockProviderKeys = { openai: "sk-key" };
1563
+ applyConfig({ provider: "deepgram-flux" });
1564
+
1565
+ expect(await resolveStreamingTranscriber()).toBeNull();
1566
+ });
1567
+
1568
+ test("telephony callers resolve to null without a Flux-specific conditional", async () => {
1569
+ // The catalog's telephonyMode: "none" is the only gate. Boundary-
1570
+ // requiring callers fall back to per-turn batch transcription.
1571
+ mockProviderKeys = { deepgram: "dg-key" };
1572
+ applyConfig({ provider: "deepgram-flux" });
1573
+
1574
+ const transcriber = await resolveStreamingTranscriber({
1575
+ utteranceBoundaryFinals: true,
1576
+ });
1577
+
1578
+ expect(transcriber).toBeNull();
1579
+ expect(loggerWarnings).toHaveLength(1);
1580
+ expect(
1581
+ (loggerWarnings[0]!.data as { providerId?: unknown }).providerId,
1582
+ ).toBe("deepgram-flux");
1583
+ });
1584
+
1585
+ test("telephony capability reports Flux as unsupported", async () => {
1586
+ mockProviderKeys = { deepgram: "dg-key" };
1587
+ applyConfig({ provider: "deepgram-flux" });
1588
+
1589
+ const result = await resolveTelephonySttCapability();
1590
+
1591
+ expect(result.status).toBe("unsupported");
1592
+ });
1593
+
1594
+ test("diarize: 'required' rejects Flux rather than silently dropping labels", async () => {
1595
+ mockProviderKeys = { deepgram: "dg-key" };
1596
+ applyConfig({ provider: "deepgram-flux" });
1597
+
1598
+ const transcriber = await resolveStreamingTranscriber({
1599
+ diarize: "required",
1600
+ });
1601
+
1602
+ expect(transcriber).toBeNull();
1603
+ });
1604
+ });
1605
+
1606
+ // ---------------------------------------------------------------------------
1607
+ // Tests: batch resolution against a streaming-only provider
1608
+ // ---------------------------------------------------------------------------
1609
+
1610
+ describe("deepgram-flux batch resolution", () => {
1611
+ beforeEach(() => {
1612
+ mockVellumAvailable = false;
1613
+ mockVelayConnection = null;
1614
+ mockProviderKeys = {};
1615
+ loggerWarnings.length = 0;
1616
+ });
1617
+
1618
+ /**
1619
+ * Every batch caller pairs a `null` resolve with "no speech-to-text
1620
+ * provider is configured". Flux is configured, and the operator who set it
1621
+ * needs to be told which of the two Deepgram entries batch runs on, so the
1622
+ * resolver raises a typed error instead of returning that `null`.
1623
+ */
1624
+ test("throws a named error rather than resolving null", async () => {
1625
+ mockProviderKeys = { deepgram: "dg-key" };
1626
+ applyConfig({ provider: "deepgram-flux" });
1627
+
1628
+ await expect(resolveBatchTranscriber()).rejects.toThrow(
1629
+ 'Deepgram Flux is streaming-only. Batch transcription requires the deepgram provider: set services.stt.provider to "deepgram".',
1630
+ );
1631
+ });
1632
+
1633
+ test("marks the error user-facing so friendly copy does not overwrite it", async () => {
1634
+ mockProviderKeys = { deepgram: "dg-key" };
1635
+ applyConfig({ provider: "deepgram-flux" });
1636
+
1637
+ const err = await resolveBatchTranscriber().catch((e: unknown) => e);
1638
+
1639
+ expect(err).toBeInstanceOf(SttError);
1640
+ expect((err as SttError).category).toBe("provider-error");
1641
+ expect((err as SttError).userFacing).toBe(true);
1642
+ });
1643
+
1644
+ test("logs the gap so a degrading caller still leaves a daemon-side trace", async () => {
1645
+ mockProviderKeys = { deepgram: "dg-key" };
1646
+ applyConfig({ provider: "deepgram-flux" });
1647
+
1648
+ await resolveBatchTranscriber().catch(() => undefined);
1649
+
1650
+ expect(loggerWarnings).toHaveLength(1);
1651
+ expect(
1652
+ (loggerWarnings[0]!.data as { providerId?: unknown }).providerId,
1653
+ ).toBe("deepgram-flux");
1654
+ });
1655
+
1656
+ test("a provider that is absent from the catalog still resolves null", async () => {
1657
+ // "Unknown provider" is a different situation from "known provider,
1658
+ // wrong boundary", and only the latter has a fix worth naming.
1659
+ applyConfig({ provider: "not-a-provider" });
1660
+
1661
+ expect(await resolveBatchTranscriber()).toBeNull();
1662
+ });
1663
+ });
1664
+
1386
1665
  describe("sttCredentialGapReason", () => {
1387
1666
  test("vellum gets connection copy; API-key providers keep key copy", () => {
1388
1667
  expect(sttCredentialGapReason("vellum")).toContain("platform connect");