@m6d/cortex-server 1.7.0 → 2.0.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/README.md +8 -3
- package/contracts/README.md +23 -0
- package/contracts/graph/embed.ts +50 -0
- package/{src → contracts}/graph/helpers.ts +4 -10
- package/{src → contracts}/graph/neo4j.ts +14 -27
- package/contracts/graph/schema.ts +65 -0
- package/{src → contracts}/graph/types.ts +2 -29
- package/contracts/graph.ts +36 -0
- package/contracts/runtime.ts +208 -0
- package/contracts/wire.ts +143 -0
- package/dist/contracts/graph/embed.d.ts +22 -0
- package/dist/{src → contracts}/graph/helpers.d.ts +14 -12
- package/dist/{src → contracts}/graph/neo4j.d.ts +5 -5
- package/dist/contracts/graph/schema.d.ts +62 -0
- package/dist/{src → contracts}/graph/types.d.ts +1 -1
- package/dist/contracts/graph.d.ts +14 -0
- package/dist/contracts/runtime.d.ts +278 -0
- package/dist/contracts/wire.d.ts +120 -0
- package/dist/index.d.ts +1 -1
- package/dist/src/lib/adapters/database/index.d.ts +61 -0
- package/dist/src/lib/adapters/database/message-content.d.ts +30 -0
- package/dist/src/lib/adapters/database/mssql/attachments.d.ts +100 -0
- package/dist/src/lib/adapters/database/mssql/client.d.ts +8 -0
- package/dist/src/lib/adapters/database/mssql/index.d.ts +194 -0
- package/dist/src/lib/adapters/database/mssql/llm-requests.d.ts +17 -0
- package/dist/src/lib/adapters/database/mssql/messages.d.ts +37 -0
- package/dist/src/lib/adapters/database/mssql/threads.d.ts +50 -0
- package/dist/src/lib/adapters/database/postgres/attachments.d.ts +113 -0
- package/dist/src/lib/adapters/database/postgres/client.d.ts +8 -0
- package/dist/src/lib/adapters/database/postgres/index.d.ts +207 -0
- package/dist/src/lib/adapters/database/postgres/llm-requests.d.ts +17 -0
- package/dist/src/lib/adapters/database/postgres/messages.d.ts +37 -0
- package/dist/src/lib/adapters/database/postgres/threads.d.ts +50 -0
- package/{src/adapters/storage.ts → dist/src/lib/adapters/storage/index.d.ts} +1 -1
- package/dist/src/{adapters → lib/adapters/storage}/minio.d.ts +1 -1
- package/dist/src/lib/ai/active-runs.d.ts +63 -0
- package/dist/src/lib/ai/attachments.d.ts +30 -0
- package/dist/src/lib/ai/backend-url.d.ts +16 -0
- package/dist/src/lib/ai/cc-runtime.d.ts +61 -0
- package/dist/src/lib/ai/commit-gate.d.ts +21 -0
- package/dist/src/{ai → lib/ai}/context/builder.d.ts +6 -7
- package/dist/src/lib/ai/context/compression-middleware.d.ts +15 -0
- package/dist/src/lib/ai/context/compressor.d.ts +7 -0
- package/dist/src/lib/ai/context/intra-turn-compressor.d.ts +24 -0
- package/dist/src/lib/ai/context/optimize-thread-context.d.ts +9 -0
- package/dist/src/lib/ai/context/summarizer.d.ts +5 -0
- package/dist/src/{ai → lib/ai}/context/token-estimator.d.ts +5 -8
- package/dist/src/{ai → lib/ai}/context/types.d.ts +2 -0
- package/dist/src/{ai → lib/ai}/fetch.d.ts +1 -1
- package/dist/src/lib/ai/finish-turn.d.ts +23 -0
- package/dist/src/lib/ai/helpers.d.ts +38 -0
- package/dist/src/lib/ai/index.d.ts +7 -0
- package/dist/src/lib/ai/inspector.d.ts +41 -0
- package/dist/src/lib/ai/interceptors/request-interceptor.d.ts +13 -0
- package/dist/src/{ai → lib/ai}/prompt.d.ts +5 -4
- package/dist/src/lib/ai/redis-runs.d.ts +41 -0
- package/dist/src/lib/ai/redis-stream-log.d.ts +39 -0
- package/dist/src/lib/ai/tools/execute-code.tool.d.ts +18 -0
- package/dist/src/lib/ai/tools/query-graph.tool.d.ts +13 -0
- package/dist/src/lib/ai/tools/read-attachment.tool.d.ts +21 -0
- package/dist/src/lib/ai/tools/search-common.d.ts +17 -0
- package/dist/src/lib/ai/tools/search-knowledge.tool.d.ts +15 -0
- package/dist/src/lib/ai/tools/search-services.tool.d.ts +12 -0
- package/dist/src/lib/ai/tools/search-tools.tool.d.ts +12 -0
- package/dist/src/lib/ai/turn-tools.d.ts +39 -0
- package/dist/src/lib/ai/vision/rasterize-pdf.d.ts +26 -0
- package/dist/src/lib/ai/vision/rasterize-pdf.worker.d.ts +1 -0
- package/dist/src/lib/ai/vision/vision-reader.d.ts +45 -0
- package/dist/src/lib/cc/client.d.ts +183 -0
- package/dist/src/lib/cc/config-cache.d.ts +20 -0
- package/dist/src/lib/cc/format.d.ts +23 -0
- package/dist/src/lib/cc/registry.d.ts +34 -0
- package/dist/src/lib/cc/types.d.ts +27 -0
- package/dist/src/lib/config.d.ts +170 -0
- package/dist/src/lib/db/drizzle.config.mssql.d.ts +2 -0
- package/dist/src/lib/db/drizzle.config.pg.d.ts +2 -0
- package/dist/src/lib/db/migrate.d.ts +7 -0
- package/dist/src/{db/schema.d.ts → lib/db/schema.mssql.d.ts} +236 -24
- package/dist/src/lib/db/schema.pg.d.ts +546 -0
- package/dist/src/{factory.d.ts → lib/factory.d.ts} +2 -12
- package/dist/src/lib/graph/index.d.ts +10 -0
- package/dist/src/{graph → lib/graph}/resolver.d.ts +2 -3
- package/dist/src/{index.d.ts → lib/index.d.ts} +3 -4
- package/dist/src/lib/redis.d.ts +19 -0
- package/dist/src/{routes → lib/routes}/chat.d.ts +1 -1
- package/dist/src/{routes → lib/routes}/files.d.ts +1 -1
- package/dist/src/lib/routes/owned-thread.d.ts +17 -0
- package/dist/src/{routes → lib/routes}/threads.d.ts +1 -1
- package/dist/src/{routes → lib/routes}/ws.d.ts +1 -1
- package/dist/src/lib/types.d.ts +58 -0
- package/dist/src/{ws → lib/ws}/connections.d.ts +2 -1
- package/index.ts +1 -1
- package/package.json +35 -15
- package/src/lib/adapters/database/index.ts +78 -0
- package/src/lib/adapters/database/message-content.ts +39 -0
- package/src/lib/adapters/database/mssql/attachments.ts +90 -0
- package/src/lib/adapters/database/mssql/client.ts +11 -0
- package/src/lib/adapters/database/mssql/index.ts +23 -0
- package/src/lib/adapters/database/mssql/llm-requests.ts +43 -0
- package/src/lib/adapters/database/mssql/messages.ts +92 -0
- package/src/lib/adapters/database/mssql/threads.ts +83 -0
- package/src/lib/adapters/database/postgres/attachments.ts +88 -0
- package/src/lib/adapters/database/postgres/client.ts +11 -0
- package/src/lib/adapters/database/postgres/index.ts +23 -0
- package/src/lib/adapters/database/postgres/llm-requests.ts +45 -0
- package/src/lib/adapters/database/postgres/messages.ts +92 -0
- package/src/lib/adapters/database/postgres/threads.ts +84 -0
- package/{dist/src/adapters/storage.d.ts → src/lib/adapters/storage/index.ts} +1 -1
- package/src/{adapters → lib/adapters/storage}/minio.ts +7 -6
- package/src/lib/ai/active-runs.ts +157 -0
- package/src/lib/ai/attachments.ts +144 -0
- package/src/lib/ai/backend-url.ts +27 -0
- package/src/lib/ai/cc-runtime.ts +63 -0
- package/src/lib/ai/commit-gate.ts +116 -0
- package/src/{ai → lib/ai}/context/builder.ts +12 -14
- package/src/lib/ai/context/compression-middleware.ts +44 -0
- package/src/lib/ai/context/compressor.ts +267 -0
- package/src/lib/ai/context/intra-turn-compressor.ts +181 -0
- package/src/lib/ai/context/optimize-thread-context.ts +50 -0
- package/src/lib/ai/context/summarizer.ts +48 -0
- package/src/lib/ai/context/token-estimator.ts +65 -0
- package/src/{ai → lib/ai}/context/types.ts +4 -1
- package/src/{ai → lib/ai}/fetch.ts +10 -5
- package/src/lib/ai/finish-turn.ts +75 -0
- package/src/lib/ai/helpers.ts +100 -0
- package/src/lib/ai/index.ts +408 -0
- package/src/lib/ai/inspector.ts +112 -0
- package/src/lib/ai/interceptors/request-interceptor.ts +89 -0
- package/src/lib/ai/prompt.ts +355 -0
- package/src/lib/ai/redis-runs.ts +167 -0
- package/src/lib/ai/redis-stream-log.ts +219 -0
- package/src/lib/ai/tools/execute-code.tool.ts +267 -0
- package/src/{ai → lib/ai}/tools/query-graph.tool.ts +7 -8
- package/src/lib/ai/tools/read-attachment.tool.ts +59 -0
- package/src/lib/ai/tools/search-common.ts +15 -0
- package/src/lib/ai/tools/search-knowledge.tool.ts +33 -0
- package/src/lib/ai/tools/search-services.tool.ts +31 -0
- package/src/lib/ai/tools/search-tools.tool.ts +29 -0
- package/src/lib/ai/turn-tools.ts +154 -0
- package/src/lib/ai/vision/rasterize-pdf.ts +69 -0
- package/src/lib/ai/vision/rasterize-pdf.worker.ts +81 -0
- package/src/lib/ai/vision/vision-reader.ts +130 -0
- package/src/{auth → lib/auth}/middleware.ts +12 -3
- package/src/lib/cc/client.ts +273 -0
- package/src/lib/cc/config-cache.ts +32 -0
- package/src/lib/cc/format.ts +96 -0
- package/src/lib/cc/registry.ts +62 -0
- package/src/lib/cc/types.ts +44 -0
- package/src/lib/config.ts +212 -0
- package/src/lib/db/drizzle.config.mssql.ts +9 -0
- package/src/lib/db/drizzle.config.pg.ts +9 -0
- package/src/lib/db/migrate.ts +56 -0
- package/src/{db/migrations/20260309012148_cloudy_maria_hill → lib/db/migrations/mssql/20260326231647_nice_speedball}/migration.sql +12 -0
- package/src/{db/migrations/20260309012148_cloudy_maria_hill → lib/db/migrations/mssql/20260326231647_nice_speedball}/snapshot.json +106 -1
- package/src/lib/db/migrations/mssql/20260729092726_talented_wilson_fisk/migration.sql +20 -0
- package/src/lib/db/migrations/mssql/20260729092726_talented_wilson_fisk/snapshot.json +497 -0
- package/src/lib/db/migrations/mssql/20260801003652_perpetual_blue_shield/migration.sql +1 -0
- package/src/lib/db/migrations/mssql/20260801003652_perpetual_blue_shield/snapshot.json +516 -0
- package/src/lib/db/migrations/mssql/20260812174642_user_id_text/migration.sql +2 -0
- package/src/lib/db/migrations/mssql/20260812174642_user_id_text/snapshot.json +516 -0
- package/src/lib/db/migrations/pg/20260801084117_thick_ben_grimm/migration.sql +53 -0
- package/src/lib/db/migrations/pg/20260801084117_thick_ben_grimm/snapshot.json +584 -0
- package/src/lib/db/migrations/pg/20260812174622_user_id_text/migration.sql +2 -0
- package/src/lib/db/migrations/pg/20260812174622_user_id_text/snapshot.json +584 -0
- package/src/lib/db/schema.mssql.ts +94 -0
- package/src/lib/db/schema.pg.ts +95 -0
- package/src/lib/factory.ts +127 -0
- package/src/lib/graph/index.ts +19 -0
- package/src/lib/graph/resolver.ts +387 -0
- package/src/{index.ts → lib/index.ts} +13 -5
- package/src/lib/redis.ts +44 -0
- package/src/lib/routes/chat.ts +115 -0
- package/src/lib/routes/files.ts +129 -0
- package/src/lib/routes/owned-thread.ts +17 -0
- package/src/{routes → lib/routes}/threads.ts +22 -31
- package/src/{routes → lib/routes}/ws.ts +9 -3
- package/src/lib/types.ts +76 -0
- package/src/{ws → lib/ws}/connections.ts +9 -1
- package/tsconfig.json +48 -0
- package/dist/src/adapters/database.d.ts +0 -30
- package/dist/src/adapters/mssql.d.ts +0 -3
- package/dist/src/ai/active-streams.d.ts +0 -14
- package/dist/src/ai/context/compressor.d.ts +0 -7
- package/dist/src/ai/context/index.d.ts +0 -15
- package/dist/src/ai/context/summarizer.d.ts +0 -5
- package/dist/src/ai/helpers.d.ts +0 -5
- package/dist/src/ai/index.d.ts +0 -4
- package/dist/src/ai/interceptors/request-interceptor.d.ts +0 -12
- package/dist/src/ai/tools/call-endpoint.tool.d.ts +0 -7
- package/dist/src/ai/tools/capture-files.tool.d.ts +0 -6
- package/dist/src/ai/tools/execute-code.tool.d.ts +0 -4
- package/dist/src/ai/tools/query-graph.tool.d.ts +0 -5
- package/dist/src/cli/extract-endpoints.d.ts +0 -6
- package/dist/src/config.d.ts +0 -165
- package/dist/src/db/migrate.d.ts +0 -1
- package/dist/src/graph/expand-domains.d.ts +0 -2
- package/dist/src/graph/generate-cypher.d.ts +0 -22
- package/dist/src/graph/index.d.ts +0 -11
- package/dist/src/graph/seed.d.ts +0 -19
- package/dist/src/graph/validate.d.ts +0 -2
- package/dist/src/routes/index.d.ts +0 -4
- package/dist/src/types.d.ts +0 -74
- package/dist/src/ws/events.d.ts +0 -39
- package/dist/src/ws/index.d.ts +0 -3
- package/dist/src/ws/notify.d.ts +0 -2
- package/src/adapters/database.ts +0 -29
- package/src/adapters/mssql.ts +0 -195
- package/src/ai/active-streams.ts +0 -123
- package/src/ai/context/compressor.ts +0 -47
- package/src/ai/context/index.ts +0 -75
- package/src/ai/context/summarizer.ts +0 -50
- package/src/ai/context/token-estimator.ts +0 -60
- package/src/ai/helpers.ts +0 -32
- package/src/ai/index.ts +0 -277
- package/src/ai/interceptors/request-interceptor.ts +0 -61
- package/src/ai/prompt.ts +0 -126
- package/src/ai/tools/call-endpoint.tool.ts +0 -89
- package/src/ai/tools/capture-files.tool.ts +0 -20
- package/src/ai/tools/execute-code.tool.ts +0 -153
- package/src/cli/extract-endpoints.ts +0 -550
- package/src/config.ts +0 -182
- package/src/db/migrate.ts +0 -21
- package/src/db/migrations/20260315000000_add_context_meta/migration.sql +0 -1
- package/src/db/schema.ts +0 -70
- package/src/factory.ts +0 -171
- package/src/graph/expand-domains.ts +0 -276
- package/src/graph/generate-cypher.ts +0 -192
- package/src/graph/index.ts +0 -47
- package/src/graph/resolver.ts +0 -357
- package/src/graph/seed.ts +0 -162
- package/src/graph/validate.ts +0 -78
- package/src/routes/chat.ts +0 -86
- package/src/routes/files.ts +0 -100
- package/src/routes/index.ts +0 -4
- package/src/types.ts +0 -74
- package/src/ws/events.ts +0 -39
- package/src/ws/index.ts +0 -11
- package/src/ws/notify.ts +0 -9
- /package/dist/src/{auth → lib/auth}/middleware.d.ts +0 -0
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import type z from "zod";
|
|
3
|
+
import type { ControlCenterConfig } from "@/config";
|
|
4
|
+
import {
|
|
5
|
+
executeResponseSchema,
|
|
6
|
+
resolveResponseSchema,
|
|
7
|
+
runtimeAgentConfigSchema,
|
|
8
|
+
runtimeErrorSchema,
|
|
9
|
+
searchKnowledgeResponseSchema,
|
|
10
|
+
searchServicesResponseSchema,
|
|
11
|
+
searchToolsResponseSchema,
|
|
12
|
+
} from "./types";
|
|
13
|
+
import type { ExecuteRequest, ResolveRequest, SearchRequest } from "./types";
|
|
14
|
+
|
|
15
|
+
type ControlCenterFetch = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
|
|
16
|
+
|
|
17
|
+
export type ExecuteOptions = {
|
|
18
|
+
readOnly: boolean;
|
|
19
|
+
endUserToken?: string;
|
|
20
|
+
threadId: string;
|
|
21
|
+
turnKey: string;
|
|
22
|
+
stepIndex: number;
|
|
23
|
+
callIndex: number;
|
|
24
|
+
timeoutMs?: number;
|
|
25
|
+
abortSignal?: AbortSignal;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const retryAfterByUrl = new Map<string, number>();
|
|
29
|
+
|
|
30
|
+
function normalizeQuery(query: string) {
|
|
31
|
+
return query.trim().slice(0, 4000);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export class ControlCenterClient {
|
|
35
|
+
public readonly url: string;
|
|
36
|
+
public readonly cacheKey: string;
|
|
37
|
+
private readonly apiKey: string;
|
|
38
|
+
private readonly fetcher: ControlCenterFetch;
|
|
39
|
+
|
|
40
|
+
public constructor(config: ControlCenterConfig, fetcher: ControlCenterFetch = fetch) {
|
|
41
|
+
this.url = config.url.replace(/\/+$/, "");
|
|
42
|
+
this.cacheKey = `${this.url}|${createHash("sha256").update(config.apiKey).digest("hex").slice(0, 16)}`;
|
|
43
|
+
this.apiKey = config.apiKey;
|
|
44
|
+
this.fetcher = fetcher;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
public async getConfig(agentId: string, etag?: string, abortSignal?: AbortSignal) {
|
|
48
|
+
try {
|
|
49
|
+
const headers = new Headers();
|
|
50
|
+
if (etag !== undefined) headers.set("If-None-Match", etag);
|
|
51
|
+
|
|
52
|
+
const response = await this.request(
|
|
53
|
+
this.agentPath(agentId, "/config"),
|
|
54
|
+
{ headers },
|
|
55
|
+
abortSignal,
|
|
56
|
+
);
|
|
57
|
+
if (!response) return null;
|
|
58
|
+
if (response.status === 304) return { status: "not-modified" } as const;
|
|
59
|
+
if (response.status === 404) return { status: "not-found" } as const;
|
|
60
|
+
if (!response.ok) return null;
|
|
61
|
+
|
|
62
|
+
const config = await this.parse(response, runtimeAgentConfigSchema);
|
|
63
|
+
if (!config) return null;
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
status: "modified",
|
|
67
|
+
config,
|
|
68
|
+
etag: response.headers.get("ETag"),
|
|
69
|
+
} as const;
|
|
70
|
+
} catch {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
public async resolve(agentId: string, request: ResolveRequest, abortSignal?: AbortSignal) {
|
|
76
|
+
const query = normalizeQuery(request.query);
|
|
77
|
+
if (!query) return null;
|
|
78
|
+
return this.post(
|
|
79
|
+
this.agentPath(agentId, "/resolve"),
|
|
80
|
+
{ ...request, query },
|
|
81
|
+
resolveResponseSchema,
|
|
82
|
+
abortSignal,
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
public async searchTools(agentId: string, request: SearchRequest, abortSignal?: AbortSignal) {
|
|
87
|
+
const query = normalizeQuery(request.query);
|
|
88
|
+
if (!query) return null;
|
|
89
|
+
return this.post(
|
|
90
|
+
this.agentPath(agentId, "/search/tools"),
|
|
91
|
+
{ ...request, query },
|
|
92
|
+
searchToolsResponseSchema,
|
|
93
|
+
abortSignal,
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
public async searchServices(
|
|
98
|
+
agentId: string,
|
|
99
|
+
request: SearchRequest,
|
|
100
|
+
abortSignal?: AbortSignal,
|
|
101
|
+
) {
|
|
102
|
+
const query = normalizeQuery(request.query);
|
|
103
|
+
if (!query) return null;
|
|
104
|
+
return this.post(
|
|
105
|
+
this.agentPath(agentId, "/search/services"),
|
|
106
|
+
{ ...request, query },
|
|
107
|
+
searchServicesResponseSchema,
|
|
108
|
+
abortSignal,
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
public async searchKnowledge(
|
|
113
|
+
agentId: string,
|
|
114
|
+
request: SearchRequest,
|
|
115
|
+
abortSignal?: AbortSignal,
|
|
116
|
+
) {
|
|
117
|
+
const query = normalizeQuery(request.query);
|
|
118
|
+
if (!query) return null;
|
|
119
|
+
return this.post(
|
|
120
|
+
this.agentPath(agentId, "/search/knowledge"),
|
|
121
|
+
{ ...request, query },
|
|
122
|
+
searchKnowledgeResponseSchema,
|
|
123
|
+
abortSignal,
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
public async execute(
|
|
128
|
+
agentId: string,
|
|
129
|
+
toolId: string,
|
|
130
|
+
request: ExecuteRequest,
|
|
131
|
+
options: ExecuteOptions,
|
|
132
|
+
) {
|
|
133
|
+
try {
|
|
134
|
+
const headers = new Headers();
|
|
135
|
+
if (options.endUserToken !== undefined) {
|
|
136
|
+
headers.set("X-End-User-Token", options.endUserToken);
|
|
137
|
+
}
|
|
138
|
+
if (!options.readOnly) {
|
|
139
|
+
headers.set(
|
|
140
|
+
"Idempotency-Key",
|
|
141
|
+
`${options.threadId}:${options.turnKey}:${options.stepIndex}:${options.callIndex}`,
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const path = this.agentPath(agentId, `/tools/${encodeURIComponent(toolId)}/execute`);
|
|
146
|
+
let response = await this.postResponse(
|
|
147
|
+
path,
|
|
148
|
+
request,
|
|
149
|
+
headers,
|
|
150
|
+
options.abortSignal,
|
|
151
|
+
options.timeoutMs,
|
|
152
|
+
);
|
|
153
|
+
if (!response) return null;
|
|
154
|
+
|
|
155
|
+
let result = await this.parse(
|
|
156
|
+
response,
|
|
157
|
+
response.ok ? executeResponseSchema : runtimeErrorSchema,
|
|
158
|
+
);
|
|
159
|
+
if (
|
|
160
|
+
response.status === 409 &&
|
|
161
|
+
result &&
|
|
162
|
+
"error" in result &&
|
|
163
|
+
result.error.kind === "rate_limited" &&
|
|
164
|
+
result.error.retryAfterSeconds !== undefined &&
|
|
165
|
+
result.error.retryAfterSeconds <= 5
|
|
166
|
+
) {
|
|
167
|
+
const retryAfterSeconds = result.error.retryAfterSeconds;
|
|
168
|
+
await new Promise<void>((resolve) => setTimeout(resolve, retryAfterSeconds * 1000));
|
|
169
|
+
response = await this.postResponse(
|
|
170
|
+
path,
|
|
171
|
+
request,
|
|
172
|
+
headers,
|
|
173
|
+
options.abortSignal,
|
|
174
|
+
options.timeoutMs,
|
|
175
|
+
);
|
|
176
|
+
if (!response) return null;
|
|
177
|
+
result = await this.parse(
|
|
178
|
+
response,
|
|
179
|
+
response.ok ? executeResponseSchema : runtimeErrorSchema,
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return result;
|
|
184
|
+
} catch {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
private agentPath(agentId: string, suffix: string) {
|
|
190
|
+
return `/api/runtime/agents/${encodeURIComponent(agentId)}${suffix}`;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
private async post<TSchema extends z.ZodType>(
|
|
194
|
+
path: string,
|
|
195
|
+
body: unknown,
|
|
196
|
+
schema: TSchema,
|
|
197
|
+
abortSignal?: AbortSignal,
|
|
198
|
+
) {
|
|
199
|
+
const response = await this.postResponse(path, body, undefined, abortSignal);
|
|
200
|
+
if (!response?.ok) return null;
|
|
201
|
+
return this.parse(response, schema);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
private async postResponse(
|
|
205
|
+
path: string,
|
|
206
|
+
body: unknown,
|
|
207
|
+
requestHeaders?: Headers,
|
|
208
|
+
abortSignal?: AbortSignal,
|
|
209
|
+
timeoutMs?: number,
|
|
210
|
+
) {
|
|
211
|
+
try {
|
|
212
|
+
const headers = new Headers(requestHeaders);
|
|
213
|
+
headers.set("Content-Type", "application/json");
|
|
214
|
+
return await this.request(
|
|
215
|
+
path,
|
|
216
|
+
{
|
|
217
|
+
method: "POST",
|
|
218
|
+
headers,
|
|
219
|
+
body: JSON.stringify(body),
|
|
220
|
+
},
|
|
221
|
+
abortSignal,
|
|
222
|
+
timeoutMs,
|
|
223
|
+
);
|
|
224
|
+
} catch {
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
private async request(
|
|
230
|
+
path: string,
|
|
231
|
+
init?: RequestInit,
|
|
232
|
+
abortSignal?: AbortSignal,
|
|
233
|
+
timeoutMs = 15_000,
|
|
234
|
+
) {
|
|
235
|
+
const url = `${this.url}${path}`;
|
|
236
|
+
const retryKey = `${this.cacheKey}|${path}`;
|
|
237
|
+
const retryAt = retryAfterByUrl.get(retryKey);
|
|
238
|
+
if (retryAt !== undefined) {
|
|
239
|
+
if (retryAt > Date.now()) return null;
|
|
240
|
+
retryAfterByUrl.delete(retryKey);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
try {
|
|
244
|
+
const headers = new Headers(init?.headers);
|
|
245
|
+
headers.set("Authorization", `Bearer ${this.apiKey}`);
|
|
246
|
+
const timeoutSignal = AbortSignal.timeout(timeoutMs);
|
|
247
|
+
const signal = abortSignal
|
|
248
|
+
? AbortSignal.any([abortSignal, timeoutSignal])
|
|
249
|
+
: timeoutSignal;
|
|
250
|
+
const response = await this.fetcher(url, { ...init, headers, signal });
|
|
251
|
+
|
|
252
|
+
if (response.status === 429) {
|
|
253
|
+
const retryAfterSeconds = Number(response.headers.get("Retry-After"));
|
|
254
|
+
if (Number.isInteger(retryAfterSeconds) && retryAfterSeconds > 0) {
|
|
255
|
+
retryAfterByUrl.set(retryKey, Date.now() + retryAfterSeconds * 1000);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return response;
|
|
260
|
+
} catch {
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
private async parse<TSchema extends z.ZodType>(response: Response, schema: TSchema) {
|
|
266
|
+
try {
|
|
267
|
+
const result = schema.safeParse(await response.json());
|
|
268
|
+
return result.success ? result.data : null;
|
|
269
|
+
} catch {
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ControlCenterClient } from "./client";
|
|
2
|
+
import type { RuntimeAgentConfig } from "./types";
|
|
3
|
+
|
|
4
|
+
type CachedConfig = {
|
|
5
|
+
etag: string | null;
|
|
6
|
+
config: RuntimeAgentConfig;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const configs = new Map<string, CachedConfig>();
|
|
10
|
+
|
|
11
|
+
export async function getControlCenterConfig(
|
|
12
|
+
cc: ControlCenterClient,
|
|
13
|
+
agentId: string,
|
|
14
|
+
abortSignal?: AbortSignal,
|
|
15
|
+
) {
|
|
16
|
+
const key = `${cc.cacheKey}|${agentId}`;
|
|
17
|
+
const cached = configs.get(key);
|
|
18
|
+
const result = await cc.getConfig(agentId, cached?.etag ?? undefined, abortSignal);
|
|
19
|
+
|
|
20
|
+
if (result?.status === "modified") {
|
|
21
|
+
configs.set(key, { etag: result.etag, config: result.config });
|
|
22
|
+
return result.config;
|
|
23
|
+
}
|
|
24
|
+
if (result?.status === "not-modified" && cached) return cached.config;
|
|
25
|
+
if (result?.status === "not-found") {
|
|
26
|
+
configs.delete(key);
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
console.warn(`Control Center config unavailable for agent "${agentId}"`);
|
|
31
|
+
return cached?.config ?? null;
|
|
32
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { ResolveResponse, RuntimeAgentConfig } from "./types";
|
|
2
|
+
|
|
3
|
+
type SharedShape = ResolveResponse["sharedShapes"][number];
|
|
4
|
+
type ToolSignature = ResolveResponse["tools"][number];
|
|
5
|
+
type ServiceCard = ResolveResponse["services"][number];
|
|
6
|
+
type KnowledgeChunk = ResolveResponse["knowledge"][number];
|
|
7
|
+
|
|
8
|
+
function substitutePromptVariables(template: string, variables: Record<string, string>) {
|
|
9
|
+
return template.replace(/\{\{(\w+)\}\}/g, function replaceVariable(match, name: string) {
|
|
10
|
+
return variables[name] ?? match;
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Values for the published prompt's `{{variable}}` placeholders, read from session/request context. */
|
|
15
|
+
export function buildPromptVariables(
|
|
16
|
+
promptVariables: RuntimeAgentConfig["promptVariables"],
|
|
17
|
+
sources: Record<string, unknown>,
|
|
18
|
+
) {
|
|
19
|
+
const variables: Record<string, string> = {};
|
|
20
|
+
for (const name of promptVariables) {
|
|
21
|
+
const value = sources[name];
|
|
22
|
+
if (typeof value === "string") variables[name] = value;
|
|
23
|
+
}
|
|
24
|
+
return variables;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function formatSharedShapes(shapes: SharedShape[]) {
|
|
28
|
+
return shapes.map((shape) => `${shape.ref}: ${shape.shape}`).join("\n");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function formatToolSignatures(tools: ToolSignature[]) {
|
|
32
|
+
return tools.map((tool) => tool.signature).join("\n\n");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function formatServiceCards(services: ServiceCard[]) {
|
|
36
|
+
return services.map((service) => service.card).join("\n\n");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function formatKnowledgeChunks(chunks: KnowledgeChunk[]) {
|
|
40
|
+
return chunks.map(formatKnowledgeChunk).join("\n\n");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function formatKnowledgeChunk(chunk: KnowledgeChunk) {
|
|
44
|
+
const { citation } = chunk;
|
|
45
|
+
const source = [
|
|
46
|
+
citation.service,
|
|
47
|
+
citation.documentTitle,
|
|
48
|
+
citation.section,
|
|
49
|
+
citation.page === null ? null : `p. ${citation.page}`,
|
|
50
|
+
]
|
|
51
|
+
.filter(Boolean)
|
|
52
|
+
.join(", ");
|
|
53
|
+
return `${chunk.text}\n[Source: ${source}]`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export type CcPromptInput = {
|
|
57
|
+
config: RuntimeAgentConfig;
|
|
58
|
+
resolved: ResolveResponse | null;
|
|
59
|
+
variables: Record<string, string>;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Dumb concatenator by contract: `signature`, `card`, and `shape` arrive
|
|
64
|
+
* pre-rendered from Control Center and are injected verbatim — never
|
|
65
|
+
* re-rendered from structured fields.
|
|
66
|
+
*/
|
|
67
|
+
export function buildCcSection(cc: CcPromptInput) {
|
|
68
|
+
const parts: string[] = [];
|
|
69
|
+
|
|
70
|
+
const prompt = substitutePromptVariables(cc.config.systemPrompt, cc.variables).trim();
|
|
71
|
+
if (prompt) parts.push(prompt);
|
|
72
|
+
|
|
73
|
+
if (cc.config.catalogBlurb) {
|
|
74
|
+
parts.push(`## Capabilities\n${cc.config.catalogBlurb}`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (cc.resolved) {
|
|
78
|
+
if (cc.resolved.sharedShapes.length > 0) {
|
|
79
|
+
parts.push(`## Response Shapes\n${formatSharedShapes(cc.resolved.sharedShapes)}`);
|
|
80
|
+
}
|
|
81
|
+
if (cc.resolved.tools.length > 0) {
|
|
82
|
+
parts.push(
|
|
83
|
+
"## Dynamic Tools\nCall these from executeCode via the `tools` global, e.g. `await tools.name(input)`.\n\n" +
|
|
84
|
+
formatToolSignatures(cc.resolved.tools),
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
if (cc.resolved.services.length > 0) {
|
|
88
|
+
parts.push(`## Services\n${formatServiceCards(cc.resolved.services)}`);
|
|
89
|
+
}
|
|
90
|
+
if (cc.resolved.knowledge.length > 0) {
|
|
91
|
+
parts.push(`## Knowledge\n${formatKnowledgeChunks(cc.resolved.knowledge)}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return parts.join("\n\n");
|
|
96
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { ControlCenterClient } from "./client";
|
|
2
|
+
import type { RuntimeAgentConfig } from "./types";
|
|
3
|
+
|
|
4
|
+
export type CcToolBinding = {
|
|
5
|
+
toolId: string;
|
|
6
|
+
readOnly: boolean;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Turn-scoped and mutable: seeded from /resolve, extended by searchTools
|
|
11
|
+
* mid-turn so a tool discovered at step 3 is callable at step 4.
|
|
12
|
+
*/
|
|
13
|
+
export type CcToolRegistry = Map<string, CcToolBinding>;
|
|
14
|
+
|
|
15
|
+
export function registerCcTools(
|
|
16
|
+
registry: CcToolRegistry,
|
|
17
|
+
tools: { name: string; toolId: string; readOnly: boolean }[],
|
|
18
|
+
) {
|
|
19
|
+
for (const tool of tools) {
|
|
20
|
+
registry.set(tool.name, { toolId: tool.toolId, readOnly: tool.readOnly });
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Dynamic tools run inside the sandbox, so they never appear as message tool
|
|
25
|
+
// parts — track invocations here to feed /resolve's hints.recentToolNames.
|
|
26
|
+
// ponytail: in-memory per-thread recency, move to the DB if multi-instance matters
|
|
27
|
+
const MAX_RECENT_TOOLS = 20;
|
|
28
|
+
const MAX_TRACKED_THREADS = 1_000;
|
|
29
|
+
const recentToolsByThread = new Map<string, string[]>();
|
|
30
|
+
|
|
31
|
+
export function recordRecentCcTool(threadId: string, name: string) {
|
|
32
|
+
const recent = recentToolsByThread.get(threadId) ?? [];
|
|
33
|
+
const next = [name, ...recent.filter((n) => n !== name)].slice(0, MAX_RECENT_TOOLS);
|
|
34
|
+
recentToolsByThread.delete(threadId);
|
|
35
|
+
recentToolsByThread.set(threadId, next);
|
|
36
|
+
|
|
37
|
+
if (recentToolsByThread.size > MAX_TRACKED_THREADS) {
|
|
38
|
+
const oldest = recentToolsByThread.keys().next().value;
|
|
39
|
+
if (oldest !== undefined) recentToolsByThread.delete(oldest);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function getRecentCcTools(threadId: string) {
|
|
44
|
+
return recentToolsByThread.get(threadId) ?? [];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Everything a CC-aware tool needs for one turn. */
|
|
48
|
+
export type CcRuntime = {
|
|
49
|
+
client: ControlCenterClient;
|
|
50
|
+
agentId: string;
|
|
51
|
+
config: RuntimeAgentConfig;
|
|
52
|
+
registry: CcToolRegistry;
|
|
53
|
+
threadId: string;
|
|
54
|
+
turnKey: string;
|
|
55
|
+
userId: string;
|
|
56
|
+
locale: RuntimeAgentConfig["defaultLocale"];
|
|
57
|
+
endUserToken: string;
|
|
58
|
+
abortSignal?: AbortSignal;
|
|
59
|
+
getStepIndex: () => number;
|
|
60
|
+
nextCallIndex: (stepIndex: number) => number;
|
|
61
|
+
onToolProgress?: (toolName: string, toolCallId: string, status: "started" | "finished") => void;
|
|
62
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import z from "zod";
|
|
2
|
+
import {
|
|
3
|
+
RUNTIME_ERROR_KINDS,
|
|
4
|
+
runtimeErrorSchema as strictRuntimeErrorSchema,
|
|
5
|
+
} from "@cortex/contracts/runtime";
|
|
6
|
+
|
|
7
|
+
export {
|
|
8
|
+
AGENT_SLUG_PATTERN,
|
|
9
|
+
executeResponseSchema,
|
|
10
|
+
resolveResponseSchema,
|
|
11
|
+
runtimeAgentConfigSchema,
|
|
12
|
+
searchKnowledgeResponseSchema,
|
|
13
|
+
searchServicesResponseSchema,
|
|
14
|
+
searchToolsResponseSchema,
|
|
15
|
+
type ExecuteRequest,
|
|
16
|
+
type ResolveRequest,
|
|
17
|
+
type ResolveResponse,
|
|
18
|
+
type RuntimeAgentConfig,
|
|
19
|
+
type SearchRequest,
|
|
20
|
+
} from "@cortex/contracts/runtime";
|
|
21
|
+
|
|
22
|
+
const knownRuntimeErrorKinds = new Set<string>(RUNTIME_ERROR_KINDS);
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The contract's error envelope, hardened for this consumer: a newer runtime may
|
|
26
|
+
* answer with an error kind this build has never heard of, which must degrade to a
|
|
27
|
+
* generic non-retryable `internal` rather than fail the parse.
|
|
28
|
+
*/
|
|
29
|
+
export const runtimeErrorSchema = z.preprocess(function normalizeUnknownErrorKind(value) {
|
|
30
|
+
if (typeof value !== "object" || value === null || !("error" in value)) return value;
|
|
31
|
+
|
|
32
|
+
const error = value.error;
|
|
33
|
+
if (typeof error !== "object" || error === null || !("kind" in error)) return value;
|
|
34
|
+
if (typeof error.kind === "string" && knownRuntimeErrorKinds.has(error.kind)) return value;
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
...value,
|
|
38
|
+
error: {
|
|
39
|
+
...error,
|
|
40
|
+
kind: "internal",
|
|
41
|
+
retryable: false,
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}, strictRuntimeErrorSchema);
|