@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
@@ -36,7 +36,7 @@ var SDK_VERSION;
36
36
  var init_version = __esm({
37
37
  "src/version.ts"() {
38
38
  "use strict";
39
- SDK_VERSION = "5.11.0";
39
+ SDK_VERSION = "5.13.0";
40
40
  }
41
41
  });
42
42
 
@@ -5057,8 +5057,22 @@ var init_well_known = __esm({
5057
5057
  }
5058
5058
  });
5059
5059
 
5060
+ // src/failure-posture.ts
5061
+ var REJECTED_IDENTITY_DIMENSIONS, UNKNOWN_AGENT_GUIDANCE;
5062
+ var init_failure_posture = __esm({
5063
+ "src/failure-posture.ts"() {
5064
+ "use strict";
5065
+ REJECTED_IDENTITY_DIMENSIONS = /* @__PURE__ */ new Set([
5066
+ "identity.invalid_agent_id",
5067
+ "identity.unknown_agent",
5068
+ "agent.lookup"
5069
+ ]);
5070
+ 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.";
5071
+ }
5072
+ });
5073
+
5060
5074
  // src/verify.ts
5061
- async function performInitCheck(apiBaseUrl, debug, strictInit, apiKey) {
5075
+ async function performInitCheck(apiBaseUrl, debug, strictInit, apiKey, counterparty) {
5062
5076
  initCheckPerformed = true;
5063
5077
  try {
5064
5078
  const probeUrl = `${apiBaseUrl}/agents/verify-access`;
@@ -5076,7 +5090,8 @@ async function performInitCheck(apiBaseUrl, debug, strictInit, apiKey) {
5076
5090
  );
5077
5091
  }
5078
5092
  if (apiKey) {
5079
- const selfCheck = await sdkFetch(`${apiBaseUrl}/agents/verify-access/self-check`, {
5093
+ const q = counterparty?.id ? `?counterpartyId=${encodeURIComponent(counterparty.id)}` : counterparty?.url ? `?counterpartyUrl=${encodeURIComponent(counterparty.url)}` : "";
5094
+ const selfCheck = await sdkFetch(`${apiBaseUrl}/agents/verify-access/self-check${q}`, {
5080
5095
  headers: { Authorization: `Bearer ${apiKey}` }
5081
5096
  });
5082
5097
  if (selfCheck.status === 401) {
@@ -5088,6 +5103,13 @@ async function performInitCheck(apiBaseUrl, debug, strictInit, apiKey) {
5088
5103
  console.error(message);
5089
5104
  } else if (selfCheck.ok) {
5090
5105
  const body = await selfCheck.json().catch(() => ({}));
5106
+ if (body.counterparty && body.counterparty.canVerify === false) {
5107
+ 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.`;
5108
+ if (strictInit) {
5109
+ throw new Error(`${message} (strictInit=true)`);
5110
+ }
5111
+ console.error(message);
5112
+ }
5091
5113
  if (typeof body.daysToExpiry === "number" && body.daysToExpiry <= 7) {
5092
5114
  console.warn(
5093
5115
  `[VerificationGateway] the configured AstraSync API key expires in ${body.daysToExpiry} day(s) \u2014 rotate it at Settings \u2192 Developer Tools before integrations start failing.`
@@ -5190,11 +5212,11 @@ function createGuidanceResponse(_config, reason, options = {}) {
5190
5212
  const isMisconfig = source === "merchant_misconfig";
5191
5213
  const urls = options.urls;
5192
5214
  const guidance = isMisconfig ? {
5193
- 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.",
5215
+ 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.",
5194
5216
  registrationUrl: urls?.registrationUrl ?? "",
5195
5217
  documentationUrl: options.remediation?.docsUrl ?? urls?.documentationUrl ?? "",
5196
5218
  steps: [
5197
- "Report the failure to the site operator \u2014 their AstraSync API key needs rotating",
5219
+ "Report the failure to the site operator \u2014 their AstraSync API key needs replacing",
5198
5220
  "Do not retry: the failure is deterministic until the operator updates their key"
5199
5221
  ]
5200
5222
  } : isApiError ? {
@@ -5255,11 +5277,43 @@ function createGuidanceResponse(_config, reason, options = {}) {
5255
5277
  // coercing the absent recommendation to a policy 'deny'. Misconfig is
5256
5278
  // deterministic and intentionally NOT marked retryable.
5257
5279
  ...isApiError && { retryable: true },
5280
+ ...options.httpStatus !== void 0 && { httpStatus: options.httpStatus },
5281
+ ...options.retryAfterMs !== void 0 && { retryAfterMs: options.retryAfterMs },
5258
5282
  correlationId: options.correlationId,
5259
5283
  attemptId: options.attemptId,
5260
5284
  verifiedAt: /* @__PURE__ */ new Date()
5261
5285
  };
5262
5286
  }
5287
+ async function readJsonSafe(response) {
5288
+ try {
5289
+ const parsed = await response.json();
5290
+ return parsed && typeof parsed === "object" ? parsed : {};
5291
+ } catch {
5292
+ return {};
5293
+ }
5294
+ }
5295
+ function rejectedIdentityGuidance(failures) {
5296
+ const f = failures?.find((x) => REJECTED_IDENTITY_DIMENSIONS.has(x.dimension));
5297
+ if (!f) return void 0;
5298
+ if (f.dimension === "agent.lookup") {
5299
+ return `The presented agent identifier is not registered with AstraSync. ${UNKNOWN_AGENT_GUIDANCE}`;
5300
+ }
5301
+ return f.guidance ? `${f.message} ${f.guidance}` : f.message;
5302
+ }
5303
+ function parseRetryAfterMs(response) {
5304
+ const raw = response.headers?.get?.("retry-after");
5305
+ if (!raw) return void 0;
5306
+ const trimmed = raw.trim();
5307
+ let ms;
5308
+ if (/^\d+(\.\d+)?$/.test(trimmed)) {
5309
+ ms = Number(trimmed) * 1e3;
5310
+ } else {
5311
+ const at = Date.parse(trimmed);
5312
+ if (Number.isNaN(at)) return void 0;
5313
+ ms = at - Date.now();
5314
+ }
5315
+ return Math.min(MAX_RETRY_AFTER_MS, Math.max(0, Math.round(ms)));
5316
+ }
5263
5317
  async function callVerifyAccessAPI(config, request2) {
5264
5318
  const { credentials, ...requestData } = request2;
5265
5319
  const body = {
@@ -5316,7 +5370,8 @@ async function callVerifyAccessAPI(config, request2) {
5316
5370
  body: JSON.stringify(body),
5317
5371
  signal: AbortSignal.timeout(config.verifyTimeoutMs ?? 1e4)
5318
5372
  });
5319
- const data = await response.json();
5373
+ const data = await readJsonSafe(response);
5374
+ const retryAfterMs = parseRetryAfterMs(response);
5320
5375
  if (response.status === 410) {
5321
5376
  return {
5322
5377
  success: true,
@@ -5354,13 +5409,31 @@ async function callVerifyAccessAPI(config, request2) {
5354
5409
  }
5355
5410
  };
5356
5411
  }
5412
+ if (response.status === 404 && credentials.astraId && (data?.error === "AGENT_NOT_FOUND" || typeof data?.message === "string" && /agent/i.test(data.message))) {
5413
+ return {
5414
+ success: true,
5415
+ attemptId: typeof data?.attemptId === "string" ? data.attemptId : void 0,
5416
+ access: {
5417
+ allowed: false,
5418
+ reason: "unknown_agent",
5419
+ failures: [
5420
+ {
5421
+ dimension: "identity.unknown_agent",
5422
+ message: "The presented agent identifier is not registered with AstraSync.",
5423
+ guidance: UNKNOWN_AGENT_GUIDANCE
5424
+ }
5425
+ ]
5426
+ }
5427
+ };
5428
+ }
5357
5429
  if (!response.ok) {
5358
- if (response.status === 401) {
5430
+ const keyMismatch = response.status === 403 && data?.error === "COUNTERPARTY_KEY_MISMATCH";
5431
+ if (response.status === 401 || keyMismatch) {
5359
5432
  return {
5360
5433
  success: false,
5361
5434
  error: data.message || data.error || "Integration API key rejected",
5362
5435
  misconfig: true,
5363
- misconfigCode: typeof data?.code === "string" ? data.code : void 0,
5436
+ misconfigCode: keyMismatch ? "counterparty_key_mismatch" : typeof data?.code === "string" ? data.code : void 0,
5364
5437
  remediation: data?.remediation && typeof data.remediation === "object" ? data.remediation : void 0,
5365
5438
  correlationId: typeof data?.correlationId === "string" ? data.correlationId : void 0,
5366
5439
  attemptId: typeof data?.attemptId === "string" ? data.attemptId : void 0
@@ -5368,9 +5441,11 @@ async function callVerifyAccessAPI(config, request2) {
5368
5441
  }
5369
5442
  return {
5370
5443
  success: false,
5371
- error: data.message || data.error || `API returned ${response.status}`,
5444
+ error: typeof data?.message === "string" && data.message || typeof data?.error === "string" && data.error || `API returned ${response.status}`,
5372
5445
  correlationId: typeof data?.correlationId === "string" ? data.correlationId : void 0,
5373
- attemptId: typeof data?.attemptId === "string" ? data.attemptId : void 0
5446
+ attemptId: typeof data?.attemptId === "string" ? data.attemptId : void 0,
5447
+ httpStatus: response.status,
5448
+ ...retryAfterMs !== void 0 && { retryAfterMs }
5374
5449
  };
5375
5450
  }
5376
5451
  return data;
@@ -5391,14 +5466,22 @@ async function verify(config, request2, options) {
5391
5466
  mergedConfig.apiBaseUrl,
5392
5467
  mergedConfig.debug,
5393
5468
  true,
5394
- mergedConfig.apiKey
5469
+ mergedConfig.apiKey,
5470
+ {
5471
+ id: mergedConfig.counterpartyId,
5472
+ url: mergedConfig.counterpartyUrl ?? request2.counterpartyUrl
5473
+ }
5395
5474
  );
5396
5475
  } else {
5397
5476
  void performInitCheck(
5398
5477
  mergedConfig.apiBaseUrl,
5399
5478
  mergedConfig.debug,
5400
5479
  false,
5401
- mergedConfig.apiKey
5480
+ mergedConfig.apiKey,
5481
+ {
5482
+ id: mergedConfig.counterpartyId,
5483
+ url: mergedConfig.counterpartyUrl ?? request2.counterpartyUrl
5484
+ }
5402
5485
  );
5403
5486
  }
5404
5487
  }
@@ -5442,12 +5525,14 @@ async function verify(config, request2, options) {
5442
5525
  correlationId: apiResponse.correlationId,
5443
5526
  attemptId: apiResponse.attemptId,
5444
5527
  remediation: apiResponse.remediation,
5528
+ httpStatus: apiResponse.httpStatus,
5529
+ retryAfterMs: apiResponse.retryAfterMs,
5445
5530
  urls
5446
5531
  });
5447
5532
  }
5448
5533
  if (!apiResponse.access?.allowed) {
5449
5534
  const aggregatedFailures = apiResponse.access?.failures;
5450
- const idVerifiedFromBackend = apiResponse.verificationContext?.idVerified === true;
5535
+ const idVerifiedFromBackend = apiResponse.verificationContext?.idVerified === true && !aggregatedFailures?.some((f) => REJECTED_IDENTITY_DIMENSIONS.has(f.dimension));
5451
5536
  const result2 = {
5452
5537
  identityVerified: idVerifiedFromBackend,
5453
5538
  policyAllowed: false,
@@ -5459,7 +5544,10 @@ async function verify(config, request2, options) {
5459
5544
  requiresStepUp: apiResponse.access?.requiresStepUp,
5460
5545
  requiresApproval: apiResponse.access?.requiresApproval,
5461
5546
  guidance: {
5462
- message: apiResponse.access?.reason || "Access denied by PDLSS policy",
5547
+ // 5.12.0: a presented-but-rejected id (malformed / unknown) carries
5548
+ // actionable registration guidance on its failure — surface it
5549
+ // instead of the bare machine reason ('unknown_agent').
5550
+ message: rejectedIdentityGuidance(aggregatedFailures) ?? (apiResponse.access?.reason || "Access denied by PDLSS policy"),
5463
5551
  registrationUrl: urls?.registrationUrl ?? "",
5464
5552
  documentationUrl: urls?.documentationUrl ?? "",
5465
5553
  // 5.1.0: anonymous callers land on this branch — steer MCP-hosted
@@ -5559,7 +5647,7 @@ async function verify(config, request2, options) {
5559
5647
  registrationUrl: urls?.registrationUrl ?? "",
5560
5648
  documentationUrl: urls?.documentationUrl ?? ""
5561
5649
  };
5562
- } else if (result.recommendation === "step_up_required") {
5650
+ } else if (result.recommendation === "step_up_required" && result.identityVerified) {
5563
5651
  result.requiresStepUp = true;
5564
5652
  result.denialReasons = serverDenialReasons ?? result.recommendationReasons ?? ["Step-up verification required"];
5565
5653
  }
@@ -5569,7 +5657,7 @@ async function verify(config, request2, options) {
5569
5657
  }
5570
5658
  return result;
5571
5659
  }
5572
- var DEFAULT_CONFIG, initCheckPerformed, deprecationWarningShown, verificationCache, DEFAULT_CACHE_TTL_SECONDS;
5660
+ var DEFAULT_CONFIG, initCheckPerformed, deprecationWarningShown, verificationCache, DEFAULT_CACHE_TTL_SECONDS, MAX_RETRY_AFTER_MS;
5573
5661
  var init_verify = __esm({
5574
5662
  "src/verify.ts"() {
5575
5663
  "use strict";
@@ -5577,6 +5665,7 @@ var init_verify = __esm({
5577
5665
  init_trust_levels();
5578
5666
  init_version();
5579
5667
  init_well_known();
5668
+ init_failure_posture();
5580
5669
  DEFAULT_CONFIG = {
5581
5670
  apiBaseUrl: "https://astrasync.ai/api",
5582
5671
  // minTrustScore + minTrustScoreForFull deprecated in v2.3.0 — server decides.
@@ -5590,6 +5679,7 @@ var init_verify = __esm({
5590
5679
  deprecationWarningShown = false;
5591
5680
  verificationCache = /* @__PURE__ */ new Map();
5592
5681
  DEFAULT_CACHE_TTL_SECONDS = 60;
5682
+ MAX_RETRY_AFTER_MS = 15 * 60 * 1e3;
5593
5683
  }
5594
5684
  });
5595
5685
 
@@ -5712,12 +5802,223 @@ var init_classify = __esm({
5712
5802
  });
5713
5803
 
5714
5804
  // src/edge-core/events.ts
5805
+ async function withBudget(promise, ms) {
5806
+ let timer;
5807
+ try {
5808
+ return await Promise.race([
5809
+ promise,
5810
+ new Promise((resolve) => {
5811
+ timer = setTimeout(() => resolve(void 0), ms);
5812
+ })
5813
+ ]);
5814
+ } catch {
5815
+ return void 0;
5816
+ } finally {
5817
+ if (timer) clearTimeout(timer);
5818
+ }
5819
+ }
5820
+ var destKeyOf, BeaconBuffer, beaconBuffer;
5715
5821
  var init_events = __esm({
5716
5822
  "src/edge-core/events.ts"() {
5717
5823
  "use strict";
5824
+ init_http();
5718
5825
  init_metadata_capture();
5719
- init_verify();
5826
+ init_version();
5720
5827
  init_request();
5828
+ destKeyOf = (d) => `${d.apiBaseUrl}\0${d.apiKey ?? ""}\0${d.counterparty}\0${d.adapter ?? ""}`;
5829
+ BeaconBuffer = class {
5830
+ constructor(options = {}) {
5831
+ this.queue = [];
5832
+ this.droppedSinceLast = 0;
5833
+ this.droppedTotal = 0;
5834
+ this.deliveredTotal = 0;
5835
+ this.backoffUntil = 0;
5836
+ this.collapse = /* @__PURE__ */ new Map();
5837
+ /** Keys the backend answered 401 for — later batches go unauthenticated. */
5838
+ this.rejectedKeys = /* @__PURE__ */ new Set();
5839
+ this.opts = {
5840
+ maxItems: options.maxItems ?? 1e3,
5841
+ maxBatch: options.maxBatch ?? 100,
5842
+ maxAttempts: options.maxAttempts ?? 3,
5843
+ collapseWindowMs: options.collapseWindowMs ?? 6e4,
5844
+ collapseMaxKeys: options.collapseMaxKeys ?? 1e3,
5845
+ requestTimeoutMs: options.requestTimeoutMs ?? 1e4,
5846
+ retryBackoffMs: options.retryBackoffMs ?? 1e3,
5847
+ now: options.now ?? Date.now
5848
+ };
5849
+ }
5850
+ /**
5851
+ * Queue one beacon. With a `collapseKey`, a repeat inside the window is
5852
+ * merged into a held aggregate (sent when the window closes) instead of
5853
+ * becoming its own item.
5854
+ */
5855
+ enqueue(dest, item, collapseKey) {
5856
+ const now = this.opts.now();
5857
+ if (collapseKey) {
5858
+ const entry = this.collapse.get(collapseKey);
5859
+ if (entry && now - entry.firstSeenAt < this.opts.collapseWindowMs) {
5860
+ for (const target of [entry.first, entry.held]) {
5861
+ if (target && !target.inFlight && this.queue.includes(target)) {
5862
+ target.item.repeatCount = (target.item.repeatCount ?? 1) + 1;
5863
+ return;
5864
+ }
5865
+ }
5866
+ const held = this.push(
5867
+ dest,
5868
+ { ...item, repeatCount: 1 },
5869
+ entry.firstSeenAt + this.opts.collapseWindowMs
5870
+ );
5871
+ entry.held = held;
5872
+ return;
5873
+ }
5874
+ this.collapse.delete(collapseKey);
5875
+ if (this.collapse.size >= this.opts.collapseMaxKeys) {
5876
+ const oldest = this.collapse.keys().next().value;
5877
+ if (oldest !== void 0) this.collapse.delete(oldest);
5878
+ }
5879
+ const fresh = { firstSeenAt: now };
5880
+ this.collapse.set(collapseKey, fresh);
5881
+ fresh.first = this.push(dest, item, now);
5882
+ return;
5883
+ }
5884
+ this.push(dest, item, now);
5885
+ }
5886
+ /**
5887
+ * Deliver every eligible queued item. Concurrent callers share ONE
5888
+ * in-flight drain (it survives the caller's budget and a Lambda freeze),
5889
+ * so nothing in flight is ever sent twice; items queued while it runs are
5890
+ * picked up by the same drain.
5891
+ */
5892
+ flush() {
5893
+ if (!this.inFlight) {
5894
+ this.inFlight = this.drain().finally(() => {
5895
+ this.inFlight = void 0;
5896
+ });
5897
+ }
5898
+ return this.inFlight;
5899
+ }
5900
+ /** Counters for tests and diagnostics. */
5901
+ stats() {
5902
+ return {
5903
+ queueDepth: this.queue.length,
5904
+ droppedSinceLast: this.droppedSinceLast,
5905
+ droppedTotal: this.droppedTotal,
5906
+ deliveredTotal: this.deliveredTotal,
5907
+ flushing: this.inFlight !== void 0
5908
+ };
5909
+ }
5910
+ push(dest, item, eligibleAt) {
5911
+ while (this.queue.length >= this.opts.maxItems) this.dropOldest();
5912
+ const entry = {
5913
+ dest,
5914
+ destKey: destKeyOf(dest),
5915
+ item,
5916
+ attempts: 0,
5917
+ eligibleAt,
5918
+ inFlight: false
5919
+ };
5920
+ this.queue.push(entry);
5921
+ return entry;
5922
+ }
5923
+ dropOldest() {
5924
+ if (this.queue.shift()) this.countDropped(1);
5925
+ }
5926
+ countDropped(n) {
5927
+ this.droppedSinceLast += n;
5928
+ this.droppedTotal += n;
5929
+ }
5930
+ async drain() {
5931
+ for (; ; ) {
5932
+ const now = this.opts.now();
5933
+ if (now < this.backoffUntil) return;
5934
+ const batch = this.takeBatch(now);
5935
+ if (batch.length === 0) return;
5936
+ const ok = await this.send(batch);
5937
+ if (!ok) return;
5938
+ }
5939
+ }
5940
+ /** Oldest eligible item + up to maxBatch-1 more for the same destination. */
5941
+ takeBatch(now) {
5942
+ const first = this.queue.find((q) => q.eligibleAt <= now);
5943
+ if (!first) return [];
5944
+ const batch = [];
5945
+ const rest = [];
5946
+ for (const q of this.queue) {
5947
+ if (batch.length < this.opts.maxBatch && q.destKey === first.destKey && q.eligibleAt <= now) {
5948
+ q.inFlight = true;
5949
+ batch.push(q);
5950
+ } else {
5951
+ rest.push(q);
5952
+ }
5953
+ }
5954
+ this.queue = rest;
5955
+ return batch;
5956
+ }
5957
+ /** POST one batch. Returns false when the drain should pause (backoff). */
5958
+ async send(batch) {
5959
+ const dest = batch[0].dest;
5960
+ const useKey = dest.apiKey && !this.rejectedKeys.has(dest.apiKey) ? dest.apiKey : void 0;
5961
+ const reportedDropped = this.droppedSinceLast;
5962
+ const meta = {
5963
+ droppedSinceLast: reportedDropped,
5964
+ queueDepth: this.queue.length,
5965
+ adapter: dest.adapter ?? "edge-core",
5966
+ version: SDK_VERSION
5967
+ };
5968
+ const controller = typeof AbortController !== "undefined" ? new AbortController() : void 0;
5969
+ const response = await withBudget(
5970
+ sdkFetch(`${dest.apiBaseUrl}/verification-activity/edge-events`, {
5971
+ method: "POST",
5972
+ headers: {
5973
+ "Content-Type": "application/json",
5974
+ ...useKey && { Authorization: `Bearer ${useKey}` }
5975
+ },
5976
+ body: JSON.stringify({ events: batch.map((q) => q.item), meta }),
5977
+ ...controller && { signal: controller.signal }
5978
+ }),
5979
+ this.opts.requestTimeoutMs
5980
+ );
5981
+ if (!response) controller?.abort();
5982
+ const status = response?.status;
5983
+ for (const q of batch) q.inFlight = false;
5984
+ if (status !== void 0 && status >= 200 && status < 300) {
5985
+ this.droppedSinceLast = Math.max(0, this.droppedSinceLast - reportedDropped);
5986
+ this.deliveredTotal += batch.length;
5987
+ return true;
5988
+ }
5989
+ if (status === 401 && useKey) {
5990
+ this.rejectedKeys.add(useKey);
5991
+ this.requeue(batch, false);
5992
+ return true;
5993
+ }
5994
+ if (status === 429) {
5995
+ const retryAfter = Number(response?.headers.get("retry-after"));
5996
+ const waitMs = Number.isFinite(retryAfter) && retryAfter > 0 ? Math.min(retryAfter, 120) * 1e3 : 3e4;
5997
+ this.backoffUntil = this.opts.now() + waitMs;
5998
+ this.requeue(batch, false);
5999
+ return false;
6000
+ }
6001
+ if (status !== void 0 && status >= 400 && status < 500) {
6002
+ this.countDropped(batch.length);
6003
+ return true;
6004
+ }
6005
+ this.backoffUntil = this.opts.now() + this.opts.retryBackoffMs;
6006
+ this.requeue(batch, true);
6007
+ return false;
6008
+ }
6009
+ /** Put a failed batch back at the head of the queue (order preserved). */
6010
+ requeue(batch, countAttempt) {
6011
+ const keep = [];
6012
+ for (const q of batch) {
6013
+ if (countAttempt) q.attempts += 1;
6014
+ if (q.attempts >= this.opts.maxAttempts) this.countDropped(1);
6015
+ else keep.push(q);
6016
+ }
6017
+ this.queue = [...keep, ...this.queue];
6018
+ while (this.queue.length > this.opts.maxItems) this.dropOldest();
6019
+ }
6020
+ };
6021
+ beaconBuffer = new BeaconBuffer();
5721
6022
  }
5722
6023
  });
5723
6024
 
@@ -5749,6 +6050,7 @@ var init_express = __esm({
5749
6050
  init_well_known();
5750
6051
  init_metadata_capture();
5751
6052
  init_anonymous_beacon();
6053
+ init_failure_posture();
5752
6054
  DEFAULT_ROUTES_REFRESH_MS = 5 * 60 * 1e3;
5753
6055
  }
5754
6056
  });
@@ -5764,6 +6066,7 @@ var init_nextjs = __esm({
5764
6066
  init_pdlss_pre_check();
5765
6067
  init_metadata_capture();
5766
6068
  init_anonymous_beacon();
6069
+ init_failure_posture();
5767
6070
  init_http_pdlss();
5768
6071
  init_approval_gate();
5769
6072
  init_verified_hop();
@@ -6085,6 +6388,7 @@ var init_mcp2 = __esm({
6085
6388
  init_approval_gate();
6086
6389
  init_stepup_poll();
6087
6390
  init_well_known();
6391
+ init_failure_posture();
6088
6392
  init_mcp_server();
6089
6393
  }
6090
6394
  });
@@ -6173,6 +6477,7 @@ var init_src = __esm({
6173
6477
  "use strict";
6174
6478
  init_trust_levels();
6175
6479
  init_verify();
6480
+ init_failure_posture();
6176
6481
  init_stepup_poll();
6177
6482
  init_settlement();
6178
6483
  init_express();