@vellumai/assistant 0.11.6 → 0.11.7-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 (478) hide show
  1. package/ARCHITECTURE.md +31 -19
  2. package/Dockerfile +1 -0
  3. package/docs/architecture/security.md +3 -3
  4. package/docs/flux-turn-detection-spike.md +8 -7
  5. package/docs/runbook-trusted-contacts.md +14 -0
  6. package/docs/stt-provider-onboarding.md +8 -4
  7. package/knip.json +1 -0
  8. package/node_modules/@vellumai/avatar-manifest/package.json +18 -0
  9. package/node_modules/@vellumai/avatar-manifest/src/__tests__/manifest.test.ts +173 -0
  10. package/node_modules/@vellumai/avatar-manifest/src/__tests__/read.test.ts +114 -0
  11. package/node_modules/@vellumai/avatar-manifest/src/index.ts +29 -0
  12. package/node_modules/@vellumai/avatar-manifest/src/layout.ts +18 -0
  13. package/node_modules/@vellumai/avatar-manifest/src/manifest.ts +156 -0
  14. package/node_modules/@vellumai/avatar-manifest/src/read.ts +109 -0
  15. package/node_modules/@vellumai/avatar-manifest/tsconfig.json +20 -0
  16. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +37 -0
  17. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/remote-web-pairing.test.ts +389 -0
  18. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/ingress.ts +22 -0
  19. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +508 -18
  20. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/rpc.ts +174 -0
  21. package/node_modules/@vellumai/ces-client/src/http-credentials.ts +161 -0
  22. package/node_modules/@vellumai/ces-client/src/index.ts +1 -0
  23. package/node_modules/@vellumai/environments/package.json +2 -1
  24. package/node_modules/@vellumai/environments/src/shell.test.ts +56 -0
  25. package/node_modules/@vellumai/environments/src/shell.ts +65 -0
  26. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +37 -0
  27. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/remote-web-pairing.test.ts +389 -0
  28. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/ingress.ts +22 -0
  29. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +508 -18
  30. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/rpc.ts +174 -0
  31. package/node_modules/@vellumai/gateway-client/src/__tests__/contact-read-contracts.test.ts +32 -0
  32. package/node_modules/@vellumai/gateway-client/src/__tests__/gateway-client.test.ts +1 -0
  33. package/node_modules/@vellumai/gateway-client/src/__tests__/inbound-event-kind.test.ts +82 -0
  34. package/node_modules/@vellumai/gateway-client/src/__tests__/plugin-admission-denied-contract.test.ts +56 -0
  35. package/node_modules/@vellumai/gateway-client/src/__tests__/trust-verdict-contract.test.ts +18 -0
  36. package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +5 -11
  37. package/node_modules/@vellumai/gateway-client/src/channel-permission-contract.ts +3 -1
  38. package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +9 -0
  39. package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +4 -0
  40. package/node_modules/@vellumai/gateway-client/src/inbound-event-kind.ts +75 -0
  41. package/node_modules/@vellumai/gateway-client/src/index.ts +17 -0
  42. package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +8 -0
  43. package/node_modules/@vellumai/gateway-client/src/plugin-admission-denied-contract.ts +47 -0
  44. package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +11 -0
  45. package/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +37 -0
  46. package/node_modules/@vellumai/service-contracts/src/__tests__/remote-web-pairing.test.ts +389 -0
  47. package/node_modules/@vellumai/service-contracts/src/ingress.ts +22 -0
  48. package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +508 -18
  49. package/node_modules/@vellumai/service-contracts/src/rpc.ts +174 -0
  50. package/openapi.yaml +419 -55
  51. package/package.json +4 -1
  52. package/scripts/sync-llm-catalog.ts +6 -0
  53. package/src/__tests__/approval-card-edit-inbound-handoff.test.ts +199 -0
  54. package/src/__tests__/approval-card-edit.test.ts +153 -0
  55. package/src/__tests__/attachments-store-heic-normalize.test.ts +1 -1
  56. package/src/__tests__/available-skills.test.ts +54 -0
  57. package/src/__tests__/avatar-identity-sync.test.ts +5 -1
  58. package/src/__tests__/catalog-install-normalize.test.ts +9 -1
  59. package/src/__tests__/channel-policy.test.ts +1 -3
  60. package/src/__tests__/channel-readiness-discord.test.ts +9 -70
  61. package/src/__tests__/compactor-call-site-logging.test.ts +61 -0
  62. package/src/__tests__/config-loader-backfill.test.ts +69 -0
  63. package/src/__tests__/config-schema.test.ts +2 -3
  64. package/src/__tests__/conversation-agent-loop.test.ts +238 -0
  65. package/src/__tests__/conversation-attachments.test.ts +0 -1
  66. package/src/__tests__/conversation-error.test.ts +44 -0
  67. package/src/__tests__/conversation-evictor.test.ts +19 -0
  68. package/src/__tests__/conversation-fork-referential.test.ts +73 -63
  69. package/src/__tests__/conversation-fork-retrospective.test.ts +99 -74
  70. package/src/__tests__/conversation-runtime-assembly.test.ts +23 -0
  71. package/src/__tests__/conversation-slash.test.ts +9 -0
  72. package/src/__tests__/conversation-title-service.test.ts +97 -0
  73. package/src/__tests__/conversation-tool-setup-attribution.test.ts +16 -0
  74. package/src/__tests__/credential-broker-server-use.test.ts +0 -218
  75. package/src/__tests__/credential-broker.test.ts +43 -420
  76. package/src/__tests__/credential-record-write-through.test.ts +78 -0
  77. package/src/__tests__/credential-routes.test.ts +17 -1
  78. package/src/__tests__/credential-security-invariants.test.ts +8 -9
  79. package/src/__tests__/default-profile-catalog-fallback.test.ts +289 -0
  80. package/src/__tests__/delete-propagation.test.ts +92 -2
  81. package/src/__tests__/discord-callback-round-trip.test.ts +93 -0
  82. package/src/__tests__/discord-channel-config.test.ts +237 -0
  83. package/src/__tests__/edit-propagation.test.ts +42 -2
  84. package/src/__tests__/evict-conversations-for-reload.test.ts +51 -1
  85. package/src/__tests__/fallback-breaker.test.ts +1335 -0
  86. package/src/__tests__/gateway-threshold-reader-mock.ts +6 -0
  87. package/src/__tests__/guardian-card-withdrawal.test.ts +30 -0
  88. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +0 -2
  89. package/src/__tests__/image-conversion.test.ts +38 -4
  90. package/src/__tests__/install-skill-routing.test.ts +25 -0
  91. package/src/__tests__/llm-catalog-parity.test.ts +4 -0
  92. package/src/__tests__/llm-resolver.test.ts +74 -0
  93. package/src/__tests__/llm-schema-fallback-profile.test.ts +359 -0
  94. package/src/__tests__/managed-fallback-dispatch.test.ts +430 -0
  95. package/src/__tests__/managed-profile-guard.test.ts +28 -0
  96. package/src/__tests__/managed-speech-defaults.test.ts +196 -1
  97. package/src/__tests__/mcp-cli.test.ts +23 -1
  98. package/src/__tests__/mcp-tool-annotations-risk.test.ts +43 -34
  99. package/src/__tests__/media-stream-stt-session.test.ts +2 -0
  100. package/src/__tests__/notification-decision-fallback.test.ts +106 -0
  101. package/src/__tests__/notification-discord-adapter.test.ts +162 -0
  102. package/src/__tests__/oauth-connect-orchestrator.test.ts +95 -0
  103. package/src/__tests__/persist-unsendable-image-downscale.test.ts +6 -8
  104. package/src/__tests__/persist-unsendable-image.test.ts +3 -3
  105. package/src/__tests__/platform.test.ts +19 -1
  106. package/src/__tests__/plugin-import-boundary-guard.test.ts +5 -1
  107. package/src/__tests__/provider-catalog-visibility.test.ts +17 -0
  108. package/src/__tests__/provider-platform-proxy-integration.test.ts +8 -1
  109. package/src/__tests__/provider-usage-tracking.test.ts +56 -0
  110. package/src/__tests__/require-fresh-approval.test.ts +5 -2
  111. package/src/__tests__/retry-fallback-escalation.test.ts +1093 -0
  112. package/src/__tests__/schedule-retry.test.ts +183 -4
  113. package/src/__tests__/scheduler-reuse-conversation.test.ts +6 -6
  114. package/src/__tests__/secret-routes-platform-proxy.test.ts +55 -0
  115. package/src/__tests__/skill-load-tool.test.ts +34 -0
  116. package/src/__tests__/skill-tool-factory.test.ts +6 -1
  117. package/src/__tests__/skill-tool-manifest.test.ts +20 -0
  118. package/src/__tests__/skills.test.ts +25 -0
  119. package/src/__tests__/subagent-manager-notify.test.ts +38 -0
  120. package/src/__tests__/terminal-tools.test.ts +2 -0
  121. package/src/__tests__/tool-approval-handler.test.ts +146 -2
  122. package/src/__tests__/tool-approval-seed-content-blocks.test.ts +108 -2
  123. package/src/__tests__/tool-audit.test.ts +111 -40
  124. package/src/__tests__/tool-executor-lifecycle-events.test.ts +228 -18
  125. package/src/__tests__/ui-shape-teaching.test.ts +1 -1
  126. package/src/__tests__/verification-control-plane-policy.test.ts +6 -2
  127. package/src/__tests__/voice-config-update.test.ts +43 -0
  128. package/src/__tests__/workspace-migration-147-rename-colliding-backup-profile-names.test.ts +480 -0
  129. package/src/__tests__/workspace-migration-148-strip-unsupported-fallback-profiles.test.ts +167 -0
  130. package/src/__tests__/workspace-migration-149-repoint-backup-profile-selections.test.ts +144 -0
  131. package/src/agent/loop.ts +31 -2
  132. package/src/approvals/guardian-card-withdrawal.ts +22 -0
  133. package/src/avatar/__tests__/ensure-raster.test.ts +291 -0
  134. package/src/avatar/avatar-manifest.ts +48 -100
  135. package/src/avatar/avatar-store.ts +7 -7
  136. package/src/avatar/ensure-raster.ts +146 -0
  137. package/src/avatar/resvg-lazy.test.ts +0 -1
  138. package/src/avatar/traits-png-sync.ts +9 -10
  139. package/src/background-wake/wake-intent-hooks.test.ts +0 -1
  140. package/src/backup/__tests__/paths.test.ts +6 -2
  141. package/src/backup/paths.ts +6 -4
  142. package/src/bundler/app-compiler.ts +2 -2
  143. package/src/bundler/compiler-tools.test.ts +36 -0
  144. package/src/bundler/compiler-tools.ts +54 -18
  145. package/src/calls/__tests__/telephony-synthesis-language.test.ts +81 -0
  146. package/src/calls/__tests__/voice-session-bridge.test.ts +57 -0
  147. package/src/calls/media-stream-stt-session.ts +2 -1
  148. package/src/calls/telephony-synthesis-language.ts +13 -5
  149. package/src/calls/voice-session-bridge.ts +19 -4
  150. package/src/channels/config.ts +23 -12
  151. package/src/cli/AGENTS.md +3 -1
  152. package/src/cli/commands/backup.help.ts +10 -13
  153. package/src/cli/commands/contacts.help.ts +16 -4
  154. package/src/cli/commands/contacts.ts +5 -1
  155. package/src/cli/commands/credentials.help.ts +1 -1
  156. package/src/cli/commands/inference-providers.ts +18 -53
  157. package/src/cli/commands/mcp.help.ts +6 -5
  158. package/src/cli/commands/mcp.ts +1 -1
  159. package/src/cli/commands/memory/memory-retrospective.ts +1 -0
  160. package/src/cli/commands/stt.help.ts +2 -2
  161. package/src/config/__tests__/backup-schema.test.ts +1 -1
  162. package/src/config/__tests__/default-profile-catalog.test.ts +83 -2
  163. package/src/config/__tests__/loader-sparse-array-cleanup.test.ts +1 -1
  164. package/src/config/__tests__/memory-retrospective-schema.test.ts +12 -15
  165. package/src/config/bundled-skills/computer-use/SKILL.md +6 -6
  166. package/src/config/bundled-skills/computer-use/TOOLS.json +9 -6
  167. package/src/config/bundled-skills/media-processing/services/audio-transcribe.ts +3 -1
  168. package/src/config/bundled-skills/media-processing/services/preprocess.ts +1 -1
  169. package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
  170. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +16 -15
  171. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +2 -1
  172. package/src/config/bundled-skills/settings/TOOLS.json +11 -5
  173. package/src/config/bundled-skills/settings/tools/open-system-settings.test.ts +85 -0
  174. package/src/config/bundled-skills/settings/tools/open-system-settings.ts +33 -8
  175. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +54 -3
  176. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +1 -1
  177. package/src/config/default-profile-catalog.ts +195 -10
  178. package/src/config/default-profile-names.ts +41 -0
  179. package/src/config/feature-flag-registry.json +34 -2
  180. package/src/config/inference-profile-validation.ts +11 -2
  181. package/src/config/llm-resolver.ts +25 -0
  182. package/src/config/loader.ts +3 -0
  183. package/src/config/managed-speech-defaults.ts +160 -10
  184. package/src/config/schema.ts +2 -0
  185. package/src/config/schemas/__tests__/live-voice.test.ts +7 -6
  186. package/src/config/schemas/__tests__/stt.test.ts +94 -0
  187. package/src/config/schemas/backup.ts +1 -1
  188. package/src/config/schemas/channels.ts +32 -0
  189. package/src/config/schemas/live-voice.ts +13 -4
  190. package/src/config/schemas/llm.ts +263 -15
  191. package/src/config/schemas/mcp.ts +10 -2
  192. package/src/config/schemas/memory-retrospective.ts +1 -19
  193. package/src/config/schemas/services.ts +24 -0
  194. package/src/config/schemas/stt.ts +128 -5
  195. package/src/config/seed-inference-profiles.ts +32 -3
  196. package/src/config/skill-state.ts +5 -0
  197. package/src/config/skills.ts +21 -0
  198. package/src/contacts/notify-contacts-changed.ts +5 -2
  199. package/src/context/compactor.ts +74 -1
  200. package/src/daemon/__tests__/conversation-tool-setup.test.ts +138 -1
  201. package/src/daemon/config-watcher.ts +2 -1
  202. package/src/daemon/conversation-agent-loop-handlers.ts +27 -0
  203. package/src/daemon/conversation-agent-loop.ts +86 -10
  204. package/src/daemon/conversation-error.ts +25 -0
  205. package/src/daemon/conversation-evictor.ts +2 -4
  206. package/src/daemon/conversation-runtime-assembly.ts +19 -7
  207. package/src/daemon/conversation-skill-tools.ts +5 -1
  208. package/src/daemon/conversation-slash.ts +2 -2
  209. package/src/daemon/conversation-store.ts +8 -1
  210. package/src/daemon/conversation-tool-setup.ts +37 -0
  211. package/src/daemon/conversation.ts +0 -4
  212. package/src/daemon/handlers/channel-config-result.ts +30 -0
  213. package/src/daemon/handlers/config-discord-channel.ts +301 -0
  214. package/src/daemon/handlers/config-ingress.ts +9 -0
  215. package/src/daemon/handlers/config-slack-channel.ts +13 -15
  216. package/src/daemon/handlers/config-telegram.ts +2 -5
  217. package/src/daemon/handlers/conversations.ts +0 -2
  218. package/src/daemon/handlers/skills.ts +25 -7
  219. package/src/daemon/lifecycle.ts +3 -1
  220. package/src/daemon/message-types/messages.ts +0 -2
  221. package/src/daemon/tool-setup-types.ts +5 -3
  222. package/src/daemon/trust-context-types.test.ts +33 -0
  223. package/src/heartbeat/__tests__/heartbeat-service.test.ts +0 -1
  224. package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +58 -4
  225. package/src/live-voice/__tests__/live-voice-managed-speech-fallback.test.ts +8 -3
  226. package/src/live-voice/__tests__/live-voice-session-manager.test.ts +73 -11
  227. package/src/live-voice/__tests__/live-voice-session-preflight.test.ts +148 -2
  228. package/src/live-voice/__tests__/live-voice-session-telemetry.test.ts +64 -0
  229. package/src/live-voice/__tests__/live-voice-stt.test.ts +3 -0
  230. package/src/live-voice/__tests__/live-voice-text-turn.test.ts +391 -0
  231. package/src/live-voice/__tests__/protocol.test.ts +224 -0
  232. package/src/live-voice/live-voice-credential-preflight.ts +5 -1
  233. package/src/live-voice/live-voice-session-manager.ts +39 -2
  234. package/src/live-voice/live-voice-session.ts +224 -9
  235. package/src/live-voice/protocol.ts +187 -1
  236. package/src/messaging/providers/discord/api.ts +2 -1
  237. package/src/messaging/providers/discord/withdraw.ts +62 -0
  238. package/src/messaging/read-provider-metadata.ts +32 -0
  239. package/src/monitoring/__tests__/file-descriptors.test.ts +32 -0
  240. package/src/monitoring/__tests__/process-memory.test.ts +34 -1
  241. package/src/monitoring/db-integrity-sample.ts +1 -0
  242. package/src/monitoring/file-descriptors.ts +39 -6
  243. package/src/monitoring/process-memory.ts +37 -5
  244. package/src/monitoring/resource-sampler.ts +25 -8
  245. package/src/notifications/__tests__/activity-failed-dedupe.test.ts +161 -0
  246. package/src/notifications/__tests__/background-failure-signal.test.ts +107 -0
  247. package/src/notifications/__tests__/connected-channels.test.ts +12 -0
  248. package/src/notifications/__tests__/copy-composer.test.ts +83 -0
  249. package/src/notifications/__tests__/destination-resolver.test.ts +38 -0
  250. package/src/notifications/activity-failed-dedupe.ts +95 -0
  251. package/src/notifications/adapters/discord.ts +132 -0
  252. package/src/notifications/adapters/shared.ts +16 -0
  253. package/src/notifications/adapters/slack.ts +6 -1
  254. package/src/notifications/adapters/telegram.ts +5 -8
  255. package/src/notifications/approval-card-data.ts +94 -1
  256. package/src/notifications/background-failure-signal.ts +116 -0
  257. package/src/notifications/broadcaster.ts +3 -9
  258. package/src/notifications/copy-composer.ts +41 -8
  259. package/src/notifications/decision-engine.ts +55 -0
  260. package/src/notifications/destination-resolver.ts +45 -5
  261. package/src/notifications/emit-signal.ts +19 -5
  262. package/src/notifications/guardian-question-mode.ts +57 -0
  263. package/src/notifications/types.ts +2 -8
  264. package/src/oauth/__tests__/seed-providers-managed.test.ts +18 -0
  265. package/src/oauth/connect-orchestrator.ts +16 -0
  266. package/src/oauth/seed-providers.ts +262 -0
  267. package/src/permissions/checker.test.ts +21 -0
  268. package/src/permissions/checker.ts +6 -0
  269. package/src/permissions/gateway-threshold-reader.test.ts +184 -0
  270. package/src/permissions/gateway-threshold-reader.ts +144 -10
  271. package/src/permissions/prompter.ts +0 -6
  272. package/src/permissions/types.ts +5 -4
  273. package/src/persistence/__tests__/bulk-write-gate.test.ts +0 -38
  274. package/src/persistence/bulk-write-gate.ts +9 -10
  275. package/src/persistence/conversation-crud.ts +88 -134
  276. package/src/persistence/conversation-title-service.ts +43 -29
  277. package/src/persistence/embeddings/embedding-local.ts +14 -85
  278. package/src/persistence/embeddings/embedding-runtime-manager.ts +1 -0
  279. package/src/persistence/lifecycle-events-store.test.ts +50 -0
  280. package/src/persistence/lifecycle-events-store.ts +75 -5
  281. package/src/platform/platform-patch-queue.test.ts +368 -0
  282. package/src/platform/platform-patch-queue.ts +190 -0
  283. package/src/platform/sync-avatar.test.ts +527 -0
  284. package/src/platform/sync-avatar.ts +209 -0
  285. package/src/platform/sync-identity.test.ts +75 -0
  286. package/src/platform/sync-identity.ts +17 -110
  287. package/src/plugin-api/model-profiles.test.ts +2 -2
  288. package/src/plugin-api/model-profiles.ts +7 -7
  289. package/src/plugin-api/transcription-session.ts +1 -1
  290. package/src/plugins/defaults/compaction/window-manager.ts +4 -0
  291. package/src/plugins/defaults/image-recovery/recover.ts +2 -2
  292. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +133 -34
  293. package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +21 -31
  294. package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +13 -9
  295. package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +31 -4
  296. package/src/plugins/defaults/memory/memory-retrospective-constants.ts +0 -11
  297. package/src/plugins/defaults/memory/memory-retrospective-fork-boundary.ts +27 -13
  298. package/src/plugins/defaults/memory/memory-retrospective-job.ts +183 -113
  299. package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +13 -13
  300. package/src/plugins/defaults/memory/src/memory-v2-routes.ts +2 -2
  301. package/src/plugins/defaults/memory/substrate/__tests__/consolidation-job.test.ts +1 -1
  302. package/src/plugins/defaults/memory/substrate/consolidation-job.ts +4 -2
  303. package/src/plugins/defaults/memory/substrate/sweep-job.ts +9 -42
  304. package/src/plugins/defaults/memory/v2/rerank-local.ts +1 -0
  305. package/src/plugins/defaults/memory/worker.ts +7 -0
  306. package/src/providers/__tests__/provider-secret-catalog.test.ts +1 -0
  307. package/src/providers/__tests__/retry-callsite.test.ts +55 -0
  308. package/src/providers/__tests__/vellum-connection-routing.test.ts +21 -3
  309. package/src/providers/connection-resolution.ts +2 -17
  310. package/src/providers/fallback-breaker.ts +389 -0
  311. package/src/providers/inference/__tests__/adapter-factory-opencode.test.ts +92 -0
  312. package/src/providers/inference/__tests__/base-url-route-validation.test.ts +16 -0
  313. package/src/providers/inference/__tests__/base-url-security.test.ts +12 -0
  314. package/src/providers/inference/adapter-factory.ts +246 -3
  315. package/src/providers/inference/auth.ts +15 -14
  316. package/src/providers/inference/chatgpt-subscription-auth.ts +70 -0
  317. package/src/providers/inference/chatgpt-subscription-credentials.ts +84 -0
  318. package/src/providers/inference/resolve-auth.ts +1 -1
  319. package/src/providers/media-resolve.ts +2 -3
  320. package/src/providers/model-catalog.ts +100 -0
  321. package/src/providers/openai/__tests__/api-error-normalization.test.ts +22 -0
  322. package/src/providers/openai/__tests__/chat-template-flatten.test.ts +198 -0
  323. package/src/providers/openai/api-error-normalization.ts +13 -0
  324. package/src/providers/openai/chat-completions-provider.ts +84 -0
  325. package/src/providers/opencode/client.test.ts +150 -0
  326. package/src/providers/opencode/client.ts +61 -0
  327. package/src/providers/platform-proxy/constants.ts +5 -0
  328. package/src/providers/provider-resolvability.ts +20 -0
  329. package/src/providers/provider-secret-catalog.ts +3 -2
  330. package/src/providers/retry.ts +956 -68
  331. package/src/providers/speech-to-text/__tests__/deepgram-flux-frames.test.ts +42 -0
  332. package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +136 -0
  333. package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +120 -0
  334. package/src/providers/speech-to-text/__tests__/resolve.test.ts +316 -12
  335. package/src/providers/speech-to-text/deepgram-flux-frames.ts +83 -3
  336. package/src/providers/speech-to-text/deepgram-flux-realtime.ts +132 -4
  337. package/src/providers/speech-to-text/provider-catalog.ts +191 -3
  338. package/src/providers/speech-to-text/resolve.ts +132 -24
  339. package/src/providers/speech-to-text/vellum-managed-flux-realtime.ts +296 -0
  340. package/src/providers/types.ts +39 -0
  341. package/src/providers/usage-tracking.ts +12 -1
  342. package/src/providers/vellum/client.ts +29 -0
  343. package/src/providers/vellum-model-routing.test.ts +2 -0
  344. package/src/providers/vellum-model-routing.ts +3 -3
  345. package/src/runtime/AGENTS.md +1 -2
  346. package/src/runtime/__tests__/agent-wake.test.ts +105 -3
  347. package/src/runtime/__tests__/background-job-runner.test.ts +89 -3
  348. package/src/runtime/agent-wake.ts +44 -5
  349. package/src/runtime/background-job-runner.ts +68 -50
  350. package/src/runtime/channel-readiness-service.ts +0 -39
  351. package/src/runtime/http-server.ts +12 -4
  352. package/src/runtime/routes/__tests__/avatar-state-routes.test.ts +18 -18
  353. package/src/runtime/routes/__tests__/backup-routes.test.ts +9 -5
  354. package/src/runtime/routes/__tests__/chatgpt-subscription-auth-routes.test.ts +372 -0
  355. package/src/runtime/routes/__tests__/contact-routes.test.ts +7 -0
  356. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +96 -0
  357. package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +37 -0
  358. package/src/runtime/routes/__tests__/ingress-status-routes.test.ts +2 -1
  359. package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +87 -0
  360. package/src/runtime/routes/acp-claude-auth-routes.ts +25 -54
  361. package/src/runtime/routes/approval-routes.ts +0 -12
  362. package/src/runtime/routes/avatar-routes.ts +17 -30
  363. package/src/runtime/routes/backup-routes.ts +2 -2
  364. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +296 -113
  365. package/src/runtime/routes/contact-prompt-routes.ts +65 -6
  366. package/src/runtime/routes/contact-routes.ts +20 -15
  367. package/src/runtime/routes/conversation-query-routes.ts +98 -10
  368. package/src/runtime/routes/conversation-routes.ts +2 -2
  369. package/src/runtime/routes/credential-routes.ts +2 -1
  370. package/src/runtime/routes/guardian-approval-interception.ts +12 -9
  371. package/src/runtime/routes/inbound-message-handler.ts +47 -48
  372. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +18 -13
  373. package/src/runtime/routes/inbound-stages/admission-policy.ts +2 -2
  374. package/src/runtime/routes/inbound-stages/edit-intercept.ts +20 -2
  375. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +9 -8
  376. package/src/runtime/routes/inbound-stages/transcribe-audio.ts +1 -1
  377. package/src/runtime/routes/index.ts +2 -0
  378. package/src/runtime/routes/inference-profile-session-handler.ts +2 -2
  379. package/src/runtime/routes/inference-profiles-routes.ts +12 -7
  380. package/src/runtime/routes/inference-provider-connection-routes.ts +2 -2
  381. package/src/runtime/routes/inference-send-routes.ts +2 -2
  382. package/src/runtime/routes/ingress-status-routes.ts +2 -2
  383. package/src/runtime/routes/integrations/discord.ts +119 -0
  384. package/src/runtime/routes/llm-call-sites-routes.ts +2 -2
  385. package/src/runtime/routes/mcp-auth-routes.ts +12 -6
  386. package/src/runtime/routes/migration-routes.ts +3 -1
  387. package/src/runtime/routes/oauth-pending-flows.ts +136 -0
  388. package/src/runtime/routes/secret-routes.ts +7 -0
  389. package/src/runtime/routes/stt-routes.ts +38 -6
  390. package/src/runtime/routes/wake-conversation-routes.ts +8 -1
  391. package/src/runtime/routes/watch-routes.ts +9 -5
  392. package/src/runtime/sync/resource-sync-events.ts +2 -0
  393. package/src/runtime/trust-verdict-consumer.ts +3 -3
  394. package/src/schedule/__tests__/worker-feature-flags.test.ts +58 -0
  395. package/src/schedule/retry-policy.ts +8 -8
  396. package/src/schedule/run-script.ts +14 -23
  397. package/src/schedule/schedule-recovery.ts +0 -1
  398. package/src/schedule/scheduler.ts +89 -51
  399. package/src/schedule/worker.ts +57 -0
  400. package/src/security/__tests__/openai-device-auth.test.ts +355 -0
  401. package/src/security/ces-rpc-record-backend.ts +123 -0
  402. package/src/security/oauth2.ts +7 -0
  403. package/src/security/openai-device-auth.ts +364 -0
  404. package/src/security/secure-keys.ts +15 -0
  405. package/src/skills/available-skills.ts +10 -1
  406. package/src/skills/catalog-install.ts +19 -1
  407. package/src/skills/inline-command-runner.ts +6 -2
  408. package/src/skills/platform-compatibility.test.ts +49 -0
  409. package/src/skills/platform-compatibility.ts +65 -0
  410. package/src/skills/tool-manifest.ts +20 -0
  411. package/src/stt/__tests__/roles.test.ts +152 -0
  412. package/src/stt/daemon-batch-transcriber.ts +4 -1
  413. package/src/stt/roles.ts +180 -0
  414. package/src/stt/stt-stream-session.ts +3 -2
  415. package/src/stt/types.ts +33 -3
  416. package/src/subagent/manager.ts +12 -0
  417. package/src/telemetry/__tests__/live-voice-funnel.test.ts +34 -0
  418. package/src/telemetry/live-voice-funnel.ts +12 -2
  419. package/src/telemetry/telemetry-wire.generated.ts +6 -0
  420. package/src/telemetry/tool-audit.ts +83 -30
  421. package/src/telemetry/tool-executed-events-store.test.ts +0 -1
  422. package/src/telemetry/turn-outcome.ts +75 -4
  423. package/src/tools/client-os.ts +115 -0
  424. package/src/tools/computer-use/definitions.ts +11 -7
  425. package/src/tools/computer-use/skill-proxy-bridge.ts +2 -2
  426. package/src/tools/credentials/broker-types.ts +0 -71
  427. package/src/tools/credentials/broker.ts +6 -201
  428. package/src/tools/credentials/metadata-store.ts +65 -16
  429. package/src/tools/credentials/store.ts +2 -0
  430. package/src/tools/executor.ts +2 -8
  431. package/src/tools/host-shell.test.ts +75 -0
  432. package/src/tools/host-shell.ts +24 -0
  433. package/src/tools/host-terminal/host-shell.ts +15 -44
  434. package/src/tools/mcp/mcp-tool-factory.ts +44 -9
  435. package/src/tools/permission-checker.ts +73 -21
  436. package/src/tools/policy-context.ts +1 -0
  437. package/src/tools/shared/filesystem/image-read.ts +1 -1
  438. package/src/tools/skills/find-similar-skills.ts +10 -2
  439. package/src/tools/skills/load.ts +41 -6
  440. package/src/tools/skills/sandbox-runner.ts +8 -16
  441. package/src/tools/skills/skill-tool-factory.ts +1 -0
  442. package/src/tools/terminal/__tests__/safe-env.test.ts +64 -1
  443. package/src/tools/terminal/safe-env.ts +39 -9
  444. package/src/tools/terminal/shell.ts +12 -31
  445. package/src/tools/tool-approval-handler.ts +64 -10
  446. package/src/tools/tool-types.ts +10 -0
  447. package/src/tools/types.ts +19 -3
  448. package/src/tools/ui-surface/surface-shape-docs.ts +4 -3
  449. package/src/tools/workflows/manage-workflows.ts +2 -2
  450. package/src/tools/workflows/run-workflow.test.ts +2 -1
  451. package/src/util/__tests__/file-use.test.ts +61 -0
  452. package/src/util/__tests__/process-table.test.ts +66 -0
  453. package/src/util/__tests__/process-tree.test.ts +46 -0
  454. package/src/util/errors.ts +1 -0
  455. package/src/util/file-use.ts +91 -0
  456. package/src/util/host-process.test.ts +17 -0
  457. package/src/util/host-process.ts +75 -0
  458. package/src/util/image-conversion.ts +39 -7
  459. package/src/util/platform.ts +32 -13
  460. package/src/util/process-table.ts +249 -0
  461. package/src/util/process-tree.ts +85 -129
  462. package/src/util/provider-error-patterns.ts +20 -0
  463. package/src/util/spawn.ts +9 -2
  464. package/src/watch/__tests__/watch-retro.test.ts +48 -1
  465. package/src/watch/watch-retro.ts +46 -4
  466. package/src/workspace/git-service.ts +4 -14
  467. package/src/workspace/migrations/147-rename-colliding-backup-profile-names.ts +360 -0
  468. package/src/workspace/migrations/148-strip-unsupported-fallback-profiles.ts +117 -0
  469. package/src/workspace/migrations/149-repoint-backup-profile-selections.ts +204 -0
  470. package/src/workspace/migrations/150-stt-flux-provider-to-model-family.ts +139 -0
  471. package/src/workspace/migrations/__tests__/150-stt-flux-provider-to-model-family.test.ts +160 -0
  472. package/src/workspace/migrations/registry.ts +8 -0
  473. package/src/__tests__/permission-types.test.ts +0 -17
  474. package/src/channels/gateway-discord-admission.ts +0 -32
  475. package/src/persistence/fork-message-copy.ts +0 -239
  476. package/src/plugins/defaults/memory/__tests__/fork-message-copy.test.ts +0 -221
  477. package/src/security/__tests__/oauth2-device-code.test.ts +0 -431
  478. package/src/security/oauth2-device-code.ts +0 -312
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Per-consumer STT role selection.
3
+ *
4
+ * These cover the two pure functions every consumer routes through: which
5
+ * `{provider, model}` pair a role names, and which catalog row that pair
6
+ * resolves to. The resolvers, the config schema and the capability gate all
7
+ * read the same pair, so a drift here is a drift everywhere.
8
+ */
9
+ import { describe, expect, test } from "bun:test";
10
+
11
+ import {
12
+ sttCatalogKeyForRole,
13
+ sttRoleCapabilityGap,
14
+ sttSelectionForRole,
15
+ } from "../roles.js";
16
+
17
+ describe("sttSelectionForRole", () => {
18
+ test("a role's own entry wins over the global provider", () => {
19
+ expect(
20
+ sttSelectionForRole(
21
+ {
22
+ provider: "openai-whisper",
23
+ roles: { dictation: { provider: "deepgram" } },
24
+ },
25
+ "dictation",
26
+ ),
27
+ ).toEqual({ provider: "deepgram" });
28
+ });
29
+
30
+ test("an unset role falls back to the global provider and its family", () => {
31
+ expect(
32
+ sttSelectionForRole(
33
+ {
34
+ provider: "deepgram",
35
+ providers: { deepgram: { model: "flux" } },
36
+ roles: { dictation: { provider: "openai-whisper" } },
37
+ },
38
+ "liveVoice",
39
+ ),
40
+ ).toEqual({ provider: "deepgram", model: "flux" });
41
+ });
42
+
43
+ test("a roleless caller reads the global provider", () => {
44
+ expect(
45
+ sttSelectionForRole(
46
+ {
47
+ provider: "openai-whisper",
48
+ roles: { dictation: { provider: "deepgram" } },
49
+ },
50
+ undefined,
51
+ ),
52
+ ).toEqual({ provider: "openai-whisper" });
53
+ });
54
+
55
+ test("a role override does not inherit the global provider's family", () => {
56
+ // The override names the whole pair. Carrying `flux` across from the
57
+ // global would pin the override to a family it never asked for.
58
+ expect(
59
+ sttSelectionForRole(
60
+ {
61
+ provider: "deepgram",
62
+ providers: { deepgram: { model: "flux" } },
63
+ roles: { batch: { provider: "deepgram" } },
64
+ },
65
+ "batch",
66
+ ),
67
+ ).toEqual({ provider: "deepgram" });
68
+ });
69
+ });
70
+
71
+ describe("sttCatalogKeyForRole", () => {
72
+ test("a role's family selects the variant row", () => {
73
+ expect(
74
+ sttCatalogKeyForRole(
75
+ {
76
+ provider: "deepgram",
77
+ roles: { liveVoice: { provider: "deepgram", model: "flux" } },
78
+ },
79
+ "liveVoice",
80
+ ),
81
+ ).toBe("deepgram-flux");
82
+ });
83
+
84
+ test("two roles on one provider resolve to different rows", () => {
85
+ const stt = {
86
+ provider: "deepgram",
87
+ roles: {
88
+ liveVoice: { provider: "deepgram", model: "flux" },
89
+ batch: { provider: "deepgram" },
90
+ },
91
+ };
92
+
93
+ expect(sttCatalogKeyForRole(stt, "liveVoice")).toBe("deepgram-flux");
94
+ expect(sttCatalogKeyForRole(stt, "batch")).toBe("deepgram");
95
+ });
96
+ });
97
+
98
+ describe("sttRoleCapabilityGap", () => {
99
+ test("live voice accepts a streaming-only family", () => {
100
+ expect(
101
+ sttRoleCapabilityGap("liveVoice", {
102
+ provider: "deepgram",
103
+ model: "flux",
104
+ }),
105
+ ).toBeNull();
106
+ });
107
+
108
+ test("batch rejects the same family, which has no batch endpoint", () => {
109
+ const gap = sttRoleCapabilityGap("batch", {
110
+ provider: "deepgram",
111
+ model: "flux",
112
+ });
113
+
114
+ expect(gap).toContain("deepgram-flux");
115
+ expect(gap).toContain("batch");
116
+ });
117
+
118
+ test("capability is judged on the resolved row, not the bare provider", () => {
119
+ // The pair is what distinguishes them: base deepgram batches and the
120
+ // flux family does not, under one provider id.
121
+ expect(sttRoleCapabilityGap("batch", { provider: "deepgram" })).toBeNull();
122
+ });
123
+
124
+ test("telephony rejects a provider that does not transcribe calls", () => {
125
+ const gap = sttRoleCapabilityGap("telephony", {
126
+ provider: "deepgram",
127
+ model: "flux",
128
+ });
129
+
130
+ expect(gap).not.toBeNull();
131
+ });
132
+
133
+ test("watch accepts a streaming-only family, having no batch leg", () => {
134
+ // Watch shares dictation's transport but not its batch fallback, so a
135
+ // family dictation must reject is legal here.
136
+ expect(
137
+ sttRoleCapabilityGap("watch", { provider: "deepgram", model: "flux" }),
138
+ ).toBeNull();
139
+ expect(
140
+ sttRoleCapabilityGap("dictation", {
141
+ provider: "deepgram",
142
+ model: "flux",
143
+ }),
144
+ ).not.toBeNull();
145
+ });
146
+
147
+ test("an unknown provider is named rather than silently accepted", () => {
148
+ expect(sttRoleCapabilityGap("batch", { provider: "not-a-provider" })).toBe(
149
+ '"not-a-provider" is not in the STT provider catalog',
150
+ );
151
+ });
152
+ });
@@ -273,8 +273,11 @@ export function createDaemonBatchTranscriber(
273
273
  case "xai":
274
274
  return new XAIBatchTranscriber(apiKey, language);
275
275
  case "deepgram-flux":
276
+ case "vellum-flux":
276
277
  // Same copy the resolver raises, so a direct factory caller and a
277
- // config-driven one report the mismatch identically.
278
+ // config-driven one report the mismatch identically. Both Flux
279
+ // variants are streaming-only: the model has no batch endpoint, BYOK
280
+ // or managed.
278
281
  throw new SttError("provider-error", batchBoundaryGapReason(providerId), {
279
282
  userFacing: true,
280
283
  });
@@ -0,0 +1,180 @@
1
+ /**
2
+ * STT consumer roles.
3
+ *
4
+ * `services.stt.provider` is one global that every STT caller reads, while the
5
+ * provider catalog already models capability per boundary. A provider that is
6
+ * right for one consumer is therefore wrong for another: Flux has no batch
7
+ * endpoint, so a global set to it breaks file transcription and telephony at
8
+ * the same moment it makes live voice better.
9
+ *
10
+ * A role names the consumer, not the boundary. Telephony is why: a single call
11
+ * resolves a streaming transcriber and falls back to a batch one within the
12
+ * same session, so a `{batch, streaming}` split would put one call on two
13
+ * roles. Each role instead declares the boundaries its call sites actually
14
+ * use, and a provider must satisfy all of them to be selectable for it.
15
+ */
16
+
17
+ import {
18
+ getProviderEntry,
19
+ sttCatalogKeyFor,
20
+ } from "../providers/speech-to-text/provider-catalog.js";
21
+ import type { SttBoundaryId, SttProviderId } from "./types.js";
22
+
23
+ /** The consumers that may select their own STT provider. */
24
+ export const STT_ROLES = [
25
+ "liveVoice",
26
+ "telephony",
27
+ "dictation",
28
+ "watch",
29
+ "batch",
30
+ ] as const;
31
+
32
+ export type SttRole = (typeof STT_ROLES)[number];
33
+
34
+ /** What a provider must support to serve a role. */
35
+ export interface SttRoleRequirements {
36
+ /** Every boundary the role's call sites resolve a transcriber on. */
37
+ readonly boundaries: readonly SttBoundaryId[];
38
+ /** Whether the role additionally requires telephony ingestion. */
39
+ readonly requiresTelephony: boolean;
40
+ /** Human-readable name for config errors and resolver logs. */
41
+ readonly label: string;
42
+ }
43
+
44
+ export const STT_ROLE_REQUIREMENTS: Record<SttRole, SttRoleRequirements> = {
45
+ /**
46
+ * The live-voice transport arms a streaming transcriber per utterance and
47
+ * has no batch fallback, so streaming alone is the whole requirement.
48
+ */
49
+ liveVoice: {
50
+ boundaries: ["daemon-streaming"],
51
+ requiresTelephony: false,
52
+ label: "live voice",
53
+ },
54
+ /**
55
+ * A call starts on streaming ingestion and falls back to per-turn batch
56
+ * when no streaming transcriber resolves, so both boundaries are load
57
+ * bearing inside one session. `telephonyMode` is a separate axis from the
58
+ * boundaries and is checked on top of them.
59
+ */
60
+ telephony: {
61
+ boundaries: ["daemon-streaming", "daemon-batch"],
62
+ requiresTelephony: true,
63
+ label: "telephony",
64
+ },
65
+ /**
66
+ * The composer mic reaches the daemon two ways: a streaming WebSocket for
67
+ * live partials, and a batch POST of the recorded blob where that socket
68
+ * is unavailable. Both are the same user-facing feature, so a provider
69
+ * that serves only one would leave dictation half broken.
70
+ */
71
+ dictation: {
72
+ boundaries: ["daemon-streaming", "daemon-batch"],
73
+ requiresTelephony: false,
74
+ label: "dictation",
75
+ },
76
+ /**
77
+ * A watch session streams the user's narration continuously while they
78
+ * work and files each final on the timeline. It shares dictation's
79
+ * transport but none of its fallback: there is no batch leg to catch a
80
+ * provider that cannot stream, so streaming alone is the requirement.
81
+ */
82
+ watch: {
83
+ boundaries: ["daemon-streaming"],
84
+ requiresTelephony: false,
85
+ label: "watch",
86
+ },
87
+ /** File, attachment and skill transcription. Batch only. */
88
+ batch: {
89
+ boundaries: ["daemon-batch"],
90
+ requiresTelephony: false,
91
+ label: "batch transcription",
92
+ },
93
+ };
94
+
95
+ /**
96
+ * Why a provider cannot serve a role, or null when it can.
97
+ *
98
+ * The catalog already knows every provider's boundaries and telephony mode;
99
+ * this only asks whether they cover what the role needs. Config validation
100
+ * rejects a pair on this, and the resolver logs it, so the same sentence
101
+ * explains the failure wherever it surfaces.
102
+ */
103
+ export function sttRoleCapabilityGap(
104
+ role: SttRole,
105
+ selection: SttRoleSelection,
106
+ ): string | null {
107
+ // Capability belongs to the resolved row: "deepgram" batches and
108
+ // "deepgram" running flux does not, and only the pair distinguishes them.
109
+ const provider = sttCatalogKeyFor(
110
+ selection.provider as SttProviderId,
111
+ selection.model,
112
+ );
113
+ const entry = getProviderEntry(provider);
114
+ if (!entry) {
115
+ return `"${selection.provider}" is not in the STT provider catalog`;
116
+ }
117
+
118
+ const requirements = STT_ROLE_REQUIREMENTS[role];
119
+ const missing = requirements.boundaries.filter(
120
+ (boundary) => !entry.supportedBoundaries.has(boundary),
121
+ );
122
+ if (missing.length > 0) {
123
+ return `"${provider}" supports no ${missing.join(" or ")} transcription, which ${requirements.label} needs`;
124
+ }
125
+
126
+ if (requirements.requiresTelephony && entry.telephonyMode === "none") {
127
+ return `"${provider}" does not transcribe calls, which ${requirements.label} needs`;
128
+ }
129
+
130
+ return null;
131
+ }
132
+
133
+ /** What a role names: a provider, and optionally which of its model families. */
134
+ export interface SttRoleSelection {
135
+ readonly provider: string;
136
+ readonly model?: string | undefined;
137
+ }
138
+
139
+ /**
140
+ * The STT config a consumer reads: its own `services.stt.roles` entry when it
141
+ * has one, otherwise the global provider and its configured model family.
142
+ *
143
+ * A role names the pair rather than a bare provider because the family is
144
+ * where capability actually differs. A role that could only name a provider
145
+ * could not say "live voice on flux", which is the whole reason to have one.
146
+ *
147
+ * The parameter is structural rather than the config type on purpose. This
148
+ * module sits under the config schema (which imports it to validate role
149
+ * entries), so importing the schema back would close a cycle, and the cycle
150
+ * would only surface as a module-init failure far from here.
151
+ */
152
+ export function sttSelectionForRole(
153
+ stt: {
154
+ readonly provider: string;
155
+ readonly providers?:
156
+ | Record<string, { readonly model?: unknown } | undefined>
157
+ | undefined;
158
+ readonly roles?: Partial<Record<SttRole, SttRoleSelection>> | undefined;
159
+ },
160
+ role: SttRole | undefined,
161
+ ): SttRoleSelection {
162
+ const override = role === undefined ? undefined : stt.roles?.[role];
163
+ if (override !== undefined) {
164
+ return override;
165
+ }
166
+ const model = stt.providers?.[stt.provider]?.model;
167
+ return {
168
+ provider: stt.provider,
169
+ ...(typeof model === "string" ? { model } : {}),
170
+ };
171
+ }
172
+
173
+ /** The catalog row a role resolves to, via the pair it names. */
174
+ export function sttCatalogKeyForRole(
175
+ stt: Parameters<typeof sttSelectionForRole>[0],
176
+ role: SttRole | undefined,
177
+ ): SttProviderId {
178
+ const selection = sttSelectionForRole(stt, role);
179
+ return sttCatalogKeyFor(selection.provider as SttProviderId, selection.model);
180
+ }
@@ -14,8 +14,9 @@
14
14
  * Session lifecycle:
15
15
  * 1. Client opens a WebSocket to `/v1/stt/stream` with required `mimeType`
16
16
  * query parameter and optional `provider` metadata. The runtime is
17
- * config-authoritative it resolves the transcriber from
18
- * `services.stt.provider` regardless of the requested provider.
17
+ * config-authoritative: it resolves the transcriber from the dictation
18
+ * role (`services.stt.roles.dictation`, else `services.stt.provider`)
19
+ * regardless of the requested provider.
19
20
  * 2. The orchestrator resolves a {@link StreamingTranscriber} via
20
21
  * `resolveStreamingTranscriber()` and starts the provider session.
21
22
  * 3. The client sends `audio` frames (binary or base64-encoded JSON) and
package/src/stt/types.ts CHANGED
@@ -17,8 +17,31 @@
17
17
  // ---------------------------------------------------------------------------
18
18
 
19
19
  /**
20
- * Canonical provider identifiers for daemon-hosted STT backends.
21
- * Extend this union as new providers are integrated.
20
+ * Model families a provider may be asked for, via
21
+ * `services.stt.providers.<id>.model`.
22
+ *
23
+ * A family is not a provider model string: `"flux"` selects the conversational
24
+ * endpoint family, and which member runs is decided downstream from the spoken
25
+ * language. Providers offering a single family ignore the setting.
26
+ */
27
+ export const VALID_STT_MODELS = ["nova-3", "flux"] as const;
28
+
29
+ export type SttModelFamily = (typeof VALID_STT_MODELS)[number];
30
+
31
+ /**
32
+ * Catalog identity for a daemon-hosted STT backend.
33
+ *
34
+ * This is the key capability is stored under, NOT the set of values a user may
35
+ * put in `services.stt.provider`. A provider that offers more than one model
36
+ * family with genuinely different capabilities gets a row per family, because
37
+ * the catalog is a capability matrix and those capabilities differ. Users
38
+ * select the family with `services.stt.providers.<id>.model`, and
39
+ * {@link resolveSttCatalogKey} maps that pair onto the row.
40
+ *
41
+ * Keeping the two apart bounds the user-facing surface: adding a model family
42
+ * costs a catalog row and nothing else, while adding a provider id costs an
43
+ * enum entry, a settings-picker row, and a place in every predicate that
44
+ * classifies providers.
22
45
  */
23
46
  export type SttProviderId =
24
47
  | "openai-whisper"
@@ -30,7 +53,14 @@ export type SttProviderId =
30
53
  | "deepgram-flux"
31
54
  | "google-gemini"
32
55
  | "xai"
33
- | "vellum";
56
+ | "vellum"
57
+ /**
58
+ * Managed Flux: the same conversational model as `deepgram-flux`, reached
59
+ * through the platform's speech relay instead of a Deepgram key. Dials the
60
+ * relay's STT v2 endpoint asking for native Flux frames, so it keeps the
61
+ * turn events the relay's default v1-shaped translation drops.
62
+ */
63
+ | "vellum-flux";
34
64
 
35
65
  /**
36
66
  * Telephony-specific STT capability class.
@@ -1478,6 +1478,18 @@ export class SubagentManager {
1478
1478
  .filter((s): s is SubagentState => s !== undefined);
1479
1479
  }
1480
1480
 
1481
+ /**
1482
+ * True when this parent still has a child that is not terminal (`pending`,
1483
+ * `running`, or `awaiting_input`). Idle-eviction and config-reload rebuild
1484
+ * skip those parents so an otherwise-idle conversation does not abort
1485
+ * mid-task children.
1486
+ */
1487
+ hasActiveChildren(parentConversationId: string): boolean {
1488
+ return this.getChildrenOf(parentConversationId).some(
1489
+ (child) => !TERMINAL_STATUSES.has(child.status),
1490
+ );
1491
+ }
1492
+
1481
1493
  /** Total number of active (non-terminal) subagents. */
1482
1494
  get activeCount(): number {
1483
1495
  return [...this.subagents.values()].filter(
@@ -12,6 +12,7 @@ describe("liveVoiceSilenceReason", () => {
12
12
  expect(
13
13
  liveVoiceSilenceReason({
14
14
  reachedActive: false,
15
+ audioInput: true,
15
16
  receivedAudio: false,
16
17
  detectedSpeech: false,
17
18
  }),
@@ -25,6 +26,7 @@ describe("liveVoiceSilenceReason", () => {
25
26
  expect(
26
27
  liveVoiceSilenceReason({
27
28
  reachedActive: false,
29
+ audioInput: true,
28
30
  receivedAudio: true,
29
31
  detectedSpeech: true,
30
32
  }),
@@ -35,6 +37,7 @@ describe("liveVoiceSilenceReason", () => {
35
37
  expect(
36
38
  liveVoiceSilenceReason({
37
39
  reachedActive: true,
40
+ audioInput: true,
38
41
  receivedAudio: false,
39
42
  detectedSpeech: false,
40
43
  }),
@@ -48,16 +51,47 @@ describe("liveVoiceSilenceReason", () => {
48
51
  expect(
49
52
  liveVoiceSilenceReason({
50
53
  reachedActive: true,
54
+ audioInput: true,
51
55
  receivedAudio: true,
52
56
  detectedSpeech: false,
53
57
  }),
54
58
  ).toBe("no_speech");
55
59
  });
56
60
 
61
+ it("does not blame the microphone on a session that never had one", () => {
62
+ // A text-only session opened because speech-to-text was unavailable and
63
+ // the client could type. Scoring the absent microphone as `no_audio` would
64
+ // file it under a failure it does not have, and inflate the very rate this
65
+ // taxonomy exists to explain.
66
+ expect(
67
+ liveVoiceSilenceReason({
68
+ reachedActive: true,
69
+ audioInput: false,
70
+ receivedAudio: false,
71
+ detectedSpeech: false,
72
+ }),
73
+ ).toBe("text_only");
74
+ });
75
+
76
+ it("still reports text_only when a text-only session streamed stray audio", () => {
77
+ // A client that has not caught up with `audioInput: false` may keep
78
+ // sending chunks. Nothing transcribes them, so they say nothing about why
79
+ // the session was silent, and the reason must not flip on their account.
80
+ expect(
81
+ liveVoiceSilenceReason({
82
+ reachedActive: true,
83
+ audioInput: false,
84
+ receivedAudio: true,
85
+ detectedSpeech: true,
86
+ }),
87
+ ).toBe("text_only");
88
+ });
89
+
57
90
  it("reports speech that never became a turn", () => {
58
91
  expect(
59
92
  liveVoiceSilenceReason({
60
93
  reachedActive: true,
94
+ audioInput: true,
61
95
  receivedAudio: true,
62
96
  detectedSpeech: true,
63
97
  }),
@@ -66,11 +66,16 @@ export type LiveVoiceSessionOutcome = "completed" | "failed";
66
66
  * microphone from a muted one from someone deliberately backing out. Those have
67
67
  * completely different fixes, so the reason is what makes the rate actionable.
68
68
  *
69
- * The four values are ordered by how far the session got, and each one narrows
70
- * the cause to a different layer:
69
+ * The values are ordered by how far the session got, and each one narrows the
70
+ * cause to a different layer:
71
71
  *
72
72
  * - `no_ready` never reached `active`: died in the credential preflight or
73
73
  * the transport, before the user could have said anything.
74
+ * - `text_only` opened without a speech-to-text leg because the client could
75
+ * type, and then the user never typed. Ranked ahead of the audio
76
+ * signals because they cannot mean anything here: no microphone
77
+ * was ever expected, so scoring such a session `no_audio` would
78
+ * read as a failure the session does not have.
74
79
  * - `no_audio` reached `active` but not one audio chunk ever arrived, so the
75
80
  * microphone never opened. Permission denial looks like this.
76
81
  * - `no_speech` audio arrived but the detector never classified any of it as
@@ -80,6 +85,7 @@ export type LiveVoiceSessionOutcome = "completed" | "failed";
80
85
  */
81
86
  export type LiveVoiceSilenceReason =
82
87
  | "no_ready"
88
+ | "text_only"
83
89
  | "no_audio"
84
90
  | "no_speech"
85
91
  | "no_turn";
@@ -91,12 +97,16 @@ export type LiveVoiceSilenceReason =
91
97
  */
92
98
  export function liveVoiceSilenceReason(signals: {
93
99
  reachedActive: boolean;
100
+ audioInput: boolean;
94
101
  receivedAudio: boolean;
95
102
  detectedSpeech: boolean;
96
103
  }): LiveVoiceSilenceReason {
97
104
  if (!signals.reachedActive) {
98
105
  return "no_ready";
99
106
  }
107
+ if (!signals.audioInput) {
108
+ return "text_only";
109
+ }
100
110
  if (!signals.receivedAudio) {
101
111
  return "no_audio";
102
112
  }
@@ -97,6 +97,7 @@ export const llmUsageTelemetryEventSchema = z.object({
97
97
  parent_turn_index: z.number().int().min(0).nullable().optional(),
98
98
  subagent_role: z.string().trim().min(1).max(64).nullable().optional(),
99
99
  subagent_spawn_mode: z.string().trim().min(1).max(64).nullable().optional(),
100
+ cron_run_id: z.string().trim().min(1).max(64).nullable().optional(),
100
101
  });
101
102
  export type LlmUsageTelemetryEvent = z.infer<
102
103
  typeof llmUsageTelemetryEventSchema
@@ -177,6 +178,11 @@ export const lifecycleTelemetryEventSchema = z.object({
177
178
  recorded_at: z.number().int(),
178
179
  assistant_version: z.string().trim().min(1).max(64).nullable().optional(),
179
180
  event_name: z.string().trim().min(1).max(64),
181
+ tool_name: z.string().trim().min(1).max(255).optional(),
182
+ risk_level: z.string().trim().min(1).max(32).optional(),
183
+ risk_threshold: z.string().trim().min(1).max(32).optional(),
184
+ surface: z.string().trim().min(1).max(64).optional(),
185
+ conversation_id: z.string().trim().min(1).max(128).optional(),
180
186
  });
181
187
  export type LifecycleTelemetryEvent = z.infer<
182
188
  typeof lifecycleTelemetryEventSchema