@agent-native/agentkit 0.1.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/ARCHITECTURE.md +290 -0
- package/README.md +822 -0
- package/dist/adapters/http.d.ts +56 -0
- package/dist/adapters/http.d.ts.map +1 -0
- package/dist/adapters/http.js +950 -0
- package/dist/adapters/http.js.map +1 -0
- package/dist/adapters/index.d.ts +2 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +2 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/client/client.d.ts +240 -0
- package/dist/client/client.d.ts.map +1 -0
- package/dist/client/client.js +1605 -0
- package/dist/client/client.js.map +1 -0
- package/dist/client/index.d.ts +3 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +3 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/state.d.ts +72 -0
- package/dist/client/state.d.ts.map +1 -0
- package/dist/client/state.js +385 -0
- package/dist/client/state.js.map +1 -0
- package/dist/conformance/index.d.ts +41 -0
- package/dist/conformance/index.d.ts.map +1 -0
- package/dist/conformance/index.js +924 -0
- package/dist/conformance/index.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/protocol/agui-codec.d.ts +28 -0
- package/dist/protocol/agui-codec.d.ts.map +1 -0
- package/dist/protocol/agui-codec.js +250 -0
- package/dist/protocol/agui-codec.js.map +1 -0
- package/dist/protocol/agui.d.ts +144 -0
- package/dist/protocol/agui.d.ts.map +1 -0
- package/dist/protocol/agui.js +253 -0
- package/dist/protocol/agui.js.map +1 -0
- package/dist/protocol/compatibility.d.ts +35 -0
- package/dist/protocol/compatibility.d.ts.map +1 -0
- package/dist/protocol/compatibility.js +160 -0
- package/dist/protocol/compatibility.js.map +1 -0
- package/dist/protocol/errors.d.ts +24 -0
- package/dist/protocol/errors.d.ts.map +1 -0
- package/dist/protocol/errors.js +77 -0
- package/dist/protocol/errors.js.map +1 -0
- package/dist/protocol/index.d.ts +982 -0
- package/dist/protocol/index.d.ts.map +1 -0
- package/dist/protocol/index.js +56 -0
- package/dist/protocol/index.js.map +1 -0
- package/dist/protocol/validation.d.ts +94 -0
- package/dist/protocol/validation.d.ts.map +1 -0
- package/dist/protocol/validation.js +1871 -0
- package/dist/protocol/validation.js.map +1 -0
- package/dist/protocol/version.d.ts +7 -0
- package/dist/protocol/version.d.ts.map +1 -0
- package/dist/protocol/version.js +9 -0
- package/dist/protocol/version.js.map +1 -0
- package/dist/react/chat.d.ts +34 -0
- package/dist/react/chat.d.ts.map +1 -0
- package/dist/react/chat.js +23 -0
- package/dist/react/chat.js.map +1 -0
- package/dist/react/components.d.ts +96 -0
- package/dist/react/components.d.ts.map +1 -0
- package/dist/react/components.js +1300 -0
- package/dist/react/components.js.map +1 -0
- package/dist/react/context.d.ts +259 -0
- package/dist/react/context.d.ts.map +1 -0
- package/dist/react/context.js +359 -0
- package/dist/react/context.js.map +1 -0
- package/dist/react/headless.d.ts +3 -0
- package/dist/react/headless.d.ts.map +1 -0
- package/dist/react/headless.js +3 -0
- package/dist/react/headless.js.map +1 -0
- package/dist/react/index.d.ts +6 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +6 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/root.d.ts +58 -0
- package/dist/react/root.d.ts.map +1 -0
- package/dist/react/root.js +151 -0
- package/dist/react/root.js.map +1 -0
- package/dist/react/streaming-text.d.ts +19 -0
- package/dist/react/streaming-text.d.ts.map +1 -0
- package/dist/react/streaming-text.js +259 -0
- package/dist/react/streaming-text.js.map +1 -0
- package/dist/react/styles.css +1477 -0
- package/package.json +112 -0
|
@@ -0,0 +1,950 @@
|
|
|
1
|
+
import { AGENTKIT_PROFILE_HEADER, AGENTKIT_PROFILE_ID, decodeAgUiEvent, encodeAgentEvent, AgentProtocolValidationError, AgentKitProtocolError, createCapabilityUnsupportedError, createAgentKitProtocolVersionOffer, createAgentProtocolEnvelope, createOperationUnsupportedError, createRequestAbortedError, isAgentKitProtocolError, negotiateAgentKitProtocolVersion, parseAgentActionResult, parseAgentCapabilitiesDiscovery, projectAgentCapabilities, parseCancelRunInput, parseCompleteUploadInput, parseCreateThreadInput, parseCreateUploadInput, parseForkThreadInput, parseAgentEvent, parseGetRunInput, parseAgentQueuedMessages, parseAgentThread, parseAgentUploadTarget, parseAgentProtocolEnvelope, parseFilePart, parseInvokeActionInput, parseListThreadsInput, parseQueueMessageInput, parseQueueMessageResult, parseResolveApprovalInput, parseResumeRunInput, parseResolveConnectionRequestInput, parseStartRunInput, parseStartRunResult, parseSubscribeToRunInput, parseSteerQueuedMessageResult, parseSubmitFeedbackInput, parseThreadMessageInput, parseListThreadsResult, parseNullableAgentRunSnapshot, parseNullableAgentThread, parseNullableAgentThreadSnapshot, parseUpdateThreadInput, parseDiscoverCapabilitiesInput, parseVoidResponse, resumeEntryFromApproval, } from "../protocol/index.js";
|
|
2
|
+
export class AgentKitHttpError extends Error {
|
|
3
|
+
status;
|
|
4
|
+
details;
|
|
5
|
+
code;
|
|
6
|
+
correlationId;
|
|
7
|
+
retryable;
|
|
8
|
+
constructor(status, message, details, code, correlationId, retryable = inferHttpRetryability(status)) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.status = status;
|
|
11
|
+
this.details = details;
|
|
12
|
+
this.code = code;
|
|
13
|
+
this.correlationId = correlationId;
|
|
14
|
+
this.retryable = retryable;
|
|
15
|
+
this.name = "AgentKitHttpError";
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function inferHttpRetryability(status) {
|
|
19
|
+
return (status === 408 ||
|
|
20
|
+
status === 425 ||
|
|
21
|
+
status === 429 ||
|
|
22
|
+
(status >= 500 && status !== 501 && status !== 505));
|
|
23
|
+
}
|
|
24
|
+
function trimSlash(value) {
|
|
25
|
+
return value.endsWith("/") ? value.slice(0, -1) : value;
|
|
26
|
+
}
|
|
27
|
+
function defaultCorrelationId() {
|
|
28
|
+
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
29
|
+
return crypto.randomUUID();
|
|
30
|
+
}
|
|
31
|
+
return `agentkit-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
32
|
+
}
|
|
33
|
+
function combineAbortSignals(first, second) {
|
|
34
|
+
if (!first)
|
|
35
|
+
return { signal: second, release() { } };
|
|
36
|
+
if (!second || first === second)
|
|
37
|
+
return { signal: first, release() { } };
|
|
38
|
+
const controller = new AbortController();
|
|
39
|
+
const abortFromFirst = () => controller.abort(first.reason);
|
|
40
|
+
const abortFromSecond = () => controller.abort(second.reason);
|
|
41
|
+
if (first.aborted)
|
|
42
|
+
abortFromFirst();
|
|
43
|
+
else
|
|
44
|
+
first.addEventListener("abort", abortFromFirst, { once: true });
|
|
45
|
+
if (second.aborted)
|
|
46
|
+
abortFromSecond();
|
|
47
|
+
else
|
|
48
|
+
second.addEventListener("abort", abortFromSecond, { once: true });
|
|
49
|
+
return {
|
|
50
|
+
signal: controller.signal,
|
|
51
|
+
release() {
|
|
52
|
+
first.removeEventListener("abort", abortFromFirst);
|
|
53
|
+
second.removeEventListener("abort", abortFromSecond);
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function requestAborted(correlationId, message) {
|
|
58
|
+
return new AgentKitProtocolError(createRequestAbortedError({ correlationId, message }));
|
|
59
|
+
}
|
|
60
|
+
function assertRequestActive(signal, correlationId) {
|
|
61
|
+
if (signal?.aborted)
|
|
62
|
+
throw requestAborted(correlationId);
|
|
63
|
+
}
|
|
64
|
+
function isAbortFailure(error) {
|
|
65
|
+
return error instanceof Error && error.name === "AbortError";
|
|
66
|
+
}
|
|
67
|
+
function assertRouteIdentifier(routeValue, payloadValue, name) {
|
|
68
|
+
if (routeValue !== payloadValue) {
|
|
69
|
+
throw new AgentProtocolValidationError(name, "the route and request payload must match");
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function responseCorrelationId(response, envelopeCorrelationId, expectedCorrelationId) {
|
|
73
|
+
const headerCorrelationId = response.headers.get("x-agentkit-correlation-id") ?? undefined;
|
|
74
|
+
const received = envelopeCorrelationId ?? headerCorrelationId;
|
|
75
|
+
if (envelopeCorrelationId &&
|
|
76
|
+
headerCorrelationId &&
|
|
77
|
+
envelopeCorrelationId !== headerCorrelationId) {
|
|
78
|
+
throw new AgentProtocolValidationError("envelope.correlationId", "must match the response correlation header");
|
|
79
|
+
}
|
|
80
|
+
if (expectedCorrelationId && received && expectedCorrelationId !== received) {
|
|
81
|
+
throw new AgentProtocolValidationError("envelope.correlationId", "must match the request correlation id");
|
|
82
|
+
}
|
|
83
|
+
return received ?? expectedCorrelationId;
|
|
84
|
+
}
|
|
85
|
+
async function parseResponse(response, parsePayload, expectedCorrelationId) {
|
|
86
|
+
let body;
|
|
87
|
+
try {
|
|
88
|
+
body = await response.json();
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
throw new AgentKitHttpError(response.status, "The AgentKit response was not valid JSON.", undefined, "invalid_response", responseCorrelationId(response, undefined, expectedCorrelationId), false);
|
|
92
|
+
}
|
|
93
|
+
const envelope = parseAgentProtocolEnvelope(body);
|
|
94
|
+
const correlationId = responseCorrelationId(response, envelope.correlationId, expectedCorrelationId);
|
|
95
|
+
if (!response.ok) {
|
|
96
|
+
if (envelope.kind !== "error") {
|
|
97
|
+
throw new AgentProtocolValidationError("envelope.kind", "expected an error envelope");
|
|
98
|
+
}
|
|
99
|
+
const payload = envelope.payload;
|
|
100
|
+
throw new AgentKitHttpError(response.status, payload.message, payload.details, payload.code, correlationId, payload.retryable ?? inferHttpRetryability(response.status));
|
|
101
|
+
}
|
|
102
|
+
if (envelope.kind !== "response") {
|
|
103
|
+
throw new AgentProtocolValidationError("envelope.kind", "expected a response envelope");
|
|
104
|
+
}
|
|
105
|
+
return parsePayload(envelope.payload, "envelope.payload");
|
|
106
|
+
}
|
|
107
|
+
async function* parseEventStream(response, expectedCorrelationId, afterSequence, context) {
|
|
108
|
+
if (!response.ok) {
|
|
109
|
+
await parseResponse(response, parseVoidResponse, expectedCorrelationId);
|
|
110
|
+
}
|
|
111
|
+
const contentType = response.headers
|
|
112
|
+
.get("content-type")
|
|
113
|
+
?.split(";", 1)[0]
|
|
114
|
+
?.trim()
|
|
115
|
+
.toLowerCase();
|
|
116
|
+
const profile = response.headers.get(AGENTKIT_PROFILE_HEADER);
|
|
117
|
+
if (profile !== AGENTKIT_PROFILE_ID) {
|
|
118
|
+
throw new AgentKitHttpError(response.status, profile === null
|
|
119
|
+
? `The event stream did not declare the required ${AGENTKIT_PROFILE_HEADER} profile.`
|
|
120
|
+
: `The event stream declared profile ${profile}, which this client cannot read.`, undefined, "unsupported_profile", responseCorrelationId(response, undefined, expectedCorrelationId), false);
|
|
121
|
+
}
|
|
122
|
+
if (contentType !== "text/event-stream") {
|
|
123
|
+
throw new AgentKitHttpError(response.status, "The AgentKit event response was not an event stream.", undefined, "invalid_event_stream", responseCorrelationId(response, undefined, expectedCorrelationId), false);
|
|
124
|
+
}
|
|
125
|
+
if (!response.body) {
|
|
126
|
+
throw new AgentKitHttpError(response.status, "The AgentKit event response did not include a body.", undefined, "invalid_event_stream", responseCorrelationId(response, undefined, expectedCorrelationId), false);
|
|
127
|
+
}
|
|
128
|
+
// AG-UI frames carry no envelope, so correlation is proven once for the whole
|
|
129
|
+
// stream here instead of per event. This throws on a mismatched response.
|
|
130
|
+
responseCorrelationId(response, undefined, expectedCorrelationId);
|
|
131
|
+
const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
|
|
132
|
+
let buffer = "";
|
|
133
|
+
let lastSequence = afterSequence;
|
|
134
|
+
let streamDone = false;
|
|
135
|
+
try {
|
|
136
|
+
while (true) {
|
|
137
|
+
const { value, done } = await reader.read();
|
|
138
|
+
buffer += value ?? "";
|
|
139
|
+
const blocks = buffer.split(/\r?\n\r?\n/);
|
|
140
|
+
buffer = blocks.pop() ?? "";
|
|
141
|
+
for (const block of blocks) {
|
|
142
|
+
const lines = block.split(/\r?\n/);
|
|
143
|
+
const data = lines
|
|
144
|
+
.filter((line) => line.startsWith("data:"))
|
|
145
|
+
.map((line) => line.slice(5).trimStart())
|
|
146
|
+
.join("\n");
|
|
147
|
+
if (!data)
|
|
148
|
+
continue;
|
|
149
|
+
const id = lines
|
|
150
|
+
.find((line) => line.startsWith("id:"))
|
|
151
|
+
?.slice(3)
|
|
152
|
+
.trim();
|
|
153
|
+
const event = decodeAgUiEvent(JSON.parse(data), context);
|
|
154
|
+
if (event === undefined)
|
|
155
|
+
continue;
|
|
156
|
+
if (id !== undefined &&
|
|
157
|
+
(!/^\d+$/.test(id) || !Number.isSafeInteger(Number(id)))) {
|
|
158
|
+
throw new AgentProtocolValidationError("event.id", "expected a non-negative safe integer cursor");
|
|
159
|
+
}
|
|
160
|
+
if (id !== undefined && Number(id) !== event.sequence) {
|
|
161
|
+
throw new AgentProtocolValidationError("event.id", "must match the event sequence");
|
|
162
|
+
}
|
|
163
|
+
if (event.sequence !== lastSequence + 1) {
|
|
164
|
+
throw new AgentProtocolValidationError("event.sequence", `must be contiguous after sequence ${lastSequence}`);
|
|
165
|
+
}
|
|
166
|
+
lastSequence = event.sequence;
|
|
167
|
+
yield event;
|
|
168
|
+
}
|
|
169
|
+
if (done) {
|
|
170
|
+
streamDone = true;
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
if (buffer.trim()) {
|
|
175
|
+
throw new Error("The AgentKit event stream ended with an incomplete event.");
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
finally {
|
|
179
|
+
if (!streamDone)
|
|
180
|
+
await Promise.allSettled([reader.cancel()]);
|
|
181
|
+
reader.releaseLock();
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
export function createAgentKitHttpTransport(options) {
|
|
185
|
+
const fetcher = options.fetch ?? globalThis.fetch;
|
|
186
|
+
const baseUrl = trimSlash(options.baseUrl);
|
|
187
|
+
const createCorrelationId = options.createCorrelationId ?? defaultCorrelationId;
|
|
188
|
+
async function request(path, parsePayload, init = {}, context) {
|
|
189
|
+
const correlationId = context?.correlationId ?? createCorrelationId();
|
|
190
|
+
const combinedSignal = combineAbortSignals(options.signal, context?.signal);
|
|
191
|
+
assertRequestActive(combinedSignal.signal, correlationId);
|
|
192
|
+
try {
|
|
193
|
+
const configuredHeaders = typeof options.headers === "function"
|
|
194
|
+
? await options.headers()
|
|
195
|
+
: options.headers;
|
|
196
|
+
assertRequestActive(combinedSignal.signal, correlationId);
|
|
197
|
+
const headers = new Headers(configuredHeaders);
|
|
198
|
+
headers.set("x-agentkit-correlation-id", correlationId);
|
|
199
|
+
let body = init.body;
|
|
200
|
+
if (body !== undefined) {
|
|
201
|
+
if (typeof body !== "string") {
|
|
202
|
+
throw new AgentProtocolValidationError("request.body", "expected a JSON request envelope");
|
|
203
|
+
}
|
|
204
|
+
const envelope = parseAgentProtocolEnvelope(JSON.parse(body));
|
|
205
|
+
if (envelope.kind !== "request") {
|
|
206
|
+
throw new AgentProtocolValidationError("envelope.kind", "expected a request envelope");
|
|
207
|
+
}
|
|
208
|
+
body = JSON.stringify({ ...envelope, correlationId });
|
|
209
|
+
}
|
|
210
|
+
if (init.body !== undefined)
|
|
211
|
+
headers.set("content-type", "application/json");
|
|
212
|
+
headers.set("accept", "application/json");
|
|
213
|
+
const response = await fetcher(`${baseUrl}${path}`, {
|
|
214
|
+
...init,
|
|
215
|
+
headers,
|
|
216
|
+
body,
|
|
217
|
+
signal: combinedSignal.signal,
|
|
218
|
+
});
|
|
219
|
+
assertRequestActive(combinedSignal.signal, correlationId);
|
|
220
|
+
return await parseResponse(response, parsePayload, correlationId);
|
|
221
|
+
}
|
|
222
|
+
catch (error) {
|
|
223
|
+
if (combinedSignal.signal?.aborted || isAbortFailure(error)) {
|
|
224
|
+
throw requestAborted(correlationId);
|
|
225
|
+
}
|
|
226
|
+
throw error;
|
|
227
|
+
}
|
|
228
|
+
finally {
|
|
229
|
+
combinedSignal.release();
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return {
|
|
233
|
+
discoverCapabilities(input, context) {
|
|
234
|
+
return request("/capabilities/discover", parseAgentCapabilitiesDiscovery, {
|
|
235
|
+
method: "POST",
|
|
236
|
+
body: JSON.stringify(createAgentProtocolEnvelope("request", input)),
|
|
237
|
+
}, context);
|
|
238
|
+
},
|
|
239
|
+
createThread(input, context) {
|
|
240
|
+
return request("/threads", parseAgentThread, {
|
|
241
|
+
method: "POST",
|
|
242
|
+
body: JSON.stringify(createAgentProtocolEnvelope("request", input ?? {})),
|
|
243
|
+
}, context);
|
|
244
|
+
},
|
|
245
|
+
listThreads(input, context) {
|
|
246
|
+
const query = new URLSearchParams();
|
|
247
|
+
if (input?.limit !== undefined)
|
|
248
|
+
query.set("limit", String(input.limit));
|
|
249
|
+
if (input?.cursor)
|
|
250
|
+
query.set("cursor", input.cursor);
|
|
251
|
+
return request(`/threads?${query}`, parseListThreadsResult, {}, context);
|
|
252
|
+
},
|
|
253
|
+
getThread(input, context) {
|
|
254
|
+
return request(`/threads/${encodeURIComponent(input.threadId)}`, parseNullableAgentThread, {}, context);
|
|
255
|
+
},
|
|
256
|
+
getThreadSnapshot(input, context) {
|
|
257
|
+
return request(`/threads/${encodeURIComponent(input.threadId)}/snapshot`, parseNullableAgentThreadSnapshot, {}, context);
|
|
258
|
+
},
|
|
259
|
+
updateThread(input, context) {
|
|
260
|
+
return request(`/threads/${encodeURIComponent(input.threadId)}`, parseAgentThread, {
|
|
261
|
+
method: "PATCH",
|
|
262
|
+
body: JSON.stringify(createAgentProtocolEnvelope("request", input)),
|
|
263
|
+
}, context);
|
|
264
|
+
},
|
|
265
|
+
forkThread(input, context) {
|
|
266
|
+
return request(`/threads/${encodeURIComponent(input.threadId)}/fork`, parseAgentThread, {
|
|
267
|
+
method: "POST",
|
|
268
|
+
body: JSON.stringify(createAgentProtocolEnvelope("request", input)),
|
|
269
|
+
}, context);
|
|
270
|
+
},
|
|
271
|
+
async deleteThread(input, context) {
|
|
272
|
+
await request(`/threads/${encodeURIComponent(input.threadId)}`, parseVoidResponse, { method: "DELETE" }, context);
|
|
273
|
+
},
|
|
274
|
+
listQueuedMessages(input, context) {
|
|
275
|
+
return request(`/threads/${encodeURIComponent(input.threadId)}/queue`, parseAgentQueuedMessages, {}, context);
|
|
276
|
+
},
|
|
277
|
+
queueMessage(input, context) {
|
|
278
|
+
return request(`/threads/${encodeURIComponent(input.threadId)}/queue`, parseQueueMessageResult, {
|
|
279
|
+
method: "POST",
|
|
280
|
+
body: JSON.stringify(createAgentProtocolEnvelope("request", input)),
|
|
281
|
+
}, context);
|
|
282
|
+
},
|
|
283
|
+
async steerQueuedMessage(input, context) {
|
|
284
|
+
return request(`/threads/${encodeURIComponent(input.threadId)}/queue/${encodeURIComponent(input.messageId)}/steer`, parseSteerQueuedMessageResult, {
|
|
285
|
+
method: "POST",
|
|
286
|
+
body: JSON.stringify(createAgentProtocolEnvelope("request", input)),
|
|
287
|
+
}, context);
|
|
288
|
+
},
|
|
289
|
+
async removeQueuedMessage(input, context) {
|
|
290
|
+
await request(`/threads/${encodeURIComponent(input.threadId)}/queue/${encodeURIComponent(input.messageId)}`, parseVoidResponse, { method: "DELETE" }, context);
|
|
291
|
+
},
|
|
292
|
+
async startRun(input, context) {
|
|
293
|
+
return request("/runs", parseStartRunResult, {
|
|
294
|
+
method: "POST",
|
|
295
|
+
body: JSON.stringify(createAgentProtocolEnvelope("request", input)),
|
|
296
|
+
}, context);
|
|
297
|
+
},
|
|
298
|
+
async *subscribeToRun(input) {
|
|
299
|
+
parseSubscribeToRunInput(input);
|
|
300
|
+
const afterSequence = input.afterSequence ?? 0;
|
|
301
|
+
const query = new URLSearchParams({
|
|
302
|
+
threadId: input.threadId,
|
|
303
|
+
afterSequence: String(afterSequence),
|
|
304
|
+
});
|
|
305
|
+
const headers = new Headers(typeof options.headers === "function"
|
|
306
|
+
? await options.headers()
|
|
307
|
+
: options.headers);
|
|
308
|
+
const correlationId = createCorrelationId();
|
|
309
|
+
headers.set("x-agentkit-correlation-id", correlationId);
|
|
310
|
+
headers.set("accept", "text/event-stream");
|
|
311
|
+
const combinedSignal = combineAbortSignals(options.signal, input.signal);
|
|
312
|
+
try {
|
|
313
|
+
const response = await fetcher(`${baseUrl}/runs/${encodeURIComponent(input.runId)}/events?${query}`, { headers, signal: combinedSignal.signal });
|
|
314
|
+
yield* parseEventStream(response, correlationId, afterSequence, {
|
|
315
|
+
threadId: input.threadId,
|
|
316
|
+
runId: input.runId,
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
finally {
|
|
320
|
+
combinedSignal.release();
|
|
321
|
+
}
|
|
322
|
+
},
|
|
323
|
+
async cancelRun(input, context) {
|
|
324
|
+
await request(`/runs/${encodeURIComponent(input.runId)}/cancel`, parseVoidResponse, {
|
|
325
|
+
method: "POST",
|
|
326
|
+
body: JSON.stringify(createAgentProtocolEnvelope("request", input)),
|
|
327
|
+
}, context);
|
|
328
|
+
},
|
|
329
|
+
getRun(input, context) {
|
|
330
|
+
const query = new URLSearchParams({ threadId: input.threadId });
|
|
331
|
+
return request(`/runs/${encodeURIComponent(input.runId)}?${query}`, parseNullableAgentRunSnapshot, {}, context);
|
|
332
|
+
},
|
|
333
|
+
resumeRun(input, context) {
|
|
334
|
+
return request(`/runs/${encodeURIComponent(input.runId)}/resume`, parseStartRunResult, {
|
|
335
|
+
method: "POST",
|
|
336
|
+
body: JSON.stringify(createAgentProtocolEnvelope("request", input)),
|
|
337
|
+
}, context);
|
|
338
|
+
},
|
|
339
|
+
async resolveApproval(input, context) {
|
|
340
|
+
await request(`/runs/${encodeURIComponent(input.runId)}/approvals/${encodeURIComponent(input.approvalId)}`, parseVoidResponse, {
|
|
341
|
+
method: "POST",
|
|
342
|
+
body: JSON.stringify(createAgentProtocolEnvelope("request", input)),
|
|
343
|
+
}, context);
|
|
344
|
+
},
|
|
345
|
+
async resolveConnectionRequest(input, context) {
|
|
346
|
+
await request(`/runs/${encodeURIComponent(input.runId)}/connections/${encodeURIComponent(input.requestId)}`, parseVoidResponse, {
|
|
347
|
+
method: "POST",
|
|
348
|
+
body: JSON.stringify(createAgentProtocolEnvelope("request", input)),
|
|
349
|
+
}, context);
|
|
350
|
+
},
|
|
351
|
+
invokeAction(input, context) {
|
|
352
|
+
return request("/actions", parseAgentActionResult, {
|
|
353
|
+
method: "POST",
|
|
354
|
+
body: JSON.stringify(createAgentProtocolEnvelope("request", input)),
|
|
355
|
+
}, context);
|
|
356
|
+
},
|
|
357
|
+
createUpload(input, context) {
|
|
358
|
+
return request("/uploads", parseAgentUploadTarget, {
|
|
359
|
+
method: "POST",
|
|
360
|
+
body: JSON.stringify(createAgentProtocolEnvelope("request", input)),
|
|
361
|
+
}, context);
|
|
362
|
+
},
|
|
363
|
+
completeUpload(input, context) {
|
|
364
|
+
return request(`/uploads/${encodeURIComponent(input.uploadId)}/complete`, parseFilePart, {
|
|
365
|
+
method: "POST",
|
|
366
|
+
body: JSON.stringify(createAgentProtocolEnvelope("request", input)),
|
|
367
|
+
}, context);
|
|
368
|
+
},
|
|
369
|
+
async cancelUpload(input, context) {
|
|
370
|
+
await request(`/uploads/${encodeURIComponent(input.uploadId)}/cancel`, parseVoidResponse, {
|
|
371
|
+
method: "POST",
|
|
372
|
+
body: JSON.stringify(createAgentProtocolEnvelope("request", input)),
|
|
373
|
+
}, context);
|
|
374
|
+
},
|
|
375
|
+
async submitFeedback(input, context) {
|
|
376
|
+
await request("/feedback", parseVoidResponse, {
|
|
377
|
+
method: "POST",
|
|
378
|
+
body: JSON.stringify(createAgentProtocolEnvelope("request", input)),
|
|
379
|
+
}, context);
|
|
380
|
+
},
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
function json(payload, status = 200, correlationId) {
|
|
384
|
+
return Response.json(createAgentProtocolEnvelope("response", payload ?? null, correlationId), {
|
|
385
|
+
status,
|
|
386
|
+
headers: correlationId
|
|
387
|
+
? { "x-agentkit-correlation-id": correlationId }
|
|
388
|
+
: undefined,
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
function unsupported(operation, message, correlationId) {
|
|
392
|
+
return Response.json(createAgentProtocolEnvelope("error", createOperationUnsupportedError(operation, {
|
|
393
|
+
message,
|
|
394
|
+
correlationId,
|
|
395
|
+
}), correlationId), {
|
|
396
|
+
status: 501,
|
|
397
|
+
headers: correlationId
|
|
398
|
+
? { "x-agentkit-correlation-id": correlationId }
|
|
399
|
+
: undefined,
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
function capabilityDescriptors(capabilities) {
|
|
403
|
+
const descriptors = [];
|
|
404
|
+
for (const [id, value] of Object.entries(capabilities)) {
|
|
405
|
+
if (id === "protocolVersion")
|
|
406
|
+
continue;
|
|
407
|
+
if (value !== true && value !== false && id !== "reasoning")
|
|
408
|
+
continue;
|
|
409
|
+
const unsupported = value === false || (id === "reasoning" && value === "none");
|
|
410
|
+
descriptors.push(unsupported
|
|
411
|
+
? {
|
|
412
|
+
id: id,
|
|
413
|
+
state: "unsupported",
|
|
414
|
+
error: createCapabilityUnsupportedError(id),
|
|
415
|
+
}
|
|
416
|
+
: {
|
|
417
|
+
id: id,
|
|
418
|
+
state: "available",
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
return descriptors;
|
|
422
|
+
}
|
|
423
|
+
async function discoverCapabilities(transport, input, correlationId, context) {
|
|
424
|
+
if (transport.discoverCapabilities) {
|
|
425
|
+
return transport.discoverCapabilities(input, context);
|
|
426
|
+
}
|
|
427
|
+
const protocol = negotiateAgentKitProtocolVersion(input.protocol, {
|
|
428
|
+
correlationId,
|
|
429
|
+
});
|
|
430
|
+
const legacy = transport.capabilities ?? {};
|
|
431
|
+
return {
|
|
432
|
+
protocol,
|
|
433
|
+
capabilities: capabilityDescriptors(legacy),
|
|
434
|
+
discoveredAt: new Date().toISOString(),
|
|
435
|
+
legacy,
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
async function requestPayload(request, parse, expectedCorrelationId) {
|
|
439
|
+
const envelope = parseAgentProtocolEnvelope(await request.json());
|
|
440
|
+
if (envelope.kind !== "request") {
|
|
441
|
+
throw new AgentProtocolValidationError("envelope.kind", "expected a request envelope");
|
|
442
|
+
}
|
|
443
|
+
if (expectedCorrelationId &&
|
|
444
|
+
envelope.correlationId &&
|
|
445
|
+
expectedCorrelationId !== envelope.correlationId) {
|
|
446
|
+
throw new AgentProtocolValidationError("envelope.correlationId", "must match the request correlation header");
|
|
447
|
+
}
|
|
448
|
+
return {
|
|
449
|
+
value: parse(envelope.payload),
|
|
450
|
+
correlationId: envelope.correlationId ?? expectedCorrelationId,
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
function eventStream(events, abortController, correlationId, subscription, onClose) {
|
|
454
|
+
const encoder = new TextEncoder();
|
|
455
|
+
const iterator = events[Symbol.asyncIterator]();
|
|
456
|
+
const signal = abortController.signal;
|
|
457
|
+
let lastSequence = subscription?.afterSequence ?? 0;
|
|
458
|
+
let readerCancelled = false;
|
|
459
|
+
let finalization;
|
|
460
|
+
const finalize = () => {
|
|
461
|
+
finalization ??= (async () => {
|
|
462
|
+
signal.removeEventListener("abort", stopIterator);
|
|
463
|
+
try {
|
|
464
|
+
await iterator.return?.();
|
|
465
|
+
}
|
|
466
|
+
finally {
|
|
467
|
+
await onClose?.();
|
|
468
|
+
}
|
|
469
|
+
})();
|
|
470
|
+
return finalization;
|
|
471
|
+
};
|
|
472
|
+
const stopIterator = () => {
|
|
473
|
+
void finalize();
|
|
474
|
+
};
|
|
475
|
+
signal.addEventListener("abort", stopIterator, { once: true });
|
|
476
|
+
return new Response(new ReadableStream({
|
|
477
|
+
async pull(controller) {
|
|
478
|
+
try {
|
|
479
|
+
const next = await iterator.next();
|
|
480
|
+
if (next.done) {
|
|
481
|
+
if (!readerCancelled)
|
|
482
|
+
controller.close();
|
|
483
|
+
await finalize();
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
const event = parseAgentEvent(next.value);
|
|
487
|
+
if (subscription && event.threadId !== subscription.threadId) {
|
|
488
|
+
throw new AgentProtocolValidationError("event.threadId", "must match the subscribed thread");
|
|
489
|
+
}
|
|
490
|
+
if (subscription && event.runId !== subscription.runId) {
|
|
491
|
+
throw new AgentProtocolValidationError("event.runId", "must match the subscribed run");
|
|
492
|
+
}
|
|
493
|
+
if (event.sequence !== lastSequence + 1) {
|
|
494
|
+
throw new AgentProtocolValidationError("event.sequence", `must be contiguous after sequence ${lastSequence}`);
|
|
495
|
+
}
|
|
496
|
+
lastSequence = event.sequence;
|
|
497
|
+
controller.enqueue(encoder.encode(`id: ${event.sequence}\ndata: ${JSON.stringify(encodeAgentEvent(event))}\n\n`));
|
|
498
|
+
}
|
|
499
|
+
catch (error) {
|
|
500
|
+
if (!signal.aborted)
|
|
501
|
+
controller.error(error);
|
|
502
|
+
else if (!readerCancelled)
|
|
503
|
+
controller.close();
|
|
504
|
+
await finalize();
|
|
505
|
+
}
|
|
506
|
+
finally {
|
|
507
|
+
if (signal.aborted)
|
|
508
|
+
await finalize();
|
|
509
|
+
}
|
|
510
|
+
},
|
|
511
|
+
async cancel() {
|
|
512
|
+
readerCancelled = true;
|
|
513
|
+
abortController.abort();
|
|
514
|
+
await finalize();
|
|
515
|
+
},
|
|
516
|
+
}), {
|
|
517
|
+
headers: {
|
|
518
|
+
"cache-control": "no-cache, no-transform",
|
|
519
|
+
"content-type": "text/event-stream",
|
|
520
|
+
[AGENTKIT_PROFILE_HEADER]: AGENTKIT_PROFILE_ID,
|
|
521
|
+
...(correlationId
|
|
522
|
+
? { "x-agentkit-correlation-id": correlationId }
|
|
523
|
+
: {}),
|
|
524
|
+
},
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
/** Framework-neutral Fetch handler for serverless, Node, and edge runtimes. */
|
|
528
|
+
export function createAgentKitHttpHandler(options) {
|
|
529
|
+
const basePath = trimSlash(options.basePath ?? "/agentkit");
|
|
530
|
+
return async (request) => {
|
|
531
|
+
let correlationId = request.headers.get("x-agentkit-correlation-id") ?? undefined;
|
|
532
|
+
let requestTransport;
|
|
533
|
+
let createdTransport;
|
|
534
|
+
let transportDisposal;
|
|
535
|
+
let streamOwnsTransportLifecycle = false;
|
|
536
|
+
const reportError = async (error) => {
|
|
537
|
+
try {
|
|
538
|
+
await options.onError?.(error, request);
|
|
539
|
+
}
|
|
540
|
+
catch (observerError) {
|
|
541
|
+
// Error observers must not prevent a standards-compliant response to
|
|
542
|
+
// the original failed request.
|
|
543
|
+
console.error("AgentKit error observer failed.", observerError);
|
|
544
|
+
}
|
|
545
|
+
};
|
|
546
|
+
const disposeRequestTransport = () => {
|
|
547
|
+
if (options.transport ||
|
|
548
|
+
options.transportOwnership === "borrowed" ||
|
|
549
|
+
!createdTransport?.dispose) {
|
|
550
|
+
return Promise.resolve();
|
|
551
|
+
}
|
|
552
|
+
transportDisposal ??= Promise.resolve().then(() => createdTransport?.dispose?.());
|
|
553
|
+
return transportDisposal;
|
|
554
|
+
};
|
|
555
|
+
const releaseRequestTransport = async () => {
|
|
556
|
+
try {
|
|
557
|
+
await disposeRequestTransport();
|
|
558
|
+
}
|
|
559
|
+
catch (error) {
|
|
560
|
+
await reportError(error);
|
|
561
|
+
}
|
|
562
|
+
};
|
|
563
|
+
const getTransport = () => {
|
|
564
|
+
if (options.transport)
|
|
565
|
+
return Promise.resolve(options.transport);
|
|
566
|
+
requestTransport ??= (async () => {
|
|
567
|
+
assertRequestActive(request.signal, correlationId);
|
|
568
|
+
const trusted = await options.resolveRequestContext(request);
|
|
569
|
+
assertRequestActive(request.signal, correlationId);
|
|
570
|
+
const context = {
|
|
571
|
+
trusted,
|
|
572
|
+
signal: request.signal,
|
|
573
|
+
get correlationId() {
|
|
574
|
+
return correlationId;
|
|
575
|
+
},
|
|
576
|
+
};
|
|
577
|
+
const transport = await options.createTransport(context);
|
|
578
|
+
createdTransport = transport;
|
|
579
|
+
assertRequestActive(request.signal, correlationId);
|
|
580
|
+
return transport;
|
|
581
|
+
})();
|
|
582
|
+
return requestTransport;
|
|
583
|
+
};
|
|
584
|
+
const respond = (payload, status = 200) => json(payload, status, correlationId);
|
|
585
|
+
const notSupported = (operation, message) => unsupported(operation, message, correlationId);
|
|
586
|
+
const readPayload = async (parse) => {
|
|
587
|
+
const parsed = await requestPayload(request, parse, correlationId);
|
|
588
|
+
correlationId = parsed.correlationId;
|
|
589
|
+
return parsed.value;
|
|
590
|
+
};
|
|
591
|
+
const invoke = async (operation) => {
|
|
592
|
+
const context = {
|
|
593
|
+
signal: request.signal,
|
|
594
|
+
...(correlationId ? { correlationId } : {}),
|
|
595
|
+
};
|
|
596
|
+
assertRequestActive(context.signal, context.correlationId);
|
|
597
|
+
try {
|
|
598
|
+
const result = await operation(context);
|
|
599
|
+
assertRequestActive(context.signal, context.correlationId);
|
|
600
|
+
return result;
|
|
601
|
+
}
|
|
602
|
+
catch (error) {
|
|
603
|
+
if (context.signal?.aborted || isAbortFailure(error)) {
|
|
604
|
+
throw requestAborted(context.correlationId);
|
|
605
|
+
}
|
|
606
|
+
throw error;
|
|
607
|
+
}
|
|
608
|
+
};
|
|
609
|
+
try {
|
|
610
|
+
assertRequestActive(request.signal, correlationId);
|
|
611
|
+
const url = new URL(request.url);
|
|
612
|
+
const path = url.pathname.startsWith(basePath)
|
|
613
|
+
? url.pathname.slice(basePath.length) || "/"
|
|
614
|
+
: url.pathname;
|
|
615
|
+
const transport = await getTransport();
|
|
616
|
+
if (request.method === "GET" && path === "/capabilities") {
|
|
617
|
+
// Serve the boolean map from discovery so a transport that only
|
|
618
|
+
// implements discoverCapabilities does not report every capability as
|
|
619
|
+
// unknown here.
|
|
620
|
+
return respond(await invoke(async (context) => {
|
|
621
|
+
if (!transport.discoverCapabilities) {
|
|
622
|
+
return transport.capabilities ?? {};
|
|
623
|
+
}
|
|
624
|
+
const discovery = await discoverCapabilities(transport, { protocol: createAgentKitProtocolVersionOffer() }, correlationId, context);
|
|
625
|
+
return discovery.legacy ?? projectAgentCapabilities(discovery);
|
|
626
|
+
}));
|
|
627
|
+
}
|
|
628
|
+
if (request.method === "POST" && path === "/capabilities/discover") {
|
|
629
|
+
const input = await readPayload(parseDiscoverCapabilitiesInput);
|
|
630
|
+
return respond(await invoke((context) => discoverCapabilities(transport, input, correlationId, context)));
|
|
631
|
+
}
|
|
632
|
+
if (request.method === "POST" && path === "/runs") {
|
|
633
|
+
const input = await readPayload(parseStartRunInput);
|
|
634
|
+
return respond(await invoke((context) => transport.startRun(input, context)), 201);
|
|
635
|
+
}
|
|
636
|
+
if (path === "/threads" && request.method === "POST") {
|
|
637
|
+
const createThread = transport.createThread;
|
|
638
|
+
if (!createThread)
|
|
639
|
+
return notSupported("createThread", "Thread creation is not supported.");
|
|
640
|
+
const input = await readPayload(parseCreateThreadInput);
|
|
641
|
+
return respond(await invoke((context) => createThread(input, context)), 201);
|
|
642
|
+
}
|
|
643
|
+
if (path === "/threads" && request.method === "GET") {
|
|
644
|
+
const listThreads = transport.listThreads;
|
|
645
|
+
if (!listThreads)
|
|
646
|
+
return notSupported("listThreads", "Thread listing is not supported.");
|
|
647
|
+
const input = parseListThreadsInput({
|
|
648
|
+
limit: url.searchParams.has("limit")
|
|
649
|
+
? Number(url.searchParams.get("limit"))
|
|
650
|
+
: undefined,
|
|
651
|
+
cursor: url.searchParams.get("cursor") ?? undefined,
|
|
652
|
+
});
|
|
653
|
+
return respond(await invoke((context) => listThreads(input, context)));
|
|
654
|
+
}
|
|
655
|
+
const snapshotMatch = path.match(/^\/threads\/([^/]+)\/snapshot$/);
|
|
656
|
+
if (snapshotMatch?.[1] && request.method === "GET") {
|
|
657
|
+
const getThreadSnapshot = transport.getThreadSnapshot;
|
|
658
|
+
if (!getThreadSnapshot)
|
|
659
|
+
return notSupported("getThreadSnapshot", "Thread snapshots are not supported.");
|
|
660
|
+
return respond(await invoke((context) => getThreadSnapshot({ threadId: decodeURIComponent(snapshotMatch[1]) }, context)));
|
|
661
|
+
}
|
|
662
|
+
const forkMatch = path.match(/^\/threads\/([^/]+)\/fork$/);
|
|
663
|
+
if (forkMatch?.[1] && request.method === "POST") {
|
|
664
|
+
const forkThread = transport.forkThread;
|
|
665
|
+
if (!forkThread)
|
|
666
|
+
return notSupported("forkThread", "Thread forking is not supported.");
|
|
667
|
+
const input = await readPayload(parseForkThreadInput);
|
|
668
|
+
assertRouteIdentifier(decodeURIComponent(forkMatch[1]), input.threadId, "threadId");
|
|
669
|
+
return respond(await invoke((context) => forkThread(input, context)), 201);
|
|
670
|
+
}
|
|
671
|
+
const queueItemMatch = path.match(/^\/threads\/([^/]+)\/queue\/([^/]+)(?:\/(steer))?$/);
|
|
672
|
+
if (queueItemMatch?.[1] && queueItemMatch[2]) {
|
|
673
|
+
const routeInput = {
|
|
674
|
+
threadId: decodeURIComponent(queueItemMatch[1]),
|
|
675
|
+
messageId: decodeURIComponent(queueItemMatch[2]),
|
|
676
|
+
};
|
|
677
|
+
if (request.method === "POST" && queueItemMatch[3] === "steer") {
|
|
678
|
+
const steerQueuedMessage = transport.steerQueuedMessage;
|
|
679
|
+
if (!steerQueuedMessage)
|
|
680
|
+
return notSupported("steerQueuedMessage", "Queue steering is not supported.");
|
|
681
|
+
const input = await readPayload(parseThreadMessageInput);
|
|
682
|
+
assertRouteIdentifier(routeInput.threadId, input.threadId, "threadId");
|
|
683
|
+
assertRouteIdentifier(routeInput.messageId, input.messageId, "messageId");
|
|
684
|
+
return respond(await invoke((context) => steerQueuedMessage(input, context)));
|
|
685
|
+
}
|
|
686
|
+
if (request.method === "DELETE" && !queueItemMatch[3]) {
|
|
687
|
+
const removeQueuedMessage = transport.removeQueuedMessage;
|
|
688
|
+
if (!removeQueuedMessage)
|
|
689
|
+
return notSupported("removeQueuedMessage", "Queue removal is not supported.");
|
|
690
|
+
const input = parseThreadMessageInput(routeInput);
|
|
691
|
+
await invoke((context) => removeQueuedMessage(input, context));
|
|
692
|
+
return respond(undefined);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
const queueMatch = path.match(/^\/threads\/([^/]+)\/queue$/);
|
|
696
|
+
if (queueMatch?.[1]) {
|
|
697
|
+
const threadId = decodeURIComponent(queueMatch[1]);
|
|
698
|
+
if (request.method === "GET") {
|
|
699
|
+
const listQueuedMessages = transport.listQueuedMessages;
|
|
700
|
+
if (!listQueuedMessages)
|
|
701
|
+
return notSupported("listQueuedMessages", "Queued message listing is not supported.");
|
|
702
|
+
return respond(await invoke((context) => listQueuedMessages({ threadId }, context)));
|
|
703
|
+
}
|
|
704
|
+
if (request.method === "POST") {
|
|
705
|
+
const queueMessage = transport.queueMessage;
|
|
706
|
+
if (!queueMessage)
|
|
707
|
+
return notSupported("queueMessage", "Message queues are not supported.");
|
|
708
|
+
const input = await readPayload(parseQueueMessageInput);
|
|
709
|
+
assertRouteIdentifier(threadId, input.threadId, "threadId");
|
|
710
|
+
return respond(await invoke((context) => queueMessage(input, context)), 201);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
const threadMatch = path.match(/^\/threads\/([^/]+)$/);
|
|
714
|
+
if (threadMatch?.[1]) {
|
|
715
|
+
const threadId = decodeURIComponent(threadMatch[1]);
|
|
716
|
+
if (request.method === "GET") {
|
|
717
|
+
const getThread = transport.getThread;
|
|
718
|
+
if (!getThread)
|
|
719
|
+
return notSupported("getThread", "Thread reads are not supported.");
|
|
720
|
+
return respond(await invoke((context) => getThread({ threadId }, context)));
|
|
721
|
+
}
|
|
722
|
+
if (request.method === "PATCH") {
|
|
723
|
+
const updateThread = transport.updateThread;
|
|
724
|
+
if (!updateThread)
|
|
725
|
+
return notSupported("updateThread", "Thread updates are not supported.");
|
|
726
|
+
const input = await readPayload(parseUpdateThreadInput);
|
|
727
|
+
assertRouteIdentifier(threadId, input.threadId, "threadId");
|
|
728
|
+
return respond(await invoke((context) => updateThread(input, context)));
|
|
729
|
+
}
|
|
730
|
+
if (request.method === "DELETE") {
|
|
731
|
+
const deleteThread = transport.deleteThread;
|
|
732
|
+
if (!deleteThread)
|
|
733
|
+
return notSupported("deleteThread", "Thread deletion is not supported.");
|
|
734
|
+
await invoke((context) => deleteThread({ threadId }, context));
|
|
735
|
+
return respond(undefined);
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
const eventsMatch = path.match(/^\/runs\/([^/]+)\/events$/);
|
|
739
|
+
if (request.method === "GET" && eventsMatch?.[1]) {
|
|
740
|
+
const threadId = url.searchParams.get("threadId");
|
|
741
|
+
if (!threadId) {
|
|
742
|
+
throw new AgentProtocolValidationError("threadId", "is required");
|
|
743
|
+
}
|
|
744
|
+
const afterSequenceValue = url.searchParams.get("afterSequence") ?? "0";
|
|
745
|
+
if (!/^\d+$/.test(afterSequenceValue) ||
|
|
746
|
+
!Number.isSafeInteger(Number(afterSequenceValue))) {
|
|
747
|
+
throw new AgentProtocolValidationError("afterSequence", "expected a non-negative safe integer");
|
|
748
|
+
}
|
|
749
|
+
const runId = decodeURIComponent(eventsMatch[1]);
|
|
750
|
+
const input = parseSubscribeToRunInput({
|
|
751
|
+
runId,
|
|
752
|
+
threadId,
|
|
753
|
+
afterSequence: Number(afterSequenceValue),
|
|
754
|
+
});
|
|
755
|
+
const abortController = new AbortController();
|
|
756
|
+
const abortFromRequest = () => abortController.abort(request.signal.reason);
|
|
757
|
+
if (request.signal.aborted)
|
|
758
|
+
abortFromRequest();
|
|
759
|
+
else
|
|
760
|
+
request.signal.addEventListener("abort", abortFromRequest, {
|
|
761
|
+
once: true,
|
|
762
|
+
});
|
|
763
|
+
const response = eventStream(transport.subscribeToRun({
|
|
764
|
+
...input,
|
|
765
|
+
signal: abortController.signal,
|
|
766
|
+
}), abortController, correlationId, {
|
|
767
|
+
threadId: input.threadId,
|
|
768
|
+
runId: input.runId,
|
|
769
|
+
afterSequence: input.afterSequence ?? 0,
|
|
770
|
+
}, async () => {
|
|
771
|
+
request.signal.removeEventListener("abort", abortFromRequest);
|
|
772
|
+
await releaseRequestTransport();
|
|
773
|
+
});
|
|
774
|
+
streamOwnsTransportLifecycle = true;
|
|
775
|
+
return response;
|
|
776
|
+
}
|
|
777
|
+
const runMatch = path.match(/^\/runs\/([^/]+)$/);
|
|
778
|
+
if (request.method === "GET" && runMatch?.[1]) {
|
|
779
|
+
const getRun = transport.getRun;
|
|
780
|
+
if (!getRun)
|
|
781
|
+
return notSupported("getRun", "Run reads are not supported.");
|
|
782
|
+
const threadId = url.searchParams.get("threadId");
|
|
783
|
+
if (!threadId) {
|
|
784
|
+
throw new AgentProtocolValidationError("threadId", "is required");
|
|
785
|
+
}
|
|
786
|
+
const input = parseGetRunInput({
|
|
787
|
+
runId: decodeURIComponent(runMatch[1]),
|
|
788
|
+
threadId,
|
|
789
|
+
});
|
|
790
|
+
return respond(await invoke((context) => getRun(input, context)));
|
|
791
|
+
}
|
|
792
|
+
const cancelMatch = path.match(/^\/runs\/([^/]+)\/cancel$/);
|
|
793
|
+
if (request.method === "POST" && cancelMatch?.[1]) {
|
|
794
|
+
const input = await readPayload(parseCancelRunInput);
|
|
795
|
+
assertRouteIdentifier(decodeURIComponent(cancelMatch[1]), input.runId, "runId");
|
|
796
|
+
await invoke((context) => transport.cancelRun(input, context));
|
|
797
|
+
return respond(undefined);
|
|
798
|
+
}
|
|
799
|
+
const resumeMatch = path.match(/^\/runs\/([^/]+)\/resume$/);
|
|
800
|
+
if (request.method === "POST" && resumeMatch) {
|
|
801
|
+
const resumeRun = transport.resumeRun;
|
|
802
|
+
if (!resumeRun) {
|
|
803
|
+
return notSupported("resumeRun", "Approvals are not supported.");
|
|
804
|
+
}
|
|
805
|
+
const input = await readPayload(parseResumeRunInput);
|
|
806
|
+
assertRouteIdentifier(decodeURIComponent(resumeMatch[1] ?? ""), input.runId, "runId");
|
|
807
|
+
return respond(await invoke((context) => resumeRun(input, context)), 201);
|
|
808
|
+
}
|
|
809
|
+
const approvalMatch = path.match(/^\/runs\/([^/]+)\/approvals\/([^/]+)$/);
|
|
810
|
+
if (request.method === "POST" && approvalMatch) {
|
|
811
|
+
const input = await readPayload(parseResolveApprovalInput);
|
|
812
|
+
assertRouteIdentifier(decodeURIComponent(approvalMatch[1] ?? ""), input.runId, "runId");
|
|
813
|
+
assertRouteIdentifier(decodeURIComponent(approvalMatch[2] ?? ""), input.approvalId, "approvalId");
|
|
814
|
+
if (transport.resolveApproval) {
|
|
815
|
+
await invoke((context) => transport.resolveApproval(input, context));
|
|
816
|
+
return respond(undefined);
|
|
817
|
+
}
|
|
818
|
+
if (!transport.resumeRun) {
|
|
819
|
+
return notSupported("resolveApproval", "Approvals are not supported.");
|
|
820
|
+
}
|
|
821
|
+
await invoke((context) => transport.resumeRun({
|
|
822
|
+
threadId: input.threadId,
|
|
823
|
+
runId: input.runId,
|
|
824
|
+
resume: [resumeEntryFromApproval(input)],
|
|
825
|
+
}, context));
|
|
826
|
+
return respond(undefined);
|
|
827
|
+
}
|
|
828
|
+
const connectionMatch = path.match(/^\/runs\/([^/]+)\/connections\/([^/]+)$/);
|
|
829
|
+
if (request.method === "POST" && connectionMatch) {
|
|
830
|
+
const resolveConnectionRequest = transport.resolveConnectionRequest;
|
|
831
|
+
if (!resolveConnectionRequest) {
|
|
832
|
+
return notSupported("resolveConnectionRequest", "Connection requests are not supported.");
|
|
833
|
+
}
|
|
834
|
+
const input = await readPayload(parseResolveConnectionRequestInput);
|
|
835
|
+
assertRouteIdentifier(decodeURIComponent(connectionMatch[1] ?? ""), input.runId, "runId");
|
|
836
|
+
assertRouteIdentifier(decodeURIComponent(connectionMatch[2] ?? ""), input.requestId, "requestId");
|
|
837
|
+
await invoke((context) => resolveConnectionRequest(input, context));
|
|
838
|
+
return respond(undefined);
|
|
839
|
+
}
|
|
840
|
+
if (request.method === "POST" && path === "/actions") {
|
|
841
|
+
const invokeAction = transport.invokeAction;
|
|
842
|
+
if (!invokeAction) {
|
|
843
|
+
return notSupported("invokeAction", "Actions are not supported.");
|
|
844
|
+
}
|
|
845
|
+
const input = await readPayload(parseInvokeActionInput);
|
|
846
|
+
return respond(await invoke((context) => invokeAction(input, context)));
|
|
847
|
+
}
|
|
848
|
+
if (request.method === "POST" && path === "/uploads") {
|
|
849
|
+
const createUpload = transport.createUpload;
|
|
850
|
+
if (!createUpload)
|
|
851
|
+
return notSupported("createUpload", "Uploads are not supported.");
|
|
852
|
+
const input = await readPayload(parseCreateUploadInput);
|
|
853
|
+
return respond(await invoke((context) => createUpload(input, context)), 201);
|
|
854
|
+
}
|
|
855
|
+
const uploadMatch = path.match(/^\/uploads\/([^/]+)\/(complete|cancel)$/);
|
|
856
|
+
if (request.method === "POST" && uploadMatch?.[1] && uploadMatch[2]) {
|
|
857
|
+
const payload = await readPayload(parseCompleteUploadInput);
|
|
858
|
+
assertRouteIdentifier(decodeURIComponent(uploadMatch[1]), payload.uploadId, "uploadId");
|
|
859
|
+
if (uploadMatch[2] === "complete") {
|
|
860
|
+
const completeUpload = transport.completeUpload;
|
|
861
|
+
if (!completeUpload)
|
|
862
|
+
return notSupported("completeUpload", "Upload completion is not supported.");
|
|
863
|
+
return respond(await invoke((context) => completeUpload(payload, context)));
|
|
864
|
+
}
|
|
865
|
+
const cancelUpload = transport.cancelUpload;
|
|
866
|
+
if (!cancelUpload)
|
|
867
|
+
return notSupported("cancelUpload", "Upload cancellation is not supported.");
|
|
868
|
+
await invoke((context) => cancelUpload(payload, context));
|
|
869
|
+
return respond(undefined);
|
|
870
|
+
}
|
|
871
|
+
if (request.method === "POST" && path === "/feedback") {
|
|
872
|
+
const submitFeedback = transport.submitFeedback;
|
|
873
|
+
if (!submitFeedback)
|
|
874
|
+
return notSupported("submitFeedback", "Feedback is not supported.");
|
|
875
|
+
const input = await readPayload(parseSubmitFeedbackInput);
|
|
876
|
+
await invoke((context) => submitFeedback(input, context));
|
|
877
|
+
return respond(undefined);
|
|
878
|
+
}
|
|
879
|
+
return Response.json(createAgentProtocolEnvelope("error", {
|
|
880
|
+
code: "route_not_found",
|
|
881
|
+
message: "AgentKit route not found.",
|
|
882
|
+
retryable: false,
|
|
883
|
+
correlationId,
|
|
884
|
+
}, correlationId), {
|
|
885
|
+
status: 404,
|
|
886
|
+
headers: correlationId
|
|
887
|
+
? { "x-agentkit-correlation-id": correlationId }
|
|
888
|
+
: undefined,
|
|
889
|
+
});
|
|
890
|
+
}
|
|
891
|
+
catch (error) {
|
|
892
|
+
await reportError(error);
|
|
893
|
+
const expected = error instanceof AgentProtocolValidationError ||
|
|
894
|
+
error instanceof SyntaxError;
|
|
895
|
+
const propagated = error instanceof AgentKitHttpError
|
|
896
|
+
? {
|
|
897
|
+
code: error.code ?? "http_error",
|
|
898
|
+
message: error.message,
|
|
899
|
+
retryable: error.retryable,
|
|
900
|
+
correlationId: error.correlationId,
|
|
901
|
+
details: error.details,
|
|
902
|
+
}
|
|
903
|
+
: isAgentKitProtocolError(error)
|
|
904
|
+
? error.protocolError
|
|
905
|
+
: request.signal.aborted || isAbortFailure(error)
|
|
906
|
+
? createRequestAbortedError({ correlationId })
|
|
907
|
+
: undefined;
|
|
908
|
+
correlationId = propagated?.correlationId ?? correlationId;
|
|
909
|
+
const status = error instanceof AgentKitHttpError
|
|
910
|
+
? error.status
|
|
911
|
+
: propagated?.code === "request_aborted"
|
|
912
|
+
? 499
|
|
913
|
+
: propagated?.code === "operation_unsupported" ||
|
|
914
|
+
propagated?.code === "capability_unsupported"
|
|
915
|
+
? 501
|
|
916
|
+
: propagated?.code === "capability_unavailable"
|
|
917
|
+
? 503
|
|
918
|
+
: propagated?.code === "protocol_version_unsupported"
|
|
919
|
+
? 426
|
|
920
|
+
: expected
|
|
921
|
+
? 400
|
|
922
|
+
: 500;
|
|
923
|
+
const payload = propagated ??
|
|
924
|
+
(expected
|
|
925
|
+
? {
|
|
926
|
+
code: "invalid_request",
|
|
927
|
+
message: error instanceof Error ? error.message : String(error),
|
|
928
|
+
retryable: false,
|
|
929
|
+
correlationId,
|
|
930
|
+
}
|
|
931
|
+
: {
|
|
932
|
+
code: "internal_error",
|
|
933
|
+
message: "The AgentKit request could not be completed.",
|
|
934
|
+
retryable: true,
|
|
935
|
+
correlationId,
|
|
936
|
+
});
|
|
937
|
+
return Response.json(createAgentProtocolEnvelope("error", payload, correlationId), {
|
|
938
|
+
status,
|
|
939
|
+
headers: correlationId
|
|
940
|
+
? { "x-agentkit-correlation-id": correlationId }
|
|
941
|
+
: undefined,
|
|
942
|
+
});
|
|
943
|
+
}
|
|
944
|
+
finally {
|
|
945
|
+
if (!streamOwnsTransportLifecycle)
|
|
946
|
+
await releaseRequestTransport();
|
|
947
|
+
}
|
|
948
|
+
};
|
|
949
|
+
}
|
|
950
|
+
//# sourceMappingURL=http.js.map
|