@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
@@ -5,6 +5,7 @@ import type {
5
5
  VoiceTurnCallbacks,
6
6
  VoiceTurnOptions,
7
7
  } from "../../calls/voice-session-bridge.js";
8
+ import { loadRawConfig, saveRawConfig } from "../../config/loader.js";
8
9
  import type {
9
10
  LiveVoiceFrontModelConfig,
10
11
  LiveVoiceProgressConfig,
@@ -13,10 +14,6 @@ import type {
13
14
  StreamingTranscriber,
14
15
  SttStreamServerEvent,
15
16
  } from "../../stt/types.js";
16
- import type {
17
- VoiceFrontDecider,
18
- VoiceProgressTextInput,
19
- } from "../front-decision.js";
20
17
  import {
21
18
  LiveVoiceSession,
22
19
  type LiveVoiceTtsStreamer,
@@ -24,9 +21,14 @@ import {
24
21
  } from "../live-voice-session.js";
25
22
  import type { LiveVoiceSessionFactoryContext } from "../live-voice-session-manager.js";
26
23
  import type { LiveVoiceTtsOptions } from "../live-voice-tts.js";
24
+ import type {
25
+ VoiceProgressNarrator,
26
+ VoiceProgressTextInput,
27
+ } from "../progress-narration.js";
27
28
  import {
28
29
  pickProgressPhrase,
29
30
  PROGRESS_FALLBACK_PHRASES,
31
+ PROGRESS_FALLBACK_PHRASES_BY_LANGUAGE,
30
32
  } from "../progress-phrases.js";
31
33
  import {
32
34
  createLiveVoiceServerFrameSequencer,
@@ -44,16 +46,7 @@ const START_FRAME = {
44
46
  },
45
47
  } as const satisfies LiveVoiceClientStartFrame;
46
48
 
47
- // Generous enough that the slow-first-delta ack never fires during a test;
48
- // the immediate tool-start ack still speaks and is asserted where relevant.
49
- const GENEROUS_ACK_TIMEOUT_MS = 60_000;
50
-
51
49
  const GENERATED_NARRATION = "Progress text.";
52
- // Acks are front-model-phrased; narrations fall back to a static phrase.
53
- // Both pass through the same TTS sanitizer, and each fresh session's
54
- // narration phrase counter starts at 0.
55
- const GENERATED_TOOL_ACK = "Let me look that up.";
56
- const EXPECTED_TOOL_ACK = sanitizeForTts(GENERATED_TOOL_ACK).trim();
57
50
  const EXPECTED_PROGRESS_FALLBACK = sanitizeForTts(pickProgressPhrase(0)).trim();
58
51
 
59
52
  class MockStreamingTranscriber implements StreamingTranscriber {
@@ -61,6 +54,13 @@ class MockStreamingTranscriber implements StreamingTranscriber {
61
54
  readonly boundaryId = "daemon-streaming" as const;
62
55
  private onEvent: ((event: SttStreamServerEvent) => void) | null = null;
63
56
 
57
+ constructor(
58
+ private readonly stopEvents: SttStreamServerEvent[] = [
59
+ { type: "final", text: "hello" },
60
+ { type: "closed" },
61
+ ],
62
+ ) {}
63
+
64
64
  async start(onEvent: (event: SttStreamServerEvent) => void): Promise<void> {
65
65
  this.onEvent = onEvent;
66
66
  }
@@ -68,8 +68,9 @@ class MockStreamingTranscriber implements StreamingTranscriber {
68
68
  sendAudio(): void {}
69
69
 
70
70
  stop(): void {
71
- this.onEvent?.({ type: "final", text: "hello" });
72
- this.onEvent?.({ type: "closed" });
71
+ for (const event of this.stopEvents) {
72
+ this.onEvent?.(event);
73
+ }
73
74
  }
74
75
  }
75
76
 
@@ -113,10 +114,13 @@ function createRecordingTtsStreamer(
113
114
  ): {
114
115
  streamTtsAudio: LiveVoiceTtsStreamer;
115
116
  ttsTexts: string[];
117
+ ttsCalls: LiveVoiceTtsOptions[];
116
118
  } {
117
119
  const ttsTexts: string[] = [];
120
+ const ttsCalls: LiveVoiceTtsOptions[] = [];
118
121
  const streamTtsAudio = mock(async (options: LiveVoiceTtsOptions) => {
119
122
  ttsTexts.push(options.text);
123
+ ttsCalls.push(options);
120
124
  await gateTtsText?.(options.text);
121
125
  return {
122
126
  provider: "fish-audio" as const,
@@ -126,14 +130,13 @@ function createRecordingTtsStreamer(
126
130
  bytes: Buffer.byteLength(options.text),
127
131
  };
128
132
  });
129
- return { streamTtsAudio, ttsTexts };
133
+ return { streamTtsAudio, ttsTexts, ttsCalls };
130
134
  }
131
135
 
132
- function makeProgressDecider(
133
- generateProgressText: VoiceFrontDecider["generateProgressText"],
134
- ): VoiceFrontDecider {
136
+ function makeProgressNarrator(
137
+ generateProgressText: VoiceProgressNarrator["generateProgressText"],
138
+ ): VoiceProgressNarrator {
135
139
  return {
136
- generateAckText: async () => GENERATED_TOOL_ACK,
137
140
  generateProgressText,
138
141
  };
139
142
  }
@@ -143,7 +146,6 @@ function progressConfig(
143
146
  ): Partial<LiveVoiceFrontModelConfig> {
144
147
  const idleIntervalMs = overrides.idleIntervalMs ?? 60_000;
145
148
  return {
146
- ackFirstDeltaTimeoutMs: GENEROUS_ACK_TIMEOUT_MS,
147
149
  progress: {
148
150
  enabled: true,
149
151
  opsThreshold: 3,
@@ -162,26 +164,31 @@ function progressConfig(
162
164
 
163
165
  function createProgressHarness(options: {
164
166
  frontModelConfig: Partial<LiveVoiceFrontModelConfig>;
165
- frontDecider: VoiceFrontDecider;
167
+ progressNarrator: VoiceProgressNarrator;
166
168
  emitMetrics?: boolean;
167
169
  gateTtsText?: (text: string) => Promise<void> | null;
170
+ // Events the transcriber flushes at utterance release; defaults to a plain
171
+ // untagged "hello" final.
172
+ sttStopEvents?: SttStreamServerEvent[];
168
173
  }) {
169
174
  const { startVoiceTurn, getCallbacks } = createCapturingTurnStarter();
170
- const { streamTtsAudio, ttsTexts } = createRecordingTtsStreamer(
175
+ const { streamTtsAudio, ttsTexts, ttsCalls } = createRecordingTtsStreamer(
171
176
  options.gateTtsText,
172
177
  );
173
178
  const { context, frames } = createContext();
174
179
  const session = new LiveVoiceSession(context, {
175
- resolveTranscriber: mock(async () => new MockStreamingTranscriber()),
180
+ resolveTranscriber: mock(
181
+ async () => new MockStreamingTranscriber(options.sttStopEvents),
182
+ ),
176
183
  startVoiceTurn,
177
184
  streamTtsAudio,
178
185
  frontModelConfig: options.frontModelConfig,
179
- frontDecider: options.frontDecider,
186
+ progressNarrator: options.progressNarrator,
180
187
  createTurnId: () => "live-turn-1",
181
188
  emitMetrics: options.emitMetrics ?? false,
182
189
  });
183
190
 
184
- return { frames, session, getCallbacks, ttsTexts };
191
+ return { frames, session, getCallbacks, ttsTexts, ttsCalls };
185
192
  }
186
193
 
187
194
  async function startReleasedTurn(
@@ -263,7 +270,7 @@ describe("LiveVoiceSession progress narration", () => {
263
270
  );
264
271
  const { session, getCallbacks, ttsTexts } = createProgressHarness({
265
272
  frontModelConfig: progressConfig({ idleIntervalMs: 40 }),
266
- frontDecider: makeProgressDecider(generateProgressText),
273
+ progressNarrator: makeProgressNarrator(generateProgressText),
267
274
  });
268
275
  await startReleasedTurn(session, getCallbacks);
269
276
 
@@ -289,22 +296,18 @@ describe("LiveVoiceSession progress narration", () => {
289
296
  });
290
297
  const { session, getCallbacks, ttsTexts } = createProgressHarness({
291
298
  frontModelConfig: progressConfig({ opsThreshold: 3, minGapMs: 10 }),
292
- frontDecider: makeProgressDecider(generateProgressText),
299
+ progressNarrator: makeProgressNarrator(generateProgressText),
293
300
  });
294
301
 
295
302
  await startReleasedTurn(session, getCallbacks);
296
303
  emitToolStart(getCallbacks, "web_search", "tool-1");
297
- await waitFor(() => ttsTexts.length === 1);
298
- expect(ttsTexts).toEqual([EXPECTED_TOOL_ACK]);
299
304
 
300
305
  emitToolResult(getCallbacks, "web_search", "tool-1", "found 3 results");
301
306
  emitToolStart(getCallbacks, "file_read", "tool-2");
302
307
  emitToolResult(getCallbacks, "file_read", "tool-2", "file contents");
303
- // Let the ack's minGapMs spacing elapse before the threshold-crossing op.
304
- await sleep(30);
305
308
  emitToolStart(getCallbacks, "web_search", "tool-3");
306
- await waitFor(() => ttsTexts.length === 2);
307
- expect(ttsTexts).toEqual([EXPECTED_TOOL_ACK, GENERATED_NARRATION]);
309
+ await waitFor(() => ttsTexts.length === 1);
310
+ expect(ttsTexts).toEqual([GENERATED_NARRATION]);
308
311
  expect(generateProgressText).toHaveBeenCalledTimes(1);
309
312
  expect(inputs[0]).toMatchObject({
310
313
  transcriptSoFar: "hello",
@@ -320,7 +323,7 @@ describe("LiveVoiceSession progress narration", () => {
320
323
  // below the threshold: still exactly one narration.
321
324
  emitToolResult(getCallbacks, "web_search", "tool-3");
322
325
  await sleep(30);
323
- expect(ttsTexts).toEqual([EXPECTED_TOOL_ACK, GENERATED_NARRATION]);
326
+ expect(ttsTexts).toEqual([GENERATED_NARRATION]);
324
327
  expect(generateProgressText).toHaveBeenCalledTimes(1);
325
328
 
326
329
  emitMessageComplete(getCallbacks);
@@ -336,12 +339,11 @@ describe("LiveVoiceSession progress narration", () => {
336
339
  });
337
340
  const { session, getCallbacks, ttsTexts } = createProgressHarness({
338
341
  frontModelConfig: progressConfig({ opsThreshold: 3, minGapMs: 10 }),
339
- frontDecider: makeProgressDecider(generateProgressText),
342
+ progressNarrator: makeProgressNarrator(generateProgressText),
340
343
  });
341
344
 
342
345
  await startReleasedTurn(session, getCallbacks);
343
346
  emitToolStart(getCallbacks, "web_search", "tool-1");
344
- await waitFor(() => ttsTexts.length === 1);
345
347
 
346
348
  // Name-only result: no toolUseId to correlate by.
347
349
  getCallbacks()?.tool_result?.({
@@ -349,11 +351,9 @@ describe("LiveVoiceSession progress narration", () => {
349
351
  resultPreview: "found it",
350
352
  });
351
353
  emitToolStart(getCallbacks, "file_read", "tool-2");
352
- // Let the ack's minGapMs spacing elapse before the threshold-crossing op.
353
- await sleep(30);
354
354
  emitToolStart(getCallbacks, "bash", "tool-3");
355
- await waitFor(() => ttsTexts.length === 2);
356
- expect(ttsTexts).toEqual([EXPECTED_TOOL_ACK, GENERATED_NARRATION]);
355
+ await waitFor(() => ttsTexts.length === 1);
356
+ expect(ttsTexts).toEqual([GENERATED_NARRATION]);
357
357
 
358
358
  // The name fallback completed the web_search op: it reports as completed
359
359
  // (with its preview) rather than lingering incomplete.
@@ -365,7 +365,7 @@ describe("LiveVoiceSession progress narration", () => {
365
365
  emitMessageComplete(getCallbacks);
366
366
  });
367
367
 
368
- test("completedOps reach the decider in completion order, not start order", async () => {
368
+ test("completedOps reach the narrator in completion order, not start order", async () => {
369
369
  const inputs: VoiceProgressTextInput[] = [];
370
370
  const generateProgressText = mock(async (input: VoiceProgressTextInput) => {
371
371
  inputs.push(input);
@@ -373,21 +373,20 @@ describe("LiveVoiceSession progress narration", () => {
373
373
  });
374
374
  const { session, getCallbacks, ttsTexts } = createProgressHarness({
375
375
  frontModelConfig: progressConfig({ opsThreshold: 3, minGapMs: 10 }),
376
- frontDecider: makeProgressDecider(generateProgressText),
376
+ progressNarrator: makeProgressNarrator(generateProgressText),
377
377
  });
378
378
 
379
379
  await startReleasedTurn(session, getCallbacks);
380
380
  // Two parallel tools start in one order…
381
381
  emitToolStart(getCallbacks, "web_search", "tool-1");
382
- await waitFor(() => ttsTexts.length === 1);
383
382
  emitToolStart(getCallbacks, "file_read", "tool-2");
384
- // …and complete in the other. The sleep separates the completion
385
- // timestamps (millisecond clock) and lets the ack's minGapMs elapse.
383
+ // They complete in the other order. The sleep separates their completion
384
+ // timestamps on the millisecond clock.
386
385
  emitToolResult(getCallbacks, "file_read", "tool-2", "file contents");
387
386
  await sleep(30);
388
387
  emitToolResult(getCallbacks, "web_search", "tool-1", "found 3 results");
389
388
  emitToolStart(getCallbacks, "bash", "tool-3");
390
- await waitFor(() => ttsTexts.length === 2);
389
+ await waitFor(() => ttsTexts.length === 1);
391
390
 
392
391
  expect(inputs[0]?.completedOps).toEqual([
393
392
  { toolName: "file_read", resultPreview: "file contents" },
@@ -397,7 +396,7 @@ describe("LiveVoiceSession progress narration", () => {
397
396
  emitMessageComplete(getCallbacks);
398
397
  });
399
398
 
400
- test("idle trigger: dead air narrates with the decider text, audio-only", async () => {
399
+ test("idle trigger: dead air narrates with generated text, audio-only", async () => {
401
400
  const inputs: VoiceProgressTextInput[] = [];
402
401
  const generateProgressText = mock(async (input: VoiceProgressTextInput) => {
403
402
  inputs.push(input);
@@ -408,7 +407,7 @@ describe("LiveVoiceSession progress narration", () => {
408
407
  idleIntervalMs: 40,
409
408
  minGapMs: 60_000,
410
409
  }),
411
- frontDecider: makeProgressDecider(generateProgressText),
410
+ progressNarrator: makeProgressNarrator(generateProgressText),
412
411
  });
413
412
 
414
413
  await startReleasedTurn(session, getCallbacks);
@@ -429,14 +428,14 @@ describe("LiveVoiceSession progress narration", () => {
429
428
  emitMessageComplete(getCallbacks);
430
429
  });
431
430
 
432
- test("idle trigger with a null decider result speaks the static fallback", async () => {
431
+ test("idle trigger with a null narrator result speaks the static fallback", async () => {
433
432
  const generateProgressText = mock(async () => null);
434
433
  const { session, getCallbacks, ttsTexts } = createProgressHarness({
435
434
  frontModelConfig: progressConfig({
436
435
  idleIntervalMs: 40,
437
436
  minGapMs: 60_000,
438
437
  }),
439
- frontDecider: makeProgressDecider(generateProgressText),
438
+ progressNarrator: makeProgressNarrator(generateProgressText),
440
439
  });
441
440
 
442
441
  await startReleasedTurn(session, getCallbacks);
@@ -449,11 +448,17 @@ describe("LiveVoiceSession progress narration", () => {
449
448
 
450
449
  test("no-ops idle fallback stays neutral: no phrase claims tool activity", async () => {
451
450
  // List invariant: the fallback can speak on a slow turn with zero tool
452
- // activity, so no phrase may claim tools or tasks are running.
453
- for (const phrase of PROGRESS_FALLBACK_PHRASES) {
454
- expect(phrase.toLowerCase()).not.toMatch(
455
- /\b(run|runs|running|tool|tools|thing|things|task|tasks|check|checking|look|looking|search|searching)\b/,
456
- );
451
+ // activity, so no phrase may claim tools or tasks are running. Every
452
+ // language's list carries the invariant; the regex names the English
453
+ // activity words, which no list may borrow.
454
+ for (const phrases of Object.values(
455
+ PROGRESS_FALLBACK_PHRASES_BY_LANGUAGE,
456
+ )) {
457
+ for (const phrase of phrases) {
458
+ expect(phrase.toLowerCase()).not.toMatch(
459
+ /\b(run|runs|running|tool|tools|thing|things|task|tasks|check|checking|look|looking|search|searching)\b/,
460
+ );
461
+ }
457
462
  }
458
463
 
459
464
  // Behavior: a slow turn with no tool events falls back to that list.
@@ -463,7 +468,7 @@ describe("LiveVoiceSession progress narration", () => {
463
468
  idleIntervalMs: 40,
464
469
  minGapMs: 60_000,
465
470
  }),
466
- frontDecider: makeProgressDecider(generateProgressText),
471
+ progressNarrator: makeProgressNarrator(generateProgressText),
467
472
  });
468
473
 
469
474
  await startReleasedTurn(session, getCallbacks);
@@ -475,104 +480,135 @@ describe("LiveVoiceSession progress narration", () => {
475
480
  emitMessageComplete(getCallbacks);
476
481
  });
477
482
 
478
- test("ops trigger with a null decider result stays silent and keeps the update budget", async () => {
479
- const generateProgressText = mock(async () => null);
483
+ test("a Hindi turn's idle fallback speaks the Hindi phrase and hints the narrator", async () => {
484
+ const inputs: VoiceProgressTextInput[] = [];
485
+ const generateProgressText = mock(async (input: VoiceProgressTextInput) => {
486
+ inputs.push(input);
487
+ return null;
488
+ });
480
489
  const { session, getCallbacks, ttsTexts } = createProgressHarness({
481
490
  frontModelConfig: progressConfig({
482
- opsThreshold: 1,
483
- idleIntervalMs: 120,
484
- minGapMs: 10,
491
+ idleIntervalMs: 40,
492
+ minGapMs: 60_000,
485
493
  }),
486
- frontDecider: makeProgressDecider(generateProgressText),
494
+ progressNarrator: makeProgressNarrator(generateProgressText),
495
+ sttStopEvents: [
496
+ { type: "final", text: "नमस्ते", languages: ["hi"] },
497
+ { type: "closed" },
498
+ ],
487
499
  });
488
500
 
489
501
  await startReleasedTurn(session, getCallbacks);
490
- emitToolStart(getCallbacks, "web_search", "tool-1");
491
502
  await waitFor(() => ttsTexts.length === 1);
492
- expect(ttsTexts).toEqual([EXPECTED_TOOL_ACK]);
493
-
494
- // Past the ack gap, the ops trigger fires but generation returns null:
495
- // nothing speaks and no update is consumed.
496
- await sleep(30);
497
- emitToolResult(getCallbacks, "web_search", "tool-1");
498
- await waitFor(() => generateProgressText.mock.calls.length === 1);
499
- await sleep(10);
500
- expect(ttsTexts).toEqual([EXPECTED_TOOL_ACK]);
501
503
 
502
- // The idle trigger still has the full budget: its own null falls back to
503
- // the static phrase.
504
- await waitFor(() => ttsTexts.length === 2);
505
- expect(ttsTexts).toEqual([EXPECTED_TOOL_ACK, EXPECTED_PROGRESS_FALLBACK]);
504
+ // The static fallback rotates through the caller's language's list, not
505
+ // the English default, and the narrator was told the language too.
506
+ expect(pickProgressPhrase(0, "hi")).toBe(
507
+ PROGRESS_FALLBACK_PHRASES_BY_LANGUAGE.hi![0]!,
508
+ );
509
+ expect(ttsTexts).toEqual([
510
+ sanitizeForTts(pickProgressPhrase(0, "hi")).trim(),
511
+ ]);
512
+ expect(inputs[0]?.languageHint).toBe("hi");
506
513
 
507
514
  emitMessageComplete(getCallbacks);
508
515
  });
509
516
 
510
- test("minGapMs: a tool-start ack suppresses narration until the gap elapses", async () => {
511
- const generateProgressText = mock(async () => GENERATED_NARRATION);
517
+ test("an out-of-roster pinned turn speaks the English fallback with an 'en' hint while model speech keeps the pin", async () => {
518
+ // "ar" is an accepted monolingual services.stt.language pin but has no
519
+ // entry in the localized phrase tables, so the idle fallback is English
520
+ // text. The filler segment must carry an "en" hint rather than the
521
+ // turn's "ar" (an enforcing TTS provider would otherwise render English
522
+ // words as Arabic). Model speech stays on the turn's language.
523
+ const originalRaw = loadRawConfig();
524
+ const rawServices = (originalRaw.services ?? {}) as Record<string, unknown>;
525
+ saveRawConfig({
526
+ ...originalRaw,
527
+ services: {
528
+ ...rawServices,
529
+ stt: {
530
+ ...((rawServices.stt ?? {}) as Record<string, unknown>),
531
+ provider: "deepgram",
532
+ language: "ar",
533
+ },
534
+ },
535
+ });
536
+ const generateProgressText = mock(async () => null);
537
+ const { session, getCallbacks, ttsTexts, ttsCalls } = createProgressHarness(
538
+ {
539
+ frontModelConfig: progressConfig({
540
+ idleIntervalMs: 40,
541
+ minGapMs: 60_000,
542
+ }),
543
+ progressNarrator: makeProgressNarrator(generateProgressText),
544
+ },
545
+ );
546
+
547
+ try {
548
+ await startReleasedTurn(session, getCallbacks);
549
+ await waitFor(() => ttsTexts.length === 1);
550
+
551
+ expect(ttsTexts[0]).toBe(EXPECTED_PROGRESS_FALLBACK);
552
+ expect(ttsCalls[0]?.language).toBe("en");
553
+
554
+ emitTextDelta(getCallbacks, "Okay.");
555
+ emitMessageComplete(getCallbacks);
556
+ await waitFor(() => ttsCalls.length >= 2);
557
+
558
+ expect(ttsCalls[1]?.text).toBe("Okay.");
559
+ expect(ttsCalls[1]?.language).toBe("ar");
560
+ } finally {
561
+ await session.close("websocket_close");
562
+ saveRawConfig(originalRaw);
563
+ }
564
+ });
565
+
566
+ test("with no detected language the narrator gets no hint and fallback stays English", async () => {
567
+ const inputs: VoiceProgressTextInput[] = [];
568
+ const progressNarrator: VoiceProgressNarrator = {
569
+ generateProgressText: async (input) => {
570
+ inputs.push(input);
571
+ return null;
572
+ },
573
+ };
512
574
  const { session, getCallbacks, ttsTexts } = createProgressHarness({
513
- frontModelConfig: progressConfig({ opsThreshold: 1, minGapMs: 150 }),
514
- frontDecider: makeProgressDecider(generateProgressText),
575
+ frontModelConfig: progressConfig({ idleIntervalMs: 40, minGapMs: 10 }),
576
+ progressNarrator,
515
577
  });
516
578
 
517
579
  await startReleasedTurn(session, getCallbacks);
518
580
  emitToolStart(getCallbacks, "web_search", "tool-1");
519
581
  await waitFor(() => ttsTexts.length === 1);
520
- // Threshold already met, but the just-spoken ack holds the floor.
521
- emitToolResult(getCallbacks, "web_search", "tool-1");
522
- await sleep(30);
523
- expect(ttsTexts).toEqual([EXPECTED_TOOL_ACK]);
524
- expect(generateProgressText).not.toHaveBeenCalled();
525
582
 
526
- await sleep(150);
527
- emitToolStart(getCallbacks, "file_read", "tool-2");
528
- await waitFor(() => ttsTexts.length === 2);
529
- expect(ttsTexts).toEqual([EXPECTED_TOOL_ACK, GENERATED_NARRATION]);
530
- expect(generateProgressText).toHaveBeenCalledTimes(1);
583
+ // Language unknown: the input has no languageHint key and the static
584
+ // fallback comes from the English list.
585
+ expect(inputs[0]).not.toHaveProperty("languageHint");
586
+ expect(ttsTexts).toEqual([EXPECTED_PROGRESS_FALLBACK]);
531
587
 
532
588
  emitMessageComplete(getCallbacks);
533
589
  });
534
590
 
535
- test("no narration generation launches while an ack generation is pending", async () => {
536
- const ackGeneration = deferred<string | null>();
537
- const generateAckText = mock(() => ackGeneration.promise);
538
- const generateProgressText = mock(async () => GENERATED_NARRATION);
539
- const frontDecider: VoiceFrontDecider = {
540
- generateAckText,
541
- generateProgressText,
542
- };
591
+ test("ops trigger with a null narrator result stays silent and keeps the update budget", async () => {
592
+ const generateProgressText = mock(async () => null);
543
593
  const { session, getCallbacks, ttsTexts } = createProgressHarness({
544
- frontModelConfig: progressConfig({ opsThreshold: 1, minGapMs: 150 }),
545
- frontDecider,
594
+ frontModelConfig: progressConfig({
595
+ opsThreshold: 1,
596
+ idleIntervalMs: 120,
597
+ minGapMs: 10,
598
+ }),
599
+ progressNarrator: makeProgressNarrator(generateProgressText),
546
600
  });
547
601
 
548
602
  await startReleasedTurn(session, getCallbacks);
549
- // The tool start speaks a generated ack (generation still pending) and
550
- // simultaneously crosses the ops threshold; the entry guard must stand
551
- // down instead of launching a narration generation whose result the
552
- // post-await re-check would only discard.
553
603
  emitToolStart(getCallbacks, "web_search", "tool-1");
554
- await sleep(30);
555
- expect(generateProgressText).not.toHaveBeenCalled();
604
+ await waitFor(() => generateProgressText.mock.calls.length === 1);
605
+ await sleep(10);
556
606
  expect(ttsTexts).toEqual([]);
557
607
 
558
- ackGeneration.resolve("Generated ack.");
608
+ // The idle trigger still has the full budget: its own null falls back to
609
+ // the static phrase.
559
610
  await waitFor(() => ttsTexts.length === 1);
560
- expect(ttsTexts).toEqual(["Generated ack."]);
561
-
562
- // Inside the ack's minGapMs window nothing else speaks even as tool
563
- // activity continues.
564
- emitToolResult(getCallbacks, "web_search", "tool-1");
565
- await sleep(30);
566
- expect(generateProgressText).not.toHaveBeenCalled();
567
- expect(ttsTexts).toEqual(["Generated ack."]);
568
-
569
- // The stand-down preserved the update budget: once the gap elapses, the
570
- // next ops trigger narrates normally.
571
- await sleep(150);
572
- emitToolStart(getCallbacks, "file_read", "tool-2");
573
- await waitFor(() => ttsTexts.length === 2);
574
- expect(ttsTexts).toEqual(["Generated ack.", GENERATED_NARRATION]);
575
- expect(generateProgressText).toHaveBeenCalledTimes(1);
611
+ expect(ttsTexts).toEqual([EXPECTED_PROGRESS_FALLBACK]);
576
612
 
577
613
  emitMessageComplete(getCallbacks);
578
614
  });
@@ -585,7 +621,7 @@ describe("LiveVoiceSession progress narration", () => {
585
621
  });
586
622
  const { session, getCallbacks, ttsTexts } = createProgressHarness({
587
623
  frontModelConfig: progressConfig({ idleIntervalMs: 30, minGapMs: 1 }),
588
- frontDecider: makeProgressDecider(generateProgressText),
624
+ progressNarrator: makeProgressNarrator(generateProgressText),
589
625
  });
590
626
 
591
627
  await startReleasedTurn(session, getCallbacks);
@@ -619,7 +655,7 @@ describe("LiveVoiceSession progress narration", () => {
619
655
  idleIntervalMs: 40,
620
656
  minGapMs: 10,
621
657
  }),
622
- frontDecider: makeProgressDecider(generateProgressText),
658
+ progressNarrator: makeProgressNarrator(generateProgressText),
623
659
  });
624
660
 
625
661
  await startReleasedTurn(session, getCallbacks);
@@ -651,7 +687,7 @@ describe("LiveVoiceSession progress narration", () => {
651
687
  idleIntervalMs: 40,
652
688
  minGapMs: 60_000,
653
689
  }),
654
- frontDecider: makeProgressDecider(generateProgressText),
690
+ progressNarrator: makeProgressNarrator(generateProgressText),
655
691
  gateTtsText: (text) => (text === "Hello there." ? ttsGate.promise : null),
656
692
  });
657
693
 
@@ -676,7 +712,7 @@ describe("LiveVoiceSession progress narration", () => {
676
712
  const generateProgressText = mock(() => generation.promise);
677
713
  const { session, getCallbacks, ttsTexts } = createProgressHarness({
678
714
  frontModelConfig: progressConfig({ idleIntervalMs: 40 }),
679
- frontDecider: makeProgressDecider(generateProgressText),
715
+ progressNarrator: makeProgressNarrator(generateProgressText),
680
716
  });
681
717
 
682
718
  await startReleasedTurn(session, getCallbacks);
@@ -697,7 +733,7 @@ describe("LiveVoiceSession progress narration", () => {
697
733
  idleIntervalMs: 40,
698
734
  minGapMs: 60_000,
699
735
  }),
700
- frontDecider: makeProgressDecider(generateProgressText),
736
+ progressNarrator: makeProgressNarrator(generateProgressText),
701
737
  emitMetrics: true,
702
738
  });
703
739
 
@@ -722,7 +758,7 @@ describe("LiveVoiceSession progress narration", () => {
722
758
  });
723
759
  });
724
760
 
725
- test("progress.enabled false: zero narrations and zero decider calls", async () => {
761
+ test("progress.enabled false: zero narrations and zero narrator calls", async () => {
726
762
  const generateProgressText = mock(async () => GENERATED_NARRATION);
727
763
  const { session, getCallbacks, ttsTexts } = createProgressHarness({
728
764
  frontModelConfig: progressConfig({
@@ -731,7 +767,7 @@ describe("LiveVoiceSession progress narration", () => {
731
767
  idleIntervalMs: 30,
732
768
  minGapMs: 1,
733
769
  }),
734
- frontDecider: makeProgressDecider(generateProgressText),
770
+ progressNarrator: makeProgressNarrator(generateProgressText),
735
771
  });
736
772
 
737
773
  await startReleasedTurn(session, getCallbacks);
@@ -741,8 +777,7 @@ describe("LiveVoiceSession progress narration", () => {
741
777
  emitToolResult(getCallbacks, "file_read", "tool-2");
742
778
  await sleep(100);
743
779
 
744
- // Only the tool-start ack spoke; the decider was never consulted.
745
- expect(ttsTexts).toEqual([EXPECTED_TOOL_ACK]);
780
+ expect(ttsTexts).toEqual([]);
746
781
  expect(generateProgressText).not.toHaveBeenCalled();
747
782
 
748
783
  emitMessageComplete(getCallbacks);
@@ -755,7 +790,7 @@ describe("LiveVoiceSession progress narration", () => {
755
790
  idleIntervalMs: 20,
756
791
  maxSilenceMs: 150,
757
792
  }),
758
- frontDecider: makeProgressDecider(generateProgressText),
793
+ progressNarrator: makeProgressNarrator(generateProgressText),
759
794
  });
760
795
 
761
796
  await startReleasedTurn(session, getCallbacks);
@@ -782,7 +817,7 @@ describe("LiveVoiceSession progress narration", () => {
782
817
  idleIntervalMs: 20,
783
818
  maxSilenceMs: 600_000,
784
819
  }),
785
- frontDecider: makeProgressDecider(generateProgressText),
820
+ progressNarrator: makeProgressNarrator(generateProgressText),
786
821
  });
787
822
 
788
823
  await startReleasedTurn(session, getCallbacks);
@@ -791,14 +826,14 @@ describe("LiveVoiceSession progress narration", () => {
791
826
 
792
827
  // A tool starting is news: the next tick narrates it.
793
828
  emitToolStart(getCallbacks, "web_search", "tool-1");
794
- await waitFor(() => ttsTexts.length === 2);
795
- expect(ttsTexts).toEqual([EXPECTED_TOOL_ACK, GENERATED_NARRATION]);
829
+ await waitFor(() => ttsTexts.length === 1);
830
+ expect(ttsTexts).toEqual([GENERATED_NARRATION]);
796
831
 
797
832
  // Nothing has happened since, so the ticks that follow stay quiet
798
833
  // instead of repeating the same update on a clock.
799
834
  await sleep(80);
800
835
  expect(generateProgressText).toHaveBeenCalledTimes(1);
801
- expect(ttsTexts).toEqual([EXPECTED_TOOL_ACK, GENERATED_NARRATION]);
836
+ expect(ttsTexts).toEqual([GENERATED_NARRATION]);
802
837
 
803
838
  emitMessageComplete(getCallbacks);
804
839
  });
@@ -817,17 +852,16 @@ describe("LiveVoiceSession progress narration", () => {
817
852
  idleIntervalMs: 60_000,
818
853
  longOpMs: 25,
819
854
  }),
820
- frontDecider: makeProgressDecider(generateProgressText),
855
+ progressNarrator: makeProgressNarrator(generateProgressText),
821
856
  });
822
857
 
823
858
  await startReleasedTurn(session, getCallbacks);
824
859
  emitToolStart(getCallbacks, "web_search", "tool-1");
825
- await waitFor(() => ttsTexts.length === 1);
826
860
  await sleep(40);
827
861
 
828
862
  emitToolResult(getCallbacks, "web_search", "tool-1");
829
- await waitFor(() => ttsTexts.length === 2);
830
- expect(ttsTexts).toEqual([EXPECTED_TOOL_ACK, GENERATED_NARRATION]);
863
+ await waitFor(() => ttsTexts.length === 1);
864
+ expect(ttsTexts).toEqual([GENERATED_NARRATION]);
831
865
  expect(inputs[0]?.completedOps).toEqual([
832
866
  { toolName: "web_search", resultPreview: "ok" },
833
867
  ]);
@@ -843,18 +877,17 @@ describe("LiveVoiceSession progress narration", () => {
843
877
  idleIntervalMs: 60_000,
844
878
  longOpMs: 60_000,
845
879
  }),
846
- frontDecider: makeProgressDecider(generateProgressText),
880
+ progressNarrator: makeProgressNarrator(generateProgressText),
847
881
  });
848
882
 
849
883
  await startReleasedTurn(session, getCallbacks);
850
884
  emitToolStart(getCallbacks, "web_search", "tool-1");
851
- await waitFor(() => ttsTexts.length === 1);
852
885
  emitToolResult(getCallbacks, "web_search", "tool-1");
853
886
  await sleep(60);
854
887
 
855
888
  // Narrating every quick lookup is the chatter the cadence avoids.
856
889
  expect(generateProgressText).not.toHaveBeenCalled();
857
- expect(ttsTexts).toEqual([EXPECTED_TOOL_ACK]);
890
+ expect(ttsTexts).toEqual([]);
858
891
 
859
892
  emitMessageComplete(getCallbacks);
860
893
  });
@@ -874,7 +907,7 @@ describe("LiveVoiceSession progress narration", () => {
874
907
  idleIntervalMs: 20,
875
908
  maxSilenceMs: 600_000,
876
909
  }),
877
- frontDecider: makeProgressDecider(generateProgressText),
910
+ progressNarrator: makeProgressNarrator(generateProgressText),
878
911
  });
879
912
 
880
913
  await startReleasedTurn(session, getCallbacks);