@astrasyncai/verification-gateway 3.1.0 → 3.2.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 (79) hide show
  1. package/dist/adapter-interface/interface.d.mts +2 -2
  2. package/dist/adapter-interface/interface.d.ts +2 -2
  3. package/dist/adapters/express.d.mts +2 -2
  4. package/dist/adapters/express.d.ts +2 -2
  5. package/dist/adapters/express.js +23 -61
  6. package/dist/adapters/express.js.map +1 -1
  7. package/dist/adapters/express.mjs +23 -61
  8. package/dist/adapters/express.mjs.map +1 -1
  9. package/dist/adapters/mcp.d.mts +12 -7
  10. package/dist/adapters/mcp.d.ts +12 -7
  11. package/dist/adapters/mcp.js +38 -100
  12. package/dist/adapters/mcp.js.map +1 -1
  13. package/dist/adapters/mcp.mjs +38 -100
  14. package/dist/adapters/mcp.mjs.map +1 -1
  15. package/dist/adapters/nextjs.d.mts +2 -2
  16. package/dist/adapters/nextjs.d.ts +2 -2
  17. package/dist/adapters/nextjs.js +20 -29
  18. package/dist/adapters/nextjs.js.map +1 -1
  19. package/dist/adapters/nextjs.mjs +20 -29
  20. package/dist/adapters/nextjs.mjs.map +1 -1
  21. package/dist/adapters/sdk.d.mts +2 -2
  22. package/dist/adapters/sdk.d.ts +2 -2
  23. package/dist/adapters/sdk.js +25 -14
  24. package/dist/adapters/sdk.js.map +1 -1
  25. package/dist/adapters/sdk.mjs +25 -14
  26. package/dist/adapters/sdk.mjs.map +1 -1
  27. package/dist/agent/index.d.mts +2 -2
  28. package/dist/agent/index.d.ts +2 -2
  29. package/dist/browser/background.js +18 -21
  30. package/dist/browser/background.js.map +1 -1
  31. package/dist/browser/background.mjs +18 -21
  32. package/dist/browser/background.mjs.map +1 -1
  33. package/dist/browser/browser-adapter.d.mts +2 -2
  34. package/dist/browser/browser-adapter.d.ts +2 -2
  35. package/dist/cli/index.d.mts +2 -2
  36. package/dist/cli/index.d.ts +2 -2
  37. package/dist/cursor/cursor-adapter.d.mts +2 -2
  38. package/dist/cursor/cursor-adapter.d.ts +2 -2
  39. package/dist/cursor/extension.d.mts +2 -2
  40. package/dist/cursor/extension.d.ts +2 -2
  41. package/dist/cursor/extension.js +18 -21
  42. package/dist/cursor/extension.js.map +1 -1
  43. package/dist/cursor/extension.mjs +18 -21
  44. package/dist/cursor/extension.mjs.map +1 -1
  45. package/dist/{express-DavQ76oF.d.ts → express-BowlMHQF.d.ts} +1 -1
  46. package/dist/{express-DFVBlXr_.d.mts → express-CeoSdOAZ.d.mts} +1 -1
  47. package/dist/gateway/gateway.d.mts +2 -2
  48. package/dist/gateway/gateway.d.ts +2 -2
  49. package/dist/gateway/gateway.js +18 -21
  50. package/dist/gateway/gateway.js.map +1 -1
  51. package/dist/gateway/gateway.mjs +18 -21
  52. package/dist/gateway/gateway.mjs.map +1 -1
  53. package/dist/git-trigger/git-hooks.d.mts +2 -2
  54. package/dist/git-trigger/git-hooks.d.ts +2 -2
  55. package/dist/{index-BhL2R65s.d.mts → index-B51W8gn8.d.mts} +1 -1
  56. package/dist/{index-BhEgEiJL.d.ts → index-DBmlycVm.d.ts} +1 -1
  57. package/dist/{index-BVxantdv.d.mts → index-DtGziFEm.d.mts} +1 -1
  58. package/dist/{index-Dk2nIA4w.d.ts → index-DzXXBuLm.d.ts} +1 -1
  59. package/dist/index.d.mts +7 -7
  60. package/dist/index.d.ts +7 -7
  61. package/dist/index.js +50 -121
  62. package/dist/index.js.map +1 -1
  63. package/dist/index.mjs +50 -121
  64. package/dist/index.mjs.map +1 -1
  65. package/dist/local-evaluator/evaluator.d.mts +2 -2
  66. package/dist/local-evaluator/evaluator.d.ts +2 -2
  67. package/dist/{nextjs-D-maqrNz.d.mts → nextjs-BW1rzr1I.d.mts} +1 -1
  68. package/dist/{nextjs-BXLH1hJj.d.ts → nextjs-V_K0qlAQ.d.ts} +1 -1
  69. package/dist/{sdk-767LaEP8.d.mts → sdk-ZYgI7G9f.d.ts} +14 -3
  70. package/dist/{sdk-K8IgssHI.d.ts → sdk-e5jg7sqW.d.mts} +14 -3
  71. package/dist/transport/index.d.mts +2 -2
  72. package/dist/transport/index.d.ts +2 -2
  73. package/dist/{types-CyFwZ_Yu.d.mts → types-BNiLZY0i.d.mts} +1 -1
  74. package/dist/{types-WIRp_BP_.d.ts → types-DJi-u3fz.d.ts} +1 -1
  75. package/dist/{types-Cuh7ELfr.d.mts → types-rFh4VMH4.d.mts} +5 -2
  76. package/dist/{types-Cuh7ELfr.d.ts → types-rFh4VMH4.d.ts} +5 -2
  77. package/dist/ui/index.d.mts +1 -1
  78. package/dist/ui/index.d.ts +1 -1
  79. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -126,7 +126,7 @@ function getCapabilities(accessLevel) {
126
126
  }
127
127
 
128
128
  // src/version.ts
129
- var SDK_VERSION = "3.1.0";
129
+ var SDK_VERSION = "3.2.0";
130
130
 
131
131
  // src/well-known.ts
132
132
  var CACHE_TTL_MS = 60 * 60 * 1e3;
@@ -231,7 +231,7 @@ async function performInitCheck(apiBaseUrl, debug, strictInit) {
231
231
  }
232
232
  }
233
233
  var verificationCache = /* @__PURE__ */ new Map();
234
- function getCacheKey(request) {
234
+ function getCacheKey(request, counterpartyId) {
235
235
  const c = request.credentials;
236
236
  return [
237
237
  c.astraId || "",
@@ -244,6 +244,14 @@ function getCacheKey(request) {
244
244
  request.jurisdiction || "",
245
245
  request.transactionValue ?? "",
246
246
  request.currency || "",
247
+ // SECURITY (cross-merchant cache leak): the merchant identity is sent via
248
+ // `config.counterpartyId`, NOT on the request, so it was previously absent
249
+ // from the key — two verifies for the SAME agent/purpose/action/value but
250
+ // DIFFERENT merchants collided, and a grant at a permissive merchant (low
251
+ // trust floor) was served for a stricter one. Same bug class as the
252
+ // duration omission (F-A1-07). counterpartyId affects the backend verdict
253
+ // (trust floor / per-route policy), so it MUST key the cache.
254
+ counterpartyId || "",
247
255
  request.counterpartyUrl || "",
248
256
  request.counterpartyType || "",
249
257
  request.isSubAgentRequest ? "1" : "0",
@@ -267,8 +275,8 @@ function getCacheKey(request) {
267
275
  request.callerMetadata?.agentCardUrl || ""
268
276
  ].join("|");
269
277
  }
270
- function getCachedResult(request) {
271
- const key = getCacheKey(request);
278
+ function getCachedResult(request, counterpartyId) {
279
+ const key = getCacheKey(request, counterpartyId);
272
280
  const cached = verificationCache.get(key);
273
281
  if (cached && cached.expiresAt > Date.now()) {
274
282
  return cached.result;
@@ -280,9 +288,9 @@ function getCachedResult(request) {
280
288
  }
281
289
  var DEFAULT_AUTONOMOUS_TTL_SECONDS = 60;
282
290
  var DEFAULT_STEP_UP_TTL_SECONDS = 300;
283
- function cacheResult(request, result, configuredTtl) {
291
+ function cacheResult(request, result, configuredTtl, counterpartyId) {
284
292
  const ttlSeconds = configuredTtl && configuredTtl > 0 ? configuredTtl : result.requiresStepUp ? DEFAULT_STEP_UP_TTL_SECONDS : DEFAULT_AUTONOMOUS_TTL_SECONDS;
285
- const key = getCacheKey(request);
293
+ const key = getCacheKey(request, counterpartyId);
286
294
  verificationCache.set(key, {
287
295
  result,
288
296
  expiresAt: Date.now() + ttlSeconds * 1e3
@@ -480,7 +488,7 @@ async function verify(config, request) {
480
488
  );
481
489
  }
482
490
  if (mergedConfig.cacheTtl !== 0) {
483
- const cached = getCachedResult(request);
491
+ const cached = getCachedResult(request, mergedConfig.counterpartyId);
484
492
  if (cached) {
485
493
  if (mergedConfig.debug) {
486
494
  console.log("[VerificationGateway] Returning cached result");
@@ -532,8 +540,8 @@ async function verify(config, request) {
532
540
  verifiedAt: /* @__PURE__ */ new Date(),
533
541
  // Extract sessionId so decisions can be recorded for denials too
534
542
  sessionId: apiResponse.sessionId,
535
- // v2.3.10 (defect #34, round-4): anonymous traffic has no session →
536
- // correlationId is the linking key for paired local_override events.
543
+ // Anonymous traffic has no session → correlationId is the per-attempt
544
+ // linking key (the sessionId-equivalent for anonymous callers).
537
545
  correlationId: apiResponse.correlationId,
538
546
  recommendation: apiResponse.recommendation,
539
547
  recommendationReasons: apiResponse.recommendationReasons
@@ -607,17 +615,14 @@ async function verify(config, request) {
607
615
  };
608
616
  } else if (result.recommendation === "step_up_required") {
609
617
  result.requiresStepUp = true;
610
- if (ACCESS_LEVEL_HIERARCHY[result.accessLevel] > ACCESS_LEVEL_HIERARCHY["read-only"]) {
611
- result.accessLevel = "read-only";
612
- }
613
618
  result.denialReasons = result.recommendationReasons || ["Step-up verification required"];
614
619
  }
615
620
  if (mergedConfig.cacheTtl !== 0 && result.recommendation !== "deny") {
616
- cacheResult(request, result, mergedConfig.cacheTtl);
621
+ cacheResult(request, result, mergedConfig.cacheTtl, mergedConfig.counterpartyId);
617
622
  }
618
623
  return result;
619
624
  }
620
- async function recordDecision(config, sessionId, decision, reason, override) {
625
+ async function recordDecision(config, sessionId, decision, reason) {
621
626
  const headers = { "Content-Type": "application/json" };
622
627
  if (config.apiKey) {
623
628
  headers["Authorization"] = `Bearer ${config.apiKey}`;
@@ -626,36 +631,7 @@ async function recordDecision(config, sessionId, decision, reason, override) {
626
631
  await fetch(`${config.apiBaseUrl}/agents/verify-access/${sessionId}/decision`, {
627
632
  method: "POST",
628
633
  headers,
629
- body: JSON.stringify({
630
- decision,
631
- reason,
632
- ...override && {
633
- overriddenBy: override.overriddenBy,
634
- toolName: override.toolName,
635
- requestedLevel: override.requestedLevel,
636
- grantedLevel: override.grantedLevel
637
- }
638
- })
639
- }).catch(() => {
640
- });
641
- }
642
- async function recordAnonymousLocalOverride(config, correlationId, override, reason) {
643
- const headers = { "Content-Type": "application/json" };
644
- if (config.apiKey) {
645
- headers["Authorization"] = `Bearer ${config.apiKey}`;
646
- headers["X-API-Key"] = config.apiKey;
647
- }
648
- await fetch(`${config.apiBaseUrl}/agents/verify-access/local-override`, {
649
- method: "POST",
650
- headers,
651
- body: JSON.stringify({
652
- correlationId,
653
- reason,
654
- overriddenBy: override.overriddenBy,
655
- toolName: override.toolName,
656
- requestedLevel: override.requestedLevel,
657
- grantedLevel: override.grantedLevel
658
- })
634
+ body: JSON.stringify({ decision, reason })
659
635
  }).catch(() => {
660
636
  });
661
637
  }
@@ -1174,35 +1150,12 @@ function createMiddleware(options) {
1174
1150
  }
1175
1151
  return next();
1176
1152
  }
1177
- if (!hasMinimumAccess(result.accessLevel, routeConfig.minAccessLevel)) {
1178
- const insufficientFailure = {
1179
- dimension: "access_level.insufficient",
1180
- message: `Endpoint requires accessLevel '${routeConfig.minAccessLevel}'; agent has '${result.accessLevel}'.`,
1181
- guidance: "Request elevated access via step-up verification (coming soon \u2014 ships this month). Step-up lets the agent owner approve a one-time elevation for this specific counterparty + purpose without changing the agent's baseline trust score."
1182
- };
1183
- result.failures = [...result.failures ?? [], insufficientFailure];
1184
- result.denialReasons = [...result.denialReasons ?? [], insufficientFailure.message];
1185
- if (!result.guidance && wellKnownUrls) {
1186
- result.guidance = {
1187
- message: insufficientFailure.message,
1188
- registrationUrl: wellKnownUrls.registrationUrl,
1189
- documentationUrl: wellKnownUrls.documentationUrl
1190
- };
1191
- }
1192
- if (shouldRecordDecisions && sessionId) {
1193
- recordDecision(config, sessionId, "denied", insufficientFailure.message).catch(() => {
1194
- });
1195
- }
1196
- dedupeFailures(result);
1197
- onDenied(result, req, res);
1198
- return;
1199
- }
1200
1153
  if (routeConfig.minTrustScore && result.agent) {
1201
1154
  if (result.agent.trustScore < routeConfig.minTrustScore) {
1202
1155
  const trustFailure = {
1203
- dimension: "access_level.insufficient",
1204
- message: `Trust score ${result.agent.trustScore} is below required ${routeConfig.minTrustScore} for this route.`,
1205
- guidance: "Request elevated access via step-up verification (coming soon \u2014 ships this month). Step-up lets the agent owner approve a one-time elevation for this specific counterparty + purpose without changing the agent's baseline trust score."
1156
+ dimension: "endpoint.trust",
1157
+ message: "Trust below the route requirement for this endpoint.",
1158
+ guidance: "Trust is below this route's floor. Trust is not overridable \u2014 the agent either meets the endpoint's trust policy or it doesn't. Raise the agent's trust via real signals (KYD, blockchain registration, agent-card), or have the operator lower the route's minTrustScore."
1206
1159
  };
1207
1160
  result.failures = [...result.failures ?? [], trustFailure];
1208
1161
  result.denialReasons = [trustFailure.message];
@@ -1633,7 +1586,7 @@ function createMiddleware2(options) {
1633
1586
  agentCardUrl: request.headers.get("x-astrasync-agent-card") || void 0
1634
1587
  }
1635
1588
  });
1636
- if (!result.identityVerified || !result.policyAllowed || !hasMinimumAccess(result.accessLevel, routeConfig.minAccessLevel)) {
1589
+ if (!result.identityVerified || !result.policyAllowed) {
1637
1590
  if (pathname.startsWith("/api/")) {
1638
1591
  return NextResponse.json(
1639
1592
  {
@@ -1641,10 +1594,8 @@ function createMiddleware2(options) {
1641
1594
  error: {
1642
1595
  // Round-18 G4: 401 → identity missing (re-auth); 403 → identity
1643
1596
  // OK, policy denied (update PDLSS / step up).
1644
- code: !result.identityVerified ? "UNAUTHORIZED" : "INSUFFICIENT_ACCESS",
1597
+ code: !result.identityVerified ? "UNAUTHORIZED" : "POLICY_DENIED",
1645
1598
  message: result.denialReasons?.[0] || "Access denied",
1646
- accessLevel: result.accessLevel,
1647
- required: routeConfig.minAccessLevel,
1648
1599
  guidance: result.guidance
1649
1600
  }
1650
1601
  },
@@ -1672,7 +1623,6 @@ function createMiddleware2(options) {
1672
1623
  response.headers.set("X-AstraSync-Access-Level", result.accessLevel);
1673
1624
  if (result.agent) {
1674
1625
  response.headers.set("X-AstraSync-Agent-Id", result.agent.astraId);
1675
- response.headers.set("X-AstraSync-Trust-Score", result.agent.trustScore.toString());
1676
1626
  }
1677
1627
  return response;
1678
1628
  };
@@ -1746,7 +1696,13 @@ var VerificationGatewayClient = class {
1746
1696
  return this.executeWithRetry(() => quickVerify(this.config, credentials));
1747
1697
  }
1748
1698
  /**
1749
- * Check if an agent has a specific access level
1699
+ * Check if an agent has a specific access level.
1700
+ *
1701
+ * @deprecated 3.2.0 — the access-level band is informational only; it no
1702
+ * longer gates in the middleware adapters (post-3.1.0 feedback #1). This
1703
+ * explicit opt-in query still works, but prefer gating on the server-side
1704
+ * policy decision (identity + policy + trust) or per-route `minTrustScore`.
1705
+ * Explicit per-route condition→constraint rules are the Phase-2 successor.
1750
1706
  */
1751
1707
  async hasAccess(credentials, requiredLevel) {
1752
1708
  const result = await this.quickVerify(credentials);
@@ -4593,7 +4549,6 @@ function createMcpMiddleware(options) {
4593
4549
  return next();
4594
4550
  }
4595
4551
  req.mcpRequest = parsed;
4596
- const wellKnownUrls = config.apiBaseUrl ? await getWellKnownUrls(config.apiBaseUrl).catch(() => void 0) : void 0;
4597
4552
  const headerRaw = req.headers["x-astra-id"] ?? req.headers["x-astra-agentid"];
4598
4553
  const headerAstraId = typeof headerRaw === "string" ? headerRaw : Array.isArray(headerRaw) ? headerRaw[0] : void 0;
4599
4554
  const bodyAstraId = parsed.agentIdFromBody;
@@ -4630,7 +4585,7 @@ function createMcpMiddleware(options) {
4630
4585
  return next();
4631
4586
  }
4632
4587
  }
4633
- const { level: minAccessLevel, source: gateSource } = resolveMinAccessLevel(parsed, {
4588
+ const { level: minAccessLevel } = resolveMinAccessLevel(parsed, {
4634
4589
  toolGates,
4635
4590
  methodGates
4636
4591
  });
@@ -4666,6 +4621,23 @@ function createMcpMiddleware(options) {
4666
4621
  resolved_action: pdlss.action
4667
4622
  });
4668
4623
  }
4624
+ if (!pdlss.purpose) {
4625
+ const id = req.body?.id ?? null;
4626
+ res.status(400).json({
4627
+ jsonrpc: "2.0",
4628
+ id,
4629
+ error: {
4630
+ code: -32602,
4631
+ message: "PDLSS_PURPOSE_REQUIRED",
4632
+ data: {
4633
+ dimension: "pdlss.purpose",
4634
+ detail: "This tool is access-gated but the call declared no PDLSS purpose. Supply a bare-category purpose via the X-Astra-Purpose header or params._meta.astrasync.purpose, or have the merchant set the tool\u2019s purpose in its toolGate config.",
4635
+ resolvedAction: pdlss.action
4636
+ }
4637
+ }
4638
+ });
4639
+ return;
4640
+ }
4669
4641
  const counterpartyUrl = config.counterpartyUrl || `${req.protocol}://${req.get("host")}${req.path}`;
4670
4642
  const shouldRecordDecisions = recordDecisions !== false;
4671
4643
  const result = await verify(config, {
@@ -4689,7 +4661,6 @@ function createMcpMiddleware(options) {
4689
4661
  });
4690
4662
  req.agentVerification = result;
4691
4663
  const sessionId = result.sessionId;
4692
- const correlationId = result.correlationId;
4693
4664
  if (!result.identityVerified || !result.policyAllowed) {
4694
4665
  if (shouldRecordDecisions && sessionId) {
4695
4666
  recordDecision(config, sessionId, "denied", result.denialReasons?.[0]).catch(() => {
@@ -4710,48 +4681,6 @@ function createMcpMiddleware(options) {
4710
4681
  }
4711
4682
  return next();
4712
4683
  }
4713
- if (!hasMinimumAccess(result.accessLevel, minAccessLevel)) {
4714
- const insufficientFailure = {
4715
- dimension: "access_level.insufficient",
4716
- message: `Tool requires accessLevel '${minAccessLevel}'; agent has '${result.accessLevel}'.`,
4717
- guidance: "Request elevated access via step-up verification (coming soon \u2014 ships this month). Step-up lets the agent owner approve a one-time elevation for this specific counterparty + purpose without changing the agent's baseline trust score."
4718
- };
4719
- result.failures = [...result.failures ?? [], insufficientFailure];
4720
- result.denialReasons = [...result.denialReasons ?? [], insufficientFailure.message];
4721
- if (!result.guidance && wellKnownUrls) {
4722
- result.guidance = {
4723
- message: insufficientFailure.message,
4724
- registrationUrl: wellKnownUrls.registrationUrl,
4725
- documentationUrl: wellKnownUrls.documentationUrl
4726
- };
4727
- }
4728
- if (shouldRecordDecisions) {
4729
- const overrideKind = gateSource === "toolGate" ? "toolGate" : gateSource === "methodGate" ? "methodGate" : "other";
4730
- const override = {
4731
- overriddenBy: overrideKind,
4732
- ...parsed.toolName && { toolName: parsed.toolName },
4733
- requestedLevel: minAccessLevel,
4734
- grantedLevel: result.accessLevel
4735
- };
4736
- if (sessionId) {
4737
- recordDecision(config, sessionId, "denied", result.denialReasons?.[0], override).catch(
4738
- () => {
4739
- }
4740
- );
4741
- } else if (correlationId) {
4742
- recordAnonymousLocalOverride(
4743
- config,
4744
- correlationId,
4745
- override,
4746
- result.denialReasons?.[0]
4747
- ).catch(() => {
4748
- });
4749
- }
4750
- }
4751
- dedupeFailures2(result);
4752
- onDenied(result, req, res);
4753
- return;
4754
- }
4755
4684
  if (effectiveAstraId) {
4756
4685
  res.setHeader(
4757
4686
  MCP_VERIFIED_HOP_HEADER,