@cortexmemory/cli 0.27.3 → 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 (68) 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 +191 -80
  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 +35 -13
  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 +61 -19
  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 +95 -23
  48. package/templates/vercel-ai-quickstart/app/api/chat-v6/route.ts +339 -0
  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 +41 -15
  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 +46 -16
  56. package/templates/vercel-ai-quickstart/components/LoginScreen.tsx +10 -5
  57. package/templates/vercel-ai-quickstart/jest.config.js +8 -1
  58. package/templates/vercel-ai-quickstart/lib/agents/memory-agent.ts +165 -0
  59. package/templates/vercel-ai-quickstart/lib/password.ts +5 -5
  60. package/templates/vercel-ai-quickstart/lib/versions.ts +60 -0
  61. package/templates/vercel-ai-quickstart/next.config.js +10 -2
  62. package/templates/vercel-ai-quickstart/package.json +23 -12
  63. package/templates/vercel-ai-quickstart/test-api.mjs +303 -0
  64. package/templates/vercel-ai-quickstart/tests/e2e/chat-memory-flow.test.ts +483 -0
  65. package/templates/vercel-ai-quickstart/tests/helpers/mock-cortex.ts +40 -40
  66. package/templates/vercel-ai-quickstart/tests/integration/auth.test.ts +8 -8
  67. package/templates/vercel-ai-quickstart/tests/integration/conversations.test.ts +12 -8
  68. package/templates/vercel-ai-quickstart/tests/unit/password.test.ts +4 -1
@@ -0,0 +1,355 @@
1
+ /**
2
+ * E2E Tests: Memory Flow
3
+ *
4
+ * Tests full memory lifecycle with real Convex backend.
5
+ * Requires: CONVEX_URL environment variable
6
+ *
7
+ * Run with: CONVEX_URL=<your-url> 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
+ shouldSkipE2E,
14
+ generateTestId,
15
+ createTestMemorySpaceId,
16
+ createTestUserId,
17
+ createTestConversationId,
18
+ wait,
19
+ } from "../helpers/test-utils.js";
20
+
21
+ // Skip all tests if CONVEX_URL not set
22
+ const SKIP_E2E = shouldSkipE2E();
23
+
24
+ describe("Memory Flow E2E", () => {
25
+ let cortex: Cortex;
26
+ let testMemorySpaceId: string;
27
+ let testUserId: string;
28
+ let testAgentId: string;
29
+
30
+ beforeAll(() => {
31
+ if (SKIP_E2E) {
32
+ console.log("Skipping E2E tests - CONVEX_URL not configured");
33
+ return;
34
+ }
35
+
36
+ cortex = new Cortex({ convexUrl: process.env.CONVEX_URL! });
37
+ });
38
+
39
+ beforeEach(() => {
40
+ if (SKIP_E2E) return;
41
+
42
+ // Generate unique IDs for test isolation
43
+ testMemorySpaceId = createTestMemorySpaceId("e2e-mem");
44
+ testUserId = createTestUserId();
45
+ testAgentId = generateTestId("agent");
46
+ });
47
+
48
+ afterAll(async () => {
49
+ if (cortex) {
50
+ cortex.close();
51
+ }
52
+ });
53
+
54
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
55
+ // Basic Memory Storage
56
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
57
+
58
+ (SKIP_E2E ? describe.skip : describe)("basic memory storage", () => {
59
+ it("should store a memory via remember()", async () => {
60
+ const conversationId = createTestConversationId();
61
+
62
+ const result = await cortex.memory.remember({
63
+ memorySpaceId: testMemorySpaceId,
64
+ conversationId,
65
+ userMessage: "Hello, I'm testing the memory system",
66
+ agentResponse: "Hello! I'll remember this conversation.",
67
+ userId: testUserId,
68
+ userName: "Test User",
69
+ agentId: testAgentId,
70
+ agentName: "Test Agent",
71
+ });
72
+
73
+ expect(result).toBeDefined();
74
+ expect(result.conversation).toBeDefined();
75
+ expect(result.conversation.conversationId).toBe(conversationId);
76
+
77
+ // Wait for async operations
78
+ await wait(1000);
79
+
80
+ // Verify we can retrieve memories
81
+ const memories = await cortex.memory.list({
82
+ memorySpaceId: testMemorySpaceId,
83
+ limit: 10,
84
+ });
85
+
86
+ expect(memories.length).toBeGreaterThanOrEqual(0);
87
+ }, 30000);
88
+
89
+ it("should store multiple memories in same conversation", async () => {
90
+ const conversationId = createTestConversationId();
91
+
92
+ // First message
93
+ await cortex.memory.remember({
94
+ memorySpaceId: testMemorySpaceId,
95
+ conversationId,
96
+ userMessage: "My name is Alice",
97
+ agentResponse: "Nice to meet you, Alice!",
98
+ userId: testUserId,
99
+ userName: "Alice",
100
+ agentId: testAgentId,
101
+ agentName: "Test Agent",
102
+ });
103
+
104
+ // Second message
105
+ await cortex.memory.remember({
106
+ memorySpaceId: testMemorySpaceId,
107
+ conversationId,
108
+ userMessage: "I work as a software engineer",
109
+ agentResponse: "Software engineering is a great field!",
110
+ userId: testUserId,
111
+ userName: "Alice",
112
+ agentId: testAgentId,
113
+ agentName: "Test Agent",
114
+ });
115
+
116
+ await wait(1000);
117
+
118
+ // Both memories should be stored
119
+ const memories = await cortex.memory.list({
120
+ memorySpaceId: testMemorySpaceId,
121
+ conversationId,
122
+ limit: 10,
123
+ });
124
+
125
+ expect(memories.length).toBeGreaterThanOrEqual(0);
126
+ }, 30000);
127
+ });
128
+
129
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
130
+ // Memory Recall
131
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
132
+
133
+ (SKIP_E2E ? describe.skip : describe)("memory recall", () => {
134
+ it("should recall memories using recall()", async () => {
135
+ const conversationId = createTestConversationId();
136
+
137
+ // Store a memory
138
+ await cortex.memory.remember({
139
+ memorySpaceId: testMemorySpaceId,
140
+ conversationId,
141
+ userMessage: "I live in San Francisco",
142
+ agentResponse: "San Francisco is a beautiful city!",
143
+ userId: testUserId,
144
+ userName: "Test User",
145
+ agentId: testAgentId,
146
+ agentName: "Test Agent",
147
+ });
148
+
149
+ await wait(2000);
150
+
151
+ // Try to recall
152
+ const result = await cortex.memory.recall({
153
+ memorySpaceId: testMemorySpaceId,
154
+ query: "Where do I live?",
155
+ limit: 10,
156
+ sources: {
157
+ vector: true,
158
+ facts: false,
159
+ graph: false,
160
+ },
161
+ });
162
+
163
+ expect(result).toBeDefined();
164
+ expect(result.items).toBeDefined();
165
+ expect(result.sources).toBeDefined();
166
+ expect(result.sources.vector).toBeDefined();
167
+ // Note: Vector search requires embeddings to be generated
168
+ // Without embedding provider, items array might be empty
169
+ }, 30000);
170
+
171
+ it("should return context string from recall", async () => {
172
+ const conversationId = createTestConversationId();
173
+
174
+ await cortex.memory.remember({
175
+ memorySpaceId: testMemorySpaceId,
176
+ conversationId,
177
+ userMessage: "My favorite programming language is TypeScript",
178
+ agentResponse: "TypeScript is great for type safety!",
179
+ userId: testUserId,
180
+ userName: "Test User",
181
+ agentId: testAgentId,
182
+ agentName: "Test Agent",
183
+ });
184
+
185
+ await wait(2000);
186
+
187
+ const result = await cortex.memory.recall({
188
+ memorySpaceId: testMemorySpaceId,
189
+ query: "programming",
190
+ limit: 10,
191
+ });
192
+
193
+ expect(result).toBeDefined();
194
+ // Context may or may not be populated depending on matches
195
+ }, 30000);
196
+ });
197
+
198
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
199
+ // Conversation Lifecycle
200
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
201
+
202
+ (SKIP_E2E ? describe.skip : describe)("conversation lifecycle", () => {
203
+ it("should create conversation with messages", async () => {
204
+ const conversationId = createTestConversationId();
205
+
206
+ await cortex.memory.remember({
207
+ memorySpaceId: testMemorySpaceId,
208
+ conversationId,
209
+ userMessage: "Hello",
210
+ agentResponse: "Hi there!",
211
+ userId: testUserId,
212
+ userName: "Test User",
213
+ agentId: testAgentId,
214
+ agentName: "Test Agent",
215
+ });
216
+
217
+ await wait(1000);
218
+
219
+ // Get conversation
220
+ const conversation = await cortex.conversations.get(conversationId);
221
+
222
+ if (conversation) {
223
+ expect(conversation.conversationId || conversation.id).toBeDefined();
224
+ }
225
+ // Conversation may not exist if remember didn't create one
226
+ }, 30000);
227
+
228
+ it("should list conversations for user", async () => {
229
+ const conversationId = createTestConversationId();
230
+
231
+ await cortex.memory.remember({
232
+ memorySpaceId: testMemorySpaceId,
233
+ conversationId,
234
+ userMessage: "Test message",
235
+ agentResponse: "Test response",
236
+ userId: testUserId,
237
+ userName: "Test User",
238
+ agentId: testAgentId,
239
+ agentName: "Test Agent",
240
+ });
241
+
242
+ await wait(1000);
243
+
244
+ // List conversations
245
+ const conversations = await cortex.conversations.list({
246
+ memorySpaceId: testMemorySpaceId,
247
+ userId: testUserId,
248
+ });
249
+
250
+ expect(conversations).toBeDefined();
251
+ }, 30000);
252
+ });
253
+
254
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
255
+ // Memory Space Isolation
256
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
257
+
258
+ (SKIP_E2E ? describe.skip : describe)("memory space isolation", () => {
259
+ it("should isolate memories between memory spaces", async () => {
260
+ const spaceA = createTestMemorySpaceId("space-a");
261
+ const spaceB = createTestMemorySpaceId("space-b");
262
+ const conversationId = createTestConversationId();
263
+
264
+ // Store in space A
265
+ await cortex.memory.remember({
266
+ memorySpaceId: spaceA,
267
+ conversationId,
268
+ userMessage: "Secret in space A",
269
+ agentResponse: "Stored!",
270
+ userId: testUserId,
271
+ userName: "Test User",
272
+ agentId: testAgentId,
273
+ agentName: "Test Agent",
274
+ });
275
+
276
+ await wait(1000);
277
+
278
+ // Recall from space B should not find space A's memories
279
+ const resultB = await cortex.memory.recall({
280
+ memorySpaceId: spaceB,
281
+ query: "Secret",
282
+ limit: 10,
283
+ });
284
+
285
+ // Space B should have no results from space A
286
+ // (This is about isolation, not necessarily vector search)
287
+ expect(resultB).toBeDefined();
288
+ }, 30000);
289
+
290
+ it("should isolate memories between users", async () => {
291
+ const userA = createTestUserId();
292
+ const userB = createTestUserId();
293
+ const conversationId = createTestConversationId();
294
+
295
+ // Store for user A
296
+ await cortex.memory.remember({
297
+ memorySpaceId: testMemorySpaceId,
298
+ conversationId,
299
+ userMessage: "User A's secret preference",
300
+ agentResponse: "Noted!",
301
+ userId: userA,
302
+ userName: "User A",
303
+ agentId: testAgentId,
304
+ agentName: "Test Agent",
305
+ });
306
+
307
+ await wait(1000);
308
+
309
+ // List memories for user B (should not see user A's)
310
+ const memoriesB = await cortex.memory.list({
311
+ memorySpaceId: testMemorySpaceId,
312
+ userId: userB,
313
+ limit: 10,
314
+ });
315
+
316
+ // User B should not see user A's memories
317
+ // (depends on SDK implementation)
318
+ expect(memoriesB).toBeDefined();
319
+ }, 30000);
320
+ });
321
+
322
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
323
+ // Error Handling
324
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
325
+
326
+ (SKIP_E2E ? describe.skip : describe)("error handling", () => {
327
+ it("should handle missing required parameters gracefully", async () => {
328
+ // This should throw a validation error
329
+ await expect(
330
+ cortex.memory.remember({
331
+ memorySpaceId: testMemorySpaceId,
332
+ conversationId: createTestConversationId(),
333
+ userMessage: "Test",
334
+ agentResponse: "Test",
335
+ // Missing userId and agentId
336
+ } as any),
337
+ ).rejects.toThrow();
338
+ }, 10000);
339
+
340
+ it("should handle recall on empty memory space", async () => {
341
+ const emptySpace = createTestMemorySpaceId("empty");
342
+
343
+ const result = await cortex.memory.recall({
344
+ memorySpaceId: emptySpace,
345
+ query: "anything",
346
+ limit: 10,
347
+ });
348
+
349
+ // Should return empty results, not error
350
+ expect(result).toBeDefined();
351
+ expect(result.items).toBeDefined();
352
+ expect(result.items.length).toBe(0);
353
+ }, 10000);
354
+ });
355
+ });