@agents24/node 0.3.0 → 0.5.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 +27 -3
- package/compatibility.json +6 -6
- package/dist/bff.cjs +481 -52
- package/dist/bff.cjs.map +1 -1
- package/dist/bff.d.cts +24 -4
- package/dist/bff.d.ts +24 -4
- package/dist/bff.js +481 -52
- package/dist/bff.js.map +1 -1
- package/dist/{client-Rmk3dh_D.d.cts → client-xWr_OGEe.d.cts} +437 -76
- package/dist/{client-Rmk3dh_D.d.ts → client-xWr_OGEe.d.ts} +437 -76
- package/dist/index.cjs +302 -57
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +302 -57
- package/dist/index.js.map +1 -1
- package/dist/types/bff-agent-resolution.d.ts +41 -0
- package/dist/types/bff-agent-resolution.d.ts.map +1 -0
- package/dist/types/bff-errors.d.ts +3 -0
- package/dist/types/bff-errors.d.ts.map +1 -0
- package/dist/types/bff.d.ts +6 -19
- package/dist/types/bff.d.ts.map +1 -1
- package/dist/types/client.d.ts +4 -2
- package/dist/types/client.d.ts.map +1 -1
- package/dist/types/embed.d.ts +8 -2
- package/dist/types/embed.d.ts.map +1 -1
- package/dist/types/generated/customerProfiles.d.ts +11 -0
- package/dist/types/generated/customerProfiles.d.ts.map +1 -0
- package/dist/types/generated/manifest.d.ts +1 -1
- package/dist/types/generated/manifest.d.ts.map +1 -1
- package/dist/types/generated/resourceInstallations.d.ts +25 -0
- package/dist/types/generated/resourceInstallations.d.ts.map +1 -0
- package/dist/types/generated/resourcePackages.d.ts +7 -3
- package/dist/types/generated/resourcePackages.d.ts.map +1 -1
- package/dist/types/generated/resourcePolicies.d.ts +3 -3
- package/dist/types/generated/resourcePolicies.d.ts.map +1 -1
- package/dist/types/types.d.ts +389 -61
- package/dist/types/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/dist/types/generated/resourceBundles.d.ts +0 -12
- package/dist/types/generated/resourceBundles.d.ts.map +0 -1
package/dist/bff.js
CHANGED
|
@@ -1,6 +1,45 @@
|
|
|
1
1
|
import { randomUUID } from 'crypto';
|
|
2
2
|
|
|
3
|
-
// src/bff.ts
|
|
3
|
+
// src/bff-agent-resolution.ts
|
|
4
|
+
function clean(value) {
|
|
5
|
+
const result = typeof value === "string" ? value.trim() : "";
|
|
6
|
+
return result || void 0;
|
|
7
|
+
}
|
|
8
|
+
function required(value, field) {
|
|
9
|
+
const result = clean(value);
|
|
10
|
+
if (!result || /[\r\n\0]/.test(result)) throw new Error(`${field} is required.`);
|
|
11
|
+
return result;
|
|
12
|
+
}
|
|
13
|
+
function prepareAgents24BffAgentResolution(options) {
|
|
14
|
+
if (!options.agents.length || options.agents.length > 50) {
|
|
15
|
+
throw new Error("agents must contain between one and 50 Agents.");
|
|
16
|
+
}
|
|
17
|
+
const configured = options.agents.map((item) => {
|
|
18
|
+
const agentId = clean(item.agentId);
|
|
19
|
+
const agentAlias = clean(item.agentAlias);
|
|
20
|
+
if (Boolean(agentId) === Boolean(agentAlias)) {
|
|
21
|
+
throw new Error("Each Agent requires exactly one of agentId or agentAlias.");
|
|
22
|
+
}
|
|
23
|
+
return { agentId, agentAlias, agentVersionId: clean(item.agentVersionId) };
|
|
24
|
+
});
|
|
25
|
+
return async () => {
|
|
26
|
+
const agents = await Promise.all(configured.map(async (item) => {
|
|
27
|
+
if (item.agentAlias) {
|
|
28
|
+
const resolution = await options.client.embed.resolveAgentAlias(item.agentAlias);
|
|
29
|
+
return {
|
|
30
|
+
agentId: required(resolution.agent_id, "agentId"),
|
|
31
|
+
agentAlias: required(item.agentAlias, "agentAlias"),
|
|
32
|
+
agentVersionId: item.agentVersionId
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
return { agentId: required(item.agentId, "agentId"), agentVersionId: item.agentVersionId };
|
|
36
|
+
}));
|
|
37
|
+
if (new Set(agents.map((item) => item.agentId)).size !== agents.length) {
|
|
38
|
+
throw new Error("Configured Agent selectors must resolve to unique Agents.");
|
|
39
|
+
}
|
|
40
|
+
return agents;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
4
43
|
|
|
5
44
|
// src/errors.ts
|
|
6
45
|
var Agents24SDKError = class extends Error {
|
|
@@ -39,29 +78,7 @@ var Agents24SDKError = class extends Error {
|
|
|
39
78
|
}
|
|
40
79
|
};
|
|
41
80
|
|
|
42
|
-
// src/bff.ts
|
|
43
|
-
var JSON_LIMIT_BYTES = 1048576;
|
|
44
|
-
function normalized(value, field) {
|
|
45
|
-
const result = String(value || "").trim();
|
|
46
|
-
if (!result || /[\r\n\0]/.test(result)) throw new Error(`${field} is required.`);
|
|
47
|
-
return result;
|
|
48
|
-
}
|
|
49
|
-
function normalizeBasePath(value) {
|
|
50
|
-
const path = String(value || "/api/agents24").trim().replace(/\/+$/, "");
|
|
51
|
-
if (!path.startsWith("/") || /[\r\n\0?#]/.test(path)) throw new Error("basePath must be an absolute path.");
|
|
52
|
-
return path;
|
|
53
|
-
}
|
|
54
|
-
function browserRequestIsSameOrigin(request) {
|
|
55
|
-
const fetchSite = String(request.headers.get("sec-fetch-site") || "").trim().toLowerCase();
|
|
56
|
-
if (fetchSite) return fetchSite === "same-origin";
|
|
57
|
-
const rawOrigin = String(request.headers.get("origin") || "").trim();
|
|
58
|
-
if (!rawOrigin) return true;
|
|
59
|
-
try {
|
|
60
|
-
return rawOrigin !== "null" && new URL(rawOrigin).origin === new URL(request.url).origin;
|
|
61
|
-
} catch {
|
|
62
|
-
return false;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
81
|
+
// src/bff-errors.ts
|
|
65
82
|
function publicError(status, code, message, retryable = false, violations) {
|
|
66
83
|
const category = status === 401 ? "authentication" : status === 403 ? "authorization" : status === 404 ? "not_found" : status === 409 ? "conflict" : status === 429 ? "rate_limit" : status >= 500 ? "internal" : "validation";
|
|
67
84
|
return Response.json({
|
|
@@ -140,13 +157,79 @@ function errorResponse(error) {
|
|
|
140
157
|
const sdkError = error;
|
|
141
158
|
const status = sdkError.status && sdkError.status >= 400 && sdkError.status <= 599 ? sdkError.status : 502;
|
|
142
159
|
const policyError = safeResourcePolicyError(sdkError.failure);
|
|
143
|
-
if (policyError)
|
|
144
|
-
return resourcePolicyFailureResponse(status, policyError);
|
|
145
|
-
}
|
|
160
|
+
if (policyError) return resourcePolicyFailureResponse(status, policyError);
|
|
146
161
|
return publicError(status, `UPSTREAM_${status}`, "The Agent request failed.", status >= 500);
|
|
147
162
|
}
|
|
148
163
|
return publicError(500, "BFF_REQUEST_FAILED", "The Agent request could not be completed.", true);
|
|
149
164
|
}
|
|
165
|
+
|
|
166
|
+
// src/bff.ts
|
|
167
|
+
var JSON_LIMIT_BYTES = 1048576;
|
|
168
|
+
var MAX_INPUT_LENGTH = 2e5;
|
|
169
|
+
var MAX_ATTACHMENTS = 10;
|
|
170
|
+
var MAX_ATTACHMENT_BYTES = 25 * 1024 * 1024;
|
|
171
|
+
var HITL_ACTIONS = /* @__PURE__ */ new Set(["approve", "reject", "connect", "skip", "respond"]);
|
|
172
|
+
var CUSTOMER_IDENTITY_CACHE_TTL_MS = 6e4;
|
|
173
|
+
var CUSTOMER_IDENTITY_CACHE_MAX_ENTRIES = 1024;
|
|
174
|
+
function createCustomerIdentityResolver(embed, integrationId, integrationName) {
|
|
175
|
+
const cache = /* @__PURE__ */ new Map();
|
|
176
|
+
return async (principal) => {
|
|
177
|
+
const profile = {
|
|
178
|
+
display_name: principal.displayName,
|
|
179
|
+
email: principal.email,
|
|
180
|
+
avatar_url: principal.avatarUrl
|
|
181
|
+
};
|
|
182
|
+
const profileFields = Object.fromEntries(Object.entries(profile).filter(([, value2]) => value2 !== void 0));
|
|
183
|
+
const key = JSON.stringify([integrationId, principal.issuer, principal.subject, profileFields]);
|
|
184
|
+
const now = Date.now();
|
|
185
|
+
const cached = cache.get(key);
|
|
186
|
+
if (cached && cached.expiresAt > now) {
|
|
187
|
+
cache.delete(key);
|
|
188
|
+
cache.set(key, cached);
|
|
189
|
+
return cached.value;
|
|
190
|
+
}
|
|
191
|
+
if (cached) cache.delete(key);
|
|
192
|
+
const value = embed.resolveCustomerPrincipal({
|
|
193
|
+
integration_id: integrationId,
|
|
194
|
+
...integrationName ? { integration_name: integrationName } : {},
|
|
195
|
+
...principal.issuer ? { issuer: principal.issuer } : {},
|
|
196
|
+
subject: principal.subject,
|
|
197
|
+
...Object.keys(profileFields).length ? { profile: profileFields } : {}
|
|
198
|
+
}).then((result) => normalized(result.customer_principal_ref, "customer_principal_ref"));
|
|
199
|
+
const entry = { expiresAt: now + CUSTOMER_IDENTITY_CACHE_TTL_MS, value };
|
|
200
|
+
cache.set(key, entry);
|
|
201
|
+
while (cache.size > CUSTOMER_IDENTITY_CACHE_MAX_ENTRIES) {
|
|
202
|
+
const oldest = cache.keys().next().value;
|
|
203
|
+
if (oldest === void 0) break;
|
|
204
|
+
cache.delete(oldest);
|
|
205
|
+
}
|
|
206
|
+
void value.catch(() => {
|
|
207
|
+
if (cache.get(key) === entry) cache.delete(key);
|
|
208
|
+
});
|
|
209
|
+
return value;
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
function normalized(value, field) {
|
|
213
|
+
const result = String(value || "").trim();
|
|
214
|
+
if (!result || /[\r\n\0]/.test(result)) throw new Error(`${field} is required.`);
|
|
215
|
+
return result;
|
|
216
|
+
}
|
|
217
|
+
function normalizeBasePath(value) {
|
|
218
|
+
const path = String(value || "/api/agents24").trim().replace(/\/+$/, "");
|
|
219
|
+
if (!path.startsWith("/") || /[\r\n\0?#]/.test(path)) throw new Error("basePath must be an absolute path.");
|
|
220
|
+
return path;
|
|
221
|
+
}
|
|
222
|
+
function browserRequestIsSameOrigin(request) {
|
|
223
|
+
const fetchSite = String(request.headers.get("sec-fetch-site") || "").trim().toLowerCase();
|
|
224
|
+
if (fetchSite) return fetchSite === "same-origin";
|
|
225
|
+
const rawOrigin = String(request.headers.get("origin") || "").trim();
|
|
226
|
+
if (!rawOrigin) return true;
|
|
227
|
+
try {
|
|
228
|
+
return rawOrigin !== "null" && new URL(rawOrigin).origin === new URL(request.url).origin;
|
|
229
|
+
} catch {
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
150
233
|
function mergeHeaders(response, additional) {
|
|
151
234
|
if (!additional) return response;
|
|
152
235
|
const headers = new Headers(response.headers);
|
|
@@ -204,7 +287,7 @@ function sseHeaders(headers) {
|
|
|
204
287
|
result.set("X-Accel-Buffering", "no");
|
|
205
288
|
return result;
|
|
206
289
|
}
|
|
207
|
-
function sseResponse(execute, headers) {
|
|
290
|
+
function sseResponse(execute, headers, failurePayload) {
|
|
208
291
|
const stream = new TransformStream();
|
|
209
292
|
const writer = stream.writable.getWriter();
|
|
210
293
|
const encoder = new TextEncoder();
|
|
@@ -212,6 +295,10 @@ function sseResponse(execute, headers) {
|
|
|
212
295
|
|
|
213
296
|
`));
|
|
214
297
|
void execute(write).catch(async (error) => {
|
|
298
|
+
if (failurePayload) {
|
|
299
|
+
await write(failurePayload(error)).catch(() => void 0);
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
215
302
|
const message = "The Agent stream failed.";
|
|
216
303
|
const event = {
|
|
217
304
|
version: "run-stream.v3",
|
|
@@ -235,7 +322,7 @@ function bootstrapDocument(raw, agentVersionId) {
|
|
|
235
322
|
version: String(raw.version || "agents24.embed.bootstrap.v1"),
|
|
236
323
|
deployment: {
|
|
237
324
|
id: "server-embed",
|
|
238
|
-
name: String(raw.agent_name || "
|
|
325
|
+
name: normalized(String(raw.agent_name || ""), "agent_name"),
|
|
239
326
|
version_policy: agentVersionId ? { mode: "pinned", version_id: agentVersionId } : { mode: "latest_published" },
|
|
240
327
|
resolved_agent_version_id: resolvedVersionId,
|
|
241
328
|
resolved_agent_version_number: Number.isFinite(versionNumber) ? versionNumber : 0,
|
|
@@ -247,9 +334,12 @@ function bootstrapDocument(raw, agentVersionId) {
|
|
|
247
334
|
native_profiles: []
|
|
248
335
|
};
|
|
249
336
|
}
|
|
250
|
-
function
|
|
337
|
+
function createAgents24BffHandlerWithResolver(options, resolveCustomerIdentity) {
|
|
251
338
|
const agentId = normalized(options.agentId, "agentId");
|
|
252
339
|
const agentVersionId = stringValue(options.agentVersionId);
|
|
340
|
+
const runtimeTarget = options.runtimeTarget || "published";
|
|
341
|
+
const integrationId = normalized(options.integrationId, "integrationId");
|
|
342
|
+
if (runtimeTarget === "draft" && agentVersionId) throw new Error("agentVersionId cannot be used with draft runtimeTarget.");
|
|
253
343
|
const basePath = normalizeBasePath(options.basePath);
|
|
254
344
|
const embed = options.client.embed;
|
|
255
345
|
return async (request) => {
|
|
@@ -274,42 +364,44 @@ function createAgents24BffHandler(options) {
|
|
|
274
364
|
const principal = resolution.principal;
|
|
275
365
|
const idempotencyKey = stringValue(request.headers.get("idempotency-key"));
|
|
276
366
|
try {
|
|
277
|
-
|
|
278
|
-
if (!(request.method === "GET" && (path === "/bootstrap" || path === "/resource-policy"))) {
|
|
279
|
-
const entitlement = await embed.getResourcePolicy(agentId, {
|
|
280
|
-
external_user_id: principal.subject,
|
|
281
|
-
...principal.issuer ? { external_user_issuer: principal.issuer } : {},
|
|
282
|
-
...agentVersionId ? { agent_version_id: agentVersionId } : {}
|
|
283
|
-
});
|
|
284
|
-
customerPrincipalRef = stringValue(entitlement.customer_principal_ref) || principal.subject;
|
|
285
|
-
}
|
|
367
|
+
const customerPrincipalRef = await resolveCustomerIdentity(principal);
|
|
286
368
|
const identity = {
|
|
287
369
|
external_user_id: customerPrincipalRef,
|
|
288
370
|
...principal.externalSessionId ? { external_session_id: principal.externalSessionId } : {}
|
|
289
371
|
};
|
|
290
372
|
const threadIdentity = {
|
|
291
373
|
externalUserId: customerPrincipalRef,
|
|
374
|
+
runtimeTarget,
|
|
292
375
|
...principal.externalSessionId ? { externalSessionId: principal.externalSessionId } : {}
|
|
293
376
|
};
|
|
294
377
|
let response;
|
|
295
378
|
if (request.method === "GET" && path === "/bootstrap") {
|
|
296
|
-
const raw = await embed.getRuntimeBootstrap(agentId, agentVersionId);
|
|
379
|
+
const raw = await embed.getRuntimeBootstrap(agentId, agentVersionId, runtimeTarget);
|
|
297
380
|
response = json(bootstrapDocument(raw, agentVersionId));
|
|
298
381
|
} else if (request.method === "GET" && path === "/resource-policy") {
|
|
299
382
|
response = json(await embed.getResourcePolicy(agentId, {
|
|
300
|
-
external_user_id:
|
|
301
|
-
|
|
302
|
-
...agentVersionId ? { agent_version_id: agentVersionId } : {}
|
|
383
|
+
external_user_id: customerPrincipalRef,
|
|
384
|
+
integration_id: integrationId,
|
|
385
|
+
...agentVersionId ? { agent_version_id: agentVersionId } : {},
|
|
386
|
+
runtime_target: runtimeTarget
|
|
303
387
|
}));
|
|
304
388
|
} else if (request.method === "POST" && path === "/chat/stream") {
|
|
305
389
|
const body = await requestJson(request);
|
|
306
390
|
const input = stringValue(body.input);
|
|
307
391
|
const attachmentIds = stringList(body.attachmentIds);
|
|
308
392
|
if (!input && attachmentIds.length === 0) throw new BffInputError(400, "INPUT_REQUIRED", "A message or attachment is required.");
|
|
393
|
+
if (input && input.length > MAX_INPUT_LENGTH) {
|
|
394
|
+
throw new BffInputError(413, "INPUT_TOO_LARGE", "The message is too large.");
|
|
395
|
+
}
|
|
396
|
+
if (attachmentIds.length > MAX_ATTACHMENTS) {
|
|
397
|
+
throw new BffInputError(400, "TOO_MANY_ATTACHMENTS", `At most ${MAX_ATTACHMENTS} attachments are allowed.`);
|
|
398
|
+
}
|
|
309
399
|
response = sseResponse(async (write) => {
|
|
310
400
|
await embed.streamAgent(agentId, {
|
|
311
401
|
...identity,
|
|
402
|
+
integration_id: integrationId,
|
|
312
403
|
...agentVersionId ? { agent_version_id: agentVersionId } : {},
|
|
404
|
+
runtime_target: runtimeTarget,
|
|
313
405
|
...input ? { input } : {},
|
|
314
406
|
attachment_ids: attachmentIds,
|
|
315
407
|
...stringValue(body.threadId) ? { thread_id: stringValue(body.threadId) } : {},
|
|
@@ -329,6 +421,8 @@ function createAgents24BffHandler(options) {
|
|
|
329
421
|
response = sseResponse(async (write) => {
|
|
330
422
|
await embed.attachAgentRun(agentId, runId, {
|
|
331
423
|
...identity,
|
|
424
|
+
runtime_target: runtimeTarget,
|
|
425
|
+
integration_id: integrationId,
|
|
332
426
|
...cursor === void 0 ? {} : { cursor: Number(cursor) }
|
|
333
427
|
}, (event) => write(event), {
|
|
334
428
|
idempotencyKey,
|
|
@@ -337,7 +431,10 @@ function createAgents24BffHandler(options) {
|
|
|
337
431
|
});
|
|
338
432
|
} else if (request.method === "POST" && /^\/runs\/[^/]+\/cancel$/.test(path)) {
|
|
339
433
|
const runId = routeId(path, /^\/runs\/([^/]+)\/cancel$/, "runId");
|
|
340
|
-
response = json(await embed.cancelAgentRun(agentId, runId,
|
|
434
|
+
response = json(await embed.cancelAgentRun(agentId, runId, {
|
|
435
|
+
...threadIdentity,
|
|
436
|
+
integrationId
|
|
437
|
+
}, { idempotencyKey }));
|
|
341
438
|
} else if (request.method === "PUT" && /^\/runs\/[^/]+\/feedback$/.test(path)) {
|
|
342
439
|
const runId = routeId(path, /^\/runs\/([^/]+)\/feedback$/, "runId");
|
|
343
440
|
const body = await requestJson(request);
|
|
@@ -356,6 +453,8 @@ function createAgents24BffHandler(options) {
|
|
|
356
453
|
}
|
|
357
454
|
response = json(await embed.setAgentRunFeedback(agentId, runId, {
|
|
358
455
|
...identity,
|
|
456
|
+
runtime_target: runtimeTarget,
|
|
457
|
+
integration_id: integrationId,
|
|
359
458
|
rating,
|
|
360
459
|
...rating === "dislike" && reason ? { reason } : {},
|
|
361
460
|
...rating === "dislike" && comment ? { comment } : {}
|
|
@@ -363,43 +462,71 @@ function createAgents24BffHandler(options) {
|
|
|
363
462
|
} else if (request.method === "GET" && path === "/threads") {
|
|
364
463
|
const skip = Number(url.searchParams.get("skip") || 0);
|
|
365
464
|
const limit = Number(url.searchParams.get("limit") || 20);
|
|
465
|
+
if (!Number.isInteger(skip) || skip < 0 || !Number.isInteger(limit) || limit < 1 || limit > 100) {
|
|
466
|
+
throw new BffInputError(400, "INVALID_PAGINATION", "skip must be non-negative and limit must be between 1 and 100.");
|
|
467
|
+
}
|
|
366
468
|
response = json(await embed.listAgentThreads(agentId, {
|
|
367
469
|
...threadIdentity,
|
|
368
|
-
|
|
369
|
-
|
|
470
|
+
integrationId,
|
|
471
|
+
runtimeTarget,
|
|
472
|
+
skip,
|
|
473
|
+
limit
|
|
370
474
|
}));
|
|
371
475
|
} else if (request.method === "GET" && path === "/threads/events") {
|
|
372
476
|
const rawCursor = url.searchParams.get("cursor");
|
|
373
477
|
const cursor = rawCursor === null ? void 0 : Number(rawCursor);
|
|
478
|
+
if (cursor !== void 0 && (!Number.isInteger(cursor) || cursor < 0)) {
|
|
479
|
+
throw new BffInputError(400, "INVALID_CURSOR", "cursor must be a non-negative integer.");
|
|
480
|
+
}
|
|
374
481
|
response = sseResponse(async (write) => {
|
|
375
482
|
await embed.subscribeAgentThreadEvents(agentId, {
|
|
376
483
|
...threadIdentity,
|
|
377
|
-
|
|
484
|
+
integrationId,
|
|
485
|
+
...cursor !== void 0 ? { cursor } : {},
|
|
378
486
|
signal: request.signal
|
|
379
487
|
}, (event) => write(event));
|
|
380
|
-
})
|
|
488
|
+
}, void 0, () => ({
|
|
489
|
+
schema_version: "agents24.thread_summary_event.v1",
|
|
490
|
+
event: "snapshot_required",
|
|
491
|
+
cursor,
|
|
492
|
+
reason: "stream_failed"
|
|
493
|
+
}));
|
|
381
494
|
} else if (/^\/threads\/[^/]+$/.test(path) && request.method === "GET") {
|
|
382
495
|
const threadId = routeId(path, /^\/threads\/([^/]+)$/, "threadId");
|
|
383
496
|
const rawBefore = url.searchParams.get("before_turn_index");
|
|
384
497
|
const rawLimit = url.searchParams.get("limit");
|
|
385
498
|
const before = rawBefore === null ? void 0 : Number(rawBefore);
|
|
386
499
|
const limit = rawLimit === null ? void 0 : Number(rawLimit);
|
|
500
|
+
if (before !== void 0 && (!Number.isInteger(before) || before < 0)) {
|
|
501
|
+
throw new BffInputError(400, "INVALID_PAGINATION", "before_turn_index is invalid.");
|
|
502
|
+
}
|
|
503
|
+
if (limit !== void 0 && (!Number.isInteger(limit) || limit < 1 || limit > 100)) {
|
|
504
|
+
throw new BffInputError(400, "INVALID_PAGINATION", "limit must be between 1 and 100.");
|
|
505
|
+
}
|
|
387
506
|
response = json(await embed.getAgentThread(agentId, threadId, {
|
|
388
507
|
...threadIdentity,
|
|
389
|
-
|
|
390
|
-
...
|
|
508
|
+
integrationId,
|
|
509
|
+
...before !== void 0 ? { beforeTurnIndex: before } : {},
|
|
510
|
+
...limit !== void 0 ? { limit } : {}
|
|
391
511
|
}));
|
|
392
512
|
} else if (/^\/threads\/[^/]+$/.test(path) && request.method === "DELETE") {
|
|
393
513
|
const threadId = routeId(path, /^\/threads\/([^/]+)$/, "threadId");
|
|
394
|
-
response = json(await embed.deleteAgentThread(agentId, threadId, threadIdentity, { idempotencyKey }));
|
|
514
|
+
response = json(await embed.deleteAgentThread(agentId, threadId, { ...threadIdentity, integrationId }, { idempotencyKey }));
|
|
395
515
|
} else if (request.method === "POST" && path === "/attachments/upload") {
|
|
396
516
|
const form = await request.formData();
|
|
397
517
|
const files = form.getAll("files").filter((entry) => entry instanceof File);
|
|
398
518
|
if (files.length === 0) throw new BffInputError(400, "ATTACHMENT_REQUIRED", "At least one attachment is required.");
|
|
519
|
+
if (files.length > MAX_ATTACHMENTS) {
|
|
520
|
+
throw new BffInputError(400, "TOO_MANY_ATTACHMENTS", `At most ${MAX_ATTACHMENTS} attachments are allowed.`);
|
|
521
|
+
}
|
|
522
|
+
if (files.some((file) => file.size > MAX_ATTACHMENT_BYTES)) {
|
|
523
|
+
throw new BffInputError(413, "ATTACHMENT_TOO_LARGE", "An attachment exceeds the allowed size.");
|
|
524
|
+
}
|
|
399
525
|
response = json(await embed.uploadAgentAttachments(agentId, {
|
|
400
526
|
...threadIdentity,
|
|
401
527
|
files,
|
|
402
528
|
...stringValue(form.get("thread_id")) ? { threadId: stringValue(form.get("thread_id")) } : {},
|
|
529
|
+
integrationId,
|
|
403
530
|
idempotencyKey
|
|
404
531
|
}));
|
|
405
532
|
} else if (request.method === "POST" && /^\/attachments\/[^/]+\/content-access$/.test(path)) {
|
|
@@ -412,6 +539,8 @@ function createAgents24BffHandler(options) {
|
|
|
412
539
|
response = json(await embed.createAttachmentContentAccess(agentId, attachmentId, {
|
|
413
540
|
external_user_id: threadIdentity.externalUserId,
|
|
414
541
|
...threadIdentity.externalSessionId ? { external_session_id: threadIdentity.externalSessionId } : {},
|
|
542
|
+
runtime_target: runtimeTarget,
|
|
543
|
+
integration_id: integrationId,
|
|
415
544
|
disposition
|
|
416
545
|
}));
|
|
417
546
|
} else if (request.method === "POST" && /^\/runs\/[^/]+\/hitl\/[^/]+\/resume$/.test(path)) {
|
|
@@ -419,11 +548,17 @@ function createAgents24BffHandler(options) {
|
|
|
419
548
|
const runId = routeId(path, /^\/runs\/([^/]+)\/hitl\//, "runId");
|
|
420
549
|
const interruptId = match?.[2] ? decodeURIComponent(match[2]) : "";
|
|
421
550
|
const body = await requestJson(request);
|
|
551
|
+
const action = stringValue(body.action);
|
|
552
|
+
if (!action || !HITL_ACTIONS.has(action)) {
|
|
553
|
+
throw new BffInputError(400, "INVALID_HITL_ACTION", "The HITL action is invalid.");
|
|
554
|
+
}
|
|
422
555
|
response = json(await embed.resumeAgentRun(agentId, runId, {
|
|
423
556
|
...identity,
|
|
557
|
+
runtime_target: runtimeTarget,
|
|
558
|
+
integration_id: integrationId,
|
|
424
559
|
schema_version: "agents24.hitl.resume.v2",
|
|
425
560
|
interrupt_id: normalized(interruptId, "interruptId"),
|
|
426
|
-
action
|
|
561
|
+
action,
|
|
427
562
|
...stringValue(body.comment) ? { comment: stringValue(body.comment) } : {},
|
|
428
563
|
...Array.isArray(body.answers) ? { answers: body.answers } : {}
|
|
429
564
|
}, { idempotencyKey }));
|
|
@@ -435,6 +570,8 @@ function createAgents24BffHandler(options) {
|
|
|
435
570
|
const callbackOrigin = new URL(request.url).origin;
|
|
436
571
|
response = json(await embed.startAgentRunMcpAuth(agentId, normalized(runId, "runId"), normalized(serverId, "serverId"), {
|
|
437
572
|
...identity,
|
|
573
|
+
runtime_target: runtimeTarget,
|
|
574
|
+
integration_id: integrationId,
|
|
438
575
|
interrupt_id: normalized(String(body.interruptId || ""), "interruptId"),
|
|
439
576
|
callback_origin: callbackOrigin,
|
|
440
577
|
popup_nonce: normalized(String(body.popupNonce || ""), "popupNonce")
|
|
@@ -456,6 +593,298 @@ function createAgents24BffHandler(options) {
|
|
|
456
593
|
}
|
|
457
594
|
};
|
|
458
595
|
}
|
|
596
|
+
function createResolvedAgents24BffHandler(options) {
|
|
597
|
+
const runtimeTarget = options.runtimeTarget || "published";
|
|
598
|
+
const basePath = normalizeBasePath(options.basePath);
|
|
599
|
+
const integrationId = normalized(options.integrationId, "integrationId");
|
|
600
|
+
const agents = options.agents.map((item) => ({
|
|
601
|
+
agentId: normalized(item.agentId, "agentId"),
|
|
602
|
+
agentAlias: stringValue(item.agentAlias),
|
|
603
|
+
agentVersionId: stringValue(item.agentVersionId)
|
|
604
|
+
}));
|
|
605
|
+
if (!agents.length || agents.length > 50) throw new Error("agents must contain between one and 50 Agents.");
|
|
606
|
+
if (new Set(agents.map((agent) => agent.agentId)).size !== agents.length) throw new Error("agents must contain unique Agent IDs.");
|
|
607
|
+
if (runtimeTarget === "draft" && agents.some((agent) => agent.agentVersionId)) {
|
|
608
|
+
throw new Error("agentVersionId cannot be used with draft runtimeTarget.");
|
|
609
|
+
}
|
|
610
|
+
const resolveCustomerIdentity = createCustomerIdentityResolver(
|
|
611
|
+
options.client.embed,
|
|
612
|
+
integrationId,
|
|
613
|
+
stringValue(options.integrationName)
|
|
614
|
+
);
|
|
615
|
+
const scopedHandlers = new Map(agents.map((agent) => [
|
|
616
|
+
agent.agentId,
|
|
617
|
+
createAgents24BffHandlerWithResolver({
|
|
618
|
+
client: options.client,
|
|
619
|
+
agentId: agent.agentId,
|
|
620
|
+
agentVersionId: agent.agentVersionId,
|
|
621
|
+
runtimeTarget,
|
|
622
|
+
integrationId,
|
|
623
|
+
integrationName: options.integrationName,
|
|
624
|
+
basePath: `${basePath}/agents/${encodeURIComponent(agent.agentId)}`,
|
|
625
|
+
resolvePrincipal: options.resolvePrincipal,
|
|
626
|
+
revokePrincipal: options.revokePrincipal
|
|
627
|
+
}, resolveCustomerIdentity)
|
|
628
|
+
]));
|
|
629
|
+
const agentBySelector = /* @__PURE__ */ new Map();
|
|
630
|
+
for (const agent of agents) {
|
|
631
|
+
agentBySelector.set(agent.agentId, agent);
|
|
632
|
+
if (agent.agentAlias) agentBySelector.set(agent.agentAlias, agent);
|
|
633
|
+
}
|
|
634
|
+
return async (request) => {
|
|
635
|
+
const url = new URL(request.url);
|
|
636
|
+
if (url.pathname !== basePath && !url.pathname.startsWith(`${basePath}/`)) {
|
|
637
|
+
return publicError(404, "NOT_FOUND", "The requested BFF route does not exist.");
|
|
638
|
+
}
|
|
639
|
+
if (!browserRequestIsSameOrigin(request)) {
|
|
640
|
+
return publicError(403, "BFF_ORIGIN_DENIED", "The BFF accepts same-origin browser requests only.");
|
|
641
|
+
}
|
|
642
|
+
const path = url.pathname.slice(basePath.length) || "/";
|
|
643
|
+
let resolution;
|
|
644
|
+
try {
|
|
645
|
+
resolution = await options.resolvePrincipal(request);
|
|
646
|
+
resolution.principal.subject = normalized(resolution.principal.subject, "subject");
|
|
647
|
+
if (resolution.principal.issuer !== void 0) resolution.principal.issuer = normalized(resolution.principal.issuer, "issuer");
|
|
648
|
+
} catch {
|
|
649
|
+
return publicError(401, "PRINCIPAL_REQUIRED", "A valid application session is required.");
|
|
650
|
+
}
|
|
651
|
+
try {
|
|
652
|
+
const externalUserId = await resolveCustomerIdentity(resolution.principal);
|
|
653
|
+
const identity = {
|
|
654
|
+
external_user_id: externalUserId,
|
|
655
|
+
...resolution.principal.externalSessionId ? { external_session_id: resolution.principal.externalSessionId } : {}
|
|
656
|
+
};
|
|
657
|
+
const requestedAgent = (value) => {
|
|
658
|
+
const agentId = stringValue(value) || agents[0].agentId;
|
|
659
|
+
const configured = agentBySelector.get(agentId);
|
|
660
|
+
if (!configured) throw new BffInputError(404, "AGENT_NOT_CONFIGURED", "The requested Agent is not available.");
|
|
661
|
+
return configured;
|
|
662
|
+
};
|
|
663
|
+
const dispatchToAgent = async (agentId) => {
|
|
664
|
+
const handler = scopedHandlers.get(agentId);
|
|
665
|
+
if (!handler) throw new BffInputError(404, "NOT_FOUND", "The requested resource does not exist.");
|
|
666
|
+
const target = new URL(request.url);
|
|
667
|
+
target.pathname = `${basePath}/agents/${encodeURIComponent(agentId)}${path}`;
|
|
668
|
+
const body = request.method === "GET" || request.method === "HEAD" ? void 0 : await request.arrayBuffer();
|
|
669
|
+
return handler(new Request(target, {
|
|
670
|
+
method: request.method,
|
|
671
|
+
headers: request.headers,
|
|
672
|
+
body,
|
|
673
|
+
signal: request.signal
|
|
674
|
+
}));
|
|
675
|
+
};
|
|
676
|
+
const resolveRunAgent = async (runId) => {
|
|
677
|
+
const result = await options.client.embed.resolveConversationRun(runId, {
|
|
678
|
+
agent_ids: agents.map((agent) => agent.agentId),
|
|
679
|
+
...identity,
|
|
680
|
+
integration_id: integrationId,
|
|
681
|
+
runtime_target: runtimeTarget
|
|
682
|
+
});
|
|
683
|
+
return requestedAgent(result.agent_id).agentId;
|
|
684
|
+
};
|
|
685
|
+
if (request.method === "GET" && path === "/bootstrap") {
|
|
686
|
+
const candidates = await Promise.all(agents.map(async (agent) => {
|
|
687
|
+
const [raw, policy] = await Promise.all([
|
|
688
|
+
options.client.embed.getRuntimeBootstrap(agent.agentId, agent.agentVersionId, runtimeTarget),
|
|
689
|
+
options.client.embed.getResourcePolicy(agent.agentId, {
|
|
690
|
+
external_user_id: externalUserId,
|
|
691
|
+
integration_id: integrationId,
|
|
692
|
+
...agent.agentVersionId ? { agent_version_id: agent.agentVersionId } : {},
|
|
693
|
+
runtime_target: runtimeTarget
|
|
694
|
+
})
|
|
695
|
+
]);
|
|
696
|
+
const alias = normalized(String(raw.agent_alias || ""), "agent_alias");
|
|
697
|
+
agent.agentAlias = alias;
|
|
698
|
+
agentBySelector.set(alias, agent);
|
|
699
|
+
for (const item of stringList(raw.agent_aliases)) agentBySelector.set(item, agent);
|
|
700
|
+
return { agent, raw, policy };
|
|
701
|
+
}));
|
|
702
|
+
const selected = requestedAgent(url.searchParams.get("agent_id"));
|
|
703
|
+
const allowed = candidates.filter((candidate) => candidate.policy.agent_access_allowed !== false);
|
|
704
|
+
const selectedCandidate = allowed.find((candidate) => candidate.agent.agentId === selected.agentId) || allowed[0];
|
|
705
|
+
if (!selectedCandidate) throw new BffInputError(403, "RESOURCE_POLICY_ACCESS_DENIED", "No configured Agent is available.");
|
|
706
|
+
const document = bootstrapDocument(selectedCandidate.raw, selectedCandidate.agent.agentVersionId);
|
|
707
|
+
const features = objectValue(document.features);
|
|
708
|
+
features.models = selectedCandidate.policy.model_selection;
|
|
709
|
+
features.agents = {
|
|
710
|
+
selectable: allowed.length > 1,
|
|
711
|
+
default_id: normalized(String(allowed[0].raw.agent_alias || ""), "agent_alias"),
|
|
712
|
+
options: allowed.map((candidate) => ({
|
|
713
|
+
id: normalized(String(candidate.raw.agent_alias || ""), "agent_alias"),
|
|
714
|
+
label: normalized(String(candidate.raw.agent_name || ""), "agent_name")
|
|
715
|
+
}))
|
|
716
|
+
};
|
|
717
|
+
document.features = features;
|
|
718
|
+
document.capabilities = [
|
|
719
|
+
.../* @__PURE__ */ new Set([
|
|
720
|
+
...document.capabilities || [],
|
|
721
|
+
...allowed.length > 1 ? ["agents.select"] : [],
|
|
722
|
+
...selectedCandidate.policy.model_selection.selectable ? ["models.select"] : []
|
|
723
|
+
])
|
|
724
|
+
];
|
|
725
|
+
return mergeHeaders(json(document), resolution.responseHeaders);
|
|
726
|
+
}
|
|
727
|
+
if (request.method === "POST" && path === "/chat/stream") {
|
|
728
|
+
const body = await requestJson(request);
|
|
729
|
+
const selected = requestedAgent(body.requestedAgentId);
|
|
730
|
+
const input = stringValue(body.input);
|
|
731
|
+
const attachmentIds = stringList(body.attachmentIds);
|
|
732
|
+
if (!input && attachmentIds.length === 0) throw new BffInputError(400, "INPUT_REQUIRED", "A message or attachment is required.");
|
|
733
|
+
if (input && input.length > MAX_INPUT_LENGTH) throw new BffInputError(413, "INPUT_TOO_LARGE", "The message is too large.");
|
|
734
|
+
if (attachmentIds.length > MAX_ATTACHMENTS) throw new BffInputError(400, "TOO_MANY_ATTACHMENTS", `At most ${MAX_ATTACHMENTS} attachments are allowed.`);
|
|
735
|
+
return mergeHeaders(sseResponse(async (write) => {
|
|
736
|
+
await options.client.embed.streamAgent(selected.agentId, {
|
|
737
|
+
...identity,
|
|
738
|
+
...selected.agentVersionId ? { agent_version_id: selected.agentVersionId } : {},
|
|
739
|
+
runtime_target: runtimeTarget,
|
|
740
|
+
integration_id: integrationId,
|
|
741
|
+
...input ? { input } : {},
|
|
742
|
+
attachment_ids: attachmentIds,
|
|
743
|
+
...stringValue(body.threadId) ? { thread_id: stringValue(body.threadId) } : {},
|
|
744
|
+
...stringValue(body.requestedModelId) ? { requested_model_id: stringValue(body.requestedModelId) } : {},
|
|
745
|
+
tool_inputs: objectValue(body.toolInputs),
|
|
746
|
+
metadata: objectValue(body.metadata),
|
|
747
|
+
client: objectValue(body.client)
|
|
748
|
+
}, (event) => write(event), { idempotencyKey: stringValue(request.headers.get("idempotency-key")), signal: request.signal });
|
|
749
|
+
}), resolution.responseHeaders);
|
|
750
|
+
}
|
|
751
|
+
if (request.method === "GET" && path === "/threads") {
|
|
752
|
+
const skip = Number(url.searchParams.get("skip") || 0);
|
|
753
|
+
const limit = Number(url.searchParams.get("limit") || 20);
|
|
754
|
+
if (!Number.isInteger(skip) || skip < 0 || !Number.isInteger(limit) || limit < 1 || limit > 100) {
|
|
755
|
+
throw new BffInputError(400, "INVALID_PAGINATION", "skip must be non-negative and limit must be between 1 and 100.");
|
|
756
|
+
}
|
|
757
|
+
return mergeHeaders(json(await options.client.embed.listAgentThreadsMulti({
|
|
758
|
+
agent_ids: agents.map((agent) => agent.agentId),
|
|
759
|
+
external_user_id: externalUserId,
|
|
760
|
+
...resolution.principal.externalSessionId ? { external_session_id: resolution.principal.externalSessionId } : {},
|
|
761
|
+
runtime_target: runtimeTarget,
|
|
762
|
+
integration_id: integrationId,
|
|
763
|
+
skip,
|
|
764
|
+
limit
|
|
765
|
+
})), resolution.responseHeaders);
|
|
766
|
+
}
|
|
767
|
+
if (request.method === "GET" && path === "/threads/events") {
|
|
768
|
+
const rawCursor = url.searchParams.get("cursor");
|
|
769
|
+
const cursor = rawCursor === null ? void 0 : Number(rawCursor);
|
|
770
|
+
if (cursor !== void 0 && (!Number.isInteger(cursor) || cursor < 0)) {
|
|
771
|
+
throw new BffInputError(400, "INVALID_CURSOR", "cursor must be a non-negative integer.");
|
|
772
|
+
}
|
|
773
|
+
return mergeHeaders(sseResponse(async (write) => {
|
|
774
|
+
await options.client.embed.subscribeAgentThreadEventsMulti({
|
|
775
|
+
agentIds: agents.map((agent) => agent.agentId),
|
|
776
|
+
externalUserId,
|
|
777
|
+
...resolution.principal.externalSessionId ? { externalSessionId: resolution.principal.externalSessionId } : {},
|
|
778
|
+
...cursor === void 0 ? {} : { cursor },
|
|
779
|
+
runtimeTarget,
|
|
780
|
+
integrationId,
|
|
781
|
+
signal: request.signal
|
|
782
|
+
}, (event) => write(event));
|
|
783
|
+
}, void 0, () => ({
|
|
784
|
+
schema_version: "agents24.thread_summary_event.v1",
|
|
785
|
+
event: "snapshot_required",
|
|
786
|
+
cursor,
|
|
787
|
+
reason: "stream_failed"
|
|
788
|
+
})), resolution.responseHeaders);
|
|
789
|
+
}
|
|
790
|
+
if (/^\/threads\/[^/]+$/.test(path) && request.method === "GET") {
|
|
791
|
+
const threadId = routeId(path, /^\/threads\/([^/]+)$/, "threadId");
|
|
792
|
+
const rawBefore = url.searchParams.get("before_turn_index");
|
|
793
|
+
const rawLimit = url.searchParams.get("limit");
|
|
794
|
+
const beforeTurnIndex = rawBefore === null ? void 0 : Number(rawBefore);
|
|
795
|
+
const detailLimit = rawLimit === null ? void 0 : Number(rawLimit);
|
|
796
|
+
if (beforeTurnIndex !== void 0 && (!Number.isInteger(beforeTurnIndex) || beforeTurnIndex < 0)) {
|
|
797
|
+
throw new BffInputError(400, "INVALID_PAGINATION", "before_turn_index is invalid.");
|
|
798
|
+
}
|
|
799
|
+
if (detailLimit !== void 0 && (!Number.isInteger(detailLimit) || detailLimit < 1 || detailLimit > 100)) {
|
|
800
|
+
throw new BffInputError(400, "INVALID_PAGINATION", "limit must be between 1 and 100.");
|
|
801
|
+
}
|
|
802
|
+
return mergeHeaders(json(await options.client.embed.getConversationThread(threadId, {
|
|
803
|
+
agentIds: agents.map((agent) => agent.agentId),
|
|
804
|
+
externalUserId,
|
|
805
|
+
...resolution.principal.externalSessionId ? { externalSessionId: resolution.principal.externalSessionId } : {},
|
|
806
|
+
integrationId,
|
|
807
|
+
runtimeTarget,
|
|
808
|
+
beforeTurnIndex,
|
|
809
|
+
limit: detailLimit
|
|
810
|
+
})), resolution.responseHeaders);
|
|
811
|
+
}
|
|
812
|
+
if (/^\/threads\/[^/]+\/runtime-selection$/.test(path) && request.method === "PATCH") {
|
|
813
|
+
const threadId = routeId(path, /^\/threads\/([^/]+)\//, "threadId");
|
|
814
|
+
const body = await requestJson(request);
|
|
815
|
+
const selected = requestedAgent(body.preferredAgentId);
|
|
816
|
+
return mergeHeaders(json(await options.client.embed.setConversationRuntimeSelection(threadId, {
|
|
817
|
+
agent_ids: agents.map((agent) => agent.agentId),
|
|
818
|
+
preferred_agent_id: selected.agentId,
|
|
819
|
+
...stringValue(body.preferredModelId) ? { preferred_model_id: stringValue(body.preferredModelId) } : {},
|
|
820
|
+
...identity,
|
|
821
|
+
integration_id: integrationId,
|
|
822
|
+
runtime_target: runtimeTarget
|
|
823
|
+
})), resolution.responseHeaders);
|
|
824
|
+
}
|
|
825
|
+
if (/^\/threads\/[^/]+$/.test(path) && request.method === "DELETE") {
|
|
826
|
+
const threadId = routeId(path, /^\/threads\/([^/]+)$/, "threadId");
|
|
827
|
+
return mergeHeaders(json(await options.client.embed.deleteConversationThread(threadId, {
|
|
828
|
+
agentIds: agents.map((agent) => agent.agentId),
|
|
829
|
+
externalUserId,
|
|
830
|
+
...resolution.principal.externalSessionId ? { externalSessionId: resolution.principal.externalSessionId } : {},
|
|
831
|
+
integrationId,
|
|
832
|
+
runtimeTarget
|
|
833
|
+
}, { idempotencyKey: stringValue(request.headers.get("idempotency-key")) })), resolution.responseHeaders);
|
|
834
|
+
}
|
|
835
|
+
if (request.method === "POST" && path === "/attachments/upload") {
|
|
836
|
+
const form = await request.clone().formData();
|
|
837
|
+
const selected = requestedAgent(form.get("requested_agent_id"));
|
|
838
|
+
const policy = await options.client.embed.getResourcePolicy(selected.agentId, {
|
|
839
|
+
external_user_id: externalUserId,
|
|
840
|
+
integration_id: integrationId,
|
|
841
|
+
...selected.agentVersionId ? { agent_version_id: selected.agentVersionId } : {},
|
|
842
|
+
runtime_target: runtimeTarget
|
|
843
|
+
});
|
|
844
|
+
if (policy.agent_access_allowed === false) throw new BffInputError(403, "RESOURCE_POLICY_ACCESS_DENIED", "The requested Agent is not available.");
|
|
845
|
+
return dispatchToAgent(selected.agentId);
|
|
846
|
+
}
|
|
847
|
+
if (request.method === "POST" && /^\/attachments\/[^/]+\/content-access$/.test(path)) {
|
|
848
|
+
return dispatchToAgent(agents[0].agentId);
|
|
849
|
+
}
|
|
850
|
+
if (/^\/runs\/[^/]+\/(?:attach|cancel|feedback)$/.test(path) || /^\/runs\/[^/]+\/hitl\/[^/]+\/resume$/.test(path) || /^\/runs\/[^/]+\/mcp\/servers\/[^/]+\/auth\/start$/.test(path)) {
|
|
851
|
+
const runId = routeId(path, /^\/runs\/([^/]+)\//, "runId");
|
|
852
|
+
return dispatchToAgent(await resolveRunAgent(runId));
|
|
853
|
+
}
|
|
854
|
+
if (request.method === "POST" && path === "/mcp/callback/redeem") {
|
|
855
|
+
throw new BffInputError(409, "MCP_CALLBACK_DIRECT", "Server-embed MCP callbacks complete in the authorization popup.");
|
|
856
|
+
}
|
|
857
|
+
if (request.method === "POST" && path === "/session/revoke") {
|
|
858
|
+
const headers = await options.revokePrincipal?.(request, resolution.principal);
|
|
859
|
+
return mergeHeaders(new Response(null, { status: 204, ...headers ? { headers } : {} }), resolution.responseHeaders);
|
|
860
|
+
}
|
|
861
|
+
return mergeHeaders(publicError(404, "NOT_FOUND", "The requested BFF route does not exist."), resolution.responseHeaders);
|
|
862
|
+
} catch (error) {
|
|
863
|
+
if (error instanceof BffInputError) return mergeHeaders(publicError(error.status, error.code, error.message), resolution.responseHeaders);
|
|
864
|
+
return mergeHeaders(errorResponse(error), resolution.responseHeaders);
|
|
865
|
+
}
|
|
866
|
+
};
|
|
867
|
+
}
|
|
868
|
+
function createAgents24BffHandler(options) {
|
|
869
|
+
const resolveAgents = prepareAgents24BffAgentResolution(options);
|
|
870
|
+
let resolvedHandler;
|
|
871
|
+
const resolve = async () => {
|
|
872
|
+
const agents = await resolveAgents();
|
|
873
|
+
return createResolvedAgents24BffHandler({ ...options, agents });
|
|
874
|
+
};
|
|
875
|
+
return async (request) => {
|
|
876
|
+
if (!browserRequestIsSameOrigin(request)) {
|
|
877
|
+
return publicError(403, "BFF_ORIGIN_DENIED", "The BFF accepts same-origin browser requests only.");
|
|
878
|
+
}
|
|
879
|
+
resolvedHandler ||= resolve();
|
|
880
|
+
try {
|
|
881
|
+
return (await resolvedHandler)(request);
|
|
882
|
+
} catch (error) {
|
|
883
|
+
resolvedHandler = void 0;
|
|
884
|
+
return errorResponse(error);
|
|
885
|
+
}
|
|
886
|
+
};
|
|
887
|
+
}
|
|
459
888
|
|
|
460
889
|
export { createAgents24BffHandler };
|
|
461
890
|
//# sourceMappingURL=bff.js.map
|