@arnilo/prism-supervisor 0.0.5
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/CHANGELOG.md +13 -0
- package/LICENSE +9 -0
- package/README.md +31 -0
- package/dist/a2a-card.d.ts +17 -0
- package/dist/a2a-card.js +129 -0
- package/dist/a2a-client.d.ts +2 -0
- package/dist/a2a-client.js +312 -0
- package/dist/a2a-server.d.ts +2 -0
- package/dist/a2a-server.js +230 -0
- package/dist/a2a-types.d.ts +135 -0
- package/dist/a2a-types.js +2 -0
- package/dist/errors.d.ts +17 -0
- package/dist/errors.js +26 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +8 -0
- package/dist/limits.d.ts +38 -0
- package/dist/limits.js +42 -0
- package/dist/supervisor.d.ts +2 -0
- package/dist/supervisor.js +218 -0
- package/dist/types.d.ts +99 -0
- package/dist/types.js +2 -0
- package/package.json +57 -0
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { createA2AAgentCard } from "./a2a-card.js";
|
|
2
|
+
import { A2AError } from "./errors.js";
|
|
3
|
+
const JSON_HEADERS = { "content-type": "application/a2a+json; charset=utf-8" };
|
|
4
|
+
const SSE_HEADERS = { "content-type": "text/event-stream; charset=utf-8", "cache-control": "no-cache, no-transform" };
|
|
5
|
+
const DEFAULTS = { maxRequestBytes: 64 * 1024, maxResponseBytes: 1024 * 1024, maxEventBytes: 64 * 1024, maxStreamBytes: 10 * 1024 * 1024, maxStreamEvents: 10_000, maxConcurrentRequests: 16, timeoutMs: 120_000, maxCardBytes: 64 * 1024 };
|
|
6
|
+
const HARD = { maxRequestBytes: 1024 * 1024, maxResponseBytes: 8 * 1024 * 1024, maxEventBytes: 1024 * 1024, maxStreamBytes: 64 * 1024 * 1024, maxStreamEvents: 100_000, maxConcurrentRequests: 256, timeoutMs: 30 * 60_000, maxCardBytes: 1024 * 1024 };
|
|
7
|
+
export function createA2AHandler(options) {
|
|
8
|
+
const limits = resolveA2ALimits(options.limits);
|
|
9
|
+
const card = createA2AAgentCard(options.card);
|
|
10
|
+
const endpointPath = options.endpointPath ?? new URL(card.supportedInterfaces[0].url).pathname;
|
|
11
|
+
if (!endpointPath.startsWith("/"))
|
|
12
|
+
throw new A2AError("endpointPath must be absolute", 400, "ERR_PRISM_A2A_CONFIG");
|
|
13
|
+
const cardJson = JSON.stringify(card);
|
|
14
|
+
if (new TextEncoder().encode(cardJson).byteLength > limits.maxCardBytes)
|
|
15
|
+
throw new A2AError("Agent card exceeds max bytes", 400, "ERR_PRISM_A2A_CARD");
|
|
16
|
+
let active = 0;
|
|
17
|
+
let sequence = 0;
|
|
18
|
+
return async (request) => {
|
|
19
|
+
let acquired = false;
|
|
20
|
+
let transferred = false;
|
|
21
|
+
try {
|
|
22
|
+
const path = new URL(request.url).pathname;
|
|
23
|
+
if (request.method === "GET" && path === "/.well-known/agent-card.json")
|
|
24
|
+
return new Response(cardJson, { status: 200, headers: JSON_HEADERS });
|
|
25
|
+
if (request.method !== "POST" || path !== endpointPath)
|
|
26
|
+
return errorResponse(404, "Not found", null);
|
|
27
|
+
if (active >= limits.maxConcurrentRequests)
|
|
28
|
+
return errorResponse(429, "Too many requests", null);
|
|
29
|
+
active += 1;
|
|
30
|
+
acquired = true;
|
|
31
|
+
const owned = ownedSignal(request.signal, limits.timeoutMs);
|
|
32
|
+
try {
|
|
33
|
+
const contentType = request.headers.get("content-type")?.split(";", 1)[0]?.trim();
|
|
34
|
+
if (contentType !== "application/json" && contentType !== "application/a2a+json")
|
|
35
|
+
return errorResponse(415, "Unsupported media type", null);
|
|
36
|
+
const body = await readJson(request, limits.maxRequestBytes, owned.signal);
|
|
37
|
+
const rpc = parseRpc(body);
|
|
38
|
+
const authorized = await abortable(Promise.resolve(options.authorize({ request, method: rpc.method, signal: owned.signal })), owned.signal);
|
|
39
|
+
if (!authorized)
|
|
40
|
+
return errorResponse(403, "Forbidden", rpc.id);
|
|
41
|
+
if (rpc.method === "GetExtendedAgentCard")
|
|
42
|
+
return boundedJson({ jsonrpc: "2.0", id: rpc.id, result: card }, limits.maxResponseBytes, options);
|
|
43
|
+
if (rpc.method !== "SendMessage" && rpc.method !== "SendStreamingMessage")
|
|
44
|
+
return boundedJson({ jsonrpc: "2.0", id: rpc.id, error: { code: -32601, message: "Method not found" } }, limits.maxResponseBytes, options);
|
|
45
|
+
const message = parseMessage(rpc.params?.message, limits.maxRequestBytes);
|
|
46
|
+
const input = message.parts.map((part) => part.text).join("\n");
|
|
47
|
+
sequence += 1;
|
|
48
|
+
const taskId = `task-${crypto.randomUUID()}`;
|
|
49
|
+
const contextId = message.contextId ?? `context-${sequence}-${crypto.randomUUID()}`;
|
|
50
|
+
const session = await abortable(Promise.resolve(options.exposure.sessionFactory(authorized)), owned.signal);
|
|
51
|
+
if (rpc.method === "SendMessage") {
|
|
52
|
+
const result = await abortable(session.run(input, { ownership: authorized.ownership, metadata: authorized.metadata, signal: owned.signal, redactor: options.redactor }), owned.signal);
|
|
53
|
+
return boundedJson({ jsonrpc: "2.0", id: rpc.id, result: { task: toTask(taskId, contextId, result, options) } }, limits.maxResponseBytes, options);
|
|
54
|
+
}
|
|
55
|
+
if (rpc.method === "SendStreamingMessage") {
|
|
56
|
+
transferred = true;
|
|
57
|
+
const stream = taskStream(rpc.id, taskId, contextId, () => session.run(input, { ownership: authorized.ownership, metadata: authorized.metadata, signal: owned.signal, redactor: options.redactor }), owned, limits, options, () => { active -= 1; });
|
|
58
|
+
return new Response(stream, { status: 200, headers: SSE_HEADERS });
|
|
59
|
+
}
|
|
60
|
+
throw new A2AError("Method not found", 400, "ERR_PRISM_A2A_METHOD");
|
|
61
|
+
}
|
|
62
|
+
finally {
|
|
63
|
+
if (!transferred)
|
|
64
|
+
owned.dispose();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
const status = error instanceof A2AError ? error.status : error instanceof DOMException && error.name === "AbortError" ? 408 : 500;
|
|
69
|
+
return errorResponse(status, safeError(error, options), null);
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
if (acquired && !transferred)
|
|
73
|
+
active -= 1;
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function taskStream(id, taskId, contextId, run, owned, limits, options, release) {
|
|
78
|
+
const iterator = (async function* () {
|
|
79
|
+
yield { jsonrpc: "2.0", id, result: { task: { id: taskId, contextId, status: { state: "TASK_STATE_WORKING", timestamp: new Date().toISOString() } } } };
|
|
80
|
+
try {
|
|
81
|
+
const result = await run();
|
|
82
|
+
yield { jsonrpc: "2.0", id, result: { task: toTask(taskId, contextId, result, options) } };
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
yield { jsonrpc: "2.0", id, error: { code: -32000, message: safeError(error, options) } };
|
|
86
|
+
}
|
|
87
|
+
})()[Symbol.asyncIterator]();
|
|
88
|
+
let events = 0;
|
|
89
|
+
let bytes = 0;
|
|
90
|
+
let released = false;
|
|
91
|
+
const finish = () => { if (!released) {
|
|
92
|
+
released = true;
|
|
93
|
+
owned.dispose();
|
|
94
|
+
release();
|
|
95
|
+
} };
|
|
96
|
+
return new ReadableStream({
|
|
97
|
+
async pull(controller) {
|
|
98
|
+
try {
|
|
99
|
+
const next = await iterator.next();
|
|
100
|
+
if (next.done) {
|
|
101
|
+
finish();
|
|
102
|
+
controller.close();
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const payload = options.redactor?.redact(next.value) ?? next.value;
|
|
106
|
+
const chunk = new TextEncoder().encode(`data: ${JSON.stringify(payload)}\n\n`);
|
|
107
|
+
events += 1;
|
|
108
|
+
bytes += chunk.byteLength;
|
|
109
|
+
if (chunk.byteLength > limits.maxEventBytes || events > limits.maxStreamEvents || bytes > limits.maxStreamBytes)
|
|
110
|
+
throw new A2AError("A2A stream limit exceeded", 507, "ERR_PRISM_A2A_STREAM_LIMIT");
|
|
111
|
+
controller.enqueue(chunk);
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
finish();
|
|
115
|
+
controller.error(error);
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
cancel(reason) { owned.abort(reason); finish(); void iterator.return?.(); },
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
function toTask(taskId, contextId, result, options) {
|
|
122
|
+
const state = result.status === "succeeded" ? "TASK_STATE_COMPLETED" : result.status === "aborted" ? "TASK_STATE_CANCELED" : "TASK_STATE_FAILED";
|
|
123
|
+
const text = options.redactor?.redact(result.text) ?? result.text;
|
|
124
|
+
return Object.freeze({
|
|
125
|
+
id: taskId,
|
|
126
|
+
contextId,
|
|
127
|
+
status: { state, timestamp: new Date().toISOString() },
|
|
128
|
+
artifacts: text ? [{ artifactId: `${taskId}-result`, parts: [{ text }] }] : undefined,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
function parseRpc(value) {
|
|
132
|
+
if (!isRecord(value) || value.jsonrpc !== "2.0" || !(typeof value.id === "string" || typeof value.id === "number" || value.id === null) || typeof value.method !== "string")
|
|
133
|
+
throw new A2AError("Invalid JSON-RPC request", 400, "ERR_PRISM_A2A_REQUEST");
|
|
134
|
+
if (value.params !== undefined && !isRecord(value.params))
|
|
135
|
+
throw new A2AError("Invalid JSON-RPC params", 400, "ERR_PRISM_A2A_REQUEST");
|
|
136
|
+
return { jsonrpc: "2.0", id: value.id, method: value.method, params: value.params };
|
|
137
|
+
}
|
|
138
|
+
function parseMessage(value, maxBytes) {
|
|
139
|
+
if (!isRecord(value) || (value.role !== "user" && value.role !== "ROLE_USER") || typeof value.messageId !== "string" || !value.messageId || !Array.isArray(value.parts) || value.parts.length < 1 || value.parts.length > 32)
|
|
140
|
+
throw new A2AError("Invalid A2A message", 400, "ERR_PRISM_A2A_MESSAGE");
|
|
141
|
+
const parts = value.parts.map((part) => {
|
|
142
|
+
if (!isRecord(part) || typeof part.text !== "string" || Object.keys(part).some((key) => key !== "text" && key !== "metadata"))
|
|
143
|
+
throw new A2AError("Only text A2A parts are supported", 400, "ERR_PRISM_A2A_MESSAGE");
|
|
144
|
+
return { text: part.text, metadata: isRecord(part.metadata) ? part.metadata : undefined };
|
|
145
|
+
});
|
|
146
|
+
const message = { role: value.role, messageId: value.messageId, parts, contextId: typeof value.contextId === "string" ? value.contextId : undefined };
|
|
147
|
+
if (encode(message).byteLength > maxBytes)
|
|
148
|
+
throw new A2AError("A2A message exceeds max bytes", 413, "ERR_PRISM_A2A_MESSAGE_LIMIT");
|
|
149
|
+
return message;
|
|
150
|
+
}
|
|
151
|
+
function resolveA2ALimits(input = {}) {
|
|
152
|
+
const output = {};
|
|
153
|
+
for (const key of Object.keys(DEFAULTS)) {
|
|
154
|
+
const value = input[key] ?? DEFAULTS[key];
|
|
155
|
+
if (!Number.isSafeInteger(value) || value < 1 || value > HARD[key])
|
|
156
|
+
throw new A2AError(`${key} is invalid`, 400, "ERR_PRISM_A2A_CONFIG");
|
|
157
|
+
output[key] = value;
|
|
158
|
+
}
|
|
159
|
+
return output;
|
|
160
|
+
}
|
|
161
|
+
async function readJson(request, maxBytes, signal) {
|
|
162
|
+
if (!request.body)
|
|
163
|
+
throw new A2AError("Request body is required", 400, "ERR_PRISM_A2A_REQUEST");
|
|
164
|
+
const reader = request.body.getReader();
|
|
165
|
+
const chunks = [];
|
|
166
|
+
let size = 0;
|
|
167
|
+
try {
|
|
168
|
+
while (true) {
|
|
169
|
+
signal.throwIfAborted();
|
|
170
|
+
const next = await reader.read();
|
|
171
|
+
if (next.done)
|
|
172
|
+
break;
|
|
173
|
+
size += next.value.byteLength;
|
|
174
|
+
if (size > maxBytes)
|
|
175
|
+
throw new A2AError("Request exceeds max bytes", 413, "ERR_PRISM_A2A_REQUEST_LIMIT");
|
|
176
|
+
chunks.push(next.value);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
finally {
|
|
180
|
+
reader.releaseLock();
|
|
181
|
+
}
|
|
182
|
+
const bytes = new Uint8Array(size);
|
|
183
|
+
let offset = 0;
|
|
184
|
+
for (const chunk of chunks) {
|
|
185
|
+
bytes.set(chunk, offset);
|
|
186
|
+
offset += chunk.byteLength;
|
|
187
|
+
}
|
|
188
|
+
try {
|
|
189
|
+
return JSON.parse(new TextDecoder().decode(bytes));
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
throw new A2AError("Invalid JSON", 400, "ERR_PRISM_A2A_REQUEST");
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
function boundedJson(value, maxBytes, options) {
|
|
196
|
+
const body = JSON.stringify(options.redactor?.redact(value) ?? value);
|
|
197
|
+
if (new TextEncoder().encode(body).byteLength > maxBytes)
|
|
198
|
+
throw new A2AError("Response exceeds max bytes", 507, "ERR_PRISM_A2A_RESPONSE_LIMIT");
|
|
199
|
+
return new Response(body, { status: 200, headers: JSON_HEADERS });
|
|
200
|
+
}
|
|
201
|
+
function errorResponse(status, message, id) {
|
|
202
|
+
const body = { jsonrpc: "2.0", id, error: { code: status === 404 ? -32601 : -32000, message } };
|
|
203
|
+
return new Response(JSON.stringify(body), { status, headers: JSON_HEADERS });
|
|
204
|
+
}
|
|
205
|
+
function ownedSignal(parent, timeoutMs) {
|
|
206
|
+
const controller = new AbortController();
|
|
207
|
+
const abort = () => controller.abort(parent.reason);
|
|
208
|
+
if (parent.aborted)
|
|
209
|
+
abort();
|
|
210
|
+
else
|
|
211
|
+
parent.addEventListener("abort", abort, { once: true });
|
|
212
|
+
const timer = setTimeout(() => controller.abort(new DOMException("A2A request timed out", "AbortError")), timeoutMs);
|
|
213
|
+
return { signal: controller.signal, abort: (reason) => controller.abort(reason), dispose: () => { clearTimeout(timer); parent.removeEventListener("abort", abort); } };
|
|
214
|
+
}
|
|
215
|
+
function abortable(promise, signal) {
|
|
216
|
+
if (signal.aborted)
|
|
217
|
+
return Promise.reject(signal.reason);
|
|
218
|
+
return new Promise((resolve, reject) => {
|
|
219
|
+
const abort = () => reject(signal.reason);
|
|
220
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
221
|
+
promise.then(resolve, reject).finally(() => signal.removeEventListener("abort", abort));
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
function encode(value) { return new TextEncoder().encode(JSON.stringify(value)); }
|
|
225
|
+
function isRecord(value) { return !!value && typeof value === "object" && !Array.isArray(value); }
|
|
226
|
+
function safeError(error, options) {
|
|
227
|
+
const message = (error instanceof Error ? error.message : "A2A request failed").slice(0, 1024);
|
|
228
|
+
return options.redactor?.redact(message) ?? message;
|
|
229
|
+
}
|
|
230
|
+
//# sourceMappingURL=a2a-server.js.map
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import type { AgentRunResult, AgentSession, OwnershipScope, SecretRedactor } from "@arnilo/prism";
|
|
2
|
+
export declare const A2A_PROTOCOL_VERSION = "1.0";
|
|
3
|
+
export interface A2AAgentInterface {
|
|
4
|
+
readonly url: string;
|
|
5
|
+
readonly protocolBinding: "JSONRPC";
|
|
6
|
+
readonly protocolVersion: "1.0";
|
|
7
|
+
}
|
|
8
|
+
export interface A2AAgentSkill {
|
|
9
|
+
readonly id: string;
|
|
10
|
+
readonly name: string;
|
|
11
|
+
readonly description: string;
|
|
12
|
+
readonly tags: readonly string[];
|
|
13
|
+
readonly examples?: readonly string[];
|
|
14
|
+
readonly inputModes?: readonly string[];
|
|
15
|
+
readonly outputModes?: readonly string[];
|
|
16
|
+
}
|
|
17
|
+
export interface A2AAgentCardSignature {
|
|
18
|
+
readonly protected: string;
|
|
19
|
+
readonly signature: string;
|
|
20
|
+
readonly header?: Readonly<Record<string, unknown>>;
|
|
21
|
+
}
|
|
22
|
+
export interface A2AAgentCard {
|
|
23
|
+
readonly name: string;
|
|
24
|
+
readonly description: string;
|
|
25
|
+
readonly supportedInterfaces: readonly A2AAgentInterface[];
|
|
26
|
+
readonly version: string;
|
|
27
|
+
readonly capabilities: {
|
|
28
|
+
readonly streaming: boolean;
|
|
29
|
+
readonly pushNotifications?: boolean;
|
|
30
|
+
readonly extendedAgentCard?: boolean;
|
|
31
|
+
};
|
|
32
|
+
readonly defaultInputModes: readonly string[];
|
|
33
|
+
readonly defaultOutputModes: readonly string[];
|
|
34
|
+
readonly skills: readonly A2AAgentSkill[];
|
|
35
|
+
readonly securitySchemes?: Readonly<Record<string, unknown>>;
|
|
36
|
+
readonly security?: readonly Readonly<Record<string, readonly string[]>>[];
|
|
37
|
+
readonly signatures?: readonly A2AAgentCardSignature[];
|
|
38
|
+
}
|
|
39
|
+
export interface A2ATextPart {
|
|
40
|
+
readonly text: string;
|
|
41
|
+
readonly metadata?: Readonly<Record<string, unknown>>;
|
|
42
|
+
}
|
|
43
|
+
export interface A2AMessage {
|
|
44
|
+
readonly role: "user" | "agent" | "ROLE_USER" | "ROLE_AGENT";
|
|
45
|
+
readonly parts: readonly A2ATextPart[];
|
|
46
|
+
readonly messageId: string;
|
|
47
|
+
readonly contextId?: string;
|
|
48
|
+
readonly taskId?: string;
|
|
49
|
+
readonly metadata?: Readonly<Record<string, unknown>>;
|
|
50
|
+
}
|
|
51
|
+
export type A2ATaskState = "TASK_STATE_SUBMITTED" | "TASK_STATE_WORKING" | "TASK_STATE_COMPLETED" | "TASK_STATE_FAILED" | "TASK_STATE_CANCELED";
|
|
52
|
+
export interface A2ATask {
|
|
53
|
+
readonly id: string;
|
|
54
|
+
readonly contextId: string;
|
|
55
|
+
readonly status: {
|
|
56
|
+
readonly state: A2ATaskState;
|
|
57
|
+
readonly timestamp: string;
|
|
58
|
+
readonly message?: A2AMessage;
|
|
59
|
+
};
|
|
60
|
+
readonly artifacts?: readonly {
|
|
61
|
+
readonly artifactId: string;
|
|
62
|
+
readonly parts: readonly A2ATextPart[];
|
|
63
|
+
}[];
|
|
64
|
+
}
|
|
65
|
+
export type A2ARequestId = string | number | null;
|
|
66
|
+
export interface A2AJsonRpcRequest {
|
|
67
|
+
readonly jsonrpc: "2.0";
|
|
68
|
+
readonly id: A2ARequestId;
|
|
69
|
+
readonly method: "SendMessage" | "SendStreamingMessage" | "GetExtendedAgentCard" | string;
|
|
70
|
+
readonly params?: Readonly<Record<string, unknown>>;
|
|
71
|
+
}
|
|
72
|
+
export interface A2AJsonRpcResponse {
|
|
73
|
+
readonly jsonrpc: "2.0";
|
|
74
|
+
readonly id: A2ARequestId;
|
|
75
|
+
readonly result?: unknown;
|
|
76
|
+
readonly error?: {
|
|
77
|
+
readonly code: number;
|
|
78
|
+
readonly message: string;
|
|
79
|
+
readonly data?: unknown;
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
export interface A2AAuthorization {
|
|
83
|
+
readonly ownership: OwnershipScope;
|
|
84
|
+
readonly metadata?: Readonly<Record<string, unknown>>;
|
|
85
|
+
}
|
|
86
|
+
export type A2AAuthorizer = (input: {
|
|
87
|
+
readonly request: Request;
|
|
88
|
+
readonly method: string;
|
|
89
|
+
readonly signal: AbortSignal;
|
|
90
|
+
}) => false | A2AAuthorization | Promise<false | A2AAuthorization>;
|
|
91
|
+
export interface A2AAgentExposure {
|
|
92
|
+
readonly sessionFactory: (authorization: A2AAuthorization) => AgentSession | Promise<AgentSession>;
|
|
93
|
+
}
|
|
94
|
+
export interface A2ALimits {
|
|
95
|
+
readonly maxRequestBytes?: number;
|
|
96
|
+
readonly maxResponseBytes?: number;
|
|
97
|
+
readonly maxEventBytes?: number;
|
|
98
|
+
readonly maxStreamBytes?: number;
|
|
99
|
+
readonly maxStreamEvents?: number;
|
|
100
|
+
readonly maxConcurrentRequests?: number;
|
|
101
|
+
readonly timeoutMs?: number;
|
|
102
|
+
readonly maxCardBytes?: number;
|
|
103
|
+
}
|
|
104
|
+
export interface CreateA2AHandlerOptions {
|
|
105
|
+
readonly card: A2AAgentCard;
|
|
106
|
+
readonly exposure: A2AAgentExposure;
|
|
107
|
+
readonly authorize: A2AAuthorizer;
|
|
108
|
+
readonly endpointPath?: string;
|
|
109
|
+
readonly redactor?: SecretRedactor;
|
|
110
|
+
readonly limits?: A2ALimits;
|
|
111
|
+
}
|
|
112
|
+
export interface A2AClientOptions {
|
|
113
|
+
readonly endpoint: string;
|
|
114
|
+
readonly allowedOrigins: readonly string[];
|
|
115
|
+
readonly fetch?: typeof globalThis.fetch;
|
|
116
|
+
readonly authorize?: (input: {
|
|
117
|
+
readonly endpoint: string;
|
|
118
|
+
readonly signal: AbortSignal;
|
|
119
|
+
}) => HeadersInit | Promise<HeadersInit>;
|
|
120
|
+
readonly verifyCard?: (card: A2AAgentCard) => void | Promise<void>;
|
|
121
|
+
readonly cardUrl?: string;
|
|
122
|
+
readonly limits?: A2ALimits;
|
|
123
|
+
readonly redactor?: SecretRedactor;
|
|
124
|
+
}
|
|
125
|
+
export interface A2AClient {
|
|
126
|
+
getCard(options?: {
|
|
127
|
+
readonly signal?: AbortSignal;
|
|
128
|
+
}): Promise<A2AAgentCard>;
|
|
129
|
+
send(input: string, options?: {
|
|
130
|
+
readonly signal?: AbortSignal;
|
|
131
|
+
}): Promise<AgentRunResult>;
|
|
132
|
+
stream(input: string, options?: {
|
|
133
|
+
readonly signal?: AbortSignal;
|
|
134
|
+
}): AsyncIterable<string>;
|
|
135
|
+
}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export declare class SupervisorError extends Error {
|
|
2
|
+
readonly code: string;
|
|
3
|
+
constructor(message: string, code?: string);
|
|
4
|
+
}
|
|
5
|
+
export declare class SupervisorValidationError extends SupervisorError {
|
|
6
|
+
constructor(message: string);
|
|
7
|
+
}
|
|
8
|
+
export declare class SupervisorLimitError extends SupervisorError {
|
|
9
|
+
constructor(message: string);
|
|
10
|
+
}
|
|
11
|
+
export declare class SupervisorDeniedError extends SupervisorError {
|
|
12
|
+
constructor(message?: string);
|
|
13
|
+
}
|
|
14
|
+
export declare class A2AError extends SupervisorError {
|
|
15
|
+
readonly status: number;
|
|
16
|
+
constructor(message: string, status?: number, code?: string);
|
|
17
|
+
}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export class SupervisorError extends Error {
|
|
2
|
+
code;
|
|
3
|
+
constructor(message, code = "ERR_PRISM_SUPERVISOR") {
|
|
4
|
+
super(message);
|
|
5
|
+
this.code = code;
|
|
6
|
+
this.name = "SupervisorError";
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export class SupervisorValidationError extends SupervisorError {
|
|
10
|
+
constructor(message) { super(message, "ERR_PRISM_SUPERVISOR_VALIDATION"); this.name = "SupervisorValidationError"; }
|
|
11
|
+
}
|
|
12
|
+
export class SupervisorLimitError extends SupervisorError {
|
|
13
|
+
constructor(message) { super(message, "ERR_PRISM_SUPERVISOR_LIMIT"); this.name = "SupervisorLimitError"; }
|
|
14
|
+
}
|
|
15
|
+
export class SupervisorDeniedError extends SupervisorError {
|
|
16
|
+
constructor(message = "Delegation denied") { super(message, "ERR_PRISM_SUPERVISOR_DENIED"); this.name = "SupervisorDeniedError"; }
|
|
17
|
+
}
|
|
18
|
+
export class A2AError extends SupervisorError {
|
|
19
|
+
status;
|
|
20
|
+
constructor(message, status = 400, code = "ERR_PRISM_A2A") {
|
|
21
|
+
super(message, code);
|
|
22
|
+
this.status = status;
|
|
23
|
+
this.name = "A2AError";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=errors.js.map
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from "./a2a-card.js";
|
|
2
|
+
export * from "./a2a-client.js";
|
|
3
|
+
export * from "./a2a-server.js";
|
|
4
|
+
export type * from "./a2a-types.js";
|
|
5
|
+
export * from "./errors.js";
|
|
6
|
+
export * from "./limits.js";
|
|
7
|
+
export * from "./supervisor.js";
|
|
8
|
+
export type * from "./types.js";
|
|
9
|
+
export declare const packageName = "@arnilo/prism-supervisor";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from "./a2a-card.js";
|
|
2
|
+
export * from "./a2a-client.js";
|
|
3
|
+
export * from "./a2a-server.js";
|
|
4
|
+
export * from "./errors.js";
|
|
5
|
+
export * from "./limits.js";
|
|
6
|
+
export * from "./supervisor.js";
|
|
7
|
+
export const packageName = "@arnilo/prism-supervisor";
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
package/dist/limits.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export declare const DEFAULT_MAX_DELEGATION_DEPTH = 4;
|
|
2
|
+
export declare const HARD_MAX_DELEGATION_DEPTH = 16;
|
|
3
|
+
export declare const DEFAULT_MAX_ACTIVE_CHILDREN = 4;
|
|
4
|
+
export declare const HARD_MAX_ACTIVE_CHILDREN = 32;
|
|
5
|
+
export declare const DEFAULT_MAX_DELEGATION_BYTES: number;
|
|
6
|
+
export declare const HARD_MAX_DELEGATION_BYTES: number;
|
|
7
|
+
export declare const DEFAULT_MAX_DELEGATION_STEPS = 8;
|
|
8
|
+
export declare const HARD_MAX_DELEGATION_STEPS = 64;
|
|
9
|
+
export declare const DEFAULT_MAX_DELEGATION_TOOL_CALLS = 32;
|
|
10
|
+
export declare const HARD_MAX_DELEGATION_TOOL_CALLS = 256;
|
|
11
|
+
export declare const DEFAULT_MAX_DELEGATION_TOKENS = 20000;
|
|
12
|
+
export declare const HARD_MAX_DELEGATION_TOKENS = 1000000;
|
|
13
|
+
export declare const DEFAULT_DELEGATION_TIMEOUT_MS = 60000;
|
|
14
|
+
export declare const HARD_DELEGATION_TIMEOUT_MS: number;
|
|
15
|
+
export declare const DEFAULT_MAX_SUPERVISOR_QUEUED_EVENTS = 128;
|
|
16
|
+
export declare const HARD_MAX_SUPERVISOR_QUEUED_EVENTS = 4096;
|
|
17
|
+
export interface SupervisorLimits {
|
|
18
|
+
readonly maxDepth?: number;
|
|
19
|
+
readonly maxActiveChildren?: number;
|
|
20
|
+
readonly maxMessageBytes?: number;
|
|
21
|
+
readonly maxSteps?: number;
|
|
22
|
+
readonly maxToolCalls?: number;
|
|
23
|
+
readonly maxTokens?: number;
|
|
24
|
+
readonly timeoutMs?: number;
|
|
25
|
+
readonly maxQueuedEvents?: number;
|
|
26
|
+
}
|
|
27
|
+
export interface ResolvedSupervisorLimits {
|
|
28
|
+
readonly maxDepth: number;
|
|
29
|
+
readonly maxActiveChildren: number;
|
|
30
|
+
readonly maxMessageBytes: number;
|
|
31
|
+
readonly maxSteps: number;
|
|
32
|
+
readonly maxToolCalls: number;
|
|
33
|
+
readonly maxTokens: number;
|
|
34
|
+
readonly timeoutMs: number;
|
|
35
|
+
readonly maxQueuedEvents: number;
|
|
36
|
+
}
|
|
37
|
+
export declare function resolveSupervisorLimits(input?: SupervisorLimits): ResolvedSupervisorLimits;
|
|
38
|
+
export declare function narrowSupervisorLimits(parent: ResolvedSupervisorLimits, input?: SupervisorLimits): ResolvedSupervisorLimits;
|
package/dist/limits.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { SupervisorValidationError } from "./errors.js";
|
|
2
|
+
export const DEFAULT_MAX_DELEGATION_DEPTH = 4;
|
|
3
|
+
export const HARD_MAX_DELEGATION_DEPTH = 16;
|
|
4
|
+
export const DEFAULT_MAX_ACTIVE_CHILDREN = 4;
|
|
5
|
+
export const HARD_MAX_ACTIVE_CHILDREN = 32;
|
|
6
|
+
export const DEFAULT_MAX_DELEGATION_BYTES = 64 * 1024;
|
|
7
|
+
export const HARD_MAX_DELEGATION_BYTES = 1024 * 1024;
|
|
8
|
+
export const DEFAULT_MAX_DELEGATION_STEPS = 8;
|
|
9
|
+
export const HARD_MAX_DELEGATION_STEPS = 64;
|
|
10
|
+
export const DEFAULT_MAX_DELEGATION_TOOL_CALLS = 32;
|
|
11
|
+
export const HARD_MAX_DELEGATION_TOOL_CALLS = 256;
|
|
12
|
+
export const DEFAULT_MAX_DELEGATION_TOKENS = 20_000;
|
|
13
|
+
export const HARD_MAX_DELEGATION_TOKENS = 1_000_000;
|
|
14
|
+
export const DEFAULT_DELEGATION_TIMEOUT_MS = 60_000;
|
|
15
|
+
export const HARD_DELEGATION_TIMEOUT_MS = 30 * 60_000;
|
|
16
|
+
export const DEFAULT_MAX_SUPERVISOR_QUEUED_EVENTS = 128;
|
|
17
|
+
export const HARD_MAX_SUPERVISOR_QUEUED_EVENTS = 4096;
|
|
18
|
+
const SPECS = {
|
|
19
|
+
maxDepth: [DEFAULT_MAX_DELEGATION_DEPTH, HARD_MAX_DELEGATION_DEPTH],
|
|
20
|
+
maxActiveChildren: [DEFAULT_MAX_ACTIVE_CHILDREN, HARD_MAX_ACTIVE_CHILDREN],
|
|
21
|
+
maxMessageBytes: [DEFAULT_MAX_DELEGATION_BYTES, HARD_MAX_DELEGATION_BYTES],
|
|
22
|
+
maxSteps: [DEFAULT_MAX_DELEGATION_STEPS, HARD_MAX_DELEGATION_STEPS],
|
|
23
|
+
maxToolCalls: [DEFAULT_MAX_DELEGATION_TOOL_CALLS, HARD_MAX_DELEGATION_TOOL_CALLS],
|
|
24
|
+
maxTokens: [DEFAULT_MAX_DELEGATION_TOKENS, HARD_MAX_DELEGATION_TOKENS],
|
|
25
|
+
timeoutMs: [DEFAULT_DELEGATION_TIMEOUT_MS, HARD_DELEGATION_TIMEOUT_MS],
|
|
26
|
+
maxQueuedEvents: [DEFAULT_MAX_SUPERVISOR_QUEUED_EVENTS, HARD_MAX_SUPERVISOR_QUEUED_EVENTS],
|
|
27
|
+
};
|
|
28
|
+
export function resolveSupervisorLimits(input = {}) {
|
|
29
|
+
return Object.fromEntries(Object.entries(SPECS).map(([key, [fallback, hard]]) => {
|
|
30
|
+
const value = input[key] ?? fallback;
|
|
31
|
+
if (!Number.isSafeInteger(value) || value < 1 || value > hard)
|
|
32
|
+
throw new SupervisorValidationError(`${key} must be a positive integer at most ${hard}`);
|
|
33
|
+
return [key, value];
|
|
34
|
+
}));
|
|
35
|
+
}
|
|
36
|
+
export function narrowSupervisorLimits(parent, input) {
|
|
37
|
+
if (!input)
|
|
38
|
+
return parent;
|
|
39
|
+
const requested = resolveSupervisorLimits({ ...parent, ...input });
|
|
40
|
+
return Object.fromEntries(Object.keys(SPECS).map((key) => [key, Math.min(parent[key], requested[key])]));
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=limits.js.map
|