@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,280 @@
1
+ /**
2
+ * Cortex Memory - HTTP Server Mode
3
+ *
4
+ * REST API server for demonstrating Cortex Memory SDK.
5
+ * Useful for testing with tools like curl, Postman, or integrating with other apps.
6
+ *
7
+ * Usage:
8
+ * npm run server
9
+ *
10
+ * Endpoints:
11
+ * POST /chat Chat and store memory
12
+ * GET /recall Search memories
13
+ * GET /facts List stored facts
14
+ * GET /history/:id Get conversation history
15
+ * GET /health Health check
16
+ */
17
+
18
+ import { serve } from "@hono/node-server";
19
+ import { Hono } from "hono";
20
+ import { cors } from "hono/cors";
21
+ import { logger } from "hono/logger";
22
+ import { closeCortex, CONFIG } from "./cortex.js";
23
+ import {
24
+ chat,
25
+ recallMemories,
26
+ listFacts,
27
+ generateConversationId,
28
+ } from "./chat.js";
29
+ import { printWelcome, printInfo, printError } from "./display.js";
30
+
31
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
32
+ // Server Setup
33
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
34
+
35
+ const app = new Hono();
36
+
37
+ // Middleware
38
+ app.use("*", cors());
39
+ app.use("*", logger());
40
+
41
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
42
+ // Routes
43
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
44
+
45
+ /**
46
+ * Health check
47
+ */
48
+ app.get("/health", (c) => {
49
+ return c.json({
50
+ status: "ok",
51
+ memorySpaceId: CONFIG.memorySpaceId,
52
+ agentId: CONFIG.agentId,
53
+ features: {
54
+ factExtraction: CONFIG.enableFactExtraction,
55
+ graphSync: CONFIG.enableGraphMemory,
56
+ llm: !!process.env.OPENAI_API_KEY,
57
+ },
58
+ });
59
+ });
60
+
61
+ /**
62
+ * Chat endpoint
63
+ *
64
+ * POST /chat
65
+ * Body: { message: string, conversationId?: string }
66
+ */
67
+ app.post("/chat", async (c) => {
68
+ try {
69
+ const body = await c.req.json();
70
+ const { message, conversationId } = body;
71
+
72
+ if (!message || typeof message !== "string") {
73
+ return c.json({ error: "message is required" }, 400);
74
+ }
75
+
76
+ const convId = conversationId || generateConversationId();
77
+
78
+ console.log(`\n[Chat] User: ${message.slice(0, 50)}${message.length > 50 ? "..." : ""}`);
79
+
80
+ const result = await chat(message, convId);
81
+
82
+ console.log(`[Chat] Response sent (${result.memoriesRecalled} memories, ${result.factsRecalled} facts recalled)\n`);
83
+
84
+ return c.json({
85
+ response: result.response,
86
+ conversationId: result.conversationId,
87
+ memoriesRecalled: result.memoriesRecalled,
88
+ factsRecalled: result.factsRecalled,
89
+ });
90
+ } catch (error) {
91
+ const message = error instanceof Error ? error.message : "Unknown error";
92
+ printError("Chat failed", error instanceof Error ? error : undefined);
93
+ return c.json({ error: message }, 500);
94
+ }
95
+ });
96
+
97
+ /**
98
+ * Recall endpoint
99
+ *
100
+ * GET /recall?query=<query>
101
+ */
102
+ app.get("/recall", async (c) => {
103
+ try {
104
+ const query = c.req.query("query");
105
+
106
+ if (!query) {
107
+ return c.json({ error: "query parameter is required" }, 400);
108
+ }
109
+
110
+ console.log(`\n[Recall] Query: ${query}`);
111
+
112
+ // Use the internal recall function but capture results
113
+ const { getCortex } = await import("./cortex.js");
114
+ const cortex = getCortex();
115
+
116
+ const result = await cortex.memory.recall({
117
+ memorySpaceId: CONFIG.memorySpaceId,
118
+ query,
119
+ limit: 10,
120
+ sources: {
121
+ vector: true,
122
+ facts: true,
123
+ graph: CONFIG.enableGraphMemory,
124
+ },
125
+ });
126
+
127
+ console.log(`[Recall] Found ${result.memories?.length || 0} memories, ${result.facts?.length || 0} facts\n`);
128
+
129
+ return c.json({
130
+ memories: result.memories || [],
131
+ facts: result.facts || [],
132
+ query,
133
+ });
134
+ } catch (error) {
135
+ const message = error instanceof Error ? error.message : "Unknown error";
136
+ return c.json({ error: message }, 500);
137
+ }
138
+ });
139
+
140
+ /**
141
+ * Facts endpoint
142
+ *
143
+ * GET /facts
144
+ */
145
+ app.get("/facts", async (c) => {
146
+ try {
147
+ const { getCortex } = await import("./cortex.js");
148
+ const cortex = getCortex();
149
+
150
+ const result = await cortex.facts.list({
151
+ memorySpaceId: CONFIG.memorySpaceId,
152
+ limit: 50,
153
+ });
154
+
155
+ const facts = result.facts || result || [];
156
+
157
+ return c.json({ facts, count: facts.length });
158
+ } catch (error) {
159
+ const message = error instanceof Error ? error.message : "Unknown error";
160
+ return c.json({ error: message }, 500);
161
+ }
162
+ });
163
+
164
+ /**
165
+ * Conversation history endpoint
166
+ *
167
+ * GET /history/:conversationId
168
+ */
169
+ app.get("/history/:conversationId", async (c) => {
170
+ try {
171
+ const conversationId = c.req.param("conversationId");
172
+
173
+ const { getCortex } = await import("./cortex.js");
174
+ const cortex = getCortex();
175
+
176
+ const conversation = await cortex.conversations.get(conversationId);
177
+
178
+ if (!conversation) {
179
+ return c.json({ error: "Conversation not found" }, 404);
180
+ }
181
+
182
+ return c.json({
183
+ conversationId,
184
+ messages: conversation.messages || [],
185
+ messageCount: conversation.messages?.length || 0,
186
+ });
187
+ } catch (error) {
188
+ const message = error instanceof Error ? error.message : "Unknown error";
189
+ return c.json({ error: message }, 500);
190
+ }
191
+ });
192
+
193
+ /**
194
+ * Root endpoint - API docs
195
+ */
196
+ app.get("/", (c) => {
197
+ return c.json({
198
+ name: "Cortex Memory - Basic Demo API",
199
+ version: "1.0.0",
200
+ endpoints: {
201
+ "POST /chat": {
202
+ description: "Chat and store memory",
203
+ body: { message: "string", conversationId: "string (optional)" },
204
+ },
205
+ "GET /recall": {
206
+ description: "Search memories",
207
+ query: { query: "string" },
208
+ },
209
+ "GET /facts": {
210
+ description: "List all stored facts",
211
+ },
212
+ "GET /history/:id": {
213
+ description: "Get conversation history",
214
+ },
215
+ "GET /health": {
216
+ description: "Health check",
217
+ },
218
+ },
219
+ });
220
+ });
221
+
222
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
223
+ // Server Start
224
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
225
+
226
+ const PORT = parseInt(process.env.PORT || "3001", 10);
227
+
228
+ async function main(): Promise<void> {
229
+ // Check for required environment
230
+ if (!process.env.CONVEX_URL) {
231
+ printError(
232
+ "CONVEX_URL is required. Set it in .env.local or run: cortex init",
233
+ );
234
+ process.exit(1);
235
+ }
236
+
237
+ // Print welcome
238
+ printWelcome("server");
239
+
240
+ // Start server
241
+ console.log(`🚀 Server starting on http://localhost:${PORT}`);
242
+ console.log("");
243
+ console.log("Endpoints:");
244
+ console.log(` POST http://localhost:${PORT}/chat`);
245
+ console.log(` GET http://localhost:${PORT}/recall?query=...`);
246
+ console.log(` GET http://localhost:${PORT}/facts`);
247
+ console.log(` GET http://localhost:${PORT}/history/:id`);
248
+ console.log(` GET http://localhost:${PORT}/health`);
249
+ console.log("");
250
+ console.log("Example:");
251
+ console.log(` curl -X POST http://localhost:${PORT}/chat \\`);
252
+ console.log(' -H "Content-Type: application/json" \\');
253
+ console.log(' -d \'{"message": "My name is Alex"}\'');
254
+ console.log("");
255
+
256
+ serve({
257
+ fetch: app.fetch,
258
+ port: PORT,
259
+ });
260
+
261
+ printInfo(`Server running on port ${PORT}`);
262
+ }
263
+
264
+ // Handle cleanup
265
+ process.on("SIGINT", () => {
266
+ console.log("\n\nShutting down...");
267
+ closeCortex();
268
+ process.exit(0);
269
+ });
270
+
271
+ process.on("SIGTERM", () => {
272
+ closeCortex();
273
+ process.exit(0);
274
+ });
275
+
276
+ // Run
277
+ main().catch((error) => {
278
+ printError("Fatal error", error instanceof Error ? error : undefined);
279
+ process.exit(1);
280
+ });
@@ -0,0 +1,33 @@
1
+ import { defineConfig } from "vitest/config";
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ globals: true,
6
+ environment: "node",
7
+ include: [
8
+ "src/**/*.test.ts",
9
+ "src/__tests__/**/*.test.ts",
10
+ ],
11
+ // Unit tests run fast
12
+ testTimeout: 10000,
13
+ coverage: {
14
+ provider: "v8",
15
+ reporter: ["text", "json", "html"],
16
+ include: ["src/**/*.ts"],
17
+ exclude: [
18
+ "src/**/*.test.ts",
19
+ "src/__tests__/**",
20
+ "src/index.ts", // Entry point, tested via integration
21
+ "src/server.ts", // Entry point, tested via integration
22
+ ],
23
+ },
24
+ clearMocks: true,
25
+ restoreMocks: true,
26
+ // Separate pools for different test types
27
+ poolOptions: {
28
+ forks: {
29
+ singleFork: true,
30
+ },
31
+ },
32
+ },
33
+ });
@@ -0,0 +1,28 @@
1
+ import { defineConfig } from "vitest/config";
2
+
3
+ /**
4
+ * Configuration for E2E tests.
5
+ * These tests require real Convex backend and optionally OpenAI API key.
6
+ *
7
+ * Run with:
8
+ * CONVEX_URL=<url> npm run test:e2e
9
+ * CONVEX_URL=<url> OPENAI_API_KEY=<key> npm run test:e2e
10
+ */
11
+ export default defineConfig({
12
+ test: {
13
+ globals: true,
14
+ environment: "node",
15
+ include: ["src/__tests__/e2e/**/*.test.ts"],
16
+ testTimeout: 180000, // 3 minutes for E2E tests (fact extraction takes time)
17
+ hookTimeout: 60000, // 1 minute for setup/teardown
18
+ clearMocks: true,
19
+ restoreMocks: true,
20
+ // Run E2E tests sequentially to avoid rate limits
21
+ pool: "forks",
22
+ poolOptions: {
23
+ forks: {
24
+ singleFork: true,
25
+ },
26
+ },
27
+ },
28
+ });
@@ -0,0 +1,25 @@
1
+ import { defineConfig } from "vitest/config";
2
+
3
+ /**
4
+ * Configuration for integration tests.
5
+ * These tests use mocked SDK but test real component interactions.
6
+ */
7
+ export default defineConfig({
8
+ test: {
9
+ globals: true,
10
+ environment: "node",
11
+ include: ["src/__tests__/integration/**/*.test.ts"],
12
+ testTimeout: 30000, // 30 seconds for integration tests
13
+ coverage: {
14
+ provider: "v8",
15
+ reporter: ["text", "json", "html"],
16
+ include: ["src/**/*.ts"],
17
+ exclude: [
18
+ "src/**/*.test.ts",
19
+ "src/__tests__/**",
20
+ ],
21
+ },
22
+ clearMocks: true,
23
+ restoreMocks: true,
24
+ },
25
+ });
@@ -24,7 +24,7 @@ export async function GET() {
24
24
 
25
25
  return Response.json(
26
26
  { error: "Failed to check admin setup status" },
27
- { status: 500 }
27
+ { status: 500 },
28
28
  );
29
29
  }
30
30
  }
@@ -12,7 +12,7 @@ import { verifyPassword, generateSessionToken } from "@/lib/password";
12
12
  * Returns validated credentials or null if invalid.
13
13
  */
14
14
  function validateLoginBody(
15
- body: unknown
15
+ body: unknown,
16
16
  ): { username: string; password: string } | null {
17
17
  if (typeof body !== "object" || body === null) {
18
18
  return null;
@@ -65,7 +65,7 @@ export async function POST(req: Request) {
65
65
  if (!credentials) {
66
66
  return Response.json(
67
67
  { error: "Username and password are required" },
68
- { status: 400 }
68
+ { status: 400 },
69
69
  );
70
70
  }
71
71
 
@@ -79,7 +79,7 @@ export async function POST(req: Request) {
79
79
  if (!user) {
80
80
  return Response.json(
81
81
  { error: "Invalid username or password" },
82
- { status: 401 }
82
+ { status: 401 },
83
83
  );
84
84
  }
85
85
 
@@ -88,7 +88,7 @@ export async function POST(req: Request) {
88
88
  if (!storedHash) {
89
89
  return Response.json(
90
90
  { error: "Invalid username or password" },
91
- { status: 401 }
91
+ { status: 401 },
92
92
  );
93
93
  }
94
94
 
@@ -96,7 +96,7 @@ export async function POST(req: Request) {
96
96
  if (!isValid) {
97
97
  return Response.json(
98
98
  { error: "Invalid username or password" },
99
- { status: 401 }
99
+ { status: 401 },
100
100
  );
101
101
  }
102
102
 
@@ -120,9 +120,6 @@ export async function POST(req: Request) {
120
120
  // Log sanitized error to prevent log injection
121
121
  console.error("[Login Error]", getSafeErrorMessage(error));
122
122
 
123
- return Response.json(
124
- { error: "Failed to authenticate" },
125
- { status: 500 }
126
- );
123
+ return Response.json({ error: "Failed to authenticate" }, { status: 500 });
127
124
  }
128
125
  }
@@ -14,39 +14,38 @@ export async function POST(req: Request) {
14
14
 
15
15
  // Validate input
16
16
  if (!username || typeof username !== "string") {
17
- return Response.json(
18
- { error: "Username is required" },
19
- { status: 400 }
20
- );
17
+ return Response.json({ error: "Username is required" }, { status: 400 });
21
18
  }
22
19
 
23
20
  if (!password || typeof password !== "string") {
24
- return Response.json(
25
- { error: "Password is required" },
26
- { status: 400 }
27
- );
21
+ return Response.json({ error: "Password is required" }, { status: 400 });
28
22
  }
29
23
 
30
24
  if (username.length < 2) {
31
25
  return Response.json(
32
26
  { error: "Username must be at least 2 characters" },
33
- { status: 400 }
27
+ { status: 400 },
34
28
  );
35
29
  }
36
30
 
37
31
  if (password.length < 4) {
38
32
  return Response.json(
39
33
  { error: "Password must be at least 4 characters" },
40
- { status: 400 }
34
+ { status: 400 },
41
35
  );
42
36
  }
43
37
 
44
38
  // Sanitize username (alphanumeric, underscore, hyphen only)
45
- const sanitizedUsername = username.toLowerCase().replace(/[^a-z0-9_-]/g, "");
39
+ const sanitizedUsername = username
40
+ .toLowerCase()
41
+ .replace(/[^a-z0-9_-]/g, "");
46
42
  if (sanitizedUsername !== username.toLowerCase()) {
47
43
  return Response.json(
48
- { error: "Username can only contain letters, numbers, underscores, and hyphens" },
49
- { status: 400 }
44
+ {
45
+ error:
46
+ "Username can only contain letters, numbers, underscores, and hyphens",
47
+ },
48
+ { status: 400 },
50
49
  );
51
50
  }
52
51
 
@@ -57,7 +56,7 @@ export async function POST(req: Request) {
57
56
  if (existingUser) {
58
57
  return Response.json(
59
58
  { error: "Username already taken" },
60
- { status: 409 }
59
+ { status: 409 },
61
60
  );
62
61
  }
63
62
 
@@ -86,9 +85,6 @@ export async function POST(req: Request) {
86
85
  } catch (error) {
87
86
  console.error("[Register Error]", error);
88
87
 
89
- return Response.json(
90
- { error: "Failed to register user" },
91
- { status: 500 }
92
- );
88
+ return Response.json({ error: "Failed to register user" }, { status: 500 });
93
89
  }
94
90
  }
@@ -16,16 +16,13 @@ export async function POST(req: Request) {
16
16
  const { password } = body;
17
17
 
18
18
  if (!password || typeof password !== "string") {
19
- return Response.json(
20
- { error: "Password is required" },
21
- { status: 400 }
22
- );
19
+ return Response.json({ error: "Password is required" }, { status: 400 });
23
20
  }
24
21
 
25
22
  if (password.length < 4) {
26
23
  return Response.json(
27
24
  { error: "Password must be at least 4 characters" },
28
- { status: 400 }
25
+ { status: 400 },
29
26
  );
30
27
  }
31
28
 
@@ -36,7 +33,7 @@ export async function POST(req: Request) {
36
33
  if (existingHash !== null) {
37
34
  return Response.json(
38
35
  { error: "Admin already configured" },
39
- { status: 409 }
36
+ { status: 409 },
40
37
  );
41
38
  }
42
39
 
@@ -53,7 +50,7 @@ export async function POST(req: Request) {
53
50
 
54
51
  return Response.json(
55
52
  { error: "Failed to configure admin password" },
56
- { status: 500 }
53
+ { status: 500 },
57
54
  );
58
55
  }
59
56
  }
@@ -164,7 +164,10 @@ function normalizeMessages(messages: unknown[]): unknown[] {
164
164
  /**
165
165
  * Extract text from a message (handles both content string and parts array)
166
166
  */
167
- function getMessageText(message: { content?: string; parts?: Array<{ type: string; text?: string }> }): string {
167
+ function getMessageText(message: {
168
+ content?: string;
169
+ parts?: Array<{ type: string; text?: string }>;
170
+ }): string {
168
171
  if (typeof message.content === "string") {
169
172
  return message.content;
170
173
  }
@@ -180,7 +183,12 @@ function getMessageText(message: { content?: string; parts?: Array<{ type: strin
180
183
  export async function POST(req: Request) {
181
184
  try {
182
185
  const body = await req.json();
183
- const { messages, memorySpaceId, userId, conversationId: providedConversationId } = body;
186
+ const {
187
+ messages,
188
+ memorySpaceId,
189
+ userId,
190
+ conversationId: providedConversationId,
191
+ } = body;
184
192
 
185
193
  // Validate messages array exists
186
194
  if (!messages || !Array.isArray(messages)) {
@@ -191,7 +199,8 @@ export async function POST(req: Request) {
191
199
  }
192
200
 
193
201
  // Generate conversation ID if not provided (new chat)
194
- const conversationId = providedConversationId ||
202
+ const conversationId =
203
+ providedConversationId ||
195
204
  `conv-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
196
205
  const isNewConversation = !providedConversationId;
197
206
 
@@ -202,20 +211,28 @@ export async function POST(req: Request) {
202
211
  // Convert UIMessage[] from useChat to ModelMessage[] for streamText
203
212
  // Note: In AI SDK v6+, convertToModelMessages may return a Promise
204
213
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
205
- const modelMessagesResult = convertToModelMessages(normalizedMessages as any);
214
+ const modelMessagesResult = convertToModelMessages(
215
+ normalizedMessages as any,
216
+ );
206
217
  const modelMessages =
207
218
  modelMessagesResult instanceof Promise
208
219
  ? await modelMessagesResult
209
220
  : modelMessagesResult;
210
221
 
211
222
  // Get the first user message for title generation
212
- const firstUserMessage = messages.find((m: { role: string }) => m.role === "user") as {
213
- role: string;
214
- content?: string;
215
- parts?: Array<{ type: string; text?: string }>;
216
- } | undefined;
217
-
218
- const messageText = firstUserMessage ? getMessageText(firstUserMessage) : "";
223
+ const firstUserMessage = messages.find(
224
+ (m: { role: string }) => m.role === "user",
225
+ ) as
226
+ | {
227
+ role: string;
228
+ content?: string;
229
+ parts?: Array<{ type: string; text?: string }>;
230
+ }
231
+ | undefined;
232
+
233
+ const messageText = firstUserMessage
234
+ ? getMessageText(firstUserMessage)
235
+ : "";
219
236
 
220
237
  // Use createUIMessageStream to send both LLM text and layer events
221
238
  return createUIMessageStreamResponse({
@@ -52,7 +52,7 @@ function getCortexMemoryConfig(
52
52
  memorySpaceId: string,
53
53
  userId: string,
54
54
  conversationId: string,
55
- layerObserver?: LayerObserver
55
+ layerObserver?: LayerObserver,
56
56
  ): CortexMemoryConfig {
57
57
  return {
58
58
  convexUrl: process.env.CONVEX_URL!,
@@ -191,7 +191,7 @@ export async function POST(req: Request) {
191
191
  if (!messages || !Array.isArray(messages)) {
192
192
  return new Response(
193
193
  JSON.stringify({ error: "messages array is required" }),
194
- { status: 400, headers: { "Content-Type": "application/json" } }
194
+ { status: 400, headers: { "Content-Type": "application/json" } },
195
195
  );
196
196
  }
197
197
 
@@ -206,7 +206,9 @@ export async function POST(req: Request) {
206
206
 
207
207
  // Convert to model messages
208
208
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
209
- const modelMessagesResult = convertToModelMessages(normalizedMessages as any);
209
+ const modelMessagesResult = convertToModelMessages(
210
+ normalizedMessages as any,
211
+ );
210
212
  const modelMessages =
211
213
  modelMessagesResult instanceof Promise
212
214
  ? await modelMessagesResult
@@ -214,14 +216,18 @@ export async function POST(req: Request) {
214
216
 
215
217
  // Get first user message for title
216
218
  const firstUserMessage = messages.find(
217
- (m: { role: string }) => m.role === "user"
218
- ) as {
219
- role: string;
220
- content?: string;
221
- parts?: Array<{ type: string; text?: string }>;
222
- } | undefined;
223
-
224
- const messageText = firstUserMessage ? getMessageText(firstUserMessage) : "";
219
+ (m: { role: string }) => m.role === "user",
220
+ ) as
221
+ | {
222
+ role: string;
223
+ content?: string;
224
+ parts?: Array<{ type: string; text?: string }>;
225
+ }
226
+ | undefined;
227
+
228
+ const messageText = firstUserMessage
229
+ ? getMessageText(firstUserMessage)
230
+ : "";
225
231
 
226
232
  // Use createUIMessageStreamResponse - same as v5 for full memory support
227
233
  return createUIMessageStreamResponse({
@@ -270,7 +276,7 @@ export async function POST(req: Request) {
270
276
  memorySpaceId,
271
277
  userId,
272
278
  conversationId,
273
- layerObserver
279
+ layerObserver,
274
280
  );
275
281
 
276
282
  // Create memory-augmented model - THIS handles both recall AND storage!
@@ -327,7 +333,7 @@ export async function POST(req: Request) {
327
333
  {
328
334
  status: 500,
329
335
  headers: { "Content-Type": "application/json" },
330
- }
336
+ },
331
337
  );
332
338
  }
333
339
  }