@vellumai/assistant 0.7.2 → 0.7.3

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 (347) hide show
  1. package/ARCHITECTURE.md +16 -1
  2. package/docs/architecture/memory.md +5 -2
  3. package/node_modules/@vellumai/gateway-client/src/ipc-client.ts +13 -4
  4. package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +0 -9
  5. package/node_modules/@vellumai/slack-text/src/index.test.ts +18 -35
  6. package/node_modules/@vellumai/slack-text/src/index.ts +2 -48
  7. package/openapi.yaml +449 -22
  8. package/package.json +1 -1
  9. package/src/__tests__/app-control-flow.test.ts +21 -11
  10. package/src/__tests__/assistant-event-hub.test.ts +48 -0
  11. package/src/__tests__/assistant-event.test.ts +0 -10
  12. package/src/__tests__/assistant-events-sse-hardening.test.ts +2 -7
  13. package/src/__tests__/assistant-feature-flags-integration.test.ts +18 -0
  14. package/src/__tests__/auto-analysis-end-to-end.test.ts +62 -1
  15. package/src/__tests__/background-workers-disk-pressure.test.ts +268 -0
  16. package/src/__tests__/call-conversation-messages.test.ts +8 -2
  17. package/src/__tests__/channel-inbound-disk-pressure.test.ts +537 -0
  18. package/src/__tests__/channel-readiness-service.test.ts +4 -2
  19. package/src/__tests__/config-loader-backfill.test.ts +379 -0
  20. package/src/__tests__/config-schema.test.ts +1 -0
  21. package/src/__tests__/config-watcher-cleanup-throttle.test.ts +18 -9
  22. package/src/__tests__/config-watcher.test.ts +140 -69
  23. package/src/__tests__/context-search-agent-runner.test.ts +61 -3
  24. package/src/__tests__/context-search-conversations-source.test.ts +0 -24
  25. package/src/__tests__/context-search-fanout.test.ts +0 -1
  26. package/src/__tests__/context-search-memory-source.test.ts +3 -7
  27. package/src/__tests__/context-search-memory-v2-source.test.ts +0 -2
  28. package/src/__tests__/context-search-pkb-source.test.ts +0 -1
  29. package/src/__tests__/context-search-workspace-source.test.ts +0 -1
  30. package/src/__tests__/conversation-abort-tool-results.test.ts +6 -0
  31. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +223 -0
  32. package/src/__tests__/conversation-agent-loop.test.ts +454 -5
  33. package/src/__tests__/conversation-error.test.ts +150 -3
  34. package/src/__tests__/conversation-process-callsite.test.ts +43 -0
  35. package/src/__tests__/conversation-provider-retry-repair.test.ts +6 -0
  36. package/src/__tests__/conversation-runtime-assembly.test.ts +65 -0
  37. package/src/__tests__/conversation-slash-unknown.test.ts +6 -0
  38. package/src/__tests__/conversation-speed-override.test.ts +0 -3
  39. package/src/__tests__/conversation-store.test.ts +0 -18
  40. package/src/__tests__/conversation-surfaces-app-control.test.ts +15 -4
  41. package/src/__tests__/conversation-surfaces-data-persist.test.ts +404 -0
  42. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +2 -5
  43. package/src/__tests__/conversation-workspace-injection.test.ts +6 -0
  44. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +6 -0
  45. package/src/__tests__/credentials-cli.test.ts +7 -0
  46. package/src/__tests__/cu-unified-flow.test.ts +176 -10
  47. package/src/__tests__/date-context.test.ts +164 -2
  48. package/src/__tests__/disk-pressure-guard.test.ts +262 -0
  49. package/src/__tests__/disk-pressure-lifecycle.test.ts +168 -0
  50. package/src/__tests__/disk-pressure-policy.test.ts +241 -0
  51. package/src/__tests__/disk-pressure-routes.test.ts +379 -0
  52. package/src/__tests__/disk-pressure-tools.test.ts +277 -0
  53. package/src/__tests__/disk-usage.test.ts +150 -0
  54. package/src/__tests__/events-client-registration.test.ts +52 -0
  55. package/src/__tests__/events-dev-bypass-actor.test.ts +162 -0
  56. package/src/__tests__/file-write-tool.test.ts +4 -10
  57. package/src/__tests__/filing-service.test.ts +3 -4
  58. package/src/__tests__/heartbeat-disk-pressure.test.ts +183 -0
  59. package/src/__tests__/heartbeat-service.test.ts +260 -11
  60. package/src/__tests__/host-app-control-proxy.test.ts +195 -25
  61. package/src/__tests__/host-bash-proxy.test.ts +227 -34
  62. package/src/__tests__/host-bash-routes.test.ts +178 -13
  63. package/src/__tests__/host-cu-proxy.test.ts +210 -3
  64. package/src/__tests__/host-cu-routes-targeted.test.ts +141 -12
  65. package/src/__tests__/host-file-proxy-targeted.test.ts +48 -9
  66. package/src/__tests__/host-file-proxy.test.ts +268 -6
  67. package/src/__tests__/host-file-routes-targeted.test.ts +175 -17
  68. package/src/__tests__/host-transfer-proxy-targeted.test.ts +408 -59
  69. package/src/__tests__/host-transfer-routes-targeted.test.ts +232 -17
  70. package/src/__tests__/http-user-message-parity.test.ts +107 -1
  71. package/src/__tests__/injector-chain.test.ts +18 -6
  72. package/src/__tests__/injector-disk-pressure.test.ts +224 -0
  73. package/src/__tests__/managed-profile-guard.test.ts +18 -0
  74. package/src/__tests__/mcp-abort-signal.test.ts +130 -0
  75. package/src/__tests__/memory-admin-recall.test.ts +3 -11
  76. package/src/__tests__/memory-retrieval-pipeline.test.ts +22 -1
  77. package/src/__tests__/normalize-onboarding.test.ts +180 -0
  78. package/src/__tests__/oauth-connect-routes.test.ts +316 -0
  79. package/src/__tests__/oauth-provider-seed-logos.test.ts +24 -2
  80. package/src/__tests__/onboarding-persona-write.test.ts +308 -0
  81. package/src/__tests__/openai-provider.test.ts +45 -8
  82. package/src/__tests__/persist-onboarding-artifacts.test.ts +44 -64
  83. package/src/__tests__/platform-callback-registration.test.ts +21 -4
  84. package/src/__tests__/platform.test.ts +2 -1
  85. package/src/__tests__/playbook-execution.test.ts +0 -43
  86. package/src/__tests__/plugin-tool-contribution.test.ts +47 -0
  87. package/src/__tests__/prechat-onboarding-contract.test.ts +214 -27
  88. package/src/__tests__/provider-tool-name.test.ts +23 -0
  89. package/src/__tests__/relay-server.test.ts +15 -4
  90. package/src/__tests__/runtime-events-sse.test.ts +4 -8
  91. package/src/__tests__/scheduler-disk-pressure.test.ts +148 -0
  92. package/src/__tests__/secret-ingress-http.test.ts +0 -1
  93. package/src/__tests__/suggestion-routes.test.ts +46 -0
  94. package/src/__tests__/twilio-validation.test.ts +2 -2
  95. package/src/__tests__/workspace-migration-065-bump-stale-heartbeat-interval.test.ts +122 -0
  96. package/src/__tests__/workspace-migration-066-seed-heartbeat-callsite-cost-default.test.ts +285 -0
  97. package/src/__tests__/workspace-migration-068-release-notes-local-timezone.test.ts +90 -0
  98. package/src/__tests__/workspace-migration-safe-storage-limits-release.test.ts +90 -0
  99. package/src/approvals/guardian-decision-primitive.ts +13 -0
  100. package/src/approvals/guardian-request-resolvers.ts +16 -17
  101. package/src/backup/snapshot-lock.ts +2 -27
  102. package/src/bundler/compiler-tools.ts +3 -2
  103. package/src/calls/call-conversation-messages.ts +46 -10
  104. package/src/cli/commands/__tests__/webhooks.test.ts +0 -4
  105. package/src/cli/commands/bash.ts +35 -108
  106. package/src/cli/commands/contacts.ts +64 -25
  107. package/src/cli/commands/credentials.ts +56 -0
  108. package/src/cli/commands/memory-v2.ts +7 -6
  109. package/src/cli/commands/oauth/__tests__/connect.test.ts +437 -1
  110. package/src/cli/commands/oauth/connect.ts +127 -1
  111. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -3
  112. package/src/cli/commands/platform/__tests__/connect.test.ts +7 -1
  113. package/src/cli/commands/platform/__tests__/disconnect.test.ts +7 -1
  114. package/src/cli/commands/platform/__tests__/status.test.ts +103 -6
  115. package/src/cli/commands/platform/index.ts +16 -7
  116. package/src/cli/commands/status.ts +57 -0
  117. package/src/cli/program.ts +4 -2
  118. package/src/config/assistant-feature-flags.ts +13 -3
  119. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +4 -3
  120. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +13 -7
  121. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +2 -2
  122. package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +2 -2
  123. package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +2 -2
  124. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +2 -2
  125. package/src/config/env.ts +0 -8
  126. package/src/config/feature-flag-registry.json +27 -3
  127. package/src/config/loader.ts +127 -8
  128. package/src/config/schemas/__tests__/memory-v2.test.ts +10 -5
  129. package/src/config/schemas/call-site-catalog.ts +14 -0
  130. package/src/config/schemas/channels.ts +0 -5
  131. package/src/config/schemas/heartbeat.ts +1 -1
  132. package/src/config/schemas/llm.ts +2 -0
  133. package/src/config/schemas/memory-lifecycle.ts +13 -0
  134. package/src/config/schemas/memory-v2.ts +75 -11
  135. package/src/config/schemas/platform.ts +43 -3
  136. package/src/config/schemas/services.ts +28 -0
  137. package/src/config/seed-inference-profiles.ts +230 -33
  138. package/src/contacts/contact-store.ts +0 -25
  139. package/src/daemon/__tests__/conversation-tool-setup.test.ts +86 -25
  140. package/src/daemon/assistant-attachments.ts +4 -4
  141. package/src/daemon/config-watcher.ts +85 -57
  142. package/src/daemon/conversation-agent-loop-handlers.ts +6 -0
  143. package/src/daemon/conversation-agent-loop.ts +170 -33
  144. package/src/daemon/conversation-error.ts +87 -15
  145. package/src/daemon/conversation-lifecycle.ts +1 -3
  146. package/src/daemon/conversation-process.ts +8 -0
  147. package/src/daemon/conversation-runtime-assembly.ts +26 -0
  148. package/src/daemon/conversation-store.ts +2 -2
  149. package/src/daemon/conversation-surfaces.ts +195 -15
  150. package/src/daemon/conversation-tool-setup.ts +57 -14
  151. package/src/daemon/conversation.ts +17 -22
  152. package/src/daemon/date-context.ts +71 -22
  153. package/src/daemon/disk-pressure-background-gate.ts +73 -0
  154. package/src/daemon/disk-pressure-guard.ts +343 -0
  155. package/src/daemon/disk-pressure-policy.ts +163 -0
  156. package/src/daemon/handlers/shared.ts +0 -1
  157. package/src/daemon/handlers/skills.ts +3 -4
  158. package/src/daemon/host-app-control-proxy.ts +137 -41
  159. package/src/daemon/host-bash-proxy.ts +46 -21
  160. package/src/daemon/host-cu-proxy.ts +49 -3
  161. package/src/daemon/host-file-proxy.ts +43 -7
  162. package/src/daemon/host-transfer-proxy.ts +95 -4
  163. package/src/daemon/lifecycle.ts +79 -28
  164. package/src/daemon/meet-host-supervisor.ts +4 -4
  165. package/src/daemon/meet-manifest-loader.ts +0 -1
  166. package/src/daemon/memory-v2-startup.ts +14 -4
  167. package/src/daemon/message-protocol.ts +3 -0
  168. package/src/daemon/message-types/conversations.ts +4 -0
  169. package/src/daemon/message-types/disk-pressure.ts +9 -0
  170. package/src/daemon/message-types/messages.ts +3 -0
  171. package/src/daemon/profiler-run-store.ts +5 -5
  172. package/src/daemon/tool-setup-types.ts +2 -2
  173. package/src/documents/document-store.ts +85 -0
  174. package/src/filing/filing-service.ts +30 -5
  175. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +9 -16
  176. package/src/heartbeat/__tests__/heartbeat-run-store.test.ts +36 -0
  177. package/src/heartbeat/heartbeat-run-store.ts +13 -0
  178. package/src/heartbeat/heartbeat-service.ts +205 -31
  179. package/src/home/feed-scheduler.ts +18 -0
  180. package/src/inbound/platform-callback-registration.ts +8 -15
  181. package/src/ipc/__tests__/clients-list-ipc.test.ts +169 -0
  182. package/src/ipc/assistant-server.ts +56 -2
  183. package/src/ipc/gateway-client.ts +37 -3
  184. package/src/live-voice/live-voice-archive.ts +4 -4
  185. package/src/live-voice/protocol.ts +5 -7
  186. package/src/media/image-service.ts +1 -7
  187. package/src/memory/__tests__/fixtures/memory-v2-activation-fixtures.ts +21 -13
  188. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +52 -22
  189. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +0 -6
  190. package/src/memory/__tests__/memory-v2-concept-frequency.test.ts +272 -0
  191. package/src/memory/admin.ts +5 -9
  192. package/src/memory/context-search/agent-runner.ts +19 -2
  193. package/src/memory/context-search/sources/conversations.ts +2 -11
  194. package/src/memory/context-search/sources/memory-v2.ts +5 -4
  195. package/src/memory/context-search/sources/memory.ts +0 -1
  196. package/src/memory/context-search/types.ts +0 -1
  197. package/src/memory/conversation-crud.ts +4 -12
  198. package/src/memory/db-init.ts +2 -0
  199. package/src/memory/embedding-runtime-manager.ts +119 -5
  200. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +32 -21
  201. package/src/memory/graph/conversation-graph-memory.ts +42 -54
  202. package/src/memory/graph/extraction.ts +1 -3
  203. package/src/memory/graph/graph-search.test.ts +10 -67
  204. package/src/memory/graph/graph-search.ts +1 -20
  205. package/src/memory/graph/retriever.test.ts +6 -0
  206. package/src/memory/graph/retriever.ts +6 -10
  207. package/src/memory/indexer.ts +54 -45
  208. package/src/memory/job-handlers/backfill.ts +2 -11
  209. package/src/memory/job-handlers/cleanup.ts +43 -0
  210. package/src/memory/job-handlers/embedding.ts +6 -8
  211. package/src/memory/job-handlers/summarization.ts +2 -7
  212. package/src/memory/jobs-store.ts +48 -0
  213. package/src/memory/jobs-worker.ts +81 -43
  214. package/src/memory/memory-v2-activation-log-store.ts +32 -14
  215. package/src/memory/memory-v2-concept-frequency.ts +169 -0
  216. package/src/memory/migrations/239-trace-events-created-at-index.ts +18 -0
  217. package/src/memory/migrations/index.ts +1 -0
  218. package/src/memory/pkb/pkb-search.test.ts +6 -0
  219. package/src/memory/qdrant-client.ts +0 -13
  220. package/src/memory/rerank-local.ts +374 -0
  221. package/src/memory/search/semantic.ts +6 -67
  222. package/src/memory/trace-event-store.ts +1 -17
  223. package/src/memory/v2/__tests__/activation.test.ts +311 -250
  224. package/src/memory/v2/__tests__/consolidation-job.test.ts +40 -8
  225. package/src/memory/v2/__tests__/injection.test.ts +157 -167
  226. package/src/memory/v2/__tests__/prompts-consolidation.test.ts +61 -2
  227. package/src/memory/v2/__tests__/qdrant.test.ts +16 -0
  228. package/src/memory/v2/__tests__/reranker.test.ts +338 -0
  229. package/src/memory/v2/__tests__/sim.test.ts +5 -199
  230. package/src/memory/v2/__tests__/skill-store.test.ts +71 -65
  231. package/src/memory/v2/__tests__/static-context.test.ts +76 -1
  232. package/src/memory/v2/activation.ts +149 -156
  233. package/src/memory/v2/consolidation-job.ts +62 -12
  234. package/src/memory/v2/injection.ts +47 -60
  235. package/src/memory/v2/prompts/consolidation.ts +36 -1
  236. package/src/memory/v2/qdrant.ts +99 -0
  237. package/src/memory/v2/reranker.ts +177 -0
  238. package/src/memory/v2/sim.ts +10 -84
  239. package/src/memory/v2/skill-content.ts +4 -3
  240. package/src/memory/v2/skill-store.ts +82 -59
  241. package/src/memory/v2/static-context.ts +22 -0
  242. package/src/memory/v2/types.ts +10 -10
  243. package/src/notifications/copy-composer.ts +13 -0
  244. package/src/notifications/signal.ts +4 -0
  245. package/src/oauth/AGENTS.md +3 -1
  246. package/src/oauth/__tests__/oauth-connect-state.test.ts +137 -0
  247. package/src/oauth/connect-orchestrator.ts +2 -0
  248. package/src/oauth/connection-resolver.test.ts +66 -1
  249. package/src/oauth/connection-resolver.ts +55 -1
  250. package/src/oauth/oauth-connect-state.ts +77 -0
  251. package/src/oauth/seed-providers.ts +58 -1
  252. package/src/plugins/defaults/injectors.ts +35 -2
  253. package/src/plugins/defaults/memory-retrieval.ts +5 -6
  254. package/src/plugins/types.ts +7 -0
  255. package/src/proactive-artifact/aux-message-injector.ts +74 -0
  256. package/src/proactive-artifact/decision.test.ts +226 -0
  257. package/src/proactive-artifact/decision.ts +165 -0
  258. package/src/proactive-artifact/index.ts +7 -0
  259. package/src/proactive-artifact/job.test.ts +867 -0
  260. package/src/proactive-artifact/job.ts +352 -0
  261. package/src/proactive-artifact/message-copy.ts +41 -0
  262. package/src/proactive-artifact/trigger-state.test.ts +277 -0
  263. package/src/proactive-artifact/trigger-state.ts +119 -0
  264. package/src/prompts/normalize-onboarding.ts +80 -0
  265. package/src/prompts/persona-resolver.ts +101 -9
  266. package/src/prompts/system-prompt.ts +21 -7
  267. package/src/prompts/templates/BOOTSTRAP.md +13 -5
  268. package/src/providers/__tests__/retry-callsite.test.ts +222 -1
  269. package/src/providers/model-intents.ts +7 -0
  270. package/src/providers/openrouter/client.ts +8 -0
  271. package/src/providers/retry.ts +50 -0
  272. package/src/providers/types.ts +1 -0
  273. package/src/runtime/__tests__/agent-wake.test.ts +456 -3
  274. package/src/runtime/agent-wake.ts +238 -100
  275. package/src/runtime/assistant-event-hub.ts +36 -6
  276. package/src/runtime/assistant-event.ts +0 -1
  277. package/src/runtime/auth/__tests__/route-policy.test.ts +64 -0
  278. package/src/runtime/auth/route-policy.ts +14 -1
  279. package/src/runtime/auth/same-actor.ts +216 -0
  280. package/src/runtime/channel-retry-sweep.ts +65 -1
  281. package/src/runtime/guardian-reply-router.ts +10 -0
  282. package/src/runtime/local-actor-identity.ts +52 -11
  283. package/src/runtime/pending-interactions.ts +8 -0
  284. package/src/runtime/routes/__tests__/client-routes.test.ts +155 -0
  285. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +0 -5
  286. package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +1 -1
  287. package/src/runtime/routes/client-routes.ts +20 -2
  288. package/src/runtime/routes/contact-routes.ts +0 -25
  289. package/src/runtime/routes/conversation-routes.ts +35 -26
  290. package/src/runtime/routes/debug-bash-routes.ts +163 -0
  291. package/src/runtime/routes/disk-pressure-routes.ts +121 -0
  292. package/src/runtime/routes/document-pdf-renderer.ts +6 -2
  293. package/src/runtime/routes/documents-routes.ts +2 -75
  294. package/src/runtime/routes/events-routes.ts +41 -9
  295. package/src/runtime/routes/host-bash-routes.ts +23 -3
  296. package/src/runtime/routes/host-cu-routes.ts +33 -6
  297. package/src/runtime/routes/host-file-routes.ts +32 -6
  298. package/src/runtime/routes/host-transfer-routes.ts +79 -16
  299. package/src/runtime/routes/identity-routes.ts +7 -138
  300. package/src/runtime/routes/inbound-message-handler.ts +77 -12
  301. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +3 -0
  302. package/src/runtime/routes/index.ts +6 -0
  303. package/src/runtime/routes/memory-item-routes.test.ts +41 -15
  304. package/src/runtime/routes/memory-v2-routes.ts +33 -0
  305. package/src/runtime/routes/oauth-connect-routes.ts +153 -0
  306. package/src/runtime/verification-outbound-actions.ts +4 -4
  307. package/src/schedule/run-script.ts +37 -5
  308. package/src/schedule/scheduler.ts +20 -1
  309. package/src/security/encrypted-store.ts +2 -0
  310. package/src/security/secure-keys.ts +55 -0
  311. package/src/skills/remote-skill-policy.ts +4 -10
  312. package/src/subagent/index.ts +1 -7
  313. package/src/subagent/manager.ts +1 -15
  314. package/src/tasks/task-runner.ts +0 -1
  315. package/src/tasks/task-store.ts +0 -3
  316. package/src/tools/background-tool-registry.ts +17 -3
  317. package/src/tools/host-filesystem/edit.test.ts +151 -0
  318. package/src/tools/host-filesystem/edit.ts +43 -1
  319. package/src/tools/host-filesystem/read.test.ts +129 -0
  320. package/src/tools/host-filesystem/read.ts +43 -1
  321. package/src/tools/host-filesystem/transfer.test.ts +127 -2
  322. package/src/tools/host-filesystem/transfer.ts +56 -11
  323. package/src/tools/host-filesystem/write.test.ts +134 -0
  324. package/src/tools/host-filesystem/write.ts +43 -1
  325. package/src/tools/host-terminal/host-shell.ts +13 -6
  326. package/src/tools/mcp/mcp-tool-factory.ts +2 -1
  327. package/src/tools/memory/register.test.ts +12 -9
  328. package/src/tools/memory/register.ts +1 -2
  329. package/src/tools/provider-tool-name.ts +28 -0
  330. package/src/tools/registry.ts +30 -9
  331. package/src/tools/terminal/shell.ts +9 -1
  332. package/src/tools/tool-approval-handler.ts +31 -6
  333. package/src/tools/types.ts +24 -2
  334. package/src/tts/provider-catalog.ts +3 -5
  335. package/src/util/disk-usage.ts +138 -0
  336. package/src/util/platform.ts +21 -11
  337. package/src/util/process-liveness.ts +26 -0
  338. package/src/workspace/heartbeat-service.ts +19 -0
  339. package/src/workspace/migrations/065-bump-stale-heartbeat-interval.ts +60 -0
  340. package/src/workspace/migrations/066-seed-heartbeat-callsite-cost-default.ts +146 -0
  341. package/src/workspace/migrations/067-release-notes-safe-storage-limits.ts +72 -0
  342. package/src/workspace/migrations/068-release-notes-local-timezone.ts +65 -0
  343. package/src/workspace/migrations/registry.ts +8 -0
  344. package/src/__tests__/conversation-tool-setup-memory-scope.test.ts +0 -167
  345. package/src/memory/v2/__tests__/skill-qdrant.test.ts +0 -657
  346. package/src/memory/v2/skill-qdrant.ts +0 -404
  347. package/src/signals/bash.ts +0 -198
@@ -351,7 +351,7 @@ async function dedupCrossCategory(
351
351
  // Context load — conversation start
352
352
  // ---------------------------------------------------------------------------
353
353
 
354
- export interface ContextLoadOpts {
354
+ interface ContextLoadOpts {
355
355
  /** Scope for memory isolation. */
356
356
  scopeId: string;
357
357
  /** Recent conversation summaries (used as retrieval queries). */
@@ -373,7 +373,7 @@ export interface ContextLoadOpts {
373
373
  userQuery?: string;
374
374
  }
375
375
 
376
- export interface ContextLoadResult {
376
+ interface ContextLoadResult {
377
377
  nodes: ScoredNode[];
378
378
  serendipityNodes: ScoredNode[];
379
379
  triggeredNodes: TriggeredResult[];
@@ -525,7 +525,6 @@ export async function loadContextMemory(
525
525
  const results = await searchGraphNodes(
526
526
  queryVector,
527
527
  maxNodes * 3,
528
- [opts.scopeId],
529
528
  sparseVector,
530
529
  );
531
530
  for (const r of results) {
@@ -548,7 +547,6 @@ export async function loadContextMemory(
548
547
  const results = await searchGraphNodes(
549
548
  userQueryVector,
550
549
  maxNodes * 3,
551
- [opts.scopeId],
552
550
  undefined,
553
551
  );
554
552
  for (const r of results) {
@@ -873,7 +871,7 @@ export async function loadContextMemory(
873
871
  // Per-turn retrieval — mid-conversation injection
874
872
  // ---------------------------------------------------------------------------
875
873
 
876
- export interface TurnRetrievalOpts {
874
+ interface TurnRetrievalOpts {
877
875
  /** The assistant's last message content. */
878
876
  assistantLastMessage: string;
879
877
  /** The user's last message content. */
@@ -886,7 +884,7 @@ export interface TurnRetrievalOpts {
886
884
  signal?: AbortSignal;
887
885
  }
888
886
 
889
- export interface TurnRetrievalResult {
887
+ interface TurnRetrievalResult {
890
888
  /** New nodes to inject (not already in context). */
891
889
  nodes: ScoredNode[];
892
890
  /** Serendipity picks included in nodes. */
@@ -1007,9 +1005,7 @@ export async function retrieveForTurn(
1007
1005
  }
1008
1006
  const imgVector = imgResult.vectors[0];
1009
1007
  if (imgVector) {
1010
- const imgResults = await searchGraphNodes(imgVector, 40, [
1011
- opts.scopeId,
1012
- ]);
1008
+ const imgResults = await searchGraphNodes(imgVector, 40);
1013
1009
  for (const r of imgResults) {
1014
1010
  const current = allCandidateIds.get(r.nodeId) ?? 0;
1015
1011
  allCandidateIds.set(r.nodeId, Math.max(current, r.score));
@@ -1097,7 +1093,7 @@ export async function retrieveForTurn(
1097
1093
  queryEmbeddings = embedResults.vectors;
1098
1094
 
1099
1095
  const searchPromises = queryEmbeddings.map((vec) =>
1100
- searchGraphNodes(vec, 40, [opts.scopeId]),
1096
+ searchGraphNodes(vec, 40),
1101
1097
  );
1102
1098
  const searchResults = await Promise.all(searchPromises);
1103
1099
 
@@ -9,6 +9,7 @@ import { getLogger } from "../util/logger.js";
9
9
  import { enqueueAutoAnalysisIfEnabled } from "./auto-analysis-enqueue.js";
10
10
  import { isAutoAnalysisConversation } from "./auto-analysis-guard.js";
11
11
  import { getMemoryCheckpoint, setMemoryCheckpoint } from "./checkpoints.js";
12
+ import { isMemoryV2ReadActive } from "./context-search/sources/memory-v2.js";
12
13
  import { getDb } from "./db-connection.js";
13
14
  import { selectedBackendSupportsMultimodal } from "./embedding-backend.js";
14
15
  import { enqueueMemoryJob, upsertDebouncedJob } from "./jobs-store.js";
@@ -175,39 +176,9 @@ export async function indexMessageNow(
175
176
  // Summaries still run — they feed the graph retrieval pipeline and
176
177
  // are not recursion-prone.
177
178
  if (!isAutoAnalysisSource) {
178
- // ── Graph extraction ────────────────────────────────────────────
179
- const graphPendingKey = `graph_extract:${input.conversationId}:pending_count`;
180
- const graphCurrentVal = getMemoryCheckpoint(graphPendingKey);
181
- const graphPendingCount =
182
- (graphCurrentVal ? parseInt(graphCurrentVal, 10) : 0) + 1;
183
- setMemoryCheckpoint(graphPendingKey, String(graphPendingCount));
184
-
185
- const graphBatchFired = graphPendingCount >= batchSize;
186
- if (graphBatchFired) {
187
- setMemoryCheckpoint(graphPendingKey, "0");
188
- }
189
-
190
- // Single pending `graph_extract` row per conversation. If the
191
- // batch threshold just fired, pull `runAfter` back to now so the
192
- // job runs immediately; otherwise debounce by the idle timeout.
193
- // Routing both paths through `upsertDebouncedJob` ensures the
194
- // row's `runAfter` reflects whichever trigger ran last, so a
195
- // batch crossing always takes effect immediately.
196
- const extractRunAfter = graphBatchFired
197
- ? Date.now()
198
- : Date.now() + idleTimeoutMs;
199
- upsertDebouncedJob(
200
- "graph_extract",
201
- {
202
- conversationId: input.conversationId,
203
- scopeId: input.scopeId ?? "default",
204
- },
205
- extractRunAfter,
206
- );
207
-
208
- // Reading config here is best-effort: feature-gated triggers below
209
- // (memory v2 sweep, auto-analyze batch) skip when it fails — the
210
- // idle-debounced enqueues above are unaffected.
179
+ // Reading config here is best-effort: when it fails we treat v2 as
180
+ // inactive (failing-open to v1) so a config error never silently
181
+ // drops both extraction paths.
211
182
  let triggerConfig: ReturnType<typeof getConfig> | null = null;
212
183
  try {
213
184
  triggerConfig = getConfig();
@@ -218,20 +189,58 @@ export async function indexMessageNow(
218
189
  );
219
190
  }
220
191
 
221
- // Memory v2 sweep mirrors graph_extract's debounce: when the v2
222
- // flag + config are on AND `sweep_enabled` is set, every extraction
223
- // trigger also enqueues a sweep. The sweep itself reads recent
224
- // messages globally, so the `conversationId` here is just the dedup
225
- // key — one pending row per active conversation. All three gates
226
- // (feature flag, v2 master toggle, sweep_enabled) must be true.
192
+ const v2Config =
193
+ triggerConfig != null && isMemoryV2ReadActive(triggerConfig)
194
+ ? triggerConfig
195
+ : null;
196
+
197
+ // ── Graph extraction (v1) ───────────────────────────────────────
198
+ // Suppressed when v2 is active — v2 reads memory from buffer.md
199
+ // and concept pages, so the v1 graph would be stale data nobody
200
+ // consumes. Pending-count tracking is suppressed too; otherwise a
201
+ // flag flip back to v1 would fire an immediate batch from counts
202
+ // accumulated during the v2 window.
203
+ let extractRunAfter: number;
204
+ if (v2Config == null) {
205
+ const graphPendingKey = `graph_extract:${input.conversationId}:pending_count`;
206
+ const graphCurrentVal = getMemoryCheckpoint(graphPendingKey);
207
+ const graphPendingCount =
208
+ (graphCurrentVal ? parseInt(graphCurrentVal, 10) : 0) + 1;
209
+ setMemoryCheckpoint(graphPendingKey, String(graphPendingCount));
210
+
211
+ const graphBatchFired = graphPendingCount >= batchSize;
212
+ if (graphBatchFired) {
213
+ setMemoryCheckpoint(graphPendingKey, "0");
214
+ }
215
+
216
+ // Single pending `graph_extract` row per conversation. If the
217
+ // batch threshold just fired, pull `runAfter` back to now so the
218
+ // job runs immediately; otherwise debounce by the idle timeout.
219
+ // Routing both paths through `upsertDebouncedJob` ensures the
220
+ // row's `runAfter` reflects whichever trigger ran last, so a
221
+ // batch crossing always takes effect immediately.
222
+ extractRunAfter = graphBatchFired
223
+ ? Date.now()
224
+ : Date.now() + idleTimeoutMs;
225
+ upsertDebouncedJob(
226
+ "graph_extract",
227
+ {
228
+ conversationId: input.conversationId,
229
+ scopeId: input.scopeId ?? "default",
230
+ },
231
+ extractRunAfter,
232
+ );
233
+ } else {
234
+ extractRunAfter = Date.now() + idleTimeoutMs;
235
+ }
236
+
237
+ // Memory v2 sweep: when v2 is on AND `sweep_enabled` is set, every
238
+ // extraction trigger also enqueues a sweep. The sweep itself reads
239
+ // recent messages globally, so the `conversationId` here is just
240
+ // the dedup key — one pending row per active conversation.
227
241
  // `sweep_enabled` defaults to false because `remember()` is the
228
242
  // primary capture path; the sweep is opt-in.
229
- if (
230
- triggerConfig != null &&
231
- isAssistantFeatureFlagEnabled("memory-v2-enabled", triggerConfig) &&
232
- triggerConfig.memory.v2.enabled &&
233
- triggerConfig.memory.v2.sweep_enabled
234
- ) {
243
+ if (v2Config != null && v2Config.memory.v2.sweep_enabled) {
235
244
  upsertDebouncedJob(
236
245
  "memory_v2_sweep",
237
246
  { conversationId: input.conversationId },
@@ -7,10 +7,7 @@ import {
7
7
  resetMessageCursorCheckpoint,
8
8
  writeMessageCursorCheckpoint,
9
9
  } from "../checkpoints.js";
10
- import {
11
- getConversationMemoryScopeId,
12
- messageMetadataSchema,
13
- } from "../conversation-crud.js";
10
+ import { messageMetadataSchema } from "../conversation-crud.js";
14
11
  import { getDb } from "../db-connection.js";
15
12
  import { indexMessageNow } from "../indexer.js";
16
13
  import { enqueueMemoryJob, type MemoryJob } from "../jobs-store.js";
@@ -72,13 +69,7 @@ export async function backfillJob(
72
69
  .all();
73
70
 
74
71
  if (batch.length > 0) {
75
- const scopeCache = new Map<string, string>();
76
72
  for (const message of batch) {
77
- let scopeId = scopeCache.get(message.conversationId);
78
- if (scopeId === undefined) {
79
- scopeId = getConversationMemoryScopeId(message.conversationId);
80
- scopeCache.set(message.conversationId, scopeId);
81
- }
82
73
  const { provenanceTrustClass, automated } = parseMessageMetadata(
83
74
  message.metadata ?? null,
84
75
  );
@@ -89,7 +80,7 @@ export async function backfillJob(
89
80
  role: message.role,
90
81
  content: message.content,
91
82
  createdAt: message.createdAt,
92
- scopeId,
83
+ scopeId: "default",
93
84
  provenanceTrustClass,
94
85
  automated,
95
86
  },
@@ -54,6 +54,49 @@ export function pruneOldLlmRequestLogsJob(
54
54
  );
55
55
  }
56
56
 
57
+ /**
58
+ * Delete trace events older than the configured retention period.
59
+ * Processes in batches to avoid long DB locks and excessive WAL growth.
60
+ * Re-enqueues itself if more rows remain.
61
+ */
62
+ export function pruneOldTraceEventsJob(
63
+ job: MemoryJob,
64
+ config: AssistantConfig,
65
+ ): void {
66
+ const rawRetention = job.payload.retentionDays;
67
+ const retentionDays =
68
+ typeof rawRetention === "number" &&
69
+ Number.isFinite(rawRetention) &&
70
+ rawRetention >= 0
71
+ ? rawRetention
72
+ : config.memory.cleanup.traceEventRetentionDays;
73
+
74
+ // 0 means disabled
75
+ if (retentionDays === 0) return;
76
+
77
+ const cutoffMs = Date.now() - retentionDays * 86_400_000;
78
+
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,
83
+ );
84
+ const deleted = rawChanges();
85
+
86
+ if (deleted >= PRUNE_LOG_BATCH_LIMIT) {
87
+ enqueueMemoryJob("prune_old_trace_events", { retentionDays });
88
+ }
89
+
90
+ log.info(
91
+ {
92
+ deleted,
93
+ retentionDays,
94
+ cutoffMs,
95
+ },
96
+ "Pruned old trace events",
97
+ );
98
+ }
99
+
57
100
  /**
58
101
  * Delete conversations that have had no activity (updatedAt) for longer than
59
102
  * the configured retention period. Processes in batches so a single job doesn't
@@ -3,7 +3,6 @@ import { readFile } from "node:fs/promises";
3
3
  import { eq } from "drizzle-orm";
4
4
 
5
5
  import type { AssistantConfig } from "../../config/types.js";
6
- import { getConversationMemoryScopeId } from "../conversation-crud.js";
7
6
  import { getDb } from "../db-connection.js";
8
7
  import type { EmbeddingInput } from "../embedding-types.js";
9
8
  import { asString, embedAndUpsert } from "../job-utils.js";
@@ -18,7 +17,7 @@ import {
18
17
 
19
18
  export async function embedSegmentJob(
20
19
  job: MemoryJob,
21
- config: AssistantConfig
20
+ config: AssistantConfig,
22
21
  ): Promise<void> {
23
22
  const segmentId = asString(job.payload.segmentId);
24
23
  if (!segmentId) return;
@@ -39,7 +38,7 @@ export async function embedSegmentJob(
39
38
 
40
39
  export async function embedSummaryJob(
41
40
  job: MemoryJob,
42
- config: AssistantConfig
41
+ config: AssistantConfig,
43
42
  ): Promise<void> {
44
43
  const summaryId = asString(job.payload.summaryId);
45
44
  if (!summaryId) return;
@@ -60,13 +59,13 @@ export async function embedSummaryJob(
60
59
  created_at: summary.startAt,
61
60
  last_seen_at: summary.endAt,
62
61
  memory_scope_id: summary.scopeId,
63
- }
62
+ },
64
63
  );
65
64
  }
66
65
 
67
66
  export async function embedMediaJob(
68
67
  job: MemoryJob,
69
- config: AssistantConfig
68
+ config: AssistantConfig,
70
69
  ): Promise<void> {
71
70
  const assetId = asString(job.payload.assetId);
72
71
  if (!assetId) return;
@@ -99,7 +98,7 @@ export async function embedMediaJob(
99
98
 
100
99
  export async function embedAttachmentJob(
101
100
  job: MemoryJob,
102
- config: AssistantConfig
101
+ config: AssistantConfig,
103
102
  ): Promise<void> {
104
103
  const messageId = asString(job.payload.messageId);
105
104
  const blockIndex = job.payload.blockIndex as number;
@@ -125,11 +124,10 @@ export async function embedAttachmentJob(
125
124
 
126
125
  // Use messageId + blockIndex as targetId for uniqueness
127
126
  const targetId = `${messageId}:${blockIndex}`;
128
- const memoryScopeId = getConversationMemoryScopeId(message.conversationId);
129
127
  await embedAndUpsert(config, "media", targetId, input, {
130
128
  created_at: message.createdAt,
131
129
  message_id: messageId,
132
130
  conversation_id: message.conversationId,
133
- memory_scope_id: memoryScopeId,
131
+ memory_scope_id: "default",
134
132
  });
135
133
  }
@@ -10,7 +10,6 @@ import {
10
10
  userMessage,
11
11
  } from "../../providers/provider-send-message.js";
12
12
  import { getLogger } from "../../util/logger.js";
13
- import { getConversationMemoryScopeId } from "../conversation-crud.js";
14
13
  import { getDb } from "../db-connection.js";
15
14
  import { asString, truncate } from "../job-utils.js";
16
15
  import { enqueueMemoryJob, type MemoryJob } from "../jobs-store.js";
@@ -91,10 +90,6 @@ export async function buildConversationSummaryJob(
91
90
  "conversation",
92
91
  );
93
92
 
94
- // Inherit the conversation's memory scope so summaries stay aligned with
95
- // the retrieval scope used by the source conversation.
96
- const scopeId = getConversationMemoryScopeId(conversationId);
97
-
98
93
  const now = Date.now();
99
94
  const summaryId = existing?.id ?? uuid();
100
95
  const nextVersion = (existing?.version ?? 0) + 1;
@@ -108,7 +103,7 @@ export async function buildConversationSummaryJob(
108
103
  id: summaryId,
109
104
  scope: "conversation",
110
105
  scopeKey: conversationId,
111
- scopeId,
106
+ scopeId: "default",
112
107
  summary: summaryText,
113
108
  tokenEstimate: estimateTextTokens(summaryText),
114
109
  version: nextVersion,
@@ -123,7 +118,7 @@ export async function buildConversationSummaryJob(
123
118
  summary: summaryText,
124
119
  tokenEstimate: estimateTextTokens(summaryText),
125
120
  version: sql`${memorySummaries.version} + 1`,
126
- scopeId,
121
+ scopeId: "default",
127
122
  startAt: earliestCovered,
128
123
  endAt: latestCovered,
129
124
  updatedAt: now,
@@ -18,6 +18,7 @@ export type MemoryJobType =
18
18
  | "embed_summary"
19
19
  | "prune_old_conversations"
20
20
  | "prune_old_llm_request_logs"
21
+ | "prune_old_trace_events"
21
22
  | "build_conversation_summary"
22
23
  | "conversation_analyze"
23
24
  | "backfill"
@@ -366,6 +367,53 @@ export function enqueuePruneOldConversationsJob(
366
367
  return enqueueMemoryJob("prune_old_conversations", payload);
367
368
  }
368
369
 
370
+ export function enqueuePruneOldTraceEventsJob(retentionDays?: number): string {
371
+ const db = getDb();
372
+ const existing = db
373
+ .select()
374
+ .from(memoryJobs)
375
+ .where(
376
+ and(
377
+ eq(memoryJobs.type, "prune_old_trace_events"),
378
+ inArray(memoryJobs.status, ["pending", "running"]),
379
+ ),
380
+ )
381
+ .orderBy(asc(memoryJobs.createdAt))
382
+ .get();
383
+ if (existing) {
384
+ if (
385
+ existing.status === "pending" &&
386
+ typeof retentionDays === "number" &&
387
+ Number.isFinite(retentionDays) &&
388
+ retentionDays >= 0
389
+ ) {
390
+ let payload: Record<string, unknown> = {};
391
+ try {
392
+ payload = JSON.parse(existing.payload) as Record<string, unknown>;
393
+ } catch {
394
+ payload = {};
395
+ }
396
+ if (payload.retentionDays !== retentionDays) {
397
+ db.update(memoryJobs)
398
+ .set({
399
+ payload: JSON.stringify({ ...payload, retentionDays }),
400
+ updatedAt: Date.now(),
401
+ })
402
+ .where(eq(memoryJobs.id, existing.id))
403
+ .run();
404
+ }
405
+ }
406
+ return existing.id;
407
+ }
408
+ const payload =
409
+ typeof retentionDays === "number" &&
410
+ Number.isFinite(retentionDays) &&
411
+ retentionDays >= 0
412
+ ? { retentionDays }
413
+ : {};
414
+ return enqueueMemoryJob("prune_old_trace_events", payload);
415
+ }
416
+
369
417
  export interface LaneBudgets {
370
418
  slowLlm: number;
371
419
  fast: number;
@@ -1,12 +1,17 @@
1
- import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
2
1
  import { getConfig } from "../config/loader.js";
3
2
  import type { AssistantConfig } from "../config/types.js";
3
+ import {
4
+ checkDiskPressureBackgroundGate,
5
+ diskPressureBackgroundSkipLogFields,
6
+ shouldLogDiskPressureBackgroundSkip,
7
+ } from "../daemon/disk-pressure-background-gate.js";
4
8
  import { getLogger } from "../util/logger.js";
5
9
  import { getMemoryCheckpoint, setMemoryCheckpoint } from "./checkpoints.js";
6
10
  import {
7
11
  getLastScheduledCleanupEnqueueMs,
8
12
  markScheduledCleanupEnqueued,
9
13
  } from "./cleanup-schedule-state.js";
14
+ import { isMemoryV2ReadActive } from "./context-search/sources/memory-v2.js";
10
15
  import { conversationAnalyzeJob } from "./conversation-analyze-job.js";
11
16
  import { maybeRunDbMaintenance } from "./db-maintenance.js";
12
17
  import { bootstrapFromHistory } from "./graph/bootstrap.js";
@@ -23,6 +28,7 @@ import { backfillJob } from "./job-handlers/backfill.js";
23
28
  import {
24
29
  pruneOldConversationsJob,
25
30
  pruneOldLlmRequestLogsJob,
31
+ pruneOldTraceEventsJob,
26
32
  } from "./job-handlers/cleanup.js";
27
33
  import { generateConversationStartersJob } from "./job-handlers/conversation-starters.js";
28
34
  // ── Per-job-type handlers ──────────────────────────────────────────
@@ -54,6 +60,7 @@ import {
54
60
  enqueueMemoryJob,
55
61
  enqueuePruneOldConversationsJob,
56
62
  enqueuePruneOldLlmRequestLogsJob,
63
+ enqueuePruneOldTraceEventsJob,
57
64
  failMemoryJob,
58
65
  failStalledJobs,
59
66
  type MemoryJob,
@@ -167,6 +174,20 @@ export async function runMemoryJobsOnce(
167
174
  if (!config.memory.enabled) return 0;
168
175
  const enableScheduledCleanup = options.enableScheduledCleanup === true;
169
176
 
177
+ const diskPressureGate = checkDiskPressureBackgroundGate("background-work");
178
+ if (diskPressureGate.action === "skip") {
179
+ if (shouldLogDiskPressureBackgroundSkip("memory-jobs-worker")) {
180
+ log.warn(
181
+ {
182
+ source: "memory",
183
+ ...diskPressureBackgroundSkipLogFields(diskPressureGate),
184
+ },
185
+ "Memory jobs worker skipped during disk pressure cleanup mode",
186
+ );
187
+ }
188
+ return 0;
189
+ }
190
+
170
191
  // Fail jobs that have been running longer than the configured timeout
171
192
  const timedOut = failStalledJobs(config.memory.jobs.stalledJobTimeoutMs);
172
193
  if (timedOut > 0) {
@@ -455,6 +476,9 @@ async function processJob(
455
476
  case "prune_old_llm_request_logs":
456
477
  pruneOldLlmRequestLogsJob(job, config);
457
478
  return;
479
+ case "prune_old_trace_events":
480
+ pruneOldTraceEventsJob(job, config);
481
+ return;
458
482
  case "build_conversation_summary":
459
483
  await buildConversationSummaryJob(job, config);
460
484
  return;
@@ -560,14 +584,20 @@ function maybeEnqueueScheduledCleanupJobs(
560
584
  cleanup.llmRequestLogRetentionMs !== null
561
585
  ? enqueuePruneOldLlmRequestLogsJob(cleanup.llmRequestLogRetentionMs)
562
586
  : null;
587
+ const pruneTraceEventsJobId =
588
+ cleanup.traceEventRetentionDays > 0
589
+ ? enqueuePruneOldTraceEventsJob(cleanup.traceEventRetentionDays)
590
+ : null;
563
591
  markScheduledCleanupEnqueued(nowMs);
564
592
  log.debug(
565
593
  {
566
594
  pruneConversationsJobId,
567
595
  pruneLlmRequestLogsJobId,
596
+ pruneTraceEventsJobId,
568
597
  enqueueIntervalMs: cleanup.enqueueIntervalMs,
569
598
  conversationRetentionDays: cleanup.conversationRetentionDays,
570
599
  llmRequestLogRetentionMs: cleanup.llmRequestLogRetentionMs,
600
+ traceEventRetentionDays: cleanup.traceEventRetentionDays,
571
601
  },
572
602
  "Enqueued scheduled memory cleanup jobs",
573
603
  );
@@ -590,58 +620,66 @@ export const GRAPH_MAINTENANCE_CHECKPOINTS = {
590
620
  } as const;
591
621
 
592
622
  /**
593
- * Enqueue periodic graph maintenance jobs (decay, consolidation, pattern scan, narrative).
594
- * Uses durable checkpoints so intervals survive daemon restarts — jobs only fire
595
- * when the actual elapsed time since last run exceeds the interval.
623
+ * Enqueue periodic graph maintenance jobs.
624
+ *
625
+ * Mutually exclusive between v1 and v2:
626
+ * - v2 active (both `memory-v2-enabled` flag and `memory.v2.enabled`
627
+ * config on) → only `memory_v2_consolidate` is scheduled.
628
+ * - v2 inactive → the four v1 entries (decay, consolidate, pattern_scan,
629
+ * narrative) are scheduled instead.
596
630
  *
597
- * The v2 consolidation entry is gated on both the `memory-v2-enabled` feature
598
- * flag and the `memory.v2.enabled` config both must be true for the cron
599
- * to fire. Sweep is intentionally not on this schedule: it is debounced from
600
- * the live `graph_extract` trigger path (see `indexMessageNow` in
601
- * `indexer.ts`) so it runs on the same idle/message-count cadence.
631
+ * Read/write paths route to v2 when the flag is on, so v1 graph data goes
632
+ * unread; running v1 maintenance alongside v2 is wasted compute and LLM
633
+ * spend. The v1 code path remains live so flipping the flag back to off
634
+ * fully re-engages v1.
635
+ *
636
+ * Uses durable checkpoints so intervals survive daemon restarts — jobs only
637
+ * fire when the actual elapsed time since last run exceeds the interval.
638
+ * Sweep is intentionally not on this schedule: it is debounced from the
639
+ * live `graph_extract` trigger path (see `indexMessageNow` in `indexer.ts`)
640
+ * so it runs on the same idle/message-count cadence.
602
641
  */
603
642
  export function maybeEnqueueGraphMaintenanceJobs(
604
643
  config: AssistantConfig,
605
644
  nowMs = Date.now(),
606
645
  ): void {
646
+ const v2Active = isMemoryV2ReadActive(config);
647
+
607
648
  const schedule: Array<{
608
649
  key: string;
609
650
  intervalMs: number;
610
651
  jobType: MemoryJobType;
611
- }> = [
612
- {
613
- key: GRAPH_MAINTENANCE_CHECKPOINTS.decay,
614
- intervalMs: GRAPH_DECAY_INTERVAL_MS,
615
- jobType: "graph_decay",
616
- },
617
- {
618
- key: GRAPH_MAINTENANCE_CHECKPOINTS.consolidate,
619
- intervalMs: GRAPH_CONSOLIDATE_INTERVAL_MS,
620
- jobType: "graph_consolidate",
621
- },
622
- {
623
- key: GRAPH_MAINTENANCE_CHECKPOINTS.patternScan,
624
- intervalMs: GRAPH_PATTERN_SCAN_INTERVAL_MS,
625
- jobType: "graph_pattern_scan",
626
- },
627
- {
628
- key: GRAPH_MAINTENANCE_CHECKPOINTS.narrative,
629
- intervalMs: GRAPH_NARRATIVE_INTERVAL_MS,
630
- jobType: "graph_narrative_refine",
631
- },
632
- ];
633
-
634
- if (
635
- isAssistantFeatureFlagEnabled("memory-v2-enabled", config) &&
636
- config.memory.v2.enabled
637
- ) {
638
- schedule.push({
639
- key: GRAPH_MAINTENANCE_CHECKPOINTS.memoryV2Consolidate,
640
- intervalMs:
641
- config.memory.v2.consolidation_interval_hours * 60 * 60 * 1000,
642
- jobType: "memory_v2_consolidate",
643
- });
644
- }
652
+ }> = v2Active
653
+ ? [
654
+ {
655
+ key: GRAPH_MAINTENANCE_CHECKPOINTS.memoryV2Consolidate,
656
+ intervalMs:
657
+ config.memory.v2.consolidation_interval_hours * 60 * 60 * 1000,
658
+ jobType: "memory_v2_consolidate",
659
+ },
660
+ ]
661
+ : [
662
+ {
663
+ key: GRAPH_MAINTENANCE_CHECKPOINTS.decay,
664
+ intervalMs: GRAPH_DECAY_INTERVAL_MS,
665
+ jobType: "graph_decay",
666
+ },
667
+ {
668
+ key: GRAPH_MAINTENANCE_CHECKPOINTS.consolidate,
669
+ intervalMs: GRAPH_CONSOLIDATE_INTERVAL_MS,
670
+ jobType: "graph_consolidate",
671
+ },
672
+ {
673
+ key: GRAPH_MAINTENANCE_CHECKPOINTS.patternScan,
674
+ intervalMs: GRAPH_PATTERN_SCAN_INTERVAL_MS,
675
+ jobType: "graph_pattern_scan",
676
+ },
677
+ {
678
+ key: GRAPH_MAINTENANCE_CHECKPOINTS.narrative,
679
+ intervalMs: GRAPH_NARRATIVE_INTERVAL_MS,
680
+ jobType: "graph_narrative_refine",
681
+ },
682
+ ];
645
683
 
646
684
  for (const { key, intervalMs, jobType } of schedule) {
647
685
  const lastRun = parseInt(getMemoryCheckpoint(key) ?? "0", 10);