@kya-os/agentshield-nextjs 0.2.12 → 0.3.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 (74) hide show
  1. package/dist/.tsbuildinfo +1 -0
  2. package/dist/api-client.d.mts +6 -1
  3. package/dist/api-client.d.ts +6 -1
  4. package/dist/api-client.js +1 -1
  5. package/dist/api-client.js.map +1 -1
  6. package/dist/api-client.mjs +1 -1
  7. package/dist/api-client.mjs.map +1 -1
  8. package/dist/api-middleware.d.mts +13 -0
  9. package/dist/api-middleware.d.ts +13 -0
  10. package/dist/api-middleware.js +146 -24
  11. package/dist/api-middleware.js.map +1 -1
  12. package/dist/api-middleware.mjs +146 -24
  13. package/dist/api-middleware.mjs.map +1 -1
  14. package/dist/create-middleware.js +565 -487
  15. package/dist/create-middleware.js.map +1 -1
  16. package/dist/create-middleware.mjs +565 -487
  17. package/dist/create-middleware.mjs.map +1 -1
  18. package/dist/edge/index.js +69 -46
  19. package/dist/edge/index.js.map +1 -1
  20. package/dist/edge/index.mjs +69 -46
  21. package/dist/edge/index.mjs.map +1 -1
  22. package/dist/edge-detector-wrapper.js +9 -1
  23. package/dist/edge-detector-wrapper.js.map +1 -1
  24. package/dist/edge-detector-wrapper.mjs +9 -1
  25. package/dist/edge-detector-wrapper.mjs.map +1 -1
  26. package/dist/edge-runtime-loader.d.mts +1 -0
  27. package/dist/edge-runtime-loader.d.ts +1 -0
  28. package/dist/edge-runtime-loader.js +19 -3
  29. package/dist/edge-runtime-loader.js.map +1 -1
  30. package/dist/edge-runtime-loader.mjs +19 -3
  31. package/dist/edge-runtime-loader.mjs.map +1 -1
  32. package/dist/edge-wasm-middleware.d.mts +1 -0
  33. package/dist/edge-wasm-middleware.d.ts +1 -0
  34. package/dist/edge-wasm-middleware.js +10 -2
  35. package/dist/edge-wasm-middleware.js.map +1 -1
  36. package/dist/edge-wasm-middleware.mjs +11 -3
  37. package/dist/edge-wasm-middleware.mjs.map +1 -1
  38. package/dist/enhanced-middleware.js +48 -20
  39. package/dist/enhanced-middleware.js.map +1 -1
  40. package/dist/enhanced-middleware.mjs +49 -21
  41. package/dist/enhanced-middleware.mjs.map +1 -1
  42. package/dist/index.d.mts +1 -1
  43. package/dist/index.d.ts +1 -1
  44. package/dist/index.js +260 -107
  45. package/dist/index.js.map +1 -1
  46. package/dist/index.mjs +261 -108
  47. package/dist/index.mjs.map +1 -1
  48. package/dist/middleware.js +565 -487
  49. package/dist/middleware.js.map +1 -1
  50. package/dist/middleware.mjs +565 -487
  51. package/dist/middleware.mjs.map +1 -1
  52. package/dist/policy.d.mts +16 -4
  53. package/dist/policy.d.ts +16 -4
  54. package/dist/policy.js +14 -10
  55. package/dist/policy.js.map +1 -1
  56. package/dist/policy.mjs +14 -10
  57. package/dist/policy.mjs.map +1 -1
  58. package/dist/session-tracker.js +13 -19
  59. package/dist/session-tracker.js.map +1 -1
  60. package/dist/session-tracker.mjs +13 -19
  61. package/dist/session-tracker.mjs.map +1 -1
  62. package/dist/signature-verifier.js +9 -1
  63. package/dist/signature-verifier.js.map +1 -1
  64. package/dist/signature-verifier.mjs +9 -1
  65. package/dist/signature-verifier.mjs.map +1 -1
  66. package/dist/wasm-middleware.d.mts +1 -0
  67. package/dist/wasm-middleware.d.ts +1 -0
  68. package/dist/wasm-middleware.js +15 -15
  69. package/dist/wasm-middleware.js.map +1 -1
  70. package/dist/wasm-middleware.mjs +15 -15
  71. package/dist/wasm-middleware.mjs.map +1 -1
  72. package/package.json +5 -5
  73. package/wasm/agentshield_wasm.d.ts +2 -2
  74. package/wasm/agentshield_wasm_bg.wasm +0 -0
package/dist/index.js CHANGED
@@ -253,7 +253,15 @@ var init_edge_detector_with_wasm = __esm({
253
253
  }
254
254
  const signaturePresent = !!(normalizedHeaders["signature"] || normalizedHeaders["signature-input"]);
255
255
  const signatureAgent = normalizedHeaders["signature-agent"];
256
- if (signatureAgent?.includes("chatgpt.com")) {
256
+ const isChatGPT = (() => {
257
+ try {
258
+ const url = new URL(signatureAgent?.replace(/^"|"$/g, "") || "");
259
+ return url.hostname === "chatgpt.com" || url.hostname.endsWith(".chatgpt.com");
260
+ } catch {
261
+ return false;
262
+ }
263
+ })();
264
+ if (isChatGPT) {
257
265
  confidence = 85;
258
266
  reasons.push("signature_agent:chatgpt");
259
267
  detectedAgent = { type: "chatgpt", name: "ChatGPT" };
@@ -780,7 +788,15 @@ async function verifyAgentSignature(method, path, headers) {
780
788
  }
781
789
  let agent;
782
790
  let agentKey;
783
- if (signatureAgent === '"https://chatgpt.com"' || signatureAgent?.includes("chatgpt.com")) {
791
+ const isChatGPT = signatureAgent === '"https://chatgpt.com"' || (() => {
792
+ try {
793
+ const url = new URL(signatureAgent?.replace(/^"|"$/g, "") || "");
794
+ return url.hostname === "chatgpt.com" || url.hostname.endsWith(".chatgpt.com");
795
+ } catch {
796
+ return false;
797
+ }
798
+ })();
799
+ if (isChatGPT) {
784
800
  agent = "ChatGPT";
785
801
  agentKey = "chatgpt";
786
802
  }
@@ -1077,11 +1093,6 @@ var EdgeAgentDetectorWrapper = class {
1077
1093
  return;
1078
1094
  }
1079
1095
  };
1080
-
1081
- // src/middleware.ts
1082
- init_edge_detector_with_wasm();
1083
-
1084
- // src/session-tracker.ts
1085
1096
  var EdgeSessionTracker = class {
1086
1097
  config;
1087
1098
  constructor(config) {
@@ -1098,7 +1109,7 @@ var EdgeSessionTracker = class {
1098
1109
  */
1099
1110
  async track(_request, response, result) {
1100
1111
  try {
1101
- if (!this.config.enabled || !result.isAgent) {
1112
+ if (!this.config.enabled || !agentshieldShared.shouldEnforce(result)) {
1102
1113
  return response;
1103
1114
  }
1104
1115
  const sessionData = {
@@ -1173,9 +1184,7 @@ var EdgeSessionTracker = class {
1173
1184
  for (let i = 0; i < encoded.length; i++) {
1174
1185
  obfuscated[i] = (encoded[i] || 0) ^ key.charCodeAt(i % key.length);
1175
1186
  }
1176
- return btoa(
1177
- Array.from(obfuscated, (byte) => String.fromCharCode(byte)).join("")
1178
- );
1187
+ return btoa(Array.from(obfuscated, (byte) => String.fromCharCode(byte)).join(""));
1179
1188
  } catch (error) {
1180
1189
  return btoa(data);
1181
1190
  }
@@ -1200,9 +1209,9 @@ var EdgeSessionTracker = class {
1200
1209
  var StatelessSessionChecker = class {
1201
1210
  static check(headers) {
1202
1211
  try {
1203
- const agent = headers["x-agentshield-session-agent"];
1204
- const confidence = headers["x-agentshield-session-confidence"];
1205
- const sessionId = headers["x-agentshield-session-id"];
1212
+ const agent = headers["kya-session-agent"];
1213
+ const confidence = headers["kya-session-confidence"];
1214
+ const sessionId = headers["kya-session-id"];
1206
1215
  if (agent && confidence && sessionId) {
1207
1216
  return {
1208
1217
  id: sessionId,
@@ -1232,33 +1241,49 @@ var StatelessSessionChecker = class {
1232
1241
  static setHeaders(response, session) {
1233
1242
  try {
1234
1243
  if (response.setHeader) {
1235
- response.setHeader("X-AgentShield-Session-Agent", session.agent);
1236
- response.setHeader(
1237
- "X-AgentShield-Session-Confidence",
1238
- session.confidence.toString()
1239
- );
1240
- response.setHeader("X-AgentShield-Session-Id", session.id);
1244
+ response.setHeader("KYA-Session-Agent", session.agent);
1245
+ response.setHeader("KYA-Session-Confidence", session.confidence.toString());
1246
+ response.setHeader("KYA-Session-Id", session.id);
1241
1247
  } else if (response.headers && response.headers.set) {
1242
- response.headers.set("x-agentshield-session-agent", session.agent);
1243
- response.headers.set(
1244
- "x-agentshield-session-confidence",
1245
- session.confidence.toString()
1246
- );
1247
- response.headers.set("x-agentshield-session-id", session.id);
1248
+ response.headers.set("kya-session-agent", session.agent);
1249
+ response.headers.set("kya-session-confidence", session.confidence.toString());
1250
+ response.headers.set("kya-session-id", session.id);
1248
1251
  }
1249
1252
  } catch {
1250
1253
  }
1251
1254
  }
1252
1255
  };
1253
1256
 
1254
- // src/middleware.ts
1257
+ // src/utils.ts
1258
+ function getClientIp(request) {
1259
+ const forwardedFor = request.headers.get("x-forwarded-for");
1260
+ if (forwardedFor) {
1261
+ const ip = forwardedFor.split(",")[0]?.trim();
1262
+ if (ip) return ip;
1263
+ }
1264
+ const realIp = request.headers.get("x-real-ip");
1265
+ if (realIp) return realIp;
1266
+ const cfIp = request.headers.get("cf-connecting-ip");
1267
+ if (cfIp) return cfIp;
1268
+ const clientIp = request.headers.get("x-client-ip");
1269
+ if (clientIp) return clientIp;
1270
+ return void 0;
1271
+ }
1272
+ function safeHostname(url) {
1273
+ try {
1274
+ return new URL(url).hostname;
1275
+ } catch {
1276
+ return "this site";
1277
+ }
1278
+ }
1255
1279
  function createAgentShieldMiddleware(config = {}) {
1256
- const detector = config.enableWasm ? new EdgeAgentDetectorWrapperWithWasm({ enableWasm: true }) : new EdgeAgentDetectorWrapper(config);
1280
+ let detector = config.enableWasm ? null : new EdgeAgentDetectorWrapper(config);
1281
+ let detectorInitPromise = null;
1257
1282
  const sessionTracker = config.sessionTracking?.enabled || config.enableWasm ? new EdgeSessionTracker({
1258
1283
  enabled: true,
1259
1284
  ...config.sessionTracking
1260
1285
  }) : null;
1261
- if (config.events) {
1286
+ if (detector && config.events) {
1262
1287
  Object.entries(config.events).forEach(([event, handler]) => {
1263
1288
  if (handler) {
1264
1289
  detector.on(event, handler);
@@ -1279,6 +1304,23 @@ function createAgentShieldMiddleware(config = {}) {
1279
1304
  } = config;
1280
1305
  return async (request) => {
1281
1306
  try {
1307
+ if (!detector) {
1308
+ if (!detectorInitPromise) {
1309
+ detectorInitPromise = (async () => {
1310
+ const { EdgeAgentDetectorWrapperWithWasm: EdgeAgentDetectorWrapperWithWasm2 } = await Promise.resolve().then(() => (init_edge_detector_with_wasm(), edge_detector_with_wasm_exports));
1311
+ detector = new EdgeAgentDetectorWrapperWithWasm2({ enableWasm: true });
1312
+ if (config.events) {
1313
+ Object.entries(config.events).forEach(([event, handler]) => {
1314
+ if (handler) {
1315
+ detector.on(event, handler);
1316
+ }
1317
+ });
1318
+ }
1319
+ })();
1320
+ }
1321
+ await detectorInitPromise;
1322
+ }
1323
+ const activeDetector = detector;
1282
1324
  const shouldSkip = skipPaths.some((pattern) => {
1283
1325
  if (typeof pattern === "string") {
1284
1326
  return request.nextUrl.pathname.startsWith(pattern);
@@ -1292,11 +1334,11 @@ function createAgentShieldMiddleware(config = {}) {
1292
1334
  const existingSession = sessionTracker ? await sessionTracker.check(request) : null;
1293
1335
  if (existingSession) {
1294
1336
  const response2 = server.NextResponse.next();
1295
- response2.headers.set("x-agentshield-detected", "true");
1296
- response2.headers.set("x-agentshield-agent", existingSession.agent);
1297
- response2.headers.set("x-agentshield-confidence", existingSession.confidence.toString());
1298
- response2.headers.set("x-agentshield-session", "continued");
1299
- response2.headers.set("x-agentshield-session-id", existingSession.id);
1337
+ response2.headers.set("kya-detected", "true");
1338
+ response2.headers.set("kya-agent", existingSession.agent);
1339
+ response2.headers.set("kya-confidence", existingSession.confidence.toString());
1340
+ response2.headers.set("kya-session", "continued");
1341
+ response2.headers.set("kya-session-id", existingSession.id);
1300
1342
  request.agentShield = {
1301
1343
  result: {
1302
1344
  isAgent: true,
@@ -1316,17 +1358,17 @@ function createAgentShieldMiddleware(config = {}) {
1316
1358
  };
1317
1359
  const context2 = {
1318
1360
  userAgent: request.headers.get("user-agent") || "",
1319
- ipAddress: (request.ip ?? request.headers.get("x-forwarded-for")) || "",
1361
+ ipAddress: getClientIp(request) || "",
1320
1362
  headers: Object.fromEntries(request.headers.entries()),
1321
1363
  url: request.url,
1322
1364
  method: request.method,
1323
1365
  timestamp: /* @__PURE__ */ new Date()
1324
1366
  };
1325
- detector.emit("agent.session.continued", existingSession, context2);
1367
+ activeDetector.emit("agent.session.continued", existingSession, context2);
1326
1368
  return response2;
1327
1369
  }
1328
1370
  const userAgent = request.headers.get("user-agent");
1329
- const ipAddress = request.ip ?? request.headers.get("x-forwarded-for");
1371
+ const ipAddress = getClientIp(request);
1330
1372
  const url = new URL(request.url);
1331
1373
  const pathWithQuery = url.pathname + url.search;
1332
1374
  const context = {
@@ -1338,15 +1380,19 @@ function createAgentShieldMiddleware(config = {}) {
1338
1380
  method: request.method,
1339
1381
  timestamp: /* @__PURE__ */ new Date()
1340
1382
  };
1341
- const result = await detector.analyze(context);
1342
- if (result.isAgent && result.confidence >= (config.confidenceThreshold ?? 70)) {
1383
+ const result = await activeDetector.analyze(context);
1384
+ const decision = agentshieldShared.evaluateEnforcement(result, {
1385
+ confidenceThreshold: config.confidenceThreshold,
1386
+ defaultAction: onAgentDetected
1387
+ });
1388
+ if (decision.shouldNotify) {
1343
1389
  if (onDetection) {
1344
1390
  const customResponse = await onDetection(request, result);
1345
1391
  if (customResponse) {
1346
1392
  return customResponse;
1347
1393
  }
1348
1394
  }
1349
- switch (onAgentDetected) {
1395
+ switch (decision.action) {
1350
1396
  case "block": {
1351
1397
  const response2 = server.NextResponse.json(
1352
1398
  {
@@ -1362,11 +1408,17 @@ function createAgentShieldMiddleware(config = {}) {
1362
1408
  response2.headers.set(key, value);
1363
1409
  });
1364
1410
  }
1365
- detector.emit("agent.blocked", result, context);
1411
+ activeDetector.emit("agent.blocked", result, context);
1366
1412
  return response2;
1367
1413
  }
1368
- case "redirect":
1369
- return server.NextResponse.redirect(new URL(redirectUrl, request.url));
1414
+ case "redirect": {
1415
+ const redirectTarget = new URL(redirectUrl, request.url);
1416
+ const agentName = result.detectedAgent?.name;
1417
+ if (agentName && !redirectTarget.searchParams.has("agent")) {
1418
+ redirectTarget.searchParams.set("agent", agentName.toLowerCase());
1419
+ }
1420
+ return server.NextResponse.redirect(redirectTarget);
1421
+ }
1370
1422
  case "rewrite":
1371
1423
  return server.NextResponse.rewrite(new URL(rewriteUrl, request.url));
1372
1424
  case "log":
@@ -1382,9 +1434,7 @@ function createAgentShieldMiddleware(config = {}) {
1382
1434
  break;
1383
1435
  case "allow":
1384
1436
  default:
1385
- if (result.isAgent && result.confidence >= (config.confidenceThreshold ?? 70)) {
1386
- detector.emit("agent.allowed", result, context);
1387
- }
1437
+ activeDetector.emit("agent.allowed", result, context);
1388
1438
  break;
1389
1439
  }
1390
1440
  }
@@ -1393,15 +1443,15 @@ function createAgentShieldMiddleware(config = {}) {
1393
1443
  skipped: false
1394
1444
  };
1395
1445
  let response = server.NextResponse.next();
1396
- response.headers.set("x-agentshield-detected", result.isAgent.toString());
1397
- response.headers.set("x-agentshield-confidence", result.confidence.toString());
1446
+ response.headers.set("kya-detected", result.isAgent.toString());
1447
+ response.headers.set("kya-confidence", result.confidence.toString());
1398
1448
  if (result.detectedAgent?.name) {
1399
- response.headers.set("x-agentshield-agent", result.detectedAgent.name);
1449
+ response.headers.set("kya-agent", result.detectedAgent.name);
1400
1450
  }
1401
- if (sessionTracker && result.isAgent && result.confidence >= (config.confidenceThreshold ?? 70)) {
1451
+ if (sessionTracker && decision.shouldNotify) {
1402
1452
  response = await sessionTracker.track(request, response, result);
1403
- response.headers.set("x-agentshield-session", "new");
1404
- detector.emit("agent.session.started", result, context);
1453
+ response.headers.set("kya-session", "new");
1454
+ activeDetector.emit("agent.session.started", result, context);
1405
1455
  }
1406
1456
  return response;
1407
1457
  } catch (error) {
@@ -1742,8 +1792,6 @@ async function createStorageAdapter(config) {
1742
1792
  }
1743
1793
  return new MemoryStorageAdapter();
1744
1794
  }
1745
-
1746
- // src/enhanced-middleware.ts
1747
1795
  var SessionManager = class {
1748
1796
  sessionLastActivity = /* @__PURE__ */ new Map();
1749
1797
  generateSessionId(ipAddress, userAgent) {
@@ -1850,7 +1898,7 @@ function createEnhancedAgentShieldMiddleware(config = {}) {
1850
1898
  return server.NextResponse.next();
1851
1899
  }
1852
1900
  const userAgent = request.headers.get("user-agent");
1853
- const ipAddress = request.ip ?? request.headers.get("x-forwarded-for");
1901
+ const ipAddress = getClientIp(request);
1854
1902
  const url = new URL(request.url);
1855
1903
  const pathWithQuery = url.pathname + url.search;
1856
1904
  const context = {
@@ -1865,14 +1913,21 @@ function createEnhancedAgentShieldMiddleware(config = {}) {
1865
1913
  const result = await activeDetector.analyze(context);
1866
1914
  let finalConfidence = result.confidence;
1867
1915
  let verificationMethod = result.verificationMethod || "pattern";
1868
- if (result.isAgent && wasmConfidenceUtils) {
1916
+ if (agentshieldShared.shouldEnforce(result) && wasmConfidenceUtils) {
1869
1917
  const reasons = result.reasons || [];
1870
1918
  if (wasmConfidenceUtils.shouldIndicateWasmVerification(result.confidence)) {
1871
1919
  finalConfidence = wasmConfidenceUtils.getWasmConfidenceBoost(result.confidence, reasons);
1872
1920
  verificationMethod = wasmConfidenceUtils.getVerificationMethod(finalConfidence, reasons);
1873
1921
  }
1874
1922
  }
1875
- if (result.isAgent && finalConfidence >= (config.confidenceThreshold ?? 0.7)) {
1923
+ const decision = agentshieldShared.evaluateEnforcement(
1924
+ { ...result, confidence: finalConfidence },
1925
+ {
1926
+ confidenceThreshold: config.confidenceThreshold,
1927
+ defaultAction: config.onAgentDetected
1928
+ }
1929
+ );
1930
+ if (decision.shouldNotify) {
1876
1931
  if (sessionTrackingEnabled) {
1877
1932
  const storage = await getStorage();
1878
1933
  const sessionId = sessionManager.generateSessionId(
@@ -1936,26 +1991,23 @@ function createEnhancedAgentShieldMiddleware(config = {}) {
1936
1991
  if (config.onDetection) {
1937
1992
  await config.onDetection(result, context);
1938
1993
  }
1939
- switch (config.onAgentDetected) {
1994
+ switch (decision.action) {
1940
1995
  case "block": {
1941
1996
  const { status = 403, message = "Access denied: AI agent detected" } = config.blockedResponse || {};
1942
1997
  const response2 = server.NextResponse.json(
1943
1998
  { error: message, detected: true, confidence: finalConfidence },
1944
1999
  { status }
1945
2000
  );
1946
- response2.headers.set("x-agentshield-detected", "true");
1947
- response2.headers.set(
1948
- "x-agentshield-confidence",
1949
- String(Math.round(finalConfidence * 100))
1950
- );
1951
- response2.headers.set("x-agentshield-agent", result.detectedAgent?.name || "Unknown");
1952
- response2.headers.set("x-agentshield-verification", verificationMethod);
2001
+ response2.headers.set("kya-detected", "true");
2002
+ response2.headers.set("kya-confidence", String(Math.round(finalConfidence * 100)));
2003
+ response2.headers.set("kya-agent", result.detectedAgent?.name || "Unknown");
2004
+ response2.headers.set("kya-verification", verificationMethod);
1953
2005
  return response2;
1954
2006
  }
1955
2007
  case "log": {
1956
2008
  const isInteresting = finalConfidence >= 0.9 || result.detectedAgent?.name?.toLowerCase().includes("chatgpt") || result.detectedAgent?.name?.toLowerCase().includes("perplexity") || verificationMethod === "signature";
1957
2009
  if (isInteresting && process.env.NODE_ENV !== "production") {
1958
- console.debug(`[AgentShield] \u{1F916} AI Agent detected (${verificationMethod}):`, {
2010
+ console.debug(`[AgentShield] AI Agent detected (${verificationMethod}):`, {
1959
2011
  agent: result.detectedAgent?.name,
1960
2012
  confidence: `${(finalConfidence * 100).toFixed(0)}%`,
1961
2013
  path: pathWithQuery,
@@ -1968,14 +2020,14 @@ function createEnhancedAgentShieldMiddleware(config = {}) {
1968
2020
  }
1969
2021
  const response = server.NextResponse.next();
1970
2022
  if (result.isAgent) {
1971
- response.headers.set("x-agentshield-detected", "true");
1972
- response.headers.set("x-agentshield-confidence", String(Math.round(finalConfidence * 100)));
1973
- response.headers.set("x-agentshield-agent", result.detectedAgent?.name || "Unknown");
1974
- response.headers.set("x-agentshield-verification", verificationMethod);
2023
+ response.headers.set("kya-detected", "true");
2024
+ response.headers.set("kya-confidence", String(Math.round(finalConfidence * 100)));
2025
+ response.headers.set("kya-agent", result.detectedAgent?.name || "Unknown");
2026
+ response.headers.set("kya-verification", verificationMethod);
1975
2027
  if (finalConfidence > 0.9) {
1976
- response.headers.set("x-ai-visitor", "true");
1977
- response.headers.set("x-ai-confidence", finalConfidence.toString());
1978
- response.headers.set("x-ai-verification", verificationMethod);
2028
+ response.headers.set("kya-ai-visitor", "true");
2029
+ response.headers.set("kya-ai-confidence", finalConfidence.toString());
2030
+ response.headers.set("kya-ai-verification", verificationMethod);
1979
2031
  }
1980
2032
  }
1981
2033
  return response;
@@ -1984,7 +2036,7 @@ function createEnhancedAgentShieldMiddleware(config = {}) {
1984
2036
 
1985
2037
  // src/api-client.ts
1986
2038
  var DEFAULT_BASE_URL = "https://kya.vouched.id";
1987
- var EDGE_DETECT_URL = "https://detect.kya-os.ai";
2039
+ var EDGE_DETECT_URL = "https://detect.checkpoint-gateway.ai";
1988
2040
  var DEFAULT_TIMEOUT = 5e3;
1989
2041
  var AgentShieldClient = class {
1990
2042
  apiKey;
@@ -2174,6 +2226,80 @@ function getAgentShieldClient(config) {
2174
2226
  function resetAgentShieldClient() {
2175
2227
  clientInstance = null;
2176
2228
  }
2229
+ var MCP_I_DOCS_URL = "https://docs.knowthat.ai/mcp-i/getting-started";
2230
+ var DEFAULT_CONNECT_PATH = "/connect";
2231
+ function buildAgentInstructionResponse(request, decision, redirectUrl) {
2232
+ const resolved = resolveUrl(redirectUrl ?? DEFAULT_CONNECT_PATH, request.url);
2233
+ const agentName = decision.agentName || decision.agentType || "unknown";
2234
+ if (!resolved.searchParams.has("agent")) {
2235
+ resolved.searchParams.set("agent", agentName.toLowerCase());
2236
+ }
2237
+ const authUrl = resolved.toString();
2238
+ const hostname = safeHostname(request.url);
2239
+ const body = {
2240
+ // Markdown-formatted so clients that render markdown (Claude Desktop,
2241
+ // ChatGPT web) surface the URL as a clickable link. Tone mirrors the
2242
+ // gateway response so messaging stays consistent across platforms.
2243
+ message: `I can't access ${hostname} yet \u2014 this site checks AI assistants at the front door.
2244
+
2245
+ **To give me access, open this link:**
2246
+ [Connect securely to ${hostname}](${authUrl})
2247
+
2248
+ It only takes a moment and you won't need to do it again. Once you're done, ask me to try again and I'll connect through the verified channel automatically.`,
2249
+ user_action_required: {
2250
+ action: `Connect securely to ${hostname}`,
2251
+ url: authUrl,
2252
+ reason: `${hostname} checks AI assistants before they connect. Open the link to give your assistant a verified key.`
2253
+ },
2254
+ mcp_i: {
2255
+ version: "1.0",
2256
+ action: "authenticate",
2257
+ authorization_url: authUrl,
2258
+ flow: {
2259
+ type: "oauth2_delegation",
2260
+ steps: [
2261
+ "1. Direct your user to the authorization_url",
2262
+ "2. User reviews requested scopes and grants consent",
2263
+ "3. Receive delegation credential (JWT)",
2264
+ "4. Include credential in KYA-Delegation header",
2265
+ "5. Retry this request with the proof"
2266
+ ]
2267
+ },
2268
+ retry_instructions: {
2269
+ header: "KYA-Delegation",
2270
+ format: "JWT delegation credential from authorization flow"
2271
+ },
2272
+ documentation: MCP_I_DOCS_URL
2273
+ },
2274
+ error: "mcp_authentication_required",
2275
+ code: "AGENT_REQUIRES_DELEGATION",
2276
+ detection: {
2277
+ agent_type: decision.agentType || "ai_agent",
2278
+ agent_name: decision.agentName || "Unknown Agent",
2279
+ confidence: decision.confidence
2280
+ }
2281
+ };
2282
+ const response = server.NextResponse.json(body, { status: 401 });
2283
+ response.headers.set("WWW-Authenticate", `KYA realm="api", authorization_uri="${authUrl}"`);
2284
+ response.headers.set(
2285
+ "Link",
2286
+ `<${authUrl}>; rel="kya-authorize", <${MCP_I_DOCS_URL}>; rel="help"`
2287
+ );
2288
+ response.headers.set("KYA-Auth-Required", "true");
2289
+ response.headers.set("KYA-Auth-Url", authUrl);
2290
+ response.headers.set("KYA-Action", "instruct");
2291
+ response.headers.set("KYA-Detected-Agent", agentName);
2292
+ response.headers.set("KYA-Confidence", decision.confidence.toString());
2293
+ response.headers.set("Cache-Control", "no-store");
2294
+ return response;
2295
+ }
2296
+ function resolveUrl(target, baseUrl2) {
2297
+ try {
2298
+ return new URL(target, baseUrl2);
2299
+ } catch {
2300
+ return new URL(DEFAULT_CONNECT_PATH, baseUrl2);
2301
+ }
2302
+ }
2177
2303
 
2178
2304
  // src/api-middleware.ts
2179
2305
  function matchPath(path, pattern) {
@@ -2194,35 +2320,52 @@ function shouldIncludePath(path, includePaths) {
2194
2320
  if (!includePaths || includePaths.length === 0) return true;
2195
2321
  return includePaths.some((pattern) => matchPath(path, pattern));
2196
2322
  }
2197
- function buildBlockedResponse(decision, config) {
2323
+ function buildBlockedResponse(request, decision, config) {
2198
2324
  const status = config.blockedResponse?.status ?? 403;
2199
- const redirectUrl = config.redirectUrl || decision.redirectUrl;
2200
- const baseMessage = config.blockedResponse?.message ?? decision.message ?? "Access denied";
2201
- const errorMessage = redirectUrl ? `${baseMessage}. Please go to ${redirectUrl}` : baseMessage;
2202
- const message = config.blockedResponse?.message ?? decision.message ?? (redirectUrl ? `AI agents are not permitted on this resource. Please go to ${redirectUrl} for more information.` : "AI agents are not permitted on this resource.");
2203
- const responseBody = {
2204
- error: errorMessage,
2325
+ const message = config.blockedResponse?.message ?? decision.message ?? "Access denied";
2326
+ const recoveryUrl = resolveRecoveryUrl(request, config.redirectUrl || decision.redirectUrl);
2327
+ const body = {
2328
+ error: message,
2205
2329
  code: "AGENT_BLOCKED",
2206
2330
  reason: decision.reason,
2207
- agentType: decision.agentType,
2208
- message
2331
+ agentType: decision.agentType
2209
2332
  };
2210
- if (redirectUrl) {
2211
- responseBody.redirectUrl = redirectUrl;
2333
+ if (recoveryUrl) {
2334
+ const hostname = safeHostname(request.url);
2335
+ body.user_action_required = {
2336
+ action: `Connect securely to ${hostname}`,
2337
+ url: recoveryUrl,
2338
+ reason: `${hostname} blocks unverified AI assistants. Open the link to give your assistant a verified key and try again.`
2339
+ };
2340
+ body.message = `I can't access ${hostname} \u2014 this site blocks unverified AI assistants.
2341
+
2342
+ **To give me access, open this link:**
2343
+ [Connect securely to ${hostname}](${recoveryUrl})
2344
+
2345
+ Once you're done, ask me to try again.`;
2212
2346
  }
2213
- const response = server.NextResponse.json(responseBody, { status });
2347
+ const response = server.NextResponse.json(body, { status });
2214
2348
  if (config.blockedResponse?.headers) {
2215
2349
  for (const [key, value] of Object.entries(config.blockedResponse.headers)) {
2216
2350
  response.headers.set(key, value);
2217
2351
  }
2218
2352
  }
2219
- response.headers.set("X-AgentShield-Action", decision.action);
2220
- response.headers.set("X-AgentShield-Reason", decision.reason);
2221
- if (redirectUrl) {
2222
- response.headers.set("X-AgentShield-Redirect", redirectUrl);
2353
+ if (recoveryUrl) {
2354
+ response.headers.set("Link", `<${recoveryUrl}>; rel="kya-authorize"`);
2355
+ response.headers.set("KYA-Auth-Url", recoveryUrl);
2223
2356
  }
2357
+ response.headers.set("KYA-Action", decision.action);
2358
+ response.headers.set("KYA-Reason", decision.reason);
2224
2359
  return response;
2225
2360
  }
2361
+ function resolveRecoveryUrl(request, target) {
2362
+ if (!target) return void 0;
2363
+ try {
2364
+ return new URL(target, request.url).toString();
2365
+ } catch {
2366
+ return void 0;
2367
+ }
2368
+ }
2226
2369
  function buildRedirectResponse(request, decision, config) {
2227
2370
  const redirectUrl = config.redirectUrl || decision.redirectUrl || "/blocked";
2228
2371
  const url = new URL(redirectUrl, request.url);
@@ -2267,7 +2410,7 @@ function withAgentShield(config = {}) {
2267
2410
  try {
2268
2411
  const client2 = getClient();
2269
2412
  const userAgent = request.headers.get("user-agent") || void 0;
2270
- const ipAddress = request.ip || request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() || request.headers.get("x-real-ip") || void 0;
2413
+ const ipAddress = getClientIp(request);
2271
2414
  const result = await client2.enforce({
2272
2415
  headers: Object.fromEntries(request.headers.entries()),
2273
2416
  userAgent,
@@ -2318,6 +2461,7 @@ function withAgentShield(config = {}) {
2318
2461
  if (decision.isAgent && config.onAgentDetected) {
2319
2462
  await config.onAgentDetected(request, decision);
2320
2463
  }
2464
+ const redirectMode = config.redirectMode ?? "instruct";
2321
2465
  switch (decision.action) {
2322
2466
  case "block": {
2323
2467
  if (config.customBlockedResponse) {
@@ -2326,10 +2470,15 @@ function withAgentShield(config = {}) {
2326
2470
  if (config.onBlock === "redirect") {
2327
2471
  return buildRedirectResponse(request, decision, config);
2328
2472
  }
2329
- return buildBlockedResponse(decision, config);
2473
+ return buildBlockedResponse(request, decision, config);
2330
2474
  }
2331
- case "redirect": {
2332
- return buildRedirectResponse(request, decision, config);
2475
+ case "redirect":
2476
+ case "instruct": {
2477
+ if (redirectMode === "http" && decision.action === "redirect") {
2478
+ return buildRedirectResponse(request, decision, config);
2479
+ }
2480
+ const targetUrl = config.redirectUrl || decision.redirectUrl;
2481
+ return buildAgentInstructionResponse(request, decision, targetUrl);
2333
2482
  }
2334
2483
  case "challenge": {
2335
2484
  return buildRedirectResponse(request, decision, config);
@@ -2339,10 +2488,10 @@ function withAgentShield(config = {}) {
2339
2488
  default: {
2340
2489
  const response = server.NextResponse.next();
2341
2490
  if (decision.isAgent) {
2342
- response.headers.set("X-AgentShield-Detected", "true");
2343
- response.headers.set("X-AgentShield-Confidence", decision.confidence.toString());
2491
+ response.headers.set("KYA-Detected", "true");
2492
+ response.headers.set("KYA-Confidence", decision.confidence.toString());
2344
2493
  if (decision.agentName) {
2345
- response.headers.set("X-AgentShield-Agent", decision.agentName);
2494
+ response.headers.set("KYA-Agent", decision.agentName);
2346
2495
  }
2347
2496
  }
2348
2497
  return response;
@@ -2400,24 +2549,28 @@ function buildBlockedResponse2(decision, config) {
2400
2549
  response.headers.set(key, value);
2401
2550
  }
2402
2551
  }
2403
- response.headers.set("X-AgentShield-Action", decision.action);
2404
- response.headers.set("X-AgentShield-Reason", decision.reason);
2405
- response.headers.set("X-AgentShield-MatchType", decision.matchType);
2552
+ response.headers.set("KYA-Action", decision.action);
2553
+ response.headers.set("KYA-Reason", decision.reason);
2554
+ response.headers.set("KYA-Match-Type", decision.matchType);
2406
2555
  return response;
2407
2556
  }
2408
- function buildRedirectResponse2(request, decision, config) {
2557
+ function buildRedirectResponse2(request, decision, config, detection) {
2409
2558
  const redirectUrl = decision.redirectUrl || config.redirectUrl || "/blocked";
2410
2559
  const url = new URL(redirectUrl, request.url);
2411
2560
  url.searchParams.set("reason", decision.reason);
2412
2561
  if (decision.ruleId) {
2413
2562
  url.searchParams.set("ruleId", decision.ruleId);
2414
2563
  }
2564
+ const agentName = detection?.detectedAgent?.name;
2565
+ if (agentName && !url.searchParams.has("agent")) {
2566
+ url.searchParams.set("agent", agentName.toLowerCase());
2567
+ }
2415
2568
  return server.NextResponse.redirect(url);
2416
2569
  }
2417
- function buildChallengeResponse(request, decision, config) {
2418
- return buildRedirectResponse2(request, decision, config);
2570
+ function buildChallengeResponse(request, decision, config, detection) {
2571
+ return buildRedirectResponse2(request, decision, config, detection);
2419
2572
  }
2420
- async function handlePolicyDecision(request, decision, config) {
2573
+ async function handlePolicyDecision(request, decision, config, detection) {
2421
2574
  switch (decision.action) {
2422
2575
  case agentshieldShared.ENFORCEMENT_ACTIONS.BLOCK:
2423
2576
  if (config.customBlockedResponse) {
@@ -2425,9 +2578,9 @@ async function handlePolicyDecision(request, decision, config) {
2425
2578
  }
2426
2579
  return buildBlockedResponse2(decision, config);
2427
2580
  case agentshieldShared.ENFORCEMENT_ACTIONS.REDIRECT:
2428
- return buildRedirectResponse2(request, decision, config);
2581
+ return buildRedirectResponse2(request, decision, config, detection);
2429
2582
  case agentshieldShared.ENFORCEMENT_ACTIONS.CHALLENGE:
2430
- return buildChallengeResponse(request, decision, config);
2583
+ return buildChallengeResponse(request, decision, config, detection);
2431
2584
  case agentshieldShared.ENFORCEMENT_ACTIONS.LOG:
2432
2585
  console.log("[AgentShield] Policy decision (log):", {
2433
2586
  path: request.nextUrl.pathname,
@@ -2501,7 +2654,7 @@ async function applyPolicy(request, detection, config) {
2501
2654
  if (config.onPolicyDecision) {
2502
2655
  await config.onPolicyDecision(request, decision, context);
2503
2656
  }
2504
- return await handlePolicyDecision(request, decision, config);
2657
+ return await handlePolicyDecision(request, decision, config, detection);
2505
2658
  } catch (error) {
2506
2659
  if (config.debug) {
2507
2660
  console.error("[AgentShield] Policy evaluation error:", error);