@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,625 @@
1
+ import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
2
+
3
+ import { setConfig } from "../../../__tests__/helpers/set-config.js";
4
+ import type {
5
+ StreamingTranscriber,
6
+ SttStreamServerEvent,
7
+ } from "../../../stt/types.js";
8
+ import { SttError } from "../../../stt/types.js";
9
+
10
+ // ---------------------------------------------------------------------------
11
+ // Logger mock: must be declared before the subject import
12
+ //
13
+ // The adapter's only observable output for the chunk-cadence measurement is a
14
+ // log line, so it is asserted through captured logs.
15
+ // ---------------------------------------------------------------------------
16
+
17
+ interface CapturedLog {
18
+ data: unknown;
19
+ message: string;
20
+ }
21
+
22
+ const infoLogs: CapturedLog[] = [];
23
+
24
+ function capture(sink: CapturedLog[]) {
25
+ return (data: unknown, message?: string) => {
26
+ sink.push(
27
+ typeof data === "string"
28
+ ? { data: undefined, message: data }
29
+ : { data, message: message ?? "" },
30
+ );
31
+ };
32
+ }
33
+
34
+ mock.module("../../../util/logger.js", () => {
35
+ const logger = {
36
+ info: capture(infoLogs),
37
+ debug: () => {},
38
+ warn: () => {},
39
+ error: () => {},
40
+ trace: () => {},
41
+ fatal: () => {},
42
+ child: () => logger,
43
+ };
44
+ return { getLogger: () => logger };
45
+ });
46
+
47
+ // ---------------------------------------------------------------------------
48
+ // Mock WebSocket
49
+ // ---------------------------------------------------------------------------
50
+
51
+ type WsEventType = "open" | "close" | "error" | "message";
52
+ type WsListener = (...args: unknown[]) => void;
53
+
54
+ /** Minimal mock WebSocket standing in for Deepgram's `/v2/listen` endpoint. */
55
+ class MockWebSocket {
56
+ readyState = 0; // CONNECTING
57
+ bufferedAmount = 0;
58
+
59
+ /** Everything passed to `.send()`, in order. */
60
+ sentData: (string | Uint8Array)[] = [];
61
+
62
+ private listeners = new Map<WsEventType, WsListener[]>();
63
+
64
+ addEventListener(type: WsEventType, listener: WsListener): void {
65
+ const list = this.listeners.get(type) ?? [];
66
+ list.push(listener);
67
+ this.listeners.set(type, list);
68
+ }
69
+
70
+ removeEventListener(type: string, listener: unknown): void {
71
+ const list = this.listeners.get(type as WsEventType);
72
+ const idx = list?.indexOf(listener as WsListener) ?? -1;
73
+ if (list && idx !== -1) {
74
+ list.splice(idx, 1);
75
+ }
76
+ }
77
+
78
+ send(data: string | Uint8Array): void {
79
+ if (this.readyState !== 1) {
80
+ throw new Error("WebSocket is not open");
81
+ }
82
+ this.sentData.push(data);
83
+ }
84
+
85
+ close(): void {
86
+ this.readyState = 3; // CLOSED
87
+ }
88
+
89
+ // ── Test helpers ──────────────────────────────────────────────────
90
+
91
+ simulateOpen(): void {
92
+ this.readyState = 1; // OPEN
93
+ for (const l of this.listeners.get("open") ?? []) {
94
+ l();
95
+ }
96
+ }
97
+
98
+ simulateMessage(data: string): void {
99
+ for (const l of this.listeners.get("message") ?? []) {
100
+ l({ data });
101
+ }
102
+ }
103
+
104
+ simulateClose(code = 1000, reason = ""): void {
105
+ this.readyState = 3;
106
+ for (const l of this.listeners.get("close") ?? []) {
107
+ l({ code, reason });
108
+ }
109
+ }
110
+
111
+ simulateError(err: unknown): void {
112
+ for (const l of this.listeners.get("error") ?? []) {
113
+ l(err);
114
+ }
115
+ }
116
+ }
117
+
118
+ // ---------------------------------------------------------------------------
119
+ // Subject import (after mocks)
120
+ // ---------------------------------------------------------------------------
121
+
122
+ const { DeepgramFluxRealtimeTranscriber } =
123
+ await import("../deepgram-flux-realtime.js");
124
+
125
+ const TEST_API_KEY = "dg-flux-test-key";
126
+
127
+ // ---------------------------------------------------------------------------
128
+ // Helpers
129
+ // ---------------------------------------------------------------------------
130
+
131
+ /** Build a Flux `TurnInfo` frame carrying the given turn state. */
132
+ function turnInfoFrame(
133
+ event: string,
134
+ extra: Record<string, unknown> = {},
135
+ ): string {
136
+ return JSON.stringify({
137
+ type: "TurnInfo",
138
+ event,
139
+ request_id: "flux-request-id",
140
+ turn_index: 0,
141
+ audio_window_start: 0,
142
+ audio_window_end: 1.5,
143
+ ...extra,
144
+ });
145
+ }
146
+
147
+ describe("DeepgramFluxRealtimeTranscriber", () => {
148
+ let mockWs: MockWebSocket;
149
+ let dialedUrls: string[];
150
+ let dialedOptions: ({ headers?: Record<string, string> } | undefined)[];
151
+ let originalWebSocket: unknown;
152
+
153
+ beforeEach(() => {
154
+ // A fully default config: the acceptance criteria are about what the
155
+ // shipped defaults dial.
156
+ setConfig("liveVoice", {});
157
+
158
+ mockWs = new MockWebSocket();
159
+ dialedUrls = [];
160
+ dialedOptions = [];
161
+ infoLogs.length = 0;
162
+
163
+ originalWebSocket = (globalThis as Record<string, unknown>).WebSocket;
164
+ (globalThis as Record<string, unknown>).WebSocket = class {
165
+ constructor(url: string, options?: { headers?: Record<string, string> }) {
166
+ dialedUrls.push(url);
167
+ dialedOptions.push(options);
168
+ return mockWs;
169
+ }
170
+ };
171
+ });
172
+
173
+ afterEach(() => {
174
+ (globalThis as Record<string, unknown>).WebSocket = originalWebSocket;
175
+ });
176
+
177
+ async function startSession(
178
+ options: ConstructorParameters<
179
+ typeof DeepgramFluxRealtimeTranscriber
180
+ >[1] = {},
181
+ ): Promise<{
182
+ transcriber: InstanceType<typeof DeepgramFluxRealtimeTranscriber>;
183
+ events: SttStreamServerEvent[];
184
+ }> {
185
+ const transcriber = new DeepgramFluxRealtimeTranscriber(TEST_API_KEY, {
186
+ // Long enough that no watchdog fires mid-test.
187
+ inactivityTimeoutMs: 60_000,
188
+ ...options,
189
+ });
190
+ const events: SttStreamServerEvent[] = [];
191
+
192
+ const startPromise = transcriber.start((event) => events.push(event));
193
+ mockWs.simulateOpen();
194
+ await startPromise;
195
+
196
+ return { transcriber, events };
197
+ }
198
+
199
+ // ─────────────────────────────────────────────────────────────────
200
+ // Dialed URL
201
+ // ─────────────────────────────────────────────────────────────────
202
+
203
+ describe("session URL", () => {
204
+ test("dials /v2/listen with the default Flux tuning and no eager threshold", async () => {
205
+ await startSession();
206
+
207
+ expect(dialedUrls).toHaveLength(1);
208
+ const url = new URL(dialedUrls[0]!);
209
+
210
+ expect(url.protocol).toBe("wss:");
211
+ expect(url.host).toBe("api.deepgram.com");
212
+ expect(url.pathname).toBe("/v2/listen");
213
+ expect(url.searchParams.get("model")).toBe("flux-general-en");
214
+ expect(url.searchParams.get("eot_threshold")).toBe("0.7");
215
+ expect(url.searchParams.get("eot_timeout_ms")).toBe("5000");
216
+ // Unset eager threshold is what keeps Deepgram from speculating turns.
217
+ expect(url.searchParams.has("eager_eot_threshold")).toBe(false);
218
+ });
219
+
220
+ test("sends the negotiated raw-audio format", async () => {
221
+ await startSession({ sampleRate: 24_000 });
222
+
223
+ const url = new URL(dialedUrls[0]!);
224
+ expect(url.searchParams.get("encoding")).toBe("linear16");
225
+ expect(url.searchParams.get("sample_rate")).toBe("24000");
226
+ });
227
+
228
+ test("authenticates with the Token header, never the query string", async () => {
229
+ await startSession();
230
+
231
+ expect(dialedOptions[0]?.headers?.Authorization).toBe(
232
+ `Token ${TEST_API_KEY}`,
233
+ );
234
+ expect(dialedUrls[0]).not.toContain(TEST_API_KEY);
235
+ });
236
+
237
+ test("takes its tuning from liveVoice.flux", async () => {
238
+ setConfig("liveVoice", {
239
+ flux: {
240
+ model: "flux-general-multi",
241
+ eotThreshold: 0.9,
242
+ eagerEotThreshold: 0.4,
243
+ eotTimeoutMs: 1_500,
244
+ },
245
+ });
246
+
247
+ await startSession();
248
+
249
+ const url = new URL(dialedUrls[0]!);
250
+ expect(url.searchParams.get("model")).toBe("flux-general-multi");
251
+ expect(url.searchParams.get("eot_threshold")).toBe("0.9");
252
+ expect(url.searchParams.get("eager_eot_threshold")).toBe("0.4");
253
+ expect(url.searchParams.get("eot_timeout_ms")).toBe("1500");
254
+ });
255
+ });
256
+
257
+ // ─────────────────────────────────────────────────────────────────
258
+ // Audio forwarding
259
+ // ─────────────────────────────────────────────────────────────────
260
+
261
+ describe("audio forwarding", () => {
262
+ test("forwards audio chunks to the socket as raw bytes", async () => {
263
+ const { transcriber } = await startSession();
264
+
265
+ transcriber.sendAudio(Buffer.from([1, 2, 3, 4]), "audio/pcm");
266
+ transcriber.sendAudio(Buffer.from([5, 6]), "audio/pcm");
267
+
268
+ expect(mockWs.sentData).toHaveLength(2);
269
+ expect(Array.from(mockWs.sentData[0] as Uint8Array)).toEqual([
270
+ 1, 2, 3, 4,
271
+ ]);
272
+ expect(Array.from(mockWs.sentData[1] as Uint8Array)).toEqual([5, 6]);
273
+ });
274
+
275
+ test("drops audio once the outbound buffer is saturated", async () => {
276
+ const { transcriber } = await startSession();
277
+ mockWs.bufferedAmount = 8 * 1024 * 1024;
278
+
279
+ transcriber.sendAudio(Buffer.from([1, 2]), "audio/pcm");
280
+
281
+ expect(mockWs.sentData).toHaveLength(0);
282
+ });
283
+
284
+ test("logs the observed chunk duration once per session at info", async () => {
285
+ const { transcriber } = await startSession({ sampleRate: 16_000 });
286
+
287
+ // 2560 bytes of mono linear16 at 16kHz is exactly 80ms.
288
+ transcriber.sendAudio(Buffer.alloc(2560), "audio/pcm");
289
+ transcriber.sendAudio(Buffer.alloc(2560), "audio/pcm");
290
+
291
+ // The runbook reads this line off a default daemon, whose pino level is
292
+ // `info`: at debug it would never reach the log it is read from.
293
+ const cadenceLogs = infoLogs.filter((entry) =>
294
+ entry.message.includes("chunk cadence"),
295
+ );
296
+ expect(cadenceLogs).toHaveLength(1);
297
+ expect(cadenceLogs[0]!.data).toMatchObject({
298
+ observedChunkMs: 80,
299
+ recommendedChunkMs: 80,
300
+ sampleRate: 16_000,
301
+ });
302
+ });
303
+ });
304
+
305
+ // ─────────────────────────────────────────────────────────────────
306
+ // Frame handling
307
+ // ─────────────────────────────────────────────────────────────────
308
+
309
+ describe("frame handling", () => {
310
+ test("a scripted turn produces the expected event stream", async () => {
311
+ const { events } = await startSession();
312
+
313
+ mockWs.simulateMessage(
314
+ JSON.stringify({ type: "Connected", request_id: "flux-request-id" }),
315
+ );
316
+ mockWs.simulateMessage(turnInfoFrame("StartOfTurn"));
317
+ mockWs.simulateMessage(
318
+ turnInfoFrame("Update", { transcript: "what is the" }),
319
+ );
320
+ mockWs.simulateMessage(
321
+ turnInfoFrame("EndOfTurn", {
322
+ transcript: "what is the weather",
323
+ end_of_turn_confidence: 0.91,
324
+ }),
325
+ );
326
+
327
+ expect(events).toEqual([
328
+ { type: "turn-start", turnIndex: 0 },
329
+ { type: "partial", text: "what is the" },
330
+ // The final comes first so a consumer that ignores turn events still
331
+ // commits the transcript exactly as it does for any other provider.
332
+ { type: "final", text: "what is the weather" },
333
+ {
334
+ type: "turn-end",
335
+ text: "what is the weather",
336
+ confidence: 0.91,
337
+ turnIndex: 0,
338
+ },
339
+ ]);
340
+ });
341
+
342
+ test("an EndOfTurn frame emits final before turn-end", async () => {
343
+ const { events } = await startSession();
344
+
345
+ mockWs.simulateMessage(
346
+ turnInfoFrame("EndOfTurn", { transcript: "done" }),
347
+ );
348
+
349
+ expect(events.map((event) => event.type)).toEqual(["final", "turn-end"]);
350
+ });
351
+
352
+ test("a fatal Error frame surfaces its category", async () => {
353
+ const { events } = await startSession();
354
+
355
+ mockWs.simulateMessage(
356
+ JSON.stringify({
357
+ type: "Error",
358
+ code: "INVALID_AUTH",
359
+ description: "Invalid credentials",
360
+ }),
361
+ );
362
+
363
+ expect(events).toHaveLength(1);
364
+ expect(events[0]).toMatchObject({ type: "error", category: "auth" });
365
+ });
366
+
367
+ test("unrecognized and malformed frames leave the session transcribing", async () => {
368
+ const { events } = await startSession();
369
+
370
+ // Deepgram can add frame types without warning, so anything the parser
371
+ // does not recognize is dropped rather than raised as a stream failure.
372
+ mockWs.simulateMessage("not json at all");
373
+ mockWs.simulateMessage(JSON.stringify([1, 2, 3]));
374
+ mockWs.simulateMessage(
375
+ JSON.stringify({ type: "SomethingNewFromDeepgram" }),
376
+ );
377
+
378
+ expect(events).toEqual([]);
379
+
380
+ mockWs.simulateMessage(
381
+ turnInfoFrame("EndOfTurn", { transcript: "still here" }),
382
+ );
383
+ expect(events).toEqual([
384
+ { type: "final", text: "still here" },
385
+ { type: "turn-end", text: "still here", turnIndex: 0 },
386
+ ]);
387
+ });
388
+ });
389
+
390
+ // ─────────────────────────────────────────────────────────────────
391
+ // Error mapping
392
+ // ─────────────────────────────────────────────────────────────────
393
+
394
+ describe("error mapping", () => {
395
+ test("an auth rejection during connect rejects with an auth SttError", async () => {
396
+ const transcriber = new DeepgramFluxRealtimeTranscriber(TEST_API_KEY);
397
+ const startPromise = transcriber.start(() => {});
398
+ mockWs.simulateClose(1008, "invalid credentials");
399
+
400
+ const err = await startPromise.catch((e: unknown) => e);
401
+ expect(err).toBeInstanceOf(SttError);
402
+ expect((err as SttError).category).toBe("auth");
403
+ });
404
+
405
+ test("a connect timeout rejects with a timeout SttError", async () => {
406
+ const transcriber = new DeepgramFluxRealtimeTranscriber(TEST_API_KEY, {
407
+ connectTimeoutMs: 20,
408
+ });
409
+
410
+ const err = await transcriber.start(() => {}).catch((e: unknown) => e);
411
+ expect(err).toBeInstanceOf(SttError);
412
+ expect((err as SttError).category).toBe("timeout");
413
+ });
414
+
415
+ test("an in-session auth close emits an auth error then closed", async () => {
416
+ const { events } = await startSession();
417
+
418
+ mockWs.simulateClose(1008, "invalid credentials");
419
+
420
+ expect(events).toHaveLength(2);
421
+ expect(events[0]).toMatchObject({ type: "error", category: "auth" });
422
+ expect(events[1]).toEqual({ type: "closed" });
423
+ });
424
+
425
+ test("a load-shedding close maps to rate-limit", async () => {
426
+ const { events } = await startSession();
427
+
428
+ mockWs.simulateClose(1013, "try again later");
429
+
430
+ expect(events[0]).toMatchObject({
431
+ type: "error",
432
+ category: "rate-limit",
433
+ });
434
+ });
435
+
436
+ test("any other unexpected close maps to provider-error", async () => {
437
+ const { events } = await startSession();
438
+
439
+ mockWs.simulateClose(1006, "abnormal");
440
+
441
+ expect(events[0]).toMatchObject({
442
+ type: "error",
443
+ category: "provider-error",
444
+ });
445
+ });
446
+
447
+ test("a socket error emits provider-error then closed", async () => {
448
+ const { events } = await startSession();
449
+
450
+ mockWs.simulateError(new Error("connection reset"));
451
+
452
+ expect(events[0]).toMatchObject({
453
+ type: "error",
454
+ category: "provider-error",
455
+ });
456
+ expect(events[1]).toEqual({ type: "closed" });
457
+ });
458
+
459
+ test("a normal close after a fatal Error frame adds no second error", async () => {
460
+ const { events } = await startSession();
461
+
462
+ mockWs.simulateMessage(
463
+ JSON.stringify({
464
+ type: "Error",
465
+ code: "INVALID_AUTH",
466
+ description: "Invalid credentials",
467
+ }),
468
+ );
469
+ mockWs.simulateClose(1000, "closing");
470
+
471
+ expect(events).toHaveLength(2);
472
+ expect(events[0]).toMatchObject({
473
+ type: "error",
474
+ category: "auth",
475
+ message: "Deepgram Flux error (INVALID_AUTH): Invalid credentials",
476
+ });
477
+ expect(events[1]).toEqual({ type: "closed" });
478
+ });
479
+
480
+ test("an abnormal close after a fatal Error frame adds no second error", async () => {
481
+ const { events } = await startSession();
482
+
483
+ mockWs.simulateMessage(
484
+ JSON.stringify({
485
+ type: "Error",
486
+ code: "RATE_LIMIT_EXCEEDED",
487
+ description: "Too many requests",
488
+ }),
489
+ );
490
+ mockWs.simulateClose(1006, "abnormal");
491
+
492
+ expect(events).toHaveLength(2);
493
+ expect(events[0]).toMatchObject({
494
+ type: "error",
495
+ category: "rate-limit",
496
+ message: "Deepgram Flux error (RATE_LIMIT_EXCEEDED): Too many requests",
497
+ });
498
+ expect(events[1]).toEqual({ type: "closed" });
499
+ });
500
+
501
+ test("a socket error after a fatal Error frame adds no second error", async () => {
502
+ const { events } = await startSession();
503
+
504
+ mockWs.simulateMessage(
505
+ JSON.stringify({
506
+ type: "Error",
507
+ code: "INTERNAL_SERVER_ERROR",
508
+ description: "Something broke",
509
+ }),
510
+ );
511
+ mockWs.simulateError(new Error("connection reset"));
512
+
513
+ expect(events).toHaveLength(2);
514
+ expect(events[0]).toMatchObject({ type: "error" });
515
+ expect(events[1]).toEqual({ type: "closed" });
516
+ });
517
+
518
+ test("the inactivity watchdog only fires while audio awaits a response", async () => {
519
+ const { transcriber, events } = await startSession({
520
+ inactivityTimeoutMs: 20,
521
+ });
522
+
523
+ // Idle with nothing owed: the watchdog re-arms rather than firing.
524
+ await Bun.sleep(60);
525
+ expect(events).toEqual([]);
526
+
527
+ transcriber.sendAudio(Buffer.from([1, 2]), "audio/pcm");
528
+ await Bun.sleep(80);
529
+
530
+ expect(events[0]).toMatchObject({ type: "error", category: "timeout" });
531
+ expect(events.at(-1)).toEqual({ type: "closed" });
532
+ });
533
+ });
534
+
535
+ // ─────────────────────────────────────────────────────────────────
536
+ // Lifecycle
537
+ // ─────────────────────────────────────────────────────────────────
538
+
539
+ describe("lifecycle", () => {
540
+ test("start() throws when called twice", async () => {
541
+ const { transcriber } = await startSession();
542
+
543
+ await expect(transcriber.start(() => {})).rejects.toThrow(
544
+ "start() called twice",
545
+ );
546
+ });
547
+
548
+ test("stop() sends CloseStream and reports closed once the socket closes", async () => {
549
+ const { transcriber, events } = await startSession();
550
+
551
+ transcriber.stop();
552
+
553
+ expect(mockWs.sentData).toEqual([
554
+ JSON.stringify({ type: "CloseStream" }),
555
+ ]);
556
+ expect(events).toEqual([]);
557
+
558
+ mockWs.simulateClose(1000, "");
559
+ expect(events).toEqual([{ type: "closed" }]);
560
+ });
561
+
562
+ test("stop() is idempotent and closed is emitted exactly once", async () => {
563
+ const { transcriber, events } = await startSession();
564
+
565
+ transcriber.stop();
566
+ mockWs.simulateClose(1000, "");
567
+ transcriber.stop();
568
+ mockWs.simulateClose(1000, "");
569
+
570
+ expect(events).toEqual([{ type: "closed" }]);
571
+ });
572
+
573
+ test("no KeepAlive is ever sent: Flux rejects it and closes", async () => {
574
+ const { transcriber } = await startSession();
575
+
576
+ // Flux accepts only CloseStream and Configure. A KeepAlive earns an
577
+ // UNPARSABLE_CLIENT_MESSAGE error frame and a server close, which on a
578
+ // stream held across turns kills it every keepalive interval.
579
+ await Bun.sleep(35);
580
+ transcriber.stop();
581
+
582
+ const controlFrames = mockWs.sentData.filter(
583
+ (data) => typeof data === "string",
584
+ );
585
+ expect(controlFrames).not.toContain(
586
+ JSON.stringify({ type: "KeepAlive" }),
587
+ );
588
+ expect(controlFrames).toContain(JSON.stringify({ type: "CloseStream" }));
589
+ });
590
+
591
+ test("finalizeUtterance is absent, Flux has no mid-stream flush", async () => {
592
+ const { transcriber } = await startSession();
593
+
594
+ // Flux commits a transcript only on EndOfTurn and its protocol has no
595
+ // flush that keeps the socket open, so a finalizeUtterance here could
596
+ // only claim a commit that never happened and lose the tail of every
597
+ // turn released on a caller-side boundary. Callers feature-detect the
598
+ // method and fall back to stop(). The contract type is where the
599
+ // optional method lives, so the check goes through it.
600
+ const asContract: StreamingTranscriber = transcriber;
601
+ expect(asContract.finalizeUtterance).toBeUndefined();
602
+ });
603
+
604
+ test("audio sent after stop() never reaches the socket", async () => {
605
+ const { transcriber } = await startSession();
606
+
607
+ transcriber.stop();
608
+ mockWs.sentData.length = 0;
609
+ transcriber.sendAudio(Buffer.from([1, 2]), "audio/pcm");
610
+
611
+ expect(mockWs.sentData).toEqual([]);
612
+ });
613
+ });
614
+
615
+ // ─────────────────────────────────────────────────────────────────
616
+ // Identity
617
+ // ─────────────────────────────────────────────────────────────────
618
+
619
+ test("reports the deepgram-flux provider on the daemon-streaming boundary", () => {
620
+ const transcriber = new DeepgramFluxRealtimeTranscriber(TEST_API_KEY);
621
+
622
+ expect(transcriber.providerId).toBe("deepgram-flux");
623
+ expect(transcriber.boundaryId).toBe("daemon-streaming");
624
+ });
625
+ });
@@ -1,6 +1,7 @@
1
1
  import { describe, expect, test } from "bun:test";
2
2
 
3
3
  import {
4
+ batchBoundaryGapReason,
4
5
  getCredentialProvider,
5
6
  getProviderEntry,
6
7
  listCredentialProviderNames,
@@ -23,6 +24,7 @@ describe("STT provider catalog", () => {
23
24
  const ids = listProviderIds();
24
25
  expect(ids).toContain("openai-whisper");
25
26
  expect(ids).toContain("deepgram");
27
+ expect(ids).toContain("deepgram-flux");
26
28
  expect(ids).toContain("google-gemini");
27
29
  });
28
30
 
@@ -101,6 +103,27 @@ describe("STT provider catalog", () => {
101
103
  expect(supportsBoundary("openai-whisper", "daemon-streaming")).toBe(true);
102
104
  });
103
105
 
106
+ test("deepgram-flux is streaming-only", () => {
107
+ // Flux has no batch endpoint; the batch transcriber factory rejects it
108
+ // explicitly rather than silently falling through.
109
+ expect(supportsBoundary("deepgram-flux", "daemon-streaming")).toBe(true);
110
+ expect(supportsBoundary("deepgram-flux", "daemon-batch")).toBe(false);
111
+ });
112
+
113
+ test("batchBoundaryGapReason names the batch provider on the same credential", () => {
114
+ const reason = batchBoundaryGapReason("deepgram-flux");
115
+ expect(reason).toBe(
116
+ 'Deepgram Flux is streaming-only. Batch transcription requires the deepgram provider: set services.stt.provider to "deepgram".',
117
+ );
118
+ });
119
+
120
+ test("batchBoundaryGapReason falls back to generic guidance for an unknown provider", () => {
121
+ // No catalog entry means no credential to search a batch-capable peer on.
122
+ const reason = batchBoundaryGapReason("nonexistent" as never);
123
+ expect(reason).toContain("nonexistent is streaming-only");
124
+ expect(reason).toContain("supports batch transcription");
125
+ });
126
+
104
127
  test("supportsBoundary returns false for unknown provider IDs", () => {
105
128
  // Cast to bypass type checking for the test
106
129
  expect(supportsBoundary("nonexistent" as never, "daemon-batch")).toBe(
@@ -150,6 +173,8 @@ describe("STT provider catalog", () => {
150
173
  ["deepgram", "manual"],
151
174
  ["vellum", "manual"],
152
175
  ["xai", "manual"],
176
+ // Flux takes no language parameter: its model is monolingual English.
177
+ ["deepgram-flux", "auto"],
153
178
  ["google-gemini", "auto"],
154
179
  ["openai-whisper", "auto"],
155
180
  ] as const;
@@ -171,6 +196,15 @@ describe("STT provider catalog", () => {
171
196
  expect(getCredentialProvider("google-gemini")).toBe("gemini");
172
197
  });
173
198
 
199
+ test("deepgram-flux shares the deepgram credential", () => {
200
+ expect(getCredentialProvider("deepgram-flux")).toBe("deepgram");
201
+ // Sharing must not duplicate the key in the API-key provider list.
202
+ const deepgramEntries = listCredentialProviderNames().filter(
203
+ (name) => name === "deepgram",
204
+ );
205
+ expect(deepgramEntries).toEqual(["deepgram"]);
206
+ });
207
+
174
208
  test("getCredentialProvider returns undefined for unknown ID", () => {
175
209
  expect(getCredentialProvider("nonexistent" as never)).toBeUndefined();
176
210
  });