@vellumai/assistant 0.6.3 → 0.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (667) hide show
  1. package/ARCHITECTURE.md +273 -10
  2. package/Dockerfile +2 -3
  3. package/bun.lock +5 -13
  4. package/docs/backup-troubleshooting.md +52 -0
  5. package/docs/browser-use-architecture-phase2.md +174 -0
  6. package/docs/stt-provider-onboarding.md +120 -0
  7. package/knip.json +12 -2
  8. package/node_modules/@vellumai/ces-contracts/bun.lock +8 -6
  9. package/node_modules/@vellumai/ces-contracts/package.json +3 -3
  10. package/openapi.yaml +982 -72
  11. package/package.json +4 -6
  12. package/scripts/generate-openapi.ts +0 -1
  13. package/scripts/test.sh +73 -18
  14. package/src/__tests__/agent-image-optimize.test.ts +28 -0
  15. package/src/__tests__/agent-loop.test.ts +123 -0
  16. package/src/__tests__/anthropic-provider.test.ts +263 -10
  17. package/src/__tests__/auto-analysis-end-to-end.test.ts +550 -0
  18. package/src/__tests__/auto-analysis-prompt.test.ts +50 -0
  19. package/src/__tests__/browser-fill-credential.test.ts +11 -0
  20. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +2 -2
  21. package/src/__tests__/browser-skill-endstate.test.ts +31 -7
  22. package/src/__tests__/btw-routes.test.ts +7 -0
  23. package/src/__tests__/call-controller.test.ts +581 -20
  24. package/src/__tests__/catalog-files.test.ts +138 -0
  25. package/src/__tests__/channel-invite-transport.test.ts +2 -2
  26. package/src/__tests__/channel-readiness-routes.test.ts +16 -20
  27. package/src/__tests__/channel-readiness-service.test.ts +12 -7
  28. package/src/__tests__/checker.test.ts +157 -10
  29. package/src/__tests__/clawhub-files.test.ts +347 -0
  30. package/src/__tests__/commit-message-enrichment-service.test.ts +36 -19
  31. package/src/__tests__/config-analysis.test.ts +100 -0
  32. package/src/__tests__/config-schema.test.ts +1013 -66
  33. package/src/__tests__/config-watcher-cleanup-throttle.test.ts +339 -0
  34. package/src/__tests__/config-watcher.test.ts +43 -8
  35. package/src/__tests__/contact-store-user-file.test.ts +512 -0
  36. package/src/__tests__/contacts-write.test.ts +197 -0
  37. package/src/__tests__/context-window-manager.test.ts +88 -0
  38. package/src/__tests__/conversation-abort-tool-results.test.ts +2 -0
  39. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
  40. package/src/__tests__/conversation-agent-loop.test.ts +98 -2
  41. package/src/__tests__/conversation-confirmation-signals.test.ts +135 -0
  42. package/src/__tests__/conversation-error.test.ts +70 -0
  43. package/src/__tests__/conversation-history-web-search.test.ts +11 -4
  44. package/src/__tests__/conversation-init.benchmark.test.ts +6 -1
  45. package/src/__tests__/conversation-launcher-skill-regression.test.ts +51 -0
  46. package/src/__tests__/conversation-list-source.test.ts +145 -0
  47. package/src/__tests__/conversation-pre-run-repair.test.ts +2 -0
  48. package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -0
  49. package/src/__tests__/conversation-queue.test.ts +901 -60
  50. package/src/__tests__/conversation-routes-disk-view.test.ts +270 -0
  51. package/src/__tests__/conversation-runtime-assembly.test.ts +55 -0
  52. package/src/__tests__/conversation-skill-tools.test.ts +7 -4
  53. package/src/__tests__/conversation-slash-commands.test.ts +33 -0
  54. package/src/__tests__/conversation-slash-queue.test.ts +89 -18
  55. package/src/__tests__/conversation-slash-unknown.test.ts +2 -0
  56. package/src/__tests__/conversation-tool-setup-batch-authorized.test.ts +226 -0
  57. package/src/__tests__/conversation-workspace-injection.test.ts +2 -0
  58. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +2 -0
  59. package/src/__tests__/credential-health-service.test.ts +352 -0
  60. package/src/__tests__/credential-security-invariants.test.ts +5 -3
  61. package/src/__tests__/credential-vault-unit.test.ts +379 -3
  62. package/src/__tests__/credentials-cli.test.ts +40 -16
  63. package/src/__tests__/cross-provider-web-search.test.ts +146 -35
  64. package/src/__tests__/deterministic-verification-control-plane.test.ts +10 -1
  65. package/src/__tests__/device-id.test.ts +112 -0
  66. package/src/__tests__/docker-signing-key-bootstrap.test.ts +167 -4
  67. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +1 -3
  68. package/src/__tests__/email-html-renderer.test.ts +71 -0
  69. package/src/__tests__/email-invite-adapter.test.ts +36 -32
  70. package/src/__tests__/emit-event-signal.test.ts +71 -0
  71. package/src/__tests__/extension-id-sync-guard.test.ts +75 -8
  72. package/src/__tests__/fixtures/mock-chrome-extension.ts +11 -0
  73. package/src/__tests__/gateway-only-enforcement.test.ts +206 -1
  74. package/src/__tests__/gateway-only-guard.test.ts +0 -1
  75. package/src/__tests__/gemini-provider.test.ts +64 -0
  76. package/src/__tests__/get-skill-detail-audit.test.ts +325 -0
  77. package/src/__tests__/gmail-archive-fallback.test.ts +193 -0
  78. package/src/__tests__/gmail-archive-gate.test.ts +246 -0
  79. package/src/__tests__/gmail-preferences.test.ts +117 -0
  80. package/src/__tests__/headless-browser-interactions.test.ts +43 -0
  81. package/src/__tests__/headless-browser-mode.test.ts +614 -0
  82. package/src/__tests__/headless-browser-navigate.test.ts +142 -5
  83. package/src/__tests__/headless-browser-read-tools.test.ts +11 -0
  84. package/src/__tests__/headless-browser-snapshot.test.ts +10 -0
  85. package/src/__tests__/heartbeat-service.test.ts +70 -17
  86. package/src/__tests__/home-state-routes.test.ts +162 -0
  87. package/src/__tests__/host-bash-proxy.test.ts +0 -5
  88. package/src/__tests__/host-browser-e2e-cloud.test.ts +138 -4
  89. package/src/__tests__/host-browser-e2e-self-hosted.test.ts +4 -4
  90. package/src/__tests__/host-browser-ws-events-e2e.test.ts +103 -0
  91. package/src/__tests__/host-cu-proxy.test.ts +0 -5
  92. package/src/__tests__/identity-intro-cache.test.ts +40 -10
  93. package/src/__tests__/init-feature-flag-overrides.test.ts +38 -112
  94. package/src/__tests__/jobs-store-upsert-debounced.test.ts +141 -0
  95. package/src/__tests__/llm-context-normalization.test.ts +488 -0
  96. package/src/__tests__/llm-context-route-provider.test.ts +86 -5
  97. package/src/__tests__/llm-usage-store.test.ts +363 -0
  98. package/src/__tests__/media-stream-output.test.ts +555 -0
  99. package/src/__tests__/media-stream-parser.test.ts +374 -0
  100. package/src/__tests__/media-stream-server-integration.test.ts +1234 -0
  101. package/src/__tests__/media-stream-stt-session.test.ts +588 -0
  102. package/src/__tests__/media-turn-detector.test.ts +440 -0
  103. package/src/__tests__/message-queue.test.ts +125 -0
  104. package/src/__tests__/migration-export-http.test.ts +6 -6
  105. package/src/__tests__/migration-import-commit-http.test.ts +8 -6
  106. package/src/__tests__/migration-import-preflight-http.test.ts +6 -5
  107. package/src/__tests__/migration-validate-http.test.ts +3 -3
  108. package/src/__tests__/mock-gateway-ipc.ts +151 -0
  109. package/src/__tests__/model-intents.test.ts +2 -2
  110. package/src/__tests__/oauth-apps-routes.test.ts +1 -0
  111. package/src/__tests__/oauth-cli.test.ts +2 -0
  112. package/src/__tests__/oauth-connect-orchestrator.test.ts +2 -0
  113. package/src/__tests__/oauth-provider-serializer.test.ts +1 -0
  114. package/src/__tests__/oauth-providers-routes.test.ts +2 -0
  115. package/src/__tests__/oauth-store.test.ts +85 -0
  116. package/src/__tests__/oauth2-gateway-transport.test.ts +249 -6
  117. package/src/__tests__/onboarding-template-contract.test.ts +6 -13
  118. package/src/__tests__/openai-provider.test.ts +176 -0
  119. package/src/__tests__/openai-responses-cutover-guard.test.ts +184 -0
  120. package/src/__tests__/openai-responses-provider.test.ts +1105 -0
  121. package/src/__tests__/openrouter-token-estimation.test.ts +100 -0
  122. package/src/__tests__/outlook-unsubscribe.test.ts +31 -2
  123. package/src/__tests__/persona-resolver.test.ts +251 -0
  124. package/src/__tests__/platform-bash-auto-approve.test.ts +4 -0
  125. package/src/__tests__/platform.test.ts +92 -1
  126. package/src/__tests__/post-turn-tool-result-truncation.test.ts +47 -0
  127. package/src/__tests__/prechat-onboarding-contract.test.ts +267 -0
  128. package/src/__tests__/pricing.test.ts +174 -0
  129. package/src/__tests__/qdrant-manager.test.ts +29 -8
  130. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +194 -0
  131. package/src/__tests__/relationship-state-contract.test.ts +175 -0
  132. package/src/__tests__/relay-server.test.ts +423 -5
  133. package/src/__tests__/search-skills-unified.test.ts +118 -0
  134. package/src/__tests__/secret-scanner-executor.test.ts +4 -0
  135. package/src/__tests__/secure-keys.test.ts +107 -0
  136. package/src/__tests__/send-endpoint-busy.test.ts +5 -1
  137. package/src/__tests__/sequence-store.test.ts +1 -1
  138. package/src/__tests__/server-history-render.test.ts +49 -0
  139. package/src/__tests__/settings-routes.test.ts +201 -0
  140. package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
  141. package/src/__tests__/skills-file-content-endpoint.test.ts +276 -145
  142. package/src/__tests__/skills-files-catalog-fallback.test.ts +381 -93
  143. package/src/__tests__/skills.test.ts +5 -2
  144. package/src/__tests__/skillssh-files.test.ts +446 -0
  145. package/src/__tests__/slack-block-formatting.test.ts +110 -0
  146. package/src/__tests__/slack-channel-config.test.ts +564 -1
  147. package/src/__tests__/stt-catalog-parity.test.ts +282 -0
  148. package/src/__tests__/stt-stream-session.test.ts +535 -0
  149. package/src/__tests__/system-prompt.test.ts +112 -26
  150. package/src/__tests__/telephony-stt-routing.test.ts +329 -0
  151. package/src/__tests__/terminal-tools.test.ts +18 -7
  152. package/src/__tests__/test-preload.ts +18 -0
  153. package/src/__tests__/test-support/browser-skill-harness.ts +4 -1
  154. package/src/__tests__/tool-executor-lifecycle-events.test.ts +9 -5
  155. package/src/__tests__/tool-executor-shell-integration.test.ts +4 -0
  156. package/src/__tests__/tool-executor.test.ts +33 -24
  157. package/src/__tests__/tool-result-truncation.test.ts +36 -0
  158. package/src/__tests__/trust-store.test.ts +7 -1
  159. package/src/__tests__/trusted-contact-approval-notifier.test.ts +1 -1
  160. package/src/__tests__/tts-catalog-parity.test.ts +345 -0
  161. package/src/__tests__/twilio-routes-twiml.test.ts +512 -114
  162. package/src/__tests__/twilio-routes.test.ts +376 -0
  163. package/src/__tests__/unicode.test.ts +293 -0
  164. package/src/__tests__/update-bulletin-format.test.ts +59 -0
  165. package/src/__tests__/update-bulletin.test.ts +206 -5
  166. package/src/__tests__/usage-routes.test.ts +25 -4
  167. package/src/__tests__/user-reference.test.ts +46 -61
  168. package/src/__tests__/verification-control-plane-policy.test.ts +4 -0
  169. package/src/__tests__/voice-config-update.test.ts +403 -0
  170. package/src/__tests__/voice-quality.test.ts +434 -19
  171. package/src/__tests__/workspace-heartbeat-service.test.ts +7 -0
  172. package/src/__tests__/workspace-migration-033-stt-service-explicit-config.test.ts +547 -0
  173. package/src/__tests__/workspace-migration-034-remove-calls-voice-transcription-provider.test.ts +596 -0
  174. package/src/__tests__/workspace-migration-drop-user-md.test.ts +368 -0
  175. package/src/__tests__/workspace-migration-meets.test.ts +244 -0
  176. package/src/__tests__/workspace-migration-seed-device-id.test.ts +14 -20
  177. package/src/__tests__/workspace-policy.test.ts +2 -0
  178. package/src/agent/image-optimize.ts +24 -12
  179. package/src/agent/loop.ts +43 -3
  180. package/src/backup/__tests__/backup-key.test.ts +152 -0
  181. package/src/backup/__tests__/backup-worker.test.ts +767 -0
  182. package/src/backup/__tests__/list-snapshots.test.ts +87 -0
  183. package/src/backup/__tests__/local-writer.test.ts +218 -0
  184. package/src/backup/__tests__/offsite-writer.test.ts +641 -0
  185. package/src/backup/__tests__/paths.test.ts +300 -0
  186. package/src/backup/__tests__/restore.test.ts +498 -0
  187. package/src/backup/__tests__/snapshot-lock.test.ts +352 -0
  188. package/src/backup/__tests__/stream-crypt.test.ts +228 -0
  189. package/src/backup/backup-key.ts +137 -0
  190. package/src/backup/backup-worker.ts +459 -0
  191. package/src/backup/list-snapshots.ts +147 -0
  192. package/src/backup/local-writer.ts +133 -0
  193. package/src/backup/offsite-writer.ts +222 -0
  194. package/src/backup/paths.ts +226 -0
  195. package/src/backup/restore.ts +322 -0
  196. package/src/backup/snapshot-lock.ts +431 -0
  197. package/src/backup/stream-crypt.ts +263 -0
  198. package/src/bundler/package-resolver.ts +4 -0
  199. package/src/calls/audio-store.ts +11 -5
  200. package/src/calls/call-controller.ts +226 -71
  201. package/src/calls/call-domain.ts +9 -0
  202. package/src/calls/call-speech-output.ts +190 -0
  203. package/src/calls/call-transport.ts +77 -0
  204. package/src/calls/media-stream-audio-transcode.ts +173 -0
  205. package/src/calls/media-stream-output.ts +660 -0
  206. package/src/calls/media-stream-parser.ts +300 -0
  207. package/src/calls/media-stream-protocol.ts +166 -0
  208. package/src/calls/media-stream-server.ts +592 -0
  209. package/src/calls/media-stream-stt-session.ts +460 -0
  210. package/src/calls/media-turn-detector.ts +230 -0
  211. package/src/calls/relay-server.ts +90 -75
  212. package/src/calls/resolve-call-tts-provider.ts +136 -0
  213. package/src/calls/telephony-stt-routing.ts +145 -0
  214. package/src/calls/tts-call-strategy.ts +161 -0
  215. package/src/calls/tts-text-sanitizer.ts +32 -16
  216. package/src/calls/twilio-routes.ts +281 -17
  217. package/src/calls/voice-quality.ts +78 -35
  218. package/src/calls/voice-session-bridge.ts +8 -1
  219. package/src/channels/types.ts +16 -0
  220. package/src/cli/__tests__/run-assistant-command.ts +11 -1
  221. package/src/cli/commands/__tests__/backup.test.ts +1165 -0
  222. package/src/cli/commands/__tests__/domain-register.test.ts +234 -0
  223. package/src/cli/commands/__tests__/domain-status.test.ts +132 -0
  224. package/src/cli/commands/__tests__/email-attachment.test.ts +422 -0
  225. package/src/cli/commands/__tests__/email-download.test.ts +16 -1
  226. package/src/cli/commands/__tests__/email-list.test.ts +22 -4
  227. package/src/cli/commands/__tests__/email-register.test.ts +4 -4
  228. package/src/cli/commands/__tests__/email-send.test.ts +37 -4
  229. package/src/cli/commands/__tests__/email-status.test.ts +5 -1
  230. package/src/cli/commands/__tests__/email-unregister.test.ts +34 -5
  231. package/src/cli/commands/backup.ts +993 -0
  232. package/src/cli/commands/conversations.ts +77 -0
  233. package/src/cli/commands/credentials.ts +0 -1
  234. package/src/cli/commands/domain.ts +210 -0
  235. package/src/cli/commands/email.ts +255 -3
  236. package/src/cli/commands/oauth/__tests__/connect.test.ts +12 -0
  237. package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +1 -0
  238. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +1 -0
  239. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -0
  240. package/src/cli/commands/oauth/mode.ts +12 -3
  241. package/src/cli/commands/oauth/providers.ts +15 -0
  242. package/src/cli/commands/oauth/shared.ts +2 -1
  243. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +4 -9
  244. package/src/cli/commands/platform/__tests__/connect.test.ts +6 -0
  245. package/src/cli/commands/platform/__tests__/disconnect.test.ts +7 -1
  246. package/src/cli/commands/platform/__tests__/status.test.ts +6 -0
  247. package/src/cli/program.ts +30 -4
  248. package/src/config/__tests__/backup-schema.test.ts +134 -0
  249. package/src/config/assistant-feature-flags.ts +61 -62
  250. package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +37 -1
  251. package/src/config/bundled-skills/browser/SKILL.md +30 -5
  252. package/src/config/bundled-skills/browser/TOOLS.json +123 -0
  253. package/src/config/bundled-skills/browser/tools/browser-attach.ts +12 -0
  254. package/src/config/bundled-skills/browser/tools/browser-detach.ts +12 -0
  255. package/src/config/bundled-skills/browser/tools/browser-status.ts +12 -0
  256. package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +17 -0
  257. package/src/config/bundled-skills/contacts/SKILL.md +2 -2
  258. package/src/config/bundled-skills/gmail/SKILL.md +53 -7
  259. package/src/config/bundled-skills/gmail/TOOLS.json +33 -3
  260. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +116 -9
  261. package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +138 -11
  262. package/src/config/bundled-skills/gmail/tools/gmail-preferences-tool.ts +59 -0
  263. package/src/config/bundled-skills/gmail/tools/gmail-preferences.ts +82 -0
  264. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +113 -17
  265. package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +2 -2
  266. package/src/config/bundled-skills/media-processing/SKILL.md +3 -9
  267. package/src/config/bundled-skills/media-processing/TOOLS.json +1 -6
  268. package/src/config/bundled-skills/media-processing/__tests__/audio-transcribe.test.ts +125 -0
  269. package/src/config/bundled-skills/media-processing/__tests__/extract-keyframes.test.ts +181 -0
  270. package/src/config/bundled-skills/media-processing/__tests__/preprocess-audio.test.ts +141 -0
  271. package/src/config/bundled-skills/media-processing/services/audio-transcribe.ts +32 -87
  272. package/src/config/bundled-skills/media-processing/services/preprocess.ts +8 -4
  273. package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +0 -10
  274. package/src/config/bundled-skills/messaging/SKILL.md +3 -3
  275. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +2 -2
  276. package/src/config/bundled-skills/outlook/SKILL.md +2 -2
  277. package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +2 -2
  278. package/src/config/bundled-skills/phone-calls/SKILL.md +2 -2
  279. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +27 -18
  280. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +3 -3
  281. package/src/config/bundled-skills/settings/TOOLS.json +3 -3
  282. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +26 -22
  283. package/src/config/bundled-skills/slack/SKILL.md +1 -0
  284. package/src/config/bundled-skills/transcribe/SKILL.md +9 -14
  285. package/src/config/bundled-skills/transcribe/TOOLS.json +2 -7
  286. package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +256 -0
  287. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +38 -188
  288. package/src/config/bundled-tool-registry.ts +8 -0
  289. package/src/config/env-registry.ts +24 -0
  290. package/src/config/env.ts +34 -10
  291. package/src/config/feature-flag-registry.json +46 -14
  292. package/src/config/loader.ts +26 -12
  293. package/src/config/schema.ts +35 -10
  294. package/src/config/schemas/__tests__/stt.test.ts +43 -0
  295. package/src/config/schemas/analysis.ts +51 -0
  296. package/src/config/schemas/backup.ts +72 -0
  297. package/src/config/schemas/calls.ts +1 -26
  298. package/src/config/schemas/elevenlabs.ts +0 -59
  299. package/src/config/schemas/filing.ts +47 -7
  300. package/src/config/schemas/heartbeat.ts +27 -5
  301. package/src/config/schemas/host-browser.ts +47 -1
  302. package/src/config/schemas/inference.ts +1 -1
  303. package/src/config/schemas/memory-lifecycle.ts +14 -2
  304. package/src/config/schemas/services.ts +44 -0
  305. package/src/config/schemas/stt.ts +59 -0
  306. package/src/config/schemas/tts.ts +230 -0
  307. package/src/config/schemas/updates.ts +14 -0
  308. package/src/config/skills.ts +4 -0
  309. package/src/config/types.ts +4 -0
  310. package/src/contacts/contact-store.ts +56 -11
  311. package/src/contacts/contacts-write.ts +38 -1
  312. package/src/context/post-turn-tool-result-truncation.ts +3 -2
  313. package/src/context/tool-result-truncation.ts +2 -1
  314. package/src/context/window-manager.ts +45 -12
  315. package/src/credential-execution/executable-discovery.ts +12 -2
  316. package/src/credential-execution/process-manager.ts +33 -2
  317. package/src/credential-health/credential-health-service.ts +366 -0
  318. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +324 -0
  319. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +497 -0
  320. package/src/daemon/__tests__/conversation-tool-setup.test.ts +17 -8
  321. package/src/daemon/__tests__/lifecycle-startup-ordering.test.ts +127 -0
  322. package/src/daemon/config-watcher.ts +99 -5
  323. package/src/daemon/conversation-agent-loop-handlers.ts +6 -0
  324. package/src/daemon/conversation-agent-loop.ts +101 -24
  325. package/src/daemon/conversation-error.ts +11 -0
  326. package/src/daemon/conversation-history.ts +40 -6
  327. package/src/daemon/conversation-launch.ts +220 -0
  328. package/src/daemon/conversation-lifecycle.ts +59 -9
  329. package/src/daemon/conversation-messaging.ts +37 -3
  330. package/src/daemon/conversation-notifiers.ts +5 -0
  331. package/src/daemon/conversation-process.ts +581 -19
  332. package/src/daemon/conversation-queue-manager.ts +24 -0
  333. package/src/daemon/conversation-runtime-assembly.ts +11 -1
  334. package/src/daemon/conversation-slash.ts +36 -0
  335. package/src/daemon/conversation-surfaces.ts +94 -4
  336. package/src/daemon/conversation-tool-setup.ts +25 -0
  337. package/src/daemon/conversation-usage.ts +7 -4
  338. package/src/daemon/conversation.ts +86 -28
  339. package/src/daemon/handlers/config-slack-channel.ts +269 -94
  340. package/src/daemon/handlers/conversations.ts +4 -1
  341. package/src/daemon/handlers/shared.ts +22 -0
  342. package/src/daemon/handlers/skills.ts +321 -77
  343. package/src/daemon/host-browser-proxy.ts +2 -1
  344. package/src/daemon/lifecycle.ts +122 -25
  345. package/src/daemon/message-protocol.ts +6 -0
  346. package/src/daemon/message-types/conversations.ts +34 -1
  347. package/src/daemon/message-types/home.ts +40 -0
  348. package/src/daemon/message-types/meet.ts +143 -0
  349. package/src/daemon/message-types/messages.ts +14 -0
  350. package/src/daemon/message-types/schedules.ts +34 -2
  351. package/src/daemon/message-types/skills.ts +16 -0
  352. package/src/daemon/message-types/surfaces.ts +2 -0
  353. package/src/daemon/server.ts +347 -2
  354. package/src/daemon/shutdown-handlers.ts +32 -4
  355. package/src/daemon/shutdown-registry.ts +40 -0
  356. package/src/daemon/tool-side-effects.ts +9 -0
  357. package/src/email/html-renderer.ts +76 -0
  358. package/src/heartbeat/heartbeat-service.ts +93 -7
  359. package/src/home/__tests__/assistant-feed-authoring.test.ts +156 -0
  360. package/src/home/__tests__/emit-feed-event.test.ts +169 -0
  361. package/src/home/__tests__/feed-scheduler.test.ts +194 -0
  362. package/src/home/__tests__/feed-types.test.ts +275 -0
  363. package/src/home/__tests__/feed-writer.test.ts +688 -0
  364. package/src/home/__tests__/phase5-exit-criteria.test.ts +212 -0
  365. package/src/home/__tests__/platform-gmail-digest.test.ts +222 -0
  366. package/src/home/__tests__/progress-formula.test.ts +213 -0
  367. package/src/home/__tests__/relationship-state-writer.test.ts +740 -0
  368. package/src/home/__tests__/rollup-producer.test.ts +398 -0
  369. package/src/home/assistant-feed-authoring.ts +124 -0
  370. package/src/home/emit-feed-event.ts +158 -0
  371. package/src/home/feed-scheduler.ts +247 -0
  372. package/src/home/feed-types.ts +181 -0
  373. package/src/home/feed-writer.ts +469 -0
  374. package/src/home/platform-gmail-digest.ts +163 -0
  375. package/src/home/progress-formula.ts +86 -0
  376. package/src/home/relationship-state-writer.ts +824 -0
  377. package/src/home/relationship-state.ts +143 -0
  378. package/src/home/rollup-producer.ts +384 -0
  379. package/src/hooks/runner.ts +7 -0
  380. package/src/inbound/platform-callback-registration.ts +12 -3
  381. package/src/inbound/public-ingress-urls.ts +12 -0
  382. package/src/instrument.ts +1 -1
  383. package/src/ipc/__tests__/cli-ipc.test.ts +200 -0
  384. package/src/ipc/cli-client.ts +151 -0
  385. package/src/ipc/cli-server.ts +234 -0
  386. package/src/ipc/gateway-client.ts +180 -0
  387. package/src/ipc/routes/index.ts +5 -0
  388. package/src/ipc/routes/wake-conversation.ts +19 -0
  389. package/src/memory/__tests__/auto-analysis-enqueue.test.ts +356 -0
  390. package/src/memory/__tests__/auto-analysis-guard.test.ts +57 -0
  391. package/src/memory/__tests__/conversation-analyze-job.test.ts +232 -0
  392. package/src/memory/__tests__/find-analysis-conversation.test.ts +196 -0
  393. package/src/memory/app-store.ts +1 -1
  394. package/src/memory/attachments-store.ts +70 -0
  395. package/src/memory/auto-analysis-enqueue.ts +127 -0
  396. package/src/memory/auto-analysis-guard.ts +27 -0
  397. package/src/memory/cleanup-schedule-state.ts +37 -0
  398. package/src/memory/conversation-analyze-job.ts +73 -0
  399. package/src/memory/conversation-crud.ts +99 -0
  400. package/src/memory/conversation-disk-view.ts +7 -0
  401. package/src/memory/conversation-group-migration.ts +34 -2
  402. package/src/memory/conversation-queries.ts +6 -5
  403. package/src/memory/db-init.ts +6 -0
  404. package/src/memory/db-maintenance.ts +108 -0
  405. package/src/memory/db.ts +1 -0
  406. package/src/memory/graph/conversation-graph-memory.ts +15 -0
  407. package/src/memory/graph/extraction.test.ts +23 -0
  408. package/src/memory/graph/extraction.ts +8 -0
  409. package/src/memory/graph/retriever.ts +27 -18
  410. package/src/memory/graph/scoring.test.ts +186 -0
  411. package/src/memory/graph/scoring.ts +31 -1
  412. package/src/memory/graph/tools.ts +1 -1
  413. package/src/memory/group-crud.ts +6 -1
  414. package/src/memory/indexer.ts +95 -16
  415. package/src/memory/job-handlers/cleanup.ts +11 -8
  416. package/src/memory/job-handlers/conversation-starters.ts +16 -10
  417. package/src/memory/jobs-store.ts +64 -4
  418. package/src/memory/jobs-worker.ts +22 -9
  419. package/src/memory/llm-usage-store.ts +92 -56
  420. package/src/memory/migrations/219-oauth-providers-token-exchange-body-format.ts +15 -0
  421. package/src/memory/migrations/220-normalize-user-file-by-principal.ts +190 -0
  422. package/src/memory/migrations/221-conversations-archived-at.ts +16 -0
  423. package/src/memory/migrations/index.ts +6 -0
  424. package/src/memory/migrations/registry.ts +8 -0
  425. package/src/memory/qdrant-manager.ts +43 -16
  426. package/src/memory/schema/conversations.ts +2 -0
  427. package/src/memory/schema/oauth.ts +3 -0
  428. package/src/memory/usage-buckets.ts +396 -0
  429. package/src/messaging/providers/gmail/client.ts +57 -6
  430. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +282 -0
  431. package/src/messaging/providers/slack/adapter.ts +143 -38
  432. package/src/messaging/providers/slack/client.ts +16 -0
  433. package/src/messaging/providers/slack/types.ts +4 -0
  434. package/src/notifications/decision-engine.ts +3 -3
  435. package/src/notifications/signal.ts +5 -0
  436. package/src/oauth/__tests__/identity-verifier.test.ts +1 -0
  437. package/src/oauth/byo-connection.test.ts +18 -1
  438. package/src/oauth/byo-connection.ts +3 -1
  439. package/src/oauth/connect-orchestrator.ts +2 -0
  440. package/src/oauth/connection-resolver.ts +6 -2
  441. package/src/oauth/connection.ts +2 -0
  442. package/src/oauth/oauth-store.ts +9 -0
  443. package/src/oauth/platform-connection.test.ts +98 -0
  444. package/src/oauth/platform-connection.ts +52 -31
  445. package/src/oauth/seed-providers.ts +7 -0
  446. package/src/permissions/checker.ts +16 -6
  447. package/src/permissions/defaults.ts +49 -1
  448. package/src/permissions/trust-store.ts +3 -3
  449. package/src/permissions/workspace-policy.ts +3 -0
  450. package/src/platform/client.test.ts +10 -0
  451. package/src/platform/sync-identity.ts +129 -0
  452. package/src/prompts/persona-resolver.ts +126 -2
  453. package/src/prompts/system-prompt.ts +59 -18
  454. package/src/prompts/templates/BOOTSTRAP.md +5 -5
  455. package/src/prompts/templates/SOUL.md +3 -1
  456. package/src/prompts/templates/UPDATES.md +12 -0
  457. package/src/prompts/templates/channels/slack.md +20 -0
  458. package/src/prompts/update-bulletin-format.ts +26 -9
  459. package/src/prompts/update-bulletin.ts +34 -23
  460. package/src/prompts/user-reference.ts +20 -17
  461. package/src/providers/__tests__/provider-secret-catalog.test.ts +42 -0
  462. package/src/providers/anthropic/client.ts +157 -61
  463. package/src/providers/fireworks/client.ts +2 -2
  464. package/src/providers/gemini/client.ts +9 -1
  465. package/src/providers/model-catalog.ts +6 -0
  466. package/src/providers/model-intents.ts +4 -4
  467. package/src/providers/ollama/client.ts +2 -2
  468. package/src/providers/openai/chat-completions-provider.ts +474 -0
  469. package/src/providers/openai/client.ts +25 -440
  470. package/src/providers/openai/responses-provider.ts +502 -0
  471. package/src/providers/openrouter/client.ts +101 -4
  472. package/src/providers/provider-secret-catalog.ts +139 -0
  473. package/src/providers/registry.ts +2 -2
  474. package/src/providers/retry.ts +14 -3
  475. package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +251 -0
  476. package/src/providers/speech-to-text/__tests__/resolve.test.ts +828 -0
  477. package/src/providers/speech-to-text/deepgram-realtime.test.ts +980 -0
  478. package/src/providers/speech-to-text/deepgram-realtime.ts +767 -0
  479. package/src/providers/speech-to-text/deepgram.test.ts +332 -0
  480. package/src/providers/speech-to-text/deepgram.ts +115 -0
  481. package/src/providers/speech-to-text/google-gemini-live-stream.test.ts +743 -0
  482. package/src/providers/speech-to-text/google-gemini-live-stream.ts +625 -0
  483. package/src/providers/speech-to-text/google-gemini.test.ts +226 -0
  484. package/src/providers/speech-to-text/google-gemini.ts +101 -0
  485. package/src/providers/speech-to-text/openai-whisper-stream.test.ts +564 -0
  486. package/src/providers/speech-to-text/openai-whisper-stream.ts +381 -0
  487. package/src/providers/speech-to-text/openai-whisper.test.ts +1 -37
  488. package/src/providers/speech-to-text/openai-whisper.ts +63 -33
  489. package/src/providers/speech-to-text/provider-catalog.ts +306 -0
  490. package/src/providers/speech-to-text/resolve.ts +386 -6
  491. package/src/providers/types.ts +9 -0
  492. package/src/runtime/AGENTS.md +43 -1
  493. package/src/runtime/__tests__/agent-wake.test.ts +831 -0
  494. package/src/runtime/__tests__/runtime-mode.test.ts +62 -0
  495. package/src/runtime/__tests__/slack-block-formatting.test.ts +481 -0
  496. package/src/runtime/agent-wake.ts +512 -0
  497. package/src/runtime/auth/__tests__/route-policy.test.ts +40 -0
  498. package/src/runtime/auth/route-policy.ts +30 -5
  499. package/src/runtime/auth/token-service.ts +56 -1
  500. package/src/runtime/btw-sidechain.ts +2 -0
  501. package/src/runtime/capability-tokens.ts +10 -10
  502. package/src/runtime/channel-invite-transport.ts +1 -1
  503. package/src/runtime/channel-invite-transports/email.ts +14 -6
  504. package/src/runtime/channel-readiness-service.ts +12 -22
  505. package/src/runtime/chrome-extension-registry.ts +38 -2
  506. package/src/runtime/http-server.ts +395 -10
  507. package/src/runtime/http-types.ts +6 -2
  508. package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +36 -0
  509. package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +360 -0
  510. package/src/runtime/migrations/migration-transport.ts +1 -0
  511. package/src/runtime/migrations/migration-wizard.ts +1 -0
  512. package/src/runtime/migrations/vbundle-import-analyzer.ts +77 -1
  513. package/src/runtime/migrations/vbundle-importer.ts +34 -0
  514. package/src/runtime/pending-interactions.ts +0 -11
  515. package/src/runtime/routes/__tests__/backup-routes.test.ts +967 -0
  516. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +507 -0
  517. package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +208 -0
  518. package/src/runtime/routes/__tests__/stt-routes.test.ts +406 -0
  519. package/src/runtime/routes/__tests__/tts-routes.test.ts +474 -0
  520. package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +148 -17
  521. package/src/runtime/routes/app-management-routes.ts +12 -18
  522. package/src/runtime/routes/attachment-routes.test.ts +9 -3
  523. package/src/runtime/routes/attachment-routes.ts +216 -17
  524. package/src/runtime/routes/backup-routes.ts +519 -0
  525. package/src/runtime/routes/browser-extension-pair-routes.ts +82 -23
  526. package/src/runtime/routes/btw-routes.ts +8 -6
  527. package/src/runtime/routes/contact-routes.test.ts +298 -0
  528. package/src/runtime/routes/contact-routes.ts +132 -5
  529. package/src/runtime/routes/conversation-analysis-routes.ts +22 -142
  530. package/src/runtime/routes/conversation-management-routes.ts +115 -0
  531. package/src/runtime/routes/conversation-routes.ts +367 -146
  532. package/src/runtime/routes/filing-routes.ts +93 -0
  533. package/src/runtime/routes/home-feed-routes.ts +334 -0
  534. package/src/runtime/routes/home-state-routes.ts +138 -0
  535. package/src/runtime/routes/host-browser-routes.ts +3 -14
  536. package/src/runtime/routes/identity-intro-cache.ts +7 -3
  537. package/src/runtime/routes/identity-routes.ts +3 -17
  538. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +46 -39
  539. package/src/runtime/routes/inbound-stages/transcribe-audio.ts +15 -15
  540. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +137 -0
  541. package/src/runtime/routes/integrations/slack/__tests__/share.test.ts +179 -0
  542. package/src/runtime/routes/integrations/slack/channel.ts +11 -3
  543. package/src/runtime/routes/integrations/slack/share.ts +45 -7
  544. package/src/runtime/routes/llm-context-normalization.ts +303 -0
  545. package/src/runtime/routes/memory-item-routes.test.ts +3 -2
  546. package/src/runtime/routes/migration-routes.ts +40 -5
  547. package/src/runtime/routes/settings-routes.ts +22 -5
  548. package/src/runtime/routes/skills-routes.ts +76 -7
  549. package/src/runtime/routes/stt-routes.ts +233 -0
  550. package/src/runtime/routes/surface-action-routes.ts +41 -2
  551. package/src/runtime/routes/tts-routes.ts +108 -24
  552. package/src/runtime/routes/usage-routes.ts +30 -2
  553. package/src/runtime/routes/user-route-dispatcher.ts +50 -5
  554. package/src/runtime/routes/user-routes.ts +13 -1
  555. package/src/runtime/routes/work-items-routes.ts +8 -1
  556. package/src/runtime/runtime-mode.ts +33 -0
  557. package/src/runtime/services/__tests__/analyze-conversation.test.ts +444 -0
  558. package/src/runtime/services/__tests__/analyze-deps-singleton.test.ts +67 -0
  559. package/src/runtime/services/__tests__/auto-analysis-prompt.test.ts +53 -0
  560. package/src/runtime/services/__tests__/manual-analysis-prompt.test.ts +41 -0
  561. package/src/runtime/services/analyze-conversation.ts +344 -0
  562. package/src/runtime/services/analyze-deps-singleton.ts +32 -0
  563. package/src/runtime/services/auto-analysis-prompt.ts +55 -0
  564. package/src/runtime/skill-route-registry.ts +49 -0
  565. package/src/runtime/slack-block-formatting.ts +437 -10
  566. package/src/schedule/scheduler.ts +50 -0
  567. package/src/security/oauth2.ts +26 -4
  568. package/src/security/secure-keys.ts +25 -2
  569. package/src/security/token-manager.ts +8 -0
  570. package/src/sequence/engine.ts +23 -0
  571. package/src/sequence/types.ts +1 -1
  572. package/src/skills/catalog-files.ts +64 -2
  573. package/src/skills/category-inference.ts +122 -0
  574. package/src/skills/clawhub-files.ts +213 -0
  575. package/src/skills/clawhub.ts +84 -23
  576. package/src/skills/skill-file-provider.ts +40 -0
  577. package/src/skills/skillssh-files.ts +395 -0
  578. package/src/skills/skillssh-registry.ts +4 -4
  579. package/src/stt/__tests__/daemon-batch-transcriber.test.ts +392 -0
  580. package/src/stt/__tests__/types.test.ts +89 -0
  581. package/src/stt/daemon-batch-transcriber.ts +195 -0
  582. package/src/stt/stt-stream-session.ts +499 -0
  583. package/src/stt/types.ts +330 -0
  584. package/src/stt/wav-encoder.test.ts +373 -0
  585. package/src/stt/wav-encoder.ts +175 -0
  586. package/src/subagent/manager.ts +38 -14
  587. package/src/tools/browser/__tests__/browser-mode.test.ts +119 -0
  588. package/src/tools/browser/__tests__/browser-status.test.ts +123 -0
  589. package/src/tools/browser/browser-execution.ts +1163 -23
  590. package/src/tools/browser/browser-manager.ts +45 -0
  591. package/src/tools/browser/browser-mode-constants.ts +12 -0
  592. package/src/tools/browser/browser-mode.ts +92 -0
  593. package/src/tools/browser/browser-status-constants.ts +33 -0
  594. package/src/tools/browser/cdp-client/__tests__/cdp-inspect-client.test.ts +393 -0
  595. package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +29 -0
  596. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +1648 -32
  597. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/discovery.test.ts +264 -0
  598. package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +183 -17
  599. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +254 -21
  600. package/src/tools/browser/cdp-client/errors.ts +15 -0
  601. package/src/tools/browser/cdp-client/extension-cdp-client.ts +39 -16
  602. package/src/tools/browser/cdp-client/factory.ts +797 -87
  603. package/src/tools/browser/cdp-client/index.ts +16 -2
  604. package/src/tools/browser/cdp-client/types.ts +68 -0
  605. package/src/tools/credentials/vault.ts +35 -6
  606. package/src/tools/network/web-fetch.ts +5 -2
  607. package/src/tools/network/web-search.ts +5 -2
  608. package/src/tools/shared/shell-output.ts +3 -1
  609. package/src/tools/side-effects.ts +2 -0
  610. package/src/tools/skills/sandbox-runner.ts +3 -2
  611. package/src/tools/terminal/safe-env.ts +10 -2
  612. package/src/tools/terminal/shell.ts +15 -4
  613. package/src/tools/tool-manifest.ts +21 -0
  614. package/src/tools/types.ts +17 -0
  615. package/src/tools/ui-surface/definitions.ts +6 -1
  616. package/src/tts/__tests__/provider-adapters.test.ts +834 -0
  617. package/src/tts/__tests__/provider-catalog-consistency.test.ts +196 -0
  618. package/src/tts/__tests__/provider-catalog.test.ts +183 -0
  619. package/src/tts/__tests__/provider-registry.test.ts +90 -0
  620. package/src/tts/provider-catalog.ts +201 -0
  621. package/src/tts/provider-registry.ts +73 -0
  622. package/src/tts/providers/deepgram-provider.ts +219 -0
  623. package/src/tts/providers/elevenlabs-provider.ts +211 -0
  624. package/src/tts/providers/fish-audio-provider.ts +183 -0
  625. package/src/tts/providers/index.ts +42 -0
  626. package/src/tts/providers/register-builtins.ts +130 -0
  627. package/src/tts/synthesize-text.ts +110 -0
  628. package/src/tts/tts-config-resolver.ts +78 -0
  629. package/src/tts/types.ts +153 -0
  630. package/src/types/onboarding-context.ts +7 -0
  631. package/src/util/abort-reasons.ts +58 -0
  632. package/src/util/device-id.ts +32 -16
  633. package/src/util/errors.ts +9 -1
  634. package/src/util/platform.ts +54 -10
  635. package/src/util/pricing.ts +66 -3
  636. package/src/util/spawn.ts +1 -1
  637. package/src/util/truncate.ts +4 -2
  638. package/src/util/unicode.ts +201 -0
  639. package/src/version.ts +19 -24
  640. package/src/watcher/engine.ts +23 -0
  641. package/src/watcher/watcher-store.ts +31 -0
  642. package/src/workspace/migrations/003-seed-device-id.ts +9 -3
  643. package/src/workspace/migrations/017-seed-persona-dirs.ts +68 -4
  644. package/src/workspace/migrations/029-seed-pkb.ts +1 -1
  645. package/src/workspace/migrations/031-drop-user-md.ts +317 -0
  646. package/src/workspace/migrations/031-llm-log-retention-zero-to-null.ts +73 -0
  647. package/src/workspace/migrations/032-tts-provider-unification.ts +227 -0
  648. package/src/workspace/migrations/033-stt-service-explicit-config.ts +122 -0
  649. package/src/workspace/migrations/034-remove-calls-voice-transcription-provider.ts +215 -0
  650. package/src/workspace/migrations/035-seed-slack-channel-persona.ts +50 -0
  651. package/src/workspace/migrations/036-update-pkb-index-bar.ts +37 -0
  652. package/src/workspace/migrations/037-create-meets-dir.ts +61 -0
  653. package/src/workspace/migrations/registry.ts +16 -0
  654. package/src/workspace/top-level-renderer.ts +13 -1
  655. package/src/workspace/turn-commit.ts +31 -0
  656. package/src/__tests__/email-cli.test.ts +0 -297
  657. package/src/__tests__/email-service-config-fallback.test.ts +0 -102
  658. package/src/cli/commands/browser-relay.ts +0 -466
  659. package/src/email/guardrails.ts +0 -221
  660. package/src/email/provider.ts +0 -117
  661. package/src/email/providers/agentmail.ts +0 -361
  662. package/src/email/providers/index.ts +0 -65
  663. package/src/email/service.ts +0 -384
  664. package/src/email/types.ts +0 -126
  665. package/src/prompts/templates/USER.md +0 -13
  666. package/src/providers/speech-to-text/types.ts +0 -17
  667. package/src/runtime/routes/browser-cdp-routes.ts +0 -229
@@ -0,0 +1,232 @@
1
+ /**
2
+ * Unit tests for the `conversation_analyze` job handler.
3
+ *
4
+ * The handler bridges the jobs worker to `analyzeConversation()` via the
5
+ * singleton deps bundle. Tests stub both the singleton and the service so we
6
+ * exercise dispatch logic without pulling in HTTP-layer wiring.
7
+ */
8
+
9
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
10
+
11
+ mock.module("../../util/logger.js", () => ({
12
+ getLogger: () =>
13
+ new Proxy({} as Record<string, unknown>, {
14
+ get: () => () => {},
15
+ }),
16
+ }));
17
+
18
+ // Mock analyze-deps singleton — each test overrides via mockGetAnalysisDeps.
19
+ type DepsStub = Record<string, unknown>;
20
+ const mockGetAnalysisDeps = mock((): DepsStub | null => null);
21
+
22
+ mock.module("../../runtime/services/analyze-deps-singleton.js", () => ({
23
+ getAnalysisDeps: mockGetAnalysisDeps,
24
+ }));
25
+
26
+ // Mock analyze-conversation service — default resolves with a success result.
27
+ type AnalyzeArgs = {
28
+ conversationId: string;
29
+ deps: DepsStub;
30
+ opts: { trigger: "manual" | "auto" };
31
+ };
32
+ const analyzeCalls: AnalyzeArgs[] = [];
33
+ type AnalyzeResultStub =
34
+ | { analysisConversationId: string; skipped?: true }
35
+ | { error: { kind: string; status: number; message: string } };
36
+ const mockAnalyzeConversation = mock(
37
+ async (
38
+ conversationId: string,
39
+ deps: DepsStub,
40
+ opts: { trigger: "manual" | "auto" },
41
+ ): Promise<AnalyzeResultStub> => {
42
+ analyzeCalls.push({ conversationId, deps, opts });
43
+ return { analysisConversationId: "analysis-1" };
44
+ },
45
+ );
46
+
47
+ mock.module("../../runtime/services/analyze-conversation.js", () => ({
48
+ analyzeConversation: mockAnalyzeConversation,
49
+ }));
50
+
51
+ // Mock auto-analysis-enqueue — track calls so we can verify requeue behavior.
52
+ type EnqueueArgs = {
53
+ conversationId: string;
54
+ trigger: string;
55
+ };
56
+ const enqueueCalls: EnqueueArgs[] = [];
57
+ const mockEnqueueAutoAnalysisIfEnabled = mock((args: EnqueueArgs) => {
58
+ enqueueCalls.push(args);
59
+ });
60
+
61
+ mock.module("../auto-analysis-enqueue.js", () => ({
62
+ enqueueAutoAnalysisIfEnabled: mockEnqueueAutoAnalysisIfEnabled,
63
+ }));
64
+
65
+ import { DEFAULT_CONFIG } from "../../config/defaults.js";
66
+ import type { AssistantConfig } from "../../config/types.js";
67
+ import { conversationAnalyzeJob } from "../conversation-analyze-job.js";
68
+ import type { MemoryJob } from "../jobs-store.js";
69
+
70
+ const TEST_CONFIG: AssistantConfig = DEFAULT_CONFIG;
71
+
72
+ function makeJob(payload: Record<string, unknown>): MemoryJob<{
73
+ conversationId?: string;
74
+ }> {
75
+ return {
76
+ id: "job-1",
77
+ type: "conversation_analyze",
78
+ payload: payload as { conversationId?: string },
79
+ status: "running",
80
+ attempts: 0,
81
+ deferrals: 0,
82
+ runAfter: 0,
83
+ lastError: null,
84
+ startedAt: Date.now(),
85
+ createdAt: Date.now(),
86
+ updatedAt: Date.now(),
87
+ };
88
+ }
89
+
90
+ describe("conversationAnalyzeJob", () => {
91
+ beforeEach(() => {
92
+ analyzeCalls.length = 0;
93
+ enqueueCalls.length = 0;
94
+ mockGetAnalysisDeps.mockReset();
95
+ mockGetAnalysisDeps.mockImplementation(() => null);
96
+ mockAnalyzeConversation.mockReset();
97
+ mockAnalyzeConversation.mockImplementation(
98
+ async (
99
+ conversationId: string,
100
+ deps: DepsStub,
101
+ opts: { trigger: "manual" | "auto" },
102
+ ) => {
103
+ analyzeCalls.push({ conversationId, deps, opts });
104
+ return { analysisConversationId: "analysis-1" };
105
+ },
106
+ );
107
+ });
108
+
109
+ test("returns without calling the service when conversationId is missing", async () => {
110
+ await conversationAnalyzeJob(makeJob({}), TEST_CONFIG);
111
+ expect(analyzeCalls).toHaveLength(0);
112
+ expect(mockGetAnalysisDeps).not.toHaveBeenCalled();
113
+ });
114
+
115
+ test("returns without calling the service when conversationId is empty string", async () => {
116
+ await conversationAnalyzeJob(
117
+ makeJob({ conversationId: "" }),
118
+ TEST_CONFIG,
119
+ );
120
+ expect(analyzeCalls).toHaveLength(0);
121
+ expect(mockGetAnalysisDeps).not.toHaveBeenCalled();
122
+ });
123
+
124
+ test("returns without calling the service when deps singleton is not yet initialized", async () => {
125
+ // Deps singleton is unset during slow daemon startup. The handler must
126
+ // NOT throw — a plain Error here is classified as fatal by
127
+ // classifyError() and the worker would mark the job permanently failed
128
+ // (failMemoryJob with maxAttempts: 1). Returning lets the next batch /
129
+ // idle / lifecycle trigger from enqueueAutoAnalysisIfEnabled() produce
130
+ // a fresh job once the daemon has fully started.
131
+ mockGetAnalysisDeps.mockImplementation(() => null);
132
+ await expect(
133
+ conversationAnalyzeJob(
134
+ makeJob({ conversationId: "conv-1" }),
135
+ TEST_CONFIG,
136
+ ),
137
+ ).resolves.toBeUndefined();
138
+ expect(analyzeCalls).toHaveLength(0);
139
+ expect(mockGetAnalysisDeps).toHaveBeenCalled();
140
+ });
141
+
142
+ test("invokes analyzeConversation with trigger=auto and the conversationId", async () => {
143
+ const depsStub: DepsStub = { _tag: "deps-stub" };
144
+ mockGetAnalysisDeps.mockImplementation(() => depsStub);
145
+
146
+ await conversationAnalyzeJob(
147
+ makeJob({ conversationId: "conv-42" }),
148
+ TEST_CONFIG,
149
+ );
150
+
151
+ expect(analyzeCalls).toHaveLength(1);
152
+ expect(analyzeCalls[0]!.conversationId).toBe("conv-42");
153
+ expect(analyzeCalls[0]!.opts).toEqual({ trigger: "auto" });
154
+ expect(analyzeCalls[0]!.deps).toBe(depsStub);
155
+ });
156
+
157
+ test("requeues a follow-up idle trigger when the service returns skipped=true", async () => {
158
+ // If the rolling analysis conversation is already processing, the
159
+ // service returns { skipped: true } without running. The job handler
160
+ // completes successfully (no retry), so we must enqueue a follow-up
161
+ // ourselves — otherwise, if no later batch/idle/lifecycle trigger
162
+ // arrives, new source messages would never be analyzed.
163
+ mockGetAnalysisDeps.mockImplementation(() => ({ _tag: "deps" }));
164
+ mockAnalyzeConversation.mockImplementation(async () => ({
165
+ analysisConversationId: "analysis-1",
166
+ skipped: true as const,
167
+ }));
168
+
169
+ await conversationAnalyzeJob(
170
+ makeJob({ conversationId: "conv-busy" }),
171
+ TEST_CONFIG,
172
+ );
173
+
174
+ expect(enqueueCalls).toHaveLength(1);
175
+ expect(enqueueCalls[0]).toEqual({
176
+ conversationId: "conv-busy",
177
+ trigger: "idle",
178
+ });
179
+ });
180
+
181
+ test("does not requeue on a normal (non-skipped) successful run", async () => {
182
+ mockGetAnalysisDeps.mockImplementation(() => ({ _tag: "deps" }));
183
+ mockAnalyzeConversation.mockImplementation(async () => ({
184
+ analysisConversationId: "analysis-1",
185
+ }));
186
+
187
+ await conversationAnalyzeJob(
188
+ makeJob({ conversationId: "conv-ok" }),
189
+ TEST_CONFIG,
190
+ );
191
+
192
+ expect(enqueueCalls).toHaveLength(0);
193
+ });
194
+
195
+ test("does not requeue when the service returns an error result", async () => {
196
+ mockGetAnalysisDeps.mockImplementation(() => ({ _tag: "deps" }));
197
+ mockAnalyzeConversation.mockImplementation(async () => ({
198
+ error: {
199
+ kind: "BAD_REQUEST",
200
+ status: 400,
201
+ message: "Cannot auto-analyze an auto-analysis conversation",
202
+ },
203
+ }));
204
+
205
+ await conversationAnalyzeJob(
206
+ makeJob({ conversationId: "conv-err" }),
207
+ TEST_CONFIG,
208
+ );
209
+
210
+ expect(enqueueCalls).toHaveLength(0);
211
+ });
212
+
213
+ test("swallows (does not throw) when the service returns an error result", async () => {
214
+ mockGetAnalysisDeps.mockImplementation(() => ({ _tag: "deps" }));
215
+ mockAnalyzeConversation.mockImplementation(async () => ({
216
+ error: {
217
+ kind: "BAD_REQUEST",
218
+ status: 400,
219
+ message: "Cannot auto-analyze an auto-analysis conversation",
220
+ },
221
+ }));
222
+
223
+ // Must not throw — the worker would otherwise retry forever on a
224
+ // deterministic rejection (e.g. the recursion guard).
225
+ await expect(
226
+ conversationAnalyzeJob(
227
+ makeJob({ conversationId: "conv-2" }),
228
+ TEST_CONFIG,
229
+ ),
230
+ ).resolves.toBeUndefined();
231
+ });
232
+ });
@@ -0,0 +1,196 @@
1
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
2
+
3
+ import { eq } from "drizzle-orm";
4
+
5
+ mock.module("../../util/logger.js", () => ({
6
+ getLogger: () =>
7
+ new Proxy({} as Record<string, unknown>, {
8
+ get: () => () => {},
9
+ }),
10
+ }));
11
+
12
+ import {
13
+ createConversation,
14
+ findAnalysisConversationFor,
15
+ getConversationSource,
16
+ } from "../conversation-crud.js";
17
+ import { getDb, initializeDb } from "../db.js";
18
+ import { conversations } from "../schema.js";
19
+
20
+ initializeDb();
21
+
22
+ function resetTables(): void {
23
+ const db = getDb();
24
+ db.run(`DELETE FROM messages`);
25
+ db.run(`DELETE FROM conversations`);
26
+ }
27
+
28
+ function setForkParent(conversationId: string, parentId: string): void {
29
+ const db = getDb();
30
+ db.update(conversations)
31
+ .set({ forkParentConversationId: parentId })
32
+ .where(eq(conversations.id, conversationId))
33
+ .run();
34
+ }
35
+
36
+ function setUpdatedAt(conversationId: string, updatedAt: number): void {
37
+ const db = getDb();
38
+ db.update(conversations)
39
+ .set({ updatedAt })
40
+ .where(eq(conversations.id, conversationId))
41
+ .run();
42
+ }
43
+
44
+ describe("findAnalysisConversationFor", () => {
45
+ beforeEach(() => {
46
+ resetTables();
47
+ });
48
+
49
+ test("returns null when no analysis conversation exists for the parent", () => {
50
+ const parent = createConversation("parent");
51
+ expect(findAnalysisConversationFor(parent.id)).toBeNull();
52
+ });
53
+
54
+ test("returns null when an unrelated analysis conversation exists", () => {
55
+ const parent = createConversation("parent");
56
+ const other = createConversation("other");
57
+ const analysisForOther = createConversation({
58
+ title: "analysis of other",
59
+ source: "auto-analysis",
60
+ });
61
+ setForkParent(analysisForOther.id, other.id);
62
+
63
+ expect(findAnalysisConversationFor(parent.id)).toBeNull();
64
+ });
65
+
66
+ test("returns the only matching analysis conversation when one exists", () => {
67
+ const parent = createConversation("parent");
68
+ const analysis = createConversation({
69
+ title: "rolling analysis",
70
+ source: "auto-analysis",
71
+ });
72
+ setForkParent(analysis.id, parent.id);
73
+
74
+ expect(findAnalysisConversationFor(parent.id)).toEqual({ id: analysis.id });
75
+ });
76
+
77
+ test("when multiple match, returns the most recently updated one", () => {
78
+ const parent = createConversation("parent");
79
+
80
+ const older = createConversation({
81
+ title: "older analysis",
82
+ source: "auto-analysis",
83
+ });
84
+ setForkParent(older.id, parent.id);
85
+ setUpdatedAt(older.id, 1_000);
86
+
87
+ const newer = createConversation({
88
+ title: "newer analysis",
89
+ source: "auto-analysis",
90
+ });
91
+ setForkParent(newer.id, parent.id);
92
+ setUpdatedAt(newer.id, 2_000);
93
+
94
+ const middle = createConversation({
95
+ title: "middle analysis",
96
+ source: "auto-analysis",
97
+ });
98
+ setForkParent(middle.id, parent.id);
99
+ setUpdatedAt(middle.id, 1_500);
100
+
101
+ expect(findAnalysisConversationFor(parent.id)).toEqual({ id: newer.id });
102
+ });
103
+
104
+ test("does not return regular user conversations whose forkParentConversationId matches", () => {
105
+ const parent = createConversation("parent");
106
+
107
+ // A regular user-forked conversation (source defaults to "user").
108
+ const userFork = createConversation("user fork");
109
+ setForkParent(userFork.id, parent.id);
110
+
111
+ expect(findAnalysisConversationFor(parent.id)).toBeNull();
112
+ });
113
+
114
+ test("ignores user forks even when an analysis conversation also exists", () => {
115
+ const parent = createConversation("parent");
116
+
117
+ const userFork = createConversation("user fork");
118
+ setForkParent(userFork.id, parent.id);
119
+ // Force the user fork to have the most recent updatedAt — it should
120
+ // still be ignored because its source is "user".
121
+ setUpdatedAt(userFork.id, 9_999);
122
+
123
+ const analysis = createConversation({
124
+ title: "analysis",
125
+ source: "auto-analysis",
126
+ });
127
+ setForkParent(analysis.id, parent.id);
128
+ setUpdatedAt(analysis.id, 1_000);
129
+
130
+ expect(findAnalysisConversationFor(parent.id)).toEqual({ id: analysis.id });
131
+ });
132
+
133
+ test("createConversation persists forkParentConversationId when supplied", () => {
134
+ const parent = createConversation("parent");
135
+
136
+ // Auto-analyze path creates the rolling analysis conversation with
137
+ // source + forkParentConversationId in the same call.
138
+ const analysis = createConversation({
139
+ title: "rolling analysis",
140
+ source: "auto-analysis",
141
+ forkParentConversationId: parent.id,
142
+ });
143
+
144
+ expect(findAnalysisConversationFor(parent.id)).toEqual({ id: analysis.id });
145
+ });
146
+
147
+ test("finds rolling analysis conversation regardless of group_id (backward-compat across the dedicated-group migration)", () => {
148
+ const parent = createConversation("parent");
149
+
150
+ const legacyAnalysis = createConversation({
151
+ title: "legacy rolling analysis",
152
+ source: "auto-analysis",
153
+ forkParentConversationId: parent.id,
154
+ });
155
+ setUpdatedAt(legacyAnalysis.id, 1_000);
156
+
157
+ expect(findAnalysisConversationFor(parent.id)).toEqual({
158
+ id: legacyAnalysis.id,
159
+ });
160
+
161
+ const newAnalysis = createConversation({
162
+ title: "new rolling analysis",
163
+ source: "auto-analysis",
164
+ groupId: "system:background",
165
+ forkParentConversationId: parent.id,
166
+ });
167
+ setUpdatedAt(newAnalysis.id, 2_000);
168
+
169
+ expect(findAnalysisConversationFor(parent.id)).toEqual({
170
+ id: newAnalysis.id,
171
+ });
172
+ });
173
+ });
174
+
175
+ describe("getConversationSource", () => {
176
+ beforeEach(() => {
177
+ resetTables();
178
+ });
179
+
180
+ test("returns the source string for an existing conversation", () => {
181
+ const conv = createConversation("user conv");
182
+ expect(getConversationSource(conv.id)).toBe("user");
183
+ });
184
+
185
+ test("returns the custom source for an analysis conversation", () => {
186
+ const conv = createConversation({
187
+ title: "analysis",
188
+ source: "auto-analysis",
189
+ });
190
+ expect(getConversationSource(conv.id)).toBe("auto-analysis");
191
+ });
192
+
193
+ test("returns null for a non-existent conversation ID", () => {
194
+ expect(getConversationSource("does-not-exist")).toBeNull();
195
+ });
196
+ });
@@ -78,7 +78,7 @@ export function resolveEffectiveAppHtml(app: AppDefinition): string {
78
78
  if (existsSync(distIndex)) {
79
79
  return inlineDistAssets(appDir, readFileSync(distIndex, "utf-8"));
80
80
  }
81
- return app.htmlDefinition;
81
+ return `<p>App compilation failed. Edit a source file to trigger a rebuild.</p>`;
82
82
  }
83
83
 
84
84
  /**
@@ -11,6 +11,7 @@ import {
11
11
  existsSync,
12
12
  mkdirSync,
13
13
  readFileSync,
14
+ realpathSync,
14
15
  unlinkSync,
15
16
  writeFileSync,
16
17
  } from "node:fs";
@@ -264,7 +265,40 @@ function materializeAttachmentIntoConversation(
264
265
  copyFileSync(readablePath, targetPath);
265
266
  }
266
267
 
268
+ // Remember the old file path before updating the DB row, so we can
269
+ // clean up the staging copy (e.g. in data/attachments/) after the
270
+ // canonical path moves to the conversation directory.
271
+ const previousFilePath = row.filePath;
272
+
267
273
  persistAttachmentFilePath(row.id, targetPath, sourcePath);
274
+
275
+ // Remove the old staging file now that the canonical copy lives in
276
+ // the conversation directory. Only delete files that live in the
277
+ // staging area (workspace/data/attachments/). When an attachment is
278
+ // cloned across conversations (e.g. during a fork), previousFilePath
279
+ // may point to another conversation's directory — deleting that would
280
+ // cause data loss for the source conversation.
281
+ const stagingDirRaw = join(getWorkspaceDir(), "data", "attachments");
282
+ let stagingDir: string;
283
+ try {
284
+ stagingDir = existsSync(stagingDirRaw)
285
+ ? realpathSync(stagingDirRaw)
286
+ : stagingDirRaw;
287
+ } catch {
288
+ stagingDir = stagingDirRaw;
289
+ }
290
+ if (
291
+ previousFilePath &&
292
+ previousFilePath !== targetPath &&
293
+ dirname(previousFilePath) === stagingDir &&
294
+ existsSync(previousFilePath)
295
+ ) {
296
+ try {
297
+ unlinkSync(previousFilePath);
298
+ } catch {
299
+ /* file may already be gone */
300
+ }
301
+ }
268
302
  }
269
303
 
270
304
  function scopeAttachmentToConversation(
@@ -458,6 +492,42 @@ export function validateAttachmentUpload(
458
492
  return { ok: true };
459
493
  }
460
494
 
495
+ // ---------------------------------------------------------------------------
496
+ // Binary upload helper (multipart / octet-stream)
497
+ // ---------------------------------------------------------------------------
498
+
499
+ /**
500
+ * Write raw bytes to the staging directory and register as a file-backed
501
+ * attachment. Used by the multipart/form-data and application/octet-stream
502
+ * upload paths.
503
+ *
504
+ * @param filename Original filename from the client
505
+ * @param mimeType MIME type of the file
506
+ * @param bytes Raw file content
507
+ * @returns The stored attachment record
508
+ */
509
+ export function uploadAttachmentFromBytes(
510
+ filename: string,
511
+ mimeType: string,
512
+ bytes: Uint8Array,
513
+ ): StoredAttachment {
514
+ const dir = join(getWorkspaceDir(), "data", "attachments");
515
+ mkdirSync(dir, { recursive: true });
516
+
517
+ const sanitized = filename.replace(/[^a-zA-Z0-9._-]/g, "_");
518
+ const stagingFilename = `${Date.now()}-${uuid().slice(0, 8)}-${sanitized}`;
519
+ const stagedPath = join(dir, stagingFilename);
520
+
521
+ writeFileSync(stagedPath, bytes);
522
+
523
+ return uploadFileBackedAttachment(
524
+ filename,
525
+ mimeType,
526
+ stagedPath,
527
+ bytes.length,
528
+ );
529
+ }
530
+
461
531
  // ---------------------------------------------------------------------------
462
532
  // File-backed attachment storage (avoids reading large files into memory)
463
533
  // ---------------------------------------------------------------------------
@@ -0,0 +1,127 @@
1
+ import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
2
+ import { getConfig } from "../config/loader.js";
3
+ import {
4
+ isUntrustedTrustClass,
5
+ type TrustClass,
6
+ } from "../runtime/actor-trust-resolver.js";
7
+ import { getLogger } from "../util/logger.js";
8
+ import { isAutoAnalysisConversation } from "./auto-analysis-guard.js";
9
+ import { getConversationType } from "./conversation-crud.js";
10
+ import { upsertAutoAnalysisJob } from "./jobs-store.js";
11
+
12
+ const log = getLogger("auto-analysis-enqueue");
13
+
14
+ /**
15
+ * Trigger reason for an auto-analysis enqueue.
16
+ * - `"batch"`: source conversation crossed the batch threshold — enqueue
17
+ * immediately (`runAfter = now`) but still upsert so a pending job
18
+ * coalesces rapid threshold crossings into one.
19
+ * - `"idle"`: source conversation has been idle long enough to warrant a
20
+ * debounced analysis pass.
21
+ * - `"lifecycle"`: a conversation lifecycle transition (e.g. resume,
22
+ * close) should trigger a debounced analysis pass.
23
+ * - `"compaction"`: context was just compacted — some recent turns are
24
+ * now hidden behind a summary, so crystallize anything worth
25
+ * remembering before the window narrows further. Fires immediately
26
+ * (`runAfter = now`) like `"batch"`.
27
+ */
28
+ export type AutoAnalysisTrigger =
29
+ | "batch"
30
+ | "idle"
31
+ | "lifecycle"
32
+ | "compaction";
33
+
34
+ /**
35
+ * Conditionally enqueue a `conversation_analyze` job for the given
36
+ * conversation. Skips silently when:
37
+ * - the `auto-analyze` feature flag is disabled, OR
38
+ * - the source conversation is itself an auto-analysis conversation
39
+ * (recursion guard — we never analyze our own analysis output), OR
40
+ * - the source conversation is private (`analyzeConversation` rejects
41
+ * private conversations, so enqueueing would guarantee a failed job).
42
+ *
43
+ * Immediate triggers (`"batch"`, `"compaction"`) and debounced triggers
44
+ * (`"idle"`, `"lifecycle"`) are written to separate rows keyed by a
45
+ * `triggerGroup` discriminator. This prevents an idle enqueue from
46
+ * pushing an already-scheduled batch row's `runAfter` into the future
47
+ * (and vice versa). Within each group, rapid enqueues still coalesce to
48
+ * a single pending row via `upsertAutoAnalysisJob`.
49
+ */
50
+ export function enqueueAutoAnalysisIfEnabled(args: {
51
+ conversationId: string;
52
+ trigger: AutoAnalysisTrigger;
53
+ }): void {
54
+ const { conversationId, trigger } = args;
55
+
56
+ let config;
57
+ try {
58
+ config = getConfig();
59
+ } catch (err) {
60
+ log.warn(
61
+ { err, conversationId },
62
+ "Skipping auto-analysis enqueue: failed to load config",
63
+ );
64
+ return;
65
+ }
66
+
67
+ if (!isAssistantFeatureFlagEnabled("auto-analyze", config)) {
68
+ return;
69
+ }
70
+
71
+ if (isAutoAnalysisConversation(conversationId)) {
72
+ log.debug(
73
+ { conversationId, trigger },
74
+ "Skipping auto-analysis enqueue: source is an auto-analysis conversation",
75
+ );
76
+ return;
77
+ }
78
+
79
+ if (getConversationType(conversationId) === "private") {
80
+ log.debug(
81
+ { conversationId, trigger },
82
+ "Skipping auto-analysis enqueue: source is a private conversation",
83
+ );
84
+ return;
85
+ }
86
+
87
+ const idleTimeoutMs = config.analysis?.idleTimeoutMs ?? 600_000;
88
+ const runImmediately = trigger === "batch" || trigger === "compaction";
89
+ const triggerGroup: "immediate" | "debounced" = runImmediately
90
+ ? "immediate"
91
+ : "debounced";
92
+ const runAfter = runImmediately ? Date.now() : Date.now() + idleTimeoutMs;
93
+
94
+ try {
95
+ upsertAutoAnalysisJob({ conversationId, triggerGroup }, runAfter);
96
+ } catch (err) {
97
+ log.warn(
98
+ { err, conversationId, trigger },
99
+ "Failed to enqueue auto-analysis job",
100
+ );
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Fire an auto-analysis enqueue from a compaction site. Wraps
106
+ * `enqueueAutoAnalysisIfEnabled` with the trust-class gate and
107
+ * best-effort error handling used at every compaction call site, so
108
+ * the six compaction paths (forceCompact, preflight, overflow reducer,
109
+ * mid-loop, and two emergency paths) stay in sync.
110
+ *
111
+ * Trust gate mirrors the memory-extraction trust boundary applied in
112
+ * `disposeConversation` — we don't trigger analysis (which runs with
113
+ * guardian trust + full tools) for conversations with an untrusted actor.
114
+ */
115
+ export function enqueueAutoAnalysisOnCompaction(
116
+ conversationId: string,
117
+ trustClass: TrustClass | undefined,
118
+ ): void {
119
+ if (isUntrustedTrustClass(trustClass)) {
120
+ return;
121
+ }
122
+ try {
123
+ enqueueAutoAnalysisIfEnabled({ conversationId, trigger: "compaction" });
124
+ } catch {
125
+ // Best-effort — never block compaction on enqueue failures.
126
+ }
127
+ }
@@ -0,0 +1,27 @@
1
+ import { getConversationSource } from "./conversation-crud.js";
2
+
3
+ /**
4
+ * The `source` value used for conversations created by the auto-analysis
5
+ * loop. Single source of truth — downstream code (enqueue helper,
6
+ * service auto-branch) imports this constant rather than hardcoding the
7
+ * string.
8
+ */
9
+ export const AUTO_ANALYSIS_SOURCE = "auto-analysis";
10
+
11
+ /**
12
+ * Dedicated `group_id` value for auto-analysis rolling conversations.
13
+ * Placed in the `system:background` group alongside heartbeat and filing
14
+ * conversations, rendered as a "Reflections" sub-group in the sidebar.
15
+ */
16
+ export const AUTO_ANALYSIS_GROUP_ID = "system:background";
17
+
18
+ /**
19
+ * Returns true if the conversation's `source` column is `"auto-analysis"`,
20
+ * meaning it was produced by the auto-analysis loop. Callers use this to
21
+ * skip both `graph_extract` and `conversation_analyze` enqueues so we
22
+ * never (a) analyze our own analysis output or (b) extract memory from
23
+ * reflective musings (the analysis agent writes memory directly via tools).
24
+ */
25
+ export function isAutoAnalysisConversation(conversationId: string): boolean {
26
+ return getConversationSource(conversationId) === AUTO_ANALYSIS_SOURCE;
27
+ }