@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
@@ -1,5 +1,8 @@
1
1
  import { z } from "zod";
2
2
 
3
+ import { SttServiceSchema } from "./stt.js";
4
+ import { TtsServiceSchema } from "./tts.js";
5
+
3
6
  export const ServiceModeSchema = z.enum(["managed", "your-own"]);
4
7
  export type ServiceMode = z.infer<typeof ServiceModeSchema>;
5
8
 
@@ -66,6 +69,16 @@ export const GitHubOAuthServiceSchema = BaseServiceSchema.extend({
66
69
  });
67
70
  export type GitHubOAuthService = z.infer<typeof GitHubOAuthServiceSchema>;
68
71
 
72
+ export const NotionOAuthServiceSchema = BaseServiceSchema.extend({
73
+ mode: ServiceModeSchema.default("your-own"),
74
+ });
75
+ export type NotionOAuthService = z.infer<typeof NotionOAuthServiceSchema>;
76
+
77
+ export const TwitterOAuthServiceSchema = BaseServiceSchema.extend({
78
+ mode: ServiceModeSchema.default("your-own"),
79
+ });
80
+ export type TwitterOAuthService = z.infer<typeof TwitterOAuthServiceSchema>;
81
+
69
82
  export const ServicesSchema = z.object({
70
83
  inference: InferenceServiceSchema.default(InferenceServiceSchema.parse({})),
71
84
  "image-generation": ImageGenerationServiceSchema.default(
@@ -74,6 +87,12 @@ export const ServicesSchema = z.object({
74
87
  "web-search": WebSearchServiceSchema.default(
75
88
  WebSearchServiceSchema.parse({}),
76
89
  ),
90
+ stt: SttServiceSchema.default({
91
+ mode: "your-own" as const,
92
+ provider: "deepgram" as const,
93
+ providers: {},
94
+ }),
95
+ tts: TtsServiceSchema.default(TtsServiceSchema.parse({})),
77
96
  "google-oauth": GoogleOAuthServiceSchema.default(
78
97
  GoogleOAuthServiceSchema.parse({}),
79
98
  ),
@@ -86,5 +105,30 @@ export const ServicesSchema = z.object({
86
105
  "github-oauth": GitHubOAuthServiceSchema.default(
87
106
  GitHubOAuthServiceSchema.parse({}),
88
107
  ),
108
+ "notion-oauth": NotionOAuthServiceSchema.default(
109
+ NotionOAuthServiceSchema.parse({}),
110
+ ),
111
+ "twitter-oauth": TwitterOAuthServiceSchema.default(
112
+ TwitterOAuthServiceSchema.parse({}),
113
+ ),
89
114
  });
90
115
  export type Services = z.infer<typeof ServicesSchema>;
116
+
117
+ /**
118
+ * Safely read the `mode` of a `services.*` entry.
119
+ *
120
+ * Most service entries (OAuth providers, inference, etc.) extend
121
+ * `BaseServiceSchema` and therefore carry a `mode: "managed" | "your-own"`
122
+ * field.
123
+ *
124
+ * Returns `undefined` when the requested service entry has no `mode` field,
125
+ * so callers can treat those entries as implicitly "your-own" without the
126
+ * compiler tripping on a union widened by non-BaseService members.
127
+ */
128
+ export function getServiceMode(
129
+ services: Services,
130
+ key: keyof Services,
131
+ ): ServiceMode | undefined {
132
+ const entry = services[key] as { mode?: ServiceMode };
133
+ return entry.mode;
134
+ }
@@ -0,0 +1,59 @@
1
+ import { z } from "zod";
2
+
3
+ /**
4
+ * Valid STT provider identifiers. New providers append here and register
5
+ * an adapter.
6
+ */
7
+ export const VALID_STT_PROVIDERS = [
8
+ "deepgram",
9
+ "google-gemini",
10
+ "openai-whisper",
11
+ ] as const;
12
+
13
+ /**
14
+ * Sparse provider config map under `services.stt.providers`.
15
+ *
16
+ * This is a forward-compatible record that accepts any provider ID as key
17
+ * with an object value. All provider entries — known (`openai-whisper`,
18
+ * `deepgram`, `google-gemini`) and unknown — are accepted with generic object
19
+ * validation. Adding a new provider ID does not require a migration to seed
20
+ * `services.stt.providers.<id>`.
21
+ *
22
+ * The map only holds entries the user has explicitly configured — it is
23
+ * NOT required to enumerate every known provider.
24
+ */
25
+ export const SttProvidersSchema = z.record(
26
+ z.string(),
27
+ z.record(z.string(), z.unknown()).default({}),
28
+ );
29
+ export type SttProviders = z.infer<typeof SttProvidersSchema>;
30
+
31
+ /**
32
+ * Canonical STT service configuration.
33
+ *
34
+ * `mode` is locked to `"your-own"` -- managed STT is not supported.
35
+ * Attempting to set `mode: "managed"` will fail schema validation.
36
+ */
37
+ export const SttServiceSchema = z
38
+ .object({
39
+ mode: z
40
+ .literal("your-own", {
41
+ error:
42
+ 'services.stt.mode must be "your-own" -- managed STT is not supported',
43
+ })
44
+ .default("your-own" as const)
45
+ .describe(
46
+ 'STT service mode -- only "your-own" is supported (managed STT is not available)',
47
+ ),
48
+ provider: z
49
+ .enum(VALID_STT_PROVIDERS, {
50
+ error: `services.stt.provider must be one of: ${VALID_STT_PROVIDERS.join(", ")}`,
51
+ })
52
+ .describe("Active STT provider used for speech-to-text transcription"),
53
+ providers: SttProvidersSchema.default({}),
54
+ })
55
+ .describe(
56
+ "Speech-to-text service configuration -- provider selection and per-provider settings",
57
+ );
58
+
59
+ export type SttService = z.infer<typeof SttServiceSchema>;
@@ -0,0 +1,230 @@
1
+ import { z } from "zod";
2
+
3
+ import { listCatalogProviderIds } from "../../tts/provider-catalog.js";
4
+ import type { TtsProviderId } from "../../tts/types.js";
5
+ import {
6
+ DEFAULT_ELEVENLABS_VOICE_ID,
7
+ VALID_CONVERSATION_TIMEOUTS,
8
+ } from "./elevenlabs.js";
9
+
10
+ /**
11
+ * Valid TTS provider identifiers derived from the canonical provider catalog.
12
+ *
13
+ * Adding a new TTS provider starts in `provider-catalog.ts` — the IDs flow
14
+ * here automatically.
15
+ */
16
+ export const VALID_TTS_PROVIDERS: readonly [string, ...string[]] =
17
+ listCatalogProviderIds() as [TtsProviderId, ...TtsProviderId[]];
18
+
19
+ /**
20
+ * Per-provider config schemas nested under `services.tts.providers.<id>`.
21
+ *
22
+ * Each provider's schema is the full provider-specific config (voice ID,
23
+ * model overrides, tuning params, etc.). These are identical to the
24
+ * legacy top-level schemas (`elevenlabs.*`, `fishAudio.*`) so that
25
+ * migration can copy values 1:1.
26
+ */
27
+ export const TtsElevenLabsProviderConfigSchema = z
28
+ .object({
29
+ voiceId: z
30
+ .string({
31
+ error: "services.tts.providers.elevenlabs.voiceId must be a string",
32
+ })
33
+ .transform((v) => v || DEFAULT_ELEVENLABS_VOICE_ID)
34
+ .default(DEFAULT_ELEVENLABS_VOICE_ID)
35
+ .describe("ElevenLabs voice ID for text-to-speech"),
36
+ voiceModelId: z
37
+ .string({
38
+ error:
39
+ "services.tts.providers.elevenlabs.voiceModelId must be a string",
40
+ })
41
+ .default("")
42
+ .describe(
43
+ "ElevenLabs model ID override (leave empty to use the default model)",
44
+ ),
45
+ speed: z
46
+ .number({
47
+ error: "services.tts.providers.elevenlabs.speed must be a number",
48
+ })
49
+ .min(0.7, "services.tts.providers.elevenlabs.speed must be >= 0.7")
50
+ .max(1.2, "services.tts.providers.elevenlabs.speed must be <= 1.2")
51
+ .default(1.0)
52
+ .describe(
53
+ "Speech playback speed multiplier (0.7 = slower, 1.2 = faster)",
54
+ ),
55
+ stability: z
56
+ .number({
57
+ error: "services.tts.providers.elevenlabs.stability must be a number",
58
+ })
59
+ .min(0, "services.tts.providers.elevenlabs.stability must be >= 0")
60
+ .max(1, "services.tts.providers.elevenlabs.stability must be <= 1")
61
+ .default(0.5)
62
+ .describe(
63
+ "Voice stability — higher values produce more consistent speech, lower values add expressiveness",
64
+ ),
65
+ similarityBoost: z
66
+ .number({
67
+ error:
68
+ "services.tts.providers.elevenlabs.similarityBoost must be a number",
69
+ })
70
+ .min(0, "services.tts.providers.elevenlabs.similarityBoost must be >= 0")
71
+ .max(1, "services.tts.providers.elevenlabs.similarityBoost must be <= 1")
72
+ .default(0.75)
73
+ .describe(
74
+ "How closely the output matches the original voice — higher values increase similarity",
75
+ ),
76
+ conversationTimeoutSeconds: z
77
+ .number({
78
+ error:
79
+ "services.tts.providers.elevenlabs.conversationTimeoutSeconds must be a number",
80
+ })
81
+ .refine(
82
+ (v) =>
83
+ VALID_CONVERSATION_TIMEOUTS.includes(
84
+ v as (typeof VALID_CONVERSATION_TIMEOUTS)[number],
85
+ ),
86
+ {
87
+ message: `services.tts.providers.elevenlabs.conversationTimeoutSeconds must be one of: ${VALID_CONVERSATION_TIMEOUTS.join(", ")}`,
88
+ },
89
+ )
90
+ .default(30)
91
+ .describe("Seconds of silence before voice conversation auto-ends"),
92
+ })
93
+ .describe("ElevenLabs provider configuration under services.tts");
94
+
95
+ export type TtsElevenLabsProviderConfig = z.infer<
96
+ typeof TtsElevenLabsProviderConfigSchema
97
+ >;
98
+
99
+ export const TtsFishAudioProviderConfigSchema = z
100
+ .object({
101
+ referenceId: z
102
+ .string({
103
+ error: "services.tts.providers.fish-audio.referenceId must be a string",
104
+ })
105
+ .default("")
106
+ .describe("Fish Audio voice/clone reference ID"),
107
+ chunkLength: z
108
+ .number({
109
+ error: "services.tts.providers.fish-audio.chunkLength must be a number",
110
+ })
111
+ .int("services.tts.providers.fish-audio.chunkLength must be an integer")
112
+ .min(100, "services.tts.providers.fish-audio.chunkLength must be >= 100")
113
+ .max(300, "services.tts.providers.fish-audio.chunkLength must be <= 300")
114
+ .default(200)
115
+ .describe("Text chunk size for streaming synthesis"),
116
+ format: z
117
+ .enum(["mp3", "wav", "opus"], {
118
+ error:
119
+ "services.tts.providers.fish-audio.format must be one of: mp3, wav, opus",
120
+ })
121
+ .default("mp3")
122
+ .describe("Output audio format"),
123
+ latency: z
124
+ .enum(["normal", "balanced"], {
125
+ error:
126
+ "services.tts.providers.fish-audio.latency must be one of: normal, balanced",
127
+ })
128
+ .default("normal")
129
+ .describe(
130
+ "Latency/quality tradeoff for Fish Audio S2 synthesis. 'normal' prioritizes lower latency; 'balanced' trades latency for higher quality.",
131
+ ),
132
+ speed: z
133
+ .number({
134
+ error: "services.tts.providers.fish-audio.speed must be a number",
135
+ })
136
+ .min(0.5, "services.tts.providers.fish-audio.speed must be >= 0.5")
137
+ .max(2.0, "services.tts.providers.fish-audio.speed must be <= 2.0")
138
+ .default(1.0)
139
+ .describe("Playback speed multiplier (0.5 = slower, 2.0 = faster)"),
140
+ })
141
+ .describe("Fish Audio provider configuration under services.tts");
142
+
143
+ export type TtsFishAudioProviderConfig = z.infer<
144
+ typeof TtsFishAudioProviderConfigSchema
145
+ >;
146
+
147
+ export const TtsDeepgramProviderConfigSchema = z
148
+ .object({
149
+ model: z
150
+ .string({
151
+ error: "services.tts.providers.deepgram.model must be a string",
152
+ })
153
+ .transform((v) => v || "aura-asteria-en")
154
+ .default("aura-asteria-en")
155
+ .describe("Deepgram TTS model identifier"),
156
+ format: z
157
+ .enum(["mp3", "wav", "opus"], {
158
+ error:
159
+ "services.tts.providers.deepgram.format must be one of: mp3, wav, opus",
160
+ })
161
+ .default("mp3")
162
+ .describe("Output audio format for call/runtime playback"),
163
+ })
164
+ .describe("Deepgram provider configuration under services.tts");
165
+
166
+ export type TtsDeepgramProviderConfig = z.infer<
167
+ typeof TtsDeepgramProviderConfigSchema
168
+ >;
169
+
170
+ export const TtsProvidersSchema = z.object({
171
+ elevenlabs: TtsElevenLabsProviderConfigSchema.default(
172
+ TtsElevenLabsProviderConfigSchema.parse({}),
173
+ ),
174
+ "fish-audio": TtsFishAudioProviderConfigSchema.default(
175
+ TtsFishAudioProviderConfigSchema.parse({}),
176
+ ),
177
+ deepgram: TtsDeepgramProviderConfigSchema.default(
178
+ TtsDeepgramProviderConfigSchema.parse({}),
179
+ ),
180
+ });
181
+ export type TtsProviders = z.infer<typeof TtsProvidersSchema>;
182
+
183
+ // ---------------------------------------------------------------------------
184
+ // Catalog-completeness guard
185
+ // ---------------------------------------------------------------------------
186
+ // Ensures every provider in the catalog has a corresponding key in
187
+ // TtsProvidersSchema. If a new provider is added to the catalog without a
188
+ // schema entry, this fires at module-load time so the oversight is caught
189
+ // immediately rather than at runtime when a user selects the provider.
190
+ // ---------------------------------------------------------------------------
191
+ const schemaKeys = new Set(Object.keys(TtsProvidersSchema.shape));
192
+ for (const id of VALID_TTS_PROVIDERS) {
193
+ if (!schemaKeys.has(id)) {
194
+ throw new Error(
195
+ `TTS provider "${id}" exists in the catalog but has no schema entry ` +
196
+ `in TtsProvidersSchema. Add a "services.tts.providers.${id}" schema.`,
197
+ );
198
+ }
199
+ }
200
+
201
+ /**
202
+ * Canonical TTS service configuration.
203
+ *
204
+ * `mode` is locked to `"your-own"` — managed TTS is not supported.
205
+ * Attempting to set `mode: "managed"` will fail schema validation.
206
+ */
207
+ export const TtsServiceSchema = z
208
+ .object({
209
+ mode: z
210
+ .literal("your-own", {
211
+ error:
212
+ 'services.tts.mode must be "your-own" — managed TTS is not supported',
213
+ })
214
+ .default("your-own" as const)
215
+ .describe(
216
+ 'TTS service mode — only "your-own" is supported (managed TTS is not available)',
217
+ ),
218
+ provider: z
219
+ .enum(VALID_TTS_PROVIDERS, {
220
+ error: `services.tts.provider must be one of: ${VALID_TTS_PROVIDERS.join(", ")}`,
221
+ })
222
+ .default("elevenlabs")
223
+ .describe("Active TTS provider used for speech synthesis"),
224
+ providers: TtsProvidersSchema.default(TtsProvidersSchema.parse({})),
225
+ })
226
+ .describe(
227
+ "Text-to-speech service configuration — provider selection and per-provider settings",
228
+ );
229
+
230
+ export type TtsService = z.infer<typeof TtsServiceSchema>;
@@ -0,0 +1,14 @@
1
+ import { z } from "zod";
2
+
3
+ export const UpdatesConfigSchema = z
4
+ .object({
5
+ enabled: z
6
+ .boolean({ error: "updates.enabled must be a boolean" })
7
+ .default(true)
8
+ .describe(
9
+ "Whether the release update bulletin (UPDATES.md) is materialized into the workspace on daemon startup",
10
+ ),
11
+ })
12
+ .describe("Release update bulletin configuration");
13
+
14
+ export type UpdatesConfig = z.infer<typeof UpdatesConfigSchema>;
@@ -784,6 +784,8 @@ export function loadSkillCatalog(
784
784
  toolManifest: detectToolManifest(directory),
785
785
  includes: parsed.includes,
786
786
  featureFlag: parsed.featureFlag,
787
+ activationHints: parsed.activationHints,
788
+ avoidWhen: parsed.avoidWhen,
787
789
  inlineCommandExpansions: parsed.inlineCommandExpansions,
788
790
  });
789
791
  } catch (err) {
@@ -879,6 +881,8 @@ export function loadSkillCatalog(
879
881
  toolManifest: detectToolManifest(directory),
880
882
  includes: parsed.includes,
881
883
  featureFlag: parsed.featureFlag,
884
+ activationHints: parsed.activationHints,
885
+ avoidWhen: parsed.avoidWhen,
882
886
  inlineCommandExpansions: parsed.inlineCommandExpansions,
883
887
  };
884
888
 
@@ -37,6 +37,10 @@ export type {
37
37
  SlackConfig,
38
38
  ThinkingConfig,
39
39
  TimeoutConfig,
40
+ TtsElevenLabsProviderConfig,
41
+ TtsFishAudioProviderConfig,
42
+ TtsProviders,
43
+ TtsService,
40
44
  UiConfig,
41
45
  WebSearchService,
42
46
  WorkspaceGitConfig,
@@ -1,4 +1,4 @@
1
- import { and, asc, desc, eq, like, sql } from "drizzle-orm";
1
+ import { and, asc, desc, eq, isNotNull, like, sql } from "drizzle-orm";
2
2
  import { v4 as uuid } from "uuid";
3
3
 
4
4
  import { getDb } from "../memory/db.js";
@@ -29,16 +29,27 @@ function escapeLike(value: string): string {
29
29
  }
30
30
 
31
31
  /**
32
- * Generate a collision-free slugified filename for a contact's per-user persona file.
33
- * Produces filenames like "alice.md", "alice-2.md", "alice-3.md", etc.
32
+ * Pure slug transform applied to a display name. No DB lookup, no collision
33
+ * handling callers that need a collision-free filename should use
34
+ * `generateUserFileSlug` instead. Exported so the migration classifier can
35
+ * recompute the expected base slug for a given display name.
34
36
  */
35
- export function generateUserFileSlug(displayName: string): string {
36
- const slug =
37
+ export function computeUserFileBaseSlug(displayName: string): string {
38
+ return (
37
39
  displayName
38
40
  .toLowerCase()
39
41
  .replace(/[^a-z0-9]+/g, "-")
40
42
  .replace(/^-+|-+$/g, "")
41
- .slice(0, 100) || "user";
43
+ .slice(0, 100) || "user"
44
+ );
45
+ }
46
+
47
+ /**
48
+ * Generate a collision-free slugified filename for a contact's per-user persona file.
49
+ * Produces filenames like "alice.md", "alice-2.md", "alice-3.md", etc.
50
+ */
51
+ export function generateUserFileSlug(displayName: string): string {
52
+ const slug = computeUserFileBaseSlug(displayName);
42
53
 
43
54
  const db = getDb();
44
55
  const rows = db
@@ -273,10 +284,27 @@ export function upsertContact(params: {
273
284
 
274
285
  // Create new contact
275
286
  contactId = contactId ?? uuid();
276
- const userFileValue =
277
- params.userFile !== undefined
278
- ? params.userFile
279
- : generateUserFileSlug(params.displayName);
287
+ // Sibling contacts sharing a principal_id must share a user_file so every
288
+ // channel for one principal resolves to the same persona + journal slug.
289
+ let resolvedUserFile: string | null;
290
+ if (params.userFile !== undefined) {
291
+ resolvedUserFile = params.userFile;
292
+ } else if (params.principalId) {
293
+ const sibling = db
294
+ .select({ userFile: contacts.userFile })
295
+ .from(contacts)
296
+ .where(
297
+ and(
298
+ eq(contacts.principalId, params.principalId),
299
+ isNotNull(contacts.userFile),
300
+ ),
301
+ )
302
+ .get();
303
+ resolvedUserFile =
304
+ sibling?.userFile ?? generateUserFileSlug(params.displayName);
305
+ } else {
306
+ resolvedUserFile = generateUserFileSlug(params.displayName);
307
+ }
280
308
  db.insert(contacts)
281
309
  .values({
282
310
  id: contactId,
@@ -285,7 +313,7 @@ export function upsertContact(params: {
285
313
  role: params.role ?? "contact",
286
314
  contactType: params.contactType ?? "human",
287
315
  principalId: params.principalId ?? null,
288
- userFile: userFileValue ?? null,
316
+ userFile: resolvedUserFile,
289
317
  createdAt: now,
290
318
  updatedAt: now,
291
319
  })
@@ -811,6 +839,23 @@ export function findContactChannel(params: {
811
839
  return null;
812
840
  }
813
841
 
842
+ /**
843
+ * Find the guardian contact regardless of channel.
844
+ * Returns the first contact with role='guardian', or null if none exists.
845
+ */
846
+ export function findGuardianContact(): ContactWithChannels | null {
847
+ const db = getDb();
848
+ const row = db
849
+ .select()
850
+ .from(contacts)
851
+ .where(eq(contacts.role, "guardian"))
852
+ .orderBy(asc(contacts.createdAt))
853
+ .limit(1)
854
+ .get();
855
+ if (!row) return null;
856
+ return withChannels(parseContact(row));
857
+ }
858
+
814
859
  /**
815
860
  * Find the guardian contact and their specific channel entry for a given channel type.
816
861
  * This is the contacts-based equivalent of getGuardianBinding(assistantId, channel).
@@ -8,6 +8,8 @@
8
8
 
9
9
  import type { ChannelId } from "../channels/types.js";
10
10
  import type { GuardianBinding } from "../memory/channel-verification-sessions.js";
11
+ import { clearCache as clearTrustCache } from "../permissions/trust-store.js";
12
+ import { ensureGuardianPersonaFile } from "../prompts/persona-resolver.js";
11
13
  import { canonicalizeInboundIdentity } from "../util/canonicalize-identity.js";
12
14
  import { getLogger } from "../util/logger.js";
13
15
  import { emitContactChange } from "./contact-events.js";
@@ -76,7 +78,7 @@ export function createGuardianBinding(params: {
76
78
  parseDisplayNameFromMetadata(params.metadataJson) ??
77
79
  params.guardianExternalUserId;
78
80
 
79
- upsertContact({
81
+ const contact = upsertContact({
80
82
  displayName,
81
83
  role: "guardian",
82
84
  notes: "guardian",
@@ -94,6 +96,35 @@ export function createGuardianBinding(params: {
94
96
  ],
95
97
  });
96
98
 
99
+ // Seed the per-user persona file so downstream readers (journaling,
100
+ // persona resolution) can rely on `users/<slug>.md` existing on disk.
101
+ // Idempotent: pre-existing customized files are preserved.
102
+ //
103
+ // Seeding is restricted to the guardian-creation path only — it must
104
+ // NOT run from inbound-message upsertContactChannel calls, since the
105
+ // `users/` directory watcher would fire on every new contact and
106
+ // evict live conversations.
107
+ if (contact.userFile) {
108
+ // Tolerate filesystem failures (read-only or full workspace) so a
109
+ // disk error doesn't leave the DB commit orphaned. The persona file
110
+ // can be reseeded later; failing the binding here would be worse.
111
+ try {
112
+ ensureGuardianPersonaFile(contact.userFile);
113
+ } catch (err) {
114
+ log.warn(
115
+ { err, userFile: contact.userFile },
116
+ "failed to seed guardian persona file; continuing",
117
+ );
118
+ }
119
+ // Invalidate the trust rule cache so the dynamic guardian-persona
120
+ // auto-allow rules from `permissions/defaults.ts` are backfilled on
121
+ // the next `getRules()` call. Without this, guardians created at
122
+ // runtime (self-heal paths, first-message-seeds-guardian) wouldn't
123
+ // get their auto-allow rule until the daemon restarts, and the
124
+ // model would prompt on its first `file_edit users/<slug>.md`.
125
+ clearTrustCache();
126
+ }
127
+
97
128
  const now = Date.now();
98
129
  const result: GuardianBinding = {
99
130
  id: `contact-binding-${params.channel}`,
@@ -210,6 +241,12 @@ export function upsertContactChannel(params: {
210
241
  reassignConflictingChannels: !!params.contactId,
211
242
  });
212
243
 
244
+ // NOTE: We intentionally do NOT seed `users/<slug>.md` here. This is the
245
+ // inbound-message hot path — every new contact (Slack, phone, email, etc)
246
+ // would otherwise fire the `users/` directory watcher in
247
+ // config-watcher.ts and evict live conversations. Persona-file seeding
248
+ // is the sole responsibility of `createGuardianBinding`.
249
+
213
250
  const contactResult = findContactChannel({
214
251
  channelType: params.sourceChannel,
215
252
  externalUserId: canonicalId ?? undefined,
@@ -8,6 +8,7 @@ import type {
8
8
  ToolResultContent,
9
9
  ToolUseContent,
10
10
  } from "../providers/types.js";
11
+ import { safeStringSlice } from "../util/unicode.js";
11
12
 
12
13
  /** Minimum content length (chars) before a tool result is eligible for truncation. ~2000 tokens at 4 chars/token. */
13
14
  export const THRESHOLD_CHARS = 8_000;
@@ -43,8 +44,8 @@ export function buildTruncatedContent(
43
44
  filePath: string,
44
45
  ): string {
45
46
  const half = Math.floor(TARGET_CHARS / 2);
46
- const prefix = original.slice(0, half);
47
- const suffix = original.slice(-half);
47
+ const prefix = safeStringSlice(original, 0, half);
48
+ const suffix = safeStringSlice(original, original.length - half, original.length);
48
49
  const omittedChars = original.length - TARGET_CHARS;
49
50
  const estimatedTokens = Math.round(omittedChars / 4);
50
51
  return `${prefix}\n\n...(${estimatedTokens} tokens omitted ${TRUNCATION_MARKER} ${filePath})\n\n${suffix}`;
@@ -3,6 +3,7 @@ import type {
3
3
  Message,
4
4
  ToolResultContent,
5
5
  } from "../providers/types.js";
6
+ import { safeStringSlice } from "../util/unicode.js";
6
7
 
7
8
  /**
8
9
  * Maximum share of the context window that a single tool result may occupy.
@@ -53,7 +54,7 @@ export function truncateToolResultText(text: string, maxChars: number): string {
53
54
  return text;
54
55
  }
55
56
 
56
- return text.slice(0, sliceEnd) + TRUNCATION_SUFFIX;
57
+ return safeStringSlice(text, 0, sliceEnd) + TRUNCATION_SUFFIX;
57
58
  }
58
59
 
59
60
  /**