@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
@@ -1,127 +0,0 @@
1
- /**
2
- * Pure, deterministic policy helpers for memory conflict eligibility.
3
- * Used by contradiction checker, session conflict gate, and background resolver.
4
- */
5
-
6
- export interface ConflictPolicyConfig {
7
- conflictableKinds: readonly string[];
8
- [key: string]: unknown;
9
- }
10
-
11
- /**
12
- * Returns true when the given memory item kind is eligible to participate
13
- * in conflict detection according to the current policy.
14
- */
15
- export function isConflictKindEligible(
16
- kind: string,
17
- config: ConflictPolicyConfig,
18
- ): boolean {
19
- return config.conflictableKinds.includes(kind);
20
- }
21
-
22
- /**
23
- * Returns true when both sides of a potential conflict pair are kind-eligible.
24
- */
25
- export function isConflictKindPairEligible(
26
- existingKind: string,
27
- candidateKind: string,
28
- config: ConflictPolicyConfig,
29
- ): boolean {
30
- return (
31
- isConflictKindEligible(existingKind, config) &&
32
- isConflictKindEligible(candidateKind, config)
33
- );
34
- }
35
-
36
- // ── Transient statement classification ─────────────────────────────────
37
-
38
- const PR_URL_PATTERN = /github\.com\/[^/]+\/[^/]+\/pull\/\d+/i;
39
- const ISSUE_TICKET_PATTERN = /\b(?:issue|pr|ticket|pull request)\s*#?\d+/i;
40
- const TRACKING_LANGUAGE_PATTERN =
41
- /\b(?:this pr|that issue|while we wait|currently tracking)\b/i;
42
-
43
- // Statements about needing clarification are transient conversational artifacts
44
- // extracted from previous conflict-gate interactions — not durable facts.
45
- // Allowing them into the conflict pipeline creates self-reinforcing loops.
46
- // Patterns are kept narrow to avoid filtering legitimate durable instructions.
47
- const META_CLARIFICATION_PATTERN =
48
- /\b(?:needs? clarification\b|unclear which (?:version|value|setting)\b|user should (?:specify|clarify)\b|conflicting (?:notes|instructions)(?:\s*[:."]|\s+(?:about|regarding|for)\b))/i;
49
-
50
- /**
51
- * Returns true when a statement looks like a transient tracking note
52
- * (PR URLs, issue references, short-lived progress notes) rather than
53
- * a durable user preference or instruction.
54
- */
55
- export function isTransientTrackingStatement(statement: string): boolean {
56
- if (PR_URL_PATTERN.test(statement)) return true;
57
- if (ISSUE_TICKET_PATTERN.test(statement)) return true;
58
- if (TRACKING_LANGUAGE_PATTERN.test(statement)) return true;
59
- if (META_CLARIFICATION_PATTERN.test(statement)) return true;
60
- return false;
61
- }
62
-
63
- const DURABLE_INSTRUCTION_CUES =
64
- /\b(?:always|never|default|every time|by default|style|format|tone|convention|standard)\b/i;
65
-
66
- /**
67
- * Returns true when a statement contains strong durable instruction cues,
68
- * suggesting it represents a persistent user preference or style rule.
69
- */
70
- export function isDurableInstructionStatement(statement: string): boolean {
71
- return DURABLE_INSTRUCTION_CUES.test(statement);
72
- }
73
-
74
- // ── Verification-state provenance ──────────────────────────────────────
75
-
76
- // States indicating user involvement — either the user directly stated
77
- // the information, explicitly confirmed it, or it was bulk-imported from
78
- // a trusted external source the user chose to connect.
79
- const USER_EVIDENCED_STATES = new Set([
80
- "user_reported",
81
- "user_confirmed",
82
- "legacy_import",
83
- ]);
84
-
85
- /**
86
- * Returns true when the verification state indicates user provenance
87
- * (as opposed to purely assistant-inferred).
88
- */
89
- export function isUserEvidencedVerificationState(state: string): boolean {
90
- return USER_EVIDENCED_STATES.has(state);
91
- }
92
-
93
- /**
94
- * Returns true when at least one side of a conflict pair has user-evidenced
95
- * provenance. Assistant-inferred-only conflicts should not escalate into
96
- * user-facing behavior.
97
- */
98
- export function isConflictUserEvidenced(
99
- existingState: string,
100
- candidateState: string,
101
- ): boolean {
102
- return (
103
- isUserEvidencedVerificationState(existingState) ||
104
- isUserEvidencedVerificationState(candidateState)
105
- );
106
- }
107
-
108
- /**
109
- * Returns true when a statement of the given kind is eligible to participate
110
- * in conflict detection at the statement level. This combines kind eligibility
111
- * with statement-level durability heuristics.
112
- *
113
- * For instruction/style kinds: requires positive durable cues and no transient cues.
114
- * For other eligible kinds: rejects if transient tracking cues dominate.
115
- */
116
- export function isStatementConflictEligible(
117
- kind: string,
118
- statement: string,
119
- config?: ConflictPolicyConfig,
120
- ): boolean {
121
- if (config && !isConflictKindEligible(kind, config)) return false;
122
- if (isTransientTrackingStatement(statement)) return false;
123
- if (kind === "instruction" || kind === "style") {
124
- return isDurableInstructionStatement(statement);
125
- }
126
- return true;
127
- }
@@ -1,410 +0,0 @@
1
- import { and, asc, eq } from "drizzle-orm";
2
- import { v4 as uuid } from "uuid";
3
-
4
- import { getDb, getSqlite, rawAll } from "./db.js";
5
- import { enqueueMemoryJob } from "./jobs-store.js";
6
- import { memoryItemConflicts, memoryItems } from "./schema.js";
7
- import { clampUnitInterval } from "./validation.js";
8
-
9
- export type MemoryConflictRelationship =
10
- | "contradiction"
11
- | "ambiguous_contradiction"
12
- | "update"
13
- | "complement";
14
-
15
- export type MemoryConflictStatus =
16
- | "pending_clarification"
17
- | "resolved_keep_existing"
18
- | "resolved_keep_candidate"
19
- | "resolved_merge"
20
- | "dismissed";
21
-
22
- export type ResolvedMemoryConflictStatus = Exclude<
23
- MemoryConflictStatus,
24
- "pending_clarification"
25
- >;
26
-
27
- export interface MemoryItemConflict {
28
- id: string;
29
- scopeId: string;
30
- existingItemId: string;
31
- candidateItemId: string;
32
- relationship: string;
33
- status: MemoryConflictStatus;
34
- clarificationQuestion: string | null;
35
- resolutionNote: string | null;
36
- lastAskedAt: number | null;
37
- resolvedAt: number | null;
38
- createdAt: number;
39
- updatedAt: number;
40
- }
41
-
42
- export interface CreatePendingConflictInput {
43
- scopeId: string;
44
- existingItemId: string;
45
- candidateItemId: string;
46
- relationship: string;
47
- clarificationQuestion?: string | null;
48
- }
49
-
50
- export interface ResolveConflictInput {
51
- status: ResolvedMemoryConflictStatus;
52
- resolutionNote?: string | null;
53
- }
54
-
55
- export interface PendingConflictDetail extends MemoryItemConflict {
56
- existingStatement: string;
57
- candidateStatement: string;
58
- existingKind: string;
59
- candidateKind: string;
60
- existingVerificationState: string;
61
- candidateVerificationState: string;
62
- }
63
-
64
- export type ConflictResolutionAction =
65
- | "keep_existing"
66
- | "keep_candidate"
67
- | "merge";
68
-
69
- export interface ApplyConflictResolutionInput {
70
- conflictId: string;
71
- resolution: ConflictResolutionAction;
72
- mergedStatement?: string | null;
73
- resolutionNote?: string | null;
74
- }
75
-
76
- export function createOrUpdatePendingConflict(
77
- input: CreatePendingConflictInput,
78
- ): MemoryItemConflict {
79
- // Wrap in BEGIN IMMEDIATE so the SELECT-then-INSERT is atomic against concurrent
80
- // writers. Without this, two parallel memory workers could both observe no
81
- // existing conflict and both attempt to INSERT the same pair, resulting in a
82
- // duplicate or an unexpected constraint violation.
83
- return getSqlite()
84
- .transaction((): MemoryItemConflict => {
85
- const db = getDb();
86
- const now = Date.now();
87
- const scopeId = input.scopeId;
88
- const existing = getPendingConflictByPair(
89
- scopeId,
90
- input.existingItemId,
91
- input.candidateItemId,
92
- );
93
-
94
- if (existing) {
95
- db.update(memoryItemConflicts)
96
- .set({
97
- relationship: input.relationship,
98
- clarificationQuestion:
99
- input.clarificationQuestion !== undefined
100
- ? input.clarificationQuestion
101
- : existing.clarificationQuestion,
102
- updatedAt: now,
103
- })
104
- .where(eq(memoryItemConflicts.id, existing.id))
105
- .run();
106
- const updated = getConflictById(existing.id);
107
- if (!updated) {
108
- throw new Error(`Failed to reload updated conflict: ${existing.id}`);
109
- }
110
- return updated;
111
- }
112
-
113
- const id = uuid();
114
- db.insert(memoryItemConflicts)
115
- .values({
116
- id,
117
- scopeId,
118
- existingItemId: input.existingItemId,
119
- candidateItemId: input.candidateItemId,
120
- relationship: input.relationship,
121
- status: "pending_clarification",
122
- clarificationQuestion: input.clarificationQuestion ?? null,
123
- resolutionNote: null,
124
- lastAskedAt: null,
125
- resolvedAt: null,
126
- createdAt: now,
127
- updatedAt: now,
128
- })
129
- .run();
130
-
131
- const created = getConflictById(id);
132
- if (!created) {
133
- throw new Error(`Failed to load created conflict: ${id}`);
134
- }
135
- return created;
136
- })
137
- .immediate();
138
- }
139
-
140
- export function getConflictById(conflictId: string): MemoryItemConflict | null {
141
- const db = getDb();
142
- const row = db
143
- .select()
144
- .from(memoryItemConflicts)
145
- .where(eq(memoryItemConflicts.id, conflictId))
146
- .get();
147
- return row ? toConflict(row) : null;
148
- }
149
-
150
- export function getPendingConflictByPair(
151
- scopeId: string,
152
- existingItemId: string,
153
- candidateItemId: string,
154
- ): MemoryItemConflict | null {
155
- const db = getDb();
156
- const row = db
157
- .select()
158
- .from(memoryItemConflicts)
159
- .where(
160
- and(
161
- eq(memoryItemConflicts.scopeId, scopeId),
162
- eq(memoryItemConflicts.existingItemId, existingItemId),
163
- eq(memoryItemConflicts.candidateItemId, candidateItemId),
164
- eq(memoryItemConflicts.status, "pending_clarification"),
165
- ),
166
- )
167
- .get();
168
- return row ? toConflict(row) : null;
169
- }
170
-
171
- export function listPendingConflicts(
172
- scopeId: string,
173
- limit = 100,
174
- ): MemoryItemConflict[] {
175
- if (limit <= 0) return [];
176
- const db = getDb();
177
- const rows = db
178
- .select()
179
- .from(memoryItemConflicts)
180
- .where(
181
- and(
182
- eq(memoryItemConflicts.scopeId, scopeId),
183
- eq(memoryItemConflicts.status, "pending_clarification"),
184
- ),
185
- )
186
- .orderBy(asc(memoryItemConflicts.createdAt))
187
- .limit(limit)
188
- .all();
189
- return rows.map(toConflict);
190
- }
191
-
192
- export function listPendingConflictDetails(
193
- scopeId: string,
194
- limit = 100,
195
- cursor?: { createdAt: number; id: string },
196
- ): PendingConflictDetail[] {
197
- if (limit <= 0) return [];
198
- interface ConflictDetailRow {
199
- id: string;
200
- scope_id: string;
201
- existing_item_id: string;
202
- candidate_item_id: string;
203
- relationship: string;
204
- status: MemoryConflictStatus;
205
- clarification_question: string | null;
206
- resolution_note: string | null;
207
- last_asked_at: number | null;
208
- resolved_at: number | null;
209
- created_at: number;
210
- updated_at: number;
211
- existing_statement: string;
212
- candidate_statement: string;
213
- existing_kind: string;
214
- candidate_kind: string;
215
- existing_verification_state: string;
216
- candidate_verification_state: string;
217
- }
218
- const cursorClause = cursor
219
- ? `AND (c.created_at > ? OR (c.created_at = ? AND c.id > ?))`
220
- : "";
221
- const params: (string | number)[] = cursor
222
- ? [scopeId, cursor.createdAt, cursor.createdAt, cursor.id, limit]
223
- : [scopeId, limit];
224
- const rows = rawAll<ConflictDetailRow>(
225
- `
226
- SELECT
227
- c.id,
228
- c.scope_id,
229
- c.existing_item_id,
230
- c.candidate_item_id,
231
- c.relationship,
232
- c.status,
233
- c.clarification_question,
234
- c.resolution_note,
235
- c.last_asked_at,
236
- c.resolved_at,
237
- c.created_at,
238
- c.updated_at,
239
- existing_item.statement AS existing_statement,
240
- candidate_item.statement AS candidate_statement,
241
- existing_item.kind AS existing_kind,
242
- candidate_item.kind AS candidate_kind,
243
- existing_item.verification_state AS existing_verification_state,
244
- candidate_item.verification_state AS candidate_verification_state
245
- FROM memory_item_conflicts c
246
- INNER JOIN memory_items existing_item ON existing_item.id = c.existing_item_id
247
- INNER JOIN memory_items candidate_item ON candidate_item.id = c.candidate_item_id
248
- WHERE c.scope_id = ?
249
- AND c.status = 'pending_clarification'
250
- ${cursorClause}
251
- ORDER BY c.created_at ASC, c.id ASC
252
- LIMIT ?
253
- `,
254
- ...params,
255
- );
256
-
257
- return rows.map((row) => ({
258
- id: row.id,
259
- scopeId: row.scope_id,
260
- existingItemId: row.existing_item_id,
261
- candidateItemId: row.candidate_item_id,
262
- relationship: row.relationship,
263
- status: row.status,
264
- clarificationQuestion: row.clarification_question,
265
- resolutionNote: row.resolution_note,
266
- lastAskedAt: row.last_asked_at,
267
- resolvedAt: row.resolved_at,
268
- createdAt: row.created_at,
269
- updatedAt: row.updated_at,
270
- existingStatement: row.existing_statement,
271
- candidateStatement: row.candidate_statement,
272
- existingKind: row.existing_kind,
273
- candidateKind: row.candidate_kind,
274
- existingVerificationState: row.existing_verification_state,
275
- candidateVerificationState: row.candidate_verification_state,
276
- }));
277
- }
278
-
279
- export function resolveConflict(
280
- conflictId: string,
281
- input: ResolveConflictInput,
282
- ): MemoryItemConflict | null {
283
- const existing = getConflictById(conflictId);
284
- if (!existing) return null;
285
-
286
- const db = getDb();
287
- const now = Date.now();
288
- db.update(memoryItemConflicts)
289
- .set({
290
- status: input.status,
291
- resolutionNote:
292
- input.resolutionNote !== undefined
293
- ? input.resolutionNote
294
- : existing.resolutionNote,
295
- resolvedAt: now,
296
- updatedAt: now,
297
- })
298
- .where(eq(memoryItemConflicts.id, conflictId))
299
- .run();
300
-
301
- return getConflictById(conflictId);
302
- }
303
-
304
- export function applyConflictResolution(
305
- input: ApplyConflictResolutionInput,
306
- ): boolean {
307
- const conflict = getConflictById(input.conflictId);
308
- if (!conflict || conflict.status !== "pending_clarification") return false;
309
-
310
- const db = getDb();
311
- const now = Date.now();
312
- const existingItem = db
313
- .select()
314
- .from(memoryItems)
315
- .where(eq(memoryItems.id, conflict.existingItemId))
316
- .get();
317
- const candidateItem = db
318
- .select()
319
- .from(memoryItems)
320
- .where(eq(memoryItems.id, conflict.candidateItemId))
321
- .get();
322
-
323
- if (!existingItem || !candidateItem) {
324
- resolveConflict(conflict.id, {
325
- status: "dismissed",
326
- resolutionNote:
327
- input.resolutionNote ?? "Conflict items missing at resolution time.",
328
- });
329
- return false;
330
- }
331
-
332
- switch (input.resolution) {
333
- case "keep_existing": {
334
- db.update(memoryItems)
335
- .set({ status: "superseded", invalidAt: now })
336
- .where(eq(memoryItems.id, candidateItem.id))
337
- .run();
338
- resolveConflict(conflict.id, {
339
- status: "resolved_keep_existing",
340
- resolutionNote: input.resolutionNote ?? null,
341
- });
342
- return true;
343
- }
344
- case "keep_candidate": {
345
- db.update(memoryItems)
346
- .set({ status: "superseded", invalidAt: now })
347
- .where(eq(memoryItems.id, existingItem.id))
348
- .run();
349
- db.update(memoryItems)
350
- .set({ status: "active", validFrom: now })
351
- .where(eq(memoryItems.id, candidateItem.id))
352
- .run();
353
- resolveConflict(conflict.id, {
354
- status: "resolved_keep_candidate",
355
- resolutionNote: input.resolutionNote ?? null,
356
- });
357
- return true;
358
- }
359
- case "merge": {
360
- const mergedStatement = (input.mergedStatement ?? "").trim();
361
- const nextStatement =
362
- mergedStatement.length > 0 ? mergedStatement : candidateItem.statement;
363
- db.update(memoryItems)
364
- .set({
365
- statement: nextStatement,
366
- status: "active",
367
- invalidAt: null,
368
- lastSeenAt: Math.max(
369
- existingItem.lastSeenAt,
370
- candidateItem.lastSeenAt,
371
- now,
372
- ),
373
- confidence: clampUnitInterval(
374
- Math.max(existingItem.confidence, candidateItem.confidence),
375
- ),
376
- })
377
- .where(eq(memoryItems.id, existingItem.id))
378
- .run();
379
- db.update(memoryItems)
380
- .set({ status: "superseded", invalidAt: now })
381
- .where(eq(memoryItems.id, candidateItem.id))
382
- .run();
383
- enqueueMemoryJob("embed_item", { itemId: existingItem.id });
384
- resolveConflict(conflict.id, {
385
- status: "resolved_merge",
386
- resolutionNote: input.resolutionNote ?? null,
387
- });
388
- return true;
389
- }
390
- }
391
- }
392
-
393
- function toConflict(
394
- row: typeof memoryItemConflicts.$inferSelect,
395
- ): MemoryItemConflict {
396
- return {
397
- id: row.id,
398
- scopeId: row.scopeId,
399
- existingItemId: row.existingItemId,
400
- candidateItemId: row.candidateItemId,
401
- relationship: row.relationship,
402
- status: row.status as MemoryConflictStatus,
403
- clarificationQuestion: row.clarificationQuestion,
404
- resolutionNote: row.resolutionNote,
405
- lastAskedAt: row.lastAskedAt,
406
- resolvedAt: row.resolvedAt,
407
- createdAt: row.createdAt,
408
- updatedAt: row.updatedAt,
409
- };
410
- }