@vellumai/assistant 0.6.2 → 0.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (895) hide show
  1. package/ARCHITECTURE.md +273 -10
  2. package/Dockerfile +2 -3
  3. package/bun.lock +41 -49
  4. package/bunfig.toml +3 -0
  5. package/docs/architecture/memory.md +1 -1
  6. package/docs/backup-troubleshooting.md +52 -0
  7. package/docs/browser-use-architecture-phase2.md +174 -0
  8. package/docs/stt-provider-onboarding.md +120 -0
  9. package/knip.json +12 -2
  10. package/node_modules/@vellumai/ces-contracts/bun.lock +8 -6
  11. package/node_modules/@vellumai/ces-contracts/package.json +3 -3
  12. package/node_modules/@vellumai/ces-contracts/src/rpc.ts +42 -0
  13. package/openapi.yaml +1111 -86
  14. package/package.json +40 -42
  15. package/scripts/generate-openapi.ts +0 -2
  16. package/scripts/test.sh +73 -18
  17. package/src/__tests__/acp-session.test.ts +43 -0
  18. package/src/__tests__/agent-image-optimize.test.ts +28 -0
  19. package/src/__tests__/agent-loop.test.ts +123 -0
  20. package/src/__tests__/anthropic-provider.test.ts +263 -10
  21. package/src/__tests__/app-builder-tool-scripts.test.ts +1 -0
  22. package/src/__tests__/app-executors.test.ts +1 -0
  23. package/src/__tests__/app-source-watcher.test.ts +37 -11
  24. package/src/__tests__/approval-routes-http.test.ts +178 -1
  25. package/src/__tests__/auto-analysis-end-to-end.test.ts +550 -0
  26. package/src/__tests__/auto-analysis-prompt.test.ts +50 -0
  27. package/src/__tests__/browser-fill-credential.test.ts +240 -94
  28. package/src/__tests__/browser-manager.test.ts +40 -27
  29. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +2 -2
  30. package/src/__tests__/browser-skill-endstate.test.ts +31 -7
  31. package/src/__tests__/btw-routes.test.ts +7 -0
  32. package/src/__tests__/call-controller.test.ts +581 -20
  33. package/src/__tests__/catalog-files.test.ts +1000 -0
  34. package/src/__tests__/channel-approvals.test.ts +53 -0
  35. package/src/__tests__/channel-invite-transport.test.ts +2 -2
  36. package/src/__tests__/channel-readiness-routes.test.ts +16 -20
  37. package/src/__tests__/channel-readiness-service.test.ts +12 -7
  38. package/src/__tests__/checker.test.ts +157 -10
  39. package/src/__tests__/clawhub-files.test.ts +347 -0
  40. package/src/__tests__/commit-message-enrichment-service.test.ts +36 -19
  41. package/src/__tests__/config-analysis.test.ts +100 -0
  42. package/src/__tests__/config-managed-gemini-defaults.test.ts +326 -0
  43. package/src/__tests__/config-schema-cmd.test.ts +2 -2
  44. package/src/__tests__/config-schema.test.ts +1248 -224
  45. package/src/__tests__/config-watcher-cleanup-throttle.test.ts +339 -0
  46. package/src/__tests__/config-watcher.test.ts +43 -8
  47. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +23 -0
  48. package/src/__tests__/contact-store-user-file.test.ts +512 -0
  49. package/src/__tests__/contacts-write.test.ts +197 -0
  50. package/src/__tests__/context-overflow-approval.test.ts +16 -1
  51. package/src/__tests__/context-window-manager.test.ts +88 -0
  52. package/src/__tests__/conversation-abort-tool-results.test.ts +2 -0
  53. package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -1
  54. package/src/__tests__/conversation-agent-loop.test.ts +99 -3
  55. package/src/__tests__/conversation-analysis-routes.test.ts +2 -2
  56. package/src/__tests__/conversation-attachments.test.ts +80 -4
  57. package/src/__tests__/conversation-confirmation-signals.test.ts +290 -0
  58. package/src/__tests__/conversation-error.test.ts +70 -0
  59. package/src/__tests__/conversation-fork-crud.test.ts +17 -0
  60. package/src/__tests__/conversation-history-web-search.test.ts +12 -4
  61. package/src/__tests__/conversation-host-access-routes.test.ts +229 -0
  62. package/src/__tests__/conversation-init.benchmark.test.ts +6 -1
  63. package/src/__tests__/conversation-inject-context.test.ts +103 -0
  64. package/src/__tests__/conversation-launcher-skill-regression.test.ts +51 -0
  65. package/src/__tests__/conversation-list-source.test.ts +145 -0
  66. package/src/__tests__/conversation-pre-run-repair.test.ts +2 -0
  67. package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -0
  68. package/src/__tests__/conversation-queue.test.ts +946 -62
  69. package/src/__tests__/conversation-routes-disk-view.test.ts +275 -0
  70. package/src/__tests__/conversation-routes-guardian-reply.test.ts +16 -0
  71. package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
  72. package/src/__tests__/conversation-runtime-assembly.test.ts +324 -46
  73. package/src/__tests__/conversation-skill-tools.test.ts +7 -4
  74. package/src/__tests__/conversation-slash-commands.test.ts +33 -0
  75. package/src/__tests__/conversation-slash-queue.test.ts +89 -18
  76. package/src/__tests__/conversation-slash-unknown.test.ts +2 -0
  77. package/src/__tests__/conversation-starter-routes.test.ts +126 -0
  78. package/src/__tests__/conversation-starters-cadence.test.ts +161 -0
  79. package/src/__tests__/conversation-store.test.ts +195 -0
  80. package/src/__tests__/conversation-tool-setup-batch-authorized.test.ts +226 -0
  81. package/src/__tests__/conversation-workspace-cache-state.test.ts +193 -0
  82. package/src/__tests__/conversation-workspace-injection.test.ts +2 -0
  83. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +2 -0
  84. package/src/__tests__/credential-execution-approval-bridge.test.ts +32 -1
  85. package/src/__tests__/credential-health-service.test.ts +352 -0
  86. package/src/__tests__/credential-security-invariants.test.ts +6 -3
  87. package/src/__tests__/credential-vault-unit.test.ts +383 -7
  88. package/src/__tests__/credential-vault.test.ts +152 -13
  89. package/src/__tests__/credentials-cli.test.ts +42 -18
  90. package/src/__tests__/cross-provider-web-search.test.ts +146 -35
  91. package/src/__tests__/date-context.test.ts +4 -4
  92. package/src/__tests__/deterministic-verification-control-plane.test.ts +10 -1
  93. package/src/__tests__/device-id.test.ts +112 -0
  94. package/src/__tests__/docker-signing-key-bootstrap.test.ts +167 -4
  95. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +1 -3
  96. package/src/__tests__/email-html-renderer.test.ts +71 -0
  97. package/src/__tests__/email-invite-adapter.test.ts +36 -32
  98. package/src/__tests__/embedding-managed-proxy-selection.test.ts +256 -0
  99. package/src/__tests__/emit-event-signal.test.ts +71 -0
  100. package/src/__tests__/extension-id-sync-guard.test.ts +222 -0
  101. package/src/__tests__/fixtures/mock-chrome-extension.ts +386 -0
  102. package/src/__tests__/gateway-only-enforcement.test.ts +206 -1
  103. package/src/__tests__/gateway-only-guard.test.ts +2 -0
  104. package/src/__tests__/gemini-provider.test.ts +66 -2
  105. package/src/__tests__/get-skill-detail-audit.test.ts +325 -0
  106. package/src/__tests__/gmail-archive-fallback.test.ts +193 -0
  107. package/src/__tests__/gmail-archive-gate.test.ts +246 -0
  108. package/src/__tests__/gmail-preferences.test.ts +117 -0
  109. package/src/__tests__/guardian-routing-invariants.test.ts +70 -2
  110. package/src/__tests__/headless-browser-interactions.test.ts +738 -359
  111. package/src/__tests__/headless-browser-mode.test.ts +614 -0
  112. package/src/__tests__/headless-browser-navigate.test.ts +528 -49
  113. package/src/__tests__/headless-browser-read-tools.test.ts +274 -100
  114. package/src/__tests__/headless-browser-snapshot.test.ts +250 -77
  115. package/src/__tests__/heartbeat-service.test.ts +70 -17
  116. package/src/__tests__/home-state-routes.test.ts +162 -0
  117. package/src/__tests__/host-bash-proxy.test.ts +145 -1
  118. package/src/__tests__/host-browser-e2e-cloud.test.ts +596 -0
  119. package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +286 -0
  120. package/src/__tests__/host-browser-e2e-self-hosted.test.ts +374 -0
  121. package/src/__tests__/host-browser-event-routes.test.ts +350 -0
  122. package/src/__tests__/host-browser-proxy.test.ts +444 -0
  123. package/src/__tests__/host-browser-routes.test.ts +198 -0
  124. package/src/__tests__/host-browser-ws-events-e2e.test.ts +423 -0
  125. package/src/__tests__/host-cu-proxy.test.ts +166 -1
  126. package/src/__tests__/host-file-proxy.test.ts +185 -1
  127. package/src/__tests__/host-file-read-tool.test.ts +52 -0
  128. package/src/__tests__/host-proxy-interface.test.ts +165 -0
  129. package/src/__tests__/host-shell-tool.test.ts +1 -11
  130. package/src/__tests__/http-user-message-parity.test.ts +1 -0
  131. package/src/__tests__/identity-intro-cache.test.ts +40 -10
  132. package/src/__tests__/init-feature-flag-overrides.test.ts +38 -112
  133. package/src/__tests__/integration-status.test.ts +6 -7
  134. package/src/__tests__/jobs-store-upsert-debounced.test.ts +141 -0
  135. package/src/__tests__/list-messages-tool-merge.test.ts +37 -12
  136. package/src/__tests__/llm-context-normalization.test.ts +488 -0
  137. package/src/__tests__/llm-context-route-provider.test.ts +86 -5
  138. package/src/__tests__/llm-usage-store.test.ts +363 -0
  139. package/src/__tests__/mcp-client-auth.test.ts +40 -4
  140. package/src/__tests__/mcp-health-check.test.ts +10 -3
  141. package/src/__tests__/media-stream-output.test.ts +555 -0
  142. package/src/__tests__/media-stream-parser.test.ts +374 -0
  143. package/src/__tests__/media-stream-server-integration.test.ts +1234 -0
  144. package/src/__tests__/media-stream-stt-session.test.ts +588 -0
  145. package/src/__tests__/media-turn-detector.test.ts +440 -0
  146. package/src/__tests__/message-queue.test.ts +125 -0
  147. package/src/__tests__/migration-cross-version-compatibility.test.ts +3 -1
  148. package/src/__tests__/migration-export-http.test.ts +67 -8
  149. package/src/__tests__/migration-export-streaming.test.ts +66 -0
  150. package/src/__tests__/migration-import-commit-http.test.ts +109 -7
  151. package/src/__tests__/migration-import-preflight-http.test.ts +6 -5
  152. package/src/__tests__/migration-validate-http.test.ts +3 -3
  153. package/src/__tests__/mock-gateway-ipc.ts +151 -0
  154. package/src/__tests__/model-intents.test.ts +2 -2
  155. package/src/__tests__/native-host-marker-sync-guard.test.ts +157 -0
  156. package/src/__tests__/oauth-apps-routes.test.ts +18 -12
  157. package/src/__tests__/oauth-cli.test.ts +709 -60
  158. package/src/__tests__/oauth-connect-orchestrator.test.ts +118 -24
  159. package/src/__tests__/oauth-provider-seed-logos.test.ts +23 -0
  160. package/src/__tests__/oauth-provider-serializer.test.ts +147 -10
  161. package/src/__tests__/oauth-provider-visibility.test.ts +19 -21
  162. package/src/__tests__/oauth-providers-routes.test.ts +52 -14
  163. package/src/__tests__/oauth-store.test.ts +1465 -176
  164. package/src/__tests__/oauth2-gateway-transport.test.ts +460 -26
  165. package/src/__tests__/onboarding-template-contract.test.ts +81 -70
  166. package/src/__tests__/openai-provider.test.ts +178 -2
  167. package/src/__tests__/openai-responses-cutover-guard.test.ts +184 -0
  168. package/src/__tests__/openai-responses-provider.test.ts +1105 -0
  169. package/src/__tests__/openrouter-token-estimation.test.ts +100 -0
  170. package/src/__tests__/outlook-categories.test.ts +1 -1
  171. package/src/__tests__/outlook-client-automation.test.ts +1 -1
  172. package/src/__tests__/outlook-compose-tools.test.ts +1 -1
  173. package/src/__tests__/outlook-email-watcher.test.ts +1 -1
  174. package/src/__tests__/outlook-follow-up.test.ts +1 -1
  175. package/src/__tests__/outlook-messaging-provider.test.ts +2 -2
  176. package/src/__tests__/outlook-trash.test.ts +1 -1
  177. package/src/__tests__/outlook-unsubscribe.test.ts +32 -3
  178. package/src/__tests__/permission-checker-host-gate.test.ts +74 -14
  179. package/src/__tests__/permission-mode.test.ts +28 -56
  180. package/src/__tests__/persona-resolver.test.ts +251 -0
  181. package/src/__tests__/platform-bash-auto-approve.test.ts +4 -0
  182. package/src/__tests__/platform-callback-registration.test.ts +19 -0
  183. package/src/__tests__/platform.test.ts +92 -1
  184. package/src/__tests__/post-turn-tool-result-truncation.test.ts +343 -0
  185. package/src/__tests__/prechat-onboarding-contract.test.ts +267 -0
  186. package/src/__tests__/pricing.test.ts +174 -0
  187. package/src/__tests__/proxy-approval-callback.test.ts +18 -0
  188. package/src/__tests__/qdrant-manager.test.ts +29 -8
  189. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +194 -0
  190. package/src/__tests__/relationship-state-contract.test.ts +175 -0
  191. package/src/__tests__/relay-server.test.ts +423 -5
  192. package/src/__tests__/require-fresh-approval.test.ts +40 -1
  193. package/src/__tests__/sanitize-config-for-transfer.test.ts +132 -0
  194. package/src/__tests__/schedule-routes.test.ts +162 -0
  195. package/src/__tests__/search-skills-unified.test.ts +118 -0
  196. package/src/__tests__/secret-detection-handler.test.ts +84 -0
  197. package/src/__tests__/secret-ingress-http.test.ts +1 -0
  198. package/src/__tests__/secret-scanner-executor.test.ts +4 -0
  199. package/src/__tests__/secure-keys.test.ts +107 -0
  200. package/src/__tests__/send-endpoint-busy.test.ts +8 -1
  201. package/src/__tests__/sequence-store.test.ts +1 -1
  202. package/src/__tests__/server-history-render.test.ts +49 -0
  203. package/src/__tests__/set-permission-mode.test.ts +13 -250
  204. package/src/__tests__/settings-routes.test.ts +201 -0
  205. package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
  206. package/src/__tests__/skills-file-content-endpoint.test.ts +801 -0
  207. package/src/__tests__/skills-files-catalog-fallback.test.ts +738 -0
  208. package/src/__tests__/skills.test.ts +5 -2
  209. package/src/__tests__/skillssh-files.test.ts +446 -0
  210. package/src/__tests__/slack-block-formatting.test.ts +110 -0
  211. package/src/__tests__/slack-channel-config.test.ts +576 -16
  212. package/src/__tests__/stt-catalog-parity.test.ts +282 -0
  213. package/src/__tests__/stt-stream-session.test.ts +535 -0
  214. package/src/__tests__/subagent-detail.test.ts +44 -2
  215. package/src/__tests__/subagent-disposal.test.ts +1 -0
  216. package/src/__tests__/subagent-fork-notifications.test.ts +291 -0
  217. package/src/__tests__/subagent-fork-spawn.test.ts +384 -0
  218. package/src/__tests__/subagent-manager-notify.test.ts +1 -0
  219. package/src/__tests__/subagent-notify-parent.test.ts +1 -0
  220. package/src/__tests__/subagent-spawn-tool-fork.test.ts +411 -0
  221. package/src/__tests__/subagent-tools.test.ts +1 -0
  222. package/src/__tests__/subagent-types.test.ts +1 -0
  223. package/src/__tests__/system-prompt-ask-mode.test.ts +27 -71
  224. package/src/__tests__/system-prompt.test.ts +184 -27
  225. package/src/__tests__/task-scheduler.test.ts +32 -6
  226. package/src/__tests__/telegram-config.test.ts +10 -13
  227. package/src/__tests__/telephony-stt-routing.test.ts +329 -0
  228. package/src/__tests__/terminal-tools.test.ts +25 -5
  229. package/src/__tests__/test-preload.ts +18 -0
  230. package/src/__tests__/test-support/browser-skill-harness.ts +4 -1
  231. package/src/__tests__/tool-approval-handler.test.ts +73 -0
  232. package/src/__tests__/tool-executor-lifecycle-events.test.ts +9 -5
  233. package/src/__tests__/tool-executor-shell-integration.test.ts +4 -0
  234. package/src/__tests__/tool-executor.test.ts +33 -24
  235. package/src/__tests__/tool-result-truncation.test.ts +36 -0
  236. package/src/__tests__/tool-side-effects-slack-dm.test.ts +22 -0
  237. package/src/__tests__/top-level-renderer.test.ts +73 -1
  238. package/src/__tests__/transport-hints-queue.test.ts +14 -29
  239. package/src/__tests__/trust-store.test.ts +7 -1
  240. package/src/__tests__/trusted-contact-approval-notifier.test.ts +1 -1
  241. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +109 -0
  242. package/src/__tests__/tts-catalog-parity.test.ts +345 -0
  243. package/src/__tests__/twilio-routes-twiml.test.ts +512 -114
  244. package/src/__tests__/twilio-routes.test.ts +376 -0
  245. package/src/__tests__/unicode.test.ts +293 -0
  246. package/src/__tests__/update-bulletin-format.test.ts +59 -0
  247. package/src/__tests__/update-bulletin.test.ts +206 -5
  248. package/src/__tests__/usage-routes.test.ts +25 -4
  249. package/src/__tests__/user-reference.test.ts +46 -61
  250. package/src/__tests__/v2-consent-policy.test.ts +103 -0
  251. package/src/__tests__/verification-control-plane-policy.test.ts +4 -0
  252. package/src/__tests__/voice-config-update.test.ts +403 -0
  253. package/src/__tests__/voice-quality.test.ts +434 -19
  254. package/src/__tests__/workspace-heartbeat-service.test.ts +7 -0
  255. package/src/__tests__/workspace-migration-033-stt-service-explicit-config.test.ts +547 -0
  256. package/src/__tests__/workspace-migration-034-remove-calls-voice-transcription-provider.test.ts +596 -0
  257. package/src/__tests__/workspace-migration-drop-user-md.test.ts +368 -0
  258. package/src/__tests__/workspace-migration-meets.test.ts +244 -0
  259. package/src/__tests__/workspace-migration-seed-device-id.test.ts +14 -20
  260. package/src/__tests__/workspace-policy.test.ts +2 -0
  261. package/src/acp/client-handler.ts +30 -4
  262. package/src/agent/image-optimize.ts +24 -12
  263. package/src/agent/loop.ts +55 -9
  264. package/src/approvals/guardian-request-resolvers.ts +21 -15
  265. package/src/backup/__tests__/backup-key.test.ts +152 -0
  266. package/src/backup/__tests__/backup-worker.test.ts +767 -0
  267. package/src/backup/__tests__/list-snapshots.test.ts +87 -0
  268. package/src/backup/__tests__/local-writer.test.ts +218 -0
  269. package/src/backup/__tests__/offsite-writer.test.ts +641 -0
  270. package/src/backup/__tests__/paths.test.ts +300 -0
  271. package/src/backup/__tests__/restore.test.ts +498 -0
  272. package/src/backup/__tests__/snapshot-lock.test.ts +352 -0
  273. package/src/backup/__tests__/stream-crypt.test.ts +228 -0
  274. package/src/backup/backup-key.ts +137 -0
  275. package/src/backup/backup-worker.ts +459 -0
  276. package/src/backup/list-snapshots.ts +147 -0
  277. package/src/backup/local-writer.ts +133 -0
  278. package/src/backup/offsite-writer.ts +222 -0
  279. package/src/backup/paths.ts +226 -0
  280. package/src/backup/restore.ts +322 -0
  281. package/src/backup/snapshot-lock.ts +431 -0
  282. package/src/backup/stream-crypt.ts +263 -0
  283. package/src/browser-session/__tests__/manager.test.ts +297 -0
  284. package/src/browser-session/backends/cdp-inspect.ts +30 -0
  285. package/src/browser-session/backends/extension.ts +26 -0
  286. package/src/browser-session/backends/local.ts +24 -0
  287. package/src/browser-session/events.ts +164 -0
  288. package/src/browser-session/index.ts +27 -0
  289. package/src/browser-session/manager.ts +159 -0
  290. package/src/browser-session/types.ts +28 -0
  291. package/src/bundler/package-resolver.ts +4 -0
  292. package/src/calls/audio-store.ts +11 -5
  293. package/src/calls/call-controller.ts +226 -71
  294. package/src/calls/call-domain.ts +9 -0
  295. package/src/calls/call-speech-output.ts +190 -0
  296. package/src/calls/call-transport.ts +77 -0
  297. package/src/calls/media-stream-audio-transcode.ts +173 -0
  298. package/src/calls/media-stream-output.ts +660 -0
  299. package/src/calls/media-stream-parser.ts +300 -0
  300. package/src/calls/media-stream-protocol.ts +166 -0
  301. package/src/calls/media-stream-server.ts +592 -0
  302. package/src/calls/media-stream-stt-session.ts +460 -0
  303. package/src/calls/media-turn-detector.ts +230 -0
  304. package/src/calls/relay-server.ts +90 -75
  305. package/src/calls/resolve-call-tts-provider.ts +136 -0
  306. package/src/calls/telephony-stt-routing.ts +145 -0
  307. package/src/calls/tts-call-strategy.ts +161 -0
  308. package/src/calls/tts-text-sanitizer.ts +32 -16
  309. package/src/calls/twilio-routes.ts +281 -17
  310. package/src/calls/voice-quality.ts +78 -35
  311. package/src/calls/voice-session-bridge.ts +8 -1
  312. package/src/channels/__tests__/types.test.ts +134 -0
  313. package/src/channels/types.ts +69 -3
  314. package/src/cli/__tests__/run-assistant-command.ts +11 -1
  315. package/src/cli/commands/__tests__/backup.test.ts +1165 -0
  316. package/src/cli/commands/__tests__/domain-register.test.ts +234 -0
  317. package/src/cli/commands/__tests__/domain-status.test.ts +132 -0
  318. package/src/cli/commands/__tests__/email-attachment.test.ts +422 -0
  319. package/src/cli/commands/__tests__/email-download.test.ts +16 -1
  320. package/src/cli/commands/__tests__/email-list.test.ts +22 -4
  321. package/src/cli/commands/__tests__/email-register.test.ts +4 -4
  322. package/src/cli/commands/__tests__/email-send.test.ts +37 -4
  323. package/src/cli/commands/__tests__/email-status.test.ts +5 -1
  324. package/src/cli/commands/__tests__/email-unregister.test.ts +34 -5
  325. package/src/cli/commands/backup.ts +993 -0
  326. package/src/cli/commands/conversations.ts +77 -0
  327. package/src/cli/commands/credentials.ts +3 -4
  328. package/src/cli/commands/domain.ts +210 -0
  329. package/src/cli/commands/email.ts +273 -16
  330. package/src/cli/commands/mcp.ts +16 -4
  331. package/src/cli/commands/oauth/__tests__/connect.test.ts +56 -44
  332. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +21 -21
  333. package/src/cli/commands/oauth/__tests__/mode.test.ts +17 -17
  334. package/src/cli/commands/oauth/__tests__/ping.test.ts +16 -16
  335. package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +32 -33
  336. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +330 -0
  337. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +117 -12
  338. package/src/cli/commands/oauth/__tests__/status.test.ts +10 -10
  339. package/src/cli/commands/oauth/__tests__/token.test.ts +7 -7
  340. package/src/cli/commands/oauth/apps.ts +7 -4
  341. package/src/cli/commands/oauth/connect.ts +6 -3
  342. package/src/cli/commands/oauth/disconnect.ts +1 -1
  343. package/src/cli/commands/oauth/mode.ts +12 -3
  344. package/src/cli/commands/oauth/providers.ts +215 -36
  345. package/src/cli/commands/oauth/shared.ts +7 -6
  346. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +254 -0
  347. package/src/cli/commands/platform/__tests__/connect.test.ts +6 -0
  348. package/src/cli/commands/platform/__tests__/disconnect.test.ts +7 -1
  349. package/src/cli/commands/platform/__tests__/status.test.ts +6 -0
  350. package/src/cli/commands/platform/index.ts +107 -10
  351. package/src/cli/commands/usage.ts +10 -9
  352. package/src/cli/lib/daemon-credential-client.ts +4 -0
  353. package/src/cli/program.ts +30 -4
  354. package/src/config/__tests__/backup-schema.test.ts +134 -0
  355. package/src/config/assistant-feature-flags.ts +61 -62
  356. package/src/config/bundled-skills/app-builder/SKILL.md +26 -249
  357. package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +141 -0
  358. package/src/config/bundled-skills/app-builder/references/INTERACTION_HOOKS.md +56 -0
  359. package/src/config/bundled-skills/app-builder/references/WIDGETS.md +125 -0
  360. package/src/config/bundled-skills/browser/SKILL.md +30 -5
  361. package/src/config/bundled-skills/browser/TOOLS.json +123 -0
  362. package/src/config/bundled-skills/browser/tools/browser-attach.ts +12 -0
  363. package/src/config/bundled-skills/browser/tools/browser-detach.ts +12 -0
  364. package/src/config/bundled-skills/browser/tools/browser-status.ts +12 -0
  365. package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +17 -0
  366. package/src/config/bundled-skills/contacts/SKILL.md +5 -2
  367. package/src/config/bundled-skills/document/SKILL.md +4 -0
  368. package/src/config/bundled-skills/gmail/SKILL.md +54 -8
  369. package/src/config/bundled-skills/gmail/TOOLS.json +33 -3
  370. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +116 -9
  371. package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +138 -11
  372. package/src/config/bundled-skills/gmail/tools/gmail-preferences-tool.ts +59 -0
  373. package/src/config/bundled-skills/gmail/tools/gmail-preferences.ts +82 -0
  374. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +113 -17
  375. package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +2 -2
  376. package/src/config/bundled-skills/media-processing/SKILL.md +3 -9
  377. package/src/config/bundled-skills/media-processing/TOOLS.json +1 -6
  378. package/src/config/bundled-skills/media-processing/__tests__/audio-transcribe.test.ts +125 -0
  379. package/src/config/bundled-skills/media-processing/__tests__/extract-keyframes.test.ts +181 -0
  380. package/src/config/bundled-skills/media-processing/__tests__/preprocess-audio.test.ts +141 -0
  381. package/src/config/bundled-skills/media-processing/services/audio-transcribe.ts +32 -87
  382. package/src/config/bundled-skills/media-processing/services/preprocess.ts +8 -4
  383. package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +0 -10
  384. package/src/config/bundled-skills/messaging/SKILL.md +3 -3
  385. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +2 -2
  386. package/src/config/bundled-skills/outlook/SKILL.md +9 -2
  387. package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +2 -2
  388. package/src/config/bundled-skills/phone-calls/SKILL.md +2 -2
  389. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +27 -18
  390. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +3 -3
  391. package/src/config/bundled-skills/settings/TOOLS.json +3 -3
  392. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +26 -22
  393. package/src/config/bundled-skills/slack/SKILL.md +1 -0
  394. package/src/config/bundled-skills/subagent/SKILL.md +21 -0
  395. package/src/config/bundled-skills/subagent/TOOLS.json +8 -4
  396. package/src/config/bundled-skills/tasks/SKILL.md +5 -0
  397. package/src/config/bundled-skills/transcribe/SKILL.md +9 -14
  398. package/src/config/bundled-skills/transcribe/TOOLS.json +2 -7
  399. package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +256 -0
  400. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +38 -188
  401. package/src/config/bundled-tool-registry.ts +8 -0
  402. package/src/config/env-registry.ts +38 -0
  403. package/src/config/env.ts +49 -4
  404. package/src/config/feature-flag-registry.json +85 -14
  405. package/src/config/loader.ts +82 -13
  406. package/src/config/sanitize-for-transfer.ts +47 -0
  407. package/src/config/schema.ts +81 -15
  408. package/src/config/schemas/__tests__/stt.test.ts +43 -0
  409. package/src/config/schemas/analysis.ts +51 -0
  410. package/src/config/schemas/backup.ts +72 -0
  411. package/src/config/schemas/calls.ts +1 -26
  412. package/src/config/schemas/elevenlabs.ts +0 -59
  413. package/src/config/schemas/filing.ts +47 -7
  414. package/src/config/schemas/heartbeat.ts +27 -5
  415. package/src/config/schemas/host-browser.ts +112 -0
  416. package/src/config/schemas/inference.ts +1 -1
  417. package/src/config/schemas/memory-lifecycle.ts +14 -2
  418. package/src/config/schemas/memory-retrieval.ts +103 -0
  419. package/src/config/schemas/security.ts +0 -6
  420. package/src/config/schemas/services.ts +52 -0
  421. package/src/config/schemas/stt.ts +59 -0
  422. package/src/config/schemas/tts.ts +230 -0
  423. package/src/config/schemas/updates.ts +14 -0
  424. package/src/config/skills.ts +4 -0
  425. package/src/config/types.ts +4 -1
  426. package/src/contacts/contact-store.ts +56 -11
  427. package/src/contacts/contacts-write.ts +38 -1
  428. package/src/context/post-turn-tool-result-truncation.ts +177 -0
  429. package/src/context/tool-result-truncation.ts +2 -1
  430. package/src/context/window-manager.ts +61 -10
  431. package/src/credential-execution/approval-bridge.ts +49 -15
  432. package/src/credential-execution/executable-discovery.ts +12 -2
  433. package/src/credential-execution/process-manager.ts +33 -2
  434. package/src/credential-health/credential-health-service.ts +366 -0
  435. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +324 -0
  436. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +497 -0
  437. package/src/daemon/__tests__/conversation-tool-setup.test.ts +195 -0
  438. package/src/daemon/__tests__/lifecycle-startup-ordering.test.ts +127 -0
  439. package/src/daemon/app-source-watcher.ts +35 -0
  440. package/src/daemon/config-watcher.ts +99 -5
  441. package/src/daemon/context-overflow-approval.ts +5 -0
  442. package/src/daemon/conversation-agent-loop-handlers.ts +23 -2
  443. package/src/daemon/conversation-agent-loop.ts +153 -42
  444. package/src/daemon/conversation-attachments.ts +40 -0
  445. package/src/daemon/conversation-error.ts +11 -0
  446. package/src/daemon/conversation-history.ts +40 -6
  447. package/src/daemon/conversation-launch.ts +220 -0
  448. package/src/daemon/conversation-lifecycle.ts +59 -9
  449. package/src/daemon/conversation-messaging.ts +37 -3
  450. package/src/daemon/conversation-notifiers.ts +5 -0
  451. package/src/daemon/conversation-process.ts +622 -13
  452. package/src/daemon/conversation-queue-manager.ts +24 -0
  453. package/src/daemon/conversation-runtime-assembly.ts +128 -36
  454. package/src/daemon/conversation-slash.ts +36 -0
  455. package/src/daemon/conversation-surfaces.ts +131 -40
  456. package/src/daemon/conversation-tool-setup.ts +99 -8
  457. package/src/daemon/conversation-usage.ts +7 -4
  458. package/src/daemon/conversation-workspace.ts +12 -0
  459. package/src/daemon/conversation.ts +292 -16
  460. package/src/daemon/date-context.ts +10 -10
  461. package/src/daemon/first-greeting.ts +3 -2
  462. package/src/daemon/handlers/config-slack-channel.ts +269 -94
  463. package/src/daemon/handlers/conversations.ts +13 -141
  464. package/src/daemon/handlers/shared.ts +80 -0
  465. package/src/daemon/handlers/skills.ts +483 -44
  466. package/src/daemon/host-bash-proxy.ts +48 -13
  467. package/src/daemon/host-browser-proxy.ts +192 -0
  468. package/src/daemon/host-cu-proxy.ts +36 -11
  469. package/src/daemon/host-file-proxy.ts +57 -9
  470. package/src/daemon/lifecycle.ts +179 -28
  471. package/src/daemon/message-protocol.ts +13 -0
  472. package/src/daemon/message-types/conversations.ts +89 -14
  473. package/src/daemon/message-types/home.ts +40 -0
  474. package/src/daemon/message-types/host-browser.ts +100 -0
  475. package/src/daemon/message-types/meet.ts +143 -0
  476. package/src/daemon/message-types/messages.ts +19 -5
  477. package/src/daemon/message-types/schedules.ts +34 -2
  478. package/src/daemon/message-types/skills.ts +26 -0
  479. package/src/daemon/message-types/subagents.ts +2 -0
  480. package/src/daemon/message-types/surfaces.ts +2 -0
  481. package/src/daemon/server.ts +439 -14
  482. package/src/daemon/shutdown-handlers.ts +32 -4
  483. package/src/daemon/shutdown-registry.ts +40 -0
  484. package/src/daemon/tool-side-effects.ts +15 -0
  485. package/src/daemon/transport-hints.ts +5 -24
  486. package/src/email/html-renderer.ts +76 -0
  487. package/src/heartbeat/heartbeat-service.ts +93 -7
  488. package/src/home/__tests__/assistant-feed-authoring.test.ts +156 -0
  489. package/src/home/__tests__/emit-feed-event.test.ts +169 -0
  490. package/src/home/__tests__/feed-scheduler.test.ts +194 -0
  491. package/src/home/__tests__/feed-types.test.ts +275 -0
  492. package/src/home/__tests__/feed-writer.test.ts +688 -0
  493. package/src/home/__tests__/phase5-exit-criteria.test.ts +212 -0
  494. package/src/home/__tests__/platform-gmail-digest.test.ts +222 -0
  495. package/src/home/__tests__/progress-formula.test.ts +213 -0
  496. package/src/home/__tests__/relationship-state-writer.test.ts +740 -0
  497. package/src/home/__tests__/rollup-producer.test.ts +398 -0
  498. package/src/home/assistant-feed-authoring.ts +124 -0
  499. package/src/home/emit-feed-event.ts +158 -0
  500. package/src/home/feed-scheduler.ts +247 -0
  501. package/src/home/feed-types.ts +181 -0
  502. package/src/home/feed-writer.ts +469 -0
  503. package/src/home/platform-gmail-digest.ts +163 -0
  504. package/src/home/progress-formula.ts +86 -0
  505. package/src/home/relationship-state-writer.ts +824 -0
  506. package/src/home/relationship-state.ts +143 -0
  507. package/src/home/rollup-producer.ts +384 -0
  508. package/src/hooks/runner.ts +7 -0
  509. package/src/inbound/platform-callback-registration.ts +30 -20
  510. package/src/inbound/public-ingress-urls.ts +12 -0
  511. package/src/instrument.ts +1 -1
  512. package/src/ipc/__tests__/cli-ipc.test.ts +200 -0
  513. package/src/ipc/cli-client.ts +151 -0
  514. package/src/ipc/cli-server.ts +234 -0
  515. package/src/ipc/gateway-client.ts +180 -0
  516. package/src/ipc/routes/index.ts +5 -0
  517. package/src/ipc/routes/wake-conversation.ts +19 -0
  518. package/src/mcp/client.ts +59 -24
  519. package/src/memory/__tests__/auto-analysis-enqueue.test.ts +356 -0
  520. package/src/memory/__tests__/auto-analysis-guard.test.ts +57 -0
  521. package/src/memory/__tests__/conversation-analyze-job.test.ts +232 -0
  522. package/src/memory/__tests__/find-analysis-conversation.test.ts +196 -0
  523. package/src/memory/app-store.ts +31 -1
  524. package/src/memory/attachments-store.ts +70 -0
  525. package/src/memory/auto-analysis-enqueue.ts +127 -0
  526. package/src/memory/auto-analysis-guard.ts +27 -0
  527. package/src/memory/cleanup-schedule-state.ts +37 -0
  528. package/src/memory/conversation-analyze-job.ts +73 -0
  529. package/src/memory/conversation-crud.ts +122 -0
  530. package/src/memory/conversation-disk-view.ts +7 -0
  531. package/src/memory/conversation-group-migration.ts +34 -2
  532. package/src/memory/conversation-queries.ts +6 -5
  533. package/src/memory/conversation-starters-cadence.ts +76 -0
  534. package/src/memory/conversation-title-service.ts +5 -2
  535. package/src/memory/db-init.ts +18 -0
  536. package/src/memory/db-maintenance.ts +108 -0
  537. package/src/memory/db.ts +1 -0
  538. package/src/memory/embedding-backend.test.ts +75 -0
  539. package/src/memory/embedding-backend.ts +131 -5
  540. package/src/memory/embedding-gemini.test.ts +54 -0
  541. package/src/memory/embedding-gemini.ts +20 -9
  542. package/src/memory/embedding-local.ts +176 -17
  543. package/src/memory/graph/consolidation.ts +10 -23
  544. package/src/memory/graph/conversation-graph-memory.ts +15 -0
  545. package/src/memory/graph/extraction-job.ts +15 -0
  546. package/src/memory/graph/extraction.test.ts +23 -0
  547. package/src/memory/graph/extraction.ts +8 -0
  548. package/src/memory/graph/retriever.ts +67 -40
  549. package/src/memory/graph/scoring.test.ts +186 -0
  550. package/src/memory/graph/scoring.ts +31 -1
  551. package/src/memory/graph/store.test.ts +7 -3
  552. package/src/memory/graph/store.ts +47 -12
  553. package/src/memory/graph/tools.ts +1 -1
  554. package/src/memory/group-crud.ts +6 -1
  555. package/src/memory/indexer.ts +95 -16
  556. package/src/memory/job-handlers/cleanup.ts +11 -8
  557. package/src/memory/job-handlers/conversation-starters.ts +16 -10
  558. package/src/memory/jobs-store.ts +64 -4
  559. package/src/memory/jobs-worker.ts +22 -9
  560. package/src/memory/llm-usage-store.ts +137 -60
  561. package/src/memory/migrations/213-oauth-providers-scope-separator.ts +13 -0
  562. package/src/memory/migrations/214-oauth-providers-refresh-url.ts +11 -0
  563. package/src/memory/migrations/215-oauth-providers-revoke.ts +14 -0
  564. package/src/memory/migrations/216-oauth-providers-token-auth-method.ts +30 -0
  565. package/src/memory/migrations/217-conversation-host-access.ts +40 -0
  566. package/src/memory/migrations/218-oauth-providers-logo-url.ts +11 -0
  567. package/src/memory/migrations/219-oauth-providers-token-exchange-body-format.ts +15 -0
  568. package/src/memory/migrations/220-normalize-user-file-by-principal.ts +190 -0
  569. package/src/memory/migrations/221-conversations-archived-at.ts +16 -0
  570. package/src/memory/migrations/index.ts +12 -0
  571. package/src/memory/migrations/registry.ts +16 -0
  572. package/src/memory/qdrant-manager.ts +43 -16
  573. package/src/memory/schema/conversations.ts +3 -0
  574. package/src/memory/schema/oauth.ts +21 -13
  575. package/src/memory/usage-buckets.ts +396 -0
  576. package/src/messaging/providers/gmail/client.ts +57 -6
  577. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +282 -0
  578. package/src/messaging/providers/slack/adapter.ts +143 -38
  579. package/src/messaging/providers/slack/client.ts +16 -0
  580. package/src/messaging/providers/slack/types.ts +4 -0
  581. package/src/notifications/decision-engine.ts +3 -3
  582. package/src/notifications/signal.ts +5 -0
  583. package/src/oauth/AGENTS.md +76 -0
  584. package/src/oauth/__tests__/identity-verifier.test.ts +25 -19
  585. package/src/oauth/__tests__/seed-providers-managed.test.ts +32 -0
  586. package/src/oauth/byo-connection.test.ts +26 -9
  587. package/src/oauth/byo-connection.ts +10 -8
  588. package/src/oauth/connect-orchestrator.ts +25 -21
  589. package/src/oauth/connect-types.ts +3 -3
  590. package/src/oauth/connection-resolver.test.ts +17 -4
  591. package/src/oauth/connection-resolver.ts +22 -18
  592. package/src/oauth/connection.ts +3 -1
  593. package/src/oauth/manual-token-connection.ts +13 -13
  594. package/src/oauth/oauth-store.ts +223 -100
  595. package/src/oauth/platform-connection.test.ts +101 -3
  596. package/src/oauth/platform-connection.ts +56 -35
  597. package/src/oauth/provider-serializer.ts +31 -5
  598. package/src/oauth/revoke.ts +76 -0
  599. package/src/oauth/seed-providers.ts +133 -87
  600. package/src/oauth/token-persistence.ts +1 -1
  601. package/src/permissions/checker.ts +16 -6
  602. package/src/permissions/defaults.ts +49 -1
  603. package/src/permissions/permission-mode.ts +4 -11
  604. package/src/permissions/prompter.ts +13 -1
  605. package/src/permissions/trust-store.ts +3 -3
  606. package/src/permissions/v2-consent-policy.ts +87 -0
  607. package/src/permissions/workspace-policy.ts +3 -0
  608. package/src/platform/client.test.ts +10 -0
  609. package/src/platform/sync-identity.ts +129 -0
  610. package/src/prompts/persona-resolver.ts +126 -2
  611. package/src/prompts/system-prompt.ts +76 -38
  612. package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +3 -65
  613. package/src/prompts/templates/BOOTSTRAP.md +59 -105
  614. package/src/prompts/templates/SOUL.md +3 -1
  615. package/src/prompts/templates/UPDATES.md +12 -0
  616. package/src/prompts/templates/channels/slack.md +20 -0
  617. package/src/prompts/update-bulletin-format.ts +26 -9
  618. package/src/prompts/update-bulletin.ts +34 -23
  619. package/src/prompts/user-reference.ts +20 -17
  620. package/src/providers/__tests__/provider-secret-catalog.test.ts +42 -0
  621. package/src/providers/anthropic/client.ts +157 -60
  622. package/src/providers/fireworks/client.ts +2 -2
  623. package/src/providers/gemini/client.ts +9 -1
  624. package/src/providers/model-catalog.ts +6 -0
  625. package/src/providers/model-intents.ts +4 -4
  626. package/src/providers/ollama/client.ts +2 -2
  627. package/src/providers/openai/chat-completions-provider.ts +474 -0
  628. package/src/providers/openai/client.ts +25 -440
  629. package/src/providers/openai/responses-provider.ts +502 -0
  630. package/src/providers/openrouter/client.ts +101 -4
  631. package/src/providers/provider-secret-catalog.ts +139 -0
  632. package/src/providers/registry.ts +2 -2
  633. package/src/providers/retry.ts +14 -3
  634. package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +251 -0
  635. package/src/providers/speech-to-text/__tests__/resolve.test.ts +828 -0
  636. package/src/providers/speech-to-text/deepgram-realtime.test.ts +980 -0
  637. package/src/providers/speech-to-text/deepgram-realtime.ts +767 -0
  638. package/src/providers/speech-to-text/deepgram.test.ts +332 -0
  639. package/src/providers/speech-to-text/deepgram.ts +115 -0
  640. package/src/providers/speech-to-text/google-gemini-live-stream.test.ts +743 -0
  641. package/src/providers/speech-to-text/google-gemini-live-stream.ts +625 -0
  642. package/src/providers/speech-to-text/google-gemini.test.ts +226 -0
  643. package/src/providers/speech-to-text/google-gemini.ts +101 -0
  644. package/src/providers/speech-to-text/openai-whisper-stream.test.ts +564 -0
  645. package/src/providers/speech-to-text/openai-whisper-stream.ts +381 -0
  646. package/src/providers/speech-to-text/openai-whisper.test.ts +1 -37
  647. package/src/providers/speech-to-text/openai-whisper.ts +63 -33
  648. package/src/providers/speech-to-text/provider-catalog.ts +306 -0
  649. package/src/providers/speech-to-text/resolve.ts +386 -6
  650. package/src/providers/types.ts +10 -1
  651. package/src/runtime/AGENTS.md +65 -0
  652. package/src/runtime/__tests__/agent-wake.test.ts +831 -0
  653. package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +715 -0
  654. package/src/runtime/__tests__/capability-tokens.test.ts +258 -0
  655. package/src/runtime/__tests__/chrome-extension-registry.test.ts +518 -0
  656. package/src/runtime/__tests__/runtime-mode.test.ts +62 -0
  657. package/src/runtime/__tests__/slack-block-formatting.test.ts +481 -0
  658. package/src/runtime/agent-wake.ts +512 -0
  659. package/src/runtime/assistant-event-hub.ts +2 -2
  660. package/src/runtime/auth/__tests__/guard-tests.test.ts +1 -0
  661. package/src/runtime/auth/__tests__/middleware.test.ts +116 -1
  662. package/src/runtime/auth/__tests__/route-policy.test.ts +48 -0
  663. package/src/runtime/auth/middleware.ts +98 -0
  664. package/src/runtime/auth/route-policy.ts +33 -9
  665. package/src/runtime/auth/token-service.ts +56 -1
  666. package/src/runtime/btw-sidechain.ts +2 -0
  667. package/src/runtime/capability-tokens.ts +414 -0
  668. package/src/runtime/channel-approvals.ts +18 -5
  669. package/src/runtime/channel-invite-transport.ts +1 -1
  670. package/src/runtime/channel-invite-transports/email.ts +14 -6
  671. package/src/runtime/channel-readiness-service.ts +12 -22
  672. package/src/runtime/chrome-extension-registry.ts +368 -0
  673. package/src/runtime/confirmation-request-guardian-bridge.ts +6 -0
  674. package/src/runtime/guardian-decision-types.ts +7 -0
  675. package/src/runtime/http-server.ts +815 -75
  676. package/src/runtime/http-types.ts +6 -2
  677. package/src/runtime/migrations/__tests__/rebind-secrets-credentials.test.ts +172 -0
  678. package/src/runtime/migrations/__tests__/vbundle-builder-credentials.test.ts +276 -0
  679. package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +198 -0
  680. package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +360 -0
  681. package/src/runtime/migrations/migration-transport.ts +7 -0
  682. package/src/runtime/migrations/migration-wizard.ts +23 -2
  683. package/src/runtime/migrations/rebind-secrets-screen.ts +76 -15
  684. package/src/runtime/migrations/vbundle-builder.ts +145 -38
  685. package/src/runtime/migrations/vbundle-import-analyzer.ts +96 -1
  686. package/src/runtime/migrations/vbundle-importer.ts +89 -5
  687. package/src/runtime/pending-interactions.ts +18 -13
  688. package/src/runtime/routes/__tests__/backup-routes.test.ts +967 -0
  689. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +507 -0
  690. package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +208 -0
  691. package/src/runtime/routes/__tests__/stt-routes.test.ts +406 -0
  692. package/src/runtime/routes/__tests__/tts-routes.test.ts +474 -0
  693. package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +148 -17
  694. package/src/runtime/routes/app-management-routes.ts +12 -18
  695. package/src/runtime/routes/approval-routes.ts +90 -16
  696. package/src/runtime/routes/attachment-routes.test.ts +9 -3
  697. package/src/runtime/routes/attachment-routes.ts +216 -17
  698. package/src/runtime/routes/backup-routes.ts +519 -0
  699. package/src/runtime/routes/browser-extension-pair-routes.ts +556 -0
  700. package/src/runtime/routes/btw-routes.ts +8 -6
  701. package/src/runtime/routes/contact-routes.test.ts +298 -0
  702. package/src/runtime/routes/contact-routes.ts +132 -5
  703. package/src/runtime/routes/conversation-analysis-routes.ts +22 -141
  704. package/src/runtime/routes/conversation-management-routes.ts +223 -0
  705. package/src/runtime/routes/conversation-routes.ts +598 -103
  706. package/src/runtime/routes/conversation-starter-routes.ts +78 -16
  707. package/src/runtime/routes/filing-routes.ts +93 -0
  708. package/src/runtime/routes/guardian-action-routes.ts +24 -13
  709. package/src/runtime/routes/home-feed-routes.ts +334 -0
  710. package/src/runtime/routes/home-state-routes.ts +138 -0
  711. package/src/runtime/routes/host-browser-routes.ts +268 -0
  712. package/src/runtime/routes/host-file-routes.ts +9 -1
  713. package/src/runtime/routes/identity-intro-cache.ts +7 -3
  714. package/src/runtime/routes/identity-routes.ts +262 -33
  715. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +46 -39
  716. package/src/runtime/routes/inbound-stages/transcribe-audio.ts +15 -15
  717. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +137 -0
  718. package/src/runtime/routes/integrations/slack/__tests__/share.test.ts +179 -0
  719. package/src/runtime/routes/integrations/slack/channel.ts +11 -3
  720. package/src/runtime/routes/integrations/slack/share.ts +45 -7
  721. package/src/runtime/routes/llm-context-normalization.ts +303 -0
  722. package/src/runtime/routes/log-export-routes.ts +42 -22
  723. package/src/runtime/routes/memory-item-routes.test.ts +3 -2
  724. package/src/runtime/routes/memory-item-routes.ts +1 -7
  725. package/src/runtime/routes/migration-routes.ts +122 -2
  726. package/src/runtime/routes/oauth-apps.ts +15 -17
  727. package/src/runtime/routes/oauth-providers.ts +4 -0
  728. package/src/runtime/routes/schedule-routes.ts +24 -11
  729. package/src/runtime/routes/settings-routes.ts +31 -102
  730. package/src/runtime/routes/skills-routes.ts +128 -9
  731. package/src/runtime/routes/stt-routes.ts +233 -0
  732. package/src/runtime/routes/subagents-routes.ts +14 -10
  733. package/src/runtime/routes/surface-action-routes.ts +41 -2
  734. package/src/runtime/routes/tts-routes.ts +108 -24
  735. package/src/runtime/routes/usage-routes.ts +38 -9
  736. package/src/runtime/routes/user-route-dispatcher.ts +50 -5
  737. package/src/runtime/routes/user-routes.ts +13 -1
  738. package/src/runtime/routes/work-items-routes.ts +8 -1
  739. package/src/runtime/routes/workspace-routes.test.ts +22 -0
  740. package/src/runtime/routes/workspace-routes.ts +8 -1
  741. package/src/runtime/routes/workspace-utils.ts +2 -0
  742. package/src/runtime/runtime-mode.ts +33 -0
  743. package/src/runtime/services/__tests__/analyze-conversation.test.ts +444 -0
  744. package/src/runtime/services/__tests__/analyze-deps-singleton.test.ts +67 -0
  745. package/src/runtime/services/__tests__/auto-analysis-prompt.test.ts +53 -0
  746. package/src/runtime/services/__tests__/manual-analysis-prompt.test.ts +41 -0
  747. package/src/runtime/services/analyze-conversation.ts +344 -0
  748. package/src/runtime/services/analyze-deps-singleton.ts +32 -0
  749. package/src/runtime/services/auto-analysis-prompt.ts +55 -0
  750. package/src/runtime/skill-route-registry.ts +49 -0
  751. package/src/runtime/slack-block-formatting.ts +437 -10
  752. package/src/schedule/scheduler.ts +57 -5
  753. package/src/security/ces-credential-client.ts +20 -0
  754. package/src/security/ces-rpc-credential-backend.ts +17 -0
  755. package/src/security/credential-backend.ts +5 -0
  756. package/src/security/oauth2.ts +68 -29
  757. package/src/security/secure-keys.ts +143 -27
  758. package/src/security/token-manager.ts +31 -10
  759. package/src/sequence/engine.ts +23 -0
  760. package/src/sequence/types.ts +1 -1
  761. package/src/skills/catalog-files.ts +554 -0
  762. package/src/skills/category-inference.ts +122 -0
  763. package/src/skills/clawhub-files.ts +213 -0
  764. package/src/skills/clawhub.ts +84 -23
  765. package/src/skills/skill-file-provider.ts +40 -0
  766. package/src/skills/skillssh-files.ts +395 -0
  767. package/src/skills/skillssh-registry.ts +4 -4
  768. package/src/stt/__tests__/daemon-batch-transcriber.test.ts +392 -0
  769. package/src/stt/__tests__/types.test.ts +89 -0
  770. package/src/stt/daemon-batch-transcriber.ts +195 -0
  771. package/src/stt/stt-stream-session.ts +499 -0
  772. package/src/stt/types.ts +330 -0
  773. package/src/stt/wav-encoder.test.ts +373 -0
  774. package/src/stt/wav-encoder.ts +175 -0
  775. package/src/subagent/manager.ts +169 -40
  776. package/src/subagent/types.ts +19 -0
  777. package/src/tools/apps/executors.ts +11 -2
  778. package/src/tools/browser/__tests__/auth-detector.test.ts +202 -108
  779. package/src/tools/browser/__tests__/browser-mode.test.ts +119 -0
  780. package/src/tools/browser/__tests__/browser-status.test.ts +123 -0
  781. package/src/tools/browser/auth-detector.ts +43 -12
  782. package/src/tools/browser/browser-execution.ts +1787 -342
  783. package/src/tools/browser/browser-manager.ts +81 -12
  784. package/src/tools/browser/browser-mode-constants.ts +12 -0
  785. package/src/tools/browser/browser-mode.ts +92 -0
  786. package/src/tools/browser/browser-status-constants.ts +33 -0
  787. package/src/tools/browser/cdp-client/__tests__/accessibility-snapshot.test.ts +318 -0
  788. package/src/tools/browser/cdp-client/__tests__/cdp-dom-helpers.test.ts +1175 -0
  789. package/src/tools/browser/cdp-client/__tests__/cdp-inspect-client.test.ts +1263 -0
  790. package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +359 -0
  791. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +1993 -0
  792. package/src/tools/browser/cdp-client/__tests__/fixtures/ax-tree-nested-frames.json +64 -0
  793. package/src/tools/browser/cdp-client/__tests__/fixtures/ax-tree-simple.json +69 -0
  794. package/src/tools/browser/cdp-client/__tests__/local-cdp-client.test.ts +310 -0
  795. package/src/tools/browser/cdp-client/__tests__/types.test.ts +96 -0
  796. package/src/tools/browser/cdp-client/accessibility-snapshot.ts +387 -0
  797. package/src/tools/browser/cdp-client/cdp-dom-helpers.ts +695 -0
  798. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/discovery.test.ts +1007 -0
  799. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +580 -0
  800. package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +744 -0
  801. package/src/tools/browser/cdp-client/cdp-inspect/ws-transport.ts +579 -0
  802. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +868 -0
  803. package/src/tools/browser/cdp-client/errors.ts +49 -0
  804. package/src/tools/browser/cdp-client/extension-cdp-client.ts +148 -0
  805. package/src/tools/browser/cdp-client/factory.ts +914 -0
  806. package/src/tools/browser/cdp-client/index.ts +28 -0
  807. package/src/tools/browser/cdp-client/local-cdp-client.ts +187 -0
  808. package/src/tools/browser/cdp-client/types.ts +120 -0
  809. package/src/tools/credentials/vault.ts +35 -6
  810. package/src/tools/filesystem/edit.ts +1 -1
  811. package/src/tools/filesystem/list.ts +1 -1
  812. package/src/tools/filesystem/read.ts +1 -1
  813. package/src/tools/filesystem/write.ts +2 -1
  814. package/src/tools/host-filesystem/edit.ts +1 -1
  815. package/src/tools/host-filesystem/read.ts +12 -15
  816. package/src/tools/host-filesystem/write.ts +1 -1
  817. package/src/tools/host-terminal/host-shell.ts +21 -16
  818. package/src/tools/network/web-fetch.ts +5 -2
  819. package/src/tools/network/web-search.ts +5 -2
  820. package/src/tools/permission-checker.ts +77 -82
  821. package/src/tools/registry.ts +0 -2
  822. package/src/tools/secret-detection-handler.ts +34 -0
  823. package/src/tools/shared/filesystem/image-read.ts +61 -40
  824. package/src/tools/shared/shell-output.ts +3 -1
  825. package/src/tools/side-effects.ts +2 -0
  826. package/src/tools/skills/sandbox-runner.ts +3 -2
  827. package/src/tools/subagent/spawn.ts +47 -3
  828. package/src/tools/subagent/status.ts +2 -0
  829. package/src/tools/system/register.ts +2 -16
  830. package/src/tools/terminal/safe-env.ts +15 -0
  831. package/src/tools/terminal/shell.ts +36 -20
  832. package/src/tools/tool-approval-handler.ts +48 -2
  833. package/src/tools/tool-manifest.ts +21 -0
  834. package/src/tools/types.ts +19 -0
  835. package/src/tools/ui-surface/definitions.ts +6 -1
  836. package/src/tts/__tests__/provider-adapters.test.ts +834 -0
  837. package/src/tts/__tests__/provider-catalog-consistency.test.ts +196 -0
  838. package/src/tts/__tests__/provider-catalog.test.ts +183 -0
  839. package/src/tts/__tests__/provider-registry.test.ts +90 -0
  840. package/src/tts/provider-catalog.ts +201 -0
  841. package/src/tts/provider-registry.ts +73 -0
  842. package/src/tts/providers/deepgram-provider.ts +219 -0
  843. package/src/tts/providers/elevenlabs-provider.ts +211 -0
  844. package/src/tts/providers/fish-audio-provider.ts +183 -0
  845. package/src/tts/providers/index.ts +42 -0
  846. package/src/tts/providers/register-builtins.ts +130 -0
  847. package/src/tts/synthesize-text.ts +110 -0
  848. package/src/tts/tts-config-resolver.ts +78 -0
  849. package/src/tts/types.ts +153 -0
  850. package/src/types/onboarding-context.ts +7 -0
  851. package/src/util/abort-reasons.ts +58 -0
  852. package/src/util/device-id.ts +32 -16
  853. package/src/util/errors.ts +9 -1
  854. package/src/util/platform.ts +63 -24
  855. package/src/util/pricing.ts +66 -3
  856. package/src/util/spawn.ts +1 -1
  857. package/src/util/truncate.ts +4 -2
  858. package/src/util/unicode.ts +201 -0
  859. package/src/version.ts +19 -24
  860. package/src/watcher/engine.ts +23 -0
  861. package/src/watcher/watcher-store.ts +31 -0
  862. package/src/workspace/migrations/003-seed-device-id.ts +9 -3
  863. package/src/workspace/migrations/017-seed-persona-dirs.ts +68 -4
  864. package/src/workspace/migrations/029-seed-pkb.ts +1 -1
  865. package/src/workspace/migrations/031-drop-user-md.ts +317 -0
  866. package/src/workspace/migrations/031-llm-log-retention-zero-to-null.ts +73 -0
  867. package/src/workspace/migrations/032-tts-provider-unification.ts +227 -0
  868. package/src/workspace/migrations/033-stt-service-explicit-config.ts +122 -0
  869. package/src/workspace/migrations/034-remove-calls-voice-transcription-provider.ts +215 -0
  870. package/src/workspace/migrations/035-seed-slack-channel-persona.ts +50 -0
  871. package/src/workspace/migrations/036-update-pkb-index-bar.ts +37 -0
  872. package/src/workspace/migrations/037-create-meets-dir.ts +61 -0
  873. package/src/workspace/migrations/registry.ts +16 -0
  874. package/src/workspace/top-level-renderer.ts +31 -1
  875. package/src/workspace/turn-commit.ts +31 -0
  876. package/src/__tests__/chrome-cdp.test.ts +0 -419
  877. package/src/__tests__/email-cli.test.ts +0 -297
  878. package/src/__tests__/email-service-config-fallback.test.ts +0 -102
  879. package/src/__tests__/permission-mode-sse.test.ts +0 -418
  880. package/src/__tests__/permission-mode-store.test.ts +0 -277
  881. package/src/browser-extension-relay/protocol.ts +0 -63
  882. package/src/browser-extension-relay/server.ts +0 -203
  883. package/src/cli/commands/browser-relay.ts +0 -536
  884. package/src/config/schemas/sandbox.ts +0 -14
  885. package/src/email/guardrails.ts +0 -221
  886. package/src/email/provider.ts +0 -117
  887. package/src/email/providers/agentmail.ts +0 -361
  888. package/src/email/providers/index.ts +0 -65
  889. package/src/email/service.ts +0 -384
  890. package/src/email/types.ts +0 -126
  891. package/src/permissions/permission-mode-store.ts +0 -180
  892. package/src/prompts/templates/USER.md +0 -13
  893. package/src/providers/speech-to-text/types.ts +0 -17
  894. package/src/tools/browser/chrome-cdp.ts +0 -239
  895. package/src/tools/system/set-permission-mode.ts +0 -103
@@ -102,16 +102,24 @@ export class AssistantError extends VellumError {
102
102
  export class ProviderError extends AssistantError {
103
103
  /** Delay (in ms) suggested by the server's Retry-After header, if present. */
104
104
  public readonly retryAfterMs?: number;
105
+ /**
106
+ * Tagged daemon-owned abort reason carried over from the AbortSignal that
107
+ * triggered this error. Untyped here to avoid a daemon→util import cycle;
108
+ * `AbortReason` from `util/abort-reasons.ts` is the only producer and
109
+ * `isAbortReason` is the canonical type guard for consumers.
110
+ */
111
+ public readonly abortReason?: unknown;
105
112
 
106
113
  constructor(
107
114
  message: string,
108
115
  public readonly provider: string,
109
116
  public readonly statusCode?: number,
110
- options?: { cause?: unknown; retryAfterMs?: number },
117
+ options?: { cause?: unknown; retryAfterMs?: number; abortReason?: unknown },
111
118
  ) {
112
119
  super(message, ErrorCode.PROVIDER_ERROR, options);
113
120
  this.name = "ProviderError";
114
121
  this.retryAfterMs = options?.retryAfterMs;
122
+ this.abortReason = options?.abortReason;
115
123
  }
116
124
  }
117
125
 
@@ -45,13 +45,24 @@ export function normalizeAssistantId(assistantId: string): string {
45
45
  }
46
46
 
47
47
  /**
48
- * Compute the root ~/.vellum directory path.
48
+ * The daemon's per-instance root data directory. Resolution order:
49
49
  *
50
- * This is a simple inline computation not a shared function — so each
51
- * helper is self-contained and the module has no hidden coupling to
52
- * env-var indirection.
50
+ * 1. `BASE_DATA_DIR` env var set by the CLI when spawning the daemon
51
+ * for a specific assistant (see `cli/src/lib/local.ts`). Points at the
52
+ * per-assistant instanceDir; we append `.vellum` to get the daemon's
53
+ * root so root-level state (PID, credentials, runtime-port, workspace)
54
+ * is isolated per instance.
55
+ * 2. Fallback: `join(homedir(), ".vellum")`. Used when the daemon runs
56
+ * outside the CLI-spawn lifecycle (containerized deployments, manual
57
+ * test invocations).
58
+ *
59
+ * Docker mode relocates the workspace via `VELLUM_WORKSPACE_DIR` rather
60
+ * than `BASE_DATA_DIR`, so honoring `BASE_DATA_DIR` here does not affect
61
+ * containerized deployments.
53
62
  */
54
63
  function vellumRoot(): string {
64
+ const baseDataDir = process.env.BASE_DATA_DIR?.trim();
65
+ if (baseDataDir) return join(baseDataDir, ".vellum");
55
66
  return join(homedir(), ".vellum");
56
67
  }
57
68
 
@@ -136,39 +147,54 @@ export function isTCPEnabled(): boolean {
136
147
 
137
148
  /**
138
149
  * Returns the hostname/address for the TCP listener.
139
- * If iOS pairing is enabled (flag file): '0.0.0.0' (LAN-accessible).
140
- * Default: '127.0.0.1' (localhost only).
150
+ * Always binds to localhost only. iOS pairing uses the gateway
151
+ * relay.
141
152
  */
142
153
  export function getTCPHost(): string {
143
- if (isIOSPairingEnabled()) return "0.0.0.0";
144
154
  return "127.0.0.1";
145
155
  }
146
156
 
157
+ // Kept in sync with `cli/src/lib/environments/seeds.ts` and
158
+ // `clients/chrome-extension/native-host/src/lockfile.ts`. Drift between
159
+ // these three sites is caught at test time by
160
+ // `cli/src/__tests__/env-drift.test.ts`. Fast follow: hoist the shared
161
+ // list into a `packages/environments` package so all three sites import
162
+ // from one place.
163
+ const KNOWN_ENVIRONMENTS: ReadonlySet<string> = new Set([
164
+ "production",
165
+ "staging",
166
+ "test",
167
+ "dev",
168
+ "local",
169
+ ]);
170
+
147
171
  /**
148
- * Returns whether iOS pairing mode is enabled.
149
- * When enabled, the TCP listener binds to 0.0.0.0 (all interfaces)
150
- * instead of 127.0.0.1 (localhost only), making the daemon reachable
151
- * from iOS devices on the same local network.
152
- *
153
- * Checks for the presence of the flag file ~/.vellum/ios-pairing-enabled.
154
- * Default: false.
172
+ * Returns the env-scoped XDG config subdirectory name for Vellum
173
+ * (`vellum` in production, `vellum-<env>` otherwise). Mirrors the Swift
174
+ * side's `VellumPaths.configDir` and the CLI's
175
+ * `environments/paths.ts:getConfigDir` so the daemon resolves to the
176
+ * same on-disk location as every other writer of these files.
155
177
  *
156
- * This is separate from isTCPEnabled() TCP can be enabled for localhost-only
157
- * access without exposing the daemon to the LAN.
178
+ * Unknown environment names fall back to production to preserve the
179
+ * legacy path for any unrecognized value.
158
180
  */
159
- export function isIOSPairingEnabled(): boolean {
160
- return existsSync(join(vellumRoot(), "ios-pairing-enabled"));
181
+ export function getXdgVellumConfigDirName(): string {
182
+ const raw = process.env.VELLUM_ENVIRONMENT?.trim();
183
+ if (!raw || raw === "production") return "vellum";
184
+ if (!KNOWN_ENVIRONMENTS.has(raw)) return "vellum";
185
+ return `vellum-${raw}`;
161
186
  }
162
187
 
163
188
  /**
164
- * Returns the XDG-compliant path for the platform API token
165
- * (~/.config/vellum/platform-token). This is the canonical location
166
- * shared by the CLI and desktop app.
189
+ * Returns the XDG-compliant path for the platform API token. Resolves to
190
+ * `$XDG_CONFIG_HOME/vellum/platform-token` in production and
191
+ * `$XDG_CONFIG_HOME/vellum-<env>/platform-token` otherwise, matching the
192
+ * Swift client and CLI.
167
193
  */
168
- function getXdgPlatformTokenPath(): string {
194
+ export function getXdgPlatformTokenPath(): string {
169
195
  const configHome =
170
196
  process.env.XDG_CONFIG_HOME?.trim() || join(homedir(), ".config");
171
- return join(configHome, "vellum", "platform-token");
197
+ return join(configHome, getXdgVellumConfigDirName(), "platform-token");
172
198
  }
173
199
 
174
200
  /**
@@ -202,6 +228,18 @@ export function getPidPath(): string {
202
228
  return join(vellumRoot(), "vellum.pid");
203
229
  }
204
230
 
231
+ /**
232
+ * Returns the path to the runtime HTTP port file (~/.vellum/runtime-port).
233
+ * The daemon writes its active HTTP port here on startup so thin helpers
234
+ * that need to reach the runtime (e.g. the chrome-extension native messaging
235
+ * helper) can locate a non-default `RUNTIME_HTTP_PORT` without a manifest
236
+ * change. Root-level path by design — the file is read by helpers that may
237
+ * not know the workspace override path.
238
+ */
239
+ export function getRuntimePortFilePath(): string {
240
+ return join(vellumRoot(), "runtime-port");
241
+ }
242
+
205
243
  export function getDbPath(): string {
206
244
  return join(getDataDir(), "db", "assistant.db");
207
245
  }
@@ -328,7 +366,7 @@ export function getConversationsDir(): string {
328
366
  return join(getWorkspaceDir(), "conversations");
329
367
  }
330
368
 
331
- /** Returns the workspace path for a prompt file (e.g. IDENTITY.md, SOUL.md, USER.md). */
369
+ /** Returns the workspace path for a prompt file (e.g. IDENTITY.md, SOUL.md). */
332
370
  export function getWorkspacePromptPath(file: string): string {
333
371
  return join(getWorkspaceDir(), file);
334
372
  }
@@ -388,6 +426,7 @@ export function ensureDataDir(): void {
388
426
  join(wsData, "memory"),
389
427
  join(wsData, "memory", "knowledge"),
390
428
  join(wsData, "apps"),
429
+ join(wsData, "attachments"),
391
430
  join(wsData, "interfaces"),
392
431
  join(wsData, "sounds"),
393
432
  ];
@@ -21,6 +21,7 @@ const ANTHROPIC_FAST_MODE_MULTIPLIER = 6;
21
21
  */
22
22
  const PROVIDER_PRICING: Record<string, Record<string, ModelPricing>> = {
23
23
  anthropic: {
24
+ "claude-opus-4-7": { inputPer1M: 5, outputPer1M: 25 },
24
25
  "claude-opus-4-6": { inputPer1M: 5, outputPer1M: 25 },
25
26
  "claude-opus-4": { inputPer1M: 15, outputPer1M: 75 },
26
27
  "claude-sonnet-4": { inputPer1M: 3, outputPer1M: 15 },
@@ -55,6 +56,44 @@ const PROVIDER_PRICING: Record<string, Record<string, ModelPricing>> = {
55
56
  },
56
57
  };
57
58
 
59
+ /**
60
+ * Identify a model ID as an Anthropic model — accepts both the OpenRouter
61
+ * prefix form (`anthropic/claude-opus-4.6`) and the bare Anthropic slug
62
+ * (`claude-opus-4-6`) that `response.model` sometimes carries.
63
+ */
64
+ function isAnthropicModelId(model: string): boolean {
65
+ return model.startsWith("anthropic/") || model.startsWith("claude-");
66
+ }
67
+
68
+ /**
69
+ * Normalize an OpenRouter-style Anthropic model ID for lookup against the
70
+ * Anthropic catalog: strip the `anthropic/` prefix and convert OpenRouter's
71
+ * dot-separated version tokens (`claude-opus-4.6`) to the dash form Anthropic
72
+ * uses natively (`claude-opus-4-6`).
73
+ */
74
+ function normalizeAnthropicModelId(model: string): string {
75
+ const bare = model.startsWith("anthropic/")
76
+ ? model.slice("anthropic/".length)
77
+ : model;
78
+ return bare.replace(/\./g, "-");
79
+ }
80
+
81
+ /**
82
+ * Whether Anthropic's pricing rules (cache-read/write multipliers, fast-mode
83
+ * surcharge) apply for the given provider/model. True for direct Anthropic
84
+ * calls and for Anthropic models routed through OpenRouter — OpenRouter
85
+ * proxies to Anthropic's Messages API, so the usage response carries the
86
+ * same cache and speed fields and is charged at Anthropic's rates.
87
+ */
88
+ export function usesAnthropicPricingRules(
89
+ provider: string,
90
+ model: string,
91
+ ): boolean {
92
+ if (provider === "anthropic") return true;
93
+ if (provider === "openrouter" && isAnthropicModelId(model)) return true;
94
+ return false;
95
+ }
96
+
58
97
  /**
59
98
  * Look up pricing for a model within a provider's catalog.
60
99
  * Tries exact match first, then longest prefix match.
@@ -147,12 +186,14 @@ function getAnthropicCacheWriteTokens(usage: PricingUsage): {
147
186
  */
148
187
  function calculateUsageCost(
149
188
  provider: string,
189
+ model: string,
150
190
  pricing: ModelPricing,
151
191
  usage: PricingUsage,
152
192
  ): number {
193
+ const useAnthropicRules = usesAnthropicPricingRules(provider, model);
153
194
  // Anthropic fast mode: 6x multiplier on base rates (cache multipliers stack on top)
154
195
  const speedMultiplier =
155
- provider === "anthropic" && usage.speed === "fast"
196
+ useAnthropicRules && usage.speed === "fast"
156
197
  ? ANTHROPIC_FAST_MODE_MULTIPLIER
157
198
  : 1;
158
199
  const effectivePricing: ModelPricing = {
@@ -169,7 +210,7 @@ function calculateUsageCost(
169
210
  usage.outputTokens,
170
211
  );
171
212
 
172
- if (provider !== "anthropic") {
213
+ if (!useAnthropicRules) {
173
214
  return (
174
215
  directInputCost +
175
216
  outputCost +
@@ -223,6 +264,27 @@ export function resolvePricingForUsage(
223
264
  model: string,
224
265
  usage: PricingUsage,
225
266
  ): PricingResult {
267
+ // Anthropic models routed through OpenRouter: look up against the Anthropic
268
+ // catalog using the normalized bare slug. OpenRouter bills these calls at
269
+ // Anthropic's rates and the underlying Messages API response includes
270
+ // Anthropic's cache- and speed-metadata fields.
271
+ if (provider === "openrouter" && isAnthropicModelId(model)) {
272
+ const anthropicCatalog = PROVIDER_PRICING.anthropic;
273
+ if (anthropicCatalog) {
274
+ const pricing = findPricing(
275
+ anthropicCatalog,
276
+ normalizeAnthropicModelId(model),
277
+ );
278
+ if (pricing) {
279
+ return {
280
+ estimatedCostUsd: calculateUsageCost(provider, model, pricing, usage),
281
+ pricingStatus: "priced",
282
+ };
283
+ }
284
+ }
285
+ return { estimatedCostUsd: null, pricingStatus: "unpriced" };
286
+ }
287
+
226
288
  const providerCatalog = PROVIDER_PRICING[provider];
227
289
  if (!providerCatalog) {
228
290
  return { estimatedCostUsd: null, pricingStatus: "unpriced" };
@@ -234,7 +296,7 @@ export function resolvePricingForUsage(
234
296
  }
235
297
 
236
298
  return {
237
- estimatedCostUsd: calculateUsageCost(provider, pricing, usage),
299
+ estimatedCostUsd: calculateUsageCost(provider, model, pricing, usage),
238
300
  pricingStatus: "priced",
239
301
  };
240
302
  }
@@ -253,6 +315,7 @@ export function resolvePricingForUsageWithOverrides(
253
315
  return {
254
316
  estimatedCostUsd: calculateUsageCost(
255
317
  provider,
318
+ model,
256
319
  {
257
320
  inputPer1M: bestOverride.inputPer1M,
258
321
  outputPer1M: bestOverride.outputPer1M,
package/src/util/spawn.ts CHANGED
@@ -22,7 +22,7 @@ export function spawnWithTimeout(
22
22
  timeoutMs: number,
23
23
  ): Promise<{ exitCode: number; stdout: string; stderr: string }> {
24
24
  return new Promise((resolve, reject) => {
25
- // Augment PATH so Homebrew-installed tools (ffmpeg, ffprobe, whisper-cpp)
25
+ // Augment PATH so Homebrew-installed tools (ffmpeg, ffprobe)
26
26
  // are found even when the daemon runs as a bundled binary with minimal PATH.
27
27
  const proc = Bun.spawn(cmd, {
28
28
  stdout: "pipe",
@@ -1,6 +1,8 @@
1
+ import { safeStringSlice } from "./unicode.js";
2
+
1
3
  /** Truncate a string to `maxLen` characters, appending `suffix` if truncated. */
2
4
  export function truncate(str: string, maxLen: number, suffix = "..."): string {
3
5
  if (str.length <= maxLen) return str;
4
- if (maxLen < suffix.length) return str.slice(0, maxLen);
5
- return str.slice(0, maxLen - suffix.length) + suffix;
6
+ if (maxLen < suffix.length) return safeStringSlice(str, 0, maxLen);
7
+ return safeStringSlice(str, 0, maxLen - suffix.length) + suffix;
6
8
  }
@@ -0,0 +1,201 @@
1
+ /**
2
+ * UTF-16 surrogate-pair-safe string utilities.
3
+ *
4
+ * JavaScript strings are UTF-16. Code points above the BMP (emoji, many CJK
5
+ * characters, mathematical symbols, etc.) are stored as surrogate pairs — a
6
+ * high surrogate (U+D800–U+DBFF) followed by a low surrogate (U+DC00–U+DFFF).
7
+ * `String.prototype.slice` operates on code units, not code points, so naive
8
+ * slicing can split a pair and leave an orphaned surrogate. Orphaned
9
+ * surrogates are invalid UTF-16 and cause `JSON.stringify` output to be
10
+ * rejected by strict JSON parsers (including Anthropic's API).
11
+ */
12
+
13
+ const HIGH_SURROGATE_START = 0xd800;
14
+ const HIGH_SURROGATE_END = 0xdbff;
15
+ const LOW_SURROGATE_START = 0xdc00;
16
+ const LOW_SURROGATE_END = 0xdfff;
17
+ const REPLACEMENT_CHAR = "\ufffd";
18
+
19
+ function isHighSurrogate(code: number): boolean {
20
+ return code >= HIGH_SURROGATE_START && code <= HIGH_SURROGATE_END;
21
+ }
22
+
23
+ function isLowSurrogate(code: number): boolean {
24
+ return code >= LOW_SURROGATE_START && code <= LOW_SURROGATE_END;
25
+ }
26
+
27
+ /**
28
+ * Slice a string like `String.prototype.slice`, but never cut a UTF-16
29
+ * surrogate pair in half.
30
+ *
31
+ * If the character at `end - 1` is a high surrogate *and* there is more of
32
+ * the string beyond `end` (so cutting would actually orphan it), `end` is
33
+ * decremented by one.
34
+ *
35
+ * If the character at `start` is a low surrogate *and* `start > 0` (so we
36
+ * would be starting mid-pair), `start` is incremented by one.
37
+ *
38
+ * When `end === str.length` we do not touch a trailing high surrogate: if it
39
+ * was already orphaned upstream, repairing it here would silently mutate
40
+ * content. That is the sanitizer's job, not this function's.
41
+ */
42
+ export function safeStringSlice(
43
+ str: string,
44
+ start = 0,
45
+ end: number = str.length,
46
+ ): string {
47
+ let safeStart = Math.max(0, Math.min(str.length, start));
48
+ let safeEnd = Math.max(safeStart, Math.min(str.length, end));
49
+
50
+ if (safeEnd < str.length && safeEnd > safeStart) {
51
+ const lastCode = str.charCodeAt(safeEnd - 1);
52
+ if (isHighSurrogate(lastCode)) {
53
+ safeEnd--;
54
+ }
55
+ }
56
+
57
+ if (safeStart > 0 && safeStart < str.length) {
58
+ const firstCode = str.charCodeAt(safeStart);
59
+ if (isLowSurrogate(firstCode)) {
60
+ safeStart++;
61
+ if (safeStart > safeEnd) safeEnd = safeStart;
62
+ }
63
+ }
64
+
65
+ return str.slice(safeStart, safeEnd);
66
+ }
67
+
68
+ /**
69
+ * Replace every orphaned UTF-16 surrogate in `str` with U+FFFD
70
+ * (REPLACEMENT CHARACTER).
71
+ *
72
+ * Returns the original reference if no changes were needed, so callers can
73
+ * check `result === input` as a cheap "nothing was stripped" signal.
74
+ */
75
+ export function stripOrphanedSurrogates(str: string): string {
76
+ let needsFix = false;
77
+ for (let i = 0; i < str.length; i++) {
78
+ const code = str.charCodeAt(i);
79
+ if (isHighSurrogate(code)) {
80
+ const next = i + 1 < str.length ? str.charCodeAt(i + 1) : 0;
81
+ if (!isLowSurrogate(next)) {
82
+ needsFix = true;
83
+ break;
84
+ }
85
+ i++;
86
+ } else if (isLowSurrogate(code)) {
87
+ needsFix = true;
88
+ break;
89
+ }
90
+ }
91
+ if (!needsFix) return str;
92
+
93
+ let out = "";
94
+ for (let i = 0; i < str.length; i++) {
95
+ const code = str.charCodeAt(i);
96
+ if (isHighSurrogate(code)) {
97
+ const next = i + 1 < str.length ? str.charCodeAt(i + 1) : 0;
98
+ if (isLowSurrogate(next)) {
99
+ out += str[i] + str[i + 1];
100
+ i++;
101
+ } else {
102
+ out += REPLACEMENT_CHAR;
103
+ }
104
+ } else if (isLowSurrogate(code)) {
105
+ out += REPLACEMENT_CHAR;
106
+ } else {
107
+ out += str[i];
108
+ }
109
+ }
110
+ return out;
111
+ }
112
+
113
+ /**
114
+ * Result of a deep sanitization walk. If `changed` is false the caller should
115
+ * use the original reference it passed in; `value` is only meaningful when
116
+ * `changed` is true.
117
+ */
118
+ export interface DeepSanitizeResult<T> {
119
+ value: T;
120
+ changed: boolean;
121
+ /** Count of string values that had at least one orphan replaced. */
122
+ fixedStringCount: number;
123
+ }
124
+
125
+ /**
126
+ * Recursively walk arrays and plain objects, replacing orphaned surrogates in
127
+ * every string value. Non-plain objects (class instances, Date, Buffer, Map,
128
+ * Set, etc.) and non-string primitives are returned unchanged.
129
+ *
130
+ * On the happy path (no orphans found anywhere in the tree) the original
131
+ * reference is returned verbatim — no copies are made.
132
+ */
133
+ export function stripOrphanedSurrogatesDeep<T>(input: T): DeepSanitizeResult<T> {
134
+ let fixedStringCount = 0;
135
+
136
+ const walk = (value: unknown): { value: unknown; changed: boolean } => {
137
+ if (typeof value === "string") {
138
+ const cleaned = stripOrphanedSurrogates(value);
139
+ if (cleaned !== value) {
140
+ fixedStringCount++;
141
+ return { value: cleaned, changed: true };
142
+ }
143
+ return { value, changed: false };
144
+ }
145
+
146
+ if (Array.isArray(value)) {
147
+ let next: unknown[] | null = null;
148
+ for (let i = 0; i < value.length; i++) {
149
+ const result = walk(value[i]);
150
+ if (result.changed && next === null) {
151
+ next = [];
152
+ for (let j = 0; j < i; j++) {
153
+ next.push(value[j]);
154
+ }
155
+ }
156
+ if (next !== null) {
157
+ next.push(result.value);
158
+ }
159
+ }
160
+ return next !== null
161
+ ? { value: next, changed: true }
162
+ : { value, changed: false };
163
+ }
164
+
165
+ if (value != null && typeof value === "object") {
166
+ const proto = Object.getPrototypeOf(value);
167
+ if (proto !== Object.prototype && proto !== null) {
168
+ return { value, changed: false };
169
+ }
170
+ const source = value as Record<string, unknown>;
171
+ const keys = Object.keys(source);
172
+ let next: Record<string, unknown> | null = null;
173
+ for (let i = 0; i < keys.length; i++) {
174
+ const key = keys[i]!;
175
+ const result = walk(source[key]);
176
+ if (result.changed && next === null) {
177
+ next = {};
178
+ for (let j = 0; j < i; j++) {
179
+ const priorKey = keys[j]!;
180
+ next[priorKey] = source[priorKey];
181
+ }
182
+ }
183
+ if (next !== null) {
184
+ next[key] = result.value;
185
+ }
186
+ }
187
+ return next !== null
188
+ ? { value: next, changed: true }
189
+ : { value, changed: false };
190
+ }
191
+
192
+ return { value, changed: false };
193
+ };
194
+
195
+ const result = walk(input);
196
+ return {
197
+ value: result.value as T,
198
+ changed: result.changed,
199
+ fixedStringCount,
200
+ };
201
+ }
package/src/version.ts CHANGED
@@ -1,36 +1,31 @@
1
1
  import { readFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
 
4
+ const DEV_VERSION_SENTINEL = "0.0.0-dev";
5
+
6
+ function readPackageVersion(): string | undefined {
7
+ try {
8
+ const pkgPath = join(
9
+ import.meta.dirname ?? __dirname,
10
+ "..",
11
+ "package.json",
12
+ );
13
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
14
+ if (pkg.version && typeof pkg.version === "string") return pkg.version;
15
+ } catch {
16
+ // package.json missing or unreadable
17
+ }
18
+ return undefined;
19
+ }
20
+
4
21
  function resolveVersion(): string {
5
22
  const envVersion = process.env.APP_VERSION;
6
23
 
7
- // When APP_VERSION is not set, we're in local development — return the dev
8
- // sentinel so Sentry (and similar) classify the session as "development".
9
- if (!envVersion) return "0.0.0-dev";
10
-
11
- // CI sets APP_VERSION to the dev placeholder during builds; resolve it to
12
- // the package.json release version so Sentry gets a meaningful release tag.
13
- if (envVersion === "0.0.0-dev") {
14
- try {
15
- const pkgPath = join(
16
- import.meta.dirname ?? __dirname,
17
- "..",
18
- "package.json",
19
- );
20
- const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
21
- if (pkg.version && typeof pkg.version === "string") return pkg.version;
22
- } catch {
23
- // package.json missing or unreadable
24
- }
25
- return "0.0.0-dev";
26
- }
24
+ if (envVersion && envVersion !== DEV_VERSION_SENTINEL) return envVersion;
27
25
 
28
- return envVersion;
26
+ return readPackageVersion() ?? DEV_VERSION_SENTINEL;
29
27
  }
30
28
 
31
- // Version is embedded at compile time via --define in CI.
32
- // Falls back to "0.0.0-dev" for local development, or resolves the dev
33
- // placeholder to package.json version when explicitly set in CI.
34
29
  export const APP_VERSION: string = resolveVersion();
35
30
 
36
31
  // Commit SHA is embedded at compile time via --define in CI.
@@ -19,6 +19,7 @@ import {
19
19
  insertWatcherEvent,
20
20
  resetStuckWatchers,
21
21
  setWatcherConversationId,
22
+ skipWatcherPoll,
22
23
  updateEventDisposition,
23
24
  } from "./watcher-store.js";
24
25
 
@@ -76,6 +77,28 @@ export async function runWatchersOnce(
76
77
  continue;
77
78
  }
78
79
 
80
+ // Pre-poll credential gate: skip if token is irrecoverably broken.
81
+ // Prevents wasting API calls and burning through circuit breaker
82
+ // attempts on credentials that need manual reauthorization.
83
+ try {
84
+ const { checkCredentialForProvider } =
85
+ await import("../credential-health/credential-health-service.js");
86
+ const health = await checkCredentialForProvider(
87
+ watcher.credentialService,
88
+ );
89
+ if (
90
+ health &&
91
+ (health.status === "revoked" ||
92
+ health.status === "missing_token" ||
93
+ (health.status === "expired" && !health.canAutoRecover))
94
+ ) {
95
+ skipWatcherPoll(watcher.id, `Credential unhealthy: ${health.details}`);
96
+ continue;
97
+ }
98
+ } catch {
99
+ // Non-fatal: proceed with normal poll if health check fails
100
+ }
101
+
79
102
  try {
80
103
  const config = watcher.configJson ? JSON.parse(watcher.configJson) : {};
81
104
 
@@ -221,6 +221,37 @@ export function completeWatcherPoll(
221
221
  db.update(watchers).set(set).where(eq(watchers.id, id)).run();
222
222
  }
223
223
 
224
+ /**
225
+ * Skip a watcher poll: apply backoff to nextPollAt without incrementing
226
+ * consecutiveErrors. Used when a poll is skipped for a recoverable reason
227
+ * (e.g. credential health gate) that should NOT count toward the circuit
228
+ * breaker threshold.
229
+ */
230
+ export function skipWatcherPoll(id: string, reason: string): void {
231
+ const db = getDb();
232
+ const watcher = db.select().from(watchers).where(eq(watchers.id, id)).get();
233
+ if (!watcher) return;
234
+
235
+ const now = Date.now();
236
+ // Use the same backoff formula but based on existing consecutiveErrors
237
+ // (which stays unchanged). Minimum backoff of 30s.
238
+ const backoff = Math.min(
239
+ 30_000 * Math.pow(2, watcher.consecutiveErrors),
240
+ 60 * 60 * 1000,
241
+ );
242
+
243
+ db.update(watchers)
244
+ .set({
245
+ status: "idle",
246
+ lastError: truncate(reason, 2000, ""),
247
+ lastPollAt: now,
248
+ nextPollAt: now + backoff,
249
+ updatedAt: now,
250
+ })
251
+ .where(eq(watchers.id, id))
252
+ .run();
253
+ }
254
+
224
255
  /**
225
256
  * Record a poll error: increment consecutive errors, apply backoff.
226
257
  */
@@ -8,15 +8,21 @@ import {
8
8
  import { homedir } from "node:os";
9
9
  import { join } from "node:path";
10
10
 
11
- import { getDeviceIdBaseDir } from "../../util/device-id.js";
12
11
  import type { WorkspaceMigration } from "./types.js";
13
12
 
13
+ function deviceIdBaseDir(): string {
14
+ const containerized =
15
+ process.env.IS_CONTAINERIZED === "true" ||
16
+ process.env.IS_CONTAINERIZED === "1";
17
+ return containerized ? "/home/assistant" : homedir();
18
+ }
19
+
14
20
  export const seedDeviceIdMigration: WorkspaceMigration = {
15
21
  id: "003-seed-device-id",
16
22
  description:
17
23
  "Seed device.json deviceId from the most recent lockfile installationId for continuity",
18
24
  run(_workspaceDir: string): void {
19
- const base = getDeviceIdBaseDir();
25
+ const base = deviceIdBaseDir();
20
26
  const vellumDir = join(base, ".vellum");
21
27
  const devicePath = join(vellumDir, "device.json");
22
28
 
@@ -109,7 +115,7 @@ export const seedDeviceIdMigration: WorkspaceMigration = {
109
115
  // The forward migration seeds deviceId in ~/.vellum/device.json from the
110
116
  // lockfile. Reverse by removing device.json entirely — getDeviceId() will
111
117
  // generate a fresh one on next startup if needed.
112
- const base = getDeviceIdBaseDir();
118
+ const base = deviceIdBaseDir();
113
119
  const devicePath = join(base, ".vellum", "device.json");
114
120
  if (existsSync(devicePath)) {
115
121
  unlinkSync(devicePath);