@astrasyncai/verification-gateway 5.12.0 → 5.14.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 (75) hide show
  1. package/dist/adapters/express.js +63 -19
  2. package/dist/adapters/express.js.map +1 -1
  3. package/dist/adapters/express.mjs +63 -19
  4. package/dist/adapters/express.mjs.map +1 -1
  5. package/dist/adapters/mcp.js +26 -9
  6. package/dist/adapters/mcp.js.map +1 -1
  7. package/dist/adapters/mcp.mjs +26 -9
  8. package/dist/adapters/mcp.mjs.map +1 -1
  9. package/dist/adapters/nextjs.js +63 -19
  10. package/dist/adapters/nextjs.js.map +1 -1
  11. package/dist/adapters/nextjs.mjs +63 -19
  12. package/dist/adapters/nextjs.mjs.map +1 -1
  13. package/dist/adapters/sdk.js +26 -9
  14. package/dist/adapters/sdk.js.map +1 -1
  15. package/dist/adapters/sdk.mjs +26 -9
  16. package/dist/adapters/sdk.mjs.map +1 -1
  17. package/dist/agent/index.js +1 -1
  18. package/dist/agent/index.js.map +1 -1
  19. package/dist/agent/index.mjs +1 -1
  20. package/dist/agent/index.mjs.map +1 -1
  21. package/dist/bin/astrasync-claude-hook.js +53 -18
  22. package/dist/bin/astrasync-codex-hook.js +53 -18
  23. package/dist/bin/astrasync-guard.js +53 -18
  24. package/dist/bin/astrasync.js +54 -19
  25. package/dist/browser/background.js +53 -18
  26. package/dist/browser/background.js.map +1 -1
  27. package/dist/browser/background.mjs +53 -18
  28. package/dist/browser/background.mjs.map +1 -1
  29. package/dist/cli/index.js +1 -1
  30. package/dist/cli/index.js.map +1 -1
  31. package/dist/cli/index.mjs +1 -1
  32. package/dist/cli/index.mjs.map +1 -1
  33. package/dist/codex/index.js +53 -18
  34. package/dist/codex/index.js.map +1 -1
  35. package/dist/codex/index.mjs +53 -18
  36. package/dist/codex/index.mjs.map +1 -1
  37. package/dist/cursor/extension.js +53 -18
  38. package/dist/cursor/extension.js.map +1 -1
  39. package/dist/cursor/extension.mjs +53 -18
  40. package/dist/cursor/extension.mjs.map +1 -1
  41. package/dist/edge-config.js +1 -1
  42. package/dist/edge-config.js.map +1 -1
  43. package/dist/edge-config.mjs +1 -1
  44. package/dist/edge-config.mjs.map +1 -1
  45. package/dist/edge-core/index.d.mts +32 -4
  46. package/dist/edge-core/index.d.ts +32 -4
  47. package/dist/edge-core/index.js +53 -18
  48. package/dist/edge-core/index.js.map +1 -1
  49. package/dist/edge-core/index.mjs +53 -18
  50. package/dist/edge-core/index.mjs.map +1 -1
  51. package/dist/gateway/gateway.js +53 -18
  52. package/dist/gateway/gateway.js.map +1 -1
  53. package/dist/gateway/gateway.mjs +53 -18
  54. package/dist/gateway/gateway.mjs.map +1 -1
  55. package/dist/index.d.mts +2 -12
  56. package/dist/index.d.ts +2 -12
  57. package/dist/index.js +63 -19
  58. package/dist/index.js.map +1 -1
  59. package/dist/index.mjs +63 -19
  60. package/dist/index.mjs.map +1 -1
  61. package/dist/registration/index.js +1 -1
  62. package/dist/registration/index.js.map +1 -1
  63. package/dist/registration/index.mjs +1 -1
  64. package/dist/registration/index.mjs.map +1 -1
  65. package/dist/transport/index.js +1 -1
  66. package/dist/transport/index.js.map +1 -1
  67. package/dist/transport/index.mjs +1 -1
  68. package/dist/transport/index.mjs.map +1 -1
  69. package/dist/verify.d.mts +0 -10
  70. package/dist/verify.d.ts +0 -10
  71. package/dist/verify.js +36 -10
  72. package/dist/verify.js.map +1 -1
  73. package/dist/verify.mjs +36 -10
  74. package/dist/verify.mjs.map +1 -1
  75. package/package.json +3 -2
@@ -3752,7 +3752,7 @@ function getTrustLevel(score) {
3752
3752
  }
3753
3753
 
3754
3754
  // src/version.ts
3755
- var SDK_VERSION = "5.12.0";
3755
+ var SDK_VERSION = "5.14.0";
3756
3756
 
3757
3757
  // src/http.ts
3758
3758
  var SDK_USER_AGENT = `astrasync-sdk/${SDK_VERSION}`;
@@ -3799,7 +3799,7 @@ var DEFAULT_CONFIG = {
3799
3799
  };
3800
3800
  var initCheckPerformed = false;
3801
3801
  var deprecationWarningShown = false;
3802
- async function performInitCheck(apiBaseUrl, debug, strictInit, apiKey) {
3802
+ async function performInitCheck(apiBaseUrl, debug, strictInit, apiKey, counterparty) {
3803
3803
  initCheckPerformed = true;
3804
3804
  try {
3805
3805
  const probeUrl = `${apiBaseUrl}/agents/verify-access`;
@@ -3817,7 +3817,8 @@ async function performInitCheck(apiBaseUrl, debug, strictInit, apiKey) {
3817
3817
  );
3818
3818
  }
3819
3819
  if (apiKey) {
3820
- const selfCheck = await sdkFetch(`${apiBaseUrl}/agents/verify-access/self-check`, {
3820
+ const q = counterparty?.id ? `?counterpartyId=${encodeURIComponent(counterparty.id)}` : counterparty?.url ? `?counterpartyUrl=${encodeURIComponent(counterparty.url)}` : "";
3821
+ const selfCheck = await sdkFetch(`${apiBaseUrl}/agents/verify-access/self-check${q}`, {
3821
3822
  headers: { Authorization: `Bearer ${apiKey}` }
3822
3823
  });
3823
3824
  if (selfCheck.status === 401) {
@@ -3829,6 +3830,13 @@ async function performInitCheck(apiBaseUrl, debug, strictInit, apiKey) {
3829
3830
  console.error(message);
3830
3831
  } else if (selfCheck.ok) {
3831
3832
  const body = await selfCheck.json().catch(() => ({}));
3833
+ if (body.counterparty && body.counterparty.canVerify === false) {
3834
+ 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.`;
3835
+ if (strictInit) {
3836
+ throw new Error(`${message} (strictInit=true)`);
3837
+ }
3838
+ console.error(message);
3839
+ }
3832
3840
  if (typeof body.daysToExpiry === "number" && body.daysToExpiry <= 7) {
3833
3841
  console.warn(
3834
3842
  `[VerificationGateway] the configured AstraSync API key expires in ${body.daysToExpiry} day(s) \u2014 rotate it at Settings \u2192 Developer Tools before integrations start failing.`
@@ -3933,11 +3941,11 @@ function createGuidanceResponse(_config, reason, options = {}) {
3933
3941
  const isMisconfig = source === "merchant_misconfig";
3934
3942
  const urls = options.urls;
3935
3943
  const guidance = isMisconfig ? {
3936
- 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.",
3944
+ 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.",
3937
3945
  registrationUrl: urls?.registrationUrl ?? "",
3938
3946
  documentationUrl: options.remediation?.docsUrl ?? urls?.documentationUrl ?? "",
3939
3947
  steps: [
3940
- "Report the failure to the site operator \u2014 their AstraSync API key needs rotating",
3948
+ "Report the failure to the site operator \u2014 their AstraSync API key needs replacing",
3941
3949
  "Do not retry: the failure is deterministic until the operator updates their key"
3942
3950
  ]
3943
3951
  } : isApiError ? {
@@ -4149,12 +4157,13 @@ async function callVerifyAccessAPI(config, request) {
4149
4157
  };
4150
4158
  }
4151
4159
  if (!response.ok) {
4152
- if (response.status === 401) {
4160
+ const keyMismatch = response.status === 403 && data?.error === "COUNTERPARTY_KEY_MISMATCH";
4161
+ if (response.status === 401 || keyMismatch) {
4153
4162
  return {
4154
4163
  success: false,
4155
4164
  error: data.message || data.error || "Integration API key rejected",
4156
4165
  misconfig: true,
4157
- misconfigCode: typeof data?.code === "string" ? data.code : void 0,
4166
+ misconfigCode: keyMismatch ? "counterparty_key_mismatch" : typeof data?.code === "string" ? data.code : void 0,
4158
4167
  remediation: data?.remediation && typeof data.remediation === "object" ? data.remediation : void 0,
4159
4168
  correlationId: typeof data?.correlationId === "string" ? data.correlationId : void 0,
4160
4169
  attemptId: typeof data?.attemptId === "string" ? data.attemptId : void 0
@@ -4187,14 +4196,22 @@ async function verify(config, request, options) {
4187
4196
  mergedConfig.apiBaseUrl,
4188
4197
  mergedConfig.debug,
4189
4198
  true,
4190
- mergedConfig.apiKey
4199
+ mergedConfig.apiKey,
4200
+ {
4201
+ id: mergedConfig.counterpartyId,
4202
+ url: mergedConfig.counterpartyUrl ?? request.counterpartyUrl
4203
+ }
4191
4204
  );
4192
4205
  } else {
4193
4206
  void performInitCheck(
4194
4207
  mergedConfig.apiBaseUrl,
4195
4208
  mergedConfig.debug,
4196
4209
  false,
4197
- mergedConfig.apiKey
4210
+ mergedConfig.apiKey,
4211
+ {
4212
+ id: mergedConfig.counterpartyId,
4213
+ url: mergedConfig.counterpartyUrl ?? request.counterpartyUrl
4214
+ }
4198
4215
  );
4199
4216
  }
4200
4217
  }
@@ -4405,6 +4422,11 @@ async function withBudget(promise, ms) {
4405
4422
  if (timer) clearTimeout(timer);
4406
4423
  }
4407
4424
  }
4425
+ var CONTAINER_ID = (() => {
4426
+ const c = globalThis.crypto;
4427
+ if (c?.randomUUID) return c.randomUUID();
4428
+ return `c-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 12)}`;
4429
+ })();
4408
4430
  var destKeyOf = (d) => `${d.apiBaseUrl}\0${d.apiKey ?? ""}\0${d.counterparty}\0${d.adapter ?? ""}`;
4409
4431
  var BeaconBuffer = class {
4410
4432
  constructor(options = {}) {
@@ -4412,6 +4434,7 @@ var BeaconBuffer = class {
4412
4434
  this.droppedSinceLast = 0;
4413
4435
  this.droppedTotal = 0;
4414
4436
  this.deliveredTotal = 0;
4437
+ this.enqueuedTotal = 0;
4415
4438
  this.backoffUntil = 0;
4416
4439
  this.collapse = /* @__PURE__ */ new Map();
4417
4440
  /** Keys the backend answered 401 for — later batches go unauthenticated. */
@@ -4424,13 +4447,16 @@ var BeaconBuffer = class {
4424
4447
  collapseMaxKeys: options.collapseMaxKeys ?? 1e3,
4425
4448
  requestTimeoutMs: options.requestTimeoutMs ?? 1e4,
4426
4449
  retryBackoffMs: options.retryBackoffMs ?? 1e3,
4450
+ holdRepeats: options.holdRepeats ?? false,
4451
+ containerId: options.containerId ?? CONTAINER_ID,
4427
4452
  now: options.now ?? Date.now
4428
4453
  };
4429
4454
  }
4430
4455
  /**
4431
4456
  * Queue one beacon. With a `collapseKey`, a repeat inside the window is
4432
- * merged into a held aggregate (sent when the window closes) instead of
4433
- * becoming its own item.
4457
+ * merged into this key's item while that item is still queued (not yet in
4458
+ * flight). Once it has left, the repeat becomes a new item, sendable on
4459
+ * this flush — nothing waits for a later invocation (round 27.6).
4434
4460
  */
4435
4461
  enqueue(dest, item, collapseKey) {
4436
4462
  const now = this.opts.now();
@@ -4443,12 +4469,15 @@ var BeaconBuffer = class {
4443
4469
  return;
4444
4470
  }
4445
4471
  }
4446
- const held = this.push(
4447
- dest,
4448
- { ...item, repeatCount: 1 },
4449
- entry.firstSeenAt + this.opts.collapseWindowMs
4450
- );
4451
- entry.held = held;
4472
+ if (this.opts.holdRepeats) {
4473
+ entry.held = this.push(
4474
+ dest,
4475
+ { ...item, repeatCount: 1 },
4476
+ entry.firstSeenAt + this.opts.collapseWindowMs
4477
+ );
4478
+ return;
4479
+ }
4480
+ entry.first = this.push(dest, { ...item, repeatCount: 1 }, now);
4452
4481
  return;
4453
4482
  }
4454
4483
  this.collapse.delete(collapseKey);
@@ -4484,6 +4513,7 @@ var BeaconBuffer = class {
4484
4513
  droppedSinceLast: this.droppedSinceLast,
4485
4514
  droppedTotal: this.droppedTotal,
4486
4515
  deliveredTotal: this.deliveredTotal,
4516
+ enqueuedTotal: this.enqueuedTotal,
4487
4517
  flushing: this.inFlight !== void 0
4488
4518
  };
4489
4519
  }
@@ -4498,6 +4528,7 @@ var BeaconBuffer = class {
4498
4528
  inFlight: false
4499
4529
  };
4500
4530
  this.queue.push(entry);
4531
+ this.enqueuedTotal += 1;
4501
4532
  return entry;
4502
4533
  }
4503
4534
  dropOldest() {
@@ -4543,7 +4574,11 @@ var BeaconBuffer = class {
4543
4574
  droppedSinceLast: reportedDropped,
4544
4575
  queueDepth: this.queue.length,
4545
4576
  adapter: dest.adapter ?? "edge-core",
4546
- version: SDK_VERSION
4577
+ version: SDK_VERSION,
4578
+ containerId: this.opts.containerId,
4579
+ enqueuedTotal: this.enqueuedTotal,
4580
+ deliveredTotal: this.deliveredTotal,
4581
+ droppedTotal: this.droppedTotal
4547
4582
  };
4548
4583
  const controller = typeof AbortController !== "undefined" ? new AbortController() : void 0;
4549
4584
  const response = await withBudget(
@@ -3751,7 +3751,7 @@ function getTrustLevel(score) {
3751
3751
  }
3752
3752
 
3753
3753
  // src/version.ts
3754
- var SDK_VERSION = "5.12.0";
3754
+ var SDK_VERSION = "5.14.0";
3755
3755
 
3756
3756
  // src/http.ts
3757
3757
  var SDK_USER_AGENT = `astrasync-sdk/${SDK_VERSION}`;
@@ -3798,7 +3798,7 @@ var DEFAULT_CONFIG = {
3798
3798
  };
3799
3799
  var initCheckPerformed = false;
3800
3800
  var deprecationWarningShown = false;
3801
- async function performInitCheck(apiBaseUrl, debug, strictInit, apiKey) {
3801
+ async function performInitCheck(apiBaseUrl, debug, strictInit, apiKey, counterparty) {
3802
3802
  initCheckPerformed = true;
3803
3803
  try {
3804
3804
  const probeUrl = `${apiBaseUrl}/agents/verify-access`;
@@ -3816,7 +3816,8 @@ async function performInitCheck(apiBaseUrl, debug, strictInit, apiKey) {
3816
3816
  );
3817
3817
  }
3818
3818
  if (apiKey) {
3819
- const selfCheck = await sdkFetch(`${apiBaseUrl}/agents/verify-access/self-check`, {
3819
+ const q = counterparty?.id ? `?counterpartyId=${encodeURIComponent(counterparty.id)}` : counterparty?.url ? `?counterpartyUrl=${encodeURIComponent(counterparty.url)}` : "";
3820
+ const selfCheck = await sdkFetch(`${apiBaseUrl}/agents/verify-access/self-check${q}`, {
3820
3821
  headers: { Authorization: `Bearer ${apiKey}` }
3821
3822
  });
3822
3823
  if (selfCheck.status === 401) {
@@ -3828,6 +3829,13 @@ async function performInitCheck(apiBaseUrl, debug, strictInit, apiKey) {
3828
3829
  console.error(message);
3829
3830
  } else if (selfCheck.ok) {
3830
3831
  const body = await selfCheck.json().catch(() => ({}));
3832
+ if (body.counterparty && body.counterparty.canVerify === false) {
3833
+ 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.`;
3834
+ if (strictInit) {
3835
+ throw new Error(`${message} (strictInit=true)`);
3836
+ }
3837
+ console.error(message);
3838
+ }
3831
3839
  if (typeof body.daysToExpiry === "number" && body.daysToExpiry <= 7) {
3832
3840
  console.warn(
3833
3841
  `[VerificationGateway] the configured AstraSync API key expires in ${body.daysToExpiry} day(s) \u2014 rotate it at Settings \u2192 Developer Tools before integrations start failing.`
@@ -3932,11 +3940,11 @@ function createGuidanceResponse(_config, reason, options = {}) {
3932
3940
  const isMisconfig = source === "merchant_misconfig";
3933
3941
  const urls = options.urls;
3934
3942
  const guidance = isMisconfig ? {
3935
- 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.",
3943
+ 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.",
3936
3944
  registrationUrl: urls?.registrationUrl ?? "",
3937
3945
  documentationUrl: options.remediation?.docsUrl ?? urls?.documentationUrl ?? "",
3938
3946
  steps: [
3939
- "Report the failure to the site operator \u2014 their AstraSync API key needs rotating",
3947
+ "Report the failure to the site operator \u2014 their AstraSync API key needs replacing",
3940
3948
  "Do not retry: the failure is deterministic until the operator updates their key"
3941
3949
  ]
3942
3950
  } : isApiError ? {
@@ -4148,12 +4156,13 @@ async function callVerifyAccessAPI(config, request) {
4148
4156
  };
4149
4157
  }
4150
4158
  if (!response.ok) {
4151
- if (response.status === 401) {
4159
+ const keyMismatch = response.status === 403 && data?.error === "COUNTERPARTY_KEY_MISMATCH";
4160
+ if (response.status === 401 || keyMismatch) {
4152
4161
  return {
4153
4162
  success: false,
4154
4163
  error: data.message || data.error || "Integration API key rejected",
4155
4164
  misconfig: true,
4156
- misconfigCode: typeof data?.code === "string" ? data.code : void 0,
4165
+ misconfigCode: keyMismatch ? "counterparty_key_mismatch" : typeof data?.code === "string" ? data.code : void 0,
4157
4166
  remediation: data?.remediation && typeof data.remediation === "object" ? data.remediation : void 0,
4158
4167
  correlationId: typeof data?.correlationId === "string" ? data.correlationId : void 0,
4159
4168
  attemptId: typeof data?.attemptId === "string" ? data.attemptId : void 0
@@ -4186,14 +4195,22 @@ async function verify(config, request, options) {
4186
4195
  mergedConfig.apiBaseUrl,
4187
4196
  mergedConfig.debug,
4188
4197
  true,
4189
- mergedConfig.apiKey
4198
+ mergedConfig.apiKey,
4199
+ {
4200
+ id: mergedConfig.counterpartyId,
4201
+ url: mergedConfig.counterpartyUrl ?? request.counterpartyUrl
4202
+ }
4190
4203
  );
4191
4204
  } else {
4192
4205
  void performInitCheck(
4193
4206
  mergedConfig.apiBaseUrl,
4194
4207
  mergedConfig.debug,
4195
4208
  false,
4196
- mergedConfig.apiKey
4209
+ mergedConfig.apiKey,
4210
+ {
4211
+ id: mergedConfig.counterpartyId,
4212
+ url: mergedConfig.counterpartyUrl ?? request.counterpartyUrl
4213
+ }
4197
4214
  );
4198
4215
  }
4199
4216
  }
@@ -4404,6 +4421,11 @@ async function withBudget(promise, ms) {
4404
4421
  if (timer) clearTimeout(timer);
4405
4422
  }
4406
4423
  }
4424
+ var CONTAINER_ID = (() => {
4425
+ const c = globalThis.crypto;
4426
+ if (c?.randomUUID) return c.randomUUID();
4427
+ return `c-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 12)}`;
4428
+ })();
4407
4429
  var destKeyOf = (d) => `${d.apiBaseUrl}\0${d.apiKey ?? ""}\0${d.counterparty}\0${d.adapter ?? ""}`;
4408
4430
  var BeaconBuffer = class {
4409
4431
  constructor(options = {}) {
@@ -4411,6 +4433,7 @@ var BeaconBuffer = class {
4411
4433
  this.droppedSinceLast = 0;
4412
4434
  this.droppedTotal = 0;
4413
4435
  this.deliveredTotal = 0;
4436
+ this.enqueuedTotal = 0;
4414
4437
  this.backoffUntil = 0;
4415
4438
  this.collapse = /* @__PURE__ */ new Map();
4416
4439
  /** Keys the backend answered 401 for — later batches go unauthenticated. */
@@ -4423,13 +4446,16 @@ var BeaconBuffer = class {
4423
4446
  collapseMaxKeys: options.collapseMaxKeys ?? 1e3,
4424
4447
  requestTimeoutMs: options.requestTimeoutMs ?? 1e4,
4425
4448
  retryBackoffMs: options.retryBackoffMs ?? 1e3,
4449
+ holdRepeats: options.holdRepeats ?? false,
4450
+ containerId: options.containerId ?? CONTAINER_ID,
4426
4451
  now: options.now ?? Date.now
4427
4452
  };
4428
4453
  }
4429
4454
  /**
4430
4455
  * Queue one beacon. With a `collapseKey`, a repeat inside the window is
4431
- * merged into a held aggregate (sent when the window closes) instead of
4432
- * becoming its own item.
4456
+ * merged into this key's item while that item is still queued (not yet in
4457
+ * flight). Once it has left, the repeat becomes a new item, sendable on
4458
+ * this flush — nothing waits for a later invocation (round 27.6).
4433
4459
  */
4434
4460
  enqueue(dest, item, collapseKey) {
4435
4461
  const now = this.opts.now();
@@ -4442,12 +4468,15 @@ var BeaconBuffer = class {
4442
4468
  return;
4443
4469
  }
4444
4470
  }
4445
- const held = this.push(
4446
- dest,
4447
- { ...item, repeatCount: 1 },
4448
- entry.firstSeenAt + this.opts.collapseWindowMs
4449
- );
4450
- entry.held = held;
4471
+ if (this.opts.holdRepeats) {
4472
+ entry.held = this.push(
4473
+ dest,
4474
+ { ...item, repeatCount: 1 },
4475
+ entry.firstSeenAt + this.opts.collapseWindowMs
4476
+ );
4477
+ return;
4478
+ }
4479
+ entry.first = this.push(dest, { ...item, repeatCount: 1 }, now);
4451
4480
  return;
4452
4481
  }
4453
4482
  this.collapse.delete(collapseKey);
@@ -4483,6 +4512,7 @@ var BeaconBuffer = class {
4483
4512
  droppedSinceLast: this.droppedSinceLast,
4484
4513
  droppedTotal: this.droppedTotal,
4485
4514
  deliveredTotal: this.deliveredTotal,
4515
+ enqueuedTotal: this.enqueuedTotal,
4486
4516
  flushing: this.inFlight !== void 0
4487
4517
  };
4488
4518
  }
@@ -4497,6 +4527,7 @@ var BeaconBuffer = class {
4497
4527
  inFlight: false
4498
4528
  };
4499
4529
  this.queue.push(entry);
4530
+ this.enqueuedTotal += 1;
4500
4531
  return entry;
4501
4532
  }
4502
4533
  dropOldest() {
@@ -4542,7 +4573,11 @@ var BeaconBuffer = class {
4542
4573
  droppedSinceLast: reportedDropped,
4543
4574
  queueDepth: this.queue.length,
4544
4575
  adapter: dest.adapter ?? "edge-core",
4545
- version: SDK_VERSION
4576
+ version: SDK_VERSION,
4577
+ containerId: this.opts.containerId,
4578
+ enqueuedTotal: this.enqueuedTotal,
4579
+ deliveredTotal: this.deliveredTotal,
4580
+ droppedTotal: this.droppedTotal
4546
4581
  };
4547
4582
  const controller = typeof AbortController !== "undefined" ? new AbortController() : void 0;
4548
4583
  const response = await withBudget(
@@ -3713,7 +3713,7 @@ Policy saved to ${outputPath}`);
3713
3713
  };
3714
3714
 
3715
3715
  // src/version.ts
3716
- var SDK_VERSION = "5.12.0";
3716
+ var SDK_VERSION = "5.14.0";
3717
3717
 
3718
3718
  // src/http.ts
3719
3719
  var SDK_USER_AGENT = `astrasync-sdk/${SDK_VERSION}`;
@@ -4664,7 +4664,7 @@ var DEFAULT_CONFIG = {
4664
4664
  };
4665
4665
  var initCheckPerformed = false;
4666
4666
  var deprecationWarningShown = false;
4667
- async function performInitCheck(apiBaseUrl, debug, strictInit, apiKey) {
4667
+ async function performInitCheck(apiBaseUrl, debug, strictInit, apiKey, counterparty) {
4668
4668
  initCheckPerformed = true;
4669
4669
  try {
4670
4670
  const probeUrl = `${apiBaseUrl}/agents/verify-access`;
@@ -4682,7 +4682,8 @@ async function performInitCheck(apiBaseUrl, debug, strictInit, apiKey) {
4682
4682
  );
4683
4683
  }
4684
4684
  if (apiKey) {
4685
- const selfCheck = await sdkFetch(`${apiBaseUrl}/agents/verify-access/self-check`, {
4685
+ const q = counterparty?.id ? `?counterpartyId=${encodeURIComponent(counterparty.id)}` : counterparty?.url ? `?counterpartyUrl=${encodeURIComponent(counterparty.url)}` : "";
4686
+ const selfCheck = await sdkFetch(`${apiBaseUrl}/agents/verify-access/self-check${q}`, {
4686
4687
  headers: { Authorization: `Bearer ${apiKey}` }
4687
4688
  });
4688
4689
  if (selfCheck.status === 401) {
@@ -4694,6 +4695,13 @@ async function performInitCheck(apiBaseUrl, debug, strictInit, apiKey) {
4694
4695
  console.error(message);
4695
4696
  } else if (selfCheck.ok) {
4696
4697
  const body = await selfCheck.json().catch(() => ({}));
4698
+ if (body.counterparty && body.counterparty.canVerify === false) {
4699
+ 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.`;
4700
+ if (strictInit) {
4701
+ throw new Error(`${message} (strictInit=true)`);
4702
+ }
4703
+ console.error(message);
4704
+ }
4697
4705
  if (typeof body.daysToExpiry === "number" && body.daysToExpiry <= 7) {
4698
4706
  console.warn(
4699
4707
  `[VerificationGateway] the configured AstraSync API key expires in ${body.daysToExpiry} day(s) \u2014 rotate it at Settings \u2192 Developer Tools before integrations start failing.`
@@ -4798,11 +4806,11 @@ function createGuidanceResponse(_config, reason, options = {}) {
4798
4806
  const isMisconfig = source === "merchant_misconfig";
4799
4807
  const urls = options.urls;
4800
4808
  const guidance = isMisconfig ? {
4801
- 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.",
4809
+ 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.",
4802
4810
  registrationUrl: urls?.registrationUrl ?? "",
4803
4811
  documentationUrl: options.remediation?.docsUrl ?? urls?.documentationUrl ?? "",
4804
4812
  steps: [
4805
- "Report the failure to the site operator \u2014 their AstraSync API key needs rotating",
4813
+ "Report the failure to the site operator \u2014 their AstraSync API key needs replacing",
4806
4814
  "Do not retry: the failure is deterministic until the operator updates their key"
4807
4815
  ]
4808
4816
  } : isApiError ? {
@@ -5014,12 +5022,13 @@ async function callVerifyAccessAPI(config, request2) {
5014
5022
  };
5015
5023
  }
5016
5024
  if (!response.ok) {
5017
- if (response.status === 401) {
5025
+ const keyMismatch = response.status === 403 && data?.error === "COUNTERPARTY_KEY_MISMATCH";
5026
+ if (response.status === 401 || keyMismatch) {
5018
5027
  return {
5019
5028
  success: false,
5020
5029
  error: data.message || data.error || "Integration API key rejected",
5021
5030
  misconfig: true,
5022
- misconfigCode: typeof data?.code === "string" ? data.code : void 0,
5031
+ misconfigCode: keyMismatch ? "counterparty_key_mismatch" : typeof data?.code === "string" ? data.code : void 0,
5023
5032
  remediation: data?.remediation && typeof data.remediation === "object" ? data.remediation : void 0,
5024
5033
  correlationId: typeof data?.correlationId === "string" ? data.correlationId : void 0,
5025
5034
  attemptId: typeof data?.attemptId === "string" ? data.attemptId : void 0
@@ -5052,14 +5061,22 @@ async function verify(config, request2, options) {
5052
5061
  mergedConfig.apiBaseUrl,
5053
5062
  mergedConfig.debug,
5054
5063
  true,
5055
- mergedConfig.apiKey
5064
+ mergedConfig.apiKey,
5065
+ {
5066
+ id: mergedConfig.counterpartyId,
5067
+ url: mergedConfig.counterpartyUrl ?? request2.counterpartyUrl
5068
+ }
5056
5069
  );
5057
5070
  } else {
5058
5071
  void performInitCheck(
5059
5072
  mergedConfig.apiBaseUrl,
5060
5073
  mergedConfig.debug,
5061
5074
  false,
5062
- mergedConfig.apiKey
5075
+ mergedConfig.apiKey,
5076
+ {
5077
+ id: mergedConfig.counterpartyId,
5078
+ url: mergedConfig.counterpartyUrl ?? request2.counterpartyUrl
5079
+ }
5063
5080
  );
5064
5081
  }
5065
5082
  }
@@ -5270,6 +5287,11 @@ async function withBudget(promise, ms) {
5270
5287
  if (timer) clearTimeout(timer);
5271
5288
  }
5272
5289
  }
5290
+ var CONTAINER_ID = (() => {
5291
+ const c = globalThis.crypto;
5292
+ if (c?.randomUUID) return c.randomUUID();
5293
+ return `c-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 12)}`;
5294
+ })();
5273
5295
  var destKeyOf = (d) => `${d.apiBaseUrl}\0${d.apiKey ?? ""}\0${d.counterparty}\0${d.adapter ?? ""}`;
5274
5296
  var BeaconBuffer = class {
5275
5297
  constructor(options = {}) {
@@ -5277,6 +5299,7 @@ var BeaconBuffer = class {
5277
5299
  this.droppedSinceLast = 0;
5278
5300
  this.droppedTotal = 0;
5279
5301
  this.deliveredTotal = 0;
5302
+ this.enqueuedTotal = 0;
5280
5303
  this.backoffUntil = 0;
5281
5304
  this.collapse = /* @__PURE__ */ new Map();
5282
5305
  /** Keys the backend answered 401 for — later batches go unauthenticated. */
@@ -5289,13 +5312,16 @@ var BeaconBuffer = class {
5289
5312
  collapseMaxKeys: options.collapseMaxKeys ?? 1e3,
5290
5313
  requestTimeoutMs: options.requestTimeoutMs ?? 1e4,
5291
5314
  retryBackoffMs: options.retryBackoffMs ?? 1e3,
5315
+ holdRepeats: options.holdRepeats ?? false,
5316
+ containerId: options.containerId ?? CONTAINER_ID,
5292
5317
  now: options.now ?? Date.now
5293
5318
  };
5294
5319
  }
5295
5320
  /**
5296
5321
  * Queue one beacon. With a `collapseKey`, a repeat inside the window is
5297
- * merged into a held aggregate (sent when the window closes) instead of
5298
- * becoming its own item.
5322
+ * merged into this key's item while that item is still queued (not yet in
5323
+ * flight). Once it has left, the repeat becomes a new item, sendable on
5324
+ * this flush — nothing waits for a later invocation (round 27.6).
5299
5325
  */
5300
5326
  enqueue(dest, item, collapseKey) {
5301
5327
  const now = this.opts.now();
@@ -5308,12 +5334,15 @@ var BeaconBuffer = class {
5308
5334
  return;
5309
5335
  }
5310
5336
  }
5311
- const held = this.push(
5312
- dest,
5313
- { ...item, repeatCount: 1 },
5314
- entry.firstSeenAt + this.opts.collapseWindowMs
5315
- );
5316
- entry.held = held;
5337
+ if (this.opts.holdRepeats) {
5338
+ entry.held = this.push(
5339
+ dest,
5340
+ { ...item, repeatCount: 1 },
5341
+ entry.firstSeenAt + this.opts.collapseWindowMs
5342
+ );
5343
+ return;
5344
+ }
5345
+ entry.first = this.push(dest, { ...item, repeatCount: 1 }, now);
5317
5346
  return;
5318
5347
  }
5319
5348
  this.collapse.delete(collapseKey);
@@ -5349,6 +5378,7 @@ var BeaconBuffer = class {
5349
5378
  droppedSinceLast: this.droppedSinceLast,
5350
5379
  droppedTotal: this.droppedTotal,
5351
5380
  deliveredTotal: this.deliveredTotal,
5381
+ enqueuedTotal: this.enqueuedTotal,
5352
5382
  flushing: this.inFlight !== void 0
5353
5383
  };
5354
5384
  }
@@ -5363,6 +5393,7 @@ var BeaconBuffer = class {
5363
5393
  inFlight: false
5364
5394
  };
5365
5395
  this.queue.push(entry);
5396
+ this.enqueuedTotal += 1;
5366
5397
  return entry;
5367
5398
  }
5368
5399
  dropOldest() {
@@ -5408,7 +5439,11 @@ var BeaconBuffer = class {
5408
5439
  droppedSinceLast: reportedDropped,
5409
5440
  queueDepth: this.queue.length,
5410
5441
  adapter: dest.adapter ?? "edge-core",
5411
- version: SDK_VERSION
5442
+ version: SDK_VERSION,
5443
+ containerId: this.opts.containerId,
5444
+ enqueuedTotal: this.enqueuedTotal,
5445
+ deliveredTotal: this.deliveredTotal,
5446
+ droppedTotal: this.droppedTotal
5412
5447
  };
5413
5448
  const controller = typeof AbortController !== "undefined" ? new AbortController() : void 0;
5414
5449
  const response = await withBudget(