@astrasyncai/verification-gateway 2.4.0 → 2.4.2

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 (87) hide show
  1. package/README.md +137 -22
  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 +37 -7
  7. package/dist/adapters/express.js.map +1 -1
  8. package/dist/adapters/express.mjs +37 -7
  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 +37 -7
  13. package/dist/adapters/mcp.js.map +1 -1
  14. package/dist/adapters/mcp.mjs +37 -7
  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 +30 -4
  19. package/dist/adapters/nextjs.js.map +1 -1
  20. package/dist/adapters/nextjs.mjs +30 -4
  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 +30 -4
  25. package/dist/adapters/sdk.js.map +1 -1
  26. package/dist/adapters/sdk.mjs +30 -4
  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.js +163 -4
  31. package/dist/browser/background.js +30 -4
  32. package/dist/browser/background.js.map +1 -1
  33. package/dist/browser/background.mjs +30 -4
  34. package/dist/browser/background.mjs.map +1 -1
  35. package/dist/browser/browser-adapter.d.mts +2 -2
  36. package/dist/browser/browser-adapter.d.ts +2 -2
  37. package/dist/cli/index.d.mts +2 -2
  38. package/dist/cli/index.d.ts +2 -2
  39. package/dist/cursor/cursor-adapter.d.mts +2 -2
  40. package/dist/cursor/cursor-adapter.d.ts +2 -2
  41. package/dist/cursor/extension.d.mts +2 -2
  42. package/dist/cursor/extension.d.ts +2 -2
  43. package/dist/cursor/extension.js +30 -4
  44. package/dist/cursor/extension.js.map +1 -1
  45. package/dist/cursor/extension.mjs +30 -4
  46. package/dist/cursor/extension.mjs.map +1 -1
  47. package/dist/{express-4Vau6x6X.d.mts → express-DneHiMhu.d.mts} +1 -1
  48. package/dist/{express-Nq-wWICa.d.ts → express-DsiaQRFt.d.ts} +1 -1
  49. package/dist/gateway/gateway.d.mts +2 -2
  50. package/dist/gateway/gateway.d.ts +2 -2
  51. package/dist/gateway/gateway.js +30 -4
  52. package/dist/gateway/gateway.js.map +1 -1
  53. package/dist/gateway/gateway.mjs +30 -4
  54. package/dist/gateway/gateway.mjs.map +1 -1
  55. package/dist/git-trigger/git-hooks.d.mts +2 -2
  56. package/dist/git-trigger/git-hooks.d.ts +2 -2
  57. package/dist/{index-DkyPV14Y.d.mts → index-C9yWlQ2Y.d.mts} +1 -1
  58. package/dist/{index-DiToN8gh.d.mts → index-DAGm-Sgf.d.mts} +1 -1
  59. package/dist/{index-B-EovXnY.d.ts → index-Dd4alF0l.d.ts} +1 -1
  60. package/dist/{index-CxwCN7AC.d.ts → index-NZiKvrtE.d.ts} +1 -1
  61. package/dist/index.d.mts +7 -7
  62. package/dist/index.d.ts +7 -7
  63. package/dist/index.js +37 -7
  64. package/dist/index.js.map +1 -1
  65. package/dist/index.mjs +37 -7
  66. package/dist/index.mjs.map +1 -1
  67. package/dist/local-evaluator/evaluator.d.mts +2 -2
  68. package/dist/local-evaluator/evaluator.d.ts +2 -2
  69. package/dist/{nextjs-DO_4crcp.d.ts → nextjs-B4WmoiVm.d.ts} +1 -1
  70. package/dist/{nextjs-BTR7Oix-.d.mts → nextjs-vUuVCaBP.d.mts} +1 -1
  71. package/dist/registration/index.d.mts +174 -7
  72. package/dist/registration/index.d.ts +174 -7
  73. package/dist/registration/index.js +171 -6
  74. package/dist/registration/index.js.map +1 -1
  75. package/dist/registration/index.mjs +167 -5
  76. package/dist/registration/index.mjs.map +1 -1
  77. package/dist/{sdk-TnHXD-Oh.d.ts → sdk-BvWp4q2q.d.ts} +1 -1
  78. package/dist/{sdk-DSLCyXIX.d.mts → sdk-Cixo6pTV.d.mts} +1 -1
  79. package/dist/transport/index.d.mts +2 -2
  80. package/dist/transport/index.d.ts +2 -2
  81. package/dist/{types-pU2O0BFq.d.mts → types-C_e1IZdU.d.mts} +1 -1
  82. package/dist/{types-BVp22KkN.d.mts → types-DLai3jly.d.mts} +16 -13
  83. package/dist/{types-BVp22KkN.d.ts → types-DLai3jly.d.ts} +16 -13
  84. package/dist/{types-DVCWReEN.d.ts → types-IUzu-A4u.d.ts} +1 -1
  85. package/dist/ui/index.d.mts +1 -1
  86. package/dist/ui/index.d.ts +1 -1
  87. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/adapters/nextjs.ts","../../src/access-levels.ts","../../src/verify.ts","../../src/transport/http.ts","../../src/pdlss-pre-check.ts"],"sourcesContent":["/**\n * AstraSync Universal Verification Gateway - Next.js Middleware\n *\n * Next.js middleware for verifying AI agents on web applications.\n * Supports Commerce Shield overlay for unverified agents.\n *\n * @example\n * ```typescript\n * // middleware.ts\n * import { createMiddleware } from '@astrasyncai/verification-gateway/nextjs';\n *\n * export const middleware = createMiddleware({\n * apiBaseUrl: 'https://api.astrasync.ai',\n * showCommerceShield: true,\n * routes: [\n * { pattern: '/api/public/*', method: '*', minAccessLevel: 'none' },\n * { pattern: '/api/*', method: '*', minAccessLevel: 'standard' },\n * { pattern: '/dashboard/*', method: '*', minAccessLevel: 'read-only' },\n * ],\n * });\n *\n * export const config = {\n * matcher: ['/api/:path*', '/dashboard/:path*'],\n * };\n * ```\n */\n\nimport type { NextRequest } from 'next/server';\nimport type {\n NextJsMiddlewareOptions,\n AgentCredentials,\n VerificationResult,\n RouteAccessConfig,\n AstraSyncCredentials,\n} from '../types';\nimport { verify, reportCounterpartyPreCheckFailure, fetchRoutes } from '../verify';\nimport { hasMinimumAccess } from '../access-levels';\nimport { extractHttpCredentials } from '../transport/http';\nimport { performCounterpartyPreCheck } from '../pdlss-pre-check';\n\n/**\n * Extract credentials from Next.js request\n */\nfunction extractCredentialsFromNextRequest(request: NextRequest): AgentCredentials {\n const credentials: AgentCredentials = {};\n\n // Check for ASTRA-ID in headers\n const astraId = request.headers.get('x-astra-id') || request.headers.get('X-Astra-Id');\n if (astraId) {\n credentials.astraId = astraId;\n }\n\n // Check for API key\n const apiKey = request.headers.get('x-api-key') || request.headers.get('X-Api-Key');\n if (apiKey) {\n credentials.apiKey = apiKey;\n }\n\n // Check Authorization header\n const authHeader = request.headers.get('authorization');\n if (authHeader) {\n credentials.authorizationHeader = authHeader;\n if (authHeader.startsWith('Bearer ')) {\n credentials.jwt = authHeader.slice(7);\n }\n }\n\n // Check query parameters\n const url = new URL(request.url);\n const astraIdParam = url.searchParams.get('astraId');\n const apiKeyParam = url.searchParams.get('apiKey');\n\n if (astraIdParam && !credentials.astraId) {\n credentials.astraId = astraIdParam;\n }\n if (apiKeyParam && !credentials.apiKey) {\n credentials.apiKey = apiKeyParam;\n }\n\n return credentials;\n}\n\n/**\n * Match a route pattern against a path\n */\nfunction matchRoute(pattern: string, path: string): boolean {\n const regexPattern = pattern.replace(/\\*/g, '.*').replace(/\\//g, '\\\\/');\n\n const regex = new RegExp(`^${regexPattern}$`);\n return regex.test(path);\n}\n\n/**\n * Find the route configuration for a request\n */\nfunction findRouteConfig(\n routes: RouteAccessConfig[],\n path: string,\n method: string\n): RouteAccessConfig | undefined {\n return routes.find((route) => {\n const methodMatches =\n route.method === '*' || route.method.toUpperCase() === method.toUpperCase();\n const pathMatches = matchRoute(route.pattern, path);\n return methodMatches && pathMatches;\n });\n}\n\n/**\n * Extract AstraSyncCredentials from Next.js request headers.\n * Returns null if no AstraSync headers are present.\n */\nfunction extractAstraSyncCredentialsFromNextRequest(\n request: NextRequest\n): AstraSyncCredentials | null {\n const headers: Record<string, string> = {};\n request.headers.forEach((value, key) => {\n headers[key] = value;\n });\n return extractHttpCredentials(headers);\n}\n\n/**\n * Extract purpose from request.\n *\n * Priority:\n * 1. Agent's declared PDLSS purpose from X-Astra-Purpose header (e.g. \"read_data:search\")\n * 2. Explicit x-purpose header\n * 3. HTTP method → PDLSS category fallback\n */\nfunction extractPurpose(request: NextRequest): string {\n // 1. Check agent's declared PDLSS purpose (X-Astra-Purpose header)\n const astraPurpose = request.headers.get('x-astra-purpose');\n if (astraPurpose) {\n // Extract category from \"category:action\" format\n return astraPurpose.split(':')[0];\n }\n\n // 2. Try explicit purpose header\n const purposeHeader = request.headers.get('x-purpose');\n if (purposeHeader) {\n return purposeHeader;\n }\n\n // 3. Infer from HTTP method using PDLSS-compatible categories\n switch (request.method.toUpperCase()) {\n case 'GET':\n return 'read_data';\n case 'POST':\n return 'write_data';\n case 'PUT':\n case 'PATCH':\n return 'write_data';\n case 'DELETE':\n return 'delete_data';\n default:\n return 'general';\n }\n}\n\n/**\n * Generate Commerce Shield HTML response\n */\nfunction generateCommerceShieldHtml(\n result: VerificationResult,\n options: NextJsMiddlewareOptions\n): string {\n const title = options.commerceShield?.title || 'AstraSync Agent Verification';\n const message =\n options.commerceShield?.message ||\n result.guidance?.message ||\n \"This site verifies AI agents before granting access. We noticed you're visiting without AstraSync credentials.\";\n const registrationUrl = result.guidance?.registrationUrl || 'https://astrasync.ai/register';\n const docsUrl = result.guidance?.documentationUrl || 'https://astrasync.ai/docs/agent-access';\n const allowGuest = options.commerceShield?.allowGuestAccess ?? true;\n\n return `\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>${title}</title>\n <style>\n * {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n }\n body {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;\n background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);\n min-height: 100vh;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 20px;\n }\n .shield-container {\n background: rgba(255, 255, 255, 0.95);\n border-radius: 16px;\n box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);\n max-width: 480px;\n width: 100%;\n padding: 40px;\n text-align: center;\n }\n .shield-icon {\n font-size: 48px;\n margin-bottom: 20px;\n }\n .shield-title {\n font-size: 24px;\n font-weight: 700;\n color: #1a1a2e;\n margin-bottom: 16px;\n }\n .shield-message {\n color: #4a5568;\n line-height: 1.6;\n margin-bottom: 24px;\n }\n .shield-steps {\n text-align: left;\n background: #f7fafc;\n border-radius: 8px;\n padding: 20px;\n margin-bottom: 24px;\n }\n .shield-steps h3 {\n font-size: 14px;\n font-weight: 600;\n color: #2d3748;\n margin-bottom: 12px;\n }\n .shield-steps ol {\n padding-left: 20px;\n color: #4a5568;\n }\n .shield-steps li {\n margin-bottom: 8px;\n }\n .shield-buttons {\n display: flex;\n flex-direction: column;\n gap: 12px;\n }\n .btn {\n display: inline-block;\n padding: 14px 24px;\n border-radius: 8px;\n font-weight: 600;\n text-decoration: none;\n transition: all 0.2s;\n cursor: pointer;\n border: none;\n font-size: 16px;\n }\n .btn-primary {\n background: linear-gradient(135deg, #6366f1 0%, #4f46e5 100%);\n color: white;\n }\n .btn-primary:hover {\n transform: translateY(-2px);\n box-shadow: 0 4px 12px rgba(99, 102, 241, 0.4);\n }\n .btn-secondary {\n background: #e2e8f0;\n color: #4a5568;\n }\n .btn-secondary:hover {\n background: #cbd5e0;\n }\n .shield-footer {\n margin-top: 24px;\n font-size: 14px;\n color: #718096;\n }\n .shield-footer a {\n color: #6366f1;\n text-decoration: none;\n }\n .shield-footer a:hover {\n text-decoration: underline;\n }\n </style>\n</head>\n<body>\n <div class=\"shield-container\">\n <div class=\"shield-icon\">🛡️</div>\n <h1 class=\"shield-title\">${title}</h1>\n <p class=\"shield-message\">${message}</p>\n\n <div class=\"shield-steps\">\n <h3>To get verified access:</h3>\n <ol>\n <li>Register at <a href=\"${registrationUrl}\">astrasync.ai/register</a></li>\n <li>Create and register your agent</li>\n <li>Add your ASTRA-ID to request headers</li>\n <li>Refresh this page</li>\n </ol>\n </div>\n\n <div class=\"shield-buttons\">\n <a href=\"${registrationUrl}\" class=\"btn btn-primary\">Register Now</a>\n ${allowGuest ? '<button onclick=\"window.location.reload()\" class=\"btn btn-secondary\">Continue as Guest (Limited)</button>' : ''}\n </div>\n\n <p class=\"shield-footer\">\n Learn more: <a href=\"${docsUrl}\">Agent Access Documentation</a>\n </p>\n </div>\n</body>\n</html>\n `.trim();\n}\n\nconst DEFAULT_ROUTES_REFRESH_MS = 5 * 60 * 1000;\n\n/**\n * Create Next.js middleware for agent verification.\n *\n * v2.9.7 moved per-route policy out of merchant-side source code into the\n * AstraSync dashboard. The middleware fetches its routes from the backend\n * via `GET /endpoints/:counterpartyId/routes` on init and refreshes\n * periodically — see `ExpressMiddlewareOptions` for the rationale (defect\n * 24, dual-config silent-conflict).\n */\nexport function createMiddleware(options: NextJsMiddlewareOptions) {\n const {\n skipPaths = [],\n showCommerceShield = true,\n enableRuntimeChallenge = true,\n routesRefreshMs = DEFAULT_ROUTES_REFRESH_MS,\n ...config\n } = options;\n\n let cachedRoutes: RouteAccessConfig[] = [];\n let lastFetchAt = 0;\n let refreshing: Promise<void> | null = null;\n let warnedNoCounterparty = false;\n\n async function refreshRoutes(): Promise<void> {\n if (!config.counterpartyId) {\n if (!warnedNoCounterparty) {\n // eslint-disable-next-line no-console\n console.warn(\n '[VerificationGateway/Next.js] No counterpartyId configured — falling through (allow all). ' +\n 'Per-route policy lives in the AstraSync dashboard now; register the endpoint and ' +\n 'set counterpartyId in your middleware config to enforce policy.'\n );\n warnedNoCounterparty = true;\n }\n return;\n }\n const fetched = await fetchRoutes(config, config.counterpartyId);\n if (fetched) {\n cachedRoutes = fetched;\n lastFetchAt = Date.now();\n }\n }\n\n refreshing = refreshRoutes().finally(() => {\n refreshing = null;\n });\n\n return async function middleware(request: NextRequest) {\n // Dynamic import NextResponse to avoid build issues\n const { NextResponse } = await import('next/server');\n\n const pathname = request.nextUrl.pathname;\n\n // Check if path should be skipped\n const shouldSkip = skipPaths.some((pattern) => matchRoute(pattern, pathname));\n if (shouldSkip) {\n return NextResponse.next();\n }\n\n if (refreshing) {\n await refreshing.catch(() => {});\n }\n if (config.counterpartyId && Date.now() - lastFetchAt > routesRefreshMs) {\n refreshing = refreshRoutes().finally(() => {\n refreshing = null;\n });\n }\n\n // Find route configuration (from remote-fetched cache)\n const routeConfig = findRouteConfig(cachedRoutes, pathname, request.method);\n\n // If no route config, allow through\n if (!routeConfig) {\n return NextResponse.next();\n }\n\n // If route requires 'none' access, allow through\n if (routeConfig.minAccessLevel === 'none') {\n return NextResponse.next();\n }\n\n // Extract credentials\n const credentials = extractCredentialsFromNextRequest(request);\n\n // v2.3.0: anonymous traffic no longer short-circuits client-side.\n // The server applies the endpoint's `unverifiedAgentPolicy` (deny /\n // allow_partial / allow_full) and emits the verification event +\n // blockchain record per the canonical flow. SDK forwards verbatim.\n\n // Auto-detect counterparty URL from the request if not explicitly configured.\n // Since the SDK is installed at this endpoint, we always know the origin.\n const counterpartyUrl = config.counterpartyUrl || request.nextUrl.origin;\n\n // Extract purpose and full AstraSync credentials (includes PDLSS from X-Astra-* headers)\n const purpose = extractPurpose(request);\n const astraCreds = extractAstraSyncCredentialsFromNextRequest(request);\n\n // Step 2: Counterparty-side PDLSS pre-check — compare agent's requested PDLSS\n // against counterparty-defined maximums on the route config.\n // Rejects immediately if outside limits, BEFORE calling verify-access.\n const preCheckFailures = performCounterpartyPreCheck(routeConfig, astraCreds, purpose);\n if (preCheckFailures.length > 0) {\n const preCheckResult: VerificationResult = {\n verified: false,\n accessLevel: 'none',\n denialReasons: preCheckFailures.map((f) => f.message),\n guidance: {\n message: 'Request exceeds counterparty-defined PDLSS limits.',\n registrationUrl: `${config.apiBaseUrl?.replace('/api', '')}/register`,\n documentationUrl: `${config.apiBaseUrl?.replace('/api', '')}/docs/pdlss`,\n },\n verifiedAt: new Date(),\n };\n\n // Fire-and-forget: notify AstraSync of the pre-check failure\n reportCounterpartyPreCheckFailure(config, {\n agentId: astraCreds?.agentId || credentials.astraId || 'unknown',\n counterpartyUrl,\n counterpartyType: config.counterpartyType || 'website',\n failures: preCheckFailures,\n requestPath: pathname,\n requestMethod: request.method,\n }).catch(() => {});\n\n // For API routes, return JSON\n if (pathname.startsWith('/api/')) {\n return NextResponse.json(\n {\n success: false,\n error: {\n code: 'PDLSS_PRE_CHECK_FAILED',\n message: preCheckResult.denialReasons?.[0] || 'PDLSS pre-check failed',\n guidance: preCheckResult.guidance,\n },\n },\n { status: 403 }\n );\n }\n\n // For web pages, show Commerce Shield\n if (showCommerceShield) {\n return new NextResponse(generateCommerceShieldHtml(preCheckResult, options), {\n status: 200,\n headers: {\n 'Content-Type': 'text/html',\n 'X-AstraSync-Verification': 'commerce-shield',\n },\n });\n }\n\n return NextResponse.redirect(new URL('/unauthorized', request.url));\n }\n\n // Step 3: Call AstraSync verify-access with runtime challenge enabled\n const forwardedFor = request.headers.get('x-forwarded-for') || undefined;\n const originalClientIp = forwardedFor?.split(',')[0]?.trim();\n const result = await verify(config, {\n credentials,\n purpose,\n action: request.method.toLowerCase(),\n resource: pathname,\n counterpartyUrl,\n counterpartyType: config.counterpartyType || 'website',\n enableRuntimeChallenge,\n durationRequired: astraCreds?.pdlss?.duration?.maxSessionDuration,\n callerMetadata: {\n sourceIp: originalClientIp,\n userAgent: request.headers.get('user-agent') || undefined,\n referer: request.headers.get('referer') || undefined,\n host: request.headers.get('host') || undefined,\n forwardedFor,\n agentCardUrl: request.headers.get('x-astrasync-agent-card') || undefined,\n },\n });\n\n // v2.3.9 (defect #30): denied verifications short-circuit BEFORE the\n // gate-level comparison. The OR-prefix shape here keeps the\n // single-expression branch this adapter uses, but ensures\n // `verified === false` triggers the denial path even if the access\n // level math would otherwise let it through (the historical bug).\n // See express.ts for the full rationale.\n if (!result.verified || !hasMinimumAccess(result.accessLevel, routeConfig.minAccessLevel)) {\n // For API routes, return JSON\n if (pathname.startsWith('/api/')) {\n return NextResponse.json(\n {\n success: false,\n error: {\n code: result.verified ? 'INSUFFICIENT_ACCESS' : 'UNAUTHORIZED',\n message: result.denialReasons?.[0] || 'Access denied',\n accessLevel: result.accessLevel,\n required: routeConfig.minAccessLevel,\n guidance: result.guidance,\n },\n },\n { status: result.verified ? 403 : 401 }\n );\n }\n\n // For web pages, show Commerce Shield\n if (showCommerceShield) {\n return new NextResponse(generateCommerceShieldHtml(result, options), {\n status: 200,\n headers: {\n 'Content-Type': 'text/html',\n 'X-AstraSync-Verification': 'commerce-shield',\n },\n });\n }\n\n // Redirect to unauthorized page\n return NextResponse.redirect(new URL('/unauthorized', request.url));\n }\n\n // All checks passed - continue with verification info in headers\n const response = NextResponse.next();\n\n // Add verification info to response headers\n response.headers.set('X-AstraSync-Verified', result.verified.toString());\n response.headers.set('X-AstraSync-Access-Level', result.accessLevel);\n\n if (result.agent) {\n response.headers.set('X-AstraSync-Agent-Id', result.agent.astraId);\n response.headers.set('X-AstraSync-Trust-Score', result.agent.trustScore.toString());\n }\n\n return response;\n };\n}\n\n/**\n * Helper to create matcher config\n */\nexport function createMatcherConfig(paths: string[]): { matcher: string[] } {\n return { matcher: paths };\n}\n","/**\n * AstraSync Universal Verification Gateway - Access Level Definitions\n *\n * Defines the hierarchy and capabilities of each access level.\n *\n * v2.3.9 (defect #30): renamed `'guidance'` band → `'restricted'`. See\n * `types.ts` AccessLevel for the rationale (value-name collision with the\n * `guidance: {...}` help-payload object on VerificationResult).\n */\n\nimport type { AccessLevel, TrustLevel } from './types';\n\n/**\n * Access level hierarchy (higher number = more access)\n */\nexport const ACCESS_LEVEL_HIERARCHY: Record<AccessLevel, number> = {\n none: 0,\n restricted: 1,\n 'read-only': 2,\n standard: 3,\n full: 4,\n internal: 5,\n};\n\n/**\n * Access level descriptions for UI\n */\nexport const ACCESS_LEVEL_DESCRIPTIONS: Record<AccessLevel, string> = {\n none: 'No access - credentials required',\n restricted: 'Restricted access - registration prompt only',\n 'read-only': 'Read-only access - can browse but not modify',\n standard: 'Standard access - normal operations per PDLSS policy',\n full: 'Full access - all operations for high-trust agents',\n internal: 'Internal access - organization member privileges',\n};\n\n/**\n * Default trust score thresholds for access levels\n */\nexport const DEFAULT_TRUST_THRESHOLDS: Record<AccessLevel, number> = {\n none: 0,\n restricted: 0,\n 'read-only': 20,\n standard: 40,\n full: 70,\n internal: 0, // Internal is based on org membership, not score\n};\n\n/**\n * Trust level score ranges\n */\nexport const TRUST_LEVEL_RANGES: Record<TrustLevel, { min: number; max: number }> = {\n BRONZE: { min: 0, max: 39 },\n SILVER: { min: 40, max: 59 },\n GOLD: { min: 60, max: 79 },\n PLATINUM: { min: 80, max: 100 },\n};\n\n/**\n * Determine trust level from score\n */\nexport function getTrustLevel(score: number): TrustLevel {\n if (score >= 80) return 'PLATINUM';\n if (score >= 60) return 'GOLD';\n if (score >= 40) return 'SILVER';\n return 'BRONZE';\n}\n\n/**\n * Check if access level A is greater than or equal to access level B\n */\nexport function hasMinimumAccess(actual: AccessLevel, required: AccessLevel): boolean {\n return ACCESS_LEVEL_HIERARCHY[actual] >= ACCESS_LEVEL_HIERARCHY[required];\n}\n\n/**\n * Get the highest access level for a given trust score\n */\nexport function getAccessLevelForScore(\n trustScore: number,\n thresholds: Record<AccessLevel, number> = DEFAULT_TRUST_THRESHOLDS\n): AccessLevel {\n if (trustScore >= thresholds.full) return 'full';\n if (trustScore >= thresholds.standard) return 'standard';\n if (trustScore >= thresholds['read-only']) return 'read-only';\n return 'restricted';\n}\n\n/**\n * Determine access level from verification result.\n *\n * v2.3.9 (defect #30): unverified callers now return `'none'` (was\n * `'guidance'`). Denials grant zero — never a positive band.\n */\nexport function determineAccessLevel(\n verified: boolean,\n trustScore: number,\n isOrgMember: boolean,\n customThresholds?: Partial<Record<AccessLevel, number>>\n): AccessLevel {\n if (!verified) {\n return 'none';\n }\n\n if (isOrgMember) {\n return 'internal';\n }\n\n const thresholds = {\n ...DEFAULT_TRUST_THRESHOLDS,\n ...customThresholds,\n };\n\n return getAccessLevelForScore(trustScore, thresholds);\n}\n\n/**\n * Access capabilities per level\n */\nexport interface AccessCapabilities {\n canRead: boolean;\n canWrite: boolean;\n canDelete: boolean;\n canAdmin: boolean;\n canAccessInternal: boolean;\n maxTransactionValue?: number;\n allowedPurposes?: string[];\n}\n\n/**\n * Get capabilities for an access level\n */\nexport function getCapabilities(accessLevel: AccessLevel): AccessCapabilities {\n switch (accessLevel) {\n case 'none':\n return {\n canRead: false,\n canWrite: false,\n canDelete: false,\n canAdmin: false,\n canAccessInternal: false,\n };\n case 'restricted':\n return {\n canRead: false,\n canWrite: false,\n canDelete: false,\n canAdmin: false,\n canAccessInternal: false,\n };\n case 'read-only':\n return {\n canRead: true,\n canWrite: false,\n canDelete: false,\n canAdmin: false,\n canAccessInternal: false,\n };\n case 'standard':\n return {\n canRead: true,\n canWrite: true,\n canDelete: false,\n canAdmin: false,\n canAccessInternal: false,\n };\n case 'full':\n return {\n canRead: true,\n canWrite: true,\n canDelete: true,\n canAdmin: false,\n canAccessInternal: false,\n };\n case 'internal':\n return {\n canRead: true,\n canWrite: true,\n canDelete: true,\n canAdmin: true,\n canAccessInternal: true,\n };\n default:\n return {\n canRead: false,\n canWrite: false,\n canDelete: false,\n canAdmin: false,\n canAccessInternal: false,\n };\n }\n}\n","/**\n * AstraSync Universal Verification Gateway - Core Verification Logic\n *\n * This module handles the core verification logic, calling the AstraSync API\n * and processing the response into a standardized VerificationResult.\n */\n\nimport type {\n GatewayConfig,\n AgentCredentials,\n VerificationRequest,\n VerificationResult,\n VerifiedAgent,\n VerifiedDeveloper,\n VerifiedOrganization,\n GuidanceInfo,\n AccessLevel,\n EnhancedVerificationResult,\n TokenGuidance,\n RuntimeChallengeResult,\n} from './types';\nimport { getTrustLevel, ACCESS_LEVEL_HIERARCHY } from './access-levels';\n\n/**\n * Default configuration values\n *\n * apiBaseUrl matches the OpenAPI authoritative server (https://astrasync.ai/api\n * for prod, https://staging.astrasync.ai/api for staging). Always include the\n * /api path prefix when overriding — registration / docs URLs are derived by\n * stripping it.\n */\nconst DEFAULT_CONFIG: Partial<GatewayConfig> = {\n apiBaseUrl: 'https://astrasync.ai/api',\n // v2.3.9 (defect #30): default for unconfigured callers is `'none'` (no\n // access). Pre-rename this defaulted to `'guidance'`, which combined with\n // a route gated at `'guidance'` to silently let unverified traffic\n // through (`hasMinimumAccess('guidance', 'guidance') === true`).\n defaultAccessLevel: 'none',\n // minTrustScore + minTrustScoreForFull deprecated in v2.3.0 — server decides.\n cacheTtl: 300, // 5 minutes\n debug: false,\n};\n\n/**\n * Init self-test state. Fires once per process on first verify() call to warn\n * if apiBaseUrl is pointing at the wrong host (e.g. a marketing site that\n * 200s with text/html instead of the API).\n */\nlet initCheckPerformed = false;\n\n/** One-shot guard for v2.3.0 deprecation warning. */\nlet deprecationWarningShown = false;\n\nasync function performInitCheck(apiBaseUrl: string, debug?: boolean): Promise<void> {\n initCheckPerformed = true;\n try {\n const probeUrl = `${apiBaseUrl}/agents/verify-access`;\n // HEAD mirrors GET semantics (running the full request pipeline without a\n // body) so the response carries the same content-type the marketing 404\n // would return. OPTIONS often gets short-circuited by CORS-preflight\n // handlers and returns no content-type, defeating the check.\n const response = await fetch(probeUrl, { method: 'HEAD' });\n const contentType = response.headers.get('content-type') ?? '';\n if (contentType.startsWith('text/html')) {\n console.warn(\n `[VerificationGateway] apiBaseUrl '${apiBaseUrl}' returned HTML (content-type: ${contentType}). ` +\n `This usually means apiBaseUrl is pointing at a marketing site instead of the API. ` +\n `Expected: 'https://astrasync.ai/api' (prod) or 'https://staging.astrasync.ai/api' (staging). ` +\n `Set disableInitChecks: true on GatewayConfig to silence this warning.`\n );\n } else if (debug) {\n console.log(\n `[VerificationGateway] init check passed for ${apiBaseUrl} (content-type: ${contentType})`\n );\n }\n } catch (err) {\n if (debug) {\n console.log(`[VerificationGateway] init check failed (non-blocking): ${String(err)}`);\n }\n }\n}\n\n/**\n * Simple in-memory cache for verification results\n */\nconst verificationCache = new Map<string, { result: VerificationResult; expiresAt: number }>();\n\n/**\n * Generate cache key from credentials\n */\nfunction getCacheKey(credentials: AgentCredentials): string {\n return `${credentials.astraId || ''}-${credentials.apiKey || ''}-${credentials.jwt || ''}`;\n}\n\n/**\n * Check if cached result is still valid\n */\nfunction getCachedResult(credentials: AgentCredentials): VerificationResult | null {\n const key = getCacheKey(credentials);\n const cached = verificationCache.get(key);\n\n if (cached && cached.expiresAt > Date.now()) {\n return cached.result;\n }\n\n if (cached) {\n verificationCache.delete(key);\n }\n\n return null;\n}\n\n/**\n * Cache a verification result\n */\nfunction cacheResult(\n credentials: AgentCredentials,\n result: VerificationResult,\n ttlSeconds: number\n): void {\n const key = getCacheKey(credentials);\n verificationCache.set(key, {\n result,\n expiresAt: Date.now() + ttlSeconds * 1000,\n });\n}\n\n/**\n * Clear the verification cache\n */\nexport function clearCache(): void {\n verificationCache.clear();\n}\n\n/**\n * Extract agent credentials from various sources\n */\nexport function extractCredentials(\n headers: Record<string, string | string[] | undefined>,\n query?: Record<string, string | undefined>\n): AgentCredentials {\n const credentials: AgentCredentials = {};\n\n // Check for ASTRA-ID in headers (case-insensitive). Accepts the historical\n // X-Astra-Id name plus the X-Astra-AgentId / x-astra-agent-id alias the\n // partner asked for in #9a — both surface the same field.\n const astraIdHeader =\n headers['x-astra-id'] ||\n headers['X-Astra-Id'] ||\n headers['X-ASTRA-ID'] ||\n headers['x-astra-agentid'] ||\n headers['X-Astra-AgentId'] ||\n headers['x-astra-agent-id'] ||\n headers['X-Astra-Agent-Id'] ||\n headers['X-ASTRA-AGENT-ID'];\n if (astraIdHeader) {\n credentials.astraId = Array.isArray(astraIdHeader) ? astraIdHeader[0] : astraIdHeader;\n }\n\n // Check for API key in headers\n const apiKeyHeader = headers['x-api-key'] || headers['X-Api-Key'] || headers['X-API-KEY'];\n if (apiKeyHeader) {\n credentials.apiKey = Array.isArray(apiKeyHeader) ? apiKeyHeader[0] : apiKeyHeader;\n }\n\n // Check Authorization header for Bearer token\n const authHeader = headers['authorization'] || headers['Authorization'];\n if (authHeader) {\n const authValue = Array.isArray(authHeader) ? authHeader[0] : authHeader;\n credentials.authorizationHeader = authValue;\n\n if (authValue.startsWith('Bearer ')) {\n credentials.jwt = authValue.slice(7);\n }\n }\n\n // Check query parameters as fallback\n if (query) {\n if (query.astraId && !credentials.astraId) {\n credentials.astraId = query.astraId;\n }\n if (query.apiKey && !credentials.apiKey) {\n credentials.apiKey = query.apiKey;\n }\n }\n\n return credentials;\n}\n\n/**\n * Check if credentials are present\n */\nexport function hasCredentials(credentials: AgentCredentials): boolean {\n return !!(credentials.astraId || credentials.apiKey || credentials.jwt);\n}\n\n/**\n * Create guidance response for unverified agents\n */\nfunction createGuidanceResponse(config: GatewayConfig, reason?: string): VerificationResult {\n const guidance: GuidanceInfo = {\n message:\n 'This service verifies AI agents before granting access. Please register your agent with AstraSync.',\n registrationUrl: `${config.apiBaseUrl.replace('/api', '')}/register`,\n documentationUrl: `${config.apiBaseUrl.replace('/api', '')}/docs/agent-access`,\n steps: [\n 'Register for an AstraSync account',\n 'Create and register your agent',\n 'Add your ASTRA-ID to request headers',\n 'Retry your request',\n ],\n };\n\n return {\n verified: false,\n // v2.3.9 (defect #30): denials grant `'none'`, NEVER a positive band.\n // Adapters additionally short-circuit on `verified === false` before\n // the gate check, but the access level still has to be honest at the\n // data layer so downstream consumers (SDK adapters in other languages,\n // custom integrations) inherit the correct semantics.\n accessLevel: 'none',\n guidance,\n denialReasons: reason ? [reason] : ['No valid agent credentials provided'],\n verifiedAt: new Date(),\n };\n}\n\n/**\n * Call the AstraSync verify-access API\n */\nasync function callVerifyAccessAPI(\n config: GatewayConfig,\n request: VerificationRequest\n): Promise<{\n success: boolean;\n access?: {\n allowed: boolean;\n /**\n * Server-decided access level. Read verbatim — do NOT remap client-side.\n * The server resolves this from endpoint policy + agent trust score using\n * the canonical thresholds (see backend `apps/backend/src/utils/access-levels.ts`).\n */\n accessLevel?: AccessLevel;\n reason?: string;\n /**\n * Aggregated denial failures (v2.9.8+). Empty / absent when allowed.\n * Each entry is `{ dimension, message, guidance? }` — see\n * `AccessFailure` for the contract.\n */\n failures?: Array<{ dimension: string; message: string; guidance?: string }>;\n requiresStepUp?: boolean;\n requiresApproval?: boolean;\n appliedPolicy?: {\n boundaryName: string;\n policyVersion: string;\n };\n counterparty?: {\n id: string;\n name: string;\n trustScoreRequirement: number;\n };\n };\n agent?: {\n kyaAgentId: string;\n astraId: string;\n name: string;\n trustScore: number;\n trustLevel: string;\n agentStatus: string;\n blockchainStatus: string;\n };\n developer?: {\n kyaOwnerId: string;\n fullName: string;\n email: string;\n identityVerified: boolean;\n trustScore: number;\n };\n organization?: {\n name: string;\n verified: boolean;\n trustScore: number;\n };\n /**\n * Structured explanation of the verification decision. Tells the merchant\n * WHY (id verified? challenge passed? request within PDLSS? trust score?)\n * without exposing thresholds, scope lists, or other-tenant counterparty\n * membership. Empty `attestations` unless the endpoint's access policy\n * declared `required_attestations`.\n */\n verificationContext?: {\n idVerified: boolean;\n runtimeChallenge: {\n status: 'passed' | 'skipped' | 'failed' | 'timeout' | 'not_supported';\n checkedAt: string | null;\n };\n pdlssCheck: {\n result: 'within' | 'exceeded' | 'denied' | 'not_evaluated';\n purpose: 'approved' | 'denied';\n scope: 'approved' | 'denied';\n };\n dynamicTrustScore: number;\n attestations: Array<{\n type: string;\n status: 'passed' | 'failed';\n validUntil?: string;\n proofType: 'reference' | 'zkp';\n proof: string;\n }>;\n };\n error?: string;\n}> {\n const { credentials, ...requestData } = request;\n\n // Build the request body. agentId is omitted when not provided so the\n // server treats it as an anonymous canonical-flow call (Branch A/B/C).\n const body: Record<string, unknown> = {\n ...(credentials.astraId && { agentId: credentials.astraId }),\n purpose: requestData.purpose || 'general',\n };\n\n // Add optional fields\n if (requestData.action) body.action = requestData.action;\n if (requestData.resourceType) body.resourceType = requestData.resourceType;\n if (requestData.resource) body.resource = requestData.resource;\n if (requestData.jurisdiction) body.jurisdiction = requestData.jurisdiction;\n if (requestData.transactionValue) body.transactionValue = requestData.transactionValue;\n if (requestData.currency) body.currency = requestData.currency;\n if (requestData.isSubAgentRequest) body.isSubAgentRequest = requestData.isSubAgentRequest;\n if (requestData.parentAgentId) body.parentAgentId = requestData.parentAgentId;\n if (requestData.subAgentDepth !== undefined) body.subAgentDepth = requestData.subAgentDepth;\n // Handshake Protocol v10 additions\n if (requestData.enableRuntimeChallenge)\n body.enableRuntimeChallenge = requestData.enableRuntimeChallenge;\n if (requestData.createSession) body.createSession = requestData.createSession;\n if (requestData.durationRequired) body.durationRequired = requestData.durationRequired;\n if (requestData.counterpartyType) body.counterpartyType = requestData.counterpartyType;\n if (requestData.counterpartyUrl) body.counterpartyUrl = requestData.counterpartyUrl;\n if (config.counterpartyId) body.counterpartyId = config.counterpartyId;\n if (requestData.runtimeChallengeOptions)\n body.runtimeChallengeOptions = requestData.runtimeChallengeOptions;\n\n // Forward caller metadata when present. Merges the legacy top-level\n // clientIp/userAgent into the nested block for backward compatibility.\n if (requestData.callerMetadata || requestData.clientIp || requestData.userAgent) {\n const meta = {\n ...(requestData.clientIp && { sourceIp: requestData.clientIp }),\n ...(requestData.userAgent && { userAgent: requestData.userAgent }),\n ...requestData.callerMetadata,\n };\n if (Object.keys(meta).length > 0) body.callerMetadata = meta;\n }\n\n // Build headers\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...config.customHeaders,\n };\n\n // verify-access requires authentication. The backend's authenticate middleware\n // accepts either a JWT or an API key (starts with kya_) via `Authorization: Bearer <token>`.\n // Credential-supplied auth header (e.g. the agent's own token) takes priority.\n if (credentials.authorizationHeader) {\n headers['Authorization'] = credentials.authorizationHeader;\n } else if (config.apiKey) {\n headers['Authorization'] = `Bearer ${config.apiKey}`;\n }\n // Legacy header kept for compatibility with any middleware that reads it directly.\n if (config.apiKey) {\n headers['X-API-Key'] = config.apiKey;\n }\n\n try {\n const response = await fetch(`${config.apiBaseUrl}/agents/verify-access`, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n });\n\n const data = await response.json();\n\n // v2.3.8 (defect #29): treat 410 Gone as a deterministic deactivated-endpoint\n // signal. Older SDKs may treat any non-2xx as transient and retry; v2.3.8+\n // surfaces it as a structured denial with `reason: 'endpoint_deactivated'`\n // so callers can distinguish \"endpoint gone\" from \"endpoint denied this\".\n if (response.status === 410) {\n return {\n success: true,\n access: {\n allowed: false,\n accessLevel: 'none',\n reason: 'endpoint_deactivated',\n failures: [\n {\n dimension: 'endpoint.deactivated',\n message:\n typeof data?.message === 'string' ? data.message : 'Endpoint has been deactivated',\n guidance:\n typeof data?.guidance === 'string'\n ? data.guidance\n : 'Reactivate via POST /api/endpoints/{id}/reactivate, or update the URL on the calling agent.',\n },\n ],\n },\n };\n }\n\n if (!response.ok) {\n return {\n success: false,\n error: data.message || data.error || `API returned ${response.status}`,\n };\n }\n\n return data;\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n return {\n success: false,\n error: `Failed to call verify-access API: ${message}`,\n };\n }\n}\n\n/**\n * Main verification function\n */\nexport async function verify(\n config: GatewayConfig,\n request: VerificationRequest\n): Promise<VerificationResult> {\n const mergedConfig = { ...DEFAULT_CONFIG, ...config };\n\n // One-time init self-test — fire-and-forget, never blocks verify().\n if (!initCheckPerformed && !mergedConfig.disableInitChecks && mergedConfig.apiBaseUrl) {\n void performInitCheck(mergedConfig.apiBaseUrl, mergedConfig.debug);\n }\n\n // Deprecation warning for v2.3.0 removed config fields. Fires once per process.\n if (\n !deprecationWarningShown &&\n (config.minTrustScore !== undefined || config.minTrustScoreForFull !== undefined)\n ) {\n deprecationWarningShown = true;\n console.warn(\n '[VerificationGateway] minTrustScore / minTrustScoreForFull are deprecated in v2.3.0 ' +\n 'and have no effect. Server is now the single source of truth for access-level decisions ' +\n '(the SDK reads access.accessLevel from the verify-access response). To gate access ' +\n \"to an endpoint, configure the endpoint's trust_score_requirement server-side.\"\n );\n }\n\n // v2.3.0: anonymous traffic no longer short-circuits here. We forward the\n // request to the server with no agentId; the server applies the endpoint's\n // unverifiedAgentPolicy and returns advisory. createGuidanceResponse remains\n // as the offline fallback if the API itself fails (handled below).\n\n // Check cache first\n if (mergedConfig.cacheTtl && mergedConfig.cacheTtl > 0) {\n const cached = getCachedResult(request.credentials);\n if (cached) {\n if (mergedConfig.debug) {\n console.log('[VerificationGateway] Returning cached result');\n }\n return cached;\n }\n }\n\n // Inject counterparty info from config if not already set in request\n const enrichedRequest = { ...request };\n if (!enrichedRequest.counterpartyUrl && mergedConfig.counterpartyUrl) {\n enrichedRequest.counterpartyUrl = mergedConfig.counterpartyUrl;\n }\n if (!enrichedRequest.counterpartyType && mergedConfig.counterpartyType) {\n enrichedRequest.counterpartyType = mergedConfig.counterpartyType;\n }\n\n // Call the API\n if (mergedConfig.debug) {\n console.log('[VerificationGateway] Calling verify-access API');\n }\n\n const apiResponse = await callVerifyAccessAPI(mergedConfig, enrichedRequest);\n\n // Handle API errors\n if (!apiResponse.success) {\n return createGuidanceResponse(mergedConfig, apiResponse.error);\n }\n\n // Check access result\n if (!apiResponse.access?.allowed) {\n // v2.9.8 (defect M1): aggregated failures across every gate that\n // denied. Surface them on the result so the integrator can see every\n // blocker in one go instead of the previous fail-fast cascade.\n const aggregatedFailures = (apiResponse.access as Record<string, unknown> | undefined)\n ?.failures as Array<{ dimension: string; message: string; guidance?: string }> | undefined;\n const result: EnhancedVerificationResult = {\n verified: false,\n // v2.3.9 (defect #30): denials grant `'none'`, NEVER a positive band.\n // Pre-rename this hardcoded `'guidance'`, which conflated with the\n // colocated `guidance: {...}` help-payload object below and let\n // denied requests pass any route gated at `'guidance'` because\n // `hasMinimumAccess('guidance', 'guidance') === true`. Adapters now\n // ALSO short-circuit on `verified === false` before the gate check —\n // belt-and-braces.\n accessLevel: 'none',\n denialReasons:\n aggregatedFailures && aggregatedFailures.length > 0\n ? aggregatedFailures.map((f) => f.message)\n : apiResponse.access?.reason\n ? [apiResponse.access.reason]\n : ['Access denied'],\n failures: aggregatedFailures,\n requiresStepUp: apiResponse.access?.requiresStepUp,\n requiresApproval: apiResponse.access?.requiresApproval,\n guidance: {\n message: apiResponse.access?.reason || 'Access denied by PDLSS policy',\n registrationUrl: `${mergedConfig.apiBaseUrl?.replace('/api', '')}/register`,\n documentationUrl: `${mergedConfig.apiBaseUrl?.replace('/api', '')}/docs/pdlss`,\n },\n verifiedAt: new Date(),\n // Extract sessionId so decisions can be recorded for denials too\n sessionId: (apiResponse as Record<string, unknown>).sessionId as string | undefined,\n // v2.3.10 (defect #34, round-4): anonymous traffic has no session →\n // correlationId is the linking key for paired local_override events.\n correlationId: (apiResponse as Record<string, unknown>).correlationId as string | undefined,\n recommendation: (apiResponse as Record<string, unknown>)\n .recommendation as EnhancedVerificationResult['recommendation'],\n recommendationReasons: (apiResponse as Record<string, unknown>).recommendationReasons as\n | string[]\n | undefined,\n };\n\n return result;\n }\n\n // Build successful result\n const agent: VerifiedAgent | undefined = apiResponse.agent\n ? {\n astraId: apiResponse.agent.astraId,\n name: apiResponse.agent.name,\n trustScore: apiResponse.agent.trustScore,\n trustLevel: getTrustLevel(apiResponse.agent.trustScore),\n blockchainVerified: apiResponse.agent.blockchainStatus === 'verified',\n status: apiResponse.agent.agentStatus as VerifiedAgent['status'],\n }\n : undefined;\n\n const developer: VerifiedDeveloper | undefined = apiResponse.developer\n ? {\n astradId: apiResponse.developer.kyaOwnerId,\n name: apiResponse.developer.fullName,\n trustScore: apiResponse.developer.trustScore || 0,\n verified: apiResponse.developer.identityVerified,\n }\n : undefined;\n\n const organization: VerifiedOrganization | undefined = apiResponse.organization\n ? {\n name: apiResponse.organization.name,\n verified: apiResponse.organization.verified,\n trustScore: apiResponse.organization.trustScore,\n }\n : undefined;\n\n // Verification context — structured \"why\" the merchant gets in v2.2.4+.\n // Carries appliedPolicy (no UUIDs), pdlssCheck summary, dynamic trust score,\n // and policy-driven attestations. Replaces the old over-sharing `pdlss` block.\n const verificationContext = apiResponse.verificationContext;\n\n // Server is the single source of truth for access level. SDK reads\n // apiResponse.access.accessLevel verbatim — no client-side trust-score remap.\n // Fallback to 'standard' if the server response is missing the field (older\n // backend without the v2.3.0 contract); it covers the verified-access case.\n const accessLevel: AccessLevel = apiResponse.access?.accessLevel ?? 'standard';\n\n const result: EnhancedVerificationResult = {\n verified: true,\n accessLevel,\n agent,\n developer,\n organization,\n appliedPolicy: apiResponse.access?.appliedPolicy,\n verificationContext,\n requiresStepUp: apiResponse.access?.requiresStepUp,\n requiresApproval: apiResponse.access?.requiresApproval,\n verifiedAt: new Date(),\n cacheTtl: mergedConfig.cacheTtl,\n // Handshake Protocol v10 enhanced fields (present when backend returns them)\n sessionId: (apiResponse as Record<string, unknown>).sessionId as string | undefined,\n // v2.3.10 (defect #34, round-4): anonymous responses surface correlationId\n // (no session row exists for unverified callers).\n correlationId: (apiResponse as Record<string, unknown>).correlationId as string | undefined,\n runtimeChallenge: (apiResponse as Record<string, unknown>).runtimeChallenge as\n | RuntimeChallengeResult\n | undefined,\n tokenGuidance: (apiResponse as Record<string, unknown>).tokenGuidance as\n | TokenGuidance\n | undefined,\n recommendation: (apiResponse as Record<string, unknown>)\n .recommendation as EnhancedVerificationResult['recommendation'],\n recommendationReasons: (apiResponse as Record<string, unknown>).recommendationReasons as\n | string[]\n | undefined,\n warningHeader: (apiResponse as Record<string, unknown>).warningHeader as\n | { name: string; value: string }\n | undefined,\n };\n\n // Enforce AstraSync recommendation\n if (result.recommendation === 'deny') {\n result.verified = false;\n result.accessLevel = 'none';\n result.denialReasons = result.recommendationReasons || [\n 'Access denied by AstraSync recommendation',\n ];\n if (result.runtimeChallenge) {\n result.guidance = {\n message: `Verification failed: ${result.runtimeChallenge.reason || 'runtime challenge failed'}`,\n registrationUrl: `${mergedConfig.apiBaseUrl?.replace('/api', '')}/register`,\n documentationUrl: `${mergedConfig.apiBaseUrl?.replace('/api', '')}/docs/runtime-challenge`,\n };\n }\n } else if (result.recommendation === 'step_up_required') {\n result.requiresStepUp = true;\n if (ACCESS_LEVEL_HIERARCHY[result.accessLevel] > ACCESS_LEVEL_HIERARCHY['read-only']) {\n result.accessLevel = 'read-only';\n }\n result.denialReasons = result.recommendationReasons || ['Step-up verification required'];\n }\n\n // Cache the result (skip caching denials — agent may fix challenge endpoint and retry)\n if (mergedConfig.cacheTtl && mergedConfig.cacheTtl > 0 && result.recommendation !== 'deny') {\n cacheResult(request.credentials, result, mergedConfig.cacheTtl);\n }\n\n return result;\n}\n\n/**\n * Record a counterparty's grant/deny decision for a verification session.\n * Fire-and-forget — errors are silently swallowed.\n */\n/**\n * v2.3.9 (defect #34): optional override metadata. Set when the SDK's\n * local enforcement (toolGate / methodGate / trustScore floor) rejected\n * a request the SERVER had granted. Backend emits a distinct\n * `verification.local_override` event so the activity feed surfaces the\n * divergence as a separate row.\n */\nexport interface DecisionOverride {\n overriddenBy: 'toolGate' | 'methodGate' | 'trustScore' | 'other';\n toolName?: string;\n requestedLevel?: AccessLevel;\n grantedLevel?: AccessLevel;\n}\n\nexport async function recordDecision(\n config: GatewayConfig,\n sessionId: string,\n decision: 'granted' | 'denied',\n reason?: string,\n override?: DecisionOverride\n): Promise<void> {\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n if (config.apiKey) {\n headers['Authorization'] = `Bearer ${config.apiKey}`;\n headers['X-API-Key'] = config.apiKey;\n }\n\n await fetch(`${config.apiBaseUrl}/agents/verify-access/${sessionId}/decision`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n decision,\n reason,\n ...(override && {\n overriddenBy: override.overriddenBy,\n toolName: override.toolName,\n requestedLevel: override.requestedLevel,\n grantedLevel: override.grantedLevel,\n }),\n }),\n }).catch(() => {\n /* fire-and-forget */\n });\n}\n\n/**\n * v2.3.10 (defect #34, round-4): record a SDK-side local override for an\n * anonymous verify-access response. Anonymous traffic has no session row, so\n * `recordDecision` (above) doesn't apply — but we still need to surface the\n * dashboard-vs-runtime divergence (e.g. server granted with audit warning\n * but local toolGate floor denied) on the activity feed.\n *\n * Backend ties the resulting `verification.local_override` event back to the\n * original `verification.unverified_audit` event via `correlationId`. The\n * endpoint is sessionless — see the docstring on the backend route for the\n * abuse-mitigation rationale (rate-limited per IP).\n *\n * Fire-and-forget — errors are silently swallowed.\n */\nexport async function recordAnonymousLocalOverride(\n config: GatewayConfig,\n correlationId: string,\n override: DecisionOverride,\n reason?: string\n): Promise<void> {\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n if (config.apiKey) {\n headers['Authorization'] = `Bearer ${config.apiKey}`;\n headers['X-API-Key'] = config.apiKey;\n }\n\n await fetch(`${config.apiBaseUrl}/agents/verify-access/local-override`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n correlationId,\n reason,\n overriddenBy: override.overriddenBy,\n toolName: override.toolName,\n requestedLevel: override.requestedLevel,\n grantedLevel: override.grantedLevel,\n }),\n }).catch(() => {\n /* fire-and-forget */\n });\n}\n\n/**\n * Fetch the per-route policy for an endpoint from the AstraSync backend.\n * v2.9.7 moved policy authority into the dashboard — the SDK no longer\n * accepts `routes` from merchant-side source code, it fetches them from\n * here on init (and refreshes periodically).\n *\n * Returns `null` when the request fails for any reason — the caller decides\n * how to fall back (the middleware allows-all when no policy is loaded so\n * a misconfigured init doesn't take down the merchant's API).\n */\nexport async function fetchRoutes(\n config: GatewayConfig,\n counterpartyId: string\n): Promise<RouteAccessConfigShape[] | null> {\n if (!counterpartyId) return null;\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n if (config.apiKey) {\n headers['Authorization'] = `Bearer ${config.apiKey}`;\n headers['X-API-Key'] = config.apiKey;\n }\n try {\n const response = await fetch(\n `${config.apiBaseUrl}/endpoints/${encodeURIComponent(counterpartyId)}/routes`,\n { method: 'GET', headers }\n );\n if (!response.ok) return null;\n const body = (await response.json()) as { data?: { routes?: RouteAccessConfigShape[] } };\n return body.data?.routes ?? [];\n } catch {\n return null;\n }\n}\n\n/**\n * Minimal shape of an EndpointRoute as the SDK consumes it. Mirrors the\n * server's `EndpointRoute` type and the SDK's `RouteAccessConfig` — same\n * JSON moves between server and SDK unchanged.\n */\nexport interface RouteAccessConfigShape {\n pattern: string;\n method: string;\n minAccessLevel: 'none' | 'restricted' | 'read-only' | 'standard' | 'full' | 'internal';\n minTrustScore?: number;\n requiredPurposes?: string[];\n allowedPurposes?: string[];\n allowedJurisdictions?: string[];\n maxDuration?: number;\n maxTransactionValue?: number;\n}\n\n/**\n * Verify an agent AND automatically record the grant/deny decision.\n *\n * This is the recommended entry point for counterparties that call verify()\n * directly (e.g. MCP servers) rather than using createMiddleware().\n * It adds createSession: true, then fire-and-forgets the decision.\n */\nexport async function verifyAndRecord(\n config: GatewayConfig,\n request: VerificationRequest\n): Promise<VerificationResult> {\n const mergedConfig = { ...DEFAULT_CONFIG, ...config };\n const result = await verify(mergedConfig, { ...request, createSession: true });\n const sessionId = (result as EnhancedVerificationResult).sessionId;\n\n if (sessionId) {\n if (result.verified) {\n recordDecision(mergedConfig, sessionId, 'granted').catch(() => {});\n } else {\n recordDecision(mergedConfig, sessionId, 'denied', result.denialReasons?.[0]).catch(() => {});\n }\n }\n\n return result;\n}\n\n/**\n * Report an unregistered agent attempt (no AstraSync credentials).\n * Called by SDK adapters when an agent is redirected to /docs/agent-access.\n * Fire-and-forget — errors are silently swallowed.\n */\nexport async function reportUnregisteredAttempt(\n config: GatewayConfig,\n data: {\n counterpartyUrl: string;\n counterpartyType?: string;\n sourceIp?: string;\n userAgent?: string;\n requestPath?: string;\n requestMethod?: string;\n }\n): Promise<void> {\n const apiBaseUrl = config.apiBaseUrl || DEFAULT_CONFIG.apiBaseUrl!;\n\n await fetch(`${apiBaseUrl}/verification-activity/unregistered-attempt`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(data),\n }).catch(() => {\n /* fire-and-forget */\n });\n}\n\n/**\n * Report a counterparty-side PDLSS pre-check failure.\n * Called by SDK adapters when the agent's requested PDLSS exceeds\n * counterparty-defined maximums BEFORE calling verify-access.\n * Fire-and-forget — errors are silently swallowed.\n */\nexport async function reportCounterpartyPreCheckFailure(\n config: GatewayConfig,\n data: {\n agentId: string;\n counterpartyUrl: string;\n counterpartyType?: string;\n failures: Array<{\n field: string;\n requested: string | number;\n limit: string | number | string[];\n message: string;\n }>;\n requestPath?: string;\n requestMethod?: string;\n }\n): Promise<void> {\n const apiBaseUrl = config.apiBaseUrl || DEFAULT_CONFIG.apiBaseUrl!;\n\n await fetch(`${apiBaseUrl}/verification-activity/counterparty-pre-check-failure`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(data),\n }).catch(() => {\n /* fire-and-forget */\n });\n}\n\n/**\n * Quick verification - just check if credentials are valid\n */\nexport async function quickVerify(\n config: GatewayConfig,\n credentials: AgentCredentials\n): Promise<{ verified: boolean; accessLevel: AccessLevel; reason?: string }> {\n const result = await verify(config, {\n credentials,\n purpose: 'verification',\n });\n\n return {\n verified: result.verified,\n accessLevel: result.accessLevel,\n reason: result.denialReasons?.[0],\n };\n}\n","/**\n * HTTP Transport Adapter\n *\n * Maps AstraSync credentials to/from HTTP headers (X-Astra-* convention).\n */\n\nimport type { AstraSyncCredentials } from '../types';\n\nconst HEADER_PREFIX = 'X-Astra-';\n\n/**\n * Inject AstraSync credentials into HTTP headers.\n */\nexport function setHttpHeaders(\n headers: Record<string, string>,\n credentials: AstraSyncCredentials,\n): Record<string, string> {\n const result = { ...headers };\n\n result[`${HEADER_PREFIX}ID`] = credentials.agentId;\n\n if (credentials.verifyUrl) {\n result[`${HEADER_PREFIX}Verify`] = credentials.verifyUrl;\n }\n\n if (credentials.challengeUrl) {\n result[`${HEADER_PREFIX}Challenge`] = credentials.challengeUrl;\n }\n\n if (credentials.pdlss?.purpose) {\n const purposeValue = credentials.pdlss.purpose.action\n ? `${credentials.pdlss.purpose.category}:${credentials.pdlss.purpose.action}`\n : credentials.pdlss.purpose.category;\n result[`${HEADER_PREFIX}Purpose`] = purposeValue;\n }\n\n if (credentials.pdlss?.duration?.maxSessionDuration) {\n result[`${HEADER_PREFIX}Duration`] = String(credentials.pdlss.duration.maxSessionDuration);\n }\n\n if (credentials.pdlss?.scope?.jurisdiction) {\n result[`${HEADER_PREFIX}Scope`] = credentials.pdlss.scope.jurisdiction;\n }\n\n return result;\n}\n\n/**\n * Extract AstraSync credentials from HTTP headers.\n */\nexport function extractHttpCredentials(\n headers: Record<string, string | string[] | undefined>,\n): AstraSyncCredentials | null {\n const getValue = (key: string): string | undefined => {\n const v = headers[key] ?? headers[key.toLowerCase()];\n return Array.isArray(v) ? v[0] : v;\n };\n\n const agentId = getValue(`${HEADER_PREFIX}ID`) ?? getValue('x-astra-id');\n if (!agentId) return null;\n\n const credentials: AstraSyncCredentials = { agentId };\n\n const verifyUrl = getValue(`${HEADER_PREFIX}Verify`) ?? getValue('x-astra-verify');\n if (verifyUrl) credentials.verifyUrl = verifyUrl;\n\n const challengeUrl = getValue(`${HEADER_PREFIX}Challenge`) ?? getValue('x-astra-challenge');\n if (challengeUrl) credentials.challengeUrl = challengeUrl;\n\n const purpose = getValue(`${HEADER_PREFIX}Purpose`) ?? getValue('x-astra-purpose');\n if (purpose) {\n const [category, action] = purpose.split(':');\n credentials.pdlss = {\n ...credentials.pdlss,\n purpose: { category, action },\n };\n }\n\n const duration = getValue(`${HEADER_PREFIX}Duration`) ?? getValue('x-astra-duration');\n if (duration) {\n credentials.pdlss = {\n ...credentials.pdlss,\n duration: { maxSessionDuration: parseInt(duration, 10) },\n };\n }\n\n const scope = getValue(`${HEADER_PREFIX}Scope`) ?? getValue('x-astra-scope');\n if (scope) {\n credentials.pdlss = {\n ...credentials.pdlss,\n scope: { jurisdiction: scope },\n };\n }\n\n return credentials;\n}\n","/**\n * Counterparty-side PDLSS pre-check.\n *\n * Compares the agent's requested PDLSS dimensions (from X-Astra-* headers)\n * against the counterparty-defined maximums on the route config.\n * Returns an array of failures — empty means all checks passed.\n *\n * This runs BEFORE calling verify-access on AstraSync. If it fails,\n * the request is rejected immediately without calling the platform.\n */\n\nimport type { RouteAccessConfig, AstraSyncCredentials, CounterpartyPreCheckFailure } from './types';\n\nexport function performCounterpartyPreCheck(\n routeConfig: RouteAccessConfig,\n astraCreds: AstraSyncCredentials | null,\n purpose: string | undefined,\n): CounterpartyPreCheckFailure[] {\n const failures: CounterpartyPreCheckFailure[] = [];\n\n // Check purpose against allowedPurposes whitelist\n if (routeConfig.allowedPurposes && routeConfig.allowedPurposes.length > 0 && purpose) {\n if (!routeConfig.allowedPurposes.includes(purpose)) {\n failures.push({\n field: 'purpose',\n requested: purpose,\n limit: routeConfig.allowedPurposes,\n message: `Purpose \"${purpose}\" is not in the allowed list: [${routeConfig.allowedPurposes.join(', ')}]`,\n });\n }\n }\n\n // Check purpose against requiredPurposes (legacy field — agent must declare one of these)\n if (routeConfig.requiredPurposes && routeConfig.requiredPurposes.length > 0 && purpose) {\n if (!routeConfig.requiredPurposes.includes(purpose)) {\n failures.push({\n field: 'purpose',\n requested: purpose,\n limit: routeConfig.requiredPurposes,\n message: `Purpose \"${purpose}\" is not in the required list: [${routeConfig.requiredPurposes.join(', ')}]`,\n });\n }\n }\n\n // Check duration against maxDuration\n if (routeConfig.maxDuration && astraCreds?.pdlss?.duration?.maxSessionDuration) {\n const requested = astraCreds.pdlss.duration.maxSessionDuration;\n if (requested > routeConfig.maxDuration) {\n failures.push({\n field: 'duration',\n requested,\n limit: routeConfig.maxDuration,\n message: `Requested duration ${requested}s exceeds maximum ${routeConfig.maxDuration}s`,\n });\n }\n }\n\n // Check jurisdiction against allowedJurisdictions\n if (\n routeConfig.allowedJurisdictions &&\n routeConfig.allowedJurisdictions.length > 0 &&\n astraCreds?.pdlss?.scope?.jurisdiction\n ) {\n const requested = astraCreds.pdlss.scope.jurisdiction;\n if (!routeConfig.allowedJurisdictions.includes(requested)) {\n failures.push({\n field: 'jurisdiction',\n requested,\n limit: routeConfig.allowedJurisdictions,\n message: `Jurisdiction \"${requested}\" is not in the allowed list: [${routeConfig.allowedJurisdictions.join(', ')}]`,\n });\n }\n }\n\n return failures;\n}\n"],"mappings":"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","names":["result"]}
1
+ {"version":3,"sources":["../../src/adapters/nextjs.ts","../../src/access-levels.ts","../../src/verify.ts","../../src/transport/http.ts","../../src/pdlss-pre-check.ts"],"sourcesContent":["/**\n * AstraSync Universal Verification Gateway - Next.js Middleware\n *\n * Next.js middleware for verifying AI agents on web applications.\n * Supports Commerce Shield overlay for unverified agents.\n *\n * @example\n * ```typescript\n * // middleware.ts\n * import { createMiddleware } from '@astrasyncai/verification-gateway/nextjs';\n *\n * export const middleware = createMiddleware({\n * apiBaseUrl: 'https://api.astrasync.ai',\n * showCommerceShield: true,\n * routes: [\n * { pattern: '/api/public/*', method: '*', minAccessLevel: 'none' },\n * { pattern: '/api/*', method: '*', minAccessLevel: 'standard' },\n * { pattern: '/dashboard/*', method: '*', minAccessLevel: 'read-only' },\n * ],\n * });\n *\n * export const config = {\n * matcher: ['/api/:path*', '/dashboard/:path*'],\n * };\n * ```\n */\n\nimport type { NextRequest } from 'next/server';\nimport type {\n NextJsMiddlewareOptions,\n AgentCredentials,\n VerificationResult,\n RouteAccessConfig,\n AstraSyncCredentials,\n} from '../types';\nimport { verify, reportCounterpartyPreCheckFailure, fetchRoutes } from '../verify';\nimport { hasMinimumAccess } from '../access-levels';\nimport { extractHttpCredentials } from '../transport/http';\nimport { performCounterpartyPreCheck } from '../pdlss-pre-check';\n\n/**\n * Extract credentials from Next.js request\n */\nfunction extractCredentialsFromNextRequest(request: NextRequest): AgentCredentials {\n const credentials: AgentCredentials = {};\n\n // Check for ASTRA-ID in headers\n const astraId = request.headers.get('x-astra-id') || request.headers.get('X-Astra-Id');\n if (astraId) {\n credentials.astraId = astraId;\n }\n\n // Check for API key\n const apiKey = request.headers.get('x-api-key') || request.headers.get('X-Api-Key');\n if (apiKey) {\n credentials.apiKey = apiKey;\n }\n\n // Check Authorization header\n const authHeader = request.headers.get('authorization');\n if (authHeader) {\n credentials.authorizationHeader = authHeader;\n if (authHeader.startsWith('Bearer ')) {\n credentials.jwt = authHeader.slice(7);\n }\n }\n\n // Check query parameters\n const url = new URL(request.url);\n const astraIdParam = url.searchParams.get('astraId');\n const apiKeyParam = url.searchParams.get('apiKey');\n\n if (astraIdParam && !credentials.astraId) {\n credentials.astraId = astraIdParam;\n }\n if (apiKeyParam && !credentials.apiKey) {\n credentials.apiKey = apiKeyParam;\n }\n\n return credentials;\n}\n\n/**\n * Match a route pattern against a path\n */\nfunction matchRoute(pattern: string, path: string): boolean {\n const regexPattern = pattern.replace(/\\*/g, '.*').replace(/\\//g, '\\\\/');\n\n const regex = new RegExp(`^${regexPattern}$`);\n return regex.test(path);\n}\n\n/**\n * Find the route configuration for a request\n */\nfunction findRouteConfig(\n routes: RouteAccessConfig[],\n path: string,\n method: string\n): RouteAccessConfig | undefined {\n return routes.find((route) => {\n const methodMatches =\n route.method === '*' || route.method.toUpperCase() === method.toUpperCase();\n const pathMatches = matchRoute(route.pattern, path);\n return methodMatches && pathMatches;\n });\n}\n\n/**\n * Extract AstraSyncCredentials from Next.js request headers.\n * Returns null if no AstraSync headers are present.\n */\nfunction extractAstraSyncCredentialsFromNextRequest(\n request: NextRequest\n): AstraSyncCredentials | null {\n const headers: Record<string, string> = {};\n request.headers.forEach((value, key) => {\n headers[key] = value;\n });\n return extractHttpCredentials(headers);\n}\n\n/**\n * Extract purpose from request.\n *\n * Priority:\n * 1. Agent's declared PDLSS purpose from X-Astra-Purpose header (e.g. \"read_data:search\")\n * 2. Explicit x-purpose header\n * 3. HTTP method → PDLSS category fallback\n */\nfunction extractPurpose(request: NextRequest): string {\n // 1. Check agent's declared PDLSS purpose (X-Astra-Purpose header)\n const astraPurpose = request.headers.get('x-astra-purpose');\n if (astraPurpose) {\n // Extract category from \"category:action\" format\n return astraPurpose.split(':')[0];\n }\n\n // 2. Try explicit purpose header\n const purposeHeader = request.headers.get('x-purpose');\n if (purposeHeader) {\n return purposeHeader;\n }\n\n // 3. Infer from HTTP method using PDLSS-compatible categories\n switch (request.method.toUpperCase()) {\n case 'GET':\n return 'read_data';\n case 'POST':\n return 'write_data';\n case 'PUT':\n case 'PATCH':\n return 'write_data';\n case 'DELETE':\n return 'delete_data';\n default:\n return 'general';\n }\n}\n\n/**\n * Generate Commerce Shield HTML response\n */\nfunction generateCommerceShieldHtml(\n result: VerificationResult,\n options: NextJsMiddlewareOptions\n): string {\n const title = options.commerceShield?.title || 'AstraSync Agent Verification';\n const message =\n options.commerceShield?.message ||\n result.guidance?.message ||\n \"This site verifies AI agents before granting access. We noticed you're visiting without AstraSync credentials.\";\n const registrationUrl = result.guidance?.registrationUrl || 'https://astrasync.ai/register';\n const docsUrl = result.guidance?.documentationUrl || 'https://astrasync.ai/docs/agent-access';\n const allowGuest = options.commerceShield?.allowGuestAccess ?? true;\n\n return `\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>${title}</title>\n <style>\n * {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n }\n body {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;\n background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);\n min-height: 100vh;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 20px;\n }\n .shield-container {\n background: rgba(255, 255, 255, 0.95);\n border-radius: 16px;\n box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);\n max-width: 480px;\n width: 100%;\n padding: 40px;\n text-align: center;\n }\n .shield-icon {\n font-size: 48px;\n margin-bottom: 20px;\n }\n .shield-title {\n font-size: 24px;\n font-weight: 700;\n color: #1a1a2e;\n margin-bottom: 16px;\n }\n .shield-message {\n color: #4a5568;\n line-height: 1.6;\n margin-bottom: 24px;\n }\n .shield-steps {\n text-align: left;\n background: #f7fafc;\n border-radius: 8px;\n padding: 20px;\n margin-bottom: 24px;\n }\n .shield-steps h3 {\n font-size: 14px;\n font-weight: 600;\n color: #2d3748;\n margin-bottom: 12px;\n }\n .shield-steps ol {\n padding-left: 20px;\n color: #4a5568;\n }\n .shield-steps li {\n margin-bottom: 8px;\n }\n .shield-buttons {\n display: flex;\n flex-direction: column;\n gap: 12px;\n }\n .btn {\n display: inline-block;\n padding: 14px 24px;\n border-radius: 8px;\n font-weight: 600;\n text-decoration: none;\n transition: all 0.2s;\n cursor: pointer;\n border: none;\n font-size: 16px;\n }\n .btn-primary {\n background: linear-gradient(135deg, #6366f1 0%, #4f46e5 100%);\n color: white;\n }\n .btn-primary:hover {\n transform: translateY(-2px);\n box-shadow: 0 4px 12px rgba(99, 102, 241, 0.4);\n }\n .btn-secondary {\n background: #e2e8f0;\n color: #4a5568;\n }\n .btn-secondary:hover {\n background: #cbd5e0;\n }\n .shield-footer {\n margin-top: 24px;\n font-size: 14px;\n color: #718096;\n }\n .shield-footer a {\n color: #6366f1;\n text-decoration: none;\n }\n .shield-footer a:hover {\n text-decoration: underline;\n }\n </style>\n</head>\n<body>\n <div class=\"shield-container\">\n <div class=\"shield-icon\">🛡️</div>\n <h1 class=\"shield-title\">${title}</h1>\n <p class=\"shield-message\">${message}</p>\n\n <div class=\"shield-steps\">\n <h3>To get verified access:</h3>\n <ol>\n <li>Register at <a href=\"${registrationUrl}\">astrasync.ai/register</a></li>\n <li>Create and register your agent</li>\n <li>Add your ASTRA-ID to request headers</li>\n <li>Refresh this page</li>\n </ol>\n </div>\n\n <div class=\"shield-buttons\">\n <a href=\"${registrationUrl}\" class=\"btn btn-primary\">Register Now</a>\n ${allowGuest ? '<button onclick=\"window.location.reload()\" class=\"btn btn-secondary\">Continue as Guest (Limited)</button>' : ''}\n </div>\n\n <p class=\"shield-footer\">\n Learn more: <a href=\"${docsUrl}\">Agent Access Documentation</a>\n </p>\n </div>\n</body>\n</html>\n `.trim();\n}\n\nconst DEFAULT_ROUTES_REFRESH_MS = 5 * 60 * 1000;\n\n/**\n * Create Next.js middleware for agent verification.\n *\n * v2.9.7 moved per-route policy out of merchant-side source code into the\n * AstraSync dashboard. The middleware fetches its routes from the backend\n * via `GET /endpoints/:counterpartyId/routes` on init and refreshes\n * periodically — see `ExpressMiddlewareOptions` for the rationale (defect\n * 24, dual-config silent-conflict).\n */\nexport function createMiddleware(options: NextJsMiddlewareOptions) {\n const {\n skipPaths = [],\n showCommerceShield = true,\n enableRuntimeChallenge = true,\n routesRefreshMs = DEFAULT_ROUTES_REFRESH_MS,\n ...config\n } = options;\n\n let cachedRoutes: RouteAccessConfig[] = [];\n let lastFetchAt = 0;\n let refreshing: Promise<void> | null = null;\n let warnedNoCounterparty = false;\n\n async function refreshRoutes(): Promise<void> {\n if (!config.counterpartyId) {\n if (!warnedNoCounterparty) {\n // eslint-disable-next-line no-console\n console.warn(\n '[VerificationGateway/Next.js] No counterpartyId configured — falling through (allow all). ' +\n 'Per-route policy lives in the AstraSync dashboard now; register the endpoint and ' +\n 'set counterpartyId in your middleware config to enforce policy.'\n );\n warnedNoCounterparty = true;\n }\n return;\n }\n const fetched = await fetchRoutes(config, config.counterpartyId);\n if (fetched) {\n cachedRoutes = fetched;\n lastFetchAt = Date.now();\n }\n }\n\n refreshing = refreshRoutes().finally(() => {\n refreshing = null;\n });\n\n return async function middleware(request: NextRequest) {\n // Dynamic import NextResponse to avoid build issues\n const { NextResponse } = await import('next/server');\n\n const pathname = request.nextUrl.pathname;\n\n // Check if path should be skipped\n const shouldSkip = skipPaths.some((pattern) => matchRoute(pattern, pathname));\n if (shouldSkip) {\n return NextResponse.next();\n }\n\n if (refreshing) {\n await refreshing.catch(() => {});\n }\n if (config.counterpartyId && Date.now() - lastFetchAt > routesRefreshMs) {\n refreshing = refreshRoutes().finally(() => {\n refreshing = null;\n });\n }\n\n // Find route configuration (from remote-fetched cache)\n const routeConfig = findRouteConfig(cachedRoutes, pathname, request.method);\n\n // If no route config, allow through\n if (!routeConfig) {\n return NextResponse.next();\n }\n\n // If route requires 'none' access, allow through\n if (routeConfig.minAccessLevel === 'none') {\n return NextResponse.next();\n }\n\n // Extract credentials\n const credentials = extractCredentialsFromNextRequest(request);\n\n // v2.3.0: anonymous traffic no longer short-circuits client-side.\n // The server applies the endpoint's `unverifiedAgentPolicy` (deny /\n // allow_partial / allow_full) and emits the verification event +\n // blockchain record per the canonical flow. SDK forwards verbatim.\n\n // Auto-detect counterparty URL from the request if not explicitly configured.\n // Since the SDK is installed at this endpoint, we always know the origin.\n const counterpartyUrl = config.counterpartyUrl || request.nextUrl.origin;\n\n // Extract purpose and full AstraSync credentials (includes PDLSS from X-Astra-* headers)\n const purpose = extractPurpose(request);\n const astraCreds = extractAstraSyncCredentialsFromNextRequest(request);\n\n // Step 2: Counterparty-side PDLSS pre-check — compare agent's requested PDLSS\n // against counterparty-defined maximums on the route config.\n // Rejects immediately if outside limits, BEFORE calling verify-access.\n const preCheckFailures = performCounterpartyPreCheck(routeConfig, astraCreds, purpose);\n if (preCheckFailures.length > 0) {\n const preCheckResult: VerificationResult = {\n verified: false,\n accessLevel: 'none',\n denialReasons: preCheckFailures.map((f) => f.message),\n guidance: {\n message: 'Request exceeds counterparty-defined PDLSS limits.',\n registrationUrl: `${config.apiBaseUrl?.replace('/api', '')}/register`,\n documentationUrl: `${config.apiBaseUrl?.replace('/api', '')}/docs/pdlss`,\n },\n verifiedAt: new Date(),\n };\n\n // Fire-and-forget: notify AstraSync of the pre-check failure\n reportCounterpartyPreCheckFailure(config, {\n agentId: astraCreds?.agentId || credentials.astraId || 'unknown',\n counterpartyUrl,\n counterpartyType: config.counterpartyType || 'website',\n failures: preCheckFailures,\n requestPath: pathname,\n requestMethod: request.method,\n }).catch(() => {});\n\n // For API routes, return JSON\n if (pathname.startsWith('/api/')) {\n return NextResponse.json(\n {\n success: false,\n error: {\n code: 'PDLSS_PRE_CHECK_FAILED',\n message: preCheckResult.denialReasons?.[0] || 'PDLSS pre-check failed',\n guidance: preCheckResult.guidance,\n },\n },\n { status: 403 }\n );\n }\n\n // For web pages, show Commerce Shield\n if (showCommerceShield) {\n return new NextResponse(generateCommerceShieldHtml(preCheckResult, options), {\n status: 200,\n headers: {\n 'Content-Type': 'text/html',\n 'X-AstraSync-Verification': 'commerce-shield',\n },\n });\n }\n\n return NextResponse.redirect(new URL('/unauthorized', request.url));\n }\n\n // Step 3: Call AstraSync verify-access with runtime challenge enabled\n const forwardedFor = request.headers.get('x-forwarded-for') || undefined;\n const originalClientIp = forwardedFor?.split(',')[0]?.trim();\n const result = await verify(config, {\n credentials,\n purpose,\n action: request.method.toLowerCase(),\n resource: pathname,\n counterpartyUrl,\n counterpartyType: config.counterpartyType || 'website',\n enableRuntimeChallenge,\n durationRequired: astraCreds?.pdlss?.duration?.maxSessionDuration,\n callerMetadata: {\n sourceIp: originalClientIp,\n userAgent: request.headers.get('user-agent') || undefined,\n referer: request.headers.get('referer') || undefined,\n host: request.headers.get('host') || undefined,\n forwardedFor,\n agentCardUrl: request.headers.get('x-astrasync-agent-card') || undefined,\n },\n });\n\n // v2.3.9 (defect #30): denied verifications short-circuit BEFORE the\n // gate-level comparison. The OR-prefix shape here keeps the\n // single-expression branch this adapter uses, but ensures\n // `verified === false` triggers the denial path even if the access\n // level math would otherwise let it through (the historical bug).\n // See express.ts for the full rationale.\n if (!result.verified || !hasMinimumAccess(result.accessLevel, routeConfig.minAccessLevel)) {\n // For API routes, return JSON\n if (pathname.startsWith('/api/')) {\n return NextResponse.json(\n {\n success: false,\n error: {\n code: result.verified ? 'INSUFFICIENT_ACCESS' : 'UNAUTHORIZED',\n message: result.denialReasons?.[0] || 'Access denied',\n accessLevel: result.accessLevel,\n required: routeConfig.minAccessLevel,\n guidance: result.guidance,\n },\n },\n { status: result.verified ? 403 : 401 }\n );\n }\n\n // For web pages, show Commerce Shield\n if (showCommerceShield) {\n return new NextResponse(generateCommerceShieldHtml(result, options), {\n status: 200,\n headers: {\n 'Content-Type': 'text/html',\n 'X-AstraSync-Verification': 'commerce-shield',\n },\n });\n }\n\n // Redirect to unauthorized page\n return NextResponse.redirect(new URL('/unauthorized', request.url));\n }\n\n // All checks passed - continue with verification info in headers\n const response = NextResponse.next();\n\n // Add verification info to response headers\n response.headers.set('X-AstraSync-Verified', result.verified.toString());\n response.headers.set('X-AstraSync-Access-Level', result.accessLevel);\n\n if (result.agent) {\n response.headers.set('X-AstraSync-Agent-Id', result.agent.astraId);\n response.headers.set('X-AstraSync-Trust-Score', result.agent.trustScore.toString());\n }\n\n return response;\n };\n}\n\n/**\n * Helper to create matcher config\n */\nexport function createMatcherConfig(paths: string[]): { matcher: string[] } {\n return { matcher: paths };\n}\n","/**\n * AstraSync Universal Verification Gateway - Access Level Definitions\n *\n * Defines the hierarchy and capabilities of each access level.\n *\n * v2.3.9 (defect #30): renamed `'guidance'` band → `'restricted'`. See\n * `types.ts` AccessLevel for the rationale (value-name collision with the\n * `guidance: {...}` help-payload object on VerificationResult).\n */\n\nimport type { AccessLevel, TrustLevel } from './types';\n\n/**\n * Access level hierarchy (higher number = more access)\n */\nexport const ACCESS_LEVEL_HIERARCHY: Record<AccessLevel, number> = {\n none: 0,\n restricted: 1,\n 'read-only': 2,\n standard: 3,\n full: 4,\n internal: 5,\n};\n\n/**\n * Access level descriptions for UI\n */\nexport const ACCESS_LEVEL_DESCRIPTIONS: Record<AccessLevel, string> = {\n none: 'No access - credentials required',\n restricted: 'Restricted access - registration prompt only',\n 'read-only': 'Read-only access - can browse but not modify',\n standard: 'Standard access - normal operations per PDLSS policy',\n full: 'Full access - all operations for high-trust agents',\n internal: 'Internal access - organization member privileges',\n};\n\n/**\n * Default trust score thresholds for access levels\n */\nexport const DEFAULT_TRUST_THRESHOLDS: Record<AccessLevel, number> = {\n none: 0,\n restricted: 0,\n 'read-only': 20,\n standard: 40,\n full: 70,\n internal: 0, // Internal is based on org membership, not score\n};\n\n/**\n * Trust level score ranges\n */\nexport const TRUST_LEVEL_RANGES: Record<TrustLevel, { min: number; max: number }> = {\n BRONZE: { min: 0, max: 39 },\n SILVER: { min: 40, max: 59 },\n GOLD: { min: 60, max: 79 },\n PLATINUM: { min: 80, max: 100 },\n};\n\n/**\n * Determine trust level from score\n */\nexport function getTrustLevel(score: number): TrustLevel {\n if (score >= 80) return 'PLATINUM';\n if (score >= 60) return 'GOLD';\n if (score >= 40) return 'SILVER';\n return 'BRONZE';\n}\n\n/**\n * Check if access level A is greater than or equal to access level B\n */\nexport function hasMinimumAccess(actual: AccessLevel, required: AccessLevel): boolean {\n return ACCESS_LEVEL_HIERARCHY[actual] >= ACCESS_LEVEL_HIERARCHY[required];\n}\n\n/**\n * Get the highest access level for a given trust score\n */\nexport function getAccessLevelForScore(\n trustScore: number,\n thresholds: Record<AccessLevel, number> = DEFAULT_TRUST_THRESHOLDS\n): AccessLevel {\n if (trustScore >= thresholds.full) return 'full';\n if (trustScore >= thresholds.standard) return 'standard';\n if (trustScore >= thresholds['read-only']) return 'read-only';\n return 'restricted';\n}\n\n/**\n * Determine access level from verification result.\n *\n * v2.3.9 (defect #30): unverified callers now return `'none'` (was\n * `'guidance'`). Denials grant zero — never a positive band.\n */\nexport function determineAccessLevel(\n verified: boolean,\n trustScore: number,\n isOrgMember: boolean,\n customThresholds?: Partial<Record<AccessLevel, number>>\n): AccessLevel {\n if (!verified) {\n return 'none';\n }\n\n if (isOrgMember) {\n return 'internal';\n }\n\n const thresholds = {\n ...DEFAULT_TRUST_THRESHOLDS,\n ...customThresholds,\n };\n\n return getAccessLevelForScore(trustScore, thresholds);\n}\n\n/**\n * Access capabilities per level\n */\nexport interface AccessCapabilities {\n canRead: boolean;\n canWrite: boolean;\n canDelete: boolean;\n canAdmin: boolean;\n canAccessInternal: boolean;\n maxTransactionValue?: number;\n allowedPurposes?: string[];\n}\n\n/**\n * Get capabilities for an access level\n */\nexport function getCapabilities(accessLevel: AccessLevel): AccessCapabilities {\n switch (accessLevel) {\n case 'none':\n return {\n canRead: false,\n canWrite: false,\n canDelete: false,\n canAdmin: false,\n canAccessInternal: false,\n };\n case 'restricted':\n return {\n canRead: false,\n canWrite: false,\n canDelete: false,\n canAdmin: false,\n canAccessInternal: false,\n };\n case 'read-only':\n return {\n canRead: true,\n canWrite: false,\n canDelete: false,\n canAdmin: false,\n canAccessInternal: false,\n };\n case 'standard':\n return {\n canRead: true,\n canWrite: true,\n canDelete: false,\n canAdmin: false,\n canAccessInternal: false,\n };\n case 'full':\n return {\n canRead: true,\n canWrite: true,\n canDelete: true,\n canAdmin: false,\n canAccessInternal: false,\n };\n case 'internal':\n return {\n canRead: true,\n canWrite: true,\n canDelete: true,\n canAdmin: true,\n canAccessInternal: true,\n };\n default:\n return {\n canRead: false,\n canWrite: false,\n canDelete: false,\n canAdmin: false,\n canAccessInternal: false,\n };\n }\n}\n","/**\n * AstraSync Universal Verification Gateway - Core Verification Logic\n *\n * This module handles the core verification logic, calling the AstraSync API\n * and processing the response into a standardized VerificationResult.\n */\n\nimport type {\n GatewayConfig,\n AgentCredentials,\n VerificationRequest,\n VerificationResult,\n VerifiedAgent,\n VerifiedDeveloper,\n VerifiedOrganization,\n GuidanceInfo,\n AccessLevel,\n EnhancedVerificationResult,\n TokenGuidance,\n RuntimeChallengeResult,\n} from './types';\nimport { getTrustLevel, ACCESS_LEVEL_HIERARCHY } from './access-levels';\n\n/**\n * Default configuration values\n *\n * apiBaseUrl matches the OpenAPI authoritative server (https://astrasync.ai/api\n * for prod, https://staging.astrasync.ai/api for staging). Always include the\n * /api path prefix when overriding — registration / docs URLs are derived by\n * stripping it.\n */\nconst DEFAULT_CONFIG: Partial<GatewayConfig> = {\n apiBaseUrl: 'https://astrasync.ai/api',\n // v2.3.9 (defect #30): default for unconfigured callers is `'none'` (no\n // access). Pre-rename this defaulted to `'guidance'`, which combined with\n // a route gated at `'guidance'` to silently let unverified traffic\n // through (`hasMinimumAccess('guidance', 'guidance') === true`).\n defaultAccessLevel: 'none',\n // minTrustScore + minTrustScoreForFull deprecated in v2.3.0 — server decides.\n cacheTtl: 300, // 5 minutes\n debug: false,\n};\n\n/**\n * Init self-test state. Fires once per process on first verify() call to warn\n * if apiBaseUrl is pointing at the wrong host (e.g. a marketing site that\n * 200s with text/html instead of the API).\n */\nlet initCheckPerformed = false;\n\n/** One-shot guard for v2.3.0 deprecation warning. */\nlet deprecationWarningShown = false;\n\nasync function performInitCheck(apiBaseUrl: string, debug?: boolean): Promise<void> {\n initCheckPerformed = true;\n try {\n const probeUrl = `${apiBaseUrl}/agents/verify-access`;\n // HEAD mirrors GET semantics (running the full request pipeline without a\n // body) so the response carries the same content-type the marketing 404\n // would return. OPTIONS often gets short-circuited by CORS-preflight\n // handlers and returns no content-type, defeating the check.\n const response = await fetch(probeUrl, { method: 'HEAD' });\n const contentType = response.headers.get('content-type') ?? '';\n if (contentType.startsWith('text/html')) {\n console.warn(\n `[VerificationGateway] apiBaseUrl '${apiBaseUrl}' returned HTML (content-type: ${contentType}). ` +\n `This usually means apiBaseUrl is pointing at a marketing site instead of the API. ` +\n `Expected: 'https://astrasync.ai/api' (prod) or 'https://staging.astrasync.ai/api' (staging). ` +\n `Set disableInitChecks: true on GatewayConfig to silence this warning.`\n );\n } else if (debug) {\n console.log(\n `[VerificationGateway] init check passed for ${apiBaseUrl} (content-type: ${contentType})`\n );\n }\n } catch (err) {\n if (debug) {\n console.log(`[VerificationGateway] init check failed (non-blocking): ${String(err)}`);\n }\n }\n}\n\n/**\n * Simple in-memory cache for verification results\n */\nconst verificationCache = new Map<string, { result: VerificationResult; expiresAt: number }>();\n\n/**\n * Generate cache key from credentials\n */\nfunction getCacheKey(credentials: AgentCredentials): string {\n return `${credentials.astraId || ''}-${credentials.apiKey || ''}-${credentials.jwt || ''}`;\n}\n\n/**\n * Check if cached result is still valid\n */\nfunction getCachedResult(credentials: AgentCredentials): VerificationResult | null {\n const key = getCacheKey(credentials);\n const cached = verificationCache.get(key);\n\n if (cached && cached.expiresAt > Date.now()) {\n return cached.result;\n }\n\n if (cached) {\n verificationCache.delete(key);\n }\n\n return null;\n}\n\n/**\n * Cache a verification result\n */\nfunction cacheResult(\n credentials: AgentCredentials,\n result: VerificationResult,\n ttlSeconds: number\n): void {\n const key = getCacheKey(credentials);\n verificationCache.set(key, {\n result,\n expiresAt: Date.now() + ttlSeconds * 1000,\n });\n}\n\n/**\n * Clear the verification cache\n */\nexport function clearCache(): void {\n verificationCache.clear();\n}\n\n/**\n * Extract agent credentials from various sources\n */\nexport function extractCredentials(\n headers: Record<string, string | string[] | undefined>,\n query?: Record<string, string | undefined>\n): AgentCredentials {\n const credentials: AgentCredentials = {};\n\n // Check for ASTRA-ID in headers (case-insensitive). Accepts the historical\n // X-Astra-Id name plus the X-Astra-AgentId / x-astra-agent-id alias the\n // partner asked for in #9a — both surface the same field.\n const astraIdHeader =\n headers['x-astra-id'] ||\n headers['X-Astra-Id'] ||\n headers['X-ASTRA-ID'] ||\n headers['x-astra-agentid'] ||\n headers['X-Astra-AgentId'] ||\n headers['x-astra-agent-id'] ||\n headers['X-Astra-Agent-Id'] ||\n headers['X-ASTRA-AGENT-ID'];\n if (astraIdHeader) {\n credentials.astraId = Array.isArray(astraIdHeader) ? astraIdHeader[0] : astraIdHeader;\n }\n\n // Check for API key in headers\n const apiKeyHeader = headers['x-api-key'] || headers['X-Api-Key'] || headers['X-API-KEY'];\n if (apiKeyHeader) {\n credentials.apiKey = Array.isArray(apiKeyHeader) ? apiKeyHeader[0] : apiKeyHeader;\n }\n\n // Check Authorization header for Bearer token\n const authHeader = headers['authorization'] || headers['Authorization'];\n if (authHeader) {\n const authValue = Array.isArray(authHeader) ? authHeader[0] : authHeader;\n credentials.authorizationHeader = authValue;\n\n if (authValue.startsWith('Bearer ')) {\n credentials.jwt = authValue.slice(7);\n }\n }\n\n // Check query parameters as fallback\n if (query) {\n if (query.astraId && !credentials.astraId) {\n credentials.astraId = query.astraId;\n }\n if (query.apiKey && !credentials.apiKey) {\n credentials.apiKey = query.apiKey;\n }\n }\n\n return credentials;\n}\n\n/**\n * Check if credentials are present\n */\nexport function hasCredentials(credentials: AgentCredentials): boolean {\n return !!(credentials.astraId || credentials.apiKey || credentials.jwt);\n}\n\n/**\n * Source of a synthesised guidance response. Round-10 split — the\n * `'no_credentials'` shape is the original (caller has no AstraSync\n * credentials, suggest registration). The `'api_error'` shape is for when\n * the verify-access HTTP call itself failed (5xx, network, etc.) — DON'T\n * tell a verified-but-currently-blocked partner to \"register your agent\".\n */\ntype GuidanceSource = 'no_credentials' | 'api_error';\n\n/**\n * Create guidance response for unverified agents or for API-error fallback.\n *\n * Round-10 (#47, O5): split source so the `register your agent` template\n * doesn't fire on transient backend failures. Also threads `correlationId`\n * through so adapter `onDenied` handlers can surface it on the merchant's\n * response body for log correlation.\n */\nfunction createGuidanceResponse(\n config: GatewayConfig,\n reason?: string,\n options: { source?: GuidanceSource; correlationId?: string } = {}\n): VerificationResult {\n const source = options.source ?? 'no_credentials';\n const isApiError = source === 'api_error';\n\n const guidance: GuidanceInfo = isApiError\n ? {\n message:\n 'Verification is temporarily unavailable. Retry with exponential backoff; if the issue persists, contact support with the correlationId.',\n registrationUrl: `${config.apiBaseUrl.replace('/api', '')}/register`,\n documentationUrl: `${config.apiBaseUrl.replace('/api', '')}/docs/agent-access`,\n steps: [\n 'Retry the request with exponential backoff',\n 'If failures persist, share the correlationId with support',\n ],\n }\n : {\n message:\n 'This service verifies AI agents before granting access. Please register your agent with AstraSync.',\n registrationUrl: `${config.apiBaseUrl.replace('/api', '')}/register`,\n documentationUrl: `${config.apiBaseUrl.replace('/api', '')}/docs/agent-access`,\n steps: [\n 'Register for an AstraSync account',\n 'Create and register your agent',\n 'Add your ASTRA-ID to request headers',\n 'Retry your request',\n ],\n };\n\n return {\n verified: false,\n // v2.3.9 (defect #30): denials grant `'none'`, NEVER a positive band.\n // Adapters additionally short-circuit on `verified === false` before\n // the gate check, but the access level still has to be honest at the\n // data layer so downstream consumers (SDK adapters in other languages,\n // custom integrations) inherit the correct semantics.\n accessLevel: 'none',\n guidance,\n denialReasons: reason ? [reason] : ['No valid agent credentials provided'],\n // Round-10 (#47, O5): on API-error fallback, surface a typed failure so\n // partners (and their custom onDenied handlers) can branch on\n // dimension. Without this, the synthesised stub was indistinguishable\n // from a real policy deny.\n failures: isApiError\n ? [\n {\n dimension: 'verify_access.api_error',\n message: reason ?? 'Verification temporarily unavailable',\n guidance: guidance.message,\n },\n ]\n : undefined,\n correlationId: options.correlationId,\n verifiedAt: new Date(),\n };\n}\n\n/**\n * Call the AstraSync verify-access API\n */\nasync function callVerifyAccessAPI(\n config: GatewayConfig,\n request: VerificationRequest\n): Promise<{\n success: boolean;\n access?: {\n allowed: boolean;\n /**\n * Server-decided access level. Read verbatim — do NOT remap client-side.\n * The server resolves this from endpoint policy + agent trust score using\n * the canonical thresholds (see backend `apps/backend/src/utils/access-levels.ts`).\n */\n accessLevel?: AccessLevel;\n reason?: string;\n /**\n * Aggregated denial failures (v2.9.8+). Empty / absent when allowed.\n * Each entry is `{ dimension, message, guidance? }` — see\n * `AccessFailure` for the contract.\n */\n failures?: Array<{ dimension: string; message: string; guidance?: string }>;\n requiresStepUp?: boolean;\n requiresApproval?: boolean;\n appliedPolicy?: {\n boundaryName: string;\n policyVersion: string;\n };\n counterparty?: {\n id: string;\n name: string;\n trustScoreRequirement: number;\n };\n };\n agent?: {\n kyaAgentId: string;\n astraId: string;\n name: string;\n trustScore: number;\n trustLevel: string;\n agentStatus: string;\n blockchainStatus: string;\n };\n developer?: {\n kyaOwnerId: string;\n fullName: string;\n email: string;\n identityVerified: boolean;\n trustScore: number;\n };\n organization?: {\n name: string;\n verified: boolean;\n trustScore: number;\n };\n /**\n * Structured explanation of the verification decision. Tells the merchant\n * WHY (id verified? challenge passed? request within PDLSS? trust score?)\n * without exposing thresholds, scope lists, or other-tenant counterparty\n * membership. Empty `attestations` unless the endpoint's access policy\n * declared `required_attestations`.\n */\n verificationContext?: {\n idVerified: boolean;\n runtimeChallenge: {\n status: 'passed' | 'skipped' | 'failed' | 'timeout' | 'not_supported';\n checkedAt: string | null;\n };\n pdlssCheck: {\n result: 'within' | 'exceeded' | 'denied' | 'not_evaluated';\n purpose: 'approved' | 'denied';\n scope: 'approved' | 'denied';\n };\n dynamicTrustScore: number;\n attestations: Array<{\n type: string;\n status: 'passed' | 'failed';\n validUntil?: string;\n proofType: 'reference' | 'zkp';\n proof: string;\n }>;\n };\n error?: string;\n /**\n * Round-10 (#47, O5): when the verify-access server response carries a\n * correlationId on an error envelope, propagate it so the SDK can thread\n * it through createGuidanceResponse → adapter onDenied → merchant body.\n */\n correlationId?: string;\n}> {\n const { credentials, ...requestData } = request;\n\n // Build the request body. agentId is omitted when not provided so the\n // server treats it as an anonymous canonical-flow call (Branch A/B/C).\n const body: Record<string, unknown> = {\n ...(credentials.astraId && { agentId: credentials.astraId }),\n purpose: requestData.purpose || 'general',\n };\n\n // Add optional fields\n if (requestData.action) body.action = requestData.action;\n if (requestData.resourceType) body.resourceType = requestData.resourceType;\n if (requestData.resource) body.resource = requestData.resource;\n if (requestData.jurisdiction) body.jurisdiction = requestData.jurisdiction;\n if (requestData.transactionValue) body.transactionValue = requestData.transactionValue;\n if (requestData.currency) body.currency = requestData.currency;\n if (requestData.isSubAgentRequest) body.isSubAgentRequest = requestData.isSubAgentRequest;\n if (requestData.parentAgentId) body.parentAgentId = requestData.parentAgentId;\n if (requestData.subAgentDepth !== undefined) body.subAgentDepth = requestData.subAgentDepth;\n // Handshake Protocol v10 additions\n if (requestData.enableRuntimeChallenge)\n body.enableRuntimeChallenge = requestData.enableRuntimeChallenge;\n if (requestData.createSession) body.createSession = requestData.createSession;\n if (requestData.durationRequired) body.durationRequired = requestData.durationRequired;\n if (requestData.counterpartyType) body.counterpartyType = requestData.counterpartyType;\n if (requestData.counterpartyUrl) body.counterpartyUrl = requestData.counterpartyUrl;\n if (config.counterpartyId) body.counterpartyId = config.counterpartyId;\n if (requestData.runtimeChallengeOptions)\n body.runtimeChallengeOptions = requestData.runtimeChallengeOptions;\n\n // Forward caller metadata when present. Merges the legacy top-level\n // clientIp/userAgent into the nested block for backward compatibility.\n if (requestData.callerMetadata || requestData.clientIp || requestData.userAgent) {\n const meta = {\n ...(requestData.clientIp && { sourceIp: requestData.clientIp }),\n ...(requestData.userAgent && { userAgent: requestData.userAgent }),\n ...requestData.callerMetadata,\n };\n if (Object.keys(meta).length > 0) body.callerMetadata = meta;\n }\n\n // Build headers\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...config.customHeaders,\n };\n\n // verify-access requires authentication. The backend's authenticate middleware\n // accepts either a JWT or an API key (starts with kya_) via `Authorization: Bearer <token>`.\n // Credential-supplied auth header (e.g. the agent's own token) takes priority.\n if (credentials.authorizationHeader) {\n headers['Authorization'] = credentials.authorizationHeader;\n } else if (config.apiKey) {\n headers['Authorization'] = `Bearer ${config.apiKey}`;\n }\n // Legacy header kept for compatibility with any middleware that reads it directly.\n if (config.apiKey) {\n headers['X-API-Key'] = config.apiKey;\n }\n\n try {\n const response = await fetch(`${config.apiBaseUrl}/agents/verify-access`, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n });\n\n const data = await response.json();\n\n // v2.3.8 (defect #29): treat 410 Gone as a deterministic deactivated-endpoint\n // signal. Older SDKs may treat any non-2xx as transient and retry; v2.3.8+\n // surfaces it as a structured denial with `reason: 'endpoint_deactivated'`\n // so callers can distinguish \"endpoint gone\" from \"endpoint denied this\".\n if (response.status === 410) {\n return {\n success: true,\n access: {\n allowed: false,\n accessLevel: 'none',\n reason: 'endpoint_deactivated',\n failures: [\n {\n dimension: 'endpoint.deactivated',\n message:\n typeof data?.message === 'string' ? data.message : 'Endpoint has been deactivated',\n guidance:\n typeof data?.guidance === 'string'\n ? data.guidance\n : 'Reactivate via POST /api/endpoints/{id}/reactivate, or update the URL on the calling agent.',\n },\n ],\n },\n };\n }\n\n if (!response.ok) {\n // Round-10 (#47, O5): propagate correlationId on the error path too if\n // the server happened to include one (some 5xx error envelopes carry\n // it). Lets the SDK pass it through to the adapter onDenied handler.\n return {\n success: false,\n error: data.message || data.error || `API returned ${response.status}`,\n correlationId: typeof data?.correlationId === 'string' ? data.correlationId : undefined,\n };\n }\n\n return data;\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n return {\n success: false,\n error: `Failed to call verify-access API: ${message}`,\n };\n }\n}\n\n/**\n * Main verification function\n */\nexport async function verify(\n config: GatewayConfig,\n request: VerificationRequest\n): Promise<VerificationResult> {\n const mergedConfig = { ...DEFAULT_CONFIG, ...config };\n\n // One-time init self-test — fire-and-forget, never blocks verify().\n if (!initCheckPerformed && !mergedConfig.disableInitChecks && mergedConfig.apiBaseUrl) {\n void performInitCheck(mergedConfig.apiBaseUrl, mergedConfig.debug);\n }\n\n // Deprecation warning for v2.3.0 removed config fields. Fires once per process.\n if (\n !deprecationWarningShown &&\n (config.minTrustScore !== undefined || config.minTrustScoreForFull !== undefined)\n ) {\n deprecationWarningShown = true;\n console.warn(\n '[VerificationGateway] minTrustScore / minTrustScoreForFull are deprecated in v2.3.0 ' +\n 'and have no effect. Server is now the single source of truth for access-level decisions ' +\n '(the SDK reads access.accessLevel from the verify-access response). To gate access ' +\n \"to an endpoint, configure the endpoint's trust_score_requirement server-side.\"\n );\n }\n\n // v2.3.0: anonymous traffic no longer short-circuits here. We forward the\n // request to the server with no agentId; the server applies the endpoint's\n // unverifiedAgentPolicy and returns advisory. createGuidanceResponse remains\n // as the offline fallback if the API itself fails (handled below).\n\n // Check cache first\n if (mergedConfig.cacheTtl && mergedConfig.cacheTtl > 0) {\n const cached = getCachedResult(request.credentials);\n if (cached) {\n if (mergedConfig.debug) {\n console.log('[VerificationGateway] Returning cached result');\n }\n return cached;\n }\n }\n\n // Inject counterparty info from config if not already set in request\n const enrichedRequest = { ...request };\n if (!enrichedRequest.counterpartyUrl && mergedConfig.counterpartyUrl) {\n enrichedRequest.counterpartyUrl = mergedConfig.counterpartyUrl;\n }\n if (!enrichedRequest.counterpartyType && mergedConfig.counterpartyType) {\n enrichedRequest.counterpartyType = mergedConfig.counterpartyType;\n }\n\n // Call the API\n if (mergedConfig.debug) {\n console.log('[VerificationGateway] Calling verify-access API');\n }\n\n const apiResponse = await callVerifyAccessAPI(mergedConfig, enrichedRequest);\n\n // Handle API errors\n if (!apiResponse.success) {\n // Round-10 (#47, O5): distinguish API errors from missing-credentials.\n // The previous default tagged any failure with the \"register your\n // agent\" template, which is misleading to a verified partner whose\n // verify-access call hit a 500. The `api_error` source surfaces a\n // typed `verify_access.api_error` failure entry instead.\n return createGuidanceResponse(mergedConfig, apiResponse.error, {\n source: 'api_error',\n correlationId: (apiResponse as { correlationId?: string }).correlationId,\n });\n }\n\n // Check access result\n if (!apiResponse.access?.allowed) {\n // v2.9.8 (defect M1): aggregated failures across every gate that\n // denied. Surface them on the result so the integrator can see every\n // blocker in one go instead of the previous fail-fast cascade.\n const aggregatedFailures = (apiResponse.access as Record<string, unknown> | undefined)\n ?.failures as Array<{ dimension: string; message: string; guidance?: string }> | undefined;\n const result: EnhancedVerificationResult = {\n verified: false,\n // v2.3.9 (defect #30): denials grant `'none'`, NEVER a positive band.\n // Pre-rename this hardcoded `'guidance'`, which conflated with the\n // colocated `guidance: {...}` help-payload object below and let\n // denied requests pass any route gated at `'guidance'` because\n // `hasMinimumAccess('guidance', 'guidance') === true`. Adapters now\n // ALSO short-circuit on `verified === false` before the gate check —\n // belt-and-braces.\n accessLevel: 'none',\n denialReasons:\n aggregatedFailures && aggregatedFailures.length > 0\n ? aggregatedFailures.map((f) => f.message)\n : apiResponse.access?.reason\n ? [apiResponse.access.reason]\n : ['Access denied'],\n failures: aggregatedFailures,\n requiresStepUp: apiResponse.access?.requiresStepUp,\n requiresApproval: apiResponse.access?.requiresApproval,\n guidance: {\n message: apiResponse.access?.reason || 'Access denied by PDLSS policy',\n registrationUrl: `${mergedConfig.apiBaseUrl?.replace('/api', '')}/register`,\n documentationUrl: `${mergedConfig.apiBaseUrl?.replace('/api', '')}/docs/pdlss`,\n },\n verifiedAt: new Date(),\n // Extract sessionId so decisions can be recorded for denials too\n sessionId: (apiResponse as Record<string, unknown>).sessionId as string | undefined,\n // v2.3.10 (defect #34, round-4): anonymous traffic has no session →\n // correlationId is the linking key for paired local_override events.\n correlationId: (apiResponse as Record<string, unknown>).correlationId as string | undefined,\n recommendation: (apiResponse as Record<string, unknown>)\n .recommendation as EnhancedVerificationResult['recommendation'],\n recommendationReasons: (apiResponse as Record<string, unknown>).recommendationReasons as\n | string[]\n | undefined,\n };\n\n return result;\n }\n\n // Build successful result\n const agent: VerifiedAgent | undefined = apiResponse.agent\n ? {\n astraId: apiResponse.agent.astraId,\n name: apiResponse.agent.name,\n trustScore: apiResponse.agent.trustScore,\n trustLevel: getTrustLevel(apiResponse.agent.trustScore),\n blockchainVerified: apiResponse.agent.blockchainStatus === 'verified',\n status: apiResponse.agent.agentStatus as VerifiedAgent['status'],\n }\n : undefined;\n\n const developer: VerifiedDeveloper | undefined = apiResponse.developer\n ? {\n astradId: apiResponse.developer.kyaOwnerId,\n name: apiResponse.developer.fullName,\n trustScore: apiResponse.developer.trustScore || 0,\n verified: apiResponse.developer.identityVerified,\n }\n : undefined;\n\n const organization: VerifiedOrganization | undefined = apiResponse.organization\n ? {\n name: apiResponse.organization.name,\n verified: apiResponse.organization.verified,\n trustScore: apiResponse.organization.trustScore,\n }\n : undefined;\n\n // Verification context — structured \"why\" the merchant gets in v2.2.4+.\n // Carries appliedPolicy (no UUIDs), pdlssCheck summary, dynamic trust score,\n // and policy-driven attestations. Replaces the old over-sharing `pdlss` block.\n const verificationContext = apiResponse.verificationContext;\n\n // Server is the single source of truth for access level. SDK reads\n // apiResponse.access.accessLevel verbatim — no client-side trust-score remap.\n // Fallback to 'standard' if the server response is missing the field (older\n // backend without the v2.3.0 contract); it covers the verified-access case.\n const accessLevel: AccessLevel = apiResponse.access?.accessLevel ?? 'standard';\n\n const result: EnhancedVerificationResult = {\n verified: true,\n accessLevel,\n agent,\n developer,\n organization,\n appliedPolicy: apiResponse.access?.appliedPolicy,\n verificationContext,\n requiresStepUp: apiResponse.access?.requiresStepUp,\n requiresApproval: apiResponse.access?.requiresApproval,\n verifiedAt: new Date(),\n cacheTtl: mergedConfig.cacheTtl,\n // Handshake Protocol v10 enhanced fields (present when backend returns them)\n sessionId: (apiResponse as Record<string, unknown>).sessionId as string | undefined,\n // v2.3.10 (defect #34, round-4): anonymous responses surface correlationId\n // (no session row exists for unverified callers).\n correlationId: (apiResponse as Record<string, unknown>).correlationId as string | undefined,\n runtimeChallenge: (apiResponse as Record<string, unknown>).runtimeChallenge as\n | RuntimeChallengeResult\n | undefined,\n tokenGuidance: (apiResponse as Record<string, unknown>).tokenGuidance as\n | TokenGuidance\n | undefined,\n recommendation: (apiResponse as Record<string, unknown>)\n .recommendation as EnhancedVerificationResult['recommendation'],\n recommendationReasons: (apiResponse as Record<string, unknown>).recommendationReasons as\n | string[]\n | undefined,\n warningHeader: (apiResponse as Record<string, unknown>).warningHeader as\n | { name: string; value: string }\n | undefined,\n };\n\n // Enforce AstraSync recommendation\n if (result.recommendation === 'deny') {\n result.verified = false;\n result.accessLevel = 'none';\n result.denialReasons = result.recommendationReasons || [\n 'Access denied by AstraSync recommendation',\n ];\n if (result.runtimeChallenge) {\n result.guidance = {\n message: `Verification failed: ${result.runtimeChallenge.reason || 'runtime challenge failed'}`,\n registrationUrl: `${mergedConfig.apiBaseUrl?.replace('/api', '')}/register`,\n documentationUrl: `${mergedConfig.apiBaseUrl?.replace('/api', '')}/docs/runtime-challenge`,\n };\n }\n } else if (result.recommendation === 'step_up_required') {\n result.requiresStepUp = true;\n if (ACCESS_LEVEL_HIERARCHY[result.accessLevel] > ACCESS_LEVEL_HIERARCHY['read-only']) {\n result.accessLevel = 'read-only';\n }\n result.denialReasons = result.recommendationReasons || ['Step-up verification required'];\n }\n\n // Cache the result (skip caching denials — agent may fix challenge endpoint and retry)\n if (mergedConfig.cacheTtl && mergedConfig.cacheTtl > 0 && result.recommendation !== 'deny') {\n cacheResult(request.credentials, result, mergedConfig.cacheTtl);\n }\n\n return result;\n}\n\n/**\n * Record a counterparty's grant/deny decision for a verification session.\n * Fire-and-forget — errors are silently swallowed.\n */\n/**\n * v2.3.9 (defect #34): optional override metadata. Set when the SDK's\n * local enforcement (toolGate / methodGate / trustScore floor) rejected\n * a request the SERVER had granted. Backend emits a distinct\n * `verification.local_override` event so the activity feed surfaces the\n * divergence as a separate row.\n */\nexport interface DecisionOverride {\n overriddenBy: 'toolGate' | 'methodGate' | 'trustScore' | 'other';\n toolName?: string;\n requestedLevel?: AccessLevel;\n grantedLevel?: AccessLevel;\n}\n\nexport async function recordDecision(\n config: GatewayConfig,\n sessionId: string,\n decision: 'granted' | 'denied',\n reason?: string,\n override?: DecisionOverride\n): Promise<void> {\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n if (config.apiKey) {\n headers['Authorization'] = `Bearer ${config.apiKey}`;\n headers['X-API-Key'] = config.apiKey;\n }\n\n await fetch(`${config.apiBaseUrl}/agents/verify-access/${sessionId}/decision`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n decision,\n reason,\n ...(override && {\n overriddenBy: override.overriddenBy,\n toolName: override.toolName,\n requestedLevel: override.requestedLevel,\n grantedLevel: override.grantedLevel,\n }),\n }),\n }).catch(() => {\n /* fire-and-forget */\n });\n}\n\n/**\n * v2.3.10 (defect #34, round-4): record a SDK-side local override for an\n * anonymous verify-access response. Anonymous traffic has no session row, so\n * `recordDecision` (above) doesn't apply — but we still need to surface the\n * dashboard-vs-runtime divergence (e.g. server granted with audit warning\n * but local toolGate floor denied) on the activity feed.\n *\n * Backend ties the resulting `verification.local_override` event back to the\n * original `verification.unverified_audit` event via `correlationId`. The\n * endpoint is sessionless — see the docstring on the backend route for the\n * abuse-mitigation rationale (rate-limited per IP).\n *\n * Fire-and-forget — errors are silently swallowed.\n */\nexport async function recordAnonymousLocalOverride(\n config: GatewayConfig,\n correlationId: string,\n override: DecisionOverride,\n reason?: string\n): Promise<void> {\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n if (config.apiKey) {\n headers['Authorization'] = `Bearer ${config.apiKey}`;\n headers['X-API-Key'] = config.apiKey;\n }\n\n await fetch(`${config.apiBaseUrl}/agents/verify-access/local-override`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n correlationId,\n reason,\n overriddenBy: override.overriddenBy,\n toolName: override.toolName,\n requestedLevel: override.requestedLevel,\n grantedLevel: override.grantedLevel,\n }),\n }).catch(() => {\n /* fire-and-forget */\n });\n}\n\n/**\n * Fetch the per-route policy for an endpoint from the AstraSync backend.\n * v2.9.7 moved policy authority into the dashboard — the SDK no longer\n * accepts `routes` from merchant-side source code, it fetches them from\n * here on init (and refreshes periodically).\n *\n * Returns `null` when the request fails for any reason — the caller decides\n * how to fall back (the middleware allows-all when no policy is loaded so\n * a misconfigured init doesn't take down the merchant's API).\n */\nexport async function fetchRoutes(\n config: GatewayConfig,\n counterpartyId: string\n): Promise<RouteAccessConfigShape[] | null> {\n if (!counterpartyId) return null;\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n if (config.apiKey) {\n headers['Authorization'] = `Bearer ${config.apiKey}`;\n headers['X-API-Key'] = config.apiKey;\n }\n try {\n const response = await fetch(\n `${config.apiBaseUrl}/endpoints/${encodeURIComponent(counterpartyId)}/routes`,\n { method: 'GET', headers }\n );\n if (!response.ok) return null;\n const body = (await response.json()) as { data?: { routes?: RouteAccessConfigShape[] } };\n return body.data?.routes ?? [];\n } catch {\n return null;\n }\n}\n\n/**\n * Minimal shape of an EndpointRoute as the SDK consumes it. Mirrors the\n * server's `EndpointRoute` type and the SDK's `RouteAccessConfig` — same\n * JSON moves between server and SDK unchanged.\n */\nexport interface RouteAccessConfigShape {\n pattern: string;\n method: string;\n minAccessLevel: 'none' | 'restricted' | 'read-only' | 'standard' | 'full' | 'internal';\n minTrustScore?: number;\n requiredPurposes?: string[];\n allowedPurposes?: string[];\n allowedJurisdictions?: string[];\n maxDuration?: number;\n maxTransactionValue?: number;\n}\n\n/**\n * Verify an agent AND automatically record the grant/deny decision.\n *\n * This is the recommended entry point for counterparties that call verify()\n * directly (e.g. MCP servers) rather than using createMiddleware().\n * It adds createSession: true, then fire-and-forgets the decision.\n */\nexport async function verifyAndRecord(\n config: GatewayConfig,\n request: VerificationRequest\n): Promise<VerificationResult> {\n const mergedConfig = { ...DEFAULT_CONFIG, ...config };\n const result = await verify(mergedConfig, { ...request, createSession: true });\n const sessionId = (result as EnhancedVerificationResult).sessionId;\n\n if (sessionId) {\n if (result.verified) {\n recordDecision(mergedConfig, sessionId, 'granted').catch(() => {});\n } else {\n recordDecision(mergedConfig, sessionId, 'denied', result.denialReasons?.[0]).catch(() => {});\n }\n }\n\n return result;\n}\n\n/**\n * Report an unregistered agent attempt (no AstraSync credentials).\n * Called by SDK adapters when an agent is redirected to /docs/agent-access.\n * Fire-and-forget — errors are silently swallowed.\n */\nexport async function reportUnregisteredAttempt(\n config: GatewayConfig,\n data: {\n counterpartyUrl: string;\n counterpartyType?: string;\n sourceIp?: string;\n userAgent?: string;\n requestPath?: string;\n requestMethod?: string;\n }\n): Promise<void> {\n const apiBaseUrl = config.apiBaseUrl || DEFAULT_CONFIG.apiBaseUrl!;\n\n await fetch(`${apiBaseUrl}/verification-activity/unregistered-attempt`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(data),\n }).catch(() => {\n /* fire-and-forget */\n });\n}\n\n/**\n * Report a counterparty-side PDLSS pre-check failure.\n * Called by SDK adapters when the agent's requested PDLSS exceeds\n * counterparty-defined maximums BEFORE calling verify-access.\n * Fire-and-forget — errors are silently swallowed.\n */\nexport async function reportCounterpartyPreCheckFailure(\n config: GatewayConfig,\n data: {\n agentId: string;\n counterpartyUrl: string;\n counterpartyType?: string;\n failures: Array<{\n field: string;\n requested: string | number;\n limit: string | number | string[];\n message: string;\n }>;\n requestPath?: string;\n requestMethod?: string;\n }\n): Promise<void> {\n const apiBaseUrl = config.apiBaseUrl || DEFAULT_CONFIG.apiBaseUrl!;\n\n await fetch(`${apiBaseUrl}/verification-activity/counterparty-pre-check-failure`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(data),\n }).catch(() => {\n /* fire-and-forget */\n });\n}\n\n/**\n * Quick verification - just check if credentials are valid\n */\nexport async function quickVerify(\n config: GatewayConfig,\n credentials: AgentCredentials\n): Promise<{ verified: boolean; accessLevel: AccessLevel; reason?: string }> {\n const result = await verify(config, {\n credentials,\n purpose: 'verification',\n });\n\n return {\n verified: result.verified,\n accessLevel: result.accessLevel,\n reason: result.denialReasons?.[0],\n };\n}\n","/**\n * HTTP Transport Adapter\n *\n * Maps AstraSync credentials to/from HTTP headers (X-Astra-* convention).\n */\n\nimport type { AstraSyncCredentials } from '../types';\n\nconst HEADER_PREFIX = 'X-Astra-';\n\n/**\n * Inject AstraSync credentials into HTTP headers.\n */\nexport function setHttpHeaders(\n headers: Record<string, string>,\n credentials: AstraSyncCredentials,\n): Record<string, string> {\n const result = { ...headers };\n\n result[`${HEADER_PREFIX}ID`] = credentials.agentId;\n\n if (credentials.verifyUrl) {\n result[`${HEADER_PREFIX}Verify`] = credentials.verifyUrl;\n }\n\n if (credentials.challengeUrl) {\n result[`${HEADER_PREFIX}Challenge`] = credentials.challengeUrl;\n }\n\n if (credentials.pdlss?.purpose) {\n const purposeValue = credentials.pdlss.purpose.action\n ? `${credentials.pdlss.purpose.category}:${credentials.pdlss.purpose.action}`\n : credentials.pdlss.purpose.category;\n result[`${HEADER_PREFIX}Purpose`] = purposeValue;\n }\n\n if (credentials.pdlss?.duration?.maxSessionDuration) {\n result[`${HEADER_PREFIX}Duration`] = String(credentials.pdlss.duration.maxSessionDuration);\n }\n\n if (credentials.pdlss?.scope?.jurisdiction) {\n result[`${HEADER_PREFIX}Scope`] = credentials.pdlss.scope.jurisdiction;\n }\n\n return result;\n}\n\n/**\n * Extract AstraSync credentials from HTTP headers.\n */\nexport function extractHttpCredentials(\n headers: Record<string, string | string[] | undefined>,\n): AstraSyncCredentials | null {\n const getValue = (key: string): string | undefined => {\n const v = headers[key] ?? headers[key.toLowerCase()];\n return Array.isArray(v) ? v[0] : v;\n };\n\n const agentId = getValue(`${HEADER_PREFIX}ID`) ?? getValue('x-astra-id');\n if (!agentId) return null;\n\n const credentials: AstraSyncCredentials = { agentId };\n\n const verifyUrl = getValue(`${HEADER_PREFIX}Verify`) ?? getValue('x-astra-verify');\n if (verifyUrl) credentials.verifyUrl = verifyUrl;\n\n const challengeUrl = getValue(`${HEADER_PREFIX}Challenge`) ?? getValue('x-astra-challenge');\n if (challengeUrl) credentials.challengeUrl = challengeUrl;\n\n const purpose = getValue(`${HEADER_PREFIX}Purpose`) ?? getValue('x-astra-purpose');\n if (purpose) {\n const [category, action] = purpose.split(':');\n credentials.pdlss = {\n ...credentials.pdlss,\n purpose: { category, action },\n };\n }\n\n const duration = getValue(`${HEADER_PREFIX}Duration`) ?? getValue('x-astra-duration');\n if (duration) {\n credentials.pdlss = {\n ...credentials.pdlss,\n duration: { maxSessionDuration: parseInt(duration, 10) },\n };\n }\n\n const scope = getValue(`${HEADER_PREFIX}Scope`) ?? getValue('x-astra-scope');\n if (scope) {\n credentials.pdlss = {\n ...credentials.pdlss,\n scope: { jurisdiction: scope },\n };\n }\n\n return credentials;\n}\n","/**\n * Counterparty-side PDLSS pre-check.\n *\n * Compares the agent's requested PDLSS dimensions (from X-Astra-* headers)\n * against the counterparty-defined maximums on the route config.\n * Returns an array of failures — empty means all checks passed.\n *\n * This runs BEFORE calling verify-access on AstraSync. If it fails,\n * the request is rejected immediately without calling the platform.\n */\n\nimport type { RouteAccessConfig, AstraSyncCredentials, CounterpartyPreCheckFailure } from './types';\n\nexport function performCounterpartyPreCheck(\n routeConfig: RouteAccessConfig,\n astraCreds: AstraSyncCredentials | null,\n purpose: string | undefined,\n): CounterpartyPreCheckFailure[] {\n const failures: CounterpartyPreCheckFailure[] = [];\n\n // Check purpose against allowedPurposes whitelist\n if (routeConfig.allowedPurposes && routeConfig.allowedPurposes.length > 0 && purpose) {\n if (!routeConfig.allowedPurposes.includes(purpose)) {\n failures.push({\n field: 'purpose',\n requested: purpose,\n limit: routeConfig.allowedPurposes,\n message: `Purpose \"${purpose}\" is not in the allowed list: [${routeConfig.allowedPurposes.join(', ')}]`,\n });\n }\n }\n\n // Check purpose against requiredPurposes (legacy field — agent must declare one of these)\n if (routeConfig.requiredPurposes && routeConfig.requiredPurposes.length > 0 && purpose) {\n if (!routeConfig.requiredPurposes.includes(purpose)) {\n failures.push({\n field: 'purpose',\n requested: purpose,\n limit: routeConfig.requiredPurposes,\n message: `Purpose \"${purpose}\" is not in the required list: [${routeConfig.requiredPurposes.join(', ')}]`,\n });\n }\n }\n\n // Check duration against maxDuration\n if (routeConfig.maxDuration && astraCreds?.pdlss?.duration?.maxSessionDuration) {\n const requested = astraCreds.pdlss.duration.maxSessionDuration;\n if (requested > routeConfig.maxDuration) {\n failures.push({\n field: 'duration',\n requested,\n limit: routeConfig.maxDuration,\n message: `Requested duration ${requested}s exceeds maximum ${routeConfig.maxDuration}s`,\n });\n }\n }\n\n // Check jurisdiction against allowedJurisdictions\n if (\n routeConfig.allowedJurisdictions &&\n routeConfig.allowedJurisdictions.length > 0 &&\n astraCreds?.pdlss?.scope?.jurisdiction\n ) {\n const requested = astraCreds.pdlss.scope.jurisdiction;\n if (!routeConfig.allowedJurisdictions.includes(requested)) {\n failures.push({\n field: 'jurisdiction',\n requested,\n limit: routeConfig.allowedJurisdictions,\n message: `Jurisdiction \"${requested}\" is not in the allowed list: [${routeConfig.allowedJurisdictions.join(', ')}]`,\n });\n }\n }\n\n return failures;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACeO,IAAM,yBAAsD;AAAA,EACjE,MAAM;AAAA,EACN,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,UAAU;AAAA,EACV,MAAM;AAAA,EACN,UAAU;AACZ;AAuCO,SAAS,cAAc,OAA2B;AACvD,MAAI,SAAS,GAAI,QAAO;AACxB,MAAI,SAAS,GAAI,QAAO;AACxB,MAAI,SAAS,GAAI,QAAO;AACxB,SAAO;AACT;AAKO,SAAS,iBAAiB,QAAqB,UAAgC;AACpF,SAAO,uBAAuB,MAAM,KAAK,uBAAuB,QAAQ;AAC1E;;;AC1CA,IAAM,iBAAyC;AAAA,EAC7C,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA,EAKZ,oBAAoB;AAAA;AAAA,EAEpB,UAAU;AAAA;AAAA,EACV,OAAO;AACT;AAOA,IAAI,qBAAqB;AAGzB,IAAI,0BAA0B;AAE9B,eAAe,iBAAiB,YAAoB,OAAgC;AAClF,uBAAqB;AACrB,MAAI;AACF,UAAM,WAAW,GAAG,UAAU;AAK9B,UAAM,WAAW,MAAM,MAAM,UAAU,EAAE,QAAQ,OAAO,CAAC;AACzD,UAAM,cAAc,SAAS,QAAQ,IAAI,cAAc,KAAK;AAC5D,QAAI,YAAY,WAAW,WAAW,GAAG;AACvC,cAAQ;AAAA,QACN,qCAAqC,UAAU,kCAAkC,WAAW;AAAA,MAI9F;AAAA,IACF,WAAW,OAAO;AAChB,cAAQ;AAAA,QACN,+CAA+C,UAAU,mBAAmB,WAAW;AAAA,MACzF;AAAA,IACF;AAAA,EACF,SAAS,KAAK;AACZ,QAAI,OAAO;AACT,cAAQ,IAAI,2DAA2D,OAAO,GAAG,CAAC,EAAE;AAAA,IACtF;AAAA,EACF;AACF;AAKA,IAAM,oBAAoB,oBAAI,IAA+D;AAK7F,SAAS,YAAY,aAAuC;AAC1D,SAAO,GAAG,YAAY,WAAW,EAAE,IAAI,YAAY,UAAU,EAAE,IAAI,YAAY,OAAO,EAAE;AAC1F;AAKA,SAAS,gBAAgB,aAA0D;AACjF,QAAM,MAAM,YAAY,WAAW;AACnC,QAAM,SAAS,kBAAkB,IAAI,GAAG;AAExC,MAAI,UAAU,OAAO,YAAY,KAAK,IAAI,GAAG;AAC3C,WAAO,OAAO;AAAA,EAChB;AAEA,MAAI,QAAQ;AACV,sBAAkB,OAAO,GAAG;AAAA,EAC9B;AAEA,SAAO;AACT;AAKA,SAAS,YACP,aACA,QACA,YACM;AACN,QAAM,MAAM,YAAY,WAAW;AACnC,oBAAkB,IAAI,KAAK;AAAA,IACzB;AAAA,IACA,WAAW,KAAK,IAAI,IAAI,aAAa;AAAA,EACvC,CAAC;AACH;AAwFA,SAAS,uBACP,QACA,QACA,UAA+D,CAAC,GAC5C;AACpB,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,aAAa,WAAW;AAE9B,QAAM,WAAyB,aAC3B;AAAA,IACE,SACE;AAAA,IACF,iBAAiB,GAAG,OAAO,WAAW,QAAQ,QAAQ,EAAE,CAAC;AAAA,IACzD,kBAAkB,GAAG,OAAO,WAAW,QAAQ,QAAQ,EAAE,CAAC;AAAA,IAC1D,OAAO;AAAA,MACL;AAAA,MACA;AAAA,IACF;AAAA,EACF,IACA;AAAA,IACE,SACE;AAAA,IACF,iBAAiB,GAAG,OAAO,WAAW,QAAQ,QAAQ,EAAE,CAAC;AAAA,IACzD,kBAAkB,GAAG,OAAO,WAAW,QAAQ,QAAQ,EAAE,CAAC;AAAA,IAC1D,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEJ,SAAO;AAAA,IACL,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMV,aAAa;AAAA,IACb;AAAA,IACA,eAAe,SAAS,CAAC,MAAM,IAAI,CAAC,qCAAqC;AAAA;AAAA;AAAA;AAAA;AAAA,IAKzE,UAAU,aACN;AAAA,MACE;AAAA,QACE,WAAW;AAAA,QACX,SAAS,UAAU;AAAA,QACnB,UAAU,SAAS;AAAA,MACrB;AAAA,IACF,IACA;AAAA,IACJ,eAAe,QAAQ;AAAA,IACvB,YAAY,oBAAI,KAAK;AAAA,EACvB;AACF;AAKA,eAAe,oBACb,QACA,SAqFC;AACD,QAAM,EAAE,aAAa,GAAG,YAAY,IAAI;AAIxC,QAAM,OAAgC;AAAA,IACpC,GAAI,YAAY,WAAW,EAAE,SAAS,YAAY,QAAQ;AAAA,IAC1D,SAAS,YAAY,WAAW;AAAA,EAClC;AAGA,MAAI,YAAY,OAAQ,MAAK,SAAS,YAAY;AAClD,MAAI,YAAY,aAAc,MAAK,eAAe,YAAY;AAC9D,MAAI,YAAY,SAAU,MAAK,WAAW,YAAY;AACtD,MAAI,YAAY,aAAc,MAAK,eAAe,YAAY;AAC9D,MAAI,YAAY,iBAAkB,MAAK,mBAAmB,YAAY;AACtE,MAAI,YAAY,SAAU,MAAK,WAAW,YAAY;AACtD,MAAI,YAAY,kBAAmB,MAAK,oBAAoB,YAAY;AACxE,MAAI,YAAY,cAAe,MAAK,gBAAgB,YAAY;AAChE,MAAI,YAAY,kBAAkB,OAAW,MAAK,gBAAgB,YAAY;AAE9E,MAAI,YAAY;AACd,SAAK,yBAAyB,YAAY;AAC5C,MAAI,YAAY,cAAe,MAAK,gBAAgB,YAAY;AAChE,MAAI,YAAY,iBAAkB,MAAK,mBAAmB,YAAY;AACtE,MAAI,YAAY,iBAAkB,MAAK,mBAAmB,YAAY;AACtE,MAAI,YAAY,gBAAiB,MAAK,kBAAkB,YAAY;AACpE,MAAI,OAAO,eAAgB,MAAK,iBAAiB,OAAO;AACxD,MAAI,YAAY;AACd,SAAK,0BAA0B,YAAY;AAI7C,MAAI,YAAY,kBAAkB,YAAY,YAAY,YAAY,WAAW;AAC/E,UAAM,OAAO;AAAA,MACX,GAAI,YAAY,YAAY,EAAE,UAAU,YAAY,SAAS;AAAA,MAC7D,GAAI,YAAY,aAAa,EAAE,WAAW,YAAY,UAAU;AAAA,MAChE,GAAG,YAAY;AAAA,IACjB;AACA,QAAI,OAAO,KAAK,IAAI,EAAE,SAAS,EAAG,MAAK,iBAAiB;AAAA,EAC1D;AAGA,QAAM,UAAkC;AAAA,IACtC,gBAAgB;AAAA,IAChB,GAAG,OAAO;AAAA,EACZ;AAKA,MAAI,YAAY,qBAAqB;AACnC,YAAQ,eAAe,IAAI,YAAY;AAAA,EACzC,WAAW,OAAO,QAAQ;AACxB,YAAQ,eAAe,IAAI,UAAU,OAAO,MAAM;AAAA,EACpD;AAEA,MAAI,OAAO,QAAQ;AACjB,YAAQ,WAAW,IAAI,OAAO;AAAA,EAChC;AAEA,MAAI;AACF,UAAM,WAAW,MAAM,MAAM,GAAG,OAAO,UAAU,yBAAyB;AAAA,MACxE,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,IAAI;AAAA,IAC3B,CAAC;AAED,UAAM,OAAO,MAAM,SAAS,KAAK;AAMjC,QAAI,SAAS,WAAW,KAAK;AAC3B,aAAO;AAAA,QACL,SAAS;AAAA,QACT,QAAQ;AAAA,UACN,SAAS;AAAA,UACT,aAAa;AAAA,UACb,QAAQ;AAAA,UACR,UAAU;AAAA,YACR;AAAA,cACE,WAAW;AAAA,cACX,SACE,OAAO,MAAM,YAAY,WAAW,KAAK,UAAU;AAAA,cACrD,UACE,OAAO,MAAM,aAAa,WACtB,KAAK,WACL;AAAA,YACR;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,QAAI,CAAC,SAAS,IAAI;AAIhB,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO,KAAK,WAAW,KAAK,SAAS,gBAAgB,SAAS,MAAM;AAAA,QACpE,eAAe,OAAO,MAAM,kBAAkB,WAAW,KAAK,gBAAgB;AAAA,MAChF;AAAA,IACF;AAEA,WAAO;AAAA,EACT,SAAS,OAAO;AACd,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU;AACzD,WAAO;AAAA,MACL,SAAS;AAAA,MACT,OAAO,qCAAqC,OAAO;AAAA,IACrD;AAAA,EACF;AACF;AAKA,eAAsB,OACpB,QACA,SAC6B;AAC7B,QAAM,eAAe,EAAE,GAAG,gBAAgB,GAAG,OAAO;AAGpD,MAAI,CAAC,sBAAsB,CAAC,aAAa,qBAAqB,aAAa,YAAY;AACrF,SAAK,iBAAiB,aAAa,YAAY,aAAa,KAAK;AAAA,EACnE;AAGA,MACE,CAAC,4BACA,OAAO,kBAAkB,UAAa,OAAO,yBAAyB,SACvE;AACA,8BAA0B;AAC1B,YAAQ;AAAA,MACN;AAAA,IAIF;AAAA,EACF;AAQA,MAAI,aAAa,YAAY,aAAa,WAAW,GAAG;AACtD,UAAM,SAAS,gBAAgB,QAAQ,WAAW;AAClD,QAAI,QAAQ;AACV,UAAI,aAAa,OAAO;AACtB,gBAAQ,IAAI,+CAA+C;AAAA,MAC7D;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAGA,QAAM,kBAAkB,EAAE,GAAG,QAAQ;AACrC,MAAI,CAAC,gBAAgB,mBAAmB,aAAa,iBAAiB;AACpE,oBAAgB,kBAAkB,aAAa;AAAA,EACjD;AACA,MAAI,CAAC,gBAAgB,oBAAoB,aAAa,kBAAkB;AACtE,oBAAgB,mBAAmB,aAAa;AAAA,EAClD;AAGA,MAAI,aAAa,OAAO;AACtB,YAAQ,IAAI,iDAAiD;AAAA,EAC/D;AAEA,QAAM,cAAc,MAAM,oBAAoB,cAAc,eAAe;AAG3E,MAAI,CAAC,YAAY,SAAS;AAMxB,WAAO,uBAAuB,cAAc,YAAY,OAAO;AAAA,MAC7D,QAAQ;AAAA,MACR,eAAgB,YAA2C;AAAA,IAC7D,CAAC;AAAA,EACH;AAGA,MAAI,CAAC,YAAY,QAAQ,SAAS;AAIhC,UAAM,qBAAsB,YAAY,QACpC;AACJ,UAAMA,UAAqC;AAAA,MACzC,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAQV,aAAa;AAAA,MACb,eACE,sBAAsB,mBAAmB,SAAS,IAC9C,mBAAmB,IAAI,CAAC,MAAM,EAAE,OAAO,IACvC,YAAY,QAAQ,SAClB,CAAC,YAAY,OAAO,MAAM,IAC1B,CAAC,eAAe;AAAA,MACxB,UAAU;AAAA,MACV,gBAAgB,YAAY,QAAQ;AAAA,MACpC,kBAAkB,YAAY,QAAQ;AAAA,MACtC,UAAU;AAAA,QACR,SAAS,YAAY,QAAQ,UAAU;AAAA,QACvC,iBAAiB,GAAG,aAAa,YAAY,QAAQ,QAAQ,EAAE,CAAC;AAAA,QAChE,kBAAkB,GAAG,aAAa,YAAY,QAAQ,QAAQ,EAAE,CAAC;AAAA,MACnE;AAAA,MACA,YAAY,oBAAI,KAAK;AAAA;AAAA,MAErB,WAAY,YAAwC;AAAA;AAAA;AAAA,MAGpD,eAAgB,YAAwC;AAAA,MACxD,gBAAiB,YACd;AAAA,MACH,uBAAwB,YAAwC;AAAA,IAGlE;AAEA,WAAOA;AAAA,EACT;AAGA,QAAM,QAAmC,YAAY,QACjD;AAAA,IACE,SAAS,YAAY,MAAM;AAAA,IAC3B,MAAM,YAAY,MAAM;AAAA,IACxB,YAAY,YAAY,MAAM;AAAA,IAC9B,YAAY,cAAc,YAAY,MAAM,UAAU;AAAA,IACtD,oBAAoB,YAAY,MAAM,qBAAqB;AAAA,IAC3D,QAAQ,YAAY,MAAM;AAAA,EAC5B,IACA;AAEJ,QAAM,YAA2C,YAAY,YACzD;AAAA,IACE,UAAU,YAAY,UAAU;AAAA,IAChC,MAAM,YAAY,UAAU;AAAA,IAC5B,YAAY,YAAY,UAAU,cAAc;AAAA,IAChD,UAAU,YAAY,UAAU;AAAA,EAClC,IACA;AAEJ,QAAM,eAAiD,YAAY,eAC/D;AAAA,IACE,MAAM,YAAY,aAAa;AAAA,IAC/B,UAAU,YAAY,aAAa;AAAA,IACnC,YAAY,YAAY,aAAa;AAAA,EACvC,IACA;AAKJ,QAAM,sBAAsB,YAAY;AAMxC,QAAM,cAA2B,YAAY,QAAQ,eAAe;AAEpE,QAAM,SAAqC;AAAA,IACzC,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,eAAe,YAAY,QAAQ;AAAA,IACnC;AAAA,IACA,gBAAgB,YAAY,QAAQ;AAAA,IACpC,kBAAkB,YAAY,QAAQ;AAAA,IACtC,YAAY,oBAAI,KAAK;AAAA,IACrB,UAAU,aAAa;AAAA;AAAA,IAEvB,WAAY,YAAwC;AAAA;AAAA;AAAA,IAGpD,eAAgB,YAAwC;AAAA,IACxD,kBAAmB,YAAwC;AAAA,IAG3D,eAAgB,YAAwC;AAAA,IAGxD,gBAAiB,YACd;AAAA,IACH,uBAAwB,YAAwC;AAAA,IAGhE,eAAgB,YAAwC;AAAA,EAG1D;AAGA,MAAI,OAAO,mBAAmB,QAAQ;AACpC,WAAO,WAAW;AAClB,WAAO,cAAc;AACrB,WAAO,gBAAgB,OAAO,yBAAyB;AAAA,MACrD;AAAA,IACF;AACA,QAAI,OAAO,kBAAkB;AAC3B,aAAO,WAAW;AAAA,QAChB,SAAS,wBAAwB,OAAO,iBAAiB,UAAU,0BAA0B;AAAA,QAC7F,iBAAiB,GAAG,aAAa,YAAY,QAAQ,QAAQ,EAAE,CAAC;AAAA,QAChE,kBAAkB,GAAG,aAAa,YAAY,QAAQ,QAAQ,EAAE,CAAC;AAAA,MACnE;AAAA,IACF;AAAA,EACF,WAAW,OAAO,mBAAmB,oBAAoB;AACvD,WAAO,iBAAiB;AACxB,QAAI,uBAAuB,OAAO,WAAW,IAAI,uBAAuB,WAAW,GAAG;AACpF,aAAO,cAAc;AAAA,IACvB;AACA,WAAO,gBAAgB,OAAO,yBAAyB,CAAC,+BAA+B;AAAA,EACzF;AAGA,MAAI,aAAa,YAAY,aAAa,WAAW,KAAK,OAAO,mBAAmB,QAAQ;AAC1F,gBAAY,QAAQ,aAAa,QAAQ,aAAa,QAAQ;AAAA,EAChE;AAEA,SAAO;AACT;AAuGA,eAAsB,YACpB,QACA,gBAC0C;AAC1C,MAAI,CAAC,eAAgB,QAAO;AAC5B,QAAM,UAAkC,EAAE,gBAAgB,mBAAmB;AAC7E,MAAI,OAAO,QAAQ;AACjB,YAAQ,eAAe,IAAI,UAAU,OAAO,MAAM;AAClD,YAAQ,WAAW,IAAI,OAAO;AAAA,EAChC;AACA,MAAI;AACF,UAAM,WAAW,MAAM;AAAA,MACrB,GAAG,OAAO,UAAU,cAAc,mBAAmB,cAAc,CAAC;AAAA,MACpE,EAAE,QAAQ,OAAO,QAAQ;AAAA,IAC3B;AACA,QAAI,CAAC,SAAS,GAAI,QAAO;AACzB,UAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,WAAO,KAAK,MAAM,UAAU,CAAC;AAAA,EAC/B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AA8EA,eAAsB,kCACpB,QACA,MAae;AACf,QAAM,aAAa,OAAO,cAAc,eAAe;AAEvD,QAAM,MAAM,GAAG,UAAU,yDAAyD;AAAA,IAChF,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,IAC9C,MAAM,KAAK,UAAU,IAAI;AAAA,EAC3B,CAAC,EAAE,MAAM,MAAM;AAAA,EAEf,CAAC;AACH;;;ACx5BA,IAAM,gBAAgB;AA0Cf,SAAS,uBACd,SAC6B;AAC7B,QAAM,WAAW,CAAC,QAAoC;AACpD,UAAM,IAAI,QAAQ,GAAG,KAAK,QAAQ,IAAI,YAAY,CAAC;AACnD,WAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI;AAAA,EACnC;AAEA,QAAM,UAAU,SAAS,GAAG,aAAa,IAAI,KAAK,SAAS,YAAY;AACvE,MAAI,CAAC,QAAS,QAAO;AAErB,QAAM,cAAoC,EAAE,QAAQ;AAEpD,QAAM,YAAY,SAAS,GAAG,aAAa,QAAQ,KAAK,SAAS,gBAAgB;AACjF,MAAI,UAAW,aAAY,YAAY;AAEvC,QAAM,eAAe,SAAS,GAAG,aAAa,WAAW,KAAK,SAAS,mBAAmB;AAC1F,MAAI,aAAc,aAAY,eAAe;AAE7C,QAAM,UAAU,SAAS,GAAG,aAAa,SAAS,KAAK,SAAS,iBAAiB;AACjF,MAAI,SAAS;AACX,UAAM,CAAC,UAAU,MAAM,IAAI,QAAQ,MAAM,GAAG;AAC5C,gBAAY,QAAQ;AAAA,MAClB,GAAG,YAAY;AAAA,MACf,SAAS,EAAE,UAAU,OAAO;AAAA,IAC9B;AAAA,EACF;AAEA,QAAM,WAAW,SAAS,GAAG,aAAa,UAAU,KAAK,SAAS,kBAAkB;AACpF,MAAI,UAAU;AACZ,gBAAY,QAAQ;AAAA,MAClB,GAAG,YAAY;AAAA,MACf,UAAU,EAAE,oBAAoB,SAAS,UAAU,EAAE,EAAE;AAAA,IACzD;AAAA,EACF;AAEA,QAAM,QAAQ,SAAS,GAAG,aAAa,OAAO,KAAK,SAAS,eAAe;AAC3E,MAAI,OAAO;AACT,gBAAY,QAAQ;AAAA,MAClB,GAAG,YAAY;AAAA,MACf,OAAO,EAAE,cAAc,MAAM;AAAA,IAC/B;AAAA,EACF;AAEA,SAAO;AACT;;;AClFO,SAAS,4BACd,aACA,YACA,SAC+B;AAC/B,QAAM,WAA0C,CAAC;AAGjD,MAAI,YAAY,mBAAmB,YAAY,gBAAgB,SAAS,KAAK,SAAS;AACpF,QAAI,CAAC,YAAY,gBAAgB,SAAS,OAAO,GAAG;AAClD,eAAS,KAAK;AAAA,QACZ,OAAO;AAAA,QACP,WAAW;AAAA,QACX,OAAO,YAAY;AAAA,QACnB,SAAS,YAAY,OAAO,kCAAkC,YAAY,gBAAgB,KAAK,IAAI,CAAC;AAAA,MACtG,CAAC;AAAA,IACH;AAAA,EACF;AAGA,MAAI,YAAY,oBAAoB,YAAY,iBAAiB,SAAS,KAAK,SAAS;AACtF,QAAI,CAAC,YAAY,iBAAiB,SAAS,OAAO,GAAG;AACnD,eAAS,KAAK;AAAA,QACZ,OAAO;AAAA,QACP,WAAW;AAAA,QACX,OAAO,YAAY;AAAA,QACnB,SAAS,YAAY,OAAO,mCAAmC,YAAY,iBAAiB,KAAK,IAAI,CAAC;AAAA,MACxG,CAAC;AAAA,IACH;AAAA,EACF;AAGA,MAAI,YAAY,eAAe,YAAY,OAAO,UAAU,oBAAoB;AAC9E,UAAM,YAAY,WAAW,MAAM,SAAS;AAC5C,QAAI,YAAY,YAAY,aAAa;AACvC,eAAS,KAAK;AAAA,QACZ,OAAO;AAAA,QACP;AAAA,QACA,OAAO,YAAY;AAAA,QACnB,SAAS,sBAAsB,SAAS,qBAAqB,YAAY,WAAW;AAAA,MACtF,CAAC;AAAA,IACH;AAAA,EACF;AAGA,MACE,YAAY,wBACZ,YAAY,qBAAqB,SAAS,KAC1C,YAAY,OAAO,OAAO,cAC1B;AACA,UAAM,YAAY,WAAW,MAAM,MAAM;AACzC,QAAI,CAAC,YAAY,qBAAqB,SAAS,SAAS,GAAG;AACzD,eAAS,KAAK;AAAA,QACZ,OAAO;AAAA,QACP;AAAA,QACA,OAAO,YAAY;AAAA,QACnB,SAAS,iBAAiB,SAAS,kCAAkC,YAAY,qBAAqB,KAAK,IAAI,CAAC;AAAA,MAClH,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AACT;;;AJhCA,SAAS,kCAAkC,SAAwC;AACjF,QAAM,cAAgC,CAAC;AAGvC,QAAM,UAAU,QAAQ,QAAQ,IAAI,YAAY,KAAK,QAAQ,QAAQ,IAAI,YAAY;AACrF,MAAI,SAAS;AACX,gBAAY,UAAU;AAAA,EACxB;AAGA,QAAM,SAAS,QAAQ,QAAQ,IAAI,WAAW,KAAK,QAAQ,QAAQ,IAAI,WAAW;AAClF,MAAI,QAAQ;AACV,gBAAY,SAAS;AAAA,EACvB;AAGA,QAAM,aAAa,QAAQ,QAAQ,IAAI,eAAe;AACtD,MAAI,YAAY;AACd,gBAAY,sBAAsB;AAClC,QAAI,WAAW,WAAW,SAAS,GAAG;AACpC,kBAAY,MAAM,WAAW,MAAM,CAAC;AAAA,IACtC;AAAA,EACF;AAGA,QAAM,MAAM,IAAI,IAAI,QAAQ,GAAG;AAC/B,QAAM,eAAe,IAAI,aAAa,IAAI,SAAS;AACnD,QAAM,cAAc,IAAI,aAAa,IAAI,QAAQ;AAEjD,MAAI,gBAAgB,CAAC,YAAY,SAAS;AACxC,gBAAY,UAAU;AAAA,EACxB;AACA,MAAI,eAAe,CAAC,YAAY,QAAQ;AACtC,gBAAY,SAAS;AAAA,EACvB;AAEA,SAAO;AACT;AAKA,SAAS,WAAW,SAAiB,MAAuB;AAC1D,QAAM,eAAe,QAAQ,QAAQ,OAAO,IAAI,EAAE,QAAQ,OAAO,KAAK;AAEtE,QAAM,QAAQ,IAAI,OAAO,IAAI,YAAY,GAAG;AAC5C,SAAO,MAAM,KAAK,IAAI;AACxB;AAKA,SAAS,gBACP,QACA,MACA,QAC+B;AAC/B,SAAO,OAAO,KAAK,CAAC,UAAU;AAC5B,UAAM,gBACJ,MAAM,WAAW,OAAO,MAAM,OAAO,YAAY,MAAM,OAAO,YAAY;AAC5E,UAAM,cAAc,WAAW,MAAM,SAAS,IAAI;AAClD,WAAO,iBAAiB;AAAA,EAC1B,CAAC;AACH;AAMA,SAAS,2CACP,SAC6B;AAC7B,QAAM,UAAkC,CAAC;AACzC,UAAQ,QAAQ,QAAQ,CAAC,OAAO,QAAQ;AACtC,YAAQ,GAAG,IAAI;AAAA,EACjB,CAAC;AACD,SAAO,uBAAuB,OAAO;AACvC;AAUA,SAAS,eAAe,SAA8B;AAEpD,QAAM,eAAe,QAAQ,QAAQ,IAAI,iBAAiB;AAC1D,MAAI,cAAc;AAEhB,WAAO,aAAa,MAAM,GAAG,EAAE,CAAC;AAAA,EAClC;AAGA,QAAM,gBAAgB,QAAQ,QAAQ,IAAI,WAAW;AACrD,MAAI,eAAe;AACjB,WAAO;AAAA,EACT;AAGA,UAAQ,QAAQ,OAAO,YAAY,GAAG;AAAA,IACpC,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAKA,SAAS,2BACP,QACA,SACQ;AACR,QAAM,QAAQ,QAAQ,gBAAgB,SAAS;AAC/C,QAAM,UACJ,QAAQ,gBAAgB,WACxB,OAAO,UAAU,WACjB;AACF,QAAM,kBAAkB,OAAO,UAAU,mBAAmB;AAC5D,QAAM,UAAU,OAAO,UAAU,oBAAoB;AACrD,QAAM,aAAa,QAAQ,gBAAgB,oBAAoB;AAE/D,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,WAME,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,+BA4Ge,KAAK;AAAA,gCACJ,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA,mCAKJ,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,iBAQjC,eAAe;AAAA,QACxB,aAAa,8GAA8G,EAAE;AAAA;AAAA;AAAA;AAAA,6BAIxG,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA,IAKhC,KAAK;AACT;AAEA,IAAM,4BAA4B,IAAI,KAAK;AAWpC,SAAS,iBAAiB,SAAkC;AACjE,QAAM;AAAA,IACJ,YAAY,CAAC;AAAA,IACb,qBAAqB;AAAA,IACrB,yBAAyB;AAAA,IACzB,kBAAkB;AAAA,IAClB,GAAG;AAAA,EACL,IAAI;AAEJ,MAAI,eAAoC,CAAC;AACzC,MAAI,cAAc;AAClB,MAAI,aAAmC;AACvC,MAAI,uBAAuB;AAE3B,iBAAe,gBAA+B;AAC5C,QAAI,CAAC,OAAO,gBAAgB;AAC1B,UAAI,CAAC,sBAAsB;AAEzB,gBAAQ;AAAA,UACN;AAAA,QAGF;AACA,+BAAuB;AAAA,MACzB;AACA;AAAA,IACF;AACA,UAAM,UAAU,MAAM,YAAY,QAAQ,OAAO,cAAc;AAC/D,QAAI,SAAS;AACX,qBAAe;AACf,oBAAc,KAAK,IAAI;AAAA,IACzB;AAAA,EACF;AAEA,eAAa,cAAc,EAAE,QAAQ,MAAM;AACzC,iBAAa;AAAA,EACf,CAAC;AAED,SAAO,eAAe,WAAW,SAAsB;AAErD,UAAM,EAAE,aAAa,IAAI,MAAM,OAAO,aAAa;AAEnD,UAAM,WAAW,QAAQ,QAAQ;AAGjC,UAAM,aAAa,UAAU,KAAK,CAAC,YAAY,WAAW,SAAS,QAAQ,CAAC;AAC5E,QAAI,YAAY;AACd,aAAO,aAAa,KAAK;AAAA,IAC3B;AAEA,QAAI,YAAY;AACd,YAAM,WAAW,MAAM,MAAM;AAAA,MAAC,CAAC;AAAA,IACjC;AACA,QAAI,OAAO,kBAAkB,KAAK,IAAI,IAAI,cAAc,iBAAiB;AACvE,mBAAa,cAAc,EAAE,QAAQ,MAAM;AACzC,qBAAa;AAAA,MACf,CAAC;AAAA,IACH;AAGA,UAAM,cAAc,gBAAgB,cAAc,UAAU,QAAQ,MAAM;AAG1E,QAAI,CAAC,aAAa;AAChB,aAAO,aAAa,KAAK;AAAA,IAC3B;AAGA,QAAI,YAAY,mBAAmB,QAAQ;AACzC,aAAO,aAAa,KAAK;AAAA,IAC3B;AAGA,UAAM,cAAc,kCAAkC,OAAO;AAS7D,UAAM,kBAAkB,OAAO,mBAAmB,QAAQ,QAAQ;AAGlE,UAAM,UAAU,eAAe,OAAO;AACtC,UAAM,aAAa,2CAA2C,OAAO;AAKrE,UAAM,mBAAmB,4BAA4B,aAAa,YAAY,OAAO;AACrF,QAAI,iBAAiB,SAAS,GAAG;AAC/B,YAAM,iBAAqC;AAAA,QACzC,UAAU;AAAA,QACV,aAAa;AAAA,QACb,eAAe,iBAAiB,IAAI,CAAC,MAAM,EAAE,OAAO;AAAA,QACpD,UAAU;AAAA,UACR,SAAS;AAAA,UACT,iBAAiB,GAAG,OAAO,YAAY,QAAQ,QAAQ,EAAE,CAAC;AAAA,UAC1D,kBAAkB,GAAG,OAAO,YAAY,QAAQ,QAAQ,EAAE,CAAC;AAAA,QAC7D;AAAA,QACA,YAAY,oBAAI,KAAK;AAAA,MACvB;AAGA,wCAAkC,QAAQ;AAAA,QACxC,SAAS,YAAY,WAAW,YAAY,WAAW;AAAA,QACvD;AAAA,QACA,kBAAkB,OAAO,oBAAoB;AAAA,QAC7C,UAAU;AAAA,QACV,aAAa;AAAA,QACb,eAAe,QAAQ;AAAA,MACzB,CAAC,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAGjB,UAAI,SAAS,WAAW,OAAO,GAAG;AAChC,eAAO,aAAa;AAAA,UAClB;AAAA,YACE,SAAS;AAAA,YACT,OAAO;AAAA,cACL,MAAM;AAAA,cACN,SAAS,eAAe,gBAAgB,CAAC,KAAK;AAAA,cAC9C,UAAU,eAAe;AAAA,YAC3B;AAAA,UACF;AAAA,UACA,EAAE,QAAQ,IAAI;AAAA,QAChB;AAAA,MACF;AAGA,UAAI,oBAAoB;AACtB,eAAO,IAAI,aAAa,2BAA2B,gBAAgB,OAAO,GAAG;AAAA,UAC3E,QAAQ;AAAA,UACR,SAAS;AAAA,YACP,gBAAgB;AAAA,YAChB,4BAA4B;AAAA,UAC9B;AAAA,QACF,CAAC;AAAA,MACH;AAEA,aAAO,aAAa,SAAS,IAAI,IAAI,iBAAiB,QAAQ,GAAG,CAAC;AAAA,IACpE;AAGA,UAAM,eAAe,QAAQ,QAAQ,IAAI,iBAAiB,KAAK;AAC/D,UAAM,mBAAmB,cAAc,MAAM,GAAG,EAAE,CAAC,GAAG,KAAK;AAC3D,UAAM,SAAS,MAAM,OAAO,QAAQ;AAAA,MAClC;AAAA,MACA;AAAA,MACA,QAAQ,QAAQ,OAAO,YAAY;AAAA,MACnC,UAAU;AAAA,MACV;AAAA,MACA,kBAAkB,OAAO,oBAAoB;AAAA,MAC7C;AAAA,MACA,kBAAkB,YAAY,OAAO,UAAU;AAAA,MAC/C,gBAAgB;AAAA,QACd,UAAU;AAAA,QACV,WAAW,QAAQ,QAAQ,IAAI,YAAY,KAAK;AAAA,QAChD,SAAS,QAAQ,QAAQ,IAAI,SAAS,KAAK;AAAA,QAC3C,MAAM,QAAQ,QAAQ,IAAI,MAAM,KAAK;AAAA,QACrC;AAAA,QACA,cAAc,QAAQ,QAAQ,IAAI,wBAAwB,KAAK;AAAA,MACjE;AAAA,IACF,CAAC;AAQD,QAAI,CAAC,OAAO,YAAY,CAAC,iBAAiB,OAAO,aAAa,YAAY,cAAc,GAAG;AAEzF,UAAI,SAAS,WAAW,OAAO,GAAG;AAChC,eAAO,aAAa;AAAA,UAClB;AAAA,YACE,SAAS;AAAA,YACT,OAAO;AAAA,cACL,MAAM,OAAO,WAAW,wBAAwB;AAAA,cAChD,SAAS,OAAO,gBAAgB,CAAC,KAAK;AAAA,cACtC,aAAa,OAAO;AAAA,cACpB,UAAU,YAAY;AAAA,cACtB,UAAU,OAAO;AAAA,YACnB;AAAA,UACF;AAAA,UACA,EAAE,QAAQ,OAAO,WAAW,MAAM,IAAI;AAAA,QACxC;AAAA,MACF;AAGA,UAAI,oBAAoB;AACtB,eAAO,IAAI,aAAa,2BAA2B,QAAQ,OAAO,GAAG;AAAA,UACnE,QAAQ;AAAA,UACR,SAAS;AAAA,YACP,gBAAgB;AAAA,YAChB,4BAA4B;AAAA,UAC9B;AAAA,QACF,CAAC;AAAA,MACH;AAGA,aAAO,aAAa,SAAS,IAAI,IAAI,iBAAiB,QAAQ,GAAG,CAAC;AAAA,IACpE;AAGA,UAAM,WAAW,aAAa,KAAK;AAGnC,aAAS,QAAQ,IAAI,wBAAwB,OAAO,SAAS,SAAS,CAAC;AACvE,aAAS,QAAQ,IAAI,4BAA4B,OAAO,WAAW;AAEnE,QAAI,OAAO,OAAO;AAChB,eAAS,QAAQ,IAAI,wBAAwB,OAAO,MAAM,OAAO;AACjE,eAAS,QAAQ,IAAI,2BAA2B,OAAO,MAAM,WAAW,SAAS,CAAC;AAAA,IACpF;AAEA,WAAO;AAAA,EACT;AACF;AAKO,SAAS,oBAAoB,OAAwC;AAC1E,SAAO,EAAE,SAAS,MAAM;AAC1B;","names":["result"]}
@@ -75,8 +75,18 @@ function cacheResult(credentials, result, ttlSeconds) {
75
75
  expiresAt: Date.now() + ttlSeconds * 1e3
76
76
  });
77
77
  }
78
- function createGuidanceResponse(config, reason) {
79
- const guidance = {
78
+ function createGuidanceResponse(config, reason, options = {}) {
79
+ const source = options.source ?? "no_credentials";
80
+ const isApiError = source === "api_error";
81
+ const guidance = isApiError ? {
82
+ message: "Verification is temporarily unavailable. Retry with exponential backoff; if the issue persists, contact support with the correlationId.",
83
+ registrationUrl: `${config.apiBaseUrl.replace("/api", "")}/register`,
84
+ documentationUrl: `${config.apiBaseUrl.replace("/api", "")}/docs/agent-access`,
85
+ steps: [
86
+ "Retry the request with exponential backoff",
87
+ "If failures persist, share the correlationId with support"
88
+ ]
89
+ } : {
80
90
  message: "This service verifies AI agents before granting access. Please register your agent with AstraSync.",
81
91
  registrationUrl: `${config.apiBaseUrl.replace("/api", "")}/register`,
82
92
  documentationUrl: `${config.apiBaseUrl.replace("/api", "")}/docs/agent-access`,
@@ -97,6 +107,18 @@ function createGuidanceResponse(config, reason) {
97
107
  accessLevel: "none",
98
108
  guidance,
99
109
  denialReasons: reason ? [reason] : ["No valid agent credentials provided"],
110
+ // Round-10 (#47, O5): on API-error fallback, surface a typed failure so
111
+ // partners (and their custom onDenied handlers) can branch on
112
+ // dimension. Without this, the synthesised stub was indistinguishable
113
+ // from a real policy deny.
114
+ failures: isApiError ? [
115
+ {
116
+ dimension: "verify_access.api_error",
117
+ message: reason ?? "Verification temporarily unavailable",
118
+ guidance: guidance.message
119
+ }
120
+ ] : void 0,
121
+ correlationId: options.correlationId,
100
122
  verifiedAt: /* @__PURE__ */ new Date()
101
123
  };
102
124
  }
@@ -171,7 +193,8 @@ async function callVerifyAccessAPI(config, request) {
171
193
  if (!response.ok) {
172
194
  return {
173
195
  success: false,
174
- error: data.message || data.error || `API returned ${response.status}`
196
+ error: data.message || data.error || `API returned ${response.status}`,
197
+ correlationId: typeof data?.correlationId === "string" ? data.correlationId : void 0
175
198
  };
176
199
  }
177
200
  return data;
@@ -215,7 +238,10 @@ async function verify(config, request) {
215
238
  }
216
239
  const apiResponse = await callVerifyAccessAPI(mergedConfig, enrichedRequest);
217
240
  if (!apiResponse.success) {
218
- return createGuidanceResponse(mergedConfig, apiResponse.error);
241
+ return createGuidanceResponse(mergedConfig, apiResponse.error, {
242
+ source: "api_error",
243
+ correlationId: apiResponse.correlationId
244
+ });
219
245
  }
220
246
  if (!apiResponse.access?.allowed) {
221
247
  const aggregatedFailures = apiResponse.access?.failures;