@m6d/cortex-server 1.1.1 → 1.2.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 (46) hide show
  1. package/README.md +38 -38
  2. package/dist/src/ai/interceptors/{resolve-captured-files.d.ts → request-interceptor.d.ts} +3 -2
  3. package/dist/src/config.d.ts +6 -3
  4. package/dist/src/factory.d.ts +13 -1
  5. package/dist/src/index.d.ts +2 -0
  6. package/dist/src/ws/index.d.ts +1 -1
  7. package/package.json +54 -54
  8. package/src/adapters/database.ts +21 -28
  9. package/src/adapters/minio.ts +69 -69
  10. package/src/adapters/mssql.ts +167 -195
  11. package/src/adapters/storage.ts +4 -4
  12. package/src/ai/fetch.ts +31 -31
  13. package/src/ai/helpers.ts +18 -22
  14. package/src/ai/index.ts +106 -114
  15. package/src/ai/interceptors/request-interceptor.ts +61 -0
  16. package/src/ai/prompt.ts +80 -83
  17. package/src/ai/tools/call-endpoint.tool.ts +75 -82
  18. package/src/ai/tools/capture-files.tool.ts +15 -17
  19. package/src/ai/tools/execute-code.tool.ts +73 -80
  20. package/src/ai/tools/query-graph.tool.ts +17 -17
  21. package/src/auth/middleware.ts +51 -51
  22. package/src/cli/extract-endpoints.ts +436 -474
  23. package/src/config.ts +128 -135
  24. package/src/db/migrate.ts +13 -13
  25. package/src/db/migrations/20260309012148_cloudy_maria_hill/snapshot.json +303 -303
  26. package/src/db/schema.ts +46 -58
  27. package/src/factory.ts +136 -139
  28. package/src/graph/generate-cypher.ts +97 -97
  29. package/src/graph/helpers.ts +37 -37
  30. package/src/graph/index.ts +20 -20
  31. package/src/graph/neo4j.ts +82 -89
  32. package/src/graph/resolver.ts +201 -211
  33. package/src/graph/seed.ts +101 -114
  34. package/src/graph/types.ts +88 -88
  35. package/src/graph/validate.ts +55 -57
  36. package/src/index.ts +12 -5
  37. package/src/routes/chat.ts +23 -23
  38. package/src/routes/files.ts +75 -80
  39. package/src/routes/threads.ts +52 -54
  40. package/src/routes/ws.ts +22 -22
  41. package/src/types.ts +30 -30
  42. package/src/ws/connections.ts +11 -11
  43. package/src/ws/events.ts +2 -2
  44. package/src/ws/index.ts +1 -5
  45. package/src/ws/notify.ts +4 -4
  46. package/src/ai/interceptors/resolve-captured-files.ts +0 -64
package/README.md CHANGED
@@ -8,50 +8,50 @@ Multi-agent AI chat server built on Hono + Bun. Supports MSSQL persistence, MinI
8
8
  import { createCortex } from "@m6d/cortex-server";
9
9
 
10
10
  const cortex = createCortex({
11
- database: {
12
- type: "mssql",
13
- connectionString: process.env.DB_CONNECTION_STRING!,
14
- },
15
- storage: {
16
- endPoint: "localhost",
17
- port: 9000,
18
- useSSL: false,
19
- accessKey: "minioadmin",
20
- secretKey: "minioadmin",
21
- },
22
- auth: {
23
- jwksUri: "https://your-auth-provider/.well-known/jwks.json",
24
- issuer: "https://your-auth-provider/",
25
- },
26
- model: {
27
- baseURL: "https://api.openai.com/v1",
28
- apiKey: process.env.MODEL_KEY!,
29
- modelName: "gpt-4o",
30
- },
31
- embedding: {
32
- baseURL: "https://api.openai.com/v1",
33
- apiKey: process.env.EMBEDDING_KEY!,
34
- modelName: "text-embedding-3-small",
35
- dimension: 1536,
36
- },
37
- neo4j: {
38
- url: "bolt://localhost:7687",
39
- user: "neo4j",
40
- password: "password",
41
- },
42
- agents: {
43
- assistant: {
44
- systemPrompt: "You are a helpful assistant.",
45
- tools: {},
11
+ database: {
12
+ type: "mssql",
13
+ connectionString: process.env.DB_CONNECTION_STRING!,
14
+ },
15
+ storage: {
16
+ endPoint: "localhost",
17
+ port: 9000,
18
+ useSSL: false,
19
+ accessKey: "minioadmin",
20
+ secretKey: "minioadmin",
21
+ },
22
+ auth: {
23
+ jwksUri: "https://your-auth-provider/.well-known/jwks.json",
24
+ issuer: "https://your-auth-provider/",
25
+ },
26
+ model: {
27
+ baseURL: "https://api.openai.com/v1",
28
+ apiKey: process.env.MODEL_KEY!,
29
+ modelName: "gpt-4o",
30
+ },
31
+ embedding: {
32
+ baseURL: "https://api.openai.com/v1",
33
+ apiKey: process.env.EMBEDDING_KEY!,
34
+ modelName: "text-embedding-3-small",
35
+ dimension: 1536,
36
+ },
37
+ neo4j: {
38
+ url: "bolt://localhost:7687",
39
+ user: "neo4j",
40
+ password: "password",
41
+ },
42
+ agents: {
43
+ assistant: {
44
+ systemPrompt: "You are a helpful assistant.",
45
+ tools: {},
46
+ },
46
47
  },
47
- },
48
48
  });
49
49
 
50
50
  const server = await cortex.serve();
51
51
 
52
52
  export default {
53
- fetch: server.fetch,
54
- websocket: server.websocket,
53
+ fetch: server.fetch,
54
+ websocket: server.websocket,
55
55
  };
56
56
  ```
57
57
 
@@ -4,8 +4,9 @@ export type ResolvedFile = {
4
4
  name: string;
5
5
  bytes: string;
6
6
  };
7
- export declare function createCapturedFileInterceptor(db: DatabaseAdapter, storage: StorageAdapter, options?: {
7
+ export type RequestInterceptorOptions = {
8
8
  transformFile?: (file: ResolvedFile) => unknown;
9
- }): (body: Record<string, unknown>, context: {
9
+ };
10
+ export declare function createRequestInterceptor(db: DatabaseAdapter, storage: StorageAdapter, options?: RequestInterceptorOptions): (body: Record<string, unknown>, context: {
10
11
  token: string;
11
12
  }) => Promise<Record<string, unknown>>;
@@ -1,7 +1,8 @@
1
- import type { UIMessage } from "ai";
1
+ import type { ToolSet, UIMessage } from "ai";
2
2
  import type { DatabaseAdapter } from "./adapters/database";
3
3
  import type { StorageAdapter } from "./adapters/storage";
4
4
  import type { DomainDef } from "./graph/types.ts";
5
+ import type { RequestInterceptorOptions } from "./ai/interceptors/request-interceptor.ts";
5
6
  export type KnowledgeConfig = {
6
7
  swagger?: {
7
8
  url: string;
@@ -22,7 +23,7 @@ export type StorageConfig = {
22
23
  };
23
24
  export type CortexAgentDefinition = {
24
25
  systemPrompt: string | ((session: Record<string, unknown> | null) => string | Promise<string>);
25
- tools?: Record<string, unknown>;
26
+ tools?: ToolSet;
26
27
  backendFetch?: {
27
28
  baseUrl: string;
28
29
  apiKey: string;
@@ -30,6 +31,7 @@ export type CortexAgentDefinition = {
30
31
  transformRequestBody?: (body: Record<string, unknown>, context: {
31
32
  token: string;
32
33
  }) => Promise<Record<string, unknown>>;
34
+ interceptor?: RequestInterceptorOptions;
33
35
  };
34
36
  loadSessionData?: (token: string) => Promise<Record<string, unknown>>;
35
37
  onToolCall?: (toolCall: {
@@ -89,7 +91,7 @@ export type ResolvedCortexAgentConfig = {
89
91
  apiKey: string;
90
92
  };
91
93
  systemPrompt: string | ((session: Record<string, unknown> | null) => string | Promise<string>);
92
- tools?: Record<string, unknown>;
94
+ tools?: ToolSet;
93
95
  backendFetch?: {
94
96
  baseUrl: string;
95
97
  apiKey: string;
@@ -97,6 +99,7 @@ export type ResolvedCortexAgentConfig = {
97
99
  transformRequestBody?: (body: Record<string, unknown>, context: {
98
100
  token: string;
99
101
  }) => Promise<Record<string, unknown>>;
102
+ interceptor?: RequestInterceptorOptions;
100
103
  };
101
104
  loadSessionData?: (token: string) => Promise<Record<string, unknown>>;
102
105
  onToolCall?: (toolCall: {
@@ -15,4 +15,16 @@ export type CortexInstance = {
15
15
  write?: boolean;
16
16
  }): Promise<void>;
17
17
  };
18
- export declare function createCortex(config: CortexConfig): CortexInstance;
18
+ export declare function createCortex(config: CortexConfig): {
19
+ serve(): Promise<{
20
+ app: Hono<AppEnv, import("hono/types").BlankSchema, "/">;
21
+ port: number;
22
+ fetch: (request: Request, Env?: {} | undefined, executionCtx?: import("hono").ExecutionContext) => Response | Promise<Response>;
23
+ websocket: import("hono/bun").BunWebSocketHandler<import("hono/bun").BunWebSocketData>;
24
+ }>;
25
+ seedGraph(): Promise<void>;
26
+ extractEndpoints(options: {
27
+ domainsDir: string;
28
+ write?: boolean;
29
+ }): Promise<void>;
30
+ };
@@ -2,6 +2,8 @@ export type { CortexConfig, CortexAgentDefinition, KnowledgeConfig, DatabaseConf
2
2
  export type { Thread, AppEnv } from "./types";
3
3
  export type { CortexInstance } from "./factory";
4
4
  export { createCortex } from "./factory";
5
+ export type { ResolvedFile, RequestInterceptorOptions, } from "./ai/interceptors/request-interceptor";
6
+ export { createRequestInterceptor } from "./ai/interceptors/request-interceptor";
5
7
  export { captureFilesTool } from "./ai/tools/capture-files.tool";
6
8
  export { createQueryGraphTool } from "./ai/tools/query-graph.tool";
7
9
  export { createCallEndpointTool } from "./ai/tools/call-endpoint.tool";
@@ -1,3 +1,3 @@
1
- export { addConnection, removeConnection, getConnections, } from "./connections.ts";
1
+ export { addConnection, removeConnection, getConnections } from "./connections.ts";
2
2
  export { notify } from "./notify.ts";
3
3
  export type { WsEvent, ThreadTitleUpdatedEvent } from "./events.ts";
package/package.json CHANGED
@@ -1,57 +1,57 @@
1
1
  {
2
- "name": "@m6d/cortex-server",
3
- "version": "1.1.1",
4
- "description": "Reusable AI agent chat server library for Hono + Bun",
5
- "license": "MIT",
6
- "type": "module",
7
- "exports": {
8
- ".": {
9
- "types": "./dist/index.d.ts",
10
- "bun": "./index.ts",
11
- "default": "./index.ts"
2
+ "name": "@m6d/cortex-server",
3
+ "version": "1.2.0",
4
+ "description": "Reusable AI agent chat server library for Hono + Bun",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "bun": "./index.ts",
11
+ "default": "./index.ts"
12
+ }
13
+ },
14
+ "main": "./index.ts",
15
+ "types": "./dist/index.d.ts",
16
+ "files": [
17
+ "dist",
18
+ "src",
19
+ "index.ts",
20
+ "README.md"
21
+ ],
22
+ "scripts": {
23
+ "build": "tsc -p tsconfig.build.json",
24
+ "db:generate": "drizzle-kit generate",
25
+ "db:migrate": "drizzle-kit migrate"
26
+ },
27
+ "dependencies": {
28
+ "@ai-sdk/provider": "^3.0.0",
29
+ "@hono/zod-validator": "^0.5.0",
30
+ "drizzle-orm": "^1.0.0-beta.15-859cf75",
31
+ "minio": "^8.0.7",
32
+ "mssql": "^12.2.0"
33
+ },
34
+ "devDependencies": {
35
+ "@ai-sdk/openai-compatible": "^2.0.0",
36
+ "@types/bun": "latest",
37
+ "@types/minio": "^7.1.1",
38
+ "@types/mssql": "^9.1.5",
39
+ "ai": "^6.0.104",
40
+ "drizzle-kit": "^1.0.0-beta.15-859cf75",
41
+ "hono": "^4.12.3",
42
+ "typescript": "^5"
43
+ },
44
+ "peerDependencies": {
45
+ "@ai-sdk/openai-compatible": "^2.0.0",
46
+ "ai": "^6.0.0",
47
+ "hono": "^4.12.3",
48
+ "jose": "^6.1.3",
49
+ "zod": "^3.24.0"
50
+ },
51
+ "engines": {
52
+ "bun": ">=1.0.0"
53
+ },
54
+ "publishConfig": {
55
+ "access": "public"
12
56
  }
13
- },
14
- "main": "./index.ts",
15
- "types": "./dist/index.d.ts",
16
- "files": [
17
- "dist",
18
- "src",
19
- "index.ts",
20
- "README.md"
21
- ],
22
- "scripts": {
23
- "build": "tsc -p tsconfig.build.json",
24
- "db:generate": "drizzle-kit generate",
25
- "db:migrate": "drizzle-kit migrate"
26
- },
27
- "dependencies": {
28
- "@ai-sdk/provider": "^3.0.0",
29
- "@hono/zod-validator": "^0.5.0",
30
- "drizzle-orm": "^1.0.0-beta.15-859cf75",
31
- "minio": "^8.0.7",
32
- "mssql": "^12.2.0"
33
- },
34
- "devDependencies": {
35
- "@ai-sdk/openai-compatible": "^2.0.0",
36
- "@types/bun": "latest",
37
- "@types/minio": "^7.1.1",
38
- "@types/mssql": "^9.1.5",
39
- "ai": "^6.0.104",
40
- "drizzle-kit": "^1.0.0-beta.15-859cf75",
41
- "hono": "^4.12.3",
42
- "typescript": "^5"
43
- },
44
- "peerDependencies": {
45
- "@ai-sdk/openai-compatible": "^2.0.0",
46
- "ai": "^6.0.0",
47
- "hono": "^4.12.3",
48
- "jose": "^6.1.3",
49
- "zod": "^3.24.0"
50
- },
51
- "engines": {
52
- "bun": ">=1.0.0"
53
- },
54
- "publishConfig": {
55
- "access": "public"
56
- }
57
57
  }
@@ -2,32 +2,25 @@ import type { ToolUIPart, UIMessage } from "ai";
2
2
  import type { Thread, StoredMessage, CapturedFileInput } from "../types";
3
3
 
4
4
  export type DatabaseAdapter = {
5
- threads: {
6
- list(userId: string, agentId: string): Promise<Thread[]>;
7
- getById(userId: string, threadId: string): Promise<Thread | null>;
8
- create(userId: string, agentId: string): Promise<Thread>;
9
- delete(userId: string, threadId: string): Promise<void>;
10
- updateTitle(threadId: string, title: string): Promise<void>;
11
- updateSession(
12
- threadId: string,
13
- session: Record<string, unknown>,
14
- ): Promise<void>;
15
- };
16
- messages: {
17
- list(
18
- userId: string,
19
- threadId: string,
20
- opts?: { limit?: number },
21
- ): Promise<StoredMessage[]>;
22
- upsert(threadId: string, messages: UIMessage[]): Promise<void>;
23
- };
24
- capturedFiles: {
25
- create(
26
- userId: string,
27
- messageId: string,
28
- toolPart: ToolUIPart,
29
- files: CapturedFileInput[],
30
- ): Promise<{ id: string; uploadId: string }[]>;
31
- getById(id: string, userId: string): Promise<{ name: string } | null>;
32
- };
5
+ threads: {
6
+ list(userId: string, agentId: string): Promise<Thread[]>;
7
+ getById(userId: string, threadId: string): Promise<Thread | null>;
8
+ create(userId: string, agentId: string): Promise<Thread>;
9
+ delete(userId: string, threadId: string): Promise<void>;
10
+ updateTitle(threadId: string, title: string): Promise<void>;
11
+ updateSession(threadId: string, session: Record<string, unknown>): Promise<void>;
12
+ };
13
+ messages: {
14
+ list(userId: string, threadId: string, opts?: { limit?: number }): Promise<StoredMessage[]>;
15
+ upsert(threadId: string, messages: UIMessage[]): Promise<void>;
16
+ };
17
+ capturedFiles: {
18
+ create(
19
+ userId: string,
20
+ messageId: string,
21
+ toolPart: ToolUIPart,
22
+ files: CapturedFileInput[],
23
+ ): Promise<{ id: string; uploadId: string }[]>;
24
+ getById(id: string, userId: string): Promise<{ name: string } | null>;
25
+ };
33
26
  };
@@ -3,87 +3,87 @@ import { Readable } from "node:stream";
3
3
  import type { StorageAdapter } from "./storage";
4
4
 
5
5
  export type MinioConfig = {
6
- endPoint: string;
7
- port: number;
8
- useSSL: boolean;
9
- accessKey: string;
10
- secretKey: string;
11
- bucketName?: string;
6
+ endPoint: string;
7
+ port: number;
8
+ useSSL: boolean;
9
+ accessKey: string;
10
+ secretKey: string;
11
+ bucketName?: string;
12
12
  };
13
13
 
14
14
  export function createMinioAdapter(config: MinioConfig) {
15
- if (!config.endPoint) {
16
- throw new Error("MinIO adapter: endPoint is required");
17
- }
15
+ if (!config.endPoint) {
16
+ throw new Error("MinIO adapter: endPoint is required");
17
+ }
18
18
 
19
- const client = new Minio.Client({
20
- endPoint: config.endPoint,
21
- port: config.port,
22
- useSSL: config.useSSL,
23
- accessKey: config.accessKey,
24
- secretKey: config.secretKey,
25
- });
19
+ const client = new Minio.Client({
20
+ endPoint: config.endPoint,
21
+ port: config.port,
22
+ useSSL: config.useSSL,
23
+ accessKey: config.accessKey,
24
+ secretKey: config.secretKey,
25
+ });
26
26
 
27
- const bucketName = config.bucketName ?? "ai-storage";
28
- let bucketReady = false;
27
+ const bucketName = config.bucketName ?? "ai-storage";
28
+ let bucketReady = false;
29
29
 
30
- async function ensureBucket() {
31
- if (bucketReady) return;
32
- if (!(await client.bucketExists(bucketName))) {
33
- await client.makeBucket(bucketName);
30
+ async function ensureBucket() {
31
+ if (bucketReady) return;
32
+ if (!(await client.bucketExists(bucketName))) {
33
+ await client.makeBucket(bucketName);
34
+ }
35
+ bucketReady = true;
34
36
  }
35
- bucketReady = true;
36
- }
37
37
 
38
- const adapter: StorageAdapter = {
39
- async put(path: string, data: string) {
40
- try {
41
- await ensureBucket();
42
- await client.putObject(bucketName, path, Buffer.from(data, "base64"));
43
- return true;
44
- } catch {
45
- return false;
46
- }
47
- },
38
+ const adapter: StorageAdapter = {
39
+ async put(path: string, data: string) {
40
+ try {
41
+ await ensureBucket();
42
+ await client.putObject(bucketName, path, Buffer.from(data, "base64"));
43
+ return true;
44
+ } catch {
45
+ return false;
46
+ }
47
+ },
48
48
 
49
- async get(path: string) {
50
- try {
51
- const stream = await client.getObject(bucketName, path);
52
- return await new Promise<string>((resolve, reject) => {
53
- const chunks: Uint8Array[] = [];
49
+ async get(path: string) {
50
+ try {
51
+ const stream = await client.getObject(bucketName, path);
52
+ return await new Promise<string>((resolve, reject) => {
53
+ const chunks: Uint8Array[] = [];
54
54
 
55
- stream.on("error", () => {
56
- reject();
57
- });
55
+ stream.on("error", () => {
56
+ reject();
57
+ });
58
58
 
59
- stream.on("data", (chunk) => chunks.push(chunk));
60
- stream.on("end", () => {
61
- const buffer = Buffer.concat(chunks);
62
- resolve(buffer.toString("base64"));
63
- });
64
- });
65
- } catch {
66
- return false;
67
- }
68
- },
59
+ stream.on("data", (chunk) => chunks.push(chunk));
60
+ stream.on("end", () => {
61
+ const buffer = Buffer.concat(chunks);
62
+ resolve(buffer.toString("base64"));
63
+ });
64
+ });
65
+ } catch {
66
+ return false;
67
+ }
68
+ },
69
69
 
70
- async delete(paths: string[]) {
71
- try {
72
- await client.removeObjects(
73
- bucketName,
74
- paths.map((x) => ({ name: x })),
75
- );
76
- return true;
77
- } catch {
78
- return false;
79
- }
80
- },
70
+ async delete(paths: string[]) {
71
+ try {
72
+ await client.removeObjects(
73
+ bucketName,
74
+ paths.map((x) => ({ name: x })),
75
+ );
76
+ return true;
77
+ } catch {
78
+ return false;
79
+ }
80
+ },
81
81
 
82
- async stream(path: string) {
83
- const nodeStream = await client.getObject(bucketName, path);
84
- return Readable.toWeb(nodeStream) as unknown as ReadableStream;
85
- },
86
- };
82
+ async stream(path: string) {
83
+ const nodeStream = await client.getObject(bucketName, path);
84
+ return Readable.toWeb(nodeStream) as unknown as ReadableStream;
85
+ },
86
+ };
87
87
 
88
- return adapter;
88
+ return adapter;
89
89
  }