@m6d/cortex-server 1.1.0 → 1.1.2

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 (42) hide show
  1. package/README.md +38 -38
  2. package/dist/src/factory.d.ts +13 -1
  3. package/dist/src/ws/index.d.ts +1 -1
  4. package/package.json +54 -54
  5. package/src/adapters/database.ts +21 -28
  6. package/src/adapters/minio.ts +69 -69
  7. package/src/adapters/mssql.ts +171 -195
  8. package/src/adapters/storage.ts +4 -4
  9. package/src/ai/fetch.ts +31 -31
  10. package/src/ai/helpers.ts +18 -22
  11. package/src/ai/index.ts +101 -113
  12. package/src/ai/interceptors/resolve-captured-files.ts +42 -49
  13. package/src/ai/prompt.ts +80 -83
  14. package/src/ai/tools/call-endpoint.tool.ts +75 -82
  15. package/src/ai/tools/capture-files.tool.ts +15 -17
  16. package/src/ai/tools/execute-code.tool.ts +73 -80
  17. package/src/ai/tools/query-graph.tool.ts +17 -17
  18. package/src/auth/middleware.ts +51 -51
  19. package/src/cli/extract-endpoints.ts +436 -474
  20. package/src/config.ts +124 -134
  21. package/src/db/migrate.ts +13 -13
  22. package/src/db/migrations/20260309012148_cloudy_maria_hill/snapshot.json +303 -303
  23. package/src/db/schema.ts +46 -58
  24. package/src/factory.ts +136 -139
  25. package/src/graph/generate-cypher.ts +97 -97
  26. package/src/graph/helpers.ts +37 -37
  27. package/src/graph/index.ts +20 -20
  28. package/src/graph/neo4j.ts +82 -89
  29. package/src/graph/resolver.ts +201 -211
  30. package/src/graph/seed.ts +101 -114
  31. package/src/graph/types.ts +88 -88
  32. package/src/graph/validate.ts +55 -57
  33. package/src/index.ts +5 -5
  34. package/src/routes/chat.ts +23 -23
  35. package/src/routes/files.ts +75 -80
  36. package/src/routes/threads.ts +52 -54
  37. package/src/routes/ws.ts +22 -22
  38. package/src/types.ts +30 -30
  39. package/src/ws/connections.ts +11 -11
  40. package/src/ws/events.ts +2 -2
  41. package/src/ws/index.ts +1 -5
  42. package/src/ws/notify.ts +4 -4
@@ -7,32 +7,32 @@ import { requireAuth } from "../auth/middleware.ts";
7
7
  import { stream } from "../ai/index.ts";
8
8
 
9
9
  export function createChatRoutes() {
10
- const app = new Hono<CortexAppEnv>();
10
+ const app = new Hono<CortexAppEnv>();
11
11
 
12
- app.post(
13
- "/chat",
14
- requireAuth,
15
- zValidator(
16
- "json",
17
- z.object({
18
- id: z.string(),
19
- messages: z.array(z.unknown()),
20
- }),
21
- ),
22
- async function (c) {
23
- const config = c.get("agentConfig");
24
- const { id: userId, token } = c.get("user");
25
- const { messages, id: threadId } = c.req.valid("json");
12
+ app.post(
13
+ "/chat",
14
+ requireAuth,
15
+ zValidator(
16
+ "json",
17
+ z.object({
18
+ id: z.string(),
19
+ messages: z.array(z.unknown()),
20
+ }),
21
+ ),
22
+ async function (c) {
23
+ const config = c.get("agentConfig");
24
+ const { id: userId, token } = c.get("user");
25
+ const { messages, id: threadId } = c.req.valid("json");
26
26
 
27
- const thread = await config.db.threads.getById(userId, threadId);
27
+ const thread = await config.db.threads.getById(userId, threadId);
28
28
 
29
- if (!thread) {
30
- throw new HTTPException(404, { message: "Not found" });
31
- }
29
+ if (!thread) {
30
+ throw new HTTPException(404, { message: "Not found" });
31
+ }
32
32
 
33
- return stream(messages, thread, userId, token, config);
34
- },
35
- );
33
+ return stream(messages, thread, userId, token, config);
34
+ },
35
+ );
36
36
 
37
- return app;
37
+ return app;
38
38
  }
@@ -7,99 +7,94 @@ import type { CortexAppEnv } from "../types.ts";
7
7
  import { requireAuth } from "../auth/middleware.ts";
8
8
 
9
9
  export function createFileRoutes() {
10
- const app = new Hono<CortexAppEnv>();
10
+ const app = new Hono<CortexAppEnv>();
11
11
 
12
- app.get("/files/:id", requireAuth, async function (c) {
13
- const config = c.get("agentConfig");
14
- const id = c.req.param("id");
15
- const { id: userId } = c.get("user");
16
- const file = await config.db.capturedFiles.getById(id, userId);
12
+ app.get("/files/:id", requireAuth, async function (c) {
13
+ const config = c.get("agentConfig");
14
+ const id = c.req.param("id");
15
+ const { id: userId } = c.get("user");
16
+ const file = await config.db.capturedFiles.getById(id, userId);
17
17
 
18
- if (!file) {
19
- throw new HTTPException(404, { message: "File not found" });
20
- }
18
+ if (!file) {
19
+ throw new HTTPException(404, { message: "File not found" });
20
+ }
21
21
 
22
- const fileStream = await config.storage.stream(`captured_files/${id}`);
23
- return new Response(fileStream, {
24
- headers: {
25
- "Content-Type": "application/octet-stream",
26
- "Content-Disposition": `attachment; filename="${file.name}"`,
27
- },
22
+ const fileStream = await config.storage.stream(`captured_files/${id}`);
23
+ return new Response(fileStream, {
24
+ headers: {
25
+ "Content-Type": "application/octet-stream",
26
+ "Content-Disposition": `attachment; filename="${file.name}"`,
27
+ },
28
+ });
28
29
  });
29
- });
30
30
 
31
- app.post(
32
- "/files",
33
- requireAuth,
34
- zValidator(
35
- "json",
36
- z.object({
37
- toolCallId: z.string(),
38
- message: z.unknown().transform(async (message, ctx) => {
39
- const result = await safeValidateUIMessages({
40
- messages: [message],
41
- });
42
- if (!result.success) {
43
- ctx.addIssue({
44
- code: "custom",
45
- path: ["message"],
46
- message: "Invalid message format",
47
- });
48
- return z.NEVER;
49
- }
31
+ app.post(
32
+ "/files",
33
+ requireAuth,
34
+ zValidator(
35
+ "json",
36
+ z.object({
37
+ toolCallId: z.string(),
38
+ message: z.unknown().transform(async (message, ctx) => {
39
+ const result = await safeValidateUIMessages({
40
+ messages: [message],
41
+ });
42
+ if (!result.success) {
43
+ ctx.addIssue({
44
+ code: "custom",
45
+ path: ["message"],
46
+ message: "Invalid message format",
47
+ });
48
+ return z.NEVER;
49
+ }
50
50
 
51
- return result.data[0]!;
52
- }),
53
- files: z.array(
54
- z.object({
55
- id: z.string(),
56
- file: z.object({
57
- name: z.string(),
58
- bytes: z.string(),
51
+ return result.data[0]!;
52
+ }),
53
+ files: z.array(
54
+ z.object({
55
+ id: z.string(),
56
+ file: z.object({
57
+ name: z.string(),
58
+ bytes: z.string(),
59
+ }),
60
+ }),
61
+ ),
59
62
  }),
60
- }),
61
63
  ),
62
- }),
63
- ),
64
- async function (c) {
65
- const config = c.get("agentConfig");
66
- const { message, toolCallId, files } = c.req.valid("json");
67
- const { id: userId } = c.get("user");
64
+ async function (c) {
65
+ const config = c.get("agentConfig");
66
+ const { message, toolCallId, files } = c.req.valid("json");
67
+ const { id: userId } = c.get("user");
68
68
 
69
- function isTool(value: UIMessage["parts"][number]): value is ToolUIPart {
70
- return value.type.startsWith("tool-");
71
- }
69
+ function isTool(value: UIMessage["parts"][number]): value is ToolUIPart {
70
+ return value.type.startsWith("tool-");
71
+ }
72
72
 
73
- const toolPart = message.parts
74
- .filter(isTool)
75
- .find((x) => x.toolCallId === toolCallId);
73
+ const toolPart = message.parts.filter(isTool).find((x) => x.toolCallId === toolCallId);
76
74
 
77
- if (!toolPart) {
78
- throw new HTTPException(423, {
79
- message: "Invalid tool call id",
80
- });
81
- }
75
+ if (!toolPart) {
76
+ throw new HTTPException(423, {
77
+ message: "Invalid tool call id",
78
+ });
79
+ }
82
80
 
83
- const result = await config.db.capturedFiles.create(
84
- userId,
85
- message.id,
86
- toolPart,
87
- files,
88
- );
81
+ const result = await config.db.capturedFiles.create(
82
+ userId,
83
+ message.id,
84
+ toolPart,
85
+ files,
86
+ );
89
87
 
90
- // Store file bytes
91
- await Promise.all(
92
- result.map(async (r, i) => {
93
- await config.storage.put(
94
- `captured_files/${r.uploadId}`,
95
- files[i]!.file.bytes,
96
- );
97
- }),
98
- );
88
+ // Store file bytes
89
+ await Promise.all(
90
+ result.map(async (r, i) => {
91
+ await config.storage.put(`captured_files/${r.uploadId}`, files[i]!.file.bytes);
92
+ }),
93
+ );
99
94
 
100
- return c.json(result);
101
- },
102
- );
95
+ return c.json(result);
96
+ },
97
+ );
103
98
 
104
- return app;
99
+ return app;
105
100
  }
@@ -6,64 +6,62 @@ import { requireAuth } from "../auth/middleware.ts";
6
6
  import { generateTitle } from "../ai/index.ts";
7
7
 
8
8
  export function createThreadRoutes() {
9
- const app = new Hono<CortexAppEnv>();
9
+ const app = new Hono<CortexAppEnv>();
10
10
 
11
- app.get("/threads", requireAuth, async function (c) {
12
- const config = c.get("agentConfig");
13
- const agentId = c.get("agentId");
14
- const threads = await config.db.threads
15
- .list(c.get("user").id, agentId)
16
- .then((x) =>
17
- x.map((y) => ({ id: y.id, title: y.title, createdAt: y.createdAt })),
18
- );
19
- return c.json(threads);
20
- });
11
+ app.get("/threads", requireAuth, async function (c) {
12
+ const config = c.get("agentConfig");
13
+ const agentId = c.get("agentId");
14
+ const threads = await config.db.threads
15
+ .list(c.get("user").id, agentId)
16
+ .then((x) => x.map((y) => ({ id: y.id, title: y.title, createdAt: y.createdAt })));
17
+ return c.json(threads);
18
+ });
21
19
 
22
- app.post(
23
- "/threads",
24
- requireAuth,
25
- zValidator("json", z.object({ prompt: z.string() })),
26
- async function (c) {
27
- const config = c.get("agentConfig");
28
- const agentId = c.get("agentId");
29
- const { prompt } = c.req.valid("json");
30
- const { id, createdAt, title } = await config.db.threads.create(
31
- c.get("user").id,
32
- agentId,
33
- );
34
- generateTitle(id, prompt, c.get("user").id, config);
35
- return c.json({ id, title, createdAt });
36
- },
37
- );
20
+ app.post(
21
+ "/threads",
22
+ requireAuth,
23
+ zValidator("json", z.object({ prompt: z.string() })),
24
+ async function (c) {
25
+ const config = c.get("agentConfig");
26
+ const agentId = c.get("agentId");
27
+ const { prompt } = c.req.valid("json");
28
+ const { id, createdAt, title } = await config.db.threads.create(
29
+ c.get("user").id,
30
+ agentId,
31
+ );
32
+ generateTitle(id, prompt, c.get("user").id, config);
33
+ return c.json({ id, title, createdAt });
34
+ },
35
+ );
38
36
 
39
- app.delete("/threads/:threadId", requireAuth, async function (c) {
40
- const config = c.get("agentConfig");
41
- const threadId = c.req.param("threadId");
42
- await config.db.threads.delete(c.get("user").id, threadId);
43
- return c.body(null, 204);
44
- });
37
+ app.delete("/threads/:threadId", requireAuth, async function (c) {
38
+ const config = c.get("agentConfig");
39
+ const threadId = c.req.param("threadId");
40
+ await config.db.threads.delete(c.get("user").id, threadId);
41
+ return c.body(null, 204);
42
+ });
45
43
 
46
- app.get("/threads/:threadId/messages", requireAuth, async function (c) {
47
- const config = c.get("agentConfig");
48
- const threadId = c.req.param("threadId");
49
- const messages = await config.db.messages
50
- .list(c.get("user").id, threadId)
51
- .then((x) => x.map((y) => y.content));
52
- return c.json(messages);
53
- });
44
+ app.get("/threads/:threadId/messages", requireAuth, async function (c) {
45
+ const config = c.get("agentConfig");
46
+ const threadId = c.req.param("threadId");
47
+ const messages = await config.db.messages
48
+ .list(c.get("user").id, threadId)
49
+ .then((x) => x.map((y) => y.content));
50
+ return c.json(messages);
51
+ });
54
52
 
55
- app.post(
56
- "/threads/:threadId/session",
57
- requireAuth,
58
- zValidator("json", z.object({ session: z.record(z.unknown()) })),
59
- async function (c) {
60
- const config = c.get("agentConfig");
61
- const threadId = c.req.param("threadId");
62
- const { session } = c.req.valid("json");
63
- await config.db.threads.updateSession(threadId, session);
64
- return c.body(null, 204);
65
- },
66
- );
53
+ app.post(
54
+ "/threads/:threadId/session",
55
+ requireAuth,
56
+ zValidator("json", z.object({ session: z.record(z.unknown()) })),
57
+ async function (c) {
58
+ const config = c.get("agentConfig");
59
+ const threadId = c.req.param("threadId");
60
+ const { session } = c.req.valid("json");
61
+ await config.db.threads.updateSession(threadId, session);
62
+ return c.body(null, 204);
63
+ },
64
+ );
67
65
 
68
- return app;
66
+ return app;
69
67
  }
package/src/routes/ws.ts CHANGED
@@ -4,30 +4,30 @@ import type { AppEnv } from "../types.ts";
4
4
  import { addConnection, removeConnection } from "../ws/index.ts";
5
5
 
6
6
  export function createWsRoute() {
7
- const app = new Hono<AppEnv>();
7
+ const app = new Hono<AppEnv>();
8
8
 
9
- app.get(
10
- "/ws",
11
- upgradeWebSocket(function (c) {
12
- const user = c.get("user");
13
- const userId = user?.id;
9
+ app.get(
10
+ "/ws",
11
+ upgradeWebSocket(function (c) {
12
+ const user = c.get("user");
13
+ const userId = user?.id;
14
14
 
15
- return {
16
- onOpen(_, ws) {
17
- if (userId) {
18
- addConnection(userId, ws);
19
- }
20
- ws.send("Connected!");
21
- },
15
+ return {
16
+ onOpen(_, ws) {
17
+ if (userId) {
18
+ addConnection(userId, ws);
19
+ }
20
+ ws.send("Connected!");
21
+ },
22
22
 
23
- onClose(_, ws) {
24
- if (userId) {
25
- removeConnection(userId, ws);
26
- }
27
- },
28
- };
29
- }),
30
- );
23
+ onClose(_, ws) {
24
+ if (userId) {
25
+ removeConnection(userId, ws);
26
+ }
27
+ },
28
+ };
29
+ }),
30
+ );
31
31
 
32
- return app;
32
+ return app;
33
33
  }
package/src/types.ts CHANGED
@@ -2,49 +2,49 @@ import type { ToolUIPart, UIMessage } from "ai";
2
2
  import type { ResolvedCortexAgentConfig } from "./config";
3
3
 
4
4
  export type Thread = {
5
- id: string;
6
- userId: string;
7
- agentId: string;
8
- title: string | null;
9
- session: Record<string, unknown> | null;
10
- createdAt: Date;
11
- updatedAt: Date;
5
+ id: string;
6
+ userId: string;
7
+ agentId: string;
8
+ title: string | null;
9
+ session: Record<string, unknown> | null;
10
+ createdAt: Date;
11
+ updatedAt: Date;
12
12
  };
13
13
 
14
14
  export type StoredMessage = {
15
- id: string;
16
- threadId: string;
17
- text: string | null;
18
- content: UIMessage;
19
- role: "system" | "user" | "assistant" | "tool";
20
- createdAt: Date;
21
- updatedAt: Date;
15
+ id: string;
16
+ threadId: string;
17
+ text: string | null;
18
+ content: UIMessage;
19
+ role: "system" | "user" | "assistant" | "tool";
20
+ createdAt: Date;
21
+ updatedAt: Date;
22
22
  };
23
23
 
24
24
  export type CapturedFileInput = {
25
- id: string;
26
- file: { name: string; bytes: string };
25
+ id: string;
26
+ file: { name: string; bytes: string };
27
27
  };
28
28
 
29
29
  export type AppEnv = {
30
- Bindings: {};
31
- Variables: {
32
- user: { id: string; token: string } | undefined;
33
- };
30
+ Bindings: {};
31
+ Variables: {
32
+ user: { id: string; token: string } | undefined;
33
+ };
34
34
  };
35
35
 
36
36
  export type AuthedAppEnv = {
37
- Bindings: {};
38
- Variables: {
39
- user: { id: string; token: string };
40
- };
37
+ Bindings: {};
38
+ Variables: {
39
+ user: { id: string; token: string };
40
+ };
41
41
  };
42
42
 
43
43
  export type CortexAppEnv = {
44
- Bindings: {};
45
- Variables: {
46
- user: { id: string; token: string };
47
- agentConfig: ResolvedCortexAgentConfig;
48
- agentId: string;
49
- };
44
+ Bindings: {};
45
+ Variables: {
46
+ user: { id: string; token: string };
47
+ agentConfig: ResolvedCortexAgentConfig;
48
+ agentId: string;
49
+ };
50
50
  };
@@ -3,21 +3,21 @@ import type { WSContext } from "hono/ws";
3
3
  const connections = new Map<string, WSContext[]>();
4
4
 
5
5
  export function addConnection(userId: string, ws: WSContext) {
6
- const sockets = connections.get(userId) ?? [];
7
- sockets.push(ws);
8
- connections.set(userId, sockets);
6
+ const sockets = connections.get(userId) ?? [];
7
+ sockets.push(ws);
8
+ connections.set(userId, sockets);
9
9
  }
10
10
 
11
11
  export function removeConnection(userId: string, ws: WSContext) {
12
- let sockets = connections.get(userId) ?? [];
13
- sockets = sockets.filter((x) => x !== ws);
14
- if (sockets.length) {
15
- connections.set(userId, sockets);
16
- } else {
17
- connections.delete(userId);
18
- }
12
+ let sockets = connections.get(userId) ?? [];
13
+ sockets = sockets.filter((x) => x !== ws);
14
+ if (sockets.length) {
15
+ connections.set(userId, sockets);
16
+ } else {
17
+ connections.delete(userId);
18
+ }
19
19
  }
20
20
 
21
21
  export function getConnections(userId: string) {
22
- return connections.get(userId) ?? [];
22
+ return connections.get(userId) ?? [];
23
23
  }
package/src/ws/events.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export type ThreadTitleUpdatedEvent = {
2
- type: "thread:title-updated";
3
- payload: { threadId: string; title: string };
2
+ type: "thread:title-updated";
3
+ payload: { threadId: string; title: string };
4
4
  };
5
5
 
6
6
  export type WsEvent = ThreadTitleUpdatedEvent;
package/src/ws/index.ts CHANGED
@@ -1,7 +1,3 @@
1
- export {
2
- addConnection,
3
- removeConnection,
4
- getConnections,
5
- } from "./connections.ts";
1
+ export { addConnection, removeConnection, getConnections } from "./connections.ts";
6
2
  export { notify } from "./notify.ts";
7
3
  export type { WsEvent, ThreadTitleUpdatedEvent } from "./events.ts";
package/src/ws/notify.ts CHANGED
@@ -2,8 +2,8 @@ import { getConnections } from "./connections.ts";
2
2
  import type { WsEvent } from "./events.ts";
3
3
 
4
4
  export function notify(userId: string, event: WsEvent) {
5
- const message = JSON.stringify(event);
6
- for (const ws of getConnections(userId)) {
7
- ws.send(message);
8
- }
5
+ const message = JSON.stringify(event);
6
+ for (const ws of getConnections(userId)) {
7
+ ws.send(message);
8
+ }
9
9
  }