@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,422 @@
1
+ /**
2
+ * Integration Tests: Chat Flow
3
+ *
4
+ * Tests the complete chat pipeline with mocked Cortex SDK.
5
+ * Verifies: recall -> generate -> remember sequence
6
+ */
7
+
8
+ import { describe, it, expect, vi, beforeEach } from "vitest";
9
+ import { generateTestId } from "../helpers/test-utils.js";
10
+
11
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
12
+ // Types
13
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
14
+
15
+ interface MockMemory {
16
+ memoryId: string;
17
+ content: string;
18
+ importance: number;
19
+ }
20
+
21
+ interface MockFact {
22
+ factId: string;
23
+ fact: string;
24
+ factType: string;
25
+ confidence: number;
26
+ }
27
+
28
+ interface MockConversation {
29
+ conversationId: string;
30
+ messages: Array<{ role: string; content: string }>;
31
+ }
32
+
33
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
34
+ // Mock State
35
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
36
+
37
+ // Mutable state for test control
38
+ let memories: MockMemory[] = [];
39
+ let facts: MockFact[] = [];
40
+ let conversations: Map<string, MockConversation> = new Map();
41
+ let recallCalled = false;
42
+ let rememberCalled = false;
43
+ let recallShouldFail = false;
44
+ let rememberShouldFail = false;
45
+ let factsListShouldFail = false;
46
+
47
+ function resetState() {
48
+ memories = [];
49
+ facts = [];
50
+ conversations = new Map();
51
+ recallCalled = false;
52
+ rememberCalled = false;
53
+ recallShouldFail = false;
54
+ rememberShouldFail = false;
55
+ factsListShouldFail = false;
56
+ }
57
+
58
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
59
+ // Mock Setup
60
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
61
+
62
+ // Create mock cortex that uses the module-level state
63
+ const createMockCortex = () => ({
64
+ memory: {
65
+ recall: vi.fn(async () => {
66
+ recallCalled = true;
67
+ if (recallShouldFail) {
68
+ throw new Error("Recall failed");
69
+ }
70
+ // Return a copy to avoid reference issues when remember modifies the array
71
+ const memoriesCopy = [...memories];
72
+ const factsCopy = [...facts];
73
+ return {
74
+ memories: memoriesCopy,
75
+ facts: factsCopy,
76
+ context: memoriesCopy.map(m => m.content).join("\n"),
77
+ totalResults: memoriesCopy.length + factsCopy.length,
78
+ };
79
+ }),
80
+ remember: vi.fn(async (params: any) => {
81
+ rememberCalled = true;
82
+ if (rememberShouldFail) {
83
+ throw new Error("Remember failed");
84
+ }
85
+ const convId = params.conversationId || generateTestId("conv");
86
+ memories.push({
87
+ memoryId: generateTestId("mem"),
88
+ content: params.userMessage,
89
+ importance: 50,
90
+ });
91
+ return {
92
+ conversation: { conversationId: convId },
93
+ memories: memories,
94
+ facts: [],
95
+ };
96
+ }),
97
+ list: vi.fn(async () => memories),
98
+ },
99
+ facts: {
100
+ list: vi.fn(async () => {
101
+ if (factsListShouldFail) {
102
+ throw new Error("List failed");
103
+ }
104
+ return { facts };
105
+ }),
106
+ },
107
+ conversations: {
108
+ get: vi.fn(async (convId: string) => conversations.get(convId) || null),
109
+ list: vi.fn(async () => Array.from(conversations.values())),
110
+ },
111
+ close: vi.fn(),
112
+ });
113
+
114
+ let mockCortex = createMockCortex();
115
+
116
+ // Mock the cortex module
117
+ vi.mock("../../cortex.js", () => ({
118
+ getCortex: vi.fn(() => mockCortex),
119
+ closeCortex: vi.fn(),
120
+ CONFIG: {
121
+ memorySpaceId: "test-space",
122
+ userId: "test-user",
123
+ userName: "Test User",
124
+ agentId: "test-agent",
125
+ agentName: "Test Agent",
126
+ enableFactExtraction: true,
127
+ enableGraphMemory: false,
128
+ debug: false,
129
+ },
130
+ buildRememberParams: vi.fn(async (msg: { userMessage: string; agentResponse: string; conversationId: string }) => ({
131
+ memorySpaceId: "test-space",
132
+ conversationId: msg.conversationId,
133
+ userMessage: msg.userMessage,
134
+ agentResponse: msg.agentResponse,
135
+ userId: "test-user",
136
+ userName: "Test User",
137
+ agentId: "test-agent",
138
+ agentName: "Test Agent",
139
+ })),
140
+ createLayerObserver: vi.fn(() => ({
141
+ onOrchestrationStart: vi.fn(),
142
+ onLayerUpdate: vi.fn(),
143
+ onOrchestrationComplete: vi.fn(),
144
+ })),
145
+ }));
146
+
147
+ // Mock LLM module
148
+ vi.mock("../../llm.js", () => ({
149
+ isLLMAvailable: vi.fn(() => false),
150
+ generateResponse: vi.fn(async (userMessage: string) => `Echo: ${userMessage}`),
151
+ }));
152
+
153
+ // Mock display module (suppress console output in tests)
154
+ vi.mock("../../display.js", () => ({
155
+ printRecallResults: vi.fn(),
156
+ printOrchestrationComplete: vi.fn(),
157
+ printInfo: vi.fn(),
158
+ printError: vi.fn(),
159
+ printSuccess: vi.fn(),
160
+ }));
161
+
162
+ // Import after mocks
163
+ import {
164
+ chat,
165
+ recallMemories,
166
+ listFacts,
167
+ getHistory,
168
+ generateConversationId,
169
+ getConversationId,
170
+ newConversation,
171
+ printConfig,
172
+ } from "../../chat.js";
173
+
174
+ import { printRecallResults, printInfo } from "../../display.js";
175
+
176
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
177
+ // Tests
178
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
179
+
180
+ describe("Chat Flow Integration", () => {
181
+ beforeEach(() => {
182
+ resetState();
183
+ mockCortex = createMockCortex();
184
+ vi.clearAllMocks();
185
+ });
186
+
187
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
188
+ // Full Chat Flow
189
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
190
+
191
+ describe("chat()", () => {
192
+ it("should complete full recall -> generate -> remember flow", async () => {
193
+ const result = await chat("Hello, world!");
194
+
195
+ // Verify recall was called
196
+ expect(recallCalled).toBe(true);
197
+
198
+ // Verify remember was called
199
+ expect(rememberCalled).toBe(true);
200
+
201
+ // Verify result structure
202
+ expect(result).toHaveProperty("response");
203
+ expect(result).toHaveProperty("conversationId");
204
+ expect(result).toHaveProperty("memoriesRecalled");
205
+ expect(result).toHaveProperty("factsRecalled");
206
+ });
207
+
208
+ it("should return recalled memories and facts count", async () => {
209
+ // Add memories and facts
210
+ memories.push({
211
+ memoryId: "mem-1",
212
+ content: "Previous memory",
213
+ importance: 80,
214
+ });
215
+ facts.push({
216
+ factId: "fact-1",
217
+ fact: "Test fact",
218
+ factType: "knowledge",
219
+ confidence: 90,
220
+ });
221
+
222
+ const result = await chat("What do you remember?");
223
+
224
+ expect(result.memoriesRecalled).toBe(1);
225
+ expect(result.factsRecalled).toBe(1);
226
+ });
227
+
228
+ it("should use provided conversation ID", async () => {
229
+ const customConvId = generateTestId("custom-conv");
230
+
231
+ const result = await chat("Hello", customConvId);
232
+
233
+ expect(result.conversationId).toBe(customConvId);
234
+ });
235
+
236
+ it("should generate conversation ID if not provided", async () => {
237
+ const result = await chat("Hello");
238
+
239
+ expect(result.conversationId).toBeDefined();
240
+ expect(result.conversationId).toMatch(/^conv-\d+-[a-z0-9]+$/);
241
+ });
242
+
243
+ it("should display recall results", async () => {
244
+ await chat("Hello");
245
+
246
+ expect(printRecallResults).toHaveBeenCalled();
247
+ });
248
+
249
+ it("should handle recall errors gracefully", async () => {
250
+ recallShouldFail = true;
251
+
252
+ // Should not throw
253
+ const result = await chat("Hello");
254
+
255
+ // Should still return a response
256
+ expect(result).toBeDefined();
257
+ expect(result.response).toBeDefined();
258
+ expect(result.memoriesRecalled).toBe(0);
259
+ expect(result.factsRecalled).toBe(0);
260
+ });
261
+
262
+ it("should still return response if remember fails", async () => {
263
+ rememberShouldFail = true;
264
+
265
+ // Should not throw
266
+ const result = await chat("Hello");
267
+
268
+ // Should still have the response
269
+ expect(result.response).toBe("Echo: Hello");
270
+ });
271
+ });
272
+
273
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
274
+ // Conversation Management
275
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
276
+
277
+ describe("conversation management", () => {
278
+ it("should generate valid conversation IDs", () => {
279
+ const id = generateConversationId();
280
+
281
+ expect(id).toMatch(/^conv-\d+-[a-z0-9]+$/);
282
+ });
283
+
284
+ it("should reuse conversation ID within same session", async () => {
285
+ const result1 = await chat("First message");
286
+ const result2 = await chat("Second message");
287
+
288
+ expect(result1.conversationId).toBe(result2.conversationId);
289
+ });
290
+
291
+ it("should create new conversation ID when requested", () => {
292
+ const oldId = getConversationId();
293
+ const newId = newConversation();
294
+
295
+ expect(newId).not.toBe(oldId);
296
+ expect(newId).toMatch(/^conv-\d+-[a-z0-9]+$/);
297
+ expect(printInfo).toHaveBeenCalledWith(expect.stringContaining("Started new conversation"));
298
+ });
299
+ });
300
+
301
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
302
+ // Query Functions
303
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
304
+
305
+ describe("recallMemories()", () => {
306
+ it("should call recall with query", async () => {
307
+ await recallMemories("test query");
308
+
309
+ expect(recallCalled).toBe(true);
310
+ });
311
+
312
+ it("should display recall results", async () => {
313
+ await recallMemories("test");
314
+
315
+ expect(printRecallResults).toHaveBeenCalled();
316
+ });
317
+
318
+ it("should handle recall errors", async () => {
319
+ recallShouldFail = true;
320
+
321
+ // Should not throw
322
+ await expect(recallMemories("test")).resolves.not.toThrow();
323
+ });
324
+ });
325
+
326
+ describe("listFacts()", () => {
327
+ it("should call facts.list", async () => {
328
+ facts.push({
329
+ factId: "fact-1",
330
+ fact: "Test fact",
331
+ factType: "knowledge",
332
+ confidence: 90,
333
+ });
334
+
335
+ await listFacts();
336
+
337
+ // Should display results
338
+ expect(printRecallResults).toHaveBeenCalled();
339
+ });
340
+
341
+ it("should display facts results", async () => {
342
+ facts.push({
343
+ factId: "fact-1",
344
+ fact: "Test fact",
345
+ factType: "knowledge",
346
+ confidence: 90,
347
+ });
348
+
349
+ await listFacts();
350
+
351
+ expect(printRecallResults).toHaveBeenCalled();
352
+ });
353
+
354
+ it("should handle list errors", async () => {
355
+ factsListShouldFail = true;
356
+
357
+ // Should not throw
358
+ await expect(listFacts()).resolves.not.toThrow();
359
+ });
360
+ });
361
+
362
+ describe("getHistory()", () => {
363
+ it("should get conversation history when available", async () => {
364
+ // First create a conversation by chatting
365
+ await chat("Hello");
366
+ const convId = getConversationId();
367
+
368
+ // Add conversation to mock state
369
+ conversations.set(convId, {
370
+ conversationId: convId,
371
+ messages: [
372
+ { role: "user", content: "Hello" },
373
+ { role: "assistant", content: "Hi there!" },
374
+ ],
375
+ });
376
+
377
+ // Should not throw
378
+ await expect(getHistory()).resolves.not.toThrow();
379
+ });
380
+
381
+ it("should handle conversation not found", async () => {
382
+ // Create a conversation first
383
+ await chat("Hello");
384
+
385
+ // Should not throw even if conversation doesn't exist in mock
386
+ await expect(getHistory()).resolves.not.toThrow();
387
+ });
388
+ });
389
+
390
+ describe("printConfig()", () => {
391
+ it("should be callable", () => {
392
+ // Just verify it doesn't throw
393
+ expect(() => printConfig()).not.toThrow();
394
+ });
395
+ });
396
+
397
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
398
+ // Memory Accumulation
399
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
400
+
401
+ describe("memory accumulation", () => {
402
+ it("should store memories across multiple messages", async () => {
403
+ await chat("First message");
404
+ await chat("Second message");
405
+ await chat("Third message");
406
+
407
+ // Should have accumulated memories
408
+ expect(memories.length).toBe(3);
409
+ });
410
+
411
+ it("should return increasing memory count on recall", async () => {
412
+ // First chat stores 1 memory
413
+ await chat("First message");
414
+
415
+ // Mock returns accumulated memories
416
+ const result2 = await chat("Second message");
417
+
418
+ // Should show memories from previous chats
419
+ expect(result2.memoriesRecalled).toBeGreaterThanOrEqual(1);
420
+ });
421
+ });
422
+ });