@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
@@ -10,11 +10,12 @@ import {
10
10
  readdirSync,
11
11
  watch,
12
12
  } from "node:fs";
13
- import { homedir } from "node:os";
14
13
  import { join } from "node:path";
15
14
 
16
15
  import { clearFeatureFlagOverridesCache } from "../config/assistant-feature-flags.js";
17
16
  import { getConfig, invalidateConfigCache } from "../config/loader.js";
17
+ import type { MemoryCleanupConfig } from "../config/schemas/memory-lifecycle.js";
18
+ import { resetCleanupScheduleThrottle } from "../memory/cleanup-schedule-state.js";
18
19
  import { clearEmbeddingBackendCache } from "../memory/embedding-backend.js";
19
20
  import { clearCache as clearTrustCache } from "../permissions/trust-store.js";
20
21
  import { initializeProviders } from "../providers/registry.js";
@@ -30,6 +31,7 @@ import { getLogger } from "../util/logger.js";
30
31
  import {
31
32
  AVATAR_IMAGE_FILENAME,
32
33
  getAvatarDir,
34
+ getProtectedDir,
33
35
  getSignalsDir,
34
36
  getSoundsDir,
35
37
  getWorkspaceDir,
@@ -38,6 +40,17 @@ import {
38
40
 
39
41
  const log = getLogger("config-watcher");
40
42
 
43
+ /**
44
+ * Attach a resilient error handler to an FSWatcher so that async errors
45
+ * (e.g. ENXIO when a Unix socket file like `gateway.sock` appears in a
46
+ * watched directory) are logged instead of crashing the process.
47
+ */
48
+ function attachWatcherErrorHandler(watcher: FSWatcher, dir: string): void {
49
+ watcher.on("error", (err) => {
50
+ log.warn({ err, dir }, "FSWatcher error (non-fatal, continuing)");
51
+ });
52
+ }
53
+
41
54
  export class ConfigWatcher {
42
55
  private watchers: FSWatcher[] = [];
43
56
  private debounceTimers = new DebouncerMap({
@@ -94,6 +107,7 @@ export class ConfigWatcher {
94
107
  * Returns true if config actually changed.
95
108
  */
96
109
  async refreshConfigFromSources(): Promise<boolean> {
110
+ const prevCleanup = safeGetCleanupConfig();
97
111
  invalidateConfigCache();
98
112
  const config = getConfig();
99
113
  const fingerprint = this.configFingerprint(config);
@@ -102,6 +116,13 @@ export class ConfigWatcher {
102
116
  }
103
117
  clearTrustCache();
104
118
  clearEmbeddingBackendCache();
119
+ // If cleanup retention settings changed, reset the cleanup scheduler
120
+ // throttle so the next worker tick re-enqueues jobs with the new values
121
+ // instead of waiting out the remaining enqueueIntervalMs (default 6h).
122
+ const nextCleanup = config.memory?.cleanup;
123
+ if (cleanupSettingsChanged(prevCleanup, nextCleanup)) {
124
+ resetCleanupScheduleThrottle();
125
+ }
105
126
  const isFirstInit = this.lastFingerprint === "";
106
127
  await initializeProviders(config);
107
128
  this.lastFingerprint = fingerprint;
@@ -151,7 +172,6 @@ export class ConfigWatcher {
151
172
  onConversationEvict();
152
173
  onIdentityChanged?.();
153
174
  },
154
- "USER.md": () => onConversationEvict(),
155
175
  "UPDATES.md": () => onConversationEvict(),
156
176
  };
157
177
 
@@ -170,6 +190,7 @@ export class ConfigWatcher {
170
190
  handlers[file]();
171
191
  });
172
192
  });
193
+ attachWatcherErrorHandler(watcher, dir);
173
194
  this.watchers.push(watcher);
174
195
  log.info({ dir }, `Watching ${label}`);
175
196
  } catch (err) {
@@ -195,6 +216,7 @@ export class ConfigWatcher {
195
216
 
196
217
  this.startFeatureFlagsWatcher(onFeatureFlagsChanged);
197
218
  this.startSignalsWatcher();
219
+ this.startUsersWatcher(onConversationEvict);
198
220
  this.startSkillsWatchers(onConversationEvict);
199
221
  }
200
222
 
@@ -227,6 +249,7 @@ export class ConfigWatcher {
227
249
  onSoundsConfigChanged();
228
250
  });
229
251
  });
252
+ attachWatcherErrorHandler(watcher, soundsDir);
230
253
  this.watchers.push(watcher);
231
254
  log.info({ dir: soundsDir }, "Watching sounds directory for changes");
232
255
  } catch (err) {
@@ -237,6 +260,40 @@ export class ConfigWatcher {
237
260
  }
238
261
  }
239
262
 
263
+ private startUsersWatcher(onConversationEvict: () => void): void {
264
+ const usersDir = join(getWorkspaceDir(), "users");
265
+ try {
266
+ if (!existsSync(usersDir)) {
267
+ mkdirSync(usersDir, { recursive: true });
268
+ }
269
+ } catch {
270
+ // If we can't create it, watching will also fail — handled below.
271
+ }
272
+
273
+ try {
274
+ const watcher = watch(usersDir, (_eventType, filename) => {
275
+ if (!filename) return;
276
+ const file = String(filename);
277
+ if (!file.endsWith(".md")) return;
278
+ this.debounceTimers.schedule(`file:users/${file}`, () => {
279
+ log.info({ file }, "Users persona file changed, reloading");
280
+ onConversationEvict();
281
+ });
282
+ });
283
+ attachWatcherErrorHandler(watcher, usersDir);
284
+ this.watchers.push(watcher);
285
+ log.info(
286
+ { dir: usersDir },
287
+ "Watching users directory for persona changes",
288
+ );
289
+ } catch (err) {
290
+ log.warn(
291
+ { err, dir: usersDir },
292
+ "Failed to watch users directory. Persona file changes will require a restart.",
293
+ );
294
+ }
295
+ }
296
+
240
297
  private startAvatarWatcher(onAvatarChanged: () => void): void {
241
298
  const avatarDir = getAvatarDir();
242
299
  try {
@@ -259,6 +316,7 @@ export class ConfigWatcher {
259
316
  onAvatarChanged();
260
317
  });
261
318
  });
319
+ attachWatcherErrorHandler(watcher, avatarDir);
262
320
  this.watchers.push(watcher);
263
321
  log.info({ dir: avatarDir }, "Watching avatar directory for changes");
264
322
  } catch (err) {
@@ -270,9 +328,7 @@ export class ConfigWatcher {
270
328
  }
271
329
 
272
330
  private startFeatureFlagsWatcher(onFeatureFlagsChanged?: () => void): void {
273
- const protectedDir = process.env.GATEWAY_SECURITY_DIR
274
- ? process.env.GATEWAY_SECURITY_DIR
275
- : join(homedir(), ".vellum", "protected");
331
+ const protectedDir = process.env.GATEWAY_SECURITY_DIR || getProtectedDir();
276
332
 
277
333
  try {
278
334
  if (!existsSync(protectedDir)) {
@@ -305,6 +361,7 @@ export class ConfigWatcher {
305
361
  500,
306
362
  );
307
363
  });
364
+ attachWatcherErrorHandler(watcher, protectedDir);
308
365
  this.watchers.push(watcher);
309
366
  log.info(
310
367
  { dir: protectedDir },
@@ -367,6 +424,7 @@ export class ConfigWatcher {
367
424
  }
368
425
  }
369
426
  });
427
+ attachWatcherErrorHandler(watcher, signalsDir);
370
428
  this.watchers.push(watcher);
371
429
  log.info({ dir: signalsDir }, "Watching signals directory");
372
430
  } catch (err) {
@@ -396,6 +454,7 @@ export class ConfigWatcher {
396
454
  scheduleSkillsReload(filename ? String(filename) : "(unknown)");
397
455
  },
398
456
  );
457
+ attachWatcherErrorHandler(recursiveWatcher, skillsDir);
399
458
  this.watchers.push(recursiveWatcher);
400
459
  log.info({ dir: skillsDir }, "Watching skills directory recursively");
401
460
  return;
@@ -416,6 +475,7 @@ export class ConfigWatcher {
416
475
  const watcher = watch(dirPath, (_eventType, filename) => {
417
476
  onChange(filename ? String(filename) : "(unknown)");
418
477
  });
478
+ attachWatcherErrorHandler(watcher, dirPath);
419
479
  this.watchers.push(watcher);
420
480
  return watcher;
421
481
  } catch (err) {
@@ -481,3 +541,37 @@ export class ConfigWatcher {
481
541
  );
482
542
  }
483
543
  }
544
+
545
+ /**
546
+ * Snapshot the current cleanup config so we can compare it against the
547
+ * post-reload value. Tolerant of config-load failures — if the config can't
548
+ * be read (e.g. first-load), returns undefined so the comparison below
549
+ * treats it as "no previous value".
550
+ */
551
+ function safeGetCleanupConfig(): MemoryCleanupConfig | undefined {
552
+ try {
553
+ return getConfig().memory?.cleanup;
554
+ } catch {
555
+ return undefined;
556
+ }
557
+ }
558
+
559
+ /**
560
+ * Return true if any cleanup field the user can change via the UI differs
561
+ * between the previous and next config snapshots. Used to decide whether to
562
+ * reset the cleanup-scheduler throttle after a config reload so retention
563
+ * changes take effect immediately instead of waiting up to 6 hours.
564
+ *
565
+ * Exported for unit testing.
566
+ */
567
+ export function cleanupSettingsChanged(
568
+ prev: MemoryCleanupConfig | undefined,
569
+ next: MemoryCleanupConfig | undefined,
570
+ ): boolean {
571
+ if (!prev || !next) return false;
572
+ return (
573
+ prev.llmRequestLogRetentionMs !== next.llmRequestLogRetentionMs ||
574
+ prev.conversationRetentionDays !== next.conversationRetentionDays ||
575
+ prev.enabled !== next.enabled
576
+ );
577
+ }
@@ -64,6 +64,8 @@ export interface EventHandlerState {
64
64
  exchangeCacheCreationInputTokens: number;
65
65
  exchangeCacheReadInputTokens: number;
66
66
  exchangeOutputTokens: number;
67
+ /** Input tokens from the most recent LLM API call (overwritten, not accumulated). */
68
+ lastCallInputTokens: number;
67
69
  /** Number of actual LLM API calls within this exchange. */
68
70
  exchangeLlmCallCount: number;
69
71
  readonly exchangeRawResponses: unknown[];
@@ -135,6 +137,7 @@ export function createEventHandlerState(): EventHandlerState {
135
137
  exchangeCacheCreationInputTokens: 0,
136
138
  exchangeCacheReadInputTokens: 0,
137
139
  exchangeOutputTokens: 0,
140
+ lastCallInputTokens: 0,
138
141
  exchangeLlmCallCount: 0,
139
142
  exchangeRawResponses: [],
140
143
  model: "",
@@ -590,6 +593,7 @@ function annotatePersistedAssistantMessage(
590
593
  data: surface.data,
591
594
  actions: surface.actions,
592
595
  display: surface.display,
596
+ ...(surface.persistent ? { persistent: true } : {}),
593
597
  } as unknown as ContentBlock);
594
598
  }
595
599
  modified = true;
@@ -759,6 +763,7 @@ export async function handleMessageComplete(
759
763
  data: surface.data,
760
764
  actions: surface.actions,
761
765
  display: surface.display,
766
+ ...(surface.persistent ? { persistent: true } : {}),
762
767
  } as unknown as ContentBlock);
763
768
  }
764
769
 
@@ -831,6 +836,7 @@ export function handleUsage(
831
836
  state.exchangeProviderName = providerName;
832
837
  state.exchangeLlmCallCount += 1;
833
838
  state.exchangeInputTokens += event.inputTokens;
839
+ state.lastCallInputTokens = event.inputTokens;
834
840
  state.exchangeCacheCreationInputTokens += event.cacheCreationInputTokens ?? 0;
835
841
  state.exchangeCacheReadInputTokens += event.cacheReadInputTokens ?? 0;
836
842
  state.exchangeOutputTokens += event.outputTokens;
@@ -24,10 +24,14 @@ import type {
24
24
  } from "../channels/types.js";
25
25
  import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
26
26
  import { getConfig } from "../config/loader.js";
27
- import { derefToolResultReReads,postTurnTruncateToolResults } from "../context/post-turn-tool-result-truncation.js";
27
+ import {
28
+ derefToolResultReReads,
29
+ postTurnTruncateToolResults,
30
+ } from "../context/post-turn-tool-result-truncation.js";
28
31
  import { estimatePromptTokens } from "../context/token-estimator.js";
29
32
  import type { ContextWindowManager } from "../context/window-manager.js";
30
33
  import type { ToolProfiler } from "../events/tool-profiling-listener.js";
34
+ import { writeRelationshipState } from "../home/relationship-state-writer.js";
31
35
  import { getHookManager } from "../hooks/manager.js";
32
36
  import {
33
37
  clearSentryConversationContext,
@@ -35,12 +39,15 @@ import {
35
39
  } from "../instrument.js";
36
40
  import { commitAppTurnChanges } from "../memory/app-git-service.js";
37
41
  import { getApp, listAppFiles, resolveAppDir } from "../memory/app-store.js";
42
+ import { enqueueAutoAnalysisOnCompaction } from "../memory/auto-analysis-enqueue.js";
38
43
  import {
39
44
  addMessage,
40
45
  deleteMessageById,
41
46
  getConversation,
42
47
  getConversationOriginChannel,
43
48
  getConversationOriginInterface,
49
+ getLastUserTimestampBefore,
50
+ getMessageById,
44
51
  provenanceFromTrustContext,
45
52
  updateConversationContextWindow,
46
53
  updateConversationTitle,
@@ -64,6 +71,7 @@ import { DAEMON_INTERNAL_ASSISTANT_ID } from "../runtime/assistant-scope.js";
64
71
  import { getSubagentManager } from "../subagent/index.js";
65
72
  import type { UsageActor } from "../usage/actors.js";
66
73
  import { getLogger } from "../util/logger.js";
74
+ import { timeAgo } from "../util/time.js";
67
75
  import { truncate } from "../util/truncate.js";
68
76
  import { getWorkspaceGitService } from "../workspace/git-service.js";
69
77
  import { commitTurnChanges } from "../workspace/turn-commit.js";
@@ -234,6 +242,7 @@ export interface AgentLoopConversationContext {
234
242
  data: SurfaceData;
235
243
  actions?: Array<{ id: string; label: string; style?: string }>;
236
244
  display?: string;
245
+ persistent?: boolean;
237
246
  }>;
238
247
 
239
248
  workingDir: string;
@@ -246,6 +255,8 @@ export interface AgentLoopConversationContext {
246
255
  currentTurnChannelCapabilities?: ChannelCapabilities;
247
256
  commandIntent?: { type: string; payload?: string; languageCode?: string };
248
257
  trustContext?: TrustContext;
258
+ /** Task-run scope for the current turn. Cleared at turn end so queued/drained turns don't inherit it. */
259
+ taskRunId?: string;
249
260
  assistantId?: string;
250
261
  voiceCallControlPrompt?: string;
251
262
  transportHints?: string[];
@@ -541,6 +552,13 @@ export async function runAgentLoopImpl(
541
552
  compacted.summaryText,
542
553
  ctx.contextCompactedMessageCount,
543
554
  );
555
+ // Fire auto-analysis on compaction so the reflective agent can
556
+ // crystallize anything worth remembering before the context window
557
+ // narrows further.
558
+ enqueueAutoAnalysisOnCompaction(
559
+ ctx.conversationId,
560
+ ctx.trustContext?.trustClass,
561
+ );
544
562
  onEvent({
545
563
  type: "context_compacted",
546
564
  previousEstimatedInputTokens: compacted.previousEstimatedInputTokens,
@@ -773,14 +791,35 @@ export async function runAgentLoopImpl(
773
791
  const isGuardian =
774
792
  resolvedInboundActorContext?.trustClass === "guardian" ||
775
793
  !resolvedInboundActorContext;
794
+
795
+ // Surface long gaps between user messages so the model can acknowledge
796
+ // the absence naturally. Gated at >12h to avoid noisy injection during
797
+ // normal back-and-forth turns.
798
+ const TWELVE_HOURS_MS = 12 * 60 * 60 * 1000;
799
+ let timeSinceLastMessage: string | null = null;
800
+ const currentUserMessage = getMessageById(userMessageId);
801
+ if (currentUserMessage) {
802
+ const prevUserTs = getLastUserTimestampBefore(
803
+ ctx.conversationId,
804
+ currentUserMessage.createdAt,
805
+ );
806
+ if (
807
+ prevUserTs > 0 &&
808
+ currentUserMessage.createdAt - prevUserTs > TWELVE_HOURS_MS
809
+ ) {
810
+ timeSinceLastMessage = timeAgo(prevUserTs);
811
+ }
812
+ }
813
+
776
814
  const unifiedTurnContextStr = buildUnifiedTurnContextBlock(
777
815
  isGuardian
778
- ? { timestamp, interfaceName, channelName }
816
+ ? { timestamp, interfaceName, channelName, timeSinceLastMessage }
779
817
  : {
780
818
  timestamp,
781
819
  interfaceName,
782
820
  channelName,
783
821
  actorContext: resolvedInboundActorContext,
822
+ timeSinceLastMessage,
784
823
  },
785
824
  );
786
825
 
@@ -908,6 +947,11 @@ export async function runAgentLoopImpl(
908
947
  step.compactionResult.summaryText,
909
948
  ctx.contextCompactedMessageCount,
910
949
  );
950
+ // Fire auto-analysis on compaction — see forceCompact() for rationale.
951
+ enqueueAutoAnalysisOnCompaction(
952
+ ctx.conversationId,
953
+ ctx.trustContext?.trustClass,
954
+ );
911
955
  onEvent({
912
956
  type: "context_compacted",
913
957
  previousEstimatedInputTokens:
@@ -945,8 +989,12 @@ export async function runAgentLoopImpl(
945
989
  // value from injectionOpts to avoid duplicate injection.
946
990
  runMessages = applyRuntimeInjections(ctx.messages, {
947
991
  ...injectionOpts,
948
- ...(step.compactionResult?.compacted && { pkbContext: currentPkbContent }),
949
- ...(step.compactionResult?.compacted && { nowScratchpad: currentNowContent }),
992
+ ...(step.compactionResult?.compacted && {
993
+ pkbContext: currentPkbContent,
994
+ }),
995
+ ...(step.compactionResult?.compacted && {
996
+ nowScratchpad: currentNowContent,
997
+ }),
950
998
  workspaceTopLevelContext: shouldInjectWorkspace
951
999
  ? ctx.workspaceTopLevelContext
952
1000
  : null,
@@ -1108,6 +1156,11 @@ export async function runAgentLoopImpl(
1108
1156
  midLoopCompact.summaryText,
1109
1157
  ctx.contextCompactedMessageCount,
1110
1158
  );
1159
+ // Fire auto-analysis on compaction — see forceCompact() for rationale.
1160
+ enqueueAutoAnalysisOnCompaction(
1161
+ ctx.conversationId,
1162
+ ctx.trustContext?.trustClass,
1163
+ );
1111
1164
  onEvent({
1112
1165
  type: "context_compacted",
1113
1166
  previousEstimatedInputTokens:
@@ -1151,8 +1204,7 @@ export async function runAgentLoopImpl(
1151
1204
  mode: currentInjectionMode,
1152
1205
  });
1153
1206
  if (isTrustedActor && currentInjectionMode !== "minimal") {
1154
- const memResult = ctx.graphMemory.reinjectCachedMemory(runMessages);
1155
- runMessages = memResult.runMessages;
1207
+ ctx.graphMemory.retrackCachedNodes();
1156
1208
  }
1157
1209
  preRepairMessages = runMessages;
1158
1210
  preRunHistoryLength = runMessages.length;
@@ -1326,6 +1378,11 @@ export async function runAgentLoopImpl(
1326
1378
  step.compactionResult.summaryText,
1327
1379
  ctx.contextCompactedMessageCount,
1328
1380
  );
1381
+ // Fire auto-analysis on compaction — see forceCompact() for rationale.
1382
+ enqueueAutoAnalysisOnCompaction(
1383
+ ctx.conversationId,
1384
+ ctx.trustContext?.trustClass,
1385
+ );
1329
1386
  onEvent({
1330
1387
  type: "context_compacted",
1331
1388
  previousEstimatedInputTokens:
@@ -1370,8 +1427,7 @@ export async function runAgentLoopImpl(
1370
1427
  mode: currentInjectionMode,
1371
1428
  });
1372
1429
  if (isTrustedActor && currentInjectionMode !== "minimal") {
1373
- const memResult = ctx.graphMemory.reinjectCachedMemory(runMessages);
1374
- runMessages = memResult.runMessages;
1430
+ ctx.graphMemory.retrackCachedNodes();
1375
1431
  }
1376
1432
  preRepairMessages = runMessages;
1377
1433
  preRunHistoryLength = runMessages.length;
@@ -1450,6 +1506,11 @@ export async function runAgentLoopImpl(
1450
1506
  emergencyCompact.summaryText,
1451
1507
  ctx.contextCompactedMessageCount,
1452
1508
  );
1509
+ // Fire auto-analysis on compaction — see forceCompact() for rationale.
1510
+ enqueueAutoAnalysisOnCompaction(
1511
+ ctx.conversationId,
1512
+ ctx.trustContext?.trustClass,
1513
+ );
1453
1514
  onEvent({
1454
1515
  type: "context_compacted",
1455
1516
  previousEstimatedInputTokens:
@@ -1493,9 +1554,7 @@ export async function runAgentLoopImpl(
1493
1554
  mode: currentInjectionMode,
1494
1555
  });
1495
1556
  if (isTrustedActor && currentInjectionMode !== "minimal") {
1496
- const memResult =
1497
- ctx.graphMemory.reinjectCachedMemory(runMessages);
1498
- runMessages = memResult.runMessages;
1557
+ ctx.graphMemory.retrackCachedNodes();
1499
1558
  }
1500
1559
  preRepairMessages = runMessages;
1501
1560
  preRunHistoryLength = runMessages.length;
@@ -1570,6 +1629,11 @@ export async function runAgentLoopImpl(
1570
1629
  emergencyCompact.summaryText,
1571
1630
  ctx.contextCompactedMessageCount,
1572
1631
  );
1632
+ // Fire auto-analysis on compaction — see forceCompact() for rationale.
1633
+ enqueueAutoAnalysisOnCompaction(
1634
+ ctx.conversationId,
1635
+ ctx.trustContext?.trustClass,
1636
+ );
1573
1637
  onEvent({
1574
1638
  type: "context_compacted",
1575
1639
  previousEstimatedInputTokens:
@@ -1613,8 +1677,7 @@ export async function runAgentLoopImpl(
1613
1677
  mode: currentInjectionMode,
1614
1678
  });
1615
1679
  if (isTrustedActor && currentInjectionMode !== "minimal") {
1616
- const memResult = ctx.graphMemory.reinjectCachedMemory(runMessages);
1617
- runMessages = memResult.runMessages;
1680
+ ctx.graphMemory.retrackCachedNodes();
1618
1681
  }
1619
1682
  preRepairMessages = runMessages;
1620
1683
  preRunHistoryLength = runMessages.length;
@@ -1753,9 +1816,16 @@ export async function runAgentLoopImpl(
1753
1816
  try {
1754
1817
  const conv = getConversation(ctx.conversationId);
1755
1818
  if (conv) {
1756
- const convDir = getResolvedConversationDirPath(ctx.conversationId, conv.createdAt);
1757
- const { messages: derefMessages, dereferencedCount } = derefToolResultReReads(restoredHistory);
1758
- const { messages: truncatedMessages, truncatedCount } = postTurnTruncateToolResults(derefMessages, { conversationDir: convDir });
1819
+ const convDir = getResolvedConversationDirPath(
1820
+ ctx.conversationId,
1821
+ conv.createdAt,
1822
+ );
1823
+ const { messages: derefMessages, dereferencedCount } =
1824
+ derefToolResultReReads(restoredHistory);
1825
+ const { messages: truncatedMessages, truncatedCount } =
1826
+ postTurnTruncateToolResults(derefMessages, {
1827
+ conversationDir: convDir,
1828
+ });
1759
1829
  if (truncatedCount > 0 || dereferencedCount > 0) {
1760
1830
  rlog.info(
1761
1831
  { truncatedCount, dereferencedCount },
@@ -1765,16 +1835,13 @@ export async function runAgentLoopImpl(
1765
1835
  restoredHistory = truncatedMessages;
1766
1836
  }
1767
1837
  } catch (err) {
1768
- rlog.warn({ err }, "Post-turn tool result truncation failed (non-fatal)");
1838
+ rlog.warn(
1839
+ { err },
1840
+ "Post-turn tool result truncation failed (non-fatal)",
1841
+ );
1769
1842
  }
1770
1843
  }
1771
1844
 
1772
- const postLoopContextEstimate = estimatePromptTokens(
1773
- restoredHistory,
1774
- ctx.systemPrompt,
1775
- { providerName: ctx.provider.name, toolTokenBudget },
1776
- );
1777
-
1778
1845
  // Persist injections in history: runtime-injected context stays on
1779
1846
  // historical user messages so the conversation prefix is stable for
1780
1847
  // Anthropic's prefix caching. Stripping only happens during
@@ -1795,7 +1862,7 @@ export async function runAgentLoopImpl(
1795
1862
  state.exchangeProviderName,
1796
1863
  state.exchangeLlmCallCount,
1797
1864
  {
1798
- tokens: postLoopContextEstimate,
1865
+ tokens: state.lastCallInputTokens,
1799
1866
  maxTokens: config.contextWindow.maxInputTokens,
1800
1867
  },
1801
1868
  );
@@ -2014,6 +2081,12 @@ export async function runAgentLoopImpl(
2014
2081
 
2015
2082
  // Commit app changes (fire-and-forget — apps repo is separate from workspace)
2016
2083
  void commitAppTurnChanges(ctx.conversationId, ctx.turnCount);
2084
+
2085
+ // Recompute relationship-state.json at turn boundary (fire-and-forget).
2086
+ // The writer swallows its own errors, but we still guard with catch()
2087
+ // here so a regression in the writer can never bubble out of the
2088
+ // agent loop and reject an otherwise-complete turn.
2089
+ void writeRelationshipState().catch(() => {});
2017
2090
  }
2018
2091
 
2019
2092
  ctx.profiler.emitSummary(ctx.traceEmitter, reqId);
@@ -2029,6 +2102,10 @@ export async function runAgentLoopImpl(
2029
2102
  // Channel command intents (e.g. Telegram /start) are single-turn metadata.
2030
2103
  // Clear at turn end so they never leak into subsequent unrelated messages.
2031
2104
  ctx.commandIntent = undefined;
2105
+ // taskRunId scopes ephemeral task-run permissions to a single turn. Clear
2106
+ // before drainQueue so queued/drained turns on a reused conversation can't
2107
+ // inherit stale in-task-run scope from the turn that just finished.
2108
+ ctx.taskRunId = undefined;
2032
2109
 
2033
2110
  // Consolidation deferred to compaction: keeping assistant + tool_result
2034
2111
  // messages unconsolidated preserves the exact message structure sent to
@@ -1,4 +1,5 @@
1
1
  import { getProviderRoutingSource } from "../providers/registry.js";
2
+ import { isAbortReason } from "../util/abort-reasons.js";
2
3
  import { ProviderError, ProviderNotConfiguredError } from "../util/errors.js";
3
4
  import type {
4
5
  ConversationErrorCode,
@@ -110,11 +111,21 @@ export interface ErrorContext {
110
111
  * Returns true if the error looks like a user-initiated cancellation
111
112
  * (AbortError or explicit cancel). These should use `generation_cancelled`
112
113
  * instead of `conversation_error`.
114
+ *
115
+ * Provider SDKs wrap the underlying AbortError in their own error class
116
+ * (e.g. `ProviderError("Anthropic API error (undefined): Request was aborted.")`),
117
+ * which erases the `AbortError` name. To compensate, the daemon tags every
118
+ * `controller.abort(reason)` call with an `AbortReason` object — when the
119
+ * wrapped `ProviderError` carries that tagged reason, we treat it as a user
120
+ * cancellation regardless of error class.
113
121
  */
114
122
  export function isUserCancellation(error: unknown, ctx: ErrorContext): boolean {
115
123
  if (!ctx.aborted) return false;
116
124
  if (error instanceof DOMException && error.name === "AbortError") return true;
117
125
  if (error instanceof Error && error.name === "AbortError") return true;
126
+ if (error instanceof ProviderError && isAbortReason(error.abortReason)) {
127
+ return true;
128
+ }
118
129
  return false;
119
130
  }
120
131
 
@@ -15,6 +15,7 @@ import { withQdrantBreaker } from "../memory/qdrant-circuit-breaker.js";
15
15
  import { getQdrantClient } from "../memory/qdrant-client.js";
16
16
  import type { ContentBlock, Message } from "../providers/types.js";
17
17
  import { getLogger } from "../util/logger.js";
18
+ import { truncate } from "../util/truncate.js";
18
19
  import type { ServerMessage } from "./message-protocol.js";
19
20
  import type { TraceEmitter } from "./trace-emitter.js";
20
21
 
@@ -564,10 +565,43 @@ export async function regenerate(
564
565
  // in both this.messages and the DB.
565
566
  conversation.processing = true;
566
567
  conversation.abortController = new AbortController();
567
- conversation.currentRequestId = requestId ?? uuid();
568
-
569
- await conversation.runAgentLoop(content, existingUserMessageId, onEvent, {
570
- skipPreMessageRollback: true,
571
- isUserMessage: true,
572
- });
568
+ const resolvedRequestId = requestId ?? uuid();
569
+ conversation.currentRequestId = resolvedRequestId;
570
+
571
+ // Fire-and-forget: matches the /v1/messages pattern so the HTTP handler
572
+ // returns 202 immediately rather than blocking on the full agent turn.
573
+ // Otherwise the client's 15s POST timeout fires on any non-trivial
574
+ // regenerate and surfaces a misleading "Failed to regenerate message"
575
+ // banner even though the response streams in normally via SSE.
576
+ //
577
+ // runAgentLoop catches most errors internally and emits `request_error`
578
+ // itself, but anything thrown from its `finally` block (commit, drain,
579
+ // profiler) would otherwise escape silently because the caller does
580
+ // not await the agent loop. Emit a structured trace event so the
581
+ // observability contract is preserved on those paths too.
582
+ void conversation
583
+ .runAgentLoop(content, existingUserMessageId, onEvent, {
584
+ skipPreMessageRollback: true,
585
+ isUserMessage: true,
586
+ })
587
+ .catch((err) => {
588
+ const message = err instanceof Error ? err.message : String(err);
589
+ log.error(
590
+ { err, conversationId: conversation.conversationId },
591
+ "runAgentLoop after regenerate failed",
592
+ );
593
+ conversation.traceEmitter.emit(
594
+ "request_error",
595
+ truncate(message, 200, ""),
596
+ {
597
+ requestId: resolvedRequestId,
598
+ status: "error",
599
+ attributes: {
600
+ errorClass: err instanceof Error ? err.constructor.name : "Error",
601
+ message: truncate(message, 500, ""),
602
+ source: "regenerate_fire_and_forget",
603
+ },
604
+ },
605
+ );
606
+ });
573
607
  }