@vellumai/assistant 0.6.3 → 0.6.5

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 (1114) hide show
  1. package/.prettierignore +5 -0
  2. package/ARCHITECTURE.md +298 -39
  3. package/Dockerfile +14 -3
  4. package/README.md +3 -4
  5. package/bun.lock +13 -16
  6. package/docs/architecture/integrations.md +1 -20
  7. package/docs/architecture/security.md +16 -16
  8. package/docs/backup-troubleshooting.md +52 -0
  9. package/docs/browser-use-architecture-phase2.md +174 -0
  10. package/docs/error-handling.md +111 -0
  11. package/docs/skills.md +10 -10
  12. package/docs/stt-provider-onboarding.md +121 -0
  13. package/knip.json +20 -3
  14. package/node_modules/@vellumai/ces-contracts/bun.lock +8 -6
  15. package/node_modules/@vellumai/ces-contracts/package.json +5 -4
  16. package/node_modules/@vellumai/ces-contracts/src/__tests__/trust-rules.test.ts +471 -0
  17. package/node_modules/@vellumai/ces-contracts/src/trust-rules.ts +398 -4
  18. package/node_modules/@vellumai/credential-storage/bun.lock +2 -2
  19. package/node_modules/@vellumai/credential-storage/package.json +2 -2
  20. package/node_modules/@vellumai/credential-storage/src/oauth-runtime.ts +20 -2
  21. package/node_modules/@vellumai/egress-proxy/bun.lock +2 -2
  22. package/node_modules/@vellumai/egress-proxy/package.json +2 -2
  23. package/openapi.yaml +1094 -72
  24. package/package.json +9 -8
  25. package/scripts/generate-openapi.ts +50 -12
  26. package/scripts/test.sh +73 -18
  27. package/src/__tests__/agent-image-optimize.test.ts +28 -0
  28. package/src/__tests__/agent-loop-callsite-precedence.test.ts +318 -0
  29. package/src/__tests__/agent-loop-sentry-hygiene.test.ts +137 -0
  30. package/src/__tests__/agent-loop.test.ts +235 -1
  31. package/src/__tests__/anthropic-error-formatting.test.ts +98 -0
  32. package/src/__tests__/anthropic-provider.test.ts +434 -12
  33. package/src/__tests__/approval-cascade.test.ts +31 -10
  34. package/src/__tests__/approval-routes-http.test.ts +134 -10
  35. package/src/__tests__/assistant-attachments.test.ts +44 -0
  36. package/src/__tests__/assistant-feature-flags-integration.test.ts +29 -0
  37. package/src/__tests__/auto-analysis-end-to-end.test.ts +550 -0
  38. package/src/__tests__/auto-analysis-prompt.test.ts +50 -0
  39. package/src/__tests__/browser-fill-credential.test.ts +12 -1
  40. package/src/__tests__/browser-identifier-parity-guard.test.ts +53 -0
  41. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +23 -33
  42. package/src/__tests__/browser-skill-endstate.test.ts +52 -159
  43. package/src/__tests__/btw-routes.test.ts +54 -1
  44. package/src/__tests__/call-controller.test.ts +582 -22
  45. package/src/__tests__/call-site-routing-provider.test.ts +214 -0
  46. package/src/__tests__/catalog-cache.test.ts +27 -4
  47. package/src/__tests__/catalog-files.test.ts +138 -0
  48. package/src/__tests__/channel-approval-routes.test.ts +4 -4
  49. package/src/__tests__/channel-invite-transport.test.ts +2 -2
  50. package/src/__tests__/channel-readiness-routes.test.ts +16 -20
  51. package/src/__tests__/channel-readiness-service.test.ts +12 -7
  52. package/src/__tests__/channel-reply-delivery.test.ts +300 -2
  53. package/src/__tests__/checker.test.ts +576 -502
  54. package/src/__tests__/clawhub-files.test.ts +347 -0
  55. package/src/__tests__/cli-command-risk-guard.test.ts +30 -33
  56. package/src/__tests__/commit-message-enrichment-service.test.ts +36 -19
  57. package/src/__tests__/compaction-circuit-breaker.test.ts +336 -0
  58. package/src/__tests__/compaction.benchmark.test.ts +1 -1
  59. package/src/__tests__/config-analysis.test.ts +83 -0
  60. package/src/__tests__/config-loader-backfill.test.ts +174 -0
  61. package/src/__tests__/config-loader-corrupt.test.ts +183 -0
  62. package/src/__tests__/config-loader-quarantine-bulletin.test.ts +202 -0
  63. package/src/__tests__/config-schema-cmd.test.ts +11 -5
  64. package/src/__tests__/config-schema.test.ts +1458 -198
  65. package/src/__tests__/config-watcher-cleanup-throttle.test.ts +339 -0
  66. package/src/__tests__/config-watcher.test.ts +45 -10
  67. package/src/__tests__/contact-store-user-file.test.ts +511 -0
  68. package/src/__tests__/contacts-write.test.ts +197 -0
  69. package/src/__tests__/context-token-estimator.test.ts +191 -1
  70. package/src/__tests__/context-window-manager.test.ts +618 -2
  71. package/src/__tests__/conversation-abort-tool-results.test.ts +32 -16
  72. package/src/__tests__/conversation-agent-loop-overflow.test.ts +62 -17
  73. package/src/__tests__/conversation-agent-loop.test.ts +510 -84
  74. package/src/__tests__/conversation-attachments.test.ts +1 -1
  75. package/src/__tests__/conversation-confirmation-signals.test.ts +165 -9
  76. package/src/__tests__/conversation-error.test.ts +102 -1
  77. package/src/__tests__/conversation-history-web-search.test.ts +17 -4
  78. package/src/__tests__/conversation-init.benchmark.test.ts +42 -1
  79. package/src/__tests__/conversation-launcher-skill-regression.test.ts +51 -0
  80. package/src/__tests__/conversation-lifecycle.test.ts +336 -0
  81. package/src/__tests__/conversation-list-source.test.ts +145 -0
  82. package/src/__tests__/conversation-load-history-repair.test.ts +27 -10
  83. package/src/__tests__/conversation-pre-run-repair.test.ts +32 -16
  84. package/src/__tests__/conversation-process-callsite.test.ts +306 -0
  85. package/src/__tests__/conversation-provider-retry-repair.test.ts +32 -16
  86. package/src/__tests__/conversation-queue.test.ts +932 -76
  87. package/src/__tests__/conversation-routes-disk-view.test.ts +299 -1
  88. package/src/__tests__/conversation-routes-slash-commands.test.ts +31 -3
  89. package/src/__tests__/conversation-runtime-assembly.test.ts +2790 -55
  90. package/src/__tests__/conversation-runtime-workspace.test.ts +12 -12
  91. package/src/__tests__/conversation-skill-tools.test.ts +12 -143
  92. package/src/__tests__/conversation-slash-commands.test.ts +33 -0
  93. package/src/__tests__/conversation-slash-queue.test.ts +120 -34
  94. package/src/__tests__/conversation-slash-unknown.test.ts +32 -16
  95. package/src/__tests__/conversation-speed-override.test.ts +30 -11
  96. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +1035 -0
  97. package/src/__tests__/conversation-surfaces-standalone.test.ts +630 -0
  98. package/src/__tests__/conversation-title-service.test.ts +2 -2
  99. package/src/__tests__/conversation-tool-setup-batch-authorized.test.ts +226 -0
  100. package/src/__tests__/conversation-unread-route.test.ts +2 -2
  101. package/src/__tests__/conversation-usage.test.ts +3 -1
  102. package/src/__tests__/conversation-workspace-cache-state.test.ts +31 -10
  103. package/src/__tests__/conversation-workspace-injection.test.ts +45 -15
  104. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +46 -16
  105. package/src/__tests__/credential-broker-browser-fill.test.ts +110 -0
  106. package/src/__tests__/credential-health-service.test.ts +352 -0
  107. package/src/__tests__/credential-security-invariants.test.ts +8 -3
  108. package/src/__tests__/credential-storage-oauth-compat.test.ts +18 -0
  109. package/src/__tests__/credential-storage-static-compat.test.ts +28 -0
  110. package/src/__tests__/credential-vault-unit.test.ts +495 -3
  111. package/src/__tests__/credentials-cli.test.ts +32 -16
  112. package/src/__tests__/cross-provider-web-search.test.ts +230 -35
  113. package/src/__tests__/daemon-server-persist-and-process-callsite.test.ts +92 -0
  114. package/src/__tests__/delete-propagation.test.ts +437 -0
  115. package/src/__tests__/deterministic-verification-control-plane.test.ts +10 -1
  116. package/src/__tests__/device-id.test.ts +112 -0
  117. package/src/__tests__/dm-backfill.test.ts +417 -0
  118. package/src/__tests__/dm-persistence.test.ts +227 -0
  119. package/src/__tests__/docker-signing-key-bootstrap.test.ts +167 -4
  120. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +1 -3
  121. package/src/__tests__/edit-propagation.test.ts +280 -0
  122. package/src/__tests__/email-html-renderer.test.ts +71 -0
  123. package/src/__tests__/email-invite-adapter.test.ts +36 -32
  124. package/src/__tests__/emit-event-signal.test.ts +71 -0
  125. package/src/__tests__/ephemeral-permissions.test.ts +93 -3
  126. package/src/__tests__/estimator-calibration-integration.test.ts +208 -0
  127. package/src/__tests__/estimator-calibration.test.ts +213 -0
  128. package/src/__tests__/extension-id-sync-guard.test.ts +101 -15
  129. package/src/__tests__/file-write-tool.test.ts +151 -1
  130. package/src/__tests__/filing-service.test.ts +255 -0
  131. package/src/__tests__/fixtures/mock-chrome-extension.ts +11 -0
  132. package/src/__tests__/gateway-only-enforcement.test.ts +206 -1
  133. package/src/__tests__/gateway-only-guard.test.ts +0 -1
  134. package/src/__tests__/gemini-provider.test.ts +64 -3
  135. package/src/__tests__/get-skill-detail-audit.test.ts +325 -0
  136. package/src/__tests__/guardian-grant-minting.test.ts +8 -0
  137. package/src/__tests__/headless-browser-interactions.test.ts +44 -1
  138. package/src/__tests__/headless-browser-mode.test.ts +614 -0
  139. package/src/__tests__/headless-browser-navigate.test.ts +142 -5
  140. package/src/__tests__/headless-browser-read-tools.test.ts +11 -0
  141. package/src/__tests__/headless-browser-snapshot.test.ts +10 -0
  142. package/src/__tests__/heartbeat-service.test.ts +166 -32
  143. package/src/__tests__/home-state-routes.test.ts +162 -0
  144. package/src/__tests__/host-bash-proxy.test.ts +0 -5
  145. package/src/__tests__/host-browser-e2e-cloud.test.ts +138 -4
  146. package/src/__tests__/host-browser-e2e-self-hosted.test.ts +4 -4
  147. package/src/__tests__/host-browser-ws-events-e2e.test.ts +103 -0
  148. package/src/__tests__/host-cu-proxy.test.ts +0 -5
  149. package/src/__tests__/host-shell-tool.test.ts +124 -18
  150. package/src/__tests__/http-user-message-parity.test.ts +29 -1
  151. package/src/__tests__/identity-intro-cache.test.ts +40 -10
  152. package/src/__tests__/inbound-slack-persistence.test.ts +340 -0
  153. package/src/__tests__/init-feature-flag-overrides.test.ts +38 -112
  154. package/src/__tests__/intent-routing.test.ts +1 -40
  155. package/src/__tests__/jobs-store-upsert-debounced.test.ts +141 -0
  156. package/src/__tests__/llm-catalog-parity.test.ts +174 -0
  157. package/src/__tests__/llm-context-normalization.test.ts +609 -0
  158. package/src/__tests__/llm-context-route-provider.test.ts +86 -5
  159. package/src/__tests__/llm-resolver.test.ts +214 -0
  160. package/src/__tests__/llm-schema.test.ts +223 -0
  161. package/src/__tests__/llm-usage-store.test.ts +363 -0
  162. package/src/__tests__/managed-proxy-context.test.ts +6 -2
  163. package/src/__tests__/media-stream-output.test.ts +555 -0
  164. package/src/__tests__/media-stream-parser.test.ts +374 -0
  165. package/src/__tests__/media-stream-server-integration.test.ts +1234 -0
  166. package/src/__tests__/media-stream-stt-session.test.ts +588 -0
  167. package/src/__tests__/media-turn-detector.test.ts +440 -0
  168. package/src/__tests__/message-queue.test.ts +125 -0
  169. package/src/__tests__/messaging-skill-split.test.ts +3 -34
  170. package/src/__tests__/migration-export-http.test.ts +6 -6
  171. package/src/__tests__/migration-import-commit-http.test.ts +8 -6
  172. package/src/__tests__/migration-import-from-url.test.ts +684 -0
  173. package/src/__tests__/migration-import-preflight-http.test.ts +6 -5
  174. package/src/__tests__/migration-validate-http.test.ts +3 -3
  175. package/src/__tests__/mock-gateway-ipc.ts +151 -0
  176. package/src/__tests__/model-intents.test.ts +10 -84
  177. package/src/__tests__/notification-decision-fallback.test.ts +0 -10
  178. package/src/__tests__/notification-decision-identity.test.ts +0 -9
  179. package/src/__tests__/notification-decision-recipient-context.test.ts +0 -9
  180. package/src/__tests__/oauth-apps-routes.test.ts +1 -0
  181. package/src/__tests__/oauth-cli.test.ts +2 -0
  182. package/src/__tests__/oauth-connect-orchestrator.test.ts +2 -0
  183. package/src/__tests__/oauth-provider-serializer.test.ts +1 -0
  184. package/src/__tests__/oauth-providers-routes.test.ts +2 -0
  185. package/src/__tests__/oauth-store.test.ts +95 -7
  186. package/src/__tests__/oauth2-gateway-transport.test.ts +257 -9
  187. package/src/__tests__/oauth2-refresh-retry.test.ts +279 -0
  188. package/src/__tests__/onboarding-template-contract.test.ts +6 -13
  189. package/src/__tests__/openai-provider.test.ts +183 -0
  190. package/src/__tests__/openai-responses-cutover-guard.test.ts +184 -0
  191. package/src/__tests__/openai-responses-provider.test.ts +1501 -0
  192. package/src/__tests__/openrouter-provider-only.test.ts +135 -0
  193. package/src/__tests__/openrouter-token-estimation.test.ts +100 -0
  194. package/src/__tests__/outbound-slack-persistence.test.ts +293 -0
  195. package/src/__tests__/permission-checker-host-gate.test.ts +1 -1
  196. package/src/__tests__/permission-mode.test.ts +16 -0
  197. package/src/__tests__/permission-types.test.ts +0 -1
  198. package/src/__tests__/persona-resolver.test.ts +251 -0
  199. package/src/__tests__/pkb-autoinject.test.ts +37 -1
  200. package/src/__tests__/platform-bash-auto-approve.test.ts +5 -1
  201. package/src/__tests__/platform.test.ts +92 -1
  202. package/src/__tests__/post-turn-tool-result-truncation.test.ts +47 -0
  203. package/src/__tests__/prechat-onboarding-contract.test.ts +267 -0
  204. package/src/__tests__/pricing.test.ts +224 -3
  205. package/src/__tests__/profiler-routes.test.ts +1 -1
  206. package/src/__tests__/provider-commit-message-generator.test.ts +14 -84
  207. package/src/__tests__/provider-env-vars-scope.test.ts +52 -0
  208. package/src/__tests__/provider-error-scenarios.test.ts +135 -6
  209. package/src/__tests__/provider-managed-proxy-integration.test.ts +42 -11
  210. package/src/__tests__/provider-registry-ollama.test.ts +1 -2
  211. package/src/__tests__/proxy-approval-callback.test.ts +0 -1
  212. package/src/__tests__/qdrant-manager.test.ts +29 -8
  213. package/src/__tests__/reaction-persistence.test.ts +560 -0
  214. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +194 -0
  215. package/src/__tests__/relationship-state-contract.test.ts +175 -0
  216. package/src/__tests__/relay-server.test.ts +424 -6
  217. package/src/__tests__/require-fresh-approval.test.ts +1 -1
  218. package/src/__tests__/retry-openrouter-only-normalization.test.ts +136 -0
  219. package/src/__tests__/retry-thinking-tool-choice.test.ts +226 -0
  220. package/src/__tests__/risk-classifier-parity.test.ts +230 -0
  221. package/src/__tests__/sanitize-config-for-transfer.test.ts +78 -1
  222. package/src/__tests__/search-skills-unified.test.ts +118 -0
  223. package/src/__tests__/secret-ingress-http.test.ts +28 -0
  224. package/src/__tests__/secret-prompter-channel-fallback.test.ts +125 -0
  225. package/src/__tests__/secret-routes-managed-proxy.test.ts +2 -3
  226. package/src/__tests__/secret-scanner-executor.test.ts +5 -1
  227. package/src/__tests__/secure-keys.test.ts +107 -0
  228. package/src/__tests__/send-endpoint-busy.test.ts +34 -2
  229. package/src/__tests__/sequence-store.test.ts +1 -1
  230. package/src/__tests__/server-history-render.test.ts +80 -0
  231. package/src/__tests__/settings-routes.test.ts +201 -0
  232. package/src/__tests__/shell-parser-property.test.ts +13 -13
  233. package/src/__tests__/skill-cache-store.test.ts +182 -0
  234. package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
  235. package/src/__tests__/skills-file-content-endpoint.test.ts +276 -145
  236. package/src/__tests__/skills-files-catalog-fallback.test.ts +381 -93
  237. package/src/__tests__/skills.test.ts +19 -30
  238. package/src/__tests__/skillssh-files.test.ts +446 -0
  239. package/src/__tests__/slack-app-setup-skill-regression.test.ts +3 -1
  240. package/src/__tests__/slack-block-formatting.test.ts +110 -0
  241. package/src/__tests__/slack-channel-config.test.ts +564 -1
  242. package/src/__tests__/slack-skill.test.ts +3 -8
  243. package/src/__tests__/starter-bundle.test.ts +35 -0
  244. package/src/__tests__/stt-catalog-parity.test.ts +282 -0
  245. package/src/__tests__/stt-stream-session.test.ts +535 -0
  246. package/src/__tests__/subagent-call-site-routing.test.ts +280 -0
  247. package/src/__tests__/suggestion-routes.test.ts +160 -3
  248. package/src/__tests__/system-prompt.test.ts +126 -53
  249. package/src/__tests__/task-runner.test.ts +3 -1
  250. package/src/__tests__/tcc-sandbox-deny.test.ts +198 -0
  251. package/src/__tests__/telephony-stt-routing.test.ts +329 -0
  252. package/src/__tests__/terminal-tools.test.ts +26 -7
  253. package/src/__tests__/test-preload.ts +18 -0
  254. package/src/__tests__/test-support/browser-skill-harness.ts +2 -49
  255. package/src/__tests__/thread-backfill.test.ts +941 -0
  256. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -2
  257. package/src/__tests__/tool-executor-lifecycle-events.test.ts +10 -6
  258. package/src/__tests__/tool-executor-shell-integration.test.ts +4 -0
  259. package/src/__tests__/tool-executor.test.ts +88 -113
  260. package/src/__tests__/tool-result-truncation.test.ts +36 -0
  261. package/src/__tests__/trust-store.test.ts +442 -103
  262. package/src/__tests__/trusted-contact-approval-notifier.test.ts +1 -1
  263. package/src/__tests__/tts-catalog-parity.test.ts +345 -0
  264. package/src/__tests__/twilio-routes-twiml.test.ts +512 -114
  265. package/src/__tests__/twilio-routes.test.ts +376 -0
  266. package/src/__tests__/unicode.test.ts +293 -0
  267. package/src/__tests__/update-bulletin-job.test.ts +389 -0
  268. package/src/__tests__/usage-cache-backfill-migration.test.ts +3 -1
  269. package/src/__tests__/usage-routes.test.ts +25 -4
  270. package/src/__tests__/user-reference.test.ts +46 -61
  271. package/src/__tests__/verification-control-plane-policy.test.ts +5 -22
  272. package/src/__tests__/voice-config-update.test.ts +403 -0
  273. package/src/__tests__/voice-quality.test.ts +434 -19
  274. package/src/__tests__/voice-session-bridge.test.ts +39 -0
  275. package/src/__tests__/volume-security-guard.test.ts +3 -2
  276. package/src/__tests__/web-search-history.test.ts +337 -0
  277. package/src/__tests__/workspace-heartbeat-service.test.ts +7 -0
  278. package/src/__tests__/workspace-migration-033-stt-service-explicit-config.test.ts +547 -0
  279. package/src/__tests__/workspace-migration-034-remove-calls-voice-transcription-provider.test.ts +596 -0
  280. package/src/__tests__/workspace-migration-039-drop-legacy-llm-keys.test.ts +343 -0
  281. package/src/__tests__/workspace-migration-043-release-notes-latex-rendering.test.ts +202 -0
  282. package/src/__tests__/workspace-migration-045-release-notes-meet-avatar.test.ts +210 -0
  283. package/src/__tests__/workspace-migration-drop-user-md.test.ts +368 -0
  284. package/src/__tests__/workspace-migration-meets.test.ts +244 -0
  285. package/src/__tests__/workspace-migration-seed-device-id.test.ts +14 -20
  286. package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +841 -0
  287. package/src/__tests__/workspace-policy.test.ts +1 -11
  288. package/src/acp/client-handler.ts +1 -2
  289. package/src/agent/image-optimize.ts +24 -12
  290. package/src/agent/loop.ts +251 -19
  291. package/src/avatar/resvg-lazy.test.ts +136 -0
  292. package/src/avatar/resvg-lazy.ts +82 -9
  293. package/src/avatar/traits-png-sync.ts +21 -1
  294. package/src/backup/__tests__/backup-key.test.ts +152 -0
  295. package/src/backup/__tests__/backup-worker.test.ts +767 -0
  296. package/src/backup/__tests__/list-snapshots.test.ts +87 -0
  297. package/src/backup/__tests__/local-writer.test.ts +218 -0
  298. package/src/backup/__tests__/offsite-writer.test.ts +641 -0
  299. package/src/backup/__tests__/paths.test.ts +300 -0
  300. package/src/backup/__tests__/restore.test.ts +498 -0
  301. package/src/backup/__tests__/snapshot-lock.test.ts +352 -0
  302. package/src/backup/__tests__/stream-crypt.test.ts +228 -0
  303. package/src/backup/backup-key.ts +137 -0
  304. package/src/backup/backup-worker.ts +459 -0
  305. package/src/backup/list-snapshots.ts +147 -0
  306. package/src/backup/local-writer.ts +133 -0
  307. package/src/backup/offsite-writer.ts +222 -0
  308. package/src/backup/paths.ts +226 -0
  309. package/src/backup/restore.ts +322 -0
  310. package/src/backup/snapshot-lock.ts +431 -0
  311. package/src/backup/stream-crypt.ts +263 -0
  312. package/src/browser/__tests__/operations.test.ts +163 -0
  313. package/src/browser/identifiers.ts +51 -0
  314. package/src/browser/operations.ts +660 -0
  315. package/src/browser/types.ts +81 -0
  316. package/src/bundler/package-resolver.ts +4 -0
  317. package/src/calls/audio-store.ts +11 -5
  318. package/src/calls/call-controller.ts +226 -71
  319. package/src/calls/call-domain.ts +9 -0
  320. package/src/calls/call-speech-output.ts +190 -0
  321. package/src/calls/call-transport.ts +77 -0
  322. package/src/calls/guardian-question-copy.ts +2 -2
  323. package/src/calls/media-stream-audio-transcode.ts +173 -0
  324. package/src/calls/media-stream-output.ts +660 -0
  325. package/src/calls/media-stream-parser.ts +300 -0
  326. package/src/calls/media-stream-protocol.ts +166 -0
  327. package/src/calls/media-stream-server.ts +592 -0
  328. package/src/calls/media-stream-stt-session.ts +460 -0
  329. package/src/calls/media-turn-detector.ts +230 -0
  330. package/src/calls/relay-server.ts +90 -75
  331. package/src/calls/resolve-call-tts-provider.ts +136 -0
  332. package/src/calls/telephony-stt-routing.ts +145 -0
  333. package/src/calls/tts-call-strategy.ts +161 -0
  334. package/src/calls/tts-text-sanitizer.ts +32 -16
  335. package/src/calls/twilio-routes.ts +281 -17
  336. package/src/calls/voice-quality.ts +78 -35
  337. package/src/calls/voice-session-bridge.ts +9 -1
  338. package/src/channels/types.ts +16 -0
  339. package/src/cli/AGENTS.md +1 -1
  340. package/src/cli/__tests__/run-assistant-command.ts +11 -1
  341. package/src/cli/commands/__tests__/attachment.test.ts +438 -0
  342. package/src/cli/commands/__tests__/backup.test.ts +1165 -0
  343. package/src/cli/commands/__tests__/browser.test.ts +554 -0
  344. package/src/cli/commands/__tests__/cache.test.ts +623 -0
  345. package/src/cli/commands/__tests__/domain-register.test.ts +234 -0
  346. package/src/cli/commands/__tests__/domain-status.test.ts +132 -0
  347. package/src/cli/commands/__tests__/email-attachment.test.ts +422 -0
  348. package/src/cli/commands/__tests__/email-download.test.ts +16 -1
  349. package/src/cli/commands/__tests__/email-list.test.ts +28 -4
  350. package/src/cli/commands/__tests__/email-register.test.ts +4 -4
  351. package/src/cli/commands/__tests__/email-send.test.ts +130 -5
  352. package/src/cli/commands/__tests__/email-status.test.ts +5 -1
  353. package/src/cli/commands/__tests__/email-unregister.test.ts +34 -5
  354. package/src/cli/commands/__tests__/image-generation.test.ts +666 -0
  355. package/src/cli/commands/__tests__/inference-send.test.ts +451 -0
  356. package/src/cli/commands/__tests__/stt-transcribe.test.ts +454 -0
  357. package/src/cli/commands/__tests__/task.test.ts +913 -0
  358. package/src/cli/commands/__tests__/tts-synthesize.test.ts +594 -0
  359. package/src/cli/commands/__tests__/ui-confirm.test.ts +650 -0
  360. package/src/cli/commands/__tests__/ui.test.ts +1215 -0
  361. package/src/cli/commands/__tests__/watchers.test.ts +716 -0
  362. package/src/cli/commands/attachment.ts +182 -0
  363. package/src/cli/commands/backup.ts +993 -0
  364. package/src/cli/commands/browser.ts +350 -0
  365. package/src/cli/commands/cache.ts +341 -0
  366. package/src/cli/commands/completions.ts +0 -3
  367. package/src/cli/commands/config.ts +6 -6
  368. package/src/cli/commands/conversations-import.ts +347 -0
  369. package/src/cli/commands/conversations.ts +90 -0
  370. package/src/cli/commands/credentials.ts +0 -1
  371. package/src/cli/commands/domain.ts +210 -0
  372. package/src/cli/commands/email.ts +308 -16
  373. package/src/cli/commands/image-generation.ts +300 -0
  374. package/src/cli/commands/inference.ts +200 -0
  375. package/src/cli/commands/memory.ts +127 -17
  376. package/src/cli/commands/oauth/__tests__/connect.test.ts +12 -0
  377. package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +1 -0
  378. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +1 -0
  379. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -0
  380. package/src/cli/commands/oauth/mode.ts +12 -3
  381. package/src/cli/commands/oauth/providers.ts +15 -0
  382. package/src/cli/commands/oauth/shared.ts +2 -1
  383. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +4 -10
  384. package/src/cli/commands/platform/__tests__/connect.test.ts +6 -1
  385. package/src/cli/commands/platform/__tests__/disconnect.test.ts +7 -2
  386. package/src/cli/commands/platform/__tests__/status.test.ts +6 -1
  387. package/src/cli/commands/stt.ts +339 -0
  388. package/src/cli/commands/task.ts +795 -0
  389. package/src/cli/commands/trust.ts +50 -19
  390. package/src/cli/commands/tts.ts +273 -0
  391. package/src/cli/commands/ui.ts +670 -0
  392. package/src/cli/commands/watchers.ts +509 -0
  393. package/src/cli/lib/daemon-credential-client.ts +0 -19
  394. package/src/cli/program.ts +53 -8
  395. package/src/cli.ts +0 -37
  396. package/src/config/__tests__/backup-schema.test.ts +134 -0
  397. package/src/config/assistant-feature-flags.ts +61 -62
  398. package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +37 -1
  399. package/src/config/bundled-skills/contacts/SKILL.md +2 -2
  400. package/src/config/bundled-skills/conversations/tools/rename-conversation.ts +23 -1
  401. package/src/config/bundled-skills/media-processing/SKILL.md +3 -9
  402. package/src/config/bundled-skills/media-processing/TOOLS.json +1 -6
  403. package/src/config/bundled-skills/media-processing/__tests__/audio-transcribe.test.ts +125 -0
  404. package/src/config/bundled-skills/media-processing/__tests__/extract-keyframes.test.ts +181 -0
  405. package/src/config/bundled-skills/media-processing/__tests__/preprocess-audio.test.ts +141 -0
  406. package/src/config/bundled-skills/media-processing/services/audio-transcribe.ts +32 -87
  407. package/src/config/bundled-skills/media-processing/services/preprocess.ts +8 -4
  408. package/src/config/bundled-skills/media-processing/services/reduce.ts +1 -1
  409. package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +0 -10
  410. package/src/config/bundled-skills/messaging/SKILL.md +5 -5
  411. package/src/config/bundled-skills/messaging/TOOLS.json +4 -0
  412. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +9 -2
  413. package/src/config/bundled-skills/messaging/tools/messaging-read.ts +15 -1
  414. package/src/config/bundled-skills/messaging/tools/messaging-search.ts +21 -1
  415. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +11 -12
  416. package/src/config/bundled-skills/phone-calls/SKILL.md +2 -2
  417. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +28 -18
  418. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +3 -3
  419. package/src/config/bundled-skills/settings/TOOLS.json +3 -3
  420. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +26 -22
  421. package/src/config/bundled-skills/transcribe/SKILL.md +9 -14
  422. package/src/config/bundled-skills/transcribe/TOOLS.json +2 -7
  423. package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +256 -0
  424. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +38 -188
  425. package/src/config/bundled-tool-registry.ts +0 -167
  426. package/src/config/env-registry.ts +24 -0
  427. package/src/config/env.ts +39 -10
  428. package/src/config/feature-flag-registry.json +63 -15
  429. package/src/config/llm-resolver.ts +128 -0
  430. package/src/config/loader.ts +220 -22
  431. package/src/config/raw-config-utils.ts +30 -2
  432. package/src/config/sanitize-for-transfer.ts +35 -0
  433. package/src/config/schema.ts +65 -51
  434. package/src/config/schemas/__tests__/stt.test.ts +43 -0
  435. package/src/config/schemas/analysis.ts +32 -0
  436. package/src/config/schemas/backup.ts +72 -0
  437. package/src/config/schemas/calls.ts +1 -30
  438. package/src/config/schemas/elevenlabs.ts +0 -59
  439. package/src/config/schemas/filing.ts +49 -14
  440. package/src/config/schemas/heartbeat.ts +27 -10
  441. package/src/config/schemas/host-browser.ts +47 -1
  442. package/src/config/schemas/inference.ts +3 -23
  443. package/src/config/schemas/llm.ts +318 -0
  444. package/src/config/schemas/memory-lifecycle.ts +14 -2
  445. package/src/config/schemas/memory-processing.ts +1 -9
  446. package/src/config/schemas/notifications.ts +4 -11
  447. package/src/config/schemas/platform.ts +3 -9
  448. package/src/config/schemas/security.ts +33 -0
  449. package/src/config/schemas/services.ts +53 -4
  450. package/src/config/schemas/stt.ts +60 -0
  451. package/src/config/schemas/tts.ts +283 -0
  452. package/src/config/schemas/updates.ts +14 -0
  453. package/src/config/schemas/workspace-git.ts +3 -40
  454. package/src/config/skills.ts +6 -2
  455. package/src/config/types.ts +4 -0
  456. package/src/contacts/contact-store.ts +56 -11
  457. package/src/contacts/contacts-write.ts +38 -1
  458. package/src/context/__tests__/compact-prompt.test.ts +45 -0
  459. package/src/context/__tests__/microcompact.test.ts +805 -0
  460. package/src/context/estimator-calibration.ts +136 -0
  461. package/src/context/microcompact.ts +443 -0
  462. package/src/context/post-turn-tool-result-truncation.ts +3 -2
  463. package/src/context/prompts/compact.md +12 -0
  464. package/src/context/token-estimator.ts +61 -3
  465. package/src/context/tool-result-truncation.ts +2 -1
  466. package/src/context/window-manager.ts +272 -35
  467. package/src/credential-execution/approval-bridge.ts +0 -1
  468. package/src/credential-execution/executable-discovery.ts +23 -2
  469. package/src/credential-execution/process-manager.test.ts +109 -0
  470. package/src/credential-execution/process-manager.ts +96 -2
  471. package/src/credential-health/credential-health-service.ts +366 -0
  472. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +324 -0
  473. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +497 -0
  474. package/src/daemon/__tests__/conversation-tool-setup.test.ts +17 -8
  475. package/src/daemon/__tests__/lifecycle-startup-ordering.test.ts +127 -0
  476. package/src/daemon/approval-generators.ts +29 -4
  477. package/src/daemon/assistant-attachments.ts +24 -13
  478. package/src/daemon/classifier.ts +2 -2
  479. package/src/daemon/config-watcher.ts +99 -6
  480. package/src/daemon/context-overflow-reducer.ts +4 -1
  481. package/src/daemon/conversation-agent-loop-handlers.ts +85 -12
  482. package/src/daemon/conversation-agent-loop.ts +563 -104
  483. package/src/daemon/conversation-attachments.ts +2 -6
  484. package/src/daemon/conversation-error.ts +46 -0
  485. package/src/daemon/conversation-history.ts +40 -6
  486. package/src/daemon/conversation-launch.ts +220 -0
  487. package/src/daemon/conversation-lifecycle.ts +85 -11
  488. package/src/daemon/conversation-messaging.ts +110 -7
  489. package/src/daemon/conversation-notifiers.ts +5 -0
  490. package/src/daemon/conversation-process.ts +591 -23
  491. package/src/daemon/conversation-queue-manager.ts +27 -0
  492. package/src/daemon/conversation-runtime-assembly.ts +769 -28
  493. package/src/daemon/conversation-slash.ts +38 -2
  494. package/src/daemon/conversation-surfaces.ts +483 -5
  495. package/src/daemon/conversation-tool-setup.ts +35 -5
  496. package/src/daemon/conversation-usage.ts +8 -5
  497. package/src/daemon/conversation.ts +193 -47
  498. package/src/daemon/external-skills-bootstrap.ts +41 -0
  499. package/src/daemon/guardian-action-generators.ts +34 -14
  500. package/src/daemon/handlers/config-model.test.ts +86 -0
  501. package/src/daemon/handlers/config-model.ts +54 -12
  502. package/src/daemon/handlers/config-slack-channel.ts +269 -94
  503. package/src/daemon/handlers/conversations.ts +13 -3
  504. package/src/daemon/handlers/shared.ts +51 -1
  505. package/src/daemon/handlers/skills.ts +323 -79
  506. package/src/daemon/handlers/slack-channel-oauth-install.ts +197 -0
  507. package/src/daemon/host-browser-proxy.ts +2 -1
  508. package/src/daemon/lifecycle.ts +185 -26
  509. package/src/daemon/message-protocol.ts +6 -0
  510. package/src/daemon/message-types/conversations.ts +48 -1
  511. package/src/daemon/message-types/home.ts +40 -0
  512. package/src/daemon/message-types/meet.ts +143 -0
  513. package/src/daemon/message-types/messages.ts +23 -1
  514. package/src/daemon/message-types/schedules.ts +34 -2
  515. package/src/daemon/message-types/skills.ts +16 -0
  516. package/src/daemon/message-types/surfaces.ts +2 -0
  517. package/src/daemon/message-types/trust.ts +0 -2
  518. package/src/daemon/parse-actual-tokens-from-error.test.ts +57 -1
  519. package/src/daemon/parse-actual-tokens-from-error.ts +66 -0
  520. package/src/daemon/pkb-context-tracker.test.ts +169 -0
  521. package/src/daemon/pkb-context-tracker.ts +125 -0
  522. package/src/daemon/pkb-reminder-builder.test.ts +70 -0
  523. package/src/daemon/pkb-reminder-builder.ts +31 -0
  524. package/src/daemon/providers-setup.ts +6 -0
  525. package/src/daemon/server.ts +463 -10
  526. package/src/daemon/shutdown-handlers.ts +32 -4
  527. package/src/daemon/shutdown-registry.ts +40 -0
  528. package/src/daemon/tool-side-effects.ts +9 -9
  529. package/src/daemon/watch-handler.ts +4 -4
  530. package/src/daemon/web-search-history.ts +126 -0
  531. package/src/email/html-renderer.ts +76 -0
  532. package/src/events/domain-events.ts +0 -1
  533. package/src/filing/filing-service.ts +9 -10
  534. package/src/heartbeat/heartbeat-service.ts +156 -22
  535. package/src/home/__tests__/assistant-feed-authoring.test.ts +156 -0
  536. package/src/home/__tests__/emit-feed-event.test.ts +169 -0
  537. package/src/home/__tests__/feed-scheduler.test.ts +222 -0
  538. package/src/home/__tests__/feed-types.test.ts +275 -0
  539. package/src/home/__tests__/feed-writer.test.ts +688 -0
  540. package/src/home/__tests__/phase5-exit-criteria.test.ts +212 -0
  541. package/src/home/__tests__/platform-gmail-digest.test.ts +222 -0
  542. package/src/home/__tests__/progress-formula.test.ts +213 -0
  543. package/src/home/__tests__/relationship-state-writer.test.ts +740 -0
  544. package/src/home/__tests__/rollup-producer.test.ts +442 -0
  545. package/src/home/assistant-feed-authoring.ts +128 -0
  546. package/src/home/emit-feed-event.ts +162 -0
  547. package/src/home/feed-scheduler.ts +263 -0
  548. package/src/home/feed-types.ts +235 -0
  549. package/src/home/feed-writer.ts +469 -0
  550. package/src/home/platform-gmail-digest.ts +163 -0
  551. package/src/home/progress-formula.ts +86 -0
  552. package/src/home/relationship-state-writer.ts +824 -0
  553. package/src/home/relationship-state.ts +143 -0
  554. package/src/home/rollup-producer.ts +413 -0
  555. package/src/home/suggested-prompts.ts +101 -0
  556. package/src/hooks/runner.ts +7 -0
  557. package/src/inbound/platform-callback-registration.ts +12 -3
  558. package/src/inbound/public-ingress-urls.ts +12 -0
  559. package/src/instrument.ts +1 -1
  560. package/src/ipc/__tests__/attachment-ipc.test.ts +213 -0
  561. package/src/ipc/__tests__/browser-ipc.test.ts +339 -0
  562. package/src/ipc/__tests__/cache-ipc.test.ts +266 -0
  563. package/src/ipc/__tests__/cli-ipc.test.ts +200 -0
  564. package/src/ipc/__tests__/socket-path.test.ts +73 -0
  565. package/src/ipc/__tests__/task-ipc.test.ts +577 -0
  566. package/src/ipc/__tests__/ui-request-route.test.ts +495 -0
  567. package/src/ipc/__tests__/watcher-ipc.test.ts +295 -0
  568. package/src/ipc/cli-client.ts +152 -0
  569. package/src/ipc/cli-server.ts +252 -0
  570. package/src/ipc/gateway-client.ts +180 -0
  571. package/src/ipc/routes/attachment.ts +114 -0
  572. package/src/ipc/routes/browser-context.ts +61 -0
  573. package/src/ipc/routes/browser.ts +96 -0
  574. package/src/ipc/routes/cache.ts +96 -0
  575. package/src/ipc/routes/index.ts +21 -0
  576. package/src/ipc/routes/task-queue.ts +226 -0
  577. package/src/ipc/routes/task.ts +173 -0
  578. package/src/ipc/routes/ui-request.ts +50 -0
  579. package/src/ipc/routes/wake-conversation.ts +19 -0
  580. package/src/ipc/routes/watcher.ts +203 -0
  581. package/src/ipc/socket-path.ts +100 -0
  582. package/src/memory/__tests__/auto-analysis-enqueue.test.ts +356 -0
  583. package/src/memory/__tests__/auto-analysis-guard.test.ts +57 -0
  584. package/src/memory/__tests__/conversation-analyze-job.test.ts +233 -0
  585. package/src/memory/__tests__/conversation-group-migration.test.ts +99 -0
  586. package/src/memory/__tests__/find-analysis-conversation.test.ts +196 -0
  587. package/src/memory/admin.ts +18 -0
  588. package/src/memory/app-store.ts +1 -1
  589. package/src/memory/attachments-store.ts +70 -0
  590. package/src/memory/auto-analysis-enqueue.ts +127 -0
  591. package/src/memory/auto-analysis-guard.ts +27 -0
  592. package/src/memory/cleanup-schedule-state.ts +37 -0
  593. package/src/memory/conversation-analyze-job.ts +74 -0
  594. package/src/memory/conversation-attention-store.ts +13 -6
  595. package/src/memory/conversation-crud.ts +199 -0
  596. package/src/memory/conversation-disk-view.ts +7 -0
  597. package/src/memory/conversation-group-migration.ts +65 -1
  598. package/src/memory/conversation-queries.ts +6 -5
  599. package/src/memory/conversation-title-service.ts +7 -4
  600. package/src/memory/db-init.ts +8 -0
  601. package/src/memory/db-maintenance.ts +108 -0
  602. package/src/memory/db.ts +1 -0
  603. package/src/memory/embedding-backend.ts +1 -1
  604. package/src/memory/graph/compaction.ts +299 -0
  605. package/src/memory/graph/consolidation.ts +4 -4
  606. package/src/memory/graph/conversation-graph-memory.ts +104 -29
  607. package/src/memory/graph/extraction.test.ts +295 -2
  608. package/src/memory/graph/extraction.ts +181 -51
  609. package/src/memory/graph/graph-search.test.ts +92 -0
  610. package/src/memory/graph/graph-search.ts +4 -1
  611. package/src/memory/graph/narrative.ts +2 -2
  612. package/src/memory/graph/pattern-scan.ts +2 -2
  613. package/src/memory/graph/retriever.test.ts +459 -0
  614. package/src/memory/graph/retriever.ts +257 -66
  615. package/src/memory/graph/scoring.test.ts +186 -0
  616. package/src/memory/graph/scoring.ts +31 -1
  617. package/src/memory/graph/store.ts +41 -0
  618. package/src/memory/graph/tool-handlers.ts +27 -0
  619. package/src/memory/graph/tools.ts +6 -1
  620. package/src/memory/group-crud.ts +6 -1
  621. package/src/memory/indexer.ts +95 -16
  622. package/src/memory/job-handlers/cleanup.ts +11 -8
  623. package/src/memory/job-handlers/conversation-starters.ts +39 -30
  624. package/src/memory/job-handlers/summarization.ts +2 -2
  625. package/src/memory/job-utils.ts +7 -1
  626. package/src/memory/jobs/embed-pkb-file.test.ts +168 -0
  627. package/src/memory/jobs/embed-pkb-file.ts +54 -0
  628. package/src/memory/jobs-store.ts +106 -5
  629. package/src/memory/jobs-worker.ts +26 -9
  630. package/src/memory/llm-usage-store.ts +92 -56
  631. package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +1 -1
  632. package/src/memory/migrations/219-oauth-providers-token-exchange-body-format.ts +15 -0
  633. package/src/memory/migrations/220-normalize-user-file-by-principal.ts +190 -0
  634. package/src/memory/migrations/221-conversations-archived-at.ts +16 -0
  635. package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +82 -0
  636. package/src/memory/migrations/index.ts +7 -0
  637. package/src/memory/migrations/registry.ts +8 -0
  638. package/src/memory/pkb/pkb-index.test.ts +368 -0
  639. package/src/memory/pkb/pkb-index.ts +255 -0
  640. package/src/memory/pkb/pkb-reconcile.test.ts +251 -0
  641. package/src/memory/pkb/pkb-reconcile.ts +148 -0
  642. package/src/memory/pkb/pkb-search.test.ts +438 -0
  643. package/src/memory/pkb/pkb-search.ts +137 -0
  644. package/src/memory/pkb/types.ts +53 -0
  645. package/src/memory/qdrant-client.ts +122 -1
  646. package/src/memory/qdrant-manager.ts +43 -16
  647. package/src/memory/schema/conversations.ts +2 -0
  648. package/src/memory/schema/oauth.ts +3 -0
  649. package/src/memory/slack-thread-store.ts +37 -0
  650. package/src/memory/usage-buckets.ts +396 -0
  651. package/src/messaging/providers/gmail/adapter.ts +6 -16
  652. package/src/messaging/providers/gmail/client.ts +79 -6
  653. package/src/messaging/providers/gmail/types.ts +7 -0
  654. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +282 -0
  655. package/src/messaging/providers/slack/adapter.ts +155 -38
  656. package/src/messaging/providers/slack/backfill.test.ts +257 -0
  657. package/src/messaging/providers/slack/backfill.ts +101 -0
  658. package/src/messaging/providers/slack/client.ts +16 -0
  659. package/src/messaging/providers/slack/message-metadata.test.ts +316 -0
  660. package/src/messaging/providers/slack/message-metadata.ts +123 -0
  661. package/src/messaging/providers/slack/render-transcript.test.ts +1373 -0
  662. package/src/messaging/providers/slack/render-transcript.ts +443 -0
  663. package/src/messaging/providers/slack/types.ts +4 -0
  664. package/src/messaging/style-analyzer.ts +5 -2
  665. package/src/notifications/README.md +9 -5
  666. package/src/notifications/decision-engine.ts +6 -12
  667. package/src/notifications/preference-extractor.ts +2 -6
  668. package/src/notifications/signal.ts +5 -0
  669. package/src/oauth/__tests__/identity-verifier.test.ts +1 -0
  670. package/src/oauth/byo-connection.test.ts +18 -1
  671. package/src/oauth/byo-connection.ts +3 -1
  672. package/src/oauth/connect-orchestrator.ts +2 -0
  673. package/src/oauth/connection-resolver.ts +6 -2
  674. package/src/oauth/connection.ts +2 -0
  675. package/src/oauth/oauth-store.ts +10 -0
  676. package/src/oauth/platform-connection.test.ts +145 -0
  677. package/src/oauth/platform-connection.ts +62 -31
  678. package/src/oauth/seed-providers.ts +10 -1
  679. package/src/permissions/approval-policy.test.ts +948 -0
  680. package/src/permissions/approval-policy.ts +257 -0
  681. package/src/permissions/bash-risk-classifier.test.ts +1208 -0
  682. package/src/permissions/bash-risk-classifier.ts +707 -0
  683. package/src/permissions/checker.ts +218 -699
  684. package/src/permissions/command-registry.test.ts +535 -0
  685. package/src/permissions/command-registry.ts +825 -0
  686. package/src/permissions/defaults.ts +71 -75
  687. package/src/permissions/file-risk-classifier.test.ts +535 -0
  688. package/src/permissions/file-risk-classifier.ts +274 -0
  689. package/src/permissions/risk-types.ts +205 -0
  690. package/src/permissions/secret-prompter.ts +53 -2
  691. package/src/permissions/skill-risk-classifier.test.ts +311 -0
  692. package/src/permissions/skill-risk-classifier.ts +214 -0
  693. package/src/permissions/trust-client.ts +52 -25
  694. package/src/permissions/trust-store-interface.ts +1 -6
  695. package/src/permissions/trust-store.ts +164 -65
  696. package/src/permissions/types.ts +23 -14
  697. package/src/permissions/web-risk-classifier.test.ts +170 -0
  698. package/src/permissions/web-risk-classifier.ts +89 -0
  699. package/src/permissions/workspace-policy.ts +1 -13
  700. package/src/platform/client.test.ts +10 -0
  701. package/src/platform/client.ts +19 -1
  702. package/src/platform/sync-identity.ts +129 -0
  703. package/src/prompts/persona-resolver.ts +127 -3
  704. package/src/prompts/system-prompt.ts +78 -38
  705. package/src/prompts/templates/BOOTSTRAP.md +5 -5
  706. package/src/prompts/templates/SOUL.md +5 -3
  707. package/src/prompts/templates/channels/slack.md +20 -0
  708. package/src/prompts/update-bulletin-job.ts +190 -0
  709. package/src/prompts/user-reference.ts +20 -17
  710. package/src/providers/__tests__/context-overflow-error.test.ts +328 -0
  711. package/src/providers/__tests__/provider-env-vars.test.ts +102 -0
  712. package/src/providers/__tests__/provider-secret-catalog.test.ts +42 -0
  713. package/src/providers/__tests__/retry-callsite.test.ts +424 -0
  714. package/src/providers/anthropic/client.ts +335 -70
  715. package/src/providers/call-site-routing.ts +71 -0
  716. package/src/providers/fireworks/client.ts +2 -2
  717. package/src/providers/gemini/client.ts +74 -3
  718. package/src/providers/managed-proxy/constants.ts +2 -1
  719. package/src/providers/model-catalog.ts +502 -28
  720. package/src/providers/model-intents.ts +8 -8
  721. package/src/providers/ollama/client.ts +2 -2
  722. package/src/providers/openai/chat-completions-provider.ts +530 -0
  723. package/src/providers/openai/client.ts +25 -440
  724. package/src/providers/openai/responses-provider.ts +579 -0
  725. package/src/providers/openrouter/client.ts +168 -4
  726. package/src/providers/provider-env-vars.ts +56 -0
  727. package/src/providers/provider-secret-catalog.ts +139 -0
  728. package/src/providers/provider-send-message.ts +22 -5
  729. package/src/providers/ratelimit.ts +4 -0
  730. package/src/providers/registry.ts +21 -10
  731. package/src/providers/retry.ts +185 -39
  732. package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +251 -0
  733. package/src/providers/speech-to-text/__tests__/resolve.test.ts +883 -0
  734. package/src/providers/speech-to-text/deepgram-realtime.test.ts +980 -0
  735. package/src/providers/speech-to-text/deepgram-realtime.ts +767 -0
  736. package/src/providers/speech-to-text/deepgram.test.ts +332 -0
  737. package/src/providers/speech-to-text/deepgram.ts +115 -0
  738. package/src/providers/speech-to-text/google-gemini-live-stream.test.ts +743 -0
  739. package/src/providers/speech-to-text/google-gemini-live-stream.ts +625 -0
  740. package/src/providers/speech-to-text/google-gemini.test.ts +226 -0
  741. package/src/providers/speech-to-text/google-gemini.ts +101 -0
  742. package/src/providers/speech-to-text/openai-whisper-stream.test.ts +564 -0
  743. package/src/providers/speech-to-text/openai-whisper-stream.ts +381 -0
  744. package/src/providers/speech-to-text/openai-whisper.test.ts +1 -37
  745. package/src/providers/speech-to-text/openai-whisper.ts +63 -33
  746. package/src/providers/speech-to-text/provider-catalog.ts +323 -0
  747. package/src/providers/speech-to-text/resolve.ts +393 -6
  748. package/src/providers/speech-to-text/xai-realtime.test.ts +578 -0
  749. package/src/providers/speech-to-text/xai-realtime.ts +796 -0
  750. package/src/providers/speech-to-text/xai.test.ts +155 -0
  751. package/src/providers/speech-to-text/xai.ts +97 -0
  752. package/src/providers/types.ts +102 -3
  753. package/src/runtime/AGENTS.md +45 -3
  754. package/src/runtime/__tests__/agent-wake.test.ts +872 -0
  755. package/src/runtime/__tests__/interactive-ui.test.ts +673 -0
  756. package/src/runtime/__tests__/runtime-mode.test.ts +62 -0
  757. package/src/runtime/__tests__/slack-block-formatting.test.ts +481 -0
  758. package/src/runtime/agent-wake.ts +553 -0
  759. package/src/runtime/auth/__tests__/route-policy.test.ts +40 -0
  760. package/src/runtime/auth/route-policy.ts +34 -5
  761. package/src/runtime/auth/token-service.ts +56 -1
  762. package/src/runtime/btw-sidechain.ts +15 -3
  763. package/src/runtime/capability-tokens.ts +10 -10
  764. package/src/runtime/channel-invite-transport.ts +1 -1
  765. package/src/runtime/channel-invite-transports/email.ts +14 -6
  766. package/src/runtime/channel-readiness-service.ts +12 -22
  767. package/src/runtime/channel-reply-delivery.ts +106 -2
  768. package/src/runtime/chrome-extension-registry.ts +38 -2
  769. package/src/runtime/decision-token.ts +116 -0
  770. package/src/runtime/gateway-client.ts +2 -2
  771. package/src/runtime/http-router.ts +32 -0
  772. package/src/runtime/http-server.ts +447 -11
  773. package/src/runtime/http-types.ts +29 -3
  774. package/src/runtime/interactive-ui.ts +362 -0
  775. package/src/runtime/invite-instruction-generator.ts +2 -2
  776. package/src/runtime/migrations/__tests__/gcs-signed-url.test.ts +176 -0
  777. package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +36 -0
  778. package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +360 -0
  779. package/src/runtime/migrations/__tests__/vbundle-metadata-merge-integration.test.ts +390 -0
  780. package/src/runtime/migrations/__tests__/vbundle-metadata-merge.test.ts +221 -0
  781. package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +1540 -0
  782. package/src/runtime/migrations/__tests__/vbundle-streaming-validator.test.ts +453 -0
  783. package/src/runtime/migrations/__tests__/vbundle-tar-stream.test.ts +222 -0
  784. package/src/runtime/migrations/gcs-signed-url.ts +162 -0
  785. package/src/runtime/migrations/migration-transport.ts +1 -0
  786. package/src/runtime/migrations/migration-wizard.ts +1 -0
  787. package/src/runtime/migrations/vbundle-import-analyzer.ts +77 -1
  788. package/src/runtime/migrations/vbundle-importer.ts +187 -8
  789. package/src/runtime/migrations/vbundle-metadata-merge.ts +124 -0
  790. package/src/runtime/migrations/vbundle-streaming-importer.ts +2522 -0
  791. package/src/runtime/migrations/vbundle-streaming-validator.ts +244 -0
  792. package/src/runtime/migrations/vbundle-tar-stream.ts +217 -0
  793. package/src/runtime/migrations/vbundle-validator.ts +15 -6
  794. package/src/runtime/pending-interactions.ts +0 -11
  795. package/src/runtime/routes/__tests__/backup-routes.test.ts +967 -0
  796. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +618 -0
  797. package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +247 -0
  798. package/src/runtime/routes/__tests__/migration-vellum-metadata-reconcile.test.ts +246 -0
  799. package/src/runtime/routes/__tests__/stt-routes.test.ts +406 -0
  800. package/src/runtime/routes/__tests__/tts-routes.test.ts +474 -0
  801. package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +148 -17
  802. package/src/runtime/routes/app-management-routes.ts +12 -18
  803. package/src/runtime/routes/approval-prompt-ts-tracker.ts +58 -0
  804. package/src/runtime/routes/approval-routes.ts +12 -17
  805. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +9 -0
  806. package/src/runtime/routes/attachment-routes.test.ts +9 -3
  807. package/src/runtime/routes/attachment-routes.ts +216 -17
  808. package/src/runtime/routes/avatar-routes.ts +20 -4
  809. package/src/runtime/routes/backup-routes.ts +519 -0
  810. package/src/runtime/routes/browser-extension-pair-routes.ts +82 -23
  811. package/src/runtime/routes/btw-routes.ts +9 -10
  812. package/src/runtime/routes/contact-routes.test.ts +298 -0
  813. package/src/runtime/routes/contact-routes.ts +132 -5
  814. package/src/runtime/routes/conversation-analysis-routes.ts +22 -142
  815. package/src/runtime/routes/conversation-management-routes.ts +133 -0
  816. package/src/runtime/routes/conversation-routes.ts +487 -160
  817. package/src/runtime/routes/debug-routes.ts +1 -1
  818. package/src/runtime/routes/diagnostics-routes.ts +6 -4
  819. package/src/runtime/routes/events-routes.ts +16 -0
  820. package/src/runtime/routes/filing-routes.ts +93 -0
  821. package/src/runtime/routes/guardian-approval-interception.ts +33 -3
  822. package/src/runtime/routes/guardian-approval-prompt.ts +13 -3
  823. package/src/runtime/routes/home-feed-routes.ts +452 -0
  824. package/src/runtime/routes/home-state-routes.ts +138 -0
  825. package/src/runtime/routes/host-browser-routes.ts +3 -14
  826. package/src/runtime/routes/identity-intro-cache.ts +7 -3
  827. package/src/runtime/routes/identity-routes.ts +3 -17
  828. package/src/runtime/routes/inbound-message-handler.ts +912 -2
  829. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +113 -2
  830. package/src/runtime/routes/inbound-stages/background-dispatch.ts +61 -3
  831. package/src/runtime/routes/inbound-stages/edit-intercept.ts +129 -6
  832. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +46 -39
  833. package/src/runtime/routes/inbound-stages/transcribe-audio.ts +15 -15
  834. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +137 -0
  835. package/src/runtime/routes/integrations/slack/__tests__/share.test.ts +179 -0
  836. package/src/runtime/routes/integrations/slack/channel.ts +36 -6
  837. package/src/runtime/routes/integrations/slack/share.ts +45 -7
  838. package/src/runtime/routes/llm-context-normalization.ts +325 -0
  839. package/src/runtime/routes/memory-item-routes.test.ts +3 -2
  840. package/src/runtime/routes/migration-routes.ts +722 -91
  841. package/src/runtime/routes/settings-routes.ts +26 -7
  842. package/src/runtime/routes/skills-routes.ts +76 -7
  843. package/src/runtime/routes/stt-routes.ts +233 -0
  844. package/src/runtime/routes/surface-action-routes.ts +41 -2
  845. package/src/runtime/routes/trust-rules-routes.ts +30 -14
  846. package/src/runtime/routes/tts-routes.ts +108 -24
  847. package/src/runtime/routes/usage-routes.ts +30 -2
  848. package/src/runtime/routes/user-route-dispatcher.ts +50 -5
  849. package/src/runtime/routes/user-routes.ts +13 -1
  850. package/src/runtime/routes/work-items-routes.test.ts +1 -1
  851. package/src/runtime/routes/work-items-routes.ts +11 -3
  852. package/src/runtime/runtime-mode.ts +33 -0
  853. package/src/runtime/services/__tests__/analyze-conversation.test.ts +426 -0
  854. package/src/runtime/services/__tests__/analyze-deps-singleton.test.ts +67 -0
  855. package/src/runtime/services/__tests__/auto-analysis-prompt.test.ts +53 -0
  856. package/src/runtime/services/__tests__/manual-analysis-prompt.test.ts +41 -0
  857. package/src/runtime/services/analyze-conversation.ts +340 -0
  858. package/src/runtime/services/analyze-deps-singleton.ts +32 -0
  859. package/src/runtime/services/auto-analysis-prompt.ts +55 -0
  860. package/src/runtime/skill-route-registry.ts +71 -0
  861. package/src/runtime/slack-block-formatting.ts +437 -10
  862. package/src/schedule/scheduler.ts +58 -0
  863. package/src/security/__tests__/provider-key-env-fallback.test.ts +119 -0
  864. package/src/security/__tests__/untrusted-content.test.ts +109 -0
  865. package/src/security/oauth2.ts +122 -37
  866. package/src/security/secure-keys.ts +32 -10
  867. package/src/security/token-manager.ts +35 -13
  868. package/src/security/untrusted-content.ts +102 -0
  869. package/src/sequence/engine.ts +23 -0
  870. package/src/sequence/types.ts +1 -1
  871. package/src/skills/catalog-cache.ts +26 -7
  872. package/src/skills/catalog-files.ts +64 -2
  873. package/src/skills/catalog-install.ts +31 -3
  874. package/src/skills/category-inference.ts +122 -0
  875. package/src/skills/clawhub-files.ts +213 -0
  876. package/src/skills/clawhub.ts +84 -23
  877. package/src/skills/skill-cache-store.ts +97 -0
  878. package/src/skills/skill-file-provider.ts +40 -0
  879. package/src/skills/skillssh-files.ts +395 -0
  880. package/src/skills/skillssh-registry.ts +4 -4
  881. package/src/stt/__tests__/daemon-batch-transcriber.test.ts +468 -0
  882. package/src/stt/__tests__/types.test.ts +89 -0
  883. package/src/stt/daemon-batch-transcriber.ts +228 -0
  884. package/src/stt/stt-stream-session.ts +506 -0
  885. package/src/stt/types.ts +334 -0
  886. package/src/stt/wav-encoder.test.ts +373 -0
  887. package/src/stt/wav-encoder.ts +175 -0
  888. package/src/subagent/manager.ts +79 -27
  889. package/src/tasks/ephemeral-permissions.ts +9 -4
  890. package/src/telemetry/usage-telemetry-reporter.ts +27 -5
  891. package/src/tools/browser/__tests__/browser-mode.test.ts +119 -0
  892. package/src/tools/browser/__tests__/browser-status.test.ts +166 -0
  893. package/src/tools/browser/browser-execution.ts +1208 -41
  894. package/src/tools/browser/browser-manager.ts +45 -0
  895. package/src/tools/browser/browser-mode-constants.ts +12 -0
  896. package/src/tools/browser/browser-mode.ts +92 -0
  897. package/src/tools/browser/browser-status-constants.ts +33 -0
  898. package/src/tools/browser/cdp-client/__tests__/cdp-inspect-client.test.ts +393 -0
  899. package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +29 -0
  900. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +1648 -32
  901. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/discovery.test.ts +264 -0
  902. package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +205 -17
  903. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +254 -21
  904. package/src/tools/browser/cdp-client/errors.ts +15 -0
  905. package/src/tools/browser/cdp-client/extension-cdp-client.ts +39 -16
  906. package/src/tools/browser/cdp-client/factory.ts +797 -87
  907. package/src/tools/browser/cdp-client/index.ts +16 -2
  908. package/src/tools/browser/cdp-client/types.ts +68 -0
  909. package/src/tools/credentials/tool-policy.ts +39 -5
  910. package/src/tools/credentials/vault.ts +41 -7
  911. package/src/tools/executor.ts +4 -0
  912. package/src/tools/filesystem/write.ts +52 -0
  913. package/src/tools/host-terminal/host-shell.ts +45 -5
  914. package/src/tools/memory/register.test.ts +185 -0
  915. package/src/tools/memory/register.ts +3 -1
  916. package/src/tools/network/web-fetch.ts +25 -12
  917. package/src/tools/network/web-search.ts +20 -2
  918. package/src/tools/permission-checker.ts +36 -15
  919. package/src/tools/policy-context.ts +25 -8
  920. package/src/tools/registry.ts +55 -3
  921. package/src/tools/shared/shell-output.ts +3 -1
  922. package/src/tools/side-effects.ts +0 -9
  923. package/src/tools/skills/execute.ts +2 -2
  924. package/src/tools/skills/sandbox-runner.ts +6 -2
  925. package/src/tools/terminal/backends/native.ts +51 -2
  926. package/src/tools/terminal/safe-env.ts +11 -2
  927. package/src/tools/terminal/shell.ts +16 -4
  928. package/src/tools/tool-manifest.ts +6 -0
  929. package/src/tools/types.ts +29 -3
  930. package/src/tools/ui-surface/definitions.ts +6 -1
  931. package/src/tools/verification-control-plane-policy.ts +1 -1
  932. package/src/tts/__tests__/provider-adapters.test.ts +1061 -0
  933. package/src/tts/__tests__/provider-catalog-consistency.test.ts +196 -0
  934. package/src/tts/__tests__/provider-catalog.test.ts +183 -0
  935. package/src/tts/__tests__/provider-registry.test.ts +90 -0
  936. package/src/tts/provider-catalog.ts +219 -0
  937. package/src/tts/provider-registry.ts +73 -0
  938. package/src/tts/providers/deepgram-provider.ts +219 -0
  939. package/src/tts/providers/elevenlabs-provider.ts +211 -0
  940. package/src/tts/providers/fish-audio-provider.ts +183 -0
  941. package/src/tts/providers/index.ts +44 -0
  942. package/src/tts/providers/register-builtins.ts +130 -0
  943. package/src/tts/providers/xai-provider.ts +224 -0
  944. package/src/tts/synthesize-text.ts +110 -0
  945. package/src/tts/tts-config-resolver.ts +78 -0
  946. package/src/tts/types.ts +199 -0
  947. package/src/types/onboarding-context.ts +7 -0
  948. package/src/types/tar-stream.d.ts +66 -0
  949. package/src/util/abort-reasons.ts +58 -0
  950. package/src/util/device-id.ts +32 -16
  951. package/src/util/errors.ts +9 -1
  952. package/src/util/json.ts +17 -0
  953. package/src/util/platform.ts +56 -12
  954. package/src/util/pricing.ts +78 -5
  955. package/src/util/spawn.ts +1 -1
  956. package/src/util/truncate.ts +4 -2
  957. package/src/util/unicode.ts +201 -0
  958. package/src/version.ts +19 -24
  959. package/src/watcher/engine.ts +24 -1
  960. package/src/watcher/providers/google-calendar.ts +134 -8
  961. package/src/watcher/providers/outlook-calendar.ts +42 -2
  962. package/src/watcher/watcher-store.ts +31 -0
  963. package/src/workspace/git-service.ts +23 -4
  964. package/src/workspace/migrations/003-seed-device-id.ts +9 -3
  965. package/src/workspace/migrations/017-seed-persona-dirs.ts +68 -4
  966. package/src/workspace/migrations/029-seed-pkb.ts +1 -1
  967. package/src/workspace/migrations/031-drop-user-md.ts +317 -0
  968. package/src/workspace/migrations/031-llm-log-retention-zero-to-null.ts +73 -0
  969. package/src/workspace/migrations/032-tts-provider-unification.ts +227 -0
  970. package/src/workspace/migrations/033-stt-service-explicit-config.ts +122 -0
  971. package/src/workspace/migrations/034-remove-calls-voice-transcription-provider.ts +215 -0
  972. package/src/workspace/migrations/035-seed-slack-channel-persona.ts +50 -0
  973. package/src/workspace/migrations/036-update-pkb-index-bar.ts +37 -0
  974. package/src/workspace/migrations/037-create-meets-dir.ts +61 -0
  975. package/src/workspace/migrations/038-unify-llm-callsite-configs.ts +516 -0
  976. package/src/workspace/migrations/039-drop-legacy-llm-keys.ts +171 -0
  977. package/src/workspace/migrations/040-seed-latency-callsite-defaults.ts +154 -0
  978. package/src/workspace/migrations/041-backfill-google-gmail-settings-scope.ts +57 -0
  979. package/src/workspace/migrations/042-fix-backfill-google-gmail-settings-scope.ts +70 -0
  980. package/src/workspace/migrations/043-release-notes-latex-rendering.ts +75 -0
  981. package/src/workspace/migrations/044-bump-stale-provider-stream-timeout.ts +51 -0
  982. package/src/workspace/migrations/045-release-notes-meet-avatar.ts +130 -0
  983. package/src/workspace/migrations/AGENTS.md +1 -1
  984. package/src/workspace/migrations/registry.ts +32 -0
  985. package/src/workspace/provider-commit-message-generator.ts +19 -38
  986. package/src/workspace/top-level-renderer.ts +13 -1
  987. package/src/workspace/turn-commit.ts +31 -0
  988. package/src/__tests__/email-cli.test.ts +0 -297
  989. package/src/__tests__/email-service-config-fallback.test.ts +0 -102
  990. package/src/__tests__/outlook-attachments.test.ts +0 -301
  991. package/src/__tests__/outlook-automation-tools.test.ts +0 -425
  992. package/src/__tests__/outlook-categories.test.ts +0 -212
  993. package/src/__tests__/outlook-compose-tools.test.ts +0 -325
  994. package/src/__tests__/outlook-declutter-tools.test.ts +0 -585
  995. package/src/__tests__/outlook-follow-up.test.ts +0 -196
  996. package/src/__tests__/outlook-trash.test.ts +0 -77
  997. package/src/__tests__/outlook-unsubscribe.test.ts +0 -250
  998. package/src/__tests__/update-bulletin-format.test.ts +0 -122
  999. package/src/__tests__/update-bulletin-state.test.ts +0 -135
  1000. package/src/__tests__/update-bulletin.test.ts +0 -277
  1001. package/src/__tests__/update-template-contract.test.ts +0 -29
  1002. package/src/cli/commands/browser-relay.ts +0 -466
  1003. package/src/cli/commands/doctor.ts +0 -341
  1004. package/src/config/bundled-skills/browser/SKILL.md +0 -63
  1005. package/src/config/bundled-skills/browser/TOOLS.json +0 -393
  1006. package/src/config/bundled-skills/browser/tools/browser-click.ts +0 -12
  1007. package/src/config/bundled-skills/browser/tools/browser-close.ts +0 -12
  1008. package/src/config/bundled-skills/browser/tools/browser-extract.ts +0 -12
  1009. package/src/config/bundled-skills/browser/tools/browser-fill-credential.ts +0 -12
  1010. package/src/config/bundled-skills/browser/tools/browser-hover.ts +0 -12
  1011. package/src/config/bundled-skills/browser/tools/browser-navigate.ts +0 -12
  1012. package/src/config/bundled-skills/browser/tools/browser-press-key.ts +0 -12
  1013. package/src/config/bundled-skills/browser/tools/browser-screenshot.ts +0 -12
  1014. package/src/config/bundled-skills/browser/tools/browser-scroll.ts +0 -12
  1015. package/src/config/bundled-skills/browser/tools/browser-select-option.ts +0 -12
  1016. package/src/config/bundled-skills/browser/tools/browser-snapshot.ts +0 -12
  1017. package/src/config/bundled-skills/browser/tools/browser-type.ts +0 -12
  1018. package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +0 -32
  1019. package/src/config/bundled-skills/browser/tools/browser-wait-for.ts +0 -12
  1020. package/src/config/bundled-skills/chatgpt-import/SKILL.md +0 -27
  1021. package/src/config/bundled-skills/chatgpt-import/TOOLS.json +0 -27
  1022. package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +0 -378
  1023. package/src/config/bundled-skills/gmail/SKILL.md +0 -175
  1024. package/src/config/bundled-skills/gmail/TOOLS.json +0 -558
  1025. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +0 -149
  1026. package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +0 -112
  1027. package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +0 -44
  1028. package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +0 -81
  1029. package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +0 -108
  1030. package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +0 -146
  1031. package/src/config/bundled-skills/gmail/tools/gmail-label.ts +0 -53
  1032. package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +0 -220
  1033. package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +0 -26
  1034. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +0 -251
  1035. package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +0 -29
  1036. package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +0 -122
  1037. package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +0 -67
  1038. package/src/config/bundled-skills/gmail/tools/scan-result-store.ts +0 -100
  1039. package/src/config/bundled-skills/gmail/tools/shared.ts +0 -47
  1040. package/src/config/bundled-skills/google-calendar/SKILL.md +0 -51
  1041. package/src/config/bundled-skills/google-calendar/TOOLS.json +0 -226
  1042. package/src/config/bundled-skills/google-calendar/calendar-client.ts +0 -223
  1043. package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +0 -27
  1044. package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +0 -48
  1045. package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +0 -19
  1046. package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +0 -36
  1047. package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +0 -58
  1048. package/src/config/bundled-skills/google-calendar/tools/shared.ts +0 -17
  1049. package/src/config/bundled-skills/google-calendar/types.ts +0 -97
  1050. package/src/config/bundled-skills/outlook/SKILL.md +0 -196
  1051. package/src/config/bundled-skills/outlook/TOOLS.json +0 -530
  1052. package/src/config/bundled-skills/outlook/tools/outlook-attachments.ts +0 -85
  1053. package/src/config/bundled-skills/outlook/tools/outlook-categories.ts +0 -77
  1054. package/src/config/bundled-skills/outlook/tools/outlook-draft.ts +0 -84
  1055. package/src/config/bundled-skills/outlook/tools/outlook-follow-up.ts +0 -94
  1056. package/src/config/bundled-skills/outlook/tools/outlook-forward.ts +0 -49
  1057. package/src/config/bundled-skills/outlook/tools/outlook-outreach-scan.ts +0 -237
  1058. package/src/config/bundled-skills/outlook/tools/outlook-rules.ts +0 -161
  1059. package/src/config/bundled-skills/outlook/tools/outlook-send-draft.ts +0 -32
  1060. package/src/config/bundled-skills/outlook/tools/outlook-sender-digest.ts +0 -272
  1061. package/src/config/bundled-skills/outlook/tools/outlook-trash.ts +0 -29
  1062. package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +0 -129
  1063. package/src/config/bundled-skills/outlook/tools/outlook-vacation.ts +0 -87
  1064. package/src/config/bundled-skills/outlook/tools/shared.ts +0 -20
  1065. package/src/config/bundled-skills/outlook-calendar/SKILL.md +0 -51
  1066. package/src/config/bundled-skills/outlook-calendar/TOOLS.json +0 -221
  1067. package/src/config/bundled-skills/outlook-calendar/calendar-client.ts +0 -252
  1068. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-check-availability.ts +0 -53
  1069. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-create-event.ts +0 -74
  1070. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-get-event.ts +0 -18
  1071. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-list-events.ts +0 -46
  1072. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-rsvp.ts +0 -36
  1073. package/src/config/bundled-skills/outlook-calendar/tools/shared.ts +0 -17
  1074. package/src/config/bundled-skills/outlook-calendar/types.ts +0 -120
  1075. package/src/config/bundled-skills/slack/SKILL.md +0 -107
  1076. package/src/config/bundled-skills/tasks/SKILL.md +0 -37
  1077. package/src/config/bundled-skills/tasks/TOOLS.json +0 -353
  1078. package/src/config/bundled-skills/tasks/icon.svg +0 -34
  1079. package/src/config/bundled-skills/tasks/tools/task-delete.ts +0 -12
  1080. package/src/config/bundled-skills/tasks/tools/task-list-add.ts +0 -12
  1081. package/src/config/bundled-skills/tasks/tools/task-list-remove.ts +0 -12
  1082. package/src/config/bundled-skills/tasks/tools/task-list-show.ts +0 -12
  1083. package/src/config/bundled-skills/tasks/tools/task-list-update.ts +0 -12
  1084. package/src/config/bundled-skills/tasks/tools/task-list.ts +0 -12
  1085. package/src/config/bundled-skills/tasks/tools/task-queue-run.ts +0 -12
  1086. package/src/config/bundled-skills/tasks/tools/task-run.ts +0 -12
  1087. package/src/config/bundled-skills/tasks/tools/task-save.ts +0 -12
  1088. package/src/config/bundled-skills/watcher/SKILL.md +0 -31
  1089. package/src/config/bundled-skills/watcher/TOOLS.json +0 -167
  1090. package/src/config/bundled-skills/watcher/tools/watcher-create.ts +0 -12
  1091. package/src/config/bundled-skills/watcher/tools/watcher-delete.ts +0 -12
  1092. package/src/config/bundled-skills/watcher/tools/watcher-digest.ts +0 -12
  1093. package/src/config/bundled-skills/watcher/tools/watcher-list.ts +0 -12
  1094. package/src/config/bundled-skills/watcher/tools/watcher-update.ts +0 -12
  1095. package/src/email/guardrails.ts +0 -221
  1096. package/src/email/provider.ts +0 -117
  1097. package/src/email/providers/agentmail.ts +0 -361
  1098. package/src/email/providers/index.ts +0 -65
  1099. package/src/email/service.ts +0 -384
  1100. package/src/email/types.ts +0 -126
  1101. package/src/prompts/templates/UPDATES.md +0 -38
  1102. package/src/prompts/templates/USER.md +0 -13
  1103. package/src/prompts/update-bulletin-format.ts +0 -68
  1104. package/src/prompts/update-bulletin-state.ts +0 -58
  1105. package/src/prompts/update-bulletin-template-path.ts +0 -13
  1106. package/src/prompts/update-bulletin.ts +0 -128
  1107. package/src/providers/speech-to-text/types.ts +0 -17
  1108. package/src/runtime/routes/browser-cdp-routes.ts +0 -229
  1109. package/src/shared/provider-env-vars.ts +0 -19
  1110. package/src/tools/watcher/create.ts +0 -86
  1111. package/src/tools/watcher/delete.ts +0 -36
  1112. package/src/tools/watcher/digest.ts +0 -54
  1113. package/src/tools/watcher/list.ts +0 -83
  1114. package/src/tools/watcher/update.ts +0 -71
@@ -0,0 +1,1061 @@
1
+ import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
2
+
3
+ // ---------------------------------------------------------------------------
4
+ // Module mocks — must appear before any imports of the modules under test
5
+ // ---------------------------------------------------------------------------
6
+
7
+ mock.module("../../util/logger.js", () => ({
8
+ getLogger: () =>
9
+ new Proxy({} as Record<string, unknown>, {
10
+ get: () => () => {},
11
+ }),
12
+ }));
13
+
14
+ // -- Config mock -----------------------------------------------------------
15
+
16
+ let mockElevenLabsConfig = {
17
+ voiceId: "test-voice-id",
18
+ voiceModelId: "",
19
+ speed: 1.0,
20
+ stability: 0.5,
21
+ similarityBoost: 0.75,
22
+ conversationTimeoutSeconds: 30,
23
+ };
24
+
25
+ let mockFishAudioConfig = {
26
+ referenceId: "test-reference-id",
27
+ chunkLength: 200,
28
+ format: "mp3" as "mp3" | "wav" | "opus",
29
+ latency: "normal" as "normal" | "balanced",
30
+ speed: 1.0,
31
+ };
32
+
33
+ let mockDeepgramConfig = {
34
+ model: "aura-asteria-en",
35
+ format: "mp3" as "mp3" | "wav" | "opus",
36
+ };
37
+
38
+ let mockXaiConfig = {
39
+ voiceId: "eve",
40
+ language: "auto",
41
+ format: "mp3" as "mp3" | "wav",
42
+ sampleRate: 24000,
43
+ bitRate: 128000,
44
+ };
45
+
46
+ mock.module("../../config/loader.js", () => ({
47
+ getConfig: () => ({
48
+ services: {
49
+ tts: {
50
+ providers: {
51
+ elevenlabs: mockElevenLabsConfig,
52
+ "fish-audio": mockFishAudioConfig,
53
+ deepgram: mockDeepgramConfig,
54
+ xai: mockXaiConfig,
55
+ },
56
+ },
57
+ },
58
+ }),
59
+ }));
60
+
61
+ // -- Secure keys mock ------------------------------------------------------
62
+
63
+ let mockApiKey: string | null = "test-elevenlabs-api-key";
64
+ let mockDeepgramApiKey: string | null = "test-deepgram-api-key";
65
+ let mockXaiApiKey: string | null = "test-xai-api-key";
66
+
67
+ mock.module("../../security/secure-keys.js", () => ({
68
+ getSecureKeyAsync: async (key?: string) => {
69
+ if (key === "credential/xai/api_key") return mockXaiApiKey;
70
+ return mockApiKey;
71
+ },
72
+ getProviderKeyAsync: async (provider: string) => {
73
+ if (provider === "deepgram") return mockDeepgramApiKey;
74
+ return mockApiKey;
75
+ },
76
+ }));
77
+
78
+ mock.module("../../security/credential-key.js", () => ({
79
+ credentialKey: (service: string, field: string) =>
80
+ `credential/${service}/${field}`,
81
+ }));
82
+
83
+ // -- Fish Audio client mock ------------------------------------------------
84
+
85
+ const mockSynthesizeWithFishAudio = mock(
86
+ async (
87
+ _text: string,
88
+ _config: unknown,
89
+ options?: { onChunk?: (chunk: Uint8Array) => void; signal?: AbortSignal },
90
+ ) => {
91
+ const audioData = Buffer.from("fake-fish-audio-data");
92
+ if (options?.onChunk) {
93
+ options.onChunk(new Uint8Array(audioData));
94
+ }
95
+ return audioData;
96
+ },
97
+ );
98
+
99
+ mock.module("../../calls/fish-audio-client.js", () => ({
100
+ synthesizeWithFishAudio: mockSynthesizeWithFishAudio,
101
+ }));
102
+
103
+ // ---------------------------------------------------------------------------
104
+ // Imports (after mocks)
105
+ // ---------------------------------------------------------------------------
106
+
107
+ import { listCatalogProviderIds } from "../provider-catalog.js";
108
+ import {
109
+ _resetTtsProviderRegistry,
110
+ getTtsProvider,
111
+ listTtsProviders,
112
+ } from "../provider-registry.js";
113
+ import {
114
+ createDeepgramProvider,
115
+ DeepgramTtsError,
116
+ } from "../providers/deepgram-provider.js";
117
+ import { createElevenLabsProvider } from "../providers/elevenlabs-provider.js";
118
+ import { ElevenLabsTtsError } from "../providers/elevenlabs-provider.js";
119
+ import { createFishAudioProvider } from "../providers/fish-audio-provider.js";
120
+ import { FishAudioTtsError } from "../providers/fish-audio-provider.js";
121
+ import { providerFactories } from "../providers/index.js";
122
+ import {
123
+ _resetBuiltinRegistration,
124
+ registerBuiltinTtsProviders,
125
+ } from "../providers/register-builtins.js";
126
+ import { createXaiProvider, XaiTtsError } from "../providers/xai-provider.js";
127
+ import type { TtsSynthesisRequest } from "../types.js";
128
+
129
+ // ---------------------------------------------------------------------------
130
+ // Fetch mock helpers
131
+ // ---------------------------------------------------------------------------
132
+
133
+ let originalFetch: typeof globalThis.fetch;
134
+
135
+ beforeEach(() => {
136
+ originalFetch = globalThis.fetch;
137
+ mockApiKey = "test-elevenlabs-api-key";
138
+ mockDeepgramApiKey = "test-deepgram-api-key";
139
+ mockElevenLabsConfig = {
140
+ voiceId: "test-voice-id",
141
+ voiceModelId: "",
142
+ speed: 1.0,
143
+ stability: 0.5,
144
+ similarityBoost: 0.75,
145
+ conversationTimeoutSeconds: 30,
146
+ };
147
+ mockFishAudioConfig = {
148
+ referenceId: "test-reference-id",
149
+ chunkLength: 200,
150
+ format: "mp3",
151
+ latency: "normal",
152
+ speed: 1.0,
153
+ };
154
+ mockDeepgramConfig = {
155
+ model: "aura-asteria-en",
156
+ format: "mp3",
157
+ };
158
+ mockXaiApiKey = "test-xai-api-key";
159
+ mockXaiConfig = {
160
+ voiceId: "eve",
161
+ language: "auto",
162
+ format: "mp3",
163
+ sampleRate: 24000,
164
+ bitRate: 128000,
165
+ };
166
+ mockSynthesizeWithFishAudio.mockClear();
167
+ });
168
+
169
+ afterEach(() => {
170
+ globalThis.fetch = originalFetch;
171
+ _resetTtsProviderRegistry();
172
+ _resetBuiltinRegistration();
173
+ });
174
+
175
+ // ---------------------------------------------------------------------------
176
+ // Helpers
177
+ // ---------------------------------------------------------------------------
178
+
179
+ function makeRequest(
180
+ overrides?: Partial<TtsSynthesisRequest>,
181
+ ): TtsSynthesisRequest {
182
+ return {
183
+ text: "Hello world",
184
+ useCase: "message-playback",
185
+ ...overrides,
186
+ };
187
+ }
188
+
189
+ function mockFetchReturning(audioBytes: Uint8Array, status = 200): void {
190
+ globalThis.fetch = mock(
191
+ async () =>
192
+ new Response(audioBytes.buffer as ArrayBuffer, {
193
+ status,
194
+ headers: { "Content-Type": "audio/mpeg" },
195
+ }),
196
+ ) as unknown as typeof globalThis.fetch;
197
+ }
198
+
199
+ function mockFetchError(status: number, body: string): void {
200
+ globalThis.fetch = mock(
201
+ async () => new Response(body, { status }),
202
+ ) as unknown as typeof globalThis.fetch;
203
+ }
204
+
205
+ // ===========================================================================
206
+ // ElevenLabs provider adapter
207
+ // ===========================================================================
208
+
209
+ describe("ElevenLabs TTS provider adapter", () => {
210
+ // -- Interface conformance -----------------------------------------------
211
+
212
+ test("has correct provider ID", () => {
213
+ const provider = createElevenLabsProvider();
214
+ expect(provider.id).toBe("elevenlabs");
215
+ });
216
+
217
+ test("advertises mp3 and pcm format support without streaming", () => {
218
+ const provider = createElevenLabsProvider();
219
+ expect(provider.capabilities.supportsStreaming).toBe(false);
220
+ expect(provider.capabilities.supportedFormats).toEqual(["mp3", "pcm"]);
221
+ });
222
+
223
+ // -- Request mapping -----------------------------------------------------
224
+
225
+ test("synthesize sends request to ElevenLabs REST API with correct voice ID", async () => {
226
+ const audioPayload = new Uint8Array([0x49, 0x44, 0x33]); // Fake MP3 header
227
+ let capturedUrl = "";
228
+ let capturedHeaders: Headers | null = null;
229
+ let capturedBody = "";
230
+
231
+ globalThis.fetch = mock(
232
+ async (input: RequestInfo | URL, init?: RequestInit) => {
233
+ capturedUrl = typeof input === "string" ? input : input.toString();
234
+ capturedHeaders = new Headers(init?.headers);
235
+ capturedBody = init?.body as string;
236
+ return new Response(audioPayload, {
237
+ status: 200,
238
+ headers: { "Content-Type": "audio/mpeg" },
239
+ });
240
+ },
241
+ ) as unknown as typeof globalThis.fetch;
242
+
243
+ const provider = createElevenLabsProvider();
244
+ await provider.synthesize(makeRequest());
245
+
246
+ expect(capturedUrl).toContain("/v1/text-to-speech/test-voice-id");
247
+ expect(capturedUrl).toContain("output_format=mp3_44100_128");
248
+ expect(capturedHeaders!.get("xi-api-key")).toBe("test-elevenlabs-api-key");
249
+ expect(capturedHeaders!.get("Content-Type")).toBe("application/json");
250
+
251
+ const body = JSON.parse(capturedBody);
252
+ expect(body.text).toBe("Hello world");
253
+ expect(body.voice_settings).toEqual({
254
+ stability: 0.5,
255
+ similarity_boost: 0.75,
256
+ speed: 1.0,
257
+ });
258
+ });
259
+
260
+ test("uses lower-quality format for phone-call use case", async () => {
261
+ const audioPayload = new Uint8Array([0x49, 0x44, 0x33]);
262
+ let capturedUrl = "";
263
+
264
+ globalThis.fetch = mock(async (input: RequestInfo | URL) => {
265
+ capturedUrl = typeof input === "string" ? input : input.toString();
266
+ return new Response(audioPayload, { status: 200 });
267
+ }) as unknown as typeof globalThis.fetch;
268
+
269
+ const provider = createElevenLabsProvider();
270
+ await provider.synthesize(makeRequest({ useCase: "phone-call" }));
271
+
272
+ expect(capturedUrl).toContain("output_format=mp3_22050_32");
273
+ });
274
+
275
+ test("request voiceId overrides config voiceId", async () => {
276
+ const audioPayload = new Uint8Array([0x49, 0x44, 0x33]);
277
+ let capturedUrl = "";
278
+
279
+ globalThis.fetch = mock(async (input: RequestInfo | URL) => {
280
+ capturedUrl = typeof input === "string" ? input : input.toString();
281
+ return new Response(audioPayload, { status: 200 });
282
+ }) as unknown as typeof globalThis.fetch;
283
+
284
+ const provider = createElevenLabsProvider();
285
+ await provider.synthesize(makeRequest({ voiceId: "override-voice" }));
286
+
287
+ expect(capturedUrl).toContain("/v1/text-to-speech/override-voice");
288
+ });
289
+
290
+ test("uses configured voiceModelId when set", async () => {
291
+ mockElevenLabsConfig.voiceModelId = "eleven_turbo_v2_5";
292
+
293
+ const audioPayload = new Uint8Array([0x49, 0x44, 0x33]);
294
+ let capturedBody = "";
295
+
296
+ globalThis.fetch = mock(
297
+ async (_input: RequestInfo | URL, init?: RequestInit) => {
298
+ capturedBody = init?.body as string;
299
+ return new Response(audioPayload, { status: 200 });
300
+ },
301
+ ) as unknown as typeof globalThis.fetch;
302
+
303
+ const provider = createElevenLabsProvider();
304
+ await provider.synthesize(makeRequest());
305
+
306
+ const body = JSON.parse(capturedBody);
307
+ expect(body.model_id).toBe("eleven_turbo_v2_5");
308
+ });
309
+
310
+ // -- Content type / format -----------------------------------------------
311
+
312
+ test("returns audio/mpeg content type for mp3 format", async () => {
313
+ const audioPayload = new Uint8Array([0x49, 0x44, 0x33]);
314
+ mockFetchReturning(audioPayload);
315
+
316
+ const provider = createElevenLabsProvider();
317
+ const result = await provider.synthesize(makeRequest());
318
+
319
+ expect(result.contentType).toBe("audio/mpeg");
320
+ expect(result.audio.byteLength).toBeGreaterThan(0);
321
+ });
322
+
323
+ // -- Required config validation ------------------------------------------
324
+
325
+ test("throws ELEVENLABS_TTS_NO_API_KEY when API key is missing", async () => {
326
+ mockApiKey = null;
327
+
328
+ const provider = createElevenLabsProvider();
329
+
330
+ try {
331
+ await provider.synthesize(makeRequest());
332
+ throw new Error("Expected synthesize to throw");
333
+ } catch (err) {
334
+ expect(err).toBeInstanceOf(ElevenLabsTtsError);
335
+ expect((err as ElevenLabsTtsError).code).toBe(
336
+ "ELEVENLABS_TTS_NO_API_KEY",
337
+ );
338
+ expect((err as ElevenLabsTtsError).message).toContain(
339
+ "API key not configured",
340
+ );
341
+ }
342
+ });
343
+
344
+ // -- Error handling ------------------------------------------------------
345
+
346
+ test("throws ELEVENLABS_TTS_HTTP_ERROR on non-200 response", async () => {
347
+ mockFetchError(401, "Unauthorized");
348
+
349
+ const provider = createElevenLabsProvider();
350
+
351
+ try {
352
+ await provider.synthesize(makeRequest());
353
+ throw new Error("Expected synthesize to throw");
354
+ } catch (err) {
355
+ expect(err).toBeInstanceOf(ElevenLabsTtsError);
356
+ expect((err as ElevenLabsTtsError).code).toBe(
357
+ "ELEVENLABS_TTS_HTTP_ERROR",
358
+ );
359
+ expect((err as ElevenLabsTtsError).statusCode).toBe(401);
360
+ }
361
+ });
362
+
363
+ test("throws ELEVENLABS_TTS_EMPTY_RESPONSE on empty audio body", async () => {
364
+ mockFetchReturning(new Uint8Array(0));
365
+
366
+ const provider = createElevenLabsProvider();
367
+
368
+ try {
369
+ await provider.synthesize(makeRequest());
370
+ throw new Error("Expected synthesize to throw");
371
+ } catch (err) {
372
+ expect(err).toBeInstanceOf(ElevenLabsTtsError);
373
+ expect((err as ElevenLabsTtsError).code).toBe(
374
+ "ELEVENLABS_TTS_EMPTY_RESPONSE",
375
+ );
376
+ }
377
+ });
378
+
379
+ test("throws ELEVENLABS_TTS_REQUEST_FAILED on network error", async () => {
380
+ globalThis.fetch = mock(async () => {
381
+ throw new Error("Network unreachable");
382
+ }) as unknown as typeof globalThis.fetch;
383
+
384
+ const provider = createElevenLabsProvider();
385
+
386
+ try {
387
+ await provider.synthesize(makeRequest());
388
+ throw new Error("Expected synthesize to throw");
389
+ } catch (err) {
390
+ expect(err).toBeInstanceOf(ElevenLabsTtsError);
391
+ expect((err as ElevenLabsTtsError).code).toBe(
392
+ "ELEVENLABS_TTS_REQUEST_FAILED",
393
+ );
394
+ expect((err as ElevenLabsTtsError).message).toContain(
395
+ "Network unreachable",
396
+ );
397
+ }
398
+ });
399
+ });
400
+
401
+ // ===========================================================================
402
+ // Fish Audio TTS provider adapter
403
+ // ===========================================================================
404
+
405
+ describe("Fish Audio TTS provider adapter", () => {
406
+ // -- Interface conformance -----------------------------------------------
407
+
408
+ test("has correct provider ID", () => {
409
+ const provider = createFishAudioProvider();
410
+ expect(provider.id).toBe("fish-audio");
411
+ });
412
+
413
+ test("advertises streaming support with multiple formats", () => {
414
+ const provider = createFishAudioProvider();
415
+ expect(provider.capabilities.supportsStreaming).toBe(true);
416
+ expect(provider.capabilities.supportedFormats).toEqual([
417
+ "mp3",
418
+ "wav",
419
+ "opus",
420
+ ]);
421
+ });
422
+
423
+ test("implements synthesizeStream", () => {
424
+ const provider = createFishAudioProvider();
425
+ expect(typeof provider.synthesizeStream).toBe("function");
426
+ });
427
+
428
+ // -- Request mapping -----------------------------------------------------
429
+
430
+ test("synthesize passes text and config to underlying client", async () => {
431
+ const provider = createFishAudioProvider();
432
+ await provider.synthesize(makeRequest({ text: "Test speech" }));
433
+
434
+ expect(mockSynthesizeWithFishAudio).toHaveBeenCalledTimes(1);
435
+ const [text, config, options] = mockSynthesizeWithFishAudio.mock.calls[0]!;
436
+ expect(text).toBe("Test speech");
437
+ expect((config as { referenceId: string }).referenceId).toBe(
438
+ "test-reference-id",
439
+ );
440
+ expect(
441
+ (options as { signal?: AbortSignal } | undefined)?.signal,
442
+ ).toBeUndefined();
443
+ });
444
+
445
+ test("request voiceId overrides config referenceId", async () => {
446
+ const provider = createFishAudioProvider();
447
+ await provider.synthesize(makeRequest({ voiceId: "custom-ref-id" }));
448
+
449
+ const [, config] = mockSynthesizeWithFishAudio.mock.calls[0]!;
450
+ expect((config as { referenceId: string }).referenceId).toBe(
451
+ "custom-ref-id",
452
+ );
453
+ });
454
+
455
+ test("passes abort signal to underlying client", async () => {
456
+ const controller = new AbortController();
457
+ const provider = createFishAudioProvider();
458
+ await provider.synthesize(makeRequest({ signal: controller.signal }));
459
+
460
+ const [, , options] = mockSynthesizeWithFishAudio.mock.calls[0]!;
461
+ expect((options as { signal?: AbortSignal } | undefined)?.signal).toBe(
462
+ controller.signal,
463
+ );
464
+ });
465
+
466
+ // -- Streaming -----------------------------------------------------------
467
+
468
+ test("synthesizeStream passes onChunk callback through", async () => {
469
+ const chunks: Uint8Array[] = [];
470
+ const provider = createFishAudioProvider();
471
+ await provider.synthesizeStream!(makeRequest(), (chunk) =>
472
+ chunks.push(chunk),
473
+ );
474
+
475
+ expect(mockSynthesizeWithFishAudio).toHaveBeenCalledTimes(1);
476
+ const [, , options] = mockSynthesizeWithFishAudio.mock.calls[0]!;
477
+ expect(typeof (options as { onChunk?: unknown } | undefined)?.onChunk).toBe(
478
+ "function",
479
+ );
480
+ // The mock calls onChunk once; verify it was received
481
+ expect(chunks.length).toBeGreaterThan(0);
482
+ });
483
+
484
+ // -- Content type / format -----------------------------------------------
485
+
486
+ test("returns audio/mpeg content type for mp3 format", async () => {
487
+ mockFishAudioConfig.format = "mp3";
488
+ const provider = createFishAudioProvider();
489
+ const result = await provider.synthesize(makeRequest());
490
+ expect(result.contentType).toBe("audio/mpeg");
491
+ });
492
+
493
+ test("returns audio/wav content type for wav format", async () => {
494
+ mockFishAudioConfig.format = "wav";
495
+ const provider = createFishAudioProvider();
496
+ const result = await provider.synthesize(makeRequest());
497
+ expect(result.contentType).toBe("audio/wav");
498
+ });
499
+
500
+ test("returns audio/opus content type for opus format", async () => {
501
+ mockFishAudioConfig.format = "opus";
502
+ const provider = createFishAudioProvider();
503
+ const result = await provider.synthesize(makeRequest());
504
+ expect(result.contentType).toBe("audio/opus");
505
+ });
506
+
507
+ // -- Required config validation ------------------------------------------
508
+
509
+ test("throws FISH_AUDIO_TTS_NO_REFERENCE_ID when no reference ID is available", async () => {
510
+ mockFishAudioConfig.referenceId = "";
511
+
512
+ const provider = createFishAudioProvider();
513
+
514
+ try {
515
+ await provider.synthesize(makeRequest());
516
+ throw new Error("Expected synthesize to throw");
517
+ } catch (err) {
518
+ expect(err).toBeInstanceOf(FishAudioTtsError);
519
+ expect((err as FishAudioTtsError).code).toBe(
520
+ "FISH_AUDIO_TTS_NO_REFERENCE_ID",
521
+ );
522
+ expect((err as FishAudioTtsError).message).toContain("reference ID");
523
+ }
524
+ });
525
+
526
+ test("throws FISH_AUDIO_TTS_NO_REFERENCE_ID in synthesizeStream when no reference ID", async () => {
527
+ mockFishAudioConfig.referenceId = "";
528
+
529
+ const provider = createFishAudioProvider();
530
+
531
+ try {
532
+ await provider.synthesizeStream!(makeRequest(), () => {});
533
+ throw new Error("Expected synthesizeStream to throw");
534
+ } catch (err) {
535
+ expect(err).toBeInstanceOf(FishAudioTtsError);
536
+ expect((err as FishAudioTtsError).code).toBe(
537
+ "FISH_AUDIO_TTS_NO_REFERENCE_ID",
538
+ );
539
+ }
540
+ });
541
+
542
+ // -- Error handling ------------------------------------------------------
543
+
544
+ test("wraps underlying client errors with FISH_AUDIO_TTS_SYNTHESIS_FAILED", async () => {
545
+ mockSynthesizeWithFishAudio.mockImplementationOnce(async () => {
546
+ throw new Error("API key not configured");
547
+ });
548
+
549
+ const provider = createFishAudioProvider();
550
+
551
+ try {
552
+ await provider.synthesize(makeRequest());
553
+ throw new Error("Expected synthesize to throw");
554
+ } catch (err) {
555
+ expect(err).toBeInstanceOf(FishAudioTtsError);
556
+ expect((err as FishAudioTtsError).code).toBe(
557
+ "FISH_AUDIO_TTS_SYNTHESIS_FAILED",
558
+ );
559
+ expect((err as FishAudioTtsError).message).toContain(
560
+ "API key not configured",
561
+ );
562
+ }
563
+ });
564
+
565
+ test("wraps streaming client errors with FISH_AUDIO_TTS_SYNTHESIS_FAILED", async () => {
566
+ mockSynthesizeWithFishAudio.mockImplementationOnce(async () => {
567
+ throw new Error("Connection reset");
568
+ });
569
+
570
+ const provider = createFishAudioProvider();
571
+
572
+ try {
573
+ await provider.synthesizeStream!(makeRequest(), () => {});
574
+ throw new Error("Expected synthesizeStream to throw");
575
+ } catch (err) {
576
+ expect(err).toBeInstanceOf(FishAudioTtsError);
577
+ expect((err as FishAudioTtsError).code).toBe(
578
+ "FISH_AUDIO_TTS_SYNTHESIS_FAILED",
579
+ );
580
+ expect((err as FishAudioTtsError).message).toContain("Connection reset");
581
+ }
582
+ });
583
+ });
584
+
585
+ // ===========================================================================
586
+ // Deepgram TTS provider adapter
587
+ // ===========================================================================
588
+
589
+ describe("Deepgram TTS provider adapter", () => {
590
+ // -- Interface conformance -----------------------------------------------
591
+
592
+ test("has correct provider ID", () => {
593
+ const provider = createDeepgramProvider();
594
+ expect(provider.id).toBe("deepgram");
595
+ });
596
+
597
+ test("advertises mp3, wav, opus format support without streaming", () => {
598
+ const provider = createDeepgramProvider();
599
+ expect(provider.capabilities.supportsStreaming).toBe(false);
600
+ expect(provider.capabilities.supportedFormats).toEqual([
601
+ "mp3",
602
+ "wav",
603
+ "opus",
604
+ ]);
605
+ });
606
+
607
+ // -- Request mapping -----------------------------------------------------
608
+
609
+ test("synthesize sends request to Deepgram REST TTS API with correct model", async () => {
610
+ const audioPayload = new Uint8Array([0x49, 0x44, 0x33]);
611
+ let capturedUrl = "";
612
+ let capturedHeaders: Headers | null = null;
613
+ let capturedBody = "";
614
+
615
+ globalThis.fetch = mock(
616
+ async (input: RequestInfo | URL, init?: RequestInit) => {
617
+ capturedUrl = typeof input === "string" ? input : input.toString();
618
+ capturedHeaders = new Headers(init?.headers);
619
+ capturedBody = init?.body as string;
620
+ return new Response(audioPayload, {
621
+ status: 200,
622
+ headers: { "Content-Type": "audio/mpeg" },
623
+ });
624
+ },
625
+ ) as unknown as typeof globalThis.fetch;
626
+
627
+ const provider = createDeepgramProvider();
628
+ await provider.synthesize(makeRequest());
629
+
630
+ expect(capturedUrl).toContain("/v1/speak");
631
+ expect(capturedUrl).toContain("model=aura-asteria-en");
632
+ expect(capturedUrl).toContain("encoding=mp3");
633
+ expect(capturedHeaders!.get("Authorization")).toBe(
634
+ "Token test-deepgram-api-key",
635
+ );
636
+ expect(capturedHeaders!.get("Content-Type")).toBe("application/json");
637
+
638
+ const body = JSON.parse(capturedBody);
639
+ expect(body.text).toBe("Hello world");
640
+ });
641
+
642
+ test("uses linear16 encoding with container=none and sample_rate=16000 when outputFormat is pcm", async () => {
643
+ const audioPayload = new Uint8Array([0x00, 0x01, 0x02]);
644
+ let capturedUrl = "";
645
+
646
+ globalThis.fetch = mock(async (input: RequestInfo | URL) => {
647
+ capturedUrl = typeof input === "string" ? input : input.toString();
648
+ return new Response(audioPayload, { status: 200 });
649
+ }) as unknown as typeof globalThis.fetch;
650
+
651
+ const provider = createDeepgramProvider();
652
+ const result = await provider.synthesize(
653
+ makeRequest({ outputFormat: "pcm" }),
654
+ );
655
+
656
+ expect(capturedUrl).toContain("encoding=linear16");
657
+ expect(capturedUrl).toContain("container=none");
658
+ expect(capturedUrl).toContain("sample_rate=16000");
659
+ expect(result.contentType).toBe("audio/pcm");
660
+ });
661
+
662
+ test("translates wav config format to linear16 encoding with container=wav", async () => {
663
+ mockDeepgramConfig.format = "wav";
664
+ const audioPayload = new Uint8Array([0x52, 0x49, 0x46, 0x46]);
665
+ let capturedUrl = "";
666
+
667
+ globalThis.fetch = mock(async (input: RequestInfo | URL) => {
668
+ capturedUrl = typeof input === "string" ? input : input.toString();
669
+ return new Response(audioPayload, { status: 200 });
670
+ }) as unknown as typeof globalThis.fetch;
671
+
672
+ const provider = createDeepgramProvider();
673
+ const result = await provider.synthesize(makeRequest());
674
+
675
+ expect(capturedUrl).toContain("encoding=linear16");
676
+ expect(capturedUrl).toContain("container=wav");
677
+ expect(capturedUrl).not.toContain("sample_rate=");
678
+ expect(result.contentType).toBe("audio/wav");
679
+ });
680
+
681
+ test("uses configured model", async () => {
682
+ mockDeepgramConfig.model = "aura-luna-en";
683
+ const audioPayload = new Uint8Array([0x49, 0x44, 0x33]);
684
+ let capturedUrl = "";
685
+
686
+ globalThis.fetch = mock(async (input: RequestInfo | URL) => {
687
+ capturedUrl = typeof input === "string" ? input : input.toString();
688
+ return new Response(audioPayload, { status: 200 });
689
+ }) as unknown as typeof globalThis.fetch;
690
+
691
+ const provider = createDeepgramProvider();
692
+ await provider.synthesize(makeRequest());
693
+
694
+ expect(capturedUrl).toContain("model=aura-luna-en");
695
+ });
696
+
697
+ // -- Content type / format -----------------------------------------------
698
+
699
+ test("returns audio/mpeg content type for mp3 format", async () => {
700
+ const audioPayload = new Uint8Array([0x49, 0x44, 0x33]);
701
+ mockFetchReturning(audioPayload);
702
+
703
+ const provider = createDeepgramProvider();
704
+ const result = await provider.synthesize(makeRequest());
705
+
706
+ expect(result.contentType).toBe("audio/mpeg");
707
+ expect(result.audio.byteLength).toBeGreaterThan(0);
708
+ });
709
+
710
+ // -- Required config validation ------------------------------------------
711
+
712
+ test("throws DEEPGRAM_TTS_NO_API_KEY when API key is missing", async () => {
713
+ mockDeepgramApiKey = null;
714
+
715
+ const provider = createDeepgramProvider();
716
+
717
+ try {
718
+ await provider.synthesize(makeRequest());
719
+ throw new Error("Expected synthesize to throw");
720
+ } catch (err) {
721
+ expect(err).toBeInstanceOf(DeepgramTtsError);
722
+ expect((err as DeepgramTtsError).code).toBe("DEEPGRAM_TTS_NO_API_KEY");
723
+ expect((err as DeepgramTtsError).message).toContain(
724
+ "API key not configured",
725
+ );
726
+ }
727
+ });
728
+
729
+ // -- Error handling ------------------------------------------------------
730
+
731
+ test("throws DEEPGRAM_TTS_HTTP_ERROR on non-200 response", async () => {
732
+ mockFetchError(401, "Unauthorized");
733
+
734
+ const provider = createDeepgramProvider();
735
+
736
+ try {
737
+ await provider.synthesize(makeRequest());
738
+ throw new Error("Expected synthesize to throw");
739
+ } catch (err) {
740
+ expect(err).toBeInstanceOf(DeepgramTtsError);
741
+ expect((err as DeepgramTtsError).code).toBe("DEEPGRAM_TTS_HTTP_ERROR");
742
+ expect((err as DeepgramTtsError).statusCode).toBe(401);
743
+ }
744
+ });
745
+
746
+ test("throws DEEPGRAM_TTS_EMPTY_RESPONSE on empty audio body", async () => {
747
+ mockFetchReturning(new Uint8Array(0));
748
+
749
+ const provider = createDeepgramProvider();
750
+
751
+ try {
752
+ await provider.synthesize(makeRequest());
753
+ throw new Error("Expected synthesize to throw");
754
+ } catch (err) {
755
+ expect(err).toBeInstanceOf(DeepgramTtsError);
756
+ expect((err as DeepgramTtsError).code).toBe(
757
+ "DEEPGRAM_TTS_EMPTY_RESPONSE",
758
+ );
759
+ }
760
+ });
761
+
762
+ test("throws DEEPGRAM_TTS_REQUEST_FAILED on network error", async () => {
763
+ globalThis.fetch = mock(async () => {
764
+ throw new Error("Network unreachable");
765
+ }) as unknown as typeof globalThis.fetch;
766
+
767
+ const provider = createDeepgramProvider();
768
+
769
+ try {
770
+ await provider.synthesize(makeRequest());
771
+ throw new Error("Expected synthesize to throw");
772
+ } catch (err) {
773
+ expect(err).toBeInstanceOf(DeepgramTtsError);
774
+ expect((err as DeepgramTtsError).code).toBe(
775
+ "DEEPGRAM_TTS_REQUEST_FAILED",
776
+ );
777
+ expect((err as DeepgramTtsError).message).toContain(
778
+ "Network unreachable",
779
+ );
780
+ }
781
+ });
782
+ });
783
+
784
+ // ===========================================================================
785
+ // xAI TTS provider adapter
786
+ // ===========================================================================
787
+
788
+ describe("xAI TTS provider adapter", () => {
789
+ // -- Interface conformance -----------------------------------------------
790
+
791
+ test("has correct provider ID", () => {
792
+ const provider = createXaiProvider();
793
+ expect(provider.id).toBe("xai");
794
+ });
795
+
796
+ test("advertises mp3 and wav format support without streaming", () => {
797
+ const provider = createXaiProvider();
798
+ expect(provider.capabilities).toEqual({
799
+ supportsStreaming: false,
800
+ supportedFormats: ["mp3", "wav"],
801
+ });
802
+ });
803
+
804
+ // -- Request mapping -----------------------------------------------------
805
+
806
+ test("synthesize posts to /v1/tts with correct auth and default body", async () => {
807
+ const audioPayload = new Uint8Array([0x49, 0x44, 0x33]);
808
+ let capturedUrl = "";
809
+ let capturedHeaders: Headers | null = null;
810
+ let capturedBody = "";
811
+
812
+ globalThis.fetch = mock(
813
+ async (input: RequestInfo | URL, init?: RequestInit) => {
814
+ capturedUrl = typeof input === "string" ? input : input.toString();
815
+ capturedHeaders = new Headers(init?.headers);
816
+ capturedBody = init?.body as string;
817
+ return new Response(audioPayload, {
818
+ status: 200,
819
+ headers: { "Content-Type": "audio/mpeg" },
820
+ });
821
+ },
822
+ ) as unknown as typeof globalThis.fetch;
823
+
824
+ const provider = createXaiProvider();
825
+ await provider.synthesize(makeRequest());
826
+
827
+ expect(capturedUrl).toBe("https://api.x.ai/v1/tts");
828
+ expect(capturedHeaders!.get("Authorization")).toBe(
829
+ "Bearer test-xai-api-key",
830
+ );
831
+ expect(capturedHeaders!.get("Content-Type")).toBe("application/json");
832
+
833
+ const body = JSON.parse(capturedBody);
834
+ expect(body.text).toBe("Hello world");
835
+ expect(body.voice_id).toBe("eve");
836
+ expect(body.language).toBe("auto");
837
+ expect(body.output_format).toEqual({
838
+ codec: "mp3",
839
+ sample_rate: 24000,
840
+ bit_rate: 128000,
841
+ });
842
+ });
843
+
844
+ test("request voiceId overrides config voiceId", async () => {
845
+ const audioPayload = new Uint8Array([0x49, 0x44, 0x33]);
846
+ let capturedBody = "";
847
+
848
+ globalThis.fetch = mock(
849
+ async (_input: RequestInfo | URL, init?: RequestInit) => {
850
+ capturedBody = init?.body as string;
851
+ return new Response(audioPayload, { status: 200 });
852
+ },
853
+ ) as unknown as typeof globalThis.fetch;
854
+
855
+ const provider = createXaiProvider();
856
+ await provider.synthesize(makeRequest({ voiceId: "rex" }));
857
+
858
+ const body = JSON.parse(capturedBody);
859
+ expect(body.voice_id).toBe("rex");
860
+ });
861
+
862
+ test("uses configured voiceId when request has none", async () => {
863
+ mockXaiConfig.voiceId = "ara";
864
+ const audioPayload = new Uint8Array([0x49, 0x44, 0x33]);
865
+ let capturedBody = "";
866
+
867
+ globalThis.fetch = mock(
868
+ async (_input: RequestInfo | URL, init?: RequestInit) => {
869
+ capturedBody = init?.body as string;
870
+ return new Response(audioPayload, { status: 200 });
871
+ },
872
+ ) as unknown as typeof globalThis.fetch;
873
+
874
+ const provider = createXaiProvider();
875
+ await provider.synthesize(makeRequest());
876
+
877
+ const body = JSON.parse(capturedBody);
878
+ expect(body.voice_id).toBe("ara");
879
+ });
880
+
881
+ test("wav config format produces codec=wav without bit_rate", async () => {
882
+ mockXaiConfig.format = "wav";
883
+ const audioPayload = new Uint8Array([0x52, 0x49, 0x46, 0x46]);
884
+ let capturedBody = "";
885
+
886
+ globalThis.fetch = mock(
887
+ async (_input: RequestInfo | URL, init?: RequestInit) => {
888
+ capturedBody = init?.body as string;
889
+ return new Response(audioPayload, { status: 200 });
890
+ },
891
+ ) as unknown as typeof globalThis.fetch;
892
+
893
+ const provider = createXaiProvider();
894
+ const result = await provider.synthesize(makeRequest());
895
+
896
+ const body = JSON.parse(capturedBody);
897
+ expect(body.output_format).toEqual({
898
+ codec: "wav",
899
+ sample_rate: 24000,
900
+ });
901
+ expect(body.output_format.bit_rate).toBeUndefined();
902
+ expect(result.contentType).toBe("audio/wav");
903
+ });
904
+
905
+ test("outputFormat=pcm uses codec=pcm and 16 kHz sample rate", async () => {
906
+ const audioPayload = new Uint8Array([0x00, 0x01, 0x02]);
907
+ let capturedBody = "";
908
+
909
+ globalThis.fetch = mock(
910
+ async (_input: RequestInfo | URL, init?: RequestInit) => {
911
+ capturedBody = init?.body as string;
912
+ return new Response(audioPayload, { status: 200 });
913
+ },
914
+ ) as unknown as typeof globalThis.fetch;
915
+
916
+ const provider = createXaiProvider();
917
+ const result = await provider.synthesize(
918
+ makeRequest({ outputFormat: "pcm" }),
919
+ );
920
+
921
+ const body = JSON.parse(capturedBody);
922
+ expect(body.output_format).toEqual({
923
+ codec: "pcm",
924
+ sample_rate: 16000,
925
+ });
926
+ expect(body.output_format.bit_rate).toBeUndefined();
927
+ expect(result.contentType).toBe("audio/pcm");
928
+ });
929
+
930
+ // -- Content type / format -----------------------------------------------
931
+
932
+ test("returns audio/mpeg content type for mp3 format", async () => {
933
+ const audioPayload = new Uint8Array([0x49, 0x44, 0x33]);
934
+ mockFetchReturning(audioPayload);
935
+
936
+ const provider = createXaiProvider();
937
+ const result = await provider.synthesize(makeRequest());
938
+
939
+ expect(result.contentType).toBe("audio/mpeg");
940
+ expect(result.audio.byteLength).toBeGreaterThan(0);
941
+ });
942
+
943
+ // -- Required config validation ------------------------------------------
944
+
945
+ test("throws XAI_TTS_NO_API_KEY when API key is missing", async () => {
946
+ mockXaiApiKey = null;
947
+
948
+ const provider = createXaiProvider();
949
+
950
+ try {
951
+ await provider.synthesize(makeRequest());
952
+ throw new Error("Expected synthesize to throw");
953
+ } catch (err) {
954
+ expect(err).toBeInstanceOf(XaiTtsError);
955
+ expect((err as XaiTtsError).code).toBe("XAI_TTS_NO_API_KEY");
956
+ expect((err as XaiTtsError).message).toContain("API key not configured");
957
+ }
958
+ });
959
+
960
+ // -- Error handling ------------------------------------------------------
961
+
962
+ test("throws XAI_TTS_HTTP_ERROR on non-200 response", async () => {
963
+ mockFetchError(401, "Unauthorized");
964
+
965
+ const provider = createXaiProvider();
966
+
967
+ try {
968
+ await provider.synthesize(makeRequest());
969
+ throw new Error("Expected synthesize to throw");
970
+ } catch (err) {
971
+ expect(err).toBeInstanceOf(XaiTtsError);
972
+ expect((err as XaiTtsError).code).toBe("XAI_TTS_HTTP_ERROR");
973
+ expect((err as XaiTtsError).statusCode).toBe(401);
974
+ expect((err as XaiTtsError).message).toContain("Unauthorized");
975
+ }
976
+ });
977
+
978
+ test("throws XAI_TTS_EMPTY_RESPONSE on empty audio body", async () => {
979
+ mockFetchReturning(new Uint8Array(0));
980
+
981
+ const provider = createXaiProvider();
982
+
983
+ try {
984
+ await provider.synthesize(makeRequest());
985
+ throw new Error("Expected synthesize to throw");
986
+ } catch (err) {
987
+ expect(err).toBeInstanceOf(XaiTtsError);
988
+ expect((err as XaiTtsError).code).toBe("XAI_TTS_EMPTY_RESPONSE");
989
+ }
990
+ });
991
+ });
992
+
993
+ // ===========================================================================
994
+ // Built-in registration
995
+ // ===========================================================================
996
+
997
+ describe("registerBuiltinTtsProviders", () => {
998
+ test("registers every catalog provider ID", () => {
999
+ registerBuiltinTtsProviders();
1000
+
1001
+ const providers = listTtsProviders();
1002
+ const ids = providers.map((p) => p.id);
1003
+ for (const id of listCatalogProviderIds()) {
1004
+ expect(ids).toContain(id);
1005
+ }
1006
+ });
1007
+
1008
+ test("providers are discoverable via getTtsProvider after registration", () => {
1009
+ registerBuiltinTtsProviders();
1010
+
1011
+ for (const id of listCatalogProviderIds()) {
1012
+ const provider = getTtsProvider(id);
1013
+ expect(provider.id).toBe(id);
1014
+ }
1015
+ });
1016
+
1017
+ test("idempotent — calling twice does not throw", () => {
1018
+ // First call registers; second should be a no-op due to the guard flag.
1019
+ // However, because tests reset the registry via afterEach, the internal
1020
+ // `registered` flag may still be true. We call it once here and verify
1021
+ // it does not throw — that exercises the guard path.
1022
+ registerBuiltinTtsProviders();
1023
+ expect(() => registerBuiltinTtsProviders()).not.toThrow();
1024
+ });
1025
+
1026
+ test("registers every provider declared in the catalog", () => {
1027
+ registerBuiltinTtsProviders();
1028
+
1029
+ const catalogIds = listCatalogProviderIds();
1030
+ const registeredProviders = listTtsProviders();
1031
+ const registeredIds = registeredProviders.map((p) => p.id);
1032
+
1033
+ for (const id of catalogIds) {
1034
+ expect(registeredIds).toContain(id);
1035
+ }
1036
+ // The registered set should match the catalog exactly (no extras).
1037
+ expect(registeredIds.length).toBe(catalogIds.length);
1038
+ });
1039
+
1040
+ test("every catalog provider has a factory in the providerFactories map", () => {
1041
+ const catalogIds = listCatalogProviderIds();
1042
+
1043
+ for (const id of catalogIds) {
1044
+ expect(providerFactories.has(id)).toBe(true);
1045
+ }
1046
+ });
1047
+
1048
+ test("throws when a catalog provider has no adapter factory", () => {
1049
+ // Verify the error path by checking that the error message format is
1050
+ // correct. We cannot easily add a fake catalog entry without modifying
1051
+ // the catalog module, but we can verify the factory map keys match the
1052
+ // catalog — if they diverge this test will catch it.
1053
+ const catalogIds = listCatalogProviderIds();
1054
+ const factoryIds = [...providerFactories.keys()];
1055
+
1056
+ const missingFactories = catalogIds.filter(
1057
+ (id) => !factoryIds.includes(id),
1058
+ );
1059
+ expect(missingFactories).toEqual([]);
1060
+ });
1061
+ });