@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
@@ -18,6 +18,7 @@ import {
18
18
  type LLMProvider,
19
19
  type ProfileEntry,
20
20
  } from "../config/schemas/llm.js";
21
+ import { ROUTING_IDENTITY_PROVIDERS } from "../providers/inference/auth.js";
21
22
  import { getLogger } from "../util/logger.js";
22
23
  import { completedProfileBody } from "./custom-profile-ensure.js";
23
24
 
@@ -135,10 +136,26 @@ const ERA_COPY_LABEL_SUFFIX = " (Custom Provider)";
135
136
  /**
136
137
  * Comparison ignores `label` and `status` (user overlay state, preserved via
137
138
  * `userOverlayState`) and `model` (checked separately against the current
138
- * intent resolution and `HISTORICAL_INTENT_MODELS`).
139
+ * intent resolution and `HISTORICAL_INTENT_MODELS`). The CONVENTIONAL
140
+ * `<vendor>-personal` binding is normalized away before comparison (it was
141
+ * machinery-written across several eras); a non-conventional binding is a
142
+ * user selection and keeps blocking conversion.
139
143
  */
140
144
  const IGNORED_COMPARISON_KEYS = new Set(["label", "status", "model"]);
141
145
 
146
+ /**
147
+ * Fold the conventional personal entry name back to its vendor. The entries
148
+ * collapse (migration 145) rewrites a hatch copy's binding into its
149
+ * provider as `<vendor>-personal`; the matcher folds that shape so every
150
+ * era of stored copy keeps converting (the same discipline as
151
+ * `HISTORICAL_INTENT_MODELS`).
152
+ */
153
+ function foldPersonalEntryName(provider: string): string {
154
+ return provider.endsWith("-personal")
155
+ ? provider.slice(0, -"-personal".length)
156
+ : provider;
157
+ }
158
+
142
159
  /**
143
160
  * Per-provider model ids that earlier intent eras pinned onto `custom-*`
144
161
  * copies, verified against `providers/model-intents.ts` git history. Hatch
@@ -146,9 +163,10 @@ const IGNORED_COMPARISON_KEYS = new Set(["label", "status", "model"]);
146
163
  * 2026-05-05, #29755), and the profile-model migrations (100, 103, 109, 113,
147
164
  * 123) deliberately rewrite only the managed default entries (`custom-*` is
148
165
  * the user's to manage), so an untouched copy still carries whichever value
149
- * its hatch-era intent resolved to. Migration 136 is the one exception: it
150
- * rewrote kimi-k2p5 pins (`custom-*` included) to deepseek-v4-flash in
151
- * place. A model listed here counts as unedited only when every non-model
166
+ * its hatch-era intent resolved to. Migrations 136 and 146 are the
167
+ * exceptions: they rewrite stale Fireworks pins (`custom-*` included) in
168
+ * place, so their targets are machinery-authored values too. A model listed
169
+ * here counts as unedited only when every non-model
152
170
  * field still matches the template.
153
171
  */
154
172
  const HISTORICAL_INTENT_MODELS: Record<
@@ -156,6 +174,10 @@ const HISTORICAL_INTENT_MODELS: Record<
156
174
  Partial<Record<string, readonly string[]>>
157
175
  > = {
158
176
  balanced: {
177
+ openai: [
178
+ // balanced intent 2026-05-05 (#29755) to the 2026-08-10 gpt-5.6 repoint.
179
+ "gpt-5.4-mini",
180
+ ],
159
181
  fireworks: [
160
182
  // balanced intent 2026-05-05 (#29755) to 2026-05-19 (#31068).
161
183
  "accounts/fireworks/models/kimi-k2p5",
@@ -163,9 +185,15 @@ const HISTORICAL_INTENT_MODELS: Record<
163
185
  "accounts/fireworks/models/kimi-k2p6",
164
186
  // migration 136's in-place rewrite of a kimi-k2p5 pin.
165
187
  "accounts/fireworks/models/deepseek-v4-flash",
188
+ // the ID migration 146 writes over stale deepseek-v4-flash pins.
189
+ "accounts/fireworks/models/deepseek-v4-flash-0731",
166
190
  ],
167
191
  },
168
192
  "quality-optimized": {
193
+ openai: [
194
+ // quality intent 2026-05-05 (#29755) to the 2026-08-10 gpt-5.6 repoint.
195
+ "gpt-5.4",
196
+ ],
169
197
  anthropic: [
170
198
  // quality intent 2026-05-05 (#29755) to 2026-06-11 (#34498).
171
199
  "claude-opus-4-7",
@@ -183,6 +211,8 @@ const HISTORICAL_INTENT_MODELS: Record<
183
211
  "accounts/fireworks/models/kimi-k2p5",
184
212
  // migration 136's in-place rewrite of a kimi-k2p5 pin.
185
213
  "accounts/fireworks/models/deepseek-v4-flash",
214
+ // the ID migration 146 writes over stale deepseek-v4-flash pins.
215
+ "accounts/fireworks/models/deepseek-v4-flash-0731",
186
216
  ],
187
217
  },
188
218
  "cost-optimized": {
@@ -196,10 +226,14 @@ const HISTORICAL_INTENT_MODELS: Record<
196
226
  ],
197
227
  fireworks: [
198
228
  // latency intent 2026-05-05 (#29755) to 2026-07-28 (#39446). On live
199
- // configs migration 136 rewrote it to deepseek-v4-flash (the current
200
- // intent), so this survives only in configs restored from pre-136
201
- // backups.
229
+ // configs migration 136 rewrites it to deepseek-v4-flash, so this
230
+ // survives only in configs restored from pre-136 backups.
202
231
  "accounts/fireworks/models/kimi-k2p5",
232
+ // latency intent 2026-07-28 (#39446) to the 2026-08-17 dated-ID
233
+ // repoint; also the ID migration 136 writes. On live configs migration
234
+ // 146 rewrites it to the dated ID, so this survives only in pre-146
235
+ // backups.
236
+ "accounts/fireworks/models/deepseek-v4-flash",
203
237
  ],
204
238
  },
205
239
  };
@@ -259,11 +293,13 @@ export function ensureByokDefaultProfiles(workspaceDir: string): void {
259
293
  const completionBase = LLMConfigBase.safeParse(llm.default ?? {}).data;
260
294
 
261
295
  // The default provider on a BYOK default, or the uniform hatch provider
262
- // across the complete copy set on a vellum default; null converts nothing.
263
- const candidateProvider =
264
- parsedDefault.data.provider !== "vellum"
265
- ? parsedDefault.data.provider
266
- : uniformCopyProvider(profiles);
296
+ // across the complete copy set on a routing-identity default (vellum,
297
+ // chatgpt); null converts nothing.
298
+ const candidateProvider = ROUTING_IDENTITY_PROVIDERS.has(
299
+ parsedDefault.data.provider,
300
+ )
301
+ ? uniformCopyProvider(profiles)
302
+ : parsedDefault.data.provider;
267
303
  const convertibleProvider =
268
304
  candidateProvider !== null && isByokDefaultProviderChoice(candidateProvider)
269
305
  ? candidateProvider
@@ -339,7 +375,11 @@ function isKnownUneditedBody(
339
375
  // (hatch materialized it once; the default provider may have changed
340
376
  // since); equality with the corroborated provider rules out a user
341
377
  // re-provision.
342
- if (convertibleProvider === null || entry.provider !== convertibleProvider) {
378
+ if (
379
+ convertibleProvider === null ||
380
+ typeof entry.provider !== "string" ||
381
+ foldPersonalEntryName(entry.provider) !== convertibleProvider
382
+ ) {
343
383
  return false;
344
384
  }
345
385
  const copyProvider = convertibleProvider;
@@ -347,11 +387,13 @@ function isKnownUneditedBody(
347
387
  if (template === undefined) {
348
388
  return false;
349
389
  }
350
- const materialized = materializeProfile(
351
- template,
352
- copyProvider,
353
- `${copyProvider}-personal`,
354
- ) as Record<string, unknown>;
390
+ // No connection binding: the bare provider means the default entry of
391
+ // that kind, and a stamped binding would re-introduce the collapsed
392
+ // field on disk.
393
+ const materialized = materializeProfile(template, copyProvider) as Record<
394
+ string,
395
+ unknown
396
+ >;
355
397
  if (
356
398
  entry.model !== materialized.model &&
357
399
  !(HISTORICAL_INTENT_MODELS[key][copyProvider] ?? []).includes(entry.model)
@@ -361,11 +403,20 @@ function isKnownUneditedBody(
361
403
  // Completion skips managed-source bodies, so a copy from the era whose
362
404
  // templates wrote `source: "managed"` (#29755 to #29768, 2026-05-05) is
363
405
  // compared as if user-source to keep both sides normalized identically.
406
+ const normalizedEntry: Record<string, unknown> = {
407
+ ...entry,
408
+ provider: copyProvider,
409
+ ...(entry.source === "managed" ? { source: "user" } : {}),
410
+ };
411
+ // The conventional personal binding is machinery-written (hatch stamps,
412
+ // completion re-stamps); normalize it away. Any other binding is user
413
+ // state and stays in the comparison, where the unbound template side
414
+ // makes it block conversion.
415
+ if (normalizedEntry.provider_connection === `${copyProvider}-personal`) {
416
+ delete normalizedEntry.provider_connection;
417
+ }
364
418
  const body = comparableBody(
365
- withCompletionBaked(
366
- entry.source === "managed" ? { ...entry, source: "user" } : entry,
367
- completionBase,
368
- ),
419
+ withCompletionBaked(normalizedEntry, completionBase),
369
420
  );
370
421
  const known = comparableBody(
371
422
  withCompletionBaked(materialized, completionBase),
@@ -392,9 +443,10 @@ function uniformCopyProvider(profiles: Record<string, unknown>): string | null {
392
443
  if (typeof entry.provider !== "string") {
393
444
  return null;
394
445
  }
446
+ const entryProvider = foldPersonalEntryName(entry.provider);
395
447
  if (provider === null) {
396
- provider = entry.provider;
397
- } else if (provider !== entry.provider) {
448
+ provider = entryProvider;
449
+ } else if (provider !== entryProvider) {
398
450
  return null;
399
451
  }
400
452
  }
@@ -3,7 +3,10 @@ import { join } from "node:path";
3
3
  import { isDeepStrictEqual } from "node:util";
4
4
 
5
5
  import { invalidateConfigCache } from "../config/loader.js";
6
- import { completeCustomProfile } from "../config/profile-materialization.js";
6
+ import {
7
+ completeCustomProfile,
8
+ mergePreservingUnknownKeys,
9
+ } from "../config/profile-materialization.js";
7
10
  import { LLMConfigBase, ProfileEntry } from "../config/schemas/llm.js";
8
11
  import { getLogger } from "../util/logger.js";
9
12
 
@@ -113,29 +116,6 @@ export function completedProfileBody(
113
116
  );
114
117
  }
115
118
 
116
- /**
117
- * `{...raw, ...completed}` recursively: completed (schema-known) values win,
118
- * raw keys the schema stripped survive at every depth. (Duplicated in the
119
- * write-path normalization in conversation-query-routes.ts — the two land in
120
- * independent PRs; consolidate when either next changes.)
121
- */
122
- function mergePreservingUnknownKeys(
123
- raw: Record<string, unknown>,
124
- completed: Record<string, unknown>,
125
- ): Record<string, unknown> {
126
- const out: Record<string, unknown> = { ...raw, ...completed };
127
- for (const [key, value] of Object.entries(completed)) {
128
- const rawValue = raw[key];
129
- if (readObject(value) !== null && readObject(rawValue) !== null) {
130
- out[key] = mergePreservingUnknownKeys(
131
- rawValue as Record<string, unknown>,
132
- value as Record<string, unknown>,
133
- );
134
- }
135
- }
136
- return out;
137
- }
138
-
139
119
  function readObject(value: unknown): Record<string, unknown> | null {
140
120
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
141
121
  return null;
@@ -0,0 +1,70 @@
1
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+
4
+ import type { WorkspaceMigration } from "./types.js";
5
+
6
+ const OLD_CALLSITE = "voiceFrontDecision";
7
+ const PROGRESS_CALLSITE = "voiceProgressNarration";
8
+ const RETIRED_ACK_KEYS = [
9
+ "ackFirstDeltaTimeoutMs",
10
+ "ackGenerationTimeoutMs",
11
+ ] as const;
12
+
13
+ export const consolidateVoiceFrontDoorMigration: WorkspaceMigration = {
14
+ id: "142-consolidate-voice-front-door",
15
+ description:
16
+ "Rename the progress narration callsite and remove retired generated-ack tuning",
17
+ run(workspaceDir: string): void {
18
+ const configPath = join(workspaceDir, "config.json");
19
+ if (!existsSync(configPath)) {
20
+ return;
21
+ }
22
+
23
+ let config: Record<string, unknown>;
24
+ try {
25
+ const raw = JSON.parse(readFileSync(configPath, "utf-8"));
26
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
27
+ return;
28
+ }
29
+ config = raw as Record<string, unknown>;
30
+ } catch {
31
+ return;
32
+ }
33
+
34
+ let changed = false;
35
+ const llm = readObject(config.llm);
36
+ const callSites = readObject(llm?.callSites);
37
+ if (callSites && Object.hasOwn(callSites, OLD_CALLSITE)) {
38
+ if (!Object.hasOwn(callSites, PROGRESS_CALLSITE)) {
39
+ callSites[PROGRESS_CALLSITE] = callSites[OLD_CALLSITE];
40
+ }
41
+ delete callSites[OLD_CALLSITE];
42
+ changed = true;
43
+ }
44
+
45
+ const liveVoice = readObject(config.liveVoice);
46
+ const frontModel = readObject(liveVoice?.frontModel);
47
+ if (frontModel) {
48
+ for (const key of RETIRED_ACK_KEYS) {
49
+ if (Object.hasOwn(frontModel, key)) {
50
+ delete frontModel[key];
51
+ changed = true;
52
+ }
53
+ }
54
+ }
55
+
56
+ if (changed) {
57
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
58
+ }
59
+ },
60
+ down(_workspaceDir: string): void {
61
+ // Forward-only.
62
+ },
63
+ };
64
+
65
+ function readObject(value: unknown): Record<string, unknown> | null {
66
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
67
+ return null;
68
+ }
69
+ return value as Record<string, unknown>;
70
+ }
@@ -0,0 +1,134 @@
1
+ import { existsSync, readFileSync, renameSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+
4
+ import type { WorkspaceMigration } from "./types.js";
5
+
6
+ /**
7
+ * Repair the deprecated `gpt-5.3-codex` model ID on ChatGPT-subscription
8
+ * config fragments.
9
+ *
10
+ * OpenAI deprecated `gpt-5.3-codex` under ChatGPT sign-in (the Codex
11
+ * endpoint rejects it with HTTP 400), so it is being removed from
12
+ * `CODEX_SUBSCRIPTION_MODEL_IDS`. A workspace can still pin it on
13
+ * `provider: "chatgpt"` fragments (migration 133 wrote exactly that shape
14
+ * for `chatgpt-subscription` entries), and once the ID leaves the allowlist,
15
+ * `LLMSchema.superRefine` rejects such a fragment. The loader's leaf
16
+ * recovery then deletes the fragment's `model`, which still fails ("chatgpt"
17
+ * requires an explicit model), and per-section salvage resets the whole
18
+ * `llm` section, discarding the user's other LLM settings.
19
+ *
20
+ * Repair rewrites the model to `gpt-5.6-terra` (OpenAI's recommended
21
+ * everyday Codex model) on an exact `provider: "chatgpt"` + model match in
22
+ * `llm.default`, `llm.callSites.*`, and `llm.profiles.*`. A call-site
23
+ * fragment is also repaired when it pins the model with no provider of its
24
+ * own: at resolve time such a fragment overlays the winning profile, so the
25
+ * stale pin rides whatever provider and connection the winner supplies
26
+ * (including the subscription connection, which routes it to the Codex
27
+ * endpoint). Fragments with any other explicit provider are left untouched:
28
+ * the allowlist only gates the "chatgpt" routing identity, and a provider
29
+ * like `openai-compatible` may legitimately serve this id.
30
+ */
31
+ export const repairDeprecatedCodexModelIdMigration: WorkspaceMigration = {
32
+ id: "143-repair-deprecated-codex-model-id",
33
+ description:
34
+ 'Repair deprecated gpt-5.3-codex model ID on provider "chatgpt" fragments in workspace LLM config',
35
+ run(workspaceDir: string): void {
36
+ const configPath = join(workspaceDir, "config.json");
37
+ if (!existsSync(configPath)) {
38
+ return;
39
+ }
40
+
41
+ // Read outside the parse catch: a transient filesystem error (EIO,
42
+ // EACCES) must reach the runner so the migration retries, while
43
+ // malformed JSON is a permanent state this migration cannot repair.
44
+ const rawText = readFileSync(configPath, "utf-8");
45
+
46
+ let config: Record<string, unknown>;
47
+ try {
48
+ const raw = JSON.parse(rawText);
49
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
50
+ return;
51
+ }
52
+ config = raw as Record<string, unknown>;
53
+ } catch {
54
+ return;
55
+ }
56
+
57
+ const llm = readObject(config.llm);
58
+ if (llm === null) {
59
+ return;
60
+ }
61
+
62
+ let changed = false;
63
+
64
+ changed = repairFragment(readObject(llm.default)) || changed;
65
+
66
+ const callSites = readObject(llm.callSites);
67
+ if (callSites !== null) {
68
+ for (const rawConfig of Object.values(callSites)) {
69
+ changed =
70
+ repairFragment(readObject(rawConfig), {
71
+ repairMissingProvider: true,
72
+ }) || changed;
73
+ }
74
+ }
75
+
76
+ const profiles = readObject(llm.profiles);
77
+ if (profiles !== null) {
78
+ for (const rawProfile of Object.values(profiles)) {
79
+ changed = repairFragment(readObject(rawProfile)) || changed;
80
+ }
81
+ }
82
+
83
+ if (!changed) {
84
+ return;
85
+ }
86
+
87
+ // Write-then-rename so an interrupted write cannot leave config.json
88
+ // truncated: a torn in-place write would parse as invalid JSON on the
89
+ // retry, which the catch above treats as "nothing to do", letting the
90
+ // runner checkpoint the migration as completed against a corrupt file.
91
+ const tmpPath = `${configPath}.migration-143.tmp`;
92
+ writeFileSync(tmpPath, JSON.stringify(config, null, 2) + "\n");
93
+ renameSync(tmpPath, configPath);
94
+ },
95
+ // The exact-match rewrite is idempotent, so a transient failure (full
96
+ // disk, I/O error) is safe to retry on later startups.
97
+ retryFailedCheckpoint: true,
98
+ down(_workspaceDir: string): void {
99
+ // Forward-only: reintroducing the deprecated model ID would fail schema
100
+ // validation once the allowlist no longer carries it.
101
+ },
102
+ };
103
+
104
+ // ---------------------------------------------------------------------------
105
+ // Helpers: self-contained per workspace migrations AGENTS.md
106
+ // ---------------------------------------------------------------------------
107
+
108
+ const DEPRECATED_MODEL_ID = "gpt-5.3-codex";
109
+ const REPLACEMENT_MODEL_ID = "gpt-5.6-terra";
110
+
111
+ function repairFragment(
112
+ fragment: Record<string, unknown> | null,
113
+ options?: { repairMissingProvider?: boolean },
114
+ ): boolean {
115
+ if (fragment === null || fragment.model !== DEPRECATED_MODEL_ID) {
116
+ return false;
117
+ }
118
+ const providerRepairable =
119
+ fragment.provider === "chatgpt" ||
120
+ (options?.repairMissingProvider === true &&
121
+ fragment.provider === undefined);
122
+ if (!providerRepairable) {
123
+ return false;
124
+ }
125
+ fragment.model = REPLACEMENT_MODEL_ID;
126
+ return true;
127
+ }
128
+
129
+ function readObject(value: unknown): Record<string, unknown> | null {
130
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
131
+ return null;
132
+ }
133
+ return value as Record<string, unknown>;
134
+ }