@vellumai/assistant 0.11.3 → 0.11.4-staging.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (324) hide show
  1. package/ARCHITECTURE.md +11 -6
  2. package/docs/architecture/memory.md +11 -0
  3. package/docs/architecture/turn-actor.md +70 -0
  4. package/docs/flux-turn-detection-spike.md +243 -0
  5. package/docs/stt-provider-onboarding.md +3 -1
  6. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
  7. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
  8. package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +34 -0
  9. package/node_modules/@vellumai/gateway-client/src/index.ts +2 -0
  10. package/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
  11. package/openapi.yaml +140 -38
  12. package/package.json +1 -1
  13. package/scripts/voice-ttft-spike.ts +3 -3
  14. package/src/__tests__/app-compiler.test.ts +38 -3
  15. package/src/__tests__/attachments-store.test.ts +21 -12
  16. package/src/__tests__/byok-default-profile-ensure.test.ts +2 -0
  17. package/src/__tests__/call-setup-flow-name-capture.test.ts +0 -1
  18. package/src/__tests__/call-site-routing-provider.test.ts +1 -1
  19. package/src/__tests__/channel-availability-routes.test.ts +14 -1
  20. package/src/__tests__/channel-capabilities-dedupe.test.ts +214 -0
  21. package/src/__tests__/channel-delivery-store.test.ts +14 -14
  22. package/src/__tests__/config-loader-backfill.test.ts +3 -3
  23. package/src/__tests__/config-schema.test.ts +25 -10
  24. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +8 -11
  25. package/src/__tests__/conversation-agent-loop-overflow.test.ts +8 -11
  26. package/src/__tests__/conversation-agent-loop.test.ts +28 -20
  27. package/src/__tests__/conversation-attention-store.test.ts +63 -0
  28. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +0 -4
  29. package/src/__tests__/conversation-fork-crud.test.ts +69 -0
  30. package/src/__tests__/conversation-fork-referential.test.ts +67 -0
  31. package/src/__tests__/conversation-fork-retrospective.test.ts +24 -0
  32. package/src/__tests__/conversation-notifiers-provenance.test.ts +59 -0
  33. package/src/__tests__/conversation-queue.test.ts +55 -4
  34. package/src/__tests__/conversation-runtime-assembly.test.ts +134 -102
  35. package/src/__tests__/conversation-runtime-workspace.test.ts +14 -10
  36. package/src/__tests__/credential-prompt-route.test.ts +7 -10
  37. package/src/__tests__/custom-profile-ensure.test.ts +5 -1
  38. package/src/__tests__/discord-access-request-privacy.test.ts +5 -1
  39. package/src/__tests__/discord-requester-notice-privacy.test.ts +3 -3
  40. package/src/__tests__/document-append-idempotency.test.ts +233 -0
  41. package/src/__tests__/edit-propagation.test.ts +0 -7
  42. package/src/__tests__/helpers/mock-actor-context.ts +49 -0
  43. package/src/__tests__/helpers/mock-conversation.ts +13 -1
  44. package/src/__tests__/injector-chain.test.ts +63 -41
  45. package/src/__tests__/injector-disk-pressure.test.ts +11 -23
  46. package/src/__tests__/llm-context-resolution.test.ts +73 -1
  47. package/src/__tests__/llm-schema.test.ts +5 -2
  48. package/src/__tests__/mcp-list-plugin-servers.test.ts +250 -0
  49. package/src/__tests__/memory-retrieval-hook.test.ts +6 -5
  50. package/src/__tests__/messages-read-boundary-guard.test.ts +134 -0
  51. package/src/__tests__/mtime-cache.test.ts +1 -1
  52. package/src/__tests__/non-member-access-request.test.ts +0 -20
  53. package/src/__tests__/outbound-slack-persistence.test.ts +40 -1
  54. package/src/__tests__/plugin-import-boundary-guard.test.ts +5 -0
  55. package/src/__tests__/plugin-secret-pattern-contribution.test.ts +1 -1
  56. package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +14 -7
  57. package/src/__tests__/provider-commit-message-generator.test.ts +20 -0
  58. package/src/__tests__/run-conversation-turn-persistence.test.ts +434 -105
  59. package/src/__tests__/scoped-approval-grants.test.ts +11 -6
  60. package/src/__tests__/secret-ingress-channel.test.ts +0 -1
  61. package/src/__tests__/skills.test.ts +32 -0
  62. package/src/__tests__/slack-edit-ordering-characterization.test.ts +0 -1
  63. package/src/__tests__/subagent-call-site-routing.test.ts +31 -19
  64. package/src/__tests__/subagent-spawn-and-await.test.ts +14 -10
  65. package/src/__tests__/turn-events-store.test.ts +43 -0
  66. package/src/__tests__/user-plugin-loader.test.ts +1 -1
  67. package/src/__tests__/visible-app-context.test.ts +16 -9
  68. package/src/__tests__/worker-entrypoint-guards.test.ts +54 -0
  69. package/src/__tests__/worker-plugin-surface.test.ts +77 -0
  70. package/src/__tests__/workspace-migration-142-consolidate-voice-front-door.test.ts +158 -0
  71. package/src/__tests__/workspace-migration-143-repair-deprecated-codex-model-id.test.ts +133 -0
  72. package/src/__tests__/workspace-migration-144-convert-stranded-subscription-openai-profiles.test.ts +316 -0
  73. package/src/__tests__/workspace-migration-145-collapse-profile-bindings-to-entries.test.ts +325 -0
  74. package/src/acp/__tests__/acp-claude-oauth.test.ts +10 -2
  75. package/src/acp/__tests__/auth-required.test.ts +161 -0
  76. package/src/acp/acp-claude-oauth.ts +19 -2
  77. package/src/acp/agent-process.test.ts +100 -0
  78. package/src/acp/agent-process.ts +29 -26
  79. package/src/acp/auth-required.ts +102 -0
  80. package/src/acp/session-manager.test.ts +119 -0
  81. package/src/acp/session-manager.ts +68 -2
  82. package/src/api/events/acp-auth-required.ts +55 -0
  83. package/src/api/index.ts +7 -0
  84. package/src/apps/app-store.ts +3 -0
  85. package/src/bundler/package-resolver.ts +2 -30
  86. package/src/calls/__tests__/voice-session-bridge.test.ts +173 -1
  87. package/src/calls/__tests__/voice-triage-escalate.test.ts +94 -0
  88. package/src/calls/call-controller.ts +9 -2
  89. package/src/calls/call-setup-flow.ts +0 -1
  90. package/src/calls/media-stream-stt-session.ts +15 -0
  91. package/src/calls/voice-session-bridge.ts +71 -16
  92. package/src/calls/voice-triage-escalate.ts +104 -2
  93. package/src/channels/__tests__/plugin-channel-declarations.test.ts +161 -0
  94. package/src/channels/config.ts +13 -0
  95. package/src/channels/plugin-channel-declarations.ts +108 -0
  96. package/src/channels/types.ts +30 -0
  97. package/src/cli/AGENTS.md +5 -2
  98. package/src/cli/commands/credentials.help.ts +2 -2
  99. package/src/cli/commands/inference-providers.ts +1 -1
  100. package/src/cli/commands/mcp.help.ts +13 -4
  101. package/src/cli/commands/mcp.ts +9 -0
  102. package/src/cli/commands/memory/__tests__/memory-v3.test.ts +128 -5
  103. package/src/cli/commands/memory/index.help.ts +43 -1
  104. package/src/cli/commands/memory/memory-v3.ts +64 -0
  105. package/src/cli/commands/stt.help.ts +27 -2
  106. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +39 -0
  107. package/src/cli/lib/bundled-marketplace.json +13 -0
  108. package/src/cli/lib/upgrade-plugin.ts +42 -0
  109. package/src/config/__tests__/default-profile-catalog.test.ts +34 -2
  110. package/src/config/__tests__/default-provider.test.ts +6 -1
  111. package/src/config/__tests__/profile-materialization.test.ts +75 -19
  112. package/src/config/bundled-skills/acp/SKILL.md +6 -7
  113. package/src/config/bundled-skills/document-editor/SKILL.md +2 -2
  114. package/src/config/bundled-skills/document-editor/TOOLS.json +2 -2
  115. package/src/config/bundled-skills/media-processing/services/preprocess.ts +14 -4
  116. package/src/config/bundled-skills/settings/TOOLS.json +3 -3
  117. package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +22 -1
  118. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +9 -2
  119. package/src/config/call-site-defaults.ts +4 -5
  120. package/src/config/default-profile-catalog.ts +82 -11
  121. package/src/config/default-profile-names.ts +4 -1
  122. package/src/config/default-provider-resolution.ts +4 -0
  123. package/src/config/llm-context-resolution.ts +11 -3
  124. package/src/config/llm-resolver.ts +28 -1
  125. package/src/config/profile-materialization.ts +70 -22
  126. package/src/config/schemas/__tests__/live-voice.test.ts +107 -4
  127. package/src/config/schemas/call-site-catalog.ts +4 -4
  128. package/src/config/schemas/live-voice.ts +57 -23
  129. package/src/config/schemas/llm.ts +59 -32
  130. package/src/config/schemas/mcp.ts +23 -0
  131. package/src/config/schemas/plugin-updates.ts +6 -2
  132. package/src/config/schemas/stt.ts +1 -0
  133. package/src/context/outbound-sanitize.ts +96 -1
  134. package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +82 -0
  135. package/src/daemon/conversation-agent-loop-handlers.ts +15 -10
  136. package/src/daemon/conversation-agent-loop.ts +17 -6
  137. package/src/daemon/conversation-messaging.ts +5 -1
  138. package/src/daemon/conversation-notifiers.ts +9 -1
  139. package/src/daemon/conversation-process.ts +9 -6
  140. package/src/daemon/conversation-runtime-assembly.ts +3 -4
  141. package/src/daemon/conversation-tool-setup.ts +1 -2
  142. package/src/daemon/conversation.ts +48 -0
  143. package/src/daemon/mcp-reload-service.ts +36 -6
  144. package/src/daemon/process-message.ts +13 -3
  145. package/src/daemon/providers-setup.ts +6 -3
  146. package/src/daemon/trust-context-types.ts +29 -0
  147. package/src/daemon/wake-conversation-ops.ts +3 -2
  148. package/src/documents/document-store.ts +138 -5
  149. package/src/hooks/hook-loader.ts +3 -3
  150. package/src/hooks/registry.ts +50 -6
  151. package/src/inbound/__tests__/oauth-callback-url.test.ts +83 -0
  152. package/src/inbound/oauth-callback-url.ts +61 -0
  153. package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +1 -104
  154. package/src/live-voice/__tests__/live-voice-events.test.ts +7 -8
  155. package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +932 -0
  156. package/src/live-voice/__tests__/live-voice-metrics.test.ts +115 -8
  157. package/src/live-voice/__tests__/live-voice-photo.test.ts +100 -0
  158. package/src/live-voice/__tests__/live-voice-progress.test.ts +60 -194
  159. package/src/live-voice/__tests__/live-voice-stt.test.ts +14 -0
  160. package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +29 -0
  161. package/src/live-voice/__tests__/live-voice-tts-session.test.ts +0 -483
  162. package/src/live-voice/__tests__/live-voice-vad.test.ts +0 -16
  163. package/src/live-voice/__tests__/progress-narration.test.ts +214 -0
  164. package/src/live-voice/live-voice-archive.ts +2 -0
  165. package/src/live-voice/live-voice-metrics.ts +57 -32
  166. package/src/live-voice/live-voice-photo.ts +1 -2
  167. package/src/live-voice/live-voice-session.ts +535 -314
  168. package/src/live-voice/progress-narration.ts +277 -0
  169. package/src/live-voice/protocol.ts +21 -1
  170. package/src/mcp/__tests__/effective-config.test.ts +238 -0
  171. package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +0 -1
  172. package/src/mcp/__tests__/mcp-oauth-client-registration.test.ts +200 -0
  173. package/src/mcp/__tests__/mcp-oauth-provider.test.ts +9 -9
  174. package/src/mcp/__tests__/plugin-server-credential-isolation.test.ts +95 -0
  175. package/src/mcp/client.ts +16 -11
  176. package/src/mcp/effective-config.ts +113 -0
  177. package/src/mcp/manager.ts +11 -6
  178. package/src/mcp/mcp-auth-orchestrator.ts +13 -22
  179. package/src/mcp/mcp-oauth-provider.ts +205 -240
  180. package/src/monitoring/__tests__/plugin-auto-update.test.ts +166 -3
  181. package/src/monitoring/plugin-auto-update.ts +128 -24
  182. package/src/notifications/signal.ts +1 -0
  183. package/src/permissions/confirmation-guardian-request.test.ts +15 -11
  184. package/src/permissions/confirmation-guardian-request.ts +2 -2
  185. package/src/permissions/question-guardian-request.test.ts +14 -6
  186. package/src/permissions/question-guardian-request.ts +1 -2
  187. package/src/persistence/attachments-store.ts +8 -1
  188. package/src/persistence/bookmark-crud.ts +3 -7
  189. package/src/persistence/conversation-attention-store.ts +16 -45
  190. package/src/persistence/conversation-crud.ts +33 -4
  191. package/src/persistence/conversation-lineage.ts +9 -0
  192. package/src/persistence/conversation-queries.ts +108 -41
  193. package/src/persistence/delivery-crud.ts +38 -29
  194. package/src/persistence/external-conversation-store.ts +32 -4
  195. package/src/persistence/llm-request-log-store.ts +4 -10
  196. package/src/persistence/llm-usage-store.ts +8 -3
  197. package/src/persistence/message-reads.test.ts +197 -0
  198. package/src/persistence/message-reads.ts +211 -0
  199. package/src/persistence/migrations/366-chatgpt-subscription-row-identity.test.ts +120 -0
  200. package/src/persistence/migrations/366-chatgpt-subscription-row-identity.ts +62 -0
  201. package/src/persistence/real-user-turn-filter.ts +27 -3
  202. package/src/persistence/steps.ts +9 -0
  203. package/src/plugin-api/__tests__/oauth-callback-url-export.test.ts +29 -0
  204. package/src/plugin-api/conversation-turn.ts +168 -5
  205. package/src/plugin-api/index.ts +21 -5
  206. package/src/plugins/__tests__/mcp-servers.test.ts +371 -0
  207. package/src/plugins/defaults/memory/AGENTS.md +4 -0
  208. package/src/plugins/defaults/memory/__tests__/buffer-format.test.ts +204 -0
  209. package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +72 -0
  210. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +4 -1
  211. package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +4 -1
  212. package/src/plugins/defaults/memory/buffer-format.ts +165 -0
  213. package/src/plugins/defaults/memory/context-search/sources/conversations.ts +6 -0
  214. package/src/plugins/defaults/memory/graph/image-ref-utils.ts +3 -0
  215. package/src/plugins/defaults/memory/graph/tool-handlers.ts +1 -30
  216. package/src/plugins/defaults/memory/graph-topology/pending-buffer.test.ts +34 -0
  217. package/src/plugins/defaults/memory/graph-topology/pending-buffer.ts +8 -12
  218. package/src/plugins/defaults/memory/hooks/post-compact.ts +1 -4
  219. package/src/plugins/defaults/memory/indexer.ts +3 -1
  220. package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +19 -7
  221. package/src/plugins/defaults/memory/src/__tests__/memory-v3-gate-stats.test.ts +281 -0
  222. package/src/plugins/defaults/memory/src/memory-v3-routes.ts +207 -0
  223. package/src/plugins/defaults/memory/substrate/__tests__/consolidation-job.test.ts +33 -0
  224. package/src/plugins/defaults/memory/substrate/__tests__/static-context.test.ts +199 -2
  225. package/src/plugins/defaults/memory/substrate/consolidation-job.ts +25 -18
  226. package/src/plugins/defaults/memory/substrate/skill-content.ts +8 -1
  227. package/src/plugins/defaults/memory/substrate/static-context.ts +160 -4
  228. package/src/plugins/defaults/memory/substrate/sweep-job.ts +2 -4
  229. package/src/plugins/defaults/memory/v1/graph/extraction.ts +3 -1
  230. package/src/plugins/defaults/memory/v3/prune.ts +2 -0
  231. package/src/plugins/defaults/memory/v3/selection-log-store.ts +2 -0
  232. package/src/plugins/defaults/memory/worker.ts +6 -3
  233. package/src/plugins/external-plugin-loader.ts +47 -0
  234. package/src/plugins/mcp-servers.ts +361 -0
  235. package/src/plugins/mtime-cache.ts +23 -49
  236. package/src/plugins/worker-plugin-surface.ts +33 -0
  237. package/src/providers/__tests__/connection-model-compat.test.ts +1 -1
  238. package/src/providers/__tests__/dispatch-connection-routing.test.ts +214 -2
  239. package/src/providers/__tests__/preflight-resolved-config.test.ts +57 -0
  240. package/src/providers/__tests__/retry-callsite.test.ts +5 -2
  241. package/src/providers/call-site-routing.ts +30 -3
  242. package/src/providers/connection-resolution.ts +194 -11
  243. package/src/providers/inference/auth.ts +6 -0
  244. package/src/providers/inference/connection-availability.ts +24 -2
  245. package/src/providers/inference/connections.ts +2 -0
  246. package/src/providers/model-intents.ts +26 -6
  247. package/src/providers/openai/codex-models.ts +2 -1
  248. package/src/providers/provider-send-message.ts +32 -3
  249. package/src/providers/speech-to-text/__tests__/deepgram-flux-frames.test.ts +433 -0
  250. package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +620 -0
  251. package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +34 -0
  252. package/src/providers/speech-to-text/__tests__/resolve.test.ts +285 -6
  253. package/src/providers/speech-to-text/deepgram-flux-frames.ts +395 -0
  254. package/src/providers/speech-to-text/deepgram-flux-realtime.ts +719 -0
  255. package/src/providers/speech-to-text/provider-catalog.ts +99 -8
  256. package/src/providers/speech-to-text/resolve.ts +25 -2
  257. package/src/routes/worker.ts +17 -5
  258. package/src/runtime/access-request-helper.ts +9 -12
  259. package/src/runtime/agent-wake.ts +3 -3
  260. package/src/runtime/pre-first-message-gate.ts +4 -0
  261. package/src/runtime/routes/__tests__/acp-claude-auth-routes.test.ts +12 -4
  262. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +219 -1
  263. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +52 -0
  264. package/src/runtime/routes/__tests__/default-provider-routes.test.ts +61 -0
  265. package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +44 -0
  266. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +102 -1
  267. package/src/runtime/routes/__tests__/plugins-routes.test.ts +44 -0
  268. package/src/runtime/routes/__tests__/stt-routes.test.ts +25 -0
  269. package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +62 -1
  270. package/src/runtime/routes/channel-availability-routes.ts +32 -14
  271. package/src/runtime/routes/channel-route-shared.ts +0 -6
  272. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +6 -6
  273. package/src/runtime/routes/conversation-list-routes.ts +112 -1
  274. package/src/runtime/routes/conversation-query-routes.ts +40 -27
  275. package/src/runtime/routes/credential-prompt-routes.ts +4 -7
  276. package/src/runtime/routes/default-provider-routes.ts +15 -0
  277. package/src/runtime/routes/inbound-message-handler.ts +17 -41
  278. package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +0 -1
  279. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +0 -9
  280. package/src/runtime/routes/inbound-stages/admission-policy.ts +1 -17
  281. package/src/runtime/routes/inbound-stages/bootstrap-intercept.test.ts +0 -1
  282. package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +2 -3
  283. package/src/runtime/routes/inbound-stages/edit-intercept.ts +1 -3
  284. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +0 -1
  285. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +3 -4
  286. package/src/runtime/routes/inbound-stages/reaction-intercept.test.ts +0 -1
  287. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +11 -20
  288. package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +2 -3
  289. package/src/runtime/routes/inference-profiles-routes.ts +20 -11
  290. package/src/runtime/routes/inference-provider-connection-routes.ts +77 -15
  291. package/src/runtime/routes/log-export-routes.ts +3 -0
  292. package/src/runtime/routes/mcp-auth-routes.ts +148 -57
  293. package/src/runtime/routes/plugins-routes.ts +21 -3
  294. package/src/runtime/routes/stt-routes.ts +31 -25
  295. package/src/runtime/routes/surface-conversation-resolver.ts +3 -0
  296. package/src/runtime/routes/user-route-dispatcher.ts +39 -14
  297. package/src/runtime/routes/user-route-import.ts +108 -0
  298. package/src/schedule/worker.ts +6 -0
  299. package/src/security/oauth2.ts +6 -22
  300. package/src/stt/__tests__/daemon-batch-transcriber.test.ts +22 -0
  301. package/src/stt/__tests__/types.test.ts +94 -0
  302. package/src/stt/daemon-batch-transcriber.ts +10 -0
  303. package/src/stt/stt-stream-session.ts +8 -4
  304. package/src/stt/types.ts +103 -0
  305. package/src/subagent/manager.ts +1 -3
  306. package/src/subagent/types.ts +7 -6
  307. package/src/tools/acp/spawn.test.ts +97 -0
  308. package/src/tools/acp/spawn.ts +32 -0
  309. package/src/tools/document/document-tool.ts +12 -3
  310. package/src/tools/registry.ts +2 -1
  311. package/src/tools/workflows/run-workflow.ts +1 -2
  312. package/src/tts/__tests__/reasoning-tag-filter.test.ts +63 -0
  313. package/src/tts/reasoning-tag-filter.ts +110 -0
  314. package/src/workspace/byok-default-profile-ensure.ts +61 -18
  315. package/src/workspace/custom-profile-ensure.ts +4 -24
  316. package/src/workspace/migrations/142-consolidate-voice-front-door.ts +70 -0
  317. package/src/workspace/migrations/143-repair-deprecated-codex-model-id.ts +134 -0
  318. package/src/workspace/migrations/144-convert-stranded-subscription-openai-profiles.ts +265 -0
  319. package/src/workspace/migrations/145-collapse-profile-bindings-to-entries.ts +328 -0
  320. package/src/workspace/migrations/__tests__/141-stt-english-default-to-multilingual.test.ts +0 -10
  321. package/src/workspace/migrations/registry.ts +8 -0
  322. package/src/workspace/provider-commit-message-generator.ts +7 -5
  323. package/src/live-voice/__tests__/front-decision.test.ts +0 -645
  324. package/src/live-voice/front-decision.ts +0 -476
@@ -0,0 +1,328 @@
1
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { Database } from "bun:sqlite";
4
+
5
+ import { getLogger } from "../../util/logger.js";
6
+ import type { WorkspaceMigration } from "./types.js";
7
+
8
+ const log = getLogger("migrations/145-collapse-profile-bindings-to-entries");
9
+
10
+ // Fold each profile's `provider_connection` into its `provider` as a
11
+ // connection entry name. Under the entries model the provider field holds
12
+ // the entry name and the row's own provider (its kind) drives dispatch, so
13
+ // an explicit binding needs no second field.
14
+ //
15
+ // Rules per `llm.profiles.<key>`:
16
+ // - Routing-identity providers ("vellum"/"chatgpt") carry no binding by
17
+ // definition: a stray `provider_connection` is deleted verbatim.
18
+ // - A binding whose row EXISTS and whose kind AGREES with the profile's
19
+ // declared provider becomes the provider value (the entry name), and the
20
+ // binding field is deleted. Kind agreement, with the identity kinds
21
+ // mapped the way dispatch maps them: a row of the same provider, a
22
+ // "chatgpt" row for a declared "openai", or a "vellum" row for a declared
23
+ // managed-routable provider.
24
+ // - A dangling binding (row deleted) or a kind-disagreeing binding keeps
25
+ // the declared provider and deletes only the binding, with a structured
26
+ // warn: dispatch's auto-resolution then behaves the way it always has
27
+ // for an unbound vendor profile, and a rewrite would either strand the
28
+ // profile on a nonexistent entry or silently change vendors.
29
+ // - A binding equal to the declared provider (conventional shapes older
30
+ // migrations kept) is a plain delete: the bare vendor value already
31
+ // means the default entry of that kind.
32
+ //
33
+ // Call-site fragments and `llm.defaultProvider.connectionName` are NOT
34
+ // touched: call-site overrides become model-only together with the
35
+ // resolver's tweak blocks (one coherent change, later), and the default
36
+ // provider keeps its closed vendor enum, so its binding needs its own
37
+ // design before it can collapse.
38
+ //
39
+ // The identity mapping and managed-routable set are frozen snapshots
40
+ // (migrations are self-contained): openai/anthropic/gemini/fireworks/
41
+ // together as of 2026-08-10. A provider outside the snapshot takes the
42
+ // conservative keep-provider path.
43
+
44
+ const ROUTING_IDENTITIES = new Set(["vellum", "chatgpt"]);
45
+ const MANAGED_ROUTABLE = new Set([
46
+ "openai",
47
+ "anthropic",
48
+ "gemini",
49
+ "fireworks",
50
+ "together",
51
+ ]);
52
+
53
+ // A binding literally named "vellum" or "chatgpt" folds into the ROUTING
54
+ // IDENTITY value, whose model the read-path schema validates (an unroutable
55
+ // pair strips the whole profile on read). Folding is therefore gated on the
56
+ // model being servable by that identity, judged against frozen snapshots of
57
+ // the routing tables as of 2026-08-10; a model outside the snapshot leaves
58
+ // the profile untouched (dispatch keeps honoring the legacy binding).
59
+ const VELLUM_ROUTABLE_MODELS = new Set([
60
+ "claude-fable-5",
61
+ "claude-opus-5",
62
+ "claude-opus-4-8",
63
+ "claude-opus-4-7",
64
+ "claude-opus-4-6",
65
+ "claude-sonnet-5",
66
+ "claude-sonnet-4-6",
67
+ "claude-sonnet-4-5-20250929",
68
+ "claude-opus-4-5-20251101",
69
+ "claude-haiku-4-5-20251001",
70
+ "gpt-5.6-sol",
71
+ "gpt-5.6-terra",
72
+ "gpt-5.6-luna",
73
+ "gpt-5.5",
74
+ "gpt-5.5-pro",
75
+ "gpt-5.4",
76
+ "gpt-5.2",
77
+ "gpt-5.4-mini",
78
+ "gpt-5.4-nano",
79
+ "gemini-3.6-flash",
80
+ "gemini-3.5-flash",
81
+ "gemini-3.5-flash-lite",
82
+ "gemini-3.1-pro-preview",
83
+ "gemini-3.1-pro-preview-customtools",
84
+ "gemini-3-flash-preview",
85
+ "gemini-3.1-flash-lite-preview",
86
+ "gemini-3.1-flash-lite",
87
+ "gemini-2.5-flash",
88
+ "gemini-2.5-flash-lite",
89
+ "gemini-2.5-pro",
90
+ "accounts/fireworks/models/kimi-k3",
91
+ "accounts/fireworks/models/kimi-k2p6",
92
+ "accounts/fireworks/models/glm-5p2",
93
+ "accounts/fireworks/models/minimax-m3",
94
+ "accounts/fireworks/models/minimax-m2p7",
95
+ "accounts/fireworks/models/deepseek-v4-pro",
96
+ "accounts/fireworks/models/deepseek-v4-flash",
97
+ "MiniMaxAI/MiniMax-M3",
98
+ ]);
99
+ const CODEX_MODELS = new Set([
100
+ "gpt-5.6-sol",
101
+ "gpt-5.6-terra",
102
+ "gpt-5.6-luna",
103
+ "gpt-5.5",
104
+ "gpt-5.4",
105
+ "gpt-5.4-mini",
106
+ ]);
107
+
108
+ function identitySafeModel(identity: string, model: unknown): boolean {
109
+ if (typeof model !== "string") {
110
+ return false;
111
+ }
112
+ return identity === "vellum"
113
+ ? VELLUM_ROUTABLE_MODELS.has(model)
114
+ : CODEX_MODELS.has(model);
115
+ }
116
+
117
+ export const collapseProfileBindingsToEntriesMigration: WorkspaceMigration = {
118
+ id: "145-collapse-profile-bindings-to-entries",
119
+ description:
120
+ "Fold llm.profiles.*.provider_connection into provider as connection entry names",
121
+ retryFailedCheckpoint: true,
122
+ run(workspaceDir: string): void {
123
+ const configPath = join(workspaceDir, "config.json");
124
+ if (!existsSync(configPath)) {
125
+ return;
126
+ }
127
+
128
+ let config: Record<string, unknown>;
129
+ try {
130
+ const raw = JSON.parse(readFileSync(configPath, "utf-8"));
131
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
132
+ return;
133
+ }
134
+ config = raw as Record<string, unknown>;
135
+ } catch {
136
+ return;
137
+ }
138
+
139
+ const llm = asObject(config.llm);
140
+ const profiles = llm === null ? null : asObject(llm.profiles);
141
+ if (profiles === null) {
142
+ return;
143
+ }
144
+
145
+ const bindings = Object.values(profiles).some((value) => {
146
+ const entry = asObject(value);
147
+ return (
148
+ entry !== null &&
149
+ typeof entry.provider_connection === "string" &&
150
+ entry.provider_connection.length > 0
151
+ );
152
+ });
153
+ if (!bindings) {
154
+ return;
155
+ }
156
+
157
+ // Bindings exist, so the rewrite needs the rows. An ABSENT DB file is a
158
+ // real state (a config restored into a fresh workspace has no rows, so
159
+ // every binding is genuinely dangling); an unreadable or unqueryable DB
160
+ // must instead fail the run (retried next boot) rather than checkpoint
161
+ // a pass that would take the destructive dangling path for every
162
+ // binding.
163
+ const rows = readConnectionRows(workspaceDir);
164
+ if (rows === null) {
165
+ throw new Error(
166
+ "provider_connections is not readable; retrying the binding collapse on the next run",
167
+ );
168
+ }
169
+
170
+ let changed = false;
171
+ for (const [key, value] of Object.entries(profiles)) {
172
+ const entry = asObject(value);
173
+ if (entry === null) {
174
+ continue;
175
+ }
176
+ const binding = entry.provider_connection;
177
+ if (typeof binding !== "string" || binding.length === 0) {
178
+ continue;
179
+ }
180
+ const provider =
181
+ typeof entry.provider === "string" ? entry.provider : undefined;
182
+
183
+ if (provider !== undefined && ROUTING_IDENTITIES.has(provider)) {
184
+ delete entry.provider_connection;
185
+ changed = true;
186
+ log.info(
187
+ { profile: key, provider, binding },
188
+ "Deleted stray binding on a routing-identity profile",
189
+ );
190
+ continue;
191
+ }
192
+
193
+ if (binding === provider) {
194
+ // A row named exactly like its vendor was an explicit pin under the
195
+ // old create route; with several rows of that kind, the bare vendor
196
+ // would auto-resolve differently, so the pin stays until the entry
197
+ // picker can express it. Without such a row the field is a plain
198
+ // conventional shape and the bare vendor already means the default.
199
+ if (rows.has(binding)) {
200
+ log.info(
201
+ { profile: key, provider },
202
+ "Kept self-named binding (an explicit pin among possible siblings)",
203
+ );
204
+ continue;
205
+ }
206
+ delete entry.provider_connection;
207
+ changed = true;
208
+ log.info(
209
+ { profile: key, provider },
210
+ "Deleted self-referential binding (bare vendor means the default entry)",
211
+ );
212
+ continue;
213
+ }
214
+
215
+ if (ROUTING_IDENTITIES.has(binding)) {
216
+ // Folding writes the identity VALUE itself; gate on the model the
217
+ // identity can serve, or the read-path schema would strip the
218
+ // profile. An unservable model leaves the profile untouched.
219
+ if (identitySafeModel(binding, entry.model)) {
220
+ entry.provider = binding;
221
+ delete entry.provider_connection;
222
+ changed = true;
223
+ log.info(
224
+ { profile: key, previousProvider: provider, identity: binding },
225
+ "Folded canonical binding into the routing identity",
226
+ );
227
+ } else {
228
+ log.warn(
229
+ { profile: key, provider, binding, reason: "model_not_servable" },
230
+ "Left canonical binding in place; the identity cannot serve this model",
231
+ );
232
+ }
233
+ continue;
234
+ }
235
+
236
+ const rowKind = rows.get(binding);
237
+ if (rowKind === undefined) {
238
+ delete entry.provider_connection;
239
+ changed = true;
240
+ log.warn(
241
+ { profile: key, provider, binding, reason: "row_missing" },
242
+ "Deleted dangling binding; profile keeps its declared provider",
243
+ );
244
+ continue;
245
+ }
246
+
247
+ if (!kindAgrees(rowKind, provider)) {
248
+ delete entry.provider_connection;
249
+ changed = true;
250
+ log.warn(
251
+ { profile: key, provider, binding, rowKind, reason: "kind_mismatch" },
252
+ "Deleted kind-disagreeing binding; profile keeps its declared provider",
253
+ );
254
+ continue;
255
+ }
256
+
257
+ entry.provider = binding;
258
+ delete entry.provider_connection;
259
+ changed = true;
260
+ log.info(
261
+ { profile: key, previousProvider: provider, entry: binding },
262
+ "Folded profile binding into provider as an entry name",
263
+ );
264
+ }
265
+
266
+ if (changed) {
267
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
268
+ }
269
+ },
270
+ down(_workspaceDir: string): void {
271
+ // Forward-only.
272
+ },
273
+ };
274
+
275
+ function asObject(value: unknown): Record<string, unknown> | null {
276
+ return value !== null && typeof value === "object" && !Array.isArray(value)
277
+ ? (value as Record<string, unknown>)
278
+ : null;
279
+ }
280
+
281
+ /**
282
+ * Whether a row of `rowKind` can serve a profile declaring `provider`,
283
+ * mirroring dispatch's identity mapping. An undeclared provider agrees with
284
+ * any row: the entry alone defines the route.
285
+ */
286
+ function kindAgrees(rowKind: string, provider: string | undefined): boolean {
287
+ if (provider === undefined) {
288
+ return true;
289
+ }
290
+ if (rowKind === provider) {
291
+ return true;
292
+ }
293
+ if (rowKind === "chatgpt") {
294
+ return provider === "openai";
295
+ }
296
+ if (rowKind === "vellum") {
297
+ return MANAGED_ROUTABLE.has(provider);
298
+ }
299
+ return false;
300
+ }
301
+
302
+ /**
303
+ * Connection name -> provider kind, or null when the DB or table is not
304
+ * readable. The caller fails the run on null: bindings must be judged
305
+ * against real rows, never guessed.
306
+ */
307
+ function readConnectionRows(workspaceDir: string): Map<string, string> | null {
308
+ const dbPath = join(workspaceDir, "data", "db", "assistant.db");
309
+ if (!existsSync(dbPath)) {
310
+ return new Map();
311
+ }
312
+ let db: Database;
313
+ try {
314
+ db = new Database(dbPath);
315
+ } catch {
316
+ return null;
317
+ }
318
+ try {
319
+ const rows = db
320
+ .query(`SELECT name, provider FROM provider_connections`)
321
+ .all() as Array<{ name: string; provider: string }>;
322
+ return new Map(rows.map((r) => [r.name, r.provider]));
323
+ } catch {
324
+ return null;
325
+ } finally {
326
+ db.close();
327
+ }
328
+ }
@@ -10,7 +10,6 @@ import { join } from "node:path";
10
10
  import { afterEach, beforeEach, describe, expect, test } from "bun:test";
11
11
 
12
12
  import { sttEnglishDefaultToMultilingualMigration as MIG } from "../141-stt-english-default-to-multilingual.js";
13
- import { WORKSPACE_MIGRATIONS } from "../registry.js";
14
13
 
15
14
  let workspaceDir: string;
16
15
  let configPath: string;
@@ -37,15 +36,6 @@ afterEach(() => {
37
36
  });
38
37
 
39
38
  describe("141-stt-english-default-to-multilingual", () => {
40
- test("is registered last, so it defines the registry ceiling", () => {
41
- // getLastWorkspaceMigrationId() reads the final entry, and the runner
42
- // executes the array in order, so a new migration belongs at the end.
43
- expect(MIG.id).toBe("141-stt-english-default-to-multilingual");
44
- expect(WORKSPACE_MIGRATIONS[WORKSPACE_MIGRATIONS.length - 1]?.id).toBe(
45
- MIG.id,
46
- );
47
- });
48
-
49
39
  test("moves an accepted English default to multilingual on deepgram", () => {
50
40
  write({ provider: "deepgram", language: "en" });
51
41
  MIG.run(workspaceDir);
@@ -139,6 +139,10 @@ import { backfillHomeFeedTitlesMigration } from "./138-backfill-home-feed-titles
139
139
  import { clearRenamedCostProfileLabelMigration } from "./139-clear-renamed-cost-profile-label.js";
140
140
  import { repairSeedPinnedMemoryV3LiveMigration } from "./140-repair-seed-pinned-memory-v3-live.js";
141
141
  import { sttEnglishDefaultToMultilingualMigration } from "./141-stt-english-default-to-multilingual.js";
142
+ import { consolidateVoiceFrontDoorMigration } from "./142-consolidate-voice-front-door.js";
143
+ import { repairDeprecatedCodexModelIdMigration } from "./143-repair-deprecated-codex-model-id.js";
144
+ import { convertStrandedSubscriptionOpenaiProfilesMigration } from "./144-convert-stranded-subscription-openai-profiles.js";
145
+ import { collapseProfileBindingsToEntriesMigration } from "./145-collapse-profile-bindings-to-entries.js";
142
146
  import { migrateToWorkspaceVolumeMigration } from "./migrate-to-workspace-volume.js";
143
147
  import type { WorkspaceMigration } from "./types.js";
144
148
 
@@ -293,4 +297,8 @@ export const WORKSPACE_MIGRATIONS: WorkspaceMigration[] = [
293
297
  clearRenamedCostProfileLabelMigration,
294
298
  repairSeedPinnedMemoryV3LiveMigration,
295
299
  sttEnglishDefaultToMultilingualMigration,
300
+ consolidateVoiceFrontDoorMigration,
301
+ repairDeprecatedCodexModelIdMigration,
302
+ convertStrandedSubscriptionOpenaiProfilesMigration,
303
+ collapseProfileBindingsToEntriesMigration,
296
304
  ];
@@ -162,13 +162,15 @@ class ProviderCommitMessageGenerator {
162
162
  const providerName = resolved.configuredProviderName;
163
163
 
164
164
  // Step 2b: API key preflight for the configured provider. Skipped for
165
- // keyless providers and for routing identities, which authenticate
166
- // through their connection row (platform token / ChatGPT OAuth) — no
167
- // provider API key exists for them, and resolution already verified the
168
- // connection credential.
165
+ // keyless providers, for routing identities, and for entry-routed
166
+ // results, since all three authenticate through their connection row
167
+ // (platform token / ChatGPT OAuth / the row's own credential slot), so
168
+ // no generic per-vendor key needs to exist, and resolution already
169
+ // verified the connection credential.
169
170
  if (
170
171
  !KEYLESS_PROVIDERS.has(providerName) &&
171
- !ROUTING_IDENTITY_PROVIDERS.has(providerName)
172
+ !ROUTING_IDENTITY_PROVIDERS.has(providerName) &&
173
+ !resolved.entryRouted
172
174
  ) {
173
175
  const providerApiKey = await getProviderKeyAsync(providerName);
174
176
  if (!providerApiKey) {