@vellumai/assistant 0.6.3 → 0.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (667) hide show
  1. package/ARCHITECTURE.md +273 -10
  2. package/Dockerfile +2 -3
  3. package/bun.lock +5 -13
  4. package/docs/backup-troubleshooting.md +52 -0
  5. package/docs/browser-use-architecture-phase2.md +174 -0
  6. package/docs/stt-provider-onboarding.md +120 -0
  7. package/knip.json +12 -2
  8. package/node_modules/@vellumai/ces-contracts/bun.lock +8 -6
  9. package/node_modules/@vellumai/ces-contracts/package.json +3 -3
  10. package/openapi.yaml +982 -72
  11. package/package.json +4 -6
  12. package/scripts/generate-openapi.ts +0 -1
  13. package/scripts/test.sh +73 -18
  14. package/src/__tests__/agent-image-optimize.test.ts +28 -0
  15. package/src/__tests__/agent-loop.test.ts +123 -0
  16. package/src/__tests__/anthropic-provider.test.ts +263 -10
  17. package/src/__tests__/auto-analysis-end-to-end.test.ts +550 -0
  18. package/src/__tests__/auto-analysis-prompt.test.ts +50 -0
  19. package/src/__tests__/browser-fill-credential.test.ts +11 -0
  20. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +2 -2
  21. package/src/__tests__/browser-skill-endstate.test.ts +31 -7
  22. package/src/__tests__/btw-routes.test.ts +7 -0
  23. package/src/__tests__/call-controller.test.ts +581 -20
  24. package/src/__tests__/catalog-files.test.ts +138 -0
  25. package/src/__tests__/channel-invite-transport.test.ts +2 -2
  26. package/src/__tests__/channel-readiness-routes.test.ts +16 -20
  27. package/src/__tests__/channel-readiness-service.test.ts +12 -7
  28. package/src/__tests__/checker.test.ts +157 -10
  29. package/src/__tests__/clawhub-files.test.ts +347 -0
  30. package/src/__tests__/commit-message-enrichment-service.test.ts +36 -19
  31. package/src/__tests__/config-analysis.test.ts +100 -0
  32. package/src/__tests__/config-schema.test.ts +1013 -66
  33. package/src/__tests__/config-watcher-cleanup-throttle.test.ts +339 -0
  34. package/src/__tests__/config-watcher.test.ts +43 -8
  35. package/src/__tests__/contact-store-user-file.test.ts +512 -0
  36. package/src/__tests__/contacts-write.test.ts +197 -0
  37. package/src/__tests__/context-window-manager.test.ts +88 -0
  38. package/src/__tests__/conversation-abort-tool-results.test.ts +2 -0
  39. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
  40. package/src/__tests__/conversation-agent-loop.test.ts +98 -2
  41. package/src/__tests__/conversation-confirmation-signals.test.ts +135 -0
  42. package/src/__tests__/conversation-error.test.ts +70 -0
  43. package/src/__tests__/conversation-history-web-search.test.ts +11 -4
  44. package/src/__tests__/conversation-init.benchmark.test.ts +6 -1
  45. package/src/__tests__/conversation-launcher-skill-regression.test.ts +51 -0
  46. package/src/__tests__/conversation-list-source.test.ts +145 -0
  47. package/src/__tests__/conversation-pre-run-repair.test.ts +2 -0
  48. package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -0
  49. package/src/__tests__/conversation-queue.test.ts +901 -60
  50. package/src/__tests__/conversation-routes-disk-view.test.ts +270 -0
  51. package/src/__tests__/conversation-runtime-assembly.test.ts +55 -0
  52. package/src/__tests__/conversation-skill-tools.test.ts +7 -4
  53. package/src/__tests__/conversation-slash-commands.test.ts +33 -0
  54. package/src/__tests__/conversation-slash-queue.test.ts +89 -18
  55. package/src/__tests__/conversation-slash-unknown.test.ts +2 -0
  56. package/src/__tests__/conversation-tool-setup-batch-authorized.test.ts +226 -0
  57. package/src/__tests__/conversation-workspace-injection.test.ts +2 -0
  58. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +2 -0
  59. package/src/__tests__/credential-health-service.test.ts +352 -0
  60. package/src/__tests__/credential-security-invariants.test.ts +5 -3
  61. package/src/__tests__/credential-vault-unit.test.ts +379 -3
  62. package/src/__tests__/credentials-cli.test.ts +40 -16
  63. package/src/__tests__/cross-provider-web-search.test.ts +146 -35
  64. package/src/__tests__/deterministic-verification-control-plane.test.ts +10 -1
  65. package/src/__tests__/device-id.test.ts +112 -0
  66. package/src/__tests__/docker-signing-key-bootstrap.test.ts +167 -4
  67. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +1 -3
  68. package/src/__tests__/email-html-renderer.test.ts +71 -0
  69. package/src/__tests__/email-invite-adapter.test.ts +36 -32
  70. package/src/__tests__/emit-event-signal.test.ts +71 -0
  71. package/src/__tests__/extension-id-sync-guard.test.ts +75 -8
  72. package/src/__tests__/fixtures/mock-chrome-extension.ts +11 -0
  73. package/src/__tests__/gateway-only-enforcement.test.ts +206 -1
  74. package/src/__tests__/gateway-only-guard.test.ts +0 -1
  75. package/src/__tests__/gemini-provider.test.ts +64 -0
  76. package/src/__tests__/get-skill-detail-audit.test.ts +325 -0
  77. package/src/__tests__/gmail-archive-fallback.test.ts +193 -0
  78. package/src/__tests__/gmail-archive-gate.test.ts +246 -0
  79. package/src/__tests__/gmail-preferences.test.ts +117 -0
  80. package/src/__tests__/headless-browser-interactions.test.ts +43 -0
  81. package/src/__tests__/headless-browser-mode.test.ts +614 -0
  82. package/src/__tests__/headless-browser-navigate.test.ts +142 -5
  83. package/src/__tests__/headless-browser-read-tools.test.ts +11 -0
  84. package/src/__tests__/headless-browser-snapshot.test.ts +10 -0
  85. package/src/__tests__/heartbeat-service.test.ts +70 -17
  86. package/src/__tests__/home-state-routes.test.ts +162 -0
  87. package/src/__tests__/host-bash-proxy.test.ts +0 -5
  88. package/src/__tests__/host-browser-e2e-cloud.test.ts +138 -4
  89. package/src/__tests__/host-browser-e2e-self-hosted.test.ts +4 -4
  90. package/src/__tests__/host-browser-ws-events-e2e.test.ts +103 -0
  91. package/src/__tests__/host-cu-proxy.test.ts +0 -5
  92. package/src/__tests__/identity-intro-cache.test.ts +40 -10
  93. package/src/__tests__/init-feature-flag-overrides.test.ts +38 -112
  94. package/src/__tests__/jobs-store-upsert-debounced.test.ts +141 -0
  95. package/src/__tests__/llm-context-normalization.test.ts +488 -0
  96. package/src/__tests__/llm-context-route-provider.test.ts +86 -5
  97. package/src/__tests__/llm-usage-store.test.ts +363 -0
  98. package/src/__tests__/media-stream-output.test.ts +555 -0
  99. package/src/__tests__/media-stream-parser.test.ts +374 -0
  100. package/src/__tests__/media-stream-server-integration.test.ts +1234 -0
  101. package/src/__tests__/media-stream-stt-session.test.ts +588 -0
  102. package/src/__tests__/media-turn-detector.test.ts +440 -0
  103. package/src/__tests__/message-queue.test.ts +125 -0
  104. package/src/__tests__/migration-export-http.test.ts +6 -6
  105. package/src/__tests__/migration-import-commit-http.test.ts +8 -6
  106. package/src/__tests__/migration-import-preflight-http.test.ts +6 -5
  107. package/src/__tests__/migration-validate-http.test.ts +3 -3
  108. package/src/__tests__/mock-gateway-ipc.ts +151 -0
  109. package/src/__tests__/model-intents.test.ts +2 -2
  110. package/src/__tests__/oauth-apps-routes.test.ts +1 -0
  111. package/src/__tests__/oauth-cli.test.ts +2 -0
  112. package/src/__tests__/oauth-connect-orchestrator.test.ts +2 -0
  113. package/src/__tests__/oauth-provider-serializer.test.ts +1 -0
  114. package/src/__tests__/oauth-providers-routes.test.ts +2 -0
  115. package/src/__tests__/oauth-store.test.ts +85 -0
  116. package/src/__tests__/oauth2-gateway-transport.test.ts +249 -6
  117. package/src/__tests__/onboarding-template-contract.test.ts +6 -13
  118. package/src/__tests__/openai-provider.test.ts +176 -0
  119. package/src/__tests__/openai-responses-cutover-guard.test.ts +184 -0
  120. package/src/__tests__/openai-responses-provider.test.ts +1105 -0
  121. package/src/__tests__/openrouter-token-estimation.test.ts +100 -0
  122. package/src/__tests__/outlook-unsubscribe.test.ts +31 -2
  123. package/src/__tests__/persona-resolver.test.ts +251 -0
  124. package/src/__tests__/platform-bash-auto-approve.test.ts +4 -0
  125. package/src/__tests__/platform.test.ts +92 -1
  126. package/src/__tests__/post-turn-tool-result-truncation.test.ts +47 -0
  127. package/src/__tests__/prechat-onboarding-contract.test.ts +267 -0
  128. package/src/__tests__/pricing.test.ts +174 -0
  129. package/src/__tests__/qdrant-manager.test.ts +29 -8
  130. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +194 -0
  131. package/src/__tests__/relationship-state-contract.test.ts +175 -0
  132. package/src/__tests__/relay-server.test.ts +423 -5
  133. package/src/__tests__/search-skills-unified.test.ts +118 -0
  134. package/src/__tests__/secret-scanner-executor.test.ts +4 -0
  135. package/src/__tests__/secure-keys.test.ts +107 -0
  136. package/src/__tests__/send-endpoint-busy.test.ts +5 -1
  137. package/src/__tests__/sequence-store.test.ts +1 -1
  138. package/src/__tests__/server-history-render.test.ts +49 -0
  139. package/src/__tests__/settings-routes.test.ts +201 -0
  140. package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
  141. package/src/__tests__/skills-file-content-endpoint.test.ts +276 -145
  142. package/src/__tests__/skills-files-catalog-fallback.test.ts +381 -93
  143. package/src/__tests__/skills.test.ts +5 -2
  144. package/src/__tests__/skillssh-files.test.ts +446 -0
  145. package/src/__tests__/slack-block-formatting.test.ts +110 -0
  146. package/src/__tests__/slack-channel-config.test.ts +564 -1
  147. package/src/__tests__/stt-catalog-parity.test.ts +282 -0
  148. package/src/__tests__/stt-stream-session.test.ts +535 -0
  149. package/src/__tests__/system-prompt.test.ts +112 -26
  150. package/src/__tests__/telephony-stt-routing.test.ts +329 -0
  151. package/src/__tests__/terminal-tools.test.ts +18 -7
  152. package/src/__tests__/test-preload.ts +18 -0
  153. package/src/__tests__/test-support/browser-skill-harness.ts +4 -1
  154. package/src/__tests__/tool-executor-lifecycle-events.test.ts +9 -5
  155. package/src/__tests__/tool-executor-shell-integration.test.ts +4 -0
  156. package/src/__tests__/tool-executor.test.ts +33 -24
  157. package/src/__tests__/tool-result-truncation.test.ts +36 -0
  158. package/src/__tests__/trust-store.test.ts +7 -1
  159. package/src/__tests__/trusted-contact-approval-notifier.test.ts +1 -1
  160. package/src/__tests__/tts-catalog-parity.test.ts +345 -0
  161. package/src/__tests__/twilio-routes-twiml.test.ts +512 -114
  162. package/src/__tests__/twilio-routes.test.ts +376 -0
  163. package/src/__tests__/unicode.test.ts +293 -0
  164. package/src/__tests__/update-bulletin-format.test.ts +59 -0
  165. package/src/__tests__/update-bulletin.test.ts +206 -5
  166. package/src/__tests__/usage-routes.test.ts +25 -4
  167. package/src/__tests__/user-reference.test.ts +46 -61
  168. package/src/__tests__/verification-control-plane-policy.test.ts +4 -0
  169. package/src/__tests__/voice-config-update.test.ts +403 -0
  170. package/src/__tests__/voice-quality.test.ts +434 -19
  171. package/src/__tests__/workspace-heartbeat-service.test.ts +7 -0
  172. package/src/__tests__/workspace-migration-033-stt-service-explicit-config.test.ts +547 -0
  173. package/src/__tests__/workspace-migration-034-remove-calls-voice-transcription-provider.test.ts +596 -0
  174. package/src/__tests__/workspace-migration-drop-user-md.test.ts +368 -0
  175. package/src/__tests__/workspace-migration-meets.test.ts +244 -0
  176. package/src/__tests__/workspace-migration-seed-device-id.test.ts +14 -20
  177. package/src/__tests__/workspace-policy.test.ts +2 -0
  178. package/src/agent/image-optimize.ts +24 -12
  179. package/src/agent/loop.ts +43 -3
  180. package/src/backup/__tests__/backup-key.test.ts +152 -0
  181. package/src/backup/__tests__/backup-worker.test.ts +767 -0
  182. package/src/backup/__tests__/list-snapshots.test.ts +87 -0
  183. package/src/backup/__tests__/local-writer.test.ts +218 -0
  184. package/src/backup/__tests__/offsite-writer.test.ts +641 -0
  185. package/src/backup/__tests__/paths.test.ts +300 -0
  186. package/src/backup/__tests__/restore.test.ts +498 -0
  187. package/src/backup/__tests__/snapshot-lock.test.ts +352 -0
  188. package/src/backup/__tests__/stream-crypt.test.ts +228 -0
  189. package/src/backup/backup-key.ts +137 -0
  190. package/src/backup/backup-worker.ts +459 -0
  191. package/src/backup/list-snapshots.ts +147 -0
  192. package/src/backup/local-writer.ts +133 -0
  193. package/src/backup/offsite-writer.ts +222 -0
  194. package/src/backup/paths.ts +226 -0
  195. package/src/backup/restore.ts +322 -0
  196. package/src/backup/snapshot-lock.ts +431 -0
  197. package/src/backup/stream-crypt.ts +263 -0
  198. package/src/bundler/package-resolver.ts +4 -0
  199. package/src/calls/audio-store.ts +11 -5
  200. package/src/calls/call-controller.ts +226 -71
  201. package/src/calls/call-domain.ts +9 -0
  202. package/src/calls/call-speech-output.ts +190 -0
  203. package/src/calls/call-transport.ts +77 -0
  204. package/src/calls/media-stream-audio-transcode.ts +173 -0
  205. package/src/calls/media-stream-output.ts +660 -0
  206. package/src/calls/media-stream-parser.ts +300 -0
  207. package/src/calls/media-stream-protocol.ts +166 -0
  208. package/src/calls/media-stream-server.ts +592 -0
  209. package/src/calls/media-stream-stt-session.ts +460 -0
  210. package/src/calls/media-turn-detector.ts +230 -0
  211. package/src/calls/relay-server.ts +90 -75
  212. package/src/calls/resolve-call-tts-provider.ts +136 -0
  213. package/src/calls/telephony-stt-routing.ts +145 -0
  214. package/src/calls/tts-call-strategy.ts +161 -0
  215. package/src/calls/tts-text-sanitizer.ts +32 -16
  216. package/src/calls/twilio-routes.ts +281 -17
  217. package/src/calls/voice-quality.ts +78 -35
  218. package/src/calls/voice-session-bridge.ts +8 -1
  219. package/src/channels/types.ts +16 -0
  220. package/src/cli/__tests__/run-assistant-command.ts +11 -1
  221. package/src/cli/commands/__tests__/backup.test.ts +1165 -0
  222. package/src/cli/commands/__tests__/domain-register.test.ts +234 -0
  223. package/src/cli/commands/__tests__/domain-status.test.ts +132 -0
  224. package/src/cli/commands/__tests__/email-attachment.test.ts +422 -0
  225. package/src/cli/commands/__tests__/email-download.test.ts +16 -1
  226. package/src/cli/commands/__tests__/email-list.test.ts +22 -4
  227. package/src/cli/commands/__tests__/email-register.test.ts +4 -4
  228. package/src/cli/commands/__tests__/email-send.test.ts +37 -4
  229. package/src/cli/commands/__tests__/email-status.test.ts +5 -1
  230. package/src/cli/commands/__tests__/email-unregister.test.ts +34 -5
  231. package/src/cli/commands/backup.ts +993 -0
  232. package/src/cli/commands/conversations.ts +77 -0
  233. package/src/cli/commands/credentials.ts +0 -1
  234. package/src/cli/commands/domain.ts +210 -0
  235. package/src/cli/commands/email.ts +255 -3
  236. package/src/cli/commands/oauth/__tests__/connect.test.ts +12 -0
  237. package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +1 -0
  238. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +1 -0
  239. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -0
  240. package/src/cli/commands/oauth/mode.ts +12 -3
  241. package/src/cli/commands/oauth/providers.ts +15 -0
  242. package/src/cli/commands/oauth/shared.ts +2 -1
  243. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +4 -9
  244. package/src/cli/commands/platform/__tests__/connect.test.ts +6 -0
  245. package/src/cli/commands/platform/__tests__/disconnect.test.ts +7 -1
  246. package/src/cli/commands/platform/__tests__/status.test.ts +6 -0
  247. package/src/cli/program.ts +30 -4
  248. package/src/config/__tests__/backup-schema.test.ts +134 -0
  249. package/src/config/assistant-feature-flags.ts +61 -62
  250. package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +37 -1
  251. package/src/config/bundled-skills/browser/SKILL.md +30 -5
  252. package/src/config/bundled-skills/browser/TOOLS.json +123 -0
  253. package/src/config/bundled-skills/browser/tools/browser-attach.ts +12 -0
  254. package/src/config/bundled-skills/browser/tools/browser-detach.ts +12 -0
  255. package/src/config/bundled-skills/browser/tools/browser-status.ts +12 -0
  256. package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +17 -0
  257. package/src/config/bundled-skills/contacts/SKILL.md +2 -2
  258. package/src/config/bundled-skills/gmail/SKILL.md +53 -7
  259. package/src/config/bundled-skills/gmail/TOOLS.json +33 -3
  260. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +116 -9
  261. package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +138 -11
  262. package/src/config/bundled-skills/gmail/tools/gmail-preferences-tool.ts +59 -0
  263. package/src/config/bundled-skills/gmail/tools/gmail-preferences.ts +82 -0
  264. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +113 -17
  265. package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +2 -2
  266. package/src/config/bundled-skills/media-processing/SKILL.md +3 -9
  267. package/src/config/bundled-skills/media-processing/TOOLS.json +1 -6
  268. package/src/config/bundled-skills/media-processing/__tests__/audio-transcribe.test.ts +125 -0
  269. package/src/config/bundled-skills/media-processing/__tests__/extract-keyframes.test.ts +181 -0
  270. package/src/config/bundled-skills/media-processing/__tests__/preprocess-audio.test.ts +141 -0
  271. package/src/config/bundled-skills/media-processing/services/audio-transcribe.ts +32 -87
  272. package/src/config/bundled-skills/media-processing/services/preprocess.ts +8 -4
  273. package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +0 -10
  274. package/src/config/bundled-skills/messaging/SKILL.md +3 -3
  275. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +2 -2
  276. package/src/config/bundled-skills/outlook/SKILL.md +2 -2
  277. package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +2 -2
  278. package/src/config/bundled-skills/phone-calls/SKILL.md +2 -2
  279. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +27 -18
  280. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +3 -3
  281. package/src/config/bundled-skills/settings/TOOLS.json +3 -3
  282. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +26 -22
  283. package/src/config/bundled-skills/slack/SKILL.md +1 -0
  284. package/src/config/bundled-skills/transcribe/SKILL.md +9 -14
  285. package/src/config/bundled-skills/transcribe/TOOLS.json +2 -7
  286. package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +256 -0
  287. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +38 -188
  288. package/src/config/bundled-tool-registry.ts +8 -0
  289. package/src/config/env-registry.ts +24 -0
  290. package/src/config/env.ts +34 -10
  291. package/src/config/feature-flag-registry.json +46 -14
  292. package/src/config/loader.ts +26 -12
  293. package/src/config/schema.ts +35 -10
  294. package/src/config/schemas/__tests__/stt.test.ts +43 -0
  295. package/src/config/schemas/analysis.ts +51 -0
  296. package/src/config/schemas/backup.ts +72 -0
  297. package/src/config/schemas/calls.ts +1 -26
  298. package/src/config/schemas/elevenlabs.ts +0 -59
  299. package/src/config/schemas/filing.ts +47 -7
  300. package/src/config/schemas/heartbeat.ts +27 -5
  301. package/src/config/schemas/host-browser.ts +47 -1
  302. package/src/config/schemas/inference.ts +1 -1
  303. package/src/config/schemas/memory-lifecycle.ts +14 -2
  304. package/src/config/schemas/services.ts +44 -0
  305. package/src/config/schemas/stt.ts +59 -0
  306. package/src/config/schemas/tts.ts +230 -0
  307. package/src/config/schemas/updates.ts +14 -0
  308. package/src/config/skills.ts +4 -0
  309. package/src/config/types.ts +4 -0
  310. package/src/contacts/contact-store.ts +56 -11
  311. package/src/contacts/contacts-write.ts +38 -1
  312. package/src/context/post-turn-tool-result-truncation.ts +3 -2
  313. package/src/context/tool-result-truncation.ts +2 -1
  314. package/src/context/window-manager.ts +45 -12
  315. package/src/credential-execution/executable-discovery.ts +12 -2
  316. package/src/credential-execution/process-manager.ts +33 -2
  317. package/src/credential-health/credential-health-service.ts +366 -0
  318. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +324 -0
  319. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +497 -0
  320. package/src/daemon/__tests__/conversation-tool-setup.test.ts +17 -8
  321. package/src/daemon/__tests__/lifecycle-startup-ordering.test.ts +127 -0
  322. package/src/daemon/config-watcher.ts +99 -5
  323. package/src/daemon/conversation-agent-loop-handlers.ts +6 -0
  324. package/src/daemon/conversation-agent-loop.ts +101 -24
  325. package/src/daemon/conversation-error.ts +11 -0
  326. package/src/daemon/conversation-history.ts +40 -6
  327. package/src/daemon/conversation-launch.ts +220 -0
  328. package/src/daemon/conversation-lifecycle.ts +59 -9
  329. package/src/daemon/conversation-messaging.ts +37 -3
  330. package/src/daemon/conversation-notifiers.ts +5 -0
  331. package/src/daemon/conversation-process.ts +581 -19
  332. package/src/daemon/conversation-queue-manager.ts +24 -0
  333. package/src/daemon/conversation-runtime-assembly.ts +11 -1
  334. package/src/daemon/conversation-slash.ts +36 -0
  335. package/src/daemon/conversation-surfaces.ts +94 -4
  336. package/src/daemon/conversation-tool-setup.ts +25 -0
  337. package/src/daemon/conversation-usage.ts +7 -4
  338. package/src/daemon/conversation.ts +86 -28
  339. package/src/daemon/handlers/config-slack-channel.ts +269 -94
  340. package/src/daemon/handlers/conversations.ts +4 -1
  341. package/src/daemon/handlers/shared.ts +22 -0
  342. package/src/daemon/handlers/skills.ts +321 -77
  343. package/src/daemon/host-browser-proxy.ts +2 -1
  344. package/src/daemon/lifecycle.ts +122 -25
  345. package/src/daemon/message-protocol.ts +6 -0
  346. package/src/daemon/message-types/conversations.ts +34 -1
  347. package/src/daemon/message-types/home.ts +40 -0
  348. package/src/daemon/message-types/meet.ts +143 -0
  349. package/src/daemon/message-types/messages.ts +14 -0
  350. package/src/daemon/message-types/schedules.ts +34 -2
  351. package/src/daemon/message-types/skills.ts +16 -0
  352. package/src/daemon/message-types/surfaces.ts +2 -0
  353. package/src/daemon/server.ts +347 -2
  354. package/src/daemon/shutdown-handlers.ts +32 -4
  355. package/src/daemon/shutdown-registry.ts +40 -0
  356. package/src/daemon/tool-side-effects.ts +9 -0
  357. package/src/email/html-renderer.ts +76 -0
  358. package/src/heartbeat/heartbeat-service.ts +93 -7
  359. package/src/home/__tests__/assistant-feed-authoring.test.ts +156 -0
  360. package/src/home/__tests__/emit-feed-event.test.ts +169 -0
  361. package/src/home/__tests__/feed-scheduler.test.ts +194 -0
  362. package/src/home/__tests__/feed-types.test.ts +275 -0
  363. package/src/home/__tests__/feed-writer.test.ts +688 -0
  364. package/src/home/__tests__/phase5-exit-criteria.test.ts +212 -0
  365. package/src/home/__tests__/platform-gmail-digest.test.ts +222 -0
  366. package/src/home/__tests__/progress-formula.test.ts +213 -0
  367. package/src/home/__tests__/relationship-state-writer.test.ts +740 -0
  368. package/src/home/__tests__/rollup-producer.test.ts +398 -0
  369. package/src/home/assistant-feed-authoring.ts +124 -0
  370. package/src/home/emit-feed-event.ts +158 -0
  371. package/src/home/feed-scheduler.ts +247 -0
  372. package/src/home/feed-types.ts +181 -0
  373. package/src/home/feed-writer.ts +469 -0
  374. package/src/home/platform-gmail-digest.ts +163 -0
  375. package/src/home/progress-formula.ts +86 -0
  376. package/src/home/relationship-state-writer.ts +824 -0
  377. package/src/home/relationship-state.ts +143 -0
  378. package/src/home/rollup-producer.ts +384 -0
  379. package/src/hooks/runner.ts +7 -0
  380. package/src/inbound/platform-callback-registration.ts +12 -3
  381. package/src/inbound/public-ingress-urls.ts +12 -0
  382. package/src/instrument.ts +1 -1
  383. package/src/ipc/__tests__/cli-ipc.test.ts +200 -0
  384. package/src/ipc/cli-client.ts +151 -0
  385. package/src/ipc/cli-server.ts +234 -0
  386. package/src/ipc/gateway-client.ts +180 -0
  387. package/src/ipc/routes/index.ts +5 -0
  388. package/src/ipc/routes/wake-conversation.ts +19 -0
  389. package/src/memory/__tests__/auto-analysis-enqueue.test.ts +356 -0
  390. package/src/memory/__tests__/auto-analysis-guard.test.ts +57 -0
  391. package/src/memory/__tests__/conversation-analyze-job.test.ts +232 -0
  392. package/src/memory/__tests__/find-analysis-conversation.test.ts +196 -0
  393. package/src/memory/app-store.ts +1 -1
  394. package/src/memory/attachments-store.ts +70 -0
  395. package/src/memory/auto-analysis-enqueue.ts +127 -0
  396. package/src/memory/auto-analysis-guard.ts +27 -0
  397. package/src/memory/cleanup-schedule-state.ts +37 -0
  398. package/src/memory/conversation-analyze-job.ts +73 -0
  399. package/src/memory/conversation-crud.ts +99 -0
  400. package/src/memory/conversation-disk-view.ts +7 -0
  401. package/src/memory/conversation-group-migration.ts +34 -2
  402. package/src/memory/conversation-queries.ts +6 -5
  403. package/src/memory/db-init.ts +6 -0
  404. package/src/memory/db-maintenance.ts +108 -0
  405. package/src/memory/db.ts +1 -0
  406. package/src/memory/graph/conversation-graph-memory.ts +15 -0
  407. package/src/memory/graph/extraction.test.ts +23 -0
  408. package/src/memory/graph/extraction.ts +8 -0
  409. package/src/memory/graph/retriever.ts +27 -18
  410. package/src/memory/graph/scoring.test.ts +186 -0
  411. package/src/memory/graph/scoring.ts +31 -1
  412. package/src/memory/graph/tools.ts +1 -1
  413. package/src/memory/group-crud.ts +6 -1
  414. package/src/memory/indexer.ts +95 -16
  415. package/src/memory/job-handlers/cleanup.ts +11 -8
  416. package/src/memory/job-handlers/conversation-starters.ts +16 -10
  417. package/src/memory/jobs-store.ts +64 -4
  418. package/src/memory/jobs-worker.ts +22 -9
  419. package/src/memory/llm-usage-store.ts +92 -56
  420. package/src/memory/migrations/219-oauth-providers-token-exchange-body-format.ts +15 -0
  421. package/src/memory/migrations/220-normalize-user-file-by-principal.ts +190 -0
  422. package/src/memory/migrations/221-conversations-archived-at.ts +16 -0
  423. package/src/memory/migrations/index.ts +6 -0
  424. package/src/memory/migrations/registry.ts +8 -0
  425. package/src/memory/qdrant-manager.ts +43 -16
  426. package/src/memory/schema/conversations.ts +2 -0
  427. package/src/memory/schema/oauth.ts +3 -0
  428. package/src/memory/usage-buckets.ts +396 -0
  429. package/src/messaging/providers/gmail/client.ts +57 -6
  430. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +282 -0
  431. package/src/messaging/providers/slack/adapter.ts +143 -38
  432. package/src/messaging/providers/slack/client.ts +16 -0
  433. package/src/messaging/providers/slack/types.ts +4 -0
  434. package/src/notifications/decision-engine.ts +3 -3
  435. package/src/notifications/signal.ts +5 -0
  436. package/src/oauth/__tests__/identity-verifier.test.ts +1 -0
  437. package/src/oauth/byo-connection.test.ts +18 -1
  438. package/src/oauth/byo-connection.ts +3 -1
  439. package/src/oauth/connect-orchestrator.ts +2 -0
  440. package/src/oauth/connection-resolver.ts +6 -2
  441. package/src/oauth/connection.ts +2 -0
  442. package/src/oauth/oauth-store.ts +9 -0
  443. package/src/oauth/platform-connection.test.ts +98 -0
  444. package/src/oauth/platform-connection.ts +52 -31
  445. package/src/oauth/seed-providers.ts +7 -0
  446. package/src/permissions/checker.ts +16 -6
  447. package/src/permissions/defaults.ts +49 -1
  448. package/src/permissions/trust-store.ts +3 -3
  449. package/src/permissions/workspace-policy.ts +3 -0
  450. package/src/platform/client.test.ts +10 -0
  451. package/src/platform/sync-identity.ts +129 -0
  452. package/src/prompts/persona-resolver.ts +126 -2
  453. package/src/prompts/system-prompt.ts +59 -18
  454. package/src/prompts/templates/BOOTSTRAP.md +5 -5
  455. package/src/prompts/templates/SOUL.md +3 -1
  456. package/src/prompts/templates/UPDATES.md +12 -0
  457. package/src/prompts/templates/channels/slack.md +20 -0
  458. package/src/prompts/update-bulletin-format.ts +26 -9
  459. package/src/prompts/update-bulletin.ts +34 -23
  460. package/src/prompts/user-reference.ts +20 -17
  461. package/src/providers/__tests__/provider-secret-catalog.test.ts +42 -0
  462. package/src/providers/anthropic/client.ts +157 -61
  463. package/src/providers/fireworks/client.ts +2 -2
  464. package/src/providers/gemini/client.ts +9 -1
  465. package/src/providers/model-catalog.ts +6 -0
  466. package/src/providers/model-intents.ts +4 -4
  467. package/src/providers/ollama/client.ts +2 -2
  468. package/src/providers/openai/chat-completions-provider.ts +474 -0
  469. package/src/providers/openai/client.ts +25 -440
  470. package/src/providers/openai/responses-provider.ts +502 -0
  471. package/src/providers/openrouter/client.ts +101 -4
  472. package/src/providers/provider-secret-catalog.ts +139 -0
  473. package/src/providers/registry.ts +2 -2
  474. package/src/providers/retry.ts +14 -3
  475. package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +251 -0
  476. package/src/providers/speech-to-text/__tests__/resolve.test.ts +828 -0
  477. package/src/providers/speech-to-text/deepgram-realtime.test.ts +980 -0
  478. package/src/providers/speech-to-text/deepgram-realtime.ts +767 -0
  479. package/src/providers/speech-to-text/deepgram.test.ts +332 -0
  480. package/src/providers/speech-to-text/deepgram.ts +115 -0
  481. package/src/providers/speech-to-text/google-gemini-live-stream.test.ts +743 -0
  482. package/src/providers/speech-to-text/google-gemini-live-stream.ts +625 -0
  483. package/src/providers/speech-to-text/google-gemini.test.ts +226 -0
  484. package/src/providers/speech-to-text/google-gemini.ts +101 -0
  485. package/src/providers/speech-to-text/openai-whisper-stream.test.ts +564 -0
  486. package/src/providers/speech-to-text/openai-whisper-stream.ts +381 -0
  487. package/src/providers/speech-to-text/openai-whisper.test.ts +1 -37
  488. package/src/providers/speech-to-text/openai-whisper.ts +63 -33
  489. package/src/providers/speech-to-text/provider-catalog.ts +306 -0
  490. package/src/providers/speech-to-text/resolve.ts +386 -6
  491. package/src/providers/types.ts +9 -0
  492. package/src/runtime/AGENTS.md +43 -1
  493. package/src/runtime/__tests__/agent-wake.test.ts +831 -0
  494. package/src/runtime/__tests__/runtime-mode.test.ts +62 -0
  495. package/src/runtime/__tests__/slack-block-formatting.test.ts +481 -0
  496. package/src/runtime/agent-wake.ts +512 -0
  497. package/src/runtime/auth/__tests__/route-policy.test.ts +40 -0
  498. package/src/runtime/auth/route-policy.ts +30 -5
  499. package/src/runtime/auth/token-service.ts +56 -1
  500. package/src/runtime/btw-sidechain.ts +2 -0
  501. package/src/runtime/capability-tokens.ts +10 -10
  502. package/src/runtime/channel-invite-transport.ts +1 -1
  503. package/src/runtime/channel-invite-transports/email.ts +14 -6
  504. package/src/runtime/channel-readiness-service.ts +12 -22
  505. package/src/runtime/chrome-extension-registry.ts +38 -2
  506. package/src/runtime/http-server.ts +395 -10
  507. package/src/runtime/http-types.ts +6 -2
  508. package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +36 -0
  509. package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +360 -0
  510. package/src/runtime/migrations/migration-transport.ts +1 -0
  511. package/src/runtime/migrations/migration-wizard.ts +1 -0
  512. package/src/runtime/migrations/vbundle-import-analyzer.ts +77 -1
  513. package/src/runtime/migrations/vbundle-importer.ts +34 -0
  514. package/src/runtime/pending-interactions.ts +0 -11
  515. package/src/runtime/routes/__tests__/backup-routes.test.ts +967 -0
  516. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +507 -0
  517. package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +208 -0
  518. package/src/runtime/routes/__tests__/stt-routes.test.ts +406 -0
  519. package/src/runtime/routes/__tests__/tts-routes.test.ts +474 -0
  520. package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +148 -17
  521. package/src/runtime/routes/app-management-routes.ts +12 -18
  522. package/src/runtime/routes/attachment-routes.test.ts +9 -3
  523. package/src/runtime/routes/attachment-routes.ts +216 -17
  524. package/src/runtime/routes/backup-routes.ts +519 -0
  525. package/src/runtime/routes/browser-extension-pair-routes.ts +82 -23
  526. package/src/runtime/routes/btw-routes.ts +8 -6
  527. package/src/runtime/routes/contact-routes.test.ts +298 -0
  528. package/src/runtime/routes/contact-routes.ts +132 -5
  529. package/src/runtime/routes/conversation-analysis-routes.ts +22 -142
  530. package/src/runtime/routes/conversation-management-routes.ts +115 -0
  531. package/src/runtime/routes/conversation-routes.ts +367 -146
  532. package/src/runtime/routes/filing-routes.ts +93 -0
  533. package/src/runtime/routes/home-feed-routes.ts +334 -0
  534. package/src/runtime/routes/home-state-routes.ts +138 -0
  535. package/src/runtime/routes/host-browser-routes.ts +3 -14
  536. package/src/runtime/routes/identity-intro-cache.ts +7 -3
  537. package/src/runtime/routes/identity-routes.ts +3 -17
  538. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +46 -39
  539. package/src/runtime/routes/inbound-stages/transcribe-audio.ts +15 -15
  540. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +137 -0
  541. package/src/runtime/routes/integrations/slack/__tests__/share.test.ts +179 -0
  542. package/src/runtime/routes/integrations/slack/channel.ts +11 -3
  543. package/src/runtime/routes/integrations/slack/share.ts +45 -7
  544. package/src/runtime/routes/llm-context-normalization.ts +303 -0
  545. package/src/runtime/routes/memory-item-routes.test.ts +3 -2
  546. package/src/runtime/routes/migration-routes.ts +40 -5
  547. package/src/runtime/routes/settings-routes.ts +22 -5
  548. package/src/runtime/routes/skills-routes.ts +76 -7
  549. package/src/runtime/routes/stt-routes.ts +233 -0
  550. package/src/runtime/routes/surface-action-routes.ts +41 -2
  551. package/src/runtime/routes/tts-routes.ts +108 -24
  552. package/src/runtime/routes/usage-routes.ts +30 -2
  553. package/src/runtime/routes/user-route-dispatcher.ts +50 -5
  554. package/src/runtime/routes/user-routes.ts +13 -1
  555. package/src/runtime/routes/work-items-routes.ts +8 -1
  556. package/src/runtime/runtime-mode.ts +33 -0
  557. package/src/runtime/services/__tests__/analyze-conversation.test.ts +444 -0
  558. package/src/runtime/services/__tests__/analyze-deps-singleton.test.ts +67 -0
  559. package/src/runtime/services/__tests__/auto-analysis-prompt.test.ts +53 -0
  560. package/src/runtime/services/__tests__/manual-analysis-prompt.test.ts +41 -0
  561. package/src/runtime/services/analyze-conversation.ts +344 -0
  562. package/src/runtime/services/analyze-deps-singleton.ts +32 -0
  563. package/src/runtime/services/auto-analysis-prompt.ts +55 -0
  564. package/src/runtime/skill-route-registry.ts +49 -0
  565. package/src/runtime/slack-block-formatting.ts +437 -10
  566. package/src/schedule/scheduler.ts +50 -0
  567. package/src/security/oauth2.ts +26 -4
  568. package/src/security/secure-keys.ts +25 -2
  569. package/src/security/token-manager.ts +8 -0
  570. package/src/sequence/engine.ts +23 -0
  571. package/src/sequence/types.ts +1 -1
  572. package/src/skills/catalog-files.ts +64 -2
  573. package/src/skills/category-inference.ts +122 -0
  574. package/src/skills/clawhub-files.ts +213 -0
  575. package/src/skills/clawhub.ts +84 -23
  576. package/src/skills/skill-file-provider.ts +40 -0
  577. package/src/skills/skillssh-files.ts +395 -0
  578. package/src/skills/skillssh-registry.ts +4 -4
  579. package/src/stt/__tests__/daemon-batch-transcriber.test.ts +392 -0
  580. package/src/stt/__tests__/types.test.ts +89 -0
  581. package/src/stt/daemon-batch-transcriber.ts +195 -0
  582. package/src/stt/stt-stream-session.ts +499 -0
  583. package/src/stt/types.ts +330 -0
  584. package/src/stt/wav-encoder.test.ts +373 -0
  585. package/src/stt/wav-encoder.ts +175 -0
  586. package/src/subagent/manager.ts +38 -14
  587. package/src/tools/browser/__tests__/browser-mode.test.ts +119 -0
  588. package/src/tools/browser/__tests__/browser-status.test.ts +123 -0
  589. package/src/tools/browser/browser-execution.ts +1163 -23
  590. package/src/tools/browser/browser-manager.ts +45 -0
  591. package/src/tools/browser/browser-mode-constants.ts +12 -0
  592. package/src/tools/browser/browser-mode.ts +92 -0
  593. package/src/tools/browser/browser-status-constants.ts +33 -0
  594. package/src/tools/browser/cdp-client/__tests__/cdp-inspect-client.test.ts +393 -0
  595. package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +29 -0
  596. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +1648 -32
  597. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/discovery.test.ts +264 -0
  598. package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +183 -17
  599. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +254 -21
  600. package/src/tools/browser/cdp-client/errors.ts +15 -0
  601. package/src/tools/browser/cdp-client/extension-cdp-client.ts +39 -16
  602. package/src/tools/browser/cdp-client/factory.ts +797 -87
  603. package/src/tools/browser/cdp-client/index.ts +16 -2
  604. package/src/tools/browser/cdp-client/types.ts +68 -0
  605. package/src/tools/credentials/vault.ts +35 -6
  606. package/src/tools/network/web-fetch.ts +5 -2
  607. package/src/tools/network/web-search.ts +5 -2
  608. package/src/tools/shared/shell-output.ts +3 -1
  609. package/src/tools/side-effects.ts +2 -0
  610. package/src/tools/skills/sandbox-runner.ts +3 -2
  611. package/src/tools/terminal/safe-env.ts +10 -2
  612. package/src/tools/terminal/shell.ts +15 -4
  613. package/src/tools/tool-manifest.ts +21 -0
  614. package/src/tools/types.ts +17 -0
  615. package/src/tools/ui-surface/definitions.ts +6 -1
  616. package/src/tts/__tests__/provider-adapters.test.ts +834 -0
  617. package/src/tts/__tests__/provider-catalog-consistency.test.ts +196 -0
  618. package/src/tts/__tests__/provider-catalog.test.ts +183 -0
  619. package/src/tts/__tests__/provider-registry.test.ts +90 -0
  620. package/src/tts/provider-catalog.ts +201 -0
  621. package/src/tts/provider-registry.ts +73 -0
  622. package/src/tts/providers/deepgram-provider.ts +219 -0
  623. package/src/tts/providers/elevenlabs-provider.ts +211 -0
  624. package/src/tts/providers/fish-audio-provider.ts +183 -0
  625. package/src/tts/providers/index.ts +42 -0
  626. package/src/tts/providers/register-builtins.ts +130 -0
  627. package/src/tts/synthesize-text.ts +110 -0
  628. package/src/tts/tts-config-resolver.ts +78 -0
  629. package/src/tts/types.ts +153 -0
  630. package/src/types/onboarding-context.ts +7 -0
  631. package/src/util/abort-reasons.ts +58 -0
  632. package/src/util/device-id.ts +32 -16
  633. package/src/util/errors.ts +9 -1
  634. package/src/util/platform.ts +54 -10
  635. package/src/util/pricing.ts +66 -3
  636. package/src/util/spawn.ts +1 -1
  637. package/src/util/truncate.ts +4 -2
  638. package/src/util/unicode.ts +201 -0
  639. package/src/version.ts +19 -24
  640. package/src/watcher/engine.ts +23 -0
  641. package/src/watcher/watcher-store.ts +31 -0
  642. package/src/workspace/migrations/003-seed-device-id.ts +9 -3
  643. package/src/workspace/migrations/017-seed-persona-dirs.ts +68 -4
  644. package/src/workspace/migrations/029-seed-pkb.ts +1 -1
  645. package/src/workspace/migrations/031-drop-user-md.ts +317 -0
  646. package/src/workspace/migrations/031-llm-log-retention-zero-to-null.ts +73 -0
  647. package/src/workspace/migrations/032-tts-provider-unification.ts +227 -0
  648. package/src/workspace/migrations/033-stt-service-explicit-config.ts +122 -0
  649. package/src/workspace/migrations/034-remove-calls-voice-transcription-provider.ts +215 -0
  650. package/src/workspace/migrations/035-seed-slack-channel-persona.ts +50 -0
  651. package/src/workspace/migrations/036-update-pkb-index-bar.ts +37 -0
  652. package/src/workspace/migrations/037-create-meets-dir.ts +61 -0
  653. package/src/workspace/migrations/registry.ts +16 -0
  654. package/src/workspace/top-level-renderer.ts +13 -1
  655. package/src/workspace/turn-commit.ts +31 -0
  656. package/src/__tests__/email-cli.test.ts +0 -297
  657. package/src/__tests__/email-service-config-fallback.test.ts +0 -102
  658. package/src/cli/commands/browser-relay.ts +0 -466
  659. package/src/email/guardrails.ts +0 -221
  660. package/src/email/provider.ts +0 -117
  661. package/src/email/providers/agentmail.ts +0 -361
  662. package/src/email/providers/index.ts +0 -65
  663. package/src/email/service.ts +0 -384
  664. package/src/email/types.ts +0 -126
  665. package/src/prompts/templates/USER.md +0 -13
  666. package/src/providers/speech-to-text/types.ts +0 -17
  667. package/src/runtime/routes/browser-cdp-routes.ts +0 -229
@@ -0,0 +1,233 @@
1
+ /**
2
+ * HTTP route definitions for speech-to-text transcription.
3
+ *
4
+ * POST /v1/stt/transcribe — transcribe base64-encoded audio to text
5
+ *
6
+ * Uses the globally configured STT provider via the `services.stt`
7
+ * abstraction. Provider selection is resolved via `resolveBatchTranscriber()`
8
+ * from `providers/speech-to-text/resolve.ts`.
9
+ */
10
+
11
+ import { z } from "zod";
12
+
13
+ import { resolveBatchTranscriber } from "../../providers/speech-to-text/resolve.js";
14
+ import { normalizeSttError } from "../../stt/daemon-batch-transcriber.js";
15
+ import type { SttErrorCategory } from "../../stt/types.js";
16
+ import { getLogger } from "../../util/logger.js";
17
+ import { httpError } from "../http-errors.js";
18
+ import type { RouteDefinition } from "../http-router.js";
19
+
20
+ const log = getLogger("stt-routes");
21
+
22
+ /** Timeout for a single transcription request. */
23
+ const TRANSCRIPTION_TIMEOUT_MS = 30_000;
24
+
25
+ // ---------------------------------------------------------------------------
26
+ // Error category -> HTTP status / message mapping
27
+ // ---------------------------------------------------------------------------
28
+
29
+ const STT_ERROR_MAP: Record<
30
+ SttErrorCategory,
31
+ { status: number; code: string; message: string }
32
+ > = {
33
+ auth: {
34
+ status: 401,
35
+ code: "UNAUTHORIZED",
36
+ message: "STT provider credentials are invalid or missing",
37
+ },
38
+ "rate-limit": {
39
+ status: 429,
40
+ code: "RATE_LIMITED",
41
+ message: "STT provider rate limit exceeded",
42
+ },
43
+ timeout: {
44
+ status: 504,
45
+ code: "INTERNAL_ERROR",
46
+ message: "STT transcription timed out",
47
+ },
48
+ "invalid-audio": {
49
+ status: 400,
50
+ code: "BAD_REQUEST",
51
+ message: "Audio payload was rejected by the STT provider",
52
+ },
53
+ "provider-error": {
54
+ status: 502,
55
+ code: "INTERNAL_ERROR",
56
+ message: "STT provider error",
57
+ },
58
+ };
59
+
60
+ // ---------------------------------------------------------------------------
61
+ // Route definitions
62
+ // ---------------------------------------------------------------------------
63
+
64
+ export function sttRouteDefinitions(): RouteDefinition[] {
65
+ return [
66
+ {
67
+ endpoint: "stt/transcribe",
68
+ method: "POST",
69
+ policyKey: "stt/transcribe",
70
+ summary: "Transcribe audio to text",
71
+ description:
72
+ "Transcribe base64-encoded audio to text using the configured STT provider. " +
73
+ "Provider selection is resolved globally via config.",
74
+ tags: ["stt"],
75
+ requestBody: z.object({
76
+ audioBase64: z
77
+ .string()
78
+ .describe("Base64-encoded audio data to transcribe"),
79
+ mimeType: z
80
+ .string()
81
+ .describe(
82
+ 'MIME type of the audio data (must start with "audio/", e.g. "audio/wav", "audio/ogg")',
83
+ ),
84
+ source: z
85
+ .string()
86
+ .optional()
87
+ .describe(
88
+ "Optional source identifier for analytics (e.g. 'dictation', 'voice-mode')",
89
+ ),
90
+ }),
91
+ handler: async ({ req }) => {
92
+ // -- Parse body -------------------------------------------------------
93
+ let body: {
94
+ audioBase64?: unknown;
95
+ mimeType?: unknown;
96
+ source?: unknown;
97
+ };
98
+ try {
99
+ body = (await req.json()) as typeof body;
100
+ } catch {
101
+ return httpError("BAD_REQUEST", "Invalid JSON body", 400);
102
+ }
103
+
104
+ if (!body || typeof body !== "object") {
105
+ return httpError("BAD_REQUEST", "Invalid JSON body", 400);
106
+ }
107
+
108
+ // -- Validate audioBase64 ---------------------------------------------
109
+ if (
110
+ !body.audioBase64 ||
111
+ typeof body.audioBase64 !== "string" ||
112
+ body.audioBase64.length === 0
113
+ ) {
114
+ return httpError(
115
+ "BAD_REQUEST",
116
+ "audioBase64 is required and must be a non-empty string",
117
+ 400,
118
+ );
119
+ }
120
+
121
+ // -- Validate mimeType ------------------------------------------------
122
+ if (
123
+ !body.mimeType ||
124
+ typeof body.mimeType !== "string" ||
125
+ !body.mimeType.startsWith("audio/")
126
+ ) {
127
+ return httpError(
128
+ "BAD_REQUEST",
129
+ 'mimeType is required and must start with "audio/"',
130
+ 400,
131
+ );
132
+ }
133
+
134
+ // -- Decode audio -----------------------------------------------------
135
+ // Buffer.from(str, "base64") silently accepts malformed input rather
136
+ // than throwing, so we validate the characters explicitly first.
137
+ const base64Str = body.audioBase64 as string;
138
+ if (
139
+ !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(
140
+ base64Str,
141
+ )
142
+ ) {
143
+ return httpError(
144
+ "BAD_REQUEST",
145
+ "Invalid base64 encoding in audioBase64",
146
+ 400,
147
+ );
148
+ }
149
+
150
+ let audioBuffer: Buffer;
151
+ try {
152
+ audioBuffer = Buffer.from(base64Str, "base64");
153
+ } catch {
154
+ return httpError(
155
+ "BAD_REQUEST",
156
+ "audioBase64 could not be decoded",
157
+ 400,
158
+ );
159
+ }
160
+
161
+ if (audioBuffer.length === 0) {
162
+ return httpError(
163
+ "BAD_REQUEST",
164
+ "Decoded audio payload is empty",
165
+ 400,
166
+ );
167
+ }
168
+
169
+ // -- Resolve transcriber ----------------------------------------------
170
+ let transcriber;
171
+ try {
172
+ transcriber = await resolveBatchTranscriber();
173
+ } catch (err) {
174
+ log.error({ err }, "Failed to resolve STT transcriber");
175
+ return httpError(
176
+ "SERVICE_UNAVAILABLE",
177
+ "STT provider is not available",
178
+ 503,
179
+ );
180
+ }
181
+
182
+ if (!transcriber) {
183
+ return httpError(
184
+ "SERVICE_UNAVAILABLE",
185
+ "No speech-to-text provider is configured",
186
+ 503,
187
+ );
188
+ }
189
+
190
+ // -- Transcribe with timeout ------------------------------------------
191
+ const abortController = new AbortController();
192
+ const timeoutId = setTimeout(
193
+ () => abortController.abort(),
194
+ TRANSCRIPTION_TIMEOUT_MS,
195
+ );
196
+
197
+ try {
198
+ const result = await transcriber.transcribe({
199
+ audio: audioBuffer,
200
+ mimeType: body.mimeType as string,
201
+ signal: abortController.signal,
202
+ });
203
+
204
+ return Response.json({
205
+ text: result.text,
206
+ providerId: transcriber.providerId,
207
+ boundaryId: transcriber.boundaryId,
208
+ });
209
+ } catch (err) {
210
+ const sttErr = normalizeSttError(err);
211
+ const mapped = STT_ERROR_MAP[sttErr.category];
212
+
213
+ log.warn(
214
+ {
215
+ category: sttErr.category,
216
+ message: sttErr.message,
217
+ source: body.source,
218
+ },
219
+ "STT transcription failed",
220
+ );
221
+
222
+ return httpError(
223
+ mapped.code as Parameters<typeof httpError>[0],
224
+ mapped.message,
225
+ mapped.status,
226
+ );
227
+ } finally {
228
+ clearTimeout(timeoutId);
229
+ }
230
+ },
231
+ },
232
+ ];
233
+ }
@@ -27,7 +27,7 @@ interface SurfaceActionTarget {
27
27
  surfaceId: string,
28
28
  actionId: string,
29
29
  data?: Record<string, unknown>,
30
- ): void;
30
+ ): void | Promise<unknown>;
31
31
  handleSurfaceUndo?(surfaceId: string): void;
32
32
  setTrustContext?(ctx: {
33
33
  trustClass: "guardian" | "trusted_contact" | "unknown";
@@ -143,11 +143,44 @@ export async function handleSurfaceAction(
143
143
  }
144
144
 
145
145
  try {
146
- conversation.handleSurfaceAction(surfaceId, actionId, data);
146
+ // Most action paths return `void` (regular button/selection forwards);
147
+ // the `launch_conversation` dispatch branch returns a structured result
148
+ // so we can surface validation errors (e.g. missing title / seedPrompt)
149
+ // as 4xx responses instead of silently reporting success.
150
+ const raw = await conversation.handleSurfaceAction(
151
+ surfaceId,
152
+ actionId,
153
+ data,
154
+ );
155
+ const result =
156
+ raw && typeof raw === "object" && "accepted" in raw
157
+ ? (raw as
158
+ | { accepted: true; conversationId?: string }
159
+ | { accepted: false; error: string })
160
+ : undefined;
161
+ if (result && result.accepted === false) {
162
+ log.warn(
163
+ {
164
+ conversationId: conversationId ?? undefined,
165
+ surfaceId,
166
+ actionId,
167
+ error: result.error,
168
+ },
169
+ "Surface action rejected",
170
+ );
171
+ return httpError("BAD_REQUEST", result.error, 400);
172
+ }
147
173
  log.info(
148
174
  { conversationId: conversationId ?? undefined, surfaceId, actionId },
149
175
  "Surface action handled via HTTP",
150
176
  );
177
+ if (
178
+ result &&
179
+ result.accepted === true &&
180
+ typeof result.conversationId === "string"
181
+ ) {
182
+ return Response.json({ ok: true, conversationId: result.conversationId });
183
+ }
151
184
  return Response.json({ ok: true });
152
185
  } catch (err) {
153
186
  log.error(
@@ -239,6 +272,12 @@ export function surfaceActionRouteDefinitions(deps: {
239
272
  }),
240
273
  responseBody: z.object({
241
274
  ok: z.boolean(),
275
+ conversationId: z
276
+ .string()
277
+ .describe(
278
+ "Id of a newly launched conversation when the action dispatched one (e.g. launch_conversation). Omitted otherwise.",
279
+ )
280
+ .optional(),
242
281
  }),
243
282
  handler: async ({ req, authContext }) => {
244
283
  if (!deps.findConversation) {
@@ -1,17 +1,22 @@
1
1
  /**
2
- * HTTP route definitions for message text-to-speech synthesis.
2
+ * HTTP route definitions for text-to-speech synthesis.
3
3
  *
4
4
  * POST /v1/messages/:id/tts?conversationId=... — synthesize message text to audio
5
+ * POST /v1/tts/synthesize — synthesize arbitrary text to audio
5
6
  *
6
- * Gated behind the `message-tts` assistant feature flag.
7
- * Uses Fish Audio for synthesis when configured.
7
+ * Both endpoints use the globally configured TTS provider via the provider
8
+ * abstraction. The message endpoint is gated behind the `message-tts`
9
+ * assistant feature flag; the generic endpoint is always available when a
10
+ * TTS provider is configured.
8
11
  */
9
12
 
10
- import { synthesizeWithFishAudio } from "../../calls/fish-audio-client.js";
13
+ import { z } from "zod";
14
+
11
15
  import { sanitizeForTts } from "../../calls/tts-text-sanitizer.js";
12
16
  import { isAssistantFeatureFlagEnabled } from "../../config/assistant-feature-flags.js";
13
17
  import { getConfig } from "../../config/loader.js";
14
18
  import { getMessageContent } from "../../daemon/handlers/conversation-history.js";
19
+ import { synthesizeText } from "../../tts/synthesize-text.js";
15
20
  import { getLogger } from "../../util/logger.js";
16
21
  import { httpError } from "../http-errors.js";
17
22
  import type { RouteDefinition } from "../http-router.js";
@@ -32,7 +37,7 @@ export function ttsRouteDefinitions(): RouteDefinition[] {
32
37
  policyKey: "messages/tts",
33
38
  summary: "Synthesize message to speech",
34
39
  description:
35
- "Synthesize a message's text content to audio using Fish Audio TTS.",
40
+ "Synthesize a message's text content to audio using the configured TTS provider.",
36
41
  tags: ["messages"],
37
42
  queryParams: [
38
43
  {
@@ -70,35 +75,114 @@ export function ttsRouteDefinitions(): RouteDefinition[] {
70
75
  );
71
76
  }
72
77
 
73
- const { fishAudio } = config;
74
- if (!fishAudio?.referenceId) {
75
- return httpError(
76
- "SERVICE_UNAVAILABLE",
77
- "Fish Audio TTS is not configured",
78
- 503,
79
- );
78
+ try {
79
+ const { audio, contentType } = await synthesizeText({
80
+ text: sanitizedText,
81
+ useCase: "message-playback",
82
+ });
83
+
84
+ return new Response(new Uint8Array(audio), {
85
+ status: 200,
86
+ headers: { "Content-Type": contentType },
87
+ });
88
+ } catch (err) {
89
+ log.error({ err, messageId }, "TTS synthesis failed");
90
+
91
+ // Surface provider-not-configured as 503
92
+ if (
93
+ err instanceof Error &&
94
+ "code" in err &&
95
+ (err as { code: string }).code === "TTS_PROVIDER_NOT_CONFIGURED"
96
+ ) {
97
+ return httpError(
98
+ "SERVICE_UNAVAILABLE",
99
+ "TTS provider is not configured",
100
+ 503,
101
+ );
102
+ }
103
+
104
+ return httpError("INTERNAL_ERROR", "TTS synthesis failed", 502);
80
105
  }
106
+ },
107
+ },
81
108
 
109
+ // -- Generic text synthesis -----------------------------------------------
110
+
111
+ {
112
+ endpoint: "tts/synthesize",
113
+ method: "POST",
114
+ policyKey: "tts/synthesize",
115
+ summary: "Synthesize text to speech",
116
+ description:
117
+ "Synthesize arbitrary text to audio using the configured TTS provider. " +
118
+ "Provider selection is resolved globally via config — callers do not " +
119
+ "specify a provider.",
120
+ tags: ["tts"],
121
+ requestBody: z.object({
122
+ text: z.string().describe("Text to synthesize into speech"),
123
+ context: z
124
+ .string()
125
+ .optional()
126
+ .describe(
127
+ "Optional context hint for output policy or capability selection (e.g. voice-mode). " +
128
+ "Does not affect provider selection.",
129
+ ),
130
+ conversationId: z
131
+ .string()
132
+ .optional()
133
+ .describe("Optional conversation ID for scoping or analytics."),
134
+ }),
135
+ handler: async ({ req }) => {
136
+ let body: { text?: string; context?: string; conversationId?: string };
82
137
  try {
83
- const audioBuffer = await synthesizeWithFishAudio(
84
- sanitizedText,
85
- fishAudio,
138
+ body = (await req.json()) as typeof body;
139
+ } catch {
140
+ return httpError("BAD_REQUEST", "Invalid JSON body", 400);
141
+ }
142
+
143
+ if (!body || typeof body !== "object") {
144
+ return httpError("BAD_REQUEST", "Invalid JSON body", 400);
145
+ }
146
+
147
+ if (!body.text || typeof body.text !== "string") {
148
+ return httpError("BAD_REQUEST", "text is required", 400);
149
+ }
150
+
151
+ const sanitizedText = sanitizeForTts(body.text).trim();
152
+ if (!sanitizedText) {
153
+ return httpError(
154
+ "BAD_REQUEST",
155
+ "Text has no speakable content after sanitization",
156
+ 400,
86
157
  );
158
+ }
87
159
 
88
- const format = fishAudio.format ?? "mp3";
89
- const contentType =
90
- format === "wav"
91
- ? "audio/wav"
92
- : format === "opus"
93
- ? "audio/opus"
94
- : "audio/mpeg";
160
+ try {
161
+ const { audio, contentType } = await synthesizeText({
162
+ text: sanitizedText,
163
+ useCase: "message-playback",
164
+ });
95
165
 
96
- return new Response(new Uint8Array(audioBuffer), {
166
+ return new Response(new Uint8Array(audio), {
97
167
  status: 200,
98
168
  headers: { "Content-Type": contentType },
99
169
  });
100
170
  } catch (err) {
101
- log.error({ err, messageId }, "TTS synthesis failed");
171
+ log.error({ err, context: body.context }, "TTS synthesis failed");
172
+
173
+ // Surface provider-not-configured as 503
174
+ if (
175
+ err instanceof Error &&
176
+ "code" in err &&
177
+ (err as { code: string }).code === "TTS_PROVIDER_NOT_CONFIGURED"
178
+ ) {
179
+ return httpError(
180
+ "SERVICE_UNAVAILABLE",
181
+ "TTS provider is not configured",
182
+ 503,
183
+ );
184
+ }
185
+
102
186
  return httpError("INTERNAL_ERROR", "TTS synthesis failed", 502);
103
187
  }
104
188
  },
@@ -14,11 +14,30 @@ import {
14
14
  getUsageHourBuckets,
15
15
  getUsageTotals,
16
16
  } from "../../memory/llm-usage-store.js";
17
+ import { validateTimezone } from "../../memory/usage-buckets.js";
17
18
  import { httpError } from "../http-errors.js";
18
19
  import type { RouteDefinition } from "../http-router.js";
19
20
 
20
21
  const VALID_GROUP_BY = new Set(["actor", "provider", "model", "conversation"]);
21
22
 
23
+ /**
24
+ * Resolve the optional `tz` query param to a validated IANA identifier.
25
+ * Returns the tz string, or an error Response if the value is not a valid tz.
26
+ */
27
+ function resolveTimezone(url: URL): string | Response {
28
+ const tz = url.searchParams.get("tz") ?? "UTC";
29
+ try {
30
+ validateTimezone(tz);
31
+ } catch (err) {
32
+ return httpError(
33
+ "BAD_REQUEST",
34
+ (err as Error).message,
35
+ 400,
36
+ );
37
+ }
38
+ return tz;
39
+ }
40
+
22
41
  /**
23
42
  * Parse and validate the `from` and `to` epoch-millis query parameters.
24
43
  * Returns the parsed range or an error Response.
@@ -110,6 +129,12 @@ export function usageRouteDefinitions(): RouteDefinition[] {
110
129
  schema: { type: "string", enum: ["daily", "hourly"] },
111
130
  description: 'Bucket granularity: "daily" (default) or "hourly"',
112
131
  },
132
+ {
133
+ name: "tz",
134
+ schema: { type: "string" },
135
+ description:
136
+ 'IANA timezone identifier (e.g. "America/Los_Angeles"). Bucket boundaries and display labels are computed in this timezone. Defaults to "UTC" for backwards compatibility.',
137
+ },
113
138
  ],
114
139
  responseBody: z.object({
115
140
  buckets: z.array(z.unknown()).describe("Usage bucket objects"),
@@ -125,10 +150,13 @@ export function usageRouteDefinitions(): RouteDefinition[] {
125
150
  400,
126
151
  );
127
152
  }
153
+ const tz = resolveTimezone(url);
154
+ if (tz instanceof Response) return tz;
155
+ // The chart wants a continuous time axis, so fill empty buckets.
128
156
  const buckets =
129
157
  granularity === "hourly"
130
- ? getUsageHourBuckets(range)
131
- : getUsageDayBuckets(range);
158
+ ? getUsageHourBuckets(range, tz, { fillEmpty: true })
159
+ : getUsageDayBuckets(range, tz, { fillEmpty: true });
132
160
  return Response.json({ buckets });
133
161
  },
134
162
  },
@@ -6,6 +6,11 @@
6
6
  * exports named functions for HTTP methods (GET, POST, PUT, etc.) using
7
7
  * the standard Web API Request/Response signature.
8
8
  *
9
+ * Handlers receive a second `context` argument with runtime singletons
10
+ * (event hub, assistant ID, etc.) that would otherwise be unreachable
11
+ * from dynamically imported modules because Bun's cache-busting import
12
+ * creates separate module instances.
13
+ *
9
14
  * Modules are lazily loaded on first request and cached by file path +
10
15
  * mtime. When a file changes on disk, the next request reloads it via
11
16
  * Bun's dynamic `import()` with a cache-busting query parameter.
@@ -16,10 +21,35 @@ import { join, resolve } from "node:path";
16
21
 
17
22
  import { getLogger } from "../../util/logger.js";
18
23
  import { getWorkspaceRoutesDir } from "../../util/platform.js";
24
+ import type { AssistantEventHub } from "../assistant-event-hub.js";
19
25
  import { httpError } from "../http-errors.js";
20
26
 
21
27
  const log = getLogger("user-routes");
22
28
 
29
+ // ---------------------------------------------------------------------------
30
+ // User route context — injected into every handler as the second argument
31
+ // ---------------------------------------------------------------------------
32
+
33
+ /**
34
+ * Runtime context passed to user-defined route handlers.
35
+ *
36
+ * Because user route modules are loaded via dynamic `import()` with
37
+ * cache-busting query parameters, they get isolated module instances
38
+ * and cannot import process-level singletons like the event hub
39
+ * directly. This context bridges the gap by carrying references to
40
+ * the daemon's real singletons.
41
+ */
42
+ export interface UserRouteContext {
43
+ /** The daemon's event hub singleton — use this to publish events to connected SSE clients. */
44
+ readonly assistantEventHub: AssistantEventHub;
45
+ /** The logical assistant ID used by the daemon (typically "self"). */
46
+ readonly assistantId: string;
47
+ }
48
+
49
+ // ---------------------------------------------------------------------------
50
+ // Route handler types
51
+ // ---------------------------------------------------------------------------
52
+
23
53
  /** HTTP methods that can be exported from a handler module. */
24
54
  const HTTP_METHODS = [
25
55
  "GET",
@@ -33,8 +63,18 @@ const HTTP_METHODS = [
33
63
 
34
64
  type HttpMethod = (typeof HTTP_METHODS)[number];
35
65
 
36
- /** The function signature that user-defined route handlers must follow. */
37
- type RouteHandler = (request: Request) => Response | Promise<Response>;
66
+ /**
67
+ * The function signature that user-defined route handlers must follow.
68
+ *
69
+ * Handlers may accept an optional second `context` argument with runtime
70
+ * singletons (event hub, assistant ID). Legacy handlers that only accept
71
+ * `request` continue to work — the context is passed positionally but
72
+ * ignored if the handler doesn't declare the parameter.
73
+ */
74
+ type RouteHandler = (
75
+ request: Request,
76
+ context: UserRouteContext,
77
+ ) => Response | Promise<Response>;
38
78
 
39
79
  /** A loaded handler module with its cached metadata. */
40
80
  interface CachedModule {
@@ -55,10 +95,15 @@ const HANDLER_EXTENSIONS = [".ts", ".js"] as const;
55
95
  export class UserRouteDispatcher {
56
96
  private moduleCache = new Map<string, CachedModule>();
57
97
  private handlerTimeoutMs: number;
98
+ private context: UserRouteContext;
58
99
 
59
- constructor(options?: { handlerTimeoutMs?: number }) {
100
+ constructor(options: {
101
+ handlerTimeoutMs?: number;
102
+ context: UserRouteContext;
103
+ }) {
60
104
  this.handlerTimeoutMs =
61
- options?.handlerTimeoutMs ?? DEFAULT_HANDLER_TIMEOUT_MS;
105
+ options.handlerTimeoutMs ?? DEFAULT_HANDLER_TIMEOUT_MS;
106
+ this.context = Object.freeze({ ...options.context });
62
107
  }
63
108
 
64
109
  /**
@@ -192,7 +237,7 @@ export class UserRouteDispatcher {
192
237
  ): Promise<Response> {
193
238
  try {
194
239
  const result = await Promise.race([
195
- Promise.resolve(handler(request)),
240
+ Promise.resolve(handler(request, this.context)),
196
241
  new Promise<never>((_, reject) =>
197
242
  setTimeout(
198
243
  () => reject(new Error("Handler timed out")),
@@ -4,12 +4,24 @@
4
4
  * Registers a single catch-all route that delegates to the
5
5
  * UserRouteDispatcher for file-based dispatch from
6
6
  * `$VELLUM_WORKSPACE_DIR/routes/`.
7
+ *
8
+ * The dispatcher injects a `UserRouteContext` into every handler so
9
+ * that dynamically imported route modules can access daemon singletons
10
+ * (event hub, assistant ID) without relying on module-level imports
11
+ * that would resolve to separate instances due to cache-busting.
7
12
  */
8
13
 
14
+ import { assistantEventHub } from "../assistant-event-hub.js";
15
+ import { DAEMON_INTERNAL_ASSISTANT_ID } from "../assistant-scope.js";
9
16
  import type { RouteDefinition } from "../http-router.js";
10
17
  import { UserRouteDispatcher } from "./user-route-dispatcher.js";
11
18
 
12
- const dispatcher = new UserRouteDispatcher();
19
+ const dispatcher = new UserRouteDispatcher({
20
+ context: {
21
+ assistantEventHub,
22
+ assistantId: DAEMON_INTERNAL_ASSISTANT_ID,
23
+ },
24
+ });
13
25
 
14
26
  /**
15
27
  * HTTP methods supported by user-defined route handlers.
@@ -19,6 +19,7 @@ import {
19
19
  getToolDescription,
20
20
  sanitizeToolList,
21
21
  } from "../../tasks/tool-sanitizer.js";
22
+ import { createAbortReason } from "../../util/abort-reasons.js";
22
23
  import { getLogger } from "../../util/logger.js";
23
24
  import { truncate } from "../../util/truncate.js";
24
25
  import { resolveRequiredTools } from "../../work-items/resolve-required-tools.js";
@@ -587,7 +588,13 @@ export function workItemRouteDefinitions(
587
588
  const conversation = deps.findConversation(conversationId);
588
589
  if (conversation) {
589
590
  conversation.headlessLock = false;
590
- conversation.abort();
591
+ conversation.abort(
592
+ createAbortReason(
593
+ "work_item_aborted",
594
+ "work-items-routes.cancel",
595
+ conversationId,
596
+ ),
597
+ );
591
598
  getSubagentManager().abortAllForParent(conversationId);
592
599
  }
593
600
  }