@agents24/node 0.2.0 → 0.3.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 +17 -4
- package/compatibility.json +10 -10
- package/dist/bff.cjs +202 -33
- package/dist/bff.cjs.map +1 -1
- package/dist/bff.d.cts +3 -2
- package/dist/bff.d.ts +3 -2
- package/dist/bff.js +202 -33
- package/dist/bff.js.map +1 -1
- package/dist/{client-6MWalxux.d.cts → client-Rmk3dh_D.d.cts} +437 -71
- package/dist/{client-6MWalxux.d.ts → client-Rmk3dh_D.d.ts} +437 -71
- package/dist/index.cjs +194 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +194 -45
- package/dist/index.js.map +1 -1
- package/dist/server.cjs +52 -6
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +14 -2
- package/dist/server.d.ts +14 -2
- package/dist/server.js +50 -7
- package/dist/server.js.map +1 -1
- package/dist/types/agents.d.ts +5 -4
- package/dist/types/agents.d.ts.map +1 -1
- package/dist/types/bff.d.ts +2 -1
- package/dist/types/bff.d.ts.map +1 -1
- package/dist/types/builders.d.ts +2 -0
- package/dist/types/builders.d.ts.map +1 -1
- package/dist/types/client-runtime-admin.d.ts +1 -1
- package/dist/types/client.d.ts +4 -0
- package/dist/types/client.d.ts.map +1 -1
- package/dist/types/embed.d.ts +6 -3
- package/dist/types/embed.d.ts.map +1 -1
- package/dist/types/errors.d.ts +10 -2
- package/dist/types/errors.d.ts.map +1 -1
- package/dist/types/generated/instructions.d.ts +8 -0
- package/dist/types/generated/instructions.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/resourceBundles.d.ts +4 -1
- package/dist/types/generated/resourceBundles.d.ts.map +1 -1
- package/dist/types/generated/resourcePolicies.d.ts +14 -0
- package/dist/types/generated/resourcePolicies.d.ts.map +1 -0
- package/dist/types/http.d.ts +0 -2
- package/dist/types/http.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/runs.d.ts +4 -2
- package/dist/types/runs.d.ts.map +1 -1
- package/dist/types/server.d.ts +13 -1
- package/dist/types/server.d.ts.map +1 -1
- package/dist/types/types.d.ts +389 -60
- package/dist/types/types.d.ts.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
# `@agents24/node`
|
|
2
2
|
|
|
3
|
-
Last Updated: 2026-
|
|
3
|
+
Last Updated: 2026-08-12
|
|
4
4
|
|
|
5
5
|
Trusted-server Agents24 SDK for organization-authenticated control-plane, server embed, authoring, artifact, client-deployment, and client-session mint operations. It supports Node 22 and 24 with ESM and CJS entrypoints.
|
|
6
6
|
|
|
7
|
+
Runtime attachment uploads return durable metadata. Use `agents.createAttachmentContentAccess(...)` for internal attachments or `embed.createAttachmentContentAccess(...)` for server-embed attachments to mint a short-lived private inline or direct-download URL.
|
|
8
|
+
|
|
7
9
|
```ts
|
|
8
10
|
import { Agents24 } from "@agents24/node";
|
|
9
11
|
|
|
10
12
|
const agents24 = new Agents24({
|
|
11
13
|
baseUrl: process.env.AGENTS24_BASE_URL!,
|
|
12
14
|
apiKey: process.env.AGENTS24_API_KEY!,
|
|
13
|
-
organizationId: process.env.AGENTS24_ORGANIZATION_ID!,
|
|
14
|
-
projectId: process.env.AGENTS24_PROJECT_ID!,
|
|
15
15
|
});
|
|
16
16
|
```
|
|
17
17
|
|
|
@@ -31,6 +31,10 @@ const session = await agents24.clientSessions.createBackendSession(
|
|
|
31
31
|
|
|
32
32
|
Administer deployments through `agents24.clientDeployments`; use `agents24.agents`, `agents24.runs`, and `agents24.embed` for trusted Agent operations. End-user token networking remains in `@agents24/client`.
|
|
33
33
|
|
|
34
|
+
HITL V2 includes `respond` for Agent-requested user input. Its payload contains ordered answers with `question_id`, `selected_option_ids`, and optional `custom_text`; use the existing resume method and then reattach to the same run.
|
|
35
|
+
|
|
36
|
+
Deployment administration uses reversible `lifecycle_status: "active" | "inactive"` updates and irreversible `clientDeployments.delete(...)`. Active deployments require a Resource Policy; removing it makes the deployment inactive and invalidates issued sessions. There is no `revoke()` method.
|
|
37
|
+
|
|
34
38
|
New deployments follow the latest published Agent version by default. Use
|
|
35
39
|
`version_policy: { mode: "pinned", version_id: "..." }` for a fixed client deployment.
|
|
36
40
|
Server embed requests accept an optional `agent_version_id`; omission resolves latest published.
|
|
@@ -43,9 +47,18 @@ import { defineArtifact, tool } from "@agents24/node/artifacts";
|
|
|
43
47
|
```
|
|
44
48
|
|
|
45
49
|
Self-hosted Node artifact servers use `@agents24/node/server`. That server subpath does not re-export the declaration DSL.
|
|
50
|
+
`createArtifactServer` exposes manifest, health, connection-verification, and
|
|
51
|
+
invocation routes. Registration uses
|
|
52
|
+
`POST /.well-known/agents24/artifact/verify`; signed servers prove possession
|
|
53
|
+
of the configured secret without transmitting it, while `authMode: "none"`
|
|
54
|
+
servers return the matching unsigned challenge with no proof.
|
|
46
55
|
|
|
47
56
|
This package is server-only. Never expose an organization API key to browser code. Persistent or native client-session enrollment requires DPoP; pass the proof as `dpopProof` in the mint request options and read a nonce challenge from `Agents24SDKError.responseHeaders`.
|
|
48
57
|
|
|
49
58
|
## Same-origin BFF
|
|
50
59
|
|
|
51
|
-
`@agents24/node/bff` exports `createAgents24BffHandler(...)`. Mount its returned Fetch handler at a same-origin path,
|
|
60
|
+
`@agents24/node/bff` exports `createAgents24BffHandler(...)`. Mount its returned Fetch handler at a same-origin path, provide an API-key client plus Agent ID, and resolve a stable customer `subject`. Supply an optional `issuer` only when an authenticated customer must receive organization-wide governance across Agents or applications. Pair it with `createBffAgentClient` from `@agents24/client/bff`. The handler rejects cross-site browser requests before principal resolution, forwards durable attach cursors, and preserves caller idempotency keys for retryable mutations. The bridge keeps the API key, Agent, and optional exact Agent version on the server; it does not use a Client Deployment. `npm create agents24-app@latest` generates the complete Hono/Vite arrangement, including an auto-generated cookie secret for its default guest session.
|
|
61
|
+
|
|
62
|
+
## Customer Resource Policies
|
|
63
|
+
|
|
64
|
+
`agents24.resourcePolicies` lists dashboard-authored Policy Sets and manages customer assignment, effective projection, quota schedules, and explicit resets. Raw `{ issuer, subject }` identity is sent only in request bodies. Supply `{ idempotencyKey }` to mutations. Organization keys need `resource_policies.read`, plus `resource_policies.write` for mutation methods.
|
package/compatibility.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"contractHash": "
|
|
2
|
+
"contractHash": "a409af6996d10a8d94e0f6332c9a381c3535a9c8511314b6c42b237d7e057029",
|
|
3
3
|
"contractVersion": "0.2.0",
|
|
4
4
|
"corpus": "conformance/v1/corpus.json",
|
|
5
5
|
"generatedBy": "@agents24/sdk-contract",
|
|
@@ -15,11 +15,11 @@
|
|
|
15
15
|
"reactDom": ">=19 <20"
|
|
16
16
|
},
|
|
17
17
|
"@agents24/client": {
|
|
18
|
-
"contract": "
|
|
19
|
-
"runtime": "run-stream.
|
|
18
|
+
"contract": "a409af6996d10a8d94e0f6332c9a381c3535a9c8511314b6c42b237d7e057029",
|
|
19
|
+
"runtime": "run-stream.v3"
|
|
20
20
|
},
|
|
21
21
|
"@agents24/node": {
|
|
22
|
-
"contract": "
|
|
22
|
+
"contract": "a409af6996d10a8d94e0f6332c9a381c3535a9c8511314b6c42b237d7e057029",
|
|
23
23
|
"node": "^22.0.0 || ^24.0.0"
|
|
24
24
|
},
|
|
25
25
|
"@agents24/react": {
|
|
@@ -27,18 +27,18 @@
|
|
|
27
27
|
"react": ">=19 <20"
|
|
28
28
|
},
|
|
29
29
|
"@talmudpedia/runtime-sdk": {
|
|
30
|
-
"contract": "
|
|
30
|
+
"contract": "a409af6996d10a8d94e0f6332c9a381c3535a9c8511314b6c42b237d7e057029",
|
|
31
31
|
"protocolOwner": "@agents24/client"
|
|
32
32
|
},
|
|
33
33
|
"agents24": {
|
|
34
|
-
"contract": "
|
|
34
|
+
"contract": "a409af6996d10a8d94e0f6332c9a381c3535a9c8511314b6c42b237d7e057029",
|
|
35
35
|
"python": ">=3.10"
|
|
36
36
|
}
|
|
37
37
|
},
|
|
38
38
|
"protocols": {
|
|
39
39
|
"runtime": {
|
|
40
40
|
"minimum": "2.0.0",
|
|
41
|
-
"wire": "run-stream.
|
|
41
|
+
"wire": "run-stream.v3"
|
|
42
42
|
},
|
|
43
43
|
"threadSummary": {
|
|
44
44
|
"minimum": "1.0.0",
|
|
@@ -47,8 +47,8 @@
|
|
|
47
47
|
},
|
|
48
48
|
"schemaVersion": "agents24.compatibility.v1",
|
|
49
49
|
"targetOperationCounts": {
|
|
50
|
-
"client":
|
|
51
|
-
"node":
|
|
52
|
-
"python":
|
|
50
|
+
"client": 22,
|
|
51
|
+
"node": 79,
|
|
52
|
+
"python": 70
|
|
53
53
|
}
|
|
54
54
|
}
|
package/dist/bff.cjs
CHANGED
|
@@ -8,16 +8,37 @@ var crypto = require('crypto');
|
|
|
8
8
|
var Agents24SDKError = class extends Error {
|
|
9
9
|
kind;
|
|
10
10
|
status;
|
|
11
|
-
|
|
11
|
+
failure;
|
|
12
12
|
responseHeaders;
|
|
13
13
|
constructor(message, options) {
|
|
14
14
|
super(message, { cause: options.cause });
|
|
15
15
|
this.name = "Agents24SDKError";
|
|
16
16
|
this.kind = options.kind;
|
|
17
17
|
this.status = options.status;
|
|
18
|
-
this.
|
|
18
|
+
this.failure = options.failure;
|
|
19
19
|
this.responseHeaders = options.responseHeaders;
|
|
20
20
|
}
|
|
21
|
+
get code() {
|
|
22
|
+
return this.failure?.code;
|
|
23
|
+
}
|
|
24
|
+
get category() {
|
|
25
|
+
return this.failure?.category;
|
|
26
|
+
}
|
|
27
|
+
get failureId() {
|
|
28
|
+
return this.failure?.failure_id;
|
|
29
|
+
}
|
|
30
|
+
get requestId() {
|
|
31
|
+
return this.failure?.request_id ?? void 0;
|
|
32
|
+
}
|
|
33
|
+
get retryable() {
|
|
34
|
+
return this.failure?.retryable ?? false;
|
|
35
|
+
}
|
|
36
|
+
get retryAfterMs() {
|
|
37
|
+
return this.failure?.retry_after_ms ?? void 0;
|
|
38
|
+
}
|
|
39
|
+
get details() {
|
|
40
|
+
return this.failure?.details ?? {};
|
|
41
|
+
}
|
|
21
42
|
};
|
|
22
43
|
|
|
23
44
|
// src/bff.ts
|
|
@@ -32,15 +53,98 @@ function normalizeBasePath(value) {
|
|
|
32
53
|
if (!path.startsWith("/") || /[\r\n\0?#]/.test(path)) throw new Error("basePath must be an absolute path.");
|
|
33
54
|
return path;
|
|
34
55
|
}
|
|
35
|
-
function
|
|
36
|
-
|
|
56
|
+
function browserRequestIsSameOrigin(request) {
|
|
57
|
+
const fetchSite = String(request.headers.get("sec-fetch-site") || "").trim().toLowerCase();
|
|
58
|
+
if (fetchSite) return fetchSite === "same-origin";
|
|
59
|
+
const rawOrigin = String(request.headers.get("origin") || "").trim();
|
|
60
|
+
if (!rawOrigin) return true;
|
|
61
|
+
try {
|
|
62
|
+
return rawOrigin !== "null" && new URL(rawOrigin).origin === new URL(request.url).origin;
|
|
63
|
+
} catch {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function publicError(status, code, message, retryable = false, violations) {
|
|
68
|
+
const category = status === 401 ? "authentication" : status === 403 ? "authorization" : status === 404 ? "not_found" : status === 409 ? "conflict" : status === 429 ? "rate_limit" : status >= 500 ? "internal" : "validation";
|
|
69
|
+
return Response.json({
|
|
70
|
+
schema_version: "agents24.failure.v1",
|
|
71
|
+
failure_id: crypto.randomUUID(),
|
|
72
|
+
code,
|
|
73
|
+
category,
|
|
74
|
+
message,
|
|
75
|
+
request_id: crypto.randomUUID(),
|
|
76
|
+
retryable,
|
|
77
|
+
retry_after_ms: null,
|
|
78
|
+
details: {}
|
|
79
|
+
}, {
|
|
80
|
+
status,
|
|
81
|
+
headers: { "Cache-Control": "no-store", "Content-Type": "application/json; charset=utf-8" }
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
function safeResourcePolicyError(details) {
|
|
85
|
+
if (!details || typeof details !== "object" || Array.isArray(details)) return null;
|
|
86
|
+
const outer = details;
|
|
87
|
+
const candidate = outer.schema_version === "agents24.failure.v1" ? outer : outer.detail && typeof outer.detail === "object" && !Array.isArray(outer.detail) ? outer.detail : outer;
|
|
88
|
+
const code = typeof candidate.code === "string" ? candidate.code : "";
|
|
89
|
+
if (!["RESOURCE_POLICY_ACCESS_DENIED", "RESOURCE_POLICY_QUOTA_EXCEEDED", "RESOURCE_POLICY_CONFIGURATION_ERROR"].includes(code)) return null;
|
|
90
|
+
const failureId = typeof candidate.failure_id === "string" && candidate.failure_id.trim() ? candidate.failure_id : "";
|
|
91
|
+
if (!failureId) return null;
|
|
92
|
+
const message = typeof candidate.message === "string" && candidate.message.trim() ? candidate.message.trim() : "The resource policy prevented this request.";
|
|
93
|
+
const allowedViolationKeys = /* @__PURE__ */ new Set([
|
|
94
|
+
"policy_subject_type",
|
|
95
|
+
"rule_type",
|
|
96
|
+
"aggregation_scope",
|
|
97
|
+
"resource_type",
|
|
98
|
+
"resource_name",
|
|
99
|
+
"quota_unit",
|
|
100
|
+
"quota_window",
|
|
101
|
+
"limit",
|
|
102
|
+
"remaining",
|
|
103
|
+
"resets_at"
|
|
104
|
+
]);
|
|
105
|
+
const candidateDetails = candidate.details && typeof candidate.details === "object" ? candidate.details : candidate;
|
|
106
|
+
const violations = Array.isArray(candidateDetails.violations) ? candidateDetails.violations.flatMap((item) => {
|
|
107
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) return [];
|
|
108
|
+
return [Object.fromEntries(
|
|
109
|
+
Object.entries(item).filter(([key, value]) => allowedViolationKeys.has(key) && ["string", "number"].includes(typeof value))
|
|
110
|
+
)];
|
|
111
|
+
}).filter((item) => Object.keys(item).length > 0).slice(0, 20) : void 0;
|
|
112
|
+
return {
|
|
113
|
+
failureId,
|
|
114
|
+
code,
|
|
115
|
+
category: code === "RESOURCE_POLICY_QUOTA_EXCEEDED" ? "quota" : "policy",
|
|
116
|
+
message,
|
|
117
|
+
requestId: typeof candidate.request_id === "string" ? candidate.request_id : null,
|
|
118
|
+
retryable: candidate.retryable === true,
|
|
119
|
+
retryAfterMs: typeof candidate.retry_after_ms === "number" && candidate.retry_after_ms >= 0 ? candidate.retry_after_ms : null,
|
|
120
|
+
...violations?.length ? { violations } : {}
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function resourcePolicyFailureResponse(status, failure) {
|
|
124
|
+
return Response.json({
|
|
125
|
+
schema_version: "agents24.failure.v1",
|
|
126
|
+
failure_id: failure.failureId,
|
|
127
|
+
code: failure.code,
|
|
128
|
+
category: failure.category,
|
|
129
|
+
message: failure.message,
|
|
130
|
+
retryable: failure.retryable,
|
|
131
|
+
request_id: failure.requestId,
|
|
132
|
+
retry_after_ms: failure.retryAfterMs,
|
|
133
|
+
details: failure.violations?.length ? { violations: failure.violations } : {}
|
|
134
|
+
}, {
|
|
37
135
|
status,
|
|
38
136
|
headers: { "Cache-Control": "no-store", "Content-Type": "application/json; charset=utf-8" }
|
|
39
137
|
});
|
|
40
138
|
}
|
|
41
139
|
function errorResponse(error) {
|
|
42
|
-
|
|
43
|
-
|
|
140
|
+
const isSdkError = error instanceof Agents24SDKError || error instanceof Error && error.name === "Agents24SDKError" && "kind" in error && ["protocol", "network", "http"].includes(String(error.kind));
|
|
141
|
+
if (isSdkError) {
|
|
142
|
+
const sdkError = error;
|
|
143
|
+
const status = sdkError.status && sdkError.status >= 400 && sdkError.status <= 599 ? sdkError.status : 502;
|
|
144
|
+
const policyError = safeResourcePolicyError(sdkError.failure);
|
|
145
|
+
if (policyError) {
|
|
146
|
+
return resourcePolicyFailureResponse(status, policyError);
|
|
147
|
+
}
|
|
44
148
|
return publicError(status, `UPSTREAM_${status}`, "The Agent request failed.", status >= 500);
|
|
45
149
|
}
|
|
46
150
|
return publicError(500, "BFF_REQUEST_FAILED", "The Agent request could not be completed.", true);
|
|
@@ -112,22 +216,21 @@ function sseResponse(execute, headers) {
|
|
|
112
216
|
void execute(write).catch(async (error) => {
|
|
113
217
|
const message = "The Agent stream failed.";
|
|
114
218
|
const event = {
|
|
115
|
-
version: "run-stream.
|
|
219
|
+
version: "run-stream.v3",
|
|
116
220
|
seq: Number.MAX_SAFE_INTEGER,
|
|
117
221
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
118
222
|
event: "run.failed",
|
|
119
223
|
run_id: "bff-stream-error",
|
|
120
224
|
stage: "run",
|
|
121
|
-
payload: { error: message }
|
|
122
|
-
diagnostics: [{ message }]
|
|
225
|
+
payload: { error: message }
|
|
123
226
|
};
|
|
124
227
|
await write(event).catch(() => void 0);
|
|
125
228
|
}).finally(() => writer.close().catch(() => void 0));
|
|
126
229
|
return new Response(stream.readable, { headers: sseHeaders(headers) });
|
|
127
230
|
}
|
|
128
231
|
function bootstrapDocument(raw, agentVersionId) {
|
|
129
|
-
const
|
|
130
|
-
const selectable =
|
|
232
|
+
const features = objectValue(raw.features);
|
|
233
|
+
const selectable = objectValue(features.models).selectable === true;
|
|
131
234
|
const resolvedVersionId = stringValue(raw.agent_version_id) || agentVersionId || "";
|
|
132
235
|
const versionNumber = Number(raw.agent_version_number || 0);
|
|
133
236
|
return {
|
|
@@ -141,8 +244,8 @@ function bootstrapDocument(raw, agentVersionId) {
|
|
|
141
244
|
policy_epoch: 0
|
|
142
245
|
},
|
|
143
246
|
authentication: { modes: ["server_embed"], access_token_lifetime_seconds: 0, jwks_path: "" },
|
|
144
|
-
capabilities: ["attachments.write", ...selectable ? ["models.select"] : []],
|
|
145
|
-
features
|
|
247
|
+
capabilities: ["attachments.read", "attachments.write", "feedback.write", ...selectable ? ["models.select"] : []],
|
|
248
|
+
features,
|
|
146
249
|
native_profiles: []
|
|
147
250
|
};
|
|
148
251
|
}
|
|
@@ -156,29 +259,50 @@ function createAgents24BffHandler(options) {
|
|
|
156
259
|
if (url.pathname !== basePath && !url.pathname.startsWith(`${basePath}/`)) {
|
|
157
260
|
return publicError(404, "NOT_FOUND", "The requested BFF route does not exist.");
|
|
158
261
|
}
|
|
262
|
+
if (!browserRequestIsSameOrigin(request)) {
|
|
263
|
+
return publicError(403, "BFF_ORIGIN_DENIED", "The BFF accepts same-origin browser requests only.");
|
|
264
|
+
}
|
|
159
265
|
const path = url.pathname.slice(basePath.length) || "/";
|
|
160
266
|
let resolution;
|
|
161
267
|
try {
|
|
162
268
|
resolution = await options.resolvePrincipal(request);
|
|
163
|
-
resolution.principal.
|
|
269
|
+
resolution.principal.subject = normalized(resolution.principal.subject, "subject");
|
|
270
|
+
if (resolution.principal.issuer !== void 0) {
|
|
271
|
+
resolution.principal.issuer = normalized(resolution.principal.issuer, "issuer");
|
|
272
|
+
}
|
|
164
273
|
} catch {
|
|
165
274
|
return publicError(401, "PRINCIPAL_REQUIRED", "A valid application session is required.");
|
|
166
275
|
}
|
|
167
276
|
const principal = resolution.principal;
|
|
168
|
-
const identity = {
|
|
169
|
-
external_user_id: principal.externalUserId,
|
|
170
|
-
...principal.externalSessionId ? { external_session_id: principal.externalSessionId } : {}
|
|
171
|
-
};
|
|
172
|
-
const threadIdentity = {
|
|
173
|
-
externalUserId: principal.externalUserId,
|
|
174
|
-
...principal.externalSessionId ? { externalSessionId: principal.externalSessionId } : {}
|
|
175
|
-
};
|
|
176
277
|
const idempotencyKey = stringValue(request.headers.get("idempotency-key"));
|
|
177
278
|
try {
|
|
279
|
+
let customerPrincipalRef = "";
|
|
280
|
+
if (!(request.method === "GET" && (path === "/bootstrap" || path === "/resource-policy"))) {
|
|
281
|
+
const entitlement = await embed.getResourcePolicy(agentId, {
|
|
282
|
+
external_user_id: principal.subject,
|
|
283
|
+
...principal.issuer ? { external_user_issuer: principal.issuer } : {},
|
|
284
|
+
...agentVersionId ? { agent_version_id: agentVersionId } : {}
|
|
285
|
+
});
|
|
286
|
+
customerPrincipalRef = stringValue(entitlement.customer_principal_ref) || principal.subject;
|
|
287
|
+
}
|
|
288
|
+
const identity = {
|
|
289
|
+
external_user_id: customerPrincipalRef,
|
|
290
|
+
...principal.externalSessionId ? { external_session_id: principal.externalSessionId } : {}
|
|
291
|
+
};
|
|
292
|
+
const threadIdentity = {
|
|
293
|
+
externalUserId: customerPrincipalRef,
|
|
294
|
+
...principal.externalSessionId ? { externalSessionId: principal.externalSessionId } : {}
|
|
295
|
+
};
|
|
178
296
|
let response;
|
|
179
297
|
if (request.method === "GET" && path === "/bootstrap") {
|
|
180
298
|
const raw = await embed.getRuntimeBootstrap(agentId, agentVersionId);
|
|
181
299
|
response = json(bootstrapDocument(raw, agentVersionId));
|
|
300
|
+
} else if (request.method === "GET" && path === "/resource-policy") {
|
|
301
|
+
response = json(await embed.getResourcePolicy(agentId, {
|
|
302
|
+
external_user_id: principal.subject,
|
|
303
|
+
...principal.issuer ? { external_user_issuer: principal.issuer } : {},
|
|
304
|
+
...agentVersionId ? { agent_version_id: agentVersionId } : {}
|
|
305
|
+
}));
|
|
182
306
|
} else if (request.method === "POST" && path === "/chat/stream") {
|
|
183
307
|
const body = await requestJson(request);
|
|
184
308
|
const input = stringValue(body.input);
|
|
@@ -199,15 +323,45 @@ function createAgents24BffHandler(options) {
|
|
|
199
323
|
});
|
|
200
324
|
} else if (request.method === "POST" && /^\/runs\/[^/]+\/attach$/.test(path)) {
|
|
201
325
|
const runId = routeId(path, /^\/runs\/([^/]+)\/attach$/, "runId");
|
|
326
|
+
const body = await requestJson(request);
|
|
327
|
+
const cursor = body.cursor;
|
|
328
|
+
if (cursor !== void 0 && (!Number.isInteger(cursor) || Number(cursor) < 0)) {
|
|
329
|
+
throw new BffInputError(400, "INVALID_CURSOR", "cursor must be a non-negative integer.");
|
|
330
|
+
}
|
|
202
331
|
response = sseResponse(async (write) => {
|
|
203
|
-
await embed.attachAgentRun(agentId, runId,
|
|
332
|
+
await embed.attachAgentRun(agentId, runId, {
|
|
333
|
+
...identity,
|
|
334
|
+
...cursor === void 0 ? {} : { cursor: Number(cursor) }
|
|
335
|
+
}, (event) => write(event), {
|
|
204
336
|
idempotencyKey,
|
|
205
337
|
signal: request.signal
|
|
206
338
|
});
|
|
207
339
|
});
|
|
208
340
|
} else if (request.method === "POST" && /^\/runs\/[^/]+\/cancel$/.test(path)) {
|
|
209
341
|
const runId = routeId(path, /^\/runs\/([^/]+)\/cancel$/, "runId");
|
|
210
|
-
response = json(await embed.cancelAgentRun(agentId, runId, threadIdentity));
|
|
342
|
+
response = json(await embed.cancelAgentRun(agentId, runId, threadIdentity, { idempotencyKey }));
|
|
343
|
+
} else if (request.method === "PUT" && /^\/runs\/[^/]+\/feedback$/.test(path)) {
|
|
344
|
+
const runId = routeId(path, /^\/runs\/([^/]+)\/feedback$/, "runId");
|
|
345
|
+
const body = await requestJson(request);
|
|
346
|
+
const rating = body.rating === null ? null : stringValue(body.rating);
|
|
347
|
+
const reasons = /* @__PURE__ */ new Set(["incorrect", "irrelevant", "incomplete", "unsafe", "citation_issue", "other"]);
|
|
348
|
+
const reason = stringValue(body.reason);
|
|
349
|
+
const comment = stringValue(body.comment);
|
|
350
|
+
if (rating !== null && rating !== "like" && rating !== "dislike") {
|
|
351
|
+
throw new BffInputError(422, "INVALID_FEEDBACK", "rating must be like, dislike, or null.");
|
|
352
|
+
}
|
|
353
|
+
if (reason && !reasons.has(reason)) {
|
|
354
|
+
throw new BffInputError(422, "INVALID_FEEDBACK", "The feedback reason is invalid.");
|
|
355
|
+
}
|
|
356
|
+
if (comment && comment.length > 1e3) {
|
|
357
|
+
throw new BffInputError(422, "INVALID_FEEDBACK", "The feedback comment is too long.");
|
|
358
|
+
}
|
|
359
|
+
response = json(await embed.setAgentRunFeedback(agentId, runId, {
|
|
360
|
+
...identity,
|
|
361
|
+
rating,
|
|
362
|
+
...rating === "dislike" && reason ? { reason } : {},
|
|
363
|
+
...rating === "dislike" && comment ? { comment } : {}
|
|
364
|
+
}));
|
|
211
365
|
} else if (request.method === "GET" && path === "/threads") {
|
|
212
366
|
const skip = Number(url.searchParams.get("skip") || 0);
|
|
213
367
|
const limit = Number(url.searchParams.get("limit") || 20);
|
|
@@ -217,27 +371,29 @@ function createAgents24BffHandler(options) {
|
|
|
217
371
|
limit: Number.isFinite(limit) ? limit : 20
|
|
218
372
|
}));
|
|
219
373
|
} else if (request.method === "GET" && path === "/threads/events") {
|
|
220
|
-
const
|
|
374
|
+
const rawCursor = url.searchParams.get("cursor");
|
|
375
|
+
const cursor = rawCursor === null ? void 0 : Number(rawCursor);
|
|
221
376
|
response = sseResponse(async (write) => {
|
|
222
377
|
await embed.subscribeAgentThreadEvents(agentId, {
|
|
223
378
|
...threadIdentity,
|
|
224
|
-
...Number.isFinite(cursor) ? { cursor } : {},
|
|
379
|
+
...cursor !== void 0 && Number.isFinite(cursor) ? { cursor } : {},
|
|
225
380
|
signal: request.signal
|
|
226
381
|
}, (event) => write(event));
|
|
227
382
|
});
|
|
228
383
|
} else if (/^\/threads\/[^/]+$/.test(path) && request.method === "GET") {
|
|
229
384
|
const threadId = routeId(path, /^\/threads\/([^/]+)$/, "threadId");
|
|
230
|
-
const
|
|
231
|
-
const
|
|
385
|
+
const rawBefore = url.searchParams.get("before_turn_index");
|
|
386
|
+
const rawLimit = url.searchParams.get("limit");
|
|
387
|
+
const before = rawBefore === null ? void 0 : Number(rawBefore);
|
|
388
|
+
const limit = rawLimit === null ? void 0 : Number(rawLimit);
|
|
232
389
|
response = json(await embed.getAgentThread(agentId, threadId, {
|
|
233
390
|
...threadIdentity,
|
|
234
|
-
...Number.isFinite(before) ? { beforeTurnIndex: before } : {},
|
|
235
|
-
...Number.isFinite(limit) ? { limit } : {}
|
|
236
|
-
includeRunEvents: url.searchParams.get("include_run_events") === "true"
|
|
391
|
+
...before !== void 0 && Number.isFinite(before) ? { beforeTurnIndex: before } : {},
|
|
392
|
+
...limit !== void 0 && Number.isFinite(limit) ? { limit } : {}
|
|
237
393
|
}));
|
|
238
394
|
} else if (/^\/threads\/[^/]+$/.test(path) && request.method === "DELETE") {
|
|
239
395
|
const threadId = routeId(path, /^\/threads\/([^/]+)$/, "threadId");
|
|
240
|
-
response = json(await embed.deleteAgentThread(agentId, threadId, threadIdentity));
|
|
396
|
+
response = json(await embed.deleteAgentThread(agentId, threadId, threadIdentity, { idempotencyKey }));
|
|
241
397
|
} else if (request.method === "POST" && path === "/attachments/upload") {
|
|
242
398
|
const form = await request.formData();
|
|
243
399
|
const files = form.getAll("files").filter((entry) => entry instanceof File);
|
|
@@ -248,6 +404,18 @@ function createAgents24BffHandler(options) {
|
|
|
248
404
|
...stringValue(form.get("thread_id")) ? { threadId: stringValue(form.get("thread_id")) } : {},
|
|
249
405
|
idempotencyKey
|
|
250
406
|
}));
|
|
407
|
+
} else if (request.method === "POST" && /^\/attachments\/[^/]+\/content-access$/.test(path)) {
|
|
408
|
+
const attachmentId = routeId(path, /^\/attachments\/([^/]+)\/content-access$/, "attachmentId");
|
|
409
|
+
const body = await requestJson(request);
|
|
410
|
+
const disposition = stringValue(body.disposition);
|
|
411
|
+
if (disposition !== "inline" && disposition !== "attachment") {
|
|
412
|
+
throw new BffInputError(400, "INVALID_DISPOSITION", "Attachment disposition is invalid.");
|
|
413
|
+
}
|
|
414
|
+
response = json(await embed.createAttachmentContentAccess(agentId, attachmentId, {
|
|
415
|
+
external_user_id: threadIdentity.externalUserId,
|
|
416
|
+
...threadIdentity.externalSessionId ? { external_session_id: threadIdentity.externalSessionId } : {},
|
|
417
|
+
disposition
|
|
418
|
+
}));
|
|
251
419
|
} else if (request.method === "POST" && /^\/runs\/[^/]+\/hitl\/[^/]+\/resume$/.test(path)) {
|
|
252
420
|
const match = /^\/runs\/([^/]+)\/hitl\/([^/]+)\/resume$/.exec(path);
|
|
253
421
|
const runId = routeId(path, /^\/runs\/([^/]+)\/hitl\//, "runId");
|
|
@@ -258,7 +426,8 @@ function createAgents24BffHandler(options) {
|
|
|
258
426
|
schema_version: "agents24.hitl.resume.v2",
|
|
259
427
|
interrupt_id: normalized(interruptId, "interruptId"),
|
|
260
428
|
action: normalized(String(body.action || ""), "action"),
|
|
261
|
-
...stringValue(body.comment) ? { comment: stringValue(body.comment) } : {}
|
|
429
|
+
...stringValue(body.comment) ? { comment: stringValue(body.comment) } : {},
|
|
430
|
+
...Array.isArray(body.answers) ? { answers: body.answers } : {}
|
|
262
431
|
}, { idempotencyKey }));
|
|
263
432
|
} else if (request.method === "POST" && /^\/runs\/[^/]+\/mcp\/servers\/[^/]+\/auth\/start$/.test(path)) {
|
|
264
433
|
const match = /^\/runs\/([^/]+)\/mcp\/servers\/([^/]+)\/auth\/start$/.exec(path);
|