@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.
- package/dist/commands/db.d.ts.map +1 -1
- package/dist/commands/db.js +18 -6
- package/dist/commands/db.js.map +1 -1
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +191 -80
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/dev.js +3 -2
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +12 -0
- package/dist/commands/init.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/app-template-sync.d.ts.map +1 -1
- package/dist/utils/app-template-sync.js +35 -13
- package/dist/utils/app-template-sync.js.map +1 -1
- package/dist/utils/init/quickstart-setup.d.ts.map +1 -1
- package/dist/utils/init/quickstart-setup.js.map +1 -1
- package/package.json +4 -4
- package/templates/basic/.env.local.example +23 -0
- package/templates/basic/README.md +181 -56
- package/templates/basic/package-lock.json +2180 -406
- package/templates/basic/package.json +23 -5
- package/templates/basic/src/__tests__/chat.test.ts +340 -0
- package/templates/basic/src/__tests__/cortex.test.ts +260 -0
- package/templates/basic/src/__tests__/display.test.ts +455 -0
- package/templates/basic/src/__tests__/e2e/fact-extraction.test.ts +498 -0
- package/templates/basic/src/__tests__/e2e/memory-flow.test.ts +355 -0
- package/templates/basic/src/__tests__/e2e/server-e2e.test.ts +414 -0
- package/templates/basic/src/__tests__/helpers/test-utils.ts +345 -0
- package/templates/basic/src/__tests__/integration/chat-flow.test.ts +422 -0
- package/templates/basic/src/__tests__/integration/server.test.ts +441 -0
- package/templates/basic/src/__tests__/llm.test.ts +344 -0
- package/templates/basic/src/chat.ts +300 -0
- package/templates/basic/src/cortex.ts +203 -0
- package/templates/basic/src/display.ts +425 -0
- package/templates/basic/src/index.ts +194 -64
- package/templates/basic/src/llm.ts +214 -0
- package/templates/basic/src/server.ts +280 -0
- package/templates/basic/vitest.config.ts +33 -0
- package/templates/basic/vitest.e2e.config.ts +28 -0
- package/templates/basic/vitest.integration.config.ts +25 -0
- package/templates/vercel-ai-quickstart/app/api/auth/check/route.ts +1 -1
- package/templates/vercel-ai-quickstart/app/api/auth/login/route.ts +61 -19
- package/templates/vercel-ai-quickstart/app/api/auth/register/route.ts +14 -18
- package/templates/vercel-ai-quickstart/app/api/auth/setup/route.ts +4 -7
- package/templates/vercel-ai-quickstart/app/api/chat/route.ts +95 -23
- package/templates/vercel-ai-quickstart/app/api/chat-v6/route.ts +339 -0
- package/templates/vercel-ai-quickstart/app/api/conversations/route.ts +16 -16
- package/templates/vercel-ai-quickstart/app/globals.css +24 -9
- package/templates/vercel-ai-quickstart/app/page.tsx +41 -15
- package/templates/vercel-ai-quickstart/components/AdminSetup.tsx +3 -1
- package/templates/vercel-ai-quickstart/components/AuthProvider.tsx +6 -6
- package/templates/vercel-ai-quickstart/components/ChatHistorySidebar.tsx +19 -8
- package/templates/vercel-ai-quickstart/components/ChatInterface.tsx +46 -16
- package/templates/vercel-ai-quickstart/components/LoginScreen.tsx +10 -5
- package/templates/vercel-ai-quickstart/jest.config.js +8 -1
- package/templates/vercel-ai-quickstart/lib/agents/memory-agent.ts +165 -0
- package/templates/vercel-ai-quickstart/lib/password.ts +5 -5
- package/templates/vercel-ai-quickstart/lib/versions.ts +60 -0
- package/templates/vercel-ai-quickstart/next.config.js +10 -2
- package/templates/vercel-ai-quickstart/package.json +23 -12
- package/templates/vercel-ai-quickstart/test-api.mjs +303 -0
- package/templates/vercel-ai-quickstart/tests/e2e/chat-memory-flow.test.ts +483 -0
- package/templates/vercel-ai-quickstart/tests/helpers/mock-cortex.ts +40 -40
- package/templates/vercel-ai-quickstart/tests/integration/auth.test.ts +8 -8
- package/templates/vercel-ai-quickstart/tests/integration/conversations.test.ts +12 -8
- 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
|
+
});
|