@agents24/node 0.2.0 → 0.2.1
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 +204 -33
- package/dist/bff.cjs.map +1 -1
- package/dist/bff.d.cts +5 -3
- package/dist/bff.d.ts +5 -3
- package/dist/bff.js +204 -33
- package/dist/bff.js.map +1 -1
- package/dist/{client-6MWalxux.d.cts → client-CnOaaCVH.d.cts} +431 -71
- package/dist/{client-6MWalxux.d.ts → client-CnOaaCVH.d.ts} +431 -71
- package/dist/index.cjs +187 -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 +187 -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 +4 -2
- 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 +5 -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 +384 -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-07
|
|
3
|
+
Last Updated: 2026-08-07
|
|
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 the deployment ID, and resolve authenticated customers as `{ issuer, subject }`. 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, deployment, and optional exact Agent version on the server. `npm create agents24-app@latest` generates the complete Hono/Vite arrangement.
|
|
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": "13f59fd11fc838b1f7a51afe15afcdb6f1429fed94e37a1341c97061882e57d2",
|
|
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": "13f59fd11fc838b1f7a51afe15afcdb6f1429fed94e37a1341c97061882e57d2",
|
|
19
|
+
"runtime": "run-stream.v3"
|
|
20
20
|
},
|
|
21
21
|
"@agents24/node": {
|
|
22
|
-
"contract": "
|
|
22
|
+
"contract": "13f59fd11fc838b1f7a51afe15afcdb6f1429fed94e37a1341c97061882e57d2",
|
|
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": "13f59fd11fc838b1f7a51afe15afcdb6f1429fed94e37a1341c97061882e57d2",
|
|
31
31
|
"protocolOwner": "@agents24/client"
|
|
32
32
|
},
|
|
33
33
|
"agents24": {
|
|
34
|
-
"contract": "
|
|
34
|
+
"contract": "13f59fd11fc838b1f7a51afe15afcdb6f1429fed94e37a1341c97061882e57d2",
|
|
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": 78,
|
|
52
|
+
"python": 69
|
|
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,14 +244,15 @@ 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
|
}
|
|
149
252
|
function createAgents24BffHandler(options) {
|
|
150
253
|
const agentId = normalized(options.agentId, "agentId");
|
|
151
254
|
const agentVersionId = stringValue(options.agentVersionId);
|
|
255
|
+
const deploymentId = normalized(options.deploymentId, "deploymentId");
|
|
152
256
|
const basePath = normalizeBasePath(options.basePath);
|
|
153
257
|
const embed = options.client.embed;
|
|
154
258
|
return async (request) => {
|
|
@@ -156,29 +260,51 @@ function createAgents24BffHandler(options) {
|
|
|
156
260
|
if (url.pathname !== basePath && !url.pathname.startsWith(`${basePath}/`)) {
|
|
157
261
|
return publicError(404, "NOT_FOUND", "The requested BFF route does not exist.");
|
|
158
262
|
}
|
|
263
|
+
if (!browserRequestIsSameOrigin(request)) {
|
|
264
|
+
return publicError(403, "BFF_ORIGIN_DENIED", "The BFF accepts same-origin browser requests only.");
|
|
265
|
+
}
|
|
159
266
|
const path = url.pathname.slice(basePath.length) || "/";
|
|
160
267
|
let resolution;
|
|
161
268
|
try {
|
|
162
269
|
resolution = await options.resolvePrincipal(request);
|
|
163
|
-
resolution.principal.
|
|
270
|
+
resolution.principal.issuer = normalized(resolution.principal.issuer, "issuer");
|
|
271
|
+
resolution.principal.subject = normalized(resolution.principal.subject, "subject");
|
|
164
272
|
} catch {
|
|
165
273
|
return publicError(401, "PRINCIPAL_REQUIRED", "A valid application session is required.");
|
|
166
274
|
}
|
|
167
275
|
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
276
|
const idempotencyKey = stringValue(request.headers.get("idempotency-key"));
|
|
177
277
|
try {
|
|
278
|
+
let customerPrincipalRef = "";
|
|
279
|
+
if (!(request.method === "GET" && (path === "/bootstrap" || path === "/resource-policy"))) {
|
|
280
|
+
const entitlement = await options.client.resourcePolicies.getEffectiveCustomerPolicy({
|
|
281
|
+
issuer: principal.issuer,
|
|
282
|
+
subject: principal.subject,
|
|
283
|
+
deployment_id: deploymentId
|
|
284
|
+
});
|
|
285
|
+
customerPrincipalRef = normalized(
|
|
286
|
+
String(entitlement.customer_principal_ref || ""),
|
|
287
|
+
"customerPrincipalRef"
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
const identity = {
|
|
291
|
+
external_user_id: customerPrincipalRef,
|
|
292
|
+
...principal.externalSessionId ? { external_session_id: principal.externalSessionId } : {}
|
|
293
|
+
};
|
|
294
|
+
const threadIdentity = {
|
|
295
|
+
externalUserId: customerPrincipalRef,
|
|
296
|
+
...principal.externalSessionId ? { externalSessionId: principal.externalSessionId } : {}
|
|
297
|
+
};
|
|
178
298
|
let response;
|
|
179
299
|
if (request.method === "GET" && path === "/bootstrap") {
|
|
180
300
|
const raw = await embed.getRuntimeBootstrap(agentId, agentVersionId);
|
|
181
301
|
response = json(bootstrapDocument(raw, agentVersionId));
|
|
302
|
+
} else if (request.method === "GET" && path === "/resource-policy") {
|
|
303
|
+
response = json(await options.client.resourcePolicies.getEffectiveCustomerPolicy({
|
|
304
|
+
issuer: principal.issuer,
|
|
305
|
+
subject: principal.subject,
|
|
306
|
+
deployment_id: deploymentId
|
|
307
|
+
}));
|
|
182
308
|
} else if (request.method === "POST" && path === "/chat/stream") {
|
|
183
309
|
const body = await requestJson(request);
|
|
184
310
|
const input = stringValue(body.input);
|
|
@@ -199,15 +325,45 @@ function createAgents24BffHandler(options) {
|
|
|
199
325
|
});
|
|
200
326
|
} else if (request.method === "POST" && /^\/runs\/[^/]+\/attach$/.test(path)) {
|
|
201
327
|
const runId = routeId(path, /^\/runs\/([^/]+)\/attach$/, "runId");
|
|
328
|
+
const body = await requestJson(request);
|
|
329
|
+
const cursor = body.cursor;
|
|
330
|
+
if (cursor !== void 0 && (!Number.isInteger(cursor) || Number(cursor) < 0)) {
|
|
331
|
+
throw new BffInputError(400, "INVALID_CURSOR", "cursor must be a non-negative integer.");
|
|
332
|
+
}
|
|
202
333
|
response = sseResponse(async (write) => {
|
|
203
|
-
await embed.attachAgentRun(agentId, runId,
|
|
334
|
+
await embed.attachAgentRun(agentId, runId, {
|
|
335
|
+
...identity,
|
|
336
|
+
...cursor === void 0 ? {} : { cursor: Number(cursor) }
|
|
337
|
+
}, (event) => write(event), {
|
|
204
338
|
idempotencyKey,
|
|
205
339
|
signal: request.signal
|
|
206
340
|
});
|
|
207
341
|
});
|
|
208
342
|
} else if (request.method === "POST" && /^\/runs\/[^/]+\/cancel$/.test(path)) {
|
|
209
343
|
const runId = routeId(path, /^\/runs\/([^/]+)\/cancel$/, "runId");
|
|
210
|
-
response = json(await embed.cancelAgentRun(agentId, runId, threadIdentity));
|
|
344
|
+
response = json(await embed.cancelAgentRun(agentId, runId, threadIdentity, { idempotencyKey }));
|
|
345
|
+
} else if (request.method === "PUT" && /^\/runs\/[^/]+\/feedback$/.test(path)) {
|
|
346
|
+
const runId = routeId(path, /^\/runs\/([^/]+)\/feedback$/, "runId");
|
|
347
|
+
const body = await requestJson(request);
|
|
348
|
+
const rating = body.rating === null ? null : stringValue(body.rating);
|
|
349
|
+
const reasons = /* @__PURE__ */ new Set(["incorrect", "irrelevant", "incomplete", "unsafe", "citation_issue", "other"]);
|
|
350
|
+
const reason = stringValue(body.reason);
|
|
351
|
+
const comment = stringValue(body.comment);
|
|
352
|
+
if (rating !== null && rating !== "like" && rating !== "dislike") {
|
|
353
|
+
throw new BffInputError(422, "INVALID_FEEDBACK", "rating must be like, dislike, or null.");
|
|
354
|
+
}
|
|
355
|
+
if (reason && !reasons.has(reason)) {
|
|
356
|
+
throw new BffInputError(422, "INVALID_FEEDBACK", "The feedback reason is invalid.");
|
|
357
|
+
}
|
|
358
|
+
if (comment && comment.length > 1e3) {
|
|
359
|
+
throw new BffInputError(422, "INVALID_FEEDBACK", "The feedback comment is too long.");
|
|
360
|
+
}
|
|
361
|
+
response = json(await embed.setAgentRunFeedback(agentId, runId, {
|
|
362
|
+
...identity,
|
|
363
|
+
rating,
|
|
364
|
+
...rating === "dislike" && reason ? { reason } : {},
|
|
365
|
+
...rating === "dislike" && comment ? { comment } : {}
|
|
366
|
+
}));
|
|
211
367
|
} else if (request.method === "GET" && path === "/threads") {
|
|
212
368
|
const skip = Number(url.searchParams.get("skip") || 0);
|
|
213
369
|
const limit = Number(url.searchParams.get("limit") || 20);
|
|
@@ -217,27 +373,29 @@ function createAgents24BffHandler(options) {
|
|
|
217
373
|
limit: Number.isFinite(limit) ? limit : 20
|
|
218
374
|
}));
|
|
219
375
|
} else if (request.method === "GET" && path === "/threads/events") {
|
|
220
|
-
const
|
|
376
|
+
const rawCursor = url.searchParams.get("cursor");
|
|
377
|
+
const cursor = rawCursor === null ? void 0 : Number(rawCursor);
|
|
221
378
|
response = sseResponse(async (write) => {
|
|
222
379
|
await embed.subscribeAgentThreadEvents(agentId, {
|
|
223
380
|
...threadIdentity,
|
|
224
|
-
...Number.isFinite(cursor) ? { cursor } : {},
|
|
381
|
+
...cursor !== void 0 && Number.isFinite(cursor) ? { cursor } : {},
|
|
225
382
|
signal: request.signal
|
|
226
383
|
}, (event) => write(event));
|
|
227
384
|
});
|
|
228
385
|
} else if (/^\/threads\/[^/]+$/.test(path) && request.method === "GET") {
|
|
229
386
|
const threadId = routeId(path, /^\/threads\/([^/]+)$/, "threadId");
|
|
230
|
-
const
|
|
231
|
-
const
|
|
387
|
+
const rawBefore = url.searchParams.get("before_turn_index");
|
|
388
|
+
const rawLimit = url.searchParams.get("limit");
|
|
389
|
+
const before = rawBefore === null ? void 0 : Number(rawBefore);
|
|
390
|
+
const limit = rawLimit === null ? void 0 : Number(rawLimit);
|
|
232
391
|
response = json(await embed.getAgentThread(agentId, threadId, {
|
|
233
392
|
...threadIdentity,
|
|
234
|
-
...Number.isFinite(before) ? { beforeTurnIndex: before } : {},
|
|
235
|
-
...Number.isFinite(limit) ? { limit } : {}
|
|
236
|
-
includeRunEvents: url.searchParams.get("include_run_events") === "true"
|
|
393
|
+
...before !== void 0 && Number.isFinite(before) ? { beforeTurnIndex: before } : {},
|
|
394
|
+
...limit !== void 0 && Number.isFinite(limit) ? { limit } : {}
|
|
237
395
|
}));
|
|
238
396
|
} else if (/^\/threads\/[^/]+$/.test(path) && request.method === "DELETE") {
|
|
239
397
|
const threadId = routeId(path, /^\/threads\/([^/]+)$/, "threadId");
|
|
240
|
-
response = json(await embed.deleteAgentThread(agentId, threadId, threadIdentity));
|
|
398
|
+
response = json(await embed.deleteAgentThread(agentId, threadId, threadIdentity, { idempotencyKey }));
|
|
241
399
|
} else if (request.method === "POST" && path === "/attachments/upload") {
|
|
242
400
|
const form = await request.formData();
|
|
243
401
|
const files = form.getAll("files").filter((entry) => entry instanceof File);
|
|
@@ -248,6 +406,18 @@ function createAgents24BffHandler(options) {
|
|
|
248
406
|
...stringValue(form.get("thread_id")) ? { threadId: stringValue(form.get("thread_id")) } : {},
|
|
249
407
|
idempotencyKey
|
|
250
408
|
}));
|
|
409
|
+
} else if (request.method === "POST" && /^\/attachments\/[^/]+\/content-access$/.test(path)) {
|
|
410
|
+
const attachmentId = routeId(path, /^\/attachments\/([^/]+)\/content-access$/, "attachmentId");
|
|
411
|
+
const body = await requestJson(request);
|
|
412
|
+
const disposition = stringValue(body.disposition);
|
|
413
|
+
if (disposition !== "inline" && disposition !== "attachment") {
|
|
414
|
+
throw new BffInputError(400, "INVALID_DISPOSITION", "Attachment disposition is invalid.");
|
|
415
|
+
}
|
|
416
|
+
response = json(await embed.createAttachmentContentAccess(agentId, attachmentId, {
|
|
417
|
+
external_user_id: threadIdentity.externalUserId,
|
|
418
|
+
...threadIdentity.externalSessionId ? { external_session_id: threadIdentity.externalSessionId } : {},
|
|
419
|
+
disposition
|
|
420
|
+
}));
|
|
251
421
|
} else if (request.method === "POST" && /^\/runs\/[^/]+\/hitl\/[^/]+\/resume$/.test(path)) {
|
|
252
422
|
const match = /^\/runs\/([^/]+)\/hitl\/([^/]+)\/resume$/.exec(path);
|
|
253
423
|
const runId = routeId(path, /^\/runs\/([^/]+)\/hitl\//, "runId");
|
|
@@ -258,7 +428,8 @@ function createAgents24BffHandler(options) {
|
|
|
258
428
|
schema_version: "agents24.hitl.resume.v2",
|
|
259
429
|
interrupt_id: normalized(interruptId, "interruptId"),
|
|
260
430
|
action: normalized(String(body.action || ""), "action"),
|
|
261
|
-
...stringValue(body.comment) ? { comment: stringValue(body.comment) } : {}
|
|
431
|
+
...stringValue(body.comment) ? { comment: stringValue(body.comment) } : {},
|
|
432
|
+
...Array.isArray(body.answers) ? { answers: body.answers } : {}
|
|
262
433
|
}, { idempotencyKey }));
|
|
263
434
|
} else if (request.method === "POST" && /^\/runs\/[^/]+\/mcp\/servers\/[^/]+\/auth\/start$/.test(path)) {
|
|
264
435
|
const match = /^\/runs\/([^/]+)\/mcp\/servers\/([^/]+)\/auth\/start$/.exec(path);
|