@agentix-security/nextjs 0.1.6 → 0.1.8

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.
package/dist/index.cjs CHANGED
@@ -418,6 +418,17 @@ async function fingerprint2(req) {
418
418
  const buf = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(data));
419
419
  return Array.from(new Uint8Array(buf)).map((b) => b.toString(16).padStart(2, "0")).join("");
420
420
  }
421
+ function agentScore(req) {
422
+ let score = 0;
423
+ const ua = (req.headers.get("user-agent") ?? "").toLowerCase();
424
+ if (/bot|crawler|spider|python-requests|curl\/|node-fetch|axios|go-http-client|claude-web|gptbot|chatgpt|google-extended|oai-searchbot|anthropic|llm/i.test(ua)) score += 0.8;
425
+ if (!ua) score += 0.6;
426
+ if (!req.headers.get("sec-fetch-site") && !req.headers.get("sec-ch-ua")) score += 0.3;
427
+ if (!req.headers.get("accept-language")) score += 0.15;
428
+ const accept = req.headers.get("accept") ?? "";
429
+ if (req.method === "GET" && !accept.includes("text/html")) score += 0.2;
430
+ return Math.min(score, 1);
431
+ }
421
432
  function bearer(req) {
422
433
  const auth = req.headers.get("authorization");
423
434
  if (!auth?.startsWith("Bearer ")) return null;
@@ -522,6 +533,46 @@ function agentixMiddleware(sdk) {
522
533
  expires_in: exp - iat
523
534
  });
524
535
  }
536
+ const isHumanPage = !pathname.startsWith("/api/") && !pathname.startsWith("/agent/") && !pathname.startsWith("/_next/") && !pathname.startsWith("/favicon");
537
+ if (isHumanPage && agentScore(req) >= 0.5) {
538
+ const registry = sdk.getIntentRegistry();
539
+ const tools = Object.fromEntries(
540
+ [...registry.entries()].map(([intent, entry]) => [intent, { routes: [...entry.routes] }])
541
+ );
542
+ void shipAudit2(cp, licenseKey, auditRow(sdk, req, pathname, 200, fp, {
543
+ trust_mode: "unmanaged_automation",
544
+ intent_scope: "none",
545
+ token_id: null,
546
+ decision: "allow",
547
+ decision_reason: "agent_redirected_to_lane",
548
+ policy_id: "agent-detection"
549
+ }));
550
+ return server_js.NextResponse.json({
551
+ service: "agentix-intent-sdk",
552
+ version: "0.2.0",
553
+ message: "AI agent detected. Use the agent API instead of the human-facing site.",
554
+ tenant_id: sdk.getResolvedTenantId(),
555
+ deployment_id: sdk.getDeploymentId(),
556
+ discovery: { well_known: `${baseUrl}/.well-known/ai-agent.json`, token_endpoint: `${baseUrl}/agent/v1/declare_intent` },
557
+ intents: [...registry.keys()],
558
+ tools
559
+ }, { headers: { "cache-control": "no-store" } });
560
+ }
561
+ if (req.method === "GET" && pathname === "/robots.txt") {
562
+ let upstream = "";
563
+ try {
564
+ const r = await fetch(`${baseUrl}/robots.txt`, { headers: { "user-agent": "agentix-sdk" } });
565
+ if (r.ok) upstream = await r.text();
566
+ } catch {
567
+ }
568
+ const directive = `
569
+ # Agentix agent discovery
570
+ Agent-Discovery: ${baseUrl}/.well-known/ai-agent.json
571
+ `;
572
+ return new server_js.NextResponse(upstream + directive, {
573
+ headers: { "content-type": "text/plain; charset=utf-8", "cache-control": "no-store" }
574
+ });
575
+ }
525
576
  const res = server_js.NextResponse.next();
526
577
  res.headers.set("link", `<${baseUrl}/.well-known/ai-agent.json>; rel="agent-discovery"`);
527
578
  return res;
package/dist/index.js CHANGED
@@ -416,6 +416,17 @@ async function fingerprint2(req) {
416
416
  const buf = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(data));
417
417
  return Array.from(new Uint8Array(buf)).map((b) => b.toString(16).padStart(2, "0")).join("");
418
418
  }
419
+ function agentScore(req) {
420
+ let score = 0;
421
+ const ua = (req.headers.get("user-agent") ?? "").toLowerCase();
422
+ if (/bot|crawler|spider|python-requests|curl\/|node-fetch|axios|go-http-client|claude-web|gptbot|chatgpt|google-extended|oai-searchbot|anthropic|llm/i.test(ua)) score += 0.8;
423
+ if (!ua) score += 0.6;
424
+ if (!req.headers.get("sec-fetch-site") && !req.headers.get("sec-ch-ua")) score += 0.3;
425
+ if (!req.headers.get("accept-language")) score += 0.15;
426
+ const accept = req.headers.get("accept") ?? "";
427
+ if (req.method === "GET" && !accept.includes("text/html")) score += 0.2;
428
+ return Math.min(score, 1);
429
+ }
419
430
  function bearer(req) {
420
431
  const auth = req.headers.get("authorization");
421
432
  if (!auth?.startsWith("Bearer ")) return null;
@@ -520,6 +531,46 @@ function agentixMiddleware(sdk) {
520
531
  expires_in: exp - iat
521
532
  });
522
533
  }
534
+ const isHumanPage = !pathname.startsWith("/api/") && !pathname.startsWith("/agent/") && !pathname.startsWith("/_next/") && !pathname.startsWith("/favicon");
535
+ if (isHumanPage && agentScore(req) >= 0.5) {
536
+ const registry = sdk.getIntentRegistry();
537
+ const tools = Object.fromEntries(
538
+ [...registry.entries()].map(([intent, entry]) => [intent, { routes: [...entry.routes] }])
539
+ );
540
+ void shipAudit2(cp, licenseKey, auditRow(sdk, req, pathname, 200, fp, {
541
+ trust_mode: "unmanaged_automation",
542
+ intent_scope: "none",
543
+ token_id: null,
544
+ decision: "allow",
545
+ decision_reason: "agent_redirected_to_lane",
546
+ policy_id: "agent-detection"
547
+ }));
548
+ return NextResponse.json({
549
+ service: "agentix-intent-sdk",
550
+ version: "0.2.0",
551
+ message: "AI agent detected. Use the agent API instead of the human-facing site.",
552
+ tenant_id: sdk.getResolvedTenantId(),
553
+ deployment_id: sdk.getDeploymentId(),
554
+ discovery: { well_known: `${baseUrl}/.well-known/ai-agent.json`, token_endpoint: `${baseUrl}/agent/v1/declare_intent` },
555
+ intents: [...registry.keys()],
556
+ tools
557
+ }, { headers: { "cache-control": "no-store" } });
558
+ }
559
+ if (req.method === "GET" && pathname === "/robots.txt") {
560
+ let upstream = "";
561
+ try {
562
+ const r = await fetch(`${baseUrl}/robots.txt`, { headers: { "user-agent": "agentix-sdk" } });
563
+ if (r.ok) upstream = await r.text();
564
+ } catch {
565
+ }
566
+ const directive = `
567
+ # Agentix agent discovery
568
+ Agent-Discovery: ${baseUrl}/.well-known/ai-agent.json
569
+ `;
570
+ return new NextResponse(upstream + directive, {
571
+ headers: { "content-type": "text/plain; charset=utf-8", "cache-control": "no-store" }
572
+ });
573
+ }
523
574
  const res = NextResponse.next();
524
575
  res.headers.set("link", `<${baseUrl}/.well-known/ai-agent.json>; rel="agent-discovery"`);
525
576
  return res;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentix-security/nextjs",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "Agentix Next.js adapter — AI agent intent-based authorization for Next.js apps",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",