@vellumai/assistant 0.8.4 → 0.8.5

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 (438) hide show
  1. package/ARCHITECTURE.md +2 -2
  2. package/docs/browser-use-architecture-phase2.md +1 -1
  3. package/knip.json +2 -1
  4. package/openapi.yaml +809 -11
  5. package/package.json +1 -1
  6. package/src/__tests__/anthropic-provider.test.ts +34 -37
  7. package/src/__tests__/assistant-event-hub-self-exclusion.test.ts +293 -0
  8. package/src/__tests__/assistant-feature-flags-integration.test.ts +3 -3
  9. package/src/__tests__/audit-log-rotation.test.ts +70 -16
  10. package/src/__tests__/background-workers-disk-pressure.test.ts +3 -3
  11. package/src/__tests__/btw-routes.test.ts +2 -3
  12. package/src/__tests__/call-controller.test.ts +0 -1
  13. package/src/__tests__/cancel-resolves-conversation-key.test.ts +1 -1
  14. package/src/__tests__/channel-guardian.test.ts +3 -3
  15. package/src/__tests__/checker.test.ts +6 -15
  16. package/src/__tests__/compaction-events.test.ts +1 -0
  17. package/src/__tests__/compactor-call-site-logging.test.ts +214 -0
  18. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +5 -11
  19. package/src/__tests__/computer-use-tools.test.ts +2 -4
  20. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -1
  21. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +1 -1
  22. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -1
  23. package/src/__tests__/conversation-agent-loop-overflow.test.ts +197 -2
  24. package/src/__tests__/conversation-agent-loop.test.ts +163 -122
  25. package/src/__tests__/conversation-app-control-instantiation.test.ts +2 -5
  26. package/src/__tests__/conversation-clear-safety.test.ts +25 -25
  27. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +1 -1
  28. package/src/__tests__/conversation-disk-view-integration.test.ts +2 -2
  29. package/src/__tests__/conversation-error.test.ts +31 -0
  30. package/src/__tests__/conversation-fork-crud.test.ts +178 -15
  31. package/src/__tests__/conversation-lifecycle.test.ts +52 -11
  32. package/src/__tests__/{conversation-load-cleaned-at.test.ts → conversation-load-history-stripped.test.ts} +13 -13
  33. package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -0
  34. package/src/__tests__/conversation-routes-disk-view.test.ts +109 -0
  35. package/src/__tests__/conversation-routes-slash-commands.test.ts +35 -0
  36. package/src/__tests__/conversation-skill-tools.test.ts +2 -5
  37. package/src/__tests__/conversation-store.test.ts +1 -1
  38. package/src/__tests__/conversation-sync-tags.test.ts +99 -32
  39. package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -0
  40. package/src/__tests__/conversation-workspace-injection.test.ts +1 -1
  41. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -1
  42. package/src/__tests__/credential-execution-feature-gates.test.ts +9 -7
  43. package/src/__tests__/credential-execution-tools.test.ts +6 -6
  44. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  45. package/src/__tests__/credential-vault-unit.test.ts +2 -2
  46. package/src/__tests__/dynamic-page-surface.test.ts +2 -2
  47. package/src/__tests__/email-html-renderer.test.ts +12 -0
  48. package/src/__tests__/gateway-flag-listener.test.ts +237 -0
  49. package/src/__tests__/gemini-provider.test.ts +78 -0
  50. package/src/__tests__/guardian-dispatch.test.ts +0 -1
  51. package/src/__tests__/guardian-outbound-http.test.ts +7 -5
  52. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
  53. package/src/__tests__/heartbeat-disk-pressure.test.ts +4 -0
  54. package/src/__tests__/heartbeat-service.test.ts +4 -0
  55. package/src/__tests__/host-shell-tool.test.ts +1 -1
  56. package/src/__tests__/init-feature-flag-overrides.test.ts +5 -6
  57. package/src/__tests__/list-messages-tool-merge.test.ts +70 -11
  58. package/src/__tests__/llm-request-log-call-site.test.ts +136 -0
  59. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +26 -0
  60. package/src/__tests__/llm-resolver.test.ts +77 -9
  61. package/src/__tests__/llm-usage-store.test.ts +66 -0
  62. package/src/__tests__/logger.test.ts +89 -0
  63. package/src/__tests__/mcp-abort-signal.test.ts +2 -2
  64. package/src/__tests__/media-generate-image.test.ts +31 -0
  65. package/src/__tests__/memory-v2-static-injector.test.ts +7 -7
  66. package/src/__tests__/model-intents.test.ts +2 -4
  67. package/src/__tests__/notification-guardian-path.test.ts +0 -1
  68. package/src/__tests__/onboarding-template-contract.test.ts +1 -1
  69. package/src/__tests__/openai-provider.test.ts +46 -0
  70. package/src/__tests__/openai-responses-provider.test.ts +114 -12
  71. package/src/__tests__/pending-interactions-resolved-event.test.ts +0 -1
  72. package/src/__tests__/platform-bash-auto-approve.test.ts +2 -2
  73. package/src/__tests__/platform.test.ts +2 -2
  74. package/src/__tests__/plugin-api-tool-definition.test.ts +92 -0
  75. package/src/__tests__/plugin-bootstrap.test.ts +2 -2
  76. package/src/__tests__/plugin-tool-contribution.test.ts +13 -6
  77. package/src/__tests__/plugin-types.test.ts +3 -2
  78. package/src/__tests__/prechat-onboarding-contract.test.ts +131 -98
  79. package/src/__tests__/pricing.test.ts +12 -0
  80. package/src/__tests__/prune-jobs-changes-parser.test.ts +61 -0
  81. package/src/__tests__/registry.test.ts +2 -8
  82. package/src/__tests__/require-fresh-approval.test.ts +2 -2
  83. package/src/__tests__/runtime-events-sse-bilingual.test.ts +154 -0
  84. package/src/__tests__/shell-tool-proxy-mode.test.ts +1 -1
  85. package/src/__tests__/skill-feature-flags.test.ts +2 -2
  86. package/src/__tests__/skill-projection-feature-flag.test.ts +4 -7
  87. package/src/__tests__/skill-projection.benchmark.test.ts +2 -6
  88. package/src/__tests__/skill-tool-factory.test.ts +1 -1
  89. package/src/__tests__/subagent-notify-parent.test.ts +1 -1
  90. package/src/__tests__/suggestion-routes.test.ts +1 -0
  91. package/src/__tests__/sync-message-contract.test.ts +59 -0
  92. package/src/__tests__/system-prompt.test.ts +145 -131
  93. package/src/__tests__/terminal-tools.test.ts +1 -1
  94. package/src/__tests__/tool-approval-handler.test.ts +1 -5
  95. package/src/__tests__/tool-execute-pipeline.test.ts +2 -2
  96. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -5
  97. package/src/__tests__/tool-executor-lifecycle-events.test.ts +15 -5
  98. package/src/__tests__/tool-executor.test.ts +9 -62
  99. package/src/__tests__/tool-grant-request-escalation.test.ts +1 -6
  100. package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
  101. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +1 -6
  102. package/src/__tests__/trusted-contact-multichannel.test.ts +0 -1
  103. package/src/__tests__/ui-file-upload-surface.test.ts +2 -2
  104. package/src/__tests__/usage-routes.test.ts +3 -0
  105. package/src/__tests__/verification-control-plane-policy.test.ts +2 -2
  106. package/src/__tests__/workspace-git-service.test.ts +6 -5
  107. package/src/__tests__/workspace-migration-089-move-memory-tree-out-of-v3.test.ts +86 -0
  108. package/src/acp/__tests__/prepare-agent-env.test.ts +146 -0
  109. package/src/acp/prepare-agent-env.ts +78 -0
  110. package/src/acp/session-manager.ts +1 -1
  111. package/src/agent/loop.ts +8 -0
  112. package/src/api/README.md +5 -0
  113. package/src/api/index.ts +4 -0
  114. package/src/api/package.json +10 -0
  115. package/src/background-wake/background-wake-routes.test.ts +233 -0
  116. package/src/background-wake/runtime-registry.ts +24 -0
  117. package/src/cli/commands/__tests__/browser.test.ts +23 -5
  118. package/src/cli/commands/__tests__/domain-register.test.ts +110 -0
  119. package/src/cli/commands/__tests__/domain-status.test.ts +33 -33
  120. package/src/cli/commands/__tests__/inference-send.test.ts +108 -5
  121. package/src/cli/commands/__tests__/memory-v2-compare-render.test.ts +98 -0
  122. package/src/cli/commands/__tests__/memory-v2.test.ts +1 -0
  123. package/src/cli/commands/__tests__/memory-v3-render.test.ts +340 -0
  124. package/src/cli/commands/browser.ts +247 -0
  125. package/src/cli/commands/domain.ts +91 -41
  126. package/src/cli/commands/inference.ts +93 -40
  127. package/src/cli/commands/memory-v2-compare-render.ts +115 -0
  128. package/src/cli/commands/memory-v2.ts +176 -1
  129. package/src/cli/commands/memory-v3-render.ts +344 -0
  130. package/src/cli/commands/memory-v3.ts +316 -0
  131. package/src/cli/program.ts +2 -0
  132. package/src/config/assistant-feature-flags.ts +21 -9
  133. package/src/config/bundled-skills/document-editor/SKILL.md +11 -2
  134. package/src/config/bundled-skills/document-editor/TOOLS.json +18 -0
  135. package/src/config/bundled-skills/document-editor/tools/document-open.ts +12 -0
  136. package/src/config/bundled-skills/image-studio/SKILL.md +4 -0
  137. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +2 -2
  138. package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +13 -8
  139. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +10 -3
  140. package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +16 -14
  141. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +7 -2
  142. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +7 -2
  143. package/src/config/bundled-tool-registry.ts +2 -0
  144. package/src/config/call-site-defaults.ts +7 -6
  145. package/src/config/feature-flag-registry.json +16 -0
  146. package/src/config/schemas/__tests__/memory-v2.test.ts +213 -1
  147. package/src/config/schemas/call-site-catalog.ts +21 -7
  148. package/src/config/schemas/llm.ts +12 -1
  149. package/src/config/schemas/memory-v2.ts +246 -0
  150. package/src/config/schemas/memory.ts +2 -1
  151. package/src/context/compactor.ts +52 -0
  152. package/src/conversations/__tests__/message-consolidation.test.ts +350 -0
  153. package/src/conversations/message-consolidation.ts +404 -0
  154. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +1 -1
  155. package/src/daemon/__tests__/meet-manifest-loader.test.ts +1 -1
  156. package/src/daemon/conversation-agent-loop-handlers.ts +2 -13
  157. package/src/daemon/conversation-agent-loop.ts +126 -76
  158. package/src/daemon/conversation-error.ts +31 -1
  159. package/src/daemon/conversation-lifecycle.ts +27 -22
  160. package/src/daemon/conversation-runtime-assembly.ts +10 -9
  161. package/src/daemon/conversation-tool-setup.ts +63 -3
  162. package/src/daemon/conversation-usage.ts +2 -0
  163. package/src/daemon/conversation.ts +14 -29
  164. package/src/daemon/disk-pressure-guard.ts +14 -2
  165. package/src/daemon/handlers/config-model.test.ts +1 -0
  166. package/src/daemon/handlers/conversations.ts +11 -3
  167. package/src/daemon/host-browser-proxy.ts +5 -5
  168. package/src/daemon/host-cu-proxy.ts +4 -4
  169. package/src/daemon/host-file-proxy.ts +4 -4
  170. package/src/daemon/host-proxy-base.ts +4 -4
  171. package/src/daemon/host-transfer-proxy.ts +10 -10
  172. package/src/daemon/lifecycle.ts +23 -20
  173. package/src/daemon/meet-manifest-loader.ts +1 -7
  174. package/src/daemon/message-types/conversations.ts +6 -9
  175. package/src/daemon/message-types/home.ts +1 -13
  176. package/src/daemon/message-types/messages.ts +6 -14
  177. package/src/daemon/message-types/sync.ts +14 -0
  178. package/src/daemon/shutdown-handlers.ts +24 -5
  179. package/src/daemon/switch-inference-profile-tool.ts +52 -0
  180. package/src/daemon/tool-setup-types.ts +13 -0
  181. package/src/events/relationship-state-updated.ts +25 -0
  182. package/src/heartbeat/__tests__/heartbeat-service.test.ts +1 -1
  183. package/src/home/home-greeting.ts +0 -9
  184. package/src/home/suggested-prompts.ts +0 -9
  185. package/src/ipc/gateway-flag-listener.ts +123 -0
  186. package/src/ipc/skill-routes/registries.ts +8 -12
  187. package/src/memory/__tests__/db-async-query.test.ts +165 -0
  188. package/src/memory/__tests__/db-maintenance.test.ts +115 -0
  189. package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +241 -0
  190. package/src/memory/__tests__/jobs-store-job-classes.test.ts +28 -1
  191. package/src/memory/__tests__/memory-retrospective-job.test.ts +7 -0
  192. package/src/memory/auto-analysis-enqueue.ts +5 -1
  193. package/src/memory/conversation-crud.ts +71 -70
  194. package/src/memory/conversation-starters-cadence.ts +3 -1
  195. package/src/memory/conversation-title-service.ts +19 -3
  196. package/src/memory/db-async-query.ts +214 -0
  197. package/src/memory/db-init.ts +10 -0
  198. package/src/memory/db-maintenance.ts +30 -21
  199. package/src/memory/graph/bootstrap.ts +8 -1
  200. package/src/memory/graph/capability-seed.ts +7 -3
  201. package/src/memory/graph/conversation-graph-memory.ts +100 -17
  202. package/src/memory/graph/extraction.ts +1 -5
  203. package/src/memory/graph/graph-search.ts +7 -1
  204. package/src/memory/indexer.ts +28 -18
  205. package/src/memory/job-handlers/cleanup.ts +76 -18
  206. package/src/memory/job-handlers/conversation-starters.ts +1 -4
  207. package/src/memory/jobs/embed-pkb-file.ts +6 -1
  208. package/src/memory/jobs-store.ts +14 -0
  209. package/src/memory/jobs-worker.ts +55 -22
  210. package/src/memory/llm-request-log-source-clickhouse.ts +42 -2
  211. package/src/memory/llm-request-log-source-local.ts +7 -0
  212. package/src/memory/llm-request-log-source.ts +9 -2
  213. package/src/memory/llm-request-log-store.ts +43 -1
  214. package/src/memory/llm-usage-store.ts +24 -0
  215. package/src/memory/memory-retrospective-enqueue.ts +8 -1
  216. package/src/memory/memory-retrospective-job.ts +5 -0
  217. package/src/memory/memory-v2-activation-log-store.ts +15 -6
  218. package/src/memory/migrations/260-rename-cleaned-at.ts +44 -0
  219. package/src/memory/migrations/261-llm-usage-add-raw-usage.ts +36 -0
  220. package/src/memory/migrations/262-memory-v3-coactivation.ts +57 -0
  221. package/src/memory/migrations/263-memory-v3-auto-edges.ts +50 -0
  222. package/src/memory/migrations/264-llm-request-log-call-site.ts +29 -0
  223. package/src/memory/migrations/index.ts +17 -0
  224. package/src/memory/migrations/registry.ts +33 -0
  225. package/src/memory/schema/conversations.ts +1 -1
  226. package/src/memory/schema/infrastructure.ts +21 -0
  227. package/src/memory/tool-usage-store.ts +36 -8
  228. package/src/memory/v2/__tests__/consolidation-job.test.ts +1 -0
  229. package/src/memory/v2/__tests__/harness-compare.test.ts +186 -0
  230. package/src/memory/v2/__tests__/harness-metrics.test.ts +74 -0
  231. package/src/memory/v2/__tests__/harness-oracle.test.ts +257 -0
  232. package/src/memory/v2/__tests__/harness-replay-input.test.ts +225 -0
  233. package/src/memory/v2/__tests__/harness-runner.test.ts +109 -0
  234. package/src/memory/v2/__tests__/injection.test.ts +127 -98
  235. package/src/memory/v2/__tests__/qdrant.test.ts +36 -0
  236. package/src/memory/v2/__tests__/router.test.ts +171 -3
  237. package/src/memory/v2/harness/compare.ts +57 -0
  238. package/src/memory/v2/harness/metrics.ts +124 -0
  239. package/src/memory/v2/harness/oracle.ts +145 -0
  240. package/src/memory/v2/harness/replay-input.ts +224 -0
  241. package/src/memory/v2/harness/retriever.ts +74 -0
  242. package/src/memory/v2/harness/router-retriever.ts +43 -0
  243. package/src/memory/v2/harness/runner.ts +106 -0
  244. package/src/memory/v2/harness/trace.ts +58 -0
  245. package/src/memory/v2/injection.ts +21 -15
  246. package/src/memory/v2/prompts/router.ts +26 -1
  247. package/src/memory/v2/qdrant.ts +14 -2
  248. package/src/memory/v2/router.ts +171 -18
  249. package/src/memory/v3/__tests__/coactivation-store.test.ts +422 -0
  250. package/src/memory/v3/__tests__/consolidation-job.test.ts +468 -0
  251. package/src/memory/v3/__tests__/edge-learning-job.test.ts +324 -0
  252. package/src/memory/v3/__tests__/edges.test.ts +563 -0
  253. package/src/memory/v3/__tests__/filter.test.ts +512 -0
  254. package/src/memory/v3/__tests__/gate.test.ts +574 -0
  255. package/src/memory/v3/__tests__/index-composition.test.ts +233 -0
  256. package/src/memory/v3/__tests__/loop.test.ts +530 -0
  257. package/src/memory/v3/__tests__/retriever.test.ts +226 -0
  258. package/src/memory/v3/__tests__/scouts.test.ts +440 -0
  259. package/src/memory/v3/__tests__/shadow-middleware.test.ts +312 -0
  260. package/src/memory/v3/__tests__/system-prompts.test.ts +154 -0
  261. package/src/memory/v3/__tests__/traversal.test.ts +469 -0
  262. package/src/memory/v3/__tests__/tree-index.test.ts +280 -0
  263. package/src/memory/v3/__tests__/tree-store.test.ts +529 -0
  264. package/src/memory/v3/__tests__/tree-walk.test.ts +707 -0
  265. package/src/memory/v3/__tests__/validate.test.ts +245 -0
  266. package/src/memory/v3/auto-edges.ts +223 -0
  267. package/src/memory/v3/coactivation-store.ts +124 -0
  268. package/src/memory/v3/consolidation-job.ts +323 -0
  269. package/src/memory/v3/edge-learning-job.ts +160 -0
  270. package/src/memory/v3/edges.ts +249 -0
  271. package/src/memory/v3/filter.ts +281 -0
  272. package/src/memory/v3/gate.ts +334 -0
  273. package/src/memory/v3/index-composition.ts +113 -0
  274. package/src/memory/v3/llm-capture.ts +46 -0
  275. package/src/memory/v3/loop.ts +382 -0
  276. package/src/memory/v3/maintenance.ts +144 -0
  277. package/src/memory/v3/prompt-context.ts +33 -0
  278. package/src/memory/v3/prompts/consolidation.ts +458 -0
  279. package/src/memory/v3/prompts/system-prompts.ts +196 -0
  280. package/src/memory/v3/retriever.ts +33 -0
  281. package/src/memory/v3/scouts.ts +420 -0
  282. package/src/memory/v3/shadow-middleware.ts +305 -0
  283. package/src/memory/v3/traversal.ts +206 -0
  284. package/src/memory/v3/tree-index.ts +237 -0
  285. package/src/memory/v3/tree-store.ts +394 -0
  286. package/src/memory/v3/tree-walk.ts +351 -0
  287. package/src/memory/v3/types.ts +65 -0
  288. package/src/memory/v3/validate.ts +300 -0
  289. package/src/notifications/adapters/macos.ts +18 -1
  290. package/src/notifications/adapters/platform.ts +1 -1
  291. package/src/notifications/decision-engine.ts +1 -4
  292. package/src/notifications/emit-signal.ts +29 -49
  293. package/src/permissions/prompter.ts +3 -3
  294. package/src/permissions/question-prompter.ts +5 -2
  295. package/src/permissions/secret-prompter.ts +2 -2
  296. package/src/plugin-api/index.ts +4 -0
  297. package/src/plugin-api/types.ts +7 -33
  298. package/src/plugins/defaults/index.ts +6 -0
  299. package/src/plugins/defaults/injectors.ts +18 -11
  300. package/src/plugins/external-plugin-loader.ts +5 -68
  301. package/src/plugins/types.ts +11 -16
  302. package/src/proactive-artifact/aux-message-injector.ts +17 -4
  303. package/src/prompts/__tests__/task-progress-hint-section.test.ts +3 -9
  304. package/src/prompts/persona-resolver.ts +36 -21
  305. package/src/prompts/sections.ts +39 -7
  306. package/src/prompts/system-prompt.ts +50 -185
  307. package/src/prompts/templates/BOOTSTRAP.md +2 -2
  308. package/src/prompts/templates/system-sections.ts +230 -8
  309. package/src/providers/__tests__/connection-model-compat.test.ts +234 -0
  310. package/src/providers/__tests__/retry-callsite.test.ts +85 -5
  311. package/src/providers/anthropic/client.ts +32 -66
  312. package/src/providers/call-site-routing.ts +14 -2
  313. package/src/providers/connection-model-compat.ts +38 -0
  314. package/src/providers/connection-resolution.ts +16 -2
  315. package/src/providers/gemini/client.ts +49 -6
  316. package/src/providers/inference/adapter-factory.ts +3 -0
  317. package/src/providers/minimax/client.ts +106 -0
  318. package/src/providers/model-catalog.ts +43 -0
  319. package/src/providers/model-intents.ts +1 -1
  320. package/src/providers/openai/chat-completions-provider.ts +6 -3
  321. package/src/providers/openai/codex-models.ts +18 -0
  322. package/src/providers/openai/responses-provider.ts +78 -21
  323. package/src/providers/provider-send-message.ts +7 -1
  324. package/src/providers/retry.ts +34 -3
  325. package/src/providers/thinking-config.ts +26 -1
  326. package/src/providers/usage-tracking.ts +2 -0
  327. package/src/runtime/AGENTS.md +2 -2
  328. package/src/runtime/agent-wake.ts +1 -0
  329. package/src/runtime/assistant-event-hub.ts +76 -6
  330. package/src/runtime/auth/route-policy.ts +36 -0
  331. package/src/runtime/btw-sidechain.ts +0 -6
  332. package/src/runtime/http-types.ts +0 -2
  333. package/src/runtime/migrations/vbundle-builder.ts +10 -3
  334. package/src/runtime/pending-interactions.ts +0 -1
  335. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +106 -0
  336. package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +25 -6
  337. package/src/runtime/routes/__tests__/plugins-routes.test.ts +512 -0
  338. package/src/runtime/routes/acp-routes.test.ts +255 -6
  339. package/src/runtime/routes/acp-routes.ts +8 -1
  340. package/src/runtime/routes/avatar-routes.ts +10 -10
  341. package/src/runtime/routes/background-wake-routes.ts +188 -0
  342. package/src/runtime/routes/browser-tabs-routes.ts +200 -0
  343. package/src/runtime/routes/btw-routes.ts +0 -6
  344. package/src/runtime/routes/conversation-cli-routes.ts +1 -1
  345. package/src/runtime/routes/conversation-list-routes.ts +12 -4
  346. package/src/runtime/routes/conversation-management-routes.ts +77 -20
  347. package/src/runtime/routes/conversation-query-routes.ts +142 -36
  348. package/src/runtime/routes/conversation-routes.ts +252 -410
  349. package/src/runtime/routes/conversation-starter-routes.ts +6 -3
  350. package/src/runtime/routes/disk-pressure-routes.ts +1 -1
  351. package/src/runtime/routes/domain-routes.ts +60 -10
  352. package/src/runtime/routes/email-routes.ts +5 -2
  353. package/src/runtime/routes/events-routes.ts +54 -10
  354. package/src/runtime/routes/group-routes.ts +24 -8
  355. package/src/runtime/routes/host-browser-routes.ts +10 -2
  356. package/src/runtime/routes/host-cu-routes.ts +2 -2
  357. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +96 -3
  358. package/src/runtime/routes/index.ts +8 -0
  359. package/src/runtime/routes/inference-profile-session-handler.ts +22 -12
  360. package/src/runtime/routes/inference-profile-session-routes.ts +7 -1
  361. package/src/runtime/routes/llm-call-sites-routes.ts +32 -5
  362. package/src/runtime/routes/memory-item-routes.ts +8 -3
  363. package/src/runtime/routes/memory-v2-routes.ts +215 -5
  364. package/src/runtime/routes/memory-v3-routes.ts +316 -0
  365. package/src/runtime/routes/migration-routes.ts +21 -24
  366. package/src/runtime/routes/plugins-routes.ts +337 -0
  367. package/src/runtime/routes/rename-conversation-routes.ts +6 -2
  368. package/src/runtime/routes/secret-routes.ts +25 -5
  369. package/src/runtime/routes/settings-routes.ts +12 -11
  370. package/src/runtime/routes/slack-channel-routes.ts +5 -4
  371. package/src/runtime/routes/workspace-routes.ts +25 -10
  372. package/src/runtime/sync/resource-sync-events.ts +106 -38
  373. package/src/runtime/sync/sync-publisher.test.ts +49 -0
  374. package/src/runtime/sync/sync-publisher.ts +2 -1
  375. package/src/runtime/verification-outbound-actions.ts +73 -1
  376. package/src/telemetry/types.ts +12 -0
  377. package/src/telemetry/usage-telemetry-reporter.test.ts +48 -0
  378. package/src/telemetry/usage-telemetry-reporter.ts +1 -0
  379. package/src/tools/acp/spawn.test.ts +119 -0
  380. package/src/tools/acp/spawn.ts +15 -2
  381. package/src/tools/apps/definitions.ts +2 -8
  382. package/src/tools/ask-question/ask-question-tool.test.ts +3 -3
  383. package/src/tools/ask-question/ask-question-tool.ts +38 -45
  384. package/src/tools/browser/__tests__/pinned-tabs.test.ts +70 -0
  385. package/src/tools/browser/browser-execution.ts +16 -3
  386. package/src/tools/browser/cdp-client/__tests__/browser-tabs-factory.test.ts +402 -0
  387. package/src/tools/browser/cdp-client/__tests__/types.test.ts +3 -0
  388. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +12 -0
  389. package/src/tools/browser/cdp-client/extension-cdp-client.ts +27 -1
  390. package/src/tools/browser/cdp-client/factory.ts +100 -17
  391. package/src/tools/browser/cdp-client/local-cdp-client.ts +12 -0
  392. package/src/tools/browser/cdp-client/types.ts +65 -0
  393. package/src/tools/browser/pinned-tabs.ts +96 -40
  394. package/src/tools/computer-use/definitions.ts +22 -78
  395. package/src/tools/credential-execution/make-authenticated-request.ts +3 -9
  396. package/src/tools/credential-execution/manage-secure-command-tool.ts +3 -9
  397. package/src/tools/credential-execution/run-authenticated-command.ts +3 -9
  398. package/src/tools/credentials/vault.ts +3 -9
  399. package/src/tools/document/document-tool.ts +59 -0
  400. package/src/tools/execution-target.ts +21 -23
  401. package/src/tools/executor.ts +6 -1
  402. package/src/tools/filesystem/edit.ts +3 -9
  403. package/src/tools/filesystem/list.ts +3 -9
  404. package/src/tools/filesystem/read.ts +3 -9
  405. package/src/tools/filesystem/write.ts +3 -9
  406. package/src/tools/host-filesystem/edit.ts +3 -9
  407. package/src/tools/host-filesystem/read.ts +3 -9
  408. package/src/tools/host-filesystem/transfer.ts +3 -9
  409. package/src/tools/host-filesystem/write.ts +3 -9
  410. package/src/tools/host-terminal/host-shell.ts +3 -9
  411. package/src/tools/mcp/mcp-tool-factory.ts +1 -8
  412. package/src/tools/memory/register.test.ts +1 -1
  413. package/src/tools/memory/register.ts +4 -9
  414. package/src/tools/network/web-fetch.ts +3 -9
  415. package/src/tools/network/web-search.ts +25 -32
  416. package/src/tools/registry.ts +7 -23
  417. package/src/tools/schema-transforms.ts +1 -1
  418. package/src/tools/skills/execute.ts +3 -9
  419. package/src/tools/skills/load.ts +3 -9
  420. package/src/tools/skills/skill-tool-factory.ts +1 -8
  421. package/src/tools/subagent/notify-parent.ts +3 -9
  422. package/src/tools/system/request-permission.ts +3 -9
  423. package/src/tools/terminal/shell.ts +3 -9
  424. package/src/tools/tool-defaults.ts +94 -0
  425. package/src/tools/types.ts +27 -98
  426. package/src/tools/ui-surface/definitions.ts +6 -22
  427. package/src/usage/pricing.ts +23 -0
  428. package/src/usage/types.ts +12 -0
  429. package/src/util/logger.ts +16 -7
  430. package/src/util/platform.ts +7 -2
  431. package/src/util/sqlite3-runtime.ts +65 -0
  432. package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +1 -0
  433. package/src/workspace/migrations/089-move-memory-tree-out-of-v3.ts +86 -0
  434. package/src/workspace/migrations/registry.ts +2 -0
  435. package/src/__tests__/compaction-strip-metadata-clear.test.ts +0 -206
  436. package/src/__tests__/message-complete-display-id.test.ts +0 -175
  437. package/src/daemon/query-complexity-router.ts +0 -75
  438. package/src/prompts/cache-boundary.ts +0 -8
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Tests for `db-maintenance.ts` (orchestration) and the underlying
3
+ * `db-async-query.ts` abstraction.
4
+ *
5
+ * The contract this PR locks in:
6
+ * 1. `runDbMaintenance` runs `VACUUM` through the async abstraction
7
+ * — when the `sqlite3` CLI is available, that means a subprocess
8
+ * and the daemon's main event loop keeps ticking. (The structural
9
+ * anti-block assertion lives in
10
+ * `db-async-query.test.ts`; here we focus on orchestration.)
11
+ * 2. The subprocess actually shrinks the on-disk page count when
12
+ * there's reclaimable space.
13
+ * 3. `maybeRunDbMaintenance` is genuinely async — callers can `await`
14
+ * it and observe completion.
15
+ * 4. The 24 h interval guard short-circuits a recent re-run.
16
+ *
17
+ * The per-file temp workspace is set up by `test-preload.ts`; tests just
18
+ * dynamic-import the DB modules so they resolve paths under that temp dir.
19
+ */
20
+ import { Database } from "bun:sqlite";
21
+ import { beforeEach, describe, expect, test } from "bun:test";
22
+
23
+ const { getSqlite } = await import("../db-connection.js");
24
+ const { initializeDb } = await import("../db-init.js");
25
+ const { deleteMemoryCheckpoint, getMemoryCheckpoint } =
26
+ await import("../checkpoints.js");
27
+ const { maybeRunDbMaintenance } = await import("../db-maintenance.js");
28
+ const { getDbPath } = await import("../../util/platform.js");
29
+
30
+ initializeDb();
31
+
32
+ const MAINTENANCE_CHECKPOINT_KEY = "db_maintenance:last_run";
33
+
34
+ beforeEach(() => {
35
+ deleteMemoryCheckpoint(MAINTENANCE_CHECKPOINT_KEY);
36
+ });
37
+
38
+ /** Inflate the test DB with bloat that VACUUM can reclaim. */
39
+ function inflateAndDelete(byteTarget: number): void {
40
+ const sqlite = getSqlite();
41
+ sqlite.exec(
42
+ "CREATE TABLE IF NOT EXISTS bloat (id INTEGER PRIMARY KEY, payload BLOB)",
43
+ );
44
+ const pageSize = (
45
+ sqlite.query("PRAGMA page_size").get() as { page_size: number }
46
+ ).page_size;
47
+ const rowsTarget = Math.max(1, Math.ceil(byteTarget / pageSize));
48
+ const payload = new Uint8Array(Math.max(1, pageSize - 64));
49
+ const insert = sqlite.prepare("INSERT INTO bloat (payload) VALUES (?)");
50
+ sqlite.exec("BEGIN");
51
+ for (let i = 0; i < rowsTarget; i++) {
52
+ insert.run(payload);
53
+ }
54
+ sqlite.exec("COMMIT");
55
+ sqlite.exec("DELETE FROM bloat");
56
+ sqlite.exec("DROP TABLE bloat");
57
+ // Force the WAL onto the main DB file so the bloat is visible on disk.
58
+ sqlite.exec("PRAGMA wal_checkpoint(TRUNCATE)");
59
+ }
60
+
61
+ describe("maybeRunDbMaintenance", () => {
62
+ test("returns a Promise that resolves", async () => {
63
+ const result = maybeRunDbMaintenance();
64
+ expect(result).toBeInstanceOf(Promise);
65
+ await result;
66
+ });
67
+
68
+ test("respects the 24h interval and skips when last run was recent", async () => {
69
+ const now = Date.now();
70
+ const recent = now - 60_000;
71
+ const { setMemoryCheckpoint } = await import("../checkpoints.js");
72
+ setMemoryCheckpoint(MAINTENANCE_CHECKPOINT_KEY, String(recent));
73
+
74
+ await maybeRunDbMaintenance(now);
75
+
76
+ expect(getMemoryCheckpoint(MAINTENANCE_CHECKPOINT_KEY)).toBe(
77
+ String(recent),
78
+ );
79
+ });
80
+
81
+ test("stamps the checkpoint after a maintenance run", async () => {
82
+ const now = Date.now();
83
+ await maybeRunDbMaintenance(now);
84
+
85
+ expect(getMemoryCheckpoint(MAINTENANCE_CHECKPOINT_KEY)).toBe(String(now));
86
+ });
87
+
88
+ test("VACUUM reclaims pages on a bloated DB", async () => {
89
+ const sqlite = getSqlite();
90
+ sqlite.exec("DROP TABLE IF EXISTS bloat");
91
+ sqlite.exec("PRAGMA wal_checkpoint(TRUNCATE)");
92
+
93
+ inflateAndDelete(8 * 1024 * 1024);
94
+
95
+ const dbPath = getDbPath();
96
+ // Read page_count from a fresh connection so we observe post-write
97
+ // ground truth without snapshot caching on the main test connection.
98
+ const readPageCount = (): number => {
99
+ const probe = new Database(dbPath, { readonly: true });
100
+ try {
101
+ return (
102
+ probe.query("PRAGMA page_count").get() as { page_count: number }
103
+ ).page_count;
104
+ } finally {
105
+ probe.close();
106
+ }
107
+ };
108
+ const pagesBefore = readPageCount();
109
+
110
+ await maybeRunDbMaintenance();
111
+
112
+ const pagesAfter = readPageCount();
113
+ expect(pagesAfter).toBeLessThan(pagesBefore);
114
+ }, 60_000);
115
+ });
@@ -0,0 +1,241 @@
1
+ // Tests for the memory enqueue gate.
2
+ //
3
+ // Architecture under test:
4
+ // 1. `isMemoryEnabled()` (jobs-store.ts) reads `config.memory.enabled`
5
+ // and returns `true` unless explicitly `false`.
6
+ // 2. Each call site that enqueues a memory job gates on this helper
7
+ // before calling `enqueueMemoryJob` / `upsertDebouncedJob` etc.
8
+ // `enqueueMemoryJob` itself is *not* gated — it preserves its
9
+ // "always returns a real job id" contract, and non-memory jobs
10
+ // (`delete_qdrant_vectors`, `prune_*`) flow through unchanged.
11
+ //
12
+ // We verify (1) directly across the four config-shape variants, then
13
+ // smoke-test (2) at two central entry-point helpers:
14
+ // - `enqueueAutoAnalysisIfEnabled`
15
+ // - `enqueueMemoryRetrospectiveIfEnabled`
16
+ // Each call site re-checks `isMemoryEnabled()` itself, so we don't
17
+ // repeat 30+ identical scenarios — the helper test is the contract.
18
+
19
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
20
+
21
+ // Silence the logger.
22
+ mock.module("../../util/logger.js", () => ({
23
+ getLogger: () =>
24
+ new Proxy({} as Record<string, unknown>, {
25
+ get: () => () => {},
26
+ }),
27
+ }));
28
+
29
+ // Mutable config shape, mutated per-test. `null` means "no `memory` key
30
+ // at all" — exercises the defensive `?.` chain in `isMemoryEnabled`.
31
+ type MemoryEnabledShape = boolean | null | undefined;
32
+ let memoryEnabled: MemoryEnabledShape = true;
33
+ let getConfigThrows = false;
34
+ mock.module("../../config/loader.js", () => ({
35
+ getConfig: () => {
36
+ if (getConfigThrows) throw new Error("config load failed");
37
+ if (memoryEnabled === null) return {};
38
+ return {
39
+ memory: { enabled: memoryEnabled, v2: { enabled: false } },
40
+ analysis: { idleTimeoutMs: 600_000, batchSize: 30 },
41
+ assistant: { featureFlags: { "auto-analyze": true } },
42
+ };
43
+ },
44
+ }));
45
+
46
+ // Stub feature flags so auto-analyze isn't gated by an unrelated flag.
47
+ mock.module("../../config/assistant-feature-flags.js", () => ({
48
+ isAssistantFeatureFlagEnabled: () => true,
49
+ }));
50
+
51
+ // Stub trust resolver — never claim the actor is untrusted in tests.
52
+ mock.module("../../runtime/actor-trust-resolver.js", () => ({
53
+ isUntrustedTrustClass: () => false,
54
+ }));
55
+
56
+ // Stub the conversation-source lookup so the recursion guards in the
57
+ // retrospective and auto-analysis paths fall through to the enqueue.
58
+ mock.module("../conversation-crud.js", () => ({
59
+ getConversationSource: () => null,
60
+ }));
61
+ mock.module("../auto-analysis-guard.js", () => ({
62
+ isAutoAnalysisConversation: () => false,
63
+ }));
64
+
65
+ // Stub the qdrant breaker so `enqueueMemoryJob` doesn't trip on it.
66
+ mock.module("../qdrant-circuit-breaker.js", () => ({
67
+ isQdrantBreakerOpen: () => false,
68
+ shouldAllowQdrantProbe: () => true,
69
+ }));
70
+
71
+ // Stub raw query helpers (used by jobs-store internally).
72
+ mock.module("../raw-query.js", () => ({
73
+ rawAll: () => [],
74
+ rawChanges: () => 0,
75
+ }));
76
+
77
+ // Drizzle-shaped no-op db. Tracks inserts/updates so tests can observe
78
+ // whether an upsert/enqueue actually wrote anything.
79
+ const dbInserts: Array<{ table?: unknown; values: unknown }> = [];
80
+ const dbUpdates: Array<{ table?: unknown; set: unknown }> = [];
81
+ function makeStubDb() {
82
+ return {
83
+ insert: (table: unknown) => ({
84
+ values: (values: unknown) => {
85
+ dbInserts.push({ table, values });
86
+ return {
87
+ run: () => {},
88
+ onConflictDoUpdate: () => ({ run: () => {} }),
89
+ };
90
+ },
91
+ }),
92
+ update: (table: unknown) => ({
93
+ set: (set: unknown) => {
94
+ dbUpdates.push({ table, set });
95
+ return {
96
+ where: () => ({ run: () => {} }),
97
+ };
98
+ },
99
+ }),
100
+ select: () => ({
101
+ from: () => ({
102
+ where: () => ({
103
+ orderBy: () => ({ get: () => null, all: () => [] }),
104
+ get: () => null,
105
+ all: () => [],
106
+ }),
107
+ }),
108
+ }),
109
+ transaction: (fn: (tx: unknown) => unknown) => fn(makeStubDb()),
110
+ };
111
+ }
112
+ const stubDb = makeStubDb();
113
+ mock.module("../db-connection.js", () => ({
114
+ getDb: () => stubDb,
115
+ }));
116
+
117
+ // Now load the real modules under test.
118
+ const { isMemoryEnabled } = await import("../jobs-store.js");
119
+ const { enqueueAutoAnalysisIfEnabled } = await import(
120
+ "../auto-analysis-enqueue.js"
121
+ );
122
+ const { enqueueMemoryRetrospectiveIfEnabled } = await import(
123
+ "../memory-retrospective-enqueue.js"
124
+ );
125
+
126
+ beforeEach(() => {
127
+ dbInserts.length = 0;
128
+ dbUpdates.length = 0;
129
+ memoryEnabled = true;
130
+ getConfigThrows = false;
131
+ });
132
+
133
+ // ---------------------------------------------------------------------
134
+ // isMemoryEnabled() contract
135
+ // ---------------------------------------------------------------------
136
+
137
+ describe("isMemoryEnabled", () => {
138
+ test("returns true when memory.enabled is true", () => {
139
+ memoryEnabled = true;
140
+ expect(isMemoryEnabled()).toBe(true);
141
+ });
142
+
143
+ test("returns true when memory.enabled is undefined (schema default)", () => {
144
+ memoryEnabled = undefined;
145
+ expect(isMemoryEnabled()).toBe(true);
146
+ });
147
+
148
+ test("returns true when memory key is absent (partial config)", () => {
149
+ memoryEnabled = null;
150
+ expect(isMemoryEnabled()).toBe(true);
151
+ });
152
+
153
+ test("returns false ONLY when memory.enabled is explicitly false", () => {
154
+ memoryEnabled = false;
155
+ expect(isMemoryEnabled()).toBe(false);
156
+ });
157
+
158
+ test("returns true (defensive) when getConfig throws", () => {
159
+ // If config can't be read, we can't tell whether memory has been
160
+ // disabled, so default to "enabled". Callers that already have their
161
+ // own getConfig try/catch (e.g. enqueueAutoAnalysisIfEnabled) keep
162
+ // controlling the silent-failure semantic for the rest of their flow.
163
+ getConfigThrows = true;
164
+ expect(isMemoryEnabled()).toBe(true);
165
+ });
166
+ });
167
+
168
+ // ---------------------------------------------------------------------
169
+ // enqueueAutoAnalysisIfEnabled — representative entry-point helper
170
+ // ---------------------------------------------------------------------
171
+
172
+ describe("enqueueAutoAnalysisIfEnabled (call-site gate)", () => {
173
+ test("does NOT enqueue when memory.enabled is false", () => {
174
+ memoryEnabled = false;
175
+ enqueueAutoAnalysisIfEnabled({
176
+ conversationId: "conv-1",
177
+ trigger: "batch",
178
+ });
179
+ expect(dbInserts.length).toBe(0);
180
+ });
181
+
182
+ test("enqueues when memory.enabled is true", () => {
183
+ memoryEnabled = true;
184
+ enqueueAutoAnalysisIfEnabled({
185
+ conversationId: "conv-1",
186
+ trigger: "batch",
187
+ });
188
+ expect(dbInserts.length).toBeGreaterThan(0);
189
+ });
190
+
191
+ test("enqueues when memory.enabled is undefined (schema default)", () => {
192
+ memoryEnabled = undefined;
193
+ enqueueAutoAnalysisIfEnabled({
194
+ conversationId: "conv-1",
195
+ trigger: "batch",
196
+ });
197
+ expect(dbInserts.length).toBeGreaterThan(0);
198
+ });
199
+
200
+ test("enqueues when memory key is absent from config", () => {
201
+ memoryEnabled = null;
202
+ enqueueAutoAnalysisIfEnabled({
203
+ conversationId: "conv-1",
204
+ trigger: "batch",
205
+ });
206
+ expect(dbInserts.length).toBeGreaterThan(0);
207
+ });
208
+ });
209
+
210
+ // ---------------------------------------------------------------------
211
+ // enqueueMemoryRetrospectiveIfEnabled — representative entry-point
212
+ // ---------------------------------------------------------------------
213
+
214
+ describe("enqueueMemoryRetrospectiveIfEnabled (call-site gate)", () => {
215
+ test("does NOT enqueue when memory.enabled is false", () => {
216
+ memoryEnabled = false;
217
+ enqueueMemoryRetrospectiveIfEnabled({
218
+ conversationId: "conv-1",
219
+ trigger: "interval",
220
+ });
221
+ expect(dbInserts.length).toBe(0);
222
+ });
223
+
224
+ test("enqueues when memory.enabled is true", () => {
225
+ memoryEnabled = true;
226
+ enqueueMemoryRetrospectiveIfEnabled({
227
+ conversationId: "conv-1",
228
+ trigger: "interval",
229
+ });
230
+ expect(dbInserts.length).toBeGreaterThan(0);
231
+ });
232
+
233
+ test("enqueues when memory.enabled is undefined (schema default)", () => {
234
+ memoryEnabled = undefined;
235
+ enqueueMemoryRetrospectiveIfEnabled({
236
+ conversationId: "conv-1",
237
+ trigger: "interval",
238
+ });
239
+ expect(dbInserts.length).toBeGreaterThan(0);
240
+ });
241
+ });
@@ -1,6 +1,24 @@
1
1
  import { describe, expect, test } from "bun:test";
2
2
 
3
- import { EMBED_JOB_TYPES, SLOW_LLM_JOB_TYPES } from "../jobs-store.js";
3
+ import {
4
+ EMBED_JOB_TYPES,
5
+ type MemoryJobType,
6
+ SLOW_LLM_JOB_TYPES,
7
+ } from "../jobs-store.js";
8
+
9
+ describe("memory v3 job types", () => {
10
+ test("the v3 job-type literals are members of MemoryJobType", () => {
11
+ // Compile-time assignability is enforced by `tsc --noEmit`; the runtime
12
+ // assertion keeps the literals visible to the test runner. These types are
13
+ // inert scaffolding until their handlers land in later PRs.
14
+ const v3JobTypes: MemoryJobType[] = [
15
+ "memory_v3_consolidate",
16
+ "memory_v3_index_maintenance",
17
+ "memory_v3_edge_learning",
18
+ ];
19
+ expect(new Set(v3JobTypes).size).toBe(3);
20
+ });
21
+ });
4
22
 
5
23
  describe("memory job classes", () => {
6
24
  test("EMBED_JOB_TYPES and SLOW_LLM_JOB_TYPES are disjoint", () => {
@@ -21,4 +39,13 @@ describe("memory job classes", () => {
21
39
  const set = new Set(SLOW_LLM_JOB_TYPES);
22
40
  expect(set.size).toBe(SLOW_LLM_JOB_TYPES.length);
23
41
  });
42
+
43
+ test("memory_v3_consolidate is a slow LLM job (lane isolation)", () => {
44
+ // It hands off to a background agent for up to 15 min, like
45
+ // memory_v2_consolidate; it must not sit in the fast lane and starve
46
+ // short jobs. The mechanical v3 jobs intentionally stay fast.
47
+ expect(SLOW_LLM_JOB_TYPES).toContain("memory_v3_consolidate");
48
+ expect(SLOW_LLM_JOB_TYPES).not.toContain("memory_v3_index_maintenance");
49
+ expect(SLOW_LLM_JOB_TYPES).not.toContain("memory_v3_edge_learning");
50
+ });
24
51
  });
@@ -315,6 +315,13 @@ describe("memoryRetrospectiveJob", () => {
315
315
  expect(bootstrapCalls[0]!.forkParentConversationId).toBe("src-conv-1");
316
316
  });
317
317
 
318
+ test("legacy path: wake opts include suppressWakeSurface so the full retrospective prompt isn't rendered as a 'Conversation Woke' card body to clients", async () => {
319
+ await memoryRetrospectiveJob(makeJob(), stubConfig);
320
+
321
+ expect(wakeCalls).toHaveLength(1);
322
+ expect(wakeCalls[0]!.opts.suppressWakeSurface).toBe(true);
323
+ });
324
+
318
325
  test("no-new-messages early return: neither field changes, no wake, no bootstrap", async () => {
319
326
  newMessages = [];
320
327
  const outcome = await memoryRetrospectiveJob(makeJob(), stubConfig);
@@ -6,7 +6,7 @@ import {
6
6
  } from "../runtime/actor-trust-resolver.js";
7
7
  import { getLogger } from "../util/logger.js";
8
8
  import { isAutoAnalysisConversation } from "./auto-analysis-guard.js";
9
- import { upsertAutoAnalysisJob } from "./jobs-store.js";
9
+ import { isMemoryEnabled, upsertAutoAnalysisJob } from "./jobs-store.js";
10
10
 
11
11
  const log = getLogger("auto-analysis-enqueue");
12
12
 
@@ -46,6 +46,10 @@ export function enqueueAutoAnalysisIfEnabled(args: {
46
46
  }): void {
47
47
  const { conversationId, trigger } = args;
48
48
 
49
+ if (!isMemoryEnabled()) {
50
+ return;
51
+ }
52
+
49
53
  let config;
50
54
  try {
51
55
  config = getConfig();
@@ -24,6 +24,7 @@ import type { ChannelId, InterfaceId } from "../channels/types.js";
24
24
  import { parseChannelId, parseInterfaceId } from "../channels/types.js";
25
25
  import { CHANNEL_IDS, isChannelId } from "../channels/types.js";
26
26
  import { getConfig } from "../config/loader.js";
27
+ import { findDisplayTurnEndIndex } from "../conversations/message-consolidation.js";
27
28
  import type { TrustContext } from "../daemon/trust-context.js";
28
29
  import { UserError } from "../util/errors.js";
29
30
  import { safeParseRecord } from "../util/json.js";
@@ -47,6 +48,7 @@ import {
47
48
  } from "./conversation-disk-view.js";
48
49
  import { ensureDisplayOrderMigration } from "./conversation-display-order-migration.js";
49
50
  import { ensureGroupMigration } from "./conversation-group-migration.js";
51
+ import { runAsyncSqlite } from "./db-async-query.js";
50
52
  import { getDb, getSqliteFrom } from "./db-connection.js";
51
53
  import { forkGraphMemoryState } from "./graph/graph-memory-state-store.js";
52
54
  import { indexMessageNow } from "./indexer.js";
@@ -190,7 +192,7 @@ export interface ConversationRow {
190
192
  contextSummary: string | null;
191
193
  contextCompactedMessageCount: number;
192
194
  contextCompactedAt: number | null;
193
- cleanedAt: number | null;
195
+ historyStrippedAt: number | null;
194
196
  slackContextCompactionWatermarkTs: string | null;
195
197
  slackContextCompactionWatermarkAt: number | null;
196
198
  conversationType: string;
@@ -224,7 +226,7 @@ export const parseConversation = createRowMapper<
224
226
  contextSummary: "contextSummary",
225
227
  contextCompactedMessageCount: "contextCompactedMessageCount",
226
228
  contextCompactedAt: "contextCompactedAt",
227
- cleanedAt: "cleanedAt",
229
+ historyStrippedAt: "historyStrippedAt",
228
230
  slackContextCompactionWatermarkTs: "slackContextCompactionWatermarkTs",
229
231
  slackContextCompactionWatermarkAt: "slackContextCompactionWatermarkAt",
230
232
  conversationType: "conversationType",
@@ -581,17 +583,30 @@ export function forkConversation(params: {
581
583
  );
582
584
  }
583
585
 
584
- const copyBoundaryIndex =
586
+ const initialBoundaryIndex =
585
587
  throughMessageId == null
586
588
  ? sourceMessages.length - 1
587
589
  : sourceMessages.findIndex((message) => message.id === throughMessageId);
588
590
 
589
- if (throughMessageId != null && copyBoundaryIndex === -1) {
591
+ if (throughMessageId != null && initialBoundaryIndex === -1) {
590
592
  throw new UserError(
591
593
  `Message ${throughMessageId} does not belong to conversation ${conversationId}`,
592
594
  );
593
595
  }
594
596
 
597
+ // Extend the boundary to cover the full display turn the client
598
+ // addressed. The read-path collapses each assistant turn across
599
+ // multiple DB rows — consecutive assistant rows AND tool-result-only
600
+ // user rows between them — so "fork through message X" semantically
601
+ // means "fork through the entire display turn containing X" no matter
602
+ // which DB row in the cluster the client supplied. Single source of
603
+ // truth is `findDisplayTurnEndIndex`, shared with the read path so
604
+ // both stay in sync.
605
+ const copyBoundaryIndex = findDisplayTurnEndIndex(
606
+ sourceMessages,
607
+ initialBoundaryIndex,
608
+ );
609
+
595
610
  const visibleWindowStartIndex = Math.max(
596
611
  0,
597
612
  Math.min(
@@ -607,15 +622,16 @@ export function forkConversation(params: {
607
622
  ? sourceMessages.slice(0, copyBoundaryIndex + 1)
608
623
  : ([] as MessageRow[]);
609
624
 
610
- // Inherit /clean state only when the fork boundary is at-or-after the
611
- // clean event. Pre-clean forks branch from history that pre-dates the
612
- // clean, so the marker would be a no-op and is misleading to copy.
613
- const sourceCleanedAt = sourceConversation.cleanedAt ?? null;
625
+ // Inherit the history-strip marker only when the fork boundary is at-or-
626
+ // after the strip event. Pre-strip forks branch from history that pre-
627
+ // dates the strip, so the marker would be a no-op and is misleading to
628
+ // copy.
629
+ const sourceHistoryStrippedAt = sourceConversation.historyStrippedAt ?? null;
614
630
  const boundaryMessageCreatedAt = messagesToCopy.at(-1)?.createdAt ?? null;
615
- const inheritsCleanedAt =
616
- sourceCleanedAt != null &&
631
+ const inheritsHistoryStrippedAt =
632
+ sourceHistoryStrippedAt != null &&
617
633
  boundaryMessageCreatedAt != null &&
618
- boundaryMessageCreatedAt >= sourceCleanedAt;
634
+ boundaryMessageCreatedAt >= sourceHistoryStrippedAt;
619
635
  const forkParentMessageId = messagesToCopy.at(-1)?.id ?? null;
620
636
  const forkTitle =
621
637
  params.title ?? `${sourceConversation.title ?? "Untitled"} (Fork)`;
@@ -662,7 +678,9 @@ export function forkConversation(params: {
662
678
  slackContextCompactionWatermarkAt: preserveSourceCompactionState
663
679
  ? sourceConversation.slackContextCompactionWatermarkAt
664
680
  : null,
665
- cleanedAt: inheritsCleanedAt ? sourceCleanedAt : null,
681
+ historyStrippedAt: inheritsHistoryStrippedAt
682
+ ? sourceHistoryStrippedAt
683
+ : null,
666
684
  inferenceProfile: sourceConversation.inferenceProfile,
667
685
  })
668
686
  .where(eq(conversations.id, fc.id))
@@ -1451,14 +1469,14 @@ export function updateConversationContextWindow(
1451
1469
  .run();
1452
1470
  }
1453
1471
 
1454
- export function setConversationCleanedAt(
1472
+ export function setConversationHistoryStrippedAt(
1455
1473
  id: string,
1456
- cleanedAt: number | null,
1474
+ historyStrippedAt: number | null,
1457
1475
  ): void {
1458
1476
  const db = getDb();
1459
1477
  db.update(conversations)
1460
1478
  .set({
1461
- cleanedAt,
1479
+ historyStrippedAt,
1462
1480
  updatedAt: Date.now(),
1463
1481
  })
1464
1482
  .where(eq(conversations.id, id))
@@ -1703,13 +1721,35 @@ export function setLastNotifiedInferenceProfile(
1703
1721
  * Delete all conversations, messages, and related data (tool invocations,
1704
1722
  * memory segments, etc.) from the daemon database.
1705
1723
  * Returns { conversations, messages } counts.
1724
+ *
1725
+ * Each bulk DELETE is dispatched through {@link runAsyncSqlite}: when
1726
+ * the host has a `sqlite3` CLI it executes in a subprocess and the
1727
+ * daemon's main event loop stays responsive while large tables
1728
+ * (`llm_request_logs`, `tool_invocations`, etc.) are wiped. On hosts
1729
+ * without the CLI the abstraction falls back to in-process blocking
1730
+ * execution — the same behaviour the daemon had before.
1706
1731
  */
1707
- export function clearAll(): { conversations: number; messages: number } {
1732
+ export async function clearAll(): Promise<{
1733
+ conversations: number;
1734
+ messages: number;
1735
+ }> {
1708
1736
  const msgCount =
1709
1737
  rawGet<{ c: number }>("SELECT COUNT(*) AS c FROM messages")?.c ?? 0;
1710
1738
  const convCount =
1711
1739
  rawGet<{ c: number }>("SELECT COUNT(*) AS c FROM conversations")?.c ?? 0;
1712
1740
 
1741
+ // Each DELETE goes through `runAsyncSqlite`. The original code threw
1742
+ // on rawExec failure; mirror that here by throwing when the async
1743
+ // result reports `ok: false`, so the route handler still returns 500.
1744
+ const runOrThrow = async (sql: string): Promise<void> => {
1745
+ const result = await runAsyncSqlite(sql);
1746
+ if (!result.ok) {
1747
+ throw new Error(
1748
+ `clearAll: \`${sql}\` failed (${result.backend}): ${result.error ?? "unknown"}`,
1749
+ );
1750
+ }
1751
+ };
1752
+
1713
1753
  // Delete in dependency order. Cascades handle memory_segments and
1714
1754
  // tool_invocations, but we explicitly clear non-cascading memory
1715
1755
  // tables too.
@@ -1719,22 +1759,21 @@ export function clearAll(): { conversations: number; messages: number } {
1719
1759
  // triggers so that the subsequent base-table DELETEs don't also fail
1720
1760
  // (SQLite triggers are atomic with the triggering statement, so a
1721
1761
  // corrupted FTS table would roll back every base-table DELETE).
1722
- rawExec("DELETE FROM memory_segments");
1723
- rawExec("DELETE FROM memory_summaries");
1724
- rawExec("DELETE FROM memory_embeddings");
1725
- rawExec("DELETE FROM memory_jobs");
1726
- rawExec("DELETE FROM memory_checkpoints");
1727
- rawExec("DELETE FROM llm_request_logs");
1728
- rawExec("DELETE FROM llm_usage_events");
1729
- rawExec("DELETE FROM message_attachments");
1730
- rawExec("DELETE FROM attachments");
1731
- rawExec("DELETE FROM tool_invocations");
1762
+ await runOrThrow("DELETE FROM memory_segments");
1763
+ await runOrThrow("DELETE FROM memory_summaries");
1764
+ await runOrThrow("DELETE FROM memory_embeddings");
1765
+ await runOrThrow("DELETE FROM memory_jobs");
1766
+ await runOrThrow("DELETE FROM memory_checkpoints");
1767
+ await runOrThrow("DELETE FROM llm_request_logs");
1768
+ await runOrThrow("DELETE FROM llm_usage_events");
1769
+ await runOrThrow("DELETE FROM message_attachments");
1770
+ await runOrThrow("DELETE FROM attachments");
1771
+ await runOrThrow("DELETE FROM tool_invocations");
1732
1772
  let messagesFtsCorrupted = false;
1733
- try {
1734
- rawExec("DELETE FROM messages_fts");
1735
- } catch (err) {
1773
+ const ftsResult = await runAsyncSqlite("DELETE FROM messages_fts");
1774
+ if (!ftsResult.ok) {
1736
1775
  log.warn(
1737
- { err },
1776
+ { error: ftsResult.error, backend: ftsResult.backend },
1738
1777
  "clearAll: failed to clear messages_fts — dropping triggers so base-table cleanup can proceed",
1739
1778
  );
1740
1779
  rawExec("DROP TRIGGER IF EXISTS messages_fts_ai");
@@ -1742,8 +1781,8 @@ export function clearAll(): { conversations: number; messages: number } {
1742
1781
  rawExec("DROP TRIGGER IF EXISTS messages_fts_au");
1743
1782
  messagesFtsCorrupted = true;
1744
1783
  }
1745
- rawExec("DELETE FROM messages");
1746
- rawExec("DELETE FROM conversations");
1784
+ await runOrThrow("DELETE FROM messages");
1785
+ await runOrThrow("DELETE FROM conversations");
1747
1786
 
1748
1787
  // Record audit event — lifecycle_events is NOT deleted by clearAll(),
1749
1788
  // so this survives the wipe and provides a permanent trail.
@@ -1910,44 +1949,6 @@ export function updateMessageMetadata(
1910
1949
  .run();
1911
1950
  }
1912
1951
 
1913
- /**
1914
- * Bulk-remove the metadata fields that back the blocks stripped by
1915
- * `stripInjectionsForCompaction` — currently `pkbSystemReminderBlock`
1916
- * (`<system_reminder>`), `nowScratchpadBlock` (`<NOW.md …>`),
1917
- * `pkbContextBlock` (`<knowledge_base>`), and `memoryV2StaticBlock`
1918
- * (the static `<memory>\n…</memory>` block matched by the `<memory>\n`
1919
- * prefix in `RUNTIME_INJECTION_PREFIXES`). Called from compaction-strip
1920
- * sites so post-restart rehydration stays consistent with the in-memory
1921
- * state produced by `stripInjectionsForCompaction` (which removes those
1922
- * tags from live messages but cannot touch the DB). Fields backing
1923
- * blocks that are intentionally NOT stripped (`turnContextBlock`,
1924
- * `workspaceBlock`, `memoryInjectedBlock`) are preserved.
1925
- */
1926
- export function clearStrippedInjectionMetadataForConversation(
1927
- conversationId: string,
1928
- ): void {
1929
- rawRun(
1930
- `UPDATE messages
1931
- SET metadata = json_remove(
1932
- metadata,
1933
- '$.pkbSystemReminderBlock',
1934
- '$.nowScratchpadBlock',
1935
- '$.pkbContextBlock',
1936
- '$.memoryV2StaticBlock'
1937
- )
1938
- WHERE conversation_id = ?
1939
- AND role = 'user'
1940
- AND metadata IS NOT NULL
1941
- AND (
1942
- json_extract(metadata, '$.pkbSystemReminderBlock') IS NOT NULL
1943
- OR json_extract(metadata, '$.nowScratchpadBlock') IS NOT NULL
1944
- OR json_extract(metadata, '$.pkbContextBlock') IS NOT NULL
1945
- OR json_extract(metadata, '$.memoryV2StaticBlock') IS NOT NULL
1946
- )`,
1947
- conversationId,
1948
- );
1949
- }
1950
-
1951
1952
  /**
1952
1953
  * Atomically update both `content` and (shallow-merged) `metadata` for a
1953
1954
  * message. Used by edit-propagation paths that need to update the message