@agents24/node 0.5.7 → 0.5.9

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/dist/bff.js CHANGED
@@ -96,38 +96,59 @@ function publicError(status, code, message, retryable = false, violations) {
96
96
  headers: { "Cache-Control": "no-store", "Content-Type": "application/json; charset=utf-8" }
97
97
  });
98
98
  }
99
- function safeResourcePolicyError(details) {
99
+ var FAILURE_CATEGORIES = /* @__PURE__ */ new Set([
100
+ "validation",
101
+ "authentication",
102
+ "authorization",
103
+ "not_found",
104
+ "policy",
105
+ "conflict",
106
+ "billing",
107
+ "quota",
108
+ "rate_limit",
109
+ "capacity",
110
+ "dependency",
111
+ "protocol",
112
+ "timeout",
113
+ "internal"
114
+ ]);
115
+ var SAFE_VIOLATION_KEYS = /* @__PURE__ */ new Set([
116
+ "field",
117
+ "code",
118
+ "message",
119
+ "policy_subject_type",
120
+ "rule_type",
121
+ "aggregation_scope",
122
+ "resource_type",
123
+ "resource_name",
124
+ "quota_unit",
125
+ "quota_window",
126
+ "limit",
127
+ "remaining",
128
+ "resets_at"
129
+ ]);
130
+ function safeFailure(details) {
100
131
  if (!details || typeof details !== "object" || Array.isArray(details)) return null;
101
132
  const outer = details;
102
133
  const candidate = outer.schema_version === "agents24.failure.v1" ? outer : outer.detail && typeof outer.detail === "object" && !Array.isArray(outer.detail) ? outer.detail : outer;
103
- const code = typeof candidate.code === "string" ? candidate.code : "";
104
- if (!["RESOURCE_POLICY_ACCESS_DENIED", "RESOURCE_POLICY_QUOTA_EXCEEDED", "RESOURCE_POLICY_CONFIGURATION_ERROR"].includes(code)) return null;
134
+ if (candidate.schema_version !== "agents24.failure.v1") return null;
135
+ const code = typeof candidate.code === "string" && /^[A-Z0-9_]{2,128}$/.test(candidate.code) ? candidate.code : "";
136
+ const category = typeof candidate.category === "string" && FAILURE_CATEGORIES.has(candidate.category) ? candidate.category : "";
137
+ if (!code || !category) return null;
105
138
  const failureId = typeof candidate.failure_id === "string" && candidate.failure_id.trim() ? candidate.failure_id : "";
106
139
  if (!failureId) return null;
107
- const message = typeof candidate.message === "string" && candidate.message.trim() ? candidate.message.trim() : "The resource policy prevented this request.";
108
- const allowedViolationKeys = /* @__PURE__ */ new Set([
109
- "policy_subject_type",
110
- "rule_type",
111
- "aggregation_scope",
112
- "resource_type",
113
- "resource_name",
114
- "quota_unit",
115
- "quota_window",
116
- "limit",
117
- "remaining",
118
- "resets_at"
119
- ]);
140
+ const message = typeof candidate.message === "string" && candidate.message.trim() && candidate.message.length <= 1e3 ? candidate.message.trim() : "The Agent request failed.";
120
141
  const candidateDetails = candidate.details && typeof candidate.details === "object" ? candidate.details : candidate;
121
142
  const violations = Array.isArray(candidateDetails.violations) ? candidateDetails.violations.flatMap((item) => {
122
143
  if (!item || typeof item !== "object" || Array.isArray(item)) return [];
123
144
  return [Object.fromEntries(
124
- Object.entries(item).filter(([key, value]) => allowedViolationKeys.has(key) && ["string", "number"].includes(typeof value))
145
+ Object.entries(item).filter(([key, value]) => SAFE_VIOLATION_KEYS.has(key) && ["string", "number", "boolean"].includes(typeof value))
125
146
  )];
126
147
  }).filter((item) => Object.keys(item).length > 0).slice(0, 20) : void 0;
127
148
  return {
128
149
  failureId,
129
150
  code,
130
- category: code === "RESOURCE_POLICY_QUOTA_EXCEEDED" ? "quota" : "policy",
151
+ category,
131
152
  message,
132
153
  requestId: typeof candidate.request_id === "string" ? candidate.request_id : null,
133
154
  retryable: candidate.retryable === true,
@@ -135,7 +156,8 @@ function safeResourcePolicyError(details) {
135
156
  ...violations?.length ? { violations } : {}
136
157
  };
137
158
  }
138
- function resourcePolicyFailureResponse(status, failure) {
159
+ function upstreamFailureResponse(status, failure) {
160
+ const candidateDetails = failure.violations?.length ? { violations: failure.violations } : {};
139
161
  return Response.json({
140
162
  schema_version: "agents24.failure.v1",
141
163
  failure_id: failure.failureId,
@@ -145,7 +167,7 @@ function resourcePolicyFailureResponse(status, failure) {
145
167
  retryable: failure.retryable,
146
168
  request_id: failure.requestId,
147
169
  retry_after_ms: failure.retryAfterMs,
148
- details: failure.violations?.length ? { violations: failure.violations } : {}
170
+ details: candidateDetails
149
171
  }, {
150
172
  status,
151
173
  headers: { "Cache-Control": "no-store", "Content-Type": "application/json; charset=utf-8" }
@@ -156,13 +178,41 @@ function errorResponse(error) {
156
178
  if (isSdkError) {
157
179
  const sdkError = error;
158
180
  const status = sdkError.status && sdkError.status >= 400 && sdkError.status <= 599 ? sdkError.status : 502;
159
- const policyError = safeResourcePolicyError(sdkError.failure);
160
- if (policyError) return resourcePolicyFailureResponse(status, policyError);
181
+ const upstreamFailure = safeFailure(sdkError.failure);
182
+ if (upstreamFailure) return upstreamFailureResponse(status, upstreamFailure);
161
183
  return publicError(status, `UPSTREAM_${status}`, "The Agent request failed.", status >= 500);
162
184
  }
163
185
  return publicError(500, "BFF_REQUEST_FAILED", "The Agent request could not be completed.", true);
164
186
  }
165
187
 
188
+ // src/bff-diagnostics.ts
189
+ function emitDiagnostic(callback, diagnostic) {
190
+ void Promise.resolve(callback(diagnostic)).catch(() => void 0);
191
+ }
192
+ function reportFailureResponse(callback, request, operation, response) {
193
+ if (!callback || response.status < 400) return response;
194
+ void response.clone().json().then((payload) => {
195
+ const row = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
196
+ const clean2 = (value, pattern) => typeof value === "string" && pattern.test(value) ? value : void 0;
197
+ emitDiagnostic(callback, {
198
+ operation,
199
+ method: request.method,
200
+ path: new URL(request.url).pathname,
201
+ status: response.status,
202
+ ...clean2(row.failure_id, /^[A-Za-z0-9_-]{1,128}$/) ? { failureId: String(row.failure_id) } : {},
203
+ ...clean2(row.request_id, /^[A-Za-z0-9_.:-]{1,128}$/) ? { requestId: String(row.request_id) } : {},
204
+ ...clean2(row.code, /^[A-Z0-9_]{2,128}$/) ? { code: String(row.code) } : {},
205
+ ...clean2(row.category, /^[a-z_]{2,64}$/) ? { category: String(row.category) } : {}
206
+ });
207
+ }).catch(() => emitDiagnostic(callback, {
208
+ operation,
209
+ method: request.method,
210
+ path: new URL(request.url).pathname,
211
+ status: response.status
212
+ }));
213
+ return response;
214
+ }
215
+
166
216
  // src/bff.ts
167
217
  var JSON_LIMIT_BYTES = 1048576;
168
218
  var MAX_INPUT_LENGTH = 2e5;
@@ -287,7 +337,7 @@ function sseHeaders(headers) {
287
337
  result.set("X-Accel-Buffering", "no");
288
338
  return result;
289
339
  }
290
- function sseResponse(execute, headers, failurePayload) {
340
+ function sseResponse(execute, headers, failurePayload, onFailure) {
291
341
  const stream = new TransformStream();
292
342
  const writer = stream.writable.getWriter();
293
343
  const encoder = new TextEncoder();
@@ -295,6 +345,7 @@ function sseResponse(execute, headers, failurePayload) {
295
345
 
296
346
  `));
297
347
  void execute(write).catch(async (error) => {
348
+ onFailure?.(error);
298
349
  if (failurePayload) {
299
350
  await write(failurePayload(error)).catch(() => void 0);
300
351
  return;
@@ -342,6 +393,9 @@ function createAgents24BffHandlerWithResolver(options, resolveCustomerIdentity)
342
393
  if (runtimeTarget === "draft" && agentVersionId) throw new Error("agentVersionId cannot be used with draft runtimeTarget.");
343
394
  const basePath = normalizeBasePath(options.basePath);
344
395
  const embed = options.client.embed;
396
+ const streamFailure = (request, operation) => (error) => {
397
+ reportFailureResponse(options.onDiagnostic, request, operation, errorResponse(error));
398
+ };
345
399
  return async (request) => {
346
400
  const url = new URL(request.url);
347
401
  if (url.pathname !== basePath && !url.pathname.startsWith(`${basePath}/`)) {
@@ -410,7 +464,7 @@ function createAgents24BffHandlerWithResolver(options, resolveCustomerIdentity)
410
464
  metadata: objectValue(body.metadata),
411
465
  client: objectValue(body.client)
412
466
  }, (event) => write(event), { idempotencyKey, signal: request.signal });
413
- });
467
+ }, void 0, void 0, streamFailure(request, "chat.stream"));
414
468
  } else if (request.method === "POST" && /^\/runs\/[^/]+\/attach$/.test(path)) {
415
469
  const runId = routeId(path, /^\/runs\/([^/]+)\/attach$/, "runId");
416
470
  const body = await requestJson(request);
@@ -428,7 +482,7 @@ function createAgents24BffHandlerWithResolver(options, resolveCustomerIdentity)
428
482
  idempotencyKey,
429
483
  signal: request.signal
430
484
  });
431
- });
485
+ }, void 0, void 0, streamFailure(request, "run.attach"));
432
486
  } else if (request.method === "POST" && /^\/runs\/[^/]+\/cancel$/.test(path)) {
433
487
  const runId = routeId(path, /^\/runs\/([^/]+)\/cancel$/, "runId");
434
488
  response = json(await embed.cancelAgentRun(agentId, runId, {
@@ -490,7 +544,7 @@ function createAgents24BffHandlerWithResolver(options, resolveCustomerIdentity)
490
544
  event: "snapshot_required",
491
545
  cursor,
492
546
  reason: "stream_failed"
493
- }));
547
+ }), streamFailure(request, "threads.events"));
494
548
  } else if (/^\/threads\/[^/]+$/.test(path) && request.method === "GET") {
495
549
  const threadId = routeId(path, /^\/threads\/([^/]+)$/, "threadId");
496
550
  const rawBefore = url.searchParams.get("before_turn_index");
@@ -587,9 +641,9 @@ function createAgents24BffHandlerWithResolver(options, resolveCustomerIdentity)
587
641
  return mergeHeaders(response, resolution.responseHeaders);
588
642
  } catch (error) {
589
643
  if (error instanceof BffInputError) {
590
- return mergeHeaders(publicError(error.status, error.code, error.message), resolution.responseHeaders);
644
+ return mergeHeaders(reportFailureResponse(options.onDiagnostic, request, "request", publicError(error.status, error.code, error.message)), resolution.responseHeaders);
591
645
  }
592
- return mergeHeaders(errorResponse(error), resolution.responseHeaders);
646
+ return mergeHeaders(reportFailureResponse(options.onDiagnostic, request, "request", errorResponse(error)), resolution.responseHeaders);
593
647
  }
594
648
  };
595
649
  }
@@ -623,7 +677,8 @@ function createResolvedAgents24BffHandler(options) {
623
677
  integrationName: options.integrationName,
624
678
  basePath: `${basePath}/agents/${encodeURIComponent(agent.agentId)}`,
625
679
  resolvePrincipal: options.resolvePrincipal,
626
- revokePrincipal: options.revokePrincipal
680
+ revokePrincipal: options.revokePrincipal,
681
+ onDiagnostic: options.onDiagnostic
627
682
  }, resolveCustomerIdentity)
628
683
  ]));
629
684
  const agentBySelector = /* @__PURE__ */ new Map();
@@ -746,7 +801,7 @@ function createResolvedAgents24BffHandler(options) {
746
801
  metadata: objectValue(body.metadata),
747
802
  client: objectValue(body.client)
748
803
  }, (event) => write(event), { idempotencyKey: stringValue(request.headers.get("idempotency-key")), signal: request.signal });
749
- }), resolution.responseHeaders);
804
+ }, void 0, void 0, (error) => reportFailureResponse(options.onDiagnostic, request, "chat.stream", errorResponse(error))), resolution.responseHeaders);
750
805
  }
751
806
  if (request.method === "GET" && path === "/threads") {
752
807
  const skip = Number(url.searchParams.get("skip") || 0);
@@ -785,7 +840,7 @@ function createResolvedAgents24BffHandler(options) {
785
840
  event: "snapshot_required",
786
841
  cursor,
787
842
  reason: "stream_failed"
788
- })), resolution.responseHeaders);
843
+ }), (error) => reportFailureResponse(options.onDiagnostic, request, "threads.events", errorResponse(error))), resolution.responseHeaders);
789
844
  }
790
845
  if (/^\/threads\/[^/]+$/.test(path) && request.method === "GET") {
791
846
  const threadId = routeId(path, /^\/threads\/([^/]+)$/, "threadId");
@@ -860,8 +915,8 @@ function createResolvedAgents24BffHandler(options) {
860
915
  }
861
916
  return mergeHeaders(publicError(404, "NOT_FOUND", "The requested BFF route does not exist."), resolution.responseHeaders);
862
917
  } 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);
918
+ if (error instanceof BffInputError) return mergeHeaders(reportFailureResponse(options.onDiagnostic, request, "request", publicError(error.status, error.code, error.message)), resolution.responseHeaders);
919
+ return mergeHeaders(reportFailureResponse(options.onDiagnostic, request, "request", errorResponse(error)), resolution.responseHeaders);
865
920
  }
866
921
  };
867
922
  }
@@ -881,7 +936,7 @@ function createAgents24BffHandler(options) {
881
936
  return (await resolvedHandler)(request);
882
937
  } catch (error) {
883
938
  resolvedHandler = void 0;
884
- return errorResponse(error);
939
+ return reportFailureResponse(options.onDiagnostic, request, "agent.resolve", errorResponse(error));
885
940
  }
886
941
  };
887
942
  }