@astrasyncai/verification-gateway 4.2.1 → 4.4.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 (138) hide show
  1. package/README.md +45 -21
  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 +145 -21
  7. package/dist/adapters/express.js.map +1 -1
  8. package/dist/adapters/express.mjs +145 -21
  9. package/dist/adapters/express.mjs.map +1 -1
  10. package/dist/adapters/mcp.d.mts +38 -30
  11. package/dist/adapters/mcp.d.ts +38 -30
  12. package/dist/adapters/mcp.js +72 -16
  13. package/dist/adapters/mcp.js.map +1 -1
  14. package/dist/adapters/mcp.mjs +72 -16
  15. package/dist/adapters/mcp.mjs.map +1 -1
  16. package/dist/adapters/nextjs.d.mts +2 -2
  17. package/dist/adapters/nextjs.d.ts +2 -2
  18. package/dist/adapters/nextjs.js +141 -19
  19. package/dist/adapters/nextjs.js.map +1 -1
  20. package/dist/adapters/nextjs.mjs +141 -19
  21. package/dist/adapters/nextjs.mjs.map +1 -1
  22. package/dist/adapters/sdk.d.mts +2 -2
  23. package/dist/adapters/sdk.d.ts +2 -2
  24. package/dist/adapters/sdk.js +24 -6
  25. package/dist/adapters/sdk.js.map +1 -1
  26. package/dist/adapters/sdk.mjs +24 -6
  27. package/dist/adapters/sdk.mjs.map +1 -1
  28. package/dist/agent/index.d.mts +2 -2
  29. package/dist/agent/index.d.ts +2 -2
  30. package/dist/agent/index.js +24 -3
  31. package/dist/agent/index.js.map +1 -1
  32. package/dist/agent/index.mjs +24 -3
  33. package/dist/agent/index.mjs.map +1 -1
  34. package/dist/bin/astrasync-claude-hook.js +22 -4
  35. package/dist/bin/astrasync-codex-hook.js +22 -4
  36. package/dist/bin/astrasync-guard.js +26 -8
  37. package/dist/bin/astrasync.js +76 -23
  38. package/dist/browser/background.js +22 -4
  39. package/dist/browser/background.js.map +1 -1
  40. package/dist/browser/background.mjs +22 -4
  41. package/dist/browser/background.mjs.map +1 -1
  42. package/dist/browser/browser-adapter.d.mts +2 -2
  43. package/dist/browser/browser-adapter.d.ts +2 -2
  44. package/dist/claude-code/claude-code-adapter.d.mts +2 -2
  45. package/dist/claude-code/claude-code-adapter.d.ts +2 -2
  46. package/dist/cli/index.d.mts +2 -2
  47. package/dist/cli/index.d.ts +2 -2
  48. package/dist/cli/index.js +23 -2
  49. package/dist/cli/index.js.map +1 -1
  50. package/dist/cli/index.mjs +23 -2
  51. package/dist/cli/index.mjs.map +1 -1
  52. package/dist/codex/index.d.mts +2 -2
  53. package/dist/codex/index.d.ts +2 -2
  54. package/dist/codex/index.js +22 -4
  55. package/dist/codex/index.js.map +1 -1
  56. package/dist/codex/index.mjs +22 -4
  57. package/dist/codex/index.mjs.map +1 -1
  58. package/dist/cursor/cursor-adapter.d.mts +2 -2
  59. package/dist/cursor/cursor-adapter.d.ts +2 -2
  60. package/dist/cursor/extension.d.mts +2 -2
  61. package/dist/cursor/extension.d.ts +2 -2
  62. package/dist/cursor/extension.js +22 -4
  63. package/dist/cursor/extension.js.map +1 -1
  64. package/dist/cursor/extension.mjs +22 -4
  65. package/dist/cursor/extension.mjs.map +1 -1
  66. package/dist/edge-config.d.mts +1 -1
  67. package/dist/edge-config.d.ts +1 -1
  68. package/dist/edge-config.js +24 -1
  69. package/dist/edge-config.js.map +1 -1
  70. package/dist/edge-config.mjs +22 -1
  71. package/dist/edge-config.mjs.map +1 -1
  72. package/dist/edge-core/index.d.mts +1 -1
  73. package/dist/edge-core/index.d.ts +1 -1
  74. package/dist/edge-core/index.js +121 -16
  75. package/dist/edge-core/index.js.map +1 -1
  76. package/dist/edge-core/index.mjs +121 -16
  77. package/dist/edge-core/index.mjs.map +1 -1
  78. package/dist/{express-D_4hTn5Z.d.mts → express-CR6F3njD.d.mts} +1 -1
  79. package/dist/{express-BVd1_3FE.d.ts → express-DaqL11tx.d.ts} +1 -1
  80. package/dist/gateway/gateway.d.mts +2 -2
  81. package/dist/gateway/gateway.d.ts +2 -2
  82. package/dist/gateway/gateway.js +22 -4
  83. package/dist/gateway/gateway.js.map +1 -1
  84. package/dist/gateway/gateway.mjs +22 -4
  85. package/dist/gateway/gateway.mjs.map +1 -1
  86. package/dist/git-trigger/git-hooks.d.mts +2 -2
  87. package/dist/git-trigger/git-hooks.d.ts +2 -2
  88. package/dist/{index-BqHKlLOU.d.ts → index-B5Ts1WtM.d.ts} +1 -1
  89. package/dist/{index-T1aBoUcc.d.ts → index-BfIxCYIZ.d.ts} +1 -1
  90. package/dist/{index-BTeQ9fgV.d.mts → index-Cub7lK-T.d.mts} +1 -1
  91. package/dist/{index-DQb5-_1X.d.mts → index-CwAh7Tln.d.mts} +1 -1
  92. package/dist/index.d.mts +58 -9
  93. package/dist/index.d.ts +58 -9
  94. package/dist/index.js +218 -54
  95. package/dist/index.js.map +1 -1
  96. package/dist/index.mjs +214 -54
  97. package/dist/index.mjs.map +1 -1
  98. package/dist/local-evaluator/evaluator.d.mts +2 -2
  99. package/dist/local-evaluator/evaluator.d.ts +2 -2
  100. package/dist/metadata-capture.d.mts +35 -5
  101. package/dist/metadata-capture.d.ts +35 -5
  102. package/dist/metadata-capture.js +44 -6
  103. package/dist/metadata-capture.js.map +1 -1
  104. package/dist/metadata-capture.mjs +43 -6
  105. package/dist/metadata-capture.mjs.map +1 -1
  106. package/dist/{nextjs-w7RTbP1P.d.ts → nextjs-DL6uE1ye.d.ts} +1 -1
  107. package/dist/{nextjs-WyeVr2Kp.d.mts → nextjs-DlX6xwbU.d.mts} +1 -1
  108. package/dist/platform-signatures.d.mts +18 -1
  109. package/dist/platform-signatures.d.ts +18 -1
  110. package/dist/platform-signatures.js +35 -2
  111. package/dist/platform-signatures.js.map +1 -1
  112. package/dist/platform-signatures.mjs +35 -2
  113. package/dist/platform-signatures.mjs.map +1 -1
  114. package/dist/registration/index.js +25 -4
  115. package/dist/registration/index.js.map +1 -1
  116. package/dist/registration/index.mjs +25 -4
  117. package/dist/registration/index.mjs.map +1 -1
  118. package/dist/{sdk-CbTNIkAa.d.mts → sdk-Bbh9B_dw.d.mts} +1 -1
  119. package/dist/{sdk-QeX0Z8Ho.d.ts → sdk-C6SASmsh.d.ts} +1 -1
  120. package/dist/transport/index.d.mts +2 -2
  121. package/dist/transport/index.d.ts +2 -2
  122. package/dist/transport/index.js +22 -1
  123. package/dist/transport/index.js.map +1 -1
  124. package/dist/transport/index.mjs +22 -1
  125. package/dist/transport/index.mjs.map +1 -1
  126. package/dist/{types-r850cOt0.d.mts → types-C0zfSXMB.d.mts} +22 -0
  127. package/dist/{types-DqfPU5Bl.d.mts → types-C7Rb-w0d.d.mts} +1 -1
  128. package/dist/{types-sMxBT-nM.d.ts → types-DwSfO7Sb.d.ts} +22 -0
  129. package/dist/{types-BLUx92FJ.d.ts → types-nIh3HtuA.d.ts} +1 -1
  130. package/dist/ui/index.d.mts +1 -1
  131. package/dist/ui/index.d.ts +1 -1
  132. package/dist/verify.d.mts +1 -1
  133. package/dist/verify.d.ts +1 -1
  134. package/dist/verify.js +29 -11
  135. package/dist/verify.js.map +1 -1
  136. package/dist/verify.mjs +29 -11
  137. package/dist/verify.mjs.map +1 -1
  138. package/package.json +1 -1
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.4.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),
@@ -718,7 +736,7 @@ async function recordDecision(config, sessionId, decision, reason) {
718
736
  headers["Authorization"] = `Bearer ${config.apiKey}`;
719
737
  headers["X-API-Key"] = config.apiKey;
720
738
  }
721
- await fetch(`${config.apiBaseUrl}/agents/verify-access/${sessionId}/decision`, {
739
+ await sdkFetch(`${config.apiBaseUrl}/agents/verify-access/${sessionId}/decision`, {
722
740
  method: "POST",
723
741
  headers,
724
742
  body: JSON.stringify({ decision, reason })
@@ -736,7 +754,7 @@ async function reportAttempt(config, report) {
736
754
  ...report,
737
755
  ...!report.counterpartyId && config.counterpartyId && { counterpartyId: config.counterpartyId }
738
756
  };
739
- await fetch(`${apiBaseUrl}/agents/verify-access/attempt-report`, {
757
+ await sdkFetch(`${apiBaseUrl}/agents/verify-access/attempt-report`, {
740
758
  method: "POST",
741
759
  headers,
742
760
  body: JSON.stringify(body)
@@ -751,7 +769,7 @@ async function fetchRoutes(config, counterpartyId) {
751
769
  headers["X-API-Key"] = config.apiKey;
752
770
  }
753
771
  try {
754
- const response = await fetch(
772
+ const response = await sdkFetch(
755
773
  `${config.apiBaseUrl}/endpoints/${encodeURIComponent(counterpartyId)}/routes`,
756
774
  { method: "GET", headers }
757
775
  );
@@ -770,7 +788,7 @@ async function reportUnregisteredAttempt(config, data) {
770
788
  ...counterpartyUrl && { counterpartyUrl },
771
789
  ...config.counterpartyId && { counterpartyId: config.counterpartyId }
772
790
  };
773
- await fetch(`${apiBaseUrl}/verification-activity/unregistered-attempt`, {
791
+ await sdkFetch(`${apiBaseUrl}/verification-activity/unregistered-attempt`, {
774
792
  method: "POST",
775
793
  headers: { "Content-Type": "application/json" },
776
794
  body: JSON.stringify(body)
@@ -779,7 +797,7 @@ async function reportUnregisteredAttempt(config, data) {
779
797
  }
780
798
  async function reportCounterpartyPreCheckFailure(config, data) {
781
799
  const apiBaseUrl = config.apiBaseUrl || DEFAULT_CONFIG.apiBaseUrl;
782
- await fetch(`${apiBaseUrl}/verification-activity/counterparty-pre-check-failure`, {
800
+ await sdkFetch(`${apiBaseUrl}/verification-activity/counterparty-pre-check-failure`, {
783
801
  method: "POST",
784
802
  headers: { "Content-Type": "application/json" },
785
803
  body: JSON.stringify(data)
@@ -813,7 +831,7 @@ function resolvePollUrl(config, pollUrl) {
813
831
  async function pollStepUpStatus(config, approval) {
814
832
  if (!approval.pollUrl) return null;
815
833
  try {
816
- const res = await fetch(resolvePollUrl(config, approval.pollUrl), {
834
+ const res = await sdkFetch(resolvePollUrl(config, approval.pollUrl), {
817
835
  method: "GET",
818
836
  signal: AbortSignal.timeout(POLL_REQUEST_TIMEOUT_MS)
819
837
  });
@@ -996,6 +1014,34 @@ function extractHttpCredentials(headers) {
996
1014
  return credentials;
997
1015
  }
998
1016
 
1017
+ // src/verified-hop.ts
1018
+ var MCP_VERIFIED_HOP_HEADER = "X-Astra-Verified-Hop";
1019
+ var MCP_VERIFIED_HOP_MAX_AGE_MS = 6e4;
1020
+ function serializeVerifiedHop(marker) {
1021
+ return `${marker.astraId};${marker.sessionId ?? ""};${marker.checkedAt}`;
1022
+ }
1023
+ function parseVerifiedHop(value) {
1024
+ if (!value) return null;
1025
+ const parts = value.split(";");
1026
+ if (parts.length !== 3) return null;
1027
+ const [astraId, sessionId, checkedAtRaw] = parts;
1028
+ if (!astraId) return null;
1029
+ const checkedAt = Number(checkedAtRaw);
1030
+ if (!Number.isFinite(checkedAt) || checkedAt <= 0) return null;
1031
+ return {
1032
+ astraId,
1033
+ ...sessionId ? { sessionId } : {},
1034
+ checkedAt
1035
+ };
1036
+ }
1037
+ function isVerifiedHopValidFor(marker, expectedAstraId, opts = {}) {
1038
+ if (!marker) return false;
1039
+ if (marker.astraId !== expectedAstraId) return false;
1040
+ const maxAge = opts.maxAgeMs ?? MCP_VERIFIED_HOP_MAX_AGE_MS;
1041
+ const now = opts.now ?? Date.now();
1042
+ return now - marker.checkedAt <= maxAge && now >= marker.checkedAt;
1043
+ }
1044
+
999
1045
  // src/adapters/http-pdlss.ts
1000
1046
  var HTTP_METHOD_ACTION_TABLE = {
1001
1047
  GET: "data.read",
@@ -1133,7 +1179,9 @@ var SECRET_HEADER_NAMES = /* @__PURE__ */ new Set([
1133
1179
  "x-astrasync-signature",
1134
1180
  "x-astrasync-secret",
1135
1181
  "x-hub-signature",
1136
- "x-hub-signature-256"
1182
+ "x-hub-signature-256",
1183
+ // Short-lived verify dedupe token — must never persist in stored metadata.
1184
+ "x-astra-verified-hop"
1137
1185
  ]);
1138
1186
  var SECRET_HEADER_PREFIXES = ["x-astrasync-", "kya-", "kya_"];
1139
1187
  var CREDENTIAL_HEADER_NAMES = /* @__PURE__ */ new Set([
@@ -1158,7 +1206,13 @@ var PLATFORM_HEADER_NAMES = /* @__PURE__ */ new Set([
1158
1206
  "x-agent-card-url",
1159
1207
  "x-agent-provider"
1160
1208
  ]);
1161
- var PLATFORM_HEADER_PREFIXES = ["x-goog-", "x-stainless-", "x-anthropic-", "x-openai-"];
1209
+ var PLATFORM_HEADER_PREFIXES = [
1210
+ "x-goog-",
1211
+ "x-stainless-",
1212
+ "x-anthropic-",
1213
+ "x-openai-",
1214
+ "sec-ch-"
1215
+ ];
1162
1216
  var KEY_FORMAT_PREFIXES = [
1163
1217
  "sk-ant-api03",
1164
1218
  "sk-ant-api",
@@ -1230,6 +1284,7 @@ function sanitizeHeaders(raw) {
1230
1284
  const headers = {};
1231
1285
  const platformHeaders = {};
1232
1286
  let apiKeyFormat;
1287
+ let cookieCount;
1233
1288
  let totalBytes = 0;
1234
1289
  let count = 0;
1235
1290
  if (!raw) return { headers };
@@ -1244,6 +1299,9 @@ function sanitizeHeaders(raw) {
1244
1299
  if (fmt && !apiKeyFormat) apiKeyFormat = fmt;
1245
1300
  continue;
1246
1301
  }
1302
+ if (name === "cookie") {
1303
+ cookieCount = joined.split(";").filter((s) => s.trim().length > 0).length;
1304
+ }
1247
1305
  if (isSecretHeader(name)) continue;
1248
1306
  let value = joined;
1249
1307
  if (byteLength(value) > MAX_HEADER_VALUE_BYTES) {
@@ -1259,6 +1317,7 @@ function sanitizeHeaders(raw) {
1259
1317
  const result = { headers };
1260
1318
  if (apiKeyFormat) result.apiKeyFormat = apiKeyFormat;
1261
1319
  if (Object.keys(platformHeaders).length > 0) result.platformHeaders = platformHeaders;
1320
+ if (cookieCount !== void 0) result.cookieCount = cookieCount;
1262
1321
  return result;
1263
1322
  }
1264
1323
  function extractPlatformHeaders(sanitized) {
@@ -1276,16 +1335,28 @@ var CONNECTION_HEADER_SOURCES = [
1276
1335
  ["cf-ipcountry", "x-vercel-ip-country", "cloudfront-viewer-country", "fastly-geo-country"]
1277
1336
  ],
1278
1337
  ["region", ["x-vercel-ip-country-region", "cloudfront-viewer-country-region"]],
1338
+ ["countryRegionName", ["cloudfront-viewer-country-region-name"]],
1279
1339
  ["city", ["x-vercel-ip-city", "cloudfront-viewer-city"]],
1340
+ ["latitude", ["x-vercel-ip-latitude", "cloudfront-viewer-latitude"]],
1341
+ ["longitude", ["x-vercel-ip-longitude", "cloudfront-viewer-longitude"]],
1342
+ ["postalCode", ["x-vercel-ip-postal-code", "cloudfront-viewer-postal-code"]],
1280
1343
  ["asn", ["cloudfront-viewer-asn"]],
1281
1344
  ["tlsVersion", ["cloudfront-viewer-tls"]],
1282
1345
  ["httpVersion", ["cloudfront-viewer-http-version"]],
1346
+ ["timeZone", ["x-vercel-ip-timezone", "cloudfront-viewer-time-zone"]],
1347
+ ["metroCode", ["cloudfront-viewer-metro-code"]],
1283
1348
  // TLS fingerprints can only be computed where TLS terminates. When a CDN
1284
1349
  // terminates TLS in front of the merchant, its fingerprint headers ARE the
1285
1350
  // connection-layer truth — normalising them here is the permanent design.
1286
- ["tlsFingerprint", ["cf-ja3-hash"]],
1287
- ["ja4", ["cf-ja4"]]
1351
+ // CloudFront delivers JA3/JA4 only via an origin request policy and only
1352
+ // for HTTPS viewer connections.
1353
+ ["tlsFingerprint", ["cf-ja3-hash", "cloudfront-viewer-ja3-fingerprint"]],
1354
+ ["ja4", ["cf-ja4", "cloudfront-viewer-ja4-fingerprint"]],
1355
+ // Header-structure fingerprint: browser/SDK-distinctive header ordering.
1356
+ ["headerOrder", ["cloudfront-viewer-header-order"]],
1357
+ ["headerCount", ["cloudfront-viewer-header-count"]]
1288
1358
  ];
1359
+ var MAX_CONNECTION_VALUE_CHARS = 256;
1289
1360
  function deriveConnectionFromHeaders(headers) {
1290
1361
  if (!headers) return void 0;
1291
1362
  const lower = {};
@@ -1298,20 +1369,30 @@ function deriveConnectionFromHeaders(headers) {
1298
1369
  for (const source of sources) {
1299
1370
  const value = lower[source]?.trim();
1300
1371
  if (value) {
1301
- connection[key] = value;
1372
+ connection[key] = value.slice(0, MAX_CONNECTION_VALUE_CHARS);
1302
1373
  break;
1303
1374
  }
1304
1375
  }
1305
1376
  }
1377
+ {
1378
+ const addr = lower["cloudfront-viewer-address"]?.trim();
1379
+ const portMatch = addr?.match(/:(\d+)$/);
1380
+ if (portMatch) connection.sourcePort = portMatch[1];
1381
+ if (!connection.ip && addr) connection.ip = addr.replace(/:\d+$/, "");
1382
+ }
1306
1383
  if (!connection.ip) {
1307
1384
  const xff = lower["x-forwarded-for"];
1308
1385
  const client = xff?.split(",")[0]?.trim();
1309
1386
  if (client) connection.ip = client;
1310
1387
  }
1388
+ if (!connection.tlsCipher) {
1389
+ const cipher = connection.tlsVersion?.split(":")[1];
1390
+ if (cipher) connection.tlsCipher = cipher;
1391
+ }
1311
1392
  return Object.keys(connection).length > 0 ? connection : void 0;
1312
1393
  }
1313
1394
  function buildSdkObservedMetadata(rawHeaders) {
1314
- const { headers, apiKeyFormat, platformHeaders } = sanitizeHeaders(rawHeaders);
1395
+ const { headers, apiKeyFormat, platformHeaders, cookieCount } = sanitizeHeaders(rawHeaders);
1315
1396
  const flat = {};
1316
1397
  if (rawHeaders) {
1317
1398
  for (const name of Object.keys(rawHeaders)) {
@@ -1326,6 +1407,7 @@ function buildSdkObservedMetadata(rawHeaders) {
1326
1407
  ...apiKeyFormat && { apiKeyFormat },
1327
1408
  ...platformHeaders && { platformHeaders },
1328
1409
  ...connection && { connection },
1410
+ ...cookieCount !== void 0 && { cookieCount },
1329
1411
  schemaVersion: CAPTURE_SCHEMA_VERSION
1330
1412
  };
1331
1413
  }
@@ -1370,7 +1452,7 @@ async function fetchEdgeConfig(config, counterpartyId, opts) {
1370
1452
  }
1371
1453
  if (opts?.etag) headers["If-None-Match"] = opts.etag;
1372
1454
  try {
1373
- const response = await fetch(
1455
+ const response = await sdkFetch(
1374
1456
  `${config.apiBaseUrl}/endpoints/${encodeURIComponent(counterpartyId)}/edge-config`,
1375
1457
  { method: "GET", headers, signal: AbortSignal.timeout(opts?.timeoutMs ?? 5e3) }
1376
1458
  );
@@ -1496,12 +1578,43 @@ var PLATFORM_AGENT_SIGNATURES = [
1496
1578
  uaPatterns: [/perplexity/i],
1497
1579
  agentCardPatterns: [/perplexity\.ai/i],
1498
1580
  apiKeyFormatPatterns: [/^pplx-/i]
1581
+ },
1582
+ // Agentic browsers — versioned-token patterns ONLY (a stock Chrome UA must
1583
+ // never match). Distinct vendor keys (not 'chatgpt'/'perplexity'): the
1584
+ // browser product is a different surface than the assistant service, and
1585
+ // the DB registry mirrors one row per vendor. These sit after the primary
1586
+ // vendor entries so a UA carrying an explicit vendor token keeps its
1587
+ // primary attribution; a browser-shaped UA with the agentic token is
1588
+ // caught here BEFORE classify's human tier, which is what makes these
1589
+ // visits observable at all. Token drift is real (some builds mimic Chrome
1590
+ // outright) — re-verify against captured events when a new browser ships,
1591
+ // and check `sec-ch-ua` platformHeaders for the real brand.
1592
+ {
1593
+ vendor: "chatgpt-atlas",
1594
+ displayName: "ChatGPT Atlas browser",
1595
+ uaPatterns: [/\batlas\/\d/i],
1596
+ headerPatterns: [/^sec-ch-ua: .*atlas/i]
1597
+ },
1598
+ {
1599
+ vendor: "perplexity-comet",
1600
+ displayName: "Perplexity Comet browser",
1601
+ uaPatterns: [/\bcomet\/\d/i],
1602
+ headerPatterns: [/^sec-ch-ua: .*comet/i]
1603
+ },
1604
+ // Our own SDK self-identifies (sdkFetch stamps this UA) — first-party
1605
+ // traffic must never pollute the unknown/anonymous pool it exists to audit.
1606
+ {
1607
+ vendor: "astrasync-sdk",
1608
+ displayName: "AstraSync SDK",
1609
+ uaPatterns: [/^astrasync-sdk\//i]
1499
1610
  }
1500
1611
  ];
1501
1612
  function matchPlatformSignature(signatures, input) {
1502
1613
  const ua = input.userAgent ?? "";
1503
1614
  const cardUrl = input.agentCardUrl ?? "";
1504
1615
  const apiKeyFormat = input.apiKeyFormat ?? "";
1616
+ const ja4 = input.ja4 ?? "";
1617
+ const asn = input.asn ?? "";
1505
1618
  const headerLines = [];
1506
1619
  if (input.platformHeaders) {
1507
1620
  for (const [name, value] of Object.entries(input.platformHeaders)) {
@@ -1513,7 +1626,9 @@ function matchPlatformSignature(signatures, input) {
1513
1626
  const cardMatch = sig.agentCardPatterns?.some((p) => p.test(cardUrl));
1514
1627
  const keyMatch = apiKeyFormat.length > 0 && sig.apiKeyFormatPatterns?.some((p) => p.test(apiKeyFormat));
1515
1628
  const headerMatch = headerLines.length > 0 && sig.headerPatterns?.some((p) => headerLines.some((line) => p.test(line)));
1516
- if (uaMatch || cardMatch || keyMatch || headerMatch) {
1629
+ const ja4Match = ja4.length > 0 && sig.ja4Patterns?.some((p) => p.test(ja4));
1630
+ const asnMatch = asn.length > 0 && sig.asnList?.includes(asn);
1631
+ if (uaMatch || cardMatch || keyMatch || headerMatch || ja4Match || asnMatch) {
1517
1632
  const uaBase = ua.split(/[\s/]/)[0]?.toLowerCase() ?? "";
1518
1633
  const cardHost = (() => {
1519
1634
  try {
@@ -1522,7 +1637,7 @@ function matchPlatformSignature(signatures, input) {
1522
1637
  return "";
1523
1638
  }
1524
1639
  })();
1525
- const keyBasis = uaBase || cardHost || apiKeyFormat || "unknown";
1640
+ const keyBasis = uaBase || cardHost || apiKeyFormat || ja4 || asn && `asn${asn}` || "unknown";
1526
1641
  return {
1527
1642
  vendor: sig.vendor,
1528
1643
  fingerprintKey: `${sig.vendor}:${keyBasis}`,
@@ -1757,6 +1872,8 @@ function createMiddleware(options) {
1757
1872
  failOnError = "open",
1758
1873
  caseInsensitiveRouteMatch = false,
1759
1874
  anonymousBeacon = true,
1875
+ trustVerifiedHop = false,
1876
+ verifiedHopMaxAgeMs,
1760
1877
  ...config
1761
1878
  } = options;
1762
1879
  if (config.apiBaseUrl) {
@@ -1849,6 +1966,16 @@ function createMiddleware(options) {
1849
1966
  beaconPassThrough?.(req);
1850
1967
  return next();
1851
1968
  }
1969
+ if (trustVerifiedHop && credentials.astraId) {
1970
+ const hopRaw = req.headers[MCP_VERIFIED_HOP_HEADER.toLowerCase()];
1971
+ const hopValue = typeof hopRaw === "string" ? hopRaw : Array.isArray(hopRaw) ? hopRaw[0] : void 0;
1972
+ const marker = parseVerifiedHop(hopValue);
1973
+ if (isVerifiedHopValidFor(marker, credentials.astraId, {
1974
+ ...verifiedHopMaxAgeMs !== void 0 ? { maxAgeMs: verifiedHopMaxAgeMs } : {}
1975
+ })) {
1976
+ return next();
1977
+ }
1978
+ }
1852
1979
  const pdlssPair = resolveRequestPdlss(
1853
1980
  req,
1854
1981
  routeConfig,
@@ -2304,6 +2431,8 @@ function createMiddleware2(options) {
2304
2431
  enableRuntimeChallenge = true,
2305
2432
  routesRefreshMs = DEFAULT_ROUTES_REFRESH_MS2,
2306
2433
  anonymousBeacon = true,
2434
+ trustVerifiedHop = false,
2435
+ verifiedHopMaxAgeMs,
2307
2436
  ...config
2308
2437
  } = options;
2309
2438
  const interstitialEnabled = showInterstitial ?? showCommerceShield ?? true;
@@ -2366,6 +2495,14 @@ function createMiddleware2(options) {
2366
2495
  return NextResponse.next();
2367
2496
  }
2368
2497
  const credentials = extractCredentialsFromNextRequest(request);
2498
+ if (trustVerifiedHop && credentials.astraId) {
2499
+ const marker = parseVerifiedHop(request.headers.get(MCP_VERIFIED_HOP_HEADER) ?? void 0);
2500
+ if (isVerifiedHopValidFor(marker, credentials.astraId, {
2501
+ ...verifiedHopMaxAgeMs !== void 0 ? { maxAgeMs: verifiedHopMaxAgeMs } : {}
2502
+ })) {
2503
+ return NextResponse.next();
2504
+ }
2505
+ }
2369
2506
  const counterpartyUrl = config.counterpartyUrl || request.nextUrl.origin;
2370
2507
  const pdlssPair = resolveNextPdlss(request, routeConfig);
2371
2508
  const purpose = pdlssPair.purpose;
@@ -3331,7 +3468,7 @@ async function fetchUCPManifest(manifestUrl, options = {}) {
3331
3468
  const controller = new AbortController();
3332
3469
  const timer = setTimeout(() => controller.abort(), timeoutMs);
3333
3470
  try {
3334
- const res = await fetch(manifestUrl, { signal: controller.signal });
3471
+ const res = await sdkFetch(manifestUrl, { signal: controller.signal });
3335
3472
  if (!res.ok) return null;
3336
3473
  return await res.json();
3337
3474
  } catch {
@@ -5257,32 +5394,6 @@ function extractCredentialsFromProtocol(protocol, context) {
5257
5394
  }
5258
5395
 
5259
5396
  // src/transport/mcp-server.ts
5260
- var MCP_VERIFIED_HOP_HEADER = "X-Astra-Verified-Hop";
5261
- var MCP_VERIFIED_HOP_MAX_AGE_MS = 6e4;
5262
- function serializeVerifiedHop(marker) {
5263
- return `${marker.astraId};${marker.sessionId ?? ""};${marker.checkedAt}`;
5264
- }
5265
- function parseVerifiedHop(value) {
5266
- if (!value) return null;
5267
- const parts = value.split(";");
5268
- if (parts.length !== 3) return null;
5269
- const [astraId, sessionId, checkedAtRaw] = parts;
5270
- if (!astraId) return null;
5271
- const checkedAt = Number(checkedAtRaw);
5272
- if (!Number.isFinite(checkedAt) || checkedAt <= 0) return null;
5273
- return {
5274
- astraId,
5275
- ...sessionId ? { sessionId } : {},
5276
- checkedAt
5277
- };
5278
- }
5279
- function isVerifiedHopValidFor(marker, expectedAstraId, opts = {}) {
5280
- if (!marker) return false;
5281
- if (marker.astraId !== expectedAstraId) return false;
5282
- const maxAge = opts.maxAgeMs ?? MCP_VERIFIED_HOP_MAX_AGE_MS;
5283
- const now = opts.now ?? Date.now();
5284
- return now - marker.checkedAt <= maxAge && now >= marker.checkedAt;
5285
- }
5286
5397
  function parseMcpJsonRpc(body) {
5287
5398
  if (!body || typeof body !== "object" || Array.isArray(body)) return null;
5288
5399
  const obj = body;
@@ -5905,7 +6016,7 @@ var AstraSync = class {
5905
6016
  */
5906
6017
  async pollRegistration(requestId) {
5907
6018
  const url = `${this.baseUrl}/api/agents/request-registration/${requestId}`;
5908
- const res = await fetch(url, { headers: { Accept: "application/json" } });
6019
+ const res = await sdkFetch(url, { headers: { Accept: "application/json" } });
5909
6020
  if (!res.ok) {
5910
6021
  const errBody = await res.json().catch(() => ({}));
5911
6022
  throw new AstraSyncError(
@@ -5962,7 +6073,7 @@ var AstraSync = class {
5962
6073
  * Check API health.
5963
6074
  */
5964
6075
  async health() {
5965
- const res = await fetch(`${this.baseUrl}/api/health/`);
6076
+ const res = await sdkFetch(`${this.baseUrl}/api/health/`);
5966
6077
  if (!res.ok) {
5967
6078
  throw new AstraSyncError(`Health check failed: ${res.status}`, res.status);
5968
6079
  }
@@ -5989,7 +6100,7 @@ var AstraSync = class {
5989
6100
  const signature = await this.signRequest(method, endpoint, body || {});
5990
6101
  headers["X-AstraSync-Signature"] = signature;
5991
6102
  }
5992
- const res = await fetch(url, {
6103
+ const res = await sdkFetch(url, {
5993
6104
  method,
5994
6105
  headers,
5995
6106
  ...body ? { body: JSON.stringify(body) } : {}
@@ -6014,7 +6125,7 @@ var AstraSync = class {
6014
6125
  if (this.cachedJwt && this.jwtExpiresAt && Date.now() < this.jwtExpiresAt) {
6015
6126
  return this.cachedJwt;
6016
6127
  }
6017
- const res = await fetch(`${this.baseUrl}/api/auth/login`, {
6128
+ const res = await sdkFetch(`${this.baseUrl}/api/auth/login`, {
6018
6129
  method: "POST",
6019
6130
  headers: { "Content-Type": "application/json" },
6020
6131
  body: JSON.stringify({ email: this.email, password: this.password })
@@ -6157,7 +6268,7 @@ var AgentClient = class _AgentClient {
6157
6268
  const url = `${this.apiBaseUrl.replace(/\/+$/, "")}/api/agents/${encodeURIComponent(
6158
6269
  this.credentials.agentId
6159
6270
  )}/ownership`;
6160
- const resp = await fetch(url, {
6271
+ const resp = await sdkFetch(url, {
6161
6272
  method: "GET",
6162
6273
  headers: {
6163
6274
  Authorization: `Bearer ${this.apiKey}`,
@@ -6195,7 +6306,7 @@ var AgentClient = class _AgentClient {
6195
6306
  }
6196
6307
  }
6197
6308
  const enrichedHeaders = setHttpHeaders(existingHeaders, creds);
6198
- return fetch(url, {
6309
+ return sdkFetch(url, {
6199
6310
  ...fetchOptions,
6200
6311
  headers: enrichedHeaders
6201
6312
  });
@@ -6384,7 +6495,7 @@ async function recordDecision2(config, params) {
6384
6495
  if (config.customHeaders) {
6385
6496
  Object.assign(headers, config.customHeaders);
6386
6497
  }
6387
- const response = await fetch(url, {
6498
+ const response = await sdkFetch(url, {
6388
6499
  method: "POST",
6389
6500
  headers,
6390
6501
  body: JSON.stringify(body)
@@ -6401,6 +6512,51 @@ async function recordDecision2(config, params) {
6401
6512
  blockchainTxHash: result.blockchainTxHash
6402
6513
  };
6403
6514
  }
6515
+
6516
+ // src/runtime-signatures.ts
6517
+ var RUNTIME_SIGNATURES = [
6518
+ {
6519
+ runtime: "browser-automation",
6520
+ displayName: "Browser automation",
6521
+ uaPatterns: [/headless/i, /phantomjs/i, /selenium/i, /playwright/i, /puppeteer/i]
6522
+ },
6523
+ {
6524
+ runtime: "nodejs",
6525
+ displayName: "Node.js runtime",
6526
+ // `node` exactly is the global-fetch default UA; the token forms cover
6527
+ // the common Node HTTP libraries.
6528
+ uaPatterns: [/^node$/i, /^undici/i, /node-fetch/i, /^axios/i, /^got\b/i, /needle/i]
6529
+ },
6530
+ {
6531
+ runtime: "python",
6532
+ displayName: "Python runtime",
6533
+ uaPatterns: [/python-requests/i, /python-urllib/i, /^python\b/i, /httpx/i, /aiohttp/i]
6534
+ },
6535
+ { runtime: "go", displayName: "Go runtime", uaPatterns: [/go-http-client/i] },
6536
+ {
6537
+ runtime: "java",
6538
+ displayName: "Java runtime",
6539
+ uaPatterns: [/^java\//i, /okhttp/i, /apache-httpclient/i]
6540
+ },
6541
+ { runtime: "curl", displayName: "curl", uaPatterns: [/^curl\//i] },
6542
+ { runtime: "wget", displayName: "wget", uaPatterns: [/^wget\//i] },
6543
+ { runtime: "ruby", displayName: "Ruby runtime", uaPatterns: [/^ruby\b/i, /faraday/i] },
6544
+ {
6545
+ runtime: "php",
6546
+ displayName: "PHP runtime",
6547
+ uaPatterns: [/^php\b/i, /guzzlehttp/i, /^symfony/i]
6548
+ },
6549
+ { runtime: "dotnet", displayName: ".NET runtime", uaPatterns: [/^dotnet/i, /httpclient/i] },
6550
+ { runtime: "dart", displayName: "Dart runtime", uaPatterns: [/^dart/i] }
6551
+ ];
6552
+ function detectRuntime(userAgent) {
6553
+ if (!userAgent) return void 0;
6554
+ const ua = userAgent.trim();
6555
+ for (const sig of RUNTIME_SIGNATURES) {
6556
+ if (sig.uaPatterns.some((p) => p.test(ua))) return sig.displayName;
6557
+ }
6558
+ return void 0;
6559
+ }
6404
6560
  export {
6405
6561
  ACCESS_LEVEL_DESCRIPTIONS,
6406
6562
  ACCESS_LEVEL_HIERARCHY,
@@ -6417,9 +6573,11 @@ export {
6417
6573
  MAX_HEADER_VALUE_BYTES,
6418
6574
  MAX_TOTAL_BYTES,
6419
6575
  PLATFORM_AGENT_SIGNATURES,
6576
+ RUNTIME_SIGNATURES,
6420
6577
  RegistrationDeniedError,
6421
6578
  RegistrationExpiredError,
6422
6579
  RegistrationTimeoutError,
6580
+ SDK_USER_AGENT,
6423
6581
  TRUST_LEVEL_RANGES,
6424
6582
  SDK_VERSION as VERSION,
6425
6583
  _resetEdgeConfigCache,
@@ -6433,6 +6591,7 @@ export {
6433
6591
  degradeToObserve,
6434
6592
  deriveConnectionFromHeaders,
6435
6593
  detectPlatformFingerprint,
6594
+ detectRuntime,
6436
6595
  determineAccessLevel,
6437
6596
  express_exports as express,
6438
6597
  extractApiKeyFormat,
@@ -6461,6 +6620,7 @@ export {
6461
6620
  reportUnregisteredAttempt,
6462
6621
  sanitizeHeaders,
6463
6622
  sdk_exports as sdk,
6623
+ sdkFetch,
6464
6624
  setMcpMeta,
6465
6625
  transport_exports as transport,
6466
6626
  verify