@kya-os/agentshield-nextjs 0.1.41 → 0.1.43

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 (79) hide show
  1. package/dist/.tsbuildinfo +1 -0
  2. package/dist/api-client.d.mts +145 -0
  3. package/dist/api-client.d.ts +145 -0
  4. package/dist/api-client.js +130 -0
  5. package/dist/api-client.js.map +1 -0
  6. package/dist/api-client.mjs +126 -0
  7. package/dist/api-client.mjs.map +1 -0
  8. package/dist/api-middleware.d.mts +118 -0
  9. package/dist/api-middleware.d.ts +118 -0
  10. package/dist/api-middleware.js +295 -0
  11. package/dist/api-middleware.js.map +1 -0
  12. package/dist/api-middleware.mjs +292 -0
  13. package/dist/api-middleware.mjs.map +1 -0
  14. package/dist/create-middleware.d.mts +2 -1
  15. package/dist/create-middleware.d.ts +2 -1
  16. package/dist/create-middleware.js +474 -200
  17. package/dist/create-middleware.js.map +1 -1
  18. package/dist/create-middleware.mjs +454 -200
  19. package/dist/create-middleware.mjs.map +1 -1
  20. package/dist/edge/index.d.mts +110 -0
  21. package/dist/edge/index.d.ts +110 -0
  22. package/dist/edge/index.js +253 -0
  23. package/dist/edge/index.js.map +1 -0
  24. package/dist/edge/index.mjs +251 -0
  25. package/dist/edge/index.mjs.map +1 -0
  26. package/dist/edge-detector-wrapper.d.mts +6 -15
  27. package/dist/edge-detector-wrapper.d.ts +6 -15
  28. package/dist/edge-detector-wrapper.js +314 -95
  29. package/dist/edge-detector-wrapper.js.map +1 -1
  30. package/dist/edge-detector-wrapper.mjs +294 -95
  31. package/dist/edge-detector-wrapper.mjs.map +1 -1
  32. package/dist/edge-runtime-loader.d.mts +1 -1
  33. package/dist/edge-runtime-loader.d.ts +1 -1
  34. package/dist/edge-runtime-loader.js +10 -25
  35. package/dist/edge-runtime-loader.js.map +1 -1
  36. package/dist/edge-runtime-loader.mjs +11 -23
  37. package/dist/edge-runtime-loader.mjs.map +1 -1
  38. package/dist/edge-wasm-middleware.js +2 -1
  39. package/dist/edge-wasm-middleware.js.map +1 -1
  40. package/dist/edge-wasm-middleware.mjs +2 -1
  41. package/dist/edge-wasm-middleware.mjs.map +1 -1
  42. package/dist/enhanced-middleware.d.mts +153 -0
  43. package/dist/enhanced-middleware.d.ts +153 -0
  44. package/dist/enhanced-middleware.js +1074 -0
  45. package/dist/enhanced-middleware.js.map +1 -0
  46. package/dist/enhanced-middleware.mjs +1072 -0
  47. package/dist/enhanced-middleware.mjs.map +1 -0
  48. package/dist/index.d.mts +8 -153
  49. package/dist/index.d.ts +8 -153
  50. package/dist/index.js +821 -233
  51. package/dist/index.js.map +1 -1
  52. package/dist/index.mjs +797 -234
  53. package/dist/index.mjs.map +1 -1
  54. package/dist/middleware.d.mts +2 -1
  55. package/dist/middleware.d.ts +2 -1
  56. package/dist/middleware.js +474 -200
  57. package/dist/middleware.js.map +1 -1
  58. package/dist/middleware.mjs +454 -200
  59. package/dist/middleware.mjs.map +1 -1
  60. package/dist/session-tracker.d.mts +1 -1
  61. package/dist/session-tracker.d.ts +1 -1
  62. package/dist/session-tracker.js.map +1 -1
  63. package/dist/session-tracker.mjs.map +1 -1
  64. package/dist/signature-verifier.d.mts +1 -0
  65. package/dist/signature-verifier.d.ts +1 -0
  66. package/dist/signature-verifier.js +204 -44
  67. package/dist/signature-verifier.js.map +1 -1
  68. package/dist/signature-verifier.mjs +184 -44
  69. package/dist/signature-verifier.mjs.map +1 -1
  70. package/dist/{types-BJTEUa4T.d.mts → types-DVmy9NE3.d.mts} +19 -2
  71. package/dist/{types-BJTEUa4T.d.ts → types-DVmy9NE3.d.ts} +19 -2
  72. package/dist/wasm-middleware.js +15 -6
  73. package/dist/wasm-middleware.js.map +1 -1
  74. package/dist/wasm-middleware.mjs +15 -6
  75. package/dist/wasm-middleware.mjs.map +1 -1
  76. package/package.json +42 -22
  77. package/wasm/agentshield_wasm.js +209 -152
  78. package/wasm/agentshield_wasm_bg.wasm +0 -0
  79. package/wasm/package.json +30 -0
package/dist/index.mjs CHANGED
@@ -1,4 +1,7 @@
1
+ import { loadRulesSync, mapVerificationMethod } from '@kya-os/agentshield-shared';
1
2
  import { NextResponse } from 'next/server';
3
+ import * as ed25519 from '@noble/ed25519';
4
+ import { sha512 } from '@noble/hashes/sha2.js';
2
5
 
3
6
  var __defProp = Object.defineProperty;
4
7
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -45,26 +48,31 @@ async function initWasm() {
45
48
  throw new Error(`Failed to fetch WASM: ${response2.status}`);
46
49
  }
47
50
  const streamResponse = response2.clone();
48
- const { instance } = await WebAssembly.instantiateStreaming(
49
- streamResponse,
50
- {
51
- wbg: {
52
- __wbg_log_1d3ae13c3d5e6b8e: (ptr, len) => {
53
- console.log("WASM:", ptr, len);
54
- },
55
- __wbindgen_throw: (ptr, len) => {
56
- throw new Error(`WASM Error at ${ptr}, length ${len}`);
51
+ const { instance } = await WebAssembly.instantiateStreaming(streamResponse, {
52
+ wbg: {
53
+ __wbg_log_1d3ae13c3d5e6b8e: (ptr, len) => {
54
+ if (process.env.NODE_ENV !== "production") {
55
+ console.debug("WASM:", ptr, len);
57
56
  }
57
+ },
58
+ __wbindgen_throw: (ptr, len) => {
59
+ throw new Error(`WASM Error at ${ptr}, length ${len}`);
58
60
  }
59
61
  }
60
- );
62
+ });
61
63
  wasmInstance = instance;
62
64
  wasmExports = instance.exports;
63
- console.log("[AgentShield] \u2705 WASM module initialized with streaming");
65
+ if (process.env.NODE_ENV !== "production") {
66
+ console.debug("[AgentShield] \u2705 WASM module initialized with streaming");
67
+ }
64
68
  return;
65
69
  } catch (streamError) {
66
70
  if (!controller.signal.aborted) {
67
- console.log("[AgentShield] Streaming compilation failed, falling back to standard compilation");
71
+ if (process.env.NODE_ENV !== "production") {
72
+ console.debug(
73
+ "[AgentShield] Streaming compilation failed, falling back to standard compilation"
74
+ );
75
+ }
68
76
  } else {
69
77
  throw streamError;
70
78
  }
@@ -80,7 +88,9 @@ async function initWasm() {
80
88
  const imports = {
81
89
  wbg: {
82
90
  __wbg_log_1d3ae13c3d5e6b8e: (ptr, len) => {
83
- console.log("WASM:", ptr, len);
91
+ if (process.env.NODE_ENV !== "production") {
92
+ console.debug("WASM:", ptr, len);
93
+ }
84
94
  },
85
95
  __wbindgen_throw: (ptr, len) => {
86
96
  throw new Error(`WASM Error at ${ptr}, length ${len}`);
@@ -89,12 +99,20 @@ async function initWasm() {
89
99
  };
90
100
  wasmInstance = await WebAssembly.instantiate(compiledModule, imports);
91
101
  wasmExports = wasmInstance.exports;
92
- console.log("[AgentShield] \u2705 WASM module initialized via fallback");
102
+ if (process.env.NODE_ENV !== "production") {
103
+ console.debug("[AgentShield] \u2705 WASM module initialized via fallback");
104
+ }
93
105
  } catch (fetchError) {
94
- if (fetchError.name === "AbortError") {
95
- console.warn("[AgentShield] WASM fetch timed out after 3 seconds - using pattern detection");
106
+ const error = fetchError;
107
+ if (error.name === "AbortError") {
108
+ console.warn(
109
+ "[AgentShield] WASM fetch timed out after 3 seconds - using pattern detection"
110
+ );
96
111
  } else {
97
- console.warn("[AgentShield] Failed to fetch WASM file:", fetchError.message);
112
+ console.warn(
113
+ "[AgentShield] Failed to fetch WASM file:",
114
+ error.message || "Unknown error"
115
+ );
98
116
  }
99
117
  wasmExports = null;
100
118
  }
@@ -172,32 +190,20 @@ __export(edge_detector_with_wasm_exports, {
172
190
  EdgeAgentDetectorWithWasm: () => EdgeAgentDetectorWithWasm,
173
191
  EdgeAgentDetectorWrapperWithWasm: () => EdgeAgentDetectorWrapperWithWasm
174
192
  });
175
- var AI_AGENT_PATTERNS2, CLOUD_PROVIDERS2, EdgeAgentDetectorWithWasm, EdgeAgentDetectorWrapperWithWasm;
193
+ var rules2, EdgeAgentDetectorWithWasm, EdgeAgentDetectorWrapperWithWasm;
176
194
  var init_edge_detector_with_wasm = __esm({
177
195
  "src/edge-detector-with-wasm.ts"() {
178
196
  init_wasm_loader();
179
- AI_AGENT_PATTERNS2 = [
180
- { pattern: /chatgpt-user/i, type: "chatgpt", name: "ChatGPT" },
181
- { pattern: /claude-web/i, type: "claude", name: "Claude" },
182
- { pattern: /perplexitybot/i, type: "perplexity", name: "Perplexity" },
183
- { pattern: /perplexity-user/i, type: "perplexity", name: "Perplexity" },
184
- { pattern: /perplexity-ai/i, type: "perplexity", name: "Perplexity" },
185
- { pattern: /perplexity/i, type: "perplexity", name: "Perplexity" },
186
- { pattern: /bingbot/i, type: "bing", name: "Bing AI" },
187
- { pattern: /anthropic-ai/i, type: "anthropic", name: "Anthropic" }
188
- ];
189
- CLOUD_PROVIDERS2 = {
190
- aws: ["54.", "52.", "35.", "18.", "3."],
191
- gcp: ["35.", "34.", "104.", "107.", "108."],
192
- azure: ["13.", "20.", "40.", "52.", "104."]
193
- };
197
+ rules2 = loadRulesSync();
194
198
  EdgeAgentDetectorWithWasm = class {
195
199
  constructor(enableWasm = true) {
196
200
  this.enableWasm = enableWasm;
201
+ this.rules = rules2;
197
202
  }
198
203
  wasmEnabled = false;
199
204
  initPromise = null;
200
205
  baseUrl = null;
206
+ rules;
201
207
  /**
202
208
  * Set the base URL for WASM loading in Edge Runtime
203
209
  */
@@ -220,11 +226,14 @@ var init_edge_detector_with_wasm = __esm({
220
226
  this.initPromise = (async () => {
221
227
  try {
222
228
  const wasmAvailable = await isWasmAvailable();
223
- this.wasmEnabled = wasmAvailable;
224
- if (this.wasmEnabled) {
225
- console.log("[AgentShield] WASM detection enabled");
229
+ if (wasmAvailable) {
230
+ if (this.baseUrl) {
231
+ setWasmBaseUrl(this.baseUrl);
232
+ }
233
+ await initWasm();
234
+ this.wasmEnabled = true;
226
235
  } else {
227
- console.log("[AgentShield] WASM not available, using pattern detection");
236
+ this.wasmEnabled = false;
228
237
  }
229
238
  } catch (error) {
230
239
  console.error("[AgentShield] Failed to initialize WASM:", error);
@@ -249,69 +258,84 @@ var init_edge_detector_with_wasm = __esm({
249
258
  const signaturePresent = !!(normalizedHeaders["signature"] || normalizedHeaders["signature-input"]);
250
259
  const signatureAgent = normalizedHeaders["signature-agent"];
251
260
  if (signatureAgent?.includes("chatgpt.com")) {
252
- confidence = 0.85;
261
+ confidence = 85;
253
262
  reasons.push("signature_agent:chatgpt");
254
263
  detectedAgent = { type: "chatgpt", name: "ChatGPT" };
255
264
  verificationMethod = "signature";
256
265
  } else if (signaturePresent) {
257
- confidence = Math.max(confidence, 0.4);
266
+ confidence = Math.max(confidence, 40);
258
267
  reasons.push("signature_present");
259
268
  }
260
269
  const userAgent = input.userAgent || input.headers?.["user-agent"] || "";
261
270
  if (userAgent) {
262
- for (const { pattern, type, name } of AI_AGENT_PATTERNS2) {
263
- if (pattern.test(userAgent)) {
264
- const highConfidenceAgents = [
265
- "chatgpt",
266
- "claude",
267
- "perplexity",
268
- "anthropic"
269
- ];
270
- const patternConfidence = highConfidenceAgents.includes(type) ? 0.85 : 0.5;
271
- confidence = Math.max(confidence, patternConfidence);
272
- reasons.push(`known_pattern:${type}`);
271
+ for (const [agentKey, agentRule] of Object.entries(this.rules.rules.userAgents)) {
272
+ const matched = agentRule.patterns.some((pattern) => {
273
+ const regex = new RegExp(pattern, "i");
274
+ return regex.test(userAgent);
275
+ });
276
+ if (matched) {
277
+ const agentType = this.getAgentType(agentKey);
278
+ const agentName = this.getAgentName(agentKey);
279
+ confidence = Math.max(confidence, Math.round(agentRule.confidence * 0.85 * 100));
280
+ reasons.push(`known_pattern:${agentType}`);
273
281
  if (!detectedAgent) {
274
- detectedAgent = { type, name };
282
+ detectedAgent = { type: agentType, name: agentName };
275
283
  verificationMethod = "pattern";
276
284
  }
277
285
  break;
278
286
  }
279
287
  }
280
288
  }
281
- const aiHeaders = [
282
- "openai-conversation-id",
283
- "openai-ephemeral-user-id",
284
- "anthropic-client-id",
285
- "x-goog-api-client",
286
- "x-ms-copilot-id"
287
- ];
288
- const foundAiHeaders = aiHeaders.filter(
289
- (header) => normalizedHeaders[header]
289
+ const suspiciousHeaders = this.rules.rules.headers.suspicious;
290
+ const foundAiHeaders = suspiciousHeaders.filter(
291
+ (headerRule) => normalizedHeaders[headerRule.name.toLowerCase()]
290
292
  );
291
293
  if (foundAiHeaders.length > 0) {
292
- confidence = Math.max(confidence, 0.6);
294
+ const maxConfidence = Math.max(...foundAiHeaders.map((h) => h.confidence));
295
+ confidence = Math.max(confidence, maxConfidence);
293
296
  reasons.push(`ai_headers:${foundAiHeaders.length}`);
294
297
  }
295
298
  const ip = input.ip || input.ipAddress;
296
299
  if (ip && !normalizedHeaders["x-forwarded-for"] && !normalizedHeaders["x-real-ip"]) {
297
- for (const [provider, prefixes] of Object.entries(CLOUD_PROVIDERS2)) {
298
- if (prefixes.some((prefix) => ip.startsWith(prefix))) {
299
- confidence = Math.max(confidence, 0.4);
300
+ const ipRanges = "providers" in this.rules.rules.ipRanges ? this.rules.rules.ipRanges.providers : this.rules.rules.ipRanges;
301
+ for (const [provider, ipRule] of Object.entries(ipRanges)) {
302
+ if (!ipRule || typeof ipRule !== "object" || !("ranges" in ipRule) || !Array.isArray(ipRule.ranges))
303
+ continue;
304
+ const matched = ipRule.ranges.some((range) => {
305
+ const prefix = range.split("/")[0];
306
+ const prefixParts = prefix.split(".");
307
+ const ipParts = ip.split(".");
308
+ for (let i = 0; i < Math.min(prefixParts.length - 1, 2); i++) {
309
+ if (prefixParts[i] !== ipParts[i] && prefixParts[i] !== "0") {
310
+ return false;
311
+ }
312
+ }
313
+ return true;
314
+ });
315
+ if (matched) {
316
+ confidence = Math.max(confidence, Math.round(ipRule.confidence * 0.4 * 100));
300
317
  reasons.push(`cloud_provider:${provider}`);
301
318
  break;
302
319
  }
303
320
  }
304
321
  }
305
322
  if (reasons.length > 2) {
306
- confidence = Math.min(confidence * 1.2, 0.95);
323
+ confidence = Math.min(Math.round(confidence * 1.2), 95);
307
324
  }
325
+ confidence = Math.min(Math.max(confidence, 0), 100);
308
326
  return {
309
- isAgent: confidence > 0.3,
327
+ isAgent: confidence > 30,
328
+ // 30% threshold
310
329
  confidence,
330
+ detectionClass: confidence > 30 && detectedAgent ? { type: "AiAgent", agentType: detectedAgent.name } : confidence > 30 ? { type: "Unknown" } : { type: "Human" },
331
+ signals: [],
332
+ // Will be populated by enhanced detection engine in future tasks
311
333
  ...detectedAgent && { detectedAgent },
312
334
  reasons,
313
- ...verificationMethod && { verificationMethod },
314
- forgeabilityRisk: confidence > 0.8 ? "medium" : "high",
335
+ ...verificationMethod && {
336
+ verificationMethod: mapVerificationMethod(verificationMethod)
337
+ },
338
+ forgeabilityRisk: confidence > 80 ? "medium" : "high",
315
339
  timestamp: /* @__PURE__ */ new Date()
316
340
  };
317
341
  }
@@ -328,15 +352,17 @@ var init_edge_detector_with_wasm = __esm({
328
352
  input.ip || input.ipAddress
329
353
  );
330
354
  if (wasmResult) {
331
- console.log("[AgentShield] Using WASM detection result");
332
355
  const detectedAgent = wasmResult.agent ? this.mapAgentName(wasmResult.agent) : void 0;
333
356
  return {
334
357
  isAgent: wasmResult.isAgent,
335
358
  confidence: wasmResult.confidence,
359
+ detectionClass: wasmResult.isAgent && detectedAgent ? { type: "AiAgent", agentType: detectedAgent.name } : wasmResult.isAgent ? { type: "Unknown" } : { type: "Human" },
360
+ signals: [],
361
+ // Will be populated by enhanced detection engine in future tasks
336
362
  ...detectedAgent && { detectedAgent },
337
363
  reasons: [`wasm:${wasmResult.verificationMethod}`],
338
- verificationMethod: wasmResult.verificationMethod,
339
- forgeabilityRisk: wasmResult.verificationMethod === "signature" ? "low" : wasmResult.confidence > 0.9 ? "medium" : "high",
364
+ verificationMethod: mapVerificationMethod(wasmResult.verificationMethod),
365
+ forgeabilityRisk: wasmResult.verificationMethod === "signature" ? "low" : wasmResult.confidence > 90 ? "medium" : "high",
340
366
  timestamp: /* @__PURE__ */ new Date()
341
367
  };
342
368
  }
@@ -345,15 +371,50 @@ var init_edge_detector_with_wasm = __esm({
345
371
  }
346
372
  }
347
373
  const patternResult = await this.patternDetection(input);
348
- if (this.wasmEnabled && patternResult.confidence >= 0.85) {
349
- patternResult.confidence = Math.min(0.95, patternResult.confidence + 0.1);
374
+ if (this.wasmEnabled && patternResult.confidence >= 85) {
375
+ patternResult.confidence = Math.min(95, patternResult.confidence + 10);
350
376
  patternResult.reasons.push("wasm_enhanced");
351
- if (!patternResult.verificationMethod) {
352
- patternResult.verificationMethod = "wasm-enhanced";
353
- }
354
377
  }
355
378
  return patternResult;
356
379
  }
380
+ /**
381
+ * Get agent type from rule key
382
+ */
383
+ getAgentType(agentKey) {
384
+ const typeMap = {
385
+ openai_gptbot: "openai",
386
+ anthropic_claude: "anthropic",
387
+ perplexity_bot: "perplexity",
388
+ google_ai: "google",
389
+ microsoft_ai: "microsoft",
390
+ meta_ai: "meta",
391
+ cohere_bot: "cohere",
392
+ huggingface_bot: "huggingface",
393
+ generic_bot: "generic",
394
+ dev_tools: "dev",
395
+ automation_tools: "automation"
396
+ };
397
+ return typeMap[agentKey] || agentKey;
398
+ }
399
+ /**
400
+ * Get agent name from rule key
401
+ */
402
+ getAgentName(agentKey) {
403
+ const nameMap = {
404
+ openai_gptbot: "ChatGPT/GPTBot",
405
+ anthropic_claude: "Claude",
406
+ perplexity_bot: "Perplexity",
407
+ google_ai: "Google AI",
408
+ microsoft_ai: "Microsoft Copilot",
409
+ meta_ai: "Meta AI",
410
+ cohere_bot: "Cohere",
411
+ huggingface_bot: "HuggingFace",
412
+ generic_bot: "Generic Bot",
413
+ dev_tools: "Development Tool",
414
+ automation_tools: "Automation Tool"
415
+ };
416
+ return nameMap[agentKey] || agentKey;
417
+ }
357
418
  /**
358
419
  * Map agent names from WASM to consistent format
359
420
  */
@@ -432,19 +493,19 @@ async function checkWasmAvailability() {
432
493
  }
433
494
  }
434
495
  function shouldIndicateWasmVerification(confidence) {
435
- return confidence >= 0.85 && confidence < 1;
496
+ return confidence >= 85 && confidence < 100;
436
497
  }
437
498
  function getWasmConfidenceBoost(baseConfidence, reasons = []) {
438
499
  if (reasons.some(
439
500
  (r) => r.includes("signature_agent") && !r.includes("signature_headers_present")
440
501
  )) {
441
- return 1;
502
+ return 100;
442
503
  }
443
- if (baseConfidence >= 0.85) {
444
- return 0.95;
504
+ if (baseConfidence >= 85) {
505
+ return 95;
445
506
  }
446
- if (baseConfidence >= 0.7) {
447
- return Math.min(baseConfidence * 1.1, 0.9);
507
+ if (baseConfidence >= 70) {
508
+ return Math.min(baseConfidence * 1.1, 90);
448
509
  }
449
510
  return baseConfidence;
450
511
  }
@@ -463,19 +524,126 @@ var init_wasm_confidence = __esm({
463
524
  "src/wasm-confidence.ts"() {
464
525
  }
465
526
  });
466
-
467
- // src/signature-verifier.ts
527
+ ed25519.etc.sha512Sync = (...m) => sha512(ed25519.etc.concatBytes(...m));
468
528
  var KNOWN_KEYS = {
469
529
  chatgpt: [
470
530
  {
471
531
  kid: "otMqcjr17mGyruktGvJU8oojQTSMHlVm7uO-lrcqbdg",
472
532
  // ChatGPT's current Ed25519 public key (base64)
533
+ // Source: https://chatgpt.com/.well-known/http-message-signatures-directory
473
534
  publicKey: "7F_3jDlxaquwh291MiACkcS3Opq88NksyHiakzS-Y1g",
474
- validFrom: (/* @__PURE__ */ new Date("2025-01-01")).getTime() / 1e3,
475
- validUntil: (/* @__PURE__ */ new Date("2025-04-11")).getTime() / 1e3
535
+ validFrom: 1735689600,
536
+ // Jan 1, 2025 (from OpenAI's nbf)
537
+ // Extended expiration as fallback safety - API fetch should provide fresh keys
538
+ // Check OpenAI's well-known endpoint for actual expiration dates
539
+ validUntil: 1799625600
540
+ // Jan 1, 2027 (extended for fallback safety)
476
541
  }
477
542
  ]
478
543
  };
544
+ var keyCache = /* @__PURE__ */ new Map();
545
+ var CACHE_TTL_MS = 5 * 60 * 1e3;
546
+ var CACHE_MAX_SIZE = 100;
547
+ function getApiBaseUrl() {
548
+ if (typeof window !== "undefined") {
549
+ return "/api/internal";
550
+ }
551
+ const baseUrl2 = process.env.NEXT_PUBLIC_APP_URL || process.env.NEXT_PUBLIC_API_URL || process.env.API_URL || (process.env.VERCEL_URL ? `https://${process.env.VERCEL_URL}` : null);
552
+ if (baseUrl2) {
553
+ return baseUrl2.replace(/\/$/, "") + "/api/internal";
554
+ }
555
+ if (process.env.NODE_ENV !== "production") {
556
+ console.warn(
557
+ "[Signature] No base URL configured for server-side fetch. Using localhost fallback."
558
+ );
559
+ return "http://localhost:3000/api/internal";
560
+ }
561
+ console.error(
562
+ "[Signature] CRITICAL: No base URL configured for server-side fetch in production!"
563
+ );
564
+ return "/api/internal";
565
+ }
566
+ function cleanupExpiredCache() {
567
+ const now = Date.now();
568
+ const entriesToDelete = [];
569
+ for (const [agent, cached] of keyCache.entries()) {
570
+ if (now - cached.cachedAt > CACHE_TTL_MS) {
571
+ entriesToDelete.push(agent);
572
+ }
573
+ }
574
+ for (const agent of entriesToDelete) {
575
+ keyCache.delete(agent);
576
+ }
577
+ if (keyCache.size > CACHE_MAX_SIZE) {
578
+ const entries = Array.from(keyCache.entries()).map(([agent, cached]) => ({
579
+ agent,
580
+ cachedAt: cached.cachedAt
581
+ }));
582
+ entries.sort((a, b) => a.cachedAt - b.cachedAt);
583
+ const toRemove = entries.slice(0, keyCache.size - CACHE_MAX_SIZE);
584
+ for (const entry of toRemove) {
585
+ keyCache.delete(entry.agent);
586
+ }
587
+ }
588
+ }
589
+ async function fetchKeysFromApi(agent) {
590
+ if (keyCache.size > CACHE_MAX_SIZE) {
591
+ cleanupExpiredCache();
592
+ }
593
+ const cached = keyCache.get(agent);
594
+ if (cached && Date.now() - cached.cachedAt < CACHE_TTL_MS) {
595
+ return cached.keys;
596
+ }
597
+ if (typeof fetch === "undefined") {
598
+ console.warn("[Signature] fetch() not available in this environment");
599
+ return null;
600
+ }
601
+ try {
602
+ const apiBaseUrl = getApiBaseUrl();
603
+ const url = `${apiBaseUrl}/signature-keys?agent=${encodeURIComponent(agent)}`;
604
+ const response = await fetch(url, {
605
+ method: "GET",
606
+ headers: {
607
+ "Content-Type": "application/json"
608
+ },
609
+ // 5 second timeout
610
+ signal: AbortSignal.timeout(5e3)
611
+ });
612
+ if (!response.ok) {
613
+ console.warn(`[Signature] Failed to fetch keys from API: ${response.status}`);
614
+ return null;
615
+ }
616
+ const data = await response.json();
617
+ if (!data.keys || !Array.isArray(data.keys) || data.keys.length === 0) {
618
+ console.warn(`[Signature] No keys returned from API for agent: ${agent}`);
619
+ return null;
620
+ }
621
+ keyCache.set(agent, {
622
+ keys: data.keys,
623
+ cachedAt: Date.now()
624
+ });
625
+ return data.keys;
626
+ } catch (error) {
627
+ console.warn("[Signature] Error fetching keys from API, using fallback", {
628
+ error: error instanceof Error ? error.message : "Unknown error",
629
+ agent
630
+ });
631
+ return null;
632
+ }
633
+ }
634
+ function isValidAgent(agent) {
635
+ return agent in KNOWN_KEYS;
636
+ }
637
+ async function getKeysForAgent(agent) {
638
+ const apiKeys = await fetchKeysFromApi(agent);
639
+ if (apiKeys && apiKeys.length > 0) {
640
+ return apiKeys;
641
+ }
642
+ if (isValidAgent(agent)) {
643
+ return KNOWN_KEYS[agent];
644
+ }
645
+ return [];
646
+ }
479
647
  function parseSignatureInput(signatureInput) {
480
648
  try {
481
649
  const match = signatureInput.match(/sig1=\((.*?)\);(.+)/);
@@ -511,21 +679,29 @@ function buildSignatureBase(method, path, headers, signedHeaders) {
511
679
  case "@authority":
512
680
  value = headers["host"] || headers["Host"] || "";
513
681
  break;
514
- default:
515
- const key = Object.keys(headers).find(
516
- (k) => k.toLowerCase() === headerName.toLowerCase()
517
- );
682
+ default: {
683
+ const key = Object.keys(headers).find((k) => k.toLowerCase() === headerName.toLowerCase());
518
684
  value = key ? headers[key] || "" : "";
519
685
  break;
686
+ }
520
687
  }
521
688
  components.push(`"${headerName}": ${value}`);
522
689
  }
523
690
  return components.join("\n");
524
691
  }
692
+ function base64ToBytes(base64) {
693
+ let standardBase64 = base64.replace(/-/g, "+").replace(/_/g, "/");
694
+ const padding = standardBase64.length % 4;
695
+ if (padding) {
696
+ standardBase64 += "=".repeat(4 - padding);
697
+ }
698
+ const binaryString = atob(standardBase64);
699
+ return Uint8Array.from(binaryString, (c) => c.charCodeAt(0));
700
+ }
525
701
  async function verifyEd25519Signature(publicKeyBase64, signatureBase64, message) {
526
702
  try {
527
- const publicKeyBytes = Uint8Array.from(atob(publicKeyBase64), (c) => c.charCodeAt(0));
528
- const signatureBytes = Uint8Array.from(atob(signatureBase64), (c) => c.charCodeAt(0));
703
+ const publicKeyBytes = base64ToBytes(publicKeyBase64);
704
+ const signatureBytes = base64ToBytes(signatureBase64);
529
705
  const messageBytes = new TextEncoder().encode(message);
530
706
  if (publicKeyBytes.length !== 32) {
531
707
  console.error("[Signature] Invalid public key length:", publicKeyBytes.length);
@@ -535,34 +711,36 @@ async function verifyEd25519Signature(publicKeyBase64, signatureBase64, message)
535
711
  console.error("[Signature] Invalid signature length:", signatureBytes.length);
536
712
  return false;
537
713
  }
538
- const publicKey = await crypto.subtle.importKey(
539
- "raw",
540
- publicKeyBytes,
541
- {
542
- name: "Ed25519",
543
- namedCurve: "Ed25519"
544
- },
545
- false,
546
- ["verify"]
547
- );
548
- const isValid = await crypto.subtle.verify(
549
- "Ed25519",
550
- publicKey,
551
- signatureBytes,
552
- messageBytes
553
- );
554
- return isValid;
555
- } catch (error) {
556
- console.error("[Signature] Ed25519 verification failed:", error);
557
- if (typeof window === "undefined") {
558
- try {
559
- console.warn("[Signature] Ed25519 not supported in this environment");
560
- return false;
561
- } catch {
562
- return false;
563
- }
714
+ return ed25519.verify(signatureBytes, messageBytes, publicKeyBytes);
715
+ } catch (nobleError) {
716
+ console.warn("[Signature] @noble/ed25519 failed, trying Web Crypto fallback:", nobleError);
717
+ try {
718
+ const publicKeyBytes = base64ToBytes(publicKeyBase64);
719
+ const signatureBytes = base64ToBytes(signatureBase64);
720
+ const messageBytes = new TextEncoder().encode(message);
721
+ const publicKey = await crypto.subtle.importKey(
722
+ "raw",
723
+ publicKeyBytes.buffer,
724
+ {
725
+ name: "Ed25519",
726
+ namedCurve: "Ed25519"
727
+ },
728
+ false,
729
+ ["verify"]
730
+ );
731
+ return await crypto.subtle.verify(
732
+ "Ed25519",
733
+ publicKey,
734
+ signatureBytes.buffer,
735
+ messageBytes
736
+ );
737
+ } catch (cryptoError) {
738
+ console.error("[Signature] Both @noble/ed25519 and Web Crypto failed:", {
739
+ nobleError: nobleError instanceof Error ? nobleError.message : "Unknown",
740
+ cryptoError: cryptoError instanceof Error ? cryptoError.message : "Unknown"
741
+ });
742
+ return false;
564
743
  }
565
- return false;
566
744
  }
567
745
  }
568
746
  async function verifyAgentSignature(method, path, headers) {
@@ -607,12 +785,12 @@ async function verifyAgentSignature(method, path, headers) {
607
785
  }
608
786
  }
609
787
  let agent;
610
- let knownKeys;
788
+ let agentKey;
611
789
  if (signatureAgent === '"https://chatgpt.com"' || signatureAgent?.includes("chatgpt.com")) {
612
790
  agent = "ChatGPT";
613
- knownKeys = KNOWN_KEYS.chatgpt;
791
+ agentKey = "chatgpt";
614
792
  }
615
- if (!agent || !knownKeys) {
793
+ if (!agent || !agentKey) {
616
794
  return {
617
795
  isValid: false,
618
796
  confidence: 0,
@@ -620,6 +798,15 @@ async function verifyAgentSignature(method, path, headers) {
620
798
  verificationMethod: "none"
621
799
  };
622
800
  }
801
+ const knownKeys = await getKeysForAgent(agentKey);
802
+ if (knownKeys.length === 0) {
803
+ return {
804
+ isValid: false,
805
+ confidence: 0,
806
+ reason: "No keys available for agent",
807
+ verificationMethod: "none"
808
+ };
809
+ }
623
810
  const key = knownKeys.find((k) => k.kid === parsed.keyid);
624
811
  if (!key) {
625
812
  return {
@@ -646,11 +833,7 @@ async function verifyAgentSignature(method, path, headers) {
646
833
  if (signatureValue.endsWith(":")) {
647
834
  signatureValue = signatureValue.slice(0, -1);
648
835
  }
649
- const isValid = await verifyEd25519Signature(
650
- key.publicKey,
651
- signatureValue,
652
- signatureBase
653
- );
836
+ const isValid = await verifyEd25519Signature(key.publicKey, signatureValue, signatureBase);
654
837
  if (isValid) {
655
838
  return {
656
839
  isValid: true,
@@ -674,27 +857,27 @@ function hasSignatureHeaders(headers) {
674
857
  }
675
858
  function isChatGPTSignature(headers) {
676
859
  const signatureAgent = headers["signature-agent"] || headers["Signature-Agent"];
677
- return signatureAgent === '"https://chatgpt.com"' || (signatureAgent?.includes("chatgpt.com") || false);
860
+ if (!signatureAgent) {
861
+ return false;
862
+ }
863
+ const agentUrlStr = signatureAgent.replace(/^"+|"+$/g, "");
864
+ if (agentUrlStr === "https://chatgpt.com") {
865
+ return true;
866
+ }
867
+ try {
868
+ const agentUrl = new URL(agentUrlStr);
869
+ const allowedHosts = ["chatgpt.com", "www.chatgpt.com"];
870
+ return allowedHosts.includes(agentUrl.host);
871
+ } catch {
872
+ return false;
873
+ }
678
874
  }
679
-
680
- // src/edge-detector-wrapper.ts
681
- var AI_AGENT_PATTERNS = [
682
- { pattern: /chatgpt-user/i, type: "chatgpt", name: "ChatGPT" },
683
- { pattern: /claude-web/i, type: "claude", name: "Claude" },
684
- { pattern: /perplexitybot/i, type: "perplexity", name: "Perplexity" },
685
- { pattern: /perplexity-user/i, type: "perplexity", name: "Perplexity" },
686
- { pattern: /perplexity-ai/i, type: "perplexity", name: "Perplexity" },
687
- { pattern: /perplexity/i, type: "perplexity", name: "Perplexity" },
688
- // Fallback
689
- { pattern: /bingbot/i, type: "bing", name: "Bing AI" },
690
- { pattern: /anthropic-ai/i, type: "anthropic", name: "Anthropic" }
691
- ];
692
- var CLOUD_PROVIDERS = {
693
- aws: ["54.", "52.", "35.", "18.", "3."],
694
- gcp: ["35.", "34.", "104.", "107.", "108."],
695
- azure: ["13.", "20.", "40.", "52.", "104."]
696
- };
875
+ var rules = loadRulesSync();
697
876
  var EdgeAgentDetector = class {
877
+ rules;
878
+ constructor() {
879
+ this.rules = rules;
880
+ }
698
881
  async analyze(input) {
699
882
  const reasons = [];
700
883
  let detectedAgent;
@@ -713,7 +896,7 @@ var EdgeAgentDetector = class {
713
896
  headers
714
897
  );
715
898
  if (signatureResult.isValid) {
716
- confidence = signatureResult.confidence;
899
+ confidence = signatureResult.confidence * 100;
717
900
  reasons.push(`verified_signature:${signatureResult.agent?.toLowerCase() || "unknown"}`);
718
901
  if (signatureResult.agent) {
719
902
  detectedAgent = {
@@ -726,7 +909,13 @@ var EdgeAgentDetector = class {
726
909
  reasons.push(`keyid:${signatureResult.keyid}`);
727
910
  }
728
911
  } else {
729
- confidence = Math.max(confidence, 0.3);
912
+ console.warn("[EdgeAgentDetector] Signature verification failed:", {
913
+ reason: signatureResult.reason,
914
+ agent: signatureResult.agent,
915
+ hasSignatureAgent: !!headers["signature-agent"] || !!headers["Signature-Agent"],
916
+ signatureAgentValue: headers["signature-agent"] || headers["Signature-Agent"]
917
+ });
918
+ confidence = Math.max(confidence, 30);
730
919
  reasons.push("invalid_signature");
731
920
  if (signatureResult.reason) {
732
921
  reasons.push(`signature_error:${signatureResult.reason}`);
@@ -738,68 +927,133 @@ var EdgeAgentDetector = class {
738
927
  }
739
928
  } catch (error) {
740
929
  console.error("[EdgeAgentDetector] Signature verification error:", error);
741
- confidence = Math.max(confidence, 0.2);
930
+ confidence = Math.max(confidence, 20);
742
931
  reasons.push("signature_verification_error");
743
932
  }
744
933
  }
745
934
  const userAgent = input.userAgent || input.headers?.["user-agent"] || "";
746
935
  if (userAgent) {
747
- for (const { pattern, type, name } of AI_AGENT_PATTERNS) {
748
- if (pattern.test(userAgent)) {
749
- const highConfidenceAgents = [
750
- "chatgpt",
751
- "claude",
752
- "perplexity",
753
- "anthropic"
754
- ];
755
- const patternConfidence = highConfidenceAgents.includes(type) ? 0.85 : 0.5;
756
- confidence = Math.max(confidence, patternConfidence);
757
- reasons.push(`known_pattern:${type}`);
936
+ const userAgentEntries = Object.entries(this.rules.rules.userAgents);
937
+ const genericKeys = ["generic_bot", "dev_tools", "automation_tools"];
938
+ const sortedEntries = userAgentEntries.sort((a, b) => {
939
+ const aIsGeneric = genericKeys.includes(a[0]);
940
+ const bIsGeneric = genericKeys.includes(b[0]);
941
+ if (aIsGeneric && !bIsGeneric) return 1;
942
+ if (!aIsGeneric && bIsGeneric) return -1;
943
+ return 0;
944
+ });
945
+ for (const [agentKey, agentRule] of sortedEntries) {
946
+ const rule = agentRule;
947
+ const matched = rule.patterns.some((pattern) => {
948
+ const regex = new RegExp(pattern, "i");
949
+ return regex.test(userAgent);
950
+ });
951
+ if (matched) {
952
+ const agentType = this.getAgentType(agentKey);
953
+ const agentName = this.getAgentName(agentKey);
954
+ confidence = Math.max(confidence, rule.confidence * 100);
955
+ reasons.push(`known_pattern:${agentType}`);
758
956
  if (!detectedAgent) {
759
- detectedAgent = { type, name };
957
+ detectedAgent = { type: agentType, name: agentName };
760
958
  verificationMethod = "pattern";
761
959
  }
762
960
  break;
763
961
  }
764
962
  }
765
963
  }
766
- const aiHeaders = [
767
- "openai-conversation-id",
768
- "openai-ephemeral-user-id",
769
- "anthropic-client-id",
770
- "x-goog-api-client",
771
- "x-ms-copilot-id"
772
- ];
773
- const foundAiHeaders = aiHeaders.filter(
774
- (header) => normalizedHeaders[header]
964
+ const suspiciousHeaders = this.rules.rules.headers.suspicious;
965
+ const foundAiHeaders = suspiciousHeaders.filter(
966
+ (headerRule) => normalizedHeaders[headerRule.name.toLowerCase()]
775
967
  );
776
968
  if (foundAiHeaders.length > 0) {
777
- confidence = Math.max(confidence, 0.6);
969
+ const maxConfidence = Math.max(...foundAiHeaders.map((h) => h.confidence * 100));
970
+ confidence = Math.max(confidence, maxConfidence);
778
971
  reasons.push(`ai_headers:${foundAiHeaders.length}`);
779
972
  }
780
973
  const ip = input.ip || input.ipAddress;
781
974
  if (ip && !normalizedHeaders["x-forwarded-for"] && !normalizedHeaders["x-real-ip"]) {
782
- for (const [provider, prefixes] of Object.entries(CLOUD_PROVIDERS)) {
783
- if (prefixes.some((prefix) => ip.startsWith(prefix))) {
784
- confidence = Math.max(confidence, 0.4);
975
+ const ipRanges = "providers" in this.rules.rules.ipRanges ? this.rules.rules.ipRanges.providers : this.rules.rules.ipRanges;
976
+ for (const [provider, ipRule] of Object.entries(ipRanges)) {
977
+ if (!ipRule || typeof ipRule !== "object" || !("ranges" in ipRule) || !Array.isArray(ipRule.ranges))
978
+ continue;
979
+ const matched = ipRule.ranges.some((range) => {
980
+ const prefix = range.split("/")[0];
981
+ const prefixParts = prefix.split(".");
982
+ const ipParts = ip.split(".");
983
+ for (let i = 0; i < Math.min(prefixParts.length - 1, 2); i++) {
984
+ if (prefixParts[i] !== ipParts[i] && prefixParts[i] !== "0") {
985
+ return false;
986
+ }
987
+ }
988
+ return true;
989
+ });
990
+ if (matched) {
991
+ const rule = ipRule;
992
+ confidence = Math.max(confidence, rule.confidence * 40);
785
993
  reasons.push(`cloud_provider:${provider}`);
786
994
  break;
787
995
  }
788
996
  }
789
997
  }
790
- if (reasons.length > 2 && confidence < 1) {
791
- confidence = Math.min(confidence * 1.2, 0.95);
998
+ if (reasons.length > 2 && confidence < 100) {
999
+ confidence = Math.min(confidence * 1.2, 95);
792
1000
  }
1001
+ confidence = Math.min(Math.max(confidence, 0), 100);
793
1002
  return {
794
- isAgent: confidence > 0.3,
1003
+ isAgent: confidence > 30,
1004
+ // Updated to 0-100 scale (was 0.3)
795
1005
  confidence,
1006
+ detectionClass: confidence > 30 && detectedAgent ? { type: "AiAgent", agentType: detectedAgent.name } : confidence > 30 ? { type: "Unknown" } : { type: "Human" },
1007
+ signals: [],
1008
+ // Will be populated by enhanced detection engine in future tasks
796
1009
  ...detectedAgent && { detectedAgent },
797
1010
  reasons,
798
- ...verificationMethod && { verificationMethod },
799
- forgeabilityRisk: verificationMethod === "signature" ? "low" : confidence > 0.8 ? "medium" : "high",
1011
+ ...verificationMethod && {
1012
+ verificationMethod
1013
+ },
1014
+ forgeabilityRisk: verificationMethod === "signature" ? "low" : confidence > 80 ? "medium" : "high",
1015
+ // Updated to 0-100 scale
800
1016
  timestamp: /* @__PURE__ */ new Date()
801
1017
  };
802
1018
  }
1019
+ /**
1020
+ * Get agent type from rule key
1021
+ */
1022
+ getAgentType(agentKey) {
1023
+ const typeMap = {
1024
+ openai_gptbot: "openai",
1025
+ anthropic_claude: "anthropic",
1026
+ perplexity_bot: "perplexity",
1027
+ google_ai: "google",
1028
+ microsoft_ai: "microsoft",
1029
+ meta_ai: "meta",
1030
+ cohere_bot: "cohere",
1031
+ huggingface_bot: "huggingface",
1032
+ generic_bot: "generic",
1033
+ dev_tools: "dev",
1034
+ automation_tools: "automation"
1035
+ };
1036
+ return typeMap[agentKey] || agentKey;
1037
+ }
1038
+ /**
1039
+ * Get agent name from rule key
1040
+ */
1041
+ getAgentName(agentKey) {
1042
+ const nameMap = {
1043
+ openai_gptbot: "ChatGPT/GPTBot",
1044
+ anthropic_claude: "Claude",
1045
+ perplexity_bot: "Perplexity",
1046
+ google_ai: "Google AI",
1047
+ microsoft_ai: "Microsoft Copilot",
1048
+ meta_ai: "Meta AI",
1049
+ cohere_bot: "Cohere",
1050
+ huggingface_bot: "HuggingFace",
1051
+ generic_bot: "Generic Bot",
1052
+ dev_tools: "Development Tool",
1053
+ automation_tools: "Automation Tool"
1054
+ };
1055
+ return nameMap[agentKey] || agentKey;
1056
+ }
803
1057
  };
804
1058
  var EdgeAgentDetectorWrapper = class {
805
1059
  detector;
@@ -1012,7 +1266,9 @@ function createAgentShieldMiddleware(config = {}) {
1012
1266
  }) : null;
1013
1267
  if (config.events) {
1014
1268
  Object.entries(config.events).forEach(([event, handler]) => {
1015
- detector.on(event, handler);
1269
+ if (handler) {
1270
+ detector.on(event, handler);
1271
+ }
1016
1272
  });
1017
1273
  }
1018
1274
  const {
@@ -1044,19 +1300,22 @@ function createAgentShieldMiddleware(config = {}) {
1044
1300
  const response2 = NextResponse.next();
1045
1301
  response2.headers.set("x-agentshield-detected", "true");
1046
1302
  response2.headers.set("x-agentshield-agent", existingSession.agent);
1047
- response2.headers.set(
1048
- "x-agentshield-confidence",
1049
- existingSession.confidence.toString()
1050
- );
1303
+ response2.headers.set("x-agentshield-confidence", existingSession.confidence.toString());
1051
1304
  response2.headers.set("x-agentshield-session", "continued");
1052
1305
  response2.headers.set("x-agentshield-session-id", existingSession.id);
1053
1306
  request.agentShield = {
1054
1307
  result: {
1055
1308
  isAgent: true,
1056
1309
  confidence: existingSession.confidence,
1057
- detectedAgent: { name: existingSession.agent },
1310
+ detectionClass: { type: "AiAgent" },
1311
+ detectedAgent: {
1312
+ type: "ai_agent",
1313
+ name: existingSession.agent
1314
+ },
1058
1315
  timestamp: /* @__PURE__ */ new Date(),
1059
- verificationMethod: "session"
1316
+ verificationMethod: "behavioral",
1317
+ reasons: ["Session continued"],
1318
+ signals: []
1060
1319
  },
1061
1320
  session: existingSession,
1062
1321
  skipped: false
@@ -1086,7 +1345,7 @@ function createAgentShieldMiddleware(config = {}) {
1086
1345
  timestamp: /* @__PURE__ */ new Date()
1087
1346
  };
1088
1347
  const result = await detector.analyze(context);
1089
- if (result.isAgent && result.confidence >= (config.confidenceThreshold ?? 0.7)) {
1348
+ if (result.isAgent && result.confidence >= (config.confidenceThreshold ?? 70)) {
1090
1349
  if (onDetection) {
1091
1350
  const customResponse = await onDetection(request, result);
1092
1351
  if (customResponse) {
@@ -1105,11 +1364,9 @@ function createAgentShieldMiddleware(config = {}) {
1105
1364
  { status: blockedResponse.status }
1106
1365
  );
1107
1366
  if (blockedResponse.headers) {
1108
- Object.entries(blockedResponse.headers).forEach(
1109
- ([key, value]) => {
1110
- response2.headers.set(key, value);
1111
- }
1112
- );
1367
+ Object.entries(blockedResponse.headers).forEach(([key, value]) => {
1368
+ response2.headers.set(key, value);
1369
+ });
1113
1370
  }
1114
1371
  detector.emit("agent.blocked", result, context);
1115
1372
  return response2;
@@ -1119,17 +1376,19 @@ function createAgentShieldMiddleware(config = {}) {
1119
1376
  case "rewrite":
1120
1377
  return NextResponse.rewrite(new URL(rewriteUrl, request.url));
1121
1378
  case "log":
1122
- console.warn("AgentShield: Agent detected", {
1123
- ipAddress: context.ipAddress,
1124
- userAgent: context.userAgent,
1125
- confidence: result.confidence,
1126
- reasons: result.reasons,
1127
- pathname: request.nextUrl.pathname
1128
- });
1379
+ if (process.env.NODE_ENV !== "production") {
1380
+ console.debug("AgentShield: Agent detected", {
1381
+ ipAddress: context.ipAddress,
1382
+ userAgent: context.userAgent,
1383
+ confidence: result.confidence,
1384
+ reasons: result.reasons,
1385
+ pathname: request.nextUrl.pathname
1386
+ });
1387
+ }
1129
1388
  break;
1130
1389
  case "allow":
1131
1390
  default:
1132
- if (result.isAgent && result.confidence >= (config.confidenceThreshold ?? 0.7)) {
1391
+ if (result.isAgent && result.confidence >= (config.confidenceThreshold ?? 70)) {
1133
1392
  detector.emit("agent.allowed", result, context);
1134
1393
  }
1135
1394
  break;
@@ -1141,14 +1400,11 @@ function createAgentShieldMiddleware(config = {}) {
1141
1400
  };
1142
1401
  let response = NextResponse.next();
1143
1402
  response.headers.set("x-agentshield-detected", result.isAgent.toString());
1144
- response.headers.set(
1145
- "x-agentshield-confidence",
1146
- result.confidence.toString()
1147
- );
1403
+ response.headers.set("x-agentshield-confidence", result.confidence.toString());
1148
1404
  if (result.detectedAgent?.name) {
1149
1405
  response.headers.set("x-agentshield-agent", result.detectedAgent.name);
1150
1406
  }
1151
- if (sessionTracker && result.isAgent && result.confidence >= (config.confidenceThreshold ?? 0.7)) {
1407
+ if (sessionTracker && result.isAgent && result.confidence >= (config.confidenceThreshold ?? 70)) {
1152
1408
  response = await sessionTracker.track(request, response, result);
1153
1409
  response.headers.set("x-agentshield-session", "new");
1154
1410
  detector.emit("agent.session.started", result, context);
@@ -1166,7 +1422,7 @@ var middlewareInstance = null;
1166
1422
  var isInitializing = false;
1167
1423
  var initPromise2 = null;
1168
1424
  function createAgentShieldMiddleware2(config) {
1169
- return async function agentShieldMiddleware(request) {
1425
+ return async function agentShieldMiddleware2(request) {
1170
1426
  if (!middlewareInstance) {
1171
1427
  if (!isInitializing) {
1172
1428
  isInitializing = true;
@@ -1184,7 +1440,7 @@ function createAgentShieldMiddleware2(config) {
1184
1440
  }
1185
1441
 
1186
1442
  // src/edge-safe-detector.ts
1187
- var AI_AGENT_PATTERNS3 = [
1443
+ var AI_AGENT_PATTERNS = [
1188
1444
  { pattern: /chatgpt-user/i, type: "chatgpt", name: "ChatGPT" },
1189
1445
  { pattern: /claude-web/i, type: "claude", name: "Claude" },
1190
1446
  { pattern: /perplexitybot/i, type: "perplexity", name: "Perplexity" },
@@ -1205,9 +1461,9 @@ var EdgeSafeDetector = class {
1205
1461
  }
1206
1462
  const userAgent = input.userAgent || normalizedHeaders["user-agent"] || "";
1207
1463
  if (userAgent) {
1208
- for (const { pattern, type, name } of AI_AGENT_PATTERNS3) {
1464
+ for (const { pattern, type, name } of AI_AGENT_PATTERNS) {
1209
1465
  if (pattern.test(userAgent)) {
1210
- confidence = 0.85;
1466
+ confidence = 85;
1211
1467
  reasons.push(`known_pattern:${type}`);
1212
1468
  detectedAgent = { type, name };
1213
1469
  break;
@@ -1229,31 +1485,32 @@ var EdgeSafeDetector = class {
1229
1485
  if (!hasAcceptLanguage) missingHeaders.push("accept-language");
1230
1486
  if (!hasCookies) missingHeaders.push("cookies");
1231
1487
  if (missingHeaders.length >= 2) {
1232
- confidence = Math.max(confidence, 0.85);
1488
+ confidence = Math.max(confidence, 85);
1233
1489
  reasons.push("browser_ua_missing_headers");
1234
1490
  if (!detectedAgent && hasChrome && !hasSecChUa) {
1235
1491
  detectedAgent = { type: "perplexity", name: "Perplexity" };
1236
1492
  }
1237
1493
  }
1238
1494
  }
1239
- const aiHeaders = [
1240
- "openai-conversation-id",
1241
- "anthropic-client-id",
1242
- "x-goog-api-client"
1243
- ];
1495
+ const aiHeaders = ["openai-conversation-id", "anthropic-client-id", "x-goog-api-client"];
1244
1496
  const foundAiHeaders = aiHeaders.filter((h) => normalizedHeaders[h]);
1245
1497
  if (foundAiHeaders.length > 0) {
1246
- confidence = Math.max(confidence, 0.6);
1498
+ confidence = Math.max(confidence, 60);
1247
1499
  reasons.push(`ai_headers:${foundAiHeaders.length}`);
1248
1500
  }
1249
1501
  return {
1250
- isAgent: confidence > 0.3,
1502
+ isAgent: confidence > 30,
1503
+ // Updated to 0-100 scale (was 0.3)
1251
1504
  confidence,
1505
+ detectionClass: confidence > 30 && detectedAgent ? { type: "AiAgent", agentType: detectedAgent.name } : confidence > 30 ? { type: "Unknown" } : { type: "Human" },
1506
+ signals: [],
1507
+ // Will be populated by enhanced detection engine in future tasks
1252
1508
  detectedAgent,
1253
1509
  reasons,
1254
1510
  verificationMethod: "pattern",
1255
1511
  timestamp: /* @__PURE__ */ new Date(),
1256
- confidenceLevel: confidence >= 0.8 ? "high" : confidence >= 0.5 ? "medium" : "low"
1512
+ confidenceLevel: confidence >= 80 ? "high" : confidence >= 50 ? "medium" : "low"
1513
+ // Updated to 0-100 scale
1257
1514
  };
1258
1515
  }
1259
1516
  };
@@ -1456,11 +1713,7 @@ var RedisStorageAdapter = class {
1456
1713
  }
1457
1714
  async cleanup(olderThan) {
1458
1715
  const cutoff = olderThan.getTime();
1459
- await this.redis.zremrangebyrank(
1460
- this.timelineKey(),
1461
- 0,
1462
- Math.floor(cutoff / 1e3)
1463
- );
1716
+ await this.redis.zremrangebyrank(this.timelineKey(), 0, Math.floor(cutoff / 1e3));
1464
1717
  }
1465
1718
  };
1466
1719
 
@@ -1481,7 +1734,10 @@ async function createStorageAdapter(config) {
1481
1734
  });
1482
1735
  return new RedisStorageAdapter(redis, config.ttl);
1483
1736
  } catch (error) {
1484
- console.warn("[AgentShield] Failed to initialize Redis storage, falling back to memory:", error);
1737
+ console.warn(
1738
+ "[AgentShield] Failed to initialize Redis storage, falling back to memory:",
1739
+ error
1740
+ );
1485
1741
  return new MemoryStorageAdapter();
1486
1742
  }
1487
1743
  }
@@ -1543,7 +1799,9 @@ function createEnhancedAgentShieldMiddleware(config = {}) {
1543
1799
  detectorInitPromise = (async () => {
1544
1800
  const isEdgeRuntime = typeof globalThis.EdgeRuntime !== "undefined" || process.env.NEXT_RUNTIME === "edge";
1545
1801
  if (isEdgeRuntime) {
1546
- console.log("[AgentShield] Edge Runtime detected - using pattern detection");
1802
+ if (process.env.NODE_ENV !== "production") {
1803
+ console.debug("[AgentShield] Edge Runtime detected - using pattern detection");
1804
+ }
1547
1805
  detector = new EdgeSafeDetector();
1548
1806
  } else {
1549
1807
  try {
@@ -1553,17 +1811,27 @@ function createEnhancedAgentShieldMiddleware(config = {}) {
1553
1811
  const wasmAvailable = await wasmUtils.checkWasmAvailability();
1554
1812
  if (wasmAvailable) {
1555
1813
  const { EdgeAgentDetectorWrapperWithWasm: EdgeAgentDetectorWrapperWithWasm2 } = await Promise.resolve().then(() => (init_edge_detector_with_wasm(), edge_detector_with_wasm_exports));
1556
- console.log("[AgentShield] \u2705 WASM support detected - enhanced detection enabled");
1814
+ if (process.env.NODE_ENV !== "production") {
1815
+ console.debug(
1816
+ "[AgentShield] \u2705 WASM support detected - enhanced detection enabled"
1817
+ );
1818
+ }
1557
1819
  detector = new EdgeAgentDetectorWrapperWithWasm2({ enableWasm: true });
1558
1820
  if (requestUrl && "setBaseUrl" in detector) {
1559
1821
  detector.setBaseUrl(requestUrl);
1560
1822
  }
1561
1823
  } else {
1562
- console.log("[AgentShield] \u2139\uFE0F Using pattern-based detection (WASM not available)");
1824
+ if (process.env.NODE_ENV !== "production") {
1825
+ console.debug(
1826
+ "[AgentShield] \u2139\uFE0F Using pattern-based detection (WASM not available)"
1827
+ );
1828
+ }
1563
1829
  detector = new EdgeSafeDetector();
1564
1830
  }
1565
1831
  } catch (wasmError) {
1566
- console.log("[AgentShield] WASM utilities not available, using pattern detection");
1832
+ if (process.env.NODE_ENV !== "production") {
1833
+ console.debug("[AgentShield] WASM utilities not available, using pattern detection");
1834
+ }
1567
1835
  detector = new EdgeSafeDetector();
1568
1836
  }
1569
1837
  } catch (error) {
@@ -1608,7 +1876,10 @@ function createEnhancedAgentShieldMiddleware(config = {}) {
1608
1876
  if (result.isAgent && finalConfidence >= (config.confidenceThreshold ?? 0.7)) {
1609
1877
  if (sessionTrackingEnabled) {
1610
1878
  const storage = await getStorage();
1611
- const sessionId = sessionManager.generateSessionId(ipAddress || void 0, userAgent || void 0);
1879
+ const sessionId = sessionManager.generateSessionId(
1880
+ ipAddress || void 0,
1881
+ userAgent || void 0
1882
+ );
1612
1883
  const event = {
1613
1884
  eventId: `agent_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`,
1614
1885
  sessionId,
@@ -1674,15 +1945,18 @@ function createEnhancedAgentShieldMiddleware(config = {}) {
1674
1945
  { status }
1675
1946
  );
1676
1947
  response2.headers.set("x-agentshield-detected", "true");
1677
- response2.headers.set("x-agentshield-confidence", String(Math.round(finalConfidence * 100)));
1948
+ response2.headers.set(
1949
+ "x-agentshield-confidence",
1950
+ String(Math.round(finalConfidence * 100))
1951
+ );
1678
1952
  response2.headers.set("x-agentshield-agent", result.detectedAgent?.name || "Unknown");
1679
1953
  response2.headers.set("x-agentshield-verification", verificationMethod);
1680
1954
  return response2;
1681
1955
  }
1682
- case "log":
1956
+ case "log": {
1683
1957
  const isInteresting = finalConfidence >= 0.9 || result.detectedAgent?.name?.toLowerCase().includes("chatgpt") || result.detectedAgent?.name?.toLowerCase().includes("perplexity") || verificationMethod === "signature";
1684
- if (isInteresting) {
1685
- console.log(`[AgentShield] \u{1F916} AI Agent detected (${verificationMethod}):`, {
1958
+ if (isInteresting && process.env.NODE_ENV !== "production") {
1959
+ console.debug(`[AgentShield] \u{1F916} AI Agent detected (${verificationMethod}):`, {
1686
1960
  agent: result.detectedAgent?.name,
1687
1961
  confidence: `${(finalConfidence * 100).toFixed(0)}%`,
1688
1962
  path: pathWithQuery,
@@ -1690,6 +1964,7 @@ function createEnhancedAgentShieldMiddleware(config = {}) {
1690
1964
  });
1691
1965
  }
1692
1966
  break;
1967
+ }
1693
1968
  }
1694
1969
  }
1695
1970
  const response = NextResponse.next();
@@ -1708,6 +1983,294 @@ function createEnhancedAgentShieldMiddleware(config = {}) {
1708
1983
  };
1709
1984
  }
1710
1985
 
1986
+ // src/api-client.ts
1987
+ var DEFAULT_BASE_URL = "https://api.agentshield.ai";
1988
+ var DEFAULT_TIMEOUT = 5e3;
1989
+ var AgentShieldClient = class {
1990
+ apiKey;
1991
+ baseUrl;
1992
+ timeout;
1993
+ debug;
1994
+ constructor(config) {
1995
+ if (!config.apiKey) {
1996
+ throw new Error("AgentShield API key is required");
1997
+ }
1998
+ this.apiKey = config.apiKey;
1999
+ this.baseUrl = config.baseUrl || DEFAULT_BASE_URL;
2000
+ this.timeout = config.timeout || DEFAULT_TIMEOUT;
2001
+ this.debug = config.debug || false;
2002
+ }
2003
+ /**
2004
+ * Call the enforce API to check if a request should be allowed
2005
+ */
2006
+ async enforce(input) {
2007
+ const startTime = Date.now();
2008
+ try {
2009
+ const controller = new AbortController();
2010
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
2011
+ try {
2012
+ const response = await fetch(`${this.baseUrl}/api/v1/enforce`, {
2013
+ method: "POST",
2014
+ headers: {
2015
+ "Content-Type": "application/json",
2016
+ Authorization: `Bearer ${this.apiKey}`,
2017
+ "X-Request-ID": input.requestId || crypto.randomUUID()
2018
+ },
2019
+ body: JSON.stringify(input),
2020
+ signal: controller.signal
2021
+ });
2022
+ clearTimeout(timeoutId);
2023
+ const data = await response.json();
2024
+ if (this.debug) {
2025
+ console.log("[AgentShield] Enforce response:", {
2026
+ status: response.status,
2027
+ action: data.data?.decision.action,
2028
+ processingTimeMs: Date.now() - startTime
2029
+ });
2030
+ }
2031
+ if (!response.ok) {
2032
+ return {
2033
+ success: false,
2034
+ error: {
2035
+ code: `HTTP_${response.status}`,
2036
+ message: data.error?.message || `HTTP error: ${response.status}`
2037
+ }
2038
+ };
2039
+ }
2040
+ return data;
2041
+ } catch (error) {
2042
+ clearTimeout(timeoutId);
2043
+ throw error;
2044
+ }
2045
+ } catch (error) {
2046
+ if (error instanceof Error && error.name === "AbortError") {
2047
+ if (this.debug) {
2048
+ console.warn("[AgentShield] Request timed out");
2049
+ }
2050
+ return {
2051
+ success: false,
2052
+ error: {
2053
+ code: "TIMEOUT",
2054
+ message: `Request timed out after ${this.timeout}ms`
2055
+ }
2056
+ };
2057
+ }
2058
+ if (this.debug) {
2059
+ console.error("[AgentShield] Request failed:", error);
2060
+ }
2061
+ return {
2062
+ success: false,
2063
+ error: {
2064
+ code: "NETWORK_ERROR",
2065
+ message: error instanceof Error ? error.message : "Network request failed"
2066
+ }
2067
+ };
2068
+ }
2069
+ }
2070
+ /**
2071
+ * Quick check - returns just the action without full response parsing
2072
+ * Useful for very fast middleware that just needs allow/block
2073
+ */
2074
+ async quickCheck(input) {
2075
+ const result = await this.enforce(input);
2076
+ if (!result.success || !result.data) {
2077
+ return {
2078
+ action: "allow",
2079
+ error: result.error?.message
2080
+ };
2081
+ }
2082
+ return {
2083
+ action: result.data.decision.action
2084
+ };
2085
+ }
2086
+ };
2087
+ var clientInstance = null;
2088
+ function getAgentShieldClient(config) {
2089
+ if (!clientInstance) {
2090
+ const apiKey = config?.apiKey || process.env.AGENTSHIELD_API_KEY;
2091
+ if (!apiKey) {
2092
+ throw new Error(
2093
+ "AgentShield API key is required. Set AGENTSHIELD_API_KEY environment variable or pass apiKey in config."
2094
+ );
2095
+ }
2096
+ clientInstance = new AgentShieldClient({
2097
+ apiKey,
2098
+ baseUrl: config?.baseUrl || process.env.AGENTSHIELD_API_URL,
2099
+ timeout: config?.timeout,
2100
+ debug: config?.debug || process.env.AGENTSHIELD_DEBUG === "true"
2101
+ });
2102
+ }
2103
+ return clientInstance;
2104
+ }
2105
+ function resetAgentShieldClient() {
2106
+ clientInstance = null;
2107
+ }
2108
+
2109
+ // src/api-middleware.ts
2110
+ function matchPath(path, pattern) {
2111
+ if (pattern === path) return true;
2112
+ if (pattern.includes("*")) {
2113
+ const regexPattern = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
2114
+ return new RegExp(`^${regexPattern}$`).test(path);
2115
+ }
2116
+ if (pattern.endsWith("/")) {
2117
+ return path.startsWith(pattern) || path === pattern.slice(0, -1);
2118
+ }
2119
+ return path.startsWith(pattern);
2120
+ }
2121
+ function shouldSkipPath(path, skipPaths) {
2122
+ return skipPaths.some((pattern) => matchPath(path, pattern));
2123
+ }
2124
+ function shouldIncludePath(path, includePaths) {
2125
+ if (!includePaths || includePaths.length === 0) return true;
2126
+ return includePaths.some((pattern) => matchPath(path, pattern));
2127
+ }
2128
+ function buildBlockedResponse(decision, config) {
2129
+ const status = config.blockedResponse?.status ?? 403;
2130
+ const message = config.blockedResponse?.message ?? decision.message ?? "Access denied";
2131
+ const response = NextResponse.json(
2132
+ {
2133
+ error: message,
2134
+ code: "AGENT_BLOCKED",
2135
+ reason: decision.reason,
2136
+ agentType: decision.agentType
2137
+ },
2138
+ { status }
2139
+ );
2140
+ if (config.blockedResponse?.headers) {
2141
+ for (const [key, value] of Object.entries(config.blockedResponse.headers)) {
2142
+ response.headers.set(key, value);
2143
+ }
2144
+ }
2145
+ response.headers.set("X-AgentShield-Action", decision.action);
2146
+ response.headers.set("X-AgentShield-Reason", decision.reason);
2147
+ return response;
2148
+ }
2149
+ function buildRedirectResponse(request, decision, config) {
2150
+ const redirectUrl = config.redirectUrl || decision.redirectUrl || "/blocked";
2151
+ const url = new URL(redirectUrl, request.url);
2152
+ url.searchParams.set("reason", decision.reason);
2153
+ if (decision.agentType) {
2154
+ url.searchParams.set("agent", decision.agentType);
2155
+ }
2156
+ return NextResponse.redirect(url);
2157
+ }
2158
+ function withAgentShield(config = {}) {
2159
+ let client = null;
2160
+ const getClient = () => {
2161
+ if (!client) {
2162
+ client = getAgentShieldClient({
2163
+ apiKey: config.apiKey,
2164
+ baseUrl: config.apiUrl,
2165
+ timeout: config.timeout,
2166
+ debug: config.debug
2167
+ });
2168
+ }
2169
+ return client;
2170
+ };
2171
+ const defaultSkipPaths = [
2172
+ "/_next/static/*",
2173
+ "/_next/image/*",
2174
+ "/favicon.ico",
2175
+ "/robots.txt",
2176
+ "/sitemap.xml"
2177
+ ];
2178
+ const skipPaths = [...defaultSkipPaths, ...config.skipPaths || []];
2179
+ const failOpen = config.failOpen ?? true;
2180
+ return async function middleware(request) {
2181
+ const path = request.nextUrl.pathname;
2182
+ const startTime = Date.now();
2183
+ if (shouldSkipPath(path, skipPaths)) {
2184
+ return NextResponse.next();
2185
+ }
2186
+ if (!shouldIncludePath(path, config.includePaths)) {
2187
+ return NextResponse.next();
2188
+ }
2189
+ try {
2190
+ const result = await getClient().enforce({
2191
+ headers: Object.fromEntries(request.headers.entries()),
2192
+ userAgent: request.headers.get("user-agent") || void 0,
2193
+ ipAddress: request.ip || request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() || request.headers.get("x-real-ip") || void 0,
2194
+ path,
2195
+ url: request.url,
2196
+ method: request.method,
2197
+ requestId: request.headers.get("x-request-id") || void 0,
2198
+ options: {
2199
+ includeDetectionResult: config.debug
2200
+ }
2201
+ });
2202
+ if (!result.success || !result.data) {
2203
+ if (config.debug) {
2204
+ console.warn("[AgentShield] API error:", result.error);
2205
+ }
2206
+ if (failOpen) {
2207
+ return NextResponse.next();
2208
+ }
2209
+ return NextResponse.json(
2210
+ { error: "Security check failed", code: "API_ERROR" },
2211
+ { status: 503 }
2212
+ );
2213
+ }
2214
+ const decision = result.data.decision;
2215
+ if (config.debug) {
2216
+ console.log("[AgentShield] Decision:", {
2217
+ path,
2218
+ action: decision.action,
2219
+ isAgent: decision.isAgent,
2220
+ confidence: decision.confidence,
2221
+ agentName: decision.agentName,
2222
+ processingTimeMs: Date.now() - startTime
2223
+ });
2224
+ }
2225
+ if (decision.isAgent && config.onAgentDetected) {
2226
+ await config.onAgentDetected(request, decision);
2227
+ }
2228
+ switch (decision.action) {
2229
+ case "block": {
2230
+ if (config.customBlockedResponse) {
2231
+ return config.customBlockedResponse(request, decision);
2232
+ }
2233
+ if (config.onBlock === "redirect") {
2234
+ return buildRedirectResponse(request, decision, config);
2235
+ }
2236
+ return buildBlockedResponse(decision, config);
2237
+ }
2238
+ case "redirect": {
2239
+ return buildRedirectResponse(request, decision, config);
2240
+ }
2241
+ case "challenge": {
2242
+ return buildRedirectResponse(request, decision, config);
2243
+ }
2244
+ case "log":
2245
+ case "allow":
2246
+ default: {
2247
+ const response = NextResponse.next();
2248
+ if (decision.isAgent) {
2249
+ response.headers.set("X-AgentShield-Detected", "true");
2250
+ response.headers.set("X-AgentShield-Confidence", decision.confidence.toString());
2251
+ if (decision.agentName) {
2252
+ response.headers.set("X-AgentShield-Agent", decision.agentName);
2253
+ }
2254
+ }
2255
+ return response;
2256
+ }
2257
+ }
2258
+ } catch (error) {
2259
+ if (config.debug) {
2260
+ console.error("[AgentShield] Middleware error:", error);
2261
+ }
2262
+ if (failOpen) {
2263
+ return NextResponse.next();
2264
+ }
2265
+ return NextResponse.json(
2266
+ { error: "Security check failed", code: "MIDDLEWARE_ERROR" },
2267
+ { status: 503 }
2268
+ );
2269
+ }
2270
+ };
2271
+ }
2272
+ var agentShieldMiddleware = withAgentShield();
2273
+
1711
2274
  // src/index.ts
1712
2275
  var VERSION = "0.1.0";
1713
2276
  /**
@@ -1716,6 +2279,6 @@ var VERSION = "0.1.0";
1716
2279
  * @license MIT OR Apache-2.0
1717
2280
  */
1718
2281
 
1719
- export { EdgeSessionTracker, StatelessSessionChecker, VERSION, createAgentShieldMiddleware2 as createAgentShieldMiddleware, createAgentShieldMiddleware as createAgentShieldMiddlewareBase, createEnhancedAgentShieldMiddleware, createAgentShieldMiddleware2 as createMiddleware };
2282
+ export { AgentShieldClient, EdgeSessionTracker, StatelessSessionChecker, VERSION, agentShieldMiddleware, createAgentShieldMiddleware2 as createAgentShieldMiddleware, createAgentShieldMiddleware as createAgentShieldMiddlewareBase, createEnhancedAgentShieldMiddleware, createAgentShieldMiddleware2 as createMiddleware, getAgentShieldClient, resetAgentShieldClient, withAgentShield };
1720
2283
  //# sourceMappingURL=index.mjs.map
1721
2284
  //# sourceMappingURL=index.mjs.map