@kya-os/agentshield-nextjs 0.1.25 → 0.1.28

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.
@@ -0,0 +1,176 @@
1
+ 'use strict';
2
+
3
+ // src/session-tracker.ts
4
+ var EdgeSessionTracker = class {
5
+ config;
6
+ constructor(config) {
7
+ this.config = {
8
+ enabled: config.enabled,
9
+ cookieName: config.cookieName || "__agentshield_session",
10
+ cookieMaxAge: config.cookieMaxAge || 3600,
11
+ // 1 hour default
12
+ encryptionKey: config.encryptionKey || process.env.AGENTSHIELD_SECRET || "agentshield-default-key"
13
+ };
14
+ }
15
+ /**
16
+ * Track a new AI agent session
17
+ */
18
+ async track(_request, response, result) {
19
+ try {
20
+ if (!this.config.enabled || !result.isAgent) {
21
+ return response;
22
+ }
23
+ const sessionData = {
24
+ id: crypto.randomUUID(),
25
+ agent: result.detectedAgent?.name || "unknown",
26
+ confidence: result.confidence,
27
+ detectedAt: Date.now(),
28
+ expires: Date.now() + this.config.cookieMaxAge * 1e3
29
+ };
30
+ const encrypted = await this.encrypt(JSON.stringify(sessionData));
31
+ response.cookies.set(this.config.cookieName, encrypted, {
32
+ httpOnly: true,
33
+ secure: process.env.NODE_ENV === "production",
34
+ sameSite: "lax",
35
+ maxAge: this.config.cookieMaxAge,
36
+ path: "/"
37
+ });
38
+ return response;
39
+ } catch (error) {
40
+ if (process.env.DEBUG_AGENTSHIELD) {
41
+ console.warn("AgentShield: Failed to track session:", error);
42
+ }
43
+ return response;
44
+ }
45
+ }
46
+ /**
47
+ * Check for existing AI agent session
48
+ */
49
+ async check(request) {
50
+ try {
51
+ if (!this.config.enabled) {
52
+ return null;
53
+ }
54
+ const cookie = request.cookies.get(this.config.cookieName);
55
+ if (!cookie?.value) {
56
+ return null;
57
+ }
58
+ const decrypted = await this.decrypt(cookie.value);
59
+ const session = JSON.parse(decrypted);
60
+ if (session.expires < Date.now()) {
61
+ return null;
62
+ }
63
+ return session;
64
+ } catch (error) {
65
+ if (process.env.DEBUG_AGENTSHIELD) {
66
+ console.warn("AgentShield: Failed to check session:", error);
67
+ }
68
+ return null;
69
+ }
70
+ }
71
+ /**
72
+ * Clear an existing session
73
+ */
74
+ clear(response) {
75
+ try {
76
+ response.cookies.delete(this.config.cookieName);
77
+ } catch (error) {
78
+ if (process.env.DEBUG_AGENTSHIELD) {
79
+ console.warn("AgentShield: Failed to clear session:", error);
80
+ }
81
+ }
82
+ return response;
83
+ }
84
+ /**
85
+ * Simple encryption using Web Crypto API (Edge-compatible)
86
+ */
87
+ async encrypt(data) {
88
+ try {
89
+ const key = this.config.encryptionKey;
90
+ const encoded = new TextEncoder().encode(data);
91
+ const obfuscated = new Uint8Array(encoded.length);
92
+ for (let i = 0; i < encoded.length; i++) {
93
+ obfuscated[i] = (encoded[i] || 0) ^ key.charCodeAt(i % key.length);
94
+ }
95
+ return btoa(
96
+ Array.from(obfuscated, (byte) => String.fromCharCode(byte)).join("")
97
+ );
98
+ } catch (error) {
99
+ return btoa(data);
100
+ }
101
+ }
102
+ /**
103
+ * Simple decryption (Edge-compatible)
104
+ */
105
+ async decrypt(data) {
106
+ try {
107
+ const key = this.config.encryptionKey;
108
+ const decoded = Uint8Array.from(atob(data), (c) => c.charCodeAt(0));
109
+ const deobfuscated = new Uint8Array(decoded.length);
110
+ for (let i = 0; i < decoded.length; i++) {
111
+ deobfuscated[i] = (decoded[i] || 0) ^ key.charCodeAt(i % key.length);
112
+ }
113
+ return new TextDecoder().decode(deobfuscated);
114
+ } catch (error) {
115
+ return atob(data);
116
+ }
117
+ }
118
+ };
119
+ var StatelessSessionChecker = class {
120
+ static check(headers) {
121
+ try {
122
+ const agent = headers["x-agentshield-session-agent"];
123
+ const confidence = headers["x-agentshield-session-confidence"];
124
+ const sessionId = headers["x-agentshield-session-id"];
125
+ if (agent && confidence && sessionId) {
126
+ return {
127
+ id: sessionId,
128
+ agent,
129
+ confidence: parseFloat(confidence),
130
+ detectedAt: Date.now(),
131
+ expires: Date.now() + 36e5
132
+ // 1 hour
133
+ };
134
+ }
135
+ const cookieHeader = headers["cookie"];
136
+ if (cookieHeader && cookieHeader.includes("__agentshield_session=")) {
137
+ const match = cookieHeader.match(/__agentshield_session=([^;]+)/);
138
+ if (match && match[1]) {
139
+ try {
140
+ const decoded = atob(match[1]);
141
+ return JSON.parse(decoded);
142
+ } catch {
143
+ }
144
+ }
145
+ }
146
+ return null;
147
+ } catch {
148
+ return null;
149
+ }
150
+ }
151
+ static setHeaders(response, session) {
152
+ try {
153
+ if (response.setHeader) {
154
+ response.setHeader("X-AgentShield-Session-Agent", session.agent);
155
+ response.setHeader(
156
+ "X-AgentShield-Session-Confidence",
157
+ session.confidence.toString()
158
+ );
159
+ response.setHeader("X-AgentShield-Session-Id", session.id);
160
+ } else if (response.headers && response.headers.set) {
161
+ response.headers.set("x-agentshield-session-agent", session.agent);
162
+ response.headers.set(
163
+ "x-agentshield-session-confidence",
164
+ session.confidence.toString()
165
+ );
166
+ response.headers.set("x-agentshield-session-id", session.id);
167
+ }
168
+ } catch {
169
+ }
170
+ }
171
+ };
172
+
173
+ exports.EdgeSessionTracker = EdgeSessionTracker;
174
+ exports.StatelessSessionChecker = StatelessSessionChecker;
175
+ //# sourceMappingURL=session-tracker.js.map
176
+ //# sourceMappingURL=session-tracker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/session-tracker.ts"],"names":[],"mappings":"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","file":"session-tracker.js","sourcesContent":["/**\n * Edge-compatible session tracking for AI agents\n * Uses cookie-based storage to work in Edge Runtime\n */\n\nimport type { NextRequest, NextResponse } from 'next/server';\nimport type { DetectionResult } from '@kya-os/agentshield';\n\nexport interface SessionData {\n id: string;\n agent: string;\n confidence: number;\n detectedAt: number;\n expires: number;\n}\n\nexport interface SessionTrackingConfig {\n enabled: boolean;\n cookieName?: string;\n cookieMaxAge?: number; // in seconds\n encryptionKey?: string;\n}\n\nexport class EdgeSessionTracker {\n private readonly config: Required<SessionTrackingConfig>;\n\n constructor(config: SessionTrackingConfig) {\n this.config = {\n enabled: config.enabled,\n cookieName: config.cookieName || '__agentshield_session',\n cookieMaxAge: config.cookieMaxAge || 3600, // 1 hour default\n encryptionKey:\n config.encryptionKey ||\n process.env.AGENTSHIELD_SECRET ||\n 'agentshield-default-key',\n };\n }\n\n /**\n * Track a new AI agent session\n */\n async track(\n _request: NextRequest,\n response: NextResponse,\n result: DetectionResult\n ): Promise<NextResponse> {\n try {\n if (!this.config.enabled || !result.isAgent) {\n return response;\n }\n\n const sessionData: SessionData = {\n id: crypto.randomUUID(),\n agent: result.detectedAgent?.name || 'unknown',\n confidence: result.confidence,\n detectedAt: Date.now(),\n expires: Date.now() + this.config.cookieMaxAge * 1000,\n };\n\n // Encrypt session data for security\n const encrypted = await this.encrypt(JSON.stringify(sessionData));\n\n // Set secure httpOnly cookie\n response.cookies.set(this.config.cookieName, encrypted, {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n maxAge: this.config.cookieMaxAge,\n path: '/',\n });\n\n return response;\n } catch (error) {\n // Fail gracefully - log error but don't break request\n if (process.env.DEBUG_AGENTSHIELD) {\n console.warn('AgentShield: Failed to track session:', error);\n }\n return response;\n }\n }\n\n /**\n * Check for existing AI agent session\n */\n async check(request: NextRequest): Promise<SessionData | null> {\n try {\n if (!this.config.enabled) {\n return null;\n }\n\n const cookie = request.cookies.get(this.config.cookieName);\n if (!cookie?.value) {\n return null;\n }\n\n // Decrypt and parse session data\n const decrypted = await this.decrypt(cookie.value);\n const session: SessionData = JSON.parse(decrypted);\n\n // Check if session is expired\n if (session.expires < Date.now()) {\n return null;\n }\n\n return session;\n } catch (error) {\n // Fail gracefully - invalid or corrupted session\n if (process.env.DEBUG_AGENTSHIELD) {\n console.warn('AgentShield: Failed to check session:', error);\n }\n return null;\n }\n }\n\n /**\n * Clear an existing session\n */\n clear(response: NextResponse): NextResponse {\n try {\n response.cookies.delete(this.config.cookieName);\n } catch (error) {\n // Fail gracefully\n if (process.env.DEBUG_AGENTSHIELD) {\n console.warn('AgentShield: Failed to clear session:', error);\n }\n }\n return response;\n }\n\n /**\n * Simple encryption using Web Crypto API (Edge-compatible)\n */\n private async encrypt(data: string): Promise<string> {\n try {\n // For Edge Runtime, use simple base64 encoding with obfuscation\n // In production, consider using Web Crypto API subtle.encrypt()\n const key = this.config.encryptionKey;\n const encoded = new TextEncoder().encode(data);\n\n // Simple XOR obfuscation\n const obfuscated = new Uint8Array(encoded.length);\n for (let i = 0; i < encoded.length; i++) {\n obfuscated[i] = (encoded[i] || 0) ^ key.charCodeAt(i % key.length);\n }\n\n // Convert to base64\n return btoa(\n Array.from(obfuscated, byte => String.fromCharCode(byte)).join('')\n );\n } catch (error) {\n // Fallback to simple base64 if encryption fails\n return btoa(data);\n }\n }\n\n /**\n * Simple decryption (Edge-compatible)\n */\n private async decrypt(data: string): Promise<string> {\n try {\n const key = this.config.encryptionKey;\n const decoded = Uint8Array.from(atob(data), c => c.charCodeAt(0));\n\n // Reverse XOR obfuscation\n const deobfuscated = new Uint8Array(decoded.length);\n for (let i = 0; i < decoded.length; i++) {\n deobfuscated[i] = (decoded[i] || 0) ^ key.charCodeAt(i % key.length);\n }\n\n return new TextDecoder().decode(deobfuscated);\n } catch (error) {\n // Fallback to simple base64 if decryption fails\n return atob(data);\n }\n }\n}\n\n/**\n * Stateless session checker for non-Next.js environments (Express, etc.)\n * Uses a combination of headers to identify continued sessions\n */\nexport class StatelessSessionChecker {\n static check(headers: Record<string, string>): SessionData | null {\n try {\n // Check for session headers (set by previous response)\n const agent = headers['x-agentshield-session-agent'];\n const confidence = headers['x-agentshield-session-confidence'];\n const sessionId = headers['x-agentshield-session-id'];\n\n if (agent && confidence && sessionId) {\n return {\n id: sessionId,\n agent,\n confidence: parseFloat(confidence),\n detectedAt: Date.now(),\n expires: Date.now() + 3600000, // 1 hour\n };\n }\n\n // Check for cookie-based session (if cookies are parsed)\n const cookieHeader = headers['cookie'];\n if (cookieHeader && cookieHeader.includes('__agentshield_session=')) {\n // Simple cookie parsing\n const match = cookieHeader.match(/__agentshield_session=([^;]+)/);\n if (match && match[1]) {\n try {\n const decoded = atob(match[1]);\n return JSON.parse(decoded);\n } catch {\n // Invalid session data\n }\n }\n }\n\n return null;\n } catch {\n return null;\n }\n }\n\n static setHeaders(response: any, session: SessionData): void {\n try {\n // Set session headers for stateless tracking\n if (response.setHeader) {\n response.setHeader('X-AgentShield-Session-Agent', session.agent);\n response.setHeader(\n 'X-AgentShield-Session-Confidence',\n session.confidence.toString()\n );\n response.setHeader('X-AgentShield-Session-Id', session.id);\n } else if (response.headers && response.headers.set) {\n response.headers.set('x-agentshield-session-agent', session.agent);\n response.headers.set(\n 'x-agentshield-session-confidence',\n session.confidence.toString()\n );\n response.headers.set('x-agentshield-session-id', session.id);\n }\n } catch {\n // Fail gracefully\n }\n }\n}\n"]}
@@ -0,0 +1,173 @@
1
+ // src/session-tracker.ts
2
+ var EdgeSessionTracker = class {
3
+ config;
4
+ constructor(config) {
5
+ this.config = {
6
+ enabled: config.enabled,
7
+ cookieName: config.cookieName || "__agentshield_session",
8
+ cookieMaxAge: config.cookieMaxAge || 3600,
9
+ // 1 hour default
10
+ encryptionKey: config.encryptionKey || process.env.AGENTSHIELD_SECRET || "agentshield-default-key"
11
+ };
12
+ }
13
+ /**
14
+ * Track a new AI agent session
15
+ */
16
+ async track(_request, response, result) {
17
+ try {
18
+ if (!this.config.enabled || !result.isAgent) {
19
+ return response;
20
+ }
21
+ const sessionData = {
22
+ id: crypto.randomUUID(),
23
+ agent: result.detectedAgent?.name || "unknown",
24
+ confidence: result.confidence,
25
+ detectedAt: Date.now(),
26
+ expires: Date.now() + this.config.cookieMaxAge * 1e3
27
+ };
28
+ const encrypted = await this.encrypt(JSON.stringify(sessionData));
29
+ response.cookies.set(this.config.cookieName, encrypted, {
30
+ httpOnly: true,
31
+ secure: process.env.NODE_ENV === "production",
32
+ sameSite: "lax",
33
+ maxAge: this.config.cookieMaxAge,
34
+ path: "/"
35
+ });
36
+ return response;
37
+ } catch (error) {
38
+ if (process.env.DEBUG_AGENTSHIELD) {
39
+ console.warn("AgentShield: Failed to track session:", error);
40
+ }
41
+ return response;
42
+ }
43
+ }
44
+ /**
45
+ * Check for existing AI agent session
46
+ */
47
+ async check(request) {
48
+ try {
49
+ if (!this.config.enabled) {
50
+ return null;
51
+ }
52
+ const cookie = request.cookies.get(this.config.cookieName);
53
+ if (!cookie?.value) {
54
+ return null;
55
+ }
56
+ const decrypted = await this.decrypt(cookie.value);
57
+ const session = JSON.parse(decrypted);
58
+ if (session.expires < Date.now()) {
59
+ return null;
60
+ }
61
+ return session;
62
+ } catch (error) {
63
+ if (process.env.DEBUG_AGENTSHIELD) {
64
+ console.warn("AgentShield: Failed to check session:", error);
65
+ }
66
+ return null;
67
+ }
68
+ }
69
+ /**
70
+ * Clear an existing session
71
+ */
72
+ clear(response) {
73
+ try {
74
+ response.cookies.delete(this.config.cookieName);
75
+ } catch (error) {
76
+ if (process.env.DEBUG_AGENTSHIELD) {
77
+ console.warn("AgentShield: Failed to clear session:", error);
78
+ }
79
+ }
80
+ return response;
81
+ }
82
+ /**
83
+ * Simple encryption using Web Crypto API (Edge-compatible)
84
+ */
85
+ async encrypt(data) {
86
+ try {
87
+ const key = this.config.encryptionKey;
88
+ const encoded = new TextEncoder().encode(data);
89
+ const obfuscated = new Uint8Array(encoded.length);
90
+ for (let i = 0; i < encoded.length; i++) {
91
+ obfuscated[i] = (encoded[i] || 0) ^ key.charCodeAt(i % key.length);
92
+ }
93
+ return btoa(
94
+ Array.from(obfuscated, (byte) => String.fromCharCode(byte)).join("")
95
+ );
96
+ } catch (error) {
97
+ return btoa(data);
98
+ }
99
+ }
100
+ /**
101
+ * Simple decryption (Edge-compatible)
102
+ */
103
+ async decrypt(data) {
104
+ try {
105
+ const key = this.config.encryptionKey;
106
+ const decoded = Uint8Array.from(atob(data), (c) => c.charCodeAt(0));
107
+ const deobfuscated = new Uint8Array(decoded.length);
108
+ for (let i = 0; i < decoded.length; i++) {
109
+ deobfuscated[i] = (decoded[i] || 0) ^ key.charCodeAt(i % key.length);
110
+ }
111
+ return new TextDecoder().decode(deobfuscated);
112
+ } catch (error) {
113
+ return atob(data);
114
+ }
115
+ }
116
+ };
117
+ var StatelessSessionChecker = class {
118
+ static check(headers) {
119
+ try {
120
+ const agent = headers["x-agentshield-session-agent"];
121
+ const confidence = headers["x-agentshield-session-confidence"];
122
+ const sessionId = headers["x-agentshield-session-id"];
123
+ if (agent && confidence && sessionId) {
124
+ return {
125
+ id: sessionId,
126
+ agent,
127
+ confidence: parseFloat(confidence),
128
+ detectedAt: Date.now(),
129
+ expires: Date.now() + 36e5
130
+ // 1 hour
131
+ };
132
+ }
133
+ const cookieHeader = headers["cookie"];
134
+ if (cookieHeader && cookieHeader.includes("__agentshield_session=")) {
135
+ const match = cookieHeader.match(/__agentshield_session=([^;]+)/);
136
+ if (match && match[1]) {
137
+ try {
138
+ const decoded = atob(match[1]);
139
+ return JSON.parse(decoded);
140
+ } catch {
141
+ }
142
+ }
143
+ }
144
+ return null;
145
+ } catch {
146
+ return null;
147
+ }
148
+ }
149
+ static setHeaders(response, session) {
150
+ try {
151
+ if (response.setHeader) {
152
+ response.setHeader("X-AgentShield-Session-Agent", session.agent);
153
+ response.setHeader(
154
+ "X-AgentShield-Session-Confidence",
155
+ session.confidence.toString()
156
+ );
157
+ response.setHeader("X-AgentShield-Session-Id", session.id);
158
+ } else if (response.headers && response.headers.set) {
159
+ response.headers.set("x-agentshield-session-agent", session.agent);
160
+ response.headers.set(
161
+ "x-agentshield-session-confidence",
162
+ session.confidence.toString()
163
+ );
164
+ response.headers.set("x-agentshield-session-id", session.id);
165
+ }
166
+ } catch {
167
+ }
168
+ }
169
+ };
170
+
171
+ export { EdgeSessionTracker, StatelessSessionChecker };
172
+ //# sourceMappingURL=session-tracker.mjs.map
173
+ //# sourceMappingURL=session-tracker.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/session-tracker.ts"],"names":[],"mappings":"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","file":"session-tracker.mjs","sourcesContent":["/**\n * Edge-compatible session tracking for AI agents\n * Uses cookie-based storage to work in Edge Runtime\n */\n\nimport type { NextRequest, NextResponse } from 'next/server';\nimport type { DetectionResult } from '@kya-os/agentshield';\n\nexport interface SessionData {\n id: string;\n agent: string;\n confidence: number;\n detectedAt: number;\n expires: number;\n}\n\nexport interface SessionTrackingConfig {\n enabled: boolean;\n cookieName?: string;\n cookieMaxAge?: number; // in seconds\n encryptionKey?: string;\n}\n\nexport class EdgeSessionTracker {\n private readonly config: Required<SessionTrackingConfig>;\n\n constructor(config: SessionTrackingConfig) {\n this.config = {\n enabled: config.enabled,\n cookieName: config.cookieName || '__agentshield_session',\n cookieMaxAge: config.cookieMaxAge || 3600, // 1 hour default\n encryptionKey:\n config.encryptionKey ||\n process.env.AGENTSHIELD_SECRET ||\n 'agentshield-default-key',\n };\n }\n\n /**\n * Track a new AI agent session\n */\n async track(\n _request: NextRequest,\n response: NextResponse,\n result: DetectionResult\n ): Promise<NextResponse> {\n try {\n if (!this.config.enabled || !result.isAgent) {\n return response;\n }\n\n const sessionData: SessionData = {\n id: crypto.randomUUID(),\n agent: result.detectedAgent?.name || 'unknown',\n confidence: result.confidence,\n detectedAt: Date.now(),\n expires: Date.now() + this.config.cookieMaxAge * 1000,\n };\n\n // Encrypt session data for security\n const encrypted = await this.encrypt(JSON.stringify(sessionData));\n\n // Set secure httpOnly cookie\n response.cookies.set(this.config.cookieName, encrypted, {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n maxAge: this.config.cookieMaxAge,\n path: '/',\n });\n\n return response;\n } catch (error) {\n // Fail gracefully - log error but don't break request\n if (process.env.DEBUG_AGENTSHIELD) {\n console.warn('AgentShield: Failed to track session:', error);\n }\n return response;\n }\n }\n\n /**\n * Check for existing AI agent session\n */\n async check(request: NextRequest): Promise<SessionData | null> {\n try {\n if (!this.config.enabled) {\n return null;\n }\n\n const cookie = request.cookies.get(this.config.cookieName);\n if (!cookie?.value) {\n return null;\n }\n\n // Decrypt and parse session data\n const decrypted = await this.decrypt(cookie.value);\n const session: SessionData = JSON.parse(decrypted);\n\n // Check if session is expired\n if (session.expires < Date.now()) {\n return null;\n }\n\n return session;\n } catch (error) {\n // Fail gracefully - invalid or corrupted session\n if (process.env.DEBUG_AGENTSHIELD) {\n console.warn('AgentShield: Failed to check session:', error);\n }\n return null;\n }\n }\n\n /**\n * Clear an existing session\n */\n clear(response: NextResponse): NextResponse {\n try {\n response.cookies.delete(this.config.cookieName);\n } catch (error) {\n // Fail gracefully\n if (process.env.DEBUG_AGENTSHIELD) {\n console.warn('AgentShield: Failed to clear session:', error);\n }\n }\n return response;\n }\n\n /**\n * Simple encryption using Web Crypto API (Edge-compatible)\n */\n private async encrypt(data: string): Promise<string> {\n try {\n // For Edge Runtime, use simple base64 encoding with obfuscation\n // In production, consider using Web Crypto API subtle.encrypt()\n const key = this.config.encryptionKey;\n const encoded = new TextEncoder().encode(data);\n\n // Simple XOR obfuscation\n const obfuscated = new Uint8Array(encoded.length);\n for (let i = 0; i < encoded.length; i++) {\n obfuscated[i] = (encoded[i] || 0) ^ key.charCodeAt(i % key.length);\n }\n\n // Convert to base64\n return btoa(\n Array.from(obfuscated, byte => String.fromCharCode(byte)).join('')\n );\n } catch (error) {\n // Fallback to simple base64 if encryption fails\n return btoa(data);\n }\n }\n\n /**\n * Simple decryption (Edge-compatible)\n */\n private async decrypt(data: string): Promise<string> {\n try {\n const key = this.config.encryptionKey;\n const decoded = Uint8Array.from(atob(data), c => c.charCodeAt(0));\n\n // Reverse XOR obfuscation\n const deobfuscated = new Uint8Array(decoded.length);\n for (let i = 0; i < decoded.length; i++) {\n deobfuscated[i] = (decoded[i] || 0) ^ key.charCodeAt(i % key.length);\n }\n\n return new TextDecoder().decode(deobfuscated);\n } catch (error) {\n // Fallback to simple base64 if decryption fails\n return atob(data);\n }\n }\n}\n\n/**\n * Stateless session checker for non-Next.js environments (Express, etc.)\n * Uses a combination of headers to identify continued sessions\n */\nexport class StatelessSessionChecker {\n static check(headers: Record<string, string>): SessionData | null {\n try {\n // Check for session headers (set by previous response)\n const agent = headers['x-agentshield-session-agent'];\n const confidence = headers['x-agentshield-session-confidence'];\n const sessionId = headers['x-agentshield-session-id'];\n\n if (agent && confidence && sessionId) {\n return {\n id: sessionId,\n agent,\n confidence: parseFloat(confidence),\n detectedAt: Date.now(),\n expires: Date.now() + 3600000, // 1 hour\n };\n }\n\n // Check for cookie-based session (if cookies are parsed)\n const cookieHeader = headers['cookie'];\n if (cookieHeader && cookieHeader.includes('__agentshield_session=')) {\n // Simple cookie parsing\n const match = cookieHeader.match(/__agentshield_session=([^;]+)/);\n if (match && match[1]) {\n try {\n const decoded = atob(match[1]);\n return JSON.parse(decoded);\n } catch {\n // Invalid session data\n }\n }\n }\n\n return null;\n } catch {\n return null;\n }\n }\n\n static setHeaders(response: any, session: SessionData): void {\n try {\n // Set session headers for stateless tracking\n if (response.setHeader) {\n response.setHeader('X-AgentShield-Session-Agent', session.agent);\n response.setHeader(\n 'X-AgentShield-Session-Confidence',\n session.confidence.toString()\n );\n response.setHeader('X-AgentShield-Session-Id', session.id);\n } else if (response.headers && response.headers.set) {\n response.headers.set('x-agentshield-session-agent', session.agent);\n response.headers.set(\n 'x-agentshield-session-confidence',\n session.confidence.toString()\n );\n response.headers.set('x-agentshield-session-id', session.id);\n }\n } catch {\n // Fail gracefully\n }\n }\n}\n"]}
@@ -46,6 +46,34 @@ interface NextJSMiddlewareConfig extends Partial<AgentShieldConfig> {
46
46
  * Confidence threshold for agent detection
47
47
  */
48
48
  confidenceThreshold?: number;
49
+ /**
50
+ * Enable WASM for enhanced detection
51
+ */
52
+ enableWasm?: boolean;
53
+ /**
54
+ * Session tracking configuration
55
+ */
56
+ sessionTracking?: {
57
+ /**
58
+ * Enable session tracking
59
+ */
60
+ enabled: boolean;
61
+ /**
62
+ * Cookie name for session storage
63
+ * Default: '__agentshield_session'
64
+ */
65
+ cookieName?: string;
66
+ /**
67
+ * Cookie max age in seconds
68
+ * Default: 3600 (1 hour)
69
+ */
70
+ cookieMaxAge?: number;
71
+ /**
72
+ * Encryption key for session data
73
+ * Default: Uses AGENTSHIELD_SECRET env var or default key
74
+ */
75
+ encryptionKey?: string;
76
+ };
49
77
  }
50
78
  /**
51
79
  * Detection context for hooks
@@ -46,6 +46,34 @@ interface NextJSMiddlewareConfig extends Partial<AgentShieldConfig> {
46
46
  * Confidence threshold for agent detection
47
47
  */
48
48
  confidenceThreshold?: number;
49
+ /**
50
+ * Enable WASM for enhanced detection
51
+ */
52
+ enableWasm?: boolean;
53
+ /**
54
+ * Session tracking configuration
55
+ */
56
+ sessionTracking?: {
57
+ /**
58
+ * Enable session tracking
59
+ */
60
+ enabled: boolean;
61
+ /**
62
+ * Cookie name for session storage
63
+ * Default: '__agentshield_session'
64
+ */
65
+ cookieName?: string;
66
+ /**
67
+ * Cookie max age in seconds
68
+ * Default: 3600 (1 hour)
69
+ */
70
+ cookieMaxAge?: number;
71
+ /**
72
+ * Encryption key for session data
73
+ * Default: Uses AGENTSHIELD_SECRET env var or default key
74
+ */
75
+ encryptionKey?: string;
76
+ };
49
77
  }
50
78
  /**
51
79
  * Detection context for hooks
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kya-os/agentshield-nextjs",
3
- "version": "0.1.25",
3
+ "version": "0.1.28",
4
4
  "description": "Next.js middleware for AgentShield AI agent detection",
5
5
  "keywords": [
6
6
  "nextjs",
@@ -108,6 +108,6 @@
108
108
  },
109
109
  "sideEffects": false,
110
110
  "dependencies": {
111
- "@kya-os/agentshield": "^0.1.25"
111
+ "@kya-os/agentshield": "^0.1.28"
112
112
  }
113
113
  }