@agentronics/sdk 0.6.0 → 0.7.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 (53) hide show
  1. package/dist/chunk-3WPDIMJP.js +18 -0
  2. package/dist/chunk-3WPDIMJP.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 +39 -7
  10. package/dist/index.d.ts +39 -7
  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
package/dist/next.js CHANGED
@@ -1,5 +1,5 @@
1
- import { createAgentAuthHandler } from './chunk-X7NFMFN6.js';
2
- export { readAgentHeaders, withAgentHeaders } from './chunk-X7NFMFN6.js';
1
+ import { createAgentAuthHandler } from './chunk-6QQS26AP.js';
2
+ export { readAgentHeaders, withAgentHeaders } from './chunk-6QQS26AP.js';
3
3
 
4
4
  // src/server/next.ts
5
5
  function agentronicsMiddleware(options = {}) {
package/dist/server.cjs CHANGED
@@ -6,9 +6,12 @@ var CRAWLER_SIGNATURES = [
6
6
  { name: "GPTBot", category: "ai", pattern: /GPTBot/i },
7
7
  { name: "OAI-SearchBot", category: "ai", pattern: /OAI-SearchBot/i },
8
8
  { name: "ChatGPT-User", category: "ai", pattern: /ChatGPT-User/i },
9
+ { name: "Claude-User", category: "ai", pattern: /Claude-User/i },
10
+ { name: "Claude-SearchBot", category: "ai", pattern: /Claude-SearchBot/i },
9
11
  { name: "ClaudeBot", category: "ai", pattern: /ClaudeBot/i },
10
12
  { name: "Claude-Web", category: "ai", pattern: /Claude-Web/i },
11
13
  { name: "anthropic-ai", category: "ai", pattern: /anthropic-ai/i },
14
+ { name: "Perplexity-User", category: "ai", pattern: /Perplexity-User/i },
12
15
  { name: "PerplexityBot", category: "ai", pattern: /PerplexityBot/i },
13
16
  { name: "Google-Extended", category: "ai", pattern: /Google-Extended/i },
14
17
  { name: "Applebot-Extended", category: "ai", pattern: /Applebot-Extended/i },
@@ -26,6 +29,96 @@ var CRAWLER_SIGNATURES = [
26
29
  { name: "Applebot", category: "search", pattern: /Applebot/i }
27
30
  ];
28
31
 
32
+ // src/server/ipRanges.ts
33
+ var PUBLISHED_IP_RANGES = {
34
+ GPTBot: ["https://openai.com/gptbot.json"],
35
+ "OAI-SearchBot": ["https://openai.com/searchbot.json"],
36
+ "ChatGPT-User": ["https://openai.com/chatgpt-user.json"],
37
+ PerplexityBot: ["https://www.perplexity.ai/perplexitybot.json"],
38
+ "Perplexity-User": ["https://www.perplexity.ai/perplexity-user.json"],
39
+ Googlebot: ["https://developers.google.com/static/crawling/ipranges/common-crawlers.json"],
40
+ Bingbot: ["https://www.bing.com/toolbox/bingbot.json"],
41
+ DuckDuckBot: ["https://duckduckgo.com/duckduckbot.json"],
42
+ CCBot: ["https://index.commoncrawl.org/ccbot.json"]
43
+ };
44
+ var TTL_MS = 24 * 36e5;
45
+ var FAILED_TTL_MS = 10 * 6e4;
46
+ var cache = /* @__PURE__ */ new Map();
47
+ async function loadUrl(url, fetcher) {
48
+ const hit = cache.get(url);
49
+ if (hit && hit.until > Date.now()) return hit.cidrs;
50
+ try {
51
+ const res = await fetcher(url, { signal: AbortSignal.timeout(5e3), redirect: "follow" });
52
+ if (!res.ok) throw new Error(String(res.status));
53
+ const body = await res.json();
54
+ const cidrs = (body.prefixes ?? []).flatMap((p) => [p.ipv4Prefix, p.ipv6Prefix]).filter((c) => typeof c === "string");
55
+ cache.set(url, { cidrs, until: Date.now() + TTL_MS });
56
+ return cidrs;
57
+ } catch {
58
+ const cidrs = hit?.cidrs ?? [];
59
+ cache.set(url, { cidrs, until: Date.now() + FAILED_TTL_MS });
60
+ return cidrs;
61
+ }
62
+ }
63
+ async function rangesFor(name, options = {}) {
64
+ const fixed = options.ipRanges?.[name];
65
+ if (fixed) return fixed;
66
+ const urls = PUBLISHED_IP_RANGES[name];
67
+ if (!urls) return null;
68
+ const fetcher = options.fetcher ?? fetch;
69
+ return (await Promise.all(urls.map((u) => loadUrl(u, fetcher)))).flat();
70
+ }
71
+ function v4ToBigInt(ip) {
72
+ const parts = ip.split(".");
73
+ if (parts.length !== 4) return null;
74
+ let n = 0n;
75
+ for (const p of parts) {
76
+ if (!/^\d{1,3}$/.test(p) || Number(p) > 255) return null;
77
+ n = n << 8n | BigInt(p);
78
+ }
79
+ return n;
80
+ }
81
+ function v6ToBigInt(ip) {
82
+ let s = ip.toLowerCase().replace(/^\[|\]$/g, "");
83
+ const zone = s.indexOf("%");
84
+ if (zone >= 0) s = s.slice(0, zone);
85
+ const v4 = s.match(/(\d+\.\d+\.\d+\.\d+)$/);
86
+ if (v4) {
87
+ const n2 = v4ToBigInt(v4[1]);
88
+ if (n2 === null) return null;
89
+ s = s.slice(0, -v4[1].length) + (n2 >> 16n & 0xffffn).toString(16) + ":" + (n2 & 0xffffn).toString(16);
90
+ }
91
+ const halves = s.split("::");
92
+ if (halves.length > 2) return null;
93
+ const head = halves[0] ? halves[0].split(":") : [];
94
+ const tail = halves.length === 2 && halves[1] ? halves[1].split(":") : [];
95
+ const missing = 8 - head.length - tail.length;
96
+ if (halves.length === 1 ? missing !== 0 : missing < 1) return null;
97
+ const groups = [...head, ...Array(halves.length === 2 ? missing : 0).fill("0"), ...tail];
98
+ let n = 0n;
99
+ for (const g of groups) {
100
+ if (!/^[0-9a-f]{1,4}$/.test(g)) return null;
101
+ n = n << 16n | BigInt(parseInt(g, 16));
102
+ }
103
+ return n;
104
+ }
105
+ function ipInCidr(ip, cidr) {
106
+ const [range, bitsRaw] = cidr.split("/");
107
+ if (!range) return false;
108
+ const isV6 = range.includes(":");
109
+ const mapped = ip.toLowerCase().match(/^::ffff:(\d+\.\d+\.\d+\.\d+)$/);
110
+ const candidate = !isV6 && mapped ? mapped[1] : ip;
111
+ const width = isV6 ? 128 : 32;
112
+ const bits = bitsRaw === void 0 ? width : Number(bitsRaw);
113
+ if (!Number.isInteger(bits) || bits < 0 || bits > width) return false;
114
+ const a = isV6 ? v6ToBigInt(candidate) : v4ToBigInt(candidate);
115
+ const b = isV6 ? v6ToBigInt(range) : v4ToBigInt(range);
116
+ if (a === null || b === null) return false;
117
+ const shift = BigInt(width - bits);
118
+ return a >> shift === b >> shift;
119
+ }
120
+ var ipInRanges = (ip, cidrs) => cidrs.some((c) => ipInCidr(ip, c));
121
+
29
122
  // src/server/crawlers.ts
30
123
  var RDNS_DOMAINS = {
31
124
  Googlebot: ["googlebot.com", "google.com", "googleusercontent.com"],
@@ -64,14 +157,19 @@ async function nodeResolver() {
64
157
  }
65
158
  }
66
159
  var rdnsCache = /* @__PURE__ */ new Map();
67
- async function verifyCrawler(claim, ip, resolver) {
160
+ async function verifyCrawler(claim, ip, resolver, ranges = {}) {
68
161
  const domains = RDNS_DOMAINS[claim.name];
69
- if (!domains) return { status: "unverified", name: claim.name, reason: "no_rdns_method_for_vendor" };
162
+ const cidrs = await rangesFor(claim.name, ranges);
163
+ if (!domains && !cidrs) return { status: "unverified", name: claim.name, reason: "no_verification_method_for_vendor" };
70
164
  if (!ip) return { status: "unverified", name: claim.name, reason: "client_ip_unknown" };
165
+ if (cidrs?.length && ipInRanges(ip, cidrs)) {
166
+ return { status: "verified", name: claim.name, host: ip, ip, via: "ip-range" };
167
+ }
168
+ if (!domains) return { status: "unverified", name: claim.name, reason: "ip_outside_published_ranges" };
71
169
  const key = `${claim.name}|${ip}`;
72
170
  const hit = rdnsCache.get(key);
73
171
  if (hit && hit.until > Date.now()) {
74
- return hit.ok ? { status: "verified", name: claim.name, host: hit.host, ip } : { status: "unverified", name: claim.name, reason: "rdns_mismatch" };
172
+ return hit.ok ? { status: "verified", name: claim.name, host: hit.host, ip, via: "rdns" } : { status: "unverified", name: claim.name, reason: "rdns_mismatch" };
75
173
  }
76
174
  const dns = resolver ?? await nodeResolver();
77
175
  if (!dns) return { status: "unverified", name: claim.name, reason: "dns_unavailable_in_runtime" };
@@ -87,7 +185,7 @@ async function verifyCrawler(claim, ip, resolver) {
87
185
  const forward = await dns.lookup(host);
88
186
  if (forward.includes(ip)) {
89
187
  remember(true, host);
90
- return { status: "verified", name: claim.name, host, ip };
188
+ return { status: "verified", name: claim.name, host, ip, via: "rdns" };
91
189
  }
92
190
  }
93
191
  remember(false);
@@ -320,6 +418,135 @@ function bytesToBase64(bytes) {
320
418
  }
321
419
  var bytesToBase64Url = (bytes) => bytesToBase64(bytes).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
322
420
 
421
+ // src/server/oauth.ts
422
+ var josePromise;
423
+ var loadJose = () => josePromise ??= import('jose');
424
+ var remoteSets = /* @__PURE__ */ new Map();
425
+ var ASYMMETRIC = ["RS256", "PS256", "ES256", "EdDSA"];
426
+ function unverifiedIssuer(token) {
427
+ try {
428
+ const payload = token.split(".")[1];
429
+ const json = atob(payload.replace(/-/g, "+").replace(/_/g, "/"));
430
+ const iss = JSON.parse(json).iss;
431
+ return typeof iss === "string" ? iss : null;
432
+ } catch {
433
+ return null;
434
+ }
435
+ }
436
+ var str = (v) => typeof v === "string" && v ? v : void 0;
437
+ async function sha256B64u(text) {
438
+ const d = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(text));
439
+ return bytesToBase64Url(new Uint8Array(d));
440
+ }
441
+ var htuOf = (url) => {
442
+ const u = new URL(url);
443
+ return `${u.origin}${u.pathname}`;
444
+ };
445
+ async function verifyDpopProof(jose, proof, token, jkt, request, replay) {
446
+ try {
447
+ const { payload, protectedHeader } = await jose.jwtVerify(proof, jose.EmbeddedJWK, {
448
+ typ: "dpop+jwt",
449
+ algorithms: ASYMMETRIC
450
+ });
451
+ const jwk = protectedHeader.jwk;
452
+ if (!jwk) return "dpop_missing_jwk";
453
+ if (await jose.calculateJwkThumbprint(jwk, "sha256") !== jkt) return "dpop_key_mismatch";
454
+ if (str(payload.htm)?.toUpperCase() !== request.method.toUpperCase()) return "dpop_htm_mismatch";
455
+ if (str(payload.htu) === void 0 || htuOf(str(payload.htu)) !== htuOf(request.url)) return "dpop_htu_mismatch";
456
+ const iat = typeof payload.iat === "number" ? payload.iat : NaN;
457
+ if (!Number.isFinite(iat) || Math.abs(Date.now() / 1e3 - iat) > 300) return "dpop_stale";
458
+ if (str(payload.ath) !== await sha256B64u(token)) return "dpop_ath_mismatch";
459
+ const jti = str(payload.jti);
460
+ if (!jti) return "dpop_missing_jti";
461
+ if (replay) {
462
+ const key = `dpop:${jkt}:${jti}`;
463
+ if (await replay.has(key)) return "dpop_replayed";
464
+ await replay.add(key, 600);
465
+ }
466
+ return null;
467
+ } catch (e) {
468
+ return `dpop_invalid:${e.code ?? e.message}`;
469
+ }
470
+ }
471
+ async function verifyOAuthToken(token, o, ctx) {
472
+ const jose = await loadJose();
473
+ const keySet = o.jwks ? jose.createLocalJWKSet(o.jwks) : (() => {
474
+ const uri = o.jwksUri ?? `${o.issuer.replace(/\/$/, "")}/.well-known/jwks.json`;
475
+ let s = remoteSets.get(uri);
476
+ if (!s) {
477
+ s = jose.createRemoteJWKSet(new URL(uri), { cacheMaxAge: 36e5, cooldownDuration: 3e4 });
478
+ remoteSets.set(uri, s);
479
+ }
480
+ return s;
481
+ })();
482
+ let payload;
483
+ try {
484
+ ;
485
+ ({ payload } = await jose.jwtVerify(token, keySet, {
486
+ issuer: o.issuer,
487
+ audience: o.audience,
488
+ algorithms: o.algorithms ?? ASYMMETRIC,
489
+ clockTolerance: o.clockToleranceSec ?? 30
490
+ }));
491
+ } catch (e) {
492
+ return { error: `invalid_token:${e.code ?? e.message}` };
493
+ }
494
+ const jkt = str(payload.cnf?.jkt);
495
+ let dpop = false;
496
+ if (jkt) {
497
+ const proof = ctx.request.headers.get("dpop");
498
+ if (ctx.scheme !== "dpop" || !proof) return { error: "dpop_proof_required" };
499
+ const bad = await verifyDpopProof(jose, proof, token, jkt, ctx.request, ctx.replayCache);
500
+ if (bad) return { error: bad };
501
+ dpop = true;
502
+ } else if (o.requireDpop) {
503
+ return { error: "token_not_dpop_bound" };
504
+ }
505
+ if (o.requiredScopes?.length) {
506
+ const granted = new Set(String(payload.scope ?? payload.scp ?? "").split(/\s+/));
507
+ const missing = o.requiredScopes.filter((s) => !granted.has(s));
508
+ if (missing.length) return { error: `missing_scopes:${missing.join(",")}` };
509
+ }
510
+ const act = payload.act;
511
+ const clientId = str(payload.client_id) ?? str(payload.azp);
512
+ const sub = str(payload.sub);
513
+ const agentId = str(act?.client_id) ?? str(act?.sub) ?? clientId ?? sub;
514
+ if (!agentId) return { error: "token_has_no_client_id" };
515
+ const user = act ? sub : sub && clientId && sub !== clientId ? sub : void 0;
516
+ const agent = {
517
+ id: `oauth2:${agentId}`,
518
+ name: str(payload.agent_name) ?? str(payload.client_name) ?? agentId,
519
+ vendor: str(payload.agent_vendor) ?? null,
520
+ claims: { iss: payload.iss, sub, scope: payload.scope ?? payload.scp, ...act ? { act } : {} },
521
+ dpop
522
+ };
523
+ if (user) {
524
+ const onBehalfOf = { sub: user };
525
+ if (str(payload.email)) onBehalfOf.email = str(payload.email);
526
+ if (str(payload.name)) onBehalfOf.name = str(payload.name);
527
+ agent.onBehalfOf = onBehalfOf;
528
+ }
529
+ return agent;
530
+ }
531
+ var PROTECTED_RESOURCE_PATH = "/.well-known/oauth-protected-resource";
532
+ function protectedResourceMetadata(o) {
533
+ return {
534
+ resource: o.resource,
535
+ authorization_servers: o.authorizationServers,
536
+ bearer_methods_supported: ["header"],
537
+ ...o.scopesSupported ? { scopes_supported: o.scopesSupported } : {},
538
+ ...o.resourceName ? { resource_name: o.resourceName } : {},
539
+ ...o.dpop === false ? {} : { dpop_signing_alg_values_supported: ["ES256", "EdDSA", "RS256", "PS256"] }
540
+ };
541
+ }
542
+ function oauthChallenge(resource, opts = {}) {
543
+ const meta = `${resource.replace(/\/$/, "")}${PROTECTED_RESOURCE_PATH}`;
544
+ const parts = [`resource_metadata="${meta}"`];
545
+ if (opts.error) parts.push(`error="${opts.error}"`);
546
+ if (opts.scope) parts.push(`scope="${opts.scope}"`);
547
+ return `Bearer ${parts.join(", ")}`;
548
+ }
549
+
323
550
  // src/server/httpSignatures.ts
324
551
  var SignatureError = class extends Error {
325
552
  constructor(code, message) {
@@ -758,61 +985,15 @@ var KNOWN_SIGNERS = {
758
985
  };
759
986
  var GENERIC_AGENT_UA = /\b(bot|crawler|spider|agent|headless|scraper)\b/i;
760
987
  var JWT_SHAPE = /^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]*$/;
761
- function bearer(request) {
988
+ function credential(request) {
762
989
  const h = request.headers.get("authorization");
763
- return h && /^bearer\s+/i.test(h) ? h.replace(/^bearer\s+/i, "").trim() : null;
764
- }
765
- function unverifiedJwtIssuer(token) {
766
- try {
767
- const payload = token.split(".")[1];
768
- const json = atob(payload.replace(/-/g, "+").replace(/_/g, "/"));
769
- const iss = JSON.parse(json).iss;
770
- return typeof iss === "string" ? iss : null;
771
- } catch {
772
- return null;
773
- }
774
- }
775
- var josePromise;
776
- var remoteSets = /* @__PURE__ */ new Map();
777
- async function verifyOAuth2(token, o) {
778
- josePromise ??= import('jose');
779
- const jose = await josePromise;
780
- const keySet = o.jwks ? jose.createLocalJWKSet(o.jwks) : (() => {
781
- const uri = o.jwksUri ?? `${o.issuer.replace(/\/$/, "")}/.well-known/jwks.json`;
782
- let s = remoteSets.get(uri);
783
- if (!s) {
784
- s = jose.createRemoteJWKSet(new URL(uri), { cacheMaxAge: 36e5, cooldownDuration: 3e4 });
785
- remoteSets.set(uri, s);
786
- }
787
- return s;
788
- })();
789
- try {
790
- const { payload } = await jose.jwtVerify(token, keySet, {
791
- issuer: o.issuer,
792
- audience: o.audience,
793
- algorithms: o.algorithms ?? ["RS256", "PS256", "ES256", "EdDSA"],
794
- clockTolerance: o.clockToleranceSec ?? 30
795
- });
796
- if (o.requiredScopes?.length) {
797
- const granted = new Set(String(payload.scope ?? payload.scp ?? "").split(/\s+/));
798
- const missing = o.requiredScopes.filter((s) => !granted.has(s));
799
- if (missing.length) return { error: `missing_scopes:${missing.join(",")}` };
800
- }
801
- const clientId = String(payload.client_id ?? payload.azp ?? payload.sub ?? "");
802
- if (!clientId) return { error: "token_has_no_client_id" };
803
- return {
804
- id: `oauth2:${clientId}`,
805
- name: typeof payload.agent_name === "string" ? payload.agent_name : clientId,
806
- vendor: typeof payload.agent_vendor === "string" ? payload.agent_vendor : null,
807
- method: "oauth2",
808
- claims: { sub: payload.sub, scope: payload.scope ?? payload.scp }
809
- };
810
- } catch (e) {
811
- return { error: `invalid_token:${e.code ?? e.message}` };
812
- }
990
+ const m = h?.match(/^(bearer|dpop)\s+(.+)$/i);
991
+ return m ? { token: m[2].trim(), scheme: m[1].toLowerCase() } : null;
813
992
  }
814
993
  function createAgentAuth(options = {}) {
815
- const wba = options.webBotAuth === false ? null : { replayCache: memoryReplayCache(), ...typeof options.webBotAuth === "object" ? options.webBotAuth : {} };
994
+ const replayCache = options.replayCache ?? memoryReplayCache();
995
+ const wba = options.webBotAuth === false ? null : { replayCache, ...typeof options.webBotAuth === "object" ? options.webBotAuth : {} };
996
+ const issuers = options.oauth2 ? Array.isArray(options.oauth2) ? options.oauth2 : [options.oauth2] : [];
816
997
  const crawlers = options.crawlers === false ? null : typeof options.crawlers === "object" ? options.crawlers : {};
817
998
  const keyPrefix = options.apiKey?.prefix ?? "agk_";
818
999
  async function authenticate(request) {
@@ -841,7 +1022,8 @@ function createAgentAuth(options = {}) {
841
1022
  claimedName ??= r.agentUrl ? new URL(r.agentUrl).hostname : void 0;
842
1023
  }
843
1024
  }
844
- const token = bearer(request) ?? request.headers.get("x-agent-key");
1025
+ const cred = credential(request);
1026
+ const token = cred?.token ?? request.headers.get("x-agent-key");
845
1027
  if (token && options.apiKey && token.startsWith(keyPrefix)) {
846
1028
  attempted.push("api-key");
847
1029
  const id = await options.apiKey.verify(token);
@@ -858,12 +1040,23 @@ function createAgentAuth(options = {}) {
858
1040
  };
859
1041
  }
860
1042
  reasons.push("api-key:unknown_or_revoked");
861
- } else if (token && options.oauth2 && JWT_SHAPE.test(token)) {
862
- const iss = unverifiedJwtIssuer(token);
863
- if (iss === options.oauth2.issuer) {
1043
+ } else if (cred && issuers.length && JWT_SHAPE.test(cred.token)) {
1044
+ const iss = unverifiedIssuer(cred.token);
1045
+ const config = issuers.find((i) => i.issuer === iss);
1046
+ if (config) {
864
1047
  attempted.push("oauth2");
865
- const r = await verifyOAuth2(token, options.oauth2);
866
- if (!("error" in r)) return { status: "verified", agent: r };
1048
+ const r = await verifyOAuthToken(cred.token, config, { request, scheme: cred.scheme, replayCache });
1049
+ if (!("error" in r)) {
1050
+ const agent = {
1051
+ id: r.id,
1052
+ name: r.name,
1053
+ vendor: r.vendor,
1054
+ method: "oauth2",
1055
+ claims: { ...r.claims, dpop: r.dpop }
1056
+ };
1057
+ if (r.onBehalfOf) agent.onBehalfOf = r.onBehalfOf;
1058
+ return { status: "verified", agent };
1059
+ }
867
1060
  reasons.push(`oauth2:${r.error}`);
868
1061
  }
869
1062
  }
@@ -873,11 +1066,11 @@ function createAgentAuth(options = {}) {
873
1066
  claimedName ??= claim.name;
874
1067
  if (crawlers) {
875
1068
  attempted.push("verified-crawler");
876
- const v = await verifyCrawler(claim, clientIp(request, crawlers.clientIp), crawlers.resolver);
1069
+ const v = await verifyCrawler(claim, clientIp(request, crawlers.clientIp), crawlers.resolver, crawlers);
877
1070
  if (v.status === "verified") {
878
1071
  return {
879
1072
  status: "verified",
880
- agent: { id: `crawler:${claim.name}`, name: claim.name, vendor: null, method: "verified-crawler", claims: { host: v.host } }
1073
+ agent: { id: `crawler:${claim.name}`, name: claim.name, vendor: null, method: "verified-crawler", claims: { host: v.host, via: v.via } }
881
1074
  };
882
1075
  }
883
1076
  reasons.push(`crawler:${v.reason}`);
@@ -938,6 +1131,7 @@ function toTraceEvent(result, ctx) {
938
1131
  if (result.status === "none") return null;
939
1132
  const url = new URL(ctx.request.url);
940
1133
  const verified = result.status === "verified";
1134
+ const delegated = verified && Boolean(result.agent.onBehalfOf);
941
1135
  return {
942
1136
  id: `evt_${crypto.randomUUID()}`,
943
1137
  siteId: ctx.siteId,
@@ -946,12 +1140,13 @@ function toTraceEvent(result, ctx) {
946
1140
  type: "auth.identity_presented",
947
1141
  agent: {
948
1142
  class: "crawler",
949
- trust: verified ? "verified" : "declared",
1143
+ // Unverified claims stay "declared"; a token acting for a signed-in user is "linked".
1144
+ trust: delegated ? "linked" : verified ? "verified" : "declared",
950
1145
  confidence: verified ? 1 : 0.5,
951
1146
  vendor: verified ? result.agent.vendor ?? result.agent.name : result.claimedName ?? null,
952
1147
  userAgent: ctx.request.headers.get("user-agent"),
953
1148
  detectionVersion: "2026.09",
954
- signals: { surface: "server" }
1149
+ signals: { surface: "server", ...verified ? { method: result.agent.method } : {} }
955
1150
  },
956
1151
  outcome: verified ? "success" : "error",
957
1152
  ...verified ? {} : { error: result.reason },
@@ -1002,24 +1197,124 @@ function expressAgentAuth(options = {}) {
1002
1197
  };
1003
1198
  }
1004
1199
 
1200
+ // src/observability/exporters/intel.ts
1201
+ var INGEST_PATH = "/v1/sdk/events";
1202
+ var createIntelExporter = ({
1203
+ url,
1204
+ ingestKey,
1205
+ fetcher = fetch
1206
+ }) => {
1207
+ const base = url.replace(/\/$/, "");
1208
+ const endpoint = base.endsWith(INGEST_PATH) ? base : `${base}${INGEST_PATH}`;
1209
+ return {
1210
+ name: "intel",
1211
+ async export(events) {
1212
+ const batch = { events };
1213
+ const response = await fetcher(endpoint, {
1214
+ method: "POST",
1215
+ headers: {
1216
+ "content-type": "application/json",
1217
+ authorization: `Bearer ${ingestKey}`
1218
+ },
1219
+ body: JSON.stringify(batch)
1220
+ });
1221
+ if (!response.ok) {
1222
+ throw new Error(`Intel exporter failed with status ${response.status}.`);
1223
+ }
1224
+ }
1225
+ };
1226
+ };
1227
+
1228
+ // src/server/relay.ts
1229
+ var KNOWN_TYPES = /* @__PURE__ */ new Set([
1230
+ "agent.detected",
1231
+ "agent.missed",
1232
+ "auth.identity_presented",
1233
+ "auth.identity_cleared",
1234
+ "authz.policies_set",
1235
+ "authz.evaluated",
1236
+ "memory.accessed",
1237
+ "memory.updated",
1238
+ "tool.registered",
1239
+ "tool.executed",
1240
+ "tool.surfaced",
1241
+ "tool.progressed",
1242
+ "sdk.error"
1243
+ ]);
1244
+ function createTraceRelay(options) {
1245
+ const exporter = createIntelExporter({ url: options.url, ingestKey: options.ingestKey, ...options.fetcher ? { fetcher: options.fetcher } : {} });
1246
+ const max = options.maxEvents ?? 100;
1247
+ return async function relay(request) {
1248
+ if (request.method !== "POST") return new Response(null, { status: 405 });
1249
+ let body;
1250
+ try {
1251
+ const text = await request.text();
1252
+ if (text.length > 512e3) return new Response(null, { status: 413 });
1253
+ body = JSON.parse(text);
1254
+ } catch {
1255
+ return new Response(null, { status: 400 });
1256
+ }
1257
+ const events = (Array.isArray(body.events) ? body.events : []).filter((e) => typeof e === "object" && e !== null && KNOWN_TYPES.has(String(e.type))).slice(0, max).map((e) => ({ ...e, siteId: options.siteId }));
1258
+ if (events.length) {
1259
+ await Promise.all([
1260
+ Promise.resolve(exporter.export(events)).catch(() => void 0),
1261
+ options.forward ? Promise.resolve().then(() => options.forward(events)).catch(() => void 0) : void 0
1262
+ ]);
1263
+ }
1264
+ return new Response(null, { status: 202 });
1265
+ };
1266
+ }
1267
+
1268
+ // src/server/agentContext.ts
1269
+ function createAgentContextHandler({ sync, cacheSeconds = 300 }) {
1270
+ let cached = null;
1271
+ return async function agentContext() {
1272
+ if (!cached || cached.until <= Date.now()) {
1273
+ try {
1274
+ const { siteMemory } = await sync.pullConfig();
1275
+ if (siteMemory && Object.keys(siteMemory).length) cached = { body: JSON.stringify(siteMemory), until: Date.now() + cacheSeconds * 1e3 };
1276
+ } catch {
1277
+ }
1278
+ }
1279
+ if (!cached) return new Response(JSON.stringify({ error: "no_site_memory" }), { status: 404, headers: { "content-type": "application/json" } });
1280
+ return new Response(cached.body, {
1281
+ headers: {
1282
+ "content-type": "application/json",
1283
+ "cache-control": `public, max-age=${cacheSeconds}`,
1284
+ "access-control-allow-origin": "*"
1285
+ }
1286
+ });
1287
+ };
1288
+ }
1289
+
1005
1290
  exports.AGENT_HEADERS = AGENT_HEADERS;
1006
1291
  exports.DIRECTORY_PATH = DIRECTORY_PATH;
1292
+ exports.PROTECTED_RESOURCE_PATH = PROTECTED_RESOURCE_PATH;
1293
+ exports.PUBLISHED_IP_RANGES = PUBLISHED_IP_RANGES;
1007
1294
  exports.RDNS_DOMAINS = RDNS_DOMAINS;
1008
1295
  exports.WEB_BOT_AUTH_TAG = WEB_BOT_AUTH_TAG;
1009
1296
  exports.classifyUserAgent = classifyUserAgent;
1010
1297
  exports.clientIp = clientIp;
1011
1298
  exports.createAgentAuth = createAgentAuth;
1012
1299
  exports.createAgentAuthHandler = createAgentAuthHandler;
1300
+ exports.createAgentContextHandler = createAgentContextHandler;
1301
+ exports.createTraceRelay = createTraceRelay;
1013
1302
  exports.expressAgentAuth = expressAgentAuth;
1014
1303
  exports.generateAgentKey = generateAgentKey;
1015
1304
  exports.hashAgentKey = hashAgentKey;
1305
+ exports.ipInCidr = ipInCidr;
1306
+ exports.ipInRanges = ipInRanges;
1016
1307
  exports.jwkThumbprint = jwkThumbprint;
1017
1308
  exports.memoryReplayCache = memoryReplayCache;
1309
+ exports.oauthChallenge = oauthChallenge;
1310
+ exports.protectedResourceMetadata = protectedResourceMetadata;
1311
+ exports.rangesFor = rangesFor;
1018
1312
  exports.readAgentHeaders = readAgentHeaders;
1019
1313
  exports.signatureBase = signatureBase;
1020
1314
  exports.staticKeyVerifier = staticKeyVerifier;
1021
1315
  exports.toTraceEvent = toTraceEvent;
1022
1316
  exports.verifyCrawler = verifyCrawler;
1317
+ exports.verifyOAuthToken = verifyOAuthToken;
1023
1318
  exports.verifyWebBotAuth = verifyWebBotAuth;
1024
1319
  exports.withAgentHeaders = withAgentHeaders;
1025
1320
  //# sourceMappingURL=server.cjs.map