@astrasyncai/verification-gateway 5.13.0 → 5.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -17
- package/dist/adapters/express.d.mts +1 -1
- package/dist/adapters/express.d.ts +1 -1
- package/dist/adapters/express.js +504 -249
- package/dist/adapters/express.js.map +1 -1
- package/dist/adapters/express.mjs +504 -249
- package/dist/adapters/express.mjs.map +1 -1
- package/dist/adapters/mcp.d.mts +1 -1
- package/dist/adapters/mcp.d.ts +1 -1
- package/dist/adapters/mcp.js +20 -6
- package/dist/adapters/mcp.js.map +1 -1
- package/dist/adapters/mcp.mjs +20 -6
- package/dist/adapters/mcp.mjs.map +1 -1
- package/dist/adapters/nextjs.d.mts +1 -1
- package/dist/adapters/nextjs.d.ts +1 -1
- package/dist/adapters/nextjs.js +487 -248
- package/dist/adapters/nextjs.js.map +1 -1
- package/dist/adapters/nextjs.mjs +487 -248
- package/dist/adapters/nextjs.mjs.map +1 -1
- package/dist/adapters/sdk.d.mts +18 -10
- package/dist/adapters/sdk.d.ts +18 -10
- package/dist/adapters/sdk.js +80 -23
- package/dist/adapters/sdk.js.map +1 -1
- package/dist/adapters/sdk.mjs +80 -23
- package/dist/adapters/sdk.mjs.map +1 -1
- package/dist/agent/index.js +1 -1
- package/dist/agent/index.js.map +1 -1
- package/dist/agent/index.mjs +1 -1
- package/dist/agent/index.mjs.map +1 -1
- package/dist/bin/astrasync-claude-hook.js +82 -20
- package/dist/bin/astrasync-codex-hook.js +82 -20
- package/dist/bin/astrasync-guard.js +82 -20
- package/dist/bin/astrasync.js +316 -43
- package/dist/browser/background.js +77 -15
- package/dist/browser/background.js.map +1 -1
- package/dist/browser/background.mjs +87 -15
- package/dist/browser/background.mjs.map +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +1 -1
- package/dist/cli/index.mjs.map +1 -1
- package/dist/codex/index.js +82 -20
- package/dist/codex/index.js.map +1 -1
- package/dist/codex/index.mjs +92 -20
- package/dist/codex/index.mjs.map +1 -1
- package/dist/cursor/extension.js +82 -20
- package/dist/cursor/extension.js.map +1 -1
- package/dist/cursor/extension.mjs +92 -20
- package/dist/cursor/extension.mjs.map +1 -1
- package/dist/edge-config.d.mts +31 -2
- package/dist/edge-config.d.ts +31 -2
- package/dist/edge-config.js +35 -5
- package/dist/edge-config.js.map +1 -1
- package/dist/edge-config.mjs +32 -4
- package/dist/edge-config.mjs.map +1 -1
- package/dist/edge-core/index.d.mts +43 -6
- package/dist/edge-core/index.d.ts +43 -6
- package/dist/edge-core/index.js +120 -19
- package/dist/edge-core/index.js.map +1 -1
- package/dist/edge-core/index.mjs +118 -19
- package/dist/edge-core/index.mjs.map +1 -1
- package/dist/gateway/gateway.js +77 -15
- package/dist/gateway/gateway.js.map +1 -1
- package/dist/gateway/gateway.mjs +87 -15
- package/dist/gateway/gateway.mjs.map +1 -1
- package/dist/git-trigger/git-hooks.d.mts +1 -1
- package/dist/git-trigger/git-hooks.d.ts +1 -1
- package/dist/index.d.mts +1314 -34
- package/dist/index.d.ts +1314 -34
- package/dist/index.js +5143 -2621
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +5090 -2623
- package/dist/index.mjs.map +1 -1
- package/dist/registration/index.d.mts +28 -2
- package/dist/registration/index.d.ts +28 -2
- package/dist/registration/index.js +18 -3
- package/dist/registration/index.js.map +1 -1
- package/dist/registration/index.mjs +15 -2
- package/dist/registration/index.mjs.map +1 -1
- package/dist/transport/index.d.mts +7 -5
- package/dist/transport/index.d.ts +7 -5
- package/dist/transport/index.js +61 -5
- package/dist/transport/index.js.map +1 -1
- package/dist/transport/index.mjs +67 -11
- package/dist/transport/index.mjs.map +1 -1
- package/dist/{types-C_b4QTyO.d.ts → types-DtJjlW4O.d.ts} +104 -10
- package/dist/{types-BCkwBA84.d.mts → types-Dxf9DBpr.d.mts} +106 -12
- package/dist/{types-BCkwBA84.d.ts → types-Dxf9DBpr.d.ts} +106 -12
- package/dist/{types-DlshIIR-.d.mts → types-Eg6VoBNH.d.mts} +104 -10
- package/dist/ui/index.d.mts +1 -1
- package/dist/ui/index.d.ts +1 -1
- package/dist/verify.d.mts +1 -1
- package/dist/verify.d.ts +1 -1
- package/dist/verify.js +20 -6
- package/dist/verify.js.map +1 -1
- package/dist/verify.mjs +20 -6
- package/dist/verify.mjs.map +1 -1
- package/package.json +1 -1
package/dist/verify.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/version.ts","../src/http.ts","../src/trust-levels.ts","../src/well-known.ts","../src/failure-posture.ts","../src/verify.ts"],"sourcesContent":["/**\n * Single source-of-truth for the SDK's\n * package version emitted on verify-access bodies (and any future\n * telemetry). Bumped alongside `package.json#version` on every release.\n *\n * Why a constant rather than `import pkg from '../package.json'`:\n * - `tsconfig.json` sets `rootDir: ./src`; importing the sibling\n * package.json fails the build with \"outside rootDir\".\n * - Build-time string replacement (tsup `define`, esbuild banner, etc.)\n * adds toolchain coupling for a trivial gain.\n * - Embedded readonly constant works in every environment (Node, browser,\n * bundlers, Deno) without runtime fs / network access.\n *\n * Release discipline: a CI lint can grep `package.json#version` against\n * this constant if the two ever diverge in the wild. A manual bump\n * is fine — bumping both in the release-ceremony commit keeps them\n * lockstep.\n */\nexport const SDK_VERSION = '5.13.0';\n","/**\n * Shared outbound-HTTP wrapper — every request the SDK makes to the\n * AstraSync backend (or anywhere else) goes through `sdkFetch` so it\n * carries an identifying User-Agent.\n *\n * Why: Node's global fetch defaults to `user-agent: node`, which is\n * indistinguishable from every other Node client on the wire. Visit\n * Intelligence observed our own SDK/beacon traffic as anonymous \"node\"\n * visits. Self-identification is the platform's own medicine.\n *\n * Browser note: some browsers filter the User-Agent request header\n * silently (never throwing), so this is a no-op there — which is correct:\n * in a browser the page's real UA is the honest identity.\n */\n\nimport { SDK_VERSION } from './version';\n\nexport const SDK_USER_AGENT = `astrasync-sdk/${SDK_VERSION}`;\n\n/**\n * Add the SDK User-Agent without changing the SHAPE of the caller's headers\n * (plain records stay plain records) — interceptors, adapters, and tests\n * read `init.headers` as a record, and `Headers` would also lowercase names.\n */\nfunction withUserAgent(initHeaders: HeadersInit | undefined): HeadersInit {\n if (!initHeaders) return { 'user-agent': SDK_USER_AGENT };\n if (initHeaders instanceof Headers) {\n const copy = new Headers(initHeaders);\n if (!copy.has('user-agent')) copy.set('user-agent', SDK_USER_AGENT);\n return copy;\n }\n if (Array.isArray(initHeaders)) {\n const has = initHeaders.some(([name]) => name.toLowerCase() === 'user-agent');\n return has ? initHeaders : [...initHeaders, ['user-agent', SDK_USER_AGENT]];\n }\n const has = Object.keys(initHeaders).some((name) => name.toLowerCase() === 'user-agent');\n return has ? initHeaders : { 'user-agent': SDK_USER_AGENT, ...initHeaders };\n}\n\nexport const sdkFetch: typeof fetch = (input, init) =>\n fetch(input, { ...init, headers: withUserAgent(init?.headers) });\n","/**\n * AstraSync Universal Verification Gateway — Trust Level helpers.\n *\n * SDK 5.0.0: the `accessLevel` band (`none`/`restricted`/`read-only`/`standard`/\n * `full`/`internal`) was removed entirely — it never gated the access decision\n * (the server does, from a verified ID + runtime challenge + PDLSS match + trust\n * score). What remains here is the coarse trust-level badge derived from the\n * numeric trust score, used only for display.\n */\n\nimport type { TrustLevel } from './types';\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","import { sdkFetch } from './http';\n/**\n * SDK-side discovery of canonical platform URLs via `/.well-known/agentic-commerce`.\n *\n * Fire-and-forget pre-fetch at middleware creation; first verify() awaits\n * the in-flight promise if it hasn't resolved. 60-minute TTL with\n * stale-while-revalidate background refresh.\n */\n\nexport interface WellKnownAgenticCommerce {\n registrationUrl: string;\n documentationUrl: string;\n verifyAccessUrl: string;\n /** 5.1.0: hosted AstraSync MCP bridge endpoint (absent on self-hosted backends). */\n mcpEndpoint?: string;\n /** 5.1.0: bridge connector-discovery doc (absent on self-hosted backends). */\n mcpDiscoveryUrl?: string;\n}\n\ninterface CacheEntry {\n data: WellKnownAgenticCommerce;\n fetchedAt: number;\n}\n\nconst CACHE_TTL_MS = 60 * 60 * 1000; // 60 minutes\n\nconst cache = new Map<string, CacheEntry>();\nconst inflight = new Map<string, Promise<WellKnownAgenticCommerce>>();\n\nfunction wellKnownUrl(apiBaseUrl: string): string {\n // apiBaseUrl is typically 'https://astrasync.ai/api' — strip the /api suffix\n // to get the platform root, then append /.well-known/agentic-commerce.\n const base = apiBaseUrl.replace(/\\/api\\/?$/, '');\n return `${base}/.well-known/agentic-commerce`;\n}\n\nasync function fetchWellKnown(apiBaseUrl: string): Promise<WellKnownAgenticCommerce> {\n const url = wellKnownUrl(apiBaseUrl);\n const response = await sdkFetch(url, {\n method: 'GET',\n headers: { Accept: 'application/json' },\n signal: AbortSignal.timeout(5000),\n });\n if (!response.ok) {\n throw new Error(\n `AstraSync platform must expose /.well-known/agentic-commerce; ` +\n `got ${response.status} from ${url}. SDK cannot initialise without it.`\n );\n }\n const data = (await response.json()) as Record<string, unknown>;\n if (!data.registrationUrl || !data.documentationUrl || !data.verifyAccessUrl) {\n throw new Error(\n `/.well-known/agentic-commerce response missing required fields ` +\n `(registrationUrl, documentationUrl, verifyAccessUrl).`\n );\n }\n return data as unknown as WellKnownAgenticCommerce;\n}\n\n/**\n * Start a background fetch. Returns the promise for callers that need\n * to await it (first verify() call).\n */\nexport function prefetchWellKnown(apiBaseUrl: string): Promise<WellKnownAgenticCommerce> {\n const existing = inflight.get(apiBaseUrl);\n if (existing) return existing;\n\n const promise = fetchWellKnown(apiBaseUrl)\n .then((data) => {\n cache.set(apiBaseUrl, { data, fetchedAt: Date.now() });\n inflight.delete(apiBaseUrl);\n return data;\n })\n .catch((err) => {\n inflight.delete(apiBaseUrl);\n throw err;\n });\n\n inflight.set(apiBaseUrl, promise);\n return promise;\n}\n\n/**\n * Get cached well-known URLs. If stale, triggers a background refresh\n * and returns stale data (stale-while-revalidate). If no cache exists,\n * awaits the in-flight fetch or starts a new one.\n */\nexport async function getWellKnownUrls(apiBaseUrl: string): Promise<WellKnownAgenticCommerce> {\n const entry = cache.get(apiBaseUrl);\n\n if (entry) {\n if (Date.now() - entry.fetchedAt > CACHE_TTL_MS) {\n // Stale — trigger background refresh, return stale data\n prefetchWellKnown(apiBaseUrl).catch(() => {\n // Background refresh failed — stale data remains until next request\n });\n }\n return entry.data;\n }\n\n // No cache — must await\n const pending = inflight.get(apiBaseUrl);\n if (pending) return pending;\n\n return prefetchWellKnown(apiBaseUrl);\n}\n\n/**\n * Synchronous cache read — returns cached URLs or undefined.\n * Never triggers a fetch. Used by verify() to avoid extra HTTP calls\n * in the hot path; adapters are responsible for prefetching.\n */\nexport function getCachedWellKnownUrls(apiBaseUrl: string): WellKnownAgenticCommerce | undefined {\n return cache.get(apiBaseUrl)?.data;\n}\n\n/** Reset cache — for testing only. */\nexport function _resetWellKnownCache(): void {\n cache.clear();\n inflight.clear();\n}\n","/**\n * 5.12.0 — HTTP adapter failure posture, lifted from edge-core\n * (`pipeline.ts` isApiErrorResult/unavailableOutcome, `enforce.ts` 503 +\n * Retry-After + buildUnavailableBody) so the Express and Next.js adapters\n * and `quickVerify` answer an AstraSync outage the way the edge does.\n *\n * The 1 Oct load test: the platform rate-limited a merchant's verify-access\n * calls (429), the SDK mapped that to `UNAUTHORIZED` 401 and failed closed\n * — even for anonymous catalogue reads — so the merchant was down for 11\n * minutes. Fail posture follows success posture:\n *\n * - observe routes and anonymous reads (GET/HEAD, no credentials) pass\n * through, stamped `X-Astra-Verification: unavailable`;\n * - enforced routes answer 503 + `Retry-After` (the backend's value, else\n * 30s) + `VERIFICATION_UNAVAILABLE`. Never 401 — the caller's identity\n * was never evaluated.\n *\n * Identity status mapping (contract correction, not a break): credentials\n * missing → 401; a presented id that is malformed or unknown → 403 with\n * registration guidance (re-authenticating with the same id cannot help).\n */\nimport type { AgentCredentials, VerificationResult } from './types';\n\n/** Response header stamped on a fail-open pass-through during an outage. */\nexport const VERIFICATION_STATUS_HEADER = 'X-Astra-Verification';\n\n/** Default Retry-After (seconds) when the backend sent none — edge parity. */\nexport const DEFAULT_UNAVAILABLE_RETRY_AFTER_S = 30;\n\n/**\n * Failure dimensions for a presented-but-rejected agent identifier.\n * `agent.lookup` is the backend's 200-with-deny shape for a well-formed id\n * that matches no registered agent (round-27.4 D4 — the same verdict as the\n * 404 → `identity.unknown_agent` mapping, delivered on a different wire).\n */\nexport const REJECTED_IDENTITY_DIMENSIONS: ReadonlySet<string> = new Set([\n 'identity.invalid_agent_id',\n 'identity.unknown_agent',\n 'agent.lookup',\n]);\n\n/** Registration guidance for a presented id that matches no registered agent. */\nexport const UNKNOWN_AGENT_GUIDANCE =\n 'Register your agent with AstraSync (or, inside an MCP host, add the AstraSync MCP connector for keyless, human-approved registration), then present the canonical ASTRA-* id issued after owner approval.';\n\n/**\n * Failure dimensions that mean \"verification could not be evaluated\" rather\n * than \"the agent was denied\":\n * - `verify_access.api_error` — the SDK's typed, retryable fallback for an\n * AstraSync outage (timeout, 5xx, the platform's 429);\n * - `middleware.internal_error` — an adapter's own `failOnError: 'closed'`\n * catch path (an exception inside the gateway middleware). Failing closed\n * there is a 503, never an identity-shaped 401.\n */\nexport const UNAVAILABLE_DIMENSIONS: ReadonlySet<string> = new Set([\n 'verify_access.api_error',\n 'middleware.internal_error',\n]);\n\n/**\n * A verify() result that is the SDK's typed, retryable api_error fallback —\n * infrastructure unavailability (timeout, 5xx, 429), not a policy deny and\n * not the deterministic merchant misconfig — or an adapter's internal-error\n * fail-closed result.\n */\nexport function isVerificationUnavailable(result: VerificationResult): boolean {\n if (result.misconfigured) return false;\n return (result.failures ?? []).some((f) => UNAVAILABLE_DIMENSIONS.has(f.dimension));\n}\n\n/** Retry-After seconds to relay: the backend's value (≥1s), else 30. */\nexport function unavailableRetryAfterSeconds(result: VerificationResult): number {\n if (typeof result.retryAfterMs === 'number' && Number.isFinite(result.retryAfterMs)) {\n return Math.max(1, Math.ceil(result.retryAfterMs / 1000));\n }\n return DEFAULT_UNAVAILABLE_RETRY_AFTER_S;\n}\n\n/** An anonymous read: GET/HEAD carrying no agent credential. */\nexport function isAnonymousRead(\n method: string | undefined,\n credentials: AgentCredentials\n): boolean {\n const m = (method ?? '').toUpperCase();\n if (m !== 'GET' && m !== 'HEAD') return false;\n return !credentials.astraId && !credentials.jwt;\n}\n\n/** Machine-readable 503 body — same shape as edge-core's buildUnavailableBody. */\nexport function buildVerificationUnavailableBody(result: VerificationResult): {\n success: false;\n error: {\n code: 'VERIFICATION_UNAVAILABLE';\n message: string;\n retryable: true;\n retryAfterSeconds: number;\n failures?: VerificationResult['failures'];\n correlationId?: string;\n guidance: { message: string };\n };\n} {\n return {\n success: false,\n error: {\n code: 'VERIFICATION_UNAVAILABLE',\n message:\n 'Agent verification is temporarily unavailable and this path enforces verification. ' +\n 'Your identity was not evaluated and no denial was recorded — retry after the Retry-After interval.',\n retryable: true,\n retryAfterSeconds: unavailableRetryAfterSeconds(result),\n ...(result.failures && { failures: result.failures }),\n ...(result.correlationId && { correlationId: result.correlationId }),\n guidance: {\n message:\n 'Retry the request after the Retry-After interval, with exponential backoff; the condition is transient.',\n },\n },\n };\n}\n\n/** True when the deny is about a presented-but-malformed/unknown agent id. */\nexport function isRejectedIdentityClaim(result: VerificationResult): boolean {\n return (result.failures ?? []).some((f) => REJECTED_IDENTITY_DIMENSIONS.has(f.dimension));\n}\n\n/**\n * HTTP status + code for an ordinary (non-outage, non-misconfig) deny:\n * missing identity → 401 UNAUTHORIZED; presented-but-rejected id → 403\n * AGENT_IDENTITY_REJECTED; identity fine but policy denied → 403 with the\n * adapter's policy code.\n */\nexport function denyStatusFor(\n result: VerificationResult,\n policyDeniedCode: string\n): { status: 401 | 403; code: string } {\n // Checked first: older backends stamp idVerified:true on the agent.lookup\n // (unknown id) deny, which would otherwise read as a policy deny.\n if (isRejectedIdentityClaim(result)) return { status: 403, code: 'AGENT_IDENTITY_REJECTED' };\n if (result.identityVerified) return { status: 403, code: policyDeniedCode };\n return { status: 401, code: 'UNAUTHORIZED' };\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 { sdkFetch } from './http';\nimport type {\n GatewayConfig,\n AgentCredentials,\n VerificationRequest,\n VerificationResult,\n VerifiedAgent,\n VerifiedDeveloper,\n VerifiedOrganization,\n GuidanceInfo,\n EnhancedVerificationResult,\n TokenGuidance,\n RuntimeChallengeResult,\n StepUpApprovalInfo,\n SettlementOutcomeInfo,\n MerchantSettlementToken,\n FulfilmentInfo,\n SettlementArtifact,\n AttemptReport,\n} from './types';\nimport { getTrustLevel } from './trust-levels';\nimport type { ObservedMetadata } from './metadata-capture';\nimport { SDK_VERSION } from './version';\nimport { getCachedWellKnownUrls, type WellKnownAgenticCommerce } from './well-known';\nimport {\n REJECTED_IDENTITY_DIMENSIONS,\n UNKNOWN_AGENT_GUIDANCE,\n isVerificationUnavailable,\n unavailableRetryAfterSeconds,\n} from './failure-posture';\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 // minTrustScore + minTrustScoreForFull deprecated in v2.3.0 — server decides.\n // cacheTtl deliberately unset. When undefined, cacheResult applies the 60s\n // default; the caller's explicit value is honoured for cacheable results.\n // Denials and step-up escalations are NEVER cached (3.6.0) regardless of\n // cacheTtl. Set cacheTtl: 0 to disable caching entirely.\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/** Test-only: re-arm the one-time init self-check. */\nexport function _resetInitCheck(): void {\n initCheckPerformed = false;\n}\n\n/** One-shot guard for v2.3.0 deprecation warning. */\nlet deprecationWarningShown = false;\n\nasync function performInitCheck(\n apiBaseUrl: string,\n debug?: boolean,\n strictInit?: boolean,\n apiKey?: string,\n counterparty?: { id?: string; url?: string }\n): 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 sdkFetch(probeUrl, { method: 'HEAD' });\n const contentType = response.headers.get('content-type') ?? '';\n if (contentType.startsWith('text/html')) {\n const message =\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 // Audit F-A6-33: when strictInit is set, throw on misconfig instead\n // of warning + continuing. Recurring footgun per\n // `[[feedback_astrasync_api_url_canonical]]` — silent warn lets\n // misconfigured prod deploys ship with all verify-access calls\n // silently failing.\n if (strictInit) {\n throw new Error(`${message} (strictInit=true)`);\n }\n console.warn(`${message} Set disableInitChecks: true on GatewayConfig to silence.`);\n } else if (debug) {\n console.log(\n `[VerificationGateway] init check passed for ${apiBaseUrl} (content-type: ${contentType})`\n );\n }\n\n // 4.0.0: key liveness probe. A dead API key otherwise surfaces only\n // per-request as inbound agents get turned away — the operator should\n // learn at deploy time. 404 = older backend without self-check: silent.\n if (apiKey) {\n // 5.13.0: name the endpoint this SDK is installed on, so the backend\n // can say whether this key may verify for it (keys are bound to the\n // endpoint's owner — a key from another account is refused on every\n // request with COUNTERPARTY_KEY_MISMATCH).\n const q = counterparty?.id\n ? `?counterpartyId=${encodeURIComponent(counterparty.id)}`\n : counterparty?.url\n ? `?counterpartyUrl=${encodeURIComponent(counterparty.url)}`\n : '';\n const selfCheck = await sdkFetch(`${apiBaseUrl}/agents/verify-access/self-check${q}`, {\n headers: { Authorization: `Bearer ${apiKey}` },\n });\n if (selfCheck.status === 401) {\n const body = (await selfCheck.json().catch(() => ({}))) as {\n code?: string;\n message?: string;\n remediation?: { manageUrl?: string };\n };\n const message =\n `[VerificationGateway] the configured AstraSync API key was REJECTED at startup ` +\n `(${body.code ?? 'api_key_invalid'}: ${body.message ?? 'invalid key'}). Every inbound agent ` +\n `will be turned away until the key is rotated` +\n (body.remediation?.manageUrl ? ` at ${body.remediation.manageUrl}` : '') +\n `.`;\n if (strictInit) {\n throw new Error(`${message} (strictInit=true)`);\n }\n console.error(message);\n } else if (selfCheck.ok) {\n const body = (await selfCheck.json().catch(() => ({}))) as {\n daysToExpiry?: number | null;\n counterparty?: { found?: boolean; canVerify?: boolean; message?: string };\n };\n if (body.counterparty && body.counterparty.canVerify === false) {\n const message =\n `[VerificationGateway] the configured AstraSync API key cannot verify for this endpoint ` +\n `(${body.counterparty.message ?? 'endpoint not found or owned by another account'}). ` +\n `Every inbound agent will be turned away until the key or counterparty is corrected.`;\n if (strictInit) {\n throw new Error(`${message} (strictInit=true)`);\n }\n console.error(message);\n }\n if (typeof body.daysToExpiry === 'number' && body.daysToExpiry <= 7) {\n console.warn(\n `[VerificationGateway] the configured AstraSync API key expires in ${body.daysToExpiry} day(s) — ` +\n `rotate it at Settings → Developer Tools before integrations start failing.`\n );\n } else if (debug) {\n console.log('[VerificationGateway] API key self-check passed');\n }\n }\n }\n } catch (err) {\n if (strictInit) {\n // strict mode propagates init failures so callers get a clear,\n // synchronous \"your config is broken\" signal at startup.\n throw err;\n }\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 the full request shape.\n *\n * Historically the key was credentials-only (`astraId-apiKey-jwt`),\n * which silently returned the cached result of a prior call even when the\n * subsequent call's purpose/action/counterparty/etc. differed. That was a\n * security gap — a warm cache could serve an allow verdict for a purpose the\n * agent hadn't actually been authorised for. Key now includes every field\n * that affects the backend verdict.\n *\n * MAINTAIN: when adding a field to VerificationRequest that affects the\n * backend verdict, add it to this key. Fields that don't affect verdict\n * (e.g. requestId, timestamps, sdkVersion) don't belong here — including\n * them would needlessly thrash the cache for repeated identical requests.\n *\n * 3.12.0 (Trust Record): `attemptId` and `considerationSet` are\n * deliberately EXCLUDED — like `callerMetadata.observedMetadata` they are\n * observational pass-through that never affects the verdict, and keying on\n * a per-attempt id would defeat verdict caching entirely. Cache hits get\n * their attemptId rewritten in verify() instead (never leak a prior\n * attempt's id from a cached result).\n */\nfunction getCacheKey(request: VerificationRequest, counterpartyId?: string): string {\n const c = request.credentials;\n return [\n c.astraId || '',\n c.apiKey || '',\n c.jwt || '',\n request.purpose || '',\n request.action || '',\n request.resourceType || '',\n request.resource || '',\n request.jurisdiction || '',\n request.transactionValue ?? '',\n request.currency || '',\n // SECURITY (cross-merchant cache leak): the merchant identity is sent via\n // `config.counterpartyId`, NOT on the request, so it was previously absent\n // from the key — two verifies for the SAME agent/purpose/action/value but\n // DIFFERENT merchants collided, and a grant at a permissive merchant (low\n // trust floor) was served for a stricter one. Same bug class as the\n // duration omission (F-A1-07). counterpartyId affects the backend verdict\n // (trust floor / per-route policy), so it MUST key the cache.\n counterpartyId || '',\n request.counterpartyUrl || '',\n request.counterpartyType || '',\n request.isSubAgentRequest ? '1' : '0',\n request.parentAgentId || '',\n request.subAgentDepth ?? '',\n // Audit F-A1-07: previously-missing dimensions that DO affect the\n // backend verdict. Without these, two requests with different\n // durations (e.g. 60s vs 86400s) collided on the same cache key and\n // the shorter-duration allow served the longer-duration request.\n request.durationRequired ?? '',\n request.invocationProtocol || '',\n request.enableRuntimeChallenge ? '1' : '0',\n // callerMetadata fields contribute to risk model; include the ones\n // backend reads. sourceIp/userAgent/forwardedFor change per-request\n // so their inclusion effectively forces a re-check for any varying\n // client (the right behavior — IP-driven anomaly scoring shouldn't\n // be cached across IPs).\n request.callerMetadata?.sourceIp || '',\n request.callerMetadata?.userAgent || '',\n request.callerMetadata?.forwardedFor || '',\n request.callerMetadata?.agentCardUrl || '',\n // UAT2 Bug B (5.4.1): the commerce phase and cart identity DO affect the\n // backend verdict — the confirm leg is the ONLY leg that may execute\n // first-party settlement. Without these, a confirm within the cache TTL of\n // its start_checkout quote was served the cached quote-leg grant (no\n // settlementOutcome) and never reached the backend. Same F-A1-07 bug class.\n request.commercePhase || '',\n request.checkoutSessionId || '',\n ].join('|');\n}\n\n/**\n * Check if cached result is still valid\n */\nfunction getCachedResult(\n request: VerificationRequest,\n counterpartyId?: string\n): VerificationResult | null {\n const key = getCacheKey(request, counterpartyId);\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 *\n * 3.6.0: uniform 60s default. The earlier step-up TTL split (300s for\n * step-up verdicts) is gone — step-up verdicts are no longer cached AT ALL\n * (see the write predicate in verify()): a cached step_up_required result\n * carries a pollUrl that goes stale (consumed/expired) and, worse, keeps\n * denying retries that the owner has already approved. When `configuredTtl`\n * is set (truthy positive number), it's honoured for cacheable results.\n */\nconst DEFAULT_CACHE_TTL_SECONDS = 60;\n\nfunction cacheResult(\n request: VerificationRequest,\n result: VerificationResult,\n configuredTtl: number | undefined,\n counterpartyId?: string\n): void {\n const ttlSeconds = configuredTtl && configuredTtl > 0 ? configuredTtl : DEFAULT_CACHE_TTL_SECONDS;\n const key = getCacheKey(request, counterpartyId);\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 // Audit F-A1-12: ASTRA-ID shape validation. Accept only the canonical\n // pattern (ASTRA-/ASTRAE- prefix + 1-64 url-safe chars) to prevent CRLF\n // header injection or downstream interpolation surprises. Reject otherwise.\n const ASTRA_ID_PATTERN = /^ASTRAE?-[A-Za-z0-9_-]{1,64}$/;\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 // Audit F-A1-11: guard array-confusion. If multiple X-Astra-Id headers\n // were sent (Express collapses to comma-joined string on most paths),\n // take only the first canonical-shaped one.\n const raw = Array.isArray(astraIdHeader)\n ? astraIdHeader[0]\n : typeof astraIdHeader === 'string'\n ? astraIdHeader\n : undefined;\n if (typeof raw === 'string' && ASTRA_ID_PATTERN.test(raw)) {\n credentials.astraId = raw;\n }\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 // Audit F-A1-11: defensive type guard — Array.isArray gives a non-string,\n // and Express path quirks can produce undefined despite the truthy check.\n const authHeader = headers['authorization'] || headers['Authorization'];\n if (authHeader) {\n const authValue = Array.isArray(authHeader) ? authHeader[0] : authHeader;\n if (typeof authValue === 'string') {\n credentials.authorizationHeader = authValue;\n if (authValue.startsWith('Bearer ')) {\n credentials.jwt = authValue.slice(7);\n }\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. The sources are 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 *\n * 4.0.0 adds `'merchant_misconfig'`: the backend rejected THIS INTEGRATION'S\n * own API key (401 api_key_revoked/expired/invalid). Neither the agent's\n * fault nor a platform outage — retrying won't help, and the inbound agent\n * deserves to know the site operator's integration is broken (the\n * gadget-emporium failure mode: a rotated key silently 401ing every\n * credentialed agent for days).\n */\ntype GuidanceSource = 'no_credentials' | 'api_error' | 'merchant_misconfig';\n\n/**\n * MCP-connector steering (5.1.0). Present only when the backend's well-known\n * doc advertises a hosted bridge. The copy must directly counter the observed\n * model refusal (\"go register manually on the dashboard\"): registration via\n * the connector is the agent's own next action, with the human approval built\n * into the flow.\n */\nfunction mcpGuidance(urls?: WellKnownAgenticCommerce): GuidanceInfo['mcp'] {\n if (!urls?.mcpEndpoint) return undefined;\n return {\n endpoint: urls.mcpEndpoint,\n discoveryUrl: urls.mcpDiscoveryUrl,\n message:\n 'If you are operating inside an MCP-capable host (Claude, ChatGPT, Cursor, or any MCP client), ' +\n 'add the AstraSync MCP connector instead of integrating these URLs by hand: registration through ' +\n 'the connector is KEYLESS and HUMAN-APPROVED — your human approves in an in-chat card, and you ' +\n 'never see, hold, or transmit an API key, password, or private key.',\n };\n}\n\n/**\n * Create guidance response for unverified agents or for API-error fallback.\n *\n * The source is split 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: {\n source?: GuidanceSource;\n correlationId?: string;\n /** 3.12.0 (Trust Record): server-echoed attempt handle, threaded like correlationId. */\n attemptId?: string;\n urls?: WellKnownAgenticCommerce;\n /** 4.0.0: backend-provided remediation URLs for the merchant (misconfig source). */\n remediation?: { docsUrl?: string; manageUrl?: string };\n /** 5.12.0: backend non-2xx status + Retry-After (api_error source). */\n httpStatus?: number;\n retryAfterMs?: number;\n } = {}\n): VerificationResult {\n const source = options.source ?? 'no_credentials';\n const isApiError = source === 'api_error';\n const isMisconfig = source === 'merchant_misconfig';\n const urls = options.urls;\n\n const guidance: GuidanceInfo = isMisconfig\n ? {\n message:\n \"This site's AstraSync integration is misconfigured (its API key was revoked, expired, rotated, or belongs to another account). \" +\n 'Your identity and the AstraSync platform are fine — contact the site operator. Retrying will not help.',\n registrationUrl: urls?.registrationUrl ?? '',\n documentationUrl: options.remediation?.docsUrl ?? urls?.documentationUrl ?? '',\n steps: [\n 'Report the failure to the site operator — their AstraSync API key needs replacing',\n 'Do not retry: the failure is deterministic until the operator updates their key',\n ],\n }\n : isApiError\n ? {\n message:\n 'Verification is temporarily unavailable. Retry with exponential backoff; if the issue persists, contact support with the correlationId.',\n registrationUrl: urls?.registrationUrl ?? '',\n documentationUrl: urls?.documentationUrl ?? '',\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: urls?.registrationUrl ?? '',\n documentationUrl: urls?.documentationUrl ?? '',\n steps: [\n // MCP steering leads when the hosted bridge is advertised — the\n // connector is the agent-actionable path (keyless, human-approved).\n ...(urls?.mcpEndpoint\n ? [\n `Fastest path (inside an MCP host): add the AstraSync MCP connector at ${urls.mcpEndpoint} — keyless, human-approved registration`,\n ]\n : []),\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 ...(mcpGuidance(urls) && { mcp: mcpGuidance(urls) }),\n };\n\n return {\n // Round-18 G4: createGuidanceResponse fires for unverified-agent path or\n // API-error fallback. Identity is not verified (no agent resolved);\n // policy is not evaluated (we never reached the gate).\n identityVerified: false,\n policyAllowed: false,\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: isMisconfig\n ? [\n {\n dimension: 'verify_access.merchant_misconfigured',\n message: reason ?? 'The integration API key was rejected by AstraSync',\n guidance: guidance.message,\n },\n ]\n : isApiError\n ? [\n {\n dimension: 'verify_access.api_error',\n message: reason ?? 'Verification temporarily unavailable',\n guidance: guidance.message,\n },\n ]\n : undefined,\n // 4.0.0: typed marker so adapters render the misconfig envelope (503 +\n // MERCHANT_VERIFICATION_MISCONFIGURED) without string-matching.\n ...(isMisconfig && { misconfigured: true }),\n // 4.6.0: a transient api_error is retryable — mark it so consumers surface\n // \"temporarily unavailable, retry\" (recommendation 'error') instead of\n // coercing the absent recommendation to a policy 'deny'. Misconfig is\n // deterministic and intentionally NOT marked retryable.\n ...(isApiError && { retryable: true }),\n ...(options.httpStatus !== undefined && { httpStatus: options.httpStatus }),\n ...(options.retryAfterMs !== undefined && { retryAfterMs: options.retryAfterMs }),\n correlationId: options.correlationId,\n attemptId: options.attemptId,\n verifiedAt: new Date(),\n };\n}\n\n/**\n * 5.12.0: parse a response body as JSON without throwing — a non-JSON body\n * (HTML error page, plain-text 429) yields `{}` so the status survives.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nasync function readJsonSafe(response: Response): Promise<any> {\n try {\n const parsed = await response.json();\n return parsed && typeof parsed === 'object' ? parsed : {};\n } catch {\n return {};\n }\n}\n\n/** 5.12.0: human guidance for a presented-but-malformed/unknown agent id deny. */\nfunction rejectedIdentityGuidance(\n failures: Array<{ dimension: string; message: string; guidance?: string }> | undefined\n): string | undefined {\n const f = failures?.find((x) => REJECTED_IDENTITY_DIMENSIONS.has(x.dimension));\n if (!f) return undefined;\n // The backend's agent.lookup guidance only says \"check the id format\";\n // an unknown id needs the registration path (404 parity).\n if (f.dimension === 'agent.lookup') {\n return `The presented agent identifier is not registered with AstraSync. ${UNKNOWN_AGENT_GUIDANCE}`;\n }\n return f.guidance ? `${f.message} ${f.guidance}` : f.message;\n}\n\n/** Upper bound on a relayed Retry-After (a hostile/buggy value can't park callers for hours). */\nconst MAX_RETRY_AFTER_MS = 15 * 60 * 1000;\n\n/**\n * 5.12.0: `Retry-After` (delta-seconds or HTTP-date) → milliseconds, or\n * undefined when absent/unparseable. Clamped to [0, 15 min].\n */\nexport function parseRetryAfterMs(response: {\n headers?: { get?(name: string): string | null };\n}): number | undefined {\n const raw = response.headers?.get?.('retry-after');\n if (!raw) return undefined;\n const trimmed = raw.trim();\n let ms: number;\n if (/^\\d+(\\.\\d+)?$/.test(trimmed)) {\n ms = Number(trimmed) * 1000;\n } else {\n const at = Date.parse(trimmed);\n if (Number.isNaN(at)) return undefined;\n ms = at - Date.now();\n }\n return Math.min(MAX_RETRY_AFTER_MS, Math.max(0, Math.round(ms)));\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 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_configured';\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 /** LMAX-POC C1: underlying check timestamp for merchant freshness gates. */\n checkedAt: string;\n validUntil?: string;\n proofType: 'reference' | 'zkp';\n proof: string;\n }>;\n };\n error?: string;\n /**\n * 4.0.0: the backend 401'd this integration's OWN API key (revoked /\n * expired / invalid) — deterministic merchant-side misconfig, not a\n * transient failure. `misconfigCode` carries the backend's api_key_* code\n * and `remediation` its operator-facing URLs when present.\n */\n misconfig?: boolean;\n misconfigCode?: string;\n remediation?: { docsUrl?: string; manageUrl?: string };\n /**\n * 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 * 3.12.0 (Trust Record): the server echoes the attempt-chain\n * handle top-level on every response branch (error envelopes included).\n */\n attemptId?: string;\n /** 5.12.0: backend HTTP status on a non-2xx (429/5xx) — see VerificationResult.httpStatus. */\n httpStatus?: number;\n /** 5.12.0: backend Retry-After in ms, when sent. */\n retryAfterMs?: number;\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 ...(requestData.purpose && { purpose: requestData.purpose }),\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 // Round-12 (F19): transport-vs-intent separation. MCP middleware sets\n // this to 'mcp'; non-MCP callers leave it unset.\n if (requestData.invocationProtocol) body.invocationProtocol = requestData.invocationProtocol;\n // 5.3.0 (astra-pay): checkout-leg discriminator — first-party settlement\n // fires only on 'confirm'; absent keeps pre-phase server behavior.\n if (requestData.commercePhase) body.commercePhase = requestData.commercePhase;\n // Per-cart idempotency key (checkout session id) forwarded on the confirm\n // leg so concurrent confirms for one cart charge at most once.\n if (requestData.checkoutSessionId) body.checkoutSessionId = requestData.checkoutSessionId;\n // Order-plane cart lines (receipts + fulfillment email) — informational.\n if (requestData.checkoutItems) body.checkoutItems = requestData.checkoutItems;\n // 5.6.0 (astra-pay): transit-only fulfilment contact — echoed back in\n // result.fulfilment for first-party confirms, never persisted server-side.\n if (requestData.buyerEmail) body.buyerEmail = requestData.buyerEmail;\n // 3.9.0 (§33.5 #1): raw commerce artifacts forwarded verbatim — the\n // backend's Step 0.5 pipeline does the cryptographic verification\n // server-side (adapters forward, never verify locally).\n if (requestData.commerceArtifacts) body.commerceArtifacts = requestData.commerceArtifacts;\n // 3.12.0 (Trust Record, §33.5 #3): attempt-chain handle + consideration set,\n // forwarded skip-when-undefined (observedMetadata precedent) — pure\n // observational pass-through; the server echoes attemptId on every branch.\n if (requestData.attemptId) body.attemptId = requestData.attemptId;\n if (requestData.considerationSet) body.considerationSet = requestData.considerationSet;\n\n // Round-13 (F14 closure / R13-7): emit the SDK package version on every\n // verify-access body so the backend can auto-populate `kya_counterparty.\n // sdk_version` for the calling endpoint. Body field (not User-Agent\n // header) is the canonical channel — works in Node, browser, behind\n // Cloudflare, and across SDK bundlers without environment-specific\n // header gymnastics. Backend's `validation.ts:verifyAccessSchema`\n // accepts the semver regex; the auto-pop logic is forward-only.\n body.sdkVersion = SDK_VERSION;\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. Use ONLY the merchant's\n // configured apiKey for outbound auth. Previously the caller's inbound\n // Authorization header took priority, which pivoted the verify-access\n // call to the caller's identity — letting an attacker who supplied\n // their own Authorization header trigger verify-access calls under\n // THEIR account and observe the resulting verdicts for arbitrary\n // lookups they triggered via the merchant. Audit F-A1-10.\n //\n // The caller-supplied JWT (if any) is already extracted to\n // credentials.jwt and travels in the verify-access request body so\n // backend can evaluate it as the agent's identity claim — separate from\n // the merchant-attribution auth on the outbound HTTPS call.\n if (config.apiKey) {\n headers['Authorization'] = `Bearer ${config.apiKey}`;\n // Legacy header kept for compatibility with any middleware that reads it directly.\n headers['X-API-Key'] = config.apiKey;\n }\n\n try {\n // 4.0.0: hard timeout — without one, a backend that accepts the\n // connection but never answers hangs the merchant's middleware and every\n // inbound agent request behind it (observed live on a partner property).\n // Timeouts land on the catch path below → api_error (fail-closed),\n // NOT misconfig. The edge adapter races its own tighter withBudget.\n const response = await sdkFetch(`${config.apiBaseUrl}/agents/verify-access`, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(config.verifyTimeoutMs ?? 10_000),\n });\n\n // 5.12.0: a non-JSON body (an HTML 502 from a load balancer, a plain-text\n // 429) used to throw here and lose the status; parse defensively.\n const data = await readJsonSafe(response);\n const retryAfterMs = parseRetryAfterMs(response);\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 // 3.12.0 (Trust Record): keep the server-echoed attempt handle even on\n // the synthesised 410 deny shape (echoed on every branch).\n attemptId: typeof data?.attemptId === 'string' ? data.attemptId : undefined,\n access: {\n allowed: false,\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 // 5.10.0: a caller-supplied agent id that isn't a canonical ASTRA-id\n // (e.g. `ASTRA-bogus123`) is a deterministic failed identity claim, not\n // an outage. Previously it fell through to the transient api_error path,\n // so the edge answered 503 \"verification unavailable — retry\" under\n // enforce; it is now an identity deny (403 + registration guidance).\n if (response.status === 400 && data?.error === 'INVALID_AGENT_ID') {\n const message =\n typeof data?.message === 'string'\n ? data.message\n : 'Agent identifier must be a canonical ASTRA-* id.';\n return {\n success: true,\n attemptId: typeof data?.attemptId === 'string' ? data.attemptId : undefined,\n access: {\n allowed: false,\n reason: 'invalid_agent_id',\n failures: [\n {\n dimension: 'identity.invalid_agent_id',\n message,\n guidance:\n typeof data?.guidance === 'string'\n ? data.guidance\n : 'Present your canonical ASTRA-* id (discover it via GET /api/agents/request-registration/{requestId} after owner approval).',\n },\n ],\n },\n };\n }\n\n // 5.12.0: a presented agent id the backend doesn't know (404 \"Agent not\n // found\") is a deterministic failed identity claim — like the malformed\n // id above — not an outage. It used to fall through to api_error, so\n // merchants answered \"temporarily unavailable, retry\" to an agent that\n // simply isn't registered. A 404 without an agent id in the request (or\n // a non-JSON 404, i.e. a wrong apiBaseUrl) stays on the api_error path.\n if (\n response.status === 404 &&\n credentials.astraId &&\n (data?.error === 'AGENT_NOT_FOUND' ||\n (typeof data?.message === 'string' && /agent/i.test(data.message)))\n ) {\n return {\n success: true,\n attemptId: typeof data?.attemptId === 'string' ? data.attemptId : undefined,\n access: {\n allowed: false,\n reason: 'unknown_agent',\n failures: [\n {\n dimension: 'identity.unknown_agent',\n message: 'The presented agent identifier is not registered with AstraSync.',\n guidance: UNKNOWN_AGENT_GUIDANCE,\n },\n ],\n },\n };\n }\n\n if (!response.ok) {\n // 4.0.0: a 401 from verify-access can ONLY mean the backend rejected\n // THIS INTEGRATION'S own API key — agent denials come back as 200 with\n // access.allowed=false. Surface it as merchant-misconfig, not a\n // transient api_error: it is deterministic until the operator rotates\n // the key, and the \"retry with backoff\" template actively misleads.\n // Backends ≥ this round also send code: api_key_* + remediation URLs.\n // 5.13.0: a 403 COUNTERPARTY_KEY_MISMATCH is the same class — this\n // integration's key belongs to an account that doesn't own the endpoint.\n const keyMismatch = response.status === 403 && data?.error === 'COUNTERPARTY_KEY_MISMATCH';\n if (response.status === 401 || keyMismatch) {\n return {\n success: false,\n error: data.message || data.error || 'Integration API key rejected',\n misconfig: true,\n misconfigCode: keyMismatch\n ? 'counterparty_key_mismatch'\n : typeof data?.code === 'string'\n ? data.code\n : undefined,\n remediation:\n data?.remediation && typeof data.remediation === 'object'\n ? (data.remediation as { docsUrl?: string; manageUrl?: string })\n : undefined,\n correlationId: typeof data?.correlationId === 'string' ? data.correlationId : undefined,\n attemptId: typeof data?.attemptId === 'string' ? data.attemptId : undefined,\n };\n }\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 // 5.12.0: keep the status + Retry-After so adapters can relay the\n // platform's back-off (429 rate limit vs 5xx outage).\n return {\n success: false,\n error:\n (typeof data?.message === 'string' && data.message) ||\n (typeof data?.error === 'string' && data.error) ||\n `API returned ${response.status}`,\n correlationId: typeof data?.correlationId === 'string' ? data.correlationId : undefined,\n attemptId: typeof data?.attemptId === 'string' ? data.attemptId : undefined,\n httpStatus: response.status,\n ...(retryAfterMs !== undefined && { retryAfterMs }),\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 * Options for a single verify() call (as opposed to GatewayConfig, which is\n * per-gateway). Mirrors the Java SDK's `verify(request, bypassCache)`.\n */\nexport interface VerifyOptions {\n /**\n * Skip the cache READ for this call so the request is guaranteed to reach\n * the backend (the result is still written to the cache if cacheable).\n * Used by the step-up hold-and-poll retry: the re-verify after an approval\n * must hit the server to redeem the single-use approval.\n */\n bypassCache?: boolean;\n}\n\n/**\n * Main verification function\n */\nexport async function verify(\n config: GatewayConfig,\n request: VerificationRequest,\n options?: VerifyOptions\n): Promise<VerificationResult> {\n const mergedConfig = { ...DEFAULT_CONFIG, ...config };\n\n // v2.5.0: read well-known URLs from cache (prefetched by adapter at middleware creation).\n // Synchronous — never triggers a fetch. If cache is empty, guidance URLs are empty strings.\n const urls = mergedConfig.apiBaseUrl\n ? getCachedWellKnownUrls(mergedConfig.apiBaseUrl)\n : undefined;\n\n // One-time init self-test — fire-and-forget by default, never blocks verify().\n // Audit F-A6-33: when GatewayConfig.strictInit is set, the init promise is\n // awaited and any failure throws synchronously so callers learn about\n // misconfigured apiBaseUrl on the first verify() call rather than silently\n // running with broken auth.\n if (!initCheckPerformed && !mergedConfig.disableInitChecks && mergedConfig.apiBaseUrl) {\n if (mergedConfig.strictInit) {\n await performInitCheck(\n mergedConfig.apiBaseUrl,\n mergedConfig.debug,\n true,\n mergedConfig.apiKey,\n {\n id: mergedConfig.counterpartyId,\n url: mergedConfig.counterpartyUrl ?? request.counterpartyUrl,\n }\n );\n } else {\n void performInitCheck(\n mergedConfig.apiBaseUrl,\n mergedConfig.debug,\n false,\n mergedConfig.apiKey,\n {\n id: mergedConfig.counterpartyId,\n url: mergedConfig.counterpartyUrl ?? request.counterpartyUrl,\n }\n );\n }\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 decisions ' +\n '(verified ID + runtime challenge + PDLSS + trust score). 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. Caching on unless caller explicitly sets cacheTtl: 0\n // to disable. Undefined falls through to the 60s default at cacheResult\n // write time. options.bypassCache skips the read only (hold-and-poll retry\n // must reach the backend to redeem its approval).\n if (mergedConfig.cacheTtl !== 0 && !options?.bypassCache) {\n const cached = getCachedResult(request, mergedConfig.counterpartyId);\n if (cached) {\n if (mergedConfig.debug) {\n console.log('[VerificationGateway] Returning cached result');\n }\n // 3.12.0 (Trust Record, §33.5 #3): attemptId is excluded from the cache\n // key, so the stored result may carry a PRIOR attempt's id. Overwrite it\n // with this request's own attemptId — or drop it when this request has\n // none — a cached verdict must never leak another attempt's handle.\n // Shallow clone: never mutate the stored entry (a concurrent holder of\n // the same cached object may still be reading it).\n const hit: VerificationResult = { ...cached };\n if (request.attemptId) {\n hit.attemptId = request.attemptId;\n } else {\n delete hit.attemptId;\n }\n return hit;\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 // 3.6.0: the server emits a top-level denialReasons array on every deny\n // path (same strings as the failure messages). Non-empty → authoritative;\n // absent/empty → fall back to the client-side reconstructions below.\n const rawServerDenialReasons = (apiResponse as Record<string, unknown>).denialReasons;\n const serverDenialReasons =\n Array.isArray(rawServerDenialReasons) && rawServerDenialReasons.length > 0\n ? (rawServerDenialReasons as string[])\n : undefined;\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 // 4.0.0: a rejected merchant key gets its own source — deterministic\n // misconfig must not wear the \"temporarily unavailable, retry\" template.\n return createGuidanceResponse(mergedConfig, apiResponse.error, {\n source: apiResponse.misconfig ? 'merchant_misconfig' : 'api_error',\n correlationId: (apiResponse as { correlationId?: string }).correlationId,\n attemptId: (apiResponse as { attemptId?: string }).attemptId,\n remediation: apiResponse.remediation,\n httpStatus: apiResponse.httpStatus,\n retryAfterMs: apiResponse.retryAfterMs,\n urls,\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 // Round-18 G4: backend denied access (PDLSS or other gate); identity status\n // depends on whether the backend resolved the caller. Read from\n // verificationContext.idVerified; default false if absent (anonymous or\n // identity-fail paths land here too). policyAllowed is false by definition\n // in this branch (apiResponse.access.allowed === false).\n // 5.12.0 (round-27.4 D4): a presented id that matches no registered\n // agent is never \"identity verified\", whatever an older backend stamped.\n const idVerifiedFromBackend =\n (apiResponse.verificationContext as { idVerified?: boolean } | undefined)?.idVerified ===\n true && !aggregatedFailures?.some((f) => REJECTED_IDENTITY_DIMENSIONS.has(f.dimension));\n const result: EnhancedVerificationResult = {\n identityVerified: idVerifiedFromBackend,\n policyAllowed: false,\n // 3.6.0: the server now emits top-level denialReasons on every deny\n // path — prefer it verbatim; the failure-message reconstruction remains\n // as the fallback for older backends.\n denialReasons:\n serverDenialReasons ??\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 // 5.12.0: a presented-but-rejected id (malformed / unknown) carries\n // actionable registration guidance on its failure — surface it\n // instead of the bare machine reason ('unknown_agent').\n message:\n rejectedIdentityGuidance(aggregatedFailures) ??\n (apiResponse.access?.reason || 'Access denied by PDLSS policy'),\n registrationUrl: urls?.registrationUrl ?? '',\n documentationUrl: urls?.documentationUrl ?? '',\n // 5.1.0: anonymous callers land on this branch — steer MCP-hosted\n // agents to the connector (this is what the edge deny body surfaces).\n ...(mcpGuidance(urls) && { mcp: mcpGuidance(urls) }),\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 // Anonymous traffic has no session → correlationId is the per-attempt\n // linking key (the sessionId-equivalent for anonymous callers).\n correlationId: (apiResponse as Record<string, unknown>).correlationId as string | undefined,\n // 3.12.0 (Trust Record): server echoes the attempt-chain handle on\n // every branch — denials included (the funnel outlives one deny).\n attemptId: (apiResponse as Record<string, unknown>).attemptId 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 stepUpApproval: (apiResponse as Record<string, unknown>).stepUpApproval as\n | StepUpApprovalInfo\n | undefined,\n settlement: (apiResponse as Record<string, unknown>).settlement as\n | SettlementArtifact\n | undefined,\n settlementOutcome: (apiResponse as Record<string, unknown>).settlementOutcome as\n | SettlementOutcomeInfo\n | undefined,\n // 5.5.0 self-settlement: merchant-only material — passes through to the\n // MERCHANT caller only (bridge callers strip it before the agent plane).\n settlementToken: (apiResponse as Record<string, unknown>).settlementToken as\n | MerchantSettlementToken\n | undefined,\n // 5.6.0: fulfilment contact — merchant-only lane, same strip rule.\n fulfilment: (apiResponse as Record<string, unknown>).fulfilment as FulfilmentInfo | 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 blockchainAnchored: apiResponse.agent.blockchainStatus === 'anchored',\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 const result: EnhancedVerificationResult = {\n // 4.5.0 (probe F1): read idVerified from verificationContext with the\n // SAME absent⇒false default as the deny branch. The previous `!== false`\n // default manufactured `identityVerified: true` for anonymous\n // allow_full/allow_partial/audit responses (which allow access but omit\n // verificationContext entirely) — the \"anonymous caller labelled\n // verified\" defect. Every authenticated success response has carried\n // `verificationContext.idVerified: true` since v2.2.4, so a truth claim\n // this load-bearing must never be defaulted on.\n identityVerified:\n (apiResponse.verificationContext as { idVerified?: boolean } | undefined)?.idVerified ===\n true,\n policyAllowed: true,\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 // 3.12.0 (Trust Record): server-echoed attempt-chain handle.\n attemptId: (apiResponse as Record<string, unknown>).attemptId 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 stepUpApproval: (apiResponse as Record<string, unknown>).stepUpApproval as\n | StepUpApprovalInfo\n | undefined,\n settlement: (apiResponse as Record<string, unknown>).settlement as\n | SettlementArtifact\n | undefined,\n settlementOutcome: (apiResponse as Record<string, unknown>).settlementOutcome as\n | SettlementOutcomeInfo\n | undefined,\n settlementToken: (apiResponse as Record<string, unknown>).settlementToken as\n | MerchantSettlementToken\n | undefined,\n // 5.6.0: fulfilment contact — merchant-only lane, same strip rule.\n fulfilment: (apiResponse as Record<string, unknown>).fulfilment as FulfilmentInfo | undefined,\n };\n\n // Enforce AstraSync recommendation\n if (result.recommendation === 'deny') {\n // Round-18 G4: recommendation-driven deny lands on the success-path\n // construction (identity was resolved). Flip policy only — identity stays\n // verified — so adapters return 403 (re-auth won't help; the policy\n // decision is the blocker).\n result.policyAllowed = false;\n // 3.6.0: server-sent top-level denialReasons wins; recommendationReasons\n // reconstruction is the fallback for older backends.\n result.denialReasons = serverDenialReasons ??\n result.recommendationReasons ?? ['Access denied by AstraSync recommendation'];\n result.guidance = result.runtimeChallenge\n ? {\n message: `Verification failed: ${result.runtimeChallenge.reason || 'runtime challenge failed'}`,\n registrationUrl: urls?.registrationUrl ?? '',\n documentationUrl: urls?.documentationUrl ?? '',\n }\n : {\n message: result.recommendationReasons?.[0] || 'Access denied by AstraSync recommendation',\n registrationUrl: urls?.registrationUrl ?? '',\n documentationUrl: urls?.documentationUrl ?? '',\n };\n } else if (result.recommendation === 'step_up_required' && result.identityVerified) {\n // The escalation signal rides on `requiresStepUp` / `stepUpApproval`, not\n // on any masquerade of the (now-removed) access band.\n //\n // 5.12.0 (round-27.4 D1): step-up is \"allowed-but-escalate\" — the\n // registered agent's OWNER approves a valued action. An unidentified\n // caller has no owner to escalate to, so an allowed response for one is\n // a grant: older backends labelled recognised-but-unregistered agents\n // (Claude-User, the AstraSync bridge) step_up_required on plain reads,\n // and the adapters 401'd an anonymous catalog GET as \"requires human\n // approval\". The backend no longer emits that; this keeps merchants on\n // 5.12.0 correct against an older backend too. Denies are untouched —\n // they take the !allowed branch above.\n result.requiresStepUp = true;\n result.denialReasons = serverDenialReasons ??\n result.recommendationReasons ?? ['Step-up verification required'];\n }\n\n // Cache the result. Never cache denials (agent may fix challenge endpoint\n // and retry) NOR step-up escalations (3.6.0 — the same bug the Java SDK\n // had: a cached step_up_required verdict keeps denying retries the owner\n // has already approved, and its pollUrl goes stale once consumed/expired).\n // Escalations stay uncached even when the caller pins an explicit cacheTtl.\n const escalates =\n result.requiresStepUp === true ||\n result.requiresApproval === true ||\n result.recommendation === 'step_up_required';\n if (mergedConfig.cacheTtl !== 0 && result.recommendation !== 'deny' && !escalates) {\n cacheResult(request, result, mergedConfig.cacheTtl, mergedConfig.counterpartyId);\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 */\nexport async function recordDecision(\n config: GatewayConfig,\n sessionId: string,\n decision: 'granted' | 'denied',\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 sdkFetch(`${config.apiBaseUrl}/agents/verify-access/${sessionId}/decision`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ decision, reason }),\n }).catch(() => {\n /* fire-and-forget */\n });\n}\n\n/**\n * Report post-hoc consideration/outcome data for an attempt chain (Trust\n * Record). Use when the signal never reached verify-access: a\n * catalog served from a local cache, a transport-side deny (sku not found,\n * over budget), or a terminal outcome (settled / blocked on step-up).\n * Authenticated with the same credentials as verify(); the backend responds\n * 202 and folds the data into the attempt chain keyed by `attemptId`.\n *\n * Fire-and-forget — never throws. 5.12.0: a 429/5xx is retried once\n * (Retry-After honoured, capped at 2s) off the caller's path; a lost report\n * is surfaced via `options.onFailure`.\n */\nexport async function reportAttempt(\n config: GatewayConfig,\n report: AttemptReport,\n options: ReportAttemptOptions = {}\n): Promise<void> {\n const apiBaseUrl = config.apiBaseUrl || DEFAULT_CONFIG.apiBaseUrl!;\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 // counterpartyId resolution mirrors reportUnregisteredAttempt: an explicit\n // report value wins; otherwise attribute to the configured endpoint.\n const body = {\n ...report,\n ...(!report.counterpartyId &&\n config.counterpartyId && { counterpartyId: config.counterpartyId }),\n };\n\n const url = `${apiBaseUrl}/agents/verify-access/attempt-report`;\n const init = { method: 'POST', headers, body: JSON.stringify(body) };\n // 5.12.0: check the status — a 429/5xx used to vanish silently (the 1 Oct\n // load test lost attempt reports this way). One bounded retry honouring\n // Retry-After (capped at 2s), scheduled OFF the caller's path; never throws.\n const fail = (failure: { status?: number; error?: string; retried: boolean }): void => {\n try {\n options.onFailure?.(failure);\n } catch {\n /* a reporter callback must never break the caller */\n }\n };\n let response: Response | undefined;\n try {\n response = await sdkFetch(url, init);\n } catch (err) {\n fail({ error: err instanceof Error ? err.message : String(err), retried: false });\n return;\n }\n if (response.ok) return;\n if (response.status !== 429 && response.status < 500) {\n fail({ status: response.status, retried: false });\n return;\n }\n const delayMs = Math.min(ATTEMPT_REPORT_MAX_RETRY_DELAY_MS, parseRetryAfterMs(response) ?? 500);\n const timer = setTimeout(() => {\n sdkFetch(url, init)\n .then((retry) => {\n if (!retry.ok) fail({ status: retry.status, retried: true });\n })\n .catch((err) => {\n fail({ error: err instanceof Error ? err.message : String(err), retried: true });\n });\n }, delayMs);\n (timer as { unref?: () => void }).unref?.();\n}\n\n/** 5.12.0: options for {@link reportAttempt}. */\nexport interface ReportAttemptOptions {\n /**\n * Called once when the report is finally lost (non-retryable status,\n * network error, or the single retry failed). For counters/telemetry;\n * exceptions it throws are swallowed.\n */\n onFailure?: (failure: { status?: number; error?: string; retried: boolean }) => void;\n}\n\n/** 5.12.0: upper bound on reportAttempt's single retry delay. */\nconst ATTEMPT_REPORT_MAX_RETRY_DELAY_MS = 2000;\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 */\n/** 5.13.0: endpoints whose policy read was refused (log once each). */\nconst refusedPolicyReads = new Set<string>();\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 sdkFetch(\n `${config.apiBaseUrl}/endpoints/${encodeURIComponent(counterpartyId)}/routes`,\n { method: 'GET', headers }\n );\n if (!response.ok) {\n // 5.13.0: a refused policy read is not \"no policy configured\". The key\n // can't see this endpoint (another account's key, or a wrong id), so\n // the middleware is passing EVERY request through unverified. Say so,\n // loudly, once per endpoint.\n if (\n (response.status === 401 || response.status === 403 || response.status === 404) &&\n !refusedPolicyReads.has(counterpartyId)\n ) {\n refusedPolicyReads.add(counterpartyId);\n // eslint-disable-next-line no-console\n console.error(\n `[VerificationGateway] the configured API key cannot read the route policy for ${counterpartyId} ` +\n `(HTTP ${response.status}). The key belongs to an account that does not own this endpoint, ` +\n `or the counterpartyId is wrong. The gateway is in PASS-THROUGH mode: no agent is being verified.`\n );\n }\n return null;\n }\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 /** Pass through unenforced (observe). Replaces `minAccessLevel: 'none'`. */\n observe?: boolean;\n minTrustScore?: number;\n requiredPurposes?: string[];\n allowedPurposes?: string[];\n allowedJurisdictions?: string[];\n maxDuration?: number;\n maxTransactionValue?: number;\n requirePurpose?: boolean;\n /** Send-mapping — see RouteAccessConfig in types.ts. */\n purpose?: string;\n action?: string;\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 options?: VerifyOptions\n): Promise<VerificationResult> {\n const mergedConfig = { ...DEFAULT_CONFIG, ...config };\n const result = await verify(mergedConfig, { ...request, createSession: true }, options);\n const sessionId = (result as EnhancedVerificationResult).sessionId;\n\n if (sessionId) {\n // Round-18 G4: a session is \"granted\" only if identity verified AND\n // policy allowed; either failing is a deny.\n if (result.identityVerified && result.policyAllowed) {\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 /**\n * 3.6.0: optional — the backend attributes by counterpartyId first (sent\n * from config below) and requires at least ONE of counterpartyId/URL.\n * A null/undefined URL is omitted from the body, not sent as null.\n */\n counterpartyUrl?: string;\n counterpartyType?: string;\n sourceIp?: string;\n userAgent?: string;\n requestPath?: string;\n requestMethod?: string;\n /**\n * VI uplift Part 3 — attempt-chain handle (`att_<32hex>`, from the\n * X-Astra-Attempt-Id header or an `?astra_attempt=` deep link). Joins\n * the beacon into the agent's research chain. Attribution only.\n */\n attemptId?: string;\n /** First-class referer (traffic-source reporting). */\n referer?: string;\n /** The caller's Accept header (surface-classifier input). */\n accept?: string;\n /** Self-declared platform from X-Astra-Source (attribution, never trust). */\n declaredSource?: string;\n /**\n * 3.9.0 — edge-adapter evidence fields, persisted into the\n * event's eventData by the backend: `protocol` = detected transport/\n * credential protocol; `evidence` = light crypto-free detection detail.\n */\n protocol?: string;\n evidence?: Record<string, unknown>;\n /**\n * Maximal metadata capture — the full sanitised metadata for an\n * unregistered caller (every signal legitimately visible on the wire).\n * Persisted into eventData; feeds platform attribution and (via\n * correlationId) registration auto-fill. Fire-and-forget, so free.\n */\n observedMetadata?: ObservedMetadata;\n }\n): Promise<void> {\n const apiBaseUrl = config.apiBaseUrl || DEFAULT_CONFIG.apiBaseUrl!;\n\n const { counterpartyUrl, ...rest } = data;\n const body = {\n ...rest,\n ...(counterpartyUrl && { counterpartyUrl }),\n ...(config.counterpartyId && { counterpartyId: config.counterpartyId }),\n };\n\n // 5.12.0: send the merchant's API key when configured — the backend then\n // stamps the row `attributionVerified: true` (it checks the key owns the\n // counterparty). Without a key the beacon is still accepted, but flagged\n // as unverified attribution in Visit Intelligence.\n await sdkFetch(`${apiBaseUrl}/verification-activity/unregistered-attempt`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(config.apiKey && { Authorization: `Bearer ${config.apiKey}` }),\n },\n body: JSON.stringify(body),\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 sdkFetch(`${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 — checks credentials and policy in one call.\n *\n * The return shape mirrors `VerificationResult`'s split — partners\n * writing custom handlers around `quickVerify` get the same identity/policy\n * distinction as those calling `verify()` directly. Map to HTTP status the\n * same way: `!identityVerified` → 401; `identityVerified && !policyAllowed`\n * → 403.\n */\nexport async function quickVerify(\n config: GatewayConfig,\n credentials: AgentCredentials\n): Promise<{\n identityVerified: boolean;\n policyAllowed: boolean;\n reason?: string;\n /**\n * 5.12.0: verification infrastructure was unavailable (timeout / 5xx /\n * the platform's 429) — NOT a denial. Callers should fail open on reads\n * or answer 503 + Retry-After (`retryAfterMs`, else 30s), never 401.\n */\n unavailable?: true;\n retryAfterMs?: number;\n}> {\n const result = await verify(config, {\n credentials,\n purpose: 'verification',\n });\n\n return {\n identityVerified: result.identityVerified,\n policyAllowed: result.policyAllowed,\n reason: result.denialReasons?.[0],\n ...(isVerificationUnavailable(result) && {\n unavailable: true as const,\n retryAfterMs: unavailableRetryAfterSeconds(result) * 1000,\n }),\n };\n}\n"],"mappings":"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","names":["has","result"]}
|
|
1
|
+
{"version":3,"sources":["../src/version.ts","../src/http.ts","../src/trust-levels.ts","../src/well-known.ts","../src/failure-posture.ts","../src/verify.ts"],"sourcesContent":["/**\n * Single source-of-truth for the SDK's\n * package version emitted on verify-access bodies (and any future\n * telemetry). Bumped alongside `package.json#version` on every release.\n *\n * Why a constant rather than `import pkg from '../package.json'`:\n * - `tsconfig.json` sets `rootDir: ./src`; importing the sibling\n * package.json fails the build with \"outside rootDir\".\n * - Build-time string replacement (tsup `define`, esbuild banner, etc.)\n * adds toolchain coupling for a trivial gain.\n * - Embedded readonly constant works in every environment (Node, browser,\n * bundlers, Deno) without runtime fs / network access.\n *\n * Release discipline: a CI lint can grep `package.json#version` against\n * this constant if the two ever diverge in the wild. A manual bump\n * is fine — bumping both in the release-ceremony commit keeps them\n * lockstep.\n */\nexport const SDK_VERSION = '5.15.0';\n","/**\n * Shared outbound-HTTP wrapper — every request the SDK makes to the\n * AstraSync backend (or anywhere else) goes through `sdkFetch` so it\n * carries an identifying User-Agent.\n *\n * Why: Node's global fetch defaults to `user-agent: node`, which is\n * indistinguishable from every other Node client on the wire. Visit\n * Intelligence observed our own SDK/beacon traffic as anonymous \"node\"\n * visits. Self-identification is the platform's own medicine.\n *\n * Browser note: some browsers filter the User-Agent request header\n * silently (never throwing), so this is a no-op there — which is correct:\n * in a browser the page's real UA is the honest identity.\n */\n\nimport { SDK_VERSION } from './version';\n\nexport const SDK_USER_AGENT = `astrasync-sdk/${SDK_VERSION}`;\n\n/**\n * Add the SDK User-Agent without changing the SHAPE of the caller's headers\n * (plain records stay plain records) — interceptors, adapters, and tests\n * read `init.headers` as a record, and `Headers` would also lowercase names.\n */\nfunction withUserAgent(initHeaders: HeadersInit | undefined): HeadersInit {\n if (!initHeaders) return { 'user-agent': SDK_USER_AGENT };\n if (initHeaders instanceof Headers) {\n const copy = new Headers(initHeaders);\n if (!copy.has('user-agent')) copy.set('user-agent', SDK_USER_AGENT);\n return copy;\n }\n if (Array.isArray(initHeaders)) {\n const has = initHeaders.some(([name]) => name.toLowerCase() === 'user-agent');\n return has ? initHeaders : [...initHeaders, ['user-agent', SDK_USER_AGENT]];\n }\n const has = Object.keys(initHeaders).some((name) => name.toLowerCase() === 'user-agent');\n return has ? initHeaders : { 'user-agent': SDK_USER_AGENT, ...initHeaders };\n}\n\nexport const sdkFetch: typeof fetch = (input, init) =>\n fetch(input, { ...init, headers: withUserAgent(init?.headers) });\n","/**\n * AstraSync Universal Verification Gateway — Trust Level helpers.\n *\n * SDK 5.0.0: the `accessLevel` band (`none`/`restricted`/`read-only`/`standard`/\n * `full`/`internal`) was removed entirely — it never gated the access decision\n * (the server does, from a verified ID + runtime challenge + PDLSS match + trust\n * score). What remains here is the coarse trust-level badge derived from the\n * numeric trust score, used only for display.\n */\n\nimport type { TrustLevel } from './types';\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","import { sdkFetch } from './http';\n/**\n * SDK-side discovery of canonical platform URLs via `/.well-known/agentic-commerce`.\n *\n * Fire-and-forget pre-fetch at middleware creation; first verify() awaits\n * the in-flight promise if it hasn't resolved. 60-minute TTL with\n * stale-while-revalidate background refresh.\n */\n\nexport interface WellKnownAgenticCommerce {\n registrationUrl: string;\n documentationUrl: string;\n verifyAccessUrl: string;\n /** 5.1.0: hosted AstraSync MCP bridge endpoint (absent on self-hosted backends). */\n mcpEndpoint?: string;\n /** 5.1.0: bridge connector-discovery doc (absent on self-hosted backends). */\n mcpDiscoveryUrl?: string;\n}\n\ninterface CacheEntry {\n data: WellKnownAgenticCommerce;\n fetchedAt: number;\n}\n\nconst CACHE_TTL_MS = 60 * 60 * 1000; // 60 minutes\n\nconst cache = new Map<string, CacheEntry>();\nconst inflight = new Map<string, Promise<WellKnownAgenticCommerce>>();\n\nfunction wellKnownUrl(apiBaseUrl: string): string {\n // apiBaseUrl is typically 'https://astrasync.ai/api' — strip the /api suffix\n // to get the platform root, then append /.well-known/agentic-commerce.\n const base = apiBaseUrl.replace(/\\/api\\/?$/, '');\n return `${base}/.well-known/agentic-commerce`;\n}\n\nasync function fetchWellKnown(apiBaseUrl: string): Promise<WellKnownAgenticCommerce> {\n const url = wellKnownUrl(apiBaseUrl);\n const response = await sdkFetch(url, {\n method: 'GET',\n headers: { Accept: 'application/json' },\n signal: AbortSignal.timeout(5000),\n });\n if (!response.ok) {\n throw new Error(\n `AstraSync platform must expose /.well-known/agentic-commerce; ` +\n `got ${response.status} from ${url}. SDK cannot initialise without it.`\n );\n }\n const data = (await response.json()) as Record<string, unknown>;\n if (!data.registrationUrl || !data.documentationUrl || !data.verifyAccessUrl) {\n throw new Error(\n `/.well-known/agentic-commerce response missing required fields ` +\n `(registrationUrl, documentationUrl, verifyAccessUrl).`\n );\n }\n return data as unknown as WellKnownAgenticCommerce;\n}\n\n/**\n * Start a background fetch. Returns the promise for callers that need\n * to await it (first verify() call).\n */\nexport function prefetchWellKnown(apiBaseUrl: string): Promise<WellKnownAgenticCommerce> {\n const existing = inflight.get(apiBaseUrl);\n if (existing) return existing;\n\n const promise = fetchWellKnown(apiBaseUrl)\n .then((data) => {\n cache.set(apiBaseUrl, { data, fetchedAt: Date.now() });\n inflight.delete(apiBaseUrl);\n return data;\n })\n .catch((err) => {\n inflight.delete(apiBaseUrl);\n throw err;\n });\n\n inflight.set(apiBaseUrl, promise);\n return promise;\n}\n\n/**\n * Get cached well-known URLs. If stale, triggers a background refresh\n * and returns stale data (stale-while-revalidate). If no cache exists,\n * awaits the in-flight fetch or starts a new one.\n */\nexport async function getWellKnownUrls(apiBaseUrl: string): Promise<WellKnownAgenticCommerce> {\n const entry = cache.get(apiBaseUrl);\n\n if (entry) {\n if (Date.now() - entry.fetchedAt > CACHE_TTL_MS) {\n // Stale — trigger background refresh, return stale data\n prefetchWellKnown(apiBaseUrl).catch(() => {\n // Background refresh failed — stale data remains until next request\n });\n }\n return entry.data;\n }\n\n // No cache — must await\n const pending = inflight.get(apiBaseUrl);\n if (pending) return pending;\n\n return prefetchWellKnown(apiBaseUrl);\n}\n\n/**\n * Synchronous cache read — returns cached URLs or undefined.\n * Never triggers a fetch. Used by verify() to avoid extra HTTP calls\n * in the hot path; adapters are responsible for prefetching.\n */\nexport function getCachedWellKnownUrls(apiBaseUrl: string): WellKnownAgenticCommerce | undefined {\n return cache.get(apiBaseUrl)?.data;\n}\n\n/** Reset cache — for testing only. */\nexport function _resetWellKnownCache(): void {\n cache.clear();\n inflight.clear();\n}\n","/**\n * 5.12.0 — HTTP adapter failure posture, lifted from edge-core\n * (`pipeline.ts` isApiErrorResult/unavailableOutcome, `enforce.ts` 503 +\n * Retry-After + buildUnavailableBody) so the Express and Next.js adapters\n * and `quickVerify` answer an AstraSync outage the way the edge does.\n *\n * The 1 Oct load test: the platform rate-limited a merchant's verify-access\n * calls (429), the SDK mapped that to `UNAUTHORIZED` 401 and failed closed\n * — even for anonymous catalogue reads — so the merchant was down for 11\n * minutes. Fail posture follows success posture:\n *\n * - observe routes and anonymous reads (GET/HEAD, no credentials) pass\n * through, stamped `X-Astra-Verification: unavailable`;\n * - enforced routes answer 503 + `Retry-After` (the backend's value, else\n * 30s) + `VERIFICATION_UNAVAILABLE`. Never 401 — the caller's identity\n * was never evaluated.\n *\n * Identity status mapping (contract correction, not a break): credentials\n * missing → 401; a presented id that is malformed or unknown → 403 with\n * registration guidance (re-authenticating with the same id cannot help).\n */\nimport type { AgentCredentials, VerificationResult } from './types';\n\n/** Response header stamped on a fail-open pass-through during an outage. */\nexport const VERIFICATION_STATUS_HEADER = 'X-Astra-Verification';\n\n/** Default Retry-After (seconds) when the backend sent none — edge parity. */\nexport const DEFAULT_UNAVAILABLE_RETRY_AFTER_S = 30;\n\n/**\n * Failure dimensions for a presented-but-rejected agent identifier.\n * `agent.lookup` is the backend's 200-with-deny shape for a well-formed id\n * that matches no registered agent (round-27.4 D4 — the same verdict as the\n * 404 → `identity.unknown_agent` mapping, delivered on a different wire).\n */\nexport const REJECTED_IDENTITY_DIMENSIONS: ReadonlySet<string> = new Set([\n 'identity.invalid_agent_id',\n 'identity.unknown_agent',\n 'agent.lookup',\n]);\n\n/** Registration guidance for a presented id that matches no registered agent. */\nexport const UNKNOWN_AGENT_GUIDANCE =\n 'Register your agent with AstraSync (or, inside an MCP host, add the AstraSync MCP connector for keyless, human-approved registration), then present the canonical ASTRA-* id issued after owner approval.';\n\n/**\n * Failure dimensions that mean \"verification could not be evaluated\" rather\n * than \"the agent was denied\":\n * - `verify_access.api_error` — the SDK's typed, retryable fallback for an\n * AstraSync outage (timeout, 5xx, the platform's 429);\n * - `middleware.internal_error` — an adapter's own `failOnError: 'closed'`\n * catch path (an exception inside the gateway middleware). Failing closed\n * there is a 503, never an identity-shaped 401.\n */\nexport const UNAVAILABLE_DIMENSIONS: ReadonlySet<string> = new Set([\n 'verify_access.api_error',\n 'middleware.internal_error',\n]);\n\n/**\n * A verify() result that is the SDK's typed, retryable api_error fallback —\n * infrastructure unavailability (timeout, 5xx, 429), not a policy deny and\n * not the deterministic merchant misconfig — or an adapter's internal-error\n * fail-closed result.\n */\nexport function isVerificationUnavailable(result: VerificationResult): boolean {\n if (result.misconfigured) return false;\n return (result.failures ?? []).some((f) => UNAVAILABLE_DIMENSIONS.has(f.dimension));\n}\n\n/** Retry-After seconds to relay: the backend's value (≥1s), else 30. */\nexport function unavailableRetryAfterSeconds(result: VerificationResult): number {\n if (typeof result.retryAfterMs === 'number' && Number.isFinite(result.retryAfterMs)) {\n return Math.max(1, Math.ceil(result.retryAfterMs / 1000));\n }\n return DEFAULT_UNAVAILABLE_RETRY_AFTER_S;\n}\n\n/** An anonymous read: GET/HEAD carrying no agent credential. */\nexport function isAnonymousRead(\n method: string | undefined,\n credentials: AgentCredentials\n): boolean {\n const m = (method ?? '').toUpperCase();\n if (m !== 'GET' && m !== 'HEAD') return false;\n return !credentials.astraId && !credentials.jwt;\n}\n\n/** Machine-readable 503 body — same shape as edge-core's buildUnavailableBody. */\nexport function buildVerificationUnavailableBody(result: VerificationResult): {\n success: false;\n error: {\n code: 'VERIFICATION_UNAVAILABLE';\n message: string;\n retryable: true;\n retryAfterSeconds: number;\n failures?: VerificationResult['failures'];\n correlationId?: string;\n guidance: { message: string };\n };\n} {\n return {\n success: false,\n error: {\n code: 'VERIFICATION_UNAVAILABLE',\n message:\n 'Agent verification is temporarily unavailable and this path enforces verification. ' +\n 'Your identity was not evaluated and no denial was recorded — retry after the Retry-After interval.',\n retryable: true,\n retryAfterSeconds: unavailableRetryAfterSeconds(result),\n ...(result.failures && { failures: result.failures }),\n ...(result.correlationId && { correlationId: result.correlationId }),\n guidance: {\n message:\n 'Retry the request after the Retry-After interval, with exponential backoff; the condition is transient.',\n },\n },\n };\n}\n\n/** True when the deny is about a presented-but-malformed/unknown agent id. */\nexport function isRejectedIdentityClaim(result: VerificationResult): boolean {\n return (result.failures ?? []).some((f) => REJECTED_IDENTITY_DIMENSIONS.has(f.dimension));\n}\n\n/**\n * HTTP status + code for an ordinary (non-outage, non-misconfig) deny:\n * missing identity → 401 UNAUTHORIZED; presented-but-rejected id → 403\n * AGENT_IDENTITY_REJECTED; identity fine but policy denied → 403 with the\n * adapter's policy code.\n */\nexport function denyStatusFor(\n result: VerificationResult,\n policyDeniedCode: string\n): { status: 401 | 403; code: string } {\n // Checked first: older backends stamp idVerified:true on the agent.lookup\n // (unknown id) deny, which would otherwise read as a policy deny.\n if (isRejectedIdentityClaim(result)) return { status: 403, code: 'AGENT_IDENTITY_REJECTED' };\n if (result.identityVerified) return { status: 403, code: policyDeniedCode };\n return { status: 401, code: 'UNAUTHORIZED' };\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 { sdkFetch } from './http';\nimport type {\n GatewayConfig,\n AgentCredentials,\n VerificationRequest,\n VerificationResult,\n VerifiedAgent,\n AgentProtocolIdentities,\n VerifiedDeveloper,\n VerifiedOrganization,\n GuidanceInfo,\n EnhancedVerificationResult,\n TokenGuidance,\n RuntimeChallengeResult,\n StepUpApprovalInfo,\n SettlementOutcomeInfo,\n MerchantSettlementToken,\n FulfilmentInfo,\n AstraSyncMandates,\n AstraSyncAepEvidence,\n SettlementArtifact,\n AttemptReport,\n} from './types';\nimport { getTrustLevel } from './trust-levels';\nimport type { ObservedMetadata } from './metadata-capture';\nimport { SDK_VERSION } from './version';\nimport { getCachedWellKnownUrls, type WellKnownAgenticCommerce } from './well-known';\nimport {\n REJECTED_IDENTITY_DIMENSIONS,\n UNKNOWN_AGENT_GUIDANCE,\n isVerificationUnavailable,\n unavailableRetryAfterSeconds,\n} from './failure-posture';\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 // minTrustScore + minTrustScoreForFull deprecated in v2.3.0 — server decides.\n // cacheTtl deliberately unset. When undefined, cacheResult applies the 60s\n // default; the caller's explicit value is honoured for cacheable results.\n // Denials and step-up escalations are NEVER cached (3.6.0) regardless of\n // cacheTtl. Set cacheTtl: 0 to disable caching entirely.\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/** Test-only: re-arm the one-time init self-check. */\nexport function _resetInitCheck(): void {\n initCheckPerformed = false;\n}\n\n/** One-shot guard for v2.3.0 deprecation warning. */\nlet deprecationWarningShown = false;\n\nasync function performInitCheck(\n apiBaseUrl: string,\n debug?: boolean,\n strictInit?: boolean,\n apiKey?: string,\n counterparty?: { id?: string; url?: string }\n): 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 sdkFetch(probeUrl, { method: 'HEAD' });\n const contentType = response.headers.get('content-type') ?? '';\n if (contentType.startsWith('text/html')) {\n const message =\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 // Audit F-A6-33: when strictInit is set, throw on misconfig instead\n // of warning + continuing. Recurring footgun per\n // `[[feedback_astrasync_api_url_canonical]]` — silent warn lets\n // misconfigured prod deploys ship with all verify-access calls\n // silently failing.\n if (strictInit) {\n throw new Error(`${message} (strictInit=true)`);\n }\n console.warn(`${message} Set disableInitChecks: true on GatewayConfig to silence.`);\n } else if (debug) {\n console.log(\n `[VerificationGateway] init check passed for ${apiBaseUrl} (content-type: ${contentType})`\n );\n }\n\n // 4.0.0: key liveness probe. A dead API key otherwise surfaces only\n // per-request as inbound agents get turned away — the operator should\n // learn at deploy time. 404 = older backend without self-check: silent.\n if (apiKey) {\n // 5.13.0: name the endpoint this SDK is installed on, so the backend\n // can say whether this key may verify for it (keys are bound to the\n // endpoint's owner — a key from another account is refused on every\n // request with COUNTERPARTY_KEY_MISMATCH).\n const q = counterparty?.id\n ? `?counterpartyId=${encodeURIComponent(counterparty.id)}`\n : counterparty?.url\n ? `?counterpartyUrl=${encodeURIComponent(counterparty.url)}`\n : '';\n const selfCheck = await sdkFetch(`${apiBaseUrl}/agents/verify-access/self-check${q}`, {\n headers: { Authorization: `Bearer ${apiKey}` },\n });\n if (selfCheck.status === 401) {\n const body = (await selfCheck.json().catch(() => ({}))) as {\n code?: string;\n message?: string;\n remediation?: { manageUrl?: string };\n };\n const message =\n `[VerificationGateway] the configured AstraSync API key was REJECTED at startup ` +\n `(${body.code ?? 'api_key_invalid'}: ${body.message ?? 'invalid key'}). Every inbound agent ` +\n `will be turned away until the key is rotated` +\n (body.remediation?.manageUrl ? ` at ${body.remediation.manageUrl}` : '') +\n `.`;\n if (strictInit) {\n throw new Error(`${message} (strictInit=true)`);\n }\n console.error(message);\n } else if (selfCheck.ok) {\n const body = (await selfCheck.json().catch(() => ({}))) as {\n daysToExpiry?: number | null;\n counterparty?: { found?: boolean; canVerify?: boolean; message?: string };\n };\n if (body.counterparty && body.counterparty.canVerify === false) {\n const message =\n `[VerificationGateway] the configured AstraSync API key cannot verify for this endpoint ` +\n `(${body.counterparty.message ?? 'endpoint not found or owned by another account'}). ` +\n `Every inbound agent will be turned away until the key or counterparty is corrected.`;\n if (strictInit) {\n throw new Error(`${message} (strictInit=true)`);\n }\n console.error(message);\n }\n if (typeof body.daysToExpiry === 'number' && body.daysToExpiry <= 7) {\n console.warn(\n `[VerificationGateway] the configured AstraSync API key expires in ${body.daysToExpiry} day(s) — ` +\n `rotate it at Settings → Developer Tools before integrations start failing.`\n );\n } else if (debug) {\n console.log('[VerificationGateway] API key self-check passed');\n }\n }\n }\n } catch (err) {\n if (strictInit) {\n // strict mode propagates init failures so callers get a clear,\n // synchronous \"your config is broken\" signal at startup.\n throw err;\n }\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 the full request shape.\n *\n * Historically the key was credentials-only (`astraId-apiKey-jwt`),\n * which silently returned the cached result of a prior call even when the\n * subsequent call's purpose/action/counterparty/etc. differed. That was a\n * security gap — a warm cache could serve an allow verdict for a purpose the\n * agent hadn't actually been authorised for. Key now includes every field\n * that affects the backend verdict.\n *\n * MAINTAIN: when adding a field to VerificationRequest that affects the\n * backend verdict, add it to this key. Fields that don't affect verdict\n * (e.g. requestId, timestamps, sdkVersion) don't belong here — including\n * them would needlessly thrash the cache for repeated identical requests.\n *\n * 3.12.0 (Trust Record): `attemptId` and `considerationSet` are\n * deliberately EXCLUDED — like `callerMetadata.observedMetadata` they are\n * observational pass-through that never affects the verdict, and keying on\n * a per-attempt id would defeat verdict caching entirely. Cache hits get\n * their attemptId rewritten in verify() instead (never leak a prior\n * attempt's id from a cached result).\n */\nfunction getCacheKey(request: VerificationRequest, counterpartyId?: string): string {\n const c = request.credentials;\n return [\n c.astraId || '',\n c.apiKey || '',\n c.jwt || '',\n request.purpose || '',\n request.action || '',\n request.resourceType || '',\n request.resource || '',\n request.jurisdiction || '',\n request.transactionValue ?? '',\n request.currency || '',\n // SECURITY (cross-merchant cache leak): the merchant identity is sent via\n // `config.counterpartyId`, NOT on the request, so it was previously absent\n // from the key — two verifies for the SAME agent/purpose/action/value but\n // DIFFERENT merchants collided, and a grant at a permissive merchant (low\n // trust floor) was served for a stricter one. Same bug class as the\n // duration omission (F-A1-07). counterpartyId affects the backend verdict\n // (trust floor / per-route policy), so it MUST key the cache.\n counterpartyId || '',\n request.counterpartyUrl || '',\n request.counterpartyType || '',\n request.isSubAgentRequest ? '1' : '0',\n request.parentAgentId || '',\n request.subAgentDepth ?? '',\n // Audit F-A1-07: previously-missing dimensions that DO affect the\n // backend verdict. Without these, two requests with different\n // durations (e.g. 60s vs 86400s) collided on the same cache key and\n // the shorter-duration allow served the longer-duration request.\n request.durationRequired ?? '',\n request.invocationProtocol || '',\n request.enableRuntimeChallenge ? '1' : '0',\n // callerMetadata fields contribute to risk model; include the ones\n // backend reads. sourceIp/userAgent/forwardedFor change per-request\n // so their inclusion effectively forces a re-check for any varying\n // client (the right behavior — IP-driven anomaly scoring shouldn't\n // be cached across IPs).\n request.callerMetadata?.sourceIp || '',\n request.callerMetadata?.userAgent || '',\n request.callerMetadata?.forwardedFor || '',\n request.callerMetadata?.agentCardUrl || '',\n // UAT2 Bug B (5.4.1): the commerce phase and cart identity DO affect the\n // backend verdict — the confirm leg is the ONLY leg that may execute\n // first-party settlement. Without these, a confirm within the cache TTL of\n // its start_checkout quote was served the cached quote-leg grant (no\n // settlementOutcome) and never reached the backend. Same F-A1-07 bug class.\n request.commercePhase || '',\n request.checkoutSessionId || '',\n ].join('|');\n}\n\n/**\n * Check if cached result is still valid\n */\nfunction getCachedResult(\n request: VerificationRequest,\n counterpartyId?: string\n): VerificationResult | null {\n const key = getCacheKey(request, counterpartyId);\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 *\n * 3.6.0: uniform 60s default. The earlier step-up TTL split (300s for\n * step-up verdicts) is gone — step-up verdicts are no longer cached AT ALL\n * (see the write predicate in verify()): a cached step_up_required result\n * carries a pollUrl that goes stale (consumed/expired) and, worse, keeps\n * denying retries that the owner has already approved. When `configuredTtl`\n * is set (truthy positive number), it's honoured for cacheable results.\n */\nconst DEFAULT_CACHE_TTL_SECONDS = 60;\n\nfunction cacheResult(\n request: VerificationRequest,\n result: VerificationResult,\n configuredTtl: number | undefined,\n counterpartyId?: string\n): void {\n const ttlSeconds = configuredTtl && configuredTtl > 0 ? configuredTtl : DEFAULT_CACHE_TTL_SECONDS;\n const key = getCacheKey(request, counterpartyId);\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 // Audit F-A1-12: ASTRA-ID shape validation. Accept only the canonical\n // pattern (ASTRA-/ASTRAE- prefix + 1-64 url-safe chars) to prevent CRLF\n // header injection or downstream interpolation surprises. Reject otherwise.\n const ASTRA_ID_PATTERN = /^ASTRAE?-[A-Za-z0-9_-]{1,64}$/;\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 // Audit F-A1-11: guard array-confusion. If multiple X-Astra-Id headers\n // were sent (Express collapses to comma-joined string on most paths),\n // take only the first canonical-shaped one.\n const raw = Array.isArray(astraIdHeader)\n ? astraIdHeader[0]\n : typeof astraIdHeader === 'string'\n ? astraIdHeader\n : undefined;\n if (typeof raw === 'string' && ASTRA_ID_PATTERN.test(raw)) {\n credentials.astraId = raw;\n }\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 // Audit F-A1-11: defensive type guard — Array.isArray gives a non-string,\n // and Express path quirks can produce undefined despite the truthy check.\n const authHeader = headers['authorization'] || headers['Authorization'];\n if (authHeader) {\n const authValue = Array.isArray(authHeader) ? authHeader[0] : authHeader;\n if (typeof authValue === 'string') {\n credentials.authorizationHeader = authValue;\n if (authValue.startsWith('Bearer ')) {\n credentials.jwt = authValue.slice(7);\n }\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. The sources are 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 *\n * 4.0.0 adds `'merchant_misconfig'`: the backend rejected THIS INTEGRATION'S\n * own API key (401 api_key_revoked/expired/invalid). Neither the agent's\n * fault nor a platform outage — retrying won't help, and the inbound agent\n * deserves to know the site operator's integration is broken (the\n * gadget-emporium failure mode: a rotated key silently 401ing every\n * credentialed agent for days).\n */\ntype GuidanceSource = 'no_credentials' | 'api_error' | 'merchant_misconfig';\n\n/**\n * MCP-connector steering (5.1.0). Present only when the backend's well-known\n * doc advertises a hosted bridge. The copy must directly counter the observed\n * model refusal (\"go register manually on the dashboard\"): registration via\n * the connector is the agent's own next action, with the human approval built\n * into the flow.\n */\nfunction mcpGuidance(urls?: WellKnownAgenticCommerce): GuidanceInfo['mcp'] {\n if (!urls?.mcpEndpoint) return undefined;\n return {\n endpoint: urls.mcpEndpoint,\n discoveryUrl: urls.mcpDiscoveryUrl,\n message:\n 'If you are operating inside an MCP-capable host (Claude, ChatGPT, Cursor, or any MCP client), ' +\n 'add the AstraSync MCP connector instead of integrating these URLs by hand: registration through ' +\n 'the connector is KEYLESS and HUMAN-APPROVED — your human approves in an in-chat card, and you ' +\n 'never see, hold, or transmit an API key, password, or private key.',\n };\n}\n\n/**\n * Create guidance response for unverified agents or for API-error fallback.\n *\n * The source is split 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: {\n source?: GuidanceSource;\n correlationId?: string;\n /** 3.12.0 (Trust Record): server-echoed attempt handle, threaded like correlationId. */\n attemptId?: string;\n urls?: WellKnownAgenticCommerce;\n /** 4.0.0: backend-provided remediation URLs for the merchant (misconfig source). */\n remediation?: { docsUrl?: string; manageUrl?: string };\n /** 5.12.0: backend non-2xx status + Retry-After (api_error source). */\n httpStatus?: number;\n retryAfterMs?: number;\n } = {}\n): VerificationResult {\n const source = options.source ?? 'no_credentials';\n const isApiError = source === 'api_error';\n const isMisconfig = source === 'merchant_misconfig';\n const urls = options.urls;\n\n const guidance: GuidanceInfo = isMisconfig\n ? {\n message:\n \"This site's AstraSync integration is misconfigured (its API key was revoked, expired, rotated, or belongs to another account). \" +\n 'Your identity and the AstraSync platform are fine — contact the site operator. Retrying will not help.',\n registrationUrl: urls?.registrationUrl ?? '',\n documentationUrl: options.remediation?.docsUrl ?? urls?.documentationUrl ?? '',\n steps: [\n 'Report the failure to the site operator — their AstraSync API key needs replacing',\n 'Do not retry: the failure is deterministic until the operator updates their key',\n ],\n }\n : isApiError\n ? {\n message:\n 'Verification is temporarily unavailable. Retry with exponential backoff; if the issue persists, contact support with the correlationId.',\n registrationUrl: urls?.registrationUrl ?? '',\n documentationUrl: urls?.documentationUrl ?? '',\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: urls?.registrationUrl ?? '',\n documentationUrl: urls?.documentationUrl ?? '',\n steps: [\n // MCP steering leads when the hosted bridge is advertised — the\n // connector is the agent-actionable path (keyless, human-approved).\n ...(urls?.mcpEndpoint\n ? [\n `Fastest path (inside an MCP host): add the AstraSync MCP connector at ${urls.mcpEndpoint} — keyless, human-approved registration`,\n ]\n : []),\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 ...(mcpGuidance(urls) && { mcp: mcpGuidance(urls) }),\n };\n\n return {\n // Round-18 G4: createGuidanceResponse fires for unverified-agent path or\n // API-error fallback. Identity is not verified (no agent resolved);\n // policy is not evaluated (we never reached the gate).\n identityVerified: false,\n policyAllowed: false,\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: isMisconfig\n ? [\n {\n dimension: 'verify_access.merchant_misconfigured',\n message: reason ?? 'The integration API key was rejected by AstraSync',\n guidance: guidance.message,\n },\n ]\n : isApiError\n ? [\n {\n dimension: 'verify_access.api_error',\n message: reason ?? 'Verification temporarily unavailable',\n guidance: guidance.message,\n },\n ]\n : undefined,\n // 4.0.0: typed marker so adapters render the misconfig envelope (503 +\n // MERCHANT_VERIFICATION_MISCONFIGURED) without string-matching.\n ...(isMisconfig && { misconfigured: true }),\n // 4.6.0: a transient api_error is retryable — mark it so consumers surface\n // \"temporarily unavailable, retry\" (recommendation 'error') instead of\n // coercing the absent recommendation to a policy 'deny'. Misconfig is\n // deterministic and intentionally NOT marked retryable.\n ...(isApiError && { retryable: true }),\n ...(options.httpStatus !== undefined && { httpStatus: options.httpStatus }),\n ...(options.retryAfterMs !== undefined && { retryAfterMs: options.retryAfterMs }),\n correlationId: options.correlationId,\n attemptId: options.attemptId,\n verifiedAt: new Date(),\n };\n}\n\n/**\n * 5.12.0: parse a response body as JSON without throwing — a non-JSON body\n * (HTML error page, plain-text 429) yields `{}` so the status survives.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nasync function readJsonSafe(response: Response): Promise<any> {\n try {\n const parsed = await response.json();\n return parsed && typeof parsed === 'object' ? parsed : {};\n } catch {\n return {};\n }\n}\n\n/** 5.12.0: human guidance for a presented-but-malformed/unknown agent id deny. */\nfunction rejectedIdentityGuidance(\n failures: Array<{ dimension: string; message: string; guidance?: string }> | undefined\n): string | undefined {\n const f = failures?.find((x) => REJECTED_IDENTITY_DIMENSIONS.has(x.dimension));\n if (!f) return undefined;\n // The backend's agent.lookup guidance only says \"check the id format\";\n // an unknown id needs the registration path (404 parity).\n if (f.dimension === 'agent.lookup') {\n return `The presented agent identifier is not registered with AstraSync. ${UNKNOWN_AGENT_GUIDANCE}`;\n }\n return f.guidance ? `${f.message} ${f.guidance}` : f.message;\n}\n\n/** Upper bound on a relayed Retry-After (a hostile/buggy value can't park callers for hours). */\nconst MAX_RETRY_AFTER_MS = 15 * 60 * 1000;\n\n/**\n * 5.12.0: `Retry-After` (delta-seconds or HTTP-date) → milliseconds, or\n * undefined when absent/unparseable. Clamped to [0, 15 min].\n */\nexport function parseRetryAfterMs(response: {\n headers?: { get?(name: string): string | null };\n}): number | undefined {\n const raw = response.headers?.get?.('retry-after');\n if (!raw) return undefined;\n const trimmed = raw.trim();\n let ms: number;\n if (/^\\d+(\\.\\d+)?$/.test(trimmed)) {\n ms = Number(trimmed) * 1000;\n } else {\n const at = Date.parse(trimmed);\n if (Number.isNaN(at)) return undefined;\n ms = at - Date.now();\n }\n return Math.min(MAX_RETRY_AFTER_MS, Math.max(0, Math.round(ms)));\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 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 protocolIdentities?: AgentProtocolIdentities;\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_configured';\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 /** LMAX-POC C1: underlying check timestamp for merchant freshness gates. */\n checkedAt: string;\n validUntil?: string;\n proofType: 'reference' | 'zkp';\n proof: string;\n }>;\n };\n error?: string;\n /**\n * 4.0.0: the backend 401'd this integration's OWN API key (revoked /\n * expired / invalid) — deterministic merchant-side misconfig, not a\n * transient failure. `misconfigCode` carries the backend's api_key_* code\n * and `remediation` its operator-facing URLs when present.\n */\n misconfig?: boolean;\n misconfigCode?: string;\n remediation?: { docsUrl?: string; manageUrl?: string };\n /**\n * 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 * 3.12.0 (Trust Record): the server echoes the attempt-chain\n * handle top-level on every response branch (error envelopes included).\n */\n attemptId?: string;\n /** 5.12.0: backend HTTP status on a non-2xx (429/5xx) — see VerificationResult.httpStatus. */\n httpStatus?: number;\n /** 5.12.0: backend Retry-After in ms, when sent. */\n retryAfterMs?: number;\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 ...(requestData.purpose && { purpose: requestData.purpose }),\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 // Round-12 (F19): transport-vs-intent separation. MCP middleware sets\n // this to 'mcp'; non-MCP callers leave it unset.\n if (requestData.invocationProtocol) body.invocationProtocol = requestData.invocationProtocol;\n // 5.3.0 (astra-pay): checkout-leg discriminator — first-party settlement\n // fires only on 'confirm'; absent keeps pre-phase server behavior.\n if (requestData.commercePhase) body.commercePhase = requestData.commercePhase;\n // Per-cart idempotency key (checkout session id) forwarded on the confirm\n // leg so concurrent confirms for one cart charge at most once.\n if (requestData.checkoutSessionId) body.checkoutSessionId = requestData.checkoutSessionId;\n if (requestData.checkoutJwt) body.checkoutJwt = requestData.checkoutJwt;\n // Order-plane cart lines (receipts + fulfillment email) — informational.\n if (requestData.checkoutItems) body.checkoutItems = requestData.checkoutItems;\n // 5.6.0 (astra-pay): transit-only fulfilment contact — echoed back in\n // result.fulfilment for first-party confirms, never persisted server-side.\n if (requestData.buyerEmail) body.buyerEmail = requestData.buyerEmail;\n // 3.9.0 (§33.5 #1): raw commerce artifacts forwarded verbatim — the\n // backend's Step 0.5 pipeline does the cryptographic verification\n // server-side (adapters forward, never verify locally).\n if (requestData.commerceArtifacts) body.commerceArtifacts = requestData.commerceArtifacts;\n // 3.12.0 (Trust Record, §33.5 #3): attempt-chain handle + consideration set,\n // forwarded skip-when-undefined (observedMetadata precedent) — pure\n // observational pass-through; the server echoes attemptId on every branch.\n if (requestData.attemptId) body.attemptId = requestData.attemptId;\n if (requestData.considerationSet) body.considerationSet = requestData.considerationSet;\n\n // Round-13 (F14 closure / R13-7): emit the SDK package version on every\n // verify-access body so the backend can auto-populate `kya_counterparty.\n // sdk_version` for the calling endpoint. Body field (not User-Agent\n // header) is the canonical channel — works in Node, browser, behind\n // Cloudflare, and across SDK bundlers without environment-specific\n // header gymnastics. Backend's `validation.ts:verifyAccessSchema`\n // accepts the semver regex; the auto-pop logic is forward-only.\n body.sdkVersion = SDK_VERSION;\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. Use ONLY the merchant's\n // configured apiKey for outbound auth. Previously the caller's inbound\n // Authorization header took priority, which pivoted the verify-access\n // call to the caller's identity — letting an attacker who supplied\n // their own Authorization header trigger verify-access calls under\n // THEIR account and observe the resulting verdicts for arbitrary\n // lookups they triggered via the merchant. Audit F-A1-10.\n //\n // The caller-supplied JWT (if any) is already extracted to\n // credentials.jwt and travels in the verify-access request body so\n // backend can evaluate it as the agent's identity claim — separate from\n // the merchant-attribution auth on the outbound HTTPS call.\n if (config.apiKey) {\n headers['Authorization'] = `Bearer ${config.apiKey}`;\n // Legacy header kept for compatibility with any middleware that reads it directly.\n headers['X-API-Key'] = config.apiKey;\n }\n\n try {\n // 4.0.0: hard timeout — without one, a backend that accepts the\n // connection but never answers hangs the merchant's middleware and every\n // inbound agent request behind it (observed live on a partner property).\n // Timeouts land on the catch path below → api_error (fail-closed),\n // NOT misconfig. The edge adapter races its own tighter withBudget.\n const response = await sdkFetch(`${config.apiBaseUrl}/agents/verify-access`, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(config.verifyTimeoutMs ?? 10_000),\n });\n\n // 5.12.0: a non-JSON body (an HTML 502 from a load balancer, a plain-text\n // 429) used to throw here and lose the status; parse defensively.\n const data = await readJsonSafe(response);\n const retryAfterMs = parseRetryAfterMs(response);\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 // 3.12.0 (Trust Record): keep the server-echoed attempt handle even on\n // the synthesised 410 deny shape (echoed on every branch).\n attemptId: typeof data?.attemptId === 'string' ? data.attemptId : undefined,\n access: {\n allowed: false,\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 // 5.10.0: a caller-supplied agent id that isn't a canonical ASTRA-id\n // (e.g. `ASTRA-bogus123`) is a deterministic failed identity claim, not\n // an outage. Previously it fell through to the transient api_error path,\n // so the edge answered 503 \"verification unavailable — retry\" under\n // enforce; it is now an identity deny (403 + registration guidance).\n if (response.status === 400 && data?.error === 'INVALID_AGENT_ID') {\n const message =\n typeof data?.message === 'string'\n ? data.message\n : 'Agent identifier must be a canonical ASTRA-* id.';\n return {\n success: true,\n attemptId: typeof data?.attemptId === 'string' ? data.attemptId : undefined,\n access: {\n allowed: false,\n reason: 'invalid_agent_id',\n failures: [\n {\n dimension: 'identity.invalid_agent_id',\n message,\n guidance:\n typeof data?.guidance === 'string'\n ? data.guidance\n : 'Present your canonical ASTRA-* id (discover it via GET /api/agents/request-registration/{requestId} after owner approval).',\n },\n ],\n },\n };\n }\n\n // 5.12.0: a presented agent id the backend doesn't know (404 \"Agent not\n // found\") is a deterministic failed identity claim — like the malformed\n // id above — not an outage. It used to fall through to api_error, so\n // merchants answered \"temporarily unavailable, retry\" to an agent that\n // simply isn't registered. A 404 without an agent id in the request (or\n // a non-JSON 404, i.e. a wrong apiBaseUrl) stays on the api_error path.\n if (\n response.status === 404 &&\n credentials.astraId &&\n (data?.error === 'AGENT_NOT_FOUND' ||\n (typeof data?.message === 'string' && /agent/i.test(data.message)))\n ) {\n return {\n success: true,\n attemptId: typeof data?.attemptId === 'string' ? data.attemptId : undefined,\n access: {\n allowed: false,\n reason: 'unknown_agent',\n failures: [\n {\n dimension: 'identity.unknown_agent',\n message: 'The presented agent identifier is not registered with AstraSync.',\n guidance: UNKNOWN_AGENT_GUIDANCE,\n },\n ],\n },\n };\n }\n\n if (!response.ok) {\n // 4.0.0: a 401 from verify-access can ONLY mean the backend rejected\n // THIS INTEGRATION'S own API key — agent denials come back as 200 with\n // access.allowed=false. Surface it as merchant-misconfig, not a\n // transient api_error: it is deterministic until the operator rotates\n // the key, and the \"retry with backoff\" template actively misleads.\n // Backends ≥ this round also send code: api_key_* + remediation URLs.\n // 5.13.0: a 403 COUNTERPARTY_KEY_MISMATCH is the same class — this\n // integration's key belongs to an account that doesn't own the endpoint.\n const keyMismatch = response.status === 403 && data?.error === 'COUNTERPARTY_KEY_MISMATCH';\n if (response.status === 401 || keyMismatch) {\n return {\n success: false,\n error: data.message || data.error || 'Integration API key rejected',\n misconfig: true,\n misconfigCode: keyMismatch\n ? 'counterparty_key_mismatch'\n : typeof data?.code === 'string'\n ? data.code\n : undefined,\n remediation:\n data?.remediation && typeof data.remediation === 'object'\n ? (data.remediation as { docsUrl?: string; manageUrl?: string })\n : undefined,\n correlationId: typeof data?.correlationId === 'string' ? data.correlationId : undefined,\n attemptId: typeof data?.attemptId === 'string' ? data.attemptId : undefined,\n };\n }\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 // 5.12.0: keep the status + Retry-After so adapters can relay the\n // platform's back-off (429 rate limit vs 5xx outage).\n return {\n success: false,\n error:\n (typeof data?.message === 'string' && data.message) ||\n (typeof data?.error === 'string' && data.error) ||\n `API returned ${response.status}`,\n correlationId: typeof data?.correlationId === 'string' ? data.correlationId : undefined,\n attemptId: typeof data?.attemptId === 'string' ? data.attemptId : undefined,\n httpStatus: response.status,\n ...(retryAfterMs !== undefined && { retryAfterMs }),\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 * Options for a single verify() call (as opposed to GatewayConfig, which is\n * per-gateway). Mirrors the Java SDK's `verify(request, bypassCache)`.\n */\nexport interface VerifyOptions {\n /**\n * Skip the cache READ for this call so the request is guaranteed to reach\n * the backend (the result is still written to the cache if cacheable).\n * Used by the step-up hold-and-poll retry: the re-verify after an approval\n * must hit the server to redeem the single-use approval.\n */\n bypassCache?: boolean;\n}\n\n/**\n * Main verification function\n */\nexport async function verify(\n config: GatewayConfig,\n request: VerificationRequest,\n options?: VerifyOptions\n): Promise<VerificationResult> {\n const mergedConfig = { ...DEFAULT_CONFIG, ...config };\n\n // v2.5.0: read well-known URLs from cache (prefetched by adapter at middleware creation).\n // Synchronous — never triggers a fetch. If cache is empty, guidance URLs are empty strings.\n const urls = mergedConfig.apiBaseUrl\n ? getCachedWellKnownUrls(mergedConfig.apiBaseUrl)\n : undefined;\n\n // One-time init self-test — fire-and-forget by default, never blocks verify().\n // Audit F-A6-33: when GatewayConfig.strictInit is set, the init promise is\n // awaited and any failure throws synchronously so callers learn about\n // misconfigured apiBaseUrl on the first verify() call rather than silently\n // running with broken auth.\n if (!initCheckPerformed && !mergedConfig.disableInitChecks && mergedConfig.apiBaseUrl) {\n if (mergedConfig.strictInit) {\n await performInitCheck(\n mergedConfig.apiBaseUrl,\n mergedConfig.debug,\n true,\n mergedConfig.apiKey,\n {\n id: mergedConfig.counterpartyId,\n url: mergedConfig.counterpartyUrl ?? request.counterpartyUrl,\n }\n );\n } else {\n void performInitCheck(\n mergedConfig.apiBaseUrl,\n mergedConfig.debug,\n false,\n mergedConfig.apiKey,\n {\n id: mergedConfig.counterpartyId,\n url: mergedConfig.counterpartyUrl ?? request.counterpartyUrl,\n }\n );\n }\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 decisions ' +\n '(verified ID + runtime challenge + PDLSS + trust score). 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. Caching on unless caller explicitly sets cacheTtl: 0\n // to disable. Undefined falls through to the 60s default at cacheResult\n // write time. options.bypassCache skips the read only (hold-and-poll retry\n // must reach the backend to redeem its approval).\n // 5.15.0: a request carrying protocol artifacts (signatures, mandates,\n // payment payloads) is never served from — or written to — the cache: each\n // one is verified on its own.\n const uncacheable = Boolean(request.commerceArtifacts);\n if (mergedConfig.cacheTtl !== 0 && !options?.bypassCache && !uncacheable) {\n const cached = getCachedResult(request, mergedConfig.counterpartyId);\n if (cached) {\n if (mergedConfig.debug) {\n console.log('[VerificationGateway] Returning cached result');\n }\n // 3.12.0 (Trust Record, §33.5 #3): attemptId is excluded from the cache\n // key, so the stored result may carry a PRIOR attempt's id. Overwrite it\n // with this request's own attemptId — or drop it when this request has\n // none — a cached verdict must never leak another attempt's handle.\n // Shallow clone: never mutate the stored entry (a concurrent holder of\n // the same cached object may still be reading it).\n const hit: VerificationResult = { ...cached };\n if (request.attemptId) {\n hit.attemptId = request.attemptId;\n } else {\n delete hit.attemptId;\n }\n return hit;\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 // 3.6.0: the server emits a top-level denialReasons array on every deny\n // path (same strings as the failure messages). Non-empty → authoritative;\n // absent/empty → fall back to the client-side reconstructions below.\n const rawServerDenialReasons = (apiResponse as Record<string, unknown>).denialReasons;\n const serverDenialReasons =\n Array.isArray(rawServerDenialReasons) && rawServerDenialReasons.length > 0\n ? (rawServerDenialReasons as string[])\n : undefined;\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 // 4.0.0: a rejected merchant key gets its own source — deterministic\n // misconfig must not wear the \"temporarily unavailable, retry\" template.\n return createGuidanceResponse(mergedConfig, apiResponse.error, {\n source: apiResponse.misconfig ? 'merchant_misconfig' : 'api_error',\n correlationId: (apiResponse as { correlationId?: string }).correlationId,\n attemptId: (apiResponse as { attemptId?: string }).attemptId,\n remediation: apiResponse.remediation,\n httpStatus: apiResponse.httpStatus,\n retryAfterMs: apiResponse.retryAfterMs,\n urls,\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 // Round-18 G4: backend denied access (PDLSS or other gate); identity status\n // depends on whether the backend resolved the caller. Read from\n // verificationContext.idVerified; default false if absent (anonymous or\n // identity-fail paths land here too). policyAllowed is false by definition\n // in this branch (apiResponse.access.allowed === false).\n // 5.12.0 (round-27.4 D4): a presented id that matches no registered\n // agent is never \"identity verified\", whatever an older backend stamped.\n const idVerifiedFromBackend =\n (apiResponse.verificationContext as { idVerified?: boolean } | undefined)?.idVerified ===\n true && !aggregatedFailures?.some((f) => REJECTED_IDENTITY_DIMENSIONS.has(f.dimension));\n const result: EnhancedVerificationResult = {\n identityVerified: idVerifiedFromBackend,\n policyAllowed: false,\n // 3.6.0: the server now emits top-level denialReasons on every deny\n // path — prefer it verbatim; the failure-message reconstruction remains\n // as the fallback for older backends.\n denialReasons:\n serverDenialReasons ??\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 // 5.12.0: a presented-but-rejected id (malformed / unknown) carries\n // actionable registration guidance on its failure — surface it\n // instead of the bare machine reason ('unknown_agent').\n message:\n rejectedIdentityGuidance(aggregatedFailures) ??\n (apiResponse.access?.reason || 'Access denied by PDLSS policy'),\n registrationUrl: urls?.registrationUrl ?? '',\n documentationUrl: urls?.documentationUrl ?? '',\n // 5.1.0: anonymous callers land on this branch — steer MCP-hosted\n // agents to the connector (this is what the edge deny body surfaces).\n ...(mcpGuidance(urls) && { mcp: mcpGuidance(urls) }),\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 // Anonymous traffic has no session → correlationId is the per-attempt\n // linking key (the sessionId-equivalent for anonymous callers).\n correlationId: (apiResponse as Record<string, unknown>).correlationId as string | undefined,\n // 3.12.0 (Trust Record): server echoes the attempt-chain handle on\n // every branch — denials included (the funnel outlives one deny).\n attemptId: (apiResponse as Record<string, unknown>).attemptId 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 stepUpApproval: (apiResponse as Record<string, unknown>).stepUpApproval as\n | StepUpApprovalInfo\n | undefined,\n settlement: (apiResponse as Record<string, unknown>).settlement as\n | SettlementArtifact\n | undefined,\n settlementOutcome: (apiResponse as Record<string, unknown>).settlementOutcome as\n | SettlementOutcomeInfo\n | undefined,\n // 5.5.0 self-settlement: merchant-only material — passes through to the\n // MERCHANT caller only (bridge callers strip it before the agent plane).\n settlementToken: (apiResponse as Record<string, unknown>).settlementToken as\n | MerchantSettlementToken\n | undefined,\n // 5.6.0: fulfilment contact — merchant-only lane, same strip rule.\n fulfilment: (apiResponse as Record<string, unknown>).fulfilment as FulfilmentInfo | undefined,\n mandates: (apiResponse as Record<string, unknown>).mandates as AstraSyncMandates | undefined,\n kyaToken: (apiResponse as Record<string, unknown>).kyaToken as string | undefined,\n aep: (apiResponse as Record<string, unknown>).aep as AstraSyncAepEvidence | 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 blockchainAnchored: apiResponse.agent.blockchainStatus === 'anchored',\n status: apiResponse.agent.agentStatus as VerifiedAgent['status'],\n ...(apiResponse.agent.protocolIdentities && {\n protocolIdentities: apiResponse.agent.protocolIdentities,\n }),\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 const result: EnhancedVerificationResult = {\n // 4.5.0 (probe F1): read idVerified from verificationContext with the\n // SAME absent⇒false default as the deny branch. The previous `!== false`\n // default manufactured `identityVerified: true` for anonymous\n // allow_full/allow_partial/audit responses (which allow access but omit\n // verificationContext entirely) — the \"anonymous caller labelled\n // verified\" defect. Every authenticated success response has carried\n // `verificationContext.idVerified: true` since v2.2.4, so a truth claim\n // this load-bearing must never be defaulted on.\n identityVerified:\n (apiResponse.verificationContext as { idVerified?: boolean } | undefined)?.idVerified ===\n true,\n policyAllowed: true,\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 // 3.12.0 (Trust Record): server-echoed attempt-chain handle.\n attemptId: (apiResponse as Record<string, unknown>).attemptId 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 stepUpApproval: (apiResponse as Record<string, unknown>).stepUpApproval as\n | StepUpApprovalInfo\n | undefined,\n settlement: (apiResponse as Record<string, unknown>).settlement as\n | SettlementArtifact\n | undefined,\n settlementOutcome: (apiResponse as Record<string, unknown>).settlementOutcome as\n | SettlementOutcomeInfo\n | undefined,\n settlementToken: (apiResponse as Record<string, unknown>).settlementToken as\n | MerchantSettlementToken\n | undefined,\n // 5.6.0: fulfilment contact — merchant-only lane, same strip rule.\n fulfilment: (apiResponse as Record<string, unknown>).fulfilment as FulfilmentInfo | undefined,\n // 5.15.0: merchant-lane AP2 mandates + KYAPay identity token.\n mandates: (apiResponse as Record<string, unknown>).mandates as AstraSyncMandates | undefined,\n kyaToken: (apiResponse as Record<string, unknown>).kyaToken as string | undefined,\n aep: (apiResponse as Record<string, unknown>).aep as AstraSyncAepEvidence | undefined,\n };\n\n // Enforce AstraSync recommendation\n if (result.recommendation === 'deny') {\n // Round-18 G4: recommendation-driven deny lands on the success-path\n // construction (identity was resolved). Flip policy only — identity stays\n // verified — so adapters return 403 (re-auth won't help; the policy\n // decision is the blocker).\n result.policyAllowed = false;\n // 3.6.0: server-sent top-level denialReasons wins; recommendationReasons\n // reconstruction is the fallback for older backends.\n result.denialReasons = serverDenialReasons ??\n result.recommendationReasons ?? ['Access denied by AstraSync recommendation'];\n result.guidance = result.runtimeChallenge\n ? {\n message: `Verification failed: ${result.runtimeChallenge.reason || 'runtime challenge failed'}`,\n registrationUrl: urls?.registrationUrl ?? '',\n documentationUrl: urls?.documentationUrl ?? '',\n }\n : {\n message: result.recommendationReasons?.[0] || 'Access denied by AstraSync recommendation',\n registrationUrl: urls?.registrationUrl ?? '',\n documentationUrl: urls?.documentationUrl ?? '',\n };\n } else if (result.recommendation === 'step_up_required' && result.identityVerified) {\n // The escalation signal rides on `requiresStepUp` / `stepUpApproval`, not\n // on any masquerade of the (now-removed) access band.\n //\n // 5.12.0 (round-27.4 D1): step-up is \"allowed-but-escalate\" — the\n // registered agent's OWNER approves a valued action. An unidentified\n // caller has no owner to escalate to, so an allowed response for one is\n // a grant: older backends labelled recognised-but-unregistered agents\n // (Claude-User, the AstraSync bridge) step_up_required on plain reads,\n // and the adapters 401'd an anonymous catalog GET as \"requires human\n // approval\". The backend no longer emits that; this keeps merchants on\n // 5.12.0 correct against an older backend too. Denies are untouched —\n // they take the !allowed branch above.\n result.requiresStepUp = true;\n result.denialReasons = serverDenialReasons ??\n result.recommendationReasons ?? ['Step-up verification required'];\n }\n\n // Cache the result. Never cache denials (agent may fix challenge endpoint\n // and retry) NOR step-up escalations (3.6.0 — the same bug the Java SDK\n // had: a cached step_up_required verdict keeps denying retries the owner\n // has already approved, and its pollUrl goes stale once consumed/expired).\n // Escalations stay uncached even when the caller pins an explicit cacheTtl.\n const escalates =\n result.requiresStepUp === true ||\n result.requiresApproval === true ||\n result.recommendation === 'step_up_required';\n if (mergedConfig.cacheTtl !== 0 && result.recommendation !== 'deny' && !escalates) {\n if (!uncacheable) {\n cacheResult(request, result, mergedConfig.cacheTtl, mergedConfig.counterpartyId);\n }\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 */\nexport async function recordDecision(\n config: GatewayConfig,\n sessionId: string,\n decision: 'granted' | 'denied',\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 sdkFetch(`${config.apiBaseUrl}/agents/verify-access/${sessionId}/decision`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ decision, reason }),\n }).catch(() => {\n /* fire-and-forget */\n });\n}\n\n/**\n * Report post-hoc consideration/outcome data for an attempt chain (Trust\n * Record). Use when the signal never reached verify-access: a\n * catalog served from a local cache, a transport-side deny (sku not found,\n * over budget), or a terminal outcome (settled / blocked on step-up).\n * Authenticated with the same credentials as verify(); the backend responds\n * 202 and folds the data into the attempt chain keyed by `attemptId`.\n *\n * Fire-and-forget — never throws. 5.12.0: a 429/5xx is retried once\n * (Retry-After honoured, capped at 2s) off the caller's path; a lost report\n * is surfaced via `options.onFailure`.\n */\nexport async function reportAttempt(\n config: GatewayConfig,\n report: AttemptReport,\n options: ReportAttemptOptions = {}\n): Promise<void> {\n const apiBaseUrl = config.apiBaseUrl || DEFAULT_CONFIG.apiBaseUrl!;\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 // counterpartyId resolution mirrors reportUnregisteredAttempt: an explicit\n // report value wins; otherwise attribute to the configured endpoint.\n const body = {\n ...report,\n ...(!report.counterpartyId &&\n config.counterpartyId && { counterpartyId: config.counterpartyId }),\n };\n\n const url = `${apiBaseUrl}/agents/verify-access/attempt-report`;\n const init = { method: 'POST', headers, body: JSON.stringify(body) };\n // 5.12.0: check the status — a 429/5xx used to vanish silently (the 1 Oct\n // load test lost attempt reports this way). One bounded retry honouring\n // Retry-After (capped at 2s), scheduled OFF the caller's path; never throws.\n const fail = (failure: { status?: number; error?: string; retried: boolean }): void => {\n try {\n options.onFailure?.(failure);\n } catch {\n /* a reporter callback must never break the caller */\n }\n };\n let response: Response | undefined;\n try {\n response = await sdkFetch(url, init);\n } catch (err) {\n fail({ error: err instanceof Error ? err.message : String(err), retried: false });\n return;\n }\n if (response.ok) return;\n if (response.status !== 429 && response.status < 500) {\n fail({ status: response.status, retried: false });\n return;\n }\n const delayMs = Math.min(ATTEMPT_REPORT_MAX_RETRY_DELAY_MS, parseRetryAfterMs(response) ?? 500);\n const timer = setTimeout(() => {\n sdkFetch(url, init)\n .then((retry) => {\n if (!retry.ok) fail({ status: retry.status, retried: true });\n })\n .catch((err) => {\n fail({ error: err instanceof Error ? err.message : String(err), retried: true });\n });\n }, delayMs);\n (timer as { unref?: () => void }).unref?.();\n}\n\n/** 5.12.0: options for {@link reportAttempt}. */\nexport interface ReportAttemptOptions {\n /**\n * Called once when the report is finally lost (non-retryable status,\n * network error, or the single retry failed). For counters/telemetry;\n * exceptions it throws are swallowed.\n */\n onFailure?: (failure: { status?: number; error?: string; retried: boolean }) => void;\n}\n\n/** 5.12.0: upper bound on reportAttempt's single retry delay. */\nconst ATTEMPT_REPORT_MAX_RETRY_DELAY_MS = 2000;\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 */\n/** 5.13.0: endpoints whose policy read was refused (log once each). */\nconst refusedPolicyReads = new Set<string>();\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 sdkFetch(\n `${config.apiBaseUrl}/endpoints/${encodeURIComponent(counterpartyId)}/routes`,\n { method: 'GET', headers }\n );\n if (!response.ok) {\n // 5.13.0: a refused policy read is not \"no policy configured\". The key\n // can't see this endpoint (another account's key, or a wrong id), so\n // the middleware is passing EVERY request through unverified. Say so,\n // loudly, once per endpoint.\n if (\n (response.status === 401 || response.status === 403 || response.status === 404) &&\n !refusedPolicyReads.has(counterpartyId)\n ) {\n refusedPolicyReads.add(counterpartyId);\n // eslint-disable-next-line no-console\n console.error(\n `[VerificationGateway] the configured API key cannot read the route policy for ${counterpartyId} ` +\n `(HTTP ${response.status}). The key belongs to an account that does not own this endpoint, ` +\n `or the counterpartyId is wrong. The gateway is in PASS-THROUGH mode: no agent is being verified.`\n );\n }\n return null;\n }\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 /** Pass through unenforced (observe). Replaces `minAccessLevel: 'none'`. */\n observe?: boolean;\n minTrustScore?: number;\n requiredPurposes?: string[];\n allowedPurposes?: string[];\n allowedJurisdictions?: string[];\n maxDuration?: number;\n maxTransactionValue?: number;\n requirePurpose?: boolean;\n /** Send-mapping — see RouteAccessConfig in types.ts. */\n purpose?: string;\n action?: string;\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 options?: VerifyOptions\n): Promise<VerificationResult> {\n const mergedConfig = { ...DEFAULT_CONFIG, ...config };\n const result = await verify(mergedConfig, { ...request, createSession: true }, options);\n const sessionId = (result as EnhancedVerificationResult).sessionId;\n\n if (sessionId) {\n // Round-18 G4: a session is \"granted\" only if identity verified AND\n // policy allowed; either failing is a deny.\n if (result.identityVerified && result.policyAllowed) {\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 /**\n * 3.6.0: optional — the backend attributes by counterpartyId first (sent\n * from config below) and requires at least ONE of counterpartyId/URL.\n * A null/undefined URL is omitted from the body, not sent as null.\n */\n counterpartyUrl?: string;\n counterpartyType?: string;\n sourceIp?: string;\n userAgent?: string;\n requestPath?: string;\n requestMethod?: string;\n /**\n * VI uplift Part 3 — attempt-chain handle (`att_<32hex>`, from the\n * X-Astra-Attempt-Id header or an `?astra_attempt=` deep link). Joins\n * the beacon into the agent's research chain. Attribution only.\n */\n attemptId?: string;\n /** First-class referer (traffic-source reporting). */\n referer?: string;\n /** The caller's Accept header (surface-classifier input). */\n accept?: string;\n /** Self-declared platform from X-Astra-Source (attribution, never trust). */\n declaredSource?: string;\n /**\n * 3.9.0 — edge-adapter evidence fields, persisted into the\n * event's eventData by the backend: `protocol` = detected transport/\n * credential protocol; `evidence` = light crypto-free detection detail.\n */\n protocol?: string;\n evidence?: Record<string, unknown>;\n /**\n * Maximal metadata capture — the full sanitised metadata for an\n * unregistered caller (every signal legitimately visible on the wire).\n * Persisted into eventData; feeds platform attribution and (via\n * correlationId) registration auto-fill. Fire-and-forget, so free.\n */\n observedMetadata?: ObservedMetadata;\n }\n): Promise<void> {\n const apiBaseUrl = config.apiBaseUrl || DEFAULT_CONFIG.apiBaseUrl!;\n\n const { counterpartyUrl, ...rest } = data;\n const body = {\n ...rest,\n ...(counterpartyUrl && { counterpartyUrl }),\n ...(config.counterpartyId && { counterpartyId: config.counterpartyId }),\n };\n\n // 5.12.0: send the merchant's API key when configured — the backend then\n // stamps the row `attributionVerified: true` (it checks the key owns the\n // counterparty). Without a key the beacon is still accepted, but flagged\n // as unverified attribution in Visit Intelligence.\n await sdkFetch(`${apiBaseUrl}/verification-activity/unregistered-attempt`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(config.apiKey && { Authorization: `Bearer ${config.apiKey}` }),\n },\n body: JSON.stringify(body),\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 sdkFetch(`${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 — checks credentials and policy in one call.\n *\n * The return shape mirrors `VerificationResult`'s split — partners\n * writing custom handlers around `quickVerify` get the same identity/policy\n * distinction as those calling `verify()` directly. Map to HTTP status the\n * same way: `!identityVerified` → 401; `identityVerified && !policyAllowed`\n * → 403.\n */\nexport async function quickVerify(\n config: GatewayConfig,\n credentials: AgentCredentials\n): Promise<{\n identityVerified: boolean;\n policyAllowed: boolean;\n reason?: string;\n /**\n * 5.12.0: verification infrastructure was unavailable (timeout / 5xx /\n * the platform's 429) — NOT a denial. Callers should fail open on reads\n * or answer 503 + Retry-After (`retryAfterMs`, else 30s), never 401.\n */\n unavailable?: true;\n retryAfterMs?: number;\n}> {\n const result = await verify(config, {\n credentials,\n purpose: 'verification',\n });\n\n return {\n identityVerified: result.identityVerified,\n policyAllowed: result.policyAllowed,\n reason: result.denialReasons?.[0],\n ...(isVerificationUnavailable(result) && {\n unavailable: true as const,\n retryAfterMs: unavailableRetryAfterSeconds(result) * 1000,\n }),\n };\n}\n"],"mappings":"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","names":["has","result"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astrasyncai/verification-gateway",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.15.0",
|
|
4
4
|
"description": "AstraSync KYA Platform SDK — counterparty verification gateway (verify incoming requests) + agent registration (register AI agents with the KYA backend).",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|