@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.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.5.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.5.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.5.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.5.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.5.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),
@@ -748,12 +770,15 @@ async function verify(config, request, options) {
748
770
  const verificationContext = apiResponse.verificationContext;
749
771
  const accessLevel = apiResponse.access?.accessLevel ?? "standard";
750
772
  const result = {
751
- // Round-18 G4: backend allowed access. Identity is verified (we resolved
752
- // the caller to an agent) and policy passed all gates. Read idVerified
753
- // from verificationContext for symmetry with the deny branch; default true
754
- // on success path since `access.allowed === true` implies identity was
755
- // resolvable (anonymous-allow paths flow through createGuidanceResponse).
756
- identityVerified: apiResponse.verificationContext?.idVerified !== false,
773
+ // 4.5.0 (probe F1): read idVerified from verificationContext with the
774
+ // SAME absent⇒false default as the deny branch. The previous `!== false`
775
+ // default manufactured `identityVerified: true` for anonymous
776
+ // allow_full/allow_partial/audit responses (which allow access but omit
777
+ // verificationContext entirely) — the "anonymous caller labelled
778
+ // verified" defect. Every authenticated success response has carried
779
+ // `verificationContext.idVerified: true` since v2.2.4, so a truth claim
780
+ // this load-bearing must never be defaulted on.
781
+ identityVerified: apiResponse.verificationContext?.idVerified === true,
757
782
  policyAllowed: true,
758
783
  accessLevel,
759
784
  agent,
@@ -809,7 +834,7 @@ async function recordDecision(config, sessionId, decision, reason) {
809
834
  headers["Authorization"] = `Bearer ${config.apiKey}`;
810
835
  headers["X-API-Key"] = config.apiKey;
811
836
  }
812
- await fetch(`${config.apiBaseUrl}/agents/verify-access/${sessionId}/decision`, {
837
+ await sdkFetch(`${config.apiBaseUrl}/agents/verify-access/${sessionId}/decision`, {
813
838
  method: "POST",
814
839
  headers,
815
840
  body: JSON.stringify({ decision, reason })
@@ -827,7 +852,7 @@ async function reportAttempt(config, report) {
827
852
  ...report,
828
853
  ...!report.counterpartyId && config.counterpartyId && { counterpartyId: config.counterpartyId }
829
854
  };
830
- await fetch(`${apiBaseUrl}/agents/verify-access/attempt-report`, {
855
+ await sdkFetch(`${apiBaseUrl}/agents/verify-access/attempt-report`, {
831
856
  method: "POST",
832
857
  headers,
833
858
  body: JSON.stringify(body)
@@ -842,7 +867,7 @@ async function fetchRoutes(config, counterpartyId) {
842
867
  headers["X-API-Key"] = config.apiKey;
843
868
  }
844
869
  try {
845
- const response = await fetch(
870
+ const response = await sdkFetch(
846
871
  `${config.apiBaseUrl}/endpoints/${encodeURIComponent(counterpartyId)}/routes`,
847
872
  { method: "GET", headers }
848
873
  );
@@ -861,7 +886,7 @@ async function reportUnregisteredAttempt(config, data) {
861
886
  ...counterpartyUrl && { counterpartyUrl },
862
887
  ...config.counterpartyId && { counterpartyId: config.counterpartyId }
863
888
  };
864
- await fetch(`${apiBaseUrl}/verification-activity/unregistered-attempt`, {
889
+ await sdkFetch(`${apiBaseUrl}/verification-activity/unregistered-attempt`, {
865
890
  method: "POST",
866
891
  headers: { "Content-Type": "application/json" },
867
892
  body: JSON.stringify(body)
@@ -870,7 +895,7 @@ async function reportUnregisteredAttempt(config, data) {
870
895
  }
871
896
  async function reportCounterpartyPreCheckFailure(config, data) {
872
897
  const apiBaseUrl = config.apiBaseUrl || DEFAULT_CONFIG.apiBaseUrl;
873
- await fetch(`${apiBaseUrl}/verification-activity/counterparty-pre-check-failure`, {
898
+ await sdkFetch(`${apiBaseUrl}/verification-activity/counterparty-pre-check-failure`, {
874
899
  method: "POST",
875
900
  headers: { "Content-Type": "application/json" },
876
901
  body: JSON.stringify(data)
@@ -904,7 +929,7 @@ function resolvePollUrl(config, pollUrl) {
904
929
  async function pollStepUpStatus(config, approval) {
905
930
  if (!approval.pollUrl) return null;
906
931
  try {
907
- const res = await fetch(resolvePollUrl(config, approval.pollUrl), {
932
+ const res = await sdkFetch(resolvePollUrl(config, approval.pollUrl), {
908
933
  method: "GET",
909
934
  signal: AbortSignal.timeout(POLL_REQUEST_TIMEOUT_MS)
910
935
  });
@@ -1279,7 +1304,13 @@ var PLATFORM_HEADER_NAMES = /* @__PURE__ */ new Set([
1279
1304
  "x-agent-card-url",
1280
1305
  "x-agent-provider"
1281
1306
  ]);
1282
- var PLATFORM_HEADER_PREFIXES = ["x-goog-", "x-stainless-", "x-anthropic-", "x-openai-"];
1307
+ var PLATFORM_HEADER_PREFIXES = [
1308
+ "x-goog-",
1309
+ "x-stainless-",
1310
+ "x-anthropic-",
1311
+ "x-openai-",
1312
+ "sec-ch-"
1313
+ ];
1283
1314
  var KEY_FORMAT_PREFIXES = [
1284
1315
  "sk-ant-api03",
1285
1316
  "sk-ant-api",
@@ -1351,6 +1382,7 @@ function sanitizeHeaders(raw) {
1351
1382
  const headers = {};
1352
1383
  const platformHeaders = {};
1353
1384
  let apiKeyFormat;
1385
+ let cookieCount;
1354
1386
  let totalBytes = 0;
1355
1387
  let count = 0;
1356
1388
  if (!raw) return { headers };
@@ -1365,6 +1397,9 @@ function sanitizeHeaders(raw) {
1365
1397
  if (fmt && !apiKeyFormat) apiKeyFormat = fmt;
1366
1398
  continue;
1367
1399
  }
1400
+ if (name === "cookie") {
1401
+ cookieCount = joined.split(";").filter((s) => s.trim().length > 0).length;
1402
+ }
1368
1403
  if (isSecretHeader(name)) continue;
1369
1404
  let value = joined;
1370
1405
  if (byteLength(value) > MAX_HEADER_VALUE_BYTES) {
@@ -1380,6 +1415,7 @@ function sanitizeHeaders(raw) {
1380
1415
  const result = { headers };
1381
1416
  if (apiKeyFormat) result.apiKeyFormat = apiKeyFormat;
1382
1417
  if (Object.keys(platformHeaders).length > 0) result.platformHeaders = platformHeaders;
1418
+ if (cookieCount !== void 0) result.cookieCount = cookieCount;
1383
1419
  return result;
1384
1420
  }
1385
1421
  function extractPlatformHeaders(sanitized) {
@@ -1399,6 +1435,9 @@ var CONNECTION_HEADER_SOURCES = [
1399
1435
  ["region", ["x-vercel-ip-country-region", "cloudfront-viewer-country-region"]],
1400
1436
  ["countryRegionName", ["cloudfront-viewer-country-region-name"]],
1401
1437
  ["city", ["x-vercel-ip-city", "cloudfront-viewer-city"]],
1438
+ ["latitude", ["x-vercel-ip-latitude", "cloudfront-viewer-latitude"]],
1439
+ ["longitude", ["x-vercel-ip-longitude", "cloudfront-viewer-longitude"]],
1440
+ ["postalCode", ["x-vercel-ip-postal-code", "cloudfront-viewer-postal-code"]],
1402
1441
  ["asn", ["cloudfront-viewer-asn"]],
1403
1442
  ["tlsVersion", ["cloudfront-viewer-tls"]],
1404
1443
  ["httpVersion", ["cloudfront-viewer-http-version"]],
@@ -1433,19 +1472,25 @@ function deriveConnectionFromHeaders(headers) {
1433
1472
  }
1434
1473
  }
1435
1474
  }
1436
- if (!connection.ip) {
1475
+ {
1437
1476
  const addr = lower["cloudfront-viewer-address"]?.trim();
1438
- if (addr) connection.ip = addr.replace(/:\d+$/, "");
1477
+ const portMatch = addr?.match(/:(\d+)$/);
1478
+ if (portMatch) connection.sourcePort = portMatch[1];
1479
+ if (!connection.ip && addr) connection.ip = addr.replace(/:\d+$/, "");
1439
1480
  }
1440
1481
  if (!connection.ip) {
1441
1482
  const xff = lower["x-forwarded-for"];
1442
1483
  const client = xff?.split(",")[0]?.trim();
1443
1484
  if (client) connection.ip = client;
1444
1485
  }
1486
+ if (!connection.tlsCipher) {
1487
+ const cipher = connection.tlsVersion?.split(":")[1];
1488
+ if (cipher) connection.tlsCipher = cipher;
1489
+ }
1445
1490
  return Object.keys(connection).length > 0 ? connection : void 0;
1446
1491
  }
1447
1492
  function buildSdkObservedMetadata(rawHeaders) {
1448
- const { headers, apiKeyFormat, platformHeaders } = sanitizeHeaders(rawHeaders);
1493
+ const { headers, apiKeyFormat, platformHeaders, cookieCount } = sanitizeHeaders(rawHeaders);
1449
1494
  const flat = {};
1450
1495
  if (rawHeaders) {
1451
1496
  for (const name of Object.keys(rawHeaders)) {
@@ -1460,6 +1505,7 @@ function buildSdkObservedMetadata(rawHeaders) {
1460
1505
  ...apiKeyFormat && { apiKeyFormat },
1461
1506
  ...platformHeaders && { platformHeaders },
1462
1507
  ...connection && { connection },
1508
+ ...cookieCount !== void 0 && { cookieCount },
1463
1509
  schemaVersion: CAPTURE_SCHEMA_VERSION
1464
1510
  };
1465
1511
  }
@@ -1504,7 +1550,7 @@ async function fetchEdgeConfig(config, counterpartyId, opts) {
1504
1550
  }
1505
1551
  if (opts?.etag) headers["If-None-Match"] = opts.etag;
1506
1552
  try {
1507
- const response = await fetch(
1553
+ const response = await sdkFetch(
1508
1554
  `${config.apiBaseUrl}/endpoints/${encodeURIComponent(counterpartyId)}/edge-config`,
1509
1555
  { method: "GET", headers, signal: AbortSignal.timeout(opts?.timeoutMs ?? 5e3) }
1510
1556
  );
@@ -1630,12 +1676,43 @@ var PLATFORM_AGENT_SIGNATURES = [
1630
1676
  uaPatterns: [/perplexity/i],
1631
1677
  agentCardPatterns: [/perplexity\.ai/i],
1632
1678
  apiKeyFormatPatterns: [/^pplx-/i]
1679
+ },
1680
+ // Agentic browsers — versioned-token patterns ONLY (a stock Chrome UA must
1681
+ // never match). Distinct vendor keys (not 'chatgpt'/'perplexity'): the
1682
+ // browser product is a different surface than the assistant service, and
1683
+ // the DB registry mirrors one row per vendor. These sit after the primary
1684
+ // vendor entries so a UA carrying an explicit vendor token keeps its
1685
+ // primary attribution; a browser-shaped UA with the agentic token is
1686
+ // caught here BEFORE classify's human tier, which is what makes these
1687
+ // visits observable at all. Token drift is real (some builds mimic Chrome
1688
+ // outright) — re-verify against captured events when a new browser ships,
1689
+ // and check `sec-ch-ua` platformHeaders for the real brand.
1690
+ {
1691
+ vendor: "chatgpt-atlas",
1692
+ displayName: "ChatGPT Atlas browser",
1693
+ uaPatterns: [/\batlas\/\d/i],
1694
+ headerPatterns: [/^sec-ch-ua: .*atlas/i]
1695
+ },
1696
+ {
1697
+ vendor: "perplexity-comet",
1698
+ displayName: "Perplexity Comet browser",
1699
+ uaPatterns: [/\bcomet\/\d/i],
1700
+ headerPatterns: [/^sec-ch-ua: .*comet/i]
1701
+ },
1702
+ // Our own SDK self-identifies (sdkFetch stamps this UA) — first-party
1703
+ // traffic must never pollute the unknown/anonymous pool it exists to audit.
1704
+ {
1705
+ vendor: "astrasync-sdk",
1706
+ displayName: "AstraSync SDK",
1707
+ uaPatterns: [/^astrasync-sdk\//i]
1633
1708
  }
1634
1709
  ];
1635
1710
  function matchPlatformSignature(signatures, input) {
1636
1711
  const ua = input.userAgent ?? "";
1637
1712
  const cardUrl = input.agentCardUrl ?? "";
1638
1713
  const apiKeyFormat = input.apiKeyFormat ?? "";
1714
+ const ja4 = input.ja4 ?? "";
1715
+ const asn = input.asn ?? "";
1639
1716
  const headerLines = [];
1640
1717
  if (input.platformHeaders) {
1641
1718
  for (const [name, value] of Object.entries(input.platformHeaders)) {
@@ -1647,7 +1724,9 @@ function matchPlatformSignature(signatures, input) {
1647
1724
  const cardMatch = sig.agentCardPatterns?.some((p) => p.test(cardUrl));
1648
1725
  const keyMatch = apiKeyFormat.length > 0 && sig.apiKeyFormatPatterns?.some((p) => p.test(apiKeyFormat));
1649
1726
  const headerMatch = headerLines.length > 0 && sig.headerPatterns?.some((p) => headerLines.some((line) => p.test(line)));
1650
- if (uaMatch || cardMatch || keyMatch || headerMatch) {
1727
+ const ja4Match = ja4.length > 0 && sig.ja4Patterns?.some((p) => p.test(ja4));
1728
+ const asnMatch = asn.length > 0 && sig.asnList?.includes(asn);
1729
+ if (uaMatch || cardMatch || keyMatch || headerMatch || ja4Match || asnMatch) {
1651
1730
  const uaBase = ua.split(/[\s/]/)[0]?.toLowerCase() ?? "";
1652
1731
  const cardHost = (() => {
1653
1732
  try {
@@ -1656,7 +1735,7 @@ function matchPlatformSignature(signatures, input) {
1656
1735
  return "";
1657
1736
  }
1658
1737
  })();
1659
- const keyBasis = uaBase || cardHost || apiKeyFormat || "unknown";
1738
+ const keyBasis = uaBase || cardHost || apiKeyFormat || ja4 || asn && `asn${asn}` || "unknown";
1660
1739
  return {
1661
1740
  vendor: sig.vendor,
1662
1741
  fingerprintKey: `${sig.vendor}:${keyBasis}`,
@@ -1672,9 +1751,10 @@ function detectPlatformFingerprint(input) {
1672
1751
 
1673
1752
  // src/edge-core/classify.ts
1674
1753
  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;
1754
+ var SELF_UA_PATTERN = /^astrasync-sdk\//i;
1675
1755
  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;
1676
1756
  var BROWSER_UA_PATTERN = /mozilla\/\d/i;
1677
- var _patterns = { STATIC_EXTENSIONS, BOT_UA_PATTERN, BROWSER_UA_PATTERN };
1757
+ var _patterns = { STATIC_EXTENSIONS, BOT_UA_PATTERN, BROWSER_UA_PATTERN, SELF_UA_PATTERN };
1678
1758
 
1679
1759
  // src/edge-core/events.ts
1680
1760
  function sampled(rate, random = Math.random) {
@@ -2094,7 +2174,7 @@ function createMiddleware(options) {
2094
2174
  let result = await verify(config, verificationRequest);
2095
2175
  req.agentVerification = result;
2096
2176
  let sessionId = result.sessionId;
2097
- if (config.stepUpMaxWaitMs !== void 0 && result.identityVerified && requiresHumanApproval(result) && result.stepUpApproval?.pollUrl) {
2177
+ if (config.stepUpMaxWaitMs !== void 0 && result.policyAllowed && requiresHumanApproval(result) && result.stepUpApproval?.pollUrl) {
2098
2178
  const outcome = await awaitStepUpApproval(config, result.stepUpApproval);
2099
2179
  res.setHeader("X-Astra-Step-Up", outcome);
2100
2180
  if (outcome === "approved") {
@@ -2105,7 +2185,7 @@ function createMiddleware(options) {
2105
2185
  result.stepUpOutcome = outcome;
2106
2186
  }
2107
2187
  }
2108
- if (!result.identityVerified || !result.policyAllowed) {
2188
+ if (!result.policyAllowed) {
2109
2189
  if (shouldRecordDecisions && sessionId) {
2110
2190
  recordDecision(config, sessionId, "denied", result.denialReasons?.[0]).catch(() => {
2111
2191
  });
@@ -2606,7 +2686,7 @@ function createMiddleware2(options) {
2606
2686
  };
2607
2687
  let result = await verify(config, verificationRequest);
2608
2688
  let stepUpHoldOutcome;
2609
- if (config.stepUpMaxWaitMs !== void 0 && result.identityVerified && requiresHumanApproval(result) && result.stepUpApproval?.pollUrl) {
2689
+ if (config.stepUpMaxWaitMs !== void 0 && result.policyAllowed && requiresHumanApproval(result) && result.stepUpApproval?.pollUrl) {
2610
2690
  stepUpHoldOutcome = await awaitStepUpApproval(config, result.stepUpApproval);
2611
2691
  if (stepUpHoldOutcome === "approved") {
2612
2692
  result = await verify(config, verificationRequest, { bypassCache: true });
@@ -2614,9 +2694,9 @@ function createMiddleware2(options) {
2614
2694
  result.stepUpOutcome = stepUpHoldOutcome;
2615
2695
  }
2616
2696
  }
2617
- const approvalRequired = result.identityVerified && result.policyAllowed && requiresHumanApproval(result);
2697
+ const approvalRequired = result.policyAllowed && requiresHumanApproval(result);
2618
2698
  if (approvalRequired) annotateApprovalRequired(result);
2619
- if (!result.identityVerified || !result.policyAllowed || approvalRequired) {
2699
+ if (!result.policyAllowed || approvalRequired) {
2620
2700
  if (result.misconfigured && pathname.startsWith("/api/")) {
2621
2701
  return NextResponse.json(
2622
2702
  {
@@ -3487,7 +3567,7 @@ async function fetchUCPManifest(manifestUrl, options = {}) {
3487
3567
  const controller = new AbortController();
3488
3568
  const timer = setTimeout(() => controller.abort(), timeoutMs);
3489
3569
  try {
3490
- const res = await fetch(manifestUrl, { signal: controller.signal });
3570
+ const res = await sdkFetch(manifestUrl, { signal: controller.signal });
3491
3571
  if (!res.ok) return null;
3492
3572
  return await res.json();
3493
3573
  } catch {
@@ -5749,7 +5829,7 @@ function createMcpMiddleware(options) {
5749
5829
  let result = await verify(config, verificationRequest);
5750
5830
  req.agentVerification = result;
5751
5831
  let sessionId = result.sessionId;
5752
- if (config.stepUpMaxWaitMs !== void 0 && result.identityVerified && requiresHumanApproval(result) && result.stepUpApproval?.pollUrl) {
5832
+ if (config.stepUpMaxWaitMs !== void 0 && result.policyAllowed && requiresHumanApproval(result) && result.stepUpApproval?.pollUrl) {
5753
5833
  const outcome = await awaitStepUpApproval(config, result.stepUpApproval);
5754
5834
  res.setHeader("X-Astra-Step-Up", outcome);
5755
5835
  if (outcome === "approved") {
@@ -5760,7 +5840,7 @@ function createMcpMiddleware(options) {
5760
5840
  result.stepUpOutcome = outcome;
5761
5841
  }
5762
5842
  }
5763
- if (!result.identityVerified || !result.policyAllowed) {
5843
+ if (!result.policyAllowed) {
5764
5844
  if (shouldRecordDecisions && sessionId) {
5765
5845
  recordDecision(config, sessionId, "denied", result.denialReasons?.[0]).catch(() => {
5766
5846
  });
@@ -6032,7 +6112,7 @@ var AstraSync = class {
6032
6112
  */
6033
6113
  async pollRegistration(requestId) {
6034
6114
  const url = `${this.baseUrl}/api/agents/request-registration/${requestId}`;
6035
- const res = await fetch(url, { headers: { Accept: "application/json" } });
6115
+ const res = await sdkFetch(url, { headers: { Accept: "application/json" } });
6036
6116
  if (!res.ok) {
6037
6117
  const errBody = await res.json().catch(() => ({}));
6038
6118
  throw new AstraSyncError(
@@ -6089,7 +6169,7 @@ var AstraSync = class {
6089
6169
  * Check API health.
6090
6170
  */
6091
6171
  async health() {
6092
- const res = await fetch(`${this.baseUrl}/api/health/`);
6172
+ const res = await sdkFetch(`${this.baseUrl}/api/health/`);
6093
6173
  if (!res.ok) {
6094
6174
  throw new AstraSyncError(`Health check failed: ${res.status}`, res.status);
6095
6175
  }
@@ -6116,7 +6196,7 @@ var AstraSync = class {
6116
6196
  const signature = await this.signRequest(method, endpoint, body || {});
6117
6197
  headers["X-AstraSync-Signature"] = signature;
6118
6198
  }
6119
- const res = await fetch(url, {
6199
+ const res = await sdkFetch(url, {
6120
6200
  method,
6121
6201
  headers,
6122
6202
  ...body ? { body: JSON.stringify(body) } : {}
@@ -6141,7 +6221,7 @@ var AstraSync = class {
6141
6221
  if (this.cachedJwt && this.jwtExpiresAt && Date.now() < this.jwtExpiresAt) {
6142
6222
  return this.cachedJwt;
6143
6223
  }
6144
- const res = await fetch(`${this.baseUrl}/api/auth/login`, {
6224
+ const res = await sdkFetch(`${this.baseUrl}/api/auth/login`, {
6145
6225
  method: "POST",
6146
6226
  headers: { "Content-Type": "application/json" },
6147
6227
  body: JSON.stringify({ email: this.email, password: this.password })
@@ -6284,7 +6364,7 @@ var AgentClient = class _AgentClient {
6284
6364
  const url = `${this.apiBaseUrl.replace(/\/+$/, "")}/api/agents/${encodeURIComponent(
6285
6365
  this.credentials.agentId
6286
6366
  )}/ownership`;
6287
- const resp = await fetch(url, {
6367
+ const resp = await sdkFetch(url, {
6288
6368
  method: "GET",
6289
6369
  headers: {
6290
6370
  Authorization: `Bearer ${this.apiKey}`,
@@ -6322,7 +6402,7 @@ var AgentClient = class _AgentClient {
6322
6402
  }
6323
6403
  }
6324
6404
  const enrichedHeaders = setHttpHeaders(existingHeaders, creds);
6325
- return fetch(url, {
6405
+ return sdkFetch(url, {
6326
6406
  ...fetchOptions,
6327
6407
  headers: enrichedHeaders
6328
6408
  });
@@ -6511,7 +6591,7 @@ async function recordDecision2(config, params) {
6511
6591
  if (config.customHeaders) {
6512
6592
  Object.assign(headers, config.customHeaders);
6513
6593
  }
6514
- const response = await fetch(url, {
6594
+ const response = await sdkFetch(url, {
6515
6595
  method: "POST",
6516
6596
  headers,
6517
6597
  body: JSON.stringify(body)
@@ -6528,6 +6608,51 @@ async function recordDecision2(config, params) {
6528
6608
  blockchainTxHash: result.blockchainTxHash
6529
6609
  };
6530
6610
  }
6611
+
6612
+ // src/runtime-signatures.ts
6613
+ var RUNTIME_SIGNATURES = [
6614
+ {
6615
+ runtime: "browser-automation",
6616
+ displayName: "Browser automation",
6617
+ uaPatterns: [/headless/i, /phantomjs/i, /selenium/i, /playwright/i, /puppeteer/i]
6618
+ },
6619
+ {
6620
+ runtime: "nodejs",
6621
+ displayName: "Node.js runtime",
6622
+ // `node` exactly is the global-fetch default UA; the token forms cover
6623
+ // the common Node HTTP libraries.
6624
+ uaPatterns: [/^node$/i, /^undici/i, /node-fetch/i, /^axios/i, /^got\b/i, /needle/i]
6625
+ },
6626
+ {
6627
+ runtime: "python",
6628
+ displayName: "Python runtime",
6629
+ uaPatterns: [/python-requests/i, /python-urllib/i, /^python\b/i, /httpx/i, /aiohttp/i]
6630
+ },
6631
+ { runtime: "go", displayName: "Go runtime", uaPatterns: [/go-http-client/i] },
6632
+ {
6633
+ runtime: "java",
6634
+ displayName: "Java runtime",
6635
+ uaPatterns: [/^java\//i, /okhttp/i, /apache-httpclient/i]
6636
+ },
6637
+ { runtime: "curl", displayName: "curl", uaPatterns: [/^curl\//i] },
6638
+ { runtime: "wget", displayName: "wget", uaPatterns: [/^wget\//i] },
6639
+ { runtime: "ruby", displayName: "Ruby runtime", uaPatterns: [/^ruby\b/i, /faraday/i] },
6640
+ {
6641
+ runtime: "php",
6642
+ displayName: "PHP runtime",
6643
+ uaPatterns: [/^php\b/i, /guzzlehttp/i, /^symfony/i]
6644
+ },
6645
+ { runtime: "dotnet", displayName: ".NET runtime", uaPatterns: [/^dotnet/i, /httpclient/i] },
6646
+ { runtime: "dart", displayName: "Dart runtime", uaPatterns: [/^dart/i] }
6647
+ ];
6648
+ function detectRuntime(userAgent) {
6649
+ if (!userAgent) return void 0;
6650
+ const ua = userAgent.trim();
6651
+ for (const sig of RUNTIME_SIGNATURES) {
6652
+ if (sig.uaPatterns.some((p) => p.test(ua))) return sig.displayName;
6653
+ }
6654
+ return void 0;
6655
+ }
6531
6656
  // Annotate the CommonJS export names for ESM import in node:
6532
6657
  0 && (module.exports = {
6533
6658
  ACCESS_LEVEL_DESCRIPTIONS,
@@ -6545,9 +6670,11 @@ async function recordDecision2(config, params) {
6545
6670
  MAX_HEADER_VALUE_BYTES,
6546
6671
  MAX_TOTAL_BYTES,
6547
6672
  PLATFORM_AGENT_SIGNATURES,
6673
+ RUNTIME_SIGNATURES,
6548
6674
  RegistrationDeniedError,
6549
6675
  RegistrationExpiredError,
6550
6676
  RegistrationTimeoutError,
6677
+ SDK_USER_AGENT,
6551
6678
  TRUST_LEVEL_RANGES,
6552
6679
  VERSION,
6553
6680
  _resetEdgeConfigCache,
@@ -6561,6 +6688,7 @@ async function recordDecision2(config, params) {
6561
6688
  degradeToObserve,
6562
6689
  deriveConnectionFromHeaders,
6563
6690
  detectPlatformFingerprint,
6691
+ detectRuntime,
6564
6692
  determineAccessLevel,
6565
6693
  express,
6566
6694
  extractApiKeyFormat,
@@ -6589,6 +6717,7 @@ async function recordDecision2(config, params) {
6589
6717
  reportUnregisteredAttempt,
6590
6718
  sanitizeHeaders,
6591
6719
  sdk,
6720
+ sdkFetch,
6592
6721
  setMcpMeta,
6593
6722
  transport,
6594
6723
  verify