@astrasyncai/verification-gateway 3.5.0 → 3.7.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 (89) 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/bin/astrasync-claude-hook.js +4791 -0
  31. package/dist/bin/astrasync-guard.js +5990 -0
  32. package/dist/bin/astrasync.js +6840 -330
  33. package/dist/browser/background.js +22 -16
  34. package/dist/browser/background.js.map +1 -1
  35. package/dist/browser/background.mjs +22 -16
  36. package/dist/browser/background.mjs.map +1 -1
  37. package/dist/browser/browser-adapter.d.mts +2 -2
  38. package/dist/browser/browser-adapter.d.ts +2 -2
  39. package/dist/claude-code/claude-code-adapter.d.mts +96 -0
  40. package/dist/claude-code/claude-code-adapter.d.ts +96 -0
  41. package/dist/claude-code/claude-code-adapter.js +277 -0
  42. package/dist/claude-code/claude-code-adapter.js.map +1 -0
  43. package/dist/claude-code/claude-code-adapter.mjs +249 -0
  44. package/dist/claude-code/claude-code-adapter.mjs.map +1 -0
  45. package/dist/cli/index.d.mts +2 -2
  46. package/dist/cli/index.d.ts +2 -2
  47. package/dist/cursor/cursor-adapter.d.mts +2 -2
  48. package/dist/cursor/cursor-adapter.d.ts +2 -2
  49. package/dist/cursor/extension.d.mts +2 -2
  50. package/dist/cursor/extension.d.ts +2 -2
  51. package/dist/cursor/extension.js +22 -16
  52. package/dist/cursor/extension.js.map +1 -1
  53. package/dist/cursor/extension.mjs +22 -16
  54. package/dist/cursor/extension.mjs.map +1 -1
  55. package/dist/{express-BH5ADAyb.d.mts → express-BnMO-H3y.d.mts} +1 -1
  56. package/dist/{express-jUzLIoep.d.ts → express-DQhDzNAt.d.ts} +1 -1
  57. package/dist/gateway/gateway.d.mts +2 -2
  58. package/dist/gateway/gateway.d.ts +2 -2
  59. package/dist/gateway/gateway.js +22 -16
  60. package/dist/gateway/gateway.js.map +1 -1
  61. package/dist/gateway/gateway.mjs +22 -16
  62. package/dist/gateway/gateway.mjs.map +1 -1
  63. package/dist/git-trigger/git-hooks.d.mts +2 -2
  64. package/dist/git-trigger/git-hooks.d.ts +2 -2
  65. package/dist/{index-aZHAWujT.d.mts → index-BT_aRn-I.d.mts} +1 -1
  66. package/dist/{index-DlCnmpJH.d.ts → index-D8-dvUFA.d.ts} +1 -1
  67. package/dist/{index-BR2nmxBU.d.mts → index-WHKq2v17.d.mts} +1 -1
  68. package/dist/{index-DEixUCu0.d.ts → index-rftma0ln.d.ts} +1 -1
  69. package/dist/index.d.mts +55 -9
  70. package/dist/index.d.ts +55 -9
  71. package/dist/index.js +145 -30
  72. package/dist/index.js.map +1 -1
  73. package/dist/index.mjs +143 -30
  74. package/dist/index.mjs.map +1 -1
  75. package/dist/local-evaluator/evaluator.d.mts +2 -2
  76. package/dist/local-evaluator/evaluator.d.ts +2 -2
  77. package/dist/{nextjs-LTeoPqMH.d.mts → nextjs-DFKYhxe6.d.mts} +1 -1
  78. package/dist/{nextjs-DfXOd0Au.d.ts → nextjs-DPj7ZNlq.d.ts} +1 -1
  79. package/dist/{sdk-LmvyDVpL.d.ts → sdk-CAcHgB5x.d.ts} +1 -1
  80. package/dist/{sdk-DKhwVL9X.d.mts → sdk-Cy3wAmDt.d.mts} +1 -1
  81. package/dist/transport/index.d.mts +2 -2
  82. package/dist/transport/index.d.ts +2 -2
  83. package/dist/{types-C8HyQEaz.d.mts → types-B-ubjHER.d.mts} +1 -1
  84. package/dist/{types-xofemo0G.d.ts → types-Bgn5gxHQ.d.ts} +1 -1
  85. package/dist/{types-DrDIouvO.d.mts → types-CKe8O0lR.d.mts} +41 -1
  86. package/dist/{types-DrDIouvO.d.ts → types-CKe8O0lR.d.ts} +41 -1
  87. package/dist/ui/index.d.mts +1 -1
  88. package/dist/ui/index.d.ts +1 -1
  89. package/package.json +9 -2
package/README.md CHANGED
@@ -485,6 +485,22 @@ interface GatewayConfig {
485
485
  // forwarded on every verify-access call so the server attributes traffic
486
486
  // directly to this endpoint rather than resolving by URL.
487
487
 
488
+ // Step-up hold-and-poll (v3.6.0+, OPT-IN). When stepUpMaxWaitMs is set, the
489
+ // express/mcp/nextjs middleware HOLDS a step_up_required request, polls the
490
+ // approval, and on 'approved' re-verifies once (cache-bypassed) so the
491
+ // backend redeems the single-use approval — the owner approves in their
492
+ // dashboard while the original request waits (MFA model). Unset = current
493
+ // fail-closed behavior (immediate 403 with stepUpApproval polling info).
494
+ // 0 → hold until the approval's expiresAt (+5s grace; 5-min fallback)
495
+ // >0 → hold at most this many milliseconds
496
+ // CAUTION: Next.js edge runtimes enforce ~25s wall-clock budgets — use a
497
+ // small value there (≤20000) or gate in a Node route handler instead.
498
+ stepUpMaxWaitMs?: number;
499
+ // Poll interval for the hold (default 3000ms, 250ms floor). The poll route
500
+ // is rate-limited 60/min/IP: at the default interval ~3 concurrent holds
501
+ // per merchant IP fit; excess polls degrade the hold to 'timeout'.
502
+ stepUpPollIntervalMs?: number;
503
+
488
504
  // Init self-test (v2.2.3+) — fires a HEAD probe to verify-access on first
489
505
  // call and warns if apiBaseUrl is pointing at HTML (catches the marketing-
490
506
  // 404 case). Set true for tests where the extra request is undesirable.
@@ -1,6 +1,6 @@
1
1
  import { AstraSyncGateway } from '../gateway/gateway.mjs';
2
- import { A as AgentAction, I as InterceptResult, P as PDLSSContext, V as VerificationDecision } from '../types-C8HyQEaz.mjs';
3
- import '../types-DrDIouvO.mjs';
2
+ import { A as AgentAction, I as InterceptResult, P as PDLSSContext, V as VerificationDecision } from '../types-B-ubjHER.mjs';
3
+ import '../types-CKe8O0lR.mjs';
4
4
 
5
5
  /**
6
6
  * PlatformAdapter Interface
@@ -1,6 +1,6 @@
1
1
  import { AstraSyncGateway } from '../gateway/gateway.js';
2
- import { A as AgentAction, I as InterceptResult, P as PDLSSContext, V as VerificationDecision } from '../types-xofemo0G.js';
3
- import '../types-DrDIouvO.js';
2
+ import { A as AgentAction, I as InterceptResult, P as PDLSSContext, V as VerificationDecision } from '../types-Bgn5gxHQ.js';
3
+ import '../types-CKe8O0lR.js';
4
4
 
5
5
  /**
6
6
  * PlatformAdapter Interface
@@ -1,3 +1,3 @@
1
1
  import 'express';
2
- import '../types-DrDIouvO.mjs';
3
- export { c as createMiddleware, a as extractAstraSyncCredentials } from '../express-BH5ADAyb.mjs';
2
+ import '../types-CKe8O0lR.mjs';
3
+ export { c as createMiddleware, a as extractAstraSyncCredentials } from '../express-BnMO-H3y.mjs';
@@ -1,3 +1,3 @@
1
1
  import 'express';
2
- import '../types-DrDIouvO.js';
3
- export { c as createMiddleware, a as extractAstraSyncCredentials } from '../express-jUzLIoep.js';
2
+ import '../types-CKe8O0lR.js';
3
+ export { c as createMiddleware, a as extractAstraSyncCredentials } from '../express-DQhDzNAt.js';
@@ -25,6 +25,64 @@ __export(express_exports, {
25
25
  });
26
26
  module.exports = __toCommonJS(express_exports);
27
27
 
28
+ // src/adapters/stepup-poll.ts
29
+ var DEFAULT_POLL_INTERVAL_MS = 3e3;
30
+ var MIN_POLL_INTERVAL_MS = 250;
31
+ var FALLBACK_MAX_WAIT_MS = 5 * 60 * 1e3;
32
+ var EXPIRY_GRACE_MS = 5e3;
33
+ var POLL_REQUEST_TIMEOUT_MS = 1e4;
34
+ function resolvePollUrl(config, pollUrl) {
35
+ if (pollUrl.startsWith("http")) return pollUrl;
36
+ const origin = (config.apiBaseUrl ?? "").replace(/\/api\/?$/, "");
37
+ return origin + pollUrl;
38
+ }
39
+ async function pollStepUpStatus(config, approval) {
40
+ if (!approval.pollUrl) return null;
41
+ try {
42
+ const res = await fetch(resolvePollUrl(config, approval.pollUrl), {
43
+ method: "GET",
44
+ signal: AbortSignal.timeout(POLL_REQUEST_TIMEOUT_MS)
45
+ });
46
+ if (!res.ok) return null;
47
+ const body = await res.json();
48
+ const data = body?.data;
49
+ const status = data && typeof data === "object" ? data.status : body?.status;
50
+ return typeof status === "string" ? status : null;
51
+ } catch {
52
+ return null;
53
+ }
54
+ }
55
+ function resolveDeadline(config, approval) {
56
+ const now = Date.now();
57
+ const maxWait = config.stepUpMaxWaitMs ?? 0;
58
+ if (maxWait > 0) return now + maxWait;
59
+ if (approval.expiresAt) {
60
+ const expiry = Date.parse(approval.expiresAt);
61
+ if (!Number.isNaN(expiry)) return Math.max(now, expiry) + EXPIRY_GRACE_MS;
62
+ }
63
+ return now + FALLBACK_MAX_WAIT_MS;
64
+ }
65
+ function sleep(ms) {
66
+ return new Promise((resolve) => setTimeout(resolve, ms));
67
+ }
68
+ async function awaitStepUpApproval(config, approval) {
69
+ const deadline = resolveDeadline(config, approval);
70
+ const pollInterval = Math.max(
71
+ MIN_POLL_INTERVAL_MS,
72
+ config.stepUpPollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS
73
+ );
74
+ while (Date.now() < deadline) {
75
+ const status = await pollStepUpStatus(config, approval);
76
+ if (status && status !== "pending") {
77
+ return status;
78
+ }
79
+ const remaining = deadline - Date.now();
80
+ if (remaining <= 0) break;
81
+ await sleep(Math.min(pollInterval, remaining));
82
+ }
83
+ return "timeout";
84
+ }
85
+
28
86
  // src/access-levels.ts
29
87
  function getTrustLevel(score) {
30
88
  if (score >= 80) return "PLATINUM";
@@ -34,7 +92,7 @@ function getTrustLevel(score) {
34
92
  }
35
93
 
36
94
  // src/version.ts
37
- var SDK_VERSION = "3.5.0";
95
+ var SDK_VERSION = "3.7.0";
38
96
 
39
97
  // src/well-known.ts
40
98
  var CACHE_TTL_MS = 60 * 60 * 1e3;
@@ -104,10 +162,10 @@ var DEFAULT_CONFIG = {
104
162
  // through (`hasMinimumAccess('guidance', 'guidance') === true`).
105
163
  defaultAccessLevel: "none",
106
164
  // minTrustScore + minTrustScoreForFull deprecated in v2.3.0 — server decides.
107
- // Round-18.5 F4: cacheTtl deliberately unset. When undefined, cacheResult
108
- // applies the split default (60s autonomous / 300s step-up). When the
109
- // caller sets cacheTtl explicitly, that value is honoured uniformly.
110
- // Set cacheTtl: 0 to disable caching entirely.
165
+ // cacheTtl deliberately unset. When undefined, cacheResult applies the 60s
166
+ // default; the caller's explicit value is honoured for cacheable results.
167
+ // Denials and step-up escalations are NEVER cached (3.6.0) regardless of
168
+ // cacheTtl. Set cacheTtl: 0 to disable caching entirely.
111
169
  debug: false
112
170
  };
113
171
  var initCheckPerformed = false;
@@ -194,10 +252,9 @@ function getCachedResult(request, counterpartyId) {
194
252
  }
195
253
  return null;
196
254
  }
197
- var DEFAULT_AUTONOMOUS_TTL_SECONDS = 60;
198
- var DEFAULT_STEP_UP_TTL_SECONDS = 300;
255
+ var DEFAULT_CACHE_TTL_SECONDS = 60;
199
256
  function cacheResult(request, result, configuredTtl, counterpartyId) {
200
- const ttlSeconds = configuredTtl && configuredTtl > 0 ? configuredTtl : result.requiresStepUp ? DEFAULT_STEP_UP_TTL_SECONDS : DEFAULT_AUTONOMOUS_TTL_SECONDS;
257
+ const ttlSeconds = configuredTtl && configuredTtl > 0 ? configuredTtl : DEFAULT_CACHE_TTL_SECONDS;
201
258
  const key = getCacheKey(request, counterpartyId);
202
259
  verificationCache.set(key, {
203
260
  result,
@@ -373,7 +430,7 @@ async function callVerifyAccessAPI(config, request) {
373
430
  };
374
431
  }
375
432
  }
376
- async function verify(config, request) {
433
+ async function verify(config, request, options) {
377
434
  const mergedConfig = { ...DEFAULT_CONFIG, ...config };
378
435
  const urls = mergedConfig.apiBaseUrl ? getCachedWellKnownUrls(mergedConfig.apiBaseUrl) : void 0;
379
436
  if (!initCheckPerformed && !mergedConfig.disableInitChecks && mergedConfig.apiBaseUrl) {
@@ -389,7 +446,7 @@ async function verify(config, request) {
389
446
  "[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."
390
447
  );
391
448
  }
392
- if (mergedConfig.cacheTtl !== 0) {
449
+ if (mergedConfig.cacheTtl !== 0 && !options?.bypassCache) {
393
450
  const cached = getCachedResult(request, mergedConfig.counterpartyId);
394
451
  if (cached) {
395
452
  if (mergedConfig.debug) {
@@ -409,6 +466,8 @@ async function verify(config, request) {
409
466
  console.log("[VerificationGateway] Calling verify-access API");
410
467
  }
411
468
  const apiResponse = await callVerifyAccessAPI(mergedConfig, enrichedRequest);
469
+ const rawServerDenialReasons = apiResponse.denialReasons;
470
+ const serverDenialReasons = Array.isArray(rawServerDenialReasons) && rawServerDenialReasons.length > 0 ? rawServerDenialReasons : void 0;
412
471
  if (!apiResponse.success) {
413
472
  return createGuidanceResponse(mergedConfig, apiResponse.error, {
414
473
  source: "api_error",
@@ -430,7 +489,10 @@ async function verify(config, request) {
430
489
  // ALSO short-circuit on `!identityVerified || !policyAllowed` before
431
490
  // the gate check — belt-and-braces.
432
491
  accessLevel: "none",
433
- denialReasons: aggregatedFailures && aggregatedFailures.length > 0 ? aggregatedFailures.map((f) => f.message) : apiResponse.access?.reason ? [apiResponse.access.reason] : ["Access denied"],
492
+ // 3.6.0: the server now emits top-level denialReasons on every deny
493
+ // path — prefer it verbatim; the failure-message reconstruction remains
494
+ // as the fallback for older backends.
495
+ denialReasons: serverDenialReasons ?? (aggregatedFailures && aggregatedFailures.length > 0 ? aggregatedFailures.map((f) => f.message) : apiResponse.access?.reason ? [apiResponse.access.reason] : ["Access denied"]),
434
496
  failures: aggregatedFailures,
435
497
  requiresStepUp: apiResponse.access?.requiresStepUp,
436
498
  requiresApproval: apiResponse.access?.requiresApproval,
@@ -507,9 +569,7 @@ async function verify(config, request) {
507
569
  if (result.recommendation === "deny") {
508
570
  result.policyAllowed = false;
509
571
  result.accessLevel = "none";
510
- result.denialReasons = result.recommendationReasons || [
511
- "Access denied by AstraSync recommendation"
512
- ];
572
+ result.denialReasons = serverDenialReasons ?? result.recommendationReasons ?? ["Access denied by AstraSync recommendation"];
513
573
  result.guidance = result.runtimeChallenge ? {
514
574
  message: `Verification failed: ${result.runtimeChallenge.reason || "runtime challenge failed"}`,
515
575
  registrationUrl: urls?.registrationUrl ?? "",
@@ -521,9 +581,10 @@ async function verify(config, request) {
521
581
  };
522
582
  } else if (result.recommendation === "step_up_required") {
523
583
  result.requiresStepUp = true;
524
- result.denialReasons = result.recommendationReasons || ["Step-up verification required"];
584
+ result.denialReasons = serverDenialReasons ?? result.recommendationReasons ?? ["Step-up verification required"];
525
585
  }
526
- if (mergedConfig.cacheTtl !== 0 && result.recommendation !== "deny") {
586
+ const escalates = result.requiresStepUp === true || result.requiresApproval === true || result.recommendation === "step_up_required";
587
+ if (mergedConfig.cacheTtl !== 0 && result.recommendation !== "deny" && !escalates) {
527
588
  cacheResult(request, result, mergedConfig.cacheTtl, mergedConfig.counterpartyId);
528
589
  }
529
590
  return result;
@@ -836,7 +897,10 @@ function defaultOnDenied(result, _req, res) {
836
897
  // Round-10: aggregated per-dimension detail + correlation handle.
837
898
  failures: result.failures,
838
899
  correlationId: result.correlationId,
839
- stepUpApproval: result.stepUpApproval
900
+ stepUpApproval: result.stepUpApproval,
901
+ // 3.6.0: terminal outcome of an opt-in hold-and-poll wait (denied /
902
+ // expired / consumed / timeout). Absent when the feature is off.
903
+ ...result.stepUpOutcome && { stepUpOutcome: result.stepUpOutcome }
840
904
  }
841
905
  });
842
906
  }
@@ -1001,7 +1065,7 @@ function createMiddleware(options) {
1001
1065
  }
1002
1066
  }
1003
1067
  }
1004
- const result = await verify(config, {
1068
+ const verificationRequest = {
1005
1069
  credentials,
1006
1070
  purpose,
1007
1071
  action: pdlssPair.action,
@@ -1021,9 +1085,21 @@ function createMiddleware(options) {
1021
1085
  forwardedFor: forwardedForStr,
1022
1086
  agentCardUrl
1023
1087
  }
1024
- });
1088
+ };
1089
+ let result = await verify(config, verificationRequest);
1025
1090
  req.agentVerification = result;
1026
- const sessionId = result.sessionId;
1091
+ let sessionId = result.sessionId;
1092
+ if (config.stepUpMaxWaitMs !== void 0 && result.identityVerified && requiresHumanApproval(result) && result.stepUpApproval?.pollUrl) {
1093
+ const outcome = await awaitStepUpApproval(config, result.stepUpApproval);
1094
+ res.setHeader("X-Astra-Step-Up", outcome);
1095
+ if (outcome === "approved") {
1096
+ result = await verify(config, verificationRequest, { bypassCache: true });
1097
+ req.agentVerification = result;
1098
+ sessionId = result.sessionId;
1099
+ } else {
1100
+ result.stepUpOutcome = outcome;
1101
+ }
1102
+ }
1027
1103
  if (!result.identityVerified || !result.policyAllowed) {
1028
1104
  if (shouldRecordDecisions && sessionId) {
1029
1105
  recordDecision(config, sessionId, "denied", result.denialReasons?.[0]).catch(() => {