@kya-os/checkpoint-nextjs 1.0.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 (122) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/EDGE_RUNTIME_WASM_SETUP.md +348 -0
  3. package/README.md +414 -0
  4. package/bin/setup-edge-wasm.js +497 -0
  5. package/dist/.tsbuildinfo +1 -0
  6. package/dist/adapt.d.mts +39 -0
  7. package/dist/adapt.d.ts +39 -0
  8. package/dist/adapt.js +58 -0
  9. package/dist/adapt.js.map +1 -0
  10. package/dist/adapt.mjs +56 -0
  11. package/dist/adapt.mjs.map +1 -0
  12. package/dist/api-client.d.mts +204 -0
  13. package/dist/api-client.d.ts +204 -0
  14. package/dist/api-client.js +206 -0
  15. package/dist/api-client.js.map +1 -0
  16. package/dist/api-client.mjs +199 -0
  17. package/dist/api-client.mjs.map +1 -0
  18. package/dist/api-middleware.d.mts +156 -0
  19. package/dist/api-middleware.d.ts +156 -0
  20. package/dist/api-middleware.js +510 -0
  21. package/dist/api-middleware.js.map +1 -0
  22. package/dist/api-middleware.mjs +505 -0
  23. package/dist/api-middleware.mjs.map +1 -0
  24. package/dist/create-middleware.d.mts +17 -0
  25. package/dist/create-middleware.d.ts +17 -0
  26. package/dist/create-middleware.js +38 -0
  27. package/dist/create-middleware.js.map +1 -0
  28. package/dist/create-middleware.mjs +35 -0
  29. package/dist/create-middleware.mjs.map +1 -0
  30. package/dist/edge/index.d.mts +110 -0
  31. package/dist/edge/index.d.ts +110 -0
  32. package/dist/edge/index.js +277 -0
  33. package/dist/edge/index.js.map +1 -0
  34. package/dist/edge/index.mjs +275 -0
  35. package/dist/edge/index.mjs.map +1 -0
  36. package/dist/edge-runtime-loader.d.mts +50 -0
  37. package/dist/edge-runtime-loader.d.ts +50 -0
  38. package/dist/edge-runtime-loader.js +204 -0
  39. package/dist/edge-runtime-loader.js.map +1 -0
  40. package/dist/edge-runtime-loader.mjs +201 -0
  41. package/dist/edge-runtime-loader.mjs.map +1 -0
  42. package/dist/edge-wasm-middleware.d.mts +68 -0
  43. package/dist/edge-wasm-middleware.d.ts +68 -0
  44. package/dist/edge-wasm-middleware.js +318 -0
  45. package/dist/edge-wasm-middleware.js.map +1 -0
  46. package/dist/edge-wasm-middleware.mjs +315 -0
  47. package/dist/edge-wasm-middleware.mjs.map +1 -0
  48. package/dist/index.d.mts +25 -0
  49. package/dist/index.d.ts +25 -0
  50. package/dist/index.js +1019 -0
  51. package/dist/index.js.map +1 -0
  52. package/dist/index.mjs +979 -0
  53. package/dist/index.mjs.map +1 -0
  54. package/dist/middleware-edge.d.mts +46 -0
  55. package/dist/middleware-edge.d.ts +46 -0
  56. package/dist/middleware-edge.js +134 -0
  57. package/dist/middleware-edge.js.map +1 -0
  58. package/dist/middleware-edge.mjs +129 -0
  59. package/dist/middleware-edge.mjs.map +1 -0
  60. package/dist/middleware-node.d.mts +89 -0
  61. package/dist/middleware-node.d.ts +89 -0
  62. package/dist/middleware-node.js +127 -0
  63. package/dist/middleware-node.js.map +1 -0
  64. package/dist/middleware-node.mjs +124 -0
  65. package/dist/middleware-node.mjs.map +1 -0
  66. package/dist/middleware.d.mts +36 -0
  67. package/dist/middleware.d.ts +36 -0
  68. package/dist/middleware.js +15 -0
  69. package/dist/middleware.js.map +1 -0
  70. package/dist/middleware.mjs +12 -0
  71. package/dist/middleware.mjs.map +1 -0
  72. package/dist/nodejs-wasm-loader.d.mts +25 -0
  73. package/dist/nodejs-wasm-loader.d.ts +25 -0
  74. package/dist/nodejs-wasm-loader.js +95 -0
  75. package/dist/nodejs-wasm-loader.js.map +1 -0
  76. package/dist/nodejs-wasm-loader.mjs +85 -0
  77. package/dist/nodejs-wasm-loader.mjs.map +1 -0
  78. package/dist/policy.d.mts +162 -0
  79. package/dist/policy.d.ts +162 -0
  80. package/dist/policy.js +189 -0
  81. package/dist/policy.js.map +1 -0
  82. package/dist/policy.mjs +165 -0
  83. package/dist/policy.mjs.map +1 -0
  84. package/dist/session-tracker.d.mts +55 -0
  85. package/dist/session-tracker.d.ts +55 -0
  86. package/dist/session-tracker.js +170 -0
  87. package/dist/session-tracker.js.map +1 -0
  88. package/dist/session-tracker.mjs +167 -0
  89. package/dist/session-tracker.mjs.map +1 -0
  90. package/dist/signature-verifier.d.mts +33 -0
  91. package/dist/signature-verifier.d.ts +33 -0
  92. package/dist/signature-verifier.js +386 -0
  93. package/dist/signature-verifier.js.map +1 -0
  94. package/dist/signature-verifier.mjs +362 -0
  95. package/dist/signature-verifier.mjs.map +1 -0
  96. package/dist/translate.d.mts +33 -0
  97. package/dist/translate.d.ts +33 -0
  98. package/dist/translate.js +38 -0
  99. package/dist/translate.js.map +1 -0
  100. package/dist/translate.mjs +36 -0
  101. package/dist/translate.mjs.map +1 -0
  102. package/dist/types-C-xCUNTr.d.mts +105 -0
  103. package/dist/types-C-xCUNTr.d.ts +105 -0
  104. package/dist/wasm-middleware.d.mts +63 -0
  105. package/dist/wasm-middleware.d.ts +63 -0
  106. package/dist/wasm-middleware.js +98 -0
  107. package/dist/wasm-middleware.js.map +1 -0
  108. package/dist/wasm-middleware.mjs +95 -0
  109. package/dist/wasm-middleware.mjs.map +1 -0
  110. package/dist/wasm-setup.d.mts +46 -0
  111. package/dist/wasm-setup.d.ts +46 -0
  112. package/dist/wasm-setup.js +176 -0
  113. package/dist/wasm-setup.js.map +1 -0
  114. package/dist/wasm-setup.mjs +167 -0
  115. package/dist/wasm-setup.mjs.map +1 -0
  116. package/package.json +156 -0
  117. package/templates/middleware-wasm-100.ts +153 -0
  118. package/wasm/agentshield_wasm.d.ts +479 -0
  119. package/wasm/agentshield_wasm.js +1536 -0
  120. package/wasm/agentshield_wasm_bg.wasm +0 -0
  121. package/wasm/package.json +30 -0
  122. package/wasm.d.ts +21 -0
package/dist/index.js ADDED
@@ -0,0 +1,1019 @@
1
+ 'use strict';
2
+
3
+ var orchestrator = require('@kya-os/checkpoint-wasm-runtime/orchestrator');
4
+ var adapters = require('@kya-os/checkpoint-wasm-runtime/adapters');
5
+ var server = require('next/server');
6
+ var checkpointShared = require('@kya-os/checkpoint-shared');
7
+
8
+ // src/middleware-node.ts
9
+ function adaptToNextResponse(rendered, req) {
10
+ const clientAcceptsHtml = checkpointShared.acceptsHtml(req.headers);
11
+ const verdictCookie = checkpointShared.encodeVerdictCookie(rendered);
12
+ const shape = checkpointShared.classifyResponseShape(rendered, clientAcceptsHtml);
13
+ switch (shape) {
14
+ case "pass-through": {
15
+ const res = server.NextResponse.next();
16
+ applyHeaders(res, rendered.headers);
17
+ setVerdictCookie(res, verdictCookie);
18
+ return res;
19
+ }
20
+ case "redirect": {
21
+ const target = new URL(rendered.headers.Location);
22
+ const res = server.NextResponse.redirect(target);
23
+ applyHeaders(res, rendered.headers);
24
+ setVerdictCookie(res, verdictCookie);
25
+ return res;
26
+ }
27
+ case "html-block": {
28
+ const blockedUrl = new URL(checkpointShared.BLOCKED_PATH, req.url);
29
+ const res = server.NextResponse.rewrite(blockedUrl, { status: 200 });
30
+ applyHeaders(res, rendered.headers);
31
+ setVerdictCookie(res, verdictCookie);
32
+ return res;
33
+ }
34
+ case "json-block": {
35
+ const body = rendered.body ?? {};
36
+ const res = server.NextResponse.json(body, { status: rendered.status });
37
+ applyHeaders(res, rendered.headers);
38
+ setVerdictCookie(res, verdictCookie);
39
+ return res;
40
+ }
41
+ }
42
+ }
43
+ function setVerdictCookie(res, value) {
44
+ res.cookies.set({
45
+ name: checkpointShared.VERDICT_COOKIE_NAME,
46
+ value,
47
+ path: "/",
48
+ sameSite: "lax",
49
+ httpOnly: false
50
+ });
51
+ }
52
+ function applyHeaders(res, headers) {
53
+ for (const [key, value] of Object.entries(headers)) {
54
+ res.headers.set(key, value);
55
+ }
56
+ }
57
+
58
+ // src/translate.ts
59
+ function nextRequestToHttpLike(req) {
60
+ const url = new URL(req.url);
61
+ return {
62
+ method: req.method,
63
+ // Path + query only — orchestrator's URL parsing expects no scheme/host.
64
+ url: url.pathname + url.search,
65
+ headers: headersToRecord(req.headers),
66
+ // NextRequest.body is a ReadableStream; we don't drain it here.
67
+ // The orchestrator routes to PlainHttp when body is falsy, which
68
+ // is the right call for streaming middlewares that don't want to
69
+ // buffer the request body just to detect agents.
70
+ body: null,
71
+ remoteAddress: extractRemoteAddress(req)
72
+ };
73
+ }
74
+ function headersToRecord(headers) {
75
+ const out = {};
76
+ headers.forEach((value, key) => {
77
+ out[key.toLowerCase()] = value;
78
+ });
79
+ return out;
80
+ }
81
+ function extractRemoteAddress(req) {
82
+ const xff = req.headers.get("x-forwarded-for");
83
+ if (xff) {
84
+ const first = xff.split(",")[0]?.trim();
85
+ if (first) return first;
86
+ }
87
+ const maybeIp = req.ip;
88
+ return maybeIp;
89
+ }
90
+
91
+ // src/middleware-node.ts
92
+ function withCheckpoint(config) {
93
+ const opts = buildVerifyOpts(config);
94
+ return async function checkpointMiddleware(req) {
95
+ const httpLike = nextRequestToHttpLike(req);
96
+ const result = await orchestrator.verifyRequest(httpLike, opts);
97
+ await dispatchOnResult(config, result, req);
98
+ const rendered = orchestrator.renderDecisionAsResponse(result);
99
+ return adaptToNextResponse(rendered, req);
100
+ };
101
+ }
102
+ function buildVerifyOpts(config) {
103
+ const overrides = config.adapters ?? {};
104
+ return {
105
+ didResolver: overrides.didResolver ?? adapters.makeDidResolver(),
106
+ statusListCache: overrides.statusListCache ?? adapters.makeStatusListCache(),
107
+ reputationOracle: overrides.reputationOracle ?? adapters.makeReputationOracle({ argusUrl: config.argusUrl }),
108
+ policyEvaluator: overrides.policyEvaluator ?? adapters.makePolicyEvaluator({ dashboardUrl: config.dashboardUrl }),
109
+ clock: adapters.makeSystemClock(),
110
+ tenantHost: config.tenantHost,
111
+ enforcementMode: config.enforcementMode ?? "enforce",
112
+ reputationBaseline: config.reputationBaseline,
113
+ argusUrl: config.argusUrl
114
+ };
115
+ }
116
+ async function dispatchOnResult(config, result, req) {
117
+ if (!config.onResult) return;
118
+ try {
119
+ await config.onResult(result, req);
120
+ } catch {
121
+ }
122
+ }
123
+
124
+ // src/middleware.ts
125
+ var MIGRATION_ERROR = "@kya-os/checkpoint-nextjs's `createAgentShieldMiddleware` / `agentShield` were deleted in Phase D (engine consolidation). The 600-line TS pattern matcher that backed them is gone. Migrate to `withCheckpoint` from `@kya-os/checkpoint-nextjs` (Node runtime) or `@kya-os/checkpoint-nextjs/edge` (Edge runtime). See packages/checkpoint-nextjs/CHANGELOG.md (1.0.0) for the recipe.";
126
+ function createAgentShieldMiddleware(_config = {}) {
127
+ throw new Error(MIGRATION_ERROR);
128
+ }
129
+
130
+ // src/create-middleware.ts
131
+ var middlewareInstance = null;
132
+ var isInitializing = false;
133
+ var initPromise = null;
134
+ function createAgentShieldMiddleware2(config) {
135
+ return async function agentShieldMiddleware2(request) {
136
+ if (!middlewareInstance) {
137
+ if (!isInitializing) {
138
+ isInitializing = true;
139
+ initPromise = (async () => {
140
+ middlewareInstance = createAgentShieldMiddleware(config);
141
+ return middlewareInstance;
142
+ })();
143
+ }
144
+ if (initPromise) {
145
+ middlewareInstance = await initPromise;
146
+ }
147
+ }
148
+ return middlewareInstance ? middlewareInstance(request) : server.NextResponse.next();
149
+ };
150
+ }
151
+
152
+ // src/api-client.ts
153
+ var DEFAULT_BASE_URL = "https://kya.vouched.id";
154
+ var EDGE_DETECT_URL = "https://detect.checkpoint-gateway.ai";
155
+ var DEFAULT_TIMEOUT = 5e3;
156
+ var CheckpointApiClient = class {
157
+ apiKey;
158
+ baseUrl;
159
+ useEdge;
160
+ timeout;
161
+ debug;
162
+ constructor(config) {
163
+ if (!config.apiKey) {
164
+ throw new Error("AgentShield API key is required");
165
+ }
166
+ this.apiKey = config.apiKey;
167
+ this.useEdge = config.useEdge !== false;
168
+ this.baseUrl = config.baseUrl || (this.useEdge ? EDGE_DETECT_URL : DEFAULT_BASE_URL);
169
+ this.timeout = config.timeout || DEFAULT_TIMEOUT;
170
+ this.debug = config.debug || false;
171
+ }
172
+ /**
173
+ * Call the enforce API to check if a request should be allowed
174
+ */
175
+ async enforce(input) {
176
+ const startTime = Date.now();
177
+ try {
178
+ const controller = new AbortController();
179
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
180
+ try {
181
+ const endpoint = this.useEdge ? `${this.baseUrl}/__detect/enforce` : `${this.baseUrl}/api/v1/enforce`;
182
+ const response = await fetch(endpoint, {
183
+ method: "POST",
184
+ headers: {
185
+ "Content-Type": "application/json",
186
+ Authorization: `Bearer ${this.apiKey}`,
187
+ "X-Request-ID": input.requestId || crypto.randomUUID()
188
+ },
189
+ body: JSON.stringify(input),
190
+ signal: controller.signal
191
+ });
192
+ clearTimeout(timeoutId);
193
+ const data = await response.json();
194
+ if (this.debug) {
195
+ console.log("[AgentShield] Enforce response:", {
196
+ status: response.status,
197
+ action: data.data?.decision.action,
198
+ processingTimeMs: Date.now() - startTime
199
+ });
200
+ }
201
+ if (!response.ok) {
202
+ return {
203
+ success: false,
204
+ error: {
205
+ code: `HTTP_${response.status}`,
206
+ message: data.error?.message || `HTTP error: ${response.status}`
207
+ }
208
+ };
209
+ }
210
+ return data;
211
+ } catch (error) {
212
+ clearTimeout(timeoutId);
213
+ throw error;
214
+ }
215
+ } catch (error) {
216
+ if (error instanceof Error && error.name === "AbortError") {
217
+ if (this.debug) {
218
+ console.warn("[AgentShield] Request timed out");
219
+ }
220
+ return {
221
+ success: false,
222
+ error: {
223
+ code: "TIMEOUT",
224
+ message: `Request timed out after ${this.timeout}ms`
225
+ }
226
+ };
227
+ }
228
+ if (this.debug) {
229
+ console.error("[AgentShield] Request failed:", error);
230
+ }
231
+ return {
232
+ success: false,
233
+ error: {
234
+ code: "NETWORK_ERROR",
235
+ message: error instanceof Error ? error.message : "Network request failed"
236
+ }
237
+ };
238
+ }
239
+ }
240
+ /**
241
+ * Quick check - returns just the action without full response parsing
242
+ * Useful for very fast middleware that just needs allow/block
243
+ */
244
+ async quickCheck(input) {
245
+ const result = await this.enforce(input);
246
+ if (!result.success || !result.data) {
247
+ return {
248
+ action: "allow",
249
+ error: result.error?.message
250
+ };
251
+ }
252
+ return {
253
+ action: result.data.decision.action
254
+ };
255
+ }
256
+ /**
257
+ * Check if this client is using edge detection (Gateway Worker)
258
+ */
259
+ isUsingEdge() {
260
+ return this.useEdge;
261
+ }
262
+ /**
263
+ * Log a detection result to AgentShield database.
264
+ * Use after Gateway Worker detection to persist results.
265
+ * Fire-and-forget - returns immediately without waiting for DB write.
266
+ *
267
+ * @example
268
+ * ```typescript
269
+ * // After receiving Gateway response
270
+ * if (client.isUsingEdge() && response.data?.detection) {
271
+ * client.logDetection({
272
+ * detection: response.data.detection,
273
+ * context: { userAgent, ipAddress, path, url, method }
274
+ * }).catch(err => console.error('Log failed:', err));
275
+ * }
276
+ * ```
277
+ */
278
+ async logDetection(input) {
279
+ const logEndpoint = this.useEdge ? `${DEFAULT_BASE_URL}/api/v1/log-detection` : `${this.baseUrl}/api/v1/log-detection`;
280
+ try {
281
+ const controller = new AbortController();
282
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
283
+ try {
284
+ const response = await fetch(logEndpoint, {
285
+ method: "POST",
286
+ headers: {
287
+ "Content-Type": "application/json",
288
+ Authorization: `Bearer ${this.apiKey}`
289
+ },
290
+ body: JSON.stringify({
291
+ detection: {
292
+ isAgent: input.detection.isAgent,
293
+ confidence: input.detection.confidence,
294
+ agentName: input.detection.agentName,
295
+ agentType: input.detection.agentType,
296
+ detectionClass: input.detection.detectionClass,
297
+ verificationMethod: input.detection.verificationMethod,
298
+ reasons: input.detection.reasons
299
+ },
300
+ context: input.context,
301
+ source: input.source || "gateway"
302
+ }),
303
+ signal: controller.signal
304
+ });
305
+ clearTimeout(timeoutId);
306
+ if (!response.ok && this.debug) {
307
+ console.warn("[AgentShield] Log detection returned non-2xx:", response.status);
308
+ }
309
+ } catch (error) {
310
+ clearTimeout(timeoutId);
311
+ throw error;
312
+ }
313
+ } catch (error) {
314
+ if (this.debug) {
315
+ console.error("[AgentShield] Log detection failed:", error);
316
+ }
317
+ throw error;
318
+ }
319
+ }
320
+ };
321
+ var clientInstance = null;
322
+ function getCheckpointApiClient(config) {
323
+ if (!clientInstance) {
324
+ const apiKey = config?.apiKey || process.env.CHECKPOINT_API_KEY;
325
+ if (!apiKey) {
326
+ throw new Error(
327
+ "AgentShield API key is required. Set CHECKPOINT_API_KEY environment variable or pass apiKey in config."
328
+ );
329
+ }
330
+ clientInstance = new CheckpointApiClient({
331
+ apiKey,
332
+ baseUrl: config?.baseUrl || process.env.AGENTSHIELD_API_URL,
333
+ // Default to edge detection unless explicitly disabled
334
+ useEdge: config?.useEdge ?? process.env.AGENTSHIELD_USE_EDGE !== "false",
335
+ timeout: config?.timeout,
336
+ debug: config?.debug || process.env.AGENTSHIELD_DEBUG === "true"
337
+ });
338
+ }
339
+ return clientInstance;
340
+ }
341
+ function resetCheckpointApiClient() {
342
+ clientInstance = null;
343
+ }
344
+ var AgentShieldClient = CheckpointApiClient;
345
+ var getAgentShieldClient = getCheckpointApiClient;
346
+ var resetAgentShieldClient = resetCheckpointApiClient;
347
+
348
+ // src/utils.ts
349
+ function getClientIp(request) {
350
+ const forwardedFor = request.headers.get("x-forwarded-for");
351
+ if (forwardedFor) {
352
+ const ip = forwardedFor.split(",")[0]?.trim();
353
+ if (ip) return ip;
354
+ }
355
+ const realIp = request.headers.get("x-real-ip");
356
+ if (realIp) return realIp;
357
+ const cfIp = request.headers.get("cf-connecting-ip");
358
+ if (cfIp) return cfIp;
359
+ const clientIp = request.headers.get("x-client-ip");
360
+ if (clientIp) return clientIp;
361
+ return void 0;
362
+ }
363
+ function safeHostname(url) {
364
+ try {
365
+ return new URL(url).hostname;
366
+ } catch {
367
+ return "this site";
368
+ }
369
+ }
370
+
371
+ // src/responses/agent-instruction.ts
372
+ var MCP_I_DOCS_URL = "https://docs.knowthat.ai/mcp-i/getting-started";
373
+ var DEFAULT_CONNECT_PATH = "/connect";
374
+ function buildAgentInstructionResponse(request, decision, redirectUrl) {
375
+ const resolved = resolveUrl(redirectUrl ?? DEFAULT_CONNECT_PATH, request.url);
376
+ const agentName = decision.agentName || decision.agentType || "unknown";
377
+ if (!resolved.searchParams.has("agent")) {
378
+ resolved.searchParams.set("agent", agentName.toLowerCase());
379
+ }
380
+ const authUrl = resolved.toString();
381
+ const hostname = safeHostname(request.url);
382
+ const body = {
383
+ // Markdown-formatted so clients that render markdown (Claude Desktop,
384
+ // ChatGPT web) surface the URL as a clickable link. Tone mirrors the
385
+ // gateway response so messaging stays consistent across platforms.
386
+ message: `I can't access ${hostname} yet \u2014 this site checks AI assistants at the front door.
387
+
388
+ **To give me access, open this link:**
389
+ [Connect securely to ${hostname}](${authUrl})
390
+
391
+ 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.`,
392
+ user_action_required: {
393
+ action: `Connect securely to ${hostname}`,
394
+ url: authUrl,
395
+ reason: `${hostname} checks AI assistants before they connect. Open the link to give your assistant a verified key.`
396
+ },
397
+ mcp_i: {
398
+ version: "1.0",
399
+ action: "authenticate",
400
+ authorization_url: authUrl,
401
+ flow: {
402
+ type: "oauth2_delegation",
403
+ steps: [
404
+ "1. Direct your user to the authorization_url",
405
+ "2. User reviews requested scopes and grants consent",
406
+ "3. Receive delegation credential (JWT)",
407
+ "4. Include credential in KYA-Delegation header",
408
+ "5. Retry this request with the proof"
409
+ ]
410
+ },
411
+ retry_instructions: {
412
+ header: "KYA-Delegation",
413
+ format: "JWT delegation credential from authorization flow"
414
+ },
415
+ documentation: MCP_I_DOCS_URL
416
+ },
417
+ error: "mcp_authentication_required",
418
+ code: "AGENT_REQUIRES_DELEGATION",
419
+ detection: {
420
+ agent_type: decision.agentType || "ai_agent",
421
+ agent_name: decision.agentName || "Unknown Agent",
422
+ confidence: decision.confidence
423
+ }
424
+ };
425
+ const response = server.NextResponse.json(body, { status: 401 });
426
+ response.headers.set("WWW-Authenticate", `KYA realm="api", authorization_uri="${authUrl}"`);
427
+ response.headers.set(
428
+ "Link",
429
+ `<${authUrl}>; rel="kya-authorize", <${MCP_I_DOCS_URL}>; rel="help"`
430
+ );
431
+ response.headers.set("KYA-Auth-Required", "true");
432
+ response.headers.set("KYA-Auth-Url", authUrl);
433
+ response.headers.set("KYA-Action", "instruct");
434
+ response.headers.set("KYA-Detected-Agent", agentName);
435
+ response.headers.set("KYA-Confidence", decision.confidence.toString());
436
+ response.headers.set("Cache-Control", "no-store");
437
+ return response;
438
+ }
439
+ function resolveUrl(target, baseUrl) {
440
+ try {
441
+ return new URL(target, baseUrl);
442
+ } catch {
443
+ return new URL(DEFAULT_CONNECT_PATH, baseUrl);
444
+ }
445
+ }
446
+
447
+ // src/api-middleware.ts
448
+ function shouldSkipPath(path, skipPaths) {
449
+ return skipPaths.some((pattern) => checkpointShared.matchPath(path, pattern));
450
+ }
451
+ function shouldIncludePath(path, includePaths) {
452
+ if (!includePaths || includePaths.length === 0) return true;
453
+ return includePaths.some((pattern) => checkpointShared.matchPath(path, pattern));
454
+ }
455
+ function buildBlockedResponse(request, decision, config) {
456
+ const status = config.blockedResponse?.status ?? 403;
457
+ const message = config.blockedResponse?.message ?? decision.message ?? "Access denied";
458
+ const recoveryUrl = resolveRecoveryUrl(request, config.redirectUrl || decision.redirectUrl);
459
+ const body = {
460
+ error: message,
461
+ code: "AGENT_BLOCKED",
462
+ reason: decision.reason,
463
+ agentType: decision.agentType
464
+ };
465
+ if (recoveryUrl) {
466
+ const hostname = safeHostname(request.url);
467
+ body.user_action_required = {
468
+ action: `Connect securely to ${hostname}`,
469
+ url: recoveryUrl,
470
+ reason: `${hostname} blocks unverified AI assistants. Open the link to give your assistant a verified key and try again.`
471
+ };
472
+ body.message = `I can't access ${hostname} \u2014 this site blocks unverified AI assistants.
473
+
474
+ **To give me access, open this link:**
475
+ [Connect securely to ${hostname}](${recoveryUrl})
476
+
477
+ Once you're done, ask me to try again.`;
478
+ }
479
+ const response = server.NextResponse.json(body, { status });
480
+ if (config.blockedResponse?.headers) {
481
+ for (const [key, value] of Object.entries(config.blockedResponse.headers)) {
482
+ response.headers.set(key, value);
483
+ }
484
+ }
485
+ if (recoveryUrl) {
486
+ response.headers.set("Link", `<${recoveryUrl}>; rel="kya-authorize"`);
487
+ response.headers.set("KYA-Auth-Url", recoveryUrl);
488
+ }
489
+ response.headers.set("KYA-Action", decision.action);
490
+ response.headers.set("KYA-Reason", decision.reason);
491
+ return response;
492
+ }
493
+ function resolveRecoveryUrl(request, target) {
494
+ if (!target) return void 0;
495
+ try {
496
+ return new URL(target, request.url).toString();
497
+ } catch {
498
+ return void 0;
499
+ }
500
+ }
501
+ function buildRedirectResponse(request, decision, config) {
502
+ const redirectUrl = config.redirectUrl || decision.redirectUrl || "/blocked";
503
+ const url = new URL(redirectUrl, request.url);
504
+ url.searchParams.set("reason", decision.reason);
505
+ if (decision.agentType) {
506
+ url.searchParams.set("agent", decision.agentType);
507
+ }
508
+ return server.NextResponse.redirect(url);
509
+ }
510
+ function withCheckpointApi(config = {}) {
511
+ let client = null;
512
+ const getClient = () => {
513
+ if (!client) {
514
+ client = getCheckpointApiClient({
515
+ apiKey: config.apiKey,
516
+ baseUrl: config.apiUrl,
517
+ useEdge: config.useEdge,
518
+ timeout: config.timeout,
519
+ debug: config.debug
520
+ });
521
+ }
522
+ return client;
523
+ };
524
+ const defaultSkipPaths = [
525
+ "/_next/static/**",
526
+ "/_next/image/**",
527
+ "/favicon.ico",
528
+ "/robots.txt",
529
+ "/sitemap.xml"
530
+ ];
531
+ const skipPaths = [...defaultSkipPaths, ...config.skipPaths || []];
532
+ const failOpen = config.failOpen ?? true;
533
+ return async function middleware(request) {
534
+ const path = request.nextUrl.pathname;
535
+ const startTime = Date.now();
536
+ if (shouldSkipPath(path, skipPaths)) {
537
+ return server.NextResponse.next();
538
+ }
539
+ if (!shouldIncludePath(path, config.includePaths)) {
540
+ return server.NextResponse.next();
541
+ }
542
+ try {
543
+ const client2 = getClient();
544
+ const userAgent = request.headers.get("user-agent") || void 0;
545
+ const ipAddress = getClientIp(request);
546
+ const result = await client2.enforce({
547
+ headers: Object.fromEntries(request.headers.entries()),
548
+ userAgent,
549
+ ipAddress,
550
+ path,
551
+ url: request.url,
552
+ method: request.method,
553
+ requestId: request.headers.get("x-request-id") || void 0,
554
+ options: {
555
+ // Always include detection results for logging (needed when using edge)
556
+ includeDetectionResult: true
557
+ }
558
+ });
559
+ if (!result.success || !result.data) {
560
+ if (config.debug) {
561
+ console.warn("[AgentShield] API error:", result.error);
562
+ }
563
+ if (failOpen) {
564
+ return server.NextResponse.next();
565
+ }
566
+ return server.NextResponse.json(
567
+ { error: "Security check failed", code: "API_ERROR" },
568
+ { status: 503 }
569
+ );
570
+ }
571
+ const decision = result.data.decision;
572
+ if (config.debug) {
573
+ console.log("[AgentShield] Decision:", {
574
+ path,
575
+ action: decision.action,
576
+ isAgent: decision.isAgent,
577
+ confidence: decision.confidence,
578
+ agentName: decision.agentName,
579
+ detectionMethod: result.data.detection?.detectionMethod || "not-included",
580
+ processingTimeMs: Date.now() - startTime
581
+ });
582
+ }
583
+ if (client2.isUsingEdge() && result.data.detection) {
584
+ client2.logDetection({
585
+ detection: result.data.detection,
586
+ context: { userAgent, ipAddress, path, url: request.url, method: request.method }
587
+ }).catch((err) => {
588
+ if (config.debug) {
589
+ console.error("[AgentShield] Log detection failed:", err);
590
+ }
591
+ });
592
+ }
593
+ if (decision.isAgent && config.onAgentDetected) {
594
+ await config.onAgentDetected(request, decision);
595
+ }
596
+ const redirectMode = config.redirectMode ?? "instruct";
597
+ switch (decision.action) {
598
+ case "block": {
599
+ if (config.customBlockedResponse) {
600
+ return await config.customBlockedResponse(request, decision);
601
+ }
602
+ if (config.onBlock === "redirect") {
603
+ return buildRedirectResponse(request, decision, config);
604
+ }
605
+ return buildBlockedResponse(request, decision, config);
606
+ }
607
+ case "redirect":
608
+ case "instruct": {
609
+ if (redirectMode === "http" && decision.action === "redirect") {
610
+ return buildRedirectResponse(request, decision, config);
611
+ }
612
+ const targetUrl = config.redirectUrl || decision.redirectUrl;
613
+ return buildAgentInstructionResponse(request, decision, targetUrl);
614
+ }
615
+ case "challenge": {
616
+ return buildRedirectResponse(request, decision, config);
617
+ }
618
+ case "log":
619
+ case "allow":
620
+ default: {
621
+ const response = server.NextResponse.next();
622
+ if (decision.isAgent) {
623
+ response.headers.set("KYA-Detected", "true");
624
+ response.headers.set("KYA-Confidence", decision.confidence.toString());
625
+ if (decision.agentName) {
626
+ response.headers.set("KYA-Agent", decision.agentName);
627
+ }
628
+ }
629
+ return response;
630
+ }
631
+ }
632
+ } catch (error) {
633
+ if (config.debug) {
634
+ console.error("[AgentShield] Middleware error:", error);
635
+ }
636
+ if (failOpen) {
637
+ return server.NextResponse.next();
638
+ }
639
+ return server.NextResponse.json(
640
+ { error: "Security check failed", code: "MIDDLEWARE_ERROR" },
641
+ { status: 503 }
642
+ );
643
+ }
644
+ };
645
+ }
646
+ var withAgentShield = withCheckpointApi;
647
+ var LEGACY_MIGRATION_ERROR = "This export was removed in Phase D. Migrate to `withCheckpoint` (local-engine deployment) or `withCheckpointApi` (SaaS-gateway deployment) from `@kya-os/checkpoint-nextjs`. See packages/checkpoint-nextjs/README.md (Two deployment shapes) for which one fits your runtime.";
648
+ function agentShieldMiddleware(_request) {
649
+ throw new Error(LEGACY_MIGRATION_ERROR);
650
+ }
651
+ function createEnhancedAgentShieldMiddleware(_config = {}) {
652
+ throw new Error(LEGACY_MIGRATION_ERROR);
653
+ }
654
+ var EdgeSessionTracker = class {
655
+ config;
656
+ constructor(config) {
657
+ this.config = {
658
+ enabled: config.enabled,
659
+ cookieName: config.cookieName || "__agentshield_session",
660
+ cookieMaxAge: config.cookieMaxAge || 3600,
661
+ // 1 hour default
662
+ encryptionKey: config.encryptionKey || process.env.AGENTSHIELD_SECRET || "agentshield-default-key"
663
+ };
664
+ }
665
+ /**
666
+ * Track a new AI agent session
667
+ */
668
+ async track(_request, response, result) {
669
+ try {
670
+ if (!this.config.enabled || !checkpointShared.shouldEnforce(result)) {
671
+ return response;
672
+ }
673
+ const sessionData = {
674
+ id: crypto.randomUUID(),
675
+ agent: result.detectedAgent?.name || "unknown",
676
+ confidence: result.confidence,
677
+ detectedAt: Date.now(),
678
+ expires: Date.now() + this.config.cookieMaxAge * 1e3
679
+ };
680
+ const encrypted = await this.encrypt(JSON.stringify(sessionData));
681
+ response.cookies.set(this.config.cookieName, encrypted, {
682
+ httpOnly: true,
683
+ secure: process.env.NODE_ENV === "production",
684
+ sameSite: "lax",
685
+ maxAge: this.config.cookieMaxAge,
686
+ path: "/"
687
+ });
688
+ return response;
689
+ } catch (error) {
690
+ if (process.env.DEBUG_AGENTSHIELD) {
691
+ console.warn("AgentShield: Failed to track session:", error);
692
+ }
693
+ return response;
694
+ }
695
+ }
696
+ /**
697
+ * Check for existing AI agent session
698
+ */
699
+ async check(request) {
700
+ try {
701
+ if (!this.config.enabled) {
702
+ return null;
703
+ }
704
+ const cookie = request.cookies.get(this.config.cookieName);
705
+ if (!cookie?.value) {
706
+ return null;
707
+ }
708
+ const decrypted = await this.decrypt(cookie.value);
709
+ const session = JSON.parse(decrypted);
710
+ if (session.expires < Date.now()) {
711
+ return null;
712
+ }
713
+ return session;
714
+ } catch (error) {
715
+ if (process.env.DEBUG_AGENTSHIELD) {
716
+ console.warn("AgentShield: Failed to check session:", error);
717
+ }
718
+ return null;
719
+ }
720
+ }
721
+ /**
722
+ * Clear an existing session
723
+ */
724
+ clear(response) {
725
+ try {
726
+ response.cookies.delete(this.config.cookieName);
727
+ } catch (error) {
728
+ if (process.env.DEBUG_AGENTSHIELD) {
729
+ console.warn("AgentShield: Failed to clear session:", error);
730
+ }
731
+ }
732
+ return response;
733
+ }
734
+ /**
735
+ * Simple encryption using Web Crypto API (Edge-compatible)
736
+ */
737
+ async encrypt(data) {
738
+ try {
739
+ const key = this.config.encryptionKey;
740
+ const encoded = new TextEncoder().encode(data);
741
+ const obfuscated = new Uint8Array(encoded.length);
742
+ for (let i = 0; i < encoded.length; i++) {
743
+ obfuscated[i] = (encoded[i] || 0) ^ key.charCodeAt(i % key.length);
744
+ }
745
+ return btoa(Array.from(obfuscated, (byte) => String.fromCharCode(byte)).join(""));
746
+ } catch (error) {
747
+ return btoa(data);
748
+ }
749
+ }
750
+ /**
751
+ * Simple decryption (Edge-compatible)
752
+ */
753
+ async decrypt(data) {
754
+ try {
755
+ const key = this.config.encryptionKey;
756
+ const decoded = Uint8Array.from(atob(data), (c) => c.charCodeAt(0));
757
+ const deobfuscated = new Uint8Array(decoded.length);
758
+ for (let i = 0; i < decoded.length; i++) {
759
+ deobfuscated[i] = (decoded[i] || 0) ^ key.charCodeAt(i % key.length);
760
+ }
761
+ return new TextDecoder().decode(deobfuscated);
762
+ } catch (error) {
763
+ return atob(data);
764
+ }
765
+ }
766
+ };
767
+ var StatelessSessionChecker = class {
768
+ static check(headers) {
769
+ try {
770
+ const agent = headers["kya-session-agent"];
771
+ const confidence = headers["kya-session-confidence"];
772
+ const sessionId = headers["kya-session-id"];
773
+ if (agent && confidence && sessionId) {
774
+ return {
775
+ id: sessionId,
776
+ agent,
777
+ confidence: parseFloat(confidence),
778
+ detectedAt: Date.now(),
779
+ expires: Date.now() + 36e5
780
+ // 1 hour
781
+ };
782
+ }
783
+ const cookieHeader = headers["cookie"];
784
+ if (cookieHeader && cookieHeader.includes("__agentshield_session=")) {
785
+ const match = cookieHeader.match(/__agentshield_session=([^;]+)/);
786
+ if (match && match[1]) {
787
+ try {
788
+ const decoded = atob(match[1]);
789
+ return JSON.parse(decoded);
790
+ } catch {
791
+ }
792
+ }
793
+ }
794
+ return null;
795
+ } catch {
796
+ return null;
797
+ }
798
+ }
799
+ static setHeaders(response, session) {
800
+ try {
801
+ if (response.setHeader) {
802
+ response.setHeader("KYA-Session-Agent", session.agent);
803
+ response.setHeader("KYA-Session-Confidence", session.confidence.toString());
804
+ response.setHeader("KYA-Session-Id", session.id);
805
+ } else if (response.headers && response.headers.set) {
806
+ response.headers.set("kya-session-agent", session.agent);
807
+ response.headers.set("kya-session-confidence", session.confidence.toString());
808
+ response.headers.set("kya-session-id", session.id);
809
+ }
810
+ } catch {
811
+ }
812
+ }
813
+ };
814
+ function createContextFromDetection(detection, request) {
815
+ return checkpointShared.createEvaluationContext({
816
+ agentType: detection.detectedAgent?.type,
817
+ agentName: detection.detectedAgent?.name,
818
+ agentVendor: detection.detectedAgent?.vendor,
819
+ confidence: detection.confidence,
820
+ riskLevel: detection.riskLevel,
821
+ path: request.nextUrl.pathname,
822
+ method: request.method,
823
+ signatureVerified: detection.verificationMethod === "signature",
824
+ isAuthenticated: false,
825
+ // TODO: integrate with auth
826
+ userAgent: request.headers.get("user-agent") || void 0
827
+ });
828
+ }
829
+ function evaluatePolicyForDetection(detection, request, policy) {
830
+ const context = createContextFromDetection(detection, request);
831
+ return checkpointShared.evaluatePolicy(policy, context);
832
+ }
833
+ function buildBlockedResponse2(decision, config) {
834
+ const status = config.blockedResponse?.status ?? 403;
835
+ const message = config.blockedResponse?.message ?? decision.message ?? "Access denied";
836
+ const response = server.NextResponse.json(
837
+ {
838
+ error: message,
839
+ code: "POLICY_BLOCKED",
840
+ reason: decision.reason,
841
+ ruleId: decision.ruleId,
842
+ matchType: decision.matchType
843
+ },
844
+ { status }
845
+ );
846
+ if (config.blockedResponse?.headers) {
847
+ for (const [key, value] of Object.entries(config.blockedResponse.headers)) {
848
+ response.headers.set(key, value);
849
+ }
850
+ }
851
+ response.headers.set("KYA-Action", decision.action);
852
+ response.headers.set("KYA-Reason", decision.reason);
853
+ response.headers.set("KYA-Match-Type", decision.matchType);
854
+ return response;
855
+ }
856
+ function buildRedirectResponse2(request, decision, config, detection) {
857
+ const redirectUrl = decision.redirectUrl || config.redirectUrl || "/blocked";
858
+ const url = new URL(redirectUrl, request.url);
859
+ url.searchParams.set("reason", decision.reason);
860
+ if (decision.ruleId) {
861
+ url.searchParams.set("ruleId", decision.ruleId);
862
+ }
863
+ const agentName = detection?.detectedAgent?.name;
864
+ if (agentName && !url.searchParams.has("agent")) {
865
+ url.searchParams.set("agent", agentName.toLowerCase());
866
+ }
867
+ return server.NextResponse.redirect(url);
868
+ }
869
+ function buildChallengeResponse(request, decision, config, detection) {
870
+ return buildRedirectResponse2(request, decision, config, detection);
871
+ }
872
+ async function handlePolicyDecision(request, decision, config, detection) {
873
+ switch (decision.action) {
874
+ case checkpointShared.ENFORCEMENT_ACTIONS.BLOCK:
875
+ if (config.customBlockedResponse) {
876
+ return await config.customBlockedResponse(request, decision);
877
+ }
878
+ return buildBlockedResponse2(decision, config);
879
+ case checkpointShared.ENFORCEMENT_ACTIONS.REDIRECT:
880
+ return buildRedirectResponse2(request, decision, config, detection);
881
+ case checkpointShared.ENFORCEMENT_ACTIONS.CHALLENGE:
882
+ return buildChallengeResponse(request, decision, config, detection);
883
+ case checkpointShared.ENFORCEMENT_ACTIONS.LOG:
884
+ console.log("[AgentShield] Policy decision (log):", {
885
+ path: request.nextUrl.pathname,
886
+ action: decision.action,
887
+ reason: decision.reason,
888
+ matchType: decision.matchType,
889
+ ruleId: decision.ruleId
890
+ });
891
+ return null;
892
+ // Continue to allow
893
+ case checkpointShared.ENFORCEMENT_ACTIONS.ALLOW:
894
+ default:
895
+ return null;
896
+ }
897
+ }
898
+ var fetcherCache = /* @__PURE__ */ new Map();
899
+ function getFetcherCacheKey(config) {
900
+ return `${config.apiUrl ?? "default"}:${config.apiKey ?? ""}:${config.cacheTtlSeconds ?? "default"}`;
901
+ }
902
+ function getPolicyFetcher(config) {
903
+ if (!config) {
904
+ throw new Error("fetchPolicy config required");
905
+ }
906
+ const cacheKey = getFetcherCacheKey(config);
907
+ let fetcher = fetcherCache.get(cacheKey);
908
+ if (!fetcher) {
909
+ const fetcherConfig = {
910
+ apiBaseUrl: config.apiUrl || "https://kya.vouched.id",
911
+ apiKey: config.apiKey,
912
+ cacheTtlSeconds: config.cacheTtlSeconds
913
+ };
914
+ fetcher = checkpointShared.createPolicyFetcher(fetcherConfig);
915
+ fetcherCache.set(cacheKey, fetcher);
916
+ }
917
+ return fetcher;
918
+ }
919
+ async function getPolicy(config) {
920
+ if (config.policy) {
921
+ return checkpointShared.PolicyConfigSchema.parse({ ...checkpointShared.DEFAULT_POLICY, ...config.policy });
922
+ }
923
+ if (config.fetchPolicy) {
924
+ try {
925
+ const fetcher = getPolicyFetcher(config.fetchPolicy);
926
+ return await fetcher.getPolicy(config.fetchPolicy.projectId);
927
+ } catch (error) {
928
+ if (config.debug) {
929
+ console.warn("[AgentShield] Policy fetch failed, using fallback:", error);
930
+ }
931
+ return checkpointShared.PolicyConfigSchema.parse({
932
+ ...checkpointShared.DEFAULT_POLICY,
933
+ ...config.fallbackPolicy || {}
934
+ });
935
+ }
936
+ }
937
+ return checkpointShared.PolicyConfigSchema.parse(checkpointShared.DEFAULT_POLICY);
938
+ }
939
+ async function applyPolicy(request, detection, config) {
940
+ try {
941
+ const path = request.nextUrl.pathname;
942
+ if (config.skipPaths?.some((pattern) => checkpointShared.matchPath(path, pattern))) {
943
+ return null;
944
+ }
945
+ if (config.includePaths && config.includePaths.length > 0) {
946
+ if (!config.includePaths.some((pattern) => checkpointShared.matchPath(path, pattern))) {
947
+ return null;
948
+ }
949
+ }
950
+ const policy = await getPolicy(config);
951
+ const context = createContextFromDetection(detection, request);
952
+ const decision = checkpointShared.evaluatePolicy(policy, context);
953
+ if (config.onPolicyDecision) {
954
+ await config.onPolicyDecision(request, decision, context);
955
+ }
956
+ return await handlePolicyDecision(request, decision, config, detection);
957
+ } catch (error) {
958
+ if (config.debug) {
959
+ console.error("[AgentShield] Policy evaluation error:", error);
960
+ }
961
+ if (config.failOpen !== false) {
962
+ return null;
963
+ }
964
+ return server.NextResponse.json(
965
+ { error: "Security check failed", code: "POLICY_ERROR" },
966
+ { status: 503 }
967
+ );
968
+ }
969
+ }
970
+
971
+ // src/index.ts
972
+ var VERSION = "0.1.0";
973
+ /**
974
+ * @fileoverview Checkpoint Next.js Integration
975
+ * @license MIT OR Apache-2.0
976
+ */
977
+
978
+ Object.defineProperty(exports, "DEFAULT_POLICY", {
979
+ enumerable: true,
980
+ get: function () { return checkpointShared.DEFAULT_POLICY; }
981
+ });
982
+ Object.defineProperty(exports, "ENFORCEMENT_ACTIONS", {
983
+ enumerable: true,
984
+ get: function () { return checkpointShared.ENFORCEMENT_ACTIONS; }
985
+ });
986
+ Object.defineProperty(exports, "createEvaluationContext", {
987
+ enumerable: true,
988
+ get: function () { return checkpointShared.createEvaluationContext; }
989
+ });
990
+ Object.defineProperty(exports, "evaluatePolicy", {
991
+ enumerable: true,
992
+ get: function () { return checkpointShared.evaluatePolicy; }
993
+ });
994
+ exports.AgentShieldClient = AgentShieldClient;
995
+ exports.CheckpointApiClient = CheckpointApiClient;
996
+ exports.EdgeSessionTracker = EdgeSessionTracker;
997
+ exports.StatelessSessionChecker = StatelessSessionChecker;
998
+ exports.VERSION = VERSION;
999
+ exports.agentShieldMiddleware = agentShieldMiddleware;
1000
+ exports.applyPolicy = applyPolicy;
1001
+ exports.buildPolicyBlockedResponse = buildBlockedResponse2;
1002
+ exports.buildPolicyRedirectResponse = buildRedirectResponse2;
1003
+ exports.createAgentShieldMiddleware = createAgentShieldMiddleware2;
1004
+ exports.createAgentShieldMiddlewareBase = createAgentShieldMiddleware;
1005
+ exports.createContextFromDetection = createContextFromDetection;
1006
+ exports.createEnhancedAgentShieldMiddleware = createEnhancedAgentShieldMiddleware;
1007
+ exports.createMiddleware = createAgentShieldMiddleware2;
1008
+ exports.evaluatePolicyForDetection = evaluatePolicyForDetection;
1009
+ exports.getAgentShieldClient = getAgentShieldClient;
1010
+ exports.getCheckpointApiClient = getCheckpointApiClient;
1011
+ exports.getPolicy = getPolicy;
1012
+ exports.handlePolicyDecision = handlePolicyDecision;
1013
+ exports.resetAgentShieldClient = resetAgentShieldClient;
1014
+ exports.resetCheckpointApiClient = resetCheckpointApiClient;
1015
+ exports.withAgentShield = withAgentShield;
1016
+ exports.withCheckpoint = withCheckpoint;
1017
+ exports.withCheckpointApi = withCheckpointApi;
1018
+ //# sourceMappingURL=index.js.map
1019
+ //# sourceMappingURL=index.js.map