@vellumai/assistant 0.6.2 → 0.6.4

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 (895) hide show
  1. package/ARCHITECTURE.md +273 -10
  2. package/Dockerfile +2 -3
  3. package/bun.lock +41 -49
  4. package/bunfig.toml +3 -0
  5. package/docs/architecture/memory.md +1 -1
  6. package/docs/backup-troubleshooting.md +52 -0
  7. package/docs/browser-use-architecture-phase2.md +174 -0
  8. package/docs/stt-provider-onboarding.md +120 -0
  9. package/knip.json +12 -2
  10. package/node_modules/@vellumai/ces-contracts/bun.lock +8 -6
  11. package/node_modules/@vellumai/ces-contracts/package.json +3 -3
  12. package/node_modules/@vellumai/ces-contracts/src/rpc.ts +42 -0
  13. package/openapi.yaml +1111 -86
  14. package/package.json +40 -42
  15. package/scripts/generate-openapi.ts +0 -2
  16. package/scripts/test.sh +73 -18
  17. package/src/__tests__/acp-session.test.ts +43 -0
  18. package/src/__tests__/agent-image-optimize.test.ts +28 -0
  19. package/src/__tests__/agent-loop.test.ts +123 -0
  20. package/src/__tests__/anthropic-provider.test.ts +263 -10
  21. package/src/__tests__/app-builder-tool-scripts.test.ts +1 -0
  22. package/src/__tests__/app-executors.test.ts +1 -0
  23. package/src/__tests__/app-source-watcher.test.ts +37 -11
  24. package/src/__tests__/approval-routes-http.test.ts +178 -1
  25. package/src/__tests__/auto-analysis-end-to-end.test.ts +550 -0
  26. package/src/__tests__/auto-analysis-prompt.test.ts +50 -0
  27. package/src/__tests__/browser-fill-credential.test.ts +240 -94
  28. package/src/__tests__/browser-manager.test.ts +40 -27
  29. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +2 -2
  30. package/src/__tests__/browser-skill-endstate.test.ts +31 -7
  31. package/src/__tests__/btw-routes.test.ts +7 -0
  32. package/src/__tests__/call-controller.test.ts +581 -20
  33. package/src/__tests__/catalog-files.test.ts +1000 -0
  34. package/src/__tests__/channel-approvals.test.ts +53 -0
  35. package/src/__tests__/channel-invite-transport.test.ts +2 -2
  36. package/src/__tests__/channel-readiness-routes.test.ts +16 -20
  37. package/src/__tests__/channel-readiness-service.test.ts +12 -7
  38. package/src/__tests__/checker.test.ts +157 -10
  39. package/src/__tests__/clawhub-files.test.ts +347 -0
  40. package/src/__tests__/commit-message-enrichment-service.test.ts +36 -19
  41. package/src/__tests__/config-analysis.test.ts +100 -0
  42. package/src/__tests__/config-managed-gemini-defaults.test.ts +326 -0
  43. package/src/__tests__/config-schema-cmd.test.ts +2 -2
  44. package/src/__tests__/config-schema.test.ts +1248 -224
  45. package/src/__tests__/config-watcher-cleanup-throttle.test.ts +339 -0
  46. package/src/__tests__/config-watcher.test.ts +43 -8
  47. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +23 -0
  48. package/src/__tests__/contact-store-user-file.test.ts +512 -0
  49. package/src/__tests__/contacts-write.test.ts +197 -0
  50. package/src/__tests__/context-overflow-approval.test.ts +16 -1
  51. package/src/__tests__/context-window-manager.test.ts +88 -0
  52. package/src/__tests__/conversation-abort-tool-results.test.ts +2 -0
  53. package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -1
  54. package/src/__tests__/conversation-agent-loop.test.ts +99 -3
  55. package/src/__tests__/conversation-analysis-routes.test.ts +2 -2
  56. package/src/__tests__/conversation-attachments.test.ts +80 -4
  57. package/src/__tests__/conversation-confirmation-signals.test.ts +290 -0
  58. package/src/__tests__/conversation-error.test.ts +70 -0
  59. package/src/__tests__/conversation-fork-crud.test.ts +17 -0
  60. package/src/__tests__/conversation-history-web-search.test.ts +12 -4
  61. package/src/__tests__/conversation-host-access-routes.test.ts +229 -0
  62. package/src/__tests__/conversation-init.benchmark.test.ts +6 -1
  63. package/src/__tests__/conversation-inject-context.test.ts +103 -0
  64. package/src/__tests__/conversation-launcher-skill-regression.test.ts +51 -0
  65. package/src/__tests__/conversation-list-source.test.ts +145 -0
  66. package/src/__tests__/conversation-pre-run-repair.test.ts +2 -0
  67. package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -0
  68. package/src/__tests__/conversation-queue.test.ts +946 -62
  69. package/src/__tests__/conversation-routes-disk-view.test.ts +275 -0
  70. package/src/__tests__/conversation-routes-guardian-reply.test.ts +16 -0
  71. package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
  72. package/src/__tests__/conversation-runtime-assembly.test.ts +324 -46
  73. package/src/__tests__/conversation-skill-tools.test.ts +7 -4
  74. package/src/__tests__/conversation-slash-commands.test.ts +33 -0
  75. package/src/__tests__/conversation-slash-queue.test.ts +89 -18
  76. package/src/__tests__/conversation-slash-unknown.test.ts +2 -0
  77. package/src/__tests__/conversation-starter-routes.test.ts +126 -0
  78. package/src/__tests__/conversation-starters-cadence.test.ts +161 -0
  79. package/src/__tests__/conversation-store.test.ts +195 -0
  80. package/src/__tests__/conversation-tool-setup-batch-authorized.test.ts +226 -0
  81. package/src/__tests__/conversation-workspace-cache-state.test.ts +193 -0
  82. package/src/__tests__/conversation-workspace-injection.test.ts +2 -0
  83. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +2 -0
  84. package/src/__tests__/credential-execution-approval-bridge.test.ts +32 -1
  85. package/src/__tests__/credential-health-service.test.ts +352 -0
  86. package/src/__tests__/credential-security-invariants.test.ts +6 -3
  87. package/src/__tests__/credential-vault-unit.test.ts +383 -7
  88. package/src/__tests__/credential-vault.test.ts +152 -13
  89. package/src/__tests__/credentials-cli.test.ts +42 -18
  90. package/src/__tests__/cross-provider-web-search.test.ts +146 -35
  91. package/src/__tests__/date-context.test.ts +4 -4
  92. package/src/__tests__/deterministic-verification-control-plane.test.ts +10 -1
  93. package/src/__tests__/device-id.test.ts +112 -0
  94. package/src/__tests__/docker-signing-key-bootstrap.test.ts +167 -4
  95. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +1 -3
  96. package/src/__tests__/email-html-renderer.test.ts +71 -0
  97. package/src/__tests__/email-invite-adapter.test.ts +36 -32
  98. package/src/__tests__/embedding-managed-proxy-selection.test.ts +256 -0
  99. package/src/__tests__/emit-event-signal.test.ts +71 -0
  100. package/src/__tests__/extension-id-sync-guard.test.ts +222 -0
  101. package/src/__tests__/fixtures/mock-chrome-extension.ts +386 -0
  102. package/src/__tests__/gateway-only-enforcement.test.ts +206 -1
  103. package/src/__tests__/gateway-only-guard.test.ts +2 -0
  104. package/src/__tests__/gemini-provider.test.ts +66 -2
  105. package/src/__tests__/get-skill-detail-audit.test.ts +325 -0
  106. package/src/__tests__/gmail-archive-fallback.test.ts +193 -0
  107. package/src/__tests__/gmail-archive-gate.test.ts +246 -0
  108. package/src/__tests__/gmail-preferences.test.ts +117 -0
  109. package/src/__tests__/guardian-routing-invariants.test.ts +70 -2
  110. package/src/__tests__/headless-browser-interactions.test.ts +738 -359
  111. package/src/__tests__/headless-browser-mode.test.ts +614 -0
  112. package/src/__tests__/headless-browser-navigate.test.ts +528 -49
  113. package/src/__tests__/headless-browser-read-tools.test.ts +274 -100
  114. package/src/__tests__/headless-browser-snapshot.test.ts +250 -77
  115. package/src/__tests__/heartbeat-service.test.ts +70 -17
  116. package/src/__tests__/home-state-routes.test.ts +162 -0
  117. package/src/__tests__/host-bash-proxy.test.ts +145 -1
  118. package/src/__tests__/host-browser-e2e-cloud.test.ts +596 -0
  119. package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +286 -0
  120. package/src/__tests__/host-browser-e2e-self-hosted.test.ts +374 -0
  121. package/src/__tests__/host-browser-event-routes.test.ts +350 -0
  122. package/src/__tests__/host-browser-proxy.test.ts +444 -0
  123. package/src/__tests__/host-browser-routes.test.ts +198 -0
  124. package/src/__tests__/host-browser-ws-events-e2e.test.ts +423 -0
  125. package/src/__tests__/host-cu-proxy.test.ts +166 -1
  126. package/src/__tests__/host-file-proxy.test.ts +185 -1
  127. package/src/__tests__/host-file-read-tool.test.ts +52 -0
  128. package/src/__tests__/host-proxy-interface.test.ts +165 -0
  129. package/src/__tests__/host-shell-tool.test.ts +1 -11
  130. package/src/__tests__/http-user-message-parity.test.ts +1 -0
  131. package/src/__tests__/identity-intro-cache.test.ts +40 -10
  132. package/src/__tests__/init-feature-flag-overrides.test.ts +38 -112
  133. package/src/__tests__/integration-status.test.ts +6 -7
  134. package/src/__tests__/jobs-store-upsert-debounced.test.ts +141 -0
  135. package/src/__tests__/list-messages-tool-merge.test.ts +37 -12
  136. package/src/__tests__/llm-context-normalization.test.ts +488 -0
  137. package/src/__tests__/llm-context-route-provider.test.ts +86 -5
  138. package/src/__tests__/llm-usage-store.test.ts +363 -0
  139. package/src/__tests__/mcp-client-auth.test.ts +40 -4
  140. package/src/__tests__/mcp-health-check.test.ts +10 -3
  141. package/src/__tests__/media-stream-output.test.ts +555 -0
  142. package/src/__tests__/media-stream-parser.test.ts +374 -0
  143. package/src/__tests__/media-stream-server-integration.test.ts +1234 -0
  144. package/src/__tests__/media-stream-stt-session.test.ts +588 -0
  145. package/src/__tests__/media-turn-detector.test.ts +440 -0
  146. package/src/__tests__/message-queue.test.ts +125 -0
  147. package/src/__tests__/migration-cross-version-compatibility.test.ts +3 -1
  148. package/src/__tests__/migration-export-http.test.ts +67 -8
  149. package/src/__tests__/migration-export-streaming.test.ts +66 -0
  150. package/src/__tests__/migration-import-commit-http.test.ts +109 -7
  151. package/src/__tests__/migration-import-preflight-http.test.ts +6 -5
  152. package/src/__tests__/migration-validate-http.test.ts +3 -3
  153. package/src/__tests__/mock-gateway-ipc.ts +151 -0
  154. package/src/__tests__/model-intents.test.ts +2 -2
  155. package/src/__tests__/native-host-marker-sync-guard.test.ts +157 -0
  156. package/src/__tests__/oauth-apps-routes.test.ts +18 -12
  157. package/src/__tests__/oauth-cli.test.ts +709 -60
  158. package/src/__tests__/oauth-connect-orchestrator.test.ts +118 -24
  159. package/src/__tests__/oauth-provider-seed-logos.test.ts +23 -0
  160. package/src/__tests__/oauth-provider-serializer.test.ts +147 -10
  161. package/src/__tests__/oauth-provider-visibility.test.ts +19 -21
  162. package/src/__tests__/oauth-providers-routes.test.ts +52 -14
  163. package/src/__tests__/oauth-store.test.ts +1465 -176
  164. package/src/__tests__/oauth2-gateway-transport.test.ts +460 -26
  165. package/src/__tests__/onboarding-template-contract.test.ts +81 -70
  166. package/src/__tests__/openai-provider.test.ts +178 -2
  167. package/src/__tests__/openai-responses-cutover-guard.test.ts +184 -0
  168. package/src/__tests__/openai-responses-provider.test.ts +1105 -0
  169. package/src/__tests__/openrouter-token-estimation.test.ts +100 -0
  170. package/src/__tests__/outlook-categories.test.ts +1 -1
  171. package/src/__tests__/outlook-client-automation.test.ts +1 -1
  172. package/src/__tests__/outlook-compose-tools.test.ts +1 -1
  173. package/src/__tests__/outlook-email-watcher.test.ts +1 -1
  174. package/src/__tests__/outlook-follow-up.test.ts +1 -1
  175. package/src/__tests__/outlook-messaging-provider.test.ts +2 -2
  176. package/src/__tests__/outlook-trash.test.ts +1 -1
  177. package/src/__tests__/outlook-unsubscribe.test.ts +32 -3
  178. package/src/__tests__/permission-checker-host-gate.test.ts +74 -14
  179. package/src/__tests__/permission-mode.test.ts +28 -56
  180. package/src/__tests__/persona-resolver.test.ts +251 -0
  181. package/src/__tests__/platform-bash-auto-approve.test.ts +4 -0
  182. package/src/__tests__/platform-callback-registration.test.ts +19 -0
  183. package/src/__tests__/platform.test.ts +92 -1
  184. package/src/__tests__/post-turn-tool-result-truncation.test.ts +343 -0
  185. package/src/__tests__/prechat-onboarding-contract.test.ts +267 -0
  186. package/src/__tests__/pricing.test.ts +174 -0
  187. package/src/__tests__/proxy-approval-callback.test.ts +18 -0
  188. package/src/__tests__/qdrant-manager.test.ts +29 -8
  189. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +194 -0
  190. package/src/__tests__/relationship-state-contract.test.ts +175 -0
  191. package/src/__tests__/relay-server.test.ts +423 -5
  192. package/src/__tests__/require-fresh-approval.test.ts +40 -1
  193. package/src/__tests__/sanitize-config-for-transfer.test.ts +132 -0
  194. package/src/__tests__/schedule-routes.test.ts +162 -0
  195. package/src/__tests__/search-skills-unified.test.ts +118 -0
  196. package/src/__tests__/secret-detection-handler.test.ts +84 -0
  197. package/src/__tests__/secret-ingress-http.test.ts +1 -0
  198. package/src/__tests__/secret-scanner-executor.test.ts +4 -0
  199. package/src/__tests__/secure-keys.test.ts +107 -0
  200. package/src/__tests__/send-endpoint-busy.test.ts +8 -1
  201. package/src/__tests__/sequence-store.test.ts +1 -1
  202. package/src/__tests__/server-history-render.test.ts +49 -0
  203. package/src/__tests__/set-permission-mode.test.ts +13 -250
  204. package/src/__tests__/settings-routes.test.ts +201 -0
  205. package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
  206. package/src/__tests__/skills-file-content-endpoint.test.ts +801 -0
  207. package/src/__tests__/skills-files-catalog-fallback.test.ts +738 -0
  208. package/src/__tests__/skills.test.ts +5 -2
  209. package/src/__tests__/skillssh-files.test.ts +446 -0
  210. package/src/__tests__/slack-block-formatting.test.ts +110 -0
  211. package/src/__tests__/slack-channel-config.test.ts +576 -16
  212. package/src/__tests__/stt-catalog-parity.test.ts +282 -0
  213. package/src/__tests__/stt-stream-session.test.ts +535 -0
  214. package/src/__tests__/subagent-detail.test.ts +44 -2
  215. package/src/__tests__/subagent-disposal.test.ts +1 -0
  216. package/src/__tests__/subagent-fork-notifications.test.ts +291 -0
  217. package/src/__tests__/subagent-fork-spawn.test.ts +384 -0
  218. package/src/__tests__/subagent-manager-notify.test.ts +1 -0
  219. package/src/__tests__/subagent-notify-parent.test.ts +1 -0
  220. package/src/__tests__/subagent-spawn-tool-fork.test.ts +411 -0
  221. package/src/__tests__/subagent-tools.test.ts +1 -0
  222. package/src/__tests__/subagent-types.test.ts +1 -0
  223. package/src/__tests__/system-prompt-ask-mode.test.ts +27 -71
  224. package/src/__tests__/system-prompt.test.ts +184 -27
  225. package/src/__tests__/task-scheduler.test.ts +32 -6
  226. package/src/__tests__/telegram-config.test.ts +10 -13
  227. package/src/__tests__/telephony-stt-routing.test.ts +329 -0
  228. package/src/__tests__/terminal-tools.test.ts +25 -5
  229. package/src/__tests__/test-preload.ts +18 -0
  230. package/src/__tests__/test-support/browser-skill-harness.ts +4 -1
  231. package/src/__tests__/tool-approval-handler.test.ts +73 -0
  232. package/src/__tests__/tool-executor-lifecycle-events.test.ts +9 -5
  233. package/src/__tests__/tool-executor-shell-integration.test.ts +4 -0
  234. package/src/__tests__/tool-executor.test.ts +33 -24
  235. package/src/__tests__/tool-result-truncation.test.ts +36 -0
  236. package/src/__tests__/tool-side-effects-slack-dm.test.ts +22 -0
  237. package/src/__tests__/top-level-renderer.test.ts +73 -1
  238. package/src/__tests__/transport-hints-queue.test.ts +14 -29
  239. package/src/__tests__/trust-store.test.ts +7 -1
  240. package/src/__tests__/trusted-contact-approval-notifier.test.ts +1 -1
  241. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +109 -0
  242. package/src/__tests__/tts-catalog-parity.test.ts +345 -0
  243. package/src/__tests__/twilio-routes-twiml.test.ts +512 -114
  244. package/src/__tests__/twilio-routes.test.ts +376 -0
  245. package/src/__tests__/unicode.test.ts +293 -0
  246. package/src/__tests__/update-bulletin-format.test.ts +59 -0
  247. package/src/__tests__/update-bulletin.test.ts +206 -5
  248. package/src/__tests__/usage-routes.test.ts +25 -4
  249. package/src/__tests__/user-reference.test.ts +46 -61
  250. package/src/__tests__/v2-consent-policy.test.ts +103 -0
  251. package/src/__tests__/verification-control-plane-policy.test.ts +4 -0
  252. package/src/__tests__/voice-config-update.test.ts +403 -0
  253. package/src/__tests__/voice-quality.test.ts +434 -19
  254. package/src/__tests__/workspace-heartbeat-service.test.ts +7 -0
  255. package/src/__tests__/workspace-migration-033-stt-service-explicit-config.test.ts +547 -0
  256. package/src/__tests__/workspace-migration-034-remove-calls-voice-transcription-provider.test.ts +596 -0
  257. package/src/__tests__/workspace-migration-drop-user-md.test.ts +368 -0
  258. package/src/__tests__/workspace-migration-meets.test.ts +244 -0
  259. package/src/__tests__/workspace-migration-seed-device-id.test.ts +14 -20
  260. package/src/__tests__/workspace-policy.test.ts +2 -0
  261. package/src/acp/client-handler.ts +30 -4
  262. package/src/agent/image-optimize.ts +24 -12
  263. package/src/agent/loop.ts +55 -9
  264. package/src/approvals/guardian-request-resolvers.ts +21 -15
  265. package/src/backup/__tests__/backup-key.test.ts +152 -0
  266. package/src/backup/__tests__/backup-worker.test.ts +767 -0
  267. package/src/backup/__tests__/list-snapshots.test.ts +87 -0
  268. package/src/backup/__tests__/local-writer.test.ts +218 -0
  269. package/src/backup/__tests__/offsite-writer.test.ts +641 -0
  270. package/src/backup/__tests__/paths.test.ts +300 -0
  271. package/src/backup/__tests__/restore.test.ts +498 -0
  272. package/src/backup/__tests__/snapshot-lock.test.ts +352 -0
  273. package/src/backup/__tests__/stream-crypt.test.ts +228 -0
  274. package/src/backup/backup-key.ts +137 -0
  275. package/src/backup/backup-worker.ts +459 -0
  276. package/src/backup/list-snapshots.ts +147 -0
  277. package/src/backup/local-writer.ts +133 -0
  278. package/src/backup/offsite-writer.ts +222 -0
  279. package/src/backup/paths.ts +226 -0
  280. package/src/backup/restore.ts +322 -0
  281. package/src/backup/snapshot-lock.ts +431 -0
  282. package/src/backup/stream-crypt.ts +263 -0
  283. package/src/browser-session/__tests__/manager.test.ts +297 -0
  284. package/src/browser-session/backends/cdp-inspect.ts +30 -0
  285. package/src/browser-session/backends/extension.ts +26 -0
  286. package/src/browser-session/backends/local.ts +24 -0
  287. package/src/browser-session/events.ts +164 -0
  288. package/src/browser-session/index.ts +27 -0
  289. package/src/browser-session/manager.ts +159 -0
  290. package/src/browser-session/types.ts +28 -0
  291. package/src/bundler/package-resolver.ts +4 -0
  292. package/src/calls/audio-store.ts +11 -5
  293. package/src/calls/call-controller.ts +226 -71
  294. package/src/calls/call-domain.ts +9 -0
  295. package/src/calls/call-speech-output.ts +190 -0
  296. package/src/calls/call-transport.ts +77 -0
  297. package/src/calls/media-stream-audio-transcode.ts +173 -0
  298. package/src/calls/media-stream-output.ts +660 -0
  299. package/src/calls/media-stream-parser.ts +300 -0
  300. package/src/calls/media-stream-protocol.ts +166 -0
  301. package/src/calls/media-stream-server.ts +592 -0
  302. package/src/calls/media-stream-stt-session.ts +460 -0
  303. package/src/calls/media-turn-detector.ts +230 -0
  304. package/src/calls/relay-server.ts +90 -75
  305. package/src/calls/resolve-call-tts-provider.ts +136 -0
  306. package/src/calls/telephony-stt-routing.ts +145 -0
  307. package/src/calls/tts-call-strategy.ts +161 -0
  308. package/src/calls/tts-text-sanitizer.ts +32 -16
  309. package/src/calls/twilio-routes.ts +281 -17
  310. package/src/calls/voice-quality.ts +78 -35
  311. package/src/calls/voice-session-bridge.ts +8 -1
  312. package/src/channels/__tests__/types.test.ts +134 -0
  313. package/src/channels/types.ts +69 -3
  314. package/src/cli/__tests__/run-assistant-command.ts +11 -1
  315. package/src/cli/commands/__tests__/backup.test.ts +1165 -0
  316. package/src/cli/commands/__tests__/domain-register.test.ts +234 -0
  317. package/src/cli/commands/__tests__/domain-status.test.ts +132 -0
  318. package/src/cli/commands/__tests__/email-attachment.test.ts +422 -0
  319. package/src/cli/commands/__tests__/email-download.test.ts +16 -1
  320. package/src/cli/commands/__tests__/email-list.test.ts +22 -4
  321. package/src/cli/commands/__tests__/email-register.test.ts +4 -4
  322. package/src/cli/commands/__tests__/email-send.test.ts +37 -4
  323. package/src/cli/commands/__tests__/email-status.test.ts +5 -1
  324. package/src/cli/commands/__tests__/email-unregister.test.ts +34 -5
  325. package/src/cli/commands/backup.ts +993 -0
  326. package/src/cli/commands/conversations.ts +77 -0
  327. package/src/cli/commands/credentials.ts +3 -4
  328. package/src/cli/commands/domain.ts +210 -0
  329. package/src/cli/commands/email.ts +273 -16
  330. package/src/cli/commands/mcp.ts +16 -4
  331. package/src/cli/commands/oauth/__tests__/connect.test.ts +56 -44
  332. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +21 -21
  333. package/src/cli/commands/oauth/__tests__/mode.test.ts +17 -17
  334. package/src/cli/commands/oauth/__tests__/ping.test.ts +16 -16
  335. package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +32 -33
  336. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +330 -0
  337. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +117 -12
  338. package/src/cli/commands/oauth/__tests__/status.test.ts +10 -10
  339. package/src/cli/commands/oauth/__tests__/token.test.ts +7 -7
  340. package/src/cli/commands/oauth/apps.ts +7 -4
  341. package/src/cli/commands/oauth/connect.ts +6 -3
  342. package/src/cli/commands/oauth/disconnect.ts +1 -1
  343. package/src/cli/commands/oauth/mode.ts +12 -3
  344. package/src/cli/commands/oauth/providers.ts +215 -36
  345. package/src/cli/commands/oauth/shared.ts +7 -6
  346. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +254 -0
  347. package/src/cli/commands/platform/__tests__/connect.test.ts +6 -0
  348. package/src/cli/commands/platform/__tests__/disconnect.test.ts +7 -1
  349. package/src/cli/commands/platform/__tests__/status.test.ts +6 -0
  350. package/src/cli/commands/platform/index.ts +107 -10
  351. package/src/cli/commands/usage.ts +10 -9
  352. package/src/cli/lib/daemon-credential-client.ts +4 -0
  353. package/src/cli/program.ts +30 -4
  354. package/src/config/__tests__/backup-schema.test.ts +134 -0
  355. package/src/config/assistant-feature-flags.ts +61 -62
  356. package/src/config/bundled-skills/app-builder/SKILL.md +26 -249
  357. package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +141 -0
  358. package/src/config/bundled-skills/app-builder/references/INTERACTION_HOOKS.md +56 -0
  359. package/src/config/bundled-skills/app-builder/references/WIDGETS.md +125 -0
  360. package/src/config/bundled-skills/browser/SKILL.md +30 -5
  361. package/src/config/bundled-skills/browser/TOOLS.json +123 -0
  362. package/src/config/bundled-skills/browser/tools/browser-attach.ts +12 -0
  363. package/src/config/bundled-skills/browser/tools/browser-detach.ts +12 -0
  364. package/src/config/bundled-skills/browser/tools/browser-status.ts +12 -0
  365. package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +17 -0
  366. package/src/config/bundled-skills/contacts/SKILL.md +5 -2
  367. package/src/config/bundled-skills/document/SKILL.md +4 -0
  368. package/src/config/bundled-skills/gmail/SKILL.md +54 -8
  369. package/src/config/bundled-skills/gmail/TOOLS.json +33 -3
  370. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +116 -9
  371. package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +138 -11
  372. package/src/config/bundled-skills/gmail/tools/gmail-preferences-tool.ts +59 -0
  373. package/src/config/bundled-skills/gmail/tools/gmail-preferences.ts +82 -0
  374. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +113 -17
  375. package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +2 -2
  376. package/src/config/bundled-skills/media-processing/SKILL.md +3 -9
  377. package/src/config/bundled-skills/media-processing/TOOLS.json +1 -6
  378. package/src/config/bundled-skills/media-processing/__tests__/audio-transcribe.test.ts +125 -0
  379. package/src/config/bundled-skills/media-processing/__tests__/extract-keyframes.test.ts +181 -0
  380. package/src/config/bundled-skills/media-processing/__tests__/preprocess-audio.test.ts +141 -0
  381. package/src/config/bundled-skills/media-processing/services/audio-transcribe.ts +32 -87
  382. package/src/config/bundled-skills/media-processing/services/preprocess.ts +8 -4
  383. package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +0 -10
  384. package/src/config/bundled-skills/messaging/SKILL.md +3 -3
  385. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +2 -2
  386. package/src/config/bundled-skills/outlook/SKILL.md +9 -2
  387. package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +2 -2
  388. package/src/config/bundled-skills/phone-calls/SKILL.md +2 -2
  389. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +27 -18
  390. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +3 -3
  391. package/src/config/bundled-skills/settings/TOOLS.json +3 -3
  392. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +26 -22
  393. package/src/config/bundled-skills/slack/SKILL.md +1 -0
  394. package/src/config/bundled-skills/subagent/SKILL.md +21 -0
  395. package/src/config/bundled-skills/subagent/TOOLS.json +8 -4
  396. package/src/config/bundled-skills/tasks/SKILL.md +5 -0
  397. package/src/config/bundled-skills/transcribe/SKILL.md +9 -14
  398. package/src/config/bundled-skills/transcribe/TOOLS.json +2 -7
  399. package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +256 -0
  400. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +38 -188
  401. package/src/config/bundled-tool-registry.ts +8 -0
  402. package/src/config/env-registry.ts +38 -0
  403. package/src/config/env.ts +49 -4
  404. package/src/config/feature-flag-registry.json +85 -14
  405. package/src/config/loader.ts +82 -13
  406. package/src/config/sanitize-for-transfer.ts +47 -0
  407. package/src/config/schema.ts +81 -15
  408. package/src/config/schemas/__tests__/stt.test.ts +43 -0
  409. package/src/config/schemas/analysis.ts +51 -0
  410. package/src/config/schemas/backup.ts +72 -0
  411. package/src/config/schemas/calls.ts +1 -26
  412. package/src/config/schemas/elevenlabs.ts +0 -59
  413. package/src/config/schemas/filing.ts +47 -7
  414. package/src/config/schemas/heartbeat.ts +27 -5
  415. package/src/config/schemas/host-browser.ts +112 -0
  416. package/src/config/schemas/inference.ts +1 -1
  417. package/src/config/schemas/memory-lifecycle.ts +14 -2
  418. package/src/config/schemas/memory-retrieval.ts +103 -0
  419. package/src/config/schemas/security.ts +0 -6
  420. package/src/config/schemas/services.ts +52 -0
  421. package/src/config/schemas/stt.ts +59 -0
  422. package/src/config/schemas/tts.ts +230 -0
  423. package/src/config/schemas/updates.ts +14 -0
  424. package/src/config/skills.ts +4 -0
  425. package/src/config/types.ts +4 -1
  426. package/src/contacts/contact-store.ts +56 -11
  427. package/src/contacts/contacts-write.ts +38 -1
  428. package/src/context/post-turn-tool-result-truncation.ts +177 -0
  429. package/src/context/tool-result-truncation.ts +2 -1
  430. package/src/context/window-manager.ts +61 -10
  431. package/src/credential-execution/approval-bridge.ts +49 -15
  432. package/src/credential-execution/executable-discovery.ts +12 -2
  433. package/src/credential-execution/process-manager.ts +33 -2
  434. package/src/credential-health/credential-health-service.ts +366 -0
  435. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +324 -0
  436. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +497 -0
  437. package/src/daemon/__tests__/conversation-tool-setup.test.ts +195 -0
  438. package/src/daemon/__tests__/lifecycle-startup-ordering.test.ts +127 -0
  439. package/src/daemon/app-source-watcher.ts +35 -0
  440. package/src/daemon/config-watcher.ts +99 -5
  441. package/src/daemon/context-overflow-approval.ts +5 -0
  442. package/src/daemon/conversation-agent-loop-handlers.ts +23 -2
  443. package/src/daemon/conversation-agent-loop.ts +153 -42
  444. package/src/daemon/conversation-attachments.ts +40 -0
  445. package/src/daemon/conversation-error.ts +11 -0
  446. package/src/daemon/conversation-history.ts +40 -6
  447. package/src/daemon/conversation-launch.ts +220 -0
  448. package/src/daemon/conversation-lifecycle.ts +59 -9
  449. package/src/daemon/conversation-messaging.ts +37 -3
  450. package/src/daemon/conversation-notifiers.ts +5 -0
  451. package/src/daemon/conversation-process.ts +622 -13
  452. package/src/daemon/conversation-queue-manager.ts +24 -0
  453. package/src/daemon/conversation-runtime-assembly.ts +128 -36
  454. package/src/daemon/conversation-slash.ts +36 -0
  455. package/src/daemon/conversation-surfaces.ts +131 -40
  456. package/src/daemon/conversation-tool-setup.ts +99 -8
  457. package/src/daemon/conversation-usage.ts +7 -4
  458. package/src/daemon/conversation-workspace.ts +12 -0
  459. package/src/daemon/conversation.ts +292 -16
  460. package/src/daemon/date-context.ts +10 -10
  461. package/src/daemon/first-greeting.ts +3 -2
  462. package/src/daemon/handlers/config-slack-channel.ts +269 -94
  463. package/src/daemon/handlers/conversations.ts +13 -141
  464. package/src/daemon/handlers/shared.ts +80 -0
  465. package/src/daemon/handlers/skills.ts +483 -44
  466. package/src/daemon/host-bash-proxy.ts +48 -13
  467. package/src/daemon/host-browser-proxy.ts +192 -0
  468. package/src/daemon/host-cu-proxy.ts +36 -11
  469. package/src/daemon/host-file-proxy.ts +57 -9
  470. package/src/daemon/lifecycle.ts +179 -28
  471. package/src/daemon/message-protocol.ts +13 -0
  472. package/src/daemon/message-types/conversations.ts +89 -14
  473. package/src/daemon/message-types/home.ts +40 -0
  474. package/src/daemon/message-types/host-browser.ts +100 -0
  475. package/src/daemon/message-types/meet.ts +143 -0
  476. package/src/daemon/message-types/messages.ts +19 -5
  477. package/src/daemon/message-types/schedules.ts +34 -2
  478. package/src/daemon/message-types/skills.ts +26 -0
  479. package/src/daemon/message-types/subagents.ts +2 -0
  480. package/src/daemon/message-types/surfaces.ts +2 -0
  481. package/src/daemon/server.ts +439 -14
  482. package/src/daemon/shutdown-handlers.ts +32 -4
  483. package/src/daemon/shutdown-registry.ts +40 -0
  484. package/src/daemon/tool-side-effects.ts +15 -0
  485. package/src/daemon/transport-hints.ts +5 -24
  486. package/src/email/html-renderer.ts +76 -0
  487. package/src/heartbeat/heartbeat-service.ts +93 -7
  488. package/src/home/__tests__/assistant-feed-authoring.test.ts +156 -0
  489. package/src/home/__tests__/emit-feed-event.test.ts +169 -0
  490. package/src/home/__tests__/feed-scheduler.test.ts +194 -0
  491. package/src/home/__tests__/feed-types.test.ts +275 -0
  492. package/src/home/__tests__/feed-writer.test.ts +688 -0
  493. package/src/home/__tests__/phase5-exit-criteria.test.ts +212 -0
  494. package/src/home/__tests__/platform-gmail-digest.test.ts +222 -0
  495. package/src/home/__tests__/progress-formula.test.ts +213 -0
  496. package/src/home/__tests__/relationship-state-writer.test.ts +740 -0
  497. package/src/home/__tests__/rollup-producer.test.ts +398 -0
  498. package/src/home/assistant-feed-authoring.ts +124 -0
  499. package/src/home/emit-feed-event.ts +158 -0
  500. package/src/home/feed-scheduler.ts +247 -0
  501. package/src/home/feed-types.ts +181 -0
  502. package/src/home/feed-writer.ts +469 -0
  503. package/src/home/platform-gmail-digest.ts +163 -0
  504. package/src/home/progress-formula.ts +86 -0
  505. package/src/home/relationship-state-writer.ts +824 -0
  506. package/src/home/relationship-state.ts +143 -0
  507. package/src/home/rollup-producer.ts +384 -0
  508. package/src/hooks/runner.ts +7 -0
  509. package/src/inbound/platform-callback-registration.ts +30 -20
  510. package/src/inbound/public-ingress-urls.ts +12 -0
  511. package/src/instrument.ts +1 -1
  512. package/src/ipc/__tests__/cli-ipc.test.ts +200 -0
  513. package/src/ipc/cli-client.ts +151 -0
  514. package/src/ipc/cli-server.ts +234 -0
  515. package/src/ipc/gateway-client.ts +180 -0
  516. package/src/ipc/routes/index.ts +5 -0
  517. package/src/ipc/routes/wake-conversation.ts +19 -0
  518. package/src/mcp/client.ts +59 -24
  519. package/src/memory/__tests__/auto-analysis-enqueue.test.ts +356 -0
  520. package/src/memory/__tests__/auto-analysis-guard.test.ts +57 -0
  521. package/src/memory/__tests__/conversation-analyze-job.test.ts +232 -0
  522. package/src/memory/__tests__/find-analysis-conversation.test.ts +196 -0
  523. package/src/memory/app-store.ts +31 -1
  524. package/src/memory/attachments-store.ts +70 -0
  525. package/src/memory/auto-analysis-enqueue.ts +127 -0
  526. package/src/memory/auto-analysis-guard.ts +27 -0
  527. package/src/memory/cleanup-schedule-state.ts +37 -0
  528. package/src/memory/conversation-analyze-job.ts +73 -0
  529. package/src/memory/conversation-crud.ts +122 -0
  530. package/src/memory/conversation-disk-view.ts +7 -0
  531. package/src/memory/conversation-group-migration.ts +34 -2
  532. package/src/memory/conversation-queries.ts +6 -5
  533. package/src/memory/conversation-starters-cadence.ts +76 -0
  534. package/src/memory/conversation-title-service.ts +5 -2
  535. package/src/memory/db-init.ts +18 -0
  536. package/src/memory/db-maintenance.ts +108 -0
  537. package/src/memory/db.ts +1 -0
  538. package/src/memory/embedding-backend.test.ts +75 -0
  539. package/src/memory/embedding-backend.ts +131 -5
  540. package/src/memory/embedding-gemini.test.ts +54 -0
  541. package/src/memory/embedding-gemini.ts +20 -9
  542. package/src/memory/embedding-local.ts +176 -17
  543. package/src/memory/graph/consolidation.ts +10 -23
  544. package/src/memory/graph/conversation-graph-memory.ts +15 -0
  545. package/src/memory/graph/extraction-job.ts +15 -0
  546. package/src/memory/graph/extraction.test.ts +23 -0
  547. package/src/memory/graph/extraction.ts +8 -0
  548. package/src/memory/graph/retriever.ts +67 -40
  549. package/src/memory/graph/scoring.test.ts +186 -0
  550. package/src/memory/graph/scoring.ts +31 -1
  551. package/src/memory/graph/store.test.ts +7 -3
  552. package/src/memory/graph/store.ts +47 -12
  553. package/src/memory/graph/tools.ts +1 -1
  554. package/src/memory/group-crud.ts +6 -1
  555. package/src/memory/indexer.ts +95 -16
  556. package/src/memory/job-handlers/cleanup.ts +11 -8
  557. package/src/memory/job-handlers/conversation-starters.ts +16 -10
  558. package/src/memory/jobs-store.ts +64 -4
  559. package/src/memory/jobs-worker.ts +22 -9
  560. package/src/memory/llm-usage-store.ts +137 -60
  561. package/src/memory/migrations/213-oauth-providers-scope-separator.ts +13 -0
  562. package/src/memory/migrations/214-oauth-providers-refresh-url.ts +11 -0
  563. package/src/memory/migrations/215-oauth-providers-revoke.ts +14 -0
  564. package/src/memory/migrations/216-oauth-providers-token-auth-method.ts +30 -0
  565. package/src/memory/migrations/217-conversation-host-access.ts +40 -0
  566. package/src/memory/migrations/218-oauth-providers-logo-url.ts +11 -0
  567. package/src/memory/migrations/219-oauth-providers-token-exchange-body-format.ts +15 -0
  568. package/src/memory/migrations/220-normalize-user-file-by-principal.ts +190 -0
  569. package/src/memory/migrations/221-conversations-archived-at.ts +16 -0
  570. package/src/memory/migrations/index.ts +12 -0
  571. package/src/memory/migrations/registry.ts +16 -0
  572. package/src/memory/qdrant-manager.ts +43 -16
  573. package/src/memory/schema/conversations.ts +3 -0
  574. package/src/memory/schema/oauth.ts +21 -13
  575. package/src/memory/usage-buckets.ts +396 -0
  576. package/src/messaging/providers/gmail/client.ts +57 -6
  577. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +282 -0
  578. package/src/messaging/providers/slack/adapter.ts +143 -38
  579. package/src/messaging/providers/slack/client.ts +16 -0
  580. package/src/messaging/providers/slack/types.ts +4 -0
  581. package/src/notifications/decision-engine.ts +3 -3
  582. package/src/notifications/signal.ts +5 -0
  583. package/src/oauth/AGENTS.md +76 -0
  584. package/src/oauth/__tests__/identity-verifier.test.ts +25 -19
  585. package/src/oauth/__tests__/seed-providers-managed.test.ts +32 -0
  586. package/src/oauth/byo-connection.test.ts +26 -9
  587. package/src/oauth/byo-connection.ts +10 -8
  588. package/src/oauth/connect-orchestrator.ts +25 -21
  589. package/src/oauth/connect-types.ts +3 -3
  590. package/src/oauth/connection-resolver.test.ts +17 -4
  591. package/src/oauth/connection-resolver.ts +22 -18
  592. package/src/oauth/connection.ts +3 -1
  593. package/src/oauth/manual-token-connection.ts +13 -13
  594. package/src/oauth/oauth-store.ts +223 -100
  595. package/src/oauth/platform-connection.test.ts +101 -3
  596. package/src/oauth/platform-connection.ts +56 -35
  597. package/src/oauth/provider-serializer.ts +31 -5
  598. package/src/oauth/revoke.ts +76 -0
  599. package/src/oauth/seed-providers.ts +133 -87
  600. package/src/oauth/token-persistence.ts +1 -1
  601. package/src/permissions/checker.ts +16 -6
  602. package/src/permissions/defaults.ts +49 -1
  603. package/src/permissions/permission-mode.ts +4 -11
  604. package/src/permissions/prompter.ts +13 -1
  605. package/src/permissions/trust-store.ts +3 -3
  606. package/src/permissions/v2-consent-policy.ts +87 -0
  607. package/src/permissions/workspace-policy.ts +3 -0
  608. package/src/platform/client.test.ts +10 -0
  609. package/src/platform/sync-identity.ts +129 -0
  610. package/src/prompts/persona-resolver.ts +126 -2
  611. package/src/prompts/system-prompt.ts +76 -38
  612. package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +3 -65
  613. package/src/prompts/templates/BOOTSTRAP.md +59 -105
  614. package/src/prompts/templates/SOUL.md +3 -1
  615. package/src/prompts/templates/UPDATES.md +12 -0
  616. package/src/prompts/templates/channels/slack.md +20 -0
  617. package/src/prompts/update-bulletin-format.ts +26 -9
  618. package/src/prompts/update-bulletin.ts +34 -23
  619. package/src/prompts/user-reference.ts +20 -17
  620. package/src/providers/__tests__/provider-secret-catalog.test.ts +42 -0
  621. package/src/providers/anthropic/client.ts +157 -60
  622. package/src/providers/fireworks/client.ts +2 -2
  623. package/src/providers/gemini/client.ts +9 -1
  624. package/src/providers/model-catalog.ts +6 -0
  625. package/src/providers/model-intents.ts +4 -4
  626. package/src/providers/ollama/client.ts +2 -2
  627. package/src/providers/openai/chat-completions-provider.ts +474 -0
  628. package/src/providers/openai/client.ts +25 -440
  629. package/src/providers/openai/responses-provider.ts +502 -0
  630. package/src/providers/openrouter/client.ts +101 -4
  631. package/src/providers/provider-secret-catalog.ts +139 -0
  632. package/src/providers/registry.ts +2 -2
  633. package/src/providers/retry.ts +14 -3
  634. package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +251 -0
  635. package/src/providers/speech-to-text/__tests__/resolve.test.ts +828 -0
  636. package/src/providers/speech-to-text/deepgram-realtime.test.ts +980 -0
  637. package/src/providers/speech-to-text/deepgram-realtime.ts +767 -0
  638. package/src/providers/speech-to-text/deepgram.test.ts +332 -0
  639. package/src/providers/speech-to-text/deepgram.ts +115 -0
  640. package/src/providers/speech-to-text/google-gemini-live-stream.test.ts +743 -0
  641. package/src/providers/speech-to-text/google-gemini-live-stream.ts +625 -0
  642. package/src/providers/speech-to-text/google-gemini.test.ts +226 -0
  643. package/src/providers/speech-to-text/google-gemini.ts +101 -0
  644. package/src/providers/speech-to-text/openai-whisper-stream.test.ts +564 -0
  645. package/src/providers/speech-to-text/openai-whisper-stream.ts +381 -0
  646. package/src/providers/speech-to-text/openai-whisper.test.ts +1 -37
  647. package/src/providers/speech-to-text/openai-whisper.ts +63 -33
  648. package/src/providers/speech-to-text/provider-catalog.ts +306 -0
  649. package/src/providers/speech-to-text/resolve.ts +386 -6
  650. package/src/providers/types.ts +10 -1
  651. package/src/runtime/AGENTS.md +65 -0
  652. package/src/runtime/__tests__/agent-wake.test.ts +831 -0
  653. package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +715 -0
  654. package/src/runtime/__tests__/capability-tokens.test.ts +258 -0
  655. package/src/runtime/__tests__/chrome-extension-registry.test.ts +518 -0
  656. package/src/runtime/__tests__/runtime-mode.test.ts +62 -0
  657. package/src/runtime/__tests__/slack-block-formatting.test.ts +481 -0
  658. package/src/runtime/agent-wake.ts +512 -0
  659. package/src/runtime/assistant-event-hub.ts +2 -2
  660. package/src/runtime/auth/__tests__/guard-tests.test.ts +1 -0
  661. package/src/runtime/auth/__tests__/middleware.test.ts +116 -1
  662. package/src/runtime/auth/__tests__/route-policy.test.ts +48 -0
  663. package/src/runtime/auth/middleware.ts +98 -0
  664. package/src/runtime/auth/route-policy.ts +33 -9
  665. package/src/runtime/auth/token-service.ts +56 -1
  666. package/src/runtime/btw-sidechain.ts +2 -0
  667. package/src/runtime/capability-tokens.ts +414 -0
  668. package/src/runtime/channel-approvals.ts +18 -5
  669. package/src/runtime/channel-invite-transport.ts +1 -1
  670. package/src/runtime/channel-invite-transports/email.ts +14 -6
  671. package/src/runtime/channel-readiness-service.ts +12 -22
  672. package/src/runtime/chrome-extension-registry.ts +368 -0
  673. package/src/runtime/confirmation-request-guardian-bridge.ts +6 -0
  674. package/src/runtime/guardian-decision-types.ts +7 -0
  675. package/src/runtime/http-server.ts +815 -75
  676. package/src/runtime/http-types.ts +6 -2
  677. package/src/runtime/migrations/__tests__/rebind-secrets-credentials.test.ts +172 -0
  678. package/src/runtime/migrations/__tests__/vbundle-builder-credentials.test.ts +276 -0
  679. package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +198 -0
  680. package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +360 -0
  681. package/src/runtime/migrations/migration-transport.ts +7 -0
  682. package/src/runtime/migrations/migration-wizard.ts +23 -2
  683. package/src/runtime/migrations/rebind-secrets-screen.ts +76 -15
  684. package/src/runtime/migrations/vbundle-builder.ts +145 -38
  685. package/src/runtime/migrations/vbundle-import-analyzer.ts +96 -1
  686. package/src/runtime/migrations/vbundle-importer.ts +89 -5
  687. package/src/runtime/pending-interactions.ts +18 -13
  688. package/src/runtime/routes/__tests__/backup-routes.test.ts +967 -0
  689. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +507 -0
  690. package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +208 -0
  691. package/src/runtime/routes/__tests__/stt-routes.test.ts +406 -0
  692. package/src/runtime/routes/__tests__/tts-routes.test.ts +474 -0
  693. package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +148 -17
  694. package/src/runtime/routes/app-management-routes.ts +12 -18
  695. package/src/runtime/routes/approval-routes.ts +90 -16
  696. package/src/runtime/routes/attachment-routes.test.ts +9 -3
  697. package/src/runtime/routes/attachment-routes.ts +216 -17
  698. package/src/runtime/routes/backup-routes.ts +519 -0
  699. package/src/runtime/routes/browser-extension-pair-routes.ts +556 -0
  700. package/src/runtime/routes/btw-routes.ts +8 -6
  701. package/src/runtime/routes/contact-routes.test.ts +298 -0
  702. package/src/runtime/routes/contact-routes.ts +132 -5
  703. package/src/runtime/routes/conversation-analysis-routes.ts +22 -141
  704. package/src/runtime/routes/conversation-management-routes.ts +223 -0
  705. package/src/runtime/routes/conversation-routes.ts +598 -103
  706. package/src/runtime/routes/conversation-starter-routes.ts +78 -16
  707. package/src/runtime/routes/filing-routes.ts +93 -0
  708. package/src/runtime/routes/guardian-action-routes.ts +24 -13
  709. package/src/runtime/routes/home-feed-routes.ts +334 -0
  710. package/src/runtime/routes/home-state-routes.ts +138 -0
  711. package/src/runtime/routes/host-browser-routes.ts +268 -0
  712. package/src/runtime/routes/host-file-routes.ts +9 -1
  713. package/src/runtime/routes/identity-intro-cache.ts +7 -3
  714. package/src/runtime/routes/identity-routes.ts +262 -33
  715. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +46 -39
  716. package/src/runtime/routes/inbound-stages/transcribe-audio.ts +15 -15
  717. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +137 -0
  718. package/src/runtime/routes/integrations/slack/__tests__/share.test.ts +179 -0
  719. package/src/runtime/routes/integrations/slack/channel.ts +11 -3
  720. package/src/runtime/routes/integrations/slack/share.ts +45 -7
  721. package/src/runtime/routes/llm-context-normalization.ts +303 -0
  722. package/src/runtime/routes/log-export-routes.ts +42 -22
  723. package/src/runtime/routes/memory-item-routes.test.ts +3 -2
  724. package/src/runtime/routes/memory-item-routes.ts +1 -7
  725. package/src/runtime/routes/migration-routes.ts +122 -2
  726. package/src/runtime/routes/oauth-apps.ts +15 -17
  727. package/src/runtime/routes/oauth-providers.ts +4 -0
  728. package/src/runtime/routes/schedule-routes.ts +24 -11
  729. package/src/runtime/routes/settings-routes.ts +31 -102
  730. package/src/runtime/routes/skills-routes.ts +128 -9
  731. package/src/runtime/routes/stt-routes.ts +233 -0
  732. package/src/runtime/routes/subagents-routes.ts +14 -10
  733. package/src/runtime/routes/surface-action-routes.ts +41 -2
  734. package/src/runtime/routes/tts-routes.ts +108 -24
  735. package/src/runtime/routes/usage-routes.ts +38 -9
  736. package/src/runtime/routes/user-route-dispatcher.ts +50 -5
  737. package/src/runtime/routes/user-routes.ts +13 -1
  738. package/src/runtime/routes/work-items-routes.ts +8 -1
  739. package/src/runtime/routes/workspace-routes.test.ts +22 -0
  740. package/src/runtime/routes/workspace-routes.ts +8 -1
  741. package/src/runtime/routes/workspace-utils.ts +2 -0
  742. package/src/runtime/runtime-mode.ts +33 -0
  743. package/src/runtime/services/__tests__/analyze-conversation.test.ts +444 -0
  744. package/src/runtime/services/__tests__/analyze-deps-singleton.test.ts +67 -0
  745. package/src/runtime/services/__tests__/auto-analysis-prompt.test.ts +53 -0
  746. package/src/runtime/services/__tests__/manual-analysis-prompt.test.ts +41 -0
  747. package/src/runtime/services/analyze-conversation.ts +344 -0
  748. package/src/runtime/services/analyze-deps-singleton.ts +32 -0
  749. package/src/runtime/services/auto-analysis-prompt.ts +55 -0
  750. package/src/runtime/skill-route-registry.ts +49 -0
  751. package/src/runtime/slack-block-formatting.ts +437 -10
  752. package/src/schedule/scheduler.ts +57 -5
  753. package/src/security/ces-credential-client.ts +20 -0
  754. package/src/security/ces-rpc-credential-backend.ts +17 -0
  755. package/src/security/credential-backend.ts +5 -0
  756. package/src/security/oauth2.ts +68 -29
  757. package/src/security/secure-keys.ts +143 -27
  758. package/src/security/token-manager.ts +31 -10
  759. package/src/sequence/engine.ts +23 -0
  760. package/src/sequence/types.ts +1 -1
  761. package/src/skills/catalog-files.ts +554 -0
  762. package/src/skills/category-inference.ts +122 -0
  763. package/src/skills/clawhub-files.ts +213 -0
  764. package/src/skills/clawhub.ts +84 -23
  765. package/src/skills/skill-file-provider.ts +40 -0
  766. package/src/skills/skillssh-files.ts +395 -0
  767. package/src/skills/skillssh-registry.ts +4 -4
  768. package/src/stt/__tests__/daemon-batch-transcriber.test.ts +392 -0
  769. package/src/stt/__tests__/types.test.ts +89 -0
  770. package/src/stt/daemon-batch-transcriber.ts +195 -0
  771. package/src/stt/stt-stream-session.ts +499 -0
  772. package/src/stt/types.ts +330 -0
  773. package/src/stt/wav-encoder.test.ts +373 -0
  774. package/src/stt/wav-encoder.ts +175 -0
  775. package/src/subagent/manager.ts +169 -40
  776. package/src/subagent/types.ts +19 -0
  777. package/src/tools/apps/executors.ts +11 -2
  778. package/src/tools/browser/__tests__/auth-detector.test.ts +202 -108
  779. package/src/tools/browser/__tests__/browser-mode.test.ts +119 -0
  780. package/src/tools/browser/__tests__/browser-status.test.ts +123 -0
  781. package/src/tools/browser/auth-detector.ts +43 -12
  782. package/src/tools/browser/browser-execution.ts +1787 -342
  783. package/src/tools/browser/browser-manager.ts +81 -12
  784. package/src/tools/browser/browser-mode-constants.ts +12 -0
  785. package/src/tools/browser/browser-mode.ts +92 -0
  786. package/src/tools/browser/browser-status-constants.ts +33 -0
  787. package/src/tools/browser/cdp-client/__tests__/accessibility-snapshot.test.ts +318 -0
  788. package/src/tools/browser/cdp-client/__tests__/cdp-dom-helpers.test.ts +1175 -0
  789. package/src/tools/browser/cdp-client/__tests__/cdp-inspect-client.test.ts +1263 -0
  790. package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +359 -0
  791. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +1993 -0
  792. package/src/tools/browser/cdp-client/__tests__/fixtures/ax-tree-nested-frames.json +64 -0
  793. package/src/tools/browser/cdp-client/__tests__/fixtures/ax-tree-simple.json +69 -0
  794. package/src/tools/browser/cdp-client/__tests__/local-cdp-client.test.ts +310 -0
  795. package/src/tools/browser/cdp-client/__tests__/types.test.ts +96 -0
  796. package/src/tools/browser/cdp-client/accessibility-snapshot.ts +387 -0
  797. package/src/tools/browser/cdp-client/cdp-dom-helpers.ts +695 -0
  798. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/discovery.test.ts +1007 -0
  799. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +580 -0
  800. package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +744 -0
  801. package/src/tools/browser/cdp-client/cdp-inspect/ws-transport.ts +579 -0
  802. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +868 -0
  803. package/src/tools/browser/cdp-client/errors.ts +49 -0
  804. package/src/tools/browser/cdp-client/extension-cdp-client.ts +148 -0
  805. package/src/tools/browser/cdp-client/factory.ts +914 -0
  806. package/src/tools/browser/cdp-client/index.ts +28 -0
  807. package/src/tools/browser/cdp-client/local-cdp-client.ts +187 -0
  808. package/src/tools/browser/cdp-client/types.ts +120 -0
  809. package/src/tools/credentials/vault.ts +35 -6
  810. package/src/tools/filesystem/edit.ts +1 -1
  811. package/src/tools/filesystem/list.ts +1 -1
  812. package/src/tools/filesystem/read.ts +1 -1
  813. package/src/tools/filesystem/write.ts +2 -1
  814. package/src/tools/host-filesystem/edit.ts +1 -1
  815. package/src/tools/host-filesystem/read.ts +12 -15
  816. package/src/tools/host-filesystem/write.ts +1 -1
  817. package/src/tools/host-terminal/host-shell.ts +21 -16
  818. package/src/tools/network/web-fetch.ts +5 -2
  819. package/src/tools/network/web-search.ts +5 -2
  820. package/src/tools/permission-checker.ts +77 -82
  821. package/src/tools/registry.ts +0 -2
  822. package/src/tools/secret-detection-handler.ts +34 -0
  823. package/src/tools/shared/filesystem/image-read.ts +61 -40
  824. package/src/tools/shared/shell-output.ts +3 -1
  825. package/src/tools/side-effects.ts +2 -0
  826. package/src/tools/skills/sandbox-runner.ts +3 -2
  827. package/src/tools/subagent/spawn.ts +47 -3
  828. package/src/tools/subagent/status.ts +2 -0
  829. package/src/tools/system/register.ts +2 -16
  830. package/src/tools/terminal/safe-env.ts +15 -0
  831. package/src/tools/terminal/shell.ts +36 -20
  832. package/src/tools/tool-approval-handler.ts +48 -2
  833. package/src/tools/tool-manifest.ts +21 -0
  834. package/src/tools/types.ts +19 -0
  835. package/src/tools/ui-surface/definitions.ts +6 -1
  836. package/src/tts/__tests__/provider-adapters.test.ts +834 -0
  837. package/src/tts/__tests__/provider-catalog-consistency.test.ts +196 -0
  838. package/src/tts/__tests__/provider-catalog.test.ts +183 -0
  839. package/src/tts/__tests__/provider-registry.test.ts +90 -0
  840. package/src/tts/provider-catalog.ts +201 -0
  841. package/src/tts/provider-registry.ts +73 -0
  842. package/src/tts/providers/deepgram-provider.ts +219 -0
  843. package/src/tts/providers/elevenlabs-provider.ts +211 -0
  844. package/src/tts/providers/fish-audio-provider.ts +183 -0
  845. package/src/tts/providers/index.ts +42 -0
  846. package/src/tts/providers/register-builtins.ts +130 -0
  847. package/src/tts/synthesize-text.ts +110 -0
  848. package/src/tts/tts-config-resolver.ts +78 -0
  849. package/src/tts/types.ts +153 -0
  850. package/src/types/onboarding-context.ts +7 -0
  851. package/src/util/abort-reasons.ts +58 -0
  852. package/src/util/device-id.ts +32 -16
  853. package/src/util/errors.ts +9 -1
  854. package/src/util/platform.ts +63 -24
  855. package/src/util/pricing.ts +66 -3
  856. package/src/util/spawn.ts +1 -1
  857. package/src/util/truncate.ts +4 -2
  858. package/src/util/unicode.ts +201 -0
  859. package/src/version.ts +19 -24
  860. package/src/watcher/engine.ts +23 -0
  861. package/src/watcher/watcher-store.ts +31 -0
  862. package/src/workspace/migrations/003-seed-device-id.ts +9 -3
  863. package/src/workspace/migrations/017-seed-persona-dirs.ts +68 -4
  864. package/src/workspace/migrations/029-seed-pkb.ts +1 -1
  865. package/src/workspace/migrations/031-drop-user-md.ts +317 -0
  866. package/src/workspace/migrations/031-llm-log-retention-zero-to-null.ts +73 -0
  867. package/src/workspace/migrations/032-tts-provider-unification.ts +227 -0
  868. package/src/workspace/migrations/033-stt-service-explicit-config.ts +122 -0
  869. package/src/workspace/migrations/034-remove-calls-voice-transcription-provider.ts +215 -0
  870. package/src/workspace/migrations/035-seed-slack-channel-persona.ts +50 -0
  871. package/src/workspace/migrations/036-update-pkb-index-bar.ts +37 -0
  872. package/src/workspace/migrations/037-create-meets-dir.ts +61 -0
  873. package/src/workspace/migrations/registry.ts +16 -0
  874. package/src/workspace/top-level-renderer.ts +31 -1
  875. package/src/workspace/turn-commit.ts +31 -0
  876. package/src/__tests__/chrome-cdp.test.ts +0 -419
  877. package/src/__tests__/email-cli.test.ts +0 -297
  878. package/src/__tests__/email-service-config-fallback.test.ts +0 -102
  879. package/src/__tests__/permission-mode-sse.test.ts +0 -418
  880. package/src/__tests__/permission-mode-store.test.ts +0 -277
  881. package/src/browser-extension-relay/protocol.ts +0 -63
  882. package/src/browser-extension-relay/server.ts +0 -203
  883. package/src/cli/commands/browser-relay.ts +0 -536
  884. package/src/config/schemas/sandbox.ts +0 -14
  885. package/src/email/guardrails.ts +0 -221
  886. package/src/email/provider.ts +0 -117
  887. package/src/email/providers/agentmail.ts +0 -361
  888. package/src/email/providers/index.ts +0 -65
  889. package/src/email/service.ts +0 -384
  890. package/src/email/types.ts +0 -126
  891. package/src/permissions/permission-mode-store.ts +0 -180
  892. package/src/prompts/templates/USER.md +0 -13
  893. package/src/providers/speech-to-text/types.ts +0 -17
  894. package/src/tools/browser/chrome-cdp.ts +0 -239
  895. package/src/tools/system/set-permission-mode.ts +0 -103
@@ -39,6 +39,40 @@ mock.module("../config/loader.js", () => {
39
39
  },
40
40
  memory: { enabled: false },
41
41
  notifications: { decisionModelIntent: "latency-optimized" },
42
+ services: {
43
+ tts: {
44
+ mode: "your-own" as const,
45
+ provider: "elevenlabs",
46
+ providers: {
47
+ elevenlabs: {
48
+ voiceId: "ZF6FPAbjXT4488VcRRnw",
49
+ voiceModelId: "",
50
+ speed: 1.0,
51
+ stability: 0.5,
52
+ similarityBoost: 0.75,
53
+ conversationTimeoutSeconds: 30,
54
+ },
55
+ "fish-audio": {
56
+ referenceId: "",
57
+ chunkLength: 200,
58
+ format: "mp3",
59
+ latency: "normal",
60
+ speed: 1.0,
61
+ },
62
+ deepgram: {
63
+ model: "aura-2-theia-en",
64
+ format: "mp3",
65
+ },
66
+ },
67
+ },
68
+ },
69
+ elevenlabs: {
70
+ voiceId: "ZF6FPAbjXT4488VcRRnw",
71
+ },
72
+ fishAudio: {
73
+ referenceId: "",
74
+ format: "mp3",
75
+ },
42
76
  };
43
77
  return {
44
78
  getConfig: () => config,
@@ -54,6 +88,17 @@ mock.module("../config/loader.js", () => {
54
88
  };
55
89
  });
56
90
 
91
+ // ── Credential mock (prevents real key lookups) ──────────────────────
92
+
93
+ mock.module("../security/secure-keys.js", () => ({
94
+ getSecureKeyAsync: async () => null,
95
+ getSecureKey: () => null,
96
+ }));
97
+
98
+ mock.module("../security/credential-key.js", () => ({
99
+ credentialKey: (...args: string[]) => args.join("/"),
100
+ }));
101
+
57
102
  // ── Call constants mock ──────────────────────────────────────────────
58
103
 
59
104
  let mockConsultationTimeoutMs = 90_000;
@@ -128,6 +173,63 @@ mock.module("../calls/voice-session-bridge.js", () => {
128
173
  };
129
174
  });
130
175
 
176
+ // ── TTS provider registry setup ──────────────────────────────────────
177
+ // Register test providers so call-controller can resolve the TTS provider
178
+ // abstraction. ElevenLabs is the default native provider (no streaming),
179
+ // while Fish Audio is a synthesized provider (streaming).
180
+
181
+ import {
182
+ _resetTtsProviderRegistry,
183
+ registerTtsProvider,
184
+ } from "../tts/provider-registry.js";
185
+ import type { TtsProvider } from "../tts/types.js";
186
+
187
+ function registerTestTtsProviders(): void {
188
+ _resetTtsProviderRegistry();
189
+
190
+ const elevenlabs: TtsProvider = {
191
+ id: "elevenlabs",
192
+ capabilities: { supportsStreaming: false, supportedFormats: ["mp3"] },
193
+ async synthesize() {
194
+ return { audio: Buffer.from(""), contentType: "audio/mpeg" };
195
+ },
196
+ };
197
+ registerTtsProvider(elevenlabs);
198
+
199
+ const fishAudio: TtsProvider = {
200
+ id: "fish-audio",
201
+ capabilities: {
202
+ supportsStreaming: true,
203
+ supportedFormats: ["mp3", "wav", "opus"],
204
+ },
205
+ async synthesize() {
206
+ return { audio: Buffer.from(""), contentType: "audio/mpeg" };
207
+ },
208
+ async synthesizeStream(_req, _onChunk) {
209
+ return { audio: Buffer.from(""), contentType: "audio/mpeg" };
210
+ },
211
+ };
212
+ registerTtsProvider(fishAudio);
213
+
214
+ const deepgram: TtsProvider = {
215
+ id: "deepgram",
216
+ capabilities: {
217
+ supportsStreaming: false,
218
+ supportedFormats: ["mp3", "wav", "opus"],
219
+ },
220
+ async synthesize() {
221
+ return {
222
+ audio: Buffer.from("fake-deepgram-audio"),
223
+ contentType: "audio/mpeg",
224
+ };
225
+ },
226
+ };
227
+ registerTtsProvider(deepgram);
228
+ }
229
+
230
+ // Register providers immediately so they're available for all tests
231
+ registerTestTtsProviders();
232
+
131
233
  // ── Import source modules after all mocks are registered ────────────
132
234
 
133
235
  import { CallController } from "../calls/call-controller.js";
@@ -139,7 +241,9 @@ import {
139
241
  getPendingQuestion,
140
242
  updateCallSession,
141
243
  } from "../calls/call-store.js";
142
- import type { RelayConnection } from "../calls/relay-server.js";
244
+ import type { CallTransport } from "../calls/call-transport.js";
245
+ import { resolveCallTtsProvider } from "../calls/resolve-call-tts-provider.js";
246
+ import { loadConfig } from "../config/loader.js";
143
247
  import {
144
248
  getCanonicalGuardianRequest,
145
249
  getPendingCanonicalRequestByCallSessionId,
@@ -154,18 +258,20 @@ afterAll(() => {
154
258
  resetDb();
155
259
  });
156
260
 
157
- // ── RelayConnection mock factory ────────────────────────────────────
261
+ // ── CallTransport mock factory ───────────────────────────────────────
158
262
 
159
- interface MockRelay extends RelayConnection {
263
+ interface MockTransport extends CallTransport {
160
264
  sentTokens: Array<{ token: string; last: boolean }>;
265
+ sentPlayUrls: string[];
161
266
  endCalled: boolean;
162
267
  endReason: string | undefined;
163
268
  mockConnectionState: string;
164
269
  }
165
270
 
166
- function createMockRelay(): MockRelay {
271
+ function createMockTransport(): MockTransport {
167
272
  const state = {
168
273
  sentTokens: [] as Array<{ token: string; last: boolean }>,
274
+ sentPlayUrls: [] as string[],
169
275
  _endCalled: false,
170
276
  _endReason: undefined as string | undefined,
171
277
  _connectionState: "connected",
@@ -175,6 +281,9 @@ function createMockRelay(): MockRelay {
175
281
  get sentTokens() {
176
282
  return state.sentTokens;
177
283
  },
284
+ get sentPlayUrls() {
285
+ return state.sentPlayUrls;
286
+ },
178
287
  get endCalled() {
179
288
  return state._endCalled;
180
289
  },
@@ -190,6 +299,9 @@ function createMockRelay(): MockRelay {
190
299
  sendTextToken(token: string, last: boolean) {
191
300
  state.sentTokens.push({ token, last });
192
301
  },
302
+ sendPlayUrl(url: string) {
303
+ state.sentPlayUrls.push(url);
304
+ },
193
305
  endSession(reason?: string) {
194
306
  state._endCalled = true;
195
307
  state._endReason = reason;
@@ -197,7 +309,7 @@ function createMockRelay(): MockRelay {
197
309
  getConnectionState() {
198
310
  return state._connectionState;
199
311
  },
200
- } as unknown as MockRelay;
312
+ } as MockTransport;
201
313
  }
202
314
 
203
315
  // ── Helpers ─────────────────────────────────────────────────────────
@@ -254,7 +366,7 @@ async function pollUntil(
254
366
  }
255
367
 
256
368
  /**
257
- * Create a call session and a controller wired to a mock relay.
369
+ * Create a call session and a controller wired to a mock transport.
258
370
  */
259
371
  function setupController(
260
372
  task?: string,
@@ -272,17 +384,12 @@ function setupController(
272
384
  task,
273
385
  });
274
386
  updateCallSession(session.id, { status: "in_progress" });
275
- const relay = createMockRelay();
276
- const controller = new CallController(
277
- session.id,
278
- relay as unknown as RelayConnection,
279
- task ?? null,
280
- {
281
- assistantId: opts?.assistantId,
282
- trustContext: opts?.trustContext,
283
- },
284
- );
285
- return { session, relay, controller };
387
+ const transport = createMockTransport();
388
+ const controller = new CallController(session.id, transport, task ?? null, {
389
+ assistantId: opts?.assistantId,
390
+ trustContext: opts?.trustContext,
391
+ });
392
+ return { session, relay: transport, controller };
286
393
  }
287
394
 
288
395
  function getLatestAssistantText(conversationId: string): string | null {
@@ -325,14 +432,14 @@ function setupControllerWithOrigin(task?: string) {
325
432
  status: "in_progress",
326
433
  startedAt: Date.now() - 30_000,
327
434
  });
328
- const relay = createMockRelay();
435
+ const transport = createMockTransport();
329
436
  const controller = new CallController(
330
437
  session.id,
331
- relay as unknown as RelayConnection,
438
+ transport,
332
439
  task ?? null,
333
440
  {},
334
441
  );
335
- return { session, relay, controller };
442
+ return { session, relay: transport, controller };
336
443
  }
337
444
 
338
445
  describe("call-controller", () => {
@@ -345,6 +452,12 @@ describe("call-controller", () => {
345
452
  // Reset consultation timeout to the default (long) value
346
453
  mockConsultationTimeoutMs = 90_000;
347
454
  mockSilenceTimeoutMs = 30_000;
455
+ // Reset TTS config to defaults so per-test mutations don't leak.
456
+ const cfg = loadConfig();
457
+ cfg.services.tts.provider = "elevenlabs";
458
+ cfg.services.tts.providers["fish-audio"].referenceId = "";
459
+ // Reset TTS provider registry to ensure clean state
460
+ registerTestTtsProviders();
348
461
  });
349
462
 
350
463
  // ── handleCallerUtterance ─────────────────────────────────────────
@@ -2291,4 +2404,452 @@ describe("call-controller", () => {
2291
2404
 
2292
2405
  controller.destroy();
2293
2406
  });
2407
+
2408
+ // ── TTS provider abstraction: native-token path ─────────────────────
2409
+
2410
+ test("native provider (ElevenLabs): streams text tokens directly to relay", async () => {
2411
+ // Default config uses ElevenLabs (native, no streaming) — the text
2412
+ // tokens should flow directly through sendTextToken to the relay.
2413
+ mockStartVoiceTurn.mockImplementation(
2414
+ createMockVoiceTurn(["Hello", ", how", " are you?"]),
2415
+ );
2416
+ const { relay, controller } = setupController();
2417
+
2418
+ await controller.handleCallerUtterance("Hi");
2419
+
2420
+ // Verify text tokens were sent directly (not empty — real text content)
2421
+ const nonEmptyTokens = relay.sentTokens.filter((t) => t.token.length > 0);
2422
+ expect(nonEmptyTokens.length).toBeGreaterThan(0);
2423
+ // At least one token should contain actual text content
2424
+ const allText = nonEmptyTokens.map((t) => t.token).join("");
2425
+ expect(allText).toContain("Hello");
2426
+ expect(allText).toContain("how");
2427
+ expect(allText).toContain("are you");
2428
+
2429
+ // The final token should signal end of turn
2430
+ const lastToken = relay.sentTokens[relay.sentTokens.length - 1];
2431
+ expect(lastToken.last).toBe(true);
2432
+
2433
+ controller.destroy();
2434
+ });
2435
+
2436
+ test("native provider (ElevenLabs): strips control markers from streamed text tokens", async () => {
2437
+ mockStartVoiceTurn.mockImplementation(
2438
+ createMockVoiceTurn([
2439
+ "I will check on that. ",
2440
+ "[ASK_GUARDIAN: Is 3pm ok?]",
2441
+ ]),
2442
+ );
2443
+ const { relay, controller } = setupController();
2444
+
2445
+ await controller.handleCallerUtterance("Book an appointment");
2446
+
2447
+ const allText = relay.sentTokens.map((t) => t.token).join("");
2448
+ expect(allText).toContain("I will check on that.");
2449
+ expect(allText).not.toContain("[ASK_GUARDIAN:");
2450
+
2451
+ controller.destroy();
2452
+ });
2453
+
2454
+ test("native provider (ElevenLabs): END_CALL marker handled correctly with text tokens", async () => {
2455
+ mockStartVoiceTurn.mockImplementation(
2456
+ createMockVoiceTurn(["Thanks for calling! ", "[END_CALL]"]),
2457
+ );
2458
+ const { session, relay, controller } = setupController();
2459
+
2460
+ await controller.handleCallerUtterance("Goodbye");
2461
+
2462
+ expect(relay.endCalled).toBe(true);
2463
+ const updatedSession = getCallSession(session.id);
2464
+ expect(updatedSession!.status).toBe("completed");
2465
+
2466
+ const allText = relay.sentTokens.map((t) => t.token).join("");
2467
+ expect(allText).not.toContain("[END_CALL]");
2468
+ expect(allText).toContain("Thanks for calling!");
2469
+
2470
+ controller.destroy();
2471
+ });
2472
+
2473
+ test("synthesized provider: if synthesis fails before first chunk, falls back to text-token speech without sending play URL", async () => {
2474
+ const cfg = loadConfig();
2475
+ cfg.services.tts.provider = "fish-audio";
2476
+ cfg.services.tts.providers["fish-audio"].referenceId = "fish-ref-123";
2477
+
2478
+ _resetTtsProviderRegistry();
2479
+ const elevenlabs: TtsProvider = {
2480
+ id: "elevenlabs",
2481
+ capabilities: { supportsStreaming: false, supportedFormats: ["mp3"] },
2482
+ async synthesize() {
2483
+ return { audio: Buffer.from(""), contentType: "audio/mpeg" };
2484
+ },
2485
+ };
2486
+ registerTtsProvider(elevenlabs);
2487
+
2488
+ const fishAudioFailing: TtsProvider = {
2489
+ id: "fish-audio",
2490
+ capabilities: {
2491
+ supportsStreaming: true,
2492
+ supportedFormats: ["mp3", "wav", "opus"],
2493
+ },
2494
+ async synthesize() {
2495
+ throw new Error("fish-audio synth failure");
2496
+ },
2497
+ async synthesizeStream() {
2498
+ throw new Error("fish-audio stream failure");
2499
+ },
2500
+ };
2501
+ registerTtsProvider(fishAudioFailing);
2502
+
2503
+ mockStartVoiceTurn.mockImplementation(
2504
+ createMockVoiceTurn(["Hello from synthesized path"]),
2505
+ );
2506
+ const { relay, controller } = setupController();
2507
+
2508
+ await controller.handleCallerUtterance("Hi");
2509
+
2510
+ // No play URL should be emitted when synthesis fails before first chunk.
2511
+ expect(relay.sentPlayUrls.length).toBe(0);
2512
+
2513
+ // Fallback token speech should still reach the caller.
2514
+ const fallbackText = relay.sentTokens.map((t) => t.token).join("");
2515
+ expect(fallbackText).toContain("Hello from synthesized path");
2516
+
2517
+ const lastToken = relay.sentTokens[relay.sentTokens.length - 1];
2518
+ expect(lastToken.last).toBe(true);
2519
+
2520
+ controller.destroy();
2521
+ });
2522
+
2523
+ test("Deepgram selected path resolves useSynthesizedPath to true", () => {
2524
+ const cfg = loadConfig();
2525
+ cfg.services.tts.provider = "deepgram";
2526
+
2527
+ const result = resolveCallTtsProvider();
2528
+ expect(result.provider).not.toBeNull();
2529
+ expect(result.provider!.id).toBe("deepgram");
2530
+ expect(result.useSynthesizedPath).toBe(true);
2531
+ });
2532
+
2533
+ test("Deepgram synthesis failure does NOT fall back to native token TTS", async () => {
2534
+ const cfg = loadConfig();
2535
+ cfg.services.tts.provider = "deepgram";
2536
+
2537
+ _resetTtsProviderRegistry();
2538
+ const elevenlabs: TtsProvider = {
2539
+ id: "elevenlabs",
2540
+ capabilities: { supportsStreaming: false, supportedFormats: ["mp3"] },
2541
+ async synthesize() {
2542
+ return { audio: Buffer.from(""), contentType: "audio/mpeg" };
2543
+ },
2544
+ };
2545
+ registerTtsProvider(elevenlabs);
2546
+
2547
+ const deepgramFailing: TtsProvider = {
2548
+ id: "deepgram",
2549
+ capabilities: {
2550
+ supportsStreaming: false,
2551
+ supportedFormats: ["mp3", "wav", "opus"],
2552
+ },
2553
+ async synthesize() {
2554
+ const err = new Error("Deepgram TTS returned 503: service unavailable");
2555
+ (err as Error & { code?: string }).code = "DEEPGRAM_TTS_HTTP_ERROR";
2556
+ throw err;
2557
+ },
2558
+ };
2559
+ registerTtsProvider(deepgramFailing);
2560
+
2561
+ mockStartVoiceTurn.mockImplementation(
2562
+ createMockVoiceTurn(["Hello from deepgram path"]),
2563
+ );
2564
+ const { relay, controller } = setupController();
2565
+
2566
+ await controller.handleCallerUtterance("Hi");
2567
+
2568
+ // No play URL should be emitted when synthesis fails.
2569
+ expect(relay.sentPlayUrls.length).toBe(0);
2570
+
2571
+ // Deepgram should NOT have fallen back to sending the LLM text as
2572
+ // native token TTS. Instead, the outer error handler should have
2573
+ // produced a recovery message ("Could you repeat that?").
2574
+ const allTokenText = relay.sentTokens.map((t) => t.token).join("");
2575
+ expect(allTokenText).not.toContain("Hello from deepgram path");
2576
+ expect(allTokenText).toContain("technical issue");
2577
+
2578
+ controller.destroy();
2579
+ });
2580
+
2581
+ test("Fish Audio synthesis failure still falls back to native token TTS (unchanged behavior)", async () => {
2582
+ const cfg = loadConfig();
2583
+ cfg.services.tts.provider = "fish-audio";
2584
+ cfg.services.tts.providers["fish-audio"].referenceId = "fish-ref-abc";
2585
+
2586
+ _resetTtsProviderRegistry();
2587
+ const elevenlabs: TtsProvider = {
2588
+ id: "elevenlabs",
2589
+ capabilities: { supportsStreaming: false, supportedFormats: ["mp3"] },
2590
+ async synthesize() {
2591
+ return { audio: Buffer.from(""), contentType: "audio/mpeg" };
2592
+ },
2593
+ };
2594
+ registerTtsProvider(elevenlabs);
2595
+
2596
+ const fishAudioFailing: TtsProvider = {
2597
+ id: "fish-audio",
2598
+ capabilities: {
2599
+ supportsStreaming: true,
2600
+ supportedFormats: ["mp3", "wav", "opus"],
2601
+ },
2602
+ async synthesize() {
2603
+ throw new Error("fish-audio synth failure");
2604
+ },
2605
+ async synthesizeStream() {
2606
+ throw new Error("fish-audio stream failure");
2607
+ },
2608
+ };
2609
+ registerTtsProvider(fishAudioFailing);
2610
+
2611
+ mockStartVoiceTurn.mockImplementation(
2612
+ createMockVoiceTurn(["Hello from fish path"]),
2613
+ );
2614
+ const { relay, controller } = setupController();
2615
+
2616
+ await controller.handleCallerUtterance("Hi");
2617
+
2618
+ // Fish Audio fallback: the LLM text should reach the caller
2619
+ // via native token TTS despite synthesis failure.
2620
+ const allTokenText = relay.sentTokens.map((t) => t.token).join("");
2621
+ expect(allTokenText).toContain("Hello from fish path");
2622
+
2623
+ controller.destroy();
2624
+ });
2625
+
2626
+ // ── TTS provider abstraction: interruption behavior ─────────────────
2627
+
2628
+ test("handleInterrupt: cancels synthesis abort controller for native provider path", async () => {
2629
+ // Using the default native provider (ElevenLabs) — no synthesis abort
2630
+ // controller should be active, but interrupt should still work cleanly.
2631
+ mockStartVoiceTurn.mockImplementation(
2632
+ async (opts: {
2633
+ signal?: AbortSignal;
2634
+ onTextDelta: (t: string) => void;
2635
+ onComplete: () => void;
2636
+ }) => {
2637
+ return new Promise((resolve) => {
2638
+ const timeout = setTimeout(() => {
2639
+ opts.onTextDelta("This should be interrupted");
2640
+ opts.onComplete();
2641
+ resolve({ turnId: "run-1", abort: () => {} });
2642
+ }, 1000);
2643
+
2644
+ opts.signal?.addEventListener(
2645
+ "abort",
2646
+ () => {
2647
+ clearTimeout(timeout);
2648
+ opts.onComplete();
2649
+ resolve({ turnId: "run-1", abort: () => {} });
2650
+ },
2651
+ { once: true },
2652
+ );
2653
+ });
2654
+ },
2655
+ );
2656
+
2657
+ const { relay, controller } = setupController();
2658
+ const turnPromise = controller.handleCallerUtterance("Start speaking");
2659
+ await new Promise((r) => setTimeout(r, 5));
2660
+ controller.handleInterrupt();
2661
+ await turnPromise;
2662
+
2663
+ // Should have sent an end-of-turn marker
2664
+ const endTurnMarkers = relay.sentTokens.filter(
2665
+ (t) => t.token === "" && t.last === true,
2666
+ );
2667
+ expect(endTurnMarkers.length).toBeGreaterThan(0);
2668
+ expect(controller.getState()).toBe("idle");
2669
+
2670
+ controller.destroy();
2671
+ });
2672
+
2673
+ // ── Shared TTS provider resolution ──────────────────────────────────
2674
+
2675
+ describe("resolveCallTtsProvider (shared helper)", () => {
2676
+ test("returns native path with elevenlabs (non-streaming provider)", () => {
2677
+ // Default config has provider: "elevenlabs" which is registered as
2678
+ // non-streaming in registerTestTtsProviders()
2679
+ const result = resolveCallTtsProvider();
2680
+ expect(result.provider).not.toBeNull();
2681
+ expect(result.provider!.id).toBe("elevenlabs");
2682
+ expect(result.useSynthesizedPath).toBe(false);
2683
+ expect(result.audioFormat).toBe("mp3");
2684
+ });
2685
+
2686
+ test("returns fallback when provider registry is empty", () => {
2687
+ _resetTtsProviderRegistry();
2688
+ const result = resolveCallTtsProvider();
2689
+ expect(result.provider).toBeNull();
2690
+ expect(result.useSynthesizedPath).toBe(false);
2691
+ expect(result.audioFormat).toBe("mp3");
2692
+ });
2693
+
2694
+ test("degrades fish-audio synthesized path when referenceId is missing", () => {
2695
+ const cfg = loadConfig();
2696
+ cfg.services.tts.provider = "fish-audio";
2697
+ cfg.services.tts.providers["fish-audio"].referenceId = "";
2698
+
2699
+ const result = resolveCallTtsProvider();
2700
+ expect(result.provider).toBeNull();
2701
+ expect(result.useSynthesizedPath).toBe(false);
2702
+ expect(result.audioFormat).toBe("mp3");
2703
+ });
2704
+
2705
+ test("call controller LLM path uses shared resolution (native provider sends text tokens)", async () => {
2706
+ // With the default elevenlabs provider (non-streaming), the call
2707
+ // controller should send text tokens directly to the relay (native path).
2708
+ mockStartVoiceTurn.mockImplementation(
2709
+ createMockVoiceTurn(["Hello", " caller"]),
2710
+ );
2711
+ const { relay, controller } = setupController();
2712
+
2713
+ await controller.handleCallerUtterance("Hi");
2714
+
2715
+ // Native path: text tokens should be sent, no play URLs
2716
+ const nonEmptyTokens = relay.sentTokens.filter((t) => t.token.length > 0);
2717
+ expect(nonEmptyTokens.length).toBeGreaterThan(0);
2718
+ expect(relay.sentPlayUrls.length).toBe(0);
2719
+
2720
+ controller.destroy();
2721
+ });
2722
+
2723
+ test("returns synthesized path with deepgram provider", () => {
2724
+ const cfg = loadConfig();
2725
+ cfg.services.tts.provider = "deepgram";
2726
+
2727
+ const result = resolveCallTtsProvider();
2728
+ expect(result.provider).not.toBeNull();
2729
+ expect(result.provider!.id).toBe("deepgram");
2730
+ expect(result.useSynthesizedPath).toBe(true);
2731
+ expect(result.audioFormat).toBe("mp3");
2732
+ });
2733
+
2734
+ test("Deepgram does not apply fish-audio referenceId gate", () => {
2735
+ // Deepgram has no referenceId requirement. Verify the fish-audio
2736
+ // config gate does not apply to deepgram resolution.
2737
+ const cfg = loadConfig();
2738
+ cfg.services.tts.provider = "deepgram";
2739
+ // fish-audio referenceId left empty — should not affect deepgram.
2740
+ cfg.services.tts.providers["fish-audio"].referenceId = "";
2741
+
2742
+ const result = resolveCallTtsProvider();
2743
+ expect(result.provider).not.toBeNull();
2744
+ expect(result.provider!.id).toBe("deepgram");
2745
+ expect(result.useSynthesizedPath).toBe(true);
2746
+ });
2747
+ });
2748
+
2749
+ // ── handleBargeIn ───────────────────────────────────────────────────
2750
+
2751
+ describe("handleBargeIn", () => {
2752
+ test("handleBargeIn returns false and does not abort when controller is idle", () => {
2753
+ const { relay, controller } = setupController();
2754
+
2755
+ // Controller starts idle after construction
2756
+ expect(controller.getState()).toBe("idle");
2757
+ const result = controller.handleBargeIn();
2758
+
2759
+ expect(result).toBe(false);
2760
+ // No end-of-turn token should have been sent (no interruption)
2761
+ const endTokens = relay.sentTokens.filter(
2762
+ (t) => t.last === true && t.token === "",
2763
+ );
2764
+ expect(endTokens.length).toBe(0);
2765
+
2766
+ controller.destroy();
2767
+ });
2768
+
2769
+ test("handleBargeIn returns false when controller is processing", async () => {
2770
+ // Use a slow turn that never completes so we can observe
2771
+ // the processing state.
2772
+ mockStartVoiceTurn.mockImplementation(
2773
+ async (opts: {
2774
+ onTextDelta: (t: string) => void;
2775
+ onComplete: () => void;
2776
+ signal?: AbortSignal;
2777
+ }) => {
2778
+ // Don't call onComplete — keep in processing/speaking
2779
+ return { turnId: "run-slow", abort: () => opts.onComplete() };
2780
+ },
2781
+ );
2782
+
2783
+ const { relay, controller } = setupController();
2784
+ // Kick off a turn (moves to speaking state)
2785
+ const turnPromise = controller.handleCallerUtterance("Hello");
2786
+
2787
+ // Wait for microtasks to settle
2788
+ for (let i = 0; i < 5; i++) await Promise.resolve();
2789
+
2790
+ // The controller transitions to "speaking" once runTurnInner starts.
2791
+ // Before any onTextDelta, a barge-in should be accepted if speaking.
2792
+ // But if no text has been emitted yet, the state is "speaking" per
2793
+ // the implementation (state is set to speaking at the start of
2794
+ // runTurnInner). So handleBargeIn should accept. Let's verify the
2795
+ // state and behavior.
2796
+ const bargeResult = controller.handleBargeIn();
2797
+
2798
+ // Regardless of the specific state, if accepted the transport
2799
+ // should see an interrupt token.
2800
+ if (bargeResult) {
2801
+ const endTokens = relay.sentTokens.filter(
2802
+ (t) => t.last === true && t.token === "",
2803
+ );
2804
+ expect(endTokens.length).toBeGreaterThan(0);
2805
+ }
2806
+
2807
+ // Cleanup: abort the pending turn
2808
+ controller.destroy();
2809
+ await turnPromise.catch(() => {});
2810
+ });
2811
+
2812
+ test("handleBargeIn returns true and interrupts when controller is speaking", async () => {
2813
+ // Create a turn that holds the speaking state long enough to test barge-in
2814
+ let resolveComplete: () => void;
2815
+ const completePromise = new Promise<void>((r) => {
2816
+ resolveComplete = r;
2817
+ });
2818
+
2819
+ mockStartVoiceTurn.mockImplementation(
2820
+ async (opts: {
2821
+ onTextDelta: (t: string) => void;
2822
+ onComplete: () => void;
2823
+ signal?: AbortSignal;
2824
+ }) => {
2825
+ opts.onTextDelta("Hello");
2826
+ opts.onTextDelta(" there");
2827
+ // Don't complete yet — wait for external signal
2828
+ opts.signal?.addEventListener("abort", () => {
2829
+ resolveComplete!();
2830
+ });
2831
+ await completePromise;
2832
+ opts.onComplete();
2833
+ return { turnId: "run-barge", abort: () => resolveComplete!() };
2834
+ },
2835
+ );
2836
+
2837
+ const { controller } = setupController();
2838
+ const turnPromise = controller.handleCallerUtterance("Hi");
2839
+
2840
+ // Let microtasks settle so onTextDelta runs
2841
+ for (let i = 0; i < 10; i++) await Promise.resolve();
2842
+
2843
+ expect(controller.getState()).toBe("speaking");
2844
+
2845
+ const result = controller.handleBargeIn();
2846
+ expect(result).toBe(true);
2847
+
2848
+ // After barge-in, controller should be idle
2849
+ expect(controller.getState()).toBe("idle");
2850
+
2851
+ controller.destroy();
2852
+ await turnPromise.catch(() => {});
2853
+ });
2854
+ });
2294
2855
  });