@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
@@ -12,7 +12,6 @@ import { join } from "node:path";
12
12
  import { and, asc, desc, eq, gt } from "drizzle-orm";
13
13
 
14
14
  import type { AssistantConfig } from "../../config/types.js";
15
- import { resolveGuardianPersona } from "../../prompts/persona-resolver.js";
16
15
  import { buildCoreIdentityContext } from "../../prompts/system-prompt.js";
17
16
  import {
18
17
  extractToolUse,
@@ -1026,10 +1025,7 @@ export async function runGraphExtraction(
1026
1025
  const candidateNodeIds = new Set(candidateNodes.map((n) => n.id));
1027
1026
 
1028
1027
  // 4. Build prompt
1029
- const userPersona = resolveGuardianPersona();
1030
- const identityContext = buildCoreIdentityContext({
1031
- userPersona: userPersona ?? undefined,
1032
- });
1028
+ const identityContext = buildCoreIdentityContext();
1033
1029
 
1034
1030
  const activeSet = opts?.activeContextNodeIds
1035
1031
  ? new Set(opts.activeContextNodeIds)
@@ -9,7 +9,11 @@ import { selectedBackendSupportsMultimodal } from "../embedding-backend.js";
9
9
  import type { EmbeddingInput } from "../embedding-types.js";
10
10
  import { embedAndUpsert } from "../job-utils.js";
11
11
  import { asString } from "../job-utils.js";
12
- import { enqueueMemoryJob, type MemoryJob } from "../jobs-store.js";
12
+ import {
13
+ enqueueMemoryJob,
14
+ isMemoryEnabled,
15
+ type MemoryJob,
16
+ } from "../jobs-store.js";
13
17
  import { isQdrantBreakerOpen } from "../qdrant-circuit-breaker.js";
14
18
  import { withQdrantBreaker } from "../qdrant-circuit-breaker.js";
15
19
  import {
@@ -238,6 +242,7 @@ export async function embedGraphNodeJob(
238
242
  * Enqueue an embedding job for a graph node (async, for live conversations).
239
243
  */
240
244
  export function enqueueGraphNodeEmbed(nodeId: string): void {
245
+ if (!isMemoryEnabled()) return;
241
246
  enqueueMemoryJob("embed_graph_node", { nodeId });
242
247
  }
243
248
 
@@ -282,5 +287,6 @@ export async function embedGraphTriggerJob(
282
287
  * Enqueue a trigger embedding job.
283
288
  */
284
289
  export function enqueueGraphTriggerEmbed(triggerId: string): void {
290
+ if (!isMemoryEnabled()) return;
285
291
  enqueueMemoryJob("graph_trigger_embed", { triggerId });
286
292
  }
@@ -11,7 +11,11 @@ import { isAutoAnalysisConversation } from "./auto-analysis-guard.js";
11
11
  import { getMemoryCheckpoint, setMemoryCheckpoint } from "./checkpoints.js";
12
12
  import { getDb } from "./db-connection.js";
13
13
  import { selectedBackendSupportsMultimodal } from "./embedding-backend.js";
14
- import { enqueueMemoryJob, upsertDebouncedJob } from "./jobs-store.js";
14
+ import {
15
+ enqueueMemoryJob,
16
+ isMemoryEnabled,
17
+ upsertDebouncedJob,
18
+ } from "./jobs-store.js";
15
19
  import { isMemoryRetrospectiveConversation } from "./memory-retrospective-enqueue.js";
16
20
  import { maybeEnqueueRetrospective } from "./memory-retrospective-trigger-check.js";
17
21
  import {
@@ -139,20 +143,22 @@ export async function indexMessageNow(
139
143
 
140
144
  if (existing?.contentHash === hash) {
141
145
  skippedEmbedJobs++;
142
- } else {
146
+ } else if (isMemoryEnabled()) {
143
147
  enqueueMemoryJob("embed_segment", { segmentId }, Date.now(), tx);
144
148
  }
145
149
  }
146
150
 
147
151
  // Enqueue embed_attachment jobs for image content blocks when the
148
152
  // embedding provider supports multimodal (Gemini only).
149
- for (const block of mediaBlocks) {
150
- enqueueMemoryJob(
151
- "embed_attachment",
152
- { messageId: input.messageId, blockIndex: block.index },
153
- Date.now(),
154
- tx,
155
- );
153
+ if (isMemoryEnabled()) {
154
+ for (const block of mediaBlocks) {
155
+ enqueueMemoryJob(
156
+ "embed_attachment",
157
+ { messageId: input.messageId, blockIndex: block.index },
158
+ Date.now(),
159
+ tx,
160
+ );
161
+ }
156
162
  }
157
163
  });
158
164
 
@@ -221,14 +227,16 @@ export async function indexMessageNow(
221
227
  extractRunAfter = graphBatchFired
222
228
  ? Date.now()
223
229
  : Date.now() + idleTimeoutMs;
224
- upsertDebouncedJob(
225
- "graph_extract",
226
- {
227
- conversationId: input.conversationId,
228
- scopeId: input.scopeId ?? "default",
229
- },
230
- extractRunAfter,
231
- );
230
+ if (isMemoryEnabled()) {
231
+ upsertDebouncedJob(
232
+ "graph_extract",
233
+ {
234
+ conversationId: input.conversationId,
235
+ scopeId: input.scopeId ?? "default",
236
+ },
237
+ extractRunAfter,
238
+ );
239
+ }
232
240
  } else {
233
241
  extractRunAfter = Date.now() + idleTimeoutMs;
234
242
  }
@@ -309,7 +317,7 @@ export async function indexMessageNow(
309
317
  // jobs-worker.ts. Debounced on the same idle timeout — no threshold
310
318
  // trigger needed since summaries compress the whole conversation, not
311
319
  // incremental batches.
312
- if (v2Config == null) {
320
+ if (v2Config == null && isMemoryEnabled()) {
313
321
  upsertDebouncedJob(
314
322
  "build_conversation_summary",
315
323
  { conversationId: input.conversationId },
@@ -372,10 +380,12 @@ export async function indexMessageNow(
372
380
  }
373
381
 
374
382
  export function enqueueBackfillJob(force = false): string {
383
+ if (!isMemoryEnabled()) return "";
375
384
  return enqueueMemoryJob("backfill", { force });
376
385
  }
377
386
 
378
387
  export function enqueueRebuildIndexJob(): string {
388
+ if (!isMemoryEnabled()) return "";
379
389
  return enqueueMemoryJob("rebuild_index", {});
380
390
  }
381
391
 
@@ -1,8 +1,9 @@
1
1
  import type { AssistantConfig } from "../../config/types.js";
2
2
  import { getLogger } from "../../util/logger.js";
3
+ import { runAsyncSqlite } from "../db-async-query.js";
3
4
  import { getDb } from "../db-connection.js";
4
5
  import { enqueueMemoryJob, type MemoryJob } from "../jobs-store.js";
5
- import { rawAll, rawChanges, rawRun } from "../raw-query.js";
6
+ import { rawAll, rawRun } from "../raw-query.js";
6
7
 
7
8
  const log = getLogger("memory-jobs-worker");
8
9
 
@@ -13,11 +14,18 @@ const PRUNE_LOG_BATCH_LIMIT = 1000;
13
14
  * Delete LLM request/response logs older than the configured retention period.
14
15
  * Processes in batches to avoid long DB locks and excessive WAL growth.
15
16
  * Re-enqueues itself if more rows remain.
17
+ *
18
+ * The DELETE is dispatched through `runAsyncSqlite` so it runs in a
19
+ * sqlite3 subprocess (when available) and does not block the daemon's
20
+ * main event loop. The two bind parameters (`cutoffMs`, batch limit)
21
+ * are integers — they're inlined directly into the SQL after a
22
+ * `Math.floor` + `Number.isFinite` guard so there is no string
23
+ * interpolation surface.
16
24
  */
17
- export function pruneOldLlmRequestLogsJob(
25
+ export async function pruneOldLlmRequestLogsJob(
18
26
  job: MemoryJob,
19
27
  config: AssistantConfig,
20
- ): void {
28
+ ): Promise<void> {
21
29
  const rawRetention = job.payload.retentionMs;
22
30
  const retentionMs =
23
31
  rawRetention === null
@@ -31,14 +39,29 @@ export function pruneOldLlmRequestLogsJob(
31
39
  // null means "keep forever" — skip pruning entirely
32
40
  if (retentionMs === null || retentionMs === undefined) return;
33
41
 
34
- const cutoffMs = Date.now() - retentionMs;
35
-
36
- rawRun(
37
- `DELETE FROM llm_request_logs WHERE rowid IN (SELECT rowid FROM llm_request_logs WHERE created_at < ? LIMIT ?)`,
38
- cutoffMs,
39
- PRUNE_LOG_BATCH_LIMIT,
42
+ const cutoffMs = Math.floor(Date.now() - retentionMs);
43
+ if (!Number.isFinite(cutoffMs)) return;
44
+
45
+ // Inline the cutoff and batch limit (both integers, both validated)
46
+ // and chain `SELECT changes()` so we can read the row count from the
47
+ // subprocess's stdout. The sqlite3 CLI prints `changes()` as a bare
48
+ // integer on its own line in default output mode; the in-process
49
+ // fallback backend in `db-async-query.ts` synthesizes the same shape
50
+ // by capturing `changes()` atomically after `exec()`. Both backends
51
+ // end up on the parser path below.
52
+ const result = await runAsyncSqlite(
53
+ `DELETE FROM llm_request_logs WHERE rowid IN (SELECT rowid FROM llm_request_logs WHERE created_at < ${cutoffMs} LIMIT ${PRUNE_LOG_BATCH_LIMIT});
54
+ SELECT changes();`,
40
55
  );
41
- const deleted = rawChanges();
56
+ if (!result.ok) {
57
+ log.warn(
58
+ { error: result.error, backend: result.backend },
59
+ "pruneOldLlmRequestLogsJob: DELETE failed",
60
+ );
61
+ return;
62
+ }
63
+
64
+ const deleted = parseDeletedCount(result.stdout);
42
65
 
43
66
  if (deleted >= PRUNE_LOG_BATCH_LIMIT) {
44
67
  enqueueMemoryJob("prune_old_llm_request_logs", { retentionMs });
@@ -58,11 +81,14 @@ export function pruneOldLlmRequestLogsJob(
58
81
  * Delete trace events older than the configured retention period.
59
82
  * Processes in batches to avoid long DB locks and excessive WAL growth.
60
83
  * Re-enqueues itself if more rows remain.
84
+ *
85
+ * Same async dispatch + integer inlining shape as
86
+ * {@link pruneOldLlmRequestLogsJob}.
61
87
  */
62
- export function pruneOldTraceEventsJob(
88
+ export async function pruneOldTraceEventsJob(
63
89
  job: MemoryJob,
64
90
  config: AssistantConfig,
65
- ): void {
91
+ ): Promise<void> {
66
92
  const rawRetention = job.payload.retentionDays;
67
93
  const retentionDays =
68
94
  typeof rawRetention === "number" &&
@@ -74,14 +100,22 @@ export function pruneOldTraceEventsJob(
74
100
  // 0 means disabled
75
101
  if (retentionDays === 0) return;
76
102
 
77
- const cutoffMs = Date.now() - retentionDays * 86_400_000;
103
+ const cutoffMs = Math.floor(Date.now() - retentionDays * 86_400_000);
104
+ if (!Number.isFinite(cutoffMs)) return;
78
105
 
79
- rawRun(
80
- `DELETE FROM trace_events WHERE rowid IN (SELECT rowid FROM trace_events WHERE created_at < ? LIMIT ?)`,
81
- cutoffMs,
82
- PRUNE_LOG_BATCH_LIMIT,
106
+ const result = await runAsyncSqlite(
107
+ `DELETE FROM trace_events WHERE rowid IN (SELECT rowid FROM trace_events WHERE created_at < ${cutoffMs} LIMIT ${PRUNE_LOG_BATCH_LIMIT});
108
+ SELECT changes();`,
83
109
  );
84
- const deleted = rawChanges();
110
+ if (!result.ok) {
111
+ log.warn(
112
+ { error: result.error, backend: result.backend },
113
+ "pruneOldTraceEventsJob: DELETE failed",
114
+ );
115
+ return;
116
+ }
117
+
118
+ const deleted = parseDeletedCount(result.stdout);
85
119
 
86
120
  if (deleted >= PRUNE_LOG_BATCH_LIMIT) {
87
121
  enqueueMemoryJob("prune_old_trace_events", { retentionDays });
@@ -97,6 +131,30 @@ export function pruneOldTraceEventsJob(
97
131
  );
98
132
  }
99
133
 
134
+ /**
135
+ * Parse the `SELECT changes()` result emitted by the sqlite3 CLI after
136
+ * the prune DELETE. Returns 0 if stdout is missing or unparseable —
137
+ * callers treat that the same as "no rows deleted, do not re-enqueue".
138
+ *
139
+ * In the CLI's default output mode the value is a bare integer on its
140
+ * own line. We tolerate trailing whitespace/blank lines and pick the
141
+ * last numeric line so any incidental output (warnings, etc.) above it
142
+ * doesn't throw the parse off.
143
+ */
144
+ export function _parseDeletedCount(stdout: string | undefined): number {
145
+ return parseDeletedCount(stdout);
146
+ }
147
+
148
+ function parseDeletedCount(stdout: string | undefined): number {
149
+ if (!stdout) return 0;
150
+ const lines = stdout.split(/\r?\n/).filter((s) => s.trim().length > 0);
151
+ for (let i = lines.length - 1; i >= 0; i--) {
152
+ const n = parseInt(lines[i].trim(), 10);
153
+ if (Number.isFinite(n) && n >= 0) return n;
154
+ }
155
+ return 0;
156
+ }
157
+
100
158
  /**
101
159
  * Delete conversations that have had no activity (updatedAt) for longer than
102
160
  * the configured retention period. Processes in batches so a single job doesn't
@@ -9,7 +9,6 @@ import { and, desc, eq, sql } from "drizzle-orm";
9
9
  import { v4 as uuid } from "uuid";
10
10
 
11
11
  import { loadSkillCatalog } from "../../config/skills.js";
12
- import { resolveGuardianPersona } from "../../prompts/persona-resolver.js";
13
12
  import { buildCoreIdentityContext } from "../../prompts/system-prompt.js";
14
13
  import {
15
14
  createTimeout,
@@ -200,9 +199,7 @@ async function generateStarters(scopeId: string): Promise<GeneratedStarter[]> {
200
199
 
201
200
  // Truncate identity context to prevent oversized prompts when SOUL.md /
202
201
  // IDENTITY.md / users/<slug>.md are large.
203
- const rawIdentityContext = buildCoreIdentityContext({
204
- userPersona: resolveGuardianPersona(),
205
- });
202
+ const rawIdentityContext = buildCoreIdentityContext();
206
203
  const identityContext = rawIdentityContext
207
204
  ? truncate(rawIdentityContext, 2000, "\n…[truncated]")
208
205
  : null;
@@ -1,6 +1,10 @@
1
1
  import type { AssistantConfig } from "../../config/types.js";
2
2
  import { asString } from "../job-utils.js";
3
- import { enqueueMemoryJob, type MemoryJob } from "../jobs-store.js";
3
+ import {
4
+ enqueueMemoryJob,
5
+ isMemoryEnabled,
6
+ type MemoryJob,
7
+ } from "../jobs-store.js";
4
8
  import { indexPkbFile } from "../pkb/pkb-index.js";
5
9
 
6
10
  /**
@@ -46,6 +50,7 @@ export async function embedPkbFileJob(
46
50
  * Enqueue an `embed_pkb_file` job (async, fire-and-forget).
47
51
  */
48
52
  export function enqueuePkbIndexJob(input: EmbedPkbFileJobInput): string {
53
+ if (!isMemoryEnabled()) return "";
49
54
  return enqueueMemoryJob("embed_pkb_file", {
50
55
  pkbRoot: input.pkbRoot,
51
56
  absPath: input.absPath,
@@ -1,6 +1,7 @@
1
1
  import { and, asc, eq, inArray, lte, notInArray, or, sql } from "drizzle-orm";
2
2
  import { v4 as uuid } from "uuid";
3
3
 
4
+ import { getConfig } from "../config/loader.js";
4
5
  import { getLogger } from "../util/logger.js";
5
6
  import { truncate } from "../util/truncate.js";
6
7
  import { getDb } from "./db-connection.js";
@@ -43,6 +44,9 @@ export type MemoryJobType =
43
44
  | "memory_v2_migrate"
44
45
  | "memory_v2_reembed"
45
46
  | "memory_v2_activation_recompute"
47
+ | "memory_v3_consolidate"
48
+ | "memory_v3_index_maintenance"
49
+ | "memory_v3_edge_learning"
46
50
  | "memory_retrospective";
47
51
 
48
52
  export const EMBED_JOB_TYPES: MemoryJobType[] = [
@@ -66,12 +70,22 @@ export const SLOW_LLM_JOB_TYPES: MemoryJobType[] = [
66
70
  "generate_conversation_starters",
67
71
  "memory_v2_sweep",
68
72
  "memory_v2_consolidate",
73
+ "memory_v3_consolidate",
69
74
  "memory_v2_migrate",
70
75
  "memory_retrospective",
71
76
  "backfill",
72
77
  "graph_bootstrap",
73
78
  ];
74
79
 
80
+ /** Returns `false` only when `config.memory.enabled` is explicitly `false`; defaults to `true` on missing config or load errors. */
81
+ export function isMemoryEnabled(): boolean {
82
+ try {
83
+ return getConfig().memory?.enabled !== false;
84
+ } catch {
85
+ return true;
86
+ }
87
+ }
88
+
75
89
  export interface MemoryJob<T = Record<string, unknown>> {
76
90
  id: string;
77
91
  type: MemoryJobType;
@@ -83,6 +83,9 @@ import {
83
83
  memoryV2ConsolidateJob,
84
84
  } from "./v2/consolidation-job.js";
85
85
  import { memoryV2SweepJob } from "./v2/sweep-job.js";
86
+ import { memoryV3ConsolidateJob } from "./v3/consolidation-job.js";
87
+ import { memoryV3EdgeLearningJob } from "./v3/edge-learning-job.js";
88
+ import { memoryV3IndexMaintenanceJob } from "./v3/maintenance.js";
86
89
 
87
90
  const log = getLogger("memory-jobs-worker");
88
91
 
@@ -259,7 +262,7 @@ export async function runMemoryJobsOnce(
259
262
  maybeEnqueueScheduledCleanupJobs(config);
260
263
  }
261
264
  maybeEnqueueGraphMaintenanceJobs(config);
262
- maybeRunDbMaintenance();
265
+ await maybeRunDbMaintenance();
263
266
  return 0;
264
267
  }
265
268
 
@@ -313,7 +316,7 @@ export async function runMemoryJobsOnce(
313
316
  maybeEnqueueScheduledCleanupJobs(config);
314
317
  }
315
318
  maybeEnqueueGraphMaintenanceJobs(config);
316
- maybeRunDbMaintenance();
319
+ await maybeRunDbMaintenance();
317
320
  return slowProcessed + fastProcessed + embedProcessed;
318
321
  }
319
322
 
@@ -524,10 +527,10 @@ async function processJob(
524
527
  pruneOldConversationsJob(job, config);
525
528
  return;
526
529
  case "prune_old_llm_request_logs":
527
- pruneOldLlmRequestLogsJob(job, config);
530
+ await pruneOldLlmRequestLogsJob(job, config);
528
531
  return;
529
532
  case "prune_old_trace_events":
530
- pruneOldTraceEventsJob(job, config);
533
+ await pruneOldTraceEventsJob(job, config);
531
534
  return;
532
535
  case "build_conversation_summary":
533
536
  // Stale rows enqueued before v2 was enabled must not consume the
@@ -603,6 +606,16 @@ async function processJob(
603
606
  case "memory_v2_consolidate":
604
607
  await memoryV2ConsolidateJob(job, config);
605
608
  return;
609
+ case "memory_v3_consolidate":
610
+ await memoryV3ConsolidateJob(job, config);
611
+ return;
612
+ case "memory_v3_index_maintenance":
613
+ await memoryV3IndexMaintenanceJob(job);
614
+ return;
615
+ case "memory_v3_edge_learning":
616
+ // Fast lane: bounded DB work (decay + reinforce + read), no LLM.
617
+ memoryV3EdgeLearningJob(job);
618
+ return;
606
619
  case "memory_v2_migrate":
607
620
  await memoryV2MigrateJob(job, config);
608
621
  return;
@@ -681,17 +694,28 @@ export const GRAPH_MAINTENANCE_CHECKPOINTS = {
681
694
  patternScan: "graph_maintenance:pattern_scan:last_run",
682
695
  narrative: "graph_maintenance:narrative:last_run",
683
696
  memoryV2Consolidate: "memory_v2_consolidate_last_run",
697
+ memoryV3Consolidate: "memory_v3_consolidate_last_run",
684
698
  } as const;
685
699
 
686
700
  /**
687
701
  * Enqueue periodic graph maintenance jobs.
688
702
  *
689
703
  * Mutually exclusive between v1 and v2:
690
- * - v2 active (`memory.v2.enabled` on) → only `memory_v2_consolidate` is
691
- * scheduled.
704
+ * - v2 active (`memory.v2.enabled` on) → only one buffer-drainer is
705
+ * scheduled (see below).
692
706
  * - v2 inactive → the four v1 entries (decay, consolidate, pattern_scan,
693
707
  * narrative) are scheduled instead.
694
708
  *
709
+ * **Buffer-drainer retarget (v2 vs v3).** The `memory/buffer.md` is shared, so
710
+ * exactly one consolidator may own the drain at a time. When
711
+ * `memory.v3.write.enabled` is on, the v3 consolidator (`memory_v3_consolidate`)
712
+ * is scheduled INSTEAD of `memory_v2_consolidate` — same shared buffer +
713
+ * standing-context files, additionally authored into the v3 tree. When the v3
714
+ * write flag is off (default) the v2 consolidator stays the sole drainer,
715
+ * unchanged. The retarget is a clean conditional, fully reversible via the flag.
716
+ * Concept pages stay the shared canonical store, so the v2 router keeps working
717
+ * off pages v3 writes regardless of which consolidator ran.
718
+ *
695
719
  * Read/write paths route to v2 when the flag is on, so v1 graph data goes
696
720
  * unread; running v1 maintenance alongside v2 is wasted compute and LLM
697
721
  * spend. The v1 code path remains live so flipping the flag back to off
@@ -708,20 +732,29 @@ export function maybeEnqueueGraphMaintenanceJobs(
708
732
  nowMs = Date.now(),
709
733
  ): void {
710
734
  const v2Active = config.memory.v2.enabled;
735
+ const v3WriteActive = config.memory.v3.write.enabled;
736
+
737
+ // The single buffer-drainer entry for the v2-active branch: v3 when the v3
738
+ // write flag owns the drain, v2 otherwise. Same shared buffer either way.
739
+ const consolidateEntry = v3WriteActive
740
+ ? {
741
+ key: GRAPH_MAINTENANCE_CHECKPOINTS.memoryV3Consolidate,
742
+ intervalMs: config.memory.v3.write.consolidateIntervalMs,
743
+ jobType: "memory_v3_consolidate" as MemoryJobType,
744
+ }
745
+ : {
746
+ key: GRAPH_MAINTENANCE_CHECKPOINTS.memoryV2Consolidate,
747
+ intervalMs:
748
+ config.memory.v2.consolidation_interval_hours * 60 * 60 * 1000,
749
+ jobType: "memory_v2_consolidate" as MemoryJobType,
750
+ };
711
751
 
712
752
  const schedule: Array<{
713
753
  key: string;
714
754
  intervalMs: number;
715
755
  jobType: MemoryJobType;
716
756
  }> = v2Active
717
- ? [
718
- {
719
- key: GRAPH_MAINTENANCE_CHECKPOINTS.memoryV2Consolidate,
720
- intervalMs:
721
- config.memory.v2.consolidation_interval_hours * 60 * 60 * 1000,
722
- jobType: "memory_v2_consolidate",
723
- },
724
- ]
757
+ ? [consolidateEntry]
725
758
  : [
726
759
  {
727
760
  key: GRAPH_MAINTENANCE_CHECKPOINTS.decay,
@@ -745,25 +778,25 @@ export function maybeEnqueueGraphMaintenanceJobs(
745
778
  },
746
779
  ];
747
780
 
748
- let enqueuedV2 = false;
781
+ let enqueuedConsolidate = false;
749
782
  for (const { key, intervalMs, jobType } of schedule) {
750
783
  const lastRun = parseInt(getMemoryCheckpoint(key) ?? "0", 10);
751
784
  if (nowMs - lastRun >= intervalMs) {
752
785
  enqueueMemoryJob(jobType, {});
753
786
  setMemoryCheckpoint(key, String(nowMs));
754
- if (jobType === "memory_v2_consolidate") enqueuedV2 = true;
787
+ if (jobType === consolidateEntry.jobType) enqueuedConsolidate = true;
755
788
  }
756
789
  }
757
790
 
791
+ // Size-based trigger: when the shared buffer crosses the configured line
792
+ // count, drain it now rather than waiting out the interval. Retargets to the
793
+ // same consolidator the interval branch above selected.
758
794
  const maxLines = config.memory.v2.consolidation_max_buffer_lines;
759
- if (v2Active && !enqueuedV2 && maxLines !== null) {
795
+ if (v2Active && !enqueuedConsolidate && maxLines !== null) {
760
796
  const bufferPath = join(getWorkspaceDir(), "memory", "buffer.md");
761
797
  if (countBufferLines(bufferPath) >= maxLines) {
762
- enqueueMemoryJob("memory_v2_consolidate", {});
763
- setMemoryCheckpoint(
764
- GRAPH_MAINTENANCE_CHECKPOINTS.memoryV2Consolidate,
765
- String(nowMs),
766
- );
798
+ enqueueMemoryJob(consolidateEntry.jobType, {});
799
+ setMemoryCheckpoint(consolidateEntry.key, String(nowMs));
767
800
  }
768
801
  }
769
802
  }
@@ -58,6 +58,16 @@ interface ClickHouseRow {
58
58
  response_payload: string;
59
59
  created_at: string;
60
60
  agent_loop_exit_reason: string;
61
+ /**
62
+ * Mirrors `llm_request_logs.call_site` from the SQLite source. Added
63
+ * to the CH `default.llm_request_logs` table via ALTER TABLE (matching
64
+ * the `agent_loop_exit_reason` precedent — see
65
+ * `memory/concepts/objects/clickhouse-mirror.md`).
66
+ *
67
+ * CH columns are `DEFAULT ''` rather than Nullable, so empty-string
68
+ * means "not set" — `toLogRow` maps that back to NULL on the JS side.
69
+ */
70
+ call_site: string;
61
71
  }
62
72
 
63
73
  /** Injectable fetch override for tests. Defaults to globalThis.fetch. */
@@ -125,7 +135,8 @@ export class ClickHouseLlmRequestLogSource implements LlmRequestLogSource {
125
135
  request_payload,
126
136
  response_payload,
127
137
  toUnixTimestamp64Milli(created_at) AS created_at,
128
- agent_loop_exit_reason
138
+ agent_loop_exit_reason,
139
+ call_site
129
140
  FROM ${this.tableRef()}
130
141
  WHERE assistant_id = {assistant_id:String}
131
142
  AND id = {log_id:String}
@@ -171,6 +182,33 @@ export class ClickHouseLlmRequestLogSource implements LlmRequestLogSource {
171
182
  );
172
183
  }
173
184
 
185
+ async getRequestLogsByConversationId(
186
+ conversationId: string,
187
+ ): Promise<LogRow[]> {
188
+ const aid = await this.assistantId();
189
+ const sql = `SELECT
190
+ id,
191
+ conversation_id,
192
+ message_id,
193
+ provider,
194
+ request_payload,
195
+ response_payload,
196
+ toUnixTimestamp64Milli(created_at) AS created_at,
197
+ agent_loop_exit_reason,
198
+ call_site
199
+ FROM ${this.tableRef()}
200
+ WHERE assistant_id = {assistant_id:String}
201
+ AND conversation_id = {conversation_id:String}
202
+ ORDER BY created_at ASC, id ASC
203
+ LIMIT 1 BY id
204
+ FORMAT JSONEachRow`;
205
+ const rows = await this.exec(sql, {
206
+ assistant_id: aid,
207
+ conversation_id: conversationId,
208
+ });
209
+ return rows.map((r) => this.toLogRow(r));
210
+ }
211
+
174
212
  private async selectByMessageIds(ids: string[]): Promise<LogRow[]> {
175
213
  if (ids.length === 0) return [];
176
214
  const aid = await this.assistantId();
@@ -197,7 +235,8 @@ export class ClickHouseLlmRequestLogSource implements LlmRequestLogSource {
197
235
  request_payload,
198
236
  response_payload,
199
237
  toUnixTimestamp64Milli(created_at) AS created_at,
200
- agent_loop_exit_reason
238
+ agent_loop_exit_reason,
239
+ call_site
201
240
  FROM ${this.tableRef()}
202
241
  WHERE assistant_id = {assistant_id:String}
203
242
  AND message_id IN (${placeholders.join(",")})
@@ -288,6 +327,7 @@ export class ClickHouseLlmRequestLogSource implements LlmRequestLogSource {
288
327
  createdAt: Number(row.created_at),
289
328
  agentLoopExitReason:
290
329
  row.agent_loop_exit_reason === "" ? null : row.agent_loop_exit_reason,
330
+ callSite: row.call_site === "" ? null : row.call_site,
291
331
  };
292
332
  }
293
333
 
@@ -11,6 +11,7 @@
11
11
  import type { LlmRequestLogSource } from "./llm-request-log-source.js";
12
12
  import {
13
13
  getRequestLogById,
14
+ getRequestLogsByConversationId,
14
15
  getRequestLogsByMessageId,
15
16
  type LogRow,
16
17
  } from "./llm-request-log-store.js";
@@ -23,4 +24,10 @@ export class LocalLlmRequestLogSource implements LlmRequestLogSource {
23
24
  async getRequestLogsByMessageId(messageId: string): Promise<LogRow[]> {
24
25
  return getRequestLogsByMessageId(messageId);
25
26
  }
27
+
28
+ async getRequestLogsByConversationId(
29
+ conversationId: string,
30
+ ): Promise<LogRow[]> {
31
+ return getRequestLogsByConversationId(conversationId);
32
+ }
26
33
  }
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Pluggable read source for LLM request logs.
3
3
  *
4
- * The Inspector view at `GET /v1/messages/:id/llm-context` (and the
5
- * single-log payload route) historically read directly from the local
4
+ * The Inspector views at `GET /v1/messages/:id/llm-context` and
5
+ * `GET /v1/conversations/llm-context` (plus the single-log payload route) historically read directly from the local
6
6
  * SQLite `llm_request_logs` table via `llm-request-log-store.ts`. The
7
7
  * source-of-truth remains local, but the *read path* is now configurable
8
8
  * via `llmRequestLogs.readSource` in workspace config.
@@ -31,6 +31,13 @@ export interface LlmRequestLogSource {
31
31
  * INSERT-only against the source-of-truth).
32
32
  */
33
33
  getRequestLogsByMessageId(messageId: string): Promise<LogRow[]>;
34
+
35
+ /**
36
+ * Fetch every LLM request log associated with the given conversation.
37
+ * This is the conversation-wide inspector read path: linked, unlinked,
38
+ * and orphaned logs are all included because they share conversation_id.
39
+ */
40
+ getRequestLogsByConversationId(conversationId: string): Promise<LogRow[]>;
34
41
  }
35
42
 
36
43
  /**