@vellumai/assistant 0.11.3 → 0.11.4-dev.202608190019.b94dbf2

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 (596) hide show
  1. package/AGENTS.md +9 -3
  2. package/ARCHITECTURE.md +32 -10
  3. package/docs/architecture/memory.md +43 -5
  4. package/docs/architecture/security.md +96 -152
  5. package/docs/architecture/turn-actor.md +70 -0
  6. package/docs/browser-use-architecture-phase2.md +128 -56
  7. package/docs/flux-turn-detection-spike.md +248 -0
  8. package/docs/guardian-request-flow.md +35 -0
  9. package/docs/stt-provider-onboarding.md +3 -1
  10. package/knip.json +3 -0
  11. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +1 -0
  12. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
  13. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
  14. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  15. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +60 -0
  16. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
  17. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -0
  18. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
  19. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
  20. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  21. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +60 -0
  22. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
  23. package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +34 -0
  24. package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +166 -1
  25. package/node_modules/@vellumai/gateway-client/src/index.ts +2 -0
  26. package/node_modules/@vellumai/service-contracts/package.json +1 -0
  27. package/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
  28. package/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
  29. package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  30. package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +60 -0
  31. package/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
  32. package/openapi.yaml +289 -113
  33. package/package.json +1 -1
  34. package/scripts/voice-ttft-spike.ts +3 -3
  35. package/scripts/write-plugin-api-shim.ts +10 -0
  36. package/src/__tests__/app-compiler.test.ts +38 -3
  37. package/src/__tests__/app-control-flow.test.ts +1 -0
  38. package/src/__tests__/approval-routes-http.test.ts +2 -2
  39. package/src/__tests__/assistant-feature-flag-guard.test.ts +25 -3
  40. package/src/__tests__/attachments-store.test.ts +21 -12
  41. package/src/__tests__/byok-default-profile-ensure.test.ts +17 -0
  42. package/src/__tests__/call-setup-flow-name-capture.test.ts +0 -1
  43. package/src/__tests__/call-site-routing-provider.test.ts +1 -1
  44. package/src/__tests__/channel-availability-routes.test.ts +14 -1
  45. package/src/__tests__/channel-capabilities-dedupe.test.ts +214 -0
  46. package/src/__tests__/channel-delivery-store.test.ts +14 -14
  47. package/src/__tests__/channel-inbound-disk-pressure.test.ts +4 -4
  48. package/src/__tests__/channel-setup-panel-ack.test.ts +1 -1
  49. package/src/__tests__/checker.test.ts +20 -314
  50. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +6 -2
  51. package/src/__tests__/compaction-events.test.ts +8 -10
  52. package/src/__tests__/config-loader-backfill.test.ts +3 -3
  53. package/src/__tests__/config-schema.test.ts +25 -10
  54. package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
  55. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +8 -11
  56. package/src/__tests__/conversation-agent-loop-overflow.test.ts +8 -11
  57. package/src/__tests__/conversation-agent-loop.test.ts +32 -21
  58. package/src/__tests__/conversation-attachments.test.ts +0 -1
  59. package/src/__tests__/conversation-attention-store.test.ts +63 -0
  60. package/src/__tests__/conversation-confirmation-signals.test.ts +112 -0
  61. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +0 -4
  62. package/src/__tests__/conversation-fork-crud.test.ts +69 -0
  63. package/src/__tests__/conversation-fork-referential.test.ts +67 -0
  64. package/src/__tests__/conversation-fork-retrospective.test.ts +24 -0
  65. package/src/__tests__/conversation-load-history-repair.test.ts +209 -0
  66. package/src/__tests__/conversation-notifiers-provenance.test.ts +59 -0
  67. package/src/__tests__/conversation-queue.test.ts +154 -4
  68. package/src/__tests__/conversation-routes-disk-view.test.ts +1 -1
  69. package/src/__tests__/conversation-routes-enabled-plugins.test.ts +1 -1
  70. package/src/__tests__/conversation-routes-guardian-reply.test.ts +9 -9
  71. package/src/__tests__/conversation-routes-hidden-queue.test.ts +1 -1
  72. package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -1
  73. package/src/__tests__/conversation-runtime-assembly.test.ts +187 -102
  74. package/src/__tests__/conversation-runtime-workspace.test.ts +14 -10
  75. package/src/__tests__/conversation-slash-queue.test.ts +3 -0
  76. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +1 -0
  77. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +1 -0
  78. package/src/__tests__/conversation-surfaces-app-control.test.ts +1 -0
  79. package/src/__tests__/conversation-surfaces-app-open.test.ts +1 -1
  80. package/src/__tests__/conversation-surfaces-data-persist.test.ts +1 -1
  81. package/src/__tests__/conversation-surfaces-history-restored-completion.test.ts +21 -14
  82. package/src/__tests__/conversation-surfaces-queued-emit.test.ts +1 -0
  83. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +1 -0
  84. package/src/__tests__/conversation-surfaces-standalone.test.ts +1 -0
  85. package/src/__tests__/conversation-surfaces-state-update.test.ts +1 -1
  86. package/src/__tests__/conversation-surfaces-table-action.test.ts +1 -1
  87. package/src/__tests__/conversation-surfaces-task-progress.test.ts +1 -1
  88. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +1 -1
  89. package/src/__tests__/conversation-tool-setup-attribution.test.ts +1 -1
  90. package/src/__tests__/credential-prompt-route.test.ts +7 -10
  91. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  92. package/src/__tests__/cu-unified-flow.test.ts +1 -0
  93. package/src/__tests__/custom-profile-ensure.test.ts +5 -1
  94. package/src/__tests__/default-plugin-names-guard.test.ts +12 -1
  95. package/src/__tests__/discord-access-request-privacy.test.ts +5 -1
  96. package/src/__tests__/discord-requester-notice-privacy.test.ts +3 -3
  97. package/src/__tests__/document-append-idempotency.test.ts +233 -0
  98. package/src/__tests__/document-sync-tags.test.ts +0 -75
  99. package/src/__tests__/dynamic-skill-background-guard.test.ts +0 -2
  100. package/src/__tests__/edit-propagation.test.ts +107 -11
  101. package/src/__tests__/file-ops-service.test.ts +163 -30
  102. package/src/__tests__/filesystem-tools.test.ts +23 -24
  103. package/src/__tests__/gateway-only-guard.test.ts +2 -5
  104. package/src/__tests__/helpers/gateway-classify-mock.ts +27 -6
  105. package/src/__tests__/helpers/mock-actor-context.ts +49 -0
  106. package/src/__tests__/helpers/mock-conversation.ts +13 -1
  107. package/src/__tests__/host-file-read-tool.test.ts +16 -19
  108. package/src/__tests__/http-user-message-parity.test.ts +1 -1
  109. package/src/__tests__/init-feature-flag-overrides.test.ts +49 -0
  110. package/src/__tests__/injector-chain.test.ts +63 -41
  111. package/src/__tests__/injector-disk-pressure.test.ts +11 -23
  112. package/src/__tests__/inline-skill-load-permissions.test.ts +47 -36
  113. package/src/__tests__/llm-context-resolution.test.ts +73 -1
  114. package/src/__tests__/llm-schema.test.ts +5 -2
  115. package/src/__tests__/managed-skill-lifecycle.test.ts +7 -0
  116. package/src/__tests__/mcp-list-plugin-servers.test.ts +250 -0
  117. package/src/__tests__/media-generate-image.test.ts +131 -21
  118. package/src/__tests__/memory-retrieval-hook.test.ts +100 -7
  119. package/src/__tests__/messages-read-boundary-guard.test.ts +134 -0
  120. package/src/__tests__/mtime-cache.test.ts +3 -1
  121. package/src/__tests__/non-member-access-request.test.ts +0 -20
  122. package/src/__tests__/outbound-slack-persistence.test.ts +40 -1
  123. package/src/__tests__/platform-bash-auto-approve.test.ts +0 -4
  124. package/src/__tests__/plugin-api-store-credential.test.ts +261 -0
  125. package/src/__tests__/plugin-api-webhook-url.test.ts +10 -7
  126. package/src/__tests__/plugin-disabled-state.test.ts +2 -0
  127. package/src/__tests__/plugin-effective-enabled-set.test.ts +55 -3
  128. package/src/__tests__/plugin-execution-context.test.ts +73 -0
  129. package/src/__tests__/plugin-import-boundary-guard.test.ts +5 -1
  130. package/src/__tests__/plugin-import-boundary-reverse-guard.test.ts +7 -4
  131. package/src/__tests__/plugin-secret-pattern-contribution.test.ts +1 -1
  132. package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +14 -7
  133. package/src/__tests__/provider-commit-message-generator.test.ts +20 -0
  134. package/src/__tests__/proxy-approval-callback.test.ts +1 -0
  135. package/src/__tests__/qdrant-manager.test.ts +14 -1
  136. package/src/__tests__/reaction-persistence.test.ts +200 -7
  137. package/src/__tests__/require-fresh-approval.test.ts +0 -4
  138. package/src/__tests__/risk-classification-boundary-guard.test.ts +115 -0
  139. package/src/__tests__/run-conversation-turn-persistence.test.ts +460 -105
  140. package/src/__tests__/run-due-schedules.test.ts +21 -0
  141. package/src/__tests__/scaffold-managed-skill-tool.test.ts +187 -18
  142. package/src/__tests__/schedule-routes.test.ts +23 -0
  143. package/src/__tests__/schedule-store.test.ts +17 -0
  144. package/src/__tests__/scoped-approval-grants.test.ts +11 -6
  145. package/src/__tests__/secret-ingress-channel.test.ts +0 -1
  146. package/src/__tests__/secret-ingress-http.test.ts +1 -1
  147. package/src/__tests__/send-endpoint-busy.test.ts +3 -3
  148. package/src/__tests__/skills.test.ts +32 -0
  149. package/src/__tests__/slack-edit-ordering-characterization.test.ts +0 -1
  150. package/src/__tests__/starter-task-flow.test.ts +5 -4
  151. package/src/__tests__/subagent-call-site-routing.test.ts +31 -19
  152. package/src/__tests__/subagent-fork-prompt-role.test.ts +1 -1
  153. package/src/__tests__/subagent-spawn-and-await.test.ts +18 -17
  154. package/src/__tests__/subagent-tool-gate-mode.test.ts +1 -1
  155. package/src/__tests__/subagent-tools.test.ts +81 -101
  156. package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +1 -0
  157. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
  158. package/src/__tests__/tool-executor-lifecycle-events.test.ts +132 -5
  159. package/src/__tests__/tool-executor.test.ts +68 -39
  160. package/src/__tests__/turn-events-store.test.ts +43 -0
  161. package/src/__tests__/ui-choice-copy-surfaces.test.ts +1 -1
  162. package/src/__tests__/ui-shape-teaching.test.ts +33 -0
  163. package/src/__tests__/ui-visual-surface.test.ts +1 -1
  164. package/src/__tests__/ui-voice-picker-surface.test.ts +128 -0
  165. package/src/__tests__/ui-work-result-surface.test.ts +1 -1
  166. package/src/__tests__/user-plugin-loader.test.ts +3 -1
  167. package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
  168. package/src/__tests__/visible-app-context.test.ts +16 -9
  169. package/src/__tests__/voice-config-update.test.ts +40 -0
  170. package/src/__tests__/voice-scoped-grant-consumer.test.ts +5 -3
  171. package/src/__tests__/voice-session-bridge.test.ts +85 -29
  172. package/src/__tests__/worker-entrypoint-guards.test.ts +54 -0
  173. package/src/__tests__/worker-plugin-surface.test.ts +77 -0
  174. package/src/__tests__/workspace-migration-142-consolidate-voice-front-door.test.ts +158 -0
  175. package/src/__tests__/workspace-migration-143-repair-deprecated-codex-model-id.test.ts +133 -0
  176. package/src/__tests__/workspace-migration-144-convert-stranded-subscription-openai-profiles.test.ts +316 -0
  177. package/src/__tests__/workspace-migration-145-collapse-profile-bindings-to-entries.test.ts +325 -0
  178. package/src/__tests__/workspace-migration-146-repair-retired-fireworks-deepseek-flash-model-id.test.ts +235 -0
  179. package/src/acp/__tests__/acp-claude-oauth.test.ts +10 -2
  180. package/src/acp/__tests__/auth-required.test.ts +161 -0
  181. package/src/acp/acp-claude-oauth.ts +19 -2
  182. package/src/acp/agent-process.test.ts +100 -0
  183. package/src/acp/agent-process.ts +29 -26
  184. package/src/acp/auth-required.ts +102 -0
  185. package/src/acp/session-manager.test.ts +119 -0
  186. package/src/acp/session-manager.ts +76 -3
  187. package/src/api/events/acp-auth-required.ts +55 -0
  188. package/src/api/events/host-file.ts +2 -2
  189. package/src/api/index.ts +7 -0
  190. package/src/api/surfaces.ts +12 -3
  191. package/src/apps/app-store.ts +3 -0
  192. package/src/bundler/package-resolver.ts +2 -30
  193. package/src/calls/__tests__/voice-session-bridge.test.ts +194 -11
  194. package/src/calls/__tests__/voice-triage-escalate.test.ts +102 -0
  195. package/src/calls/call-controller.ts +19 -3
  196. package/src/calls/call-setup-flow.ts +0 -1
  197. package/src/calls/media-stream-stt-session.ts +15 -0
  198. package/src/calls/voice-session-bridge.ts +115 -41
  199. package/src/calls/voice-triage-escalate.ts +105 -2
  200. package/src/channels/__tests__/plugin-channel-declarations.test.ts +161 -0
  201. package/src/channels/config.ts +13 -0
  202. package/src/channels/plugin-channel-declarations.ts +108 -0
  203. package/src/channels/types.ts +30 -0
  204. package/src/cli/AGENTS.md +5 -2
  205. package/src/cli/bundled-modules.ts +29 -0
  206. package/src/cli/commands/credentials.help.ts +2 -2
  207. package/src/cli/commands/db/repair.ts +4 -8
  208. package/src/cli/commands/domain.ts +6 -3
  209. package/src/cli/commands/email.ts +6 -3
  210. package/src/cli/commands/inference-providers.ts +1 -1
  211. package/src/cli/commands/keys.ts +8 -3
  212. package/src/cli/commands/mcp.help.ts +13 -4
  213. package/src/cli/commands/mcp.ts +9 -0
  214. package/src/cli/commands/memory/__tests__/memory-v2.test.ts +57 -7
  215. package/src/cli/commands/memory/__tests__/memory-v3.test.ts +128 -5
  216. package/src/cli/commands/memory/index.help.ts +84 -28
  217. package/src/cli/commands/memory/index.ts +2 -0
  218. package/src/cli/commands/memory/memory-v2.ts +58 -54
  219. package/src/cli/commands/memory/memory-v3.ts +64 -0
  220. package/src/cli/commands/memory/memory-validate.ts +18 -0
  221. package/src/cli/commands/plugins.ts +85 -36
  222. package/src/cli/commands/schedules.ts +35 -1
  223. package/src/cli/commands/stt.help.ts +27 -2
  224. package/src/cli/commands/trust.ts +3 -14
  225. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +39 -0
  226. package/src/cli/lib/bundled-marketplace.json +14 -1
  227. package/src/cli/lib/upgrade-plugin.ts +42 -0
  228. package/src/config/__tests__/balanced-model-experiment.test.ts +278 -0
  229. package/src/config/__tests__/default-profile-catalog.test.ts +34 -2
  230. package/src/config/__tests__/default-provider.test.ts +6 -1
  231. package/src/config/__tests__/profile-materialization.test.ts +75 -19
  232. package/src/config/assistant-feature-flags.ts +36 -15
  233. package/src/config/balanced-model-experiment.ts +35 -0
  234. package/src/config/bundled-skills/acp/SKILL.md +6 -7
  235. package/src/config/bundled-skills/document-editor/SKILL.md +2 -2
  236. package/src/config/bundled-skills/document-editor/TOOLS.json +2 -2
  237. package/src/config/bundled-skills/image-studio/SKILL.md +5 -4
  238. package/src/config/bundled-skills/image-studio/TOOLS.json +1 -1
  239. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +101 -0
  240. package/src/config/bundled-skills/media-processing/services/preprocess.ts +14 -4
  241. package/src/config/bundled-skills/settings/TOOLS.json +3 -3
  242. package/src/config/bundled-skills/settings/tools/navigate-settings-tab.test.ts +65 -0
  243. package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +7 -1
  244. package/src/config/bundled-skills/settings/tools/shared.ts +16 -0
  245. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +19 -1
  246. package/src/config/bundled-skills/skill-management/TOOLS.json +9 -3
  247. package/src/config/bundled-skills/subagent/SKILL.md +17 -12
  248. package/src/config/bundled-skills/subagent/TOOLS.json +4 -4
  249. package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +22 -1
  250. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +9 -2
  251. package/src/config/call-site-defaults.ts +11 -5
  252. package/src/config/default-profile-catalog.ts +179 -16
  253. package/src/config/default-profile-names.ts +4 -1
  254. package/src/config/default-provider-resolution.ts +4 -0
  255. package/src/config/feature-flag-registry.json +12 -12
  256. package/src/config/llm-context-resolution.ts +11 -3
  257. package/src/config/llm-resolver.ts +28 -1
  258. package/src/config/profile-materialization.ts +70 -22
  259. package/src/config/schemas/__tests__/live-voice.test.ts +107 -4
  260. package/src/config/schemas/call-site-catalog.ts +4 -4
  261. package/src/config/schemas/live-voice.ts +57 -23
  262. package/src/config/schemas/llm.ts +59 -32
  263. package/src/config/schemas/mcp.ts +23 -0
  264. package/src/config/schemas/plugin-updates.ts +6 -2
  265. package/src/config/schemas/stt.ts +1 -0
  266. package/src/config/skills.ts +9 -2
  267. package/src/context/outbound-sanitize.ts +96 -1
  268. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +1 -1
  269. package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +82 -0
  270. package/src/daemon/conversation-agent-loop-handlers.ts +15 -10
  271. package/src/daemon/conversation-agent-loop.ts +31 -19
  272. package/src/daemon/conversation-messaging.ts +5 -1
  273. package/src/daemon/conversation-notifiers.ts +20 -10
  274. package/src/daemon/conversation-process.ts +20 -6
  275. package/src/daemon/conversation-runtime-assembly.ts +12 -6
  276. package/src/daemon/conversation-store.ts +14 -4
  277. package/src/daemon/conversation-surfaces.ts +75 -26
  278. package/src/daemon/conversation-tool-setup.ts +3 -6
  279. package/src/daemon/conversation.ts +154 -51
  280. package/src/daemon/doordash-steps.ts +2 -2
  281. package/src/daemon/lifecycle.ts +14 -1
  282. package/src/daemon/mcp-reload-service.ts +36 -6
  283. package/src/daemon/process-message.ts +13 -26
  284. package/src/daemon/providers-setup.ts +6 -3
  285. package/src/daemon/trust-context-types.ts +29 -0
  286. package/src/daemon/trust-context.ts +12 -7
  287. package/src/daemon/wake-conversation-ops.ts +3 -2
  288. package/src/daemon/windows-compiled-entry.ts +4 -0
  289. package/src/documents/document-store.ts +143 -240
  290. package/src/hooks/hook-loader.ts +3 -3
  291. package/src/hooks/registry.ts +103 -45
  292. package/src/hooks/types.ts +5 -0
  293. package/src/inbound/__tests__/oauth-callback-url.test.ts +83 -0
  294. package/src/inbound/oauth-callback-url.ts +61 -0
  295. package/src/ipc/gateway-client.test.ts +1 -1
  296. package/src/ipc/gateway-client.ts +14 -21
  297. package/src/ipc/gateway-flag-listener.ts +17 -3
  298. package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +1 -104
  299. package/src/live-voice/__tests__/live-voice-events.test.ts +7 -8
  300. package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +1050 -0
  301. package/src/live-voice/__tests__/live-voice-metrics.test.ts +115 -8
  302. package/src/live-voice/__tests__/live-voice-photo.test.ts +100 -0
  303. package/src/live-voice/__tests__/live-voice-progress.test.ts +60 -194
  304. package/src/live-voice/__tests__/live-voice-stt.test.ts +14 -0
  305. package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +29 -0
  306. package/src/live-voice/__tests__/live-voice-tts-session.test.ts +0 -483
  307. package/src/live-voice/__tests__/live-voice-vad.test.ts +0 -16
  308. package/src/live-voice/__tests__/progress-narration.test.ts +214 -0
  309. package/src/live-voice/live-voice-archive.ts +2 -0
  310. package/src/live-voice/live-voice-manager.ts +16 -3
  311. package/src/live-voice/live-voice-metrics.ts +57 -32
  312. package/src/live-voice/live-voice-photo.ts +1 -2
  313. package/src/live-voice/live-voice-session.ts +598 -323
  314. package/src/live-voice/progress-narration.ts +277 -0
  315. package/src/live-voice/protocol.ts +21 -1
  316. package/src/live-voice/windows-compiled-live-voice.ts +4 -0
  317. package/src/mcp/__tests__/effective-config.test.ts +238 -0
  318. package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +0 -1
  319. package/src/mcp/__tests__/mcp-oauth-client-registration.test.ts +200 -0
  320. package/src/mcp/__tests__/mcp-oauth-provider.test.ts +9 -9
  321. package/src/mcp/__tests__/plugin-server-credential-isolation.test.ts +95 -0
  322. package/src/mcp/client.ts +16 -11
  323. package/src/mcp/effective-config.ts +113 -0
  324. package/src/mcp/manager.ts +11 -6
  325. package/src/mcp/mcp-auth-orchestrator.ts +13 -22
  326. package/src/mcp/mcp-oauth-provider.ts +205 -240
  327. package/src/monitoring/__tests__/plugin-auto-update.test.ts +166 -3
  328. package/src/monitoring/control.ts +1 -0
  329. package/src/monitoring/db-integrity-sample.ts +4 -5
  330. package/src/monitoring/plugin-auto-update.ts +128 -24
  331. package/src/notifications/AGENTS.md +2 -0
  332. package/src/notifications/approval-card-data.ts +33 -0
  333. package/src/notifications/signal.ts +1 -0
  334. package/src/permissions/AGENTS.md +16 -0
  335. package/src/permissions/checker.test.ts +75 -122
  336. package/src/permissions/checker.ts +44 -560
  337. package/src/permissions/confirmation-guardian-request.test.ts +15 -11
  338. package/src/permissions/confirmation-guardian-request.ts +2 -2
  339. package/src/permissions/prompter.ts +1 -5
  340. package/src/permissions/question-guardian-request.test.ts +14 -6
  341. package/src/permissions/question-guardian-request.ts +1 -2
  342. package/src/persistence/attachments-store.ts +8 -1
  343. package/src/persistence/bookmark-crud.ts +3 -7
  344. package/src/persistence/conversation-attention-store.ts +16 -45
  345. package/src/persistence/conversation-crud.ts +33 -4
  346. package/src/persistence/conversation-lineage.ts +9 -0
  347. package/src/persistence/conversation-queries.ts +182 -52
  348. package/src/persistence/delivery-crud.ts +150 -36
  349. package/src/persistence/embeddings/qdrant-manager.ts +84 -49
  350. package/src/persistence/external-conversation-store.ts +32 -4
  351. package/src/persistence/llm-request-log-store.ts +4 -10
  352. package/src/persistence/llm-usage-store.ts +8 -3
  353. package/src/persistence/message-reads.test.ts +197 -0
  354. package/src/persistence/message-reads.ts +211 -0
  355. package/src/persistence/migrations/360-add-document-workspace-path.ts +5 -14
  356. package/src/persistence/migrations/366-chatgpt-subscription-row-identity.test.ts +120 -0
  357. package/src/persistence/migrations/366-chatgpt-subscription-row-identity.ts +62 -0
  358. package/src/persistence/real-user-turn-filter.ts +27 -3
  359. package/src/persistence/schema/documents.ts +4 -4
  360. package/src/persistence/steps.ts +9 -0
  361. package/src/plugin-api/__tests__/oauth-callback-url-export.test.ts +29 -0
  362. package/src/plugin-api/constants.ts +8 -0
  363. package/src/plugin-api/conversation-turn.ts +195 -5
  364. package/src/plugin-api/index.ts +38 -6
  365. package/src/plugin-api/resolve-credential.ts +3 -2
  366. package/src/plugin-api/store-credential.ts +146 -0
  367. package/src/plugin-api/vision-support.test.ts +1 -1
  368. package/src/plugin-api/webhook-url.ts +13 -11
  369. package/src/plugins/__tests__/mcp-servers.test.ts +371 -0
  370. package/src/plugins/defaults/main.ts +21 -7
  371. package/src/plugins/defaults/memory/AGENTS.md +15 -3
  372. package/src/plugins/defaults/memory/__tests__/buffer-format.test.ts +204 -0
  373. package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +72 -0
  374. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +4 -1
  375. package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +4 -1
  376. package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +0 -1
  377. package/src/plugins/defaults/memory/buffer-format.ts +165 -0
  378. package/src/plugins/defaults/memory/context-search/sources/conversations.ts +6 -0
  379. package/src/plugins/defaults/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +77 -0
  380. package/src/plugins/defaults/memory/graph/conversation-graph-memory.ts +24 -6
  381. package/src/plugins/defaults/memory/graph/image-ref-utils.ts +3 -0
  382. package/src/plugins/defaults/memory/graph/tool-handlers.ts +1 -30
  383. package/src/plugins/defaults/memory/graph-topology/pending-buffer.test.ts +34 -0
  384. package/src/plugins/defaults/memory/graph-topology/pending-buffer.ts +17 -24
  385. package/src/plugins/defaults/memory/hooks/post-compact.ts +1 -4
  386. package/src/plugins/defaults/memory/hooks/user-prompt-submit.ts +53 -5
  387. package/src/plugins/defaults/memory/indexer.ts +3 -1
  388. package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +19 -7
  389. package/src/plugins/defaults/memory/memory-retrospective-job.ts +4 -4
  390. package/src/plugins/defaults/memory/src/__tests__/memory-v3-gate-stats.test.ts +281 -0
  391. package/src/plugins/defaults/memory/src/memory-v2-routes.ts +12 -10
  392. package/src/plugins/defaults/memory/src/memory-v3-routes.ts +207 -0
  393. package/src/plugins/defaults/memory/substrate/__tests__/consolidation-job.test.ts +147 -0
  394. package/src/plugins/defaults/memory/substrate/__tests__/consolidation-prompt-flag-gating-guard.test.ts +8 -0
  395. package/src/plugins/defaults/memory/substrate/__tests__/edge-index.test.ts +0 -30
  396. package/src/plugins/defaults/memory/substrate/__tests__/ingest.test.ts +73 -1
  397. package/src/plugins/defaults/memory/substrate/__tests__/page-index.test.ts +37 -0
  398. package/src/plugins/defaults/memory/substrate/__tests__/page-links.test.ts +208 -0
  399. package/src/plugins/defaults/memory/substrate/__tests__/prompts-consolidation.test.ts +158 -1
  400. package/src/plugins/defaults/memory/substrate/__tests__/static-context.test.ts +195 -58
  401. package/src/plugins/defaults/memory/substrate/consolidation-job.ts +93 -24
  402. package/src/plugins/defaults/memory/substrate/edge-index.ts +0 -31
  403. package/src/plugins/defaults/memory/substrate/ingest.ts +59 -0
  404. package/src/plugins/defaults/memory/substrate/page-index.ts +35 -8
  405. package/src/plugins/defaults/memory/substrate/page-links.ts +133 -0
  406. package/src/plugins/defaults/memory/substrate/page-store.ts +10 -0
  407. package/src/plugins/defaults/memory/substrate/prompts/consolidation.ts +124 -34
  408. package/src/plugins/defaults/memory/substrate/skill-content.ts +8 -1
  409. package/src/plugins/defaults/memory/substrate/static-context.ts +160 -33
  410. package/src/plugins/defaults/memory/substrate/sweep-job.ts +2 -4
  411. package/src/plugins/defaults/memory/v1/graph/extraction.ts +3 -1
  412. package/src/plugins/defaults/memory/v3/__tests__/injection.test.ts +18 -0
  413. package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +66 -1
  414. package/src/plugins/defaults/memory/v3/card.ts +9 -9
  415. package/src/plugins/defaults/memory/v3/core-set.test.ts +7 -0
  416. package/src/plugins/defaults/memory/v3/core-set.ts +5 -1
  417. package/src/plugins/defaults/memory/v3/edge.ts +13 -57
  418. package/src/plugins/defaults/memory/v3/injector.ts +8 -0
  419. package/src/plugins/defaults/memory/v3/prune.ts +2 -0
  420. package/src/plugins/defaults/memory/v3/selection-log-store.ts +2 -0
  421. package/src/plugins/defaults/memory/v3/shadow-plugin.ts +23 -9
  422. package/src/plugins/defaults/memory/worker-control.ts +1 -0
  423. package/src/plugins/defaults/memory/worker.ts +6 -3
  424. package/src/plugins/defaults/worker-entrypoints.ts +3 -0
  425. package/src/plugins/external-plugin-loader.ts +47 -0
  426. package/src/plugins/mcp-servers.ts +361 -0
  427. package/src/plugins/mtime-cache.ts +37 -46
  428. package/src/plugins/pipeline.ts +14 -7
  429. package/src/plugins/plugin-execution-context.ts +35 -11
  430. package/src/plugins/worker-plugin-surface.ts +33 -0
  431. package/src/prompts/templates/system-sections.ts +0 -7
  432. package/src/providers/__tests__/connection-model-compat.test.ts +1 -1
  433. package/src/providers/__tests__/context-overflow-error.test.ts +24 -0
  434. package/src/providers/__tests__/dispatch-connection-routing.test.ts +214 -2
  435. package/src/providers/__tests__/preflight-resolved-config.test.ts +57 -0
  436. package/src/providers/__tests__/retry-callsite.test.ts +25 -2
  437. package/src/providers/anthropic/__tests__/pause-turn-continuation.test.ts +170 -0
  438. package/src/providers/anthropic/client.ts +332 -241
  439. package/src/providers/call-site-routing.ts +30 -3
  440. package/src/providers/connection-resolution.ts +194 -11
  441. package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +4 -0
  442. package/src/providers/inference/adapter-factory.ts +3 -0
  443. package/src/providers/inference/auth.ts +6 -0
  444. package/src/providers/inference/connection-availability.ts +24 -2
  445. package/src/providers/inference/connections.ts +2 -0
  446. package/src/providers/model-catalog.ts +3 -3
  447. package/src/providers/model-intents.ts +28 -8
  448. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +163 -40
  449. package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +125 -2
  450. package/src/providers/openai/chat-completions-provider.ts +133 -36
  451. package/src/providers/openai/codex-models.ts +2 -1
  452. package/src/providers/provider-send-message.ts +32 -3
  453. package/src/providers/speech-to-text/__tests__/deepgram-flux-frames.test.ts +433 -0
  454. package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +625 -0
  455. package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +34 -0
  456. package/src/providers/speech-to-text/__tests__/resolve.test.ts +285 -6
  457. package/src/providers/speech-to-text/deepgram-flux-frames.ts +395 -0
  458. package/src/providers/speech-to-text/deepgram-flux-realtime.ts +679 -0
  459. package/src/providers/speech-to-text/provider-catalog.ts +99 -8
  460. package/src/providers/speech-to-text/resolve.ts +25 -2
  461. package/src/routes/control.ts +1 -0
  462. package/src/routes/route-host-client.ts +1 -0
  463. package/src/routes/route-host-protocol.ts +7 -0
  464. package/src/routes/worker.ts +48 -15
  465. package/src/runtime/AGENTS.md +16 -17
  466. package/src/runtime/access-request-helper.ts +9 -12
  467. package/src/runtime/agent-wake.ts +18 -15
  468. package/src/runtime/effective-capabilities.test.ts +43 -0
  469. package/src/runtime/effective-capabilities.ts +27 -0
  470. package/src/runtime/guardian-reply-router.ts +6 -2
  471. package/src/runtime/pre-first-message-gate.ts +4 -0
  472. package/src/runtime/routes/__tests__/acp-claude-auth-routes.test.ts +12 -4
  473. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +522 -2
  474. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +52 -0
  475. package/src/runtime/routes/__tests__/default-provider-routes.test.ts +61 -0
  476. package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +44 -0
  477. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +102 -1
  478. package/src/runtime/routes/__tests__/plugins-routes.test.ts +132 -35
  479. package/src/runtime/routes/__tests__/schedule-routes-disarm-reason.test.ts +215 -0
  480. package/src/runtime/routes/__tests__/stt-routes.test.ts +25 -0
  481. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +55 -6
  482. package/src/runtime/routes/__tests__/user-route-dispatcher-host.test.ts +52 -1
  483. package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +133 -1
  484. package/src/runtime/routes/channel-availability-routes.ts +32 -14
  485. package/src/runtime/routes/channel-route-shared.ts +0 -6
  486. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +6 -6
  487. package/src/runtime/routes/conversation-list-routes.ts +181 -23
  488. package/src/runtime/routes/conversation-management-routes.ts +17 -12
  489. package/src/runtime/routes/conversation-query-routes.ts +40 -27
  490. package/src/runtime/routes/conversation-routes.ts +43 -29
  491. package/src/runtime/routes/credential-prompt-routes.ts +4 -7
  492. package/src/runtime/routes/credential-routes.ts +26 -82
  493. package/src/runtime/routes/default-provider-routes.ts +15 -0
  494. package/src/runtime/routes/documents-routes.ts +3 -222
  495. package/src/runtime/routes/inbound-message-handler.ts +21 -45
  496. package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +0 -1
  497. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +0 -9
  498. package/src/runtime/routes/inbound-stages/admission-policy.ts +1 -17
  499. package/src/runtime/routes/inbound-stages/bootstrap-intercept.test.ts +0 -1
  500. package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +2 -3
  501. package/src/runtime/routes/inbound-stages/edit-intercept.ts +101 -79
  502. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +0 -1
  503. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +12 -7
  504. package/src/runtime/routes/inbound-stages/reaction-intercept.test.ts +91 -8
  505. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +72 -79
  506. package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +2 -3
  507. package/src/runtime/routes/inference-profiles-routes.ts +20 -11
  508. package/src/runtime/routes/inference-provider-connection-routes.ts +77 -15
  509. package/src/runtime/routes/log-export-routes.ts +3 -0
  510. package/src/runtime/routes/mcp-auth-routes.ts +148 -57
  511. package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +2 -0
  512. package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +3 -0
  513. package/src/runtime/routes/playground/inject-failures.ts +2 -2
  514. package/src/runtime/routes/playground/reset-circuit.ts +1 -1
  515. package/src/runtime/routes/plugins-routes.ts +38 -8
  516. package/src/runtime/routes/schedule-routes.ts +94 -7
  517. package/src/runtime/routes/settings-routes.ts +6 -6
  518. package/src/runtime/routes/stt-routes.ts +31 -25
  519. package/src/runtime/routes/surface-action-routes.ts +20 -19
  520. package/src/runtime/routes/surface-conversation-resolver.ts +3 -0
  521. package/src/runtime/routes/user-route-dispatcher.ts +69 -15
  522. package/src/runtime/routes/user-route-import.ts +108 -0
  523. package/src/runtime/routes/user-route-resolution.ts +18 -1
  524. package/src/runtime/routes/workspace-routes.ts +0 -9
  525. package/src/runtime/routes/workspace-utils.ts +3 -13
  526. package/src/runtime/services/conversation-serializer.ts +7 -2
  527. package/src/schedule/__tests__/plugin-schedule-declarations.test.ts +68 -5
  528. package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +81 -0
  529. package/src/schedule/plugin-schedule-availability.ts +58 -0
  530. package/src/schedule/plugin-schedule-declarations.ts +23 -27
  531. package/src/schedule/plugin-schedule-reconciler.ts +12 -3
  532. package/src/schedule/schedule-store.ts +5 -1
  533. package/src/schedule/scheduler.ts +9 -4
  534. package/src/schedule/worker-control.ts +1 -0
  535. package/src/schedule/worker.ts +6 -0
  536. package/src/security/oauth2.ts +6 -22
  537. package/src/stt/__tests__/daemon-batch-transcriber.test.ts +22 -0
  538. package/src/stt/__tests__/types.test.ts +94 -0
  539. package/src/stt/daemon-batch-transcriber.ts +10 -0
  540. package/src/stt/stt-stream-session.ts +8 -4
  541. package/src/stt/types.ts +103 -0
  542. package/src/subagent/__tests__/consult-prompt.test.ts +26 -15
  543. package/src/subagent/consult-context.ts +11 -11
  544. package/src/subagent/consult-prompt.ts +26 -35
  545. package/src/subagent/manager.ts +21 -40
  546. package/src/subagent/notify.ts +7 -1
  547. package/src/subagent/types.ts +15 -12
  548. package/src/tools/__tests__/tool-input-schemas.test.ts +7 -7
  549. package/src/tools/acp/spawn.test.ts +97 -0
  550. package/src/tools/acp/spawn.ts +38 -4
  551. package/src/tools/credentials/ref-parse.ts +35 -0
  552. package/src/tools/credentials/resolve.ts +4 -10
  553. package/src/tools/credentials/store.ts +168 -0
  554. package/src/tools/document/document-tool.ts +12 -3
  555. package/src/tools/executor.ts +53 -15
  556. package/src/tools/filesystem/read.ts +27 -10
  557. package/src/tools/host-filesystem/read.ts +27 -15
  558. package/src/tools/network/url-safety.ts +9 -16
  559. package/src/tools/permission-checker.ts +43 -52
  560. package/src/tools/registry.ts +2 -1
  561. package/src/tools/shared/filesystem/file-ops-service.ts +63 -35
  562. package/src/tools/shared/filesystem/legacy-read-args.ts +22 -0
  563. package/src/tools/shared/filesystem/types.ts +5 -5
  564. package/src/tools/skills/scaffold-managed.ts +25 -7
  565. package/src/tools/subagent/spawn.ts +28 -88
  566. package/src/tools/tool-types.ts +26 -13
  567. package/src/tools/types.ts +6 -6
  568. package/src/tools/ui-surface/surface-shape-docs.ts +12 -1
  569. package/src/tools/workflows/run-workflow.ts +1 -2
  570. package/src/tts/__tests__/reasoning-tag-filter.test.ts +78 -0
  571. package/src/tts/reasoning-tag-filter.ts +89 -0
  572. package/src/util/__tests__/worker-process-command.test.ts +37 -0
  573. package/src/util/logger.ts +16 -0
  574. package/src/util/think-tag-stream.ts +95 -0
  575. package/src/util/worker-process.ts +37 -4
  576. package/src/windows-compiled-cli.ts +32 -0
  577. package/src/windows-compiled-entry.ts +4 -0
  578. package/src/windows-compiled-logger.ts +6 -0
  579. package/src/windows-compiled-worker-entry.ts +29 -0
  580. package/src/workspace/byok-default-profile-ensure.ts +76 -24
  581. package/src/workspace/custom-profile-ensure.ts +4 -24
  582. package/src/workspace/migrations/142-consolidate-voice-front-door.ts +70 -0
  583. package/src/workspace/migrations/143-repair-deprecated-codex-model-id.ts +134 -0
  584. package/src/workspace/migrations/144-convert-stranded-subscription-openai-profiles.ts +265 -0
  585. package/src/workspace/migrations/145-collapse-profile-bindings-to-entries.ts +328 -0
  586. package/src/workspace/migrations/146-repair-retired-fireworks-deepseek-flash-model-id.ts +195 -0
  587. package/src/workspace/migrations/__tests__/141-stt-english-default-to-multilingual.test.ts +0 -10
  588. package/src/workspace/migrations/registry.ts +10 -0
  589. package/src/workspace/provider-commit-message-generator.ts +7 -5
  590. package/src/__tests__/document-workspace-file.test.ts +0 -467
  591. package/src/live-voice/__tests__/front-decision.test.ts +0 -645
  592. package/src/live-voice/front-decision.ts +0 -476
  593. package/src/permissions/ipc-risk-types.ts +0 -143
  594. package/src/permissions/risk-types.ts +0 -76
  595. package/src/subagent/__tests__/consult-transcript.test.ts +0 -184
  596. package/src/subagent/consult-transcript.ts +0 -90
@@ -0,0 +1,679 @@
1
+ /**
2
+ * Deepgram Flux realtime streaming STT adapter (`wss://api.deepgram.com/v2/listen`).
3
+ *
4
+ * Flux is Deepgram's conversational speech API: the model itself decides where
5
+ * a turn ends, so this adapter carries no endpointing heuristics of its own.
6
+ * It owns the socket lifecycle (connect, teardown) and delegates
7
+ * every inbound transcript frame to {@link parseFluxFrame}, the pure protocol
8
+ * module, which maps Flux's wire shapes onto the daemon's
9
+ * {@link SttStreamServerEvent} contract.
10
+ *
11
+ * Lifecycle:
12
+ * 1. {@link start} opens the WebSocket and resolves once it is established.
13
+ * 2. {@link sendAudio} forwards raw audio with a backpressure guard.
14
+ * 3. {@link stop} sends `CloseStream` and waits for Flux to flush the turn
15
+ * in progress before closing.
16
+ * 4. The `onEvent` callback receives `partial`, `final`, the four
17
+ * turn-detection events, `error`, and finally `closed`.
18
+ *
19
+ * There is **no `finalizeUtterance`**. Flux commits a transcript only when
20
+ * its model closes a turn, and its wire protocol offers no mid-stream flush:
21
+ * `CloseStream` is the only way to make it answer for a turn still in
22
+ * progress. A method that returned `finalized` without flushing would claim a
23
+ * commit the provider never made and lose the tail of every turn released on
24
+ * a caller-side boundary, so the optional method is left off and callers
25
+ * feature-detect it and fall back to {@link stop}.
26
+ *
27
+ * There is also **no keepalive**. Flux accepts exactly two control messages,
28
+ * `CloseStream` and `Configure`; the v1 streaming `KeepAlive` is not one of
29
+ * them, and sending it earns an `UNPARSABLE_CLIENT_MESSAGE` error frame
30
+ * followed by a server close. Audio is the only thing that holds a Flux stream
31
+ * open, so a stream that has to survive a long silence must carry silent
32
+ * frames rather than a control message.
33
+ * See https://developers.deepgram.com/docs/flux/close-stream
34
+ *
35
+ * Error handling mirrors `deepgram-realtime.ts`: socket closes and errors map
36
+ * onto {@link SttErrorCategory} values (`auth`, `rate-limit`, `timeout`,
37
+ * `provider-error`), in-session failures surface as `error` events, and
38
+ * teardown always emits `closed`. One failure produces exactly one `error`:
39
+ * a fatal `Error` frame is reported with the provider's own diagnostic, and
40
+ * the close Deepgram sends immediately after it goes straight to `closed`.
41
+ */
42
+
43
+ import { getConfig } from "../../config/loader.js";
44
+ import type { LiveVoiceFluxConfig } from "../../config/schemas/live-voice.js";
45
+ import type {
46
+ StreamingTranscriber,
47
+ SttErrorCategory,
48
+ SttStreamServerEvent,
49
+ } from "../../stt/types.js";
50
+ import { SttError } from "../../stt/types.js";
51
+ import { getLogger } from "../../util/logger.js";
52
+ import type { FluxEncoding } from "./deepgram-flux-frames.js";
53
+ import {
54
+ buildFluxQueryParams,
55
+ parseFluxFrame,
56
+ } from "./deepgram-flux-frames.js";
57
+
58
+ const log = getLogger("deepgram-flux-realtime");
59
+
60
+ // ---------------------------------------------------------------------------
61
+ // Constants
62
+ // ---------------------------------------------------------------------------
63
+
64
+ const WS_BASE_URL = "wss://api.deepgram.com";
65
+
66
+ /** Flux lives on the v2 listen route; the v1 route speaks a different protocol. */
67
+ const FLUX_PATH = "/v2/listen";
68
+
69
+ /** Timeout (ms) for the WebSocket handshake before {@link start} rejects. */
70
+ const DEFAULT_CONNECT_TIMEOUT_MS = 10_000;
71
+
72
+ /**
73
+ * Inactivity timeout (ms). If audio has been sent but Flux says nothing back
74
+ * for this long, the adapter closes with a `timeout` error. A stream with no
75
+ * audio awaiting a response (mic gated while the assistant speaks) is
76
+ * legitimately silent and never times out.
77
+ */
78
+ const DEFAULT_INACTIVITY_TIMEOUT_MS = 30_000;
79
+
80
+ /** Outbound buffer ceiling (bytes) before {@link sendAudio} drops frames. */
81
+ const MAX_BUFFERED_AMOUNT = 1024 * 1024; // 1 MiB
82
+
83
+ /** Grace (ms) after `CloseStream` before the socket is force-closed. */
84
+ const CLOSE_GRACE_MS = 5_000;
85
+
86
+ /** Raw-audio encoding of the stream: clients send linear16 PCM. */
87
+ const AUDIO_ENCODING: FluxEncoding = "linear16";
88
+
89
+ /** Default sample rate (Hz) when the client negotiates none. */
90
+ const DEFAULT_SAMPLE_RATE = 16_000;
91
+
92
+ /** Bytes per sample of mono linear16 PCM. */
93
+ const LINEAR16_BYTES_PER_SAMPLE = 2;
94
+
95
+ /**
96
+ * Chunk duration Deepgram recommends for Flux. Logged alongside the observed
97
+ * duration so a runbook can compare the two without instrumenting capture.
98
+ */
99
+ const RECOMMENDED_CHUNK_MS = 80;
100
+
101
+ // ---------------------------------------------------------------------------
102
+ // Options
103
+ // ---------------------------------------------------------------------------
104
+
105
+ /**
106
+ * Transport-level wiring for a Flux session. Turn-detection tuning (model,
107
+ * thresholds, force-end timeout) is not here: it comes from `liveVoice.flux`,
108
+ * which the adapter reads itself, so there is exactly one place to turn those
109
+ * dials.
110
+ */
111
+ export interface DeepgramFluxRealtimeOptions {
112
+ /** Audio sample rate in Hz (default: 16000). */
113
+ sampleRate?: number;
114
+ /** Connect timeout in milliseconds. Default: 10_000. */
115
+ connectTimeoutMs?: number;
116
+ /** Inactivity timeout in milliseconds. Default: 30_000. */
117
+ inactivityTimeoutMs?: number;
118
+ }
119
+
120
+ // ---------------------------------------------------------------------------
121
+ // Minimal WebSocket interface
122
+ // ---------------------------------------------------------------------------
123
+
124
+ /**
125
+ * Minimal structural WebSocket interface so tests can substitute a mock
126
+ * without depending on Bun's global WebSocket type at the type level.
127
+ */
128
+ interface WsLike {
129
+ readonly readyState: number;
130
+ readonly bufferedAmount: number;
131
+ send(data: string | ArrayBufferLike | ArrayBuffer | Uint8Array): void;
132
+ close(code?: number, reason?: string): void;
133
+ addEventListener(type: "open", listener: () => void): void;
134
+ addEventListener(
135
+ type: "close",
136
+ listener: (ev: { code: number; reason: string }) => void,
137
+ ): void;
138
+ addEventListener(type: "error", listener: (ev: unknown) => void): void;
139
+ addEventListener(
140
+ type: "message",
141
+ listener: (ev: { data: unknown }) => void,
142
+ ): void;
143
+ removeEventListener(type: string, listener: unknown): void;
144
+ }
145
+
146
+ const WS_OPEN = 1;
147
+
148
+ // ---------------------------------------------------------------------------
149
+ // Adapter implementation
150
+ // ---------------------------------------------------------------------------
151
+
152
+ /**
153
+ * Deepgram Flux streaming transcriber.
154
+ *
155
+ * Implements the daemon {@link StreamingTranscriber} contract on top of
156
+ * Deepgram's conversational `/v2/listen` WebSocket API.
157
+ */
158
+ export class DeepgramFluxRealtimeTranscriber implements StreamingTranscriber {
159
+ readonly providerId = "deepgram-flux" as const;
160
+ readonly boundaryId = "daemon-streaming" as const;
161
+
162
+ private readonly apiKey: string;
163
+ /** Turn-detection tuning, snapshotted when the transcriber is built. */
164
+ private readonly flux: LiveVoiceFluxConfig;
165
+ private readonly sampleRate: number;
166
+ private readonly connectTimeoutMs: number;
167
+ private readonly inactivityTimeoutMs: number;
168
+
169
+ /** The live WebSocket connection, set during start(). */
170
+ private ws: WsLike | null = null;
171
+
172
+ /** Callback for emitting events to the session orchestrator. */
173
+ private onEvent: ((event: SttStreamServerEvent) => void) | null = null;
174
+
175
+ /** Whether the session has been fully closed. */
176
+ private closed = false;
177
+
178
+ /** Whether stop() has been called. */
179
+ private stopping = false;
180
+
181
+ /**
182
+ * Whether a fatal `Error` frame has already been reported as an `error`
183
+ * event. Deepgram closes the socket right after that frame, so the close
184
+ * that follows carries no information the provider's own diagnostic did not
185
+ * already carry and must not raise a second, more generic error.
186
+ */
187
+ private fatalErrorReported = false;
188
+
189
+ /** Whether the per-session chunk-cadence line has already been logged. */
190
+ private chunkCadenceLogged = false;
191
+
192
+ /** Inactivity timer handle. */
193
+ private inactivityTimer: ReturnType<typeof setTimeout> | null = null;
194
+
195
+ /**
196
+ * When the first audio frame went out after the last inbound provider
197
+ * message; null while nothing is owed a response. The inactivity watchdog
198
+ * only rules "hung" while this is set: Flux says nothing during silence,
199
+ * so inbound quiet alone is not evidence of a hang.
200
+ */
201
+ private awaitingResponseSinceMs: number | null = null;
202
+
203
+ /** Close grace timer handle. */
204
+ private closeGraceTimer: ReturnType<typeof setTimeout> | null = null;
205
+
206
+ constructor(apiKey: string, options: DeepgramFluxRealtimeOptions = {}) {
207
+ this.apiKey = apiKey;
208
+ this.flux = getConfig().liveVoice.flux;
209
+ this.sampleRate = options.sampleRate ?? DEFAULT_SAMPLE_RATE;
210
+ this.connectTimeoutMs =
211
+ options.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS;
212
+ this.inactivityTimeoutMs =
213
+ options.inactivityTimeoutMs ?? DEFAULT_INACTIVITY_TIMEOUT_MS;
214
+ }
215
+
216
+ // ── StreamingTranscriber interface ──────────────────────────────────
217
+
218
+ async start(onEvent: (event: SttStreamServerEvent) => void): Promise<void> {
219
+ if (this.ws) {
220
+ throw new Error("DeepgramFluxRealtimeTranscriber: start() called twice");
221
+ }
222
+ this.onEvent = onEvent;
223
+
224
+ const url = this.buildWebSocketUrl();
225
+ log.info({ url }, "Opening Deepgram Flux session");
226
+
227
+ const ws = this.createWebSocket(url);
228
+ this.ws = ws;
229
+
230
+ // Wait for the WebSocket to open or fail. Failures reject as SttError so
231
+ // the caller gets the same normalized category an in-session failure
232
+ // would carry. A bad key is an `auth` problem whether it lands during
233
+ // the handshake or after it.
234
+ await new Promise<void>((resolve, reject) => {
235
+ let settled = false;
236
+
237
+ const connectTimer = setTimeout(() => {
238
+ if (settled) {
239
+ return;
240
+ }
241
+ settled = true;
242
+ this.forceClose();
243
+ reject(
244
+ new SttError("timeout", "Deepgram Flux realtime connect timeout"),
245
+ );
246
+ }, this.connectTimeoutMs);
247
+
248
+ const onOpen = () => {
249
+ if (settled) {
250
+ return;
251
+ }
252
+ settled = true;
253
+ clearTimeout(connectTimer);
254
+ resolve();
255
+ };
256
+
257
+ const onError = (ev: unknown) => {
258
+ if (settled) {
259
+ return;
260
+ }
261
+ settled = true;
262
+ clearTimeout(connectTimer);
263
+ reject(
264
+ new SttError(
265
+ "provider-error",
266
+ `Deepgram Flux realtime connect error: ${describeSocketEvent(ev)}`,
267
+ ),
268
+ );
269
+ };
270
+
271
+ const onClose = (ev: { code: number; reason: string }) => {
272
+ if (settled) {
273
+ return;
274
+ }
275
+ settled = true;
276
+ clearTimeout(connectTimer);
277
+ reject(
278
+ new SttError(
279
+ closeCodeCategory(ev.code),
280
+ `Deepgram Flux WebSocket closed before open (code=${ev.code}, reason=${ev.reason})`,
281
+ ),
282
+ );
283
+ };
284
+
285
+ ws.addEventListener("open", onOpen);
286
+ ws.addEventListener("error", onError);
287
+ ws.addEventListener("close", onClose);
288
+ });
289
+
290
+ // Socket is open. Attach the handlers for the active session lifetime.
291
+ this.attachSessionHandlers(ws);
292
+ this.resetInactivityTimer();
293
+
294
+ log.info({ model: this.flux.model }, "Deepgram Flux session opened");
295
+ }
296
+
297
+ sendAudio(audio: Buffer, _mimeType: string): void {
298
+ if (this.closed || this.stopping) {
299
+ return;
300
+ }
301
+
302
+ const ws = this.ws;
303
+ if (!ws || ws.readyState !== WS_OPEN) {
304
+ return;
305
+ }
306
+
307
+ // Backpressure check: drop frames rather than grow the outbound buffer
308
+ // without bound when the network cannot keep up with the audio rate.
309
+ if (ws.bufferedAmount > MAX_BUFFERED_AMOUNT) {
310
+ log.warn(
311
+ { bufferedAmount: ws.bufferedAmount },
312
+ "Deepgram Flux backpressure: dropping audio frame",
313
+ );
314
+ return;
315
+ }
316
+
317
+ ws.send(new Uint8Array(audio));
318
+ this.awaitingResponseSinceMs ??= Date.now();
319
+ this.logChunkCadenceOnce(audio.byteLength);
320
+ }
321
+
322
+ stop(): void {
323
+ if (this.closed || this.stopping) {
324
+ return;
325
+ }
326
+ this.stopping = true;
327
+
328
+ log.info("Stopping Deepgram Flux session");
329
+
330
+ const ws = this.ws;
331
+ if (!ws || ws.readyState !== WS_OPEN) {
332
+ this.emitClosedAndCleanup();
333
+ return;
334
+ }
335
+
336
+ // `CloseStream` tells Flux to finish the turn in progress and answer
337
+ // before it terminates the socket.
338
+ try {
339
+ ws.send(JSON.stringify({ type: "CloseStream" }));
340
+ } catch {
341
+ this.emitClosedAndCleanup();
342
+ return;
343
+ }
344
+
345
+ this.closeGraceTimer = setTimeout(() => {
346
+ log.warn("Deepgram Flux close grace timeout, forcing close");
347
+ this.emitClosedAndCleanup();
348
+ }, CLOSE_GRACE_MS);
349
+ }
350
+
351
+ // ── WebSocket lifecycle ─────────────────────────────────────────────
352
+
353
+ /**
354
+ * Create a WebSocket instance. Factored out for test mockability.
355
+ *
356
+ * The key travels in the `Authorization: Token` header. Flux needs no
357
+ * query auth, so no URL ever carries the credential and nothing here needs
358
+ * redacting before it reaches a log.
359
+ */
360
+ private createWebSocket(url: string): WsLike {
361
+ const WebSocketCtor = (
362
+ globalThis as unknown as {
363
+ WebSocket: new (
364
+ url: string,
365
+ options?: { headers?: Record<string, string> },
366
+ ) => WsLike;
367
+ }
368
+ ).WebSocket;
369
+ if (typeof WebSocketCtor !== "function") {
370
+ throw new Error("global WebSocket is not available in this runtime");
371
+ }
372
+ return new WebSocketCtor(url, {
373
+ headers: { Authorization: `Token ${this.apiKey}` },
374
+ });
375
+ }
376
+
377
+ private attachSessionHandlers(ws: WsLike): void {
378
+ ws.addEventListener("message", (ev: { data: unknown }) => {
379
+ this.handleProviderMessage(ev.data);
380
+ });
381
+
382
+ ws.addEventListener("close", (ev: { code: number; reason: string }) => {
383
+ this.handleProviderClose(ev.code, ev.reason);
384
+ });
385
+
386
+ ws.addEventListener("error", (ev: unknown) => {
387
+ this.handleProviderError(ev);
388
+ });
389
+ }
390
+
391
+ // ── Provider message handling ───────────────────────────────────────
392
+
393
+ /**
394
+ * Normalize one inbound Flux frame into daemon events.
395
+ *
396
+ * Frames go straight to {@link parseFluxFrame}, which owns JSON decoding,
397
+ * the wire shapes, and the graceful handling of anything it does not
398
+ * recognize.
399
+ */
400
+ private handleProviderMessage(data: unknown): void {
401
+ if (this.closed) {
402
+ return;
403
+ }
404
+
405
+ this.resetInactivityTimer();
406
+
407
+ const raw =
408
+ typeof data === "string"
409
+ ? data
410
+ : data instanceof ArrayBuffer
411
+ ? new TextDecoder().decode(data)
412
+ : null;
413
+ if (raw === null) {
414
+ // Unexpected binary format, ignore.
415
+ return;
416
+ }
417
+
418
+ for (const event of parseFluxFrame(raw)) {
419
+ if (event.type === "error") {
420
+ this.fatalErrorReported = true;
421
+ }
422
+ this.emitEvent(event);
423
+ }
424
+ }
425
+
426
+ /** Handle provider-side WebSocket close. */
427
+ private handleProviderClose(code: number, reason: string): void {
428
+ if (this.closed) {
429
+ return;
430
+ }
431
+
432
+ // Normal close (1000) or going-away (1001) after stop() is expected.
433
+ if (this.stopping && (code === 1000 || code === 1001)) {
434
+ log.info({ code, reason }, "Deepgram Flux session closed normally");
435
+ this.emitClosedAndCleanup();
436
+ return;
437
+ }
438
+
439
+ // A fatal `Error` frame already reported the provider's own diagnostic,
440
+ // and the close is that frame's second half. Go straight to `closed` so
441
+ // callers see exactly one error for one failure.
442
+ if (this.fatalErrorReported) {
443
+ log.info(
444
+ { code, reason },
445
+ "Deepgram Flux session closed after a fatal error frame",
446
+ );
447
+ this.emitClosedAndCleanup();
448
+ return;
449
+ }
450
+
451
+ log.warn({ code, reason }, "Deepgram Flux session closed unexpectedly");
452
+
453
+ this.emitEvent({
454
+ type: "error",
455
+ category: closeCodeCategory(code),
456
+ message: `Deepgram Flux WebSocket closed (code=${code}, reason=${reason})`,
457
+ });
458
+ this.emitClosedAndCleanup();
459
+ }
460
+
461
+ /** Handle provider-side WebSocket error. */
462
+ private handleProviderError(ev: unknown): void {
463
+ if (this.closed) {
464
+ return;
465
+ }
466
+
467
+ const message = describeSocketEvent(ev);
468
+
469
+ // Same one-error-per-failure rule as the close path: a socket error that
470
+ // trails a fatal `Error` frame is that failure surfacing again.
471
+ if (this.fatalErrorReported) {
472
+ log.info(
473
+ { error: message },
474
+ "Deepgram Flux WebSocket error after a fatal error frame",
475
+ );
476
+ this.emitClosedAndCleanup();
477
+ return;
478
+ }
479
+
480
+ log.error({ error: message }, "Deepgram Flux WebSocket error");
481
+
482
+ this.emitEvent({
483
+ type: "error",
484
+ category: "provider-error",
485
+ message: `Deepgram Flux WebSocket error: ${message}`,
486
+ });
487
+ this.emitClosedAndCleanup();
488
+ }
489
+
490
+ // ── Event emission & cleanup ────────────────────────────────────────
491
+
492
+ /**
493
+ * Emit a server event to the session orchestrator. Swallows listener errors
494
+ * so a bad consumer cannot tear down the adapter.
495
+ */
496
+ private emitEvent(event: SttStreamServerEvent): void {
497
+ if (!this.onEvent) {
498
+ return;
499
+ }
500
+ try {
501
+ this.onEvent(event);
502
+ } catch (err) {
503
+ log.warn({ error: err }, "Listener error in Deepgram Flux adapter");
504
+ }
505
+ }
506
+
507
+ /**
508
+ * Log the observed audio chunk duration once per session, next to the
509
+ * cadence Deepgram recommends for Flux. Capture cadence is a client
510
+ * concern; this only makes it measurable from the daemon side.
511
+ */
512
+ private logChunkCadenceOnce(byteLength: number): void {
513
+ if (this.chunkCadenceLogged) {
514
+ return;
515
+ }
516
+ this.chunkCadenceLogged = true;
517
+
518
+ const observedChunkMs =
519
+ this.sampleRate > 0
520
+ ? Math.round(
521
+ (byteLength / LINEAR16_BYTES_PER_SAMPLE / this.sampleRate) * 1_000,
522
+ )
523
+ : undefined;
524
+
525
+ log.info(
526
+ {
527
+ byteLength,
528
+ sampleRate: this.sampleRate,
529
+ encoding: AUDIO_ENCODING,
530
+ observedChunkMs,
531
+ recommendedChunkMs: RECOMMENDED_CHUNK_MS,
532
+ },
533
+ "Deepgram Flux audio chunk cadence",
534
+ );
535
+ }
536
+
537
+ /**
538
+ * Emit `closed` and release every resource. Idempotent, safe to call from
539
+ * any teardown path.
540
+ */
541
+ private emitClosedAndCleanup(): void {
542
+ if (this.closed) {
543
+ return;
544
+ }
545
+ this.closed = true;
546
+
547
+ this.clearTimers();
548
+ this.forceClose();
549
+
550
+ this.emitEvent({ type: "closed" });
551
+ this.onEvent = null;
552
+ }
553
+
554
+ /** Force-close the WebSocket without emitting events. */
555
+ private forceClose(): void {
556
+ const ws = this.ws;
557
+ this.ws = null;
558
+ if (!ws) {
559
+ return;
560
+ }
561
+
562
+ try {
563
+ ws.close();
564
+ } catch {
565
+ // Best effort: already closed sockets may throw.
566
+ }
567
+ }
568
+
569
+ private clearTimers(): void {
570
+ if (this.inactivityTimer !== null) {
571
+ clearTimeout(this.inactivityTimer);
572
+ this.inactivityTimer = null;
573
+ }
574
+ if (this.closeGraceTimer !== null) {
575
+ clearTimeout(this.closeGraceTimer);
576
+ this.closeGraceTimer = null;
577
+ }
578
+ }
579
+
580
+ /**
581
+ * Reset the inactivity watchdog on inbound provider messages. Not reset on
582
+ * outbound audio: continuous audio from the caller must not mask a silent
583
+ * provider.
584
+ */
585
+ private resetInactivityTimer(): void {
586
+ this.awaitingResponseSinceMs = null;
587
+ this.armInactivityTimer(this.inactivityTimeoutMs);
588
+ }
589
+
590
+ /**
591
+ * (Re)arm the inactivity watchdog. On fire it only rules "hung" when audio
592
+ * has been awaiting a response for a full timeout window; otherwise the
593
+ * stream is merely idle and the timer re-arms for the remainder.
594
+ */
595
+ private armInactivityTimer(delayMs: number): void {
596
+ if (this.closed || this.stopping) {
597
+ return;
598
+ }
599
+
600
+ if (this.inactivityTimer !== null) {
601
+ clearTimeout(this.inactivityTimer);
602
+ }
603
+
604
+ this.inactivityTimer = setTimeout(() => {
605
+ if (this.closed) {
606
+ return;
607
+ }
608
+
609
+ const since = this.awaitingResponseSinceMs;
610
+ if (since === null) {
611
+ this.armInactivityTimer(this.inactivityTimeoutMs);
612
+ return;
613
+ }
614
+ const waitedMs = Date.now() - since;
615
+ if (waitedMs < this.inactivityTimeoutMs) {
616
+ this.armInactivityTimer(this.inactivityTimeoutMs - waitedMs);
617
+ return;
618
+ }
619
+
620
+ log.warn("Deepgram Flux inactivity timeout");
621
+ this.emitEvent({
622
+ type: "error",
623
+ category: "timeout",
624
+ message: "Deepgram Flux session timed out due to inactivity",
625
+ });
626
+ this.emitClosedAndCleanup();
627
+ }, delayMs);
628
+ }
629
+
630
+ // ── URL construction ────────────────────────────────────────────────
631
+
632
+ /**
633
+ * Build the Flux WebSocket URL. Query construction (including threshold
634
+ * clamping and omitting `eager_eot_threshold` when unset) belongs to
635
+ * {@link buildFluxQueryParams}.
636
+ */
637
+ private buildWebSocketUrl(): string {
638
+ const query = buildFluxQueryParams({
639
+ model: this.flux.model,
640
+ encoding: AUDIO_ENCODING,
641
+ sampleRate: this.sampleRate,
642
+ eotThreshold: this.flux.eotThreshold,
643
+ eagerEotThreshold: this.flux.eagerEotThreshold,
644
+ eotTimeoutMs: this.flux.eotTimeoutMs,
645
+ });
646
+ return `${WS_BASE_URL}${FLUX_PATH}?${query}`;
647
+ }
648
+ }
649
+
650
+ // ---------------------------------------------------------------------------
651
+ // Helpers
652
+ // ---------------------------------------------------------------------------
653
+
654
+ /**
655
+ * Map a WebSocket close code onto a normalized STT error category, matching
656
+ * how `DeepgramRealtimeTranscriber` classifies the same codes: 1008 (policy
657
+ * violation) and 4001 are how Deepgram rejects credentials, and 1013 (try
658
+ * again later) is how it sheds load.
659
+ */
660
+ function closeCodeCategory(code: number): SttErrorCategory {
661
+ if (code === 1008 || code === 4001) {
662
+ return "auth";
663
+ }
664
+ if (code === 1013) {
665
+ return "rate-limit";
666
+ }
667
+ return "provider-error";
668
+ }
669
+
670
+ /** Best-effort human-readable text for a WebSocket error event. */
671
+ function describeSocketEvent(ev: unknown): string {
672
+ if (ev instanceof Error) {
673
+ return ev.message;
674
+ }
675
+ if (typeof ev === "object" && ev !== null && "message" in ev) {
676
+ return String((ev as { message: unknown }).message);
677
+ }
678
+ return "WebSocket error";
679
+ }