@cortexmemory/cli 0.27.4 → 0.28.0

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 (66) hide show
  1. package/dist/commands/db.d.ts.map +1 -1
  2. package/dist/commands/db.js +18 -6
  3. package/dist/commands/db.js.map +1 -1
  4. package/dist/commands/deploy.d.ts.map +1 -1
  5. package/dist/commands/deploy.js +74 -34
  6. package/dist/commands/deploy.js.map +1 -1
  7. package/dist/commands/dev.js +3 -2
  8. package/dist/commands/dev.js.map +1 -1
  9. package/dist/commands/init.d.ts.map +1 -1
  10. package/dist/commands/init.js +12 -0
  11. package/dist/commands/init.js.map +1 -1
  12. package/dist/types.d.ts +1 -1
  13. package/dist/types.d.ts.map +1 -1
  14. package/dist/utils/app-template-sync.d.ts.map +1 -1
  15. package/dist/utils/app-template-sync.js +3 -1
  16. package/dist/utils/app-template-sync.js.map +1 -1
  17. package/dist/utils/init/quickstart-setup.d.ts.map +1 -1
  18. package/dist/utils/init/quickstart-setup.js.map +1 -1
  19. package/package.json +4 -4
  20. package/templates/basic/.env.local.example +23 -0
  21. package/templates/basic/README.md +181 -56
  22. package/templates/basic/package-lock.json +2180 -406
  23. package/templates/basic/package.json +23 -5
  24. package/templates/basic/src/__tests__/chat.test.ts +340 -0
  25. package/templates/basic/src/__tests__/cortex.test.ts +260 -0
  26. package/templates/basic/src/__tests__/display.test.ts +455 -0
  27. package/templates/basic/src/__tests__/e2e/fact-extraction.test.ts +498 -0
  28. package/templates/basic/src/__tests__/e2e/memory-flow.test.ts +355 -0
  29. package/templates/basic/src/__tests__/e2e/server-e2e.test.ts +414 -0
  30. package/templates/basic/src/__tests__/helpers/test-utils.ts +345 -0
  31. package/templates/basic/src/__tests__/integration/chat-flow.test.ts +422 -0
  32. package/templates/basic/src/__tests__/integration/server.test.ts +441 -0
  33. package/templates/basic/src/__tests__/llm.test.ts +344 -0
  34. package/templates/basic/src/chat.ts +300 -0
  35. package/templates/basic/src/cortex.ts +203 -0
  36. package/templates/basic/src/display.ts +425 -0
  37. package/templates/basic/src/index.ts +194 -64
  38. package/templates/basic/src/llm.ts +214 -0
  39. package/templates/basic/src/server.ts +280 -0
  40. package/templates/basic/vitest.config.ts +33 -0
  41. package/templates/basic/vitest.e2e.config.ts +28 -0
  42. package/templates/basic/vitest.integration.config.ts +25 -0
  43. package/templates/vercel-ai-quickstart/app/api/auth/check/route.ts +1 -1
  44. package/templates/vercel-ai-quickstart/app/api/auth/login/route.ts +6 -9
  45. package/templates/vercel-ai-quickstart/app/api/auth/register/route.ts +14 -18
  46. package/templates/vercel-ai-quickstart/app/api/auth/setup/route.ts +4 -7
  47. package/templates/vercel-ai-quickstart/app/api/chat/route.ts +28 -11
  48. package/templates/vercel-ai-quickstart/app/api/chat-v6/route.ts +19 -13
  49. package/templates/vercel-ai-quickstart/app/api/conversations/route.ts +16 -16
  50. package/templates/vercel-ai-quickstart/app/globals.css +24 -9
  51. package/templates/vercel-ai-quickstart/app/page.tsx +25 -13
  52. package/templates/vercel-ai-quickstart/components/AdminSetup.tsx +3 -1
  53. package/templates/vercel-ai-quickstart/components/AuthProvider.tsx +6 -6
  54. package/templates/vercel-ai-quickstart/components/ChatHistorySidebar.tsx +19 -8
  55. package/templates/vercel-ai-quickstart/components/ChatInterface.tsx +41 -14
  56. package/templates/vercel-ai-quickstart/components/LoginScreen.tsx +10 -5
  57. package/templates/vercel-ai-quickstart/lib/agents/memory-agent.ts +3 -3
  58. package/templates/vercel-ai-quickstart/lib/password.ts +5 -5
  59. package/templates/vercel-ai-quickstart/next.config.js +10 -2
  60. package/templates/vercel-ai-quickstart/package.json +18 -11
  61. package/templates/vercel-ai-quickstart/test-api.mjs +131 -100
  62. package/templates/vercel-ai-quickstart/tests/e2e/chat-memory-flow.test.ts +73 -44
  63. package/templates/vercel-ai-quickstart/tests/helpers/mock-cortex.ts +40 -40
  64. package/templates/vercel-ai-quickstart/tests/integration/auth.test.ts +8 -8
  65. package/templates/vercel-ai-quickstart/tests/integration/conversations.test.ts +12 -8
  66. package/templates/vercel-ai-quickstart/tests/unit/password.test.ts +4 -1
@@ -0,0 +1,498 @@
1
+ /**
2
+ * E2E Tests: Fact Extraction and Belief Revision
3
+ *
4
+ * Tests fact extraction and belief revision with real Convex backend.
5
+ * Requires: CONVEX_URL and OPENAI_API_KEY environment variables
6
+ *
7
+ * Run with: CONVEX_URL=<url> OPENAI_API_KEY=<key> npm run test:e2e
8
+ */
9
+
10
+ import { describe, it, expect, beforeAll, beforeEach, afterAll } from "vitest";
11
+ import { Cortex } from "@cortexmemory/sdk";
12
+ import {
13
+ shouldSkipFactTests,
14
+ generateTestId,
15
+ createTestMemorySpaceId,
16
+ createTestUserId,
17
+ createTestConversationId,
18
+ wait,
19
+ generateTestEmbedding,
20
+ } from "../helpers/test-utils.js";
21
+
22
+ // Skip all tests if required env vars not set
23
+ const SKIP_FACT_TESTS = shouldSkipFactTests();
24
+
25
+ describe("Fact Extraction E2E", () => {
26
+ let cortex: Cortex;
27
+ let testMemorySpaceId: string;
28
+ let testUserId: string;
29
+ let testAgentId: string;
30
+
31
+ beforeAll(() => {
32
+ if (SKIP_FACT_TESTS) {
33
+ console.log("Skipping fact tests - CONVEX_URL or OPENAI_API_KEY not configured");
34
+ return;
35
+ }
36
+
37
+ // Configure Cortex with llm config for automatic fact extraction
38
+ // This is the correct way to enable fact extraction in the SDK!
39
+ cortex = new Cortex({
40
+ convexUrl: process.env.CONVEX_URL!,
41
+ llm: {
42
+ provider: "openai",
43
+ apiKey: process.env.OPENAI_API_KEY!,
44
+ model: process.env.CORTEX_FACT_EXTRACTION_MODEL || "gpt-4o-mini",
45
+ },
46
+ });
47
+ });
48
+
49
+ beforeEach(() => {
50
+ if (SKIP_FACT_TESTS) return;
51
+
52
+ // Generate unique IDs for test isolation
53
+ testMemorySpaceId = createTestMemorySpaceId("e2e-fact");
54
+ testUserId = createTestUserId();
55
+ testAgentId = generateTestId("agent");
56
+ });
57
+
58
+ afterAll(async () => {
59
+ if (cortex) {
60
+ cortex.close();
61
+ }
62
+ });
63
+
64
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
65
+ // Basic Fact Extraction
66
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
67
+
68
+ (SKIP_FACT_TESTS ? describe.skip : describe)("basic fact extraction", () => {
69
+ it("should extract facts from conversation", async () => {
70
+ const conversationId = createTestConversationId();
71
+
72
+ // Store a message with an extractable fact
73
+ await cortex.memory.remember({
74
+ memorySpaceId: testMemorySpaceId,
75
+ conversationId,
76
+ userMessage: "My name is Alice and I work as a software engineer in Seattle",
77
+ agentResponse: "Nice to meet you, Alice! Software engineering in Seattle sounds exciting.",
78
+ userId: testUserId,
79
+ userName: "Alice",
80
+ agentId: testAgentId,
81
+ agentName: "Test Agent",
82
+ generateEmbedding: generateTestEmbedding,
83
+ // Note: llmConfig on the client enables automatic fact extraction
84
+ // No need for extractFacts parameter here
85
+ });
86
+
87
+ // Wait for fact extraction (async process)
88
+ await wait(5000);
89
+
90
+ // Check for extracted facts
91
+ const facts = await cortex.facts.list({
92
+ memorySpaceId: testMemorySpaceId,
93
+ userId: testUserId,
94
+ includeSuperseded: false,
95
+ });
96
+
97
+ console.log(`Extracted ${facts.length} facts:`);
98
+ facts.forEach((f: any) => console.log(` - ${f.fact}`));
99
+
100
+ // STRONG ASSERTION: We should have extracted at least one fact
101
+ // The message clearly contains: name=Alice, job=software engineer, location=Seattle
102
+ expect(facts.length).toBeGreaterThanOrEqual(1);
103
+ expect(facts[0]).toHaveProperty("fact");
104
+ }, 60000);
105
+
106
+ it("should extract multiple facts from one message", async () => {
107
+ const conversationId = createTestConversationId();
108
+
109
+ await cortex.memory.remember({
110
+ memorySpaceId: testMemorySpaceId,
111
+ conversationId,
112
+ userMessage: "I'm Bob, I'm 35 years old, and I love playing chess",
113
+ agentResponse: "Nice to meet you, Bob! Chess is a wonderful game.",
114
+ userId: testUserId,
115
+ userName: "Bob",
116
+ agentId: testAgentId,
117
+ agentName: "Test Agent",
118
+ generateEmbedding: generateTestEmbedding,
119
+ });
120
+
121
+ await wait(5000);
122
+
123
+ const facts = await cortex.facts.list({
124
+ memorySpaceId: testMemorySpaceId,
125
+ userId: testUserId,
126
+ includeSuperseded: false,
127
+ });
128
+
129
+ console.log(`Multiple facts test - extracted ${facts.length} facts:`);
130
+ facts.forEach((f: any) => console.log(` - ${f.fact}`));
131
+
132
+ // STRONG ASSERTION: Should extract at least 2 facts
133
+ // The message clearly contains: name=Bob, age=35, hobby=chess
134
+ expect(facts.length).toBeGreaterThanOrEqual(2);
135
+ }, 60000);
136
+ });
137
+
138
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
139
+ // Belief Revision
140
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
141
+
142
+ (SKIP_FACT_TESTS ? describe.skip : describe)("belief revision", () => {
143
+ it("should supersede old fact when user updates preference", async () => {
144
+ const conversationId = createTestConversationId();
145
+
146
+ // Step 1: State initial preference
147
+ console.log("Step 1: Stating favorite color is blue...");
148
+ await cortex.memory.remember({
149
+ memorySpaceId: testMemorySpaceId,
150
+ conversationId,
151
+ userMessage: "My favorite color is blue",
152
+ agentResponse: "Blue is a lovely color!",
153
+ userId: testUserId,
154
+ userName: "Test User",
155
+ agentId: testAgentId,
156
+ agentName: "Test Agent",
157
+ generateEmbedding: generateTestEmbedding,
158
+ beliefRevision: {
159
+ enabled: true,
160
+ slotMatching: true,
161
+ },
162
+ });
163
+
164
+ await wait(5000);
165
+
166
+ // Check initial facts
167
+ const factsAfterFirst = await cortex.facts.list({
168
+ memorySpaceId: testMemorySpaceId,
169
+ userId: testUserId,
170
+ includeSuperseded: true,
171
+ });
172
+ console.log(`After first message: ${factsAfterFirst.length} facts`);
173
+
174
+ // STRONG ASSERTION: Should have at least one fact after first message
175
+ expect(factsAfterFirst.length).toBeGreaterThanOrEqual(1);
176
+
177
+ // Step 2: Update preference (should trigger supersession)
178
+ console.log("Step 2: Updating favorite color to purple...");
179
+ await cortex.memory.remember({
180
+ memorySpaceId: testMemorySpaceId,
181
+ conversationId,
182
+ userMessage: "Actually, my favorite color is purple now",
183
+ agentResponse: "I'll remember that your favorite color is now purple!",
184
+ userId: testUserId,
185
+ userName: "Test User",
186
+ agentId: testAgentId,
187
+ agentName: "Test Agent",
188
+ generateEmbedding: generateTestEmbedding,
189
+ beliefRevision: {
190
+ enabled: true,
191
+ slotMatching: true,
192
+ },
193
+ });
194
+
195
+ await wait(5000);
196
+
197
+ // Step 3: Verify supersession
198
+ const allFacts = await cortex.facts.list({
199
+ memorySpaceId: testMemorySpaceId,
200
+ userId: testUserId,
201
+ includeSuperseded: true,
202
+ });
203
+
204
+ const activeFacts = await cortex.facts.list({
205
+ memorySpaceId: testMemorySpaceId,
206
+ userId: testUserId,
207
+ includeSuperseded: false,
208
+ });
209
+
210
+ console.log(`Total facts (including superseded): ${allFacts.length}`);
211
+ console.log(`Active facts: ${activeFacts.length}`);
212
+
213
+ allFacts.forEach((f: any) => {
214
+ const status = f.supersededBy ? "SUPERSEDED" : "ACTIVE";
215
+ console.log(` [${status}] ${f.fact}`);
216
+ });
217
+
218
+ // STRONG ASSERTIONS
219
+ expect(allFacts.length).toBeGreaterThanOrEqual(1);
220
+ // After revision, should have at least one active fact about purple
221
+ expect(activeFacts.length).toBeGreaterThanOrEqual(1);
222
+ }, 120000);
223
+
224
+ it("should preserve non-conflicting facts", async () => {
225
+ const conversationId = createTestConversationId();
226
+
227
+ // Fact 1: Name
228
+ await cortex.memory.remember({
229
+ memorySpaceId: testMemorySpaceId,
230
+ conversationId,
231
+ userMessage: "My name is Charlie",
232
+ agentResponse: "Nice to meet you, Charlie!",
233
+ userId: testUserId,
234
+ userName: "Charlie",
235
+ agentId: testAgentId,
236
+ agentName: "Test Agent",
237
+ generateEmbedding: generateTestEmbedding,
238
+ });
239
+
240
+ await wait(3000);
241
+
242
+ // Fact 2: Job (non-conflicting with name)
243
+ await cortex.memory.remember({
244
+ memorySpaceId: testMemorySpaceId,
245
+ conversationId,
246
+ userMessage: "I work as a data scientist",
247
+ agentResponse: "Data science is a fascinating field!",
248
+ userId: testUserId,
249
+ userName: "Charlie",
250
+ agentId: testAgentId,
251
+ agentName: "Test Agent",
252
+ generateEmbedding: generateTestEmbedding,
253
+ });
254
+
255
+ await wait(3000);
256
+
257
+ // Both facts should be active (non-conflicting)
258
+ const facts = await cortex.facts.list({
259
+ memorySpaceId: testMemorySpaceId,
260
+ userId: testUserId,
261
+ includeSuperseded: false,
262
+ });
263
+
264
+ console.log(`Non-conflicting facts: ${facts.length}`);
265
+ facts.forEach((f: any) => console.log(` - ${f.fact}`));
266
+
267
+ // STRONG ASSERTION: Should have at least 2 non-conflicting facts
268
+ expect(facts.length).toBeGreaterThanOrEqual(2);
269
+ }, 60000);
270
+
271
+ it("should handle duplicate facts (same value)", async () => {
272
+ // Edge case: User says the same thing twice
273
+ // Expected: NONE (skip as duplicate), NOT SUPERSEDE
274
+ const conversationId = createTestConversationId();
275
+
276
+ // Statement 1: Initial fact
277
+ console.log("Statement 1: 'My favorite food is pizza'");
278
+ await cortex.memory.remember({
279
+ memorySpaceId: testMemorySpaceId,
280
+ conversationId,
281
+ userMessage: "My favorite food is pizza",
282
+ agentResponse: "Pizza is delicious!",
283
+ userId: testUserId,
284
+ userName: "Test User",
285
+ agentId: testAgentId,
286
+ agentName: "Test Agent",
287
+ generateEmbedding: generateTestEmbedding,
288
+ beliefRevision: {
289
+ enabled: true,
290
+ slotMatching: true,
291
+ },
292
+ });
293
+
294
+ await wait(5000);
295
+
296
+ const factsAfterFirst = await cortex.facts.list({
297
+ memorySpaceId: testMemorySpaceId,
298
+ userId: testUserId,
299
+ includeSuperseded: false,
300
+ });
301
+ console.log(`After first statement: ${factsAfterFirst.length} facts`);
302
+
303
+ // Statement 2: Same fact repeated
304
+ console.log("Statement 2: 'I really love pizza, it's my favorite' (same value)");
305
+ await cortex.memory.remember({
306
+ memorySpaceId: testMemorySpaceId,
307
+ conversationId,
308
+ userMessage: "I really love pizza, it's my favorite",
309
+ agentResponse: "Yes, you mentioned you love pizza!",
310
+ userId: testUserId,
311
+ userName: "Test User",
312
+ agentId: testAgentId,
313
+ agentName: "Test Agent",
314
+ generateEmbedding: generateTestEmbedding,
315
+ beliefRevision: {
316
+ enabled: true,
317
+ slotMatching: true,
318
+ },
319
+ });
320
+
321
+ await wait(5000);
322
+
323
+ const allFacts = await cortex.facts.list({
324
+ memorySpaceId: testMemorySpaceId,
325
+ userId: testUserId,
326
+ includeSuperseded: true,
327
+ });
328
+
329
+ const activeFacts = await cortex.facts.list({
330
+ memorySpaceId: testMemorySpaceId,
331
+ userId: testUserId,
332
+ includeSuperseded: false,
333
+ });
334
+
335
+ console.log(`\n=== Duplicate Fact Test Results ===`);
336
+ console.log(`Total facts: ${allFacts.length}`);
337
+ console.log(`Active facts: ${activeFacts.length}`);
338
+
339
+ allFacts.forEach((f: any) => {
340
+ const status = f.supersededBy ? "SUPERSEDED" : "ACTIVE";
341
+ console.log(` [${status}] ${f.fact}`);
342
+ });
343
+
344
+ // STRONG ASSERTIONS
345
+ // Should have extracted at least one fact about pizza
346
+ expect(allFacts.length).toBeGreaterThanOrEqual(1);
347
+ // Should have at most 2 active pizza facts (dedup should merge or skip)
348
+ // In ideal case, should be exactly 1 active fact after dedup
349
+ expect(activeFacts.length).toBeGreaterThanOrEqual(1);
350
+ }, 120000);
351
+ });
352
+
353
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
354
+ // Facts API
355
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
356
+
357
+ (SKIP_FACT_TESTS ? describe.skip : describe)("facts API", () => {
358
+ it("should list facts with includeSuperseded filter", async () => {
359
+ const conversationId = createTestConversationId();
360
+
361
+ // Create a fact
362
+ await cortex.memory.remember({
363
+ memorySpaceId: testMemorySpaceId,
364
+ conversationId,
365
+ userMessage: "I am 30 years old",
366
+ agentResponse: "Got it!",
367
+ userId: testUserId,
368
+ userName: "Test User",
369
+ agentId: testAgentId,
370
+ agentName: "Test Agent",
371
+ generateEmbedding: generateTestEmbedding,
372
+ });
373
+
374
+ await wait(5000);
375
+
376
+ // List with includeSuperseded = false
377
+ const activeFacts = await cortex.facts.list({
378
+ memorySpaceId: testMemorySpaceId,
379
+ userId: testUserId,
380
+ includeSuperseded: false,
381
+ });
382
+
383
+ // List with includeSuperseded = true
384
+ const allFacts = await cortex.facts.list({
385
+ memorySpaceId: testMemorySpaceId,
386
+ userId: testUserId,
387
+ includeSuperseded: true,
388
+ });
389
+
390
+ console.log(`Active: ${activeFacts.length}, All: ${allFacts.length}`);
391
+
392
+ // STRONG ASSERTIONS
393
+ expect(activeFacts.length).toBeGreaterThanOrEqual(1);
394
+ expect(allFacts.length).toBeGreaterThanOrEqual(1);
395
+ expect(activeFacts.length).toBeLessThanOrEqual(allFacts.length);
396
+ }, 60000);
397
+
398
+ it("should filter facts by memory space", async () => {
399
+ const spaceA = createTestMemorySpaceId("fact-space-a");
400
+ const spaceB = createTestMemorySpaceId("fact-space-b");
401
+
402
+ // Add fact to space A
403
+ await cortex.memory.remember({
404
+ memorySpaceId: spaceA,
405
+ conversationId: createTestConversationId(),
406
+ userMessage: "I like cats",
407
+ agentResponse: "Cats are great!",
408
+ userId: testUserId,
409
+ userName: "Test User",
410
+ agentId: testAgentId,
411
+ agentName: "Test Agent",
412
+ generateEmbedding: generateTestEmbedding,
413
+ });
414
+
415
+ await wait(5000);
416
+
417
+ // Verify space A has facts
418
+ const factsA = await cortex.facts.list({
419
+ memorySpaceId: spaceA,
420
+ userId: testUserId,
421
+ });
422
+ console.log(`Space A facts: ${factsA.length}`);
423
+ expect(factsA.length).toBeGreaterThanOrEqual(1);
424
+
425
+ // Facts in space B should not include space A's facts
426
+ const factsB = await cortex.facts.list({
427
+ memorySpaceId: spaceB,
428
+ userId: testUserId,
429
+ });
430
+
431
+ // Space B should be empty
432
+ expect(factsB.length).toBe(0);
433
+ }, 60000);
434
+ });
435
+
436
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
437
+ // Recall with Facts
438
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
439
+
440
+ (SKIP_FACT_TESTS ? describe.skip : describe)("recall with facts", () => {
441
+ it("should include facts in recall results", async () => {
442
+ const conversationId = createTestConversationId();
443
+
444
+ // Store message with fact - use a very clear, searchable statement
445
+ await cortex.memory.remember({
446
+ memorySpaceId: testMemorySpaceId,
447
+ conversationId,
448
+ userMessage: "I prefer dark mode for all my applications",
449
+ agentResponse: "Dark mode is easier on the eyes!",
450
+ userId: testUserId,
451
+ userName: "Test User",
452
+ agentId: testAgentId,
453
+ agentName: "Test Agent",
454
+ generateEmbedding: generateTestEmbedding,
455
+ });
456
+
457
+ await wait(5000);
458
+
459
+ // Verify fact was created - this is the key test
460
+ const facts = await cortex.facts.list({
461
+ memorySpaceId: testMemorySpaceId,
462
+ userId: testUserId,
463
+ includeSuperseded: false,
464
+ });
465
+ console.log(`Facts created: ${facts.length}`);
466
+ facts.forEach((f: any) => console.log(` - ${f.fact}`));
467
+
468
+ // STRONG ASSERTION: Fact was extracted
469
+ expect(facts.length).toBeGreaterThanOrEqual(1);
470
+
471
+ // Recall with facts enabled - use more specific query matching the fact
472
+ const result = await cortex.memory.recall({
473
+ memorySpaceId: testMemorySpaceId,
474
+ userId: testUserId,
475
+ query: "dark mode preference",
476
+ limit: 10,
477
+ sources: {
478
+ vector: true,
479
+ facts: true,
480
+ graph: false,
481
+ },
482
+ generateEmbedding: generateTestEmbedding,
483
+ });
484
+
485
+ console.log(`Recall results: ${result.sources?.vector?.count || 0} memories, ${result.sources?.facts?.count || 0} facts`);
486
+
487
+ // Verify recall works and returns structured response
488
+ expect(result).toBeDefined();
489
+ expect(result.sources).toBeDefined();
490
+
491
+ // Either facts source returns results OR vector source found it
492
+ // The fact was definitely created (verified above), recall behavior may vary
493
+ const totalResults = (result.sources?.vector?.count || 0) + (result.sources?.facts?.count || 0);
494
+ console.log(`Total recall results: ${totalResults}`);
495
+ expect(totalResults).toBeGreaterThanOrEqual(1);
496
+ }, 60000);
497
+ });
498
+ });