@agentronics/sdk 0.6.0 → 0.7.1

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 (53) hide show
  1. package/dist/chunk-4FLZQPCO.js +18 -0
  2. package/dist/chunk-4FLZQPCO.js.map +1 -0
  3. package/dist/{chunk-X7NFMFN6.js → chunk-6QQS26AP.js} +264 -69
  4. package/dist/chunk-6QQS26AP.js.map +1 -0
  5. package/dist/chunk-BPDWJCPY.js +2 -0
  6. package/dist/chunk-BPDWJCPY.js.map +1 -0
  7. package/dist/index.cjs +8 -7
  8. package/dist/index.cjs.map +1 -1
  9. package/dist/index.d.cts +57 -8
  10. package/dist/index.d.ts +57 -8
  11. package/dist/index.global.js +8 -7
  12. package/dist/index.global.js.map +1 -1
  13. package/dist/index.js +1 -1
  14. package/dist/index.js.map +1 -1
  15. package/dist/{init-only-7f8_LQAI.d.ts → init-only-C80qhujz.d.ts} +227 -30
  16. package/dist/{init-only-Cwll8MGz.d.cts → init-only-s9E9bVC0.d.cts} +227 -30
  17. package/dist/init-only.cjs +8 -7
  18. package/dist/init-only.cjs.map +1 -1
  19. package/dist/init-only.d.cts +2 -2
  20. package/dist/init-only.d.ts +2 -2
  21. package/dist/init-only.global.js +8 -7
  22. package/dist/init-only.global.js.map +1 -1
  23. package/dist/init-only.js +1 -1
  24. package/dist/lite.cjs +1 -1
  25. package/dist/lite.cjs.map +1 -1
  26. package/dist/lite.d.cts +2 -2
  27. package/dist/lite.d.ts +2 -2
  28. package/dist/lite.global.js +1 -1
  29. package/dist/lite.global.js.map +1 -1
  30. package/dist/lite.js +1 -1
  31. package/dist/middleware-Dd2T772u.d.cts +429 -0
  32. package/dist/middleware-Dd2T772u.d.ts +429 -0
  33. package/dist/next.cjs +239 -64
  34. package/dist/next.cjs.map +1 -1
  35. package/dist/next.d.cts +2 -2
  36. package/dist/next.d.ts +2 -2
  37. package/dist/next.js +2 -2
  38. package/dist/server.cjs +361 -66
  39. package/dist/server.cjs.map +1 -1
  40. package/dist/server.d.cts +103 -308
  41. package/dist/server.d.ts +103 -308
  42. package/dist/server.js +93 -1
  43. package/dist/server.js.map +1 -1
  44. package/dist/webmcp-BgKzHeQ3.d.cts +97 -0
  45. package/dist/webmcp-BgKzHeQ3.d.ts +97 -0
  46. package/package.json +4 -4
  47. package/dist/chunk-LLD3Y2LZ.js +0 -2
  48. package/dist/chunk-LLD3Y2LZ.js.map +0 -1
  49. package/dist/chunk-X7NFMFN6.js.map +0 -1
  50. package/dist/chunk-Y7JYD5RA.js +0 -17
  51. package/dist/chunk-Y7JYD5RA.js.map +0 -1
  52. package/dist/webmcp-C4ikhM4g.d.cts +0 -12
  53. package/dist/webmcp-C4ikhM4g.d.ts +0 -12
@@ -1,12 +1,105 @@
1
+ // src/server/ipRanges.ts
2
+ var PUBLISHED_IP_RANGES = {
3
+ GPTBot: ["https://openai.com/gptbot.json"],
4
+ "OAI-SearchBot": ["https://openai.com/searchbot.json"],
5
+ "ChatGPT-User": ["https://openai.com/chatgpt-user.json"],
6
+ PerplexityBot: ["https://www.perplexity.ai/perplexitybot.json"],
7
+ "Perplexity-User": ["https://www.perplexity.ai/perplexity-user.json"],
8
+ Googlebot: ["https://developers.google.com/static/crawling/ipranges/common-crawlers.json"],
9
+ Bingbot: ["https://www.bing.com/toolbox/bingbot.json"],
10
+ DuckDuckBot: ["https://duckduckgo.com/duckduckbot.json"],
11
+ CCBot: ["https://index.commoncrawl.org/ccbot.json"]
12
+ };
13
+ var TTL_MS = 24 * 36e5;
14
+ var FAILED_TTL_MS = 10 * 6e4;
15
+ var cache = /* @__PURE__ */ new Map();
16
+ async function loadUrl(url, fetcher) {
17
+ const hit = cache.get(url);
18
+ if (hit && hit.until > Date.now()) return hit.cidrs;
19
+ try {
20
+ const res = await fetcher(url, { signal: AbortSignal.timeout(5e3), redirect: "follow" });
21
+ if (!res.ok) throw new Error(String(res.status));
22
+ const body = await res.json();
23
+ const cidrs = (body.prefixes ?? []).flatMap((p) => [p.ipv4Prefix, p.ipv6Prefix]).filter((c) => typeof c === "string");
24
+ cache.set(url, { cidrs, until: Date.now() + TTL_MS });
25
+ return cidrs;
26
+ } catch {
27
+ const cidrs = hit?.cidrs ?? [];
28
+ cache.set(url, { cidrs, until: Date.now() + FAILED_TTL_MS });
29
+ return cidrs;
30
+ }
31
+ }
32
+ async function rangesFor(name, options = {}) {
33
+ const fixed = options.ipRanges?.[name];
34
+ if (fixed) return fixed;
35
+ const urls = PUBLISHED_IP_RANGES[name];
36
+ if (!urls) return null;
37
+ const fetcher = options.fetcher ?? fetch;
38
+ return (await Promise.all(urls.map((u) => loadUrl(u, fetcher)))).flat();
39
+ }
40
+ function v4ToBigInt(ip) {
41
+ const parts = ip.split(".");
42
+ if (parts.length !== 4) return null;
43
+ let n = 0n;
44
+ for (const p of parts) {
45
+ if (!/^\d{1,3}$/.test(p) || Number(p) > 255) return null;
46
+ n = n << 8n | BigInt(p);
47
+ }
48
+ return n;
49
+ }
50
+ function v6ToBigInt(ip) {
51
+ let s = ip.toLowerCase().replace(/^\[|\]$/g, "");
52
+ const zone = s.indexOf("%");
53
+ if (zone >= 0) s = s.slice(0, zone);
54
+ const v4 = s.match(/(\d+\.\d+\.\d+\.\d+)$/);
55
+ if (v4) {
56
+ const n2 = v4ToBigInt(v4[1]);
57
+ if (n2 === null) return null;
58
+ s = s.slice(0, -v4[1].length) + (n2 >> 16n & 0xffffn).toString(16) + ":" + (n2 & 0xffffn).toString(16);
59
+ }
60
+ const halves = s.split("::");
61
+ if (halves.length > 2) return null;
62
+ const head = halves[0] ? halves[0].split(":") : [];
63
+ const tail = halves.length === 2 && halves[1] ? halves[1].split(":") : [];
64
+ const missing = 8 - head.length - tail.length;
65
+ if (halves.length === 1 ? missing !== 0 : missing < 1) return null;
66
+ const groups = [...head, ...Array(halves.length === 2 ? missing : 0).fill("0"), ...tail];
67
+ let n = 0n;
68
+ for (const g of groups) {
69
+ if (!/^[0-9a-f]{1,4}$/.test(g)) return null;
70
+ n = n << 16n | BigInt(parseInt(g, 16));
71
+ }
72
+ return n;
73
+ }
74
+ function ipInCidr(ip, cidr) {
75
+ const [range, bitsRaw] = cidr.split("/");
76
+ if (!range) return false;
77
+ const isV6 = range.includes(":");
78
+ const mapped = ip.toLowerCase().match(/^::ffff:(\d+\.\d+\.\d+\.\d+)$/);
79
+ const candidate = !isV6 && mapped ? mapped[1] : ip;
80
+ const width = isV6 ? 128 : 32;
81
+ const bits = bitsRaw === void 0 ? width : Number(bitsRaw);
82
+ if (!Number.isInteger(bits) || bits < 0 || bits > width) return false;
83
+ const a = isV6 ? v6ToBigInt(candidate) : v4ToBigInt(candidate);
84
+ const b = isV6 ? v6ToBigInt(range) : v4ToBigInt(range);
85
+ if (a === null || b === null) return false;
86
+ const shift = BigInt(width - bits);
87
+ return a >> shift === b >> shift;
88
+ }
89
+ var ipInRanges = (ip, cidrs) => cidrs.some((c) => ipInCidr(ip, c));
90
+
1
91
  // src/detection/crawler.ts
2
92
  var CRAWLER_SIGNATURES = [
3
93
  // AI / LLM crawlers
4
94
  { name: "GPTBot", category: "ai", pattern: /GPTBot/i },
5
95
  { name: "OAI-SearchBot", category: "ai", pattern: /OAI-SearchBot/i },
6
96
  { name: "ChatGPT-User", category: "ai", pattern: /ChatGPT-User/i },
97
+ { name: "Claude-User", category: "ai", pattern: /Claude-User/i },
98
+ { name: "Claude-SearchBot", category: "ai", pattern: /Claude-SearchBot/i },
7
99
  { name: "ClaudeBot", category: "ai", pattern: /ClaudeBot/i },
8
100
  { name: "Claude-Web", category: "ai", pattern: /Claude-Web/i },
9
101
  { name: "anthropic-ai", category: "ai", pattern: /anthropic-ai/i },
102
+ { name: "Perplexity-User", category: "ai", pattern: /Perplexity-User/i },
10
103
  { name: "PerplexityBot", category: "ai", pattern: /PerplexityBot/i },
11
104
  { name: "Google-Extended", category: "ai", pattern: /Google-Extended/i },
12
105
  { name: "Applebot-Extended", category: "ai", pattern: /Applebot-Extended/i },
@@ -62,14 +155,19 @@ async function nodeResolver() {
62
155
  }
63
156
  }
64
157
  var rdnsCache = /* @__PURE__ */ new Map();
65
- async function verifyCrawler(claim, ip, resolver) {
158
+ async function verifyCrawler(claim, ip, resolver, ranges = {}) {
66
159
  const domains = RDNS_DOMAINS[claim.name];
67
- if (!domains) return { status: "unverified", name: claim.name, reason: "no_rdns_method_for_vendor" };
160
+ const cidrs = await rangesFor(claim.name, ranges);
161
+ if (!domains && !cidrs) return { status: "unverified", name: claim.name, reason: "no_verification_method_for_vendor" };
68
162
  if (!ip) return { status: "unverified", name: claim.name, reason: "client_ip_unknown" };
163
+ if (cidrs?.length && ipInRanges(ip, cidrs)) {
164
+ return { status: "verified", name: claim.name, host: ip, ip, via: "ip-range" };
165
+ }
166
+ if (!domains) return { status: "unverified", name: claim.name, reason: "ip_outside_published_ranges" };
69
167
  const key = `${claim.name}|${ip}`;
70
168
  const hit = rdnsCache.get(key);
71
169
  if (hit && hit.until > Date.now()) {
72
- return hit.ok ? { status: "verified", name: claim.name, host: hit.host, ip } : { status: "unverified", name: claim.name, reason: "rdns_mismatch" };
170
+ return hit.ok ? { status: "verified", name: claim.name, host: hit.host, ip, via: "rdns" } : { status: "unverified", name: claim.name, reason: "rdns_mismatch" };
73
171
  }
74
172
  const dns = resolver ?? await nodeResolver();
75
173
  if (!dns) return { status: "unverified", name: claim.name, reason: "dns_unavailable_in_runtime" };
@@ -85,7 +183,7 @@ async function verifyCrawler(claim, ip, resolver) {
85
183
  const forward = await dns.lookup(host);
86
184
  if (forward.includes(ip)) {
87
185
  remember(true, host);
88
- return { status: "verified", name: claim.name, host, ip };
186
+ return { status: "verified", name: claim.name, host, ip, via: "rdns" };
89
187
  }
90
188
  }
91
189
  remember(false);
@@ -318,6 +416,135 @@ function bytesToBase64(bytes) {
318
416
  }
319
417
  var bytesToBase64Url = (bytes) => bytesToBase64(bytes).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
320
418
 
419
+ // src/server/oauth.ts
420
+ var josePromise;
421
+ var loadJose = () => josePromise ??= import('jose');
422
+ var remoteSets = /* @__PURE__ */ new Map();
423
+ var ASYMMETRIC = ["RS256", "PS256", "ES256", "EdDSA"];
424
+ function unverifiedIssuer(token) {
425
+ try {
426
+ const payload = token.split(".")[1];
427
+ const json = atob(payload.replace(/-/g, "+").replace(/_/g, "/"));
428
+ const iss = JSON.parse(json).iss;
429
+ return typeof iss === "string" ? iss : null;
430
+ } catch {
431
+ return null;
432
+ }
433
+ }
434
+ var str = (v) => typeof v === "string" && v ? v : void 0;
435
+ async function sha256B64u(text) {
436
+ const d = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(text));
437
+ return bytesToBase64Url(new Uint8Array(d));
438
+ }
439
+ var htuOf = (url) => {
440
+ const u = new URL(url);
441
+ return `${u.origin}${u.pathname}`;
442
+ };
443
+ async function verifyDpopProof(jose, proof, token, jkt, request, replay) {
444
+ try {
445
+ const { payload, protectedHeader } = await jose.jwtVerify(proof, jose.EmbeddedJWK, {
446
+ typ: "dpop+jwt",
447
+ algorithms: ASYMMETRIC
448
+ });
449
+ const jwk = protectedHeader.jwk;
450
+ if (!jwk) return "dpop_missing_jwk";
451
+ if (await jose.calculateJwkThumbprint(jwk, "sha256") !== jkt) return "dpop_key_mismatch";
452
+ if (str(payload.htm)?.toUpperCase() !== request.method.toUpperCase()) return "dpop_htm_mismatch";
453
+ if (str(payload.htu) === void 0 || htuOf(str(payload.htu)) !== htuOf(request.url)) return "dpop_htu_mismatch";
454
+ const iat = typeof payload.iat === "number" ? payload.iat : NaN;
455
+ if (!Number.isFinite(iat) || Math.abs(Date.now() / 1e3 - iat) > 300) return "dpop_stale";
456
+ if (str(payload.ath) !== await sha256B64u(token)) return "dpop_ath_mismatch";
457
+ const jti = str(payload.jti);
458
+ if (!jti) return "dpop_missing_jti";
459
+ if (replay) {
460
+ const key = `dpop:${jkt}:${jti}`;
461
+ if (await replay.has(key)) return "dpop_replayed";
462
+ await replay.add(key, 600);
463
+ }
464
+ return null;
465
+ } catch (e) {
466
+ return `dpop_invalid:${e.code ?? e.message}`;
467
+ }
468
+ }
469
+ async function verifyOAuthToken(token, o, ctx) {
470
+ const jose = await loadJose();
471
+ const keySet = o.jwks ? jose.createLocalJWKSet(o.jwks) : (() => {
472
+ const uri = o.jwksUri ?? `${o.issuer.replace(/\/$/, "")}/.well-known/jwks.json`;
473
+ let s = remoteSets.get(uri);
474
+ if (!s) {
475
+ s = jose.createRemoteJWKSet(new URL(uri), { cacheMaxAge: 36e5, cooldownDuration: 3e4 });
476
+ remoteSets.set(uri, s);
477
+ }
478
+ return s;
479
+ })();
480
+ let payload;
481
+ try {
482
+ ;
483
+ ({ payload } = await jose.jwtVerify(token, keySet, {
484
+ issuer: o.issuer,
485
+ audience: o.audience,
486
+ algorithms: o.algorithms ?? ASYMMETRIC,
487
+ clockTolerance: o.clockToleranceSec ?? 30
488
+ }));
489
+ } catch (e) {
490
+ return { error: `invalid_token:${e.code ?? e.message}` };
491
+ }
492
+ const jkt = str(payload.cnf?.jkt);
493
+ let dpop = false;
494
+ if (jkt) {
495
+ const proof = ctx.request.headers.get("dpop");
496
+ if (ctx.scheme !== "dpop" || !proof) return { error: "dpop_proof_required" };
497
+ const bad = await verifyDpopProof(jose, proof, token, jkt, ctx.request, ctx.replayCache);
498
+ if (bad) return { error: bad };
499
+ dpop = true;
500
+ } else if (o.requireDpop) {
501
+ return { error: "token_not_dpop_bound" };
502
+ }
503
+ if (o.requiredScopes?.length) {
504
+ const granted = new Set(String(payload.scope ?? payload.scp ?? "").split(/\s+/));
505
+ const missing = o.requiredScopes.filter((s) => !granted.has(s));
506
+ if (missing.length) return { error: `missing_scopes:${missing.join(",")}` };
507
+ }
508
+ const act = payload.act;
509
+ const clientId = str(payload.client_id) ?? str(payload.azp);
510
+ const sub = str(payload.sub);
511
+ const agentId = str(act?.client_id) ?? str(act?.sub) ?? clientId ?? sub;
512
+ if (!agentId) return { error: "token_has_no_client_id" };
513
+ const user = act ? sub : sub && clientId && sub !== clientId ? sub : void 0;
514
+ const agent = {
515
+ id: `oauth2:${agentId}`,
516
+ name: str(payload.agent_name) ?? str(payload.client_name) ?? agentId,
517
+ vendor: str(payload.agent_vendor) ?? null,
518
+ claims: { iss: payload.iss, sub, scope: payload.scope ?? payload.scp, ...act ? { act } : {} },
519
+ dpop
520
+ };
521
+ if (user) {
522
+ const onBehalfOf = { sub: user };
523
+ if (str(payload.email)) onBehalfOf.email = str(payload.email);
524
+ if (str(payload.name)) onBehalfOf.name = str(payload.name);
525
+ agent.onBehalfOf = onBehalfOf;
526
+ }
527
+ return agent;
528
+ }
529
+ var PROTECTED_RESOURCE_PATH = "/.well-known/oauth-protected-resource";
530
+ function protectedResourceMetadata(o) {
531
+ return {
532
+ resource: o.resource,
533
+ authorization_servers: o.authorizationServers,
534
+ bearer_methods_supported: ["header"],
535
+ ...o.scopesSupported ? { scopes_supported: o.scopesSupported } : {},
536
+ ...o.resourceName ? { resource_name: o.resourceName } : {},
537
+ ...o.dpop === false ? {} : { dpop_signing_alg_values_supported: ["ES256", "EdDSA", "RS256", "PS256"] }
538
+ };
539
+ }
540
+ function oauthChallenge(resource, opts = {}) {
541
+ const meta = `${resource.replace(/\/$/, "")}${PROTECTED_RESOURCE_PATH}`;
542
+ const parts = [`resource_metadata="${meta}"`];
543
+ if (opts.error) parts.push(`error="${opts.error}"`);
544
+ if (opts.scope) parts.push(`scope="${opts.scope}"`);
545
+ return `Bearer ${parts.join(", ")}`;
546
+ }
547
+
321
548
  // src/server/httpSignatures.ts
322
549
  var SignatureError = class extends Error {
323
550
  constructor(code, message) {
@@ -756,61 +983,15 @@ var KNOWN_SIGNERS = {
756
983
  };
757
984
  var GENERIC_AGENT_UA = /\b(bot|crawler|spider|agent|headless|scraper)\b/i;
758
985
  var JWT_SHAPE = /^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]*$/;
759
- function bearer(request) {
986
+ function credential(request) {
760
987
  const h = request.headers.get("authorization");
761
- return h && /^bearer\s+/i.test(h) ? h.replace(/^bearer\s+/i, "").trim() : null;
762
- }
763
- function unverifiedJwtIssuer(token) {
764
- try {
765
- const payload = token.split(".")[1];
766
- const json = atob(payload.replace(/-/g, "+").replace(/_/g, "/"));
767
- const iss = JSON.parse(json).iss;
768
- return typeof iss === "string" ? iss : null;
769
- } catch {
770
- return null;
771
- }
772
- }
773
- var josePromise;
774
- var remoteSets = /* @__PURE__ */ new Map();
775
- async function verifyOAuth2(token, o) {
776
- josePromise ??= import('jose');
777
- const jose = await josePromise;
778
- const keySet = o.jwks ? jose.createLocalJWKSet(o.jwks) : (() => {
779
- const uri = o.jwksUri ?? `${o.issuer.replace(/\/$/, "")}/.well-known/jwks.json`;
780
- let s = remoteSets.get(uri);
781
- if (!s) {
782
- s = jose.createRemoteJWKSet(new URL(uri), { cacheMaxAge: 36e5, cooldownDuration: 3e4 });
783
- remoteSets.set(uri, s);
784
- }
785
- return s;
786
- })();
787
- try {
788
- const { payload } = await jose.jwtVerify(token, keySet, {
789
- issuer: o.issuer,
790
- audience: o.audience,
791
- algorithms: o.algorithms ?? ["RS256", "PS256", "ES256", "EdDSA"],
792
- clockTolerance: o.clockToleranceSec ?? 30
793
- });
794
- if (o.requiredScopes?.length) {
795
- const granted = new Set(String(payload.scope ?? payload.scp ?? "").split(/\s+/));
796
- const missing = o.requiredScopes.filter((s) => !granted.has(s));
797
- if (missing.length) return { error: `missing_scopes:${missing.join(",")}` };
798
- }
799
- const clientId = String(payload.client_id ?? payload.azp ?? payload.sub ?? "");
800
- if (!clientId) return { error: "token_has_no_client_id" };
801
- return {
802
- id: `oauth2:${clientId}`,
803
- name: typeof payload.agent_name === "string" ? payload.agent_name : clientId,
804
- vendor: typeof payload.agent_vendor === "string" ? payload.agent_vendor : null,
805
- method: "oauth2",
806
- claims: { sub: payload.sub, scope: payload.scope ?? payload.scp }
807
- };
808
- } catch (e) {
809
- return { error: `invalid_token:${e.code ?? e.message}` };
810
- }
988
+ const m = h?.match(/^(bearer|dpop)\s+(.+)$/i);
989
+ return m ? { token: m[2].trim(), scheme: m[1].toLowerCase() } : null;
811
990
  }
812
991
  function createAgentAuth(options = {}) {
813
- const wba = options.webBotAuth === false ? null : { replayCache: memoryReplayCache(), ...typeof options.webBotAuth === "object" ? options.webBotAuth : {} };
992
+ const replayCache = options.replayCache ?? memoryReplayCache();
993
+ const wba = options.webBotAuth === false ? null : { replayCache, ...typeof options.webBotAuth === "object" ? options.webBotAuth : {} };
994
+ const issuers = options.oauth2 ? Array.isArray(options.oauth2) ? options.oauth2 : [options.oauth2] : [];
814
995
  const crawlers = options.crawlers === false ? null : typeof options.crawlers === "object" ? options.crawlers : {};
815
996
  const keyPrefix = options.apiKey?.prefix ?? "agk_";
816
997
  async function authenticate(request) {
@@ -839,7 +1020,8 @@ function createAgentAuth(options = {}) {
839
1020
  claimedName ??= r.agentUrl ? new URL(r.agentUrl).hostname : void 0;
840
1021
  }
841
1022
  }
842
- const token = bearer(request) ?? request.headers.get("x-agent-key");
1023
+ const cred = credential(request);
1024
+ const token = cred?.token ?? request.headers.get("x-agent-key");
843
1025
  if (token && options.apiKey && token.startsWith(keyPrefix)) {
844
1026
  attempted.push("api-key");
845
1027
  const id = await options.apiKey.verify(token);
@@ -856,12 +1038,23 @@ function createAgentAuth(options = {}) {
856
1038
  };
857
1039
  }
858
1040
  reasons.push("api-key:unknown_or_revoked");
859
- } else if (token && options.oauth2 && JWT_SHAPE.test(token)) {
860
- const iss = unverifiedJwtIssuer(token);
861
- if (iss === options.oauth2.issuer) {
1041
+ } else if (cred && issuers.length && JWT_SHAPE.test(cred.token)) {
1042
+ const iss = unverifiedIssuer(cred.token);
1043
+ const config = issuers.find((i) => i.issuer === iss);
1044
+ if (config) {
862
1045
  attempted.push("oauth2");
863
- const r = await verifyOAuth2(token, options.oauth2);
864
- if (!("error" in r)) return { status: "verified", agent: r };
1046
+ const r = await verifyOAuthToken(cred.token, config, { request, scheme: cred.scheme, replayCache });
1047
+ if (!("error" in r)) {
1048
+ const agent = {
1049
+ id: r.id,
1050
+ name: r.name,
1051
+ vendor: r.vendor,
1052
+ method: "oauth2",
1053
+ claims: { ...r.claims, dpop: r.dpop }
1054
+ };
1055
+ if (r.onBehalfOf) agent.onBehalfOf = r.onBehalfOf;
1056
+ return { status: "verified", agent };
1057
+ }
865
1058
  reasons.push(`oauth2:${r.error}`);
866
1059
  }
867
1060
  }
@@ -871,11 +1064,11 @@ function createAgentAuth(options = {}) {
871
1064
  claimedName ??= claim.name;
872
1065
  if (crawlers) {
873
1066
  attempted.push("verified-crawler");
874
- const v = await verifyCrawler(claim, clientIp(request, crawlers.clientIp), crawlers.resolver);
1067
+ const v = await verifyCrawler(claim, clientIp(request, crawlers.clientIp), crawlers.resolver, crawlers);
875
1068
  if (v.status === "verified") {
876
1069
  return {
877
1070
  status: "verified",
878
- agent: { id: `crawler:${claim.name}`, name: claim.name, vendor: null, method: "verified-crawler", claims: { host: v.host } }
1071
+ agent: { id: `crawler:${claim.name}`, name: claim.name, vendor: null, method: "verified-crawler", claims: { host: v.host, via: v.via } }
879
1072
  };
880
1073
  }
881
1074
  reasons.push(`crawler:${v.reason}`);
@@ -936,6 +1129,7 @@ function toTraceEvent(result, ctx) {
936
1129
  if (result.status === "none") return null;
937
1130
  const url = new URL(ctx.request.url);
938
1131
  const verified = result.status === "verified";
1132
+ const delegated = verified && Boolean(result.agent.onBehalfOf);
939
1133
  return {
940
1134
  id: `evt_${crypto.randomUUID()}`,
941
1135
  siteId: ctx.siteId,
@@ -944,12 +1138,13 @@ function toTraceEvent(result, ctx) {
944
1138
  type: "auth.identity_presented",
945
1139
  agent: {
946
1140
  class: "crawler",
947
- trust: verified ? "verified" : "declared",
1141
+ // Unverified claims stay "declared"; a token acting for a signed-in user is "linked".
1142
+ trust: delegated ? "linked" : verified ? "verified" : "declared",
948
1143
  confidence: verified ? 1 : 0.5,
949
1144
  vendor: verified ? result.agent.vendor ?? result.agent.name : result.claimedName ?? null,
950
1145
  userAgent: ctx.request.headers.get("user-agent"),
951
1146
  detectionVersion: "2026.09",
952
- signals: { surface: "server" }
1147
+ signals: { surface: "server", ...verified ? { method: result.agent.method } : {} }
953
1148
  },
954
1149
  outcome: verified ? "success" : "error",
955
1150
  ...verified ? {} : { error: result.reason },
@@ -1000,6 +1195,6 @@ function expressAgentAuth(options = {}) {
1000
1195
  };
1001
1196
  }
1002
1197
 
1003
- export { AGENT_HEADERS, DIRECTORY_PATH, RDNS_DOMAINS, WEB_BOT_AUTH_TAG, classifyUserAgent, clientIp, createAgentAuth, createAgentAuthHandler, expressAgentAuth, generateAgentKey, hashAgentKey, jwkThumbprint, memoryReplayCache, readAgentHeaders, signatureBase, staticKeyVerifier, toTraceEvent, verifyCrawler, verifyWebBotAuth, withAgentHeaders };
1004
- //# sourceMappingURL=chunk-X7NFMFN6.js.map
1005
- //# sourceMappingURL=chunk-X7NFMFN6.js.map
1198
+ export { AGENT_HEADERS, DIRECTORY_PATH, PROTECTED_RESOURCE_PATH, PUBLISHED_IP_RANGES, RDNS_DOMAINS, WEB_BOT_AUTH_TAG, classifyUserAgent, clientIp, createAgentAuth, createAgentAuthHandler, expressAgentAuth, generateAgentKey, hashAgentKey, ipInCidr, ipInRanges, jwkThumbprint, memoryReplayCache, oauthChallenge, protectedResourceMetadata, rangesFor, readAgentHeaders, signatureBase, staticKeyVerifier, toTraceEvent, verifyCrawler, verifyOAuthToken, verifyWebBotAuth, withAgentHeaders };
1199
+ //# sourceMappingURL=chunk-6QQS26AP.js.map
1200
+ //# sourceMappingURL=chunk-6QQS26AP.js.map