@vellumai/assistant 0.4.49 → 0.4.50

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 (239) hide show
  1. package/ARCHITECTURE.md +24 -33
  2. package/README.md +3 -3
  3. package/docs/architecture/memory.md +180 -119
  4. package/package.json +2 -2
  5. package/src/__tests__/agent-loop.test.ts +3 -1
  6. package/src/__tests__/anthropic-provider.test.ts +114 -23
  7. package/src/__tests__/approval-cascade.test.ts +1 -15
  8. package/src/__tests__/approval-routes-http.test.ts +2 -0
  9. package/src/__tests__/assistant-feature-flag-guard.test.ts +0 -23
  10. package/src/__tests__/canonical-guardian-store.test.ts +95 -0
  11. package/src/__tests__/checker.test.ts +13 -0
  12. package/src/__tests__/config-schema.test.ts +1 -68
  13. package/src/__tests__/context-memory-e2e.test.ts +11 -100
  14. package/src/__tests__/conversation-routes-guardian-reply.test.ts +8 -0
  15. package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
  16. package/src/__tests__/credential-security-e2e.test.ts +1 -0
  17. package/src/__tests__/credential-vault-unit.test.ts +4 -0
  18. package/src/__tests__/credential-vault.test.ts +13 -1
  19. package/src/__tests__/cu-unified-flow.test.ts +532 -0
  20. package/src/__tests__/date-context.test.ts +93 -77
  21. package/src/__tests__/deterministic-verification-control-plane.test.ts +64 -0
  22. package/src/__tests__/guardian-routing-invariants.test.ts +93 -0
  23. package/src/__tests__/history-repair.test.ts +245 -0
  24. package/src/__tests__/host-cu-proxy.test.ts +165 -3
  25. package/src/__tests__/http-user-message-parity.test.ts +1 -0
  26. package/src/__tests__/invite-redemption-service.test.ts +65 -1
  27. package/src/__tests__/keychain-broker-client.test.ts +4 -4
  28. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +56 -18
  29. package/src/__tests__/memory-lifecycle-e2e.test.ts +244 -387
  30. package/src/__tests__/memory-recall-quality.test.ts +244 -407
  31. package/src/__tests__/memory-regressions.experimental.test.ts +126 -101
  32. package/src/__tests__/memory-regressions.test.ts +477 -2841
  33. package/src/__tests__/memory-retrieval.benchmark.test.ts +33 -150
  34. package/src/__tests__/memory-upsert-concurrency.test.ts +5 -244
  35. package/src/__tests__/mime-builder.test.ts +28 -0
  36. package/src/__tests__/native-web-search.test.ts +1 -0
  37. package/src/__tests__/oauth-cli.test.ts +572 -5
  38. package/src/__tests__/oauth-store.test.ts +120 -6
  39. package/src/__tests__/qdrant-collection-migration.test.ts +53 -8
  40. package/src/__tests__/registry.test.ts +0 -1
  41. package/src/__tests__/relay-server.test.ts +46 -1
  42. package/src/__tests__/schedule-tools.test.ts +32 -0
  43. package/src/__tests__/script-proxy-certs.test.ts +1 -1
  44. package/src/__tests__/secret-onetime-send.test.ts +1 -0
  45. package/src/__tests__/secure-keys.test.ts +7 -2
  46. package/src/__tests__/send-endpoint-busy.test.ts +3 -0
  47. package/src/__tests__/session-abort-tool-results.test.ts +1 -14
  48. package/src/__tests__/session-agent-loop-overflow.test.ts +1583 -0
  49. package/src/__tests__/session-agent-loop.test.ts +19 -15
  50. package/src/__tests__/session-confirmation-signals.test.ts +1 -15
  51. package/src/__tests__/session-error.test.ts +124 -2
  52. package/src/__tests__/session-history-web-search.test.ts +918 -0
  53. package/src/__tests__/session-pre-run-repair.test.ts +1 -14
  54. package/src/__tests__/session-provider-retry-repair.test.ts +25 -28
  55. package/src/__tests__/session-queue.test.ts +37 -27
  56. package/src/__tests__/session-runtime-assembly.test.ts +54 -0
  57. package/src/__tests__/session-slash-known.test.ts +1 -15
  58. package/src/__tests__/session-slash-queue.test.ts +1 -15
  59. package/src/__tests__/session-slash-unknown.test.ts +1 -15
  60. package/src/__tests__/session-workspace-cache-state.test.ts +3 -33
  61. package/src/__tests__/session-workspace-injection.test.ts +3 -37
  62. package/src/__tests__/session-workspace-tool-tracking.test.ts +3 -37
  63. package/src/__tests__/skills-install-extract.test.ts +93 -0
  64. package/src/__tests__/skillssh-registry.test.ts +451 -0
  65. package/src/__tests__/trust-store.test.ts +15 -0
  66. package/src/__tests__/voice-invite-redemption.test.ts +32 -1
  67. package/src/agent/ax-tree-compaction.test.ts +51 -0
  68. package/src/agent/loop.ts +39 -12
  69. package/src/approvals/AGENTS.md +1 -1
  70. package/src/approvals/guardian-request-resolvers.ts +14 -2
  71. package/src/bundler/compiler-tools.ts +66 -2
  72. package/src/calls/call-domain.ts +132 -0
  73. package/src/calls/call-store.ts +6 -0
  74. package/src/calls/relay-server.ts +43 -5
  75. package/src/calls/relay-setup-router.ts +17 -1
  76. package/src/calls/twilio-config.ts +1 -1
  77. package/src/calls/types.ts +3 -1
  78. package/src/cli/commands/doctor.ts +4 -3
  79. package/src/cli/commands/mcp.ts +46 -59
  80. package/src/cli/commands/memory.ts +16 -165
  81. package/src/cli/commands/oauth/apps.ts +31 -2
  82. package/src/cli/commands/oauth/connections.ts +431 -97
  83. package/src/cli/commands/oauth/providers.ts +15 -1
  84. package/src/cli/commands/sessions.ts +5 -2
  85. package/src/cli/commands/skills.ts +173 -1
  86. package/src/cli/http-client.ts +0 -20
  87. package/src/cli/main-screen.tsx +2 -2
  88. package/src/cli/program.ts +5 -6
  89. package/src/cli.ts +4 -10
  90. package/src/config/bundled-skills/computer-use/TOOLS.json +1 -1
  91. package/src/config/bundled-skills/computer-use/tools/computer-use-observe.ts +12 -0
  92. package/src/config/bundled-tool-registry.ts +2 -5
  93. package/src/config/schema.ts +1 -12
  94. package/src/config/schemas/memory-lifecycle.ts +0 -9
  95. package/src/config/schemas/memory-processing.ts +0 -180
  96. package/src/config/schemas/memory-retrieval.ts +32 -104
  97. package/src/config/schemas/memory.ts +0 -10
  98. package/src/config/types.ts +0 -4
  99. package/src/context/window-manager.ts +4 -1
  100. package/src/daemon/config-watcher.ts +61 -3
  101. package/src/daemon/daemon-control.ts +1 -1
  102. package/src/daemon/date-context.ts +114 -31
  103. package/src/daemon/handlers/sessions.ts +18 -13
  104. package/src/daemon/handlers/skills.ts +20 -1
  105. package/src/daemon/history-repair.ts +72 -8
  106. package/src/daemon/host-cu-proxy.ts +55 -26
  107. package/src/daemon/lifecycle.ts +31 -3
  108. package/src/daemon/mcp-reload-service.ts +2 -2
  109. package/src/daemon/message-types/computer-use.ts +1 -12
  110. package/src/daemon/message-types/memory.ts +4 -16
  111. package/src/daemon/message-types/messages.ts +1 -0
  112. package/src/daemon/message-types/sessions.ts +4 -0
  113. package/src/daemon/server.ts +12 -1
  114. package/src/daemon/session-agent-loop-handlers.ts +38 -0
  115. package/src/daemon/session-agent-loop.ts +334 -48
  116. package/src/daemon/session-error.ts +89 -6
  117. package/src/daemon/session-history.ts +17 -7
  118. package/src/daemon/session-media-retry.ts +6 -2
  119. package/src/daemon/session-memory.ts +69 -149
  120. package/src/daemon/session-process.ts +10 -1
  121. package/src/daemon/session-runtime-assembly.ts +49 -19
  122. package/src/daemon/session-surfaces.ts +4 -1
  123. package/src/daemon/session-tool-setup.ts +7 -1
  124. package/src/daemon/session.ts +12 -2
  125. package/src/instrument.ts +61 -1
  126. package/src/memory/admin.ts +2 -191
  127. package/src/memory/canonical-guardian-store.ts +38 -2
  128. package/src/memory/conversation-crud.ts +0 -33
  129. package/src/memory/conversation-queries.ts +22 -3
  130. package/src/memory/db-init.ts +28 -0
  131. package/src/memory/embedding-backend.ts +84 -8
  132. package/src/memory/embedding-types.ts +9 -1
  133. package/src/memory/indexer.ts +7 -46
  134. package/src/memory/items-extractor.ts +274 -76
  135. package/src/memory/job-handlers/backfill.ts +2 -127
  136. package/src/memory/job-handlers/cleanup.ts +2 -16
  137. package/src/memory/job-handlers/extraction.ts +2 -138
  138. package/src/memory/job-handlers/index-maintenance.ts +1 -6
  139. package/src/memory/job-handlers/summarization.ts +3 -148
  140. package/src/memory/job-utils.ts +21 -59
  141. package/src/memory/jobs-store.ts +1 -159
  142. package/src/memory/jobs-worker.ts +9 -52
  143. package/src/memory/migrations/104-core-indexes.ts +3 -3
  144. package/src/memory/migrations/149-oauth-tables.ts +2 -0
  145. package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +98 -0
  146. package/src/memory/migrations/151-oauth-providers-ping-url.ts +11 -0
  147. package/src/memory/migrations/152-memory-item-supersession.ts +44 -0
  148. package/src/memory/migrations/153-drop-entity-tables.ts +15 -0
  149. package/src/memory/migrations/154-drop-fts.ts +20 -0
  150. package/src/memory/migrations/155-drop-conflicts.ts +7 -0
  151. package/src/memory/migrations/156-call-session-invite-metadata.ts +24 -0
  152. package/src/memory/migrations/index.ts +7 -0
  153. package/src/memory/qdrant-client.ts +148 -51
  154. package/src/memory/raw-query.ts +1 -1
  155. package/src/memory/retriever.test.ts +294 -273
  156. package/src/memory/retriever.ts +421 -645
  157. package/src/memory/schema/calls.ts +2 -0
  158. package/src/memory/schema/memory-core.ts +3 -48
  159. package/src/memory/schema/oauth.ts +2 -0
  160. package/src/memory/search/formatting.ts +263 -176
  161. package/src/memory/search/lexical.ts +1 -254
  162. package/src/memory/search/ranking.ts +0 -455
  163. package/src/memory/search/semantic.ts +100 -14
  164. package/src/memory/search/staleness.ts +47 -0
  165. package/src/memory/search/tier-classifier.ts +21 -0
  166. package/src/memory/search/types.ts +15 -77
  167. package/src/memory/task-memory-cleanup.ts +4 -6
  168. package/src/messaging/providers/gmail/mime-builder.ts +17 -7
  169. package/src/oauth/byo-connection.test.ts +8 -1
  170. package/src/oauth/oauth-store.ts +113 -27
  171. package/src/oauth/seed-providers.ts +6 -0
  172. package/src/oauth/token-persistence.ts +11 -3
  173. package/src/permissions/defaults.ts +1 -0
  174. package/src/permissions/trust-store.ts +23 -1
  175. package/src/playbooks/playbook-compiler.ts +1 -1
  176. package/src/prompts/system-prompt.ts +18 -2
  177. package/src/providers/anthropic/client.ts +56 -126
  178. package/src/providers/types.ts +7 -1
  179. package/src/runtime/AGENTS.md +9 -0
  180. package/src/runtime/auth/route-policy.ts +6 -3
  181. package/src/runtime/guardian-reply-router.ts +24 -22
  182. package/src/runtime/http-server.ts +2 -2
  183. package/src/runtime/invite-redemption-service.ts +19 -1
  184. package/src/runtime/invite-service.ts +25 -0
  185. package/src/runtime/pending-interactions.ts +2 -2
  186. package/src/runtime/routes/brain-graph-routes.ts +10 -90
  187. package/src/runtime/routes/conversation-routes.ts +9 -1
  188. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +21 -12
  189. package/src/runtime/routes/memory-item-routes.test.ts +754 -0
  190. package/src/runtime/routes/memory-item-routes.ts +503 -0
  191. package/src/runtime/routes/session-management-routes.ts +3 -3
  192. package/src/runtime/routes/settings-routes.ts +2 -2
  193. package/src/runtime/routes/trust-rules-routes.ts +14 -0
  194. package/src/runtime/routes/workspace-routes.ts +2 -1
  195. package/src/security/keychain-broker-client.ts +17 -4
  196. package/src/security/secure-keys.ts +25 -3
  197. package/src/security/token-manager.ts +36 -36
  198. package/src/skills/catalog-install.ts +74 -18
  199. package/src/skills/skillssh-registry.ts +503 -0
  200. package/src/tools/assets/search.ts +5 -1
  201. package/src/tools/computer-use/definitions.ts +0 -10
  202. package/src/tools/computer-use/registry.ts +1 -1
  203. package/src/tools/credentials/vault.ts +1 -3
  204. package/src/tools/memory/definitions.ts +4 -13
  205. package/src/tools/memory/handlers.test.ts +83 -103
  206. package/src/tools/memory/handlers.ts +50 -85
  207. package/src/tools/schedule/create.ts +8 -1
  208. package/src/tools/schedule/update.ts +8 -1
  209. package/src/tools/skills/load.ts +25 -2
  210. package/src/__tests__/clarification-resolver.test.ts +0 -193
  211. package/src/__tests__/conflict-intent-tokenization.test.ts +0 -160
  212. package/src/__tests__/conflict-policy.test.ts +0 -269
  213. package/src/__tests__/conflict-store.test.ts +0 -372
  214. package/src/__tests__/contradiction-checker.test.ts +0 -361
  215. package/src/__tests__/entity-extractor.test.ts +0 -211
  216. package/src/__tests__/entity-search.test.ts +0 -1117
  217. package/src/__tests__/profile-compiler.test.ts +0 -392
  218. package/src/__tests__/session-conflict-gate.test.ts +0 -1228
  219. package/src/__tests__/session-profile-injection.test.ts +0 -557
  220. package/src/config/bundled-skills/knowledge-graph/SKILL.md +0 -25
  221. package/src/config/bundled-skills/knowledge-graph/TOOLS.json +0 -66
  222. package/src/config/bundled-skills/knowledge-graph/tools/graph-query.ts +0 -211
  223. package/src/daemon/session-conflict-gate.ts +0 -167
  224. package/src/daemon/session-dynamic-profile.ts +0 -77
  225. package/src/memory/clarification-resolver.ts +0 -417
  226. package/src/memory/conflict-intent.ts +0 -205
  227. package/src/memory/conflict-policy.ts +0 -127
  228. package/src/memory/conflict-store.ts +0 -410
  229. package/src/memory/contradiction-checker.ts +0 -508
  230. package/src/memory/entity-extractor.ts +0 -535
  231. package/src/memory/format-recall.ts +0 -47
  232. package/src/memory/fts-reconciler.ts +0 -165
  233. package/src/memory/job-handlers/conflict.ts +0 -200
  234. package/src/memory/profile-compiler.ts +0 -195
  235. package/src/memory/recall-cache.ts +0 -117
  236. package/src/memory/search/entity.ts +0 -535
  237. package/src/memory/search/query-expansion.test.ts +0 -70
  238. package/src/memory/search/query-expansion.ts +0 -118
  239. package/src/runtime/routes/mcp-routes.ts +0 -20
@@ -31,6 +31,23 @@ mock.module("../util/logger.js", () => ({
31
31
  }),
32
32
  }));
33
33
 
34
+ // Dynamic Qdrant mock: tests can push results to be returned by searchWithFilter/hybridSearch
35
+ let mockQdrantResults: Array<{
36
+ id: string;
37
+ score: number;
38
+ payload: Record<string, unknown>;
39
+ }> = [];
40
+
41
+ mock.module("../memory/qdrant-client.js", () => ({
42
+ getQdrantClient: () => ({
43
+ searchWithFilter: async () => mockQdrantResults,
44
+ hybridSearch: async () => mockQdrantResults,
45
+ upsert: async () => {},
46
+ deletePoints: async () => {},
47
+ }),
48
+ initQdrantClient: () => {},
49
+ }));
50
+
34
51
  import { eq } from "drizzle-orm";
35
52
 
36
53
  import { DEFAULT_CONFIG } from "../config/defaults.js";
@@ -55,7 +72,6 @@ mock.module("../config/loader.js", () => ({
55
72
  }));
56
73
  import { getDb, initializeDb, resetDb } from "../memory/db.js";
57
74
  import { indexMessageNow } from "../memory/indexer.js";
58
- import { vectorToBlob } from "../memory/job-utils.js";
59
75
  import { enqueueMemoryJob } from "../memory/jobs-store.js";
60
76
  import {
61
77
  resetCleanupScheduleThrottle,
@@ -65,7 +81,6 @@ import {
65
81
  import { buildMemoryRecall } from "../memory/retriever.js";
66
82
  import {
67
83
  conversations,
68
- memoryEmbeddings,
69
84
  memoryItems,
70
85
  memoryItemSources,
71
86
  memoryJobs,
@@ -80,15 +95,11 @@ describe("Memory regressions (experimental)", () => {
80
95
 
81
96
  beforeEach(() => {
82
97
  const db = getDb();
83
- db.run("DELETE FROM memory_item_conflicts");
84
- db.run("DELETE FROM memory_item_entities");
85
- db.run("DELETE FROM memory_entity_relations");
86
- db.run("DELETE FROM memory_entities");
87
98
  db.run("DELETE FROM memory_item_sources");
88
99
  db.run("DELETE FROM memory_embeddings");
89
- db.run("DELETE FROM memory_summaries");
90
100
  db.run("DELETE FROM memory_items");
91
- db.run("DELETE FROM memory_segment_fts");
101
+
102
+ db.run("DELETE FROM memory_summaries");
92
103
  db.run("DELETE FROM memory_segments");
93
104
  db.run("DELETE FROM messages");
94
105
  db.run("DELETE FROM conversations");
@@ -96,6 +107,7 @@ describe("Memory regressions (experimental)", () => {
96
107
  db.run("DELETE FROM memory_checkpoints");
97
108
  resetCleanupScheduleThrottle();
98
109
  resetStaleSweepThrottle();
110
+ mockQdrantResults = [];
99
111
  });
100
112
 
101
113
  afterAll(() => {
@@ -111,8 +123,11 @@ describe("Memory regressions (experimental)", () => {
111
123
  run: () => Promise<T>,
112
124
  ): Promise<T> {
113
125
  const originalFetch = globalThis.fetch;
126
+ // Return 384-dim vectors to match the Qdrant collection's expected size
127
+ const mockVector = new Array(384).fill(0);
128
+ mockVector[0] = 1;
114
129
  globalThis.fetch = (async () =>
115
- new Response(JSON.stringify({ data: [{ embedding: [1, 0, 0] }] }), {
130
+ new Response(JSON.stringify({ data: [{ embedding: mockVector }] }), {
116
131
  status: 200,
117
132
  headers: { "content-type": "application/json" },
118
133
  })) as unknown as typeof globalThis.fetch;
@@ -135,8 +150,6 @@ describe("Memory regressions (experimental)", () => {
135
150
  },
136
151
  retrieval: {
137
152
  ...DEFAULT_CONFIG.memory.retrieval,
138
- lexicalTopK: 0,
139
- semanticTopK: 10,
140
153
  maxInjectTokens: 2000,
141
154
  },
142
155
  },
@@ -184,7 +197,7 @@ describe("Memory regressions (experimental)", () => {
184
197
  .values([
185
198
  {
186
199
  id: "item-semantic-old",
187
- kind: "fact",
200
+ kind: "identity",
188
201
  subject: "timezone",
189
202
  statement: "User timezone is PST",
190
203
  status: "active",
@@ -196,7 +209,7 @@ describe("Memory regressions (experimental)", () => {
196
209
  },
197
210
  {
198
211
  id: "item-semantic-current",
199
- kind: "fact",
212
+ kind: "identity",
200
213
  subject: "timezone",
201
214
  statement: "User timezone is PST (current turn)",
202
215
  status: "active",
@@ -224,32 +237,35 @@ describe("Memory regressions (experimental)", () => {
224
237
  },
225
238
  ])
226
239
  .run();
227
- db.insert(memoryEmbeddings)
228
- .values([
229
- {
230
- id: "emb-semantic-old",
231
- targetType: "item",
232
- targetId: "item-semantic-old",
233
- provider: "ollama",
234
- model: DEFAULT_CONFIG.memory.embeddings.ollamaModel,
235
- dimensions: 3,
236
- vectorBlob: vectorToBlob([1, 0, 0]),
237
- createdAt: now,
238
- updatedAt: now,
240
+ // Mock Qdrant to return both items as search results
241
+ mockQdrantResults = [
242
+ {
243
+ id: "emb-semantic-old",
244
+ score: 0.95,
245
+ payload: {
246
+ target_type: "item",
247
+ target_id: "item-semantic-old",
248
+ text: "User timezone is PST",
249
+ kind: "identity",
250
+ status: "active",
251
+ created_at: now - 10_000,
252
+ last_seen_at: now - 10_000,
239
253
  },
240
- {
241
- id: "emb-semantic-current",
242
- targetType: "item",
243
- targetId: "item-semantic-current",
244
- provider: "ollama",
245
- model: DEFAULT_CONFIG.memory.embeddings.ollamaModel,
246
- dimensions: 3,
247
- vectorBlob: vectorToBlob([1, 0, 0]),
248
- createdAt: now,
249
- updatedAt: now,
254
+ },
255
+ {
256
+ id: "emb-semantic-current",
257
+ score: 0.93,
258
+ payload: {
259
+ target_type: "item",
260
+ target_id: "item-semantic-current",
261
+ text: "User timezone is PST (current turn)",
262
+ kind: "identity",
263
+ status: "active",
264
+ created_at: now,
265
+ last_seen_at: now,
250
266
  },
251
- ])
252
- .run();
267
+ },
268
+ ];
253
269
 
254
270
  const recall = await withMockOllamaQueryEmbedding(() =>
255
271
  buildMemoryRecall(
@@ -294,7 +310,7 @@ describe("Memory regressions (experimental)", () => {
294
310
  .values([
295
311
  {
296
312
  id: "item-semantic-with-evidence",
297
- kind: "fact",
313
+ kind: "identity",
298
314
  subject: "timezone",
299
315
  statement: "User timezone is PST",
300
316
  status: "active",
@@ -306,7 +322,7 @@ describe("Memory regressions (experimental)", () => {
306
322
  },
307
323
  {
308
324
  id: "item-semantic-orphan",
309
- kind: "fact",
325
+ kind: "identity",
310
326
  subject: "timezone",
311
327
  statement: "Stale orphan fact",
312
328
  status: "active",
@@ -326,32 +342,35 @@ describe("Memory regressions (experimental)", () => {
326
342
  createdAt: now,
327
343
  })
328
344
  .run();
329
- db.insert(memoryEmbeddings)
330
- .values([
331
- {
332
- id: "emb-semantic-with-evidence",
333
- targetType: "item",
334
- targetId: "item-semantic-with-evidence",
335
- provider: "ollama",
336
- model: DEFAULT_CONFIG.memory.embeddings.ollamaModel,
337
- dimensions: 3,
338
- vectorBlob: vectorToBlob([1, 0, 0]),
339
- createdAt: now,
340
- updatedAt: now,
345
+ // Mock Qdrant to return both items as search results
346
+ mockQdrantResults = [
347
+ {
348
+ id: "emb-semantic-with-evidence",
349
+ score: 0.95,
350
+ payload: {
351
+ target_type: "item",
352
+ target_id: "item-semantic-with-evidence",
353
+ text: "User timezone is PST",
354
+ kind: "identity",
355
+ status: "active",
356
+ created_at: now,
357
+ last_seen_at: now,
341
358
  },
342
- {
343
- id: "emb-semantic-orphan",
344
- targetType: "item",
345
- targetId: "item-semantic-orphan",
346
- provider: "ollama",
347
- model: DEFAULT_CONFIG.memory.embeddings.ollamaModel,
348
- dimensions: 3,
349
- vectorBlob: vectorToBlob([1, 0, 0]),
350
- createdAt: now,
351
- updatedAt: now,
359
+ },
360
+ {
361
+ id: "emb-semantic-orphan",
362
+ score: 0.9,
363
+ payload: {
364
+ target_type: "item",
365
+ target_id: "item-semantic-orphan",
366
+ text: "Stale orphan fact",
367
+ kind: "identity",
368
+ status: "active",
369
+ created_at: now,
370
+ last_seen_at: now,
352
371
  },
353
- ])
354
- .run();
372
+ },
373
+ ];
355
374
 
356
375
  const recall = await withMockOllamaQueryEmbedding(() =>
357
376
  buildMemoryRecall(
@@ -420,48 +439,51 @@ describe("Memory regressions (experimental)", () => {
420
439
  },
421
440
  ])
422
441
  .run();
423
- db.insert(memoryEmbeddings)
424
- .values([
425
- {
426
- id: "emb-summary-semantic-conversation",
427
- targetType: "summary",
428
- targetId: "summary-semantic-conversation",
429
- provider: "ollama",
430
- model: DEFAULT_CONFIG.memory.embeddings.ollamaModel,
431
- dimensions: 3,
432
- vectorBlob: vectorToBlob([1, 0, 0]),
433
- createdAt: now,
434
- updatedAt: now,
442
+ // Mock Qdrant to return both summaries as search results.
443
+ // The new pipeline does not exclude conversation summaries based on
444
+ // time overlap with excluded messages — that was old-pipeline behavior.
445
+ // Both summaries pass through; we verify the pipeline runs correctly.
446
+ mockQdrantResults = [
447
+ {
448
+ id: "emb-summary-semantic-conversation",
449
+ score: 0.95,
450
+ payload: {
451
+ target_type: "summary",
452
+ target_id: "summary-semantic-conversation",
453
+ text: "[conversation] Conversation summary containing current turn details",
454
+ kind: "conversation",
455
+ created_at: now,
456
+ last_seen_at: now,
435
457
  },
436
- {
437
- id: "emb-summary-semantic-weekly",
438
- targetType: "summary",
439
- targetId: "summary-semantic-weekly",
440
- provider: "ollama",
441
- model: DEFAULT_CONFIG.memory.embeddings.ollamaModel,
442
- dimensions: 3,
443
- vectorBlob: vectorToBlob([1, 0, 0]),
444
- createdAt: now,
445
- updatedAt: now,
458
+ },
459
+ {
460
+ id: "emb-summary-semantic-weekly",
461
+ score: 0.9,
462
+ payload: {
463
+ target_type: "summary",
464
+ target_id: "summary-semantic-weekly",
465
+ text: "[weekly_global] Weekly summary that should remain eligible",
466
+ kind: "global",
467
+ created_at: now,
468
+ last_seen_at: now,
446
469
  },
447
- ])
448
- .run();
470
+ },
471
+ ];
449
472
 
450
473
  const recall = await withMockOllamaQueryEmbedding(() =>
451
474
  buildMemoryRecall("summary", conversationId, semanticRecallConfig(), {
452
475
  excludeMessageIds: ["msg-semantic-summary-excluded"],
453
476
  }),
454
477
  );
455
- expect(recall.semanticHits).toBe(1);
456
- expect(recall.injectedText).not.toContain(
457
- "Conversation summary containing current turn details",
458
- );
478
+ // Both summaries are returned from Qdrant and both pass post-filtering.
479
+ // Verify the pipeline completes successfully with semantic hits.
480
+ expect(recall.semanticHits).toBe(2);
459
481
  expect(recall.injectedText).toContain(
460
482
  "Weekly summary that should remain eligible",
461
483
  );
462
484
  });
463
485
 
464
- test("indexing no longer enqueues segment embedding jobs", () => {
486
+ test("indexing enqueues embed_segment, extract_items, and build_conversation_summary for user messages", () => {
465
487
  const db = getDb();
466
488
  const createdAt = 2_000;
467
489
  db.insert(conversations)
@@ -502,14 +524,15 @@ describe("Memory regressions (experimental)", () => {
502
524
  },
503
525
  DEFAULT_CONFIG.memory,
504
526
  );
505
- expect(result.enqueuedJobs).toBe(2);
527
+ // embed_segment (1 segment) + extract_items + build_conversation_summary = 3
528
+ expect(result.enqueuedJobs).toBe(3);
506
529
 
507
530
  const embedSegmentJobs = db
508
531
  .select()
509
532
  .from(memoryJobs)
510
533
  .where(eq(memoryJobs.type, "embed_segment"))
511
534
  .all();
512
- expect(embedSegmentJobs).toHaveLength(0);
535
+ expect(embedSegmentJobs).toHaveLength(1);
513
536
  });
514
537
 
515
538
  test("indexing skips durable item extraction for assistant messages when extractFromAssistant is false", () => {
@@ -561,7 +584,9 @@ describe("Memory regressions (experimental)", () => {
561
584
  },
562
585
  memoryConfig,
563
586
  );
564
- expect(result.enqueuedJobs).toBe(1);
587
+ // embed_segment (1 segment) + build_conversation_summary = 2
588
+ // (extract_items is skipped for assistant messages when extractFromAssistant=false)
589
+ expect(result.enqueuedJobs).toBe(2);
565
590
 
566
591
  const extractionJobs = db
567
592
  .select()
@@ -571,24 +596,24 @@ describe("Memory regressions (experimental)", () => {
571
596
  expect(extractionJobs).toHaveLength(0);
572
597
  });
573
598
 
574
- test("embed jobs are skipped (not failed) when no embedding backend is configured", async () => {
599
+ test("embed jobs complete successfully when backend and Qdrant mock are available", async () => {
575
600
  const db = getDb();
576
601
  const now = 3_000;
577
602
  db.insert(memoryItems)
578
603
  .values({
579
- id: "item-no-backend",
580
- kind: "fact",
604
+ id: "item-embed-test",
605
+ kind: "identity",
581
606
  subject: "backend",
582
- statement: "No embedding backend configured in test",
607
+ statement: "Embedding pipeline test item",
583
608
  status: "active",
584
609
  confidence: 0.8,
585
- fingerprint: "item-no-backend-fingerprint",
610
+ fingerprint: "item-embed-test-fingerprint",
586
611
  firstSeenAt: now,
587
612
  lastSeenAt: now,
588
613
  lastUsedAt: null,
589
614
  })
590
615
  .run();
591
- const jobId = enqueueMemoryJob("embed_item", { itemId: "item-no-backend" });
616
+ const jobId = enqueueMemoryJob("embed_item", { itemId: "item-embed-test" });
592
617
 
593
618
  const processed = await runMemoryJobsOnce();
594
619
  expect(processed).toBe(1);