@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
package/src/db/schema.ts CHANGED
@@ -1,77 +1,65 @@
1
1
  import type { ToolUIPart, UIMessage } from "ai";
2
2
  import { sql } from "drizzle-orm";
3
- import {
4
- customType,
5
- datetime2,
6
- index,
7
- mssqlSchema,
8
- nvarchar,
9
- } from "drizzle-orm/mssql-core";
3
+ import { customType, datetime2, index, mssqlSchema, nvarchar } from "drizzle-orm/mssql-core";
10
4
 
11
5
  const uniqueIdentifier = customType<{ data: string }>({
12
- dataType() {
13
- return "UNIQUEIDENTIFIER";
14
- },
6
+ dataType() {
7
+ return "UNIQUEIDENTIFIER";
8
+ },
15
9
  });
16
10
 
17
11
  export const aiSchema = mssqlSchema("ai");
18
12
 
19
13
  const auditColumns = {
20
- createdAt: datetime2({ mode: "date" })
21
- .$default(() => new Date())
22
- .notNull(),
23
- updatedAt: datetime2({ mode: "date" })
24
- .$default(() => new Date())
25
- .$onUpdate(() => new Date())
26
- .notNull(),
14
+ createdAt: datetime2({ mode: "date" })
15
+ .$default(() => new Date())
16
+ .notNull(),
17
+ updatedAt: datetime2({ mode: "date" })
18
+ .$default(() => new Date())
19
+ .$onUpdate(() => new Date())
20
+ .notNull(),
27
21
  };
28
22
 
29
23
  export const threads = aiSchema.table("threads", {
30
- id: uniqueIdentifier()
31
- .default(sql`NEWID()`)
32
- .primaryKey(),
33
- userId: uniqueIdentifier().notNull(),
34
- agentId: nvarchar({ length: 128 }).notNull().default("default"),
35
- title: nvarchar({ length: 256 }),
36
- session: nvarchar({ mode: "json", length: "max" }).$type<
37
- Record<string, unknown>
38
- >(),
39
- ...auditColumns,
24
+ id: uniqueIdentifier()
25
+ .default(sql`NEWID()`)
26
+ .primaryKey(),
27
+ userId: uniqueIdentifier().notNull(),
28
+ agentId: nvarchar({ length: 128 }).notNull().default("default"),
29
+ title: nvarchar({ length: 256 }),
30
+ session: nvarchar({ mode: "json", length: "max" }).$type<Record<string, unknown>>(),
31
+ ...auditColumns,
40
32
  });
41
33
 
42
34
  export const messages = aiSchema.table("messages", {
43
- id: nvarchar({ length: 128 }).primaryKey(),
44
- threadId: uniqueIdentifier()
45
- .references(() => threads.id, { onDelete: "cascade" })
46
- .notNull(),
47
- text: nvarchar({ length: "max" }),
48
- content: nvarchar({ mode: "json", length: "max" })
49
- .notNull()
50
- .$type<UIMessage>(),
51
- role: nvarchar({
52
- enum: ["system", "user", "assistant", "tool"],
53
- length: 16,
54
- }).notNull(),
55
- ...auditColumns,
35
+ id: nvarchar({ length: 128 }).primaryKey(),
36
+ threadId: uniqueIdentifier()
37
+ .references(() => threads.id, { onDelete: "cascade" })
38
+ .notNull(),
39
+ text: nvarchar({ length: "max" }),
40
+ content: nvarchar({ mode: "json", length: "max" }).notNull().$type<UIMessage>(),
41
+ role: nvarchar({
42
+ enum: ["system", "user", "assistant", "tool"],
43
+ length: 16,
44
+ }).notNull(),
45
+ ...auditColumns,
56
46
  });
57
47
 
58
48
  export const capturedFiles = aiSchema.table(
59
- "captured_files",
60
- {
61
- id: uniqueIdentifier()
62
- .default(sql`NEWID()`)
63
- .primaryKey(),
64
- name: nvarchar({ length: 2048 }).notNull(),
65
- userId: uniqueIdentifier().notNull(),
66
- agentGeneratedId: nvarchar({ length: 1024 }).notNull(),
67
- messageId: nvarchar({ length: 128 })
68
- .references(() => messages.id, {
69
- onDelete: "cascade",
70
- })
71
- .notNull(),
72
- toolPart: nvarchar({ length: "max", mode: "json" })
73
- .notNull()
74
- .$type<ToolUIPart>(),
75
- },
76
- (table) => [index(table.agentGeneratedId)],
49
+ "captured_files",
50
+ {
51
+ id: uniqueIdentifier()
52
+ .default(sql`NEWID()`)
53
+ .primaryKey(),
54
+ name: nvarchar({ length: 2048 }).notNull(),
55
+ userId: uniqueIdentifier().notNull(),
56
+ agentGeneratedId: nvarchar({ length: 1024 }).notNull(),
57
+ messageId: nvarchar({ length: 128 })
58
+ .references(() => messages.id, {
59
+ onDelete: "cascade",
60
+ })
61
+ .notNull(),
62
+ toolPart: nvarchar({ length: "max", mode: "json" }).notNull().$type<ToolUIPart>(),
63
+ },
64
+ (table) => [index(table.agentGeneratedId)],
77
65
  );
package/src/factory.ts CHANGED
@@ -16,148 +16,145 @@ import { seedGraph as seedGraphFn } from "./graph/seed.ts";
16
16
  import { extractEndpoints as extractEndpointsFn } from "./cli/extract-endpoints.ts";
17
17
 
18
18
  export type CortexInstance = {
19
- serve(): Promise<{
20
- app: Hono<AppEnv>;
21
- port: number;
22
- fetch: Hono<AppEnv>["fetch"];
23
- websocket: typeof websocket;
24
- }>;
25
- seedGraph(): Promise<void>;
26
- extractEndpoints(options: {
27
- domainsDir: string;
28
- write?: boolean;
29
- }): Promise<void>;
19
+ serve(): Promise<{
20
+ app: Hono<AppEnv>;
21
+ port: number;
22
+ fetch: Hono<AppEnv>["fetch"];
23
+ websocket: typeof websocket;
24
+ }>;
25
+ seedGraph(): Promise<void>;
26
+ extractEndpoints(options: { domainsDir: string; write?: boolean }): Promise<void>;
30
27
  };
31
28
 
32
- export function createCortex(config: CortexConfig): CortexInstance {
33
- function collectAllDomains() {
34
- const all: Record<string, DomainDef> = {};
35
- if (config.knowledge?.domains) Object.assign(all, config.knowledge.domains);
36
- for (const agent of Object.values(config.agents)) {
37
- if (agent.knowledge && agent.knowledge.domains) {
38
- Object.assign(all, agent.knowledge.domains);
39
- }
29
+ export function createCortex(config: CortexConfig) {
30
+ function collectAllDomains() {
31
+ const all: Record<string, DomainDef> = {};
32
+ if (config.knowledge?.domains) Object.assign(all, config.knowledge.domains);
33
+ for (const agent of Object.values(config.agents)) {
34
+ if (agent.knowledge && agent.knowledge.domains) {
35
+ Object.assign(all, agent.knowledge.domains);
36
+ }
37
+ }
38
+ return all;
40
39
  }
41
- return all;
42
- }
43
-
44
- function collectAllSwaggerUrls() {
45
- const urls = new Set<string>();
46
- if (config.knowledge?.swagger?.url) urls.add(config.knowledge.swagger.url);
47
- for (const agent of Object.values(config.agents)) {
48
- if (agent.knowledge && agent.knowledge.swagger?.url) {
49
- urls.add(agent.knowledge.swagger.url);
50
- }
40
+
41
+ function collectAllSwaggerUrls() {
42
+ const urls = new Set<string>();
43
+ if (config.knowledge?.swagger?.url) urls.add(config.knowledge.swagger.url);
44
+ for (const agent of Object.values(config.agents)) {
45
+ if (agent.knowledge && agent.knowledge.swagger?.url) {
46
+ urls.add(agent.knowledge.swagger.url);
47
+ }
48
+ }
49
+ return [...urls];
51
50
  }
52
- return [...urls];
53
- }
54
-
55
- return {
56
- async serve() {
57
- const storage = createMinioAdapter(config.storage);
58
- const db = createMssqlAdapter(config.database.connectionString, storage);
59
-
60
- await runMigrations(config.database.connectionString);
61
-
62
- const app = new Hono<AppEnv>();
63
-
64
- const userLoader = createUserLoaderMiddleware(config.auth);
65
- app.use("*", userLoader);
66
-
67
- // WebSocket route (global, not agent-scoped)
68
- app.route("/", createWsRoute());
69
-
70
- // Agent-scoped routes
71
- const agentApp = new Hono<CortexAppEnv>();
72
-
73
- agentApp.use("*", async function (c, next) {
74
- const agentId = c.req.param("agentId") as string;
75
- const agentDef = config.agents[agentId];
76
- if (!agentDef) return c.json({ error: "Agent not found" }, 404);
77
-
78
- const resolvedConfig: ResolvedCortexAgentConfig = {
79
- db,
80
- storage,
81
- model: agentDef.model ?? config.model,
82
- embedding: agentDef.embedding ?? config.embedding,
83
- neo4j: agentDef.neo4j ?? config.neo4j,
84
- reranker: agentDef.reranker ?? config.reranker,
85
- knowledge:
86
- agentDef.knowledge === null
87
- ? undefined
88
- : (agentDef.knowledge ?? config.knowledge),
89
- systemPrompt: agentDef.systemPrompt,
90
- tools: agentDef.tools,
91
- backendFetch: agentDef.backendFetch,
92
- loadSessionData: agentDef.loadSessionData,
93
- onToolCall: agentDef.onToolCall,
94
- onStreamFinish: agentDef.onStreamFinish,
95
- };
96
-
97
- c.set("agentConfig", resolvedConfig);
98
- c.set("agentId", agentId);
99
- await next();
100
- });
101
-
102
- agentApp.route("/", createThreadRoutes());
103
- agentApp.route("/", createChatRoutes());
104
- agentApp.route("/", createFileRoutes());
105
-
106
- app.route("/agents/:agentId", agentApp);
107
-
108
- const port = config.port ?? 3000;
109
-
110
- return {
111
- app,
112
- port,
113
- fetch: app.fetch,
114
- websocket,
115
- };
116
- },
117
-
118
- async seedGraph() {
119
- const domains = collectAllDomains();
120
- if (!Object.keys(domains).length) {
121
- throw new Error("No domains found in knowledge config");
122
- }
123
- if (!config.neo4j) {
124
- throw new Error("neo4j config is required for seedGraph");
125
- }
126
- if (!config.embedding) {
127
- throw new Error("embedding config is required for seedGraph");
128
- }
129
-
130
- const provider = createOpenAICompatible({
131
- name: "embedding-provider",
132
- baseURL: config.embedding.baseURL,
133
- apiKey: config.embedding.apiKey,
134
- });
135
-
136
- await seedGraphFn(
137
- {
138
- neo4j: config.neo4j,
139
- embedding: {
140
- model: provider.textEmbeddingModel(config.embedding.modelName),
141
- dimension: config.embedding.dimension,
142
- },
51
+
52
+ return {
53
+ async serve() {
54
+ const storage = createMinioAdapter(config.storage);
55
+ const db = createMssqlAdapter(config.database.connectionString, storage);
56
+
57
+ await runMigrations(config.database.connectionString);
58
+
59
+ const app = new Hono<AppEnv>();
60
+
61
+ const userLoader = createUserLoaderMiddleware(config.auth);
62
+ app.use("*", userLoader);
63
+
64
+ // WebSocket route (global, not agent-scoped)
65
+ app.route("/", createWsRoute());
66
+
67
+ // Agent-scoped routes
68
+ const agentApp = new Hono<CortexAppEnv>();
69
+
70
+ agentApp.use("*", async function (c, next) {
71
+ const agentId = c.req.param("agentId") as string;
72
+ const agentDef = config.agents[agentId];
73
+ if (!agentDef) return c.json({ error: "Agent not found" }, 404);
74
+
75
+ const resolvedConfig: ResolvedCortexAgentConfig = {
76
+ db,
77
+ storage,
78
+ model: agentDef.model ?? config.model,
79
+ embedding: agentDef.embedding ?? config.embedding,
80
+ neo4j: agentDef.neo4j ?? config.neo4j,
81
+ reranker: agentDef.reranker ?? config.reranker,
82
+ knowledge:
83
+ agentDef.knowledge === null
84
+ ? undefined
85
+ : (agentDef.knowledge ?? config.knowledge),
86
+ systemPrompt: agentDef.systemPrompt,
87
+ tools: agentDef.tools,
88
+ backendFetch: agentDef.backendFetch,
89
+ loadSessionData: agentDef.loadSessionData,
90
+ onToolCall: agentDef.onToolCall,
91
+ onStreamFinish: agentDef.onStreamFinish,
92
+ };
93
+
94
+ c.set("agentConfig", resolvedConfig);
95
+ c.set("agentId", agentId);
96
+ await next();
97
+ });
98
+
99
+ agentApp.route("/", createThreadRoutes());
100
+ agentApp.route("/", createChatRoutes());
101
+ agentApp.route("/", createFileRoutes());
102
+
103
+ app.route("/agents/:agentId", agentApp);
104
+
105
+ const port = config.port ?? 3331;
106
+
107
+ return {
108
+ app,
109
+ port,
110
+ fetch: app.fetch,
111
+ websocket,
112
+ };
113
+ },
114
+
115
+ async seedGraph() {
116
+ const domains = collectAllDomains();
117
+ if (!Object.keys(domains).length) {
118
+ throw new Error("No domains found in knowledge config");
119
+ }
120
+ if (!config.neo4j) {
121
+ throw new Error("neo4j config is required for seedGraph");
122
+ }
123
+ if (!config.embedding) {
124
+ throw new Error("embedding config is required for seedGraph");
125
+ }
126
+
127
+ const provider = createOpenAICompatible({
128
+ name: "embedding-provider",
129
+ baseURL: config.embedding.baseURL,
130
+ apiKey: config.embedding.apiKey,
131
+ });
132
+
133
+ await seedGraphFn(
134
+ {
135
+ neo4j: config.neo4j,
136
+ embedding: {
137
+ model: provider.textEmbeddingModel(config.embedding.modelName),
138
+ dimension: config.embedding.dimension,
139
+ },
140
+ },
141
+ domains,
142
+ );
143
+ },
144
+
145
+ async extractEndpoints(options) {
146
+ const swaggerUrls = collectAllSwaggerUrls();
147
+ if (!swaggerUrls.length) {
148
+ throw new Error("No swagger URLs found in knowledge config");
149
+ }
150
+
151
+ for (const swaggerUrl of swaggerUrls) {
152
+ await extractEndpointsFn({
153
+ swaggerUrl,
154
+ domainsDir: options.domainsDir,
155
+ write: options.write ?? true,
156
+ });
157
+ }
143
158
  },
144
- domains,
145
- );
146
- },
147
-
148
- async extractEndpoints(options) {
149
- const swaggerUrls = collectAllSwaggerUrls();
150
- if (!swaggerUrls.length) {
151
- throw new Error("No swagger URLs found in knowledge config");
152
- }
153
-
154
- for (const swaggerUrl of swaggerUrls) {
155
- await extractEndpointsFn({
156
- swaggerUrl,
157
- domainsDir: options.domainsDir,
158
- write: options.write ?? true,
159
- });
160
- }
161
- },
162
- };
159
+ } satisfies CortexInstance;
163
160
  }