@vellumai/assistant 0.4.49 → 0.4.51

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 (353) hide show
  1. package/ARCHITECTURE.md +24 -33
  2. package/README.md +3 -3
  3. package/docs/architecture/integrations.md +2 -2
  4. package/docs/architecture/keychain-broker.md +6 -6
  5. package/docs/architecture/memory.md +180 -119
  6. package/knip.json +32 -0
  7. package/package.json +3 -2
  8. package/src/__tests__/agent-loop.test.ts +3 -1
  9. package/src/__tests__/anthropic-provider.test.ts +114 -23
  10. package/src/__tests__/approval-cascade.test.ts +1 -15
  11. package/src/__tests__/approval-routes-http.test.ts +2 -0
  12. package/src/__tests__/assistant-feature-flag-guard.test.ts +0 -23
  13. package/src/__tests__/btw-routes.test.ts +61 -5
  14. package/src/__tests__/canonical-guardian-store.test.ts +95 -0
  15. package/src/__tests__/checker.test.ts +13 -0
  16. package/src/__tests__/config-schema.test.ts +1 -68
  17. package/src/__tests__/config-watcher.test.ts +8 -0
  18. package/src/__tests__/context-memory-e2e.test.ts +11 -100
  19. package/src/__tests__/conversation-routes-guardian-reply.test.ts +8 -0
  20. package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
  21. package/src/__tests__/credential-security-e2e.test.ts +1 -0
  22. package/src/__tests__/credential-security-invariants.test.ts +8 -7
  23. package/src/__tests__/credential-vault-unit.test.ts +23 -18
  24. package/src/__tests__/credential-vault.test.ts +30 -18
  25. package/src/__tests__/credentials-cli.test.ts +257 -82
  26. package/src/__tests__/cu-unified-flow.test.ts +532 -0
  27. package/src/__tests__/date-context.test.ts +93 -77
  28. package/src/__tests__/deterministic-verification-control-plane.test.ts +64 -0
  29. package/src/__tests__/guardian-routing-invariants.test.ts +93 -0
  30. package/src/__tests__/history-repair.test.ts +245 -0
  31. package/src/__tests__/host-cu-proxy.test.ts +165 -3
  32. package/src/__tests__/http-user-message-parity.test.ts +1 -0
  33. package/src/__tests__/inbound-invite-redemption.test.ts +36 -7
  34. package/src/__tests__/integration-status.test.ts +31 -30
  35. package/src/__tests__/invite-redemption-service.test.ts +166 -13
  36. package/src/__tests__/invite-routes-http.test.ts +166 -5
  37. package/src/__tests__/keychain-broker-client.test.ts +4 -4
  38. package/src/__tests__/list-messages-attachments.test.ts +193 -0
  39. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +56 -18
  40. package/src/__tests__/memory-lifecycle-e2e.test.ts +244 -387
  41. package/src/__tests__/memory-recall-quality.test.ts +244 -407
  42. package/src/__tests__/memory-regressions.experimental.test.ts +126 -101
  43. package/src/__tests__/memory-regressions.test.ts +477 -2841
  44. package/src/__tests__/memory-retrieval.benchmark.test.ts +33 -150
  45. package/src/__tests__/memory-upsert-concurrency.test.ts +5 -244
  46. package/src/__tests__/mime-builder.test.ts +28 -0
  47. package/src/__tests__/native-web-search.test.ts +1 -0
  48. package/src/__tests__/oauth-cli.test.ts +824 -31
  49. package/src/__tests__/oauth-provider-profiles.test.ts +1 -1
  50. package/src/__tests__/oauth-store.test.ts +363 -17
  51. package/src/__tests__/qdrant-collection-migration.test.ts +53 -8
  52. package/src/__tests__/registry.test.ts +0 -1
  53. package/src/__tests__/relay-server.test.ts +55 -1
  54. package/src/__tests__/schedule-tools.test.ts +32 -0
  55. package/src/__tests__/script-proxy-certs.test.ts +1 -1
  56. package/src/__tests__/secret-onetime-send.test.ts +1 -0
  57. package/src/__tests__/secret-routes-managed-proxy.test.ts +183 -0
  58. package/src/__tests__/secure-keys.test.ts +78 -18
  59. package/src/__tests__/send-endpoint-busy.test.ts +3 -0
  60. package/src/__tests__/server-history-render.test.ts +2 -2
  61. package/src/__tests__/session-abort-tool-results.test.ts +1 -14
  62. package/src/__tests__/session-agent-loop-overflow.test.ts +1583 -0
  63. package/src/__tests__/session-agent-loop.test.ts +19 -15
  64. package/src/__tests__/session-confirmation-signals.test.ts +1 -15
  65. package/src/__tests__/session-error.test.ts +124 -2
  66. package/src/__tests__/session-history-web-search.test.ts +918 -0
  67. package/src/__tests__/session-pre-run-repair.test.ts +1 -14
  68. package/src/__tests__/session-provider-retry-repair.test.ts +25 -28
  69. package/src/__tests__/session-queue.test.ts +37 -27
  70. package/src/__tests__/session-runtime-assembly.test.ts +54 -0
  71. package/src/__tests__/session-slash-known.test.ts +1 -15
  72. package/src/__tests__/session-slash-queue.test.ts +1 -15
  73. package/src/__tests__/session-slash-unknown.test.ts +1 -15
  74. package/src/__tests__/session-workspace-cache-state.test.ts +3 -33
  75. package/src/__tests__/session-workspace-injection.test.ts +3 -37
  76. package/src/__tests__/session-workspace-tool-tracking.test.ts +3 -37
  77. package/src/__tests__/skills-install-extract.test.ts +93 -0
  78. package/src/__tests__/skills.test.ts +2 -2
  79. package/src/__tests__/skillssh-registry.test.ts +451 -0
  80. package/src/__tests__/slack-channel-config.test.ts +10 -8
  81. package/src/__tests__/trust-store.test.ts +15 -0
  82. package/src/__tests__/twilio-config.test.ts +11 -10
  83. package/src/__tests__/twilio-provider.test.ts +9 -4
  84. package/src/__tests__/voice-invite-redemption.test.ts +85 -5
  85. package/src/agent/ax-tree-compaction.test.ts +51 -0
  86. package/src/agent/loop.ts +39 -12
  87. package/src/approvals/AGENTS.md +1 -1
  88. package/src/approvals/guardian-request-resolvers.ts +14 -2
  89. package/src/bundler/compiler-tools.ts +66 -2
  90. package/src/calls/call-domain.ts +134 -3
  91. package/src/calls/call-store.ts +6 -0
  92. package/src/calls/relay-server.ts +44 -6
  93. package/src/calls/relay-setup-router.ts +17 -1
  94. package/src/calls/twilio-config.ts +5 -4
  95. package/src/calls/twilio-provider.ts +14 -9
  96. package/src/calls/twilio-rest.ts +10 -7
  97. package/src/calls/types.ts +3 -1
  98. package/src/cli/commands/config.ts +14 -9
  99. package/src/cli/commands/contacts.ts +3 -0
  100. package/src/cli/commands/credentials.ts +170 -174
  101. package/src/cli/commands/doctor.ts +11 -8
  102. package/src/cli/commands/keys.ts +9 -9
  103. package/src/cli/commands/mcp.ts +46 -59
  104. package/src/cli/commands/memory.ts +16 -165
  105. package/src/cli/commands/oauth/apps.ts +68 -10
  106. package/src/cli/commands/oauth/connections.ts +475 -105
  107. package/src/cli/commands/oauth/index.ts +3 -3
  108. package/src/cli/commands/oauth/providers.ts +18 -4
  109. package/src/cli/commands/sessions.ts +5 -2
  110. package/src/cli/commands/skills.ts +173 -1
  111. package/src/cli/http-client.ts +0 -20
  112. package/src/cli/main-screen.tsx +2 -2
  113. package/src/cli/program.ts +5 -6
  114. package/src/cli.ts +20 -22
  115. package/src/config/__tests__/feature-flag-registry-bundled.test.ts +39 -0
  116. package/src/config/bundled-skills/computer-use/TOOLS.json +1 -1
  117. package/src/config/bundled-skills/computer-use/tools/computer-use-observe.ts +12 -0
  118. package/src/config/bundled-skills/contacts/SKILL.md +35 -11
  119. package/src/config/bundled-skills/contacts/tools/google-contacts.ts +1 -1
  120. package/src/config/bundled-skills/gmail/SKILL.md +1 -1
  121. package/src/config/bundled-skills/gmail/TOOLS.json +52 -0
  122. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +13 -3
  123. package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +9 -2
  124. package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +5 -1
  125. package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +5 -1
  126. package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +5 -1
  127. package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +5 -1
  128. package/src/config/bundled-skills/gmail/tools/gmail-label.ts +9 -2
  129. package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +5 -1
  130. package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +5 -1
  131. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +5 -1
  132. package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +5 -1
  133. package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +5 -1
  134. package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +5 -1
  135. package/src/config/bundled-skills/google-calendar/TOOLS.json +20 -0
  136. package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +2 -1
  137. package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +2 -1
  138. package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +2 -1
  139. package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +2 -1
  140. package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +2 -1
  141. package/src/config/bundled-skills/google-calendar/tools/shared.ts +8 -2
  142. package/src/config/bundled-skills/messaging/SKILL.md +1 -1
  143. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +2 -2
  144. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +2 -2
  145. package/src/config/bundled-skills/messaging/tools/messaging-auth-test.ts +2 -2
  146. package/src/config/bundled-skills/messaging/tools/messaging-list-conversations.ts +2 -2
  147. package/src/config/bundled-skills/messaging/tools/messaging-mark-read.ts +2 -2
  148. package/src/config/bundled-skills/messaging/tools/messaging-read.ts +2 -2
  149. package/src/config/bundled-skills/messaging/tools/messaging-search.ts +2 -2
  150. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +2 -2
  151. package/src/config/bundled-skills/messaging/tools/messaging-sender-digest.ts +2 -2
  152. package/src/config/bundled-skills/messaging/tools/shared.ts +7 -5
  153. package/src/config/bundled-skills/slack/tools/shared.ts +1 -1
  154. package/src/config/bundled-skills/slack/tools/slack-add-reaction.ts +1 -1
  155. package/src/config/bundled-skills/slack/tools/slack-channel-details.ts +1 -1
  156. package/src/config/bundled-skills/slack/tools/slack-delete-message.ts +1 -1
  157. package/src/config/bundled-skills/slack/tools/slack-edit-message.ts +1 -1
  158. package/src/config/bundled-skills/slack/tools/slack-leave-channel.ts +1 -1
  159. package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +1 -1
  160. package/src/config/bundled-tool-registry.ts +2 -5
  161. package/src/config/loader.ts +6 -42
  162. package/src/config/schema.ts +1 -12
  163. package/src/config/schemas/memory-lifecycle.ts +0 -9
  164. package/src/config/schemas/memory-processing.ts +0 -180
  165. package/src/config/schemas/memory-retrieval.ts +32 -104
  166. package/src/config/schemas/memory.ts +0 -10
  167. package/src/config/types.ts +0 -4
  168. package/src/contacts/contact-store.ts +39 -2
  169. package/src/contacts/contacts-write.ts +9 -0
  170. package/src/context/window-manager.ts +4 -1
  171. package/src/daemon/config-watcher.ts +55 -2
  172. package/src/daemon/daemon-control.ts +1 -1
  173. package/src/daemon/date-context.ts +114 -31
  174. package/src/daemon/handlers/config-ingress.ts +2 -2
  175. package/src/daemon/handlers/config-slack-channel.ts +59 -39
  176. package/src/daemon/handlers/config-telegram.ts +23 -14
  177. package/src/daemon/handlers/session-history.ts +1 -358
  178. package/src/daemon/handlers/sessions.ts +18 -13
  179. package/src/daemon/handlers/shared.ts +3 -17
  180. package/src/daemon/handlers/skills.ts +20 -1
  181. package/src/daemon/history-repair.ts +72 -8
  182. package/src/daemon/host-cu-proxy.ts +55 -26
  183. package/src/daemon/lifecycle.ts +39 -4
  184. package/src/daemon/mcp-reload-service.ts +2 -2
  185. package/src/daemon/message-types/computer-use.ts +1 -12
  186. package/src/daemon/message-types/memory.ts +4 -16
  187. package/src/daemon/message-types/messages.ts +1 -0
  188. package/src/daemon/message-types/sessions.ts +4 -42
  189. package/src/daemon/server.ts +6 -1
  190. package/src/daemon/session-agent-loop-handlers.ts +38 -0
  191. package/src/daemon/session-agent-loop.ts +334 -48
  192. package/src/daemon/session-error.ts +89 -6
  193. package/src/daemon/session-history.ts +17 -7
  194. package/src/daemon/session-media-retry.ts +6 -2
  195. package/src/daemon/session-memory.ts +69 -149
  196. package/src/daemon/session-process.ts +10 -1
  197. package/src/daemon/session-runtime-assembly.ts +49 -19
  198. package/src/daemon/session-slash.ts +3 -5
  199. package/src/daemon/session-surfaces.ts +4 -1
  200. package/src/daemon/session-tool-setup.ts +7 -1
  201. package/src/daemon/session.ts +12 -2
  202. package/src/email/providers/index.ts +2 -2
  203. package/src/instrument.ts +61 -1
  204. package/src/media/avatar-router.ts +1 -1
  205. package/src/memory/admin.ts +2 -191
  206. package/src/memory/canonical-guardian-store.ts +38 -2
  207. package/src/memory/conversation-crud.ts +0 -33
  208. package/src/memory/conversation-queries.ts +25 -83
  209. package/src/memory/db-init.ts +32 -0
  210. package/src/memory/embedding-backend.ts +84 -8
  211. package/src/memory/embedding-types.ts +9 -1
  212. package/src/memory/indexer.ts +7 -46
  213. package/src/memory/invite-store.ts +19 -0
  214. package/src/memory/items-extractor.ts +274 -76
  215. package/src/memory/job-handlers/backfill.ts +2 -127
  216. package/src/memory/job-handlers/cleanup.ts +2 -16
  217. package/src/memory/job-handlers/extraction.ts +2 -138
  218. package/src/memory/job-handlers/index-maintenance.ts +1 -6
  219. package/src/memory/job-handlers/summarization.ts +3 -148
  220. package/src/memory/job-utils.ts +21 -59
  221. package/src/memory/jobs-store.ts +1 -159
  222. package/src/memory/jobs-worker.ts +9 -52
  223. package/src/memory/migrations/104-core-indexes.ts +3 -3
  224. package/src/memory/migrations/149-oauth-tables.ts +2 -0
  225. package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +98 -0
  226. package/src/memory/migrations/151-oauth-providers-ping-url.ts +11 -0
  227. package/src/memory/migrations/152-memory-item-supersession.ts +44 -0
  228. package/src/memory/migrations/153-drop-entity-tables.ts +15 -0
  229. package/src/memory/migrations/154-drop-fts.ts +20 -0
  230. package/src/memory/migrations/155-drop-conflicts.ts +7 -0
  231. package/src/memory/migrations/156-call-session-invite-metadata.ts +24 -0
  232. package/src/memory/migrations/157-invite-contact-id.ts +104 -0
  233. package/src/memory/migrations/index.ts +8 -0
  234. package/src/memory/migrations/registry.ts +6 -0
  235. package/src/memory/qdrant-client.ts +148 -51
  236. package/src/memory/raw-query.ts +1 -1
  237. package/src/memory/retriever.test.ts +294 -273
  238. package/src/memory/retriever.ts +421 -645
  239. package/src/memory/schema/calls.ts +2 -0
  240. package/src/memory/schema/contacts.ts +1 -0
  241. package/src/memory/schema/memory-core.ts +3 -48
  242. package/src/memory/schema/oauth.ts +2 -0
  243. package/src/memory/search/formatting.ts +263 -176
  244. package/src/memory/search/lexical.ts +1 -254
  245. package/src/memory/search/ranking.ts +0 -455
  246. package/src/memory/search/semantic.ts +100 -14
  247. package/src/memory/search/staleness.ts +47 -0
  248. package/src/memory/search/tier-classifier.ts +21 -0
  249. package/src/memory/search/types.ts +15 -77
  250. package/src/memory/task-memory-cleanup.ts +4 -6
  251. package/src/messaging/provider.ts +1 -1
  252. package/src/messaging/providers/gmail/adapter.ts +1 -1
  253. package/src/messaging/providers/gmail/mime-builder.ts +17 -7
  254. package/src/messaging/providers/telegram-bot/adapter.ts +17 -8
  255. package/src/messaging/providers/whatsapp/adapter.ts +13 -9
  256. package/src/messaging/registry.ts +9 -5
  257. package/src/oauth/byo-connection.test.ts +40 -25
  258. package/src/oauth/connect-orchestrator.ts +4 -10
  259. package/src/oauth/connection-resolver.ts +20 -6
  260. package/src/oauth/manual-token-connection.ts +5 -5
  261. package/src/oauth/oauth-store.ts +183 -31
  262. package/src/oauth/platform-connection.test.ts +1 -1
  263. package/src/oauth/provider-behaviors.ts +503 -4
  264. package/src/oauth/seed-providers.ts +214 -8
  265. package/src/oauth/token-persistence.ts +31 -16
  266. package/src/permissions/defaults.ts +1 -0
  267. package/src/permissions/trust-store.ts +23 -1
  268. package/src/playbooks/playbook-compiler.ts +1 -1
  269. package/src/prompts/system-prompt.ts +18 -2
  270. package/src/providers/anthropic/client.ts +56 -126
  271. package/src/providers/types.ts +7 -1
  272. package/src/runtime/AGENTS.md +9 -0
  273. package/src/runtime/auth/route-policy.ts +6 -3
  274. package/src/runtime/channel-readiness-service.ts +48 -40
  275. package/src/runtime/guardian-reply-router.ts +24 -22
  276. package/src/runtime/http-server.ts +2 -2
  277. package/src/runtime/http-types.ts +2 -0
  278. package/src/runtime/invite-redemption-service.ts +72 -12
  279. package/src/runtime/invite-service.ts +43 -0
  280. package/src/runtime/middleware/twilio-validation.ts +1 -1
  281. package/src/runtime/pending-interactions.ts +2 -2
  282. package/src/runtime/routes/brain-graph-routes.ts +10 -90
  283. package/src/runtime/routes/btw-routes.ts +10 -5
  284. package/src/runtime/routes/conversation-routes.ts +56 -11
  285. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +21 -12
  286. package/src/runtime/routes/integrations/slack/channel.ts +2 -2
  287. package/src/runtime/routes/integrations/telegram.ts +2 -2
  288. package/src/runtime/routes/integrations/twilio.ts +17 -17
  289. package/src/runtime/routes/invite-routes.ts +29 -4
  290. package/src/runtime/routes/memory-item-routes.test.ts +754 -0
  291. package/src/runtime/routes/memory-item-routes.ts +503 -0
  292. package/src/runtime/routes/secret-routes.ts +17 -0
  293. package/src/runtime/routes/session-management-routes.ts +3 -3
  294. package/src/runtime/routes/settings-routes.ts +3 -3
  295. package/src/runtime/routes/trust-rules-routes.ts +14 -0
  296. package/src/runtime/routes/workspace-routes.ts +9 -4
  297. package/src/runtime/routes/workspace-utils.ts +8 -2
  298. package/src/schedule/integration-status.ts +26 -19
  299. package/src/security/keychain-broker-client.ts +17 -4
  300. package/src/security/oauth2.ts +6 -7
  301. package/src/security/secure-keys.ts +44 -19
  302. package/src/security/token-manager.ts +46 -39
  303. package/src/services/vercel-deploy.ts +0 -24
  304. package/src/signals/confirm.ts +78 -0
  305. package/src/signals/mcp-reload.ts +18 -0
  306. package/src/skills/catalog-install.ts +74 -18
  307. package/src/skills/skillssh-registry.ts +503 -0
  308. package/src/tools/assets/search.ts +5 -1
  309. package/src/tools/computer-use/definitions.ts +0 -10
  310. package/src/tools/computer-use/registry.ts +1 -1
  311. package/src/tools/credentials/vault.ts +22 -7
  312. package/src/tools/memory/definitions.ts +4 -13
  313. package/src/tools/memory/handlers.test.ts +83 -103
  314. package/src/tools/memory/handlers.ts +50 -85
  315. package/src/tools/network/script-proxy/session-manager.ts +8 -8
  316. package/src/tools/schedule/create.ts +10 -3
  317. package/src/tools/schedule/update.ts +8 -1
  318. package/src/tools/skills/load.ts +25 -2
  319. package/src/watcher/provider-types.ts +1 -1
  320. package/src/watcher/providers/github.ts +1 -1
  321. package/src/watcher/providers/gmail.ts +3 -3
  322. package/src/watcher/providers/google-calendar.ts +3 -3
  323. package/src/watcher/providers/linear.ts +1 -1
  324. package/src/__tests__/clarification-resolver.test.ts +0 -193
  325. package/src/__tests__/conflict-intent-tokenization.test.ts +0 -160
  326. package/src/__tests__/conflict-policy.test.ts +0 -269
  327. package/src/__tests__/conflict-store.test.ts +0 -372
  328. package/src/__tests__/contradiction-checker.test.ts +0 -361
  329. package/src/__tests__/entity-extractor.test.ts +0 -211
  330. package/src/__tests__/entity-search.test.ts +0 -1117
  331. package/src/__tests__/profile-compiler.test.ts +0 -392
  332. package/src/__tests__/session-conflict-gate.test.ts +0 -1228
  333. package/src/__tests__/session-profile-injection.test.ts +0 -557
  334. package/src/config/bundled-skills/knowledge-graph/SKILL.md +0 -25
  335. package/src/config/bundled-skills/knowledge-graph/TOOLS.json +0 -66
  336. package/src/config/bundled-skills/knowledge-graph/tools/graph-query.ts +0 -211
  337. package/src/daemon/session-conflict-gate.ts +0 -167
  338. package/src/daemon/session-dynamic-profile.ts +0 -77
  339. package/src/memory/clarification-resolver.ts +0 -417
  340. package/src/memory/conflict-intent.ts +0 -205
  341. package/src/memory/conflict-policy.ts +0 -127
  342. package/src/memory/conflict-store.ts +0 -410
  343. package/src/memory/contradiction-checker.ts +0 -508
  344. package/src/memory/entity-extractor.ts +0 -535
  345. package/src/memory/format-recall.ts +0 -47
  346. package/src/memory/fts-reconciler.ts +0 -165
  347. package/src/memory/job-handlers/conflict.ts +0 -200
  348. package/src/memory/profile-compiler.ts +0 -195
  349. package/src/memory/recall-cache.ts +0 -117
  350. package/src/memory/search/entity.ts +0 -535
  351. package/src/memory/search/query-expansion.test.ts +0 -70
  352. package/src/memory/search/query-expansion.ts +0 -118
  353. package/src/runtime/routes/mcp-routes.ts +0 -20
@@ -2,26 +2,15 @@ import { and, asc, eq, inArray, lt } from "drizzle-orm";
2
2
 
3
3
  import type { AssistantConfig } from "../../config/types.js";
4
4
  import { getLogger } from "../../util/logger.js";
5
- import { checkContradictions } from "../contradiction-checker.js";
6
5
  import { getDb, rawAll, rawRun } from "../db.js";
7
- import { asPositiveMs, asString } from "../job-utils.js";
6
+ import { asPositiveMs } from "../job-utils.js";
8
7
  import { enqueueMemoryJob, type MemoryJob } from "../jobs-store.js";
9
- import {
10
- memoryEmbeddings,
11
- memoryItemEntities,
12
- memoryItems,
13
- } from "../schema.js";
8
+ import { memoryEmbeddings, memoryItems } from "../schema.js";
14
9
 
15
10
  const log = getLogger("memory-jobs-worker");
16
11
 
17
12
  const CLEANUP_BATCH_LIMIT = 250;
18
13
 
19
- export async function checkContradictionsJob(job: MemoryJob): Promise<void> {
20
- const itemId = asString(job.payload.itemId);
21
- if (!itemId) return;
22
- await checkContradictions(itemId);
23
- }
24
-
25
14
  export function cleanupStaleSupersededItemsJob(
26
15
  job: MemoryJob,
27
16
  config: AssistantConfig,
@@ -46,9 +35,6 @@ export function cleanupStaleSupersededItemsJob(
46
35
  if (stale.length === 0) return;
47
36
 
48
37
  const ids = stale.map((row) => row.id);
49
- db.delete(memoryItemEntities)
50
- .where(inArray(memoryItemEntities.memoryItemId, ids))
51
- .run();
52
38
  db.delete(memoryEmbeddings)
53
39
  .where(
54
40
  and(
@@ -1,21 +1,11 @@
1
1
  import { eq } from "drizzle-orm";
2
2
 
3
- import { getConfig } from "../../config/loader.js";
4
- import type { AssistantConfig } from "../../config/types.js";
5
3
  import { getLogger } from "../../util/logger.js";
6
4
  import { getDb } from "../db.js";
7
- import {
8
- extractEntitiesWithLLM,
9
- linkMemoryItemToEntity,
10
- resolveEntityName,
11
- upsertEntity,
12
- upsertEntityRelation,
13
- } from "../entity-extractor.js";
14
5
  import { extractAndUpsertMemoryItemsForMessage } from "../items-extractor.js";
15
6
  import { asString } from "../job-utils.js";
16
- import { enqueueMemoryJob, type MemoryJob } from "../jobs-store.js";
17
- import { extractTextFromStoredMessageContent } from "../message-content.js";
18
- import { memoryItemSources, messages } from "../schema.js";
7
+ import type { MemoryJob } from "../jobs-store.js";
8
+ import { messages } from "../schema.js";
19
9
  import { isConversationFailed } from "../task-memory-cleanup.js";
20
10
 
21
11
  const log = getLogger("memory-jobs-worker");
@@ -47,130 +37,4 @@ export async function extractItemsJob(job: MemoryJob): Promise<void> {
47
37
  scopeId,
48
38
  msg?.conversationId,
49
39
  );
50
- // Queue entity extraction for this message after items are extracted
51
- const config = getConfig();
52
- if (config.memory.entity.enabled) {
53
- enqueueMemoryJob("extract_entities", { messageId, scopeId });
54
- }
55
- }
56
-
57
- export async function extractEntitiesJob(
58
- job: MemoryJob,
59
- config: AssistantConfig,
60
- ): Promise<void> {
61
- const messageId = asString(job.payload.messageId);
62
- if (!messageId) return;
63
-
64
- const db = getDb();
65
-
66
- // Guard: skip entity extraction for failed conversations. Entity extraction
67
- // jobs are enqueued by extractItemsJob after items are extracted; while new
68
- // jobs won't be queued (extractItemsJob returns early for failed convos),
69
- // any entity jobs enqueued before the failure marker was set must be caught.
70
- const msgRow = db
71
- .select({ conversationId: messages.conversationId })
72
- .from(messages)
73
- .where(eq(messages.id, messageId))
74
- .get();
75
- if (msgRow && isConversationFailed(msgRow.conversationId)) {
76
- log.info(
77
- { messageId, conversationId: msgRow.conversationId },
78
- "Skipping entity extraction for failed conversation",
79
- );
80
- return;
81
- }
82
-
83
- const message = db
84
- .select({
85
- id: messages.id,
86
- content: messages.content,
87
- })
88
- .from(messages)
89
- .where(eq(messages.id, messageId))
90
- .get();
91
- if (!message) return;
92
-
93
- const text = extractTextFromStoredMessageContent(message.content);
94
- if (text.trim().length < 15) return;
95
-
96
- const extracted = await extractEntitiesWithLLM(text, config.memory.entity);
97
- const entities = extracted.entities;
98
- const relations = extracted.relations;
99
- if (entities.length === 0 && relations.length === 0) return;
100
-
101
- // Find all memory items linked to this message via memory_item_sources
102
- const linkedItems = db
103
- .select({ memoryItemId: memoryItemSources.memoryItemId })
104
- .from(memoryItemSources)
105
- .where(eq(memoryItemSources.messageId, messageId))
106
- .all();
107
- const itemIds = linkedItems.map((row) => row.memoryItemId);
108
- const entityNameToId = new Map<string, string>();
109
-
110
- for (const entity of entities) {
111
- const entityId = upsertEntity(entity);
112
- entityNameToId.set(entity.name.toLowerCase(), entityId);
113
- for (const alias of entity.aliases) {
114
- entityNameToId.set(alias.toLowerCase(), entityId);
115
- }
116
- // Link all memory items from this message to the entity
117
- for (const itemId of itemIds) {
118
- linkMemoryItemToEntity(itemId, entityId);
119
- }
120
- }
121
-
122
- const relationTelemetry = {
123
- attempted: 0,
124
- parsed: relations.length,
125
- persisted: 0,
126
- dropped: 0,
127
- };
128
-
129
- if (config.memory.entity.extractRelations.enabled && relations.length > 0) {
130
- const seenRelationKeys = new Set<string>();
131
- for (const relation of relations) {
132
- relationTelemetry.attempted += 1;
133
- const sourceLookup = relation.sourceEntityName.toLowerCase();
134
- const targetLookup = relation.targetEntityName.toLowerCase();
135
- const sourceEntityId =
136
- entityNameToId.get(sourceLookup) ??
137
- resolveEntityName(relation.sourceEntityName);
138
- const targetEntityId =
139
- entityNameToId.get(targetLookup) ??
140
- resolveEntityName(relation.targetEntityName);
141
- if (
142
- !sourceEntityId ||
143
- !targetEntityId ||
144
- sourceEntityId === targetEntityId
145
- ) {
146
- relationTelemetry.dropped += 1;
147
- continue;
148
- }
149
-
150
- const dedupeKey = `${sourceEntityId}|${targetEntityId}|${relation.relation}`;
151
- if (seenRelationKeys.has(dedupeKey)) continue;
152
- seenRelationKeys.add(dedupeKey);
153
-
154
- upsertEntityRelation({
155
- sourceEntityId,
156
- targetEntityId,
157
- relation: relation.relation,
158
- evidence: relation.evidence,
159
- });
160
- relationTelemetry.persisted += 1;
161
- }
162
- }
163
-
164
- log.debug(
165
- {
166
- messageId,
167
- entityCount: entities.length,
168
- linkedItems: itemIds.length,
169
- relationAttempts: relationTelemetry.attempted,
170
- relationParsed: relationTelemetry.parsed,
171
- relationPersisted: relationTelemetry.persisted,
172
- relationDropped: relationTelemetry.dropped,
173
- },
174
- "Extracted entity graph from message",
175
- );
176
40
  }
@@ -2,7 +2,7 @@ import { eq, like } from "drizzle-orm";
2
2
 
3
3
  import { getConfig } from "../../config/loader.js";
4
4
  import { getLogger } from "../../util/logger.js";
5
- import { getDb, rawExec } from "../db.js";
5
+ import { getDb } from "../db.js";
6
6
  import { selectedBackendSupportsMultimodal } from "../embedding-backend.js";
7
7
  import { asString, BackendUnavailableError } from "../job-utils.js";
8
8
  import { enqueueMemoryJob, type MemoryJob } from "../jobs-store.js";
@@ -22,11 +22,6 @@ const log = getLogger("memory-jobs-worker");
22
22
 
23
23
  export function rebuildIndexJob(): void {
24
24
  const db = getDb();
25
- rawExec(/*sql*/ `DELETE FROM memory_segment_fts`);
26
- rawExec(/*sql*/ `
27
- INSERT INTO memory_segment_fts(segment_id, text)
28
- SELECT id, text FROM memory_segments
29
- `);
30
25
  db.delete(memoryEmbeddings).run();
31
26
 
32
27
  const items = db
@@ -1,4 +1,4 @@
1
- import { and, desc, eq, gte, isNull, lt, sql } from "drizzle-orm";
1
+ import { and, desc, eq, sql } from "drizzle-orm";
2
2
  import { v4 as uuid } from "uuid";
3
3
 
4
4
  import type { AssistantConfig } from "../../config/types.js";
@@ -12,14 +12,9 @@ import {
12
12
  import { getLogger } from "../../util/logger.js";
13
13
  import { getConversationMemoryScopeId } from "../conversation-crud.js";
14
14
  import { getDb } from "../db.js";
15
- import {
16
- asString,
17
- currentMonthWindow,
18
- currentWeekWindow,
19
- truncate,
20
- } from "../job-utils.js";
15
+ import { asString, truncate } from "../job-utils.js";
21
16
  import { enqueueMemoryJob, type MemoryJob } from "../jobs-store.js";
22
- import { memoryItems, memorySegments, memorySummaries } from "../schema.js";
17
+ import { memorySegments, memorySummaries } from "../schema.js";
23
18
 
24
19
  const log = getLogger("memory-jobs-worker");
25
20
 
@@ -38,18 +33,6 @@ const CONVERSATION_SUMMARY_SYSTEM_PROMPT = [
38
33
  "- If updating an existing summary with new data, merge new information and remove anything that was superseded.",
39
34
  ].join("\n");
40
35
 
41
- const GLOBAL_SUMMARY_SYSTEM_PROMPT = [
42
- "You are a memory summarization system. Your job is to synthesize a higher-level summary from multiple conversation summaries and memory items.",
43
- "",
44
- "Guidelines:",
45
- "- Identify recurring themes, cross-cutting decisions, and persistent user preferences.",
46
- "- Highlight the most important facts, active projects, and ongoing concerns.",
47
- "- De-duplicate information that appears across multiple conversations.",
48
- "- Use concise sections with bullet points.",
49
- "- Target 400-600 tokens. Be dense but readable.",
50
- "- If updating an existing summary with new data, merge new information and remove anything that was superseded.",
51
- ].join("\n");
52
-
53
36
  export async function buildConversationSummaryJob(
54
37
  job: MemoryJob,
55
38
  config: AssistantConfig,
@@ -144,134 +127,6 @@ export async function buildConversationSummaryJob(
144
127
  }
145
128
  }
146
129
 
147
- export async function buildGlobalSummaryJob(
148
- scope: "weekly_global" | "monthly_global",
149
- config: AssistantConfig,
150
- ): Promise<void> {
151
- const db = getDb();
152
- const now = new Date();
153
- const { startMs, endMs, scopeKey } =
154
- scope === "weekly_global"
155
- ? currentWeekWindow(now)
156
- : currentMonthWindow(now);
157
-
158
- const items = db
159
- .select()
160
- .from(memoryItems)
161
- .where(
162
- and(
163
- eq(memoryItems.status, "active"),
164
- isNull(memoryItems.invalidAt),
165
- eq(memoryItems.scopeId, "default"),
166
- gte(memoryItems.lastSeenAt, startMs),
167
- lt(memoryItems.lastSeenAt, endMs),
168
- ),
169
- )
170
- .orderBy(desc(memoryItems.lastSeenAt))
171
- .limit(80)
172
- .all();
173
-
174
- // Gather conversation summaries from this period for higher-level synthesis
175
- const convSummaries = db
176
- .select()
177
- .from(memorySummaries)
178
- .where(
179
- and(
180
- eq(memorySummaries.scope, "conversation"),
181
- eq(memorySummaries.scopeId, "default"),
182
- gte(memorySummaries.endAt, startMs),
183
- lt(memorySummaries.startAt, endMs),
184
- ),
185
- )
186
- .orderBy(desc(memorySummaries.endAt))
187
- .limit(20)
188
- .all();
189
-
190
- if (items.length === 0 && convSummaries.length === 0) return;
191
-
192
- // Build input for LLM: conversation summaries + active items
193
- const parts: string[] = [];
194
- if (convSummaries.length > 0) {
195
- parts.push("## Conversation Summaries");
196
- for (const cs of convSummaries) {
197
- parts.push(`### ${cs.scopeKey}\n${truncate(cs.summary, 600)}`);
198
- }
199
- }
200
- if (items.length > 0) {
201
- parts.push("## Active Memory Items");
202
- for (const item of items.slice(0, 40)) {
203
- parts.push(
204
- `- [${item.kind}] ${item.subject}: ${truncate(item.statement, 180)}`,
205
- );
206
- }
207
- }
208
- const inputText = parts.join("\n\n");
209
-
210
- const existing = db
211
- .select()
212
- .from(memorySummaries)
213
- .where(
214
- and(
215
- eq(memorySummaries.scope, scope),
216
- eq(memorySummaries.scopeKey, scopeKey),
217
- ),
218
- )
219
- .get();
220
-
221
- const label = scope === "weekly_global" ? "weekly" : "monthly";
222
- const summaryText = await summarizeWithLLM(
223
- config,
224
- GLOBAL_SUMMARY_SYSTEM_PROMPT,
225
- existing?.summary ?? null,
226
- inputText,
227
- label,
228
- );
229
-
230
- const ts = Date.now();
231
- const summaryId = existing?.id ?? uuid();
232
- db.insert(memorySummaries)
233
- .values({
234
- id: summaryId,
235
- scope,
236
- scopeKey,
237
- summary: summaryText,
238
- tokenEstimate: estimateTextTokens(summaryText),
239
- version: (existing?.version ?? 0) + 1,
240
- startAt: startMs,
241
- endAt: endMs,
242
- createdAt: ts,
243
- updatedAt: ts,
244
- })
245
- .onConflictDoUpdate({
246
- target: [memorySummaries.scope, memorySummaries.scopeKey],
247
- set: {
248
- summary: summaryText,
249
- tokenEstimate: estimateTextTokens(summaryText),
250
- version: sql`${memorySummaries.version} + 1`,
251
- startAt: startMs,
252
- endAt: endMs,
253
- updatedAt: ts,
254
- },
255
- })
256
- .run();
257
-
258
- // Re-query to get the actual persisted row ID — during a race the ON CONFLICT
259
- // path keeps the winner's ID, not the pre-generated UUID from the loser.
260
- const actualRow = db
261
- .select({ id: memorySummaries.id })
262
- .from(memorySummaries)
263
- .where(
264
- and(
265
- eq(memorySummaries.scope, scope),
266
- eq(memorySummaries.scopeKey, scopeKey),
267
- ),
268
- )
269
- .get();
270
- if (actualRow) {
271
- enqueueMemoryJob("embed_summary", { summaryId: actualRow.id });
272
- }
273
- }
274
-
275
130
  async function summarizeWithLLM(
276
131
  config: AssistantConfig,
277
132
  systemPrompt: string,
@@ -8,6 +8,7 @@ import { getLogger } from "../util/logger.js";
8
8
  import { getDb } from "./db.js";
9
9
  import {
10
10
  embedWithBackend,
11
+ generateSparseEmbedding,
11
12
  getMemoryBackendStatus,
12
13
  } from "./embedding-backend.js";
13
14
  import type { EmbeddingInput } from "./embedding-types.js";
@@ -201,6 +202,14 @@ export async function embedAndUpsert(
201
202
  ? normalized.text
202
203
  : `[${normalized.type}:${normalized.mimeType}]`;
203
204
 
205
+ // Generate sparse embedding from the same source text used for dense embedding.
206
+ // For non-text (media) inputs, sparse vectors are skipped since tokenization
207
+ // only applies to text content.
208
+ const sparseVector =
209
+ normalized.type === "text"
210
+ ? generateSparseEmbedding(normalized.text)
211
+ : undefined;
212
+
204
213
  // Persist embedding in SQLite for cross-restart cache
205
214
  const now = Date.now();
206
215
  try {
@@ -249,12 +258,18 @@ export async function embedAndUpsert(
249
258
  try {
250
259
  const modality = normalized.type;
251
260
  await withQdrantBreaker(() =>
252
- qdrant.upsert(targetType, targetId, vector, {
253
- text: payloadText,
254
- modality,
255
- created_at: (extraPayload?.created_at as number) ?? now,
256
- ...(extraPayload as Record<string, unknown> | undefined),
257
- }),
261
+ qdrant.upsert(
262
+ targetType,
263
+ targetId,
264
+ vector,
265
+ {
266
+ text: payloadText,
267
+ modality,
268
+ created_at: (extraPayload?.created_at as number) ?? now,
269
+ ...(extraPayload as Record<string, unknown> | undefined),
270
+ },
271
+ sparseVector,
272
+ ),
258
273
  );
259
274
  } catch (err) {
260
275
  log.warn(
@@ -265,56 +280,3 @@ export async function embedAndUpsert(
265
280
  }
266
281
  }
267
282
 
268
- // ── Time window utilities ──────────────────────────────────────────
269
-
270
- export function currentWeekWindow(now: Date): {
271
- scopeKey: string;
272
- startMs: number;
273
- endMs: number;
274
- } {
275
- const day = (now.getUTCDay() + 6) % 7;
276
- const start = new Date(
277
- Date.UTC(
278
- now.getUTCFullYear(),
279
- now.getUTCMonth(),
280
- now.getUTCDate() - day,
281
- 0,
282
- 0,
283
- 0,
284
- 0,
285
- ),
286
- );
287
- const end = new Date(start);
288
- end.setUTCDate(end.getUTCDate() + 7);
289
- const scopeKey = `${start.getUTCFullYear()}-W${weekNumber(start)
290
- .toString()
291
- .padStart(2, "0")}`;
292
- return { scopeKey, startMs: start.getTime(), endMs: end.getTime() };
293
- }
294
-
295
- export function currentMonthWindow(now: Date): {
296
- scopeKey: string;
297
- startMs: number;
298
- endMs: number;
299
- } {
300
- const start = new Date(
301
- Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1, 0, 0, 0, 0),
302
- );
303
- const end = new Date(
304
- Date.UTC(now.getUTCFullYear(), now.getUTCMonth() + 1, 1, 0, 0, 0, 0),
305
- );
306
- const scopeKey = `${start.getUTCFullYear()}-${String(
307
- start.getUTCMonth() + 1,
308
- ).padStart(2, "0")}`;
309
- return { scopeKey, startMs: start.getTime(), endMs: end.getTime() };
310
- }
311
-
312
- function weekNumber(date: Date): number {
313
- const tmp = new Date(
314
- Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()),
315
- );
316
- const dayNum = tmp.getUTCDay() || 7;
317
- tmp.setUTCDate(tmp.getUTCDate() + 4 - dayNum);
318
- const yearStart = new Date(Date.UTC(tmp.getUTCFullYear(), 0, 1));
319
- return Math.ceil(((tmp.getTime() - yearStart.getTime()) / 86400000 + 1) / 7);
320
- }
@@ -3,7 +3,7 @@ import { v4 as uuid } from "uuid";
3
3
 
4
4
  import { getLogger } from "../util/logger.js";
5
5
  import { truncate } from "../util/truncate.js";
6
- import { getDb, rawAll, rawChanges, rawGet } from "./db.js";
6
+ import { getDb, rawAll, rawChanges } from "./db.js";
7
7
  import { memoryJobs } from "./schema.js";
8
8
 
9
9
  const log = getLogger("memory-jobs-store");
@@ -14,18 +14,14 @@ export type MemoryJobType =
14
14
  | "embed_summary"
15
15
  | "extract_items"
16
16
  | "extract_entities"
17
- | "resolve_pending_conflicts_for_message"
18
- | "cleanup_resolved_conflicts"
19
17
  | "cleanup_stale_superseded_items"
20
18
  | "prune_old_conversations"
21
19
  | "backfill_entity_relations"
22
- | "check_contradictions"
23
20
  | "refresh_weekly_summary"
24
21
  | "refresh_monthly_summary"
25
22
  | "build_conversation_summary"
26
23
  | "backfill"
27
24
  | "rebuild_index"
28
- | "reconcile_fts"
29
25
  | "delete_qdrant_vectors"
30
26
  | "media_processing"
31
27
  | "embed_media"
@@ -83,143 +79,6 @@ export function enqueueMemoryJob(
83
79
  return id;
84
80
  }
85
81
 
86
- /**
87
- * Ensure there is only one pending relation backfill orchestrator job.
88
- * If `force=true` arrives while a pending job exists, its payload is upgraded.
89
- */
90
- export function enqueueBackfillEntityRelationsJob(force = false): string {
91
- const db = getDb();
92
- const now = Date.now();
93
- const existing = db
94
- .select()
95
- .from(memoryJobs)
96
- .where(
97
- and(
98
- eq(memoryJobs.type, "backfill_entity_relations"),
99
- eq(memoryJobs.status, "pending"),
100
- ),
101
- )
102
- .orderBy(asc(memoryJobs.createdAt))
103
- .get();
104
-
105
- if (existing) {
106
- if (force) {
107
- let payload: Record<string, unknown> = {};
108
- try {
109
- payload = JSON.parse(existing.payload) as Record<string, unknown>;
110
- } catch {
111
- payload = {};
112
- }
113
- if (payload.force !== true) {
114
- db.update(memoryJobs)
115
- .set({
116
- payload: JSON.stringify({ ...payload, force: true }),
117
- updatedAt: now,
118
- })
119
- .where(eq(memoryJobs.id, existing.id))
120
- .run();
121
- }
122
- }
123
- return existing.id;
124
- }
125
-
126
- return enqueueMemoryJob("backfill_entity_relations", { force });
127
- }
128
-
129
- export function enqueueResolvePendingConflictsForMessageJob(
130
- messageId: string,
131
- scopeId = "default",
132
- dbOverride?: Parameters<ReturnType<typeof getDb>["transaction"]>[0] extends (
133
- tx: infer T,
134
- ) => unknown
135
- ? T
136
- : never,
137
- ): string {
138
- const normalizedMessageId = messageId.trim();
139
- if (!normalizedMessageId) {
140
- throw new Error(
141
- "enqueueResolvePendingConflictsForMessageJob requires a non-empty messageId",
142
- );
143
- }
144
- const normalizedScopeId = scopeId.trim() || "default";
145
- // Dedup check always uses root db since tx doesn't expose raw client
146
- const existing = rawGet<{ id: string }>(
147
- `
148
- SELECT id
149
- FROM memory_jobs
150
- WHERE type = 'resolve_pending_conflicts_for_message'
151
- AND status IN ('pending', 'running')
152
- AND json_extract(payload, '$.messageId') = ?
153
- AND COALESCE(json_extract(payload, '$.scopeId'), 'default') = ?
154
- ORDER BY created_at ASC
155
- LIMIT 1
156
- `,
157
- normalizedMessageId,
158
- normalizedScopeId,
159
- );
160
- if (existing?.id) return existing.id;
161
-
162
- return enqueueMemoryJob(
163
- "resolve_pending_conflicts_for_message",
164
- {
165
- messageId: normalizedMessageId,
166
- scopeId: normalizedScopeId,
167
- },
168
- Date.now(),
169
- dbOverride,
170
- );
171
- }
172
-
173
- export function enqueueCleanupResolvedConflictsJob(
174
- retentionMs?: number,
175
- ): string {
176
- const db = getDb();
177
- const now = Date.now();
178
- const existing = db
179
- .select()
180
- .from(memoryJobs)
181
- .where(
182
- and(
183
- eq(memoryJobs.type, "cleanup_resolved_conflicts"),
184
- inArray(memoryJobs.status, ["pending", "running"]),
185
- ),
186
- )
187
- .orderBy(asc(memoryJobs.createdAt))
188
- .get();
189
- if (existing) {
190
- if (
191
- existing.status === "pending" &&
192
- typeof retentionMs === "number" &&
193
- Number.isFinite(retentionMs) &&
194
- retentionMs > 0
195
- ) {
196
- let payload: Record<string, unknown> = {};
197
- try {
198
- payload = JSON.parse(existing.payload) as Record<string, unknown>;
199
- } catch {
200
- payload = {};
201
- }
202
- if (payload.retentionMs !== retentionMs) {
203
- db.update(memoryJobs)
204
- .set({
205
- payload: JSON.stringify({ ...payload, retentionMs }),
206
- updatedAt: now,
207
- })
208
- .where(eq(memoryJobs.id, existing.id))
209
- .run();
210
- }
211
- }
212
- return existing.id;
213
- }
214
- const payload =
215
- typeof retentionMs === "number" &&
216
- Number.isFinite(retentionMs) &&
217
- retentionMs > 0
218
- ? { retentionMs }
219
- : {};
220
- return enqueueMemoryJob("cleanup_resolved_conflicts", payload);
221
- }
222
-
223
82
  export function enqueueCleanupStaleSupersededItemsJob(
224
83
  retentionMs?: number,
225
84
  ): string {
@@ -319,23 +178,6 @@ export function enqueuePruneOldConversationsJob(
319
178
  return enqueueMemoryJob("prune_old_conversations", payload);
320
179
  }
321
180
 
322
- export function enqueueReconcileFtsJob(): string {
323
- const db = getDb();
324
- const existing = db
325
- .select()
326
- .from(memoryJobs)
327
- .where(
328
- and(
329
- eq(memoryJobs.type, "reconcile_fts"),
330
- inArray(memoryJobs.status, ["pending", "running"]),
331
- ),
332
- )
333
- .orderBy(asc(memoryJobs.createdAt))
334
- .get();
335
- if (existing) return existing.id;
336
- return enqueueMemoryJob("reconcile_fts", {});
337
- }
338
-
339
181
  export function claimMemoryJobs(limit: number): MemoryJob[] {
340
182
  if (limit <= 0) return [];
341
183
  const db = getDb();