@vellumai/assistant 0.11.2 → 0.11.3-dev.202608080427.92f668a

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 (826) hide show
  1. package/ARCHITECTURE.md +69 -9
  2. package/docs/flux-turn-detection-spike.md +243 -0
  3. package/docs/stt-provider-onboarding.md +3 -1
  4. package/docs/trusted-contact-access.md +9 -5
  5. package/docs/vellum-doctor.md +195 -0
  6. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +50 -0
  7. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
  8. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
  9. package/node_modules/@vellumai/ces-client/src/index.ts +1 -0
  10. package/node_modules/@vellumai/ces-client/src/rpc-client.ts +19 -2
  11. package/node_modules/@vellumai/environments/package.json +2 -1
  12. package/node_modules/@vellumai/environments/src/__tests__/cloud-assistant-hub-url.test.ts +38 -0
  13. package/node_modules/@vellumai/environments/src/__tests__/install-layout.test.ts +98 -0
  14. package/node_modules/@vellumai/environments/src/__tests__/package-boundary.test.ts +5 -5
  15. package/node_modules/@vellumai/environments/src/index.ts +17 -6
  16. package/node_modules/@vellumai/environments/src/install-layout.ts +49 -0
  17. package/node_modules/@vellumai/environments/src/seeds.ts +29 -0
  18. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +50 -0
  19. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
  20. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
  21. package/node_modules/@vellumai/gateway-client/src/index.ts +4 -0
  22. package/node_modules/@vellumai/gateway-client/src/verification-session-contract.ts +91 -6
  23. package/node_modules/@vellumai/ipc-server-utils/src/endpoint.test.ts +36 -0
  24. package/node_modules/@vellumai/ipc-server-utils/src/endpoint.ts +142 -0
  25. package/node_modules/@vellumai/ipc-server-utils/src/index.ts +2 -0
  26. package/node_modules/@vellumai/ipc-server-utils/src/listen-options.ts +3 -0
  27. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.test.ts +15 -1
  28. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.ts +17 -2
  29. package/node_modules/@vellumai/service-contracts/src/channels.ts +50 -0
  30. package/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
  31. package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
  32. package/node_modules/@vellumai/slack-text/src/index.ts +16 -2
  33. package/openapi.yaml +619 -47
  34. package/package.json +1 -1
  35. package/scripts/voice-ttft-spike.ts +3 -3
  36. package/src/__tests__/agent-image-optimize.test.ts +4 -4
  37. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +113 -66
  38. package/src/__tests__/agent-loop-override-profile.test.ts +38 -48
  39. package/src/__tests__/agent-loop-subagent-billing-headers.test.ts +166 -0
  40. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +1 -0
  41. package/src/__tests__/agent-wake-override-profile.test.ts +1 -0
  42. package/src/__tests__/answered-question-persistence.test.ts +240 -0
  43. package/src/__tests__/anthropic-provider.test.ts +165 -30
  44. package/src/__tests__/app-control-flow.test.ts +9 -8
  45. package/src/__tests__/assistant-event-hub.test.ts +48 -0
  46. package/src/__tests__/attachments-store-heic-normalize.test.ts +16 -12
  47. package/src/__tests__/attachments-store.test.ts +130 -68
  48. package/src/__tests__/attachments.test.ts +36 -36
  49. package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
  50. package/src/__tests__/call-controller.test.ts +120 -0
  51. package/src/__tests__/call-start-guardian-guard.test.ts +18 -4
  52. package/src/__tests__/canned-reply-release.test.ts +86 -0
  53. package/src/__tests__/channel-availability-routes.test.ts +14 -1
  54. package/src/__tests__/channel-guardian.test.ts +59 -29
  55. package/src/__tests__/channel-readiness-telegram-remote.test.ts +154 -0
  56. package/src/__tests__/channel-setup-panel-ack.test.ts +6 -5
  57. package/src/__tests__/client-os-metadata-persistence.test.ts +103 -4
  58. package/src/__tests__/compactor-image-manifest-trust.test.ts +1 -1
  59. package/src/__tests__/config-loader-backfill.test.ts +19 -0
  60. package/src/__tests__/config-schema.test.ts +150 -7
  61. package/src/__tests__/consult-deadline.test.ts +65 -0
  62. package/src/__tests__/conversation-agent-loop.test.ts +438 -2
  63. package/src/__tests__/conversation-disk-view-integration.test.ts +5 -1
  64. package/src/__tests__/conversation-disk-view.test.ts +4 -4
  65. package/src/__tests__/conversation-error.test.ts +22 -3
  66. package/src/__tests__/conversation-fork-crud.test.ts +5 -1
  67. package/src/__tests__/conversation-fork-referential.test.ts +352 -0
  68. package/src/__tests__/conversation-fork-retrospective.test.ts +5 -1
  69. package/src/__tests__/conversation-group-tools.test.ts +32 -2
  70. package/src/__tests__/conversation-inference-profile-route.test.ts +27 -1
  71. package/src/__tests__/conversation-initial-prompt.test.ts +51 -22
  72. package/src/__tests__/conversation-key-store-bootstrap-cleanup.test.ts +119 -0
  73. package/src/__tests__/conversation-key-store-origin.test.ts +105 -0
  74. package/src/__tests__/conversation-list-recency-ordering.test.ts +104 -0
  75. package/src/__tests__/conversation-origin-at-creation.test.ts +153 -0
  76. package/src/__tests__/conversation-origin-channel-filter.test.ts +95 -0
  77. package/src/__tests__/conversation-placement-promotion.test.ts +172 -0
  78. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +123 -0
  79. package/src/__tests__/conversation-queue.test.ts +433 -1
  80. package/src/__tests__/conversation-retry-route.test.ts +3 -1
  81. package/src/__tests__/conversation-routes-hidden-queue.test.ts +64 -1
  82. package/src/__tests__/conversation-store-ephemeral.test.ts +4 -0
  83. package/src/__tests__/conversation-store.test.ts +26 -10
  84. package/src/__tests__/conversation-summarize-route.test.ts +1 -1
  85. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +15 -6
  86. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -5
  87. package/src/__tests__/conversation-surfaces-app-control.test.ts +12 -11
  88. package/src/__tests__/conversation-surfaces-app-open.test.ts +11 -5
  89. package/src/__tests__/conversation-surfaces-data-persist.test.ts +5 -4
  90. package/src/__tests__/conversation-surfaces-history-restored-completion.test.ts +967 -0
  91. package/src/__tests__/conversation-surfaces-persisted-info.test.ts +312 -0
  92. package/src/__tests__/conversation-surfaces-queued-emit.test.ts +6 -5
  93. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +12 -8
  94. package/src/__tests__/conversation-surfaces-standalone.test.ts +21 -11
  95. package/src/__tests__/conversation-surfaces-state-update.test.ts +7 -11
  96. package/src/__tests__/conversation-surfaces-table-action.test.ts +7 -6
  97. package/src/__tests__/conversation-surfaces-task-progress.test.ts +11 -6
  98. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +9 -6
  99. package/src/__tests__/conversation-tool-setup-attribution.test.ts +70 -7
  100. package/src/__tests__/conversation-wait-for-idle.test.ts +6 -11
  101. package/src/__tests__/credential-prompt-route.test.ts +54 -7
  102. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  103. package/src/__tests__/cu-unified-flow.test.ts +89 -28
  104. package/src/__tests__/custom-group-listing-visibility.test.ts +1 -1
  105. package/src/__tests__/db-schedule-syntax-migration.test.ts +4 -1
  106. package/src/__tests__/discord-access-request-privacy.test.ts +366 -0
  107. package/src/__tests__/discord-requester-notice-privacy.test.ts +87 -0
  108. package/src/__tests__/disk-pressure-guard.test.ts +55 -55
  109. package/src/__tests__/disk-pressure-routes.test.ts +10 -10
  110. package/src/__tests__/disk-pressure-tools.test.ts +2 -2
  111. package/src/__tests__/disk-usage.test.ts +35 -20
  112. package/src/__tests__/document-sync-tags.test.ts +379 -0
  113. package/src/__tests__/document-update-default-surface.test.ts +28 -0
  114. package/src/__tests__/dynamic-skill-background-guard.test.ts +2 -2
  115. package/src/__tests__/emit-signal-routing-intent.test.ts +3 -4
  116. package/src/__tests__/ensure-conversation-exists.test.ts +8 -5
  117. package/src/__tests__/events-dev-bypass-actor.test.ts +294 -1
  118. package/src/__tests__/evict-conversations-for-reload.test.ts +90 -0
  119. package/src/__tests__/file-list-tool.test.ts +12 -10
  120. package/src/__tests__/file-ops-service.test.ts +151 -44
  121. package/src/__tests__/file-read-container-boundary.test.ts +137 -0
  122. package/src/__tests__/file-read-tool.test.ts +21 -9
  123. package/src/__tests__/file-url-path-guard.test.ts +83 -0
  124. package/src/__tests__/filesystem-tools.test.ts +82 -66
  125. package/src/__tests__/guardian-outbound-http.test.ts +8 -8
  126. package/src/__tests__/helpers/gateway-verification-sessions-stub.ts +14 -1
  127. package/src/__tests__/helpers/hub-clients.ts +42 -0
  128. package/src/__tests__/helpers/mock-conversation.ts +46 -0
  129. package/src/__tests__/helpers/verification-sessions-ipc-sim.ts +61 -57
  130. package/src/__tests__/history-repair.test.ts +112 -12
  131. package/src/__tests__/host-bash-proxy.test.ts +10 -5
  132. package/src/__tests__/host-bash-routes.test.ts +67 -0
  133. package/src/__tests__/host-browser-proxy.test.ts +2 -2
  134. package/src/__tests__/host-cu-proxy.test.ts +3 -7
  135. package/src/__tests__/host-file-proxy.test.ts +3 -7
  136. package/src/__tests__/host-file-read-tool.test.ts +5 -1
  137. package/src/__tests__/host-transfer-proxy-targeted.test.ts +6 -4
  138. package/src/__tests__/http-conversation-lineage.test.ts +2 -5
  139. package/src/__tests__/http-user-message-parity.test.ts +44 -0
  140. package/src/__tests__/identity-routes.test.ts +13 -13
  141. package/src/__tests__/image-conversion.test.ts +29 -26
  142. package/src/__tests__/image-source-path-reinject.test.ts +2 -2
  143. package/src/__tests__/inference-profile-session-handler.test.ts +95 -0
  144. package/src/__tests__/inference-profile-session-ipc.test.ts +25 -0
  145. package/src/__tests__/introduction-card-resolver.test.ts +6 -1
  146. package/src/__tests__/list-messages-attachments.test.ts +56 -32
  147. package/src/__tests__/list-messages-background-tool-completion.test.ts +4 -4
  148. package/src/__tests__/list-messages-client-message-id.test.ts +2 -2
  149. package/src/__tests__/list-messages-hidden-metadata.test.ts +16 -16
  150. package/src/__tests__/list-messages-page-latest.test.ts +61 -61
  151. package/src/__tests__/list-messages-provider-error.test.ts +6 -6
  152. package/src/__tests__/list-messages-queued.test.ts +49 -6
  153. package/src/__tests__/list-messages-system-card.test.ts +4 -4
  154. package/src/__tests__/list-messages-tool-merge.test.ts +9 -9
  155. package/src/__tests__/llm-resolver-override-or-default.test.ts +141 -0
  156. package/src/__tests__/llm-usage-store.test.ts +55 -0
  157. package/src/__tests__/manual-token-reconciliation.test.ts +45 -0
  158. package/src/__tests__/mcp-list-plugin-servers.test.ts +250 -0
  159. package/src/__tests__/mcp-tool-annotations-risk.test.ts +133 -0
  160. package/src/__tests__/media-stream-output.test.ts +175 -0
  161. package/src/__tests__/media-stream-server-integration.test.ts +36 -0
  162. package/src/__tests__/media-stream-stt-session.test.ts +67 -0
  163. package/src/__tests__/migration-export-http.test.ts +1 -1
  164. package/src/__tests__/migration-import-commit-http.test.ts +1 -1
  165. package/src/__tests__/migration-import-preflight-http.test.ts +1 -1
  166. package/src/__tests__/migration-validate-http.test.ts +1 -1
  167. package/src/__tests__/mtime-cache.test.ts +65 -3
  168. package/src/__tests__/notification-platform-adapter.test.ts +104 -0
  169. package/src/__tests__/notification-schedule-notify-dedup.test.ts +1 -1
  170. package/src/__tests__/oauth-provider-profiles.test.ts +4 -2
  171. package/src/__tests__/oauth-provider-seed-logos.test.ts +65 -0
  172. package/src/__tests__/oauth-provider-serializer.test.ts +17 -0
  173. package/src/__tests__/oauth-providers-routes.test.ts +1 -0
  174. package/src/__tests__/openai-responses-prompt-cache.test.ts +5 -2
  175. package/src/__tests__/path-policy.test.ts +164 -1
  176. package/src/__tests__/persist-media-references.test.ts +2 -2
  177. package/src/__tests__/persist-unsendable-image-downscale.test.ts +5 -5
  178. package/src/__tests__/persist-unsendable-image.test.ts +23 -23
  179. package/src/__tests__/persona-resolver.test.ts +154 -0
  180. package/src/__tests__/plugin-api-webhook-url.test.ts +170 -0
  181. package/src/__tests__/plugin-import-boundary-guard.test.ts +6 -0
  182. package/src/__tests__/processing-flag-persist-failure.test.ts +15 -10
  183. package/src/__tests__/profile-availability-incomplete.test.ts +113 -0
  184. package/src/__tests__/profiler-routes.test.ts +13 -13
  185. package/src/__tests__/prompt-cache-cross-turn-stability.test.ts +593 -0
  186. package/src/__tests__/proxy-approval-callback.test.ts +3 -3
  187. package/src/__tests__/queued-message-cancel-and-steer.test.ts +189 -0
  188. package/src/__tests__/queued-message-steer-actor-scoping.test.ts +164 -0
  189. package/src/__tests__/resolve-trust-class.test.ts +20 -0
  190. package/src/__tests__/run-conversation-turn-persistence.test.ts +1 -1
  191. package/src/__tests__/run-due-schedules.test.ts +174 -0
  192. package/src/__tests__/runtime-attachment-metadata.test.ts +15 -7
  193. package/src/__tests__/scaffold-managed-skill-tool.test.ts +166 -0
  194. package/src/__tests__/schedule-routes.test.ts +730 -8
  195. package/src/__tests__/schedule-store.test.ts +540 -0
  196. package/src/__tests__/schedule-tools.test.ts +103 -0
  197. package/src/__tests__/scheduler-wake.test.ts +5 -0
  198. package/src/__tests__/secret-routes-platform-proxy.test.ts +29 -1
  199. package/src/__tests__/secure-keys.test.ts +91 -0
  200. package/src/__tests__/server-history-render.test.ts +97 -7
  201. package/src/__tests__/size-guard.test.ts +8 -8
  202. package/src/__tests__/skill-docs-sync-guard.test.ts +3 -2
  203. package/src/__tests__/skill-tool-factory.test.ts +247 -0
  204. package/src/__tests__/skills-install-staging.test.ts +15 -6
  205. package/src/__tests__/skills.test.ts +32 -0
  206. package/src/__tests__/slack-edit-ordering-characterization.test.ts +103 -0
  207. package/src/__tests__/slack-inbound-verification.test.ts +4 -1
  208. package/src/__tests__/slack-mention-binding-invariance.test.ts +103 -0
  209. package/src/__tests__/slack-mention-provider-leak-audit.test.ts +135 -0
  210. package/src/__tests__/starter-task-flow.test.ts +5 -4
  211. package/src/__tests__/steer-on-enqueue-question.test.ts +3 -2
  212. package/src/__tests__/stt-language-catalog-parity.test.ts +38 -0
  213. package/src/__tests__/subagent-allowlist-validation.test.ts +26 -4
  214. package/src/__tests__/subagent-cron-run-attribution.test.ts +301 -0
  215. package/src/__tests__/subagent-disposal.test.ts +10 -0
  216. package/src/__tests__/subagent-fork-notifications.test.ts +10 -0
  217. package/src/__tests__/subagent-fork-prompt-role.test.ts +85 -11
  218. package/src/__tests__/subagent-fork-spawn.test.ts +15 -7
  219. package/src/__tests__/subagent-manager-notify.test.ts +148 -0
  220. package/src/__tests__/subagent-notify-parent.test.ts +1 -1
  221. package/src/__tests__/subagent-persistence.test.ts +141 -0
  222. package/src/__tests__/subagent-role-registry.test.ts +172 -63
  223. package/src/__tests__/subagent-role-resolution.test.ts +88 -0
  224. package/src/__tests__/subagent-spawn-and-await.test.ts +48 -0
  225. package/src/__tests__/subagent-spawn-tool-fork.test.ts +111 -10
  226. package/src/__tests__/subagent-terminal-message.test.ts +88 -1
  227. package/src/__tests__/subagent-tool-gate-mode.test.ts +225 -10
  228. package/src/__tests__/subagent-tools.test.ts +1901 -108
  229. package/src/__tests__/surface-completion-compaction-boundary.test.ts +124 -0
  230. package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +298 -0
  231. package/src/__tests__/system-prompt.test.ts +82 -7
  232. package/src/__tests__/tool-grant-request-escalation.test.ts +40 -1
  233. package/src/__tests__/tool-result-spool.test.ts +74 -18
  234. package/src/__tests__/tool-side-effects-documents.test.ts +174 -0
  235. package/src/__tests__/tool-side-effects-hook-failures.test.ts +129 -0
  236. package/src/__tests__/tools-audio-read.test.ts +10 -10
  237. package/src/__tests__/tools-get-route.test.ts +85 -12
  238. package/src/__tests__/tools-list-cli.test.ts +171 -0
  239. package/src/__tests__/top-level-renderer.test.ts +54 -1
  240. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +68 -1
  241. package/src/__tests__/trusted-contact-multichannel.test.ts +4 -1
  242. package/src/__tests__/turn-tail-chain.test.ts +135 -0
  243. package/src/__tests__/ui-choice-copy-surfaces.test.ts +6 -4
  244. package/src/__tests__/ui-visual-surface.test.ts +5 -4
  245. package/src/__tests__/ui-work-result-surface.test.ts +5 -4
  246. package/src/__tests__/unified-turn-context-visible-app.test.ts +7 -8
  247. package/src/__tests__/usage-routes.test.ts +62 -0
  248. package/src/__tests__/verification-outbound-delivery.test.ts +176 -0
  249. package/src/__tests__/visible-app-context.test.ts +20 -4
  250. package/src/__tests__/web-search-history.test.ts +96 -0
  251. package/src/__tests__/workspace-migration-139-clear-renamed-cost-profile-label.test.ts +6 -6
  252. package/src/__tests__/workspace-migration-140-repair-seed-pinned-memory-v3-live.test.ts +330 -0
  253. package/src/__tests__/workspace-migration-142-consolidate-voice-front-door.test.ts +150 -0
  254. package/src/acp/failure-error.ts +3 -5
  255. package/src/agent/attachments.ts +26 -24
  256. package/src/agent/history-repair/history-repair.ts +27 -26
  257. package/src/agent/image-optimize.ts +6 -6
  258. package/src/agent/loop.ts +40 -22
  259. package/src/agent/message-types.ts +3 -3
  260. package/src/api/constants/document-tools.ts +55 -0
  261. package/src/api/events/question-answered.ts +53 -0
  262. package/src/api/events/tool-result.ts +7 -0
  263. package/src/api/index.ts +13 -0
  264. package/src/api/responses/conversation-message.ts +11 -0
  265. package/src/api/surfaces-oauth-connect.test.ts +96 -0
  266. package/src/api/surfaces.ts +36 -8
  267. package/src/approvals/guardian-channel-delivery.ts +83 -0
  268. package/src/approvals/guardian-expiry-notifier.ts +13 -11
  269. package/src/approvals/guardian-request-resolvers.ts +222 -135
  270. package/src/calls/__tests__/tts-text-sanitizer.test.ts +13 -0
  271. package/src/calls/__tests__/voice-session-bridge.test.ts +131 -12
  272. package/src/calls/__tests__/voice-triage-escalate.test.ts +106 -0
  273. package/src/calls/call-controller.ts +30 -2
  274. package/src/calls/call-speech-output.ts +12 -4
  275. package/src/calls/call-transport.ts +18 -1
  276. package/src/calls/media-stream-output.ts +53 -5
  277. package/src/calls/media-stream-server.ts +21 -1
  278. package/src/calls/media-stream-stt-session.ts +49 -0
  279. package/src/calls/telephony-synthesis-language.ts +84 -0
  280. package/src/calls/tts-text-sanitizer.ts +12 -5
  281. package/src/calls/voice-session-bridge.ts +86 -17
  282. package/src/calls/voice-triage-escalate.ts +54 -7
  283. package/src/channels/__tests__/gateway-verification-sessions.test.ts +3 -3
  284. package/src/channels/__tests__/plugin-channel-declarations.test.ts +161 -0
  285. package/src/channels/config.ts +20 -2
  286. package/src/channels/gateway-verification-sessions.ts +7 -2
  287. package/src/channels/plugin-channel-declarations.ts +108 -0
  288. package/src/channels/types.ts +37 -1
  289. package/src/cli/AGENTS.md +5 -2
  290. package/src/cli/commands/__tests__/cache.test.ts +7 -4
  291. package/src/cli/commands/__tests__/cli-test-harness.ts +82 -0
  292. package/src/cli/commands/__tests__/conversations-wake.test.ts +51 -6
  293. package/src/cli/commands/__tests__/inference-models.test.ts +14 -0
  294. package/src/cli/commands/__tests__/inference-profiles.test.ts +44 -1
  295. package/src/cli/commands/__tests__/inference-providers.test.ts +7 -0
  296. package/src/cli/commands/__tests__/plugins.test.ts +692 -0
  297. package/src/cli/commands/__tests__/schedules.test.ts +136 -37
  298. package/src/cli/commands/conversations.ts +7 -3
  299. package/src/cli/commands/credentials.help.ts +7 -0
  300. package/src/cli/commands/inference-profiles.ts +9 -3
  301. package/src/cli/commands/inference.help.ts +16 -2
  302. package/src/cli/commands/mcp.help.ts +17 -6
  303. package/src/cli/commands/mcp.ts +9 -0
  304. package/src/cli/commands/memory/memory-retrospective.ts +18 -0
  305. package/src/cli/commands/notifications.ts +3 -6
  306. package/src/cli/commands/oauth/connect-surface-guidance.test.ts +16 -0
  307. package/src/cli/commands/oauth/connect-surface-guidance.ts +25 -7
  308. package/src/cli/commands/oauth/connect.test.ts +26 -1
  309. package/src/cli/commands/oauth/connect.ts +4 -1
  310. package/src/cli/commands/oauth/index.help.ts +3 -1
  311. package/src/cli/commands/oauth/providers.ts +3 -0
  312. package/src/cli/commands/plugins.help.ts +25 -2
  313. package/src/cli/commands/plugins.ts +289 -10
  314. package/src/cli/commands/schedules.help.ts +14 -12
  315. package/src/cli/commands/schedules.ts +19 -5
  316. package/src/cli/commands/stt.help.ts +27 -2
  317. package/src/cli/commands/tools.help.ts +4 -2
  318. package/src/cli/commands/tools.ts +38 -0
  319. package/src/cli/lib/__tests__/inspect-plugin.test.ts +1 -0
  320. package/src/cli/lib/__tests__/install-from-github.test.ts +79 -0
  321. package/src/cli/lib/__tests__/install-from-platform.test.ts +47 -1
  322. package/src/cli/lib/__tests__/plugin-surfaces.test.ts +86 -7
  323. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +161 -0
  324. package/src/cli/lib/install-from-github.ts +61 -0
  325. package/src/cli/lib/install-from-platform.ts +13 -2
  326. package/src/cli/lib/plugin-surfaces.ts +124 -4
  327. package/src/cli/lib/upgrade-plugin.ts +60 -0
  328. package/src/cli.ts +1 -1
  329. package/src/config/__tests__/default-profile-catalog.test.ts +11 -1
  330. package/src/config/__tests__/deployment-context-defaults.test.ts +73 -0
  331. package/src/config/__tests__/memory-retrospective-schema.test.ts +40 -0
  332. package/src/config/__tests__/plugin-updates-schema.test.ts +43 -0
  333. package/src/config/__tests__/profile-tool-support.test.ts +131 -0
  334. package/src/config/bundled-skills/AGENTS.md +1 -1
  335. package/src/config/bundled-skills/acp/SKILL.md +6 -7
  336. package/src/config/bundled-skills/app-builder/SKILL.md +1 -1
  337. package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +3 -3
  338. package/src/config/bundled-skills/media-processing/services/preprocess.ts +14 -4
  339. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +15 -15
  340. package/src/config/bundled-skills/schedule/SKILL.md +5 -1
  341. package/src/config/bundled-skills/schedule/TOOLS.json +2 -2
  342. package/src/config/bundled-skills/settings/TOOLS.json +3 -3
  343. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +1 -0
  344. package/src/config/bundled-skills/subagent/SKILL.md +53 -22
  345. package/src/config/bundled-skills/subagent/TOOLS.json +14 -13
  346. package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +22 -1
  347. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +9 -2
  348. package/src/config/bundled-skills/visualize/SKILL.md +1 -1
  349. package/src/config/call-site-defaults.ts +4 -5
  350. package/src/config/default-profile-catalog.ts +9 -5
  351. package/src/config/feature-flag-registry.json +25 -17
  352. package/src/config/llm-resolver.ts +45 -15
  353. package/src/config/loader.ts +75 -1
  354. package/src/config/profile-tool-support.ts +110 -0
  355. package/src/config/sanitize-for-transfer.ts +11 -0
  356. package/src/config/schema.ts +4 -0
  357. package/src/config/schemas/__tests__/live-voice.test.ts +142 -4
  358. package/src/config/schemas/__tests__/stt.test.ts +20 -5
  359. package/src/config/schemas/call-site-catalog.ts +4 -4
  360. package/src/config/schemas/calls.ts +0 -4
  361. package/src/config/schemas/channels.ts +13 -0
  362. package/src/config/schemas/live-voice.ts +82 -23
  363. package/src/config/schemas/llm.ts +1 -1
  364. package/src/config/schemas/memory-retrospective.ts +25 -0
  365. package/src/config/schemas/plugin-updates.ts +59 -0
  366. package/src/config/schemas/services.ts +8 -4
  367. package/src/config/schemas/stt.ts +18 -5
  368. package/src/config/schemas/timeouts.ts +1 -1
  369. package/src/config/schemas/tts.ts +63 -0
  370. package/src/contacts/__tests__/guardian-delivery-reader.test.ts +16 -0
  371. package/src/contacts/guardian-delivery-reader.ts +29 -2
  372. package/src/context/compactor.ts +4 -4
  373. package/src/context/post-turn-tool-result-truncation.ts +32 -16
  374. package/src/context/strip-injections.ts +42 -0
  375. package/src/context/tool-result-spool.ts +32 -18
  376. package/src/credential-execution/ces-runtime.ts +3 -0
  377. package/src/credential-execution/client.ts +22 -1
  378. package/src/credential-execution/process-manager.test.ts +99 -0
  379. package/src/credential-execution/process-manager.ts +62 -16
  380. package/src/daemon/__tests__/abort-null-controller.test.ts +141 -5
  381. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +8 -12
  382. package/src/daemon/__tests__/install-assistant-command.test.ts +234 -0
  383. package/src/daemon/__tests__/turn-tail-assistant-reply-notify.test.ts +2 -4
  384. package/src/daemon/__tests__/turn-tail-deleted-conversation.test.ts +20 -29
  385. package/src/daemon/channel-ui-capability.ts +4 -4
  386. package/src/daemon/conversation-agent-loop-handlers.ts +117 -61
  387. package/src/daemon/conversation-agent-loop.ts +318 -148
  388. package/src/daemon/conversation-attachments.ts +1 -1
  389. package/src/daemon/conversation-error.ts +8 -1
  390. package/src/daemon/conversation-initial-prompt.ts +16 -38
  391. package/src/daemon/conversation-lifecycle.ts +154 -32
  392. package/src/daemon/conversation-messaging.ts +106 -43
  393. package/src/daemon/conversation-process.ts +131 -33
  394. package/src/daemon/conversation-queue-manager.ts +10 -0
  395. package/src/daemon/conversation-runtime-assembly.ts +9 -4
  396. package/src/daemon/conversation-store.ts +52 -3
  397. package/src/daemon/conversation-surface-state.ts +9 -0
  398. package/src/daemon/conversation-surfaces.ts +403 -210
  399. package/src/daemon/conversation-tool-setup.ts +56 -7
  400. package/src/daemon/conversation-turn-finalize.ts +134 -89
  401. package/src/daemon/conversation.ts +111 -27
  402. package/src/daemon/disk-pressure-guard-lifecycle.ts +2 -2
  403. package/src/daemon/disk-pressure-guard.ts +3 -3
  404. package/src/daemon/doordash-steps.ts +5 -5
  405. package/src/daemon/external-plugins-bootstrap.ts +7 -1
  406. package/src/daemon/handlers/config-channels.ts +48 -5
  407. package/src/daemon/handlers/conversation-history.ts +1 -1
  408. package/src/daemon/handlers/conversations.ts +62 -17
  409. package/src/daemon/handlers/shared.ts +12 -0
  410. package/src/daemon/handlers/skills.ts +11 -1
  411. package/src/daemon/identity-helpers.ts +38 -10
  412. package/src/daemon/install-assistant-command.ts +322 -0
  413. package/src/daemon/lifecycle.ts +14 -3
  414. package/src/daemon/message-types/conversations.ts +6 -7
  415. package/src/daemon/message-types/sync.ts +1 -0
  416. package/src/daemon/persist-media-references.ts +23 -14
  417. package/src/daemon/process-message.ts +6 -6
  418. package/src/daemon/tool-setup-types.ts +19 -122
  419. package/src/daemon/tool-side-effects.ts +17 -94
  420. package/src/daemon/trust-context-types.ts +39 -0
  421. package/src/daemon/trust-context.ts +14 -6
  422. package/src/daemon/turn-tail-chain.ts +73 -0
  423. package/src/daemon/web-search-history.ts +47 -33
  424. package/src/heartbeat/heartbeat-service.ts +6 -6
  425. package/src/inbound/__tests__/oauth-callback-url.test.ts +83 -0
  426. package/src/inbound/oauth-callback-url.ts +61 -0
  427. package/src/ipc/__tests__/socket-path.test.ts +21 -0
  428. package/src/ipc/assistant-server.ts +17 -10
  429. package/src/ipc/cli-client.ts +10 -1
  430. package/src/ipc/routes/__tests__/documents-sync-ipc-routes.test.ts +56 -0
  431. package/src/ipc/routes/documents-sync-ipc-routes.ts +42 -0
  432. package/src/ipc/socket-cleanup.ts +9 -7
  433. package/src/ipc/socket-path.ts +4 -96
  434. package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +1 -104
  435. package/src/live-voice/__tests__/live-voice-archive.test.ts +16 -16
  436. package/src/live-voice/__tests__/live-voice-attach-image.test.ts +209 -0
  437. package/src/live-voice/__tests__/live-voice-connection.test.ts +20 -0
  438. package/src/live-voice/__tests__/live-voice-events.test.ts +7 -8
  439. package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +932 -0
  440. package/src/live-voice/__tests__/live-voice-integration.test.ts +12 -5
  441. package/src/live-voice/__tests__/live-voice-metrics.test.ts +115 -8
  442. package/src/live-voice/__tests__/live-voice-photo.test.ts +100 -0
  443. package/src/live-voice/__tests__/live-voice-progress.test.ts +179 -146
  444. package/src/live-voice/__tests__/live-voice-session-telemetry.test.ts +265 -0
  445. package/src/live-voice/__tests__/live-voice-stt.test.ts +324 -2
  446. package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +131 -2
  447. package/src/live-voice/__tests__/live-voice-tts-session.test.ts +0 -483
  448. package/src/live-voice/__tests__/live-voice-tts.test.ts +148 -2
  449. package/src/live-voice/__tests__/live-voice-vad.test.ts +313 -32
  450. package/src/live-voice/__tests__/progress-narration.test.ts +214 -0
  451. package/src/live-voice/__tests__/progress-phrases.test.ts +167 -0
  452. package/src/live-voice/__tests__/protocol.test.ts +52 -0
  453. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +3 -0
  454. package/src/live-voice/live-voice-archive.ts +13 -13
  455. package/src/live-voice/live-voice-connection.ts +7 -1
  456. package/src/live-voice/live-voice-metrics.ts +57 -32
  457. package/src/live-voice/live-voice-photo.ts +168 -0
  458. package/src/live-voice/live-voice-session.ts +1137 -351
  459. package/src/live-voice/live-voice-tts.ts +18 -2
  460. package/src/live-voice/progress-narration.ts +277 -0
  461. package/src/live-voice/progress-phrases.ts +105 -2
  462. package/src/live-voice/protocol.ts +110 -2
  463. package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +0 -1
  464. package/src/mcp/__tests__/mcp-oauth-client-registration.test.ts +196 -0
  465. package/src/mcp/__tests__/mcp-oauth-provider.test.ts +9 -9
  466. package/src/mcp/client.ts +14 -7
  467. package/src/mcp/mcp-auth-orchestrator.ts +13 -22
  468. package/src/mcp/mcp-oauth-provider.ts +195 -240
  469. package/src/messaging/providers/__tests__/callback-routing.test.ts +5 -1
  470. package/src/messaging/providers/__tests__/transport-dispatch.test.ts +66 -4
  471. package/src/messaging/providers/callback-routing.ts +1 -0
  472. package/src/messaging/providers/discord/api.ts +312 -0
  473. package/src/messaging/providers/discord/dm-delivery.test.ts +204 -0
  474. package/src/messaging/providers/discord/render.test.ts +204 -0
  475. package/src/messaging/providers/discord/render.ts +195 -0
  476. package/src/messaging/providers/discord/send.test.ts +179 -0
  477. package/src/messaging/providers/discord/send.ts +212 -0
  478. package/src/messaging/providers/discord/transport.ts +81 -0
  479. package/src/messaging/providers/index.ts +3 -1
  480. package/src/messaging/providers/retry-policy.test.ts +247 -0
  481. package/src/messaging/providers/retry-policy.ts +206 -0
  482. package/src/messaging/providers/slack/mention-source.test.ts +285 -0
  483. package/src/messaging/providers/slack/mention-source.ts +287 -0
  484. package/src/messaging/providers/telegram-bot/api.ts +47 -130
  485. package/src/messaging/providers/whatsapp/api.ts +47 -136
  486. package/src/monitoring/__tests__/db-integrity-sample.test.ts +4 -1
  487. package/src/monitoring/__tests__/plugin-auto-update.test.ts +495 -0
  488. package/src/monitoring/db-integrity-sample.ts +6 -1
  489. package/src/monitoring/plugin-auto-update.ts +463 -0
  490. package/src/monitoring/resource-sampler.ts +21 -5
  491. package/src/monitoring/worker.ts +13 -0
  492. package/src/notifications/__tests__/assistant-reply-producer.test.ts +375 -2
  493. package/src/notifications/__tests__/broadcaster.test.ts +37 -1
  494. package/src/notifications/__tests__/copy-composer.test.ts +165 -0
  495. package/src/notifications/__tests__/emit-signal-pipeline-failure.test.ts +205 -0
  496. package/src/notifications/__tests__/notification-utils.test.ts +249 -0
  497. package/src/notifications/adapters/platform.ts +31 -2
  498. package/src/notifications/assistant-reply-producer.ts +79 -32
  499. package/src/notifications/broadcaster.ts +30 -10
  500. package/src/notifications/copy-composer.ts +69 -0
  501. package/src/notifications/emit-signal.ts +82 -15
  502. package/src/notifications/events-store.ts +10 -2
  503. package/src/notifications/home-feed-side-effect.ts +6 -3
  504. package/src/notifications/notification-utils.ts +173 -5
  505. package/src/notifications/signal.ts +13 -0
  506. package/src/oauth/AGENTS.md +4 -2
  507. package/src/oauth/byo-connection.ts +6 -1
  508. package/src/oauth/credential-token-resolver.ts +3 -10
  509. package/src/oauth/manual-token-connection.ts +29 -64
  510. package/src/oauth/manual-token-providers.ts +62 -0
  511. package/src/oauth/provider-serializer.ts +13 -0
  512. package/src/oauth/seed-providers.ts +62 -11
  513. package/src/onboarding/onboarding-events-store.ts +91 -1
  514. package/src/permissions/question-prompter.test.ts +4 -0
  515. package/src/permissions/question-prompter.ts +21 -4
  516. package/src/persistence/__tests__/conversation-lineage.test.ts +248 -0
  517. package/src/persistence/attachments-store.ts +15 -15
  518. package/src/persistence/conversation-attention-store.ts +30 -10
  519. package/src/persistence/conversation-bootstrap.ts +17 -0
  520. package/src/persistence/conversation-crud.ts +363 -97
  521. package/src/persistence/conversation-group-migration.ts +1 -1
  522. package/src/persistence/conversation-key-store.ts +38 -10
  523. package/src/persistence/conversation-lineage.ts +226 -0
  524. package/src/persistence/conversation-plugin-facade.ts +28 -3
  525. package/src/persistence/conversation-queries.ts +207 -33
  526. package/src/persistence/conversation-types.ts +107 -1
  527. package/src/persistence/jobs-store.ts +32 -8
  528. package/src/persistence/llm-usage-store.ts +22 -0
  529. package/src/persistence/migrations/361-normalize-managed-connection-rows.test.ts +168 -0
  530. package/src/persistence/migrations/361-normalize-managed-connection-rows.ts +76 -0
  531. package/src/persistence/migrations/362-add-conversation-subagent-kind.test.ts +187 -0
  532. package/src/persistence/migrations/362-add-conversation-subagent-kind.ts +93 -0
  533. package/src/persistence/migrations/363-backfill-schedule-inference-profile.test.ts +229 -0
  534. package/src/persistence/migrations/363-backfill-schedule-inference-profile.ts +127 -0
  535. package/src/persistence/migrations/364-add-schedule-source-key.test.ts +120 -0
  536. package/src/persistence/migrations/364-add-schedule-source-key.ts +43 -0
  537. package/src/persistence/migrations/365-add-conversation-fork-strategy.ts +27 -0
  538. package/src/persistence/schema/conversations.ts +32 -0
  539. package/src/persistence/schema/infrastructure.ts +3 -0
  540. package/src/persistence/steps.ts +30 -0
  541. package/src/persistence/subagent-store.ts +148 -6
  542. package/src/platform/managed-speech.test.ts +20 -0
  543. package/src/platform/managed-speech.ts +9 -0
  544. package/src/plugin-api/__tests__/oauth-callback-url-export.test.ts +29 -0
  545. package/src/plugin-api/index.ts +28 -5
  546. package/src/plugin-api/webhook-url.ts +151 -0
  547. package/src/plugins/__tests__/installed-plugin-dirs.test.ts +82 -0
  548. package/src/plugins/__tests__/mcp-servers.test.ts +370 -0
  549. package/src/plugins/__tests__/source-fingerprint.test.ts +5 -1
  550. package/src/plugins/collect-source-versions.ts +4 -27
  551. package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +5 -5
  552. package/src/plugins/defaults/exploration-drift/package.json +1 -1
  553. package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +4 -2
  554. package/src/plugins/defaults/image-recovery/recover.ts +67 -57
  555. package/src/plugins/defaults/index.ts +1 -1
  556. package/src/plugins/defaults/memory/__tests__/conversation-queries.test.ts +64 -18
  557. package/src/plugins/defaults/memory/__tests__/jobs-worker-outcome-contract.test.ts +242 -0
  558. package/src/plugins/defaults/memory/__tests__/jobs-worker-retrospective-sweep.test.ts +24 -1
  559. package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +48 -3
  560. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +511 -19
  561. package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +39 -3
  562. package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +493 -0
  563. package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +5 -4
  564. package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +5 -1
  565. package/src/plugins/defaults/memory/job-handlers.ts +87 -5
  566. package/src/plugins/defaults/memory/jobs-worker.ts +60 -6
  567. package/src/plugins/defaults/memory/memory-retrospective-constants.ts +11 -0
  568. package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +29 -0
  569. package/src/plugins/defaults/memory/memory-retrospective-job.ts +342 -60
  570. package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +23 -3
  571. package/src/plugins/defaults/memory/substrate/consolidation-lock.ts +9 -4
  572. package/src/plugins/defaults/memory/substrate/skill-content.ts +8 -1
  573. package/src/plugins/defaults/memory/substrate/sweep-job.ts +7 -0
  574. package/src/plugins/defaults/memory/v1/graph/injection.ts +1 -1
  575. package/src/plugins/defaults/memory/v2/router.ts +8 -0
  576. package/src/plugins/defaults/turn-context/unified-turn-context.ts +1 -2
  577. package/src/plugins/external-plugin-loader.ts +64 -8
  578. package/src/plugins/installed-plugin-dirs.ts +101 -0
  579. package/src/plugins/mcp-servers.ts +343 -0
  580. package/src/plugins/mtime-cache.ts +34 -22
  581. package/src/plugins/types.ts +69 -8
  582. package/src/prompts/persona-resolver.ts +6 -2
  583. package/src/prompts/system-prompt.ts +72 -17
  584. package/src/providers/__tests__/inference.test.ts +40 -0
  585. package/src/providers/__tests__/preflight-resolved-config.test.ts +58 -1
  586. package/src/providers/__tests__/retry-callsite.test.ts +72 -2
  587. package/src/providers/__tests__/vellum-connection-routing.test.ts +42 -11
  588. package/src/providers/anthropic/client.ts +112 -95
  589. package/src/providers/connection-resolution.ts +35 -1
  590. package/src/providers/gemini/client.ts +5 -5
  591. package/src/providers/inference/adapter-factory.ts +16 -10
  592. package/src/providers/inference/auth.ts +16 -0
  593. package/src/providers/inference/connection-availability.ts +164 -12
  594. package/src/providers/inference/connections.ts +25 -1
  595. package/src/providers/media-resolve.ts +21 -11
  596. package/src/providers/model-catalog.ts +15 -0
  597. package/src/providers/openai/chat-completions-provider.ts +4 -4
  598. package/src/providers/openai/responses-provider.ts +12 -25
  599. package/src/providers/registry.ts +9 -4
  600. package/src/providers/retry.ts +25 -0
  601. package/src/providers/server-tool-pairing.ts +134 -0
  602. package/src/providers/speech-to-text/__tests__/deepgram-flux-frames.test.ts +433 -0
  603. package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +620 -0
  604. package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +34 -0
  605. package/src/providers/speech-to-text/__tests__/resolve.test.ts +472 -12
  606. package/src/providers/speech-to-text/deepgram-flux-frames.ts +395 -0
  607. package/src/providers/speech-to-text/deepgram-flux-realtime.ts +719 -0
  608. package/src/providers/speech-to-text/deepgram-realtime.test.ts +283 -1
  609. package/src/providers/speech-to-text/deepgram-realtime.ts +117 -3
  610. package/src/providers/speech-to-text/deepgram.ts +1 -0
  611. package/src/providers/speech-to-text/provider-catalog.ts +137 -8
  612. package/src/providers/speech-to-text/resolve.ts +105 -12
  613. package/src/providers/speech-to-text/vellum-managed.ts +7 -1
  614. package/src/providers/speech-to-text/vellum-speech-relay-connection.ts +3 -1
  615. package/src/providers/types.ts +26 -6
  616. package/src/providers/vellum-model-routing.ts +6 -7
  617. package/src/runtime/AGENTS.md +2 -0
  618. package/src/runtime/__tests__/agent-wake.test.ts +16 -16
  619. package/src/runtime/__tests__/background-job-runner.test.ts +0 -144
  620. package/src/runtime/__tests__/desktop-presence.test.ts +162 -0
  621. package/src/runtime/__tests__/local-actor-identity-force-refresh.test.ts +104 -0
  622. package/src/runtime/agent-wake.ts +63 -24
  623. package/src/runtime/assistant-event-hub.ts +110 -12
  624. package/src/runtime/auth/__tests__/guard-tests.test.ts +2 -0
  625. package/src/runtime/auth/__tests__/scopes.test.ts +20 -0
  626. package/src/runtime/auth/same-actor.ts +56 -8
  627. package/src/runtime/auth/scopes.ts +12 -0
  628. package/src/runtime/auth/types.ts +2 -0
  629. package/src/runtime/background-job-runner.ts +5 -27
  630. package/src/runtime/channel-readiness-service.ts +59 -3
  631. package/src/runtime/channel-readiness-types.ts +18 -0
  632. package/src/runtime/desktop-presence.ts +69 -0
  633. package/src/runtime/http-server.ts +12 -0
  634. package/src/runtime/local-actor-identity.ts +29 -14
  635. package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -0
  636. package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
  637. package/src/runtime/routes/__tests__/client-routes.test.ts +186 -14
  638. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +547 -1
  639. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +162 -7
  640. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +14 -10
  641. package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +340 -3
  642. package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +28 -1
  643. package/src/runtime/routes/__tests__/plugins-routes.test.ts +217 -1
  644. package/src/runtime/routes/__tests__/question-routes.test.ts +64 -0
  645. package/src/runtime/routes/__tests__/retrospective-routes.test.ts +20 -0
  646. package/src/runtime/routes/__tests__/skill-history-route.test.ts +116 -0
  647. package/src/runtime/routes/__tests__/sse-actor-principal-heal.test.ts +165 -0
  648. package/src/runtime/routes/__tests__/stt-routes.test.ts +25 -0
  649. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +2 -0
  650. package/src/runtime/routes/__tests__/wake-conversation-routes.test.ts +60 -2
  651. package/src/runtime/routes/archive-utils.ts +16 -12
  652. package/src/runtime/routes/attachment-routes.ts +13 -6
  653. package/src/runtime/routes/canned-reply-release.ts +54 -0
  654. package/src/runtime/routes/channel-availability-routes.ts +32 -14
  655. package/src/runtime/routes/channel-verification-routes.ts +2 -27
  656. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +5 -0
  657. package/src/runtime/routes/client-routes.ts +114 -2
  658. package/src/runtime/routes/conversation-cli-routes.ts +10 -12
  659. package/src/runtime/routes/conversation-list-routes.ts +59 -10
  660. package/src/runtime/routes/conversation-management-routes.ts +47 -18
  661. package/src/runtime/routes/conversation-query-routes.ts +32 -53
  662. package/src/runtime/routes/conversation-routes.ts +360 -293
  663. package/src/runtime/routes/credential-prompt-routes.ts +20 -2
  664. package/src/runtime/routes/debug-routes.ts +4 -1
  665. package/src/runtime/routes/default-provider-routes.ts +7 -11
  666. package/src/runtime/routes/documents-routes.ts +44 -1
  667. package/src/runtime/routes/events-routes.ts +29 -1
  668. package/src/runtime/routes/host-app-control-routes.ts +2 -0
  669. package/src/runtime/routes/host-bash-routes.ts +2 -0
  670. package/src/runtime/routes/host-browser-routes.ts +2 -0
  671. package/src/runtime/routes/host-cu-routes.ts +2 -0
  672. package/src/runtime/routes/host-file-routes.ts +2 -0
  673. package/src/runtime/routes/host-transfer-routes.ts +4 -0
  674. package/src/runtime/routes/identity-routes.ts +6 -6
  675. package/src/runtime/routes/inbound-message-handler.ts +5 -5
  676. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +7 -3
  677. package/src/runtime/routes/inbound-stages/background-dispatch.ts +14 -0
  678. package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +2 -2
  679. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +14 -11
  680. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +14 -17
  681. package/src/runtime/routes/inference-profile-availability-guard.ts +171 -0
  682. package/src/runtime/routes/inference-profile-session-handler.ts +26 -2
  683. package/src/runtime/routes/inference-profile-session-routes.ts +2 -1
  684. package/src/runtime/routes/inference-profiles-routes.ts +152 -73
  685. package/src/runtime/routes/inference-provider-connection-routes.ts +0 -16
  686. package/src/runtime/routes/llm-call-sites-routes.ts +13 -0
  687. package/src/runtime/routes/log-export-routes.ts +1 -1
  688. package/src/runtime/routes/mcp-auth-routes.ts +141 -57
  689. package/src/runtime/routes/notification-routes.ts +0 -14
  690. package/src/runtime/routes/oauth-providers.ts +1 -0
  691. package/src/runtime/routes/plugins-routes.ts +127 -4
  692. package/src/runtime/routes/profiler-routes.ts +4 -2
  693. package/src/runtime/routes/question-routes.ts +28 -10
  694. package/src/runtime/routes/retrospective-routes.ts +9 -3
  695. package/src/runtime/routes/schedule-routes.ts +210 -12
  696. package/src/runtime/routes/secret-routes.ts +40 -23
  697. package/src/runtime/routes/settings-routes.ts +41 -13
  698. package/src/runtime/routes/skills-routes.ts +79 -0
  699. package/src/runtime/routes/sse-actor-principal-heal.ts +113 -0
  700. package/src/runtime/routes/stt-routes.ts +31 -25
  701. package/src/runtime/routes/ui-snapshot-routes.ts +1 -0
  702. package/src/runtime/routes/wake-conversation-routes.ts +23 -0
  703. package/src/runtime/services/__tests__/conversation-serializer.test.ts +1 -0
  704. package/src/runtime/services/conversation-serializer.ts +24 -9
  705. package/src/runtime/sync/documents-sidecar-publish.test.ts +101 -0
  706. package/src/runtime/sync/resource-sync-events.ts +79 -1
  707. package/src/runtime/sync/worker-daemon-notify.test.ts +34 -0
  708. package/src/runtime/sync/worker-daemon-notify.ts +44 -6
  709. package/src/runtime/verification-outbound-actions.ts +250 -222
  710. package/src/runtime/verification-templates.ts +41 -30
  711. package/src/schedule/__tests__/plugin-schedule-declarations.test.ts +614 -0
  712. package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +1066 -0
  713. package/src/schedule/inference-profile.ts +64 -3
  714. package/src/schedule/plugin-schedule-declarations.ts +518 -0
  715. package/src/schedule/plugin-schedule-reconciler.ts +605 -0
  716. package/src/schedule/plugin-schedules-gate.ts +23 -0
  717. package/src/schedule/schedule-store.ts +520 -26
  718. package/src/schedule/scheduler.ts +38 -0
  719. package/src/security/credential-backend.ts +51 -4
  720. package/src/security/encrypted-store.ts +64 -42
  721. package/src/security/oauth2.ts +6 -22
  722. package/src/security/secure-keys.ts +55 -7
  723. package/src/skills/catalog-install.ts +20 -7
  724. package/src/skills/inline-command-runner.ts +2 -7
  725. package/src/skills/skill-history.test.ts +269 -0
  726. package/src/skills/skill-history.ts +250 -0
  727. package/src/skills/skillssh-registry.ts +1 -1
  728. package/src/stt/__tests__/daemon-batch-transcriber.test.ts +22 -0
  729. package/src/stt/__tests__/language-metadata.test.ts +85 -0
  730. package/src/stt/__tests__/speech-energy.test.ts +79 -0
  731. package/src/stt/__tests__/types.test.ts +94 -0
  732. package/src/stt/daemon-batch-transcriber.ts +18 -6
  733. package/src/stt/language-metadata.ts +65 -0
  734. package/src/stt/speech-energy.ts +115 -12
  735. package/src/stt/stt-stream-session.ts +8 -4
  736. package/src/stt/types.ts +119 -0
  737. package/src/subagent/__tests__/consult-prompt.test.ts +25 -0
  738. package/src/subagent/consult-prompt.ts +3 -1
  739. package/src/subagent/consult-transcript.ts +1 -1
  740. package/src/subagent/manager.ts +304 -44
  741. package/src/subagent/progress-events.ts +35 -0
  742. package/src/subagent/role-resolution.ts +88 -0
  743. package/src/subagent/types.ts +303 -67
  744. package/src/subagent/validate-allowlists.ts +3 -4
  745. package/src/telegram/__tests__/webhook-health.test.ts +73 -1
  746. package/src/telegram/webhook-health.ts +101 -3
  747. package/src/telemetry/__tests__/telemetry-event-fixtures.ts +2 -0
  748. package/src/telemetry/activation-funnel.ts +6 -1
  749. package/src/telemetry/live-voice-funnel.ts +75 -0
  750. package/src/telemetry/telemetry-event-sources.ts +6 -0
  751. package/src/telemetry/telemetry-wire-source.json +1 -1
  752. package/src/telemetry/telemetry-wire.generated.ts +2 -0
  753. package/src/telemetry/types.ts +23 -4
  754. package/src/telemetry/usage-telemetry-reporter.test.ts +4 -0
  755. package/src/tools/__tests__/subagent-workflow-tool-input-schemas.test.ts +1 -1
  756. package/src/tools/ask-question/ask-question-tool.test.ts +118 -3
  757. package/src/tools/ask-question/ask-question-tool.ts +56 -2
  758. package/src/tools/browser/browser-execution.ts +1 -1
  759. package/src/tools/calls/call-start.ts +8 -5
  760. package/src/tools/conversation-groups/move_to_group.ts +10 -12
  761. package/src/tools/document/document-tool.ts +11 -4
  762. package/src/tools/execution-timeout.ts +9 -2
  763. package/src/tools/executor.ts +7 -6
  764. package/src/tools/filesystem/edit.ts +1 -1
  765. package/src/tools/filesystem/list.ts +1 -1
  766. package/src/tools/filesystem/read.ts +7 -13
  767. package/src/tools/filesystem/search.ts +23 -21
  768. package/src/tools/filesystem/write.ts +4 -1
  769. package/src/tools/host-filesystem/edit.ts +7 -2
  770. package/src/tools/host-filesystem/read.ts +21 -8
  771. package/src/tools/host-filesystem/write.ts +10 -2
  772. package/src/tools/host-terminal/host-shell.ts +2 -2
  773. package/src/tools/mcp/mcp-tool-factory.ts +25 -1
  774. package/src/tools/permission-checker.ts +8 -3
  775. package/src/tools/schedule/delete.ts +13 -1
  776. package/src/tools/schedule/list.ts +14 -3
  777. package/src/tools/schedule/update.ts +56 -33
  778. package/src/tools/shared/filesystem/audio-read.ts +6 -4
  779. package/src/tools/shared/filesystem/file-ops-service.ts +176 -116
  780. package/src/tools/shared/filesystem/image-read.ts +12 -12
  781. package/src/tools/shared/filesystem/path-policy.ts +70 -5
  782. package/src/tools/shared/filesystem/size-guard.ts +6 -6
  783. package/src/tools/shared/input-misuse.ts +72 -0
  784. package/src/tools/skills/scaffold-managed.ts +82 -0
  785. package/src/tools/skills/skill-tool-factory.ts +14 -1
  786. package/src/tools/subagent/consult-deadline.ts +12 -7
  787. package/src/tools/subagent/message.ts +3 -1
  788. package/src/tools/subagent/read.ts +66 -4
  789. package/src/tools/subagent/spawn.ts +500 -78
  790. package/src/tools/tool-approval-handler.ts +45 -4
  791. package/src/tools/types.ts +22 -3
  792. package/src/tools/ui-surface/surface-shape-docs.ts +1 -1
  793. package/src/tools/ui-surface/visual-validation.ts +2 -0
  794. package/src/tts/__tests__/provider-adapters.test.ts +147 -0
  795. package/src/tts/__tests__/speakable-segments.test.ts +470 -0
  796. package/src/tts/language-voices.ts +23 -0
  797. package/src/tts/providers/deepgram-provider.ts +3 -1
  798. package/src/tts/providers/elevenlabs-provider.ts +73 -1
  799. package/src/tts/providers/xai-provider.ts +28 -2
  800. package/src/tts/speakable-segments.ts +293 -23
  801. package/src/tts/synthesis-stream.ts +7 -0
  802. package/src/tts/types.ts +7 -0
  803. package/src/usage/__tests__/subagent-attribution.test.ts +109 -0
  804. package/src/usage/subagent-attribution.ts +108 -0
  805. package/src/util/__tests__/language-subtag.test.ts +54 -0
  806. package/src/util/__tests__/sqlite-retry.test.ts +112 -0
  807. package/src/util/abort-reasons.ts +16 -0
  808. package/src/util/ansi.ts +44 -0
  809. package/src/util/disk-usage.ts +17 -13
  810. package/src/util/image-conversion.ts +20 -13
  811. package/src/util/language-subtag.ts +43 -0
  812. package/src/util/schedule-source-key.ts +23 -0
  813. package/src/util/unicode.ts +1 -1
  814. package/src/util/worker-process.ts +7 -1
  815. package/src/workspace/git-service.ts +33 -9
  816. package/src/workspace/migrations/140-repair-seed-pinned-memory-v3-live.ts +260 -0
  817. package/src/workspace/migrations/141-stt-english-default-to-multilingual.ts +111 -0
  818. package/src/workspace/migrations/142-consolidate-voice-front-door.ts +70 -0
  819. package/src/workspace/migrations/__tests__/141-stt-english-default-to-multilingual.test.ts +116 -0
  820. package/src/workspace/migrations/registry.ts +6 -0
  821. package/src/workspace/top-level-renderer.ts +15 -2
  822. package/src/__tests__/tool-side-effects-slack-dm.test.ts +0 -357
  823. package/src/daemon/install-symlink.ts +0 -207
  824. package/src/live-voice/__tests__/front-decision.test.ts +0 -525
  825. package/src/live-voice/front-decision.ts +0 -429
  826. package/src/notifications/deferred-emit.ts +0 -147
@@ -0,0 +1,265 @@
1
+ /**
2
+ * Live-voice session telemetry: the started/ended funnel rows a session
3
+ * records, and the attribution carried on its turns.
4
+ *
5
+ * The recorder is mocked: the outbox and its consent gate are covered by
6
+ * `telemetry-events-outbox.test.ts`. What these tests pin is the wiring that
7
+ * decides whether a row exists at all and what it says, which is where the
8
+ * measurement can silently go wrong: a session that fails before `ready` still
9
+ * has to count, a failed session still has to record an end, and a session's
10
+ * turns have to carry its id or the turn count has nothing to group on.
11
+ */
12
+
13
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
14
+
15
+ import type { VoiceTurnOptions } from "../../calls/voice-session-bridge.js";
16
+ import type {
17
+ StreamingTranscriber,
18
+ SttStreamServerEvent,
19
+ } from "../../stt/types.js";
20
+
21
+ const recordLiveVoiceSessionStarted = mock(() => null);
22
+ const recordLiveVoiceSessionEnded = mock(() => null);
23
+
24
+ // Spread the real module rather than replacing it: other importers pull
25
+ // `recordActivationEvent` and friends out of it, and a bare replacement makes
26
+ // those imports fail to resolve.
27
+ const onboardingEventsStore =
28
+ await import("../../onboarding/onboarding-events-store.js");
29
+ mock.module("../../onboarding/onboarding-events-store.js", () => ({
30
+ ...onboardingEventsStore,
31
+ recordLiveVoiceSessionStarted,
32
+ recordLiveVoiceSessionEnded,
33
+ }));
34
+
35
+ // Types are erased, so they import statically regardless of the mock ordering
36
+ // the values below depend on.
37
+ import type { LiveVoiceCredentialReadiness } from "../live-voice-credential-preflight.js";
38
+ import type { LiveVoiceSessionFactoryContext } from "../live-voice-session-manager.js";
39
+ import type {
40
+ LiveVoiceClientStartFrame,
41
+ LiveVoiceServerFrame,
42
+ } from "../protocol.js";
43
+
44
+ // Values load after `mock.module` so the session picks up the mocked recorder.
45
+ const { LiveVoiceSession } = await import("../live-voice-session.js");
46
+ const { LiveVoiceSessionStartupError } =
47
+ await import("../live-voice-session-manager.js");
48
+ const { createLiveVoiceServerFrameSequencer } = await import("../protocol.js");
49
+
50
+ const START_FRAME = {
51
+ type: "start",
52
+ conversationId: "conversation-123",
53
+ audio: { mimeType: "audio/pcm", sampleRate: 24_000, channels: 1 },
54
+ } as const satisfies LiveVoiceClientStartFrame;
55
+
56
+ const NOT_READY: LiveVoiceCredentialReadiness = {
57
+ status: "not-ready",
58
+ missing: [
59
+ {
60
+ kind: "tts",
61
+ providerId: "fish-audio",
62
+ reason: 'TTS provider "fish-audio" is missing credentials',
63
+ },
64
+ ],
65
+ userMessage: "Live voice is unavailable because it requires an API key.",
66
+ };
67
+
68
+ class MockStreamingTranscriber implements StreamingTranscriber {
69
+ readonly providerId = "deepgram" as const;
70
+ readonly boundaryId = "daemon-streaming" as const;
71
+ private onEvent: ((event: SttStreamServerEvent) => void) | null = null;
72
+
73
+ constructor(private readonly stopEvents: SttStreamServerEvent[] = []) {}
74
+
75
+ async start(onEvent: (event: SttStreamServerEvent) => void): Promise<void> {
76
+ this.onEvent = onEvent;
77
+ }
78
+
79
+ sendAudio(): void {}
80
+
81
+ stop(): void {
82
+ for (const event of this.stopEvents) {
83
+ this.onEvent?.(event);
84
+ }
85
+ }
86
+ }
87
+
88
+ function createContext(
89
+ startFrame: LiveVoiceClientStartFrame = START_FRAME,
90
+ ): LiveVoiceSessionFactoryContext {
91
+ const sequencer = createLiveVoiceServerFrameSequencer();
92
+ const frames: LiveVoiceServerFrame[] = [];
93
+ return {
94
+ sessionId: "session-123",
95
+ startFrame,
96
+ sendFrame: mock(async (payload) => {
97
+ const frame = sequencer.next(payload);
98
+ frames.push(frame);
99
+ return frame;
100
+ }),
101
+ };
102
+ }
103
+
104
+ function readySession(startFrame?: LiveVoiceClientStartFrame) {
105
+ return new LiveVoiceSession(createContext(startFrame), {
106
+ resolveTranscriber: mock(async () => new MockStreamingTranscriber()),
107
+ resolveCredentialReadiness: mock(
108
+ async (): Promise<LiveVoiceCredentialReadiness> => ({ status: "ready" }),
109
+ ),
110
+ });
111
+ }
112
+
113
+ describe("live-voice session telemetry", () => {
114
+ beforeEach(() => {
115
+ recordLiveVoiceSessionStarted.mockClear();
116
+ recordLiveVoiceSessionEnded.mockClear();
117
+ });
118
+
119
+ test("records a started row for a session that reaches ready", async () => {
120
+ const session = readySession();
121
+
122
+ await session.start();
123
+
124
+ expect(recordLiveVoiceSessionStarted).toHaveBeenCalledWith("session-123");
125
+
126
+ await session.close("client_end");
127
+ });
128
+
129
+ test("records a started row even when the preflight rejects the session", async () => {
130
+ const session = new LiveVoiceSession(createContext(), {
131
+ resolveTranscriber: mock(async () => new MockStreamingTranscriber()),
132
+ resolveCredentialReadiness: mock(async () => NOT_READY),
133
+ });
134
+
135
+ await expect(session.start()).rejects.toBeInstanceOf(
136
+ LiveVoiceSessionStartupError,
137
+ );
138
+
139
+ // The denominator of the failure rate: a session rejected before `ready`
140
+ // is exactly the one that must not go missing from the started count.
141
+ expect(recordLiveVoiceSessionStarted).toHaveBeenCalledWith("session-123");
142
+ });
143
+
144
+ test("records the close reason and a completed outcome on a clean end", async () => {
145
+ const session = readySession();
146
+ await session.start();
147
+
148
+ await session.close("client_end");
149
+
150
+ expect(recordLiveVoiceSessionEnded).toHaveBeenCalledWith({
151
+ sessionId: "session-123",
152
+ screen: "ended_client_end",
153
+ outcome: "completed",
154
+ });
155
+ });
156
+
157
+ test("a dropped socket ends as completed, not failed", async () => {
158
+ const session = readySession();
159
+ await session.start();
160
+
161
+ await session.close("websocket_close");
162
+
163
+ // The session ran and then stopped; only an error makes it `failed`, and
164
+ // the reason on `screen` is what distinguishes a drop from a hangup.
165
+ expect(recordLiveVoiceSessionEnded).toHaveBeenCalledWith({
166
+ sessionId: "session-123",
167
+ screen: "ended_websocket_close",
168
+ outcome: "completed",
169
+ });
170
+ });
171
+
172
+ test("a failed session records an ended row carrying the failure code", async () => {
173
+ const session = new LiveVoiceSession(createContext(), {
174
+ resolveTranscriber: mock(async () => new MockStreamingTranscriber()),
175
+ resolveCredentialReadiness: mock(async () => NOT_READY),
176
+ });
177
+ await expect(session.start()).rejects.toBeInstanceOf(
178
+ LiveVoiceSessionStartupError,
179
+ );
180
+
181
+ // What the manager does after a startup failure.
182
+ await session.close("error");
183
+
184
+ // A failed session suppresses the client-facing `metrics` frame; the end
185
+ // row must not be suppressed with it.
186
+ expect(recordLiveVoiceSessionEnded).toHaveBeenCalledWith({
187
+ sessionId: "session-123",
188
+ screen: "ended_error:credentials_unavailable",
189
+ outcome: "failed",
190
+ });
191
+ });
192
+
193
+ test("records exactly one ended row when close is called twice", async () => {
194
+ const session = readySession();
195
+ await session.start();
196
+
197
+ await session.close("client_end");
198
+ await session.close("websocket_close");
199
+
200
+ expect(recordLiveVoiceSessionEnded).toHaveBeenCalledTimes(1);
201
+ });
202
+ });
203
+
204
+ describe("live-voice turn attribution", () => {
205
+ /** Drives one push-to-talk turn and returns the options the bridge saw. */
206
+ async function captureTurnOptions(
207
+ startFrame: LiveVoiceClientStartFrame,
208
+ ): Promise<VoiceTurnOptions> {
209
+ let captured: VoiceTurnOptions | undefined;
210
+ const startVoiceTurn = mock(async (options: VoiceTurnOptions) => {
211
+ captured = options;
212
+ return { turnId: "bridge-turn-1", abort: mock() };
213
+ });
214
+ const session = new LiveVoiceSession(createContext(startFrame), {
215
+ resolveTranscriber: mock(
216
+ async () =>
217
+ new MockStreamingTranscriber([
218
+ { type: "final", text: "hello" },
219
+ { type: "closed" },
220
+ ]),
221
+ ),
222
+ startVoiceTurn,
223
+ emitMetrics: false,
224
+ });
225
+
226
+ await session.start();
227
+ await session.handleClientFrame({ type: "ptt_release" });
228
+ for (let attempt = 0; attempt < 40 && !captured; attempt += 1) {
229
+ await new Promise((resolve) => setTimeout(resolve, 5));
230
+ }
231
+ await session.close("client_end");
232
+
233
+ if (!captured) {
234
+ throw new Error("the live-voice session never started a bridge turn");
235
+ }
236
+ return captured;
237
+ }
238
+
239
+ test("carries the session id and originating client onto the turn", async () => {
240
+ const options = await captureTurnOptions({ ...START_FRAME, client: "ios" });
241
+
242
+ expect(options.voiceTelemetry).toEqual({
243
+ sessionId: "session-123",
244
+ client: "ios",
245
+ });
246
+ });
247
+
248
+ test("leaves the turn's interface id alone while attributing the client", async () => {
249
+ const options = await captureTurnOptions({ ...START_FRAME, client: "ios" });
250
+
251
+ // The load-bearing half of the attribution fix. `userMessageInterface`
252
+ // resolves the turn's channel capabilities, where `macos` is what grants a
253
+ // live-voice turn its dynamic surfaces; reporting the true client here
254
+ // would strip them from every iOS session. Attribution rides
255
+ // `voiceTelemetry` precisely so this can stay put.
256
+ expect(options.userMessageInterface).toBe("macos");
257
+ expect(options.assistantMessageInterface).toBe("macos");
258
+ });
259
+
260
+ test("omits the client when the start frame declares none", async () => {
261
+ const options = await captureTurnOptions(START_FRAME);
262
+
263
+ expect(options.voiceTelemetry).toEqual({ sessionId: "session-123" });
264
+ });
265
+ });
@@ -12,6 +12,7 @@ import {
12
12
  type LiveVoiceStreamingTranscriberResolver,
13
13
  } from "../live-voice-session.js";
14
14
  import type { LiveVoiceSessionFactoryContext } from "../live-voice-session-manager.js";
15
+ import type { LiveVoiceTtsOptions } from "../live-voice-tts.js";
15
16
  import {
16
17
  createLiveVoiceServerFrameSequencer,
17
18
  type LiveVoiceClientStartFrame,
@@ -29,7 +30,9 @@ const START_FRAME = {
29
30
  } as const satisfies LiveVoiceClientStartFrame;
30
31
 
31
32
  class MockStreamingTranscriber implements StreamingTranscriber {
32
- readonly providerId = "deepgram" as const;
33
+ // Configurable: the session gates the language-pin fallback on the DIALED
34
+ // transcriber's providerId (see turnLanguageFor).
35
+ readonly providerId: StreamingTranscriber["providerId"];
33
36
  readonly boundaryId = "daemon-streaming" as const;
34
37
  readonly audioChunks: Buffer[] = [];
35
38
  readonly mimeTypes: string[] = [];
@@ -37,6 +40,10 @@ class MockStreamingTranscriber implements StreamingTranscriber {
37
40
  stopped = false;
38
41
  private onEvent: ((event: SttStreamServerEvent) => void) | null = null;
39
42
 
43
+ constructor(providerId: StreamingTranscriber["providerId"] = "deepgram") {
44
+ this.providerId = providerId;
45
+ }
46
+
40
47
  async start(onEvent: (event: SttStreamServerEvent) => void): Promise<void> {
41
48
  this.started = true;
42
49
  this.onEvent = onEvent;
@@ -135,6 +142,39 @@ function completingVoiceTurnStarter() {
135
142
  });
136
143
  }
137
144
 
145
+ // A starter whose leg streams one spoken sentence so the turn reaches TTS.
146
+ function speakingVoiceTurnStarter(reply = "Okay.") {
147
+ return mock(async (options: VoiceTurnOptions) => {
148
+ options.callbacks?.assistant_text_delta?.({
149
+ type: "assistant_text_delta",
150
+ text: reply,
151
+ conversationId: options.conversationId,
152
+ });
153
+ options.callbacks?.message_complete?.({
154
+ type: "message_complete",
155
+ conversationId: options.conversationId,
156
+ messageId: "assistant-message-123",
157
+ });
158
+ return { turnId: "bridge-turn-1", abort: mock() };
159
+ });
160
+ }
161
+
162
+ // Records the exact options object of every TTS synthesis request.
163
+ function recordingTtsStreamer() {
164
+ const ttsCalls: LiveVoiceTtsOptions[] = [];
165
+ const streamTtsAudio = mock(async (options: LiveVoiceTtsOptions) => {
166
+ ttsCalls.push(options);
167
+ return {
168
+ provider: "fish-audio" as const,
169
+ contentType: "audio/pcm",
170
+ sampleRate: 24_000,
171
+ chunks: 0,
172
+ bytes: 0,
173
+ };
174
+ });
175
+ return { streamTtsAudio, ttsCalls };
176
+ }
177
+
138
178
  function createContext(overrides: Partial<LiveVoiceClientStartFrame> = {}): {
139
179
  context: LiveVoiceSessionFactoryContext;
140
180
  frames: LiveVoiceServerFrame[];
@@ -203,7 +243,12 @@ describe("LiveVoiceSession STT", () => {
203
243
 
204
244
  await session.start();
205
245
 
206
- expect(resolver).toHaveBeenCalledWith({ sampleRate: 24_000 });
246
+ // The config default is "multi", so the session carries a language into
247
+ // the resolver rather than leaving it to be inferred downstream.
248
+ expect(resolver).toHaveBeenCalledWith({
249
+ sampleRate: 24_000,
250
+ language: "multi",
251
+ });
207
252
  expect(transcriber.started).toBe(true);
208
253
  expect(frames).toEqual([
209
254
  {
@@ -216,6 +261,20 @@ describe("LiveVoiceSession STT", () => {
216
261
  ]);
217
262
  });
218
263
 
264
+ test("ignores model-integrated turn-detection events", async () => {
265
+ const { frames, session, transcriber } = createSessionWithTranscriber();
266
+
267
+ await session.start();
268
+ transcriber.emit({ type: "turn-start" });
269
+ transcriber.emit({ type: "eager-turn-end", text: "eager" });
270
+ transcriber.emit({ type: "turn-resumed" });
271
+ transcriber.emit({ type: "turn-end", text: "committed", confidence: 0.9 });
272
+ await new Promise((resolve) => setTimeout(resolve, 10));
273
+
274
+ expect(frames.map((frame) => frame.type)).toEqual(["ready"]);
275
+ expect(session.finalTranscriptText).toBe("");
276
+ });
277
+
219
278
  test("marks transient transcriber errors recoverable while the session continues", async () => {
220
279
  const { frames, session, transcriber } = createSessionWithTranscriber();
221
280
 
@@ -1332,6 +1391,269 @@ describe("LiveVoiceSession STT", () => {
1332
1391
  ]);
1333
1392
  });
1334
1393
 
1394
+ test("finals tagged with a detected language carry it to TTS and the control prompt", async () => {
1395
+ const transcriber = new MockStreamingTranscriber();
1396
+ const startVoiceTurn = speakingVoiceTurnStarter();
1397
+ const { streamTtsAudio, ttsCalls } = recordingTtsStreamer();
1398
+ const { context } = createContext();
1399
+ const session = new LiveVoiceSession(context, {
1400
+ resolveTranscriber: mock(async () => transcriber),
1401
+ startVoiceTurn,
1402
+ streamTtsAudio,
1403
+ });
1404
+
1405
+ await session.start();
1406
+ await session.handleBinaryAudio(new Uint8Array([1]));
1407
+ transcriber.emit({ type: "final", text: "नमस्ते", languages: ["hi"] });
1408
+ await session.handleClientFrame({ type: "ptt_release" });
1409
+ await waitFor(() => ttsCalls.length >= 1);
1410
+
1411
+ expect(ttsCalls[0]?.language).toBe("hi");
1412
+ expect(startVoiceTurn.mock.calls[0]?.[0]?.voiceControlPrompt).toContain(
1413
+ 'speaking the language with code "hi"',
1414
+ );
1415
+ });
1416
+
1417
+ test("a tagged partial supplies the turn language when finals carry no tags", async () => {
1418
+ // Speculative turns dispatch from partials before the first tagged
1419
+ // final, so the latest tagged partial must resolve the language.
1420
+ const transcriber = new MockStreamingTranscriber();
1421
+ const startVoiceTurn = speakingVoiceTurnStarter();
1422
+ const { streamTtsAudio, ttsCalls } = recordingTtsStreamer();
1423
+ const { context } = createContext();
1424
+ const session = new LiveVoiceSession(context, {
1425
+ resolveTranscriber: mock(async () => transcriber),
1426
+ startVoiceTurn,
1427
+ streamTtsAudio,
1428
+ });
1429
+
1430
+ await session.start();
1431
+ await session.handleBinaryAudio(new Uint8Array([1]));
1432
+ transcriber.emit({ type: "partial", text: "hola", languages: ["es"] });
1433
+ transcriber.emit({ type: "final", text: "hola amigo" });
1434
+ await session.handleClientFrame({ type: "ptt_release" });
1435
+ await waitFor(() => ttsCalls.length >= 1);
1436
+
1437
+ expect(ttsCalls[0]?.language).toBe("es");
1438
+ });
1439
+
1440
+ test("empty finals carrying language tags do not vote", async () => {
1441
+ // Silence frames can carry container-level tags describing no emitted
1442
+ // words; they must not outvote the language of real speech.
1443
+ const transcriber = new MockStreamingTranscriber();
1444
+ const startVoiceTurn = speakingVoiceTurnStarter();
1445
+ const { streamTtsAudio, ttsCalls } = recordingTtsStreamer();
1446
+ const { context } = createContext();
1447
+ const session = new LiveVoiceSession(context, {
1448
+ resolveTranscriber: mock(async () => transcriber),
1449
+ startVoiceTurn,
1450
+ streamTtsAudio,
1451
+ });
1452
+
1453
+ await session.start();
1454
+ await session.handleBinaryAudio(new Uint8Array([1]));
1455
+ transcriber.emit({ type: "final", text: "", languages: ["es"] });
1456
+ transcriber.emit({ type: "final", text: "", languages: ["es"] });
1457
+ transcriber.emit({ type: "final", text: "hello there", languages: ["en"] });
1458
+ await session.handleClientFrame({ type: "ptt_release" });
1459
+ await waitFor(() => ttsCalls.length >= 1);
1460
+
1461
+ expect(ttsCalls[0]?.language).toBe("en");
1462
+ });
1463
+
1464
+ test("only each final's dominant language votes", async () => {
1465
+ // `languages` is dominance-ranked per event: a Spanish-dominant segment
1466
+ // tagged ["es", "en"] plus a short English segment tagged ["en"] must
1467
+ // resolve to Spanish (one vote each, tie broken by first appearance),
1468
+ // not English by counting the secondary tag as a full vote.
1469
+ const transcriber = new MockStreamingTranscriber();
1470
+ const startVoiceTurn = speakingVoiceTurnStarter();
1471
+ const { streamTtsAudio, ttsCalls } = recordingTtsStreamer();
1472
+ const { context } = createContext();
1473
+ const session = new LiveVoiceSession(context, {
1474
+ resolveTranscriber: mock(async () => transcriber),
1475
+ startVoiceTurn,
1476
+ streamTtsAudio,
1477
+ });
1478
+
1479
+ await session.start();
1480
+ await session.handleBinaryAudio(new Uint8Array([1]));
1481
+ transcriber.emit({
1482
+ type: "final",
1483
+ text: "hola amigo como estas hoy",
1484
+ languages: ["es", "en"],
1485
+ });
1486
+ transcriber.emit({ type: "final", text: "ok", languages: ["en"] });
1487
+ await session.handleClientFrame({ type: "ptt_release" });
1488
+ await waitFor(() => ttsCalls.length >= 1);
1489
+
1490
+ expect(ttsCalls[0]?.language).toBe("es");
1491
+ });
1492
+
1493
+ test("a tagged final outranks an earlier tagged partial", async () => {
1494
+ const transcriber = new MockStreamingTranscriber();
1495
+ const startVoiceTurn = speakingVoiceTurnStarter();
1496
+ const { streamTtsAudio, ttsCalls } = recordingTtsStreamer();
1497
+ const { context } = createContext();
1498
+ const session = new LiveVoiceSession(context, {
1499
+ resolveTranscriber: mock(async () => transcriber),
1500
+ startVoiceTurn,
1501
+ streamTtsAudio,
1502
+ });
1503
+
1504
+ await session.start();
1505
+ await session.handleBinaryAudio(new Uint8Array([1]));
1506
+ transcriber.emit({ type: "partial", text: "hola", languages: ["es"] });
1507
+ transcriber.emit({ type: "final", text: "नमस्ते", languages: ["hi"] });
1508
+ await session.handleClientFrame({ type: "ptt_release" });
1509
+ await waitFor(() => ttsCalls.length >= 1);
1510
+
1511
+ expect(ttsCalls[0]?.language).toBe("hi");
1512
+ });
1513
+
1514
+ test("a monolingual services.stt.language pin is the turn language when finals carry no tags", async () => {
1515
+ const originalRaw = loadRawConfig();
1516
+ const rawServices = (originalRaw.services ?? {}) as Record<string, unknown>;
1517
+ saveRawConfig({
1518
+ ...originalRaw,
1519
+ services: {
1520
+ ...rawServices,
1521
+ stt: {
1522
+ ...((rawServices.stt ?? {}) as Record<string, unknown>),
1523
+ language: "ja",
1524
+ },
1525
+ },
1526
+ });
1527
+ const transcriber = new MockStreamingTranscriber();
1528
+ const startVoiceTurn = speakingVoiceTurnStarter();
1529
+ const { streamTtsAudio, ttsCalls } = recordingTtsStreamer();
1530
+ const { context } = createContext();
1531
+ const session = new LiveVoiceSession(context, {
1532
+ resolveTranscriber: mock(async () => transcriber),
1533
+ startVoiceTurn,
1534
+ streamTtsAudio,
1535
+ });
1536
+
1537
+ try {
1538
+ await session.start();
1539
+ await session.handleBinaryAudio(new Uint8Array([1]));
1540
+ await session.handleClientFrame({ type: "ptt_release" });
1541
+ await waitFor(() => ttsCalls.length >= 1);
1542
+
1543
+ expect(ttsCalls[0]?.language).toBe("ja");
1544
+ expect(startVoiceTurn.mock.calls[0]?.[0]?.voiceControlPrompt).toContain(
1545
+ 'speaking the language with code "ja"',
1546
+ );
1547
+ } finally {
1548
+ await session.close("websocket_close");
1549
+ saveRawConfig(originalRaw);
1550
+ }
1551
+ });
1552
+
1553
+ test("a pin on an auto-detecting provider does not become the turn language", async () => {
1554
+ // google-gemini and openai-whisper ignore services.stt.language, so a
1555
+ // stale persisted pin must not force every turn into that language.
1556
+ const originalRaw = loadRawConfig();
1557
+ const rawServices = (originalRaw.services ?? {}) as Record<string, unknown>;
1558
+ saveRawConfig({
1559
+ ...originalRaw,
1560
+ services: {
1561
+ ...rawServices,
1562
+ stt: {
1563
+ ...((rawServices.stt ?? {}) as Record<string, unknown>),
1564
+ provider: "google-gemini",
1565
+ language: "es",
1566
+ },
1567
+ },
1568
+ });
1569
+ const transcriber = new MockStreamingTranscriber("google-gemini");
1570
+ const startVoiceTurn = speakingVoiceTurnStarter();
1571
+ const { streamTtsAudio, ttsCalls } = recordingTtsStreamer();
1572
+ const { context } = createContext();
1573
+ const session = new LiveVoiceSession(context, {
1574
+ resolveTranscriber: mock(async () => transcriber),
1575
+ startVoiceTurn,
1576
+ streamTtsAudio,
1577
+ });
1578
+
1579
+ try {
1580
+ await session.start();
1581
+ await session.handleBinaryAudio(new Uint8Array([1]));
1582
+ await session.handleClientFrame({ type: "ptt_release" });
1583
+ await waitFor(() => ttsCalls.length >= 1);
1584
+
1585
+ expect(ttsCalls[0] && "language" in ttsCalls[0]).toBe(false);
1586
+ } finally {
1587
+ await session.close("websocket_close");
1588
+ saveRawConfig(originalRaw);
1589
+ }
1590
+ });
1591
+
1592
+ test("the pin gate follows the dialed provider, not the configured one", async () => {
1593
+ // A BYOK gemini config without credentials silently dials the managed
1594
+ // vellum transcriber, which DOES honor the pin; the gate must follow
1595
+ // what was dialed, not what was configured.
1596
+ const originalRaw = loadRawConfig();
1597
+ const rawServices = (originalRaw.services ?? {}) as Record<string, unknown>;
1598
+ saveRawConfig({
1599
+ ...originalRaw,
1600
+ services: {
1601
+ ...rawServices,
1602
+ stt: {
1603
+ ...((rawServices.stt ?? {}) as Record<string, unknown>),
1604
+ provider: "google-gemini",
1605
+ language: "es",
1606
+ },
1607
+ },
1608
+ });
1609
+ const transcriber = new MockStreamingTranscriber("vellum");
1610
+ const startVoiceTurn = speakingVoiceTurnStarter();
1611
+ const { streamTtsAudio, ttsCalls } = recordingTtsStreamer();
1612
+ const { context } = createContext();
1613
+ const session = new LiveVoiceSession(context, {
1614
+ resolveTranscriber: mock(async () => transcriber),
1615
+ startVoiceTurn,
1616
+ streamTtsAudio,
1617
+ });
1618
+
1619
+ try {
1620
+ await session.start();
1621
+ await session.handleBinaryAudio(new Uint8Array([1]));
1622
+ await session.handleClientFrame({ type: "ptt_release" });
1623
+ await waitFor(() => ttsCalls.length >= 1);
1624
+
1625
+ expect(ttsCalls[0]?.language).toBe("es");
1626
+ } finally {
1627
+ await session.close("websocket_close");
1628
+ saveRawConfig(originalRaw);
1629
+ }
1630
+ });
1631
+
1632
+ test('the default "multi" with tag-less finals leaves every language-aware path untouched', async () => {
1633
+ const transcriber = new MockStreamingTranscriber();
1634
+ const startVoiceTurn = speakingVoiceTurnStarter();
1635
+ const { streamTtsAudio, ttsCalls } = recordingTtsStreamer();
1636
+ const { context } = createContext();
1637
+ const session = new LiveVoiceSession(context, {
1638
+ resolveTranscriber: mock(async () => transcriber),
1639
+ startVoiceTurn,
1640
+ streamTtsAudio,
1641
+ });
1642
+
1643
+ await session.start();
1644
+ await session.handleBinaryAudio(new Uint8Array([1]));
1645
+ await session.handleClientFrame({ type: "ptt_release" });
1646
+ await waitFor(() => ttsCalls.length >= 1);
1647
+
1648
+ // With the language unknown the TTS request carries no language key at
1649
+ // all and the control prompt has no per-turn language note: byte-for-byte
1650
+ // the language-blind behavior.
1651
+ expect("language" in ttsCalls[0]!).toBe(false);
1652
+ expect(startVoiceTurn.mock.calls[0]?.[0]?.voiceControlPrompt).not.toContain(
1653
+ "language with code",
1654
+ );
1655
+ });
1656
+
1335
1657
  test("uses the production streaming transcriber resolver by default", () => {
1336
1658
  const source = readFileSync(
1337
1659
  new URL("../live-voice-session.ts", import.meta.url),