@astrasyncai/verification-gateway 3.7.1 → 3.9.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 (109) hide show
  1. package/README.md +99 -11
  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 +2 -1
  7. package/dist/adapters/express.js.map +1 -1
  8. package/dist/adapters/express.mjs +2 -1
  9. package/dist/adapters/express.mjs.map +1 -1
  10. package/dist/adapters/http-pdlss.d.mts +81 -0
  11. package/dist/adapters/http-pdlss.d.ts +81 -0
  12. package/dist/adapters/http-pdlss.js +117 -0
  13. package/dist/adapters/http-pdlss.js.map +1 -0
  14. package/dist/adapters/http-pdlss.mjs +87 -0
  15. package/dist/adapters/http-pdlss.mjs.map +1 -0
  16. package/dist/adapters/mcp.d.mts +1 -1
  17. package/dist/adapters/mcp.d.ts +1 -1
  18. package/dist/adapters/mcp.js +2 -1
  19. package/dist/adapters/mcp.js.map +1 -1
  20. package/dist/adapters/mcp.mjs +2 -1
  21. package/dist/adapters/mcp.mjs.map +1 -1
  22. package/dist/adapters/nextjs.d.mts +2 -2
  23. package/dist/adapters/nextjs.d.ts +2 -2
  24. package/dist/adapters/nextjs.js +2 -1
  25. package/dist/adapters/nextjs.js.map +1 -1
  26. package/dist/adapters/nextjs.mjs +2 -1
  27. package/dist/adapters/nextjs.mjs.map +1 -1
  28. package/dist/adapters/sdk.d.mts +2 -2
  29. package/dist/adapters/sdk.d.ts +2 -2
  30. package/dist/adapters/sdk.js +2 -1
  31. package/dist/adapters/sdk.js.map +1 -1
  32. package/dist/adapters/sdk.mjs +2 -1
  33. package/dist/adapters/sdk.mjs.map +1 -1
  34. package/dist/agent/index.d.mts +2 -2
  35. package/dist/agent/index.d.ts +2 -2
  36. package/dist/bin/astrasync-claude-hook.js +14 -1
  37. package/dist/bin/astrasync-guard.js +14 -1
  38. package/dist/bin/astrasync.js +31 -1
  39. package/dist/browser/background.js +14 -1
  40. package/dist/browser/background.js.map +1 -1
  41. package/dist/browser/background.mjs +14 -1
  42. package/dist/browser/background.mjs.map +1 -1
  43. package/dist/browser/browser-adapter.d.mts +2 -2
  44. package/dist/browser/browser-adapter.d.ts +2 -2
  45. package/dist/claude-code/claude-code-adapter.d.mts +2 -2
  46. package/dist/claude-code/claude-code-adapter.d.ts +2 -2
  47. package/dist/cli/index.d.mts +2 -2
  48. package/dist/cli/index.d.ts +2 -2
  49. package/dist/cursor/cursor-adapter.d.mts +2 -2
  50. package/dist/cursor/cursor-adapter.d.ts +2 -2
  51. package/dist/cursor/extension.d.mts +2 -2
  52. package/dist/cursor/extension.d.ts +2 -2
  53. package/dist/cursor/extension.js +14 -1
  54. package/dist/cursor/extension.js.map +1 -1
  55. package/dist/cursor/extension.mjs +14 -1
  56. package/dist/cursor/extension.mjs.map +1 -1
  57. package/dist/edge-config.d.mts +130 -0
  58. package/dist/edge-config.d.ts +130 -0
  59. package/dist/edge-config.js +162 -0
  60. package/dist/edge-config.js.map +1 -0
  61. package/dist/edge-config.mjs +131 -0
  62. package/dist/edge-config.mjs.map +1 -0
  63. package/dist/{express-BnMO-H3y.d.mts → express-DXUyvv7t.d.mts} +1 -1
  64. package/dist/{express-DQhDzNAt.d.ts → express-sGXhDdd0.d.ts} +1 -1
  65. package/dist/gateway/gateway.d.mts +2 -2
  66. package/dist/gateway/gateway.d.ts +2 -2
  67. package/dist/gateway/gateway.js +14 -1
  68. package/dist/gateway/gateway.js.map +1 -1
  69. package/dist/gateway/gateway.mjs +14 -1
  70. package/dist/gateway/gateway.mjs.map +1 -1
  71. package/dist/git-trigger/git-hooks.d.mts +2 -2
  72. package/dist/git-trigger/git-hooks.d.ts +2 -2
  73. package/dist/{index-D8-dvUFA.d.ts → index-C61VL_OW.d.ts} +1 -1
  74. package/dist/{index-rftma0ln.d.ts → index-CMg-vjEK.d.ts} +1 -1
  75. package/dist/{index-WHKq2v17.d.mts → index-Dalih3EB.d.mts} +1 -1
  76. package/dist/{index-BT_aRn-I.d.mts → index-DmmdehNu.d.mts} +1 -1
  77. package/dist/index.d.mts +26 -82
  78. package/dist/index.d.ts +26 -82
  79. package/dist/index.js +226 -10
  80. package/dist/index.js.map +1 -1
  81. package/dist/index.mjs +215 -10
  82. package/dist/index.mjs.map +1 -1
  83. package/dist/local-evaluator/evaluator.d.mts +2 -2
  84. package/dist/local-evaluator/evaluator.d.ts +2 -2
  85. package/dist/{nextjs-DPj7ZNlq.d.ts → nextjs-DwUW-Nm1.d.ts} +1 -1
  86. package/dist/{nextjs-DFKYhxe6.d.mts → nextjs-F_2pbPIA.d.mts} +1 -1
  87. package/dist/platform-signatures.d.mts +47 -0
  88. package/dist/platform-signatures.d.ts +47 -0
  89. package/dist/platform-signatures.js +89 -0
  90. package/dist/platform-signatures.js.map +1 -0
  91. package/dist/platform-signatures.mjs +63 -0
  92. package/dist/platform-signatures.mjs.map +1 -0
  93. package/dist/{sdk-CAcHgB5x.d.ts → sdk-CfHANs4A.d.ts} +1 -1
  94. package/dist/{sdk-Cy3wAmDt.d.mts → sdk-Clob2-0A.d.mts} +1 -1
  95. package/dist/transport/index.d.mts +2 -2
  96. package/dist/transport/index.d.ts +2 -2
  97. package/dist/{types-CKe8O0lR.d.mts → types-ACMsV07B.d.mts} +112 -9
  98. package/dist/{types-CKe8O0lR.d.ts → types-ACMsV07B.d.ts} +112 -9
  99. package/dist/{types-B-ubjHER.d.mts → types-BtqUvK8v.d.mts} +1 -1
  100. package/dist/{types-Bgn5gxHQ.d.ts → types-DmSDgPpG.d.ts} +1 -1
  101. package/dist/ui/index.d.mts +1 -1
  102. package/dist/ui/index.d.ts +1 -1
  103. package/dist/verify.d.mts +144 -0
  104. package/dist/verify.d.ts +144 -0
  105. package/dist/verify.js +593 -0
  106. package/dist/verify.js.map +1 -0
  107. package/dist/verify.mjs +557 -0
  108. package/dist/verify.mjs.map +1 -0
  109. package/package.json +21 -1
@@ -0,0 +1,557 @@
1
+ // src/access-levels.ts
2
+ function getTrustLevel(score) {
3
+ if (score >= 80) return "PLATINUM";
4
+ if (score >= 60) return "GOLD";
5
+ if (score >= 40) return "SILVER";
6
+ return "BRONZE";
7
+ }
8
+
9
+ // src/version.ts
10
+ var SDK_VERSION = "3.9.0";
11
+
12
+ // src/well-known.ts
13
+ var CACHE_TTL_MS = 60 * 60 * 1e3;
14
+ var cache = /* @__PURE__ */ new Map();
15
+ function getCachedWellKnownUrls(apiBaseUrl) {
16
+ return cache.get(apiBaseUrl)?.data;
17
+ }
18
+
19
+ // src/verify.ts
20
+ var DEFAULT_CONFIG = {
21
+ apiBaseUrl: "https://astrasync.ai/api",
22
+ // v2.3.9 (defect #30): default for unconfigured callers is `'none'` (no
23
+ // access). Pre-rename this defaulted to `'guidance'`, which combined with
24
+ // a route gated at `'guidance'` to silently let unverified traffic
25
+ // through (`hasMinimumAccess('guidance', 'guidance') === true`).
26
+ defaultAccessLevel: "none",
27
+ // minTrustScore + minTrustScoreForFull deprecated in v2.3.0 — server decides.
28
+ // cacheTtl deliberately unset. When undefined, cacheResult applies the 60s
29
+ // default; the caller's explicit value is honoured for cacheable results.
30
+ // Denials and step-up escalations are NEVER cached (3.6.0) regardless of
31
+ // cacheTtl. Set cacheTtl: 0 to disable caching entirely.
32
+ debug: false
33
+ };
34
+ var initCheckPerformed = false;
35
+ var deprecationWarningShown = false;
36
+ async function performInitCheck(apiBaseUrl, debug, strictInit) {
37
+ initCheckPerformed = true;
38
+ try {
39
+ const probeUrl = `${apiBaseUrl}/agents/verify-access`;
40
+ const response = await fetch(probeUrl, { method: "HEAD" });
41
+ const contentType = response.headers.get("content-type") ?? "";
42
+ if (contentType.startsWith("text/html")) {
43
+ const message = `[VerificationGateway] apiBaseUrl '${apiBaseUrl}' returned HTML (content-type: ${contentType}). This usually means apiBaseUrl is pointing at a marketing site instead of the API. Expected: 'https://astrasync.ai/api' (prod) or 'https://staging.astrasync.ai/api' (staging).`;
44
+ if (strictInit) {
45
+ throw new Error(`${message} (strictInit=true)`);
46
+ }
47
+ console.warn(`${message} Set disableInitChecks: true on GatewayConfig to silence.`);
48
+ } else if (debug) {
49
+ console.log(
50
+ `[VerificationGateway] init check passed for ${apiBaseUrl} (content-type: ${contentType})`
51
+ );
52
+ }
53
+ } catch (err) {
54
+ if (strictInit) {
55
+ throw err;
56
+ }
57
+ if (debug) {
58
+ console.log(`[VerificationGateway] init check failed (non-blocking): ${String(err)}`);
59
+ }
60
+ }
61
+ }
62
+ var verificationCache = /* @__PURE__ */ new Map();
63
+ function getCacheKey(request, counterpartyId) {
64
+ const c = request.credentials;
65
+ return [
66
+ c.astraId || "",
67
+ c.apiKey || "",
68
+ c.jwt || "",
69
+ request.purpose || "",
70
+ request.action || "",
71
+ request.resourceType || "",
72
+ request.resource || "",
73
+ request.jurisdiction || "",
74
+ request.transactionValue ?? "",
75
+ request.currency || "",
76
+ // SECURITY (cross-merchant cache leak): the merchant identity is sent via
77
+ // `config.counterpartyId`, NOT on the request, so it was previously absent
78
+ // from the key — two verifies for the SAME agent/purpose/action/value but
79
+ // DIFFERENT merchants collided, and a grant at a permissive merchant (low
80
+ // trust floor) was served for a stricter one. Same bug class as the
81
+ // duration omission (F-A1-07). counterpartyId affects the backend verdict
82
+ // (trust floor / per-route policy), so it MUST key the cache.
83
+ counterpartyId || "",
84
+ request.counterpartyUrl || "",
85
+ request.counterpartyType || "",
86
+ request.isSubAgentRequest ? "1" : "0",
87
+ request.parentAgentId || "",
88
+ request.subAgentDepth ?? "",
89
+ // Audit F-A1-07: previously-missing dimensions that DO affect the
90
+ // backend verdict. Without these, two requests with different
91
+ // durations (e.g. 60s vs 86400s) collided on the same cache key and
92
+ // the shorter-duration allow served the longer-duration request.
93
+ request.durationRequired ?? "",
94
+ request.invocationProtocol || "",
95
+ request.enableRuntimeChallenge ? "1" : "0",
96
+ // callerMetadata fields contribute to risk model; include the ones
97
+ // backend reads. sourceIp/userAgent/forwardedFor change per-request
98
+ // so their inclusion effectively forces a re-check for any varying
99
+ // client (the right behavior — IP-driven anomaly scoring shouldn't
100
+ // be cached across IPs).
101
+ request.callerMetadata?.sourceIp || "",
102
+ request.callerMetadata?.userAgent || "",
103
+ request.callerMetadata?.forwardedFor || "",
104
+ request.callerMetadata?.agentCardUrl || ""
105
+ ].join("|");
106
+ }
107
+ function getCachedResult(request, counterpartyId) {
108
+ const key = getCacheKey(request, counterpartyId);
109
+ const cached = verificationCache.get(key);
110
+ if (cached && cached.expiresAt > Date.now()) {
111
+ return cached.result;
112
+ }
113
+ if (cached) {
114
+ verificationCache.delete(key);
115
+ }
116
+ return null;
117
+ }
118
+ var DEFAULT_CACHE_TTL_SECONDS = 60;
119
+ function cacheResult(request, result, configuredTtl, counterpartyId) {
120
+ const ttlSeconds = configuredTtl && configuredTtl > 0 ? configuredTtl : DEFAULT_CACHE_TTL_SECONDS;
121
+ const key = getCacheKey(request, counterpartyId);
122
+ verificationCache.set(key, {
123
+ result,
124
+ expiresAt: Date.now() + ttlSeconds * 1e3
125
+ });
126
+ }
127
+ function clearCache() {
128
+ verificationCache.clear();
129
+ }
130
+ function extractCredentials(headers, query) {
131
+ const credentials = {};
132
+ const ASTRA_ID_PATTERN = /^ASTRAE?-[A-Za-z0-9_-]{1,64}$/;
133
+ const astraIdHeader = headers["x-astra-id"] || headers["X-Astra-Id"] || headers["X-ASTRA-ID"] || headers["x-astra-agentid"] || headers["X-Astra-AgentId"] || headers["x-astra-agent-id"] || headers["X-Astra-Agent-Id"] || headers["X-ASTRA-AGENT-ID"];
134
+ if (astraIdHeader) {
135
+ const raw = Array.isArray(astraIdHeader) ? astraIdHeader[0] : typeof astraIdHeader === "string" ? astraIdHeader : void 0;
136
+ if (typeof raw === "string" && ASTRA_ID_PATTERN.test(raw)) {
137
+ credentials.astraId = raw;
138
+ }
139
+ }
140
+ const apiKeyHeader = headers["x-api-key"] || headers["X-Api-Key"] || headers["X-API-KEY"];
141
+ if (apiKeyHeader) {
142
+ credentials.apiKey = Array.isArray(apiKeyHeader) ? apiKeyHeader[0] : apiKeyHeader;
143
+ }
144
+ const authHeader = headers["authorization"] || headers["Authorization"];
145
+ if (authHeader) {
146
+ const authValue = Array.isArray(authHeader) ? authHeader[0] : authHeader;
147
+ if (typeof authValue === "string") {
148
+ credentials.authorizationHeader = authValue;
149
+ if (authValue.startsWith("Bearer ")) {
150
+ credentials.jwt = authValue.slice(7);
151
+ }
152
+ }
153
+ }
154
+ if (query) {
155
+ if (query.astraId && !credentials.astraId) {
156
+ credentials.astraId = query.astraId;
157
+ }
158
+ if (query.apiKey && !credentials.apiKey) {
159
+ credentials.apiKey = query.apiKey;
160
+ }
161
+ }
162
+ return credentials;
163
+ }
164
+ function hasCredentials(credentials) {
165
+ return !!(credentials.astraId || credentials.apiKey || credentials.jwt);
166
+ }
167
+ function createGuidanceResponse(_config, reason, options = {}) {
168
+ const source = options.source ?? "no_credentials";
169
+ const isApiError = source === "api_error";
170
+ const urls = options.urls;
171
+ const guidance = isApiError ? {
172
+ message: "Verification is temporarily unavailable. Retry with exponential backoff; if the issue persists, contact support with the correlationId.",
173
+ registrationUrl: urls?.registrationUrl ?? "",
174
+ documentationUrl: urls?.documentationUrl ?? "",
175
+ steps: [
176
+ "Retry the request with exponential backoff",
177
+ "If failures persist, share the correlationId with support"
178
+ ]
179
+ } : {
180
+ message: "This service verifies AI agents before granting access. Please register your agent with AstraSync.",
181
+ registrationUrl: urls?.registrationUrl ?? "",
182
+ documentationUrl: urls?.documentationUrl ?? "",
183
+ steps: [
184
+ "Register for an AstraSync account",
185
+ "Create and register your agent",
186
+ "Add your ASTRA-ID to request headers",
187
+ "Retry your request"
188
+ ]
189
+ };
190
+ return {
191
+ // Round-18 G4: createGuidanceResponse fires for unverified-agent path or
192
+ // API-error fallback. Identity is not verified (no agent resolved);
193
+ // policy is not evaluated (we never reached the gate).
194
+ identityVerified: false,
195
+ policyAllowed: false,
196
+ // v2.3.9 (defect #30): denials grant `'none'`, NEVER a positive band.
197
+ // Adapters additionally short-circuit on `!identityVerified ||
198
+ // !policyAllowed` before the gate check, but the access level still has
199
+ // to be honest at the data layer so downstream consumers (SDK adapters
200
+ // in other languages, custom integrations) inherit the correct
201
+ // semantics.
202
+ accessLevel: "none",
203
+ guidance,
204
+ denialReasons: reason ? [reason] : ["No valid agent credentials provided"],
205
+ // Round-10 (#47, O5): on API-error fallback, surface a typed failure so
206
+ // partners (and their custom onDenied handlers) can branch on
207
+ // dimension. Without this, the synthesised stub was indistinguishable
208
+ // from a real policy deny.
209
+ failures: isApiError ? [
210
+ {
211
+ dimension: "verify_access.api_error",
212
+ message: reason ?? "Verification temporarily unavailable",
213
+ guidance: guidance.message
214
+ }
215
+ ] : void 0,
216
+ correlationId: options.correlationId,
217
+ verifiedAt: /* @__PURE__ */ new Date()
218
+ };
219
+ }
220
+ async function callVerifyAccessAPI(config, request) {
221
+ const { credentials, ...requestData } = request;
222
+ const body = {
223
+ ...credentials.astraId && { agentId: credentials.astraId },
224
+ ...requestData.purpose && { purpose: requestData.purpose }
225
+ };
226
+ if (requestData.action) body.action = requestData.action;
227
+ if (requestData.resourceType) body.resourceType = requestData.resourceType;
228
+ if (requestData.resource) body.resource = requestData.resource;
229
+ if (requestData.jurisdiction) body.jurisdiction = requestData.jurisdiction;
230
+ if (requestData.transactionValue) body.transactionValue = requestData.transactionValue;
231
+ if (requestData.currency) body.currency = requestData.currency;
232
+ if (requestData.isSubAgentRequest) body.isSubAgentRequest = requestData.isSubAgentRequest;
233
+ if (requestData.parentAgentId) body.parentAgentId = requestData.parentAgentId;
234
+ if (requestData.subAgentDepth !== void 0) body.subAgentDepth = requestData.subAgentDepth;
235
+ if (requestData.enableRuntimeChallenge)
236
+ body.enableRuntimeChallenge = requestData.enableRuntimeChallenge;
237
+ if (requestData.createSession) body.createSession = requestData.createSession;
238
+ if (requestData.durationRequired) body.durationRequired = requestData.durationRequired;
239
+ if (requestData.counterpartyType) body.counterpartyType = requestData.counterpartyType;
240
+ if (requestData.counterpartyUrl) body.counterpartyUrl = requestData.counterpartyUrl;
241
+ if (config.counterpartyId) body.counterpartyId = config.counterpartyId;
242
+ if (requestData.runtimeChallengeOptions)
243
+ body.runtimeChallengeOptions = requestData.runtimeChallengeOptions;
244
+ if (requestData.invocationProtocol) body.invocationProtocol = requestData.invocationProtocol;
245
+ if (requestData.commerceArtifacts) body.commerceArtifacts = requestData.commerceArtifacts;
246
+ body.sdkVersion = SDK_VERSION;
247
+ if (requestData.callerMetadata || requestData.clientIp || requestData.userAgent) {
248
+ const meta = {
249
+ ...requestData.clientIp && { sourceIp: requestData.clientIp },
250
+ ...requestData.userAgent && { userAgent: requestData.userAgent },
251
+ ...requestData.callerMetadata
252
+ };
253
+ if (Object.keys(meta).length > 0) body.callerMetadata = meta;
254
+ }
255
+ const headers = {
256
+ "Content-Type": "application/json",
257
+ ...config.customHeaders
258
+ };
259
+ if (config.apiKey) {
260
+ headers["Authorization"] = `Bearer ${config.apiKey}`;
261
+ headers["X-API-Key"] = config.apiKey;
262
+ }
263
+ try {
264
+ const response = await fetch(`${config.apiBaseUrl}/agents/verify-access`, {
265
+ method: "POST",
266
+ headers,
267
+ body: JSON.stringify(body)
268
+ });
269
+ const data = await response.json();
270
+ if (response.status === 410) {
271
+ return {
272
+ success: true,
273
+ access: {
274
+ allowed: false,
275
+ accessLevel: "none",
276
+ reason: "endpoint_deactivated",
277
+ failures: [
278
+ {
279
+ dimension: "endpoint.deactivated",
280
+ message: typeof data?.message === "string" ? data.message : "Endpoint has been deactivated",
281
+ guidance: typeof data?.guidance === "string" ? data.guidance : "Reactivate via POST /api/endpoints/{id}/reactivate, or update the URL on the calling agent."
282
+ }
283
+ ]
284
+ }
285
+ };
286
+ }
287
+ if (!response.ok) {
288
+ return {
289
+ success: false,
290
+ error: data.message || data.error || `API returned ${response.status}`,
291
+ correlationId: typeof data?.correlationId === "string" ? data.correlationId : void 0
292
+ };
293
+ }
294
+ return data;
295
+ } catch (error) {
296
+ const message = error instanceof Error ? error.message : "Unknown error";
297
+ return {
298
+ success: false,
299
+ error: `Failed to call verify-access API: ${message}`
300
+ };
301
+ }
302
+ }
303
+ async function verify(config, request, options) {
304
+ const mergedConfig = { ...DEFAULT_CONFIG, ...config };
305
+ const urls = mergedConfig.apiBaseUrl ? getCachedWellKnownUrls(mergedConfig.apiBaseUrl) : void 0;
306
+ if (!initCheckPerformed && !mergedConfig.disableInitChecks && mergedConfig.apiBaseUrl) {
307
+ if (mergedConfig.strictInit) {
308
+ await performInitCheck(mergedConfig.apiBaseUrl, mergedConfig.debug, true);
309
+ } else {
310
+ void performInitCheck(mergedConfig.apiBaseUrl, mergedConfig.debug, false);
311
+ }
312
+ }
313
+ if (!deprecationWarningShown && (config.minTrustScore !== void 0 || config.minTrustScoreForFull !== void 0)) {
314
+ deprecationWarningShown = true;
315
+ console.warn(
316
+ "[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."
317
+ );
318
+ }
319
+ if (mergedConfig.cacheTtl !== 0 && !options?.bypassCache) {
320
+ const cached = getCachedResult(request, mergedConfig.counterpartyId);
321
+ if (cached) {
322
+ if (mergedConfig.debug) {
323
+ console.log("[VerificationGateway] Returning cached result");
324
+ }
325
+ return cached;
326
+ }
327
+ }
328
+ const enrichedRequest = { ...request };
329
+ if (!enrichedRequest.counterpartyUrl && mergedConfig.counterpartyUrl) {
330
+ enrichedRequest.counterpartyUrl = mergedConfig.counterpartyUrl;
331
+ }
332
+ if (!enrichedRequest.counterpartyType && mergedConfig.counterpartyType) {
333
+ enrichedRequest.counterpartyType = mergedConfig.counterpartyType;
334
+ }
335
+ if (mergedConfig.debug) {
336
+ console.log("[VerificationGateway] Calling verify-access API");
337
+ }
338
+ const apiResponse = await callVerifyAccessAPI(mergedConfig, enrichedRequest);
339
+ const rawServerDenialReasons = apiResponse.denialReasons;
340
+ const serverDenialReasons = Array.isArray(rawServerDenialReasons) && rawServerDenialReasons.length > 0 ? rawServerDenialReasons : void 0;
341
+ if (!apiResponse.success) {
342
+ return createGuidanceResponse(mergedConfig, apiResponse.error, {
343
+ source: "api_error",
344
+ correlationId: apiResponse.correlationId,
345
+ urls
346
+ });
347
+ }
348
+ if (!apiResponse.access?.allowed) {
349
+ const aggregatedFailures = apiResponse.access?.failures;
350
+ const idVerifiedFromBackend = apiResponse.verificationContext?.idVerified === true;
351
+ const result2 = {
352
+ identityVerified: idVerifiedFromBackend,
353
+ policyAllowed: false,
354
+ // v2.3.9 (defect #30): denials grant `'none'`, NEVER a positive band.
355
+ // Pre-rename this hardcoded `'guidance'`, which conflated with the
356
+ // colocated `guidance: {...}` help-payload object below and let
357
+ // denied requests pass any route gated at `'guidance'` because
358
+ // `hasMinimumAccess('guidance', 'guidance') === true`. Adapters now
359
+ // ALSO short-circuit on `!identityVerified || !policyAllowed` before
360
+ // the gate check — belt-and-braces.
361
+ accessLevel: "none",
362
+ // 3.6.0: the server now emits top-level denialReasons on every deny
363
+ // path — prefer it verbatim; the failure-message reconstruction remains
364
+ // as the fallback for older backends.
365
+ denialReasons: serverDenialReasons ?? (aggregatedFailures && aggregatedFailures.length > 0 ? aggregatedFailures.map((f) => f.message) : apiResponse.access?.reason ? [apiResponse.access.reason] : ["Access denied"]),
366
+ failures: aggregatedFailures,
367
+ requiresStepUp: apiResponse.access?.requiresStepUp,
368
+ requiresApproval: apiResponse.access?.requiresApproval,
369
+ guidance: {
370
+ message: apiResponse.access?.reason || "Access denied by PDLSS policy",
371
+ registrationUrl: urls?.registrationUrl ?? "",
372
+ documentationUrl: urls?.documentationUrl ?? ""
373
+ },
374
+ verifiedAt: /* @__PURE__ */ new Date(),
375
+ // Extract sessionId so decisions can be recorded for denials too
376
+ sessionId: apiResponse.sessionId,
377
+ // Anonymous traffic has no session → correlationId is the per-attempt
378
+ // linking key (the sessionId-equivalent for anonymous callers).
379
+ correlationId: apiResponse.correlationId,
380
+ recommendation: apiResponse.recommendation,
381
+ recommendationReasons: apiResponse.recommendationReasons,
382
+ stepUpApproval: apiResponse.stepUpApproval,
383
+ settlement: apiResponse.settlement
384
+ };
385
+ return result2;
386
+ }
387
+ const agent = apiResponse.agent ? {
388
+ astraId: apiResponse.agent.astraId,
389
+ name: apiResponse.agent.name,
390
+ trustScore: apiResponse.agent.trustScore,
391
+ trustLevel: getTrustLevel(apiResponse.agent.trustScore),
392
+ blockchainVerified: apiResponse.agent.blockchainStatus === "verified",
393
+ status: apiResponse.agent.agentStatus
394
+ } : void 0;
395
+ const developer = apiResponse.developer ? {
396
+ astradId: apiResponse.developer.kyaOwnerId,
397
+ name: apiResponse.developer.fullName,
398
+ trustScore: apiResponse.developer.trustScore || 0,
399
+ verified: apiResponse.developer.identityVerified
400
+ } : void 0;
401
+ const organization = apiResponse.organization ? {
402
+ name: apiResponse.organization.name,
403
+ verified: apiResponse.organization.verified,
404
+ trustScore: apiResponse.organization.trustScore
405
+ } : void 0;
406
+ const verificationContext = apiResponse.verificationContext;
407
+ const accessLevel = apiResponse.access?.accessLevel ?? "standard";
408
+ const result = {
409
+ // Round-18 G4: backend allowed access. Identity is verified (we resolved
410
+ // the caller to an agent) and policy passed all gates. Read idVerified
411
+ // from verificationContext for symmetry with the deny branch; default true
412
+ // on success path since `access.allowed === true` implies identity was
413
+ // resolvable (anonymous-allow paths flow through createGuidanceResponse).
414
+ identityVerified: apiResponse.verificationContext?.idVerified !== false,
415
+ policyAllowed: true,
416
+ accessLevel,
417
+ agent,
418
+ developer,
419
+ organization,
420
+ appliedPolicy: apiResponse.access?.appliedPolicy,
421
+ verificationContext,
422
+ requiresStepUp: apiResponse.access?.requiresStepUp,
423
+ requiresApproval: apiResponse.access?.requiresApproval,
424
+ verifiedAt: /* @__PURE__ */ new Date(),
425
+ cacheTtl: mergedConfig.cacheTtl,
426
+ // Handshake Protocol v10 enhanced fields (present when backend returns them)
427
+ sessionId: apiResponse.sessionId,
428
+ // v2.3.10 (defect #34, round-4): anonymous responses surface correlationId
429
+ // (no session row exists for unverified callers).
430
+ correlationId: apiResponse.correlationId,
431
+ runtimeChallenge: apiResponse.runtimeChallenge,
432
+ tokenGuidance: apiResponse.tokenGuidance,
433
+ recommendation: apiResponse.recommendation,
434
+ recommendationReasons: apiResponse.recommendationReasons,
435
+ warningHeader: apiResponse.warningHeader,
436
+ stepUpApproval: apiResponse.stepUpApproval,
437
+ settlement: apiResponse.settlement
438
+ };
439
+ if (result.recommendation === "deny") {
440
+ result.policyAllowed = false;
441
+ result.accessLevel = "none";
442
+ result.denialReasons = serverDenialReasons ?? result.recommendationReasons ?? ["Access denied by AstraSync recommendation"];
443
+ result.guidance = result.runtimeChallenge ? {
444
+ message: `Verification failed: ${result.runtimeChallenge.reason || "runtime challenge failed"}`,
445
+ registrationUrl: urls?.registrationUrl ?? "",
446
+ documentationUrl: urls?.documentationUrl ?? ""
447
+ } : {
448
+ message: result.recommendationReasons?.[0] || "Access denied by AstraSync recommendation",
449
+ registrationUrl: urls?.registrationUrl ?? "",
450
+ documentationUrl: urls?.documentationUrl ?? ""
451
+ };
452
+ } else if (result.recommendation === "step_up_required") {
453
+ result.requiresStepUp = true;
454
+ result.denialReasons = serverDenialReasons ?? result.recommendationReasons ?? ["Step-up verification required"];
455
+ }
456
+ const escalates = result.requiresStepUp === true || result.requiresApproval === true || result.recommendation === "step_up_required";
457
+ if (mergedConfig.cacheTtl !== 0 && result.recommendation !== "deny" && !escalates) {
458
+ cacheResult(request, result, mergedConfig.cacheTtl, mergedConfig.counterpartyId);
459
+ }
460
+ return result;
461
+ }
462
+ async function recordDecision(config, sessionId, decision, reason) {
463
+ const headers = { "Content-Type": "application/json" };
464
+ if (config.apiKey) {
465
+ headers["Authorization"] = `Bearer ${config.apiKey}`;
466
+ headers["X-API-Key"] = config.apiKey;
467
+ }
468
+ await fetch(`${config.apiBaseUrl}/agents/verify-access/${sessionId}/decision`, {
469
+ method: "POST",
470
+ headers,
471
+ body: JSON.stringify({ decision, reason })
472
+ }).catch(() => {
473
+ });
474
+ }
475
+ async function fetchRoutes(config, counterpartyId) {
476
+ if (!counterpartyId) return null;
477
+ const headers = { "Content-Type": "application/json" };
478
+ if (config.apiKey) {
479
+ headers["Authorization"] = `Bearer ${config.apiKey}`;
480
+ headers["X-API-Key"] = config.apiKey;
481
+ }
482
+ try {
483
+ const response = await fetch(
484
+ `${config.apiBaseUrl}/endpoints/${encodeURIComponent(counterpartyId)}/routes`,
485
+ { method: "GET", headers }
486
+ );
487
+ if (!response.ok) return null;
488
+ const body = await response.json();
489
+ return body.data?.routes ?? [];
490
+ } catch {
491
+ return null;
492
+ }
493
+ }
494
+ async function verifyAndRecord(config, request, options) {
495
+ const mergedConfig = { ...DEFAULT_CONFIG, ...config };
496
+ const result = await verify(mergedConfig, { ...request, createSession: true }, options);
497
+ const sessionId = result.sessionId;
498
+ if (sessionId) {
499
+ if (result.identityVerified && result.policyAllowed) {
500
+ recordDecision(mergedConfig, sessionId, "granted").catch(() => {
501
+ });
502
+ } else {
503
+ recordDecision(mergedConfig, sessionId, "denied", result.denialReasons?.[0]).catch(() => {
504
+ });
505
+ }
506
+ }
507
+ return result;
508
+ }
509
+ async function reportUnregisteredAttempt(config, data) {
510
+ const apiBaseUrl = config.apiBaseUrl || DEFAULT_CONFIG.apiBaseUrl;
511
+ const { counterpartyUrl, ...rest } = data;
512
+ const body = {
513
+ ...rest,
514
+ ...counterpartyUrl && { counterpartyUrl },
515
+ ...config.counterpartyId && { counterpartyId: config.counterpartyId }
516
+ };
517
+ await fetch(`${apiBaseUrl}/verification-activity/unregistered-attempt`, {
518
+ method: "POST",
519
+ headers: { "Content-Type": "application/json" },
520
+ body: JSON.stringify(body)
521
+ }).catch(() => {
522
+ });
523
+ }
524
+ async function reportCounterpartyPreCheckFailure(config, data) {
525
+ const apiBaseUrl = config.apiBaseUrl || DEFAULT_CONFIG.apiBaseUrl;
526
+ await fetch(`${apiBaseUrl}/verification-activity/counterparty-pre-check-failure`, {
527
+ method: "POST",
528
+ headers: { "Content-Type": "application/json" },
529
+ body: JSON.stringify(data)
530
+ }).catch(() => {
531
+ });
532
+ }
533
+ async function quickVerify(config, credentials) {
534
+ const result = await verify(config, {
535
+ credentials,
536
+ purpose: "verification"
537
+ });
538
+ return {
539
+ identityVerified: result.identityVerified,
540
+ policyAllowed: result.policyAllowed,
541
+ accessLevel: result.accessLevel,
542
+ reason: result.denialReasons?.[0]
543
+ };
544
+ }
545
+ export {
546
+ clearCache,
547
+ extractCredentials,
548
+ fetchRoutes,
549
+ hasCredentials,
550
+ quickVerify,
551
+ recordDecision,
552
+ reportCounterpartyPreCheckFailure,
553
+ reportUnregisteredAttempt,
554
+ verify,
555
+ verifyAndRecord
556
+ };
557
+ //# sourceMappingURL=verify.mjs.map