@vellumai/assistant 0.11.3 → 0.11.4-staging.2

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 (341) hide show
  1. package/ARCHITECTURE.md +11 -6
  2. package/docs/architecture/memory.md +11 -0
  3. package/docs/architecture/turn-actor.md +70 -0
  4. package/docs/flux-turn-detection-spike.md +243 -0
  5. package/docs/stt-provider-onboarding.md +3 -1
  6. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
  7. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
  8. package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +34 -0
  9. package/node_modules/@vellumai/gateway-client/src/index.ts +2 -0
  10. package/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
  11. package/openapi.yaml +139 -37
  12. package/package.json +1 -1
  13. package/scripts/voice-ttft-spike.ts +3 -3
  14. package/src/__tests__/app-compiler.test.ts +38 -3
  15. package/src/__tests__/attachments-store.test.ts +21 -12
  16. package/src/__tests__/byok-default-profile-ensure.test.ts +17 -0
  17. package/src/__tests__/call-setup-flow-name-capture.test.ts +0 -1
  18. package/src/__tests__/call-site-routing-provider.test.ts +1 -1
  19. package/src/__tests__/channel-availability-routes.test.ts +14 -1
  20. package/src/__tests__/channel-capabilities-dedupe.test.ts +214 -0
  21. package/src/__tests__/channel-delivery-store.test.ts +14 -14
  22. package/src/__tests__/config-loader-backfill.test.ts +3 -3
  23. package/src/__tests__/config-schema.test.ts +25 -10
  24. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +8 -11
  25. package/src/__tests__/conversation-agent-loop-overflow.test.ts +8 -11
  26. package/src/__tests__/conversation-agent-loop.test.ts +28 -20
  27. package/src/__tests__/conversation-attention-store.test.ts +63 -0
  28. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +0 -4
  29. package/src/__tests__/conversation-fork-crud.test.ts +69 -0
  30. package/src/__tests__/conversation-fork-referential.test.ts +67 -0
  31. package/src/__tests__/conversation-fork-retrospective.test.ts +24 -0
  32. package/src/__tests__/conversation-notifiers-provenance.test.ts +59 -0
  33. package/src/__tests__/conversation-queue.test.ts +177 -4
  34. package/src/__tests__/conversation-runtime-assembly.test.ts +134 -102
  35. package/src/__tests__/conversation-runtime-workspace.test.ts +14 -10
  36. package/src/__tests__/credential-prompt-route.test.ts +7 -10
  37. package/src/__tests__/custom-profile-ensure.test.ts +5 -1
  38. package/src/__tests__/discord-access-request-privacy.test.ts +5 -1
  39. package/src/__tests__/discord-requester-notice-privacy.test.ts +3 -3
  40. package/src/__tests__/document-append-idempotency.test.ts +233 -0
  41. package/src/__tests__/edit-propagation.test.ts +0 -7
  42. package/src/__tests__/helpers/mock-actor-context.ts +49 -0
  43. package/src/__tests__/helpers/mock-conversation.ts +13 -1
  44. package/src/__tests__/injector-chain.test.ts +63 -41
  45. package/src/__tests__/injector-disk-pressure.test.ts +11 -23
  46. package/src/__tests__/llm-context-resolution.test.ts +73 -1
  47. package/src/__tests__/llm-schema.test.ts +5 -2
  48. package/src/__tests__/mcp-list-plugin-servers.test.ts +250 -0
  49. package/src/__tests__/memory-retrieval-hook.test.ts +6 -5
  50. package/src/__tests__/messages-read-boundary-guard.test.ts +134 -0
  51. package/src/__tests__/mtime-cache.test.ts +1 -1
  52. package/src/__tests__/non-member-access-request.test.ts +0 -20
  53. package/src/__tests__/outbound-slack-persistence.test.ts +40 -1
  54. package/src/__tests__/plugin-import-boundary-guard.test.ts +5 -0
  55. package/src/__tests__/plugin-secret-pattern-contribution.test.ts +1 -1
  56. package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +14 -7
  57. package/src/__tests__/provider-commit-message-generator.test.ts +20 -0
  58. package/src/__tests__/run-conversation-turn-persistence.test.ts +434 -105
  59. package/src/__tests__/scoped-approval-grants.test.ts +11 -6
  60. package/src/__tests__/secret-ingress-channel.test.ts +0 -1
  61. package/src/__tests__/skills.test.ts +32 -0
  62. package/src/__tests__/slack-edit-ordering-characterization.test.ts +0 -1
  63. package/src/__tests__/subagent-call-site-routing.test.ts +31 -19
  64. package/src/__tests__/subagent-spawn-and-await.test.ts +14 -10
  65. package/src/__tests__/turn-events-store.test.ts +43 -0
  66. package/src/__tests__/ui-shape-teaching.test.ts +33 -0
  67. package/src/__tests__/ui-voice-picker-surface.test.ts +128 -0
  68. package/src/__tests__/user-plugin-loader.test.ts +1 -1
  69. package/src/__tests__/visible-app-context.test.ts +16 -9
  70. package/src/__tests__/voice-config-update.test.ts +40 -0
  71. package/src/__tests__/worker-entrypoint-guards.test.ts +54 -0
  72. package/src/__tests__/worker-plugin-surface.test.ts +77 -0
  73. package/src/__tests__/workspace-migration-142-consolidate-voice-front-door.test.ts +158 -0
  74. package/src/__tests__/workspace-migration-143-repair-deprecated-codex-model-id.test.ts +133 -0
  75. package/src/__tests__/workspace-migration-144-convert-stranded-subscription-openai-profiles.test.ts +316 -0
  76. package/src/__tests__/workspace-migration-145-collapse-profile-bindings-to-entries.test.ts +325 -0
  77. package/src/__tests__/workspace-migration-146-repair-retired-fireworks-deepseek-flash-model-id.test.ts +235 -0
  78. package/src/acp/__tests__/acp-claude-oauth.test.ts +10 -2
  79. package/src/acp/__tests__/auth-required.test.ts +161 -0
  80. package/src/acp/acp-claude-oauth.ts +19 -2
  81. package/src/acp/agent-process.test.ts +100 -0
  82. package/src/acp/agent-process.ts +29 -26
  83. package/src/acp/auth-required.ts +102 -0
  84. package/src/acp/session-manager.test.ts +119 -0
  85. package/src/acp/session-manager.ts +68 -2
  86. package/src/api/events/acp-auth-required.ts +55 -0
  87. package/src/api/index.ts +7 -0
  88. package/src/api/surfaces.ts +7 -3
  89. package/src/apps/app-store.ts +3 -0
  90. package/src/bundler/package-resolver.ts +2 -30
  91. package/src/calls/__tests__/voice-session-bridge.test.ts +173 -1
  92. package/src/calls/__tests__/voice-triage-escalate.test.ts +94 -0
  93. package/src/calls/call-controller.ts +19 -3
  94. package/src/calls/call-setup-flow.ts +0 -1
  95. package/src/calls/media-stream-stt-session.ts +15 -0
  96. package/src/calls/voice-session-bridge.ts +71 -16
  97. package/src/calls/voice-triage-escalate.ts +104 -2
  98. package/src/channels/__tests__/plugin-channel-declarations.test.ts +161 -0
  99. package/src/channels/config.ts +13 -0
  100. package/src/channels/plugin-channel-declarations.ts +108 -0
  101. package/src/channels/types.ts +30 -0
  102. package/src/cli/AGENTS.md +5 -2
  103. package/src/cli/commands/credentials.help.ts +2 -2
  104. package/src/cli/commands/inference-providers.ts +1 -1
  105. package/src/cli/commands/mcp.help.ts +13 -4
  106. package/src/cli/commands/mcp.ts +9 -0
  107. package/src/cli/commands/memory/__tests__/memory-v3.test.ts +128 -5
  108. package/src/cli/commands/memory/index.help.ts +43 -1
  109. package/src/cli/commands/memory/memory-v3.ts +64 -0
  110. package/src/cli/commands/stt.help.ts +27 -2
  111. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +39 -0
  112. package/src/cli/lib/bundled-marketplace.json +13 -0
  113. package/src/cli/lib/upgrade-plugin.ts +42 -0
  114. package/src/config/__tests__/default-profile-catalog.test.ts +34 -2
  115. package/src/config/__tests__/default-provider.test.ts +6 -1
  116. package/src/config/__tests__/profile-materialization.test.ts +75 -19
  117. package/src/config/bundled-skills/acp/SKILL.md +6 -7
  118. package/src/config/bundled-skills/document-editor/SKILL.md +2 -2
  119. package/src/config/bundled-skills/document-editor/TOOLS.json +2 -2
  120. package/src/config/bundled-skills/media-processing/services/preprocess.ts +14 -4
  121. package/src/config/bundled-skills/settings/TOOLS.json +3 -3
  122. package/src/config/bundled-skills/settings/tools/navigate-settings-tab.test.ts +65 -0
  123. package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +7 -1
  124. package/src/config/bundled-skills/settings/tools/shared.ts +16 -0
  125. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +19 -1
  126. package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +22 -1
  127. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +9 -2
  128. package/src/config/call-site-defaults.ts +4 -5
  129. package/src/config/default-profile-catalog.ts +83 -12
  130. package/src/config/default-profile-names.ts +4 -1
  131. package/src/config/default-provider-resolution.ts +4 -0
  132. package/src/config/llm-context-resolution.ts +11 -3
  133. package/src/config/llm-resolver.ts +28 -1
  134. package/src/config/profile-materialization.ts +70 -22
  135. package/src/config/schemas/__tests__/live-voice.test.ts +107 -4
  136. package/src/config/schemas/call-site-catalog.ts +4 -4
  137. package/src/config/schemas/live-voice.ts +57 -23
  138. package/src/config/schemas/llm.ts +59 -32
  139. package/src/config/schemas/mcp.ts +23 -0
  140. package/src/config/schemas/plugin-updates.ts +6 -2
  141. package/src/config/schemas/stt.ts +1 -0
  142. package/src/context/outbound-sanitize.ts +96 -1
  143. package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +82 -0
  144. package/src/daemon/conversation-agent-loop-handlers.ts +15 -10
  145. package/src/daemon/conversation-agent-loop.ts +17 -6
  146. package/src/daemon/conversation-messaging.ts +5 -1
  147. package/src/daemon/conversation-notifiers.ts +9 -1
  148. package/src/daemon/conversation-process.ts +36 -6
  149. package/src/daemon/conversation-runtime-assembly.ts +3 -4
  150. package/src/daemon/conversation-surfaces.ts +27 -5
  151. package/src/daemon/conversation-tool-setup.ts +1 -2
  152. package/src/daemon/conversation.ts +48 -0
  153. package/src/daemon/interactive-turn-sender.ts +59 -0
  154. package/src/daemon/mcp-reload-service.ts +36 -6
  155. package/src/daemon/process-message.ts +24 -24
  156. package/src/daemon/providers-setup.ts +6 -3
  157. package/src/daemon/trust-context-types.ts +29 -0
  158. package/src/daemon/wake-conversation-ops.ts +3 -2
  159. package/src/documents/document-store.ts +138 -5
  160. package/src/hooks/hook-loader.ts +3 -3
  161. package/src/hooks/registry.ts +50 -6
  162. package/src/inbound/__tests__/oauth-callback-url.test.ts +83 -0
  163. package/src/inbound/oauth-callback-url.ts +61 -0
  164. package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +1 -104
  165. package/src/live-voice/__tests__/live-voice-events.test.ts +7 -8
  166. package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +932 -0
  167. package/src/live-voice/__tests__/live-voice-metrics.test.ts +115 -8
  168. package/src/live-voice/__tests__/live-voice-photo.test.ts +100 -0
  169. package/src/live-voice/__tests__/live-voice-progress.test.ts +60 -194
  170. package/src/live-voice/__tests__/live-voice-stt.test.ts +14 -0
  171. package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +29 -0
  172. package/src/live-voice/__tests__/live-voice-tts-session.test.ts +0 -483
  173. package/src/live-voice/__tests__/live-voice-vad.test.ts +0 -16
  174. package/src/live-voice/__tests__/progress-narration.test.ts +214 -0
  175. package/src/live-voice/live-voice-archive.ts +2 -0
  176. package/src/live-voice/live-voice-metrics.ts +57 -32
  177. package/src/live-voice/live-voice-photo.ts +1 -2
  178. package/src/live-voice/live-voice-session.ts +535 -314
  179. package/src/live-voice/progress-narration.ts +277 -0
  180. package/src/live-voice/protocol.ts +21 -1
  181. package/src/mcp/__tests__/effective-config.test.ts +238 -0
  182. package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +0 -1
  183. package/src/mcp/__tests__/mcp-oauth-client-registration.test.ts +200 -0
  184. package/src/mcp/__tests__/mcp-oauth-provider.test.ts +9 -9
  185. package/src/mcp/__tests__/plugin-server-credential-isolation.test.ts +95 -0
  186. package/src/mcp/client.ts +16 -11
  187. package/src/mcp/effective-config.ts +113 -0
  188. package/src/mcp/manager.ts +11 -6
  189. package/src/mcp/mcp-auth-orchestrator.ts +13 -22
  190. package/src/mcp/mcp-oauth-provider.ts +205 -240
  191. package/src/monitoring/__tests__/plugin-auto-update.test.ts +166 -3
  192. package/src/monitoring/plugin-auto-update.ts +128 -24
  193. package/src/notifications/signal.ts +1 -0
  194. package/src/permissions/confirmation-guardian-request.test.ts +15 -11
  195. package/src/permissions/confirmation-guardian-request.ts +2 -2
  196. package/src/permissions/question-guardian-request.test.ts +14 -6
  197. package/src/permissions/question-guardian-request.ts +1 -2
  198. package/src/persistence/attachments-store.ts +8 -1
  199. package/src/persistence/bookmark-crud.ts +3 -7
  200. package/src/persistence/conversation-attention-store.ts +16 -45
  201. package/src/persistence/conversation-crud.ts +33 -4
  202. package/src/persistence/conversation-lineage.ts +9 -0
  203. package/src/persistence/conversation-queries.ts +108 -41
  204. package/src/persistence/delivery-crud.ts +38 -29
  205. package/src/persistence/external-conversation-store.ts +32 -4
  206. package/src/persistence/llm-request-log-store.ts +4 -10
  207. package/src/persistence/llm-usage-store.ts +8 -3
  208. package/src/persistence/message-reads.test.ts +197 -0
  209. package/src/persistence/message-reads.ts +211 -0
  210. package/src/persistence/migrations/366-chatgpt-subscription-row-identity.test.ts +120 -0
  211. package/src/persistence/migrations/366-chatgpt-subscription-row-identity.ts +62 -0
  212. package/src/persistence/real-user-turn-filter.ts +27 -3
  213. package/src/persistence/steps.ts +9 -0
  214. package/src/plugin-api/__tests__/oauth-callback-url-export.test.ts +29 -0
  215. package/src/plugin-api/conversation-turn.ts +168 -5
  216. package/src/plugin-api/index.ts +21 -5
  217. package/src/plugin-api/vision-support.test.ts +1 -1
  218. package/src/plugins/__tests__/mcp-servers.test.ts +371 -0
  219. package/src/plugins/defaults/memory/AGENTS.md +4 -0
  220. package/src/plugins/defaults/memory/__tests__/buffer-format.test.ts +204 -0
  221. package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +72 -0
  222. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +4 -1
  223. package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +4 -1
  224. package/src/plugins/defaults/memory/buffer-format.ts +165 -0
  225. package/src/plugins/defaults/memory/context-search/sources/conversations.ts +6 -0
  226. package/src/plugins/defaults/memory/graph/image-ref-utils.ts +3 -0
  227. package/src/plugins/defaults/memory/graph/tool-handlers.ts +1 -30
  228. package/src/plugins/defaults/memory/graph-topology/pending-buffer.test.ts +34 -0
  229. package/src/plugins/defaults/memory/graph-topology/pending-buffer.ts +8 -12
  230. package/src/plugins/defaults/memory/hooks/post-compact.ts +1 -4
  231. package/src/plugins/defaults/memory/indexer.ts +3 -1
  232. package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +19 -7
  233. package/src/plugins/defaults/memory/src/__tests__/memory-v3-gate-stats.test.ts +281 -0
  234. package/src/plugins/defaults/memory/src/memory-v3-routes.ts +207 -0
  235. package/src/plugins/defaults/memory/substrate/__tests__/consolidation-job.test.ts +33 -0
  236. package/src/plugins/defaults/memory/substrate/__tests__/static-context.test.ts +199 -2
  237. package/src/plugins/defaults/memory/substrate/consolidation-job.ts +25 -18
  238. package/src/plugins/defaults/memory/substrate/skill-content.ts +8 -1
  239. package/src/plugins/defaults/memory/substrate/static-context.ts +160 -4
  240. package/src/plugins/defaults/memory/substrate/sweep-job.ts +2 -4
  241. package/src/plugins/defaults/memory/v1/graph/extraction.ts +3 -1
  242. package/src/plugins/defaults/memory/v3/prune.ts +2 -0
  243. package/src/plugins/defaults/memory/v3/selection-log-store.ts +2 -0
  244. package/src/plugins/defaults/memory/worker.ts +6 -3
  245. package/src/plugins/external-plugin-loader.ts +47 -0
  246. package/src/plugins/mcp-servers.ts +361 -0
  247. package/src/plugins/mtime-cache.ts +23 -49
  248. package/src/plugins/worker-plugin-surface.ts +33 -0
  249. package/src/providers/__tests__/connection-model-compat.test.ts +1 -1
  250. package/src/providers/__tests__/dispatch-connection-routing.test.ts +214 -2
  251. package/src/providers/__tests__/preflight-resolved-config.test.ts +57 -0
  252. package/src/providers/__tests__/retry-callsite.test.ts +5 -2
  253. package/src/providers/call-site-routing.ts +30 -3
  254. package/src/providers/connection-resolution.ts +194 -11
  255. package/src/providers/inference/auth.ts +6 -0
  256. package/src/providers/inference/connection-availability.ts +24 -2
  257. package/src/providers/inference/connections.ts +2 -0
  258. package/src/providers/model-catalog.ts +3 -3
  259. package/src/providers/model-intents.ts +28 -8
  260. package/src/providers/openai/chat-completions-provider.ts +5 -6
  261. package/src/providers/openai/codex-models.ts +2 -1
  262. package/src/providers/provider-send-message.ts +32 -3
  263. package/src/providers/speech-to-text/__tests__/deepgram-flux-frames.test.ts +433 -0
  264. package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +620 -0
  265. package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +34 -0
  266. package/src/providers/speech-to-text/__tests__/resolve.test.ts +285 -6
  267. package/src/providers/speech-to-text/deepgram-flux-frames.ts +395 -0
  268. package/src/providers/speech-to-text/deepgram-flux-realtime.ts +719 -0
  269. package/src/providers/speech-to-text/provider-catalog.ts +99 -8
  270. package/src/providers/speech-to-text/resolve.ts +25 -2
  271. package/src/routes/worker.ts +17 -5
  272. package/src/runtime/access-request-helper.ts +9 -12
  273. package/src/runtime/agent-wake.ts +3 -3
  274. package/src/runtime/pre-first-message-gate.ts +4 -0
  275. package/src/runtime/routes/__tests__/acp-claude-auth-routes.test.ts +12 -4
  276. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +219 -1
  277. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +52 -0
  278. package/src/runtime/routes/__tests__/default-provider-routes.test.ts +61 -0
  279. package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +44 -0
  280. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +102 -1
  281. package/src/runtime/routes/__tests__/plugins-routes.test.ts +44 -0
  282. package/src/runtime/routes/__tests__/stt-routes.test.ts +25 -0
  283. package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +62 -1
  284. package/src/runtime/routes/channel-availability-routes.ts +32 -14
  285. package/src/runtime/routes/channel-route-shared.ts +0 -6
  286. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +6 -6
  287. package/src/runtime/routes/conversation-list-routes.ts +112 -1
  288. package/src/runtime/routes/conversation-query-routes.ts +40 -27
  289. package/src/runtime/routes/credential-prompt-routes.ts +4 -7
  290. package/src/runtime/routes/default-provider-routes.ts +15 -0
  291. package/src/runtime/routes/inbound-message-handler.ts +17 -41
  292. package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +0 -1
  293. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +0 -9
  294. package/src/runtime/routes/inbound-stages/admission-policy.ts +1 -17
  295. package/src/runtime/routes/inbound-stages/bootstrap-intercept.test.ts +0 -1
  296. package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +2 -3
  297. package/src/runtime/routes/inbound-stages/edit-intercept.ts +1 -3
  298. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +0 -1
  299. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +3 -4
  300. package/src/runtime/routes/inbound-stages/reaction-intercept.test.ts +0 -1
  301. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +11 -20
  302. package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +2 -3
  303. package/src/runtime/routes/inference-profiles-routes.ts +20 -11
  304. package/src/runtime/routes/inference-provider-connection-routes.ts +77 -15
  305. package/src/runtime/routes/log-export-routes.ts +3 -0
  306. package/src/runtime/routes/mcp-auth-routes.ts +148 -57
  307. package/src/runtime/routes/plugins-routes.ts +21 -3
  308. package/src/runtime/routes/stt-routes.ts +31 -25
  309. package/src/runtime/routes/surface-conversation-resolver.ts +3 -0
  310. package/src/runtime/routes/user-route-dispatcher.ts +39 -14
  311. package/src/runtime/routes/user-route-import.ts +108 -0
  312. package/src/schedule/worker.ts +6 -0
  313. package/src/security/oauth2.ts +6 -22
  314. package/src/stt/__tests__/daemon-batch-transcriber.test.ts +22 -0
  315. package/src/stt/__tests__/types.test.ts +94 -0
  316. package/src/stt/daemon-batch-transcriber.ts +10 -0
  317. package/src/stt/stt-stream-session.ts +8 -4
  318. package/src/stt/types.ts +103 -0
  319. package/src/subagent/manager.ts +1 -3
  320. package/src/subagent/types.ts +7 -6
  321. package/src/tools/acp/spawn.test.ts +97 -0
  322. package/src/tools/acp/spawn.ts +32 -0
  323. package/src/tools/document/document-tool.ts +12 -3
  324. package/src/tools/registry.ts +2 -1
  325. package/src/tools/ui-surface/surface-shape-docs.ts +11 -0
  326. package/src/tools/workflows/run-workflow.ts +1 -2
  327. package/src/tts/__tests__/reasoning-tag-filter.test.ts +78 -0
  328. package/src/tts/reasoning-tag-filter.ts +89 -0
  329. package/src/util/think-tag-stream.ts +95 -0
  330. package/src/workspace/byok-default-profile-ensure.ts +76 -24
  331. package/src/workspace/custom-profile-ensure.ts +4 -24
  332. package/src/workspace/migrations/142-consolidate-voice-front-door.ts +70 -0
  333. package/src/workspace/migrations/143-repair-deprecated-codex-model-id.ts +134 -0
  334. package/src/workspace/migrations/144-convert-stranded-subscription-openai-profiles.ts +265 -0
  335. package/src/workspace/migrations/145-collapse-profile-bindings-to-entries.ts +328 -0
  336. package/src/workspace/migrations/146-repair-retired-fireworks-deepseek-flash-model-id.ts +195 -0
  337. package/src/workspace/migrations/__tests__/141-stt-english-default-to-multilingual.test.ts +0 -10
  338. package/src/workspace/migrations/registry.ts +10 -0
  339. package/src/workspace/provider-commit-message-generator.ts +7 -5
  340. package/src/live-voice/__tests__/front-decision.test.ts +0 -645
  341. package/src/live-voice/front-decision.ts +0 -476
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Guards that `resolveOauthCallbackUrl` reaches plugins.
3
+ *
4
+ * The shim written into `<workspaceDir>/node_modules/@vellumai/plugin-api`
5
+ * re-binds whatever `Object.keys()` finds on the plugin-api namespace, so an
6
+ * export missing from `index.ts` is missing from every installed plugin with
7
+ * no other signal than a plugin failing to import it.
8
+ */
9
+
10
+ import { describe, expect, test } from "bun:test";
11
+
12
+ import { PLUGIN_API_EXPORTS } from "../../embedded/plugin-api.js";
13
+ import * as pluginApi from "../index.js";
14
+
15
+ describe("plugin-api OAuth callback export", () => {
16
+ test("is exported from the plugin-api surface", () => {
17
+ expect(typeof pluginApi.resolveOauthCallbackUrl).toEqual("function");
18
+ });
19
+
20
+ test("is carried into the generated shim's binding list", () => {
21
+ expect(PLUGIN_API_EXPORTS).toContain("resolveOauthCallbackUrl");
22
+ });
23
+
24
+ test("takes no arguments, so a plugin cannot vary the URI", () => {
25
+ // The value a Client ID Metadata Document publishes and the value the
26
+ // host later authorizes with have to be the same string.
27
+ expect(pluginApi.resolveOauthCallbackUrl.length).toEqual(0);
28
+ });
29
+ });
@@ -14,6 +14,7 @@
14
14
  */
15
15
 
16
16
  import type { AssistantEvent } from "../api/index.js";
17
+ import type { ChannelId } from "../channels/types.js";
17
18
  import type { LLMCallSite } from "../config/schemas/llm.js";
18
19
  import type { UserMessageAttachment } from "../daemon/message-types/shared.js";
19
20
  import type { ContentBlock, MediaSource } from "../providers/types.js";
@@ -22,12 +23,62 @@ import type { ContentBlock, MediaSource } from "../providers/types.js";
22
23
  // Public types
23
24
  // ---------------------------------------------------------------------------
24
25
 
26
+ /**
27
+ * The chat a turn belongs to, in the channel's own terms.
28
+ *
29
+ * Supplied instead of a `conversationId` when the caller knows where the
30
+ * message came from but not which conversation that is. The pair resolves to
31
+ * the same conversation an inbound message on those coordinates would land
32
+ * in, because it resolves through the same binding.
33
+ *
34
+ * That sameness is the reason this exists at all. A caller could keep its own
35
+ * `externalChatId -> conversationId` map and pass the id, and turns would run
36
+ * in the right place. What it could not do is make the rest of the assistant
37
+ * agree: conversation reset is addressed by channel coordinates
38
+ * (`handleDeleteConversation`), the deny lanes attach an access-request card
39
+ * by them (`findInboundConversationId`), and the conversation and session
40
+ * APIs read the channel, chat name and sender off the external binding. A
41
+ * private map is a second name for the same conversation, and everything
42
+ * keyed on the public one quietly misses.
43
+ */
44
+ export interface ConversationChannelAddress {
45
+ /** Channel the message arrived on. */
46
+ sourceChannel: ChannelId;
47
+ /** The chat, in the channel's own id space. */
48
+ externalChatId: string;
49
+ /**
50
+ * Thread within the chat, where the channel has threads. Only Slack and
51
+ * Telegram scope a conversation by thread; elsewhere this is carried as
52
+ * binding metadata and does not split the conversation.
53
+ */
54
+ externalThreadId?: string | null;
55
+ /** Human-readable chat name, for the conversation list. */
56
+ externalChatName?: string | null;
57
+ /** Who sent it, in the channel's id space. */
58
+ externalUserId?: string | null;
59
+ displayName?: string | null;
60
+ username?: string | null;
61
+ }
62
+
25
63
  export interface RunConversationTurnOptions {
26
64
  /**
27
65
  * Conversation to run the turn in. If omitted, a new conversation is
28
- * created (its ID is generated with `uuidv7` and returned in the result).
66
+ * created (its ID is generated with `uuidv7` and returned in the result),
67
+ * unless {@link RunConversationTurnOptions.channel} says which chat this
68
+ * belongs to, in which case that chat's conversation is used.
29
69
  */
30
70
  conversationId?: string;
71
+ /**
72
+ * The chat this turn belongs to, resolved to a conversation and bound to
73
+ * the channel. See {@link ConversationChannelAddress}.
74
+ *
75
+ * Ignored when `conversationId` is given: an explicit conversation is the
76
+ * caller saying which one, and re-resolving would overrule it. A caller
77
+ * that wants both the binding and a conversation of its own choosing is
78
+ * describing two different conversations, which is a bug worth surfacing
79
+ * as one rather than silently picking a winner.
80
+ */
81
+ channel?: ConversationChannelAddress;
31
82
  /**
32
83
  * User message content blocks for this turn. Text blocks become the
33
84
  * user message body; image/file blocks are resolved to inline
@@ -121,6 +172,77 @@ function extractContentAndAttachments(
121
172
  return { text: textParts.join("\n"), attachments };
122
173
  }
123
174
 
175
+ /**
176
+ * Resolve a chat to its conversation, binding the two if they are not already.
177
+ *
178
+ * Two writes, the same two `handleChannelInbound` makes for an arriving
179
+ * message, and for the same reasons. The conversation key is what makes the
180
+ * chat resolve to this conversation next time and what conversation reset
181
+ * addresses; the external binding is what the conversation and session APIs
182
+ * read to show which channel a conversation came from and who is on the other
183
+ * end. One without the other is a conversation that is half-registered, and
184
+ * which half is missing decides which feature quietly stops working.
185
+ *
186
+ * Deliberately not `recordInbound`, which is the same resolution plus an
187
+ * inbound event row. That row exists to dedup a vendor's redelivery and to
188
+ * correlate later edits, and a caller driving a turn on its own schedule has
189
+ * neither a vendor nor a message id to correlate. Plugin deliveries are
190
+ * deduped upstream in the gateway before the message ever gets here.
191
+ */
192
+ async function resolveChannelConversation(
193
+ channel: ConversationChannelAddress,
194
+ conversationType?: "standard" | "background",
195
+ ): Promise<{ conversationId: string; created: boolean }> {
196
+ const { findInboundConversationId, resolveInboundConversation } =
197
+ await import("../persistence/delivery-crud.js");
198
+ const { upsertBinding } =
199
+ await import("../persistence/external-conversation-store.js");
200
+
201
+ // Asked before resolving, because resolving is what creates it: once the id
202
+ // is in hand a new conversation is indistinguishable from an existing one,
203
+ // and the caller announces only the new. `findInboundConversationId` is the
204
+ // read-only mirror of the resolution below, so the two agree on what
205
+ // "already bound" means, including the Slack flat-key alias.
206
+ const created =
207
+ findInboundConversationId(
208
+ channel.sourceChannel,
209
+ channel.externalChatId,
210
+ channel.externalThreadId,
211
+ ) === null;
212
+
213
+ const { conversationId } = resolveInboundConversation(
214
+ channel.sourceChannel,
215
+ channel.externalChatId,
216
+ channel.externalThreadId,
217
+ // Both apply only if this call is what mints the conversation. `origin` is
218
+ // first-message attribution, which a chat's first turn is the only chance
219
+ // to record.
220
+ {
221
+ origin: channel.sourceChannel,
222
+ ...(conversationType ? { conversationType } : {}),
223
+ },
224
+ );
225
+
226
+ // Refreshed on every turn rather than only on creation, because the sender's
227
+ // display name and the chat's name are the vendor's to change and the
228
+ // conversation list shows whichever we last heard. The optional fields pass
229
+ // through as given: a caller that omits one on a later turn knows only the
230
+ // chat coordinates this time, which `upsertBinding` reads as silence rather
231
+ // than as the sender having no name.
232
+ upsertBinding({
233
+ conversationId,
234
+ sourceChannel: channel.sourceChannel,
235
+ externalChatId: channel.externalChatId,
236
+ externalChatName: channel.externalChatName,
237
+ externalThreadId: channel.externalThreadId ?? null,
238
+ externalUserId: channel.externalUserId,
239
+ displayName: channel.displayName,
240
+ username: channel.username,
241
+ });
242
+
243
+ return { conversationId, created };
244
+ }
245
+
124
246
  // ---------------------------------------------------------------------------
125
247
  // Public API
126
248
  // ---------------------------------------------------------------------------
@@ -166,8 +288,25 @@ export async function runConversationTurn(
166
288
  // below) without requiring a client to approve prompts.
167
289
  const { INTERNAL_GUARDIAN_TRUST_CONTEXT } =
168
290
  await import("../daemon/trust-context.js");
169
- const conversationId = options.conversationId ?? uuidv7();
170
- const rowExisted = getConversation(conversationId) != null;
291
+
292
+ // A channel address resolves through the same binding an inbound message
293
+ // uses, so a turn addressed by chat lands in that chat's conversation
294
+ // rather than a private one only this caller can find.
295
+ const channelConversation =
296
+ !options.conversationId && options.channel
297
+ ? await resolveChannelConversation(
298
+ options.channel,
299
+ options.conversationType,
300
+ )
301
+ : undefined;
302
+ const conversationId =
303
+ options.conversationId ?? channelConversation?.conversationId ?? uuidv7();
304
+ // A channel address reports its own novelty, since resolving it creates the
305
+ // row: asking the DB here answers yes for a conversation minted a line ago
306
+ // and swallows the announcement it is owed.
307
+ const rowExisted = channelConversation
308
+ ? !channelConversation.created
309
+ : getConversation(conversationId) != null;
171
310
  const conversation = await getOrCreateConversation(conversationId, {
172
311
  trustContext: INTERNAL_GUARDIAN_TRUST_CONTEXT,
173
312
  ...(options.conversationType
@@ -199,6 +338,28 @@ export async function runConversationTurn(
199
338
  resolveMediaSourceData,
200
339
  );
201
340
 
341
+ // The channel this turn speaks on. Runtime assembly reads the per-turn
342
+ // context first and falls back to the conversation's `originChannel`, then
343
+ // to `vellum`, so a turn into a conversation carrying no origin runs as
344
+ // `vellum` without this: the wrong channel for the message row and for the
345
+ // channel-permission cascade the tools are approved against. Null when the
346
+ // caller names no channel, which both restores that fallback and clears any
347
+ // context left by a previous turn.
348
+ const turnChannelContext = options.channel
349
+ ? {
350
+ userMessageChannel: options.channel.sourceChannel,
351
+ assistantMessageChannel: options.channel.sourceChannel,
352
+ }
353
+ : null;
354
+ // Carried on the metadata as well, because a queued turn is drained after
355
+ // this call returns and reads its channel from there. Without it the drain
356
+ // inherits whichever turn was in flight, which on a shared conversation is
357
+ // some other channel entirely.
358
+ const metadata = {
359
+ ...PLUGIN_TURN_MESSAGE_METADATA,
360
+ ...(turnChannelContext ?? {}),
361
+ };
362
+
202
363
  // Build the event emitter: fan out to SSE/event-hub subscribers via
203
364
  // broadcastMessage, then invoke the collector callback. This mirrors the
204
365
  // buildEventEmitter pattern from process-message.ts so plugin-driven
@@ -221,7 +382,7 @@ export async function runConversationTurn(
221
382
  onEvent,
222
383
  requestId,
223
384
  isInteractive: false,
224
- metadata: { ...PLUGIN_TURN_MESSAGE_METADATA },
385
+ metadata,
225
386
  });
226
387
  if (enqueueResult.rejected) {
227
388
  throw new Error(
@@ -236,12 +397,14 @@ export async function runConversationTurn(
236
397
  };
237
398
  }
238
399
 
400
+ conversation.setTurnChannelContext(turnChannelContext);
401
+
239
402
  const userMessageId = await conversation.processMessage({
240
403
  content: text,
241
404
  attachments,
242
405
  onEvent,
243
406
  isInteractive: false,
244
- metadata: { ...PLUGIN_TURN_MESSAGE_METADATA },
407
+ metadata,
245
408
  ...(options.callSite ? { callSite: options.callSite } : {}),
246
409
  });
247
410
 
@@ -180,6 +180,16 @@ export {
180
180
  // the same resolution as `webhooks register`, and registers the callback route
181
181
  // on the managed branches. The plugin defaults to the one in context.
182
182
  export { resolveWebhookUrl, type WebhookUrlOptions } from "./webhook-url.js";
183
+ // Resolve the redirect URI an authorization server should send a user back
184
+ // to after consent. One shared route serves every OAuth flow in the
185
+ // assistant, demultiplexed by OAuth `state`, so this takes no arguments and
186
+ // returns the same URL for every caller and every attempt. A plugin needs it
187
+ // when it publishes something that has to name the redirect URI ahead of the
188
+ // flow, such as a Client ID Metadata Document, whose `redirect_uris` an
189
+ // authorization server matches exactly. Throws when no public ingress is
190
+ // configured and the assistant is not connected to the platform, which is the
191
+ // case where no URL would work.
192
+ export { resolveOauthCallbackUrl } from "../inbound/oauth-callback-url.js";
183
193
  // Resolve a provider for a call site (optionally overriding the profile) so a
184
194
  // plugin can run inference through the workspace's configured profiles and
185
195
  // credentials — managed-proxy or BYOK — without supplying its own API key.
@@ -345,21 +355,26 @@ export { synthesizeText, TtsSynthesisError } from "../tts/synthesize-text.js";
345
355
  export type { TtsSynthesisResult } from "../tts/types.js";
346
356
  // Streaming speech-to-text — open a live transcription session against the
347
357
  // assistant's globally configured STT provider stack. The plugin feeds audio
348
- // chunks via `sendAudio` and receives partial/final transcript events through
349
- // the `start(onEvent)` callback, closing with `stop`. `SttStreamServerEvent`
350
- // and its variants type the events handed to `onEvent`; `SttErrorCategory`
351
- // classifies `error` events; `SttProviderId` names the resolved session's
352
- // provider.
358
+ // chunks via `sendAudio` and receives transcript and turn-boundary events
359
+ // through the `start(onEvent)` callback, closing with `stop`.
360
+ // `SttStreamServerEvent` types the events handed to `onEvent`; every member of
361
+ // that union is exported alongside it so a plugin can name each variant in an
362
+ // exhaustive switch. `SttErrorCategory` classifies `error` events;
363
+ // `SttProviderId` names the resolved session's provider.
353
364
  export type {
354
365
  StreamingTranscriber,
355
366
  SttErrorCategory,
356
367
  SttProviderId,
357
368
  SttStreamServerClosedEvent,
369
+ SttStreamServerEagerTurnEndEvent,
358
370
  SttStreamServerErrorEvent,
359
371
  SttStreamServerEvent,
360
372
  SttStreamServerFinalEvent,
361
373
  SttStreamServerFinalizedEvent,
362
374
  SttStreamServerPartialEvent,
375
+ SttStreamServerTurnEndEvent,
376
+ SttStreamServerTurnResumedEvent,
377
+ SttStreamServerTurnStartEvent,
363
378
  } from "../stt/types.js";
364
379
  export { openTranscriptionSession } from "./transcription-session.js";
365
380
  // Conversation agent-loop turn — run a full conversation turn (persist user
@@ -370,6 +385,7 @@ export { openTranscriptionSession } from "./transcription-session.js";
370
385
  // (e.g. meeting-bot flushing a transcript excerpt) should prefer this over the
371
386
  // stateless `provider.sendMessage()` call.
372
387
  export type {
388
+ ConversationChannelAddress,
373
389
  RunConversationTurnOptions,
374
390
  RunConversationTurnResult,
375
391
  } from "./conversation-turn.js";
@@ -107,7 +107,7 @@ describe("doesSupportVision", () => {
107
107
  managed: { provider: "vellum", model: "claude-fable-5" },
108
108
  "managed-text": {
109
109
  provider: "vellum",
110
- model: "accounts/fireworks/models/deepseek-v4-flash",
110
+ model: "accounts/fireworks/models/deepseek-v4-flash-0731",
111
111
  },
112
112
  });
113
113
  expect(doesSupportVision(profile("managed"))).toBe(true);