@astrasyncai/verification-gateway 3.5.0 → 3.6.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 (80) hide show
  1. package/README.md +16 -0
  2. package/dist/adapter-interface/interface.d.mts +2 -2
  3. package/dist/adapter-interface/interface.d.ts +2 -2
  4. package/dist/adapters/express.d.mts +2 -2
  5. package/dist/adapters/express.d.ts +2 -2
  6. package/dist/adapters/express.js +96 -20
  7. package/dist/adapters/express.js.map +1 -1
  8. package/dist/adapters/express.mjs +96 -20
  9. package/dist/adapters/express.mjs.map +1 -1
  10. package/dist/adapters/mcp.d.mts +1 -1
  11. package/dist/adapters/mcp.d.ts +1 -1
  12. package/dist/adapters/mcp.js +96 -20
  13. package/dist/adapters/mcp.js.map +1 -1
  14. package/dist/adapters/mcp.mjs +96 -20
  15. package/dist/adapters/mcp.mjs.map +1 -1
  16. package/dist/adapters/nextjs.d.mts +2 -2
  17. package/dist/adapters/nextjs.d.ts +2 -2
  18. package/dist/adapters/nextjs.js +101 -20
  19. package/dist/adapters/nextjs.js.map +1 -1
  20. package/dist/adapters/nextjs.mjs +101 -20
  21. package/dist/adapters/nextjs.mjs.map +1 -1
  22. package/dist/adapters/sdk.d.mts +2 -2
  23. package/dist/adapters/sdk.d.ts +2 -2
  24. package/dist/adapters/sdk.js +19 -16
  25. package/dist/adapters/sdk.js.map +1 -1
  26. package/dist/adapters/sdk.mjs +19 -16
  27. package/dist/adapters/sdk.mjs.map +1 -1
  28. package/dist/agent/index.d.mts +2 -2
  29. package/dist/agent/index.d.ts +2 -2
  30. package/dist/browser/background.js +22 -16
  31. package/dist/browser/background.js.map +1 -1
  32. package/dist/browser/background.mjs +22 -16
  33. package/dist/browser/background.mjs.map +1 -1
  34. package/dist/browser/browser-adapter.d.mts +2 -2
  35. package/dist/browser/browser-adapter.d.ts +2 -2
  36. package/dist/cli/index.d.mts +2 -2
  37. package/dist/cli/index.d.ts +2 -2
  38. package/dist/cursor/cursor-adapter.d.mts +2 -2
  39. package/dist/cursor/cursor-adapter.d.ts +2 -2
  40. package/dist/cursor/extension.d.mts +2 -2
  41. package/dist/cursor/extension.d.ts +2 -2
  42. package/dist/cursor/extension.js +22 -16
  43. package/dist/cursor/extension.js.map +1 -1
  44. package/dist/cursor/extension.mjs +22 -16
  45. package/dist/cursor/extension.mjs.map +1 -1
  46. package/dist/{express-BH5ADAyb.d.mts → express-BnMO-H3y.d.mts} +1 -1
  47. package/dist/{express-jUzLIoep.d.ts → express-DQhDzNAt.d.ts} +1 -1
  48. package/dist/gateway/gateway.d.mts +2 -2
  49. package/dist/gateway/gateway.d.ts +2 -2
  50. package/dist/gateway/gateway.js +22 -16
  51. package/dist/gateway/gateway.js.map +1 -1
  52. package/dist/gateway/gateway.mjs +22 -16
  53. package/dist/gateway/gateway.mjs.map +1 -1
  54. package/dist/git-trigger/git-hooks.d.mts +2 -2
  55. package/dist/git-trigger/git-hooks.d.ts +2 -2
  56. package/dist/{index-aZHAWujT.d.mts → index-BT_aRn-I.d.mts} +1 -1
  57. package/dist/{index-DlCnmpJH.d.ts → index-D8-dvUFA.d.ts} +1 -1
  58. package/dist/{index-BR2nmxBU.d.mts → index-WHKq2v17.d.mts} +1 -1
  59. package/dist/{index-DEixUCu0.d.ts → index-rftma0ln.d.ts} +1 -1
  60. package/dist/index.d.mts +55 -9
  61. package/dist/index.d.ts +55 -9
  62. package/dist/index.js +145 -30
  63. package/dist/index.js.map +1 -1
  64. package/dist/index.mjs +143 -30
  65. package/dist/index.mjs.map +1 -1
  66. package/dist/local-evaluator/evaluator.d.mts +2 -2
  67. package/dist/local-evaluator/evaluator.d.ts +2 -2
  68. package/dist/{nextjs-LTeoPqMH.d.mts → nextjs-DFKYhxe6.d.mts} +1 -1
  69. package/dist/{nextjs-DfXOd0Au.d.ts → nextjs-DPj7ZNlq.d.ts} +1 -1
  70. package/dist/{sdk-LmvyDVpL.d.ts → sdk-CAcHgB5x.d.ts} +1 -1
  71. package/dist/{sdk-DKhwVL9X.d.mts → sdk-Cy3wAmDt.d.mts} +1 -1
  72. package/dist/transport/index.d.mts +2 -2
  73. package/dist/transport/index.d.ts +2 -2
  74. package/dist/{types-C8HyQEaz.d.mts → types-B-ubjHER.d.mts} +1 -1
  75. package/dist/{types-xofemo0G.d.ts → types-Bgn5gxHQ.d.ts} +1 -1
  76. package/dist/{types-DrDIouvO.d.mts → types-CKe8O0lR.d.mts} +41 -1
  77. package/dist/{types-DrDIouvO.d.ts → types-CKe8O0lR.d.ts} +41 -1
  78. package/dist/ui/index.d.mts +1 -1
  79. package/dist/ui/index.d.ts +1 -1
  80. package/package.json +1 -1
@@ -7,7 +7,7 @@ function getTrustLevel(score) {
7
7
  }
8
8
 
9
9
  // src/version.ts
10
- var SDK_VERSION = "3.5.0";
10
+ var SDK_VERSION = "3.6.0";
11
11
 
12
12
  // src/well-known.ts
13
13
  var CACHE_TTL_MS = 60 * 60 * 1e3;
@@ -77,10 +77,10 @@ var DEFAULT_CONFIG = {
77
77
  // through (`hasMinimumAccess('guidance', 'guidance') === true`).
78
78
  defaultAccessLevel: "none",
79
79
  // minTrustScore + minTrustScoreForFull deprecated in v2.3.0 — server decides.
80
- // Round-18.5 F4: cacheTtl deliberately unset. When undefined, cacheResult
81
- // applies the split default (60s autonomous / 300s step-up). When the
82
- // caller sets cacheTtl explicitly, that value is honoured uniformly.
83
- // Set cacheTtl: 0 to disable caching entirely.
80
+ // cacheTtl deliberately unset. When undefined, cacheResult applies the 60s
81
+ // default; the caller's explicit value is honoured for cacheable results.
82
+ // Denials and step-up escalations are NEVER cached (3.6.0) regardless of
83
+ // cacheTtl. Set cacheTtl: 0 to disable caching entirely.
84
84
  debug: false
85
85
  };
86
86
  var initCheckPerformed = false;
@@ -167,10 +167,9 @@ function getCachedResult(request, counterpartyId) {
167
167
  }
168
168
  return null;
169
169
  }
170
- var DEFAULT_AUTONOMOUS_TTL_SECONDS = 60;
171
- var DEFAULT_STEP_UP_TTL_SECONDS = 300;
170
+ var DEFAULT_CACHE_TTL_SECONDS = 60;
172
171
  function cacheResult(request, result, configuredTtl, counterpartyId) {
173
- const ttlSeconds = configuredTtl && configuredTtl > 0 ? configuredTtl : result.requiresStepUp ? DEFAULT_STEP_UP_TTL_SECONDS : DEFAULT_AUTONOMOUS_TTL_SECONDS;
172
+ const ttlSeconds = configuredTtl && configuredTtl > 0 ? configuredTtl : DEFAULT_CACHE_TTL_SECONDS;
174
173
  const key = getCacheKey(request, counterpartyId);
175
174
  verificationCache.set(key, {
176
175
  result,
@@ -346,7 +345,7 @@ async function callVerifyAccessAPI(config, request) {
346
345
  };
347
346
  }
348
347
  }
349
- async function verify(config, request) {
348
+ async function verify(config, request, options) {
350
349
  const mergedConfig = { ...DEFAULT_CONFIG, ...config };
351
350
  const urls = mergedConfig.apiBaseUrl ? getCachedWellKnownUrls(mergedConfig.apiBaseUrl) : void 0;
352
351
  if (!initCheckPerformed && !mergedConfig.disableInitChecks && mergedConfig.apiBaseUrl) {
@@ -362,7 +361,7 @@ async function verify(config, request) {
362
361
  "[VerificationGateway] minTrustScore / minTrustScoreForFull are deprecated in v2.3.0 and have no effect. Server is now the single source of truth for access-level decisions (the SDK reads access.accessLevel from the verify-access response). To gate access to an endpoint, configure the endpoint's trust_score_requirement server-side."
363
362
  );
364
363
  }
365
- if (mergedConfig.cacheTtl !== 0) {
364
+ if (mergedConfig.cacheTtl !== 0 && !options?.bypassCache) {
366
365
  const cached = getCachedResult(request, mergedConfig.counterpartyId);
367
366
  if (cached) {
368
367
  if (mergedConfig.debug) {
@@ -382,6 +381,8 @@ async function verify(config, request) {
382
381
  console.log("[VerificationGateway] Calling verify-access API");
383
382
  }
384
383
  const apiResponse = await callVerifyAccessAPI(mergedConfig, enrichedRequest);
384
+ const rawServerDenialReasons = apiResponse.denialReasons;
385
+ const serverDenialReasons = Array.isArray(rawServerDenialReasons) && rawServerDenialReasons.length > 0 ? rawServerDenialReasons : void 0;
385
386
  if (!apiResponse.success) {
386
387
  return createGuidanceResponse(mergedConfig, apiResponse.error, {
387
388
  source: "api_error",
@@ -403,7 +404,10 @@ async function verify(config, request) {
403
404
  // ALSO short-circuit on `!identityVerified || !policyAllowed` before
404
405
  // the gate check — belt-and-braces.
405
406
  accessLevel: "none",
406
- denialReasons: aggregatedFailures && aggregatedFailures.length > 0 ? aggregatedFailures.map((f) => f.message) : apiResponse.access?.reason ? [apiResponse.access.reason] : ["Access denied"],
407
+ // 3.6.0: the server now emits top-level denialReasons on every deny
408
+ // path — prefer it verbatim; the failure-message reconstruction remains
409
+ // as the fallback for older backends.
410
+ denialReasons: serverDenialReasons ?? (aggregatedFailures && aggregatedFailures.length > 0 ? aggregatedFailures.map((f) => f.message) : apiResponse.access?.reason ? [apiResponse.access.reason] : ["Access denied"]),
407
411
  failures: aggregatedFailures,
408
412
  requiresStepUp: apiResponse.access?.requiresStepUp,
409
413
  requiresApproval: apiResponse.access?.requiresApproval,
@@ -480,9 +484,7 @@ async function verify(config, request) {
480
484
  if (result.recommendation === "deny") {
481
485
  result.policyAllowed = false;
482
486
  result.accessLevel = "none";
483
- result.denialReasons = result.recommendationReasons || [
484
- "Access denied by AstraSync recommendation"
485
- ];
487
+ result.denialReasons = serverDenialReasons ?? result.recommendationReasons ?? ["Access denied by AstraSync recommendation"];
486
488
  result.guidance = result.runtimeChallenge ? {
487
489
  message: `Verification failed: ${result.runtimeChallenge.reason || "runtime challenge failed"}`,
488
490
  registrationUrl: urls?.registrationUrl ?? "",
@@ -494,9 +496,10 @@ async function verify(config, request) {
494
496
  };
495
497
  } else if (result.recommendation === "step_up_required") {
496
498
  result.requiresStepUp = true;
497
- result.denialReasons = result.recommendationReasons || ["Step-up verification required"];
499
+ result.denialReasons = serverDenialReasons ?? result.recommendationReasons ?? ["Step-up verification required"];
498
500
  }
499
- if (mergedConfig.cacheTtl !== 0 && result.recommendation !== "deny") {
501
+ const escalates = result.requiresStepUp === true || result.requiresApproval === true || result.recommendation === "step_up_required";
502
+ if (mergedConfig.cacheTtl !== 0 && result.recommendation !== "deny" && !escalates) {
500
503
  cacheResult(request, result, mergedConfig.cacheTtl, mergedConfig.counterpartyId);
501
504
  }
502
505
  return result;
@@ -528,6 +531,64 @@ function annotateApprovalRequired(result) {
528
531
  result.denialReasons = [APPROVAL_REASON, ...result.denialReasons ?? []];
529
532
  }
530
533
 
534
+ // src/adapters/stepup-poll.ts
535
+ var DEFAULT_POLL_INTERVAL_MS = 3e3;
536
+ var MIN_POLL_INTERVAL_MS = 250;
537
+ var FALLBACK_MAX_WAIT_MS = 5 * 60 * 1e3;
538
+ var EXPIRY_GRACE_MS = 5e3;
539
+ var POLL_REQUEST_TIMEOUT_MS = 1e4;
540
+ function resolvePollUrl(config, pollUrl) {
541
+ if (pollUrl.startsWith("http")) return pollUrl;
542
+ const origin = (config.apiBaseUrl ?? "").replace(/\/api\/?$/, "");
543
+ return origin + pollUrl;
544
+ }
545
+ async function pollStepUpStatus(config, approval) {
546
+ if (!approval.pollUrl) return null;
547
+ try {
548
+ const res = await fetch(resolvePollUrl(config, approval.pollUrl), {
549
+ method: "GET",
550
+ signal: AbortSignal.timeout(POLL_REQUEST_TIMEOUT_MS)
551
+ });
552
+ if (!res.ok) return null;
553
+ const body = await res.json();
554
+ const data = body?.data;
555
+ const status = data && typeof data === "object" ? data.status : body?.status;
556
+ return typeof status === "string" ? status : null;
557
+ } catch {
558
+ return null;
559
+ }
560
+ }
561
+ function resolveDeadline(config, approval) {
562
+ const now = Date.now();
563
+ const maxWait = config.stepUpMaxWaitMs ?? 0;
564
+ if (maxWait > 0) return now + maxWait;
565
+ if (approval.expiresAt) {
566
+ const expiry = Date.parse(approval.expiresAt);
567
+ if (!Number.isNaN(expiry)) return Math.max(now, expiry) + EXPIRY_GRACE_MS;
568
+ }
569
+ return now + FALLBACK_MAX_WAIT_MS;
570
+ }
571
+ function sleep(ms) {
572
+ return new Promise((resolve) => setTimeout(resolve, ms));
573
+ }
574
+ async function awaitStepUpApproval(config, approval) {
575
+ const deadline = resolveDeadline(config, approval);
576
+ const pollInterval = Math.max(
577
+ MIN_POLL_INTERVAL_MS,
578
+ config.stepUpPollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS
579
+ );
580
+ while (Date.now() < deadline) {
581
+ const status = await pollStepUpStatus(config, approval);
582
+ if (status && status !== "pending") {
583
+ return status;
584
+ }
585
+ const remaining = deadline - Date.now();
586
+ if (remaining <= 0) break;
587
+ await sleep(Math.min(pollInterval, remaining));
588
+ }
589
+ return "timeout";
590
+ }
591
+
531
592
  // src/transport/mcp-server.ts
532
593
  var MCP_VERIFIED_HOP_HEADER = "X-Astra-Verified-Hop";
533
594
  var MCP_VERIFIED_HOP_MAX_AGE_MS = 6e4;
@@ -691,7 +752,10 @@ function defaultMcpDenied(result, req, res) {
691
752
  // Round-10: aggregated per-dimension detail + correlation handle.
692
753
  failures: result.failures,
693
754
  correlationId: result.correlationId,
694
- stepUpApproval: result.stepUpApproval
755
+ stepUpApproval: result.stepUpApproval,
756
+ // 3.6.0: terminal outcome of an opt-in hold-and-poll wait (denied /
757
+ // expired / consumed / timeout). Absent when the feature is off.
758
+ ...result.stepUpOutcome && { stepUpOutcome: result.stepUpOutcome }
695
759
  }
696
760
  }
697
761
  });
@@ -832,7 +896,7 @@ function createMcpMiddleware(options) {
832
896
  }
833
897
  const counterpartyUrl = config.counterpartyUrl || `${req.protocol}://${req.get("host")}${req.path}`;
834
898
  const shouldRecordDecisions = recordDecisions !== false;
835
- const result = await verify(config, {
899
+ const verificationRequest = {
836
900
  credentials,
837
901
  purpose: pdlss.purpose,
838
902
  action: pdlss.action,
@@ -850,9 +914,21 @@ function createMcpMiddleware(options) {
850
914
  userAgent: req.headers["user-agent"],
851
915
  host: req.headers.host
852
916
  }
853
- });
917
+ };
918
+ let result = await verify(config, verificationRequest);
854
919
  req.agentVerification = result;
855
- const sessionId = result.sessionId;
920
+ let sessionId = result.sessionId;
921
+ if (config.stepUpMaxWaitMs !== void 0 && result.identityVerified && requiresHumanApproval(result) && result.stepUpApproval?.pollUrl) {
922
+ const outcome = await awaitStepUpApproval(config, result.stepUpApproval);
923
+ res.setHeader("X-Astra-Step-Up", outcome);
924
+ if (outcome === "approved") {
925
+ result = await verify(config, verificationRequest, { bypassCache: true });
926
+ req.agentVerification = result;
927
+ sessionId = result.sessionId;
928
+ } else {
929
+ result.stepUpOutcome = outcome;
930
+ }
931
+ }
856
932
  if (!result.identityVerified || !result.policyAllowed) {
857
933
  if (shouldRecordDecisions && sessionId) {
858
934
  recordDecision(config, sessionId, "denied", result.denialReasons?.[0]).catch(() => {