@astrasyncai/verification-gateway 4.3.0 → 4.5.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 (87) hide show
  1. package/dist/adapters/express.js +104 -28
  2. package/dist/adapters/express.js.map +1 -1
  3. package/dist/adapters/express.mjs +104 -28
  4. package/dist/adapters/express.mjs.map +1 -1
  5. package/dist/adapters/mcp.js +63 -21
  6. package/dist/adapters/mcp.js.map +1 -1
  7. package/dist/adapters/mcp.mjs +63 -21
  8. package/dist/adapters/mcp.mjs.map +1 -1
  9. package/dist/adapters/nextjs.js +103 -27
  10. package/dist/adapters/nextjs.js.map +1 -1
  11. package/dist/adapters/nextjs.mjs +103 -27
  12. package/dist/adapters/nextjs.mjs.map +1 -1
  13. package/dist/adapters/sdk.js +33 -12
  14. package/dist/adapters/sdk.js.map +1 -1
  15. package/dist/adapters/sdk.mjs +33 -12
  16. package/dist/adapters/sdk.mjs.map +1 -1
  17. package/dist/agent/index.js +24 -3
  18. package/dist/agent/index.js.map +1 -1
  19. package/dist/agent/index.mjs +24 -3
  20. package/dist/agent/index.mjs.map +1 -1
  21. package/dist/bin/astrasync-claude-hook.js +31 -10
  22. package/dist/bin/astrasync-codex-hook.js +31 -10
  23. package/dist/bin/astrasync-guard.js +35 -14
  24. package/dist/bin/astrasync.js +75 -29
  25. package/dist/browser/background.js +31 -10
  26. package/dist/browser/background.js.map +1 -1
  27. package/dist/browser/background.mjs +31 -10
  28. package/dist/browser/background.mjs.map +1 -1
  29. package/dist/cli/index.js +23 -2
  30. package/dist/cli/index.js.map +1 -1
  31. package/dist/cli/index.mjs +23 -2
  32. package/dist/cli/index.mjs.map +1 -1
  33. package/dist/codex/index.js +31 -10
  34. package/dist/codex/index.js.map +1 -1
  35. package/dist/codex/index.mjs +31 -10
  36. package/dist/codex/index.mjs.map +1 -1
  37. package/dist/cursor/extension.js +31 -10
  38. package/dist/cursor/extension.js.map +1 -1
  39. package/dist/cursor/extension.mjs +31 -10
  40. package/dist/cursor/extension.mjs.map +1 -1
  41. package/dist/edge-config.d.mts +11 -4
  42. package/dist/edge-config.d.ts +11 -4
  43. package/dist/edge-config.js +24 -1
  44. package/dist/edge-config.js.map +1 -1
  45. package/dist/edge-config.mjs +22 -1
  46. package/dist/edge-config.mjs.map +1 -1
  47. package/dist/edge-core/index.d.mts +21 -7
  48. package/dist/edge-core/index.d.ts +21 -7
  49. package/dist/edge-core/index.js +170 -25
  50. package/dist/edge-core/index.js.map +1 -1
  51. package/dist/edge-core/index.mjs +170 -25
  52. package/dist/edge-core/index.mjs.map +1 -1
  53. package/dist/gateway/gateway.js +31 -10
  54. package/dist/gateway/gateway.js.map +1 -1
  55. package/dist/gateway/gateway.mjs +31 -10
  56. package/dist/gateway/gateway.mjs.map +1 -1
  57. package/dist/index.d.mts +51 -2
  58. package/dist/index.d.ts +51 -2
  59. package/dist/index.js +169 -40
  60. package/dist/index.js.map +1 -1
  61. package/dist/index.mjs +165 -40
  62. package/dist/index.mjs.map +1 -1
  63. package/dist/metadata-capture.d.mts +17 -0
  64. package/dist/metadata-capture.d.ts +17 -0
  65. package/dist/metadata-capture.js +25 -4
  66. package/dist/metadata-capture.js.map +1 -1
  67. package/dist/metadata-capture.mjs +25 -4
  68. package/dist/metadata-capture.mjs.map +1 -1
  69. package/dist/platform-signatures.d.mts +18 -1
  70. package/dist/platform-signatures.d.ts +18 -1
  71. package/dist/platform-signatures.js +35 -2
  72. package/dist/platform-signatures.js.map +1 -1
  73. package/dist/platform-signatures.mjs +35 -2
  74. package/dist/platform-signatures.mjs.map +1 -1
  75. package/dist/registration/index.js +25 -4
  76. package/dist/registration/index.js.map +1 -1
  77. package/dist/registration/index.mjs +25 -4
  78. package/dist/registration/index.mjs.map +1 -1
  79. package/dist/transport/index.js +22 -1
  80. package/dist/transport/index.js.map +1 -1
  81. package/dist/transport/index.mjs +22 -1
  82. package/dist/transport/index.mjs.map +1 -1
  83. package/dist/verify.js +38 -17
  84. package/dist/verify.js.map +1 -1
  85. package/dist/verify.mjs +38 -17
  86. package/dist/verify.mjs.map +1 -1
  87. package/package.json +2 -2
package/dist/index.mjs CHANGED
@@ -126,7 +126,25 @@ function getCapabilities(accessLevel) {
126
126
  }
127
127
 
128
128
  // src/version.ts
129
- var SDK_VERSION = "4.1.0";
129
+ var SDK_VERSION = "4.5.0";
130
+
131
+ // src/http.ts
132
+ var SDK_USER_AGENT = `astrasync-sdk/${SDK_VERSION}`;
133
+ function withUserAgent(initHeaders) {
134
+ if (!initHeaders) return { "user-agent": SDK_USER_AGENT };
135
+ if (initHeaders instanceof Headers) {
136
+ const copy = new Headers(initHeaders);
137
+ if (!copy.has("user-agent")) copy.set("user-agent", SDK_USER_AGENT);
138
+ return copy;
139
+ }
140
+ if (Array.isArray(initHeaders)) {
141
+ const has2 = initHeaders.some(([name]) => name.toLowerCase() === "user-agent");
142
+ return has2 ? initHeaders : [...initHeaders, ["user-agent", SDK_USER_AGENT]];
143
+ }
144
+ const has = Object.keys(initHeaders).some((name) => name.toLowerCase() === "user-agent");
145
+ return has ? initHeaders : { "user-agent": SDK_USER_AGENT, ...initHeaders };
146
+ }
147
+ var sdkFetch = (input, init) => fetch(input, { ...init, headers: withUserAgent(init?.headers) });
130
148
 
131
149
  // src/well-known.ts
132
150
  var CACHE_TTL_MS = 60 * 60 * 1e3;
@@ -138,7 +156,7 @@ function wellKnownUrl(apiBaseUrl) {
138
156
  }
139
157
  async function fetchWellKnown(apiBaseUrl) {
140
158
  const url = wellKnownUrl(apiBaseUrl);
141
- const response = await fetch(url, {
159
+ const response = await sdkFetch(url, {
142
160
  method: "GET",
143
161
  headers: { Accept: "application/json" },
144
162
  signal: AbortSignal.timeout(5e3)
@@ -208,7 +226,7 @@ async function performInitCheck(apiBaseUrl, debug, strictInit, apiKey) {
208
226
  initCheckPerformed = true;
209
227
  try {
210
228
  const probeUrl = `${apiBaseUrl}/agents/verify-access`;
211
- const response = await fetch(probeUrl, { method: "HEAD" });
229
+ const response = await sdkFetch(probeUrl, { method: "HEAD" });
212
230
  const contentType = response.headers.get("content-type") ?? "";
213
231
  if (contentType.startsWith("text/html")) {
214
232
  const message = `[VerificationGateway] apiBaseUrl '${apiBaseUrl}' returned HTML (content-type: ${contentType}). This usually means apiBaseUrl is pointing at a marketing site instead of the API. Expected: 'https://astrasync.ai/api' (prod) or 'https://staging.astrasync.ai/api' (staging).`;
@@ -222,7 +240,7 @@ async function performInitCheck(apiBaseUrl, debug, strictInit, apiKey) {
222
240
  );
223
241
  }
224
242
  if (apiKey) {
225
- const selfCheck = await fetch(`${apiBaseUrl}/agents/verify-access/self-check`, {
243
+ const selfCheck = await sdkFetch(`${apiBaseUrl}/agents/verify-access/self-check`, {
226
244
  headers: { Authorization: `Bearer ${apiKey}` }
227
245
  });
228
246
  if (selfCheck.status === 401) {
@@ -475,7 +493,7 @@ async function callVerifyAccessAPI(config, request) {
475
493
  headers["X-API-Key"] = config.apiKey;
476
494
  }
477
495
  try {
478
- const response = await fetch(`${config.apiBaseUrl}/agents/verify-access`, {
496
+ const response = await sdkFetch(`${config.apiBaseUrl}/agents/verify-access`, {
479
497
  method: "POST",
480
498
  headers,
481
499
  body: JSON.stringify(body),
@@ -657,12 +675,15 @@ async function verify(config, request, options) {
657
675
  const verificationContext = apiResponse.verificationContext;
658
676
  const accessLevel = apiResponse.access?.accessLevel ?? "standard";
659
677
  const result = {
660
- // Round-18 G4: backend allowed access. Identity is verified (we resolved
661
- // the caller to an agent) and policy passed all gates. Read idVerified
662
- // from verificationContext for symmetry with the deny branch; default true
663
- // on success path since `access.allowed === true` implies identity was
664
- // resolvable (anonymous-allow paths flow through createGuidanceResponse).
665
- identityVerified: apiResponse.verificationContext?.idVerified !== false,
678
+ // 4.5.0 (probe F1): read idVerified from verificationContext with the
679
+ // SAME absent⇒false default as the deny branch. The previous `!== false`
680
+ // default manufactured `identityVerified: true` for anonymous
681
+ // allow_full/allow_partial/audit responses (which allow access but omit
682
+ // verificationContext entirely) — the "anonymous caller labelled
683
+ // verified" defect. Every authenticated success response has carried
684
+ // `verificationContext.idVerified: true` since v2.2.4, so a truth claim
685
+ // this load-bearing must never be defaulted on.
686
+ identityVerified: apiResponse.verificationContext?.idVerified === true,
666
687
  policyAllowed: true,
667
688
  accessLevel,
668
689
  agent,
@@ -718,7 +739,7 @@ async function recordDecision(config, sessionId, decision, reason) {
718
739
  headers["Authorization"] = `Bearer ${config.apiKey}`;
719
740
  headers["X-API-Key"] = config.apiKey;
720
741
  }
721
- await fetch(`${config.apiBaseUrl}/agents/verify-access/${sessionId}/decision`, {
742
+ await sdkFetch(`${config.apiBaseUrl}/agents/verify-access/${sessionId}/decision`, {
722
743
  method: "POST",
723
744
  headers,
724
745
  body: JSON.stringify({ decision, reason })
@@ -736,7 +757,7 @@ async function reportAttempt(config, report) {
736
757
  ...report,
737
758
  ...!report.counterpartyId && config.counterpartyId && { counterpartyId: config.counterpartyId }
738
759
  };
739
- await fetch(`${apiBaseUrl}/agents/verify-access/attempt-report`, {
760
+ await sdkFetch(`${apiBaseUrl}/agents/verify-access/attempt-report`, {
740
761
  method: "POST",
741
762
  headers,
742
763
  body: JSON.stringify(body)
@@ -751,7 +772,7 @@ async function fetchRoutes(config, counterpartyId) {
751
772
  headers["X-API-Key"] = config.apiKey;
752
773
  }
753
774
  try {
754
- const response = await fetch(
775
+ const response = await sdkFetch(
755
776
  `${config.apiBaseUrl}/endpoints/${encodeURIComponent(counterpartyId)}/routes`,
756
777
  { method: "GET", headers }
757
778
  );
@@ -770,7 +791,7 @@ async function reportUnregisteredAttempt(config, data) {
770
791
  ...counterpartyUrl && { counterpartyUrl },
771
792
  ...config.counterpartyId && { counterpartyId: config.counterpartyId }
772
793
  };
773
- await fetch(`${apiBaseUrl}/verification-activity/unregistered-attempt`, {
794
+ await sdkFetch(`${apiBaseUrl}/verification-activity/unregistered-attempt`, {
774
795
  method: "POST",
775
796
  headers: { "Content-Type": "application/json" },
776
797
  body: JSON.stringify(body)
@@ -779,7 +800,7 @@ async function reportUnregisteredAttempt(config, data) {
779
800
  }
780
801
  async function reportCounterpartyPreCheckFailure(config, data) {
781
802
  const apiBaseUrl = config.apiBaseUrl || DEFAULT_CONFIG.apiBaseUrl;
782
- await fetch(`${apiBaseUrl}/verification-activity/counterparty-pre-check-failure`, {
803
+ await sdkFetch(`${apiBaseUrl}/verification-activity/counterparty-pre-check-failure`, {
783
804
  method: "POST",
784
805
  headers: { "Content-Type": "application/json" },
785
806
  body: JSON.stringify(data)
@@ -813,7 +834,7 @@ function resolvePollUrl(config, pollUrl) {
813
834
  async function pollStepUpStatus(config, approval) {
814
835
  if (!approval.pollUrl) return null;
815
836
  try {
816
- const res = await fetch(resolvePollUrl(config, approval.pollUrl), {
837
+ const res = await sdkFetch(resolvePollUrl(config, approval.pollUrl), {
817
838
  method: "GET",
818
839
  signal: AbortSignal.timeout(POLL_REQUEST_TIMEOUT_MS)
819
840
  });
@@ -1188,7 +1209,13 @@ var PLATFORM_HEADER_NAMES = /* @__PURE__ */ new Set([
1188
1209
  "x-agent-card-url",
1189
1210
  "x-agent-provider"
1190
1211
  ]);
1191
- var PLATFORM_HEADER_PREFIXES = ["x-goog-", "x-stainless-", "x-anthropic-", "x-openai-"];
1212
+ var PLATFORM_HEADER_PREFIXES = [
1213
+ "x-goog-",
1214
+ "x-stainless-",
1215
+ "x-anthropic-",
1216
+ "x-openai-",
1217
+ "sec-ch-"
1218
+ ];
1192
1219
  var KEY_FORMAT_PREFIXES = [
1193
1220
  "sk-ant-api03",
1194
1221
  "sk-ant-api",
@@ -1260,6 +1287,7 @@ function sanitizeHeaders(raw) {
1260
1287
  const headers = {};
1261
1288
  const platformHeaders = {};
1262
1289
  let apiKeyFormat;
1290
+ let cookieCount;
1263
1291
  let totalBytes = 0;
1264
1292
  let count = 0;
1265
1293
  if (!raw) return { headers };
@@ -1274,6 +1302,9 @@ function sanitizeHeaders(raw) {
1274
1302
  if (fmt && !apiKeyFormat) apiKeyFormat = fmt;
1275
1303
  continue;
1276
1304
  }
1305
+ if (name === "cookie") {
1306
+ cookieCount = joined.split(";").filter((s) => s.trim().length > 0).length;
1307
+ }
1277
1308
  if (isSecretHeader(name)) continue;
1278
1309
  let value = joined;
1279
1310
  if (byteLength(value) > MAX_HEADER_VALUE_BYTES) {
@@ -1289,6 +1320,7 @@ function sanitizeHeaders(raw) {
1289
1320
  const result = { headers };
1290
1321
  if (apiKeyFormat) result.apiKeyFormat = apiKeyFormat;
1291
1322
  if (Object.keys(platformHeaders).length > 0) result.platformHeaders = platformHeaders;
1323
+ if (cookieCount !== void 0) result.cookieCount = cookieCount;
1292
1324
  return result;
1293
1325
  }
1294
1326
  function extractPlatformHeaders(sanitized) {
@@ -1308,6 +1340,9 @@ var CONNECTION_HEADER_SOURCES = [
1308
1340
  ["region", ["x-vercel-ip-country-region", "cloudfront-viewer-country-region"]],
1309
1341
  ["countryRegionName", ["cloudfront-viewer-country-region-name"]],
1310
1342
  ["city", ["x-vercel-ip-city", "cloudfront-viewer-city"]],
1343
+ ["latitude", ["x-vercel-ip-latitude", "cloudfront-viewer-latitude"]],
1344
+ ["longitude", ["x-vercel-ip-longitude", "cloudfront-viewer-longitude"]],
1345
+ ["postalCode", ["x-vercel-ip-postal-code", "cloudfront-viewer-postal-code"]],
1311
1346
  ["asn", ["cloudfront-viewer-asn"]],
1312
1347
  ["tlsVersion", ["cloudfront-viewer-tls"]],
1313
1348
  ["httpVersion", ["cloudfront-viewer-http-version"]],
@@ -1342,19 +1377,25 @@ function deriveConnectionFromHeaders(headers) {
1342
1377
  }
1343
1378
  }
1344
1379
  }
1345
- if (!connection.ip) {
1380
+ {
1346
1381
  const addr = lower["cloudfront-viewer-address"]?.trim();
1347
- if (addr) connection.ip = addr.replace(/:\d+$/, "");
1382
+ const portMatch = addr?.match(/:(\d+)$/);
1383
+ if (portMatch) connection.sourcePort = portMatch[1];
1384
+ if (!connection.ip && addr) connection.ip = addr.replace(/:\d+$/, "");
1348
1385
  }
1349
1386
  if (!connection.ip) {
1350
1387
  const xff = lower["x-forwarded-for"];
1351
1388
  const client = xff?.split(",")[0]?.trim();
1352
1389
  if (client) connection.ip = client;
1353
1390
  }
1391
+ if (!connection.tlsCipher) {
1392
+ const cipher = connection.tlsVersion?.split(":")[1];
1393
+ if (cipher) connection.tlsCipher = cipher;
1394
+ }
1354
1395
  return Object.keys(connection).length > 0 ? connection : void 0;
1355
1396
  }
1356
1397
  function buildSdkObservedMetadata(rawHeaders) {
1357
- const { headers, apiKeyFormat, platformHeaders } = sanitizeHeaders(rawHeaders);
1398
+ const { headers, apiKeyFormat, platformHeaders, cookieCount } = sanitizeHeaders(rawHeaders);
1358
1399
  const flat = {};
1359
1400
  if (rawHeaders) {
1360
1401
  for (const name of Object.keys(rawHeaders)) {
@@ -1369,6 +1410,7 @@ function buildSdkObservedMetadata(rawHeaders) {
1369
1410
  ...apiKeyFormat && { apiKeyFormat },
1370
1411
  ...platformHeaders && { platformHeaders },
1371
1412
  ...connection && { connection },
1413
+ ...cookieCount !== void 0 && { cookieCount },
1372
1414
  schemaVersion: CAPTURE_SCHEMA_VERSION
1373
1415
  };
1374
1416
  }
@@ -1413,7 +1455,7 @@ async function fetchEdgeConfig(config, counterpartyId, opts) {
1413
1455
  }
1414
1456
  if (opts?.etag) headers["If-None-Match"] = opts.etag;
1415
1457
  try {
1416
- const response = await fetch(
1458
+ const response = await sdkFetch(
1417
1459
  `${config.apiBaseUrl}/endpoints/${encodeURIComponent(counterpartyId)}/edge-config`,
1418
1460
  { method: "GET", headers, signal: AbortSignal.timeout(opts?.timeoutMs ?? 5e3) }
1419
1461
  );
@@ -1539,12 +1581,43 @@ var PLATFORM_AGENT_SIGNATURES = [
1539
1581
  uaPatterns: [/perplexity/i],
1540
1582
  agentCardPatterns: [/perplexity\.ai/i],
1541
1583
  apiKeyFormatPatterns: [/^pplx-/i]
1584
+ },
1585
+ // Agentic browsers — versioned-token patterns ONLY (a stock Chrome UA must
1586
+ // never match). Distinct vendor keys (not 'chatgpt'/'perplexity'): the
1587
+ // browser product is a different surface than the assistant service, and
1588
+ // the DB registry mirrors one row per vendor. These sit after the primary
1589
+ // vendor entries so a UA carrying an explicit vendor token keeps its
1590
+ // primary attribution; a browser-shaped UA with the agentic token is
1591
+ // caught here BEFORE classify's human tier, which is what makes these
1592
+ // visits observable at all. Token drift is real (some builds mimic Chrome
1593
+ // outright) — re-verify against captured events when a new browser ships,
1594
+ // and check `sec-ch-ua` platformHeaders for the real brand.
1595
+ {
1596
+ vendor: "chatgpt-atlas",
1597
+ displayName: "ChatGPT Atlas browser",
1598
+ uaPatterns: [/\batlas\/\d/i],
1599
+ headerPatterns: [/^sec-ch-ua: .*atlas/i]
1600
+ },
1601
+ {
1602
+ vendor: "perplexity-comet",
1603
+ displayName: "Perplexity Comet browser",
1604
+ uaPatterns: [/\bcomet\/\d/i],
1605
+ headerPatterns: [/^sec-ch-ua: .*comet/i]
1606
+ },
1607
+ // Our own SDK self-identifies (sdkFetch stamps this UA) — first-party
1608
+ // traffic must never pollute the unknown/anonymous pool it exists to audit.
1609
+ {
1610
+ vendor: "astrasync-sdk",
1611
+ displayName: "AstraSync SDK",
1612
+ uaPatterns: [/^astrasync-sdk\//i]
1542
1613
  }
1543
1614
  ];
1544
1615
  function matchPlatformSignature(signatures, input) {
1545
1616
  const ua = input.userAgent ?? "";
1546
1617
  const cardUrl = input.agentCardUrl ?? "";
1547
1618
  const apiKeyFormat = input.apiKeyFormat ?? "";
1619
+ const ja4 = input.ja4 ?? "";
1620
+ const asn = input.asn ?? "";
1548
1621
  const headerLines = [];
1549
1622
  if (input.platformHeaders) {
1550
1623
  for (const [name, value] of Object.entries(input.platformHeaders)) {
@@ -1556,7 +1629,9 @@ function matchPlatformSignature(signatures, input) {
1556
1629
  const cardMatch = sig.agentCardPatterns?.some((p) => p.test(cardUrl));
1557
1630
  const keyMatch = apiKeyFormat.length > 0 && sig.apiKeyFormatPatterns?.some((p) => p.test(apiKeyFormat));
1558
1631
  const headerMatch = headerLines.length > 0 && sig.headerPatterns?.some((p) => headerLines.some((line) => p.test(line)));
1559
- if (uaMatch || cardMatch || keyMatch || headerMatch) {
1632
+ const ja4Match = ja4.length > 0 && sig.ja4Patterns?.some((p) => p.test(ja4));
1633
+ const asnMatch = asn.length > 0 && sig.asnList?.includes(asn);
1634
+ if (uaMatch || cardMatch || keyMatch || headerMatch || ja4Match || asnMatch) {
1560
1635
  const uaBase = ua.split(/[\s/]/)[0]?.toLowerCase() ?? "";
1561
1636
  const cardHost = (() => {
1562
1637
  try {
@@ -1565,7 +1640,7 @@ function matchPlatformSignature(signatures, input) {
1565
1640
  return "";
1566
1641
  }
1567
1642
  })();
1568
- const keyBasis = uaBase || cardHost || apiKeyFormat || "unknown";
1643
+ const keyBasis = uaBase || cardHost || apiKeyFormat || ja4 || asn && `asn${asn}` || "unknown";
1569
1644
  return {
1570
1645
  vendor: sig.vendor,
1571
1646
  fingerprintKey: `${sig.vendor}:${keyBasis}`,
@@ -1581,9 +1656,10 @@ function detectPlatformFingerprint(input) {
1581
1656
 
1582
1657
  // src/edge-core/classify.ts
1583
1658
  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;
1659
+ var SELF_UA_PATTERN = /^astrasync-sdk\//i;
1584
1660
  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;
1585
1661
  var BROWSER_UA_PATTERN = /mozilla\/\d/i;
1586
- var _patterns = { STATIC_EXTENSIONS, BOT_UA_PATTERN, BROWSER_UA_PATTERN };
1662
+ var _patterns = { STATIC_EXTENSIONS, BOT_UA_PATTERN, BROWSER_UA_PATTERN, SELF_UA_PATTERN };
1587
1663
 
1588
1664
  // src/edge-core/events.ts
1589
1665
  function sampled(rate, random = Math.random) {
@@ -2003,7 +2079,7 @@ function createMiddleware(options) {
2003
2079
  let result = await verify(config, verificationRequest);
2004
2080
  req.agentVerification = result;
2005
2081
  let sessionId = result.sessionId;
2006
- if (config.stepUpMaxWaitMs !== void 0 && result.identityVerified && requiresHumanApproval(result) && result.stepUpApproval?.pollUrl) {
2082
+ if (config.stepUpMaxWaitMs !== void 0 && result.policyAllowed && requiresHumanApproval(result) && result.stepUpApproval?.pollUrl) {
2007
2083
  const outcome = await awaitStepUpApproval(config, result.stepUpApproval);
2008
2084
  res.setHeader("X-Astra-Step-Up", outcome);
2009
2085
  if (outcome === "approved") {
@@ -2014,7 +2090,7 @@ function createMiddleware(options) {
2014
2090
  result.stepUpOutcome = outcome;
2015
2091
  }
2016
2092
  }
2017
- if (!result.identityVerified || !result.policyAllowed) {
2093
+ if (!result.policyAllowed) {
2018
2094
  if (shouldRecordDecisions && sessionId) {
2019
2095
  recordDecision(config, sessionId, "denied", result.denialReasons?.[0]).catch(() => {
2020
2096
  });
@@ -2515,7 +2591,7 @@ function createMiddleware2(options) {
2515
2591
  };
2516
2592
  let result = await verify(config, verificationRequest);
2517
2593
  let stepUpHoldOutcome;
2518
- if (config.stepUpMaxWaitMs !== void 0 && result.identityVerified && requiresHumanApproval(result) && result.stepUpApproval?.pollUrl) {
2594
+ if (config.stepUpMaxWaitMs !== void 0 && result.policyAllowed && requiresHumanApproval(result) && result.stepUpApproval?.pollUrl) {
2519
2595
  stepUpHoldOutcome = await awaitStepUpApproval(config, result.stepUpApproval);
2520
2596
  if (stepUpHoldOutcome === "approved") {
2521
2597
  result = await verify(config, verificationRequest, { bypassCache: true });
@@ -2523,9 +2599,9 @@ function createMiddleware2(options) {
2523
2599
  result.stepUpOutcome = stepUpHoldOutcome;
2524
2600
  }
2525
2601
  }
2526
- const approvalRequired = result.identityVerified && result.policyAllowed && requiresHumanApproval(result);
2602
+ const approvalRequired = result.policyAllowed && requiresHumanApproval(result);
2527
2603
  if (approvalRequired) annotateApprovalRequired(result);
2528
- if (!result.identityVerified || !result.policyAllowed || approvalRequired) {
2604
+ if (!result.policyAllowed || approvalRequired) {
2529
2605
  if (result.misconfigured && pathname.startsWith("/api/")) {
2530
2606
  return NextResponse.json(
2531
2607
  {
@@ -3396,7 +3472,7 @@ async function fetchUCPManifest(manifestUrl, options = {}) {
3396
3472
  const controller = new AbortController();
3397
3473
  const timer = setTimeout(() => controller.abort(), timeoutMs);
3398
3474
  try {
3399
- const res = await fetch(manifestUrl, { signal: controller.signal });
3475
+ const res = await sdkFetch(manifestUrl, { signal: controller.signal });
3400
3476
  if (!res.ok) return null;
3401
3477
  return await res.json();
3402
3478
  } catch {
@@ -5661,7 +5737,7 @@ function createMcpMiddleware(options) {
5661
5737
  let result = await verify(config, verificationRequest);
5662
5738
  req.agentVerification = result;
5663
5739
  let sessionId = result.sessionId;
5664
- if (config.stepUpMaxWaitMs !== void 0 && result.identityVerified && requiresHumanApproval(result) && result.stepUpApproval?.pollUrl) {
5740
+ if (config.stepUpMaxWaitMs !== void 0 && result.policyAllowed && requiresHumanApproval(result) && result.stepUpApproval?.pollUrl) {
5665
5741
  const outcome = await awaitStepUpApproval(config, result.stepUpApproval);
5666
5742
  res.setHeader("X-Astra-Step-Up", outcome);
5667
5743
  if (outcome === "approved") {
@@ -5672,7 +5748,7 @@ function createMcpMiddleware(options) {
5672
5748
  result.stepUpOutcome = outcome;
5673
5749
  }
5674
5750
  }
5675
- if (!result.identityVerified || !result.policyAllowed) {
5751
+ if (!result.policyAllowed) {
5676
5752
  if (shouldRecordDecisions && sessionId) {
5677
5753
  recordDecision(config, sessionId, "denied", result.denialReasons?.[0]).catch(() => {
5678
5754
  });
@@ -5944,7 +6020,7 @@ var AstraSync = class {
5944
6020
  */
5945
6021
  async pollRegistration(requestId) {
5946
6022
  const url = `${this.baseUrl}/api/agents/request-registration/${requestId}`;
5947
- const res = await fetch(url, { headers: { Accept: "application/json" } });
6023
+ const res = await sdkFetch(url, { headers: { Accept: "application/json" } });
5948
6024
  if (!res.ok) {
5949
6025
  const errBody = await res.json().catch(() => ({}));
5950
6026
  throw new AstraSyncError(
@@ -6001,7 +6077,7 @@ var AstraSync = class {
6001
6077
  * Check API health.
6002
6078
  */
6003
6079
  async health() {
6004
- const res = await fetch(`${this.baseUrl}/api/health/`);
6080
+ const res = await sdkFetch(`${this.baseUrl}/api/health/`);
6005
6081
  if (!res.ok) {
6006
6082
  throw new AstraSyncError(`Health check failed: ${res.status}`, res.status);
6007
6083
  }
@@ -6028,7 +6104,7 @@ var AstraSync = class {
6028
6104
  const signature = await this.signRequest(method, endpoint, body || {});
6029
6105
  headers["X-AstraSync-Signature"] = signature;
6030
6106
  }
6031
- const res = await fetch(url, {
6107
+ const res = await sdkFetch(url, {
6032
6108
  method,
6033
6109
  headers,
6034
6110
  ...body ? { body: JSON.stringify(body) } : {}
@@ -6053,7 +6129,7 @@ var AstraSync = class {
6053
6129
  if (this.cachedJwt && this.jwtExpiresAt && Date.now() < this.jwtExpiresAt) {
6054
6130
  return this.cachedJwt;
6055
6131
  }
6056
- const res = await fetch(`${this.baseUrl}/api/auth/login`, {
6132
+ const res = await sdkFetch(`${this.baseUrl}/api/auth/login`, {
6057
6133
  method: "POST",
6058
6134
  headers: { "Content-Type": "application/json" },
6059
6135
  body: JSON.stringify({ email: this.email, password: this.password })
@@ -6196,7 +6272,7 @@ var AgentClient = class _AgentClient {
6196
6272
  const url = `${this.apiBaseUrl.replace(/\/+$/, "")}/api/agents/${encodeURIComponent(
6197
6273
  this.credentials.agentId
6198
6274
  )}/ownership`;
6199
- const resp = await fetch(url, {
6275
+ const resp = await sdkFetch(url, {
6200
6276
  method: "GET",
6201
6277
  headers: {
6202
6278
  Authorization: `Bearer ${this.apiKey}`,
@@ -6234,7 +6310,7 @@ var AgentClient = class _AgentClient {
6234
6310
  }
6235
6311
  }
6236
6312
  const enrichedHeaders = setHttpHeaders(existingHeaders, creds);
6237
- return fetch(url, {
6313
+ return sdkFetch(url, {
6238
6314
  ...fetchOptions,
6239
6315
  headers: enrichedHeaders
6240
6316
  });
@@ -6423,7 +6499,7 @@ async function recordDecision2(config, params) {
6423
6499
  if (config.customHeaders) {
6424
6500
  Object.assign(headers, config.customHeaders);
6425
6501
  }
6426
- const response = await fetch(url, {
6502
+ const response = await sdkFetch(url, {
6427
6503
  method: "POST",
6428
6504
  headers,
6429
6505
  body: JSON.stringify(body)
@@ -6440,6 +6516,51 @@ async function recordDecision2(config, params) {
6440
6516
  blockchainTxHash: result.blockchainTxHash
6441
6517
  };
6442
6518
  }
6519
+
6520
+ // src/runtime-signatures.ts
6521
+ var RUNTIME_SIGNATURES = [
6522
+ {
6523
+ runtime: "browser-automation",
6524
+ displayName: "Browser automation",
6525
+ uaPatterns: [/headless/i, /phantomjs/i, /selenium/i, /playwright/i, /puppeteer/i]
6526
+ },
6527
+ {
6528
+ runtime: "nodejs",
6529
+ displayName: "Node.js runtime",
6530
+ // `node` exactly is the global-fetch default UA; the token forms cover
6531
+ // the common Node HTTP libraries.
6532
+ uaPatterns: [/^node$/i, /^undici/i, /node-fetch/i, /^axios/i, /^got\b/i, /needle/i]
6533
+ },
6534
+ {
6535
+ runtime: "python",
6536
+ displayName: "Python runtime",
6537
+ uaPatterns: [/python-requests/i, /python-urllib/i, /^python\b/i, /httpx/i, /aiohttp/i]
6538
+ },
6539
+ { runtime: "go", displayName: "Go runtime", uaPatterns: [/go-http-client/i] },
6540
+ {
6541
+ runtime: "java",
6542
+ displayName: "Java runtime",
6543
+ uaPatterns: [/^java\//i, /okhttp/i, /apache-httpclient/i]
6544
+ },
6545
+ { runtime: "curl", displayName: "curl", uaPatterns: [/^curl\//i] },
6546
+ { runtime: "wget", displayName: "wget", uaPatterns: [/^wget\//i] },
6547
+ { runtime: "ruby", displayName: "Ruby runtime", uaPatterns: [/^ruby\b/i, /faraday/i] },
6548
+ {
6549
+ runtime: "php",
6550
+ displayName: "PHP runtime",
6551
+ uaPatterns: [/^php\b/i, /guzzlehttp/i, /^symfony/i]
6552
+ },
6553
+ { runtime: "dotnet", displayName: ".NET runtime", uaPatterns: [/^dotnet/i, /httpclient/i] },
6554
+ { runtime: "dart", displayName: "Dart runtime", uaPatterns: [/^dart/i] }
6555
+ ];
6556
+ function detectRuntime(userAgent) {
6557
+ if (!userAgent) return void 0;
6558
+ const ua = userAgent.trim();
6559
+ for (const sig of RUNTIME_SIGNATURES) {
6560
+ if (sig.uaPatterns.some((p) => p.test(ua))) return sig.displayName;
6561
+ }
6562
+ return void 0;
6563
+ }
6443
6564
  export {
6444
6565
  ACCESS_LEVEL_DESCRIPTIONS,
6445
6566
  ACCESS_LEVEL_HIERARCHY,
@@ -6456,9 +6577,11 @@ export {
6456
6577
  MAX_HEADER_VALUE_BYTES,
6457
6578
  MAX_TOTAL_BYTES,
6458
6579
  PLATFORM_AGENT_SIGNATURES,
6580
+ RUNTIME_SIGNATURES,
6459
6581
  RegistrationDeniedError,
6460
6582
  RegistrationExpiredError,
6461
6583
  RegistrationTimeoutError,
6584
+ SDK_USER_AGENT,
6462
6585
  TRUST_LEVEL_RANGES,
6463
6586
  SDK_VERSION as VERSION,
6464
6587
  _resetEdgeConfigCache,
@@ -6472,6 +6595,7 @@ export {
6472
6595
  degradeToObserve,
6473
6596
  deriveConnectionFromHeaders,
6474
6597
  detectPlatformFingerprint,
6598
+ detectRuntime,
6475
6599
  determineAccessLevel,
6476
6600
  express_exports as express,
6477
6601
  extractApiKeyFormat,
@@ -6500,6 +6624,7 @@ export {
6500
6624
  reportUnregisteredAttempt,
6501
6625
  sanitizeHeaders,
6502
6626
  sdk_exports as sdk,
6627
+ sdkFetch,
6503
6628
  setMcpMeta,
6504
6629
  transport_exports as transport,
6505
6630
  verify