@astrasyncai/verification-gateway 4.0.0 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/README.md +106 -29
  2. package/dist/adapter-interface/interface.d.mts +2 -2
  3. package/dist/adapter-interface/interface.d.ts +2 -2
  4. package/dist/adapters/express.d.mts +2 -2
  5. package/dist/adapters/express.d.ts +2 -2
  6. package/dist/adapters/express.js +398 -15
  7. package/dist/adapters/express.js.map +1 -1
  8. package/dist/adapters/express.mjs +398 -15
  9. package/dist/adapters/express.mjs.map +1 -1
  10. package/dist/adapters/http-pdlss.d.mts +3 -3
  11. package/dist/adapters/http-pdlss.d.ts +3 -3
  12. package/dist/adapters/http-pdlss.js.map +1 -1
  13. package/dist/adapters/http-pdlss.mjs.map +1 -1
  14. package/dist/adapters/mcp.d.mts +56 -36
  15. package/dist/adapters/mcp.d.ts +56 -36
  16. package/dist/adapters/mcp.js +220 -3
  17. package/dist/adapters/mcp.js.map +1 -1
  18. package/dist/adapters/mcp.mjs +220 -3
  19. package/dist/adapters/mcp.mjs.map +1 -1
  20. package/dist/adapters/nextjs.d.mts +2 -2
  21. package/dist/adapters/nextjs.d.ts +2 -2
  22. package/dist/adapters/nextjs.js +433 -18
  23. package/dist/adapters/nextjs.js.map +1 -1
  24. package/dist/adapters/nextjs.mjs +433 -18
  25. package/dist/adapters/nextjs.mjs.map +1 -1
  26. package/dist/adapters/sdk.d.mts +2 -2
  27. package/dist/adapters/sdk.d.ts +2 -2
  28. package/dist/adapters/sdk.js +2 -2
  29. package/dist/adapters/sdk.js.map +1 -1
  30. package/dist/adapters/sdk.mjs +2 -2
  31. package/dist/adapters/sdk.mjs.map +1 -1
  32. package/dist/agent/index.d.mts +2 -2
  33. package/dist/agent/index.d.ts +2 -2
  34. package/dist/agent/index.js.map +1 -1
  35. package/dist/agent/index.mjs.map +1 -1
  36. package/dist/bin/astrasync-claude-hook.js +13 -13
  37. package/dist/bin/astrasync-codex-hook.js +13 -13
  38. package/dist/bin/astrasync-guard.js +100 -13
  39. package/dist/bin/astrasync.js +168 -27
  40. package/dist/browser/background.js +13 -13
  41. package/dist/browser/background.js.map +1 -1
  42. package/dist/browser/background.mjs +13 -13
  43. package/dist/browser/background.mjs.map +1 -1
  44. package/dist/browser/browser-adapter.d.mts +2 -2
  45. package/dist/browser/browser-adapter.d.ts +2 -2
  46. package/dist/claude-code/claude-code-adapter.d.mts +2 -2
  47. package/dist/claude-code/claude-code-adapter.d.ts +2 -2
  48. package/dist/cli/index.d.mts +2 -2
  49. package/dist/cli/index.d.ts +2 -2
  50. package/dist/codex/index.d.mts +3 -3
  51. package/dist/codex/index.d.ts +3 -3
  52. package/dist/codex/index.js +24 -13
  53. package/dist/codex/index.js.map +1 -1
  54. package/dist/codex/index.mjs +24 -13
  55. package/dist/codex/index.mjs.map +1 -1
  56. package/dist/cursor/cursor-adapter.d.mts +2 -2
  57. package/dist/cursor/cursor-adapter.d.ts +2 -2
  58. package/dist/cursor/extension.d.mts +2 -2
  59. package/dist/cursor/extension.d.ts +2 -2
  60. package/dist/cursor/extension.js +13 -13
  61. package/dist/cursor/extension.js.map +1 -1
  62. package/dist/cursor/extension.mjs +13 -13
  63. package/dist/cursor/extension.mjs.map +1 -1
  64. package/dist/edge-config.d.mts +22 -7
  65. package/dist/edge-config.d.ts +22 -7
  66. package/dist/edge-config.js.map +1 -1
  67. package/dist/edge-config.mjs.map +1 -1
  68. package/dist/edge-core/index.d.mts +425 -0
  69. package/dist/edge-core/index.d.ts +425 -0
  70. package/dist/edge-core/index.js +1482 -0
  71. package/dist/edge-core/index.js.map +1 -0
  72. package/dist/edge-core/index.mjs +1437 -0
  73. package/dist/edge-core/index.mjs.map +1 -0
  74. package/dist/{express-BZajRs4i.d.mts → express-BVd1_3FE.d.ts} +9 -7
  75. package/dist/{express-CjCUB1k7.d.ts → express-D_4hTn5Z.d.mts} +9 -7
  76. package/dist/gateway/gateway.d.mts +2 -2
  77. package/dist/gateway/gateway.d.ts +2 -2
  78. package/dist/gateway/gateway.js +13 -13
  79. package/dist/gateway/gateway.js.map +1 -1
  80. package/dist/gateway/gateway.mjs +13 -13
  81. package/dist/gateway/gateway.mjs.map +1 -1
  82. package/dist/git-trigger/git-hooks.d.mts +2 -2
  83. package/dist/git-trigger/git-hooks.d.ts +2 -2
  84. package/dist/{index-adgKhujR.d.ts → index-B0YHu_SP.d.ts} +30 -27
  85. package/dist/{index-B0cyMZDL.d.mts → index-BPEBlOsE.d.mts} +30 -27
  86. package/dist/{index-BRYteOqO.d.mts → index-DQb5-_1X.d.mts} +1 -1
  87. package/dist/{index-BZQdVqNw.d.ts → index-T1aBoUcc.d.ts} +1 -1
  88. package/dist/index.d.mts +11 -11
  89. package/dist/index.d.ts +11 -11
  90. package/dist/index.js +477 -265
  91. package/dist/index.js.map +1 -1
  92. package/dist/index.mjs +474 -265
  93. package/dist/index.mjs.map +1 -1
  94. package/dist/local-evaluator/evaluator.d.mts +2 -2
  95. package/dist/local-evaluator/evaluator.d.ts +2 -2
  96. package/dist/metadata-capture.d.mts +63 -4
  97. package/dist/metadata-capture.d.ts +63 -4
  98. package/dist/metadata-capture.js +67 -0
  99. package/dist/metadata-capture.js.map +1 -1
  100. package/dist/metadata-capture.mjs +64 -0
  101. package/dist/metadata-capture.mjs.map +1 -1
  102. package/dist/{nextjs-DeUTnOBd.d.mts → nextjs-WyeVr2Kp.d.mts} +3 -3
  103. package/dist/{nextjs-D_iFf5k-.d.ts → nextjs-w7RTbP1P.d.ts} +3 -3
  104. package/dist/platform-signatures.d.mts +5 -6
  105. package/dist/platform-signatures.d.ts +5 -6
  106. package/dist/platform-signatures.js.map +1 -1
  107. package/dist/platform-signatures.mjs.map +1 -1
  108. package/dist/registration/index.d.mts +10 -11
  109. package/dist/registration/index.d.ts +10 -11
  110. package/dist/registration/index.js.map +1 -1
  111. package/dist/registration/index.mjs.map +1 -1
  112. package/dist/{sdk-BM3mwEAq.d.ts → sdk-CbTNIkAa.d.mts} +4 -4
  113. package/dist/{sdk-_il1Q41f.d.mts → sdk-QeX0Z8Ho.d.ts} +4 -4
  114. package/dist/transport/index.d.mts +2 -2
  115. package/dist/transport/index.d.ts +2 -2
  116. package/dist/transport/index.js.map +1 -1
  117. package/dist/transport/index.mjs.map +1 -1
  118. package/dist/{types-CHKCgDFC.d.ts → types-BLUx92FJ.d.ts} +1 -1
  119. package/dist/{types-DfnP7JiM.d.mts → types-DqfPU5Bl.d.mts} +1 -1
  120. package/dist/{types-BcDf9Kk0.d.mts → types-r850cOt0.d.mts} +106 -88
  121. package/dist/{types-fTQD448H.d.ts → types-sMxBT-nM.d.ts} +106 -88
  122. package/dist/ui/index.d.mts +11 -7
  123. package/dist/ui/index.d.ts +11 -7
  124. package/dist/ui/index.js +13 -9
  125. package/dist/ui/index.js.map +1 -1
  126. package/dist/ui/index.mjs +10 -8
  127. package/dist/ui/index.mjs.map +1 -1
  128. package/dist/verify.d.mts +8 -7
  129. package/dist/verify.d.ts +8 -7
  130. package/dist/verify.js +1 -1
  131. package/dist/verify.js.map +1 -1
  132. package/dist/verify.mjs +1 -1
  133. package/dist/verify.mjs.map +1 -1
  134. package/package.json +8 -2
package/dist/index.mjs CHANGED
@@ -126,7 +126,7 @@ function getCapabilities(accessLevel) {
126
126
  }
127
127
 
128
128
  // src/version.ts
129
- var SDK_VERSION = "4.0.0";
129
+ var SDK_VERSION = "4.1.0";
130
130
 
131
131
  // src/well-known.ts
132
132
  var CACHE_TTL_MS = 60 * 60 * 1e3;
@@ -1124,6 +1124,7 @@ function performCounterpartyPreCheck(routeConfig, astraCreds, purpose) {
1124
1124
  }
1125
1125
 
1126
1126
  // src/metadata-capture.ts
1127
+ var CAPTURE_SCHEMA_VERSION = 1;
1127
1128
  var SECRET_HEADER_NAMES = /* @__PURE__ */ new Set([
1128
1129
  "cookie",
1129
1130
  "set-cookie",
@@ -1268,6 +1269,373 @@ function extractPlatformHeaders(sanitized) {
1268
1269
  }
1269
1270
  return out;
1270
1271
  }
1272
+ var CONNECTION_HEADER_SOURCES = [
1273
+ ["ip", ["cf-connecting-ip", "fastly-client-ip", "true-client-ip", "x-real-ip"]],
1274
+ [
1275
+ "country",
1276
+ ["cf-ipcountry", "x-vercel-ip-country", "cloudfront-viewer-country", "fastly-geo-country"]
1277
+ ],
1278
+ ["region", ["x-vercel-ip-country-region", "cloudfront-viewer-country-region"]],
1279
+ ["city", ["x-vercel-ip-city", "cloudfront-viewer-city"]],
1280
+ ["asn", ["cloudfront-viewer-asn"]],
1281
+ ["tlsVersion", ["cloudfront-viewer-tls"]],
1282
+ ["httpVersion", ["cloudfront-viewer-http-version"]],
1283
+ // TLS fingerprints can only be computed where TLS terminates. When a CDN
1284
+ // terminates TLS in front of the merchant, its fingerprint headers ARE the
1285
+ // connection-layer truth — normalising them here is the permanent design.
1286
+ ["tlsFingerprint", ["cf-ja3-hash"]],
1287
+ ["ja4", ["cf-ja4"]]
1288
+ ];
1289
+ function deriveConnectionFromHeaders(headers) {
1290
+ if (!headers) return void 0;
1291
+ const lower = {};
1292
+ for (const name of Object.keys(headers)) {
1293
+ const value = headers[name];
1294
+ if (typeof value === "string" && value.length > 0) lower[name.toLowerCase()] = value;
1295
+ }
1296
+ const connection = {};
1297
+ for (const [key, sources] of CONNECTION_HEADER_SOURCES) {
1298
+ for (const source of sources) {
1299
+ const value = lower[source]?.trim();
1300
+ if (value) {
1301
+ connection[key] = value;
1302
+ break;
1303
+ }
1304
+ }
1305
+ }
1306
+ if (!connection.ip) {
1307
+ const xff = lower["x-forwarded-for"];
1308
+ const client = xff?.split(",")[0]?.trim();
1309
+ if (client) connection.ip = client;
1310
+ }
1311
+ return Object.keys(connection).length > 0 ? connection : void 0;
1312
+ }
1313
+ function buildSdkObservedMetadata(rawHeaders) {
1314
+ const { headers, apiKeyFormat, platformHeaders } = sanitizeHeaders(rawHeaders);
1315
+ const flat = {};
1316
+ if (rawHeaders) {
1317
+ for (const name of Object.keys(rawHeaders)) {
1318
+ const value = rawHeaders[name];
1319
+ if (value === void 0) continue;
1320
+ flat[name.toLowerCase()] = Array.isArray(value) ? value.join(", ") : String(value);
1321
+ }
1322
+ }
1323
+ const connection = deriveConnectionFromHeaders(flat);
1324
+ return {
1325
+ headers,
1326
+ ...apiKeyFormat && { apiKeyFormat },
1327
+ ...platformHeaders && { platformHeaders },
1328
+ ...connection && { connection },
1329
+ schemaVersion: CAPTURE_SCHEMA_VERSION
1330
+ };
1331
+ }
1332
+
1333
+ // src/edge-config.ts
1334
+ var DEFAULT_EDGE_CONFIG = Object.freeze({
1335
+ version: 1,
1336
+ mode: "observe",
1337
+ depth: "classify",
1338
+ pathRules: [],
1339
+ sampling: Object.freeze({ anonymousBeaconRate: 0.1 }),
1340
+ failurePosture: "open"
1341
+ });
1342
+ var EDGE_MODES = ["observe", "enforce"];
1343
+ var EDGE_DEPTHS = ["classify", "authenticate", "authorize"];
1344
+ function isEdgeConfig(value) {
1345
+ if (typeof value !== "object" || value === null) return false;
1346
+ const v = value;
1347
+ if (v.version !== 1) return false;
1348
+ if (!EDGE_MODES.includes(v.mode)) return false;
1349
+ if (!EDGE_DEPTHS.includes(v.depth)) return false;
1350
+ if (!Array.isArray(v.pathRules)) return false;
1351
+ for (const rule of v.pathRules) {
1352
+ if (typeof rule !== "object" || rule === null) return false;
1353
+ const r = rule;
1354
+ if (typeof r.pattern !== "string" || r.pattern.length === 0) return false;
1355
+ if (r.mode !== void 0 && !EDGE_MODES.includes(r.mode)) return false;
1356
+ if (r.depth !== void 0 && !EDGE_DEPTHS.includes(r.depth)) return false;
1357
+ }
1358
+ const sampling = v.sampling;
1359
+ if (typeof sampling !== "object" || sampling === null || typeof sampling.anonymousBeaconRate !== "number" || sampling.anonymousBeaconRate < 0 || sampling.anonymousBeaconRate > 1) {
1360
+ return false;
1361
+ }
1362
+ return v.failurePosture === "open";
1363
+ }
1364
+ async function fetchEdgeConfig(config, counterpartyId, opts) {
1365
+ if (!counterpartyId) return null;
1366
+ const headers = { Accept: "application/json" };
1367
+ if (config.apiKey) {
1368
+ headers["Authorization"] = `Bearer ${config.apiKey}`;
1369
+ headers["X-API-Key"] = config.apiKey;
1370
+ }
1371
+ if (opts?.etag) headers["If-None-Match"] = opts.etag;
1372
+ try {
1373
+ const response = await fetch(
1374
+ `${config.apiBaseUrl}/endpoints/${encodeURIComponent(counterpartyId)}/edge-config`,
1375
+ { method: "GET", headers, signal: AbortSignal.timeout(opts?.timeoutMs ?? 5e3) }
1376
+ );
1377
+ if (response.status === 304) return { notModified: true };
1378
+ if (!response.ok) return null;
1379
+ const body = await response.json();
1380
+ const edgeConfig = body.data?.edgeConfig;
1381
+ if (!isEdgeConfig(edgeConfig)) return null;
1382
+ return { edgeConfig, etag: response.headers.get("etag") ?? void 0 };
1383
+ } catch {
1384
+ return null;
1385
+ }
1386
+ }
1387
+ var DEFAULT_TTL_MS = 60 * 1e3;
1388
+ var DEFAULT_MAX_STALE_MS = 24 * 60 * 60 * 1e3;
1389
+ var cache2 = /* @__PURE__ */ new Map();
1390
+ var inflight2 = /* @__PURE__ */ new Map();
1391
+ function cacheKey(apiBaseUrl, counterpartyId) {
1392
+ return `${apiBaseUrl}|${counterpartyId}`;
1393
+ }
1394
+ function degradeToObserve(config) {
1395
+ if (config.mode !== "enforce" && !config.pathRules.some((r) => r.mode === "enforce")) {
1396
+ return config;
1397
+ }
1398
+ return {
1399
+ ...config,
1400
+ mode: "observe",
1401
+ pathRules: config.pathRules.map((r) => r.mode === "enforce" ? { ...r, mode: "observe" } : r)
1402
+ };
1403
+ }
1404
+ async function revalidate(config, counterpartyId) {
1405
+ const key = cacheKey(config.apiBaseUrl, counterpartyId);
1406
+ const existing = inflight2.get(key);
1407
+ if (existing) return existing;
1408
+ const promise = fetchEdgeConfig(config, counterpartyId, { etag: cache2.get(key)?.etag }).then((result) => {
1409
+ if (result === null) return;
1410
+ const now = Date.now();
1411
+ if ("notModified" in result) {
1412
+ const entry = cache2.get(key);
1413
+ if (entry) entry.lastSuccessAt = now;
1414
+ return;
1415
+ }
1416
+ cache2.set(key, { config: result.edgeConfig, etag: result.etag, lastSuccessAt: now });
1417
+ }).finally(() => {
1418
+ inflight2.delete(key);
1419
+ });
1420
+ inflight2.set(key, promise);
1421
+ return promise;
1422
+ }
1423
+ async function getEdgeConfig(config, counterpartyId, opts) {
1424
+ const ttlMs = opts?.ttlMs ?? DEFAULT_TTL_MS;
1425
+ const maxStaleMs = opts?.maxStaleMs ?? DEFAULT_MAX_STALE_MS;
1426
+ const key = cacheKey(config.apiBaseUrl, counterpartyId);
1427
+ const entry = cache2.get(key);
1428
+ if (entry) {
1429
+ const age = Date.now() - entry.lastSuccessAt;
1430
+ if (age > ttlMs) {
1431
+ revalidate(config, counterpartyId).catch(() => {
1432
+ });
1433
+ }
1434
+ return age > maxStaleMs ? degradeToObserve(entry.config) : entry.config;
1435
+ }
1436
+ await revalidate(config, counterpartyId).catch(() => {
1437
+ });
1438
+ return cache2.get(key)?.config ?? DEFAULT_EDGE_CONFIG;
1439
+ }
1440
+ function matchEdgePathRule(rules, path) {
1441
+ for (const rule of rules) {
1442
+ if (globToRegExp(rule.pattern).test(path)) return rule;
1443
+ }
1444
+ return void 0;
1445
+ }
1446
+ function globToRegExp(pattern) {
1447
+ const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
1448
+ return new RegExp(`^${escaped}$`, "i");
1449
+ }
1450
+ function _resetEdgeConfigCache() {
1451
+ cache2.clear();
1452
+ inflight2.clear();
1453
+ }
1454
+
1455
+ // src/platform-signatures.ts
1456
+ var PLATFORM_AGENT_SIGNATURES = [
1457
+ {
1458
+ vendor: "claude",
1459
+ displayName: "Anthropic Claude",
1460
+ uaPatterns: [/claude/i, /anthropic/i],
1461
+ agentCardPatterns: [/anthropic\.com/i, /claude\.ai/i],
1462
+ apiKeyFormatPatterns: [/^sk-ant/i],
1463
+ headerPatterns: [/^anthropic-version/i, /^anthropic-beta/i, /^x-anthropic-/i]
1464
+ },
1465
+ {
1466
+ vendor: "chatgpt",
1467
+ displayName: "OpenAI ChatGPT",
1468
+ uaPatterns: [/chatgpt/i, /openai/i, /gpt-?\d/i],
1469
+ agentCardPatterns: [/openai\.com/i, /chatgpt\.com/i],
1470
+ apiKeyFormatPatterns: [/^sk-proj/i, /^sk-svcacct/i],
1471
+ headerPatterns: [/^openai-organization/i, /^openai-project/i, /^openai-version/i]
1472
+ },
1473
+ {
1474
+ vendor: "gemini",
1475
+ displayName: "Google Gemini",
1476
+ uaPatterns: [/gemini/i, /google-gemini/i, /bard/i],
1477
+ agentCardPatterns: [/gemini\.google\.com/i, /ai\.google\.dev/i],
1478
+ apiKeyFormatPatterns: [/^aiza/i],
1479
+ headerPatterns: [/^x-goog-user-project/i, /^x-goog-api-client/i]
1480
+ },
1481
+ {
1482
+ vendor: "cursor",
1483
+ displayName: "Cursor",
1484
+ uaPatterns: [/cursor/i],
1485
+ agentCardPatterns: [/cursor\.sh/i, /cursor\.com/i]
1486
+ },
1487
+ {
1488
+ vendor: "goose",
1489
+ displayName: "Block Goose",
1490
+ uaPatterns: [/goose/i, /block-goose/i],
1491
+ agentCardPatterns: [/block\.xyz\/goose/i, /goose\.tools/i]
1492
+ },
1493
+ {
1494
+ vendor: "perplexity",
1495
+ displayName: "Perplexity",
1496
+ uaPatterns: [/perplexity/i],
1497
+ agentCardPatterns: [/perplexity\.ai/i],
1498
+ apiKeyFormatPatterns: [/^pplx-/i]
1499
+ }
1500
+ ];
1501
+ function matchPlatformSignature(signatures, input) {
1502
+ const ua = input.userAgent ?? "";
1503
+ const cardUrl = input.agentCardUrl ?? "";
1504
+ const apiKeyFormat = input.apiKeyFormat ?? "";
1505
+ const headerLines = [];
1506
+ if (input.platformHeaders) {
1507
+ for (const [name, value] of Object.entries(input.platformHeaders)) {
1508
+ headerLines.push(name, `${name}: ${value}`);
1509
+ }
1510
+ }
1511
+ for (const sig of signatures) {
1512
+ const uaMatch = sig.uaPatterns?.some((p) => p.test(ua));
1513
+ const cardMatch = sig.agentCardPatterns?.some((p) => p.test(cardUrl));
1514
+ const keyMatch = apiKeyFormat.length > 0 && sig.apiKeyFormatPatterns?.some((p) => p.test(apiKeyFormat));
1515
+ const headerMatch = headerLines.length > 0 && sig.headerPatterns?.some((p) => headerLines.some((line) => p.test(line)));
1516
+ if (uaMatch || cardMatch || keyMatch || headerMatch) {
1517
+ const uaBase = ua.split(/[\s/]/)[0]?.toLowerCase() ?? "";
1518
+ const cardHost = (() => {
1519
+ try {
1520
+ return cardUrl ? new URL(cardUrl).host : "";
1521
+ } catch {
1522
+ return "";
1523
+ }
1524
+ })();
1525
+ const keyBasis = uaBase || cardHost || apiKeyFormat || "unknown";
1526
+ return {
1527
+ vendor: sig.vendor,
1528
+ fingerprintKey: `${sig.vendor}:${keyBasis}`,
1529
+ displayName: sig.displayName
1530
+ };
1531
+ }
1532
+ }
1533
+ return void 0;
1534
+ }
1535
+ function detectPlatformFingerprint(input) {
1536
+ return matchPlatformSignature(PLATFORM_AGENT_SIGNATURES, input);
1537
+ }
1538
+
1539
+ // src/edge-core/classify.ts
1540
+ var STATIC_EXTENSIONS = /\.(css|js|mjs|map|png|jpe?g|gif|webp|avif|svg|ico|woff2?|ttf|otf|eot|mp4|webm|pdf|txt|xml)$/i;
1541
+ var BOT_UA_PATTERN = /curl|wget|python-requests|python-urllib|node-fetch|axios|got|okhttp|go-http-client|libwww|httpie|java\/|ruby|php|scrapy|headless|phantomjs|selenium|playwright|puppeteer|bot|crawler|spider|slurp/i;
1542
+ var BROWSER_UA_PATTERN = /mozilla\/\d/i;
1543
+ var _patterns = { STATIC_EXTENSIONS, BOT_UA_PATTERN, BROWSER_UA_PATTERN };
1544
+
1545
+ // src/edge-core/events.ts
1546
+ function sampled(rate, random = Math.random) {
1547
+ if (rate <= 0) return false;
1548
+ if (rate >= 1) return true;
1549
+ return random() < rate;
1550
+ }
1551
+
1552
+ // src/adapters/anonymous-beacon.ts
1553
+ function readHeader(raw, name) {
1554
+ const value = raw[name];
1555
+ if (typeof value === "string") return value;
1556
+ if (Array.isArray(value)) return value[0];
1557
+ return void 0;
1558
+ }
1559
+ function classifyPassThroughTraffic(path, rawHeaders) {
1560
+ if (_patterns.STATIC_EXTENSIONS.test(path)) return { kind: "never" };
1561
+ const credentials = extractCredentials(rawHeaders, {});
1562
+ if (credentials.astraId || credentials.apiKey || credentials.jwt) {
1563
+ return { kind: "never" };
1564
+ }
1565
+ const userAgent = readHeader(rawHeaders, "user-agent") ?? "";
1566
+ const { apiKeyFormat, platformHeaders } = sanitizeHeaders(rawHeaders);
1567
+ const fingerprint = detectPlatformFingerprint({ userAgent, apiKeyFormat, platformHeaders });
1568
+ if (fingerprint) {
1569
+ return {
1570
+ kind: "platform",
1571
+ protocol: "platform-ua",
1572
+ evidence: { vendor: fingerprint.vendor, userAgent: userAgent || "(none)" }
1573
+ };
1574
+ }
1575
+ if (!userAgent || _patterns.BOT_UA_PATTERN.test(userAgent) || !_patterns.BROWSER_UA_PATTERN.test(userAgent)) {
1576
+ return {
1577
+ kind: "anonymous-bot",
1578
+ protocol: "ua-heuristic",
1579
+ evidence: { userAgent: userAgent || "(none)" }
1580
+ };
1581
+ }
1582
+ if (!readHeader(rawHeaders, "sec-fetch-mode") && !readHeader(rawHeaders, "accept-language")) {
1583
+ return {
1584
+ kind: "anonymous-bot",
1585
+ protocol: "ua-heuristic",
1586
+ evidence: { userAgent, spoofedBrowser: true }
1587
+ };
1588
+ }
1589
+ return { kind: "never" };
1590
+ }
1591
+ function createAnonymousBeacon(config, options) {
1592
+ const counterpartyId = config.counterpartyId;
1593
+ if (!counterpartyId) return () => {
1594
+ };
1595
+ const random = options?.random ?? Math.random;
1596
+ let edgeConfig;
1597
+ let refreshing = false;
1598
+ const refreshConfig = () => {
1599
+ if (refreshing) return;
1600
+ refreshing = true;
1601
+ getEdgeConfig(config, counterpartyId).then((cfg) => {
1602
+ edgeConfig = cfg;
1603
+ }).catch(() => {
1604
+ }).finally(() => {
1605
+ refreshing = false;
1606
+ });
1607
+ };
1608
+ refreshConfig();
1609
+ return (input) => {
1610
+ try {
1611
+ refreshConfig();
1612
+ const cfg = edgeConfig;
1613
+ if (!cfg) return;
1614
+ const traffic = classifyPassThroughTraffic(input.requestPath, input.rawHeaders);
1615
+ if (traffic.kind === "never") return;
1616
+ if (traffic.kind === "anonymous-bot" && !sampled(cfg.sampling.anonymousBeaconRate, random)) {
1617
+ return;
1618
+ }
1619
+ const userAgent = readHeader(input.rawHeaders, "user-agent");
1620
+ const xff = input.rawHeaders["x-forwarded-for"];
1621
+ const xffStr = Array.isArray(xff) ? xff.join(", ") : xff;
1622
+ const sourceIp = input.sourceIp ?? xffStr?.split(",")[0]?.trim();
1623
+ reportUnregisteredAttempt(config, {
1624
+ ...input.counterpartyUrl && { counterpartyUrl: input.counterpartyUrl },
1625
+ counterpartyType: options?.counterpartyType ?? config.counterpartyType ?? "api",
1626
+ ...sourceIp && { sourceIp },
1627
+ ...userAgent && { userAgent },
1628
+ requestPath: input.requestPath,
1629
+ requestMethod: input.requestMethod,
1630
+ ...traffic.protocol && { protocol: traffic.protocol },
1631
+ ...traffic.evidence && { evidence: traffic.evidence },
1632
+ observedMetadata: buildSdkObservedMetadata(input.rawHeaders)
1633
+ }).catch(() => {
1634
+ });
1635
+ } catch {
1636
+ }
1637
+ };
1638
+ }
1271
1639
 
1272
1640
  // src/adapters/express.ts
1273
1641
  function defaultExtractCredentials(req) {
@@ -1279,7 +1647,7 @@ function defaultExtractCredentials(req) {
1279
1647
  function extractAstraSyncCredentials(req) {
1280
1648
  return extractHttpCredentials(req.headers);
1281
1649
  }
1282
- function headerValue(value) {
1650
+ function headerValue2(value) {
1283
1651
  if (typeof value === "string") return value;
1284
1652
  if (Array.isArray(value)) return value[0];
1285
1653
  return void 0;
@@ -1287,9 +1655,9 @@ function headerValue(value) {
1287
1655
  function resolveRequestPdlss(req, routeConfig, customExtractPurpose, customExtractAction) {
1288
1656
  return resolveHttpPdlss({
1289
1657
  method: req.method,
1290
- astraPurpose: headerValue(req.headers["x-astra-purpose"]),
1291
- astraAction: headerValue(req.headers["x-astra-action"]),
1292
- legacyPurpose: headerValue(req.headers["x-purpose"] ?? req.headers["X-Purpose"]),
1658
+ astraPurpose: headerValue2(req.headers["x-astra-purpose"]),
1659
+ astraAction: headerValue2(req.headers["x-astra-action"]),
1660
+ legacyPurpose: headerValue2(req.headers["x-purpose"] ?? req.headers["X-Purpose"]),
1293
1661
  queryPurpose: typeof req.query.purpose === "string" ? req.query.purpose : void 0,
1294
1662
  routePurpose: routeConfig?.purpose,
1295
1663
  routeAction: routeConfig?.action,
@@ -1372,6 +1740,11 @@ function defaultOnDenied(result, _req, res) {
1372
1740
  }
1373
1741
  var DEFAULT_ROUTES_REFRESH_MS = 5 * 60 * 1e3;
1374
1742
  function createMiddleware(options) {
1743
+ if ("routes" in options) {
1744
+ console.warn(
1745
+ "[VerificationGateway] `routes` is dashboard-managed since v2.9.7; this option is ignored \u2014 configure route policy in your AstraSync dashboard."
1746
+ );
1747
+ }
1375
1748
  const {
1376
1749
  extractCredentials: customExtractCredentials,
1377
1750
  extractPurpose: customExtractPurpose,
@@ -1383,12 +1756,25 @@ function createMiddleware(options) {
1383
1756
  routesRefreshMs = DEFAULT_ROUTES_REFRESH_MS,
1384
1757
  failOnError = "open",
1385
1758
  caseInsensitiveRouteMatch = false,
1759
+ anonymousBeacon = true,
1386
1760
  ...config
1387
1761
  } = options;
1388
1762
  if (config.apiBaseUrl) {
1389
1763
  prefetchWellKnown(config.apiBaseUrl).catch(() => {
1390
1764
  });
1391
1765
  }
1766
+ const emitAnonymousBeacon = anonymousBeacon ? createAnonymousBeacon(config, { counterpartyType: config.counterpartyType ?? "api" }) : void 0;
1767
+ const beaconPassThrough = emitAnonymousBeacon ? (req) => {
1768
+ const xff = req.headers["x-forwarded-for"];
1769
+ const xffStr = Array.isArray(xff) ? xff.join(", ") : xff;
1770
+ emitAnonymousBeacon({
1771
+ requestPath: req.path,
1772
+ requestMethod: req.method,
1773
+ counterpartyUrl: config.counterpartyUrl || `${req.protocol}://${req.get("host")}`,
1774
+ sourceIp: req.ip ?? (xffStr ? xffStr.split(",")[0].trim() : void 0),
1775
+ rawHeaders: req.headers
1776
+ });
1777
+ } : void 0;
1392
1778
  let cachedRoutes = [];
1393
1779
  let lastFetchAt = 0;
1394
1780
  let refreshing = null;
@@ -1449,6 +1835,7 @@ function createMiddleware(options) {
1449
1835
  cachedRoutes.length === 0 ? "no-policy" : "no-match"
1450
1836
  );
1451
1837
  }
1838
+ beaconPassThrough?.(req);
1452
1839
  return next();
1453
1840
  }
1454
1841
  const wellKnownUrls = config.apiBaseUrl ? await getWellKnownUrls(config.apiBaseUrl).catch(() => void 0) : void 0;
@@ -1459,6 +1846,7 @@ function createMiddleware(options) {
1459
1846
  res.setHeader("X-Astra-Gateway-Mode", "unenforced");
1460
1847
  res.setHeader("X-Astra-Gateway-Reason", "route-none");
1461
1848
  }
1849
+ beaconPassThrough?.(req);
1462
1850
  return next();
1463
1851
  }
1464
1852
  const pdlssPair = resolveRequestPdlss(
@@ -1551,16 +1939,10 @@ function createMiddleware(options) {
1551
1939
  forwardedFor: forwardedForStr,
1552
1940
  agentCardUrl,
1553
1941
  // Workstream D — capture EVERY inbound header (SDK-only merchants
1554
- // have no edge to do it upstream). Local string work, secrets
1555
- // stripped; out of the verify cache key so it doesn't defeat caching.
1556
- observedMetadata: (() => {
1557
- const { headers, apiKeyFormat, platformHeaders } = sanitizeHeaders(req.headers);
1558
- return {
1559
- headers,
1560
- ...apiKeyFormat && { apiKeyFormat },
1561
- ...platformHeaders && { platformHeaders }
1562
- };
1563
- })()
1942
+ // have no edge to do it upstream) plus the CDN-derived connection
1943
+ // block. Local string work, secrets stripped; out of the verify
1944
+ // cache key so it doesn't defeat caching.
1945
+ observedMetadata: buildSdkObservedMetadata(req.headers)
1564
1946
  }
1565
1947
  };
1566
1948
  let result = await verify(config, verificationRequest);
@@ -1754,7 +2136,7 @@ function resolveNextPdlss(request, routeConfig) {
1754
2136
  routeAction: routeConfig?.action
1755
2137
  });
1756
2138
  }
1757
- function generateCommerceShieldHtml(result, options) {
2139
+ function generateInterstitialHtml(result, options) {
1758
2140
  const title = escapeHtml(options.commerceShield?.title || "AstraSync Agent Verification");
1759
2141
  const message = escapeHtml(
1760
2142
  options.commerceShield?.message || result.guidance?.message || "This site verifies AI agents before granting access. We noticed you're visiting without AstraSync credentials."
@@ -1910,13 +2292,31 @@ function generateCommerceShieldHtml(result, options) {
1910
2292
  }
1911
2293
  var DEFAULT_ROUTES_REFRESH_MS2 = 5 * 60 * 1e3;
1912
2294
  function createMiddleware2(options) {
2295
+ if ("routes" in options) {
2296
+ console.warn(
2297
+ "[VerificationGateway/Next.js] `routes` is dashboard-managed since v2.9.7; this option is ignored \u2014 configure route policy in your AstraSync dashboard."
2298
+ );
2299
+ }
1913
2300
  const {
1914
2301
  skipPaths = [],
1915
- showCommerceShield = true,
2302
+ showInterstitial,
2303
+ showCommerceShield,
1916
2304
  enableRuntimeChallenge = true,
1917
2305
  routesRefreshMs = DEFAULT_ROUTES_REFRESH_MS2,
2306
+ anonymousBeacon = true,
1918
2307
  ...config
1919
2308
  } = options;
2309
+ const interstitialEnabled = showInterstitial ?? showCommerceShield ?? true;
2310
+ const emitAnonymousBeacon = anonymousBeacon ? createAnonymousBeacon(config, { counterpartyType: config.counterpartyType ?? "website" }) : void 0;
2311
+ const beaconPassThrough = emitAnonymousBeacon ? (request, pathname) => {
2312
+ emitAnonymousBeacon({
2313
+ requestPath: pathname,
2314
+ requestMethod: request.method,
2315
+ counterpartyUrl: config.counterpartyUrl || request.nextUrl.origin,
2316
+ sourceIp: request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() || void 0,
2317
+ rawHeaders: Object.fromEntries(request.headers.entries())
2318
+ });
2319
+ } : void 0;
1920
2320
  let cachedRoutes = [];
1921
2321
  let lastFetchAt = 0;
1922
2322
  let refreshing = null;
@@ -1958,9 +2358,11 @@ function createMiddleware2(options) {
1958
2358
  }
1959
2359
  const routeConfig = findRouteConfig2(cachedRoutes, pathname, request.method);
1960
2360
  if (!routeConfig) {
2361
+ beaconPassThrough?.(request, pathname);
1961
2362
  return NextResponse.next();
1962
2363
  }
1963
2364
  if (routeConfig.minAccessLevel === "none") {
2365
+ beaconPassThrough?.(request, pathname);
1964
2366
  return NextResponse.next();
1965
2367
  }
1966
2368
  const credentials = extractCredentialsFromNextRequest(request);
@@ -2012,8 +2414,8 @@ function createMiddleware2(options) {
2012
2414
  { status: 403 }
2013
2415
  );
2014
2416
  }
2015
- if (showCommerceShield) {
2016
- return new NextResponse(generateCommerceShieldHtml(preCheckResult, options), {
2417
+ if (interstitialEnabled) {
2418
+ return new NextResponse(generateInterstitialHtml(preCheckResult, options), {
2017
2419
  status: 200,
2018
2420
  headers: {
2019
2421
  "Content-Type": "text/html",
@@ -2041,17 +2443,9 @@ function createMiddleware2(options) {
2041
2443
  host: request.headers.get("host") || void 0,
2042
2444
  forwardedFor,
2043
2445
  agentCardUrl: request.headers.get("x-astrasync-agent-card") || void 0,
2044
- // Workstream D — full sanitised header capture (Web Headers → plain map).
2045
- observedMetadata: (() => {
2046
- const { headers, apiKeyFormat, platformHeaders } = sanitizeHeaders(
2047
- Object.fromEntries(request.headers.entries())
2048
- );
2049
- return {
2050
- headers,
2051
- ...apiKeyFormat && { apiKeyFormat },
2052
- ...platformHeaders && { platformHeaders }
2053
- };
2054
- })()
2446
+ // Workstream D — full sanitised header capture + CDN-derived
2447
+ // connection block (Web Headers → plain map).
2448
+ observedMetadata: buildSdkObservedMetadata(Object.fromEntries(request.headers.entries()))
2055
2449
  }
2056
2450
  };
2057
2451
  let result = await verify(config, verificationRequest);
@@ -2106,8 +2500,8 @@ function createMiddleware2(options) {
2106
2500
  }
2107
2501
  );
2108
2502
  }
2109
- if (showCommerceShield) {
2110
- return new NextResponse(generateCommerceShieldHtml(result, options), {
2503
+ if (interstitialEnabled) {
2504
+ return new NextResponse(generateInterstitialHtml(result, options), {
2111
2505
  status: 200,
2112
2506
  headers: {
2113
2507
  "Content-Type": "text/html",
@@ -2195,7 +2589,7 @@ var VerificationGatewayClient = class {
2195
2589
  /**
2196
2590
  * Quick verification — checks credentials and policy in one call.
2197
2591
  *
2198
- * Round-18 G4: return shape mirrors `VerificationResult`'s identity/policy
2592
+ * The return shape mirrors `VerificationResult`'s identity/policy
2199
2593
  * split. Map to HTTP status the same way: `!identityVerified` → 401,
2200
2594
  * `identityVerified && !policyAllowed` → 403.
2201
2595
  */
@@ -2625,8 +3019,8 @@ function extractX402TransactionValue(req) {
2625
3019
  // src/transport/rfc9421.ts
2626
3020
  import { parseDictionary } from "structured-headers";
2627
3021
  function parseRFC9421(headers) {
2628
- const sigInput = readHeader(headers, "signature-input");
2629
- const sig = readHeader(headers, "signature");
3022
+ const sigInput = readHeader2(headers, "signature-input");
3023
+ const sig = readHeader2(headers, "signature");
2630
3024
  if (!sigInput || !sig) return null;
2631
3025
  let inputDict;
2632
3026
  let sigDict;
@@ -2670,7 +3064,7 @@ function parseRFC9421(headers) {
2670
3064
  if (signatures.length === 0) return null;
2671
3065
  return { signatures };
2672
3066
  }
2673
- function readHeader(headers, name) {
3067
+ function readHeader2(headers, name) {
2674
3068
  for (const key of Object.keys(headers)) {
2675
3069
  if (key.toLowerCase() === name) {
2676
3070
  const raw = headers[key];
@@ -2988,11 +3382,11 @@ function extractACPContext(request) {
2988
3382
  const purpose = mapACPRequestToPurpose(method, path);
2989
3383
  const sessionId = extractSessionId2(path);
2990
3384
  const headers = request.headers ?? {};
2991
- const signatureHeader = readHeader2(headers, "signature");
2992
- const timestampHeader = readHeader2(headers, "timestamp");
2993
- const idempotencyKey = readHeader2(headers, "idempotency-key");
2994
- const apiVersion = readHeader2(headers, "api-version");
2995
- const bearer = extractBearer(readHeader2(headers, "authorization"));
3385
+ const signatureHeader = readHeader3(headers, "signature");
3386
+ const timestampHeader = readHeader3(headers, "timestamp");
3387
+ const idempotencyKey = readHeader3(headers, "idempotency-key");
3388
+ const apiVersion = readHeader3(headers, "api-version");
3389
+ const bearer = extractBearer(readHeader3(headers, "authorization"));
2996
3390
  const body = request.body ?? {};
2997
3391
  const merchantId = coerceString3(body.merchant_id ?? body.merchantId);
2998
3392
  const totals = Array.isArray(body.totals) ? body.totals : void 0;
@@ -3060,7 +3454,7 @@ function extractFulfillmentOption(body) {
3060
3454
  }
3061
3455
  return void 0;
3062
3456
  }
3063
- function readHeader2(headers, name) {
3457
+ function readHeader3(headers, name) {
3064
3458
  for (const key of Object.keys(headers)) {
3065
3459
  if (key.toLowerCase() === name) {
3066
3460
  const raw = headers[key];
@@ -3854,9 +4248,9 @@ function toBuf(bytes) {
3854
4248
  new Uint8Array(out).set(bytes);
3855
4249
  return out;
3856
4250
  }
3857
- function checkTimestamp(headerValue2, toleranceSec, nowFn) {
3858
- if (!headerValue2) return { ok: false, error: "missing Timestamp header" };
3859
- const ts = parseTimestamp(headerValue2);
4251
+ function checkTimestamp(headerValue3, toleranceSec, nowFn) {
4252
+ if (!headerValue3) return { ok: false, error: "missing Timestamp header" };
4253
+ const ts = parseTimestamp(headerValue3);
3860
4254
  if (ts === null) return { ok: false, error: "unparseable Timestamp header" };
3861
4255
  const now = nowFn ? nowFn() : Math.floor(Date.now() / 1e3);
3862
4256
  if (Math.abs(now - ts) > toleranceSec) {
@@ -3900,7 +4294,7 @@ async function getSubtle() {
3900
4294
  // src/transport/mpp.ts
3901
4295
  import { Challenge, Credential, Receipt } from "mppx";
3902
4296
  function extractMPPFromRequest(request) {
3903
- const auth = readHeader3(request.headers, "authorization");
4297
+ const auth = readHeader4(request.headers, "authorization");
3904
4298
  if (!auth || !/^\s*Payment\s+/i.test(auth)) return null;
3905
4299
  try {
3906
4300
  const credential = Credential.deserialize(auth);
@@ -3928,7 +4322,7 @@ function extractMPPFromResponse(response) {
3928
4322
  offeredMethods: methods
3929
4323
  };
3930
4324
  }
3931
- const receiptHeader = readHeader3(response.headers, "payment-receipt");
4325
+ const receiptHeader = readHeader4(response.headers, "payment-receipt");
3932
4326
  if (receiptHeader) {
3933
4327
  try {
3934
4328
  const parsed = Receipt.deserialize(receiptHeader);
@@ -3948,7 +4342,7 @@ function extractMPPFromResponse(response) {
3948
4342
  return { kind: "error", error: { type: "invalid-receipt-encoding" } };
3949
4343
  }
3950
4344
  }
3951
- const contentType = readHeader3(response.headers, "content-type");
4345
+ const contentType = readHeader4(response.headers, "content-type");
3952
4346
  if (contentType && /application\/problem\+json/i.test(contentType)) {
3953
4347
  const body = typeof response.body === "object" && response.body !== null ? response.body : {};
3954
4348
  return {
@@ -3971,7 +4365,7 @@ function extractMPPContext(message) {
3971
4365
  return extractMPPFromRequest(message);
3972
4366
  }
3973
4367
  function collectChallenges(response) {
3974
- const wwwAuth = readHeader3(response.headers, "www-authenticate");
4368
+ const wwwAuth = readHeader4(response.headers, "www-authenticate");
3975
4369
  if (!wwwAuth) return [];
3976
4370
  const headers = new Headers();
3977
4371
  headers.set("www-authenticate", wwwAuth);
@@ -3999,7 +4393,7 @@ function summarizeChallenge(ch) {
3999
4393
  opaque: raw.opaque
4000
4394
  };
4001
4395
  }
4002
- function readHeader3(headers, name) {
4396
+ function readHeader4(headers, name) {
4003
4397
  for (const key of Object.keys(headers)) {
4004
4398
  if (key.toLowerCase() === name) {
4005
4399
  const raw = headers[key];
@@ -4091,14 +4485,14 @@ import {
4091
4485
  } from "@x402/core/schemas";
4092
4486
  import { safeBase64Decode } from "@x402/core/utils";
4093
4487
  function extractX402FromRequest(request) {
4094
- const headerValue2 = readHeader4(request.headers, "x-payment");
4488
+ const headerValue3 = readHeader5(request.headers, "x-payment");
4095
4489
  if (request.body && typeof request.body === "object") {
4096
4490
  const parsed = tryParsePayload(request.body);
4097
4491
  if (parsed) return buildPayloadContext(parsed, "body");
4098
4492
  }
4099
- if (headerValue2) {
4493
+ if (headerValue3) {
4100
4494
  try {
4101
- const decoded = safeBase64Decode(headerValue2);
4495
+ const decoded = safeBase64Decode(headerValue3);
4102
4496
  if (decoded) {
4103
4497
  const json = JSON.parse(decoded);
4104
4498
  const parsed = tryParsePayload(json);
@@ -4121,10 +4515,10 @@ function extractX402FromResponse(response) {
4121
4515
  const parsed = tryParseRequired(response.body);
4122
4516
  if (parsed) return buildRequiredContext(parsed, "body");
4123
4517
  }
4124
- const headerValue2 = readHeader4(response.headers, "x-payment-required");
4125
- if (headerValue2) {
4518
+ const headerValue3 = readHeader5(response.headers, "x-payment-required");
4519
+ if (headerValue3) {
4126
4520
  try {
4127
- const decoded = safeBase64Decode(headerValue2);
4521
+ const decoded = safeBase64Decode(headerValue3);
4128
4522
  if (decoded) {
4129
4523
  const json = JSON.parse(decoded);
4130
4524
  const parsed = tryParseRequired(json);
@@ -4221,7 +4615,7 @@ function coerceVersion(v) {
4221
4615
  if (v === 1 || v === 2) return v;
4222
4616
  return null;
4223
4617
  }
4224
- function readHeader4(headers, name) {
4618
+ function readHeader5(headers, name) {
4225
4619
  if (!headers) return void 0;
4226
4620
  for (const key of Object.keys(headers)) {
4227
4621
  if (key.toLowerCase() === name) {
@@ -4969,8 +5363,11 @@ function mcpToPdlss(parsed, requestPath, headerPurpose, headerAction, toolGate)
4969
5363
  } else if (parsed.actionFromBody && parsed.actionSourceFromBody) {
4970
5364
  action = parsed.actionFromBody;
4971
5365
  actionSource = parsed.actionSourceFromBody;
5366
+ } else if (parsed.method === "tools/call") {
5367
+ action = void 0;
5368
+ actionSource = void 0;
4972
5369
  } else {
4973
- action = parsed.toolName ? parsed.method === "tools/call" ? parsed.toolName : `${parsed.method}:${parsed.toolName}` : parsed.method;
5370
+ action = parsed.toolName ? `${parsed.method}:${parsed.toolName}` : parsed.method;
4974
5371
  actionSource = "transport_layer";
4975
5372
  }
4976
5373
  return { purpose, action, resource, purposeSource, actionSource };
@@ -5085,6 +5482,15 @@ function createMcpMiddleware(options) {
5085
5482
  prefetchWellKnown(config.apiBaseUrl).catch(() => {
5086
5483
  });
5087
5484
  }
5485
+ if (toolGates) {
5486
+ for (const [toolName, gate] of Object.entries(toolGates)) {
5487
+ if (typeof gate === "string") {
5488
+ console.warn(
5489
+ `[VerificationGateway/MCP] toolGates['${toolName}'] uses the bare-string shorthand ('${gate}'). Gated tools need a PDLSS purpose (else PDLSS_PURPOSE_REQUIRED 400) and evaluate purpose-only without a declared action. Use the object form instead: { minAccessLevel: '${gate}', purpose: '...', action: '...' }.`
5490
+ );
5491
+ }
5492
+ }
5493
+ }
5088
5494
  return async (req, res, next) => {
5089
5495
  try {
5090
5496
  if (skip) return next();
@@ -5204,7 +5610,13 @@ function createMcpMiddleware(options) {
5204
5610
  callerMetadata: {
5205
5611
  sourceIp: req.ip,
5206
5612
  userAgent: req.headers["user-agent"],
5207
- host: req.headers.host
5613
+ host: req.headers.host,
5614
+ // Capture EVERY inbound header (parity with the express adapter —
5615
+ // MCP-only merchants have no edge to do it upstream) plus the
5616
+ // CDN-derived connection block. Local string work, secrets
5617
+ // stripped; out of the verify cache key so it doesn't defeat
5618
+ // caching.
5619
+ observedMetadata: buildSdkObservedMetadata(req.headers)
5208
5620
  }
5209
5621
  };
5210
5622
  let result = await verify(config, verificationRequest);
@@ -5989,212 +6401,6 @@ async function recordDecision2(config, params) {
5989
6401
  blockchainTxHash: result.blockchainTxHash
5990
6402
  };
5991
6403
  }
5992
-
5993
- // src/edge-config.ts
5994
- var DEFAULT_EDGE_CONFIG = Object.freeze({
5995
- version: 1,
5996
- mode: "observe",
5997
- depth: "classify",
5998
- pathRules: [],
5999
- sampling: Object.freeze({ anonymousBeaconRate: 0.1 }),
6000
- failurePosture: "open"
6001
- });
6002
- var EDGE_MODES = ["observe", "enforce"];
6003
- var EDGE_DEPTHS = ["classify", "authenticate", "authorize"];
6004
- function isEdgeConfig(value) {
6005
- if (typeof value !== "object" || value === null) return false;
6006
- const v = value;
6007
- if (v.version !== 1) return false;
6008
- if (!EDGE_MODES.includes(v.mode)) return false;
6009
- if (!EDGE_DEPTHS.includes(v.depth)) return false;
6010
- if (!Array.isArray(v.pathRules)) return false;
6011
- for (const rule of v.pathRules) {
6012
- if (typeof rule !== "object" || rule === null) return false;
6013
- const r = rule;
6014
- if (typeof r.pattern !== "string" || r.pattern.length === 0) return false;
6015
- if (r.mode !== void 0 && !EDGE_MODES.includes(r.mode)) return false;
6016
- if (r.depth !== void 0 && !EDGE_DEPTHS.includes(r.depth)) return false;
6017
- }
6018
- const sampling = v.sampling;
6019
- if (typeof sampling !== "object" || sampling === null || typeof sampling.anonymousBeaconRate !== "number" || sampling.anonymousBeaconRate < 0 || sampling.anonymousBeaconRate > 1) {
6020
- return false;
6021
- }
6022
- return v.failurePosture === "open";
6023
- }
6024
- async function fetchEdgeConfig(config, counterpartyId, opts) {
6025
- if (!counterpartyId) return null;
6026
- const headers = { Accept: "application/json" };
6027
- if (config.apiKey) {
6028
- headers["Authorization"] = `Bearer ${config.apiKey}`;
6029
- headers["X-API-Key"] = config.apiKey;
6030
- }
6031
- if (opts?.etag) headers["If-None-Match"] = opts.etag;
6032
- try {
6033
- const response = await fetch(
6034
- `${config.apiBaseUrl}/endpoints/${encodeURIComponent(counterpartyId)}/edge-config`,
6035
- { method: "GET", headers, signal: AbortSignal.timeout(opts?.timeoutMs ?? 5e3) }
6036
- );
6037
- if (response.status === 304) return { notModified: true };
6038
- if (!response.ok) return null;
6039
- const body = await response.json();
6040
- const edgeConfig = body.data?.edgeConfig;
6041
- if (!isEdgeConfig(edgeConfig)) return null;
6042
- return { edgeConfig, etag: response.headers.get("etag") ?? void 0 };
6043
- } catch {
6044
- return null;
6045
- }
6046
- }
6047
- var DEFAULT_TTL_MS = 60 * 1e3;
6048
- var DEFAULT_MAX_STALE_MS = 24 * 60 * 60 * 1e3;
6049
- var cache2 = /* @__PURE__ */ new Map();
6050
- var inflight2 = /* @__PURE__ */ new Map();
6051
- function cacheKey(apiBaseUrl, counterpartyId) {
6052
- return `${apiBaseUrl}|${counterpartyId}`;
6053
- }
6054
- function degradeToObserve(config) {
6055
- if (config.mode !== "enforce" && !config.pathRules.some((r) => r.mode === "enforce")) {
6056
- return config;
6057
- }
6058
- return {
6059
- ...config,
6060
- mode: "observe",
6061
- pathRules: config.pathRules.map((r) => r.mode === "enforce" ? { ...r, mode: "observe" } : r)
6062
- };
6063
- }
6064
- async function revalidate(config, counterpartyId) {
6065
- const key = cacheKey(config.apiBaseUrl, counterpartyId);
6066
- const existing = inflight2.get(key);
6067
- if (existing) return existing;
6068
- const promise = fetchEdgeConfig(config, counterpartyId, { etag: cache2.get(key)?.etag }).then((result) => {
6069
- if (result === null) return;
6070
- const now = Date.now();
6071
- if ("notModified" in result) {
6072
- const entry = cache2.get(key);
6073
- if (entry) entry.lastSuccessAt = now;
6074
- return;
6075
- }
6076
- cache2.set(key, { config: result.edgeConfig, etag: result.etag, lastSuccessAt: now });
6077
- }).finally(() => {
6078
- inflight2.delete(key);
6079
- });
6080
- inflight2.set(key, promise);
6081
- return promise;
6082
- }
6083
- async function getEdgeConfig(config, counterpartyId, opts) {
6084
- const ttlMs = opts?.ttlMs ?? DEFAULT_TTL_MS;
6085
- const maxStaleMs = opts?.maxStaleMs ?? DEFAULT_MAX_STALE_MS;
6086
- const key = cacheKey(config.apiBaseUrl, counterpartyId);
6087
- const entry = cache2.get(key);
6088
- if (entry) {
6089
- const age = Date.now() - entry.lastSuccessAt;
6090
- if (age > ttlMs) {
6091
- revalidate(config, counterpartyId).catch(() => {
6092
- });
6093
- }
6094
- return age > maxStaleMs ? degradeToObserve(entry.config) : entry.config;
6095
- }
6096
- await revalidate(config, counterpartyId).catch(() => {
6097
- });
6098
- return cache2.get(key)?.config ?? DEFAULT_EDGE_CONFIG;
6099
- }
6100
- function matchEdgePathRule(rules, path) {
6101
- for (const rule of rules) {
6102
- if (globToRegExp(rule.pattern).test(path)) return rule;
6103
- }
6104
- return void 0;
6105
- }
6106
- function globToRegExp(pattern) {
6107
- const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
6108
- return new RegExp(`^${escaped}$`, "i");
6109
- }
6110
- function _resetEdgeConfigCache() {
6111
- cache2.clear();
6112
- inflight2.clear();
6113
- }
6114
-
6115
- // src/platform-signatures.ts
6116
- var PLATFORM_AGENT_SIGNATURES = [
6117
- {
6118
- vendor: "claude",
6119
- displayName: "Anthropic Claude",
6120
- uaPatterns: [/claude/i, /anthropic/i],
6121
- agentCardPatterns: [/anthropic\.com/i, /claude\.ai/i],
6122
- apiKeyFormatPatterns: [/^sk-ant/i],
6123
- headerPatterns: [/^anthropic-version/i, /^anthropic-beta/i, /^x-anthropic-/i]
6124
- },
6125
- {
6126
- vendor: "chatgpt",
6127
- displayName: "OpenAI ChatGPT",
6128
- uaPatterns: [/chatgpt/i, /openai/i, /gpt-?\d/i],
6129
- agentCardPatterns: [/openai\.com/i, /chatgpt\.com/i],
6130
- apiKeyFormatPatterns: [/^sk-proj/i, /^sk-svcacct/i],
6131
- headerPatterns: [/^openai-organization/i, /^openai-project/i, /^openai-version/i]
6132
- },
6133
- {
6134
- vendor: "gemini",
6135
- displayName: "Google Gemini",
6136
- uaPatterns: [/gemini/i, /google-gemini/i, /bard/i],
6137
- agentCardPatterns: [/gemini\.google\.com/i, /ai\.google\.dev/i],
6138
- apiKeyFormatPatterns: [/^aiza/i],
6139
- headerPatterns: [/^x-goog-user-project/i, /^x-goog-api-client/i]
6140
- },
6141
- {
6142
- vendor: "cursor",
6143
- displayName: "Cursor",
6144
- uaPatterns: [/cursor/i],
6145
- agentCardPatterns: [/cursor\.sh/i, /cursor\.com/i]
6146
- },
6147
- {
6148
- vendor: "goose",
6149
- displayName: "Block Goose",
6150
- uaPatterns: [/goose/i, /block-goose/i],
6151
- agentCardPatterns: [/block\.xyz\/goose/i, /goose\.tools/i]
6152
- },
6153
- {
6154
- vendor: "perplexity",
6155
- displayName: "Perplexity",
6156
- uaPatterns: [/perplexity/i],
6157
- agentCardPatterns: [/perplexity\.ai/i],
6158
- apiKeyFormatPatterns: [/^pplx-/i]
6159
- }
6160
- ];
6161
- function matchPlatformSignature(signatures, input) {
6162
- const ua = input.userAgent ?? "";
6163
- const cardUrl = input.agentCardUrl ?? "";
6164
- const apiKeyFormat = input.apiKeyFormat ?? "";
6165
- const headerLines = [];
6166
- if (input.platformHeaders) {
6167
- for (const [name, value] of Object.entries(input.platformHeaders)) {
6168
- headerLines.push(name, `${name}: ${value}`);
6169
- }
6170
- }
6171
- for (const sig of signatures) {
6172
- const uaMatch = sig.uaPatterns?.some((p) => p.test(ua));
6173
- const cardMatch = sig.agentCardPatterns?.some((p) => p.test(cardUrl));
6174
- const keyMatch = apiKeyFormat.length > 0 && sig.apiKeyFormatPatterns?.some((p) => p.test(apiKeyFormat));
6175
- const headerMatch = headerLines.length > 0 && sig.headerPatterns?.some((p) => headerLines.some((line) => p.test(line)));
6176
- if (uaMatch || cardMatch || keyMatch || headerMatch) {
6177
- const uaBase = ua.split(/[\s/]/)[0]?.toLowerCase() ?? "";
6178
- const cardHost = (() => {
6179
- try {
6180
- return cardUrl ? new URL(cardUrl).host : "";
6181
- } catch {
6182
- return "";
6183
- }
6184
- })();
6185
- const keyBasis = uaBase || cardHost || apiKeyFormat || "unknown";
6186
- return {
6187
- vendor: sig.vendor,
6188
- fingerprintKey: `${sig.vendor}:${keyBasis}`,
6189
- displayName: sig.displayName
6190
- };
6191
- }
6192
- }
6193
- return void 0;
6194
- }
6195
- function detectPlatformFingerprint(input) {
6196
- return matchPlatformSignature(PLATFORM_AGENT_SIGNATURES, input);
6197
- }
6198
6404
  export {
6199
6405
  ACCESS_LEVEL_DESCRIPTIONS,
6200
6406
  ACCESS_LEVEL_HIERARCHY,
@@ -6202,6 +6408,7 @@ export {
6202
6408
  AstraSync,
6203
6409
  AstraSyncError,
6204
6410
  AuthenticationError,
6411
+ CAPTURE_SCHEMA_VERSION,
6205
6412
  ChallengeHandler,
6206
6413
  DEFAULT_EDGE_CONFIG,
6207
6414
  DEFAULT_TRUST_THRESHOLDS,
@@ -6220,9 +6427,11 @@ export {
6220
6427
  authorizeSettlement,
6221
6428
  awaitStepUpApproval,
6222
6429
  buildGuidance,
6430
+ buildSdkObservedMetadata,
6223
6431
  clearCache,
6224
6432
  createMcpMiddleware,
6225
6433
  degradeToObserve,
6434
+ deriveConnectionFromHeaders,
6226
6435
  detectPlatformFingerprint,
6227
6436
  determineAccessLevel,
6228
6437
  express_exports as express,