@astrasyncai/verification-gateway 5.18.1 → 5.20.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 (129) hide show
  1. package/CHANGELOG.md +232 -0
  2. package/README.md +217 -4
  3. package/dist/adapter-interface/interface.d.mts +2 -2
  4. package/dist/adapter-interface/interface.d.ts +2 -2
  5. package/dist/adapters/express.d.mts +9 -1
  6. package/dist/adapters/express.d.ts +9 -1
  7. package/dist/adapters/express.js +160 -52
  8. package/dist/adapters/express.js.map +1 -1
  9. package/dist/adapters/express.mjs +160 -52
  10. package/dist/adapters/express.mjs.map +1 -1
  11. package/dist/adapters/http-pdlss.d.mts +7 -1
  12. package/dist/adapters/http-pdlss.d.ts +7 -1
  13. package/dist/adapters/http-pdlss.js +18 -2
  14. package/dist/adapters/http-pdlss.js.map +1 -1
  15. package/dist/adapters/http-pdlss.mjs +16 -1
  16. package/dist/adapters/http-pdlss.mjs.map +1 -1
  17. package/dist/adapters/kit-express.d.mts +93 -1
  18. package/dist/adapters/kit-express.d.ts +93 -1
  19. package/dist/adapters/kit-express.js +181 -2
  20. package/dist/adapters/kit-express.js.map +1 -1
  21. package/dist/adapters/kit-express.mjs +180 -2
  22. package/dist/adapters/kit-express.mjs.map +1 -1
  23. package/dist/adapters/mcp.d.mts +102 -9
  24. package/dist/adapters/mcp.d.ts +102 -9
  25. package/dist/adapters/mcp.js +242 -19
  26. package/dist/adapters/mcp.js.map +1 -1
  27. package/dist/adapters/mcp.mjs +240 -19
  28. package/dist/adapters/mcp.mjs.map +1 -1
  29. package/dist/adapters/nextjs.d.mts +1 -1
  30. package/dist/adapters/nextjs.d.ts +1 -1
  31. package/dist/adapters/nextjs.js +97 -14
  32. package/dist/adapters/nextjs.js.map +1 -1
  33. package/dist/adapters/nextjs.mjs +97 -14
  34. package/dist/adapters/nextjs.mjs.map +1 -1
  35. package/dist/adapters/sdk.d.mts +101 -2
  36. package/dist/adapters/sdk.d.ts +101 -2
  37. package/dist/adapters/sdk.js +199 -12
  38. package/dist/adapters/sdk.js.map +1 -1
  39. package/dist/adapters/sdk.mjs +199 -12
  40. package/dist/adapters/sdk.mjs.map +1 -1
  41. package/dist/agent/index.d.mts +1 -1
  42. package/dist/agent/index.d.ts +1 -1
  43. package/dist/agent/index.js +1 -1
  44. package/dist/agent/index.js.map +1 -1
  45. package/dist/agent/index.mjs +1 -1
  46. package/dist/agent/index.mjs.map +1 -1
  47. package/dist/bin/astrasync-claude-hook.js +94 -16
  48. package/dist/bin/astrasync-codex-hook.js +94 -16
  49. package/dist/bin/astrasync-guard.js +94 -16
  50. package/dist/bin/astrasync.js +127 -16
  51. package/dist/browser/background.js +94 -16
  52. package/dist/browser/background.js.map +1 -1
  53. package/dist/browser/background.mjs +94 -16
  54. package/dist/browser/background.mjs.map +1 -1
  55. package/dist/cli/index.js +1 -1
  56. package/dist/cli/index.js.map +1 -1
  57. package/dist/cli/index.mjs +1 -1
  58. package/dist/cli/index.mjs.map +1 -1
  59. package/dist/codex/index.js +94 -16
  60. package/dist/codex/index.js.map +1 -1
  61. package/dist/codex/index.mjs +94 -16
  62. package/dist/codex/index.mjs.map +1 -1
  63. package/dist/cursor/extension.js +94 -16
  64. package/dist/cursor/extension.js.map +1 -1
  65. package/dist/cursor/extension.mjs +94 -16
  66. package/dist/cursor/extension.mjs.map +1 -1
  67. package/dist/edge-config.d.mts +1 -1
  68. package/dist/edge-config.d.ts +1 -1
  69. package/dist/edge-config.js +1 -1
  70. package/dist/edge-config.js.map +1 -1
  71. package/dist/edge-config.mjs +1 -1
  72. package/dist/edge-config.mjs.map +1 -1
  73. package/dist/edge-core/index.d.mts +1 -1
  74. package/dist/edge-core/index.d.ts +1 -1
  75. package/dist/edge-core/index.js +69 -12
  76. package/dist/edge-core/index.js.map +1 -1
  77. package/dist/edge-core/index.mjs +69 -12
  78. package/dist/edge-core/index.mjs.map +1 -1
  79. package/dist/gateway/gateway.d.mts +2 -2
  80. package/dist/gateway/gateway.d.ts +2 -2
  81. package/dist/gateway/gateway.js +94 -16
  82. package/dist/gateway/gateway.js.map +1 -1
  83. package/dist/gateway/gateway.mjs +94 -16
  84. package/dist/gateway/gateway.mjs.map +1 -1
  85. package/dist/git-trigger/git-hooks.d.mts +1 -1
  86. package/dist/git-trigger/git-hooks.d.ts +1 -1
  87. package/dist/index.d.mts +706 -34
  88. package/dist/index.d.ts +706 -34
  89. package/dist/index.js +1391 -205
  90. package/dist/index.js.map +1 -1
  91. package/dist/index.mjs +1374 -195
  92. package/dist/index.mjs.map +1 -1
  93. package/dist/local-evaluator/evaluator.d.mts +2 -2
  94. package/dist/local-evaluator/evaluator.d.ts +2 -2
  95. package/dist/registration/index.d.mts +11 -2
  96. package/dist/registration/index.d.ts +11 -2
  97. package/dist/registration/index.js +1 -1
  98. package/dist/registration/index.js.map +1 -1
  99. package/dist/registration/index.mjs +1 -1
  100. package/dist/registration/index.mjs.map +1 -1
  101. package/dist/transport/index.d.mts +1 -1
  102. package/dist/transport/index.d.ts +1 -1
  103. package/dist/transport/index.js +3 -2
  104. package/dist/transport/index.js.map +1 -1
  105. package/dist/transport/index.mjs +3 -2
  106. package/dist/transport/index.mjs.map +1 -1
  107. package/dist/{types-BRz2U0Pn.d.ts → types-B_HLhZ9n.d.ts} +1 -1
  108. package/dist/{types-BfILnheI.d.mts → types-CU17R4Nr.d.mts} +11 -0
  109. package/dist/{types-BfILnheI.d.ts → types-CU17R4Nr.d.ts} +11 -0
  110. package/dist/{types-Cns4l8gX.d.mts → types-CxjqmPl-.d.mts} +115 -13
  111. package/dist/{types-Cns4l8gX.d.ts → types-CxjqmPl-.d.ts} +115 -13
  112. package/dist/{types-3ObBpsnf.d.mts → types-D37aSQ9c.d.mts} +106 -3
  113. package/dist/{types-CfBpm3w5.d.mts → types-Djpz-L0w.d.mts} +1 -1
  114. package/dist/{types-BoxZEYPS.d.ts → types-Dn8tXnei.d.ts} +106 -3
  115. package/dist/ui/index.d.mts +1 -1
  116. package/dist/ui/index.d.ts +1 -1
  117. package/dist/verify.d.mts +50 -4
  118. package/dist/verify.d.ts +50 -4
  119. package/dist/verify.js +93 -14
  120. package/dist/verify.js.map +1 -1
  121. package/dist/verify.mjs +91 -14
  122. package/dist/verify.mjs.map +1 -1
  123. package/dist/webhooks.d.mts +16 -3
  124. package/dist/webhooks.d.ts +16 -3
  125. package/dist/webhooks.js +24 -13
  126. package/dist/webhooks.js.map +1 -1
  127. package/dist/webhooks.mjs +24 -13
  128. package/dist/webhooks.mjs.map +1 -1
  129. package/package.json +1 -1
@@ -3752,7 +3752,7 @@ function getTrustLevel(score) {
3752
3752
  }
3753
3753
 
3754
3754
  // src/version.ts
3755
- var SDK_VERSION = "5.18.1";
3755
+ var SDK_VERSION = "5.20.0";
3756
3756
 
3757
3757
  // src/http.ts
3758
3758
  var SDK_USER_AGENT = `astrasync-sdk/${SDK_VERSION}`;
@@ -3907,7 +3907,12 @@ function getCacheKey(request, counterpartyId) {
3907
3907
  // its start_checkout quote was served the cached quote-leg grant (no
3908
3908
  // settlementOutcome) and never reached the backend. Same F-A1-07 bug class.
3909
3909
  request.commercePhase || "",
3910
- request.checkoutSessionId || ""
3910
+ request.checkoutSessionId || "",
3911
+ // 5.19.0: a route_config purpose claim is checked server-side against
3912
+ // the stored rule for this method + path, so it changes the verdict.
3913
+ request.purposeSource || "",
3914
+ request.requestMethod || "",
3915
+ request.requestPath || ""
3911
3916
  ].join("|");
3912
3917
  }
3913
3918
  function getCachedResult(request, counterpartyId) {
@@ -4065,6 +4070,9 @@ async function callVerifyAccessAPI(config, request) {
4065
4070
  if (requestData.enableRuntimeChallenge)
4066
4071
  body.enableRuntimeChallenge = requestData.enableRuntimeChallenge;
4067
4072
  if (requestData.routePolicy) body.routePolicy = requestData.routePolicy;
4073
+ if (config.actingProof?.connectorToken) {
4074
+ body.actingProof = { connectorToken: config.actingProof.connectorToken };
4075
+ }
4068
4076
  if (requestData.approvalHeldByRoute) body.approvalHeldByRoute = true;
4069
4077
  if (requestData.createSession) body.createSession = requestData.createSession;
4070
4078
  if (requestData.durationRequired) body.durationRequired = requestData.durationRequired;
@@ -4081,6 +4089,11 @@ async function callVerifyAccessAPI(config, request) {
4081
4089
  if (requestData.buyerEmail) body.buyerEmail = requestData.buyerEmail;
4082
4090
  if (requestData.commerceArtifacts) body.commerceArtifacts = requestData.commerceArtifacts;
4083
4091
  if (requestData.attemptId) body.attemptId = requestData.attemptId;
4092
+ if (requestData.purposeSource) body.purposeSource = requestData.purposeSource;
4093
+ if (requestData.agentArtifacts) body.agentArtifacts = requestData.agentArtifacts;
4094
+ if (requestData.metadata) body.metadata = requestData.metadata;
4095
+ if (requestData.requestMethod) body.requestMethod = requestData.requestMethod;
4096
+ if (requestData.requestPath) body.requestPath = requestData.requestPath;
4084
4097
  if (requestData.considerationSet) body.considerationSet = requestData.considerationSet;
4085
4098
  body.sdkVersion = SDK_VERSION;
4086
4099
  if (requestData.callerMetadata || requestData.clientIp || requestData.userAgent) {
@@ -4227,22 +4240,43 @@ async function verify(config, request, options) {
4227
4240
  "[VerificationGateway] minTrustScore / minTrustScoreForFull are deprecated in v2.3.0 and have no effect. Server is now the single source of truth for access decisions (verified ID + runtime challenge + PDLSS + trust score). To gate access to an endpoint, configure the endpoint's trust_score_requirement server-side."
4228
4241
  );
4229
4242
  }
4230
- const uncacheable = Boolean(request.commerceArtifacts);
4243
+ const uncacheable = Boolean(request.commerceArtifacts) || Boolean(mergedConfig.actingProof);
4231
4244
  if (mergedConfig.cacheTtl !== 0 && !options?.bypassCache && !uncacheable) {
4232
4245
  const cached = getCachedResult(request, mergedConfig.counterpartyId);
4233
4246
  if (cached) {
4234
4247
  if (mergedConfig.debug) {
4235
4248
  console.log("[VerificationGateway] Returning cached result");
4236
4249
  }
4237
- const hit = { ...cached };
4238
- if (request.attemptId) {
4239
- hit.attemptId = request.attemptId;
4240
- } else {
4241
- delete hit.attemptId;
4242
- }
4243
- return hit;
4250
+ return sharedVerdict(cached, request);
4251
+ }
4252
+ }
4253
+ if (mergedConfig.cacheTtl !== 0 && !options?.bypassCache && !uncacheable) {
4254
+ const key = getCacheKey(request, mergedConfig.counterpartyId);
4255
+ const pending = inflightVerifies.get(key);
4256
+ if (pending) return sharedVerdict(await pending, request);
4257
+ const flight = verifyViaApi(mergedConfig, request, urls, uncacheable);
4258
+ inflightVerifies.set(key, flight);
4259
+ try {
4260
+ return await flight;
4261
+ } finally {
4262
+ inflightVerifies.delete(key);
4244
4263
  }
4245
4264
  }
4265
+ return verifyViaApi(mergedConfig, request, urls, uncacheable);
4266
+ }
4267
+ var inflightVerifies = /* @__PURE__ */ new Map();
4268
+ var sharedVerdicts = /* @__PURE__ */ new WeakSet();
4269
+ function sharedVerdict(source, request) {
4270
+ const hit = { ...source };
4271
+ if (request.attemptId) {
4272
+ hit.attemptId = request.attemptId;
4273
+ } else {
4274
+ delete hit.attemptId;
4275
+ }
4276
+ sharedVerdicts.add(hit);
4277
+ return hit;
4278
+ }
4279
+ async function verifyViaApi(mergedConfig, request, urls, uncacheable) {
4246
4280
  const enrichedRequest = { ...request };
4247
4281
  if (!enrichedRequest.counterpartyUrl && mergedConfig.counterpartyUrl) {
4248
4282
  enrichedRequest.counterpartyUrl = mergedConfig.counterpartyUrl;
@@ -4270,9 +4304,18 @@ async function verify(config, request, options) {
4270
4304
  if (!apiResponse.access?.allowed) {
4271
4305
  const aggregatedFailures = apiResponse.access?.failures;
4272
4306
  const idVerifiedFromBackend = apiResponse.verificationContext?.idVerified === true && !aggregatedFailures?.some((f) => REJECTED_IDENTITY_DIMENSIONS.has(f.dimension));
4307
+ const denyAgent = idVerifiedFromBackend && apiResponse.agent ? {
4308
+ astraId: apiResponse.agent.astraId,
4309
+ name: apiResponse.agent.name,
4310
+ trustScore: apiResponse.agent.trustScore,
4311
+ trustLevel: getTrustLevel(apiResponse.agent.trustScore),
4312
+ blockchainAnchored: apiResponse.agent.blockchainStatus === "anchored",
4313
+ status: apiResponse.agent.agentStatus
4314
+ } : void 0;
4273
4315
  const result2 = {
4274
4316
  identityVerified: idVerifiedFromBackend,
4275
4317
  policyAllowed: false,
4318
+ ...denyAgent && { agent: denyAgent },
4276
4319
  // 3.6.0: the server now emits top-level denialReasons on every deny
4277
4320
  // path — prefer it verbatim; the failure-message reconstruction remains
4278
4321
  // as the fallback for older backends.
@@ -4312,7 +4355,9 @@ async function verify(config, request, options) {
4312
4355
  fulfilment: apiResponse.fulfilment,
4313
4356
  mandates: apiResponse.mandates,
4314
4357
  kyaToken: apiResponse.kyaToken,
4315
- aep: apiResponse.aep
4358
+ aep: apiResponse.aep,
4359
+ // 5.20.0 (round 29.1): non-fatal integration warnings (e.g. events_url_missing).
4360
+ warnings: apiResponse.warnings
4316
4361
  };
4317
4362
  return result2;
4318
4363
  }
@@ -4380,7 +4425,9 @@ async function verify(config, request, options) {
4380
4425
  // 5.15.0: merchant-lane AP2 mandates + KYAPay identity token.
4381
4426
  mandates: apiResponse.mandates,
4382
4427
  kyaToken: apiResponse.kyaToken,
4383
- aep: apiResponse.aep
4428
+ aep: apiResponse.aep,
4429
+ // 5.20.0 (round 29.1): non-fatal integration warnings (e.g. events_url_missing).
4430
+ warnings: apiResponse.warnings
4384
4431
  };
4385
4432
  if (result.recommendation === "deny") {
4386
4433
  result.policyAllowed = false;
@@ -4406,8 +4453,18 @@ async function verify(config, request, options) {
4406
4453
  cacheResult(request, result, mergedConfig.cacheTtl, mergedConfig.counterpartyId);
4407
4454
  }
4408
4455
  }
4456
+ notifyVerificationListeners(request, result);
4409
4457
  return result;
4410
4458
  }
4459
+ var verificationListeners = /* @__PURE__ */ new Set();
4460
+ function notifyVerificationListeners(request, result) {
4461
+ for (const listener of verificationListeners) {
4462
+ try {
4463
+ listener(result, request);
4464
+ } catch {
4465
+ }
4466
+ }
4467
+ }
4411
4468
 
4412
4469
  // src/adapters/stepup-poll.ts
4413
4470
  var FALLBACK_MAX_WAIT_MS = 5 * 60 * 1e3;
@@ -4418,6 +4475,9 @@ var MAX_TOTAL_BYTES = 16 * 1024;
4418
4475
  // src/adapters/commerce-detect.ts
4419
4476
  var MAX_BODY_BYTES = 64 * 1024;
4420
4477
 
4478
+ // src/adapters/routes-cache.ts
4479
+ var DEFAULT_ROUTES_REFRESH_MS = 5 * 60 * 1e3;
4480
+
4421
4481
  // src/edge-config.ts
4422
4482
  var DEFAULT_EDGE_CONFIG = Object.freeze({
4423
4483
  version: 1,
@@ -4659,9 +4719,6 @@ var BeaconBuffer = class {
4659
4719
  };
4660
4720
  var beaconBuffer = new BeaconBuffer();
4661
4721
 
4662
- // src/adapters/express.ts
4663
- var DEFAULT_ROUTES_REFRESH_MS = 5 * 60 * 1e3;
4664
-
4665
4722
  // src/adapters/nextjs.ts
4666
4723
  var DEFAULT_ROUTES_REFRESH_MS2 = 5 * 60 * 1e3;
4667
4724
 
@@ -4747,6 +4804,25 @@ var import_jose2 = require("jose");
4747
4804
  // src/idempotency/index.ts
4748
4805
  var DEFAULT_IDEMPOTENCY_TTL_MS = 24 * 60 * 60 * 1e3;
4749
4806
 
4807
+ // src/ucp/approval-events.ts
4808
+ var MemorySeenEvents = class {
4809
+ constructor(max = 1e4) {
4810
+ this.max = max;
4811
+ this.ids = /* @__PURE__ */ new Set();
4812
+ }
4813
+ async has(eventId) {
4814
+ return this.ids.has(eventId);
4815
+ }
4816
+ async add(eventId) {
4817
+ if (this.ids.size >= this.max) {
4818
+ const first = this.ids.values().next().value;
4819
+ if (first !== void 0) this.ids.delete(first);
4820
+ }
4821
+ this.ids.add(eventId);
4822
+ }
4823
+ };
4824
+ var defaultSeen = new MemorySeenEvents();
4825
+
4750
4826
  // src/payments/kyapay.ts
4751
4827
  var import_jose4 = require("jose");
4752
4828
 
@@ -4786,7 +4862,9 @@ function toDecision(result) {
4786
4862
  if (result.identityVerified && result.policyAllowed) {
4787
4863
  return {
4788
4864
  recommendation: "ALLOW",
4789
- reason: result.agent?.trustScore ? `Verified (trust score ${result.agent.trustScore})` : "Verified",
4865
+ // 5.19.0: the score is on `trustScore` for the merchant; `reason` is
4866
+ // text a merchant may relay to the agent, so it never embeds the number.
4867
+ reason: "Verified",
4790
4868
  trustScore: result.agent?.trustScore,
4791
4869
  sessionId: "sessionId" in result ? result.sessionId : void 0
4792
4870
  };
@@ -3751,7 +3751,7 @@ function getTrustLevel(score) {
3751
3751
  }
3752
3752
 
3753
3753
  // src/version.ts
3754
- var SDK_VERSION = "5.18.1";
3754
+ var SDK_VERSION = "5.20.0";
3755
3755
 
3756
3756
  // src/http.ts
3757
3757
  var SDK_USER_AGENT = `astrasync-sdk/${SDK_VERSION}`;
@@ -3906,7 +3906,12 @@ function getCacheKey(request, counterpartyId) {
3906
3906
  // its start_checkout quote was served the cached quote-leg grant (no
3907
3907
  // settlementOutcome) and never reached the backend. Same F-A1-07 bug class.
3908
3908
  request.commercePhase || "",
3909
- request.checkoutSessionId || ""
3909
+ request.checkoutSessionId || "",
3910
+ // 5.19.0: a route_config purpose claim is checked server-side against
3911
+ // the stored rule for this method + path, so it changes the verdict.
3912
+ request.purposeSource || "",
3913
+ request.requestMethod || "",
3914
+ request.requestPath || ""
3910
3915
  ].join("|");
3911
3916
  }
3912
3917
  function getCachedResult(request, counterpartyId) {
@@ -4064,6 +4069,9 @@ async function callVerifyAccessAPI(config, request) {
4064
4069
  if (requestData.enableRuntimeChallenge)
4065
4070
  body.enableRuntimeChallenge = requestData.enableRuntimeChallenge;
4066
4071
  if (requestData.routePolicy) body.routePolicy = requestData.routePolicy;
4072
+ if (config.actingProof?.connectorToken) {
4073
+ body.actingProof = { connectorToken: config.actingProof.connectorToken };
4074
+ }
4067
4075
  if (requestData.approvalHeldByRoute) body.approvalHeldByRoute = true;
4068
4076
  if (requestData.createSession) body.createSession = requestData.createSession;
4069
4077
  if (requestData.durationRequired) body.durationRequired = requestData.durationRequired;
@@ -4080,6 +4088,11 @@ async function callVerifyAccessAPI(config, request) {
4080
4088
  if (requestData.buyerEmail) body.buyerEmail = requestData.buyerEmail;
4081
4089
  if (requestData.commerceArtifacts) body.commerceArtifacts = requestData.commerceArtifacts;
4082
4090
  if (requestData.attemptId) body.attemptId = requestData.attemptId;
4091
+ if (requestData.purposeSource) body.purposeSource = requestData.purposeSource;
4092
+ if (requestData.agentArtifacts) body.agentArtifacts = requestData.agentArtifacts;
4093
+ if (requestData.metadata) body.metadata = requestData.metadata;
4094
+ if (requestData.requestMethod) body.requestMethod = requestData.requestMethod;
4095
+ if (requestData.requestPath) body.requestPath = requestData.requestPath;
4083
4096
  if (requestData.considerationSet) body.considerationSet = requestData.considerationSet;
4084
4097
  body.sdkVersion = SDK_VERSION;
4085
4098
  if (requestData.callerMetadata || requestData.clientIp || requestData.userAgent) {
@@ -4226,22 +4239,43 @@ async function verify(config, request, options) {
4226
4239
  "[VerificationGateway] minTrustScore / minTrustScoreForFull are deprecated in v2.3.0 and have no effect. Server is now the single source of truth for access decisions (verified ID + runtime challenge + PDLSS + trust score). To gate access to an endpoint, configure the endpoint's trust_score_requirement server-side."
4227
4240
  );
4228
4241
  }
4229
- const uncacheable = Boolean(request.commerceArtifacts);
4242
+ const uncacheable = Boolean(request.commerceArtifacts) || Boolean(mergedConfig.actingProof);
4230
4243
  if (mergedConfig.cacheTtl !== 0 && !options?.bypassCache && !uncacheable) {
4231
4244
  const cached = getCachedResult(request, mergedConfig.counterpartyId);
4232
4245
  if (cached) {
4233
4246
  if (mergedConfig.debug) {
4234
4247
  console.log("[VerificationGateway] Returning cached result");
4235
4248
  }
4236
- const hit = { ...cached };
4237
- if (request.attemptId) {
4238
- hit.attemptId = request.attemptId;
4239
- } else {
4240
- delete hit.attemptId;
4241
- }
4242
- return hit;
4249
+ return sharedVerdict(cached, request);
4250
+ }
4251
+ }
4252
+ if (mergedConfig.cacheTtl !== 0 && !options?.bypassCache && !uncacheable) {
4253
+ const key = getCacheKey(request, mergedConfig.counterpartyId);
4254
+ const pending = inflightVerifies.get(key);
4255
+ if (pending) return sharedVerdict(await pending, request);
4256
+ const flight = verifyViaApi(mergedConfig, request, urls, uncacheable);
4257
+ inflightVerifies.set(key, flight);
4258
+ try {
4259
+ return await flight;
4260
+ } finally {
4261
+ inflightVerifies.delete(key);
4243
4262
  }
4244
4263
  }
4264
+ return verifyViaApi(mergedConfig, request, urls, uncacheable);
4265
+ }
4266
+ var inflightVerifies = /* @__PURE__ */ new Map();
4267
+ var sharedVerdicts = /* @__PURE__ */ new WeakSet();
4268
+ function sharedVerdict(source, request) {
4269
+ const hit = { ...source };
4270
+ if (request.attemptId) {
4271
+ hit.attemptId = request.attemptId;
4272
+ } else {
4273
+ delete hit.attemptId;
4274
+ }
4275
+ sharedVerdicts.add(hit);
4276
+ return hit;
4277
+ }
4278
+ async function verifyViaApi(mergedConfig, request, urls, uncacheable) {
4245
4279
  const enrichedRequest = { ...request };
4246
4280
  if (!enrichedRequest.counterpartyUrl && mergedConfig.counterpartyUrl) {
4247
4281
  enrichedRequest.counterpartyUrl = mergedConfig.counterpartyUrl;
@@ -4269,9 +4303,18 @@ async function verify(config, request, options) {
4269
4303
  if (!apiResponse.access?.allowed) {
4270
4304
  const aggregatedFailures = apiResponse.access?.failures;
4271
4305
  const idVerifiedFromBackend = apiResponse.verificationContext?.idVerified === true && !aggregatedFailures?.some((f) => REJECTED_IDENTITY_DIMENSIONS.has(f.dimension));
4306
+ const denyAgent = idVerifiedFromBackend && apiResponse.agent ? {
4307
+ astraId: apiResponse.agent.astraId,
4308
+ name: apiResponse.agent.name,
4309
+ trustScore: apiResponse.agent.trustScore,
4310
+ trustLevel: getTrustLevel(apiResponse.agent.trustScore),
4311
+ blockchainAnchored: apiResponse.agent.blockchainStatus === "anchored",
4312
+ status: apiResponse.agent.agentStatus
4313
+ } : void 0;
4272
4314
  const result2 = {
4273
4315
  identityVerified: idVerifiedFromBackend,
4274
4316
  policyAllowed: false,
4317
+ ...denyAgent && { agent: denyAgent },
4275
4318
  // 3.6.0: the server now emits top-level denialReasons on every deny
4276
4319
  // path — prefer it verbatim; the failure-message reconstruction remains
4277
4320
  // as the fallback for older backends.
@@ -4311,7 +4354,9 @@ async function verify(config, request, options) {
4311
4354
  fulfilment: apiResponse.fulfilment,
4312
4355
  mandates: apiResponse.mandates,
4313
4356
  kyaToken: apiResponse.kyaToken,
4314
- aep: apiResponse.aep
4357
+ aep: apiResponse.aep,
4358
+ // 5.20.0 (round 29.1): non-fatal integration warnings (e.g. events_url_missing).
4359
+ warnings: apiResponse.warnings
4315
4360
  };
4316
4361
  return result2;
4317
4362
  }
@@ -4379,7 +4424,9 @@ async function verify(config, request, options) {
4379
4424
  // 5.15.0: merchant-lane AP2 mandates + KYAPay identity token.
4380
4425
  mandates: apiResponse.mandates,
4381
4426
  kyaToken: apiResponse.kyaToken,
4382
- aep: apiResponse.aep
4427
+ aep: apiResponse.aep,
4428
+ // 5.20.0 (round 29.1): non-fatal integration warnings (e.g. events_url_missing).
4429
+ warnings: apiResponse.warnings
4383
4430
  };
4384
4431
  if (result.recommendation === "deny") {
4385
4432
  result.policyAllowed = false;
@@ -4405,8 +4452,18 @@ async function verify(config, request, options) {
4405
4452
  cacheResult(request, result, mergedConfig.cacheTtl, mergedConfig.counterpartyId);
4406
4453
  }
4407
4454
  }
4455
+ notifyVerificationListeners(request, result);
4408
4456
  return result;
4409
4457
  }
4458
+ var verificationListeners = /* @__PURE__ */ new Set();
4459
+ function notifyVerificationListeners(request, result) {
4460
+ for (const listener of verificationListeners) {
4461
+ try {
4462
+ listener(result, request);
4463
+ } catch {
4464
+ }
4465
+ }
4466
+ }
4410
4467
 
4411
4468
  // src/adapters/stepup-poll.ts
4412
4469
  var FALLBACK_MAX_WAIT_MS = 5 * 60 * 1e3;
@@ -4417,6 +4474,9 @@ var MAX_TOTAL_BYTES = 16 * 1024;
4417
4474
  // src/adapters/commerce-detect.ts
4418
4475
  var MAX_BODY_BYTES = 64 * 1024;
4419
4476
 
4477
+ // src/adapters/routes-cache.ts
4478
+ var DEFAULT_ROUTES_REFRESH_MS = 5 * 60 * 1e3;
4479
+
4420
4480
  // src/edge-config.ts
4421
4481
  var DEFAULT_EDGE_CONFIG = Object.freeze({
4422
4482
  version: 1,
@@ -4658,9 +4718,6 @@ var BeaconBuffer = class {
4658
4718
  };
4659
4719
  var beaconBuffer = new BeaconBuffer();
4660
4720
 
4661
- // src/adapters/express.ts
4662
- var DEFAULT_ROUTES_REFRESH_MS = 5 * 60 * 1e3;
4663
-
4664
4721
  // src/adapters/nextjs.ts
4665
4722
  var DEFAULT_ROUTES_REFRESH_MS2 = 5 * 60 * 1e3;
4666
4723
 
@@ -4746,6 +4803,25 @@ var import_jose2 = require("jose");
4746
4803
  // src/idempotency/index.ts
4747
4804
  var DEFAULT_IDEMPOTENCY_TTL_MS = 24 * 60 * 60 * 1e3;
4748
4805
 
4806
+ // src/ucp/approval-events.ts
4807
+ var MemorySeenEvents = class {
4808
+ constructor(max = 1e4) {
4809
+ this.max = max;
4810
+ this.ids = /* @__PURE__ */ new Set();
4811
+ }
4812
+ async has(eventId) {
4813
+ return this.ids.has(eventId);
4814
+ }
4815
+ async add(eventId) {
4816
+ if (this.ids.size >= this.max) {
4817
+ const first = this.ids.values().next().value;
4818
+ if (first !== void 0) this.ids.delete(first);
4819
+ }
4820
+ this.ids.add(eventId);
4821
+ }
4822
+ };
4823
+ var defaultSeen = new MemorySeenEvents();
4824
+
4749
4825
  // src/payments/kyapay.ts
4750
4826
  var import_jose4 = require("jose");
4751
4827
 
@@ -4785,7 +4861,9 @@ function toDecision(result) {
4785
4861
  if (result.identityVerified && result.policyAllowed) {
4786
4862
  return {
4787
4863
  recommendation: "ALLOW",
4788
- reason: result.agent?.trustScore ? `Verified (trust score ${result.agent.trustScore})` : "Verified",
4864
+ // 5.19.0: the score is on `trustScore` for the merchant; `reason` is
4865
+ // text a merchant may relay to the agent, so it never embeds the number.
4866
+ reason: "Verified",
4789
4867
  trustScore: result.agent?.trustScore,
4790
4868
  sessionId: "sessionId" in result ? result.sessionId : void 0
4791
4869
  };
@@ -3713,7 +3713,7 @@ Policy saved to ${outputPath}`);
3713
3713
  };
3714
3714
 
3715
3715
  // src/version.ts
3716
- var SDK_VERSION = "5.18.1";
3716
+ var SDK_VERSION = "5.20.0";
3717
3717
 
3718
3718
  // src/http.ts
3719
3719
  var SDK_USER_AGENT = `astrasync-sdk/${SDK_VERSION}`;
@@ -4772,7 +4772,12 @@ function getCacheKey(request2, counterpartyId) {
4772
4772
  // its start_checkout quote was served the cached quote-leg grant (no
4773
4773
  // settlementOutcome) and never reached the backend. Same F-A1-07 bug class.
4774
4774
  request2.commercePhase || "",
4775
- request2.checkoutSessionId || ""
4775
+ request2.checkoutSessionId || "",
4776
+ // 5.19.0: a route_config purpose claim is checked server-side against
4777
+ // the stored rule for this method + path, so it changes the verdict.
4778
+ request2.purposeSource || "",
4779
+ request2.requestMethod || "",
4780
+ request2.requestPath || ""
4776
4781
  ].join("|");
4777
4782
  }
4778
4783
  function getCachedResult(request2, counterpartyId) {
@@ -4930,6 +4935,9 @@ async function callVerifyAccessAPI(config, request2) {
4930
4935
  if (requestData.enableRuntimeChallenge)
4931
4936
  body.enableRuntimeChallenge = requestData.enableRuntimeChallenge;
4932
4937
  if (requestData.routePolicy) body.routePolicy = requestData.routePolicy;
4938
+ if (config.actingProof?.connectorToken) {
4939
+ body.actingProof = { connectorToken: config.actingProof.connectorToken };
4940
+ }
4933
4941
  if (requestData.approvalHeldByRoute) body.approvalHeldByRoute = true;
4934
4942
  if (requestData.createSession) body.createSession = requestData.createSession;
4935
4943
  if (requestData.durationRequired) body.durationRequired = requestData.durationRequired;
@@ -4946,6 +4954,11 @@ async function callVerifyAccessAPI(config, request2) {
4946
4954
  if (requestData.buyerEmail) body.buyerEmail = requestData.buyerEmail;
4947
4955
  if (requestData.commerceArtifacts) body.commerceArtifacts = requestData.commerceArtifacts;
4948
4956
  if (requestData.attemptId) body.attemptId = requestData.attemptId;
4957
+ if (requestData.purposeSource) body.purposeSource = requestData.purposeSource;
4958
+ if (requestData.agentArtifacts) body.agentArtifacts = requestData.agentArtifacts;
4959
+ if (requestData.metadata) body.metadata = requestData.metadata;
4960
+ if (requestData.requestMethod) body.requestMethod = requestData.requestMethod;
4961
+ if (requestData.requestPath) body.requestPath = requestData.requestPath;
4949
4962
  if (requestData.considerationSet) body.considerationSet = requestData.considerationSet;
4950
4963
  body.sdkVersion = SDK_VERSION;
4951
4964
  if (requestData.callerMetadata || requestData.clientIp || requestData.userAgent) {
@@ -5092,22 +5105,43 @@ async function verify(config, request2, options) {
5092
5105
  "[VerificationGateway] minTrustScore / minTrustScoreForFull are deprecated in v2.3.0 and have no effect. Server is now the single source of truth for access decisions (verified ID + runtime challenge + PDLSS + trust score). To gate access to an endpoint, configure the endpoint's trust_score_requirement server-side."
5093
5106
  );
5094
5107
  }
5095
- const uncacheable = Boolean(request2.commerceArtifacts);
5108
+ const uncacheable = Boolean(request2.commerceArtifacts) || Boolean(mergedConfig.actingProof);
5096
5109
  if (mergedConfig.cacheTtl !== 0 && !options?.bypassCache && !uncacheable) {
5097
5110
  const cached = getCachedResult(request2, mergedConfig.counterpartyId);
5098
5111
  if (cached) {
5099
5112
  if (mergedConfig.debug) {
5100
5113
  console.log("[VerificationGateway] Returning cached result");
5101
5114
  }
5102
- const hit = { ...cached };
5103
- if (request2.attemptId) {
5104
- hit.attemptId = request2.attemptId;
5105
- } else {
5106
- delete hit.attemptId;
5107
- }
5108
- return hit;
5115
+ return sharedVerdict(cached, request2);
5116
+ }
5117
+ }
5118
+ if (mergedConfig.cacheTtl !== 0 && !options?.bypassCache && !uncacheable) {
5119
+ const key = getCacheKey(request2, mergedConfig.counterpartyId);
5120
+ const pending = inflightVerifies.get(key);
5121
+ if (pending) return sharedVerdict(await pending, request2);
5122
+ const flight = verifyViaApi(mergedConfig, request2, urls, uncacheable);
5123
+ inflightVerifies.set(key, flight);
5124
+ try {
5125
+ return await flight;
5126
+ } finally {
5127
+ inflightVerifies.delete(key);
5109
5128
  }
5110
5129
  }
5130
+ return verifyViaApi(mergedConfig, request2, urls, uncacheable);
5131
+ }
5132
+ var inflightVerifies = /* @__PURE__ */ new Map();
5133
+ var sharedVerdicts = /* @__PURE__ */ new WeakSet();
5134
+ function sharedVerdict(source, request2) {
5135
+ const hit = { ...source };
5136
+ if (request2.attemptId) {
5137
+ hit.attemptId = request2.attemptId;
5138
+ } else {
5139
+ delete hit.attemptId;
5140
+ }
5141
+ sharedVerdicts.add(hit);
5142
+ return hit;
5143
+ }
5144
+ async function verifyViaApi(mergedConfig, request2, urls, uncacheable) {
5111
5145
  const enrichedRequest = { ...request2 };
5112
5146
  if (!enrichedRequest.counterpartyUrl && mergedConfig.counterpartyUrl) {
5113
5147
  enrichedRequest.counterpartyUrl = mergedConfig.counterpartyUrl;
@@ -5135,9 +5169,18 @@ async function verify(config, request2, options) {
5135
5169
  if (!apiResponse.access?.allowed) {
5136
5170
  const aggregatedFailures = apiResponse.access?.failures;
5137
5171
  const idVerifiedFromBackend = apiResponse.verificationContext?.idVerified === true && !aggregatedFailures?.some((f) => REJECTED_IDENTITY_DIMENSIONS.has(f.dimension));
5172
+ const denyAgent = idVerifiedFromBackend && apiResponse.agent ? {
5173
+ astraId: apiResponse.agent.astraId,
5174
+ name: apiResponse.agent.name,
5175
+ trustScore: apiResponse.agent.trustScore,
5176
+ trustLevel: getTrustLevel(apiResponse.agent.trustScore),
5177
+ blockchainAnchored: apiResponse.agent.blockchainStatus === "anchored",
5178
+ status: apiResponse.agent.agentStatus
5179
+ } : void 0;
5138
5180
  const result2 = {
5139
5181
  identityVerified: idVerifiedFromBackend,
5140
5182
  policyAllowed: false,
5183
+ ...denyAgent && { agent: denyAgent },
5141
5184
  // 3.6.0: the server now emits top-level denialReasons on every deny
5142
5185
  // path — prefer it verbatim; the failure-message reconstruction remains
5143
5186
  // as the fallback for older backends.
@@ -5177,7 +5220,9 @@ async function verify(config, request2, options) {
5177
5220
  fulfilment: apiResponse.fulfilment,
5178
5221
  mandates: apiResponse.mandates,
5179
5222
  kyaToken: apiResponse.kyaToken,
5180
- aep: apiResponse.aep
5223
+ aep: apiResponse.aep,
5224
+ // 5.20.0 (round 29.1): non-fatal integration warnings (e.g. events_url_missing).
5225
+ warnings: apiResponse.warnings
5181
5226
  };
5182
5227
  return result2;
5183
5228
  }
@@ -5245,7 +5290,9 @@ async function verify(config, request2, options) {
5245
5290
  // 5.15.0: merchant-lane AP2 mandates + KYAPay identity token.
5246
5291
  mandates: apiResponse.mandates,
5247
5292
  kyaToken: apiResponse.kyaToken,
5248
- aep: apiResponse.aep
5293
+ aep: apiResponse.aep,
5294
+ // 5.20.0 (round 29.1): non-fatal integration warnings (e.g. events_url_missing).
5295
+ warnings: apiResponse.warnings
5249
5296
  };
5250
5297
  if (result.recommendation === "deny") {
5251
5298
  result.policyAllowed = false;
@@ -5271,8 +5318,18 @@ async function verify(config, request2, options) {
5271
5318
  cacheResult(request2, result, mergedConfig.cacheTtl, mergedConfig.counterpartyId);
5272
5319
  }
5273
5320
  }
5321
+ notifyVerificationListeners(request2, result);
5274
5322
  return result;
5275
5323
  }
5324
+ var verificationListeners = /* @__PURE__ */ new Set();
5325
+ function notifyVerificationListeners(request2, result) {
5326
+ for (const listener of verificationListeners) {
5327
+ try {
5328
+ listener(result, request2);
5329
+ } catch {
5330
+ }
5331
+ }
5332
+ }
5276
5333
 
5277
5334
  // src/adapters/stepup-poll.ts
5278
5335
  var FALLBACK_MAX_WAIT_MS = 5 * 60 * 1e3;
@@ -5283,6 +5340,9 @@ var MAX_TOTAL_BYTES = 16 * 1024;
5283
5340
  // src/adapters/commerce-detect.ts
5284
5341
  var MAX_BODY_BYTES = 64 * 1024;
5285
5342
 
5343
+ // src/adapters/routes-cache.ts
5344
+ var DEFAULT_ROUTES_REFRESH_MS = 5 * 60 * 1e3;
5345
+
5286
5346
  // src/edge-config.ts
5287
5347
  var DEFAULT_EDGE_CONFIG = Object.freeze({
5288
5348
  version: 1,
@@ -5524,9 +5584,6 @@ var BeaconBuffer = class {
5524
5584
  };
5525
5585
  var beaconBuffer = new BeaconBuffer();
5526
5586
 
5527
- // src/adapters/express.ts
5528
- var DEFAULT_ROUTES_REFRESH_MS = 5 * 60 * 1e3;
5529
-
5530
5587
  // src/adapters/nextjs.ts
5531
5588
  var DEFAULT_ROUTES_REFRESH_MS2 = 5 * 60 * 1e3;
5532
5589
 
@@ -5612,6 +5669,25 @@ var import_jose2 = require("jose");
5612
5669
  // src/idempotency/index.ts
5613
5670
  var DEFAULT_IDEMPOTENCY_TTL_MS = 24 * 60 * 60 * 1e3;
5614
5671
 
5672
+ // src/ucp/approval-events.ts
5673
+ var MemorySeenEvents = class {
5674
+ constructor(max = 1e4) {
5675
+ this.max = max;
5676
+ this.ids = /* @__PURE__ */ new Set();
5677
+ }
5678
+ async has(eventId) {
5679
+ return this.ids.has(eventId);
5680
+ }
5681
+ async add(eventId) {
5682
+ if (this.ids.size >= this.max) {
5683
+ const first = this.ids.values().next().value;
5684
+ if (first !== void 0) this.ids.delete(first);
5685
+ }
5686
+ this.ids.add(eventId);
5687
+ }
5688
+ };
5689
+ var defaultSeen = new MemorySeenEvents();
5690
+
5615
5691
  // src/payments/kyapay.ts
5616
5692
  var import_jose4 = require("jose");
5617
5693
 
@@ -5651,7 +5727,9 @@ function toDecision(result) {
5651
5727
  if (result.identityVerified && result.policyAllowed) {
5652
5728
  return {
5653
5729
  recommendation: "ALLOW",
5654
- reason: result.agent?.trustScore ? `Verified (trust score ${result.agent.trustScore})` : "Verified",
5730
+ // 5.19.0: the score is on `trustScore` for the merchant; `reason` is
5731
+ // text a merchant may relay to the agent, so it never embeds the number.
5732
+ reason: "Verified",
5655
5733
  trustScore: result.agent?.trustScore,
5656
5734
  sessionId: "sessionId" in result ? result.sessionId : void 0
5657
5735
  };