@astrasyncai/verification-gateway 5.11.0 → 5.13.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.
Files changed (98) hide show
  1. package/README.md +4 -0
  2. package/dist/adapters/express.d.mts +1 -1
  3. package/dist/adapters/express.d.ts +1 -1
  4. package/dist/adapters/express.js +394 -39
  5. package/dist/adapters/express.js.map +1 -1
  6. package/dist/adapters/express.mjs +394 -39
  7. package/dist/adapters/express.mjs.map +1 -1
  8. package/dist/adapters/mcp.d.mts +5 -1
  9. package/dist/adapters/mcp.d.ts +5 -1
  10. package/dist/adapters/mcp.js +193 -49
  11. package/dist/adapters/mcp.js.map +1 -1
  12. package/dist/adapters/mcp.mjs +193 -49
  13. package/dist/adapters/mcp.mjs.map +1 -1
  14. package/dist/adapters/nextjs.d.mts +1 -1
  15. package/dist/adapters/nextjs.d.ts +1 -1
  16. package/dist/adapters/nextjs.js +387 -22
  17. package/dist/adapters/nextjs.js.map +1 -1
  18. package/dist/adapters/nextjs.mjs +387 -22
  19. package/dist/adapters/nextjs.mjs.map +1 -1
  20. package/dist/adapters/sdk.d.mts +9 -1
  21. package/dist/adapters/sdk.d.ts +9 -1
  22. package/dist/adapters/sdk.js +125 -16
  23. package/dist/adapters/sdk.js.map +1 -1
  24. package/dist/adapters/sdk.mjs +125 -16
  25. package/dist/adapters/sdk.mjs.map +1 -1
  26. package/dist/agent/index.js +1 -1
  27. package/dist/agent/index.js.map +1 -1
  28. package/dist/agent/index.mjs +1 -1
  29. package/dist/agent/index.mjs.map +1 -1
  30. package/dist/bin/astrasync-claude-hook.js +309 -15
  31. package/dist/bin/astrasync-codex-hook.js +309 -15
  32. package/dist/bin/astrasync-guard.js +309 -15
  33. package/dist/bin/astrasync.js +322 -17
  34. package/dist/browser/background.js +309 -15
  35. package/dist/browser/background.js.map +1 -1
  36. package/dist/browser/background.mjs +309 -15
  37. package/dist/browser/background.mjs.map +1 -1
  38. package/dist/cli/index.js +1 -1
  39. package/dist/cli/index.js.map +1 -1
  40. package/dist/cli/index.mjs +1 -1
  41. package/dist/cli/index.mjs.map +1 -1
  42. package/dist/codex/index.js +309 -15
  43. package/dist/codex/index.js.map +1 -1
  44. package/dist/codex/index.mjs +309 -15
  45. package/dist/codex/index.mjs.map +1 -1
  46. package/dist/cursor/extension.js +309 -15
  47. package/dist/cursor/extension.js.map +1 -1
  48. package/dist/cursor/extension.mjs +309 -15
  49. package/dist/cursor/extension.mjs.map +1 -1
  50. package/dist/edge-config.d.mts +1 -1
  51. package/dist/edge-config.d.ts +1 -1
  52. package/dist/edge-config.js +1 -1
  53. package/dist/edge-config.js.map +1 -1
  54. package/dist/edge-config.mjs +1 -1
  55. package/dist/edge-config.mjs.map +1 -1
  56. package/dist/edge-core/index.d.mts +128 -13
  57. package/dist/edge-core/index.d.ts +128 -13
  58. package/dist/edge-core/index.js +362 -63
  59. package/dist/edge-core/index.js.map +1 -1
  60. package/dist/edge-core/index.mjs +359 -63
  61. package/dist/edge-core/index.mjs.map +1 -1
  62. package/dist/gateway/gateway.js +309 -15
  63. package/dist/gateway/gateway.js.map +1 -1
  64. package/dist/gateway/gateway.mjs +309 -15
  65. package/dist/gateway/gateway.mjs.map +1 -1
  66. package/dist/git-trigger/git-hooks.d.mts +1 -1
  67. package/dist/git-trigger/git-hooks.d.ts +1 -1
  68. package/dist/index.d.mts +118 -15
  69. package/dist/index.d.ts +118 -15
  70. package/dist/index.js +521 -72
  71. package/dist/index.js.map +1 -1
  72. package/dist/index.mjs +515 -72
  73. package/dist/index.mjs.map +1 -1
  74. package/dist/platform-signatures.js +4 -1
  75. package/dist/platform-signatures.js.map +1 -1
  76. package/dist/platform-signatures.mjs +4 -1
  77. package/dist/platform-signatures.mjs.map +1 -1
  78. package/dist/registration/index.js +1 -1
  79. package/dist/registration/index.js.map +1 -1
  80. package/dist/registration/index.mjs +1 -1
  81. package/dist/registration/index.mjs.map +1 -1
  82. package/dist/transport/index.js +1 -1
  83. package/dist/transport/index.js.map +1 -1
  84. package/dist/transport/index.mjs +1 -1
  85. package/dist/transport/index.mjs.map +1 -1
  86. package/dist/{types-CTmUpNpS.d.mts → types-BCkwBA84.d.mts} +12 -0
  87. package/dist/{types-CTmUpNpS.d.ts → types-BCkwBA84.d.ts} +12 -0
  88. package/dist/{types-CtFXmDzm.d.ts → types-C_b4QTyO.d.ts} +18 -0
  89. package/dist/{types-DQrn_kei.d.mts → types-DlshIIR-.d.mts} +18 -0
  90. package/dist/ui/index.d.mts +1 -1
  91. package/dist/ui/index.d.ts +1 -1
  92. package/dist/verify.d.mts +35 -14
  93. package/dist/verify.d.ts +35 -14
  94. package/dist/verify.js +165 -25
  95. package/dist/verify.js.map +1 -1
  96. package/dist/verify.mjs +164 -25
  97. package/dist/verify.mjs.map +1 -1
  98. package/package.json +3 -2
package/dist/index.mjs CHANGED
@@ -19,7 +19,7 @@ function getTrustLevel(score) {
19
19
  }
20
20
 
21
21
  // src/version.ts
22
- var SDK_VERSION = "5.11.0";
22
+ var SDK_VERSION = "5.13.0";
23
23
 
24
24
  // src/http.ts
25
25
  var SDK_USER_AGENT = `astrasync-sdk/${SDK_VERSION}`;
@@ -98,6 +98,59 @@ function getCachedWellKnownUrls(apiBaseUrl) {
98
98
  return cache.get(apiBaseUrl)?.data;
99
99
  }
100
100
 
101
+ // src/failure-posture.ts
102
+ var VERIFICATION_STATUS_HEADER = "X-Astra-Verification";
103
+ var DEFAULT_UNAVAILABLE_RETRY_AFTER_S = 30;
104
+ var REJECTED_IDENTITY_DIMENSIONS = /* @__PURE__ */ new Set([
105
+ "identity.invalid_agent_id",
106
+ "identity.unknown_agent",
107
+ "agent.lookup"
108
+ ]);
109
+ var UNKNOWN_AGENT_GUIDANCE = "Register your agent with AstraSync (or, inside an MCP host, add the AstraSync MCP connector for keyless, human-approved registration), then present the canonical ASTRA-* id issued after owner approval.";
110
+ var UNAVAILABLE_DIMENSIONS = /* @__PURE__ */ new Set([
111
+ "verify_access.api_error",
112
+ "middleware.internal_error"
113
+ ]);
114
+ function isVerificationUnavailable(result) {
115
+ if (result.misconfigured) return false;
116
+ return (result.failures ?? []).some((f) => UNAVAILABLE_DIMENSIONS.has(f.dimension));
117
+ }
118
+ function unavailableRetryAfterSeconds(result) {
119
+ if (typeof result.retryAfterMs === "number" && Number.isFinite(result.retryAfterMs)) {
120
+ return Math.max(1, Math.ceil(result.retryAfterMs / 1e3));
121
+ }
122
+ return DEFAULT_UNAVAILABLE_RETRY_AFTER_S;
123
+ }
124
+ function isAnonymousRead(method, credentials) {
125
+ const m = (method ?? "").toUpperCase();
126
+ if (m !== "GET" && m !== "HEAD") return false;
127
+ return !credentials.astraId && !credentials.jwt;
128
+ }
129
+ function buildVerificationUnavailableBody(result) {
130
+ return {
131
+ success: false,
132
+ error: {
133
+ code: "VERIFICATION_UNAVAILABLE",
134
+ message: "Agent verification is temporarily unavailable and this path enforces verification. Your identity was not evaluated and no denial was recorded \u2014 retry after the Retry-After interval.",
135
+ retryable: true,
136
+ retryAfterSeconds: unavailableRetryAfterSeconds(result),
137
+ ...result.failures && { failures: result.failures },
138
+ ...result.correlationId && { correlationId: result.correlationId },
139
+ guidance: {
140
+ message: "Retry the request after the Retry-After interval, with exponential backoff; the condition is transient."
141
+ }
142
+ }
143
+ };
144
+ }
145
+ function isRejectedIdentityClaim(result) {
146
+ return (result.failures ?? []).some((f) => REJECTED_IDENTITY_DIMENSIONS.has(f.dimension));
147
+ }
148
+ function denyStatusFor(result, policyDeniedCode) {
149
+ if (isRejectedIdentityClaim(result)) return { status: 403, code: "AGENT_IDENTITY_REJECTED" };
150
+ if (result.identityVerified) return { status: 403, code: policyDeniedCode };
151
+ return { status: 401, code: "UNAUTHORIZED" };
152
+ }
153
+
101
154
  // src/verify.ts
102
155
  var DEFAULT_CONFIG = {
103
156
  apiBaseUrl: "https://astrasync.ai/api",
@@ -110,7 +163,7 @@ var DEFAULT_CONFIG = {
110
163
  };
111
164
  var initCheckPerformed = false;
112
165
  var deprecationWarningShown = false;
113
- async function performInitCheck(apiBaseUrl, debug, strictInit, apiKey) {
166
+ async function performInitCheck(apiBaseUrl, debug, strictInit, apiKey, counterparty) {
114
167
  initCheckPerformed = true;
115
168
  try {
116
169
  const probeUrl = `${apiBaseUrl}/agents/verify-access`;
@@ -128,7 +181,8 @@ async function performInitCheck(apiBaseUrl, debug, strictInit, apiKey) {
128
181
  );
129
182
  }
130
183
  if (apiKey) {
131
- const selfCheck = await sdkFetch(`${apiBaseUrl}/agents/verify-access/self-check`, {
184
+ const q = counterparty?.id ? `?counterpartyId=${encodeURIComponent(counterparty.id)}` : counterparty?.url ? `?counterpartyUrl=${encodeURIComponent(counterparty.url)}` : "";
185
+ const selfCheck = await sdkFetch(`${apiBaseUrl}/agents/verify-access/self-check${q}`, {
132
186
  headers: { Authorization: `Bearer ${apiKey}` }
133
187
  });
134
188
  if (selfCheck.status === 401) {
@@ -140,6 +194,13 @@ async function performInitCheck(apiBaseUrl, debug, strictInit, apiKey) {
140
194
  console.error(message);
141
195
  } else if (selfCheck.ok) {
142
196
  const body = await selfCheck.json().catch(() => ({}));
197
+ if (body.counterparty && body.counterparty.canVerify === false) {
198
+ const message = `[VerificationGateway] the configured AstraSync API key cannot verify for this endpoint (${body.counterparty.message ?? "endpoint not found or owned by another account"}). Every inbound agent will be turned away until the key or counterparty is corrected.`;
199
+ if (strictInit) {
200
+ throw new Error(`${message} (strictInit=true)`);
201
+ }
202
+ console.error(message);
203
+ }
143
204
  if (typeof body.daysToExpiry === "number" && body.daysToExpiry <= 7) {
144
205
  console.warn(
145
206
  `[VerificationGateway] the configured AstraSync API key expires in ${body.daysToExpiry} day(s) \u2014 rotate it at Settings \u2192 Developer Tools before integrations start failing.`
@@ -284,11 +345,11 @@ function createGuidanceResponse(_config, reason, options = {}) {
284
345
  const isMisconfig = source === "merchant_misconfig";
285
346
  const urls = options.urls;
286
347
  const guidance = isMisconfig ? {
287
- message: "This site's AstraSync integration is misconfigured (its API key was revoked, expired, or rotated). Your identity and the AstraSync platform are fine \u2014 contact the site operator. Retrying will not help.",
348
+ message: "This site's AstraSync integration is misconfigured (its API key was revoked, expired, rotated, or belongs to another account). Your identity and the AstraSync platform are fine \u2014 contact the site operator. Retrying will not help.",
288
349
  registrationUrl: urls?.registrationUrl ?? "",
289
350
  documentationUrl: options.remediation?.docsUrl ?? urls?.documentationUrl ?? "",
290
351
  steps: [
291
- "Report the failure to the site operator \u2014 their AstraSync API key needs rotating",
352
+ "Report the failure to the site operator \u2014 their AstraSync API key needs replacing",
292
353
  "Do not retry: the failure is deterministic until the operator updates their key"
293
354
  ]
294
355
  } : isApiError ? {
@@ -349,11 +410,44 @@ function createGuidanceResponse(_config, reason, options = {}) {
349
410
  // coercing the absent recommendation to a policy 'deny'. Misconfig is
350
411
  // deterministic and intentionally NOT marked retryable.
351
412
  ...isApiError && { retryable: true },
413
+ ...options.httpStatus !== void 0 && { httpStatus: options.httpStatus },
414
+ ...options.retryAfterMs !== void 0 && { retryAfterMs: options.retryAfterMs },
352
415
  correlationId: options.correlationId,
353
416
  attemptId: options.attemptId,
354
417
  verifiedAt: /* @__PURE__ */ new Date()
355
418
  };
356
419
  }
420
+ async function readJsonSafe(response) {
421
+ try {
422
+ const parsed = await response.json();
423
+ return parsed && typeof parsed === "object" ? parsed : {};
424
+ } catch {
425
+ return {};
426
+ }
427
+ }
428
+ function rejectedIdentityGuidance(failures) {
429
+ const f = failures?.find((x) => REJECTED_IDENTITY_DIMENSIONS.has(x.dimension));
430
+ if (!f) return void 0;
431
+ if (f.dimension === "agent.lookup") {
432
+ return `The presented agent identifier is not registered with AstraSync. ${UNKNOWN_AGENT_GUIDANCE}`;
433
+ }
434
+ return f.guidance ? `${f.message} ${f.guidance}` : f.message;
435
+ }
436
+ var MAX_RETRY_AFTER_MS = 15 * 60 * 1e3;
437
+ function parseRetryAfterMs(response) {
438
+ const raw = response.headers?.get?.("retry-after");
439
+ if (!raw) return void 0;
440
+ const trimmed = raw.trim();
441
+ let ms;
442
+ if (/^\d+(\.\d+)?$/.test(trimmed)) {
443
+ ms = Number(trimmed) * 1e3;
444
+ } else {
445
+ const at = Date.parse(trimmed);
446
+ if (Number.isNaN(at)) return void 0;
447
+ ms = at - Date.now();
448
+ }
449
+ return Math.min(MAX_RETRY_AFTER_MS, Math.max(0, Math.round(ms)));
450
+ }
357
451
  async function callVerifyAccessAPI(config, request) {
358
452
  const { credentials, ...requestData } = request;
359
453
  const body = {
@@ -410,7 +504,8 @@ async function callVerifyAccessAPI(config, request) {
410
504
  body: JSON.stringify(body),
411
505
  signal: AbortSignal.timeout(config.verifyTimeoutMs ?? 1e4)
412
506
  });
413
- const data = await response.json();
507
+ const data = await readJsonSafe(response);
508
+ const retryAfterMs = parseRetryAfterMs(response);
414
509
  if (response.status === 410) {
415
510
  return {
416
511
  success: true,
@@ -448,13 +543,31 @@ async function callVerifyAccessAPI(config, request) {
448
543
  }
449
544
  };
450
545
  }
546
+ if (response.status === 404 && credentials.astraId && (data?.error === "AGENT_NOT_FOUND" || typeof data?.message === "string" && /agent/i.test(data.message))) {
547
+ return {
548
+ success: true,
549
+ attemptId: typeof data?.attemptId === "string" ? data.attemptId : void 0,
550
+ access: {
551
+ allowed: false,
552
+ reason: "unknown_agent",
553
+ failures: [
554
+ {
555
+ dimension: "identity.unknown_agent",
556
+ message: "The presented agent identifier is not registered with AstraSync.",
557
+ guidance: UNKNOWN_AGENT_GUIDANCE
558
+ }
559
+ ]
560
+ }
561
+ };
562
+ }
451
563
  if (!response.ok) {
452
- if (response.status === 401) {
564
+ const keyMismatch = response.status === 403 && data?.error === "COUNTERPARTY_KEY_MISMATCH";
565
+ if (response.status === 401 || keyMismatch) {
453
566
  return {
454
567
  success: false,
455
568
  error: data.message || data.error || "Integration API key rejected",
456
569
  misconfig: true,
457
- misconfigCode: typeof data?.code === "string" ? data.code : void 0,
570
+ misconfigCode: keyMismatch ? "counterparty_key_mismatch" : typeof data?.code === "string" ? data.code : void 0,
458
571
  remediation: data?.remediation && typeof data.remediation === "object" ? data.remediation : void 0,
459
572
  correlationId: typeof data?.correlationId === "string" ? data.correlationId : void 0,
460
573
  attemptId: typeof data?.attemptId === "string" ? data.attemptId : void 0
@@ -462,9 +575,11 @@ async function callVerifyAccessAPI(config, request) {
462
575
  }
463
576
  return {
464
577
  success: false,
465
- error: data.message || data.error || `API returned ${response.status}`,
578
+ error: typeof data?.message === "string" && data.message || typeof data?.error === "string" && data.error || `API returned ${response.status}`,
466
579
  correlationId: typeof data?.correlationId === "string" ? data.correlationId : void 0,
467
- attemptId: typeof data?.attemptId === "string" ? data.attemptId : void 0
580
+ attemptId: typeof data?.attemptId === "string" ? data.attemptId : void 0,
581
+ httpStatus: response.status,
582
+ ...retryAfterMs !== void 0 && { retryAfterMs }
468
583
  };
469
584
  }
470
585
  return data;
@@ -485,14 +600,22 @@ async function verify(config, request, options) {
485
600
  mergedConfig.apiBaseUrl,
486
601
  mergedConfig.debug,
487
602
  true,
488
- mergedConfig.apiKey
603
+ mergedConfig.apiKey,
604
+ {
605
+ id: mergedConfig.counterpartyId,
606
+ url: mergedConfig.counterpartyUrl ?? request.counterpartyUrl
607
+ }
489
608
  );
490
609
  } else {
491
610
  void performInitCheck(
492
611
  mergedConfig.apiBaseUrl,
493
612
  mergedConfig.debug,
494
613
  false,
495
- mergedConfig.apiKey
614
+ mergedConfig.apiKey,
615
+ {
616
+ id: mergedConfig.counterpartyId,
617
+ url: mergedConfig.counterpartyUrl ?? request.counterpartyUrl
618
+ }
496
619
  );
497
620
  }
498
621
  }
@@ -536,12 +659,14 @@ async function verify(config, request, options) {
536
659
  correlationId: apiResponse.correlationId,
537
660
  attemptId: apiResponse.attemptId,
538
661
  remediation: apiResponse.remediation,
662
+ httpStatus: apiResponse.httpStatus,
663
+ retryAfterMs: apiResponse.retryAfterMs,
539
664
  urls
540
665
  });
541
666
  }
542
667
  if (!apiResponse.access?.allowed) {
543
668
  const aggregatedFailures = apiResponse.access?.failures;
544
- const idVerifiedFromBackend = apiResponse.verificationContext?.idVerified === true;
669
+ const idVerifiedFromBackend = apiResponse.verificationContext?.idVerified === true && !aggregatedFailures?.some((f) => REJECTED_IDENTITY_DIMENSIONS.has(f.dimension));
545
670
  const result2 = {
546
671
  identityVerified: idVerifiedFromBackend,
547
672
  policyAllowed: false,
@@ -553,7 +678,10 @@ async function verify(config, request, options) {
553
678
  requiresStepUp: apiResponse.access?.requiresStepUp,
554
679
  requiresApproval: apiResponse.access?.requiresApproval,
555
680
  guidance: {
556
- message: apiResponse.access?.reason || "Access denied by PDLSS policy",
681
+ // 5.12.0: a presented-but-rejected id (malformed / unknown) carries
682
+ // actionable registration guidance on its failure — surface it
683
+ // instead of the bare machine reason ('unknown_agent').
684
+ message: rejectedIdentityGuidance(aggregatedFailures) ?? (apiResponse.access?.reason || "Access denied by PDLSS policy"),
557
685
  registrationUrl: urls?.registrationUrl ?? "",
558
686
  documentationUrl: urls?.documentationUrl ?? "",
559
687
  // 5.1.0: anonymous callers land on this branch — steer MCP-hosted
@@ -653,7 +781,7 @@ async function verify(config, request, options) {
653
781
  registrationUrl: urls?.registrationUrl ?? "",
654
782
  documentationUrl: urls?.documentationUrl ?? ""
655
783
  };
656
- } else if (result.recommendation === "step_up_required") {
784
+ } else if (result.recommendation === "step_up_required" && result.identityVerified) {
657
785
  result.requiresStepUp = true;
658
786
  result.denialReasons = serverDenialReasons ?? result.recommendationReasons ?? ["Step-up verification required"];
659
787
  }
@@ -676,7 +804,7 @@ async function recordDecision(config, sessionId, decision, reason) {
676
804
  }).catch(() => {
677
805
  });
678
806
  }
679
- async function reportAttempt(config, report) {
807
+ async function reportAttempt(config, report, options = {}) {
680
808
  const apiBaseUrl = config.apiBaseUrl || DEFAULT_CONFIG.apiBaseUrl;
681
809
  const headers = { "Content-Type": "application/json" };
682
810
  if (config.apiKey) {
@@ -687,13 +815,38 @@ async function reportAttempt(config, report) {
687
815
  ...report,
688
816
  ...!report.counterpartyId && config.counterpartyId && { counterpartyId: config.counterpartyId }
689
817
  };
690
- await sdkFetch(`${apiBaseUrl}/agents/verify-access/attempt-report`, {
691
- method: "POST",
692
- headers,
693
- body: JSON.stringify(body)
694
- }).catch(() => {
695
- });
818
+ const url = `${apiBaseUrl}/agents/verify-access/attempt-report`;
819
+ const init = { method: "POST", headers, body: JSON.stringify(body) };
820
+ const fail = (failure) => {
821
+ try {
822
+ options.onFailure?.(failure);
823
+ } catch {
824
+ }
825
+ };
826
+ let response;
827
+ try {
828
+ response = await sdkFetch(url, init);
829
+ } catch (err) {
830
+ fail({ error: err instanceof Error ? err.message : String(err), retried: false });
831
+ return;
832
+ }
833
+ if (response.ok) return;
834
+ if (response.status !== 429 && response.status < 500) {
835
+ fail({ status: response.status, retried: false });
836
+ return;
837
+ }
838
+ const delayMs = Math.min(ATTEMPT_REPORT_MAX_RETRY_DELAY_MS, parseRetryAfterMs(response) ?? 500);
839
+ const timer = setTimeout(() => {
840
+ sdkFetch(url, init).then((retry) => {
841
+ if (!retry.ok) fail({ status: retry.status, retried: true });
842
+ }).catch((err) => {
843
+ fail({ error: err instanceof Error ? err.message : String(err), retried: true });
844
+ });
845
+ }, delayMs);
846
+ timer.unref?.();
696
847
  }
848
+ var ATTEMPT_REPORT_MAX_RETRY_DELAY_MS = 2e3;
849
+ var refusedPolicyReads = /* @__PURE__ */ new Set();
697
850
  async function fetchRoutes(config, counterpartyId) {
698
851
  if (!counterpartyId) return null;
699
852
  const headers = { "Content-Type": "application/json" };
@@ -706,7 +859,15 @@ async function fetchRoutes(config, counterpartyId) {
706
859
  `${config.apiBaseUrl}/endpoints/${encodeURIComponent(counterpartyId)}/routes`,
707
860
  { method: "GET", headers }
708
861
  );
709
- if (!response.ok) return null;
862
+ if (!response.ok) {
863
+ if ((response.status === 401 || response.status === 403 || response.status === 404) && !refusedPolicyReads.has(counterpartyId)) {
864
+ refusedPolicyReads.add(counterpartyId);
865
+ console.error(
866
+ `[VerificationGateway] the configured API key cannot read the route policy for ${counterpartyId} (HTTP ${response.status}). The key belongs to an account that does not own this endpoint, or the counterpartyId is wrong. The gateway is in PASS-THROUGH mode: no agent is being verified.`
867
+ );
868
+ }
869
+ return null;
870
+ }
710
871
  const body = await response.json();
711
872
  return body.data?.routes ?? [];
712
873
  } catch {
@@ -723,7 +884,10 @@ async function reportUnregisteredAttempt(config, data) {
723
884
  };
724
885
  await sdkFetch(`${apiBaseUrl}/verification-activity/unregistered-attempt`, {
725
886
  method: "POST",
726
- headers: { "Content-Type": "application/json" },
887
+ headers: {
888
+ "Content-Type": "application/json",
889
+ ...config.apiKey && { Authorization: `Bearer ${config.apiKey}` }
890
+ },
727
891
  body: JSON.stringify(body)
728
892
  }).catch(() => {
729
893
  });
@@ -745,7 +909,11 @@ async function quickVerify(config, credentials) {
745
909
  return {
746
910
  identityVerified: result.identityVerified,
747
911
  policyAllowed: result.policyAllowed,
748
- reason: result.denialReasons?.[0]
912
+ reason: result.denialReasons?.[0],
913
+ ...isVerificationUnavailable(result) && {
914
+ unavailable: true,
915
+ retryAfterMs: unavailableRetryAfterSeconds(result) * 1e3
916
+ }
749
917
  };
750
918
  }
751
919
 
@@ -1493,7 +1661,10 @@ var PLATFORM_AGENT_SIGNATURES = [
1493
1661
  {
1494
1662
  vendor: "chatgpt",
1495
1663
  displayName: "OpenAI ChatGPT",
1496
- uaPatterns: [/chatgpt/i, /openai/i, /gpt-?\d/i],
1664
+ // GPTBot (training crawler) and OAI-SearchBot (ChatGPT search) are
1665
+ // OpenAI's declared crawler UAs — `gpt-?\d` never matched them, so they
1666
+ // classified as unattributed crawlers (round-27.4 W5).
1667
+ uaPatterns: [/chatgpt/i, /openai/i, /gpt-?\d/i, /\bGPTBot\b/i, /\bOAI-SearchBot\b/i],
1497
1668
  agentCardPatterns: [/openai\.com/i, /chatgpt\.com/i],
1498
1669
  apiKeyFormatPatterns: [/^sk-proj/i, /^sk-svcacct/i],
1499
1670
  headerPatterns: [/^openai-organization/i, /^openai-project/i, /^openai-version/i]
@@ -1628,6 +1799,215 @@ var BROWSER_UA_PATTERN = /mozilla\/\d/i;
1628
1799
  var _patterns = { STATIC_EXTENSIONS, BOT_UA_PATTERN, BROWSER_UA_PATTERN, SELF_UA_PATTERN };
1629
1800
 
1630
1801
  // src/edge-core/events.ts
1802
+ async function withBudget(promise, ms) {
1803
+ let timer;
1804
+ try {
1805
+ return await Promise.race([
1806
+ promise,
1807
+ new Promise((resolve) => {
1808
+ timer = setTimeout(() => resolve(void 0), ms);
1809
+ })
1810
+ ]);
1811
+ } catch {
1812
+ return void 0;
1813
+ } finally {
1814
+ if (timer) clearTimeout(timer);
1815
+ }
1816
+ }
1817
+ var destKeyOf = (d) => `${d.apiBaseUrl}\0${d.apiKey ?? ""}\0${d.counterparty}\0${d.adapter ?? ""}`;
1818
+ var BeaconBuffer = class {
1819
+ constructor(options = {}) {
1820
+ this.queue = [];
1821
+ this.droppedSinceLast = 0;
1822
+ this.droppedTotal = 0;
1823
+ this.deliveredTotal = 0;
1824
+ this.backoffUntil = 0;
1825
+ this.collapse = /* @__PURE__ */ new Map();
1826
+ /** Keys the backend answered 401 for — later batches go unauthenticated. */
1827
+ this.rejectedKeys = /* @__PURE__ */ new Set();
1828
+ this.opts = {
1829
+ maxItems: options.maxItems ?? 1e3,
1830
+ maxBatch: options.maxBatch ?? 100,
1831
+ maxAttempts: options.maxAttempts ?? 3,
1832
+ collapseWindowMs: options.collapseWindowMs ?? 6e4,
1833
+ collapseMaxKeys: options.collapseMaxKeys ?? 1e3,
1834
+ requestTimeoutMs: options.requestTimeoutMs ?? 1e4,
1835
+ retryBackoffMs: options.retryBackoffMs ?? 1e3,
1836
+ now: options.now ?? Date.now
1837
+ };
1838
+ }
1839
+ /**
1840
+ * Queue one beacon. With a `collapseKey`, a repeat inside the window is
1841
+ * merged into a held aggregate (sent when the window closes) instead of
1842
+ * becoming its own item.
1843
+ */
1844
+ enqueue(dest, item, collapseKey) {
1845
+ const now = this.opts.now();
1846
+ if (collapseKey) {
1847
+ const entry = this.collapse.get(collapseKey);
1848
+ if (entry && now - entry.firstSeenAt < this.opts.collapseWindowMs) {
1849
+ for (const target of [entry.first, entry.held]) {
1850
+ if (target && !target.inFlight && this.queue.includes(target)) {
1851
+ target.item.repeatCount = (target.item.repeatCount ?? 1) + 1;
1852
+ return;
1853
+ }
1854
+ }
1855
+ const held = this.push(
1856
+ dest,
1857
+ { ...item, repeatCount: 1 },
1858
+ entry.firstSeenAt + this.opts.collapseWindowMs
1859
+ );
1860
+ entry.held = held;
1861
+ return;
1862
+ }
1863
+ this.collapse.delete(collapseKey);
1864
+ if (this.collapse.size >= this.opts.collapseMaxKeys) {
1865
+ const oldest = this.collapse.keys().next().value;
1866
+ if (oldest !== void 0) this.collapse.delete(oldest);
1867
+ }
1868
+ const fresh = { firstSeenAt: now };
1869
+ this.collapse.set(collapseKey, fresh);
1870
+ fresh.first = this.push(dest, item, now);
1871
+ return;
1872
+ }
1873
+ this.push(dest, item, now);
1874
+ }
1875
+ /**
1876
+ * Deliver every eligible queued item. Concurrent callers share ONE
1877
+ * in-flight drain (it survives the caller's budget and a Lambda freeze),
1878
+ * so nothing in flight is ever sent twice; items queued while it runs are
1879
+ * picked up by the same drain.
1880
+ */
1881
+ flush() {
1882
+ if (!this.inFlight) {
1883
+ this.inFlight = this.drain().finally(() => {
1884
+ this.inFlight = void 0;
1885
+ });
1886
+ }
1887
+ return this.inFlight;
1888
+ }
1889
+ /** Counters for tests and diagnostics. */
1890
+ stats() {
1891
+ return {
1892
+ queueDepth: this.queue.length,
1893
+ droppedSinceLast: this.droppedSinceLast,
1894
+ droppedTotal: this.droppedTotal,
1895
+ deliveredTotal: this.deliveredTotal,
1896
+ flushing: this.inFlight !== void 0
1897
+ };
1898
+ }
1899
+ push(dest, item, eligibleAt) {
1900
+ while (this.queue.length >= this.opts.maxItems) this.dropOldest();
1901
+ const entry = {
1902
+ dest,
1903
+ destKey: destKeyOf(dest),
1904
+ item,
1905
+ attempts: 0,
1906
+ eligibleAt,
1907
+ inFlight: false
1908
+ };
1909
+ this.queue.push(entry);
1910
+ return entry;
1911
+ }
1912
+ dropOldest() {
1913
+ if (this.queue.shift()) this.countDropped(1);
1914
+ }
1915
+ countDropped(n) {
1916
+ this.droppedSinceLast += n;
1917
+ this.droppedTotal += n;
1918
+ }
1919
+ async drain() {
1920
+ for (; ; ) {
1921
+ const now = this.opts.now();
1922
+ if (now < this.backoffUntil) return;
1923
+ const batch = this.takeBatch(now);
1924
+ if (batch.length === 0) return;
1925
+ const ok = await this.send(batch);
1926
+ if (!ok) return;
1927
+ }
1928
+ }
1929
+ /** Oldest eligible item + up to maxBatch-1 more for the same destination. */
1930
+ takeBatch(now) {
1931
+ const first = this.queue.find((q) => q.eligibleAt <= now);
1932
+ if (!first) return [];
1933
+ const batch = [];
1934
+ const rest = [];
1935
+ for (const q of this.queue) {
1936
+ if (batch.length < this.opts.maxBatch && q.destKey === first.destKey && q.eligibleAt <= now) {
1937
+ q.inFlight = true;
1938
+ batch.push(q);
1939
+ } else {
1940
+ rest.push(q);
1941
+ }
1942
+ }
1943
+ this.queue = rest;
1944
+ return batch;
1945
+ }
1946
+ /** POST one batch. Returns false when the drain should pause (backoff). */
1947
+ async send(batch) {
1948
+ const dest = batch[0].dest;
1949
+ const useKey = dest.apiKey && !this.rejectedKeys.has(dest.apiKey) ? dest.apiKey : void 0;
1950
+ const reportedDropped = this.droppedSinceLast;
1951
+ const meta = {
1952
+ droppedSinceLast: reportedDropped,
1953
+ queueDepth: this.queue.length,
1954
+ adapter: dest.adapter ?? "edge-core",
1955
+ version: SDK_VERSION
1956
+ };
1957
+ const controller = typeof AbortController !== "undefined" ? new AbortController() : void 0;
1958
+ const response = await withBudget(
1959
+ sdkFetch(`${dest.apiBaseUrl}/verification-activity/edge-events`, {
1960
+ method: "POST",
1961
+ headers: {
1962
+ "Content-Type": "application/json",
1963
+ ...useKey && { Authorization: `Bearer ${useKey}` }
1964
+ },
1965
+ body: JSON.stringify({ events: batch.map((q) => q.item), meta }),
1966
+ ...controller && { signal: controller.signal }
1967
+ }),
1968
+ this.opts.requestTimeoutMs
1969
+ );
1970
+ if (!response) controller?.abort();
1971
+ const status = response?.status;
1972
+ for (const q of batch) q.inFlight = false;
1973
+ if (status !== void 0 && status >= 200 && status < 300) {
1974
+ this.droppedSinceLast = Math.max(0, this.droppedSinceLast - reportedDropped);
1975
+ this.deliveredTotal += batch.length;
1976
+ return true;
1977
+ }
1978
+ if (status === 401 && useKey) {
1979
+ this.rejectedKeys.add(useKey);
1980
+ this.requeue(batch, false);
1981
+ return true;
1982
+ }
1983
+ if (status === 429) {
1984
+ const retryAfter = Number(response?.headers.get("retry-after"));
1985
+ const waitMs = Number.isFinite(retryAfter) && retryAfter > 0 ? Math.min(retryAfter, 120) * 1e3 : 3e4;
1986
+ this.backoffUntil = this.opts.now() + waitMs;
1987
+ this.requeue(batch, false);
1988
+ return false;
1989
+ }
1990
+ if (status !== void 0 && status >= 400 && status < 500) {
1991
+ this.countDropped(batch.length);
1992
+ return true;
1993
+ }
1994
+ this.backoffUntil = this.opts.now() + this.opts.retryBackoffMs;
1995
+ this.requeue(batch, true);
1996
+ return false;
1997
+ }
1998
+ /** Put a failed batch back at the head of the queue (order preserved). */
1999
+ requeue(batch, countAttempt) {
2000
+ const keep = [];
2001
+ for (const q of batch) {
2002
+ if (countAttempt) q.attempts += 1;
2003
+ if (q.attempts >= this.opts.maxAttempts) this.countDropped(1);
2004
+ else keep.push(q);
2005
+ }
2006
+ this.queue = [...keep, ...this.queue];
2007
+ while (this.queue.length > this.opts.maxItems) this.dropOldest();
2008
+ }
2009
+ };
2010
+ var beaconBuffer = new BeaconBuffer();
1631
2011
  function sampled(rate, random = Math.random) {
1632
2012
  if (rate <= 0) return false;
1633
2013
  if (rate >= 1) return true;
@@ -1804,12 +2184,19 @@ function defaultOnDenied(result, _req, res) {
1804
2184
  });
1805
2185
  return;
1806
2186
  }
1807
- const statusCode = !result.identityVerified ? 401 : 403;
2187
+ if (isVerificationUnavailable(result)) {
2188
+ res.setHeader("X-Astra-Gateway-Mode", "enforced");
2189
+ res.setHeader(VERIFICATION_STATUS_HEADER, "unavailable");
2190
+ res.setHeader("Retry-After", String(unavailableRetryAfterSeconds(result)));
2191
+ res.status(503).json(buildVerificationUnavailableBody(result));
2192
+ return;
2193
+ }
2194
+ const { status: statusCode, code } = denyStatusFor(result, "INSUFFICIENT_ACCESS");
1808
2195
  res.setHeader("X-Astra-Gateway-Mode", "enforced");
1809
2196
  res.status(statusCode).json({
1810
2197
  success: false,
1811
2198
  error: {
1812
- code: !result.identityVerified ? "UNAUTHORIZED" : "INSUFFICIENT_ACCESS",
2199
+ code,
1813
2200
  message: result.denialReasons?.[0] || "Access denied",
1814
2201
  guidance: result.guidance,
1815
2202
  // Round-10: aggregated per-dimension detail + correlation handle.
@@ -2057,6 +2444,21 @@ function createMiddleware(options) {
2057
2444
  result.stepUpOutcome = outcome;
2058
2445
  }
2059
2446
  }
2447
+ if (!result.policyAllowed && isVerificationUnavailable(result) && (!shouldEnforce || isAnonymousRead(req.method, credentials))) {
2448
+ res.setHeader(VERIFICATION_STATUS_HEADER, "unavailable");
2449
+ return next();
2450
+ }
2451
+ if (!shouldEnforce) {
2452
+ if (config.setPassThroughHeader) {
2453
+ res.setHeader("X-Astra-Gateway-Mode", "enforced");
2454
+ res.setHeader("X-Astra-Gateway-Reason", "evaluated-not-enforced");
2455
+ }
2456
+ if (shouldRecordDecisions && sessionId) {
2457
+ recordDecision(config, sessionId, "granted").catch(() => {
2458
+ });
2459
+ }
2460
+ return next();
2461
+ }
2060
2462
  if (!result.policyAllowed) {
2061
2463
  if (shouldRecordDecisions && sessionId) {
2062
2464
  recordDecision(config, sessionId, "denied", result.denialReasons?.[0]).catch(() => {
@@ -2076,17 +2478,6 @@ function createMiddleware(options) {
2076
2478
  onDenied(result, req, res);
2077
2479
  return;
2078
2480
  }
2079
- if (!shouldEnforce) {
2080
- if (config.setPassThroughHeader) {
2081
- res.setHeader("X-Astra-Gateway-Mode", "enforced");
2082
- res.setHeader("X-Astra-Gateway-Reason", "evaluated-not-enforced");
2083
- }
2084
- if (shouldRecordDecisions && sessionId) {
2085
- recordDecision(config, sessionId, "granted").catch(() => {
2086
- });
2087
- }
2088
- return next();
2089
- }
2090
2481
  if (routeConfig.minTrustScore && result.agent) {
2091
2482
  if (result.agent.trustScore < routeConfig.minTrustScore) {
2092
2483
  const trustFailure = {
@@ -2142,14 +2533,6 @@ function createMiddleware(options) {
2142
2533
  verifiedAt: /* @__PURE__ */ new Date(),
2143
2534
  correlationId
2144
2535
  };
2145
- const catchUrls = config.apiBaseUrl ? await getWellKnownUrls(config.apiBaseUrl).catch(() => void 0) : void 0;
2146
- if (catchUrls) {
2147
- result.guidance = {
2148
- message: `Middleware threw ${errorClass} \u2014 failing closed`,
2149
- registrationUrl: catchUrls.registrationUrl,
2150
- documentationUrl: catchUrls.documentationUrl
2151
- };
2152
- }
2153
2536
  dedupeFailures(result);
2154
2537
  return onDenied(result, req, res);
2155
2538
  }
@@ -2569,6 +2952,20 @@ function createMiddleware2(options) {
2569
2952
  }
2570
2953
  const approvalRequired = result.policyAllowed && requiresHumanApproval(result);
2571
2954
  if (approvalRequired) annotateApprovalRequired(result);
2955
+ if (!result.policyAllowed && isVerificationUnavailable(result)) {
2956
+ if (isAnonymousRead(request.method, credentials)) {
2957
+ const passThrough = NextResponse.next();
2958
+ passThrough.headers.set(VERIFICATION_STATUS_HEADER, "unavailable");
2959
+ return passThrough;
2960
+ }
2961
+ return NextResponse.json(buildVerificationUnavailableBody(result), {
2962
+ status: 503,
2963
+ headers: {
2964
+ "Retry-After": String(unavailableRetryAfterSeconds(result)),
2965
+ [VERIFICATION_STATUS_HEADER]: "unavailable"
2966
+ }
2967
+ });
2968
+ }
2572
2969
  if (!result.policyAllowed || approvalRequired) {
2573
2970
  if (result.misconfigured && pathname.startsWith("/api/")) {
2574
2971
  return NextResponse.json(
@@ -2586,13 +2983,12 @@ function createMiddleware2(options) {
2586
2983
  );
2587
2984
  }
2588
2985
  if (pathname.startsWith("/api/")) {
2986
+ const denyStatus = denyStatusFor(result, "POLICY_DENIED");
2589
2987
  return NextResponse.json(
2590
2988
  {
2591
2989
  success: false,
2592
2990
  error: {
2593
- // Round-18 G4: 401 → identity missing (re-auth); 403 → identity
2594
- // OK, policy denied (update PDLSS / step up).
2595
- code: !result.identityVerified ? "UNAUTHORIZED" : "POLICY_DENIED",
2991
+ code: denyStatus.code,
2596
2992
  message: result.denialReasons?.[0] || "Access denied",
2597
2993
  guidance: result.guidance,
2598
2994
  failures: result.failures,
@@ -2602,7 +2998,7 @@ function createMiddleware2(options) {
2602
2998
  }
2603
2999
  },
2604
3000
  {
2605
- status: !result.identityVerified ? 401 : 403,
3001
+ status: denyStatus.status,
2606
3002
  ...stepUpHoldOutcome && {
2607
3003
  headers: { "X-Astra-Step-Up": stepUpHoldOutcome }
2608
3004
  }
@@ -2729,6 +3125,12 @@ var VerificationGatewayClient = class {
2729
3125
  * fulfil against it only once the order is settled / pending_merchant. An
2730
3126
  * explicit `buyerEmail` (here or on the bridge handoff body) wins over the
2731
3127
  * account default.
3128
+ *
3129
+ * 5.12.0 approval binding: pass the bridge handoff body's `attemptId` (and
3130
+ * the same `checkoutSessionId`) so a step-up approval minted for this cart
3131
+ * is matched to — and only to — this confirm. A held cart's approval is
3132
+ * bound to its checkout session and exact value; a confirm for any other
3133
+ * cart is held for its own approval.
2732
3134
  */
2733
3135
  async confirmCheckout(options) {
2734
3136
  const credentials = {
@@ -2748,6 +3150,7 @@ var VerificationGatewayClient = class {
2748
3150
  commercePhase: "confirm",
2749
3151
  checkoutSessionId: options.checkoutSessionId,
2750
3152
  checkoutItems: options.checkoutItems,
3153
+ ...options.attemptId && { attemptId: options.attemptId },
2751
3154
  buyerEmail: options.buyerEmail,
2752
3155
  counterpartyUrl: options.counterpartyUrl,
2753
3156
  counterpartyType: options.counterpartyType
@@ -5551,8 +5954,37 @@ function dedupeFailures2(result) {
5551
5954
  result.denialReasons = [...new Set(result.denialReasons)];
5552
5955
  }
5553
5956
  }
5957
+ function isReadOnlyMcpMethod(parsed) {
5958
+ return parsed.method !== "tools/call";
5959
+ }
5554
5960
  function defaultMcpDenied(result, req, res) {
5555
5961
  const id = req.body?.id ?? null;
5962
+ if (isVerificationUnavailable(result)) {
5963
+ const retryAfterSeconds = unavailableRetryAfterSeconds(result);
5964
+ const body = buildVerificationUnavailableBody(result).error;
5965
+ res.setHeader("X-Astra-Gateway-Mode", "enforced");
5966
+ res.setHeader(VERIFICATION_STATUS_HEADER, "unavailable");
5967
+ res.setHeader("Retry-After", String(retryAfterSeconds));
5968
+ res.status(503).json({
5969
+ jsonrpc: "2.0",
5970
+ id,
5971
+ error: {
5972
+ // -32003 → verification temporarily unavailable (retryable)
5973
+ code: -32003,
5974
+ message: body.message,
5975
+ data: {
5976
+ code: body.code,
5977
+ recommendation: "error",
5978
+ retryable: true,
5979
+ retryAfterSeconds,
5980
+ guidance: body.guidance,
5981
+ failures: result.failures,
5982
+ correlationId: result.correlationId
5983
+ }
5984
+ }
5985
+ });
5986
+ return;
5987
+ }
5556
5988
  if (result.misconfigured) {
5557
5989
  res.setHeader("X-Astra-Gateway-Mode", "enforced");
5558
5990
  res.status(503).json({
@@ -5571,7 +6003,7 @@ function defaultMcpDenied(result, req, res) {
5571
6003
  });
5572
6004
  return;
5573
6005
  }
5574
- const status = !result.identityVerified ? 401 : 403;
6006
+ const { status, code: denyCode } = denyStatusFor(result, "INSUFFICIENT_ACCESS");
5575
6007
  res.setHeader("X-Astra-Gateway-Mode", "enforced");
5576
6008
  res.status(status).json({
5577
6009
  jsonrpc: "2.0",
@@ -5581,9 +6013,11 @@ function defaultMcpDenied(result, req, res) {
5581
6013
  // -32000 → unauthorized (no identity resolved)
5582
6014
  // -32001 → insufficient access (identity OK, policy denied)
5583
6015
  // -32002 → merchant verification misconfigured (handled above)
6016
+ // -32003 → verification temporarily unavailable (handled above)
5584
6017
  code: !result.identityVerified ? -32e3 : -32001,
5585
6018
  message: result.denialReasons?.[0] ?? "Access denied",
5586
6019
  data: {
6020
+ code: denyCode,
5587
6021
  guidance: result.guidance,
5588
6022
  // Round-10: aggregated per-dimension detail + correlation handle.
5589
6023
  failures: result.failures,
@@ -5711,6 +6145,9 @@ function createMcpMiddleware(options) {
5711
6145
  resolved_action: pdlss.action
5712
6146
  });
5713
6147
  }
6148
+ if (!pdlss.purpose && !shouldEnforce) {
6149
+ return next();
6150
+ }
5714
6151
  if (!pdlss.purpose) {
5715
6152
  const id = req.body?.id ?? null;
5716
6153
  res.status(400).json({
@@ -5730,6 +6167,9 @@ function createMcpMiddleware(options) {
5730
6167
  }
5731
6168
  const counterpartyUrl = config.counterpartyUrl || `${req.protocol}://${req.get("host")}${req.path}`;
5732
6169
  const shouldRecordDecisions = recordDecisions !== false;
6170
+ const forwardedFor = req.headers["x-forwarded-for"];
6171
+ const forwardedForStr = Array.isArray(forwardedFor) ? forwardedFor.join(", ") : forwardedFor;
6172
+ const sourceIp = req.ip ?? (forwardedForStr ? forwardedForStr.split(",")[0].trim() : void 0);
5733
6173
  const verificationRequest = {
5734
6174
  credentials,
5735
6175
  purpose: pdlss.purpose,
@@ -5744,9 +6184,10 @@ function createMcpMiddleware(options) {
5744
6184
  counterpartyType: config.counterpartyType || "mcp_server",
5745
6185
  enableRuntimeChallenge,
5746
6186
  callerMetadata: {
5747
- sourceIp: req.ip,
6187
+ sourceIp,
5748
6188
  userAgent: req.headers["user-agent"],
5749
6189
  host: req.headers.host,
6190
+ ...forwardedForStr && { forwardedFor: forwardedForStr },
5750
6191
  // Capture EVERY inbound header (parity with the express adapter —
5751
6192
  // MCP-only merchants have no edge to do it upstream) plus the
5752
6193
  // CDN-derived connection block. Local string work, secrets
@@ -5769,6 +6210,21 @@ function createMcpMiddleware(options) {
5769
6210
  result.stepUpOutcome = outcome;
5770
6211
  }
5771
6212
  }
6213
+ if (!result.policyAllowed && isVerificationUnavailable(result) && (!shouldEnforce || isReadOnlyMcpMethod(parsed) && !credentials.astraId && !credentials.jwt)) {
6214
+ res.setHeader(VERIFICATION_STATUS_HEADER, "unavailable");
6215
+ return next();
6216
+ }
6217
+ if (!shouldEnforce) {
6218
+ if (config.setPassThroughHeader) {
6219
+ res.setHeader("X-Astra-Gateway-Mode", "enforced");
6220
+ res.setHeader("X-Astra-Gateway-Reason", "evaluated-not-enforced");
6221
+ }
6222
+ if (shouldRecordDecisions && sessionId) {
6223
+ recordDecision(config, sessionId, "granted").catch(() => {
6224
+ });
6225
+ }
6226
+ return next();
6227
+ }
5772
6228
  if (!result.policyAllowed) {
5773
6229
  if (shouldRecordDecisions && sessionId) {
5774
6230
  recordDecision(config, sessionId, "denied", result.denialReasons?.[0]).catch(() => {
@@ -5788,17 +6244,6 @@ function createMcpMiddleware(options) {
5788
6244
  onDenied(result, req, res);
5789
6245
  return;
5790
6246
  }
5791
- if (!shouldEnforce) {
5792
- if (config.setPassThroughHeader) {
5793
- res.setHeader("X-Astra-Gateway-Mode", "enforced");
5794
- res.setHeader("X-Astra-Gateway-Reason", "evaluated-not-enforced");
5795
- }
5796
- if (shouldRecordDecisions && sessionId) {
5797
- recordDecision(config, sessionId, "granted").catch(() => {
5798
- });
5799
- }
5800
- return next();
5801
- }
5802
6247
  if (effectiveAstraId) {
5803
6248
  res.setHeader(
5804
6249
  MCP_VERIFIED_HOP_HEADER,
@@ -5839,14 +6284,6 @@ function createMcpMiddleware(options) {
5839
6284
  verifiedAt: /* @__PURE__ */ new Date(),
5840
6285
  correlationId
5841
6286
  };
5842
- const catchUrls = config.apiBaseUrl ? await getWellKnownUrls(config.apiBaseUrl).catch(() => void 0) : void 0;
5843
- if (catchUrls) {
5844
- result.guidance = {
5845
- message: `Middleware threw ${errorClass} \u2014 failing closed`,
5846
- registrationUrl: catchUrls.registrationUrl,
5847
- documentationUrl: catchUrls.documentationUrl
5848
- };
5849
- }
5850
6287
  dedupeFailures2(result);
5851
6288
  return onDenied(result, req, res);
5852
6289
  }
@@ -9853,6 +10290,7 @@ export {
9853
10290
  RegistrationTimeoutError,
9854
10291
  SDK_USER_AGENT,
9855
10292
  TRUST_LEVEL_RANGES,
10293
+ VERIFICATION_STATUS_HEADER,
9856
10294
  SDK_VERSION as VERSION,
9857
10295
  _resetEdgeConfigCache,
9858
10296
  agent_exports as agent,
@@ -9860,9 +10298,11 @@ export {
9860
10298
  awaitStepUpApproval,
9861
10299
  buildGuidance,
9862
10300
  buildSdkObservedMetadata,
10301
+ buildVerificationUnavailableBody,
9863
10302
  clearCache,
9864
10303
  createMcpMiddleware,
9865
10304
  degradeToObserve,
10305
+ denyStatusFor,
9866
10306
  deriveConnectionFromHeaders,
9867
10307
  detectPlatformFingerprint,
9868
10308
  detectRuntime,
@@ -9879,6 +10319,8 @@ export {
9879
10319
  getWellKnownUrls,
9880
10320
  hasCredentials,
9881
10321
  isEdgeConfig,
10322
+ isRejectedIdentityClaim,
10323
+ isVerificationUnavailable,
9882
10324
  isVerifiedHopValidFor,
9883
10325
  matchEdgePathRule,
9884
10326
  matchPlatformSignature,
@@ -9898,6 +10340,7 @@ export {
9898
10340
  serializeVerifiedHop,
9899
10341
  setMcpMeta,
9900
10342
  transport_exports as transport,
10343
+ unavailableRetryAfterSeconds,
9901
10344
  validatePolicy as validateLocalPolicy,
9902
10345
  verify
9903
10346
  };