@astrasyncai/verification-gateway 4.3.0 → 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 (83) hide show
  1. package/dist/adapters/express.js +91 -19
  2. package/dist/adapters/express.js.map +1 -1
  3. package/dist/adapters/express.mjs +91 -19
  4. package/dist/adapters/express.mjs.map +1 -1
  5. package/dist/adapters/mcp.js +52 -13
  6. package/dist/adapters/mcp.js.map +1 -1
  7. package/dist/adapters/mcp.mjs +52 -13
  8. package/dist/adapters/mcp.mjs.map +1 -1
  9. package/dist/adapters/nextjs.js +89 -17
  10. package/dist/adapters/nextjs.js.map +1 -1
  11. package/dist/adapters/nextjs.mjs +89 -17
  12. package/dist/adapters/nextjs.mjs.map +1 -1
  13. package/dist/adapters/sdk.js +24 -6
  14. package/dist/adapters/sdk.js.map +1 -1
  15. package/dist/adapters/sdk.mjs +24 -6
  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 +22 -4
  22. package/dist/bin/astrasync-codex-hook.js +22 -4
  23. package/dist/bin/astrasync-guard.js +26 -8
  24. package/dist/bin/astrasync.js +66 -23
  25. package/dist/browser/background.js +22 -4
  26. package/dist/browser/background.js.map +1 -1
  27. package/dist/browser/background.mjs +22 -4
  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 +22 -4
  34. package/dist/codex/index.js.map +1 -1
  35. package/dist/codex/index.mjs +22 -4
  36. package/dist/codex/index.mjs.map +1 -1
  37. package/dist/cursor/extension.js +22 -4
  38. package/dist/cursor/extension.js.map +1 -1
  39. package/dist/cursor/extension.mjs +22 -4
  40. package/dist/cursor/extension.mjs.map +1 -1
  41. package/dist/edge-config.js +24 -1
  42. package/dist/edge-config.js.map +1 -1
  43. package/dist/edge-config.mjs +22 -1
  44. package/dist/edge-config.mjs.map +1 -1
  45. package/dist/edge-core/index.js +96 -11
  46. package/dist/edge-core/index.js.map +1 -1
  47. package/dist/edge-core/index.mjs +96 -11
  48. package/dist/edge-core/index.mjs.map +1 -1
  49. package/dist/gateway/gateway.js +22 -4
  50. package/dist/gateway/gateway.js.map +1 -1
  51. package/dist/gateway/gateway.mjs +22 -4
  52. package/dist/gateway/gateway.mjs.map +1 -1
  53. package/dist/index.d.mts +51 -2
  54. package/dist/index.d.ts +51 -2
  55. package/dist/index.js +151 -26
  56. package/dist/index.js.map +1 -1
  57. package/dist/index.mjs +147 -26
  58. package/dist/index.mjs.map +1 -1
  59. package/dist/metadata-capture.d.mts +17 -0
  60. package/dist/metadata-capture.d.ts +17 -0
  61. package/dist/metadata-capture.js +25 -4
  62. package/dist/metadata-capture.js.map +1 -1
  63. package/dist/metadata-capture.mjs +25 -4
  64. package/dist/metadata-capture.mjs.map +1 -1
  65. package/dist/platform-signatures.d.mts +18 -1
  66. package/dist/platform-signatures.d.ts +18 -1
  67. package/dist/platform-signatures.js +35 -2
  68. package/dist/platform-signatures.js.map +1 -1
  69. package/dist/platform-signatures.mjs +35 -2
  70. package/dist/platform-signatures.mjs.map +1 -1
  71. package/dist/registration/index.js +25 -4
  72. package/dist/registration/index.js.map +1 -1
  73. package/dist/registration/index.mjs +25 -4
  74. package/dist/registration/index.mjs.map +1 -1
  75. package/dist/transport/index.js +22 -1
  76. package/dist/transport/index.js.map +1 -1
  77. package/dist/transport/index.mjs +22 -1
  78. package/dist/transport/index.mjs.map +1 -1
  79. package/dist/verify.js +29 -11
  80. package/dist/verify.js.map +1 -1
  81. package/dist/verify.mjs +29 -11
  82. package/dist/verify.mjs.map +1 -1
  83. package/package.json +1 -1
package/dist/index.d.mts CHANGED
@@ -127,6 +127,55 @@ declare function getWellKnownUrls(apiBaseUrl: string): Promise<WellKnownAgenticC
127
127
  */
128
128
  declare function getCachedWellKnownUrls(apiBaseUrl: string): WellKnownAgenticCommerce | undefined;
129
129
 
130
+ /**
131
+ * HTTP-client RUNTIME signatures — a display-only vocabulary, disjoint from
132
+ * the platform-agent vendor vocabulary in `platform-signatures.ts`.
133
+ *
134
+ * A UA of `node` or `python-requests` identifies the HTTP STACK, never the
135
+ * operator: OpenAI Codex, Claude Code, Perplexity's tooling, and our own SDK
136
+ * all fetch through Node — labeling any of them a vendor from the runtime
137
+ * alone would be fabrication. What we CAN say honestly is "undeclared
138
+ * automation on the Node.js runtime", which beats "unknown" in every surface.
139
+ *
140
+ * HARD RULE: this module must NEVER be consulted by `edge-core/classify.ts`,
141
+ * `detectPlatformFingerprint`, or any tiering / orphan-provisioning path.
142
+ * Runtime matches carry no identity: promoting them to the `identified` tier
143
+ * would flip anonymous-bot traffic to 1:1 beacons and auto-provision orphan
144
+ * agents literally named "node". Display and clustering layers only.
145
+ */
146
+ interface RuntimeSignature {
147
+ /** Stable runtime key — namespace disjoint from `PlatformAgentVendor`. */
148
+ runtime: string;
149
+ /** Human-readable label, e.g. "Node.js runtime". */
150
+ displayName: string;
151
+ uaPatterns: RegExp[];
152
+ }
153
+ /** First match wins — order more-specific tokens before generic ones. */
154
+ declare const RUNTIME_SIGNATURES: ReadonlyArray<RuntimeSignature>;
155
+ /**
156
+ * Detect the HTTP-client runtime from a User-Agent. Returns the displayName
157
+ * (e.g. "Node.js runtime") or undefined when the UA doesn't look like a bare
158
+ * runtime/library client.
159
+ */
160
+ declare function detectRuntime(userAgent: string | undefined | null): string | undefined;
161
+
162
+ /**
163
+ * Shared outbound-HTTP wrapper — every request the SDK makes to the
164
+ * AstraSync backend (or anywhere else) goes through `sdkFetch` so it
165
+ * carries an identifying User-Agent.
166
+ *
167
+ * Why: Node's global fetch defaults to `user-agent: node`, which is
168
+ * indistinguishable from every other Node client on the wire. Visit
169
+ * Intelligence observed our own SDK/beacon traffic as anonymous "node"
170
+ * visits. Self-identification is the platform's own medicine.
171
+ *
172
+ * Browser note: some browsers filter the User-Agent request header
173
+ * silently (never throwing), so this is a no-op there — which is correct:
174
+ * in a browser the page's real UA is the honest identity.
175
+ */
176
+ declare const SDK_USER_AGENT = "astrasync-sdk/4.4.0";
177
+ declare const sdkFetch: typeof fetch;
178
+
130
179
  /**
131
180
  * Single source-of-truth for the SDK's
132
181
  * package version emitted on verify-access bodies (and any future
@@ -145,6 +194,6 @@ declare function getCachedWellKnownUrls(apiBaseUrl: string): WellKnownAgenticCom
145
194
  * is fine — bumping both in the release-ceremony commit keeps them
146
195
  * lockstep.
147
196
  */
148
- declare const SDK_VERSION = "4.1.0";
197
+ declare const SDK_VERSION = "4.4.0";
149
198
 
150
- export { EnhancedVerificationResult, GatewayConfig, type SettlementDecision, type SettlementRequest, StepUpApprovalInfo, StepUpApprovalStatus, StepUpOutcome, SDK_VERSION as VERSION, type WellKnownAgenticCommerce, authorizeSettlement, awaitStepUpApproval, getCachedWellKnownUrls, getWellKnownUrls, pollStepUpStatus, prefetchWellKnown };
199
+ export { EnhancedVerificationResult, GatewayConfig, RUNTIME_SIGNATURES, type RuntimeSignature, SDK_USER_AGENT, type SettlementDecision, type SettlementRequest, StepUpApprovalInfo, StepUpApprovalStatus, StepUpOutcome, SDK_VERSION as VERSION, type WellKnownAgenticCommerce, authorizeSettlement, awaitStepUpApproval, detectRuntime, getCachedWellKnownUrls, getWellKnownUrls, pollStepUpStatus, prefetchWellKnown, sdkFetch };
package/dist/index.d.ts CHANGED
@@ -127,6 +127,55 @@ declare function getWellKnownUrls(apiBaseUrl: string): Promise<WellKnownAgenticC
127
127
  */
128
128
  declare function getCachedWellKnownUrls(apiBaseUrl: string): WellKnownAgenticCommerce | undefined;
129
129
 
130
+ /**
131
+ * HTTP-client RUNTIME signatures — a display-only vocabulary, disjoint from
132
+ * the platform-agent vendor vocabulary in `platform-signatures.ts`.
133
+ *
134
+ * A UA of `node` or `python-requests` identifies the HTTP STACK, never the
135
+ * operator: OpenAI Codex, Claude Code, Perplexity's tooling, and our own SDK
136
+ * all fetch through Node — labeling any of them a vendor from the runtime
137
+ * alone would be fabrication. What we CAN say honestly is "undeclared
138
+ * automation on the Node.js runtime", which beats "unknown" in every surface.
139
+ *
140
+ * HARD RULE: this module must NEVER be consulted by `edge-core/classify.ts`,
141
+ * `detectPlatformFingerprint`, or any tiering / orphan-provisioning path.
142
+ * Runtime matches carry no identity: promoting them to the `identified` tier
143
+ * would flip anonymous-bot traffic to 1:1 beacons and auto-provision orphan
144
+ * agents literally named "node". Display and clustering layers only.
145
+ */
146
+ interface RuntimeSignature {
147
+ /** Stable runtime key — namespace disjoint from `PlatformAgentVendor`. */
148
+ runtime: string;
149
+ /** Human-readable label, e.g. "Node.js runtime". */
150
+ displayName: string;
151
+ uaPatterns: RegExp[];
152
+ }
153
+ /** First match wins — order more-specific tokens before generic ones. */
154
+ declare const RUNTIME_SIGNATURES: ReadonlyArray<RuntimeSignature>;
155
+ /**
156
+ * Detect the HTTP-client runtime from a User-Agent. Returns the displayName
157
+ * (e.g. "Node.js runtime") or undefined when the UA doesn't look like a bare
158
+ * runtime/library client.
159
+ */
160
+ declare function detectRuntime(userAgent: string | undefined | null): string | undefined;
161
+
162
+ /**
163
+ * Shared outbound-HTTP wrapper — every request the SDK makes to the
164
+ * AstraSync backend (or anywhere else) goes through `sdkFetch` so it
165
+ * carries an identifying User-Agent.
166
+ *
167
+ * Why: Node's global fetch defaults to `user-agent: node`, which is
168
+ * indistinguishable from every other Node client on the wire. Visit
169
+ * Intelligence observed our own SDK/beacon traffic as anonymous "node"
170
+ * visits. Self-identification is the platform's own medicine.
171
+ *
172
+ * Browser note: some browsers filter the User-Agent request header
173
+ * silently (never throwing), so this is a no-op there — which is correct:
174
+ * in a browser the page's real UA is the honest identity.
175
+ */
176
+ declare const SDK_USER_AGENT = "astrasync-sdk/4.4.0";
177
+ declare const sdkFetch: typeof fetch;
178
+
130
179
  /**
131
180
  * Single source-of-truth for the SDK's
132
181
  * package version emitted on verify-access bodies (and any future
@@ -145,6 +194,6 @@ declare function getCachedWellKnownUrls(apiBaseUrl: string): WellKnownAgenticCom
145
194
  * is fine — bumping both in the release-ceremony commit keeps them
146
195
  * lockstep.
147
196
  */
148
- declare const SDK_VERSION = "4.1.0";
197
+ declare const SDK_VERSION = "4.4.0";
149
198
 
150
- export { EnhancedVerificationResult, GatewayConfig, type SettlementDecision, type SettlementRequest, StepUpApprovalInfo, StepUpApprovalStatus, StepUpOutcome, SDK_VERSION as VERSION, type WellKnownAgenticCommerce, authorizeSettlement, awaitStepUpApproval, getCachedWellKnownUrls, getWellKnownUrls, pollStepUpStatus, prefetchWellKnown };
199
+ export { EnhancedVerificationResult, GatewayConfig, RUNTIME_SIGNATURES, type RuntimeSignature, SDK_USER_AGENT, type SettlementDecision, type SettlementRequest, StepUpApprovalInfo, StepUpApprovalStatus, StepUpOutcome, SDK_VERSION as VERSION, type WellKnownAgenticCommerce, authorizeSettlement, awaitStepUpApproval, detectRuntime, getCachedWellKnownUrls, getWellKnownUrls, pollStepUpStatus, prefetchWellKnown, sdkFetch };
package/dist/index.js CHANGED
@@ -45,9 +45,11 @@ __export(src_exports, {
45
45
  MAX_HEADER_VALUE_BYTES: () => MAX_HEADER_VALUE_BYTES,
46
46
  MAX_TOTAL_BYTES: () => MAX_TOTAL_BYTES,
47
47
  PLATFORM_AGENT_SIGNATURES: () => PLATFORM_AGENT_SIGNATURES,
48
+ RUNTIME_SIGNATURES: () => RUNTIME_SIGNATURES,
48
49
  RegistrationDeniedError: () => RegistrationDeniedError,
49
50
  RegistrationExpiredError: () => RegistrationExpiredError,
50
51
  RegistrationTimeoutError: () => RegistrationTimeoutError,
52
+ SDK_USER_AGENT: () => SDK_USER_AGENT,
51
53
  TRUST_LEVEL_RANGES: () => TRUST_LEVEL_RANGES,
52
54
  VERSION: () => SDK_VERSION,
53
55
  _resetEdgeConfigCache: () => _resetEdgeConfigCache,
@@ -61,6 +63,7 @@ __export(src_exports, {
61
63
  degradeToObserve: () => degradeToObserve,
62
64
  deriveConnectionFromHeaders: () => deriveConnectionFromHeaders,
63
65
  detectPlatformFingerprint: () => detectPlatformFingerprint,
66
+ detectRuntime: () => detectRuntime,
64
67
  determineAccessLevel: () => determineAccessLevel,
65
68
  express: () => express_exports,
66
69
  extractApiKeyFormat: () => extractApiKeyFormat,
@@ -89,6 +92,7 @@ __export(src_exports, {
89
92
  reportUnregisteredAttempt: () => reportUnregisteredAttempt,
90
93
  sanitizeHeaders: () => sanitizeHeaders,
91
94
  sdk: () => sdk_exports,
95
+ sdkFetch: () => sdkFetch,
92
96
  setMcpMeta: () => setMcpMeta,
93
97
  transport: () => transport_exports,
94
98
  verify: () => verify
@@ -217,7 +221,25 @@ function getCapabilities(accessLevel) {
217
221
  }
218
222
 
219
223
  // src/version.ts
220
- var SDK_VERSION = "4.1.0";
224
+ var SDK_VERSION = "4.4.0";
225
+
226
+ // src/http.ts
227
+ var SDK_USER_AGENT = `astrasync-sdk/${SDK_VERSION}`;
228
+ function withUserAgent(initHeaders) {
229
+ if (!initHeaders) return { "user-agent": SDK_USER_AGENT };
230
+ if (initHeaders instanceof Headers) {
231
+ const copy = new Headers(initHeaders);
232
+ if (!copy.has("user-agent")) copy.set("user-agent", SDK_USER_AGENT);
233
+ return copy;
234
+ }
235
+ if (Array.isArray(initHeaders)) {
236
+ const has2 = initHeaders.some(([name]) => name.toLowerCase() === "user-agent");
237
+ return has2 ? initHeaders : [...initHeaders, ["user-agent", SDK_USER_AGENT]];
238
+ }
239
+ const has = Object.keys(initHeaders).some((name) => name.toLowerCase() === "user-agent");
240
+ return has ? initHeaders : { "user-agent": SDK_USER_AGENT, ...initHeaders };
241
+ }
242
+ var sdkFetch = (input, init) => fetch(input, { ...init, headers: withUserAgent(init?.headers) });
221
243
 
222
244
  // src/well-known.ts
223
245
  var CACHE_TTL_MS = 60 * 60 * 1e3;
@@ -229,7 +251,7 @@ function wellKnownUrl(apiBaseUrl) {
229
251
  }
230
252
  async function fetchWellKnown(apiBaseUrl) {
231
253
  const url = wellKnownUrl(apiBaseUrl);
232
- const response = await fetch(url, {
254
+ const response = await sdkFetch(url, {
233
255
  method: "GET",
234
256
  headers: { Accept: "application/json" },
235
257
  signal: AbortSignal.timeout(5e3)
@@ -299,7 +321,7 @@ async function performInitCheck(apiBaseUrl, debug, strictInit, apiKey) {
299
321
  initCheckPerformed = true;
300
322
  try {
301
323
  const probeUrl = `${apiBaseUrl}/agents/verify-access`;
302
- const response = await fetch(probeUrl, { method: "HEAD" });
324
+ const response = await sdkFetch(probeUrl, { method: "HEAD" });
303
325
  const contentType = response.headers.get("content-type") ?? "";
304
326
  if (contentType.startsWith("text/html")) {
305
327
  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).`;
@@ -313,7 +335,7 @@ async function performInitCheck(apiBaseUrl, debug, strictInit, apiKey) {
313
335
  );
314
336
  }
315
337
  if (apiKey) {
316
- const selfCheck = await fetch(`${apiBaseUrl}/agents/verify-access/self-check`, {
338
+ const selfCheck = await sdkFetch(`${apiBaseUrl}/agents/verify-access/self-check`, {
317
339
  headers: { Authorization: `Bearer ${apiKey}` }
318
340
  });
319
341
  if (selfCheck.status === 401) {
@@ -566,7 +588,7 @@ async function callVerifyAccessAPI(config, request) {
566
588
  headers["X-API-Key"] = config.apiKey;
567
589
  }
568
590
  try {
569
- const response = await fetch(`${config.apiBaseUrl}/agents/verify-access`, {
591
+ const response = await sdkFetch(`${config.apiBaseUrl}/agents/verify-access`, {
570
592
  method: "POST",
571
593
  headers,
572
594
  body: JSON.stringify(body),
@@ -809,7 +831,7 @@ async function recordDecision(config, sessionId, decision, reason) {
809
831
  headers["Authorization"] = `Bearer ${config.apiKey}`;
810
832
  headers["X-API-Key"] = config.apiKey;
811
833
  }
812
- await fetch(`${config.apiBaseUrl}/agents/verify-access/${sessionId}/decision`, {
834
+ await sdkFetch(`${config.apiBaseUrl}/agents/verify-access/${sessionId}/decision`, {
813
835
  method: "POST",
814
836
  headers,
815
837
  body: JSON.stringify({ decision, reason })
@@ -827,7 +849,7 @@ async function reportAttempt(config, report) {
827
849
  ...report,
828
850
  ...!report.counterpartyId && config.counterpartyId && { counterpartyId: config.counterpartyId }
829
851
  };
830
- await fetch(`${apiBaseUrl}/agents/verify-access/attempt-report`, {
852
+ await sdkFetch(`${apiBaseUrl}/agents/verify-access/attempt-report`, {
831
853
  method: "POST",
832
854
  headers,
833
855
  body: JSON.stringify(body)
@@ -842,7 +864,7 @@ async function fetchRoutes(config, counterpartyId) {
842
864
  headers["X-API-Key"] = config.apiKey;
843
865
  }
844
866
  try {
845
- const response = await fetch(
867
+ const response = await sdkFetch(
846
868
  `${config.apiBaseUrl}/endpoints/${encodeURIComponent(counterpartyId)}/routes`,
847
869
  { method: "GET", headers }
848
870
  );
@@ -861,7 +883,7 @@ async function reportUnregisteredAttempt(config, data) {
861
883
  ...counterpartyUrl && { counterpartyUrl },
862
884
  ...config.counterpartyId && { counterpartyId: config.counterpartyId }
863
885
  };
864
- await fetch(`${apiBaseUrl}/verification-activity/unregistered-attempt`, {
886
+ await sdkFetch(`${apiBaseUrl}/verification-activity/unregistered-attempt`, {
865
887
  method: "POST",
866
888
  headers: { "Content-Type": "application/json" },
867
889
  body: JSON.stringify(body)
@@ -870,7 +892,7 @@ async function reportUnregisteredAttempt(config, data) {
870
892
  }
871
893
  async function reportCounterpartyPreCheckFailure(config, data) {
872
894
  const apiBaseUrl = config.apiBaseUrl || DEFAULT_CONFIG.apiBaseUrl;
873
- await fetch(`${apiBaseUrl}/verification-activity/counterparty-pre-check-failure`, {
895
+ await sdkFetch(`${apiBaseUrl}/verification-activity/counterparty-pre-check-failure`, {
874
896
  method: "POST",
875
897
  headers: { "Content-Type": "application/json" },
876
898
  body: JSON.stringify(data)
@@ -904,7 +926,7 @@ function resolvePollUrl(config, pollUrl) {
904
926
  async function pollStepUpStatus(config, approval) {
905
927
  if (!approval.pollUrl) return null;
906
928
  try {
907
- const res = await fetch(resolvePollUrl(config, approval.pollUrl), {
929
+ const res = await sdkFetch(resolvePollUrl(config, approval.pollUrl), {
908
930
  method: "GET",
909
931
  signal: AbortSignal.timeout(POLL_REQUEST_TIMEOUT_MS)
910
932
  });
@@ -1279,7 +1301,13 @@ var PLATFORM_HEADER_NAMES = /* @__PURE__ */ new Set([
1279
1301
  "x-agent-card-url",
1280
1302
  "x-agent-provider"
1281
1303
  ]);
1282
- var PLATFORM_HEADER_PREFIXES = ["x-goog-", "x-stainless-", "x-anthropic-", "x-openai-"];
1304
+ var PLATFORM_HEADER_PREFIXES = [
1305
+ "x-goog-",
1306
+ "x-stainless-",
1307
+ "x-anthropic-",
1308
+ "x-openai-",
1309
+ "sec-ch-"
1310
+ ];
1283
1311
  var KEY_FORMAT_PREFIXES = [
1284
1312
  "sk-ant-api03",
1285
1313
  "sk-ant-api",
@@ -1351,6 +1379,7 @@ function sanitizeHeaders(raw) {
1351
1379
  const headers = {};
1352
1380
  const platformHeaders = {};
1353
1381
  let apiKeyFormat;
1382
+ let cookieCount;
1354
1383
  let totalBytes = 0;
1355
1384
  let count = 0;
1356
1385
  if (!raw) return { headers };
@@ -1365,6 +1394,9 @@ function sanitizeHeaders(raw) {
1365
1394
  if (fmt && !apiKeyFormat) apiKeyFormat = fmt;
1366
1395
  continue;
1367
1396
  }
1397
+ if (name === "cookie") {
1398
+ cookieCount = joined.split(";").filter((s) => s.trim().length > 0).length;
1399
+ }
1368
1400
  if (isSecretHeader(name)) continue;
1369
1401
  let value = joined;
1370
1402
  if (byteLength(value) > MAX_HEADER_VALUE_BYTES) {
@@ -1380,6 +1412,7 @@ function sanitizeHeaders(raw) {
1380
1412
  const result = { headers };
1381
1413
  if (apiKeyFormat) result.apiKeyFormat = apiKeyFormat;
1382
1414
  if (Object.keys(platformHeaders).length > 0) result.platformHeaders = platformHeaders;
1415
+ if (cookieCount !== void 0) result.cookieCount = cookieCount;
1383
1416
  return result;
1384
1417
  }
1385
1418
  function extractPlatformHeaders(sanitized) {
@@ -1399,6 +1432,9 @@ var CONNECTION_HEADER_SOURCES = [
1399
1432
  ["region", ["x-vercel-ip-country-region", "cloudfront-viewer-country-region"]],
1400
1433
  ["countryRegionName", ["cloudfront-viewer-country-region-name"]],
1401
1434
  ["city", ["x-vercel-ip-city", "cloudfront-viewer-city"]],
1435
+ ["latitude", ["x-vercel-ip-latitude", "cloudfront-viewer-latitude"]],
1436
+ ["longitude", ["x-vercel-ip-longitude", "cloudfront-viewer-longitude"]],
1437
+ ["postalCode", ["x-vercel-ip-postal-code", "cloudfront-viewer-postal-code"]],
1402
1438
  ["asn", ["cloudfront-viewer-asn"]],
1403
1439
  ["tlsVersion", ["cloudfront-viewer-tls"]],
1404
1440
  ["httpVersion", ["cloudfront-viewer-http-version"]],
@@ -1433,19 +1469,25 @@ function deriveConnectionFromHeaders(headers) {
1433
1469
  }
1434
1470
  }
1435
1471
  }
1436
- if (!connection.ip) {
1472
+ {
1437
1473
  const addr = lower["cloudfront-viewer-address"]?.trim();
1438
- if (addr) connection.ip = addr.replace(/:\d+$/, "");
1474
+ const portMatch = addr?.match(/:(\d+)$/);
1475
+ if (portMatch) connection.sourcePort = portMatch[1];
1476
+ if (!connection.ip && addr) connection.ip = addr.replace(/:\d+$/, "");
1439
1477
  }
1440
1478
  if (!connection.ip) {
1441
1479
  const xff = lower["x-forwarded-for"];
1442
1480
  const client = xff?.split(",")[0]?.trim();
1443
1481
  if (client) connection.ip = client;
1444
1482
  }
1483
+ if (!connection.tlsCipher) {
1484
+ const cipher = connection.tlsVersion?.split(":")[1];
1485
+ if (cipher) connection.tlsCipher = cipher;
1486
+ }
1445
1487
  return Object.keys(connection).length > 0 ? connection : void 0;
1446
1488
  }
1447
1489
  function buildSdkObservedMetadata(rawHeaders) {
1448
- const { headers, apiKeyFormat, platformHeaders } = sanitizeHeaders(rawHeaders);
1490
+ const { headers, apiKeyFormat, platformHeaders, cookieCount } = sanitizeHeaders(rawHeaders);
1449
1491
  const flat = {};
1450
1492
  if (rawHeaders) {
1451
1493
  for (const name of Object.keys(rawHeaders)) {
@@ -1460,6 +1502,7 @@ function buildSdkObservedMetadata(rawHeaders) {
1460
1502
  ...apiKeyFormat && { apiKeyFormat },
1461
1503
  ...platformHeaders && { platformHeaders },
1462
1504
  ...connection && { connection },
1505
+ ...cookieCount !== void 0 && { cookieCount },
1463
1506
  schemaVersion: CAPTURE_SCHEMA_VERSION
1464
1507
  };
1465
1508
  }
@@ -1504,7 +1547,7 @@ async function fetchEdgeConfig(config, counterpartyId, opts) {
1504
1547
  }
1505
1548
  if (opts?.etag) headers["If-None-Match"] = opts.etag;
1506
1549
  try {
1507
- const response = await fetch(
1550
+ const response = await sdkFetch(
1508
1551
  `${config.apiBaseUrl}/endpoints/${encodeURIComponent(counterpartyId)}/edge-config`,
1509
1552
  { method: "GET", headers, signal: AbortSignal.timeout(opts?.timeoutMs ?? 5e3) }
1510
1553
  );
@@ -1630,12 +1673,43 @@ var PLATFORM_AGENT_SIGNATURES = [
1630
1673
  uaPatterns: [/perplexity/i],
1631
1674
  agentCardPatterns: [/perplexity\.ai/i],
1632
1675
  apiKeyFormatPatterns: [/^pplx-/i]
1676
+ },
1677
+ // Agentic browsers — versioned-token patterns ONLY (a stock Chrome UA must
1678
+ // never match). Distinct vendor keys (not 'chatgpt'/'perplexity'): the
1679
+ // browser product is a different surface than the assistant service, and
1680
+ // the DB registry mirrors one row per vendor. These sit after the primary
1681
+ // vendor entries so a UA carrying an explicit vendor token keeps its
1682
+ // primary attribution; a browser-shaped UA with the agentic token is
1683
+ // caught here BEFORE classify's human tier, which is what makes these
1684
+ // visits observable at all. Token drift is real (some builds mimic Chrome
1685
+ // outright) — re-verify against captured events when a new browser ships,
1686
+ // and check `sec-ch-ua` platformHeaders for the real brand.
1687
+ {
1688
+ vendor: "chatgpt-atlas",
1689
+ displayName: "ChatGPT Atlas browser",
1690
+ uaPatterns: [/\batlas\/\d/i],
1691
+ headerPatterns: [/^sec-ch-ua: .*atlas/i]
1692
+ },
1693
+ {
1694
+ vendor: "perplexity-comet",
1695
+ displayName: "Perplexity Comet browser",
1696
+ uaPatterns: [/\bcomet\/\d/i],
1697
+ headerPatterns: [/^sec-ch-ua: .*comet/i]
1698
+ },
1699
+ // Our own SDK self-identifies (sdkFetch stamps this UA) — first-party
1700
+ // traffic must never pollute the unknown/anonymous pool it exists to audit.
1701
+ {
1702
+ vendor: "astrasync-sdk",
1703
+ displayName: "AstraSync SDK",
1704
+ uaPatterns: [/^astrasync-sdk\//i]
1633
1705
  }
1634
1706
  ];
1635
1707
  function matchPlatformSignature(signatures, input) {
1636
1708
  const ua = input.userAgent ?? "";
1637
1709
  const cardUrl = input.agentCardUrl ?? "";
1638
1710
  const apiKeyFormat = input.apiKeyFormat ?? "";
1711
+ const ja4 = input.ja4 ?? "";
1712
+ const asn = input.asn ?? "";
1639
1713
  const headerLines = [];
1640
1714
  if (input.platformHeaders) {
1641
1715
  for (const [name, value] of Object.entries(input.platformHeaders)) {
@@ -1647,7 +1721,9 @@ function matchPlatformSignature(signatures, input) {
1647
1721
  const cardMatch = sig.agentCardPatterns?.some((p) => p.test(cardUrl));
1648
1722
  const keyMatch = apiKeyFormat.length > 0 && sig.apiKeyFormatPatterns?.some((p) => p.test(apiKeyFormat));
1649
1723
  const headerMatch = headerLines.length > 0 && sig.headerPatterns?.some((p) => headerLines.some((line) => p.test(line)));
1650
- if (uaMatch || cardMatch || keyMatch || headerMatch) {
1724
+ const ja4Match = ja4.length > 0 && sig.ja4Patterns?.some((p) => p.test(ja4));
1725
+ const asnMatch = asn.length > 0 && sig.asnList?.includes(asn);
1726
+ if (uaMatch || cardMatch || keyMatch || headerMatch || ja4Match || asnMatch) {
1651
1727
  const uaBase = ua.split(/[\s/]/)[0]?.toLowerCase() ?? "";
1652
1728
  const cardHost = (() => {
1653
1729
  try {
@@ -1656,7 +1732,7 @@ function matchPlatformSignature(signatures, input) {
1656
1732
  return "";
1657
1733
  }
1658
1734
  })();
1659
- const keyBasis = uaBase || cardHost || apiKeyFormat || "unknown";
1735
+ const keyBasis = uaBase || cardHost || apiKeyFormat || ja4 || asn && `asn${asn}` || "unknown";
1660
1736
  return {
1661
1737
  vendor: sig.vendor,
1662
1738
  fingerprintKey: `${sig.vendor}:${keyBasis}`,
@@ -3487,7 +3563,7 @@ async function fetchUCPManifest(manifestUrl, options = {}) {
3487
3563
  const controller = new AbortController();
3488
3564
  const timer = setTimeout(() => controller.abort(), timeoutMs);
3489
3565
  try {
3490
- const res = await fetch(manifestUrl, { signal: controller.signal });
3566
+ const res = await sdkFetch(manifestUrl, { signal: controller.signal });
3491
3567
  if (!res.ok) return null;
3492
3568
  return await res.json();
3493
3569
  } catch {
@@ -6032,7 +6108,7 @@ var AstraSync = class {
6032
6108
  */
6033
6109
  async pollRegistration(requestId) {
6034
6110
  const url = `${this.baseUrl}/api/agents/request-registration/${requestId}`;
6035
- const res = await fetch(url, { headers: { Accept: "application/json" } });
6111
+ const res = await sdkFetch(url, { headers: { Accept: "application/json" } });
6036
6112
  if (!res.ok) {
6037
6113
  const errBody = await res.json().catch(() => ({}));
6038
6114
  throw new AstraSyncError(
@@ -6089,7 +6165,7 @@ var AstraSync = class {
6089
6165
  * Check API health.
6090
6166
  */
6091
6167
  async health() {
6092
- const res = await fetch(`${this.baseUrl}/api/health/`);
6168
+ const res = await sdkFetch(`${this.baseUrl}/api/health/`);
6093
6169
  if (!res.ok) {
6094
6170
  throw new AstraSyncError(`Health check failed: ${res.status}`, res.status);
6095
6171
  }
@@ -6116,7 +6192,7 @@ var AstraSync = class {
6116
6192
  const signature = await this.signRequest(method, endpoint, body || {});
6117
6193
  headers["X-AstraSync-Signature"] = signature;
6118
6194
  }
6119
- const res = await fetch(url, {
6195
+ const res = await sdkFetch(url, {
6120
6196
  method,
6121
6197
  headers,
6122
6198
  ...body ? { body: JSON.stringify(body) } : {}
@@ -6141,7 +6217,7 @@ var AstraSync = class {
6141
6217
  if (this.cachedJwt && this.jwtExpiresAt && Date.now() < this.jwtExpiresAt) {
6142
6218
  return this.cachedJwt;
6143
6219
  }
6144
- const res = await fetch(`${this.baseUrl}/api/auth/login`, {
6220
+ const res = await sdkFetch(`${this.baseUrl}/api/auth/login`, {
6145
6221
  method: "POST",
6146
6222
  headers: { "Content-Type": "application/json" },
6147
6223
  body: JSON.stringify({ email: this.email, password: this.password })
@@ -6284,7 +6360,7 @@ var AgentClient = class _AgentClient {
6284
6360
  const url = `${this.apiBaseUrl.replace(/\/+$/, "")}/api/agents/${encodeURIComponent(
6285
6361
  this.credentials.agentId
6286
6362
  )}/ownership`;
6287
- const resp = await fetch(url, {
6363
+ const resp = await sdkFetch(url, {
6288
6364
  method: "GET",
6289
6365
  headers: {
6290
6366
  Authorization: `Bearer ${this.apiKey}`,
@@ -6322,7 +6398,7 @@ var AgentClient = class _AgentClient {
6322
6398
  }
6323
6399
  }
6324
6400
  const enrichedHeaders = setHttpHeaders(existingHeaders, creds);
6325
- return fetch(url, {
6401
+ return sdkFetch(url, {
6326
6402
  ...fetchOptions,
6327
6403
  headers: enrichedHeaders
6328
6404
  });
@@ -6511,7 +6587,7 @@ async function recordDecision2(config, params) {
6511
6587
  if (config.customHeaders) {
6512
6588
  Object.assign(headers, config.customHeaders);
6513
6589
  }
6514
- const response = await fetch(url, {
6590
+ const response = await sdkFetch(url, {
6515
6591
  method: "POST",
6516
6592
  headers,
6517
6593
  body: JSON.stringify(body)
@@ -6528,6 +6604,51 @@ async function recordDecision2(config, params) {
6528
6604
  blockchainTxHash: result.blockchainTxHash
6529
6605
  };
6530
6606
  }
6607
+
6608
+ // src/runtime-signatures.ts
6609
+ var RUNTIME_SIGNATURES = [
6610
+ {
6611
+ runtime: "browser-automation",
6612
+ displayName: "Browser automation",
6613
+ uaPatterns: [/headless/i, /phantomjs/i, /selenium/i, /playwright/i, /puppeteer/i]
6614
+ },
6615
+ {
6616
+ runtime: "nodejs",
6617
+ displayName: "Node.js runtime",
6618
+ // `node` exactly is the global-fetch default UA; the token forms cover
6619
+ // the common Node HTTP libraries.
6620
+ uaPatterns: [/^node$/i, /^undici/i, /node-fetch/i, /^axios/i, /^got\b/i, /needle/i]
6621
+ },
6622
+ {
6623
+ runtime: "python",
6624
+ displayName: "Python runtime",
6625
+ uaPatterns: [/python-requests/i, /python-urllib/i, /^python\b/i, /httpx/i, /aiohttp/i]
6626
+ },
6627
+ { runtime: "go", displayName: "Go runtime", uaPatterns: [/go-http-client/i] },
6628
+ {
6629
+ runtime: "java",
6630
+ displayName: "Java runtime",
6631
+ uaPatterns: [/^java\//i, /okhttp/i, /apache-httpclient/i]
6632
+ },
6633
+ { runtime: "curl", displayName: "curl", uaPatterns: [/^curl\//i] },
6634
+ { runtime: "wget", displayName: "wget", uaPatterns: [/^wget\//i] },
6635
+ { runtime: "ruby", displayName: "Ruby runtime", uaPatterns: [/^ruby\b/i, /faraday/i] },
6636
+ {
6637
+ runtime: "php",
6638
+ displayName: "PHP runtime",
6639
+ uaPatterns: [/^php\b/i, /guzzlehttp/i, /^symfony/i]
6640
+ },
6641
+ { runtime: "dotnet", displayName: ".NET runtime", uaPatterns: [/^dotnet/i, /httpclient/i] },
6642
+ { runtime: "dart", displayName: "Dart runtime", uaPatterns: [/^dart/i] }
6643
+ ];
6644
+ function detectRuntime(userAgent) {
6645
+ if (!userAgent) return void 0;
6646
+ const ua = userAgent.trim();
6647
+ for (const sig of RUNTIME_SIGNATURES) {
6648
+ if (sig.uaPatterns.some((p) => p.test(ua))) return sig.displayName;
6649
+ }
6650
+ return void 0;
6651
+ }
6531
6652
  // Annotate the CommonJS export names for ESM import in node:
6532
6653
  0 && (module.exports = {
6533
6654
  ACCESS_LEVEL_DESCRIPTIONS,
@@ -6545,9 +6666,11 @@ async function recordDecision2(config, params) {
6545
6666
  MAX_HEADER_VALUE_BYTES,
6546
6667
  MAX_TOTAL_BYTES,
6547
6668
  PLATFORM_AGENT_SIGNATURES,
6669
+ RUNTIME_SIGNATURES,
6548
6670
  RegistrationDeniedError,
6549
6671
  RegistrationExpiredError,
6550
6672
  RegistrationTimeoutError,
6673
+ SDK_USER_AGENT,
6551
6674
  TRUST_LEVEL_RANGES,
6552
6675
  VERSION,
6553
6676
  _resetEdgeConfigCache,
@@ -6561,6 +6684,7 @@ async function recordDecision2(config, params) {
6561
6684
  degradeToObserve,
6562
6685
  deriveConnectionFromHeaders,
6563
6686
  detectPlatformFingerprint,
6687
+ detectRuntime,
6564
6688
  determineAccessLevel,
6565
6689
  express,
6566
6690
  extractApiKeyFormat,
@@ -6589,6 +6713,7 @@ async function recordDecision2(config, params) {
6589
6713
  reportUnregisteredAttempt,
6590
6714
  sanitizeHeaders,
6591
6715
  sdk,
6716
+ sdkFetch,
6592
6717
  setMcpMeta,
6593
6718
  transport,
6594
6719
  verify