@agent-wall/core 0.1.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.
@@ -0,0 +1,1297 @@
1
+ import { z } from 'zod';
2
+ import { EventEmitter } from 'node:events';
3
+
4
+ /**
5
+ * Agent Wall JSON-RPC Types
6
+ *
7
+ * Mirrors the MCP protocol's JSON-RPC 2.0 message format.
8
+ * We define our own types instead of depending on the MCP SDK
9
+ * so Agent Wall has zero coupling to any specific MCP version.
10
+ */
11
+
12
+ declare const JsonRpcRequestSchema: z.ZodObject<{
13
+ jsonrpc: z.ZodLiteral<"2.0">;
14
+ id: z.ZodUnion<[z.ZodString, z.ZodNumber]>;
15
+ method: z.ZodString;
16
+ params: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
17
+ }, "strip", z.ZodTypeAny, {
18
+ jsonrpc: "2.0";
19
+ id: string | number;
20
+ method: string;
21
+ params?: Record<string, unknown> | undefined;
22
+ }, {
23
+ jsonrpc: "2.0";
24
+ id: string | number;
25
+ method: string;
26
+ params?: Record<string, unknown> | undefined;
27
+ }>;
28
+ declare const JsonRpcNotificationSchema: z.ZodObject<{
29
+ jsonrpc: z.ZodLiteral<"2.0">;
30
+ method: z.ZodString;
31
+ params: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
32
+ }, "strip", z.ZodTypeAny, {
33
+ jsonrpc: "2.0";
34
+ method: string;
35
+ params?: Record<string, unknown> | undefined;
36
+ }, {
37
+ jsonrpc: "2.0";
38
+ method: string;
39
+ params?: Record<string, unknown> | undefined;
40
+ }>;
41
+ declare const JsonRpcResponseSchema: z.ZodObject<{
42
+ jsonrpc: z.ZodLiteral<"2.0">;
43
+ id: z.ZodUnion<[z.ZodString, z.ZodNumber]>;
44
+ result: z.ZodOptional<z.ZodUnknown>;
45
+ error: z.ZodOptional<z.ZodObject<{
46
+ code: z.ZodNumber;
47
+ message: z.ZodString;
48
+ data: z.ZodOptional<z.ZodUnknown>;
49
+ }, "strip", z.ZodTypeAny, {
50
+ code: number;
51
+ message: string;
52
+ data?: unknown;
53
+ }, {
54
+ code: number;
55
+ message: string;
56
+ data?: unknown;
57
+ }>>;
58
+ }, "strip", z.ZodTypeAny, {
59
+ jsonrpc: "2.0";
60
+ id: string | number;
61
+ result?: unknown;
62
+ error?: {
63
+ code: number;
64
+ message: string;
65
+ data?: unknown;
66
+ } | undefined;
67
+ }, {
68
+ jsonrpc: "2.0";
69
+ id: string | number;
70
+ result?: unknown;
71
+ error?: {
72
+ code: number;
73
+ message: string;
74
+ data?: unknown;
75
+ } | undefined;
76
+ }>;
77
+ declare const JsonRpcMessageSchema: z.ZodUnion<[z.ZodObject<{
78
+ jsonrpc: z.ZodLiteral<"2.0">;
79
+ id: z.ZodUnion<[z.ZodString, z.ZodNumber]>;
80
+ method: z.ZodString;
81
+ params: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
82
+ }, "strip", z.ZodTypeAny, {
83
+ jsonrpc: "2.0";
84
+ id: string | number;
85
+ method: string;
86
+ params?: Record<string, unknown> | undefined;
87
+ }, {
88
+ jsonrpc: "2.0";
89
+ id: string | number;
90
+ method: string;
91
+ params?: Record<string, unknown> | undefined;
92
+ }>, z.ZodObject<{
93
+ jsonrpc: z.ZodLiteral<"2.0">;
94
+ method: z.ZodString;
95
+ params: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
96
+ }, "strip", z.ZodTypeAny, {
97
+ jsonrpc: "2.0";
98
+ method: string;
99
+ params?: Record<string, unknown> | undefined;
100
+ }, {
101
+ jsonrpc: "2.0";
102
+ method: string;
103
+ params?: Record<string, unknown> | undefined;
104
+ }>, z.ZodObject<{
105
+ jsonrpc: z.ZodLiteral<"2.0">;
106
+ id: z.ZodUnion<[z.ZodString, z.ZodNumber]>;
107
+ result: z.ZodOptional<z.ZodUnknown>;
108
+ error: z.ZodOptional<z.ZodObject<{
109
+ code: z.ZodNumber;
110
+ message: z.ZodString;
111
+ data: z.ZodOptional<z.ZodUnknown>;
112
+ }, "strip", z.ZodTypeAny, {
113
+ code: number;
114
+ message: string;
115
+ data?: unknown;
116
+ }, {
117
+ code: number;
118
+ message: string;
119
+ data?: unknown;
120
+ }>>;
121
+ }, "strip", z.ZodTypeAny, {
122
+ jsonrpc: "2.0";
123
+ id: string | number;
124
+ result?: unknown;
125
+ error?: {
126
+ code: number;
127
+ message: string;
128
+ data?: unknown;
129
+ } | undefined;
130
+ }, {
131
+ jsonrpc: "2.0";
132
+ id: string | number;
133
+ result?: unknown;
134
+ error?: {
135
+ code: number;
136
+ message: string;
137
+ data?: unknown;
138
+ } | undefined;
139
+ }>]>;
140
+ type JsonRpcRequest = z.infer<typeof JsonRpcRequestSchema>;
141
+ type JsonRpcNotification = z.infer<typeof JsonRpcNotificationSchema>;
142
+ type JsonRpcResponse = z.infer<typeof JsonRpcResponseSchema>;
143
+ type JsonRpcMessage = z.infer<typeof JsonRpcMessageSchema>;
144
+ /** MCP tools/call request params */
145
+ interface ToolCallParams {
146
+ name: string;
147
+ arguments?: Record<string, unknown>;
148
+ }
149
+ /** MCP tools/list result */
150
+ interface ToolListResult {
151
+ tools: Array<{
152
+ name: string;
153
+ description?: string;
154
+ inputSchema?: Record<string, unknown>;
155
+ annotations?: Record<string, unknown>;
156
+ }>;
157
+ nextCursor?: string;
158
+ }
159
+ /** MCP response content block (text, image, resource) */
160
+ interface McpContentBlock {
161
+ type: string;
162
+ text?: string;
163
+ data?: string;
164
+ mimeType?: string;
165
+ [key: string]: unknown;
166
+ }
167
+ declare function isRequest(msg: JsonRpcMessage): msg is JsonRpcRequest;
168
+ declare function isNotification(msg: JsonRpcMessage): msg is JsonRpcNotification;
169
+ declare function isResponse(msg: JsonRpcMessage): msg is JsonRpcResponse;
170
+ declare function isToolCall(msg: JsonRpcMessage): boolean;
171
+ declare function isToolList(msg: JsonRpcMessage): boolean;
172
+ declare function getToolCallParams(msg: JsonRpcRequest): ToolCallParams | null;
173
+ /**
174
+ * Create a JSON-RPC error response for a denied tool call.
175
+ */
176
+ declare function createDenyResponse(id: string | number, message: string): JsonRpcResponse;
177
+ /**
178
+ * Create a JSON-RPC error response for a tool call requiring approval.
179
+ */
180
+ declare function createPromptResponse(id: string | number, message: string): JsonRpcResponse;
181
+
182
+ /**
183
+ * Agent Wall Read Buffer
184
+ *
185
+ * Accumulates raw bytes from a stream and extracts
186
+ * newline-delimited JSON-RPC messages one at a time.
187
+ *
188
+ * Directly mirrors the MCP SDK's ReadBuffer pattern:
189
+ * - Append raw chunks
190
+ * - Scan for '\n' delimiter
191
+ * - Extract line, strip '\r', JSON.parse, validate
192
+ *
193
+ * Security: Enforces maximum buffer size to prevent DOS
194
+ * via unbounded memory growth from a single large message.
195
+ */
196
+
197
+ declare class ReadBuffer {
198
+ private buffer;
199
+ private maxBufferSize;
200
+ constructor(maxBufferSize?: number);
201
+ /**
202
+ * Append raw bytes from a stream chunk.
203
+ * Throws if the buffer exceeds the configured maximum size.
204
+ */
205
+ append(chunk: Buffer): void;
206
+ /**
207
+ * Try to extract the next complete JSON-RPC message.
208
+ * Returns null if no complete message is available yet.
209
+ * Automatically skips empty lines.
210
+ */
211
+ readMessage(): JsonRpcMessage | null;
212
+ /**
213
+ * Extract ALL available messages from the buffer.
214
+ */
215
+ readAllMessages(): JsonRpcMessage[];
216
+ /**
217
+ * Clear the buffer.
218
+ */
219
+ clear(): void;
220
+ /**
221
+ * Check if there's any pending data in the buffer.
222
+ */
223
+ get hasPendingData(): boolean;
224
+ /**
225
+ * Get current buffer size in bytes.
226
+ */
227
+ get currentSize(): number;
228
+ }
229
+ /**
230
+ * Error thrown when buffer exceeds maximum size.
231
+ */
232
+ declare class BufferOverflowError extends Error {
233
+ constructor(message: string);
234
+ }
235
+ /**
236
+ * Parse a single line of text into a validated JSON-RPC message.
237
+ */
238
+ declare function deserializeMessage(line: string): JsonRpcMessage;
239
+ /**
240
+ * Serialize a JSON-RPC message to a newline-delimited string.
241
+ */
242
+ declare function serializeMessage(message: JsonRpcMessage): string;
243
+
244
+ /**
245
+ * Agent Wall Prompt Injection Detector
246
+ *
247
+ * Detects prompt injection attacks in tool call arguments.
248
+ * This is the #1 attack vector for AI agents — an attacker embeds
249
+ * instructions in data (emails, documents, web pages) that trick
250
+ * the AI into executing malicious actions.
251
+ *
252
+ * Detection layers:
253
+ * 1. Known injection phrases (e.g., "ignore previous instructions")
254
+ * 2. Role/system prompt markers (e.g., "<|im_start|>system")
255
+ * 3. Instruction override patterns (e.g., "IMPORTANT: new instructions")
256
+ * 4. Encoded injection (base64-encoded injection strings)
257
+ * 5. Unicode obfuscation (homoglyphs, zero-width chars)
258
+ */
259
+
260
+ interface InjectionDetectorConfig {
261
+ /** Enable injection detection (default: true) */
262
+ enabled?: boolean;
263
+ /** Sensitivity: "low" (fewer false positives), "medium", "high" (catches more) */
264
+ sensitivity?: "low" | "medium" | "high";
265
+ /** Custom patterns to add (regex strings) */
266
+ customPatterns?: string[];
267
+ /** Tool names to exclude from injection scanning */
268
+ excludeTools?: string[];
269
+ }
270
+ interface InjectionScanResult {
271
+ /** Whether injection was detected */
272
+ detected: boolean;
273
+ /** Confidence: "low", "medium", "high" */
274
+ confidence: "low" | "medium" | "high";
275
+ /** All matches found */
276
+ matches: InjectionMatch[];
277
+ /** Human-readable summary */
278
+ summary: string;
279
+ }
280
+ interface InjectionMatch {
281
+ /** Which pattern category matched */
282
+ category: string;
283
+ /** The matched text (truncated for safety) */
284
+ matched: string;
285
+ /** Which argument key contained the match */
286
+ argumentKey: string;
287
+ /** Confidence level for this specific match */
288
+ confidence: "low" | "medium" | "high";
289
+ }
290
+ declare class InjectionDetector {
291
+ private config;
292
+ private customRegexes;
293
+ constructor(config?: InjectionDetectorConfig);
294
+ /**
295
+ * Scan a tool call's arguments for prompt injection.
296
+ */
297
+ scan(toolCall: ToolCallParams): InjectionScanResult;
298
+ /**
299
+ * Extract a string from an argument value (handles nested objects).
300
+ */
301
+ private extractString;
302
+ }
303
+
304
+ /**
305
+ * Agent Wall Egress Control (URL/SSRF Protection)
306
+ *
307
+ * Inspects tool call arguments for URLs and blocks requests to:
308
+ * - Private/internal IPs (RFC1918: 10.x, 172.16-31.x, 192.168.x)
309
+ * - Loopback addresses (127.x, ::1, localhost)
310
+ * - Link-local addresses (169.254.x — AWS/cloud metadata endpoint)
311
+ * - Cloud metadata endpoints (169.254.169.254)
312
+ * - Blocked domains (configurable)
313
+ *
314
+ * Supports allowlist mode: only explicitly allowed domains pass.
315
+ */
316
+
317
+ interface EgressControlConfig {
318
+ /** Enable egress control (default: true when configured) */
319
+ enabled?: boolean;
320
+ /** Allowed domains (if set, ONLY these domains pass — allowlist mode) */
321
+ allowedDomains?: string[];
322
+ /** Blocked domains (blocklist mode, used when allowedDomains is not set) */
323
+ blockedDomains?: string[];
324
+ /** Block private/internal IPs (default: true) */
325
+ blockPrivateIPs?: boolean;
326
+ /** Block cloud metadata endpoints (default: true) */
327
+ blockMetadataEndpoints?: boolean;
328
+ /** Tool names to exclude from egress scanning */
329
+ excludeTools?: string[];
330
+ }
331
+ interface EgressCheckResult {
332
+ /** Whether the call is safe to proceed */
333
+ allowed: boolean;
334
+ /** URLs found in arguments */
335
+ urlsFound: EgressUrlInfo[];
336
+ /** URLs that were blocked */
337
+ blocked: EgressUrlInfo[];
338
+ /** Human-readable summary */
339
+ summary: string;
340
+ }
341
+ interface EgressUrlInfo {
342
+ /** The original URL string */
343
+ url: string;
344
+ /** Parsed hostname */
345
+ hostname: string;
346
+ /** Why it was blocked (if blocked) */
347
+ reason?: string;
348
+ /** Which argument key contained this URL */
349
+ argumentKey: string;
350
+ }
351
+ declare class EgressControl {
352
+ private config;
353
+ constructor(config?: EgressControlConfig);
354
+ /**
355
+ * Check a tool call's arguments for blocked URLs.
356
+ */
357
+ check(toolCall: ToolCallParams): EgressCheckResult;
358
+ /**
359
+ * Check a single hostname/URL against all rules.
360
+ * Returns the block reason, or null if allowed.
361
+ *
362
+ * Check order matters: more specific checks (obfuscated, metadata) run
363
+ * before generic private IP, so the reason message is precise.
364
+ */
365
+ private checkUrl;
366
+ }
367
+
368
+ /**
369
+ * Agent Wall Kill Switch
370
+ *
371
+ * Emergency "deny all" mechanism for instantly shutting down
372
+ * all tool call forwarding. Three activation methods:
373
+ *
374
+ * 1. File-based: touch .agent-wall-kill in cwd or home dir
375
+ * 2. Programmatic: killSwitch.activate() / killSwitch.deactivate()
376
+ * 3. Signal-based: SIGUSR2 toggles the kill switch (Unix only)
377
+ *
378
+ * When active, ALL tool calls are denied immediately with a clear message.
379
+ * The kill switch is checked FIRST in the proxy pipeline — before policy
380
+ * engine, injection detection, or any other check.
381
+ */
382
+ interface KillSwitchConfig {
383
+ /** Enable kill switch checking (default: true) */
384
+ enabled?: boolean;
385
+ /** Check for kill file on filesystem (default: true) */
386
+ checkFile?: boolean;
387
+ /** File names to check (default: [".agent-wall-kill"]) */
388
+ killFileNames?: string[];
389
+ /** Directories to check for kill files (default: [cwd, home]) */
390
+ checkDirs?: string[];
391
+ /** How often to check the kill file in ms (default: 1000) */
392
+ pollIntervalMs?: number;
393
+ /** Register SIGUSR2 signal handler to toggle (default: true on Unix) */
394
+ registerSignal?: boolean;
395
+ }
396
+ interface KillSwitchStatus {
397
+ /** Whether the kill switch is currently active */
398
+ active: boolean;
399
+ /** How it was activated */
400
+ reason: string;
401
+ /** When it was activated */
402
+ activatedAt: string | null;
403
+ }
404
+ declare class KillSwitch {
405
+ private config;
406
+ private manuallyActive;
407
+ private fileActive;
408
+ private activeReason;
409
+ private activatedAt;
410
+ private pollTimer;
411
+ constructor(config?: KillSwitchConfig);
412
+ /**
413
+ * Check if the kill switch is currently active.
414
+ * This should be called at the TOP of the proxy pipeline.
415
+ */
416
+ isActive(): boolean;
417
+ /**
418
+ * Get the current kill switch status.
419
+ */
420
+ getStatus(): KillSwitchStatus;
421
+ /**
422
+ * Programmatically activate the kill switch.
423
+ */
424
+ activate(reason?: string): void;
425
+ /**
426
+ * Programmatically deactivate the kill switch.
427
+ * Note: file-based kill switch must be deactivated by removing the file.
428
+ */
429
+ deactivate(): void;
430
+ /**
431
+ * Start polling for kill files.
432
+ */
433
+ private startPolling;
434
+ /**
435
+ * Check if any kill file exists.
436
+ */
437
+ private checkKillFiles;
438
+ /**
439
+ * Register SIGUSR2 signal handler to toggle kill switch.
440
+ * SIGUSR2 is used (not SIGUSR1) because some tools use SIGUSR1.
441
+ */
442
+ private registerSignalHandler;
443
+ /**
444
+ * Stop the kill switch (cleanup timers and signal handlers).
445
+ */
446
+ dispose(): void;
447
+ }
448
+
449
+ /**
450
+ * Agent Wall Tool Call Chain Detector
451
+ *
452
+ * Detects suspicious sequences of tool calls that indicate
453
+ * multi-step attacks. Individual calls may look innocent,
454
+ * but the CHAIN reveals the attack:
455
+ *
456
+ * read_file(.env) → write_file(tmp.txt) → bash(curl) = EXFILTRATION
457
+ * list_directory(/) → read_file(passwd) → read_file(shadow) = RECON
458
+ * write_file(script.sh) → bash(chmod +x) → bash(./script.sh) = DROPPER
459
+ *
460
+ * The detector maintains a sliding window of recent tool calls
461
+ * and matches against known attack chain patterns.
462
+ */
463
+
464
+ interface ChainDetectorConfig {
465
+ /** Enable chain detection (default: true) */
466
+ enabled?: boolean;
467
+ /** Sliding window size (number of recent calls to track) */
468
+ windowSize?: number;
469
+ /** Time window in ms (calls older than this are dropped) */
470
+ windowMs?: number;
471
+ /** Custom chain patterns to add */
472
+ customChains?: ChainPattern[];
473
+ }
474
+ interface ChainPattern {
475
+ /** Unique name for this chain pattern */
476
+ name: string;
477
+ /** Ordered sequence of tool name glob patterns */
478
+ sequence: string[];
479
+ /** Severity: "low", "medium", "high", "critical" */
480
+ severity: "low" | "medium" | "high" | "critical";
481
+ /** Human-readable description */
482
+ message: string;
483
+ /** Whether argument values must match across steps (e.g., same file read then sent) */
484
+ trackArguments?: boolean;
485
+ }
486
+ interface ChainDetectionResult {
487
+ /** Whether a suspicious chain was detected */
488
+ detected: boolean;
489
+ /** Matched chain patterns */
490
+ matches: ChainMatchInfo[];
491
+ /** Human-readable summary */
492
+ summary: string;
493
+ }
494
+ interface ChainMatchInfo {
495
+ /** Name of the matched chain pattern */
496
+ chain: string;
497
+ /** Severity level */
498
+ severity: "low" | "medium" | "high" | "critical";
499
+ /** The tool calls that formed the chain */
500
+ calls: string[];
501
+ /** Description */
502
+ message: string;
503
+ }
504
+ declare class ChainDetector {
505
+ private config;
506
+ private history;
507
+ private allChains;
508
+ constructor(config?: ChainDetectorConfig);
509
+ /**
510
+ * Record a tool call and check for suspicious chains.
511
+ * Call this AFTER the policy engine allows the call.
512
+ */
513
+ record(toolCall: ToolCallParams): ChainDetectionResult;
514
+ /**
515
+ * Check if the current history matches a chain pattern.
516
+ * Looks for the sequence appearing in order (not necessarily consecutive).
517
+ */
518
+ private matchesChain;
519
+ /**
520
+ * Match a tool name against a pipe-separated glob-like pattern.
521
+ */
522
+ private matchesToolPattern;
523
+ /**
524
+ * Remove entries outside the time window or exceeding window size.
525
+ */
526
+ private pruneHistory;
527
+ /**
528
+ * Clear the call history (e.g., on session reset).
529
+ */
530
+ reset(): void;
531
+ /**
532
+ * Get the current call history length.
533
+ */
534
+ getHistoryLength(): number;
535
+ }
536
+
537
+ /**
538
+ * Agent Wall Policy Engine
539
+ *
540
+ * Evaluates tool calls against YAML-defined rules.
541
+ * Rules are matched in order — first match wins.
542
+ * If no rule matches, the default action applies.
543
+ *
544
+ * Security hardening:
545
+ * - Path normalization (resolves ../ before matching)
546
+ * - Unicode NFC normalization (prevents homoglyph bypass)
547
+ * - Zero-trust "strict" mode (default deny, only explicit allows pass)
548
+ * - Safe regex construction (prevents ReDoS in argument matching)
549
+ * - Deep argument scanning across all string values
550
+ */
551
+
552
+ type RuleAction = "allow" | "deny" | "prompt";
553
+ /** Policy mode: "standard" (backward-compatible) or "strict" (zero-trust) */
554
+ type PolicyMode = "standard" | "strict";
555
+ interface RuleMatch {
556
+ /** Glob patterns matched against string values in arguments */
557
+ arguments?: Record<string, string>;
558
+ }
559
+ interface RateLimitConfig {
560
+ maxCalls: number;
561
+ windowSeconds: number;
562
+ }
563
+ interface PolicyRule {
564
+ name: string;
565
+ /** Glob pattern for tool name(s). Use "|" to separate multiple patterns. */
566
+ tool: string;
567
+ /** Optional argument matching */
568
+ match?: RuleMatch;
569
+ /** What to do when this rule matches */
570
+ action: RuleAction;
571
+ /** Human-readable message shown when rule triggers */
572
+ message?: string;
573
+ /** Rate limiting for this rule's scope */
574
+ rateLimit?: RateLimitConfig;
575
+ }
576
+ /** Security modules configuration. */
577
+ interface SecurityConfig {
578
+ /** Prompt injection detection */
579
+ injectionDetection?: InjectionDetectorConfig;
580
+ /** URL/SSRF egress control */
581
+ egressControl?: EgressControlConfig;
582
+ /** Emergency kill switch */
583
+ killSwitch?: KillSwitchConfig;
584
+ /** Tool call chain/sequence detection */
585
+ chainDetection?: ChainDetectorConfig;
586
+ /** Enable HMAC-SHA256 audit log signing */
587
+ signing?: boolean;
588
+ /** Signing key (auto-generated per session if not provided) */
589
+ signingKey?: string;
590
+ }
591
+ interface PolicyConfig {
592
+ version: number;
593
+ /** Policy mode: "standard" (default) or "strict" (zero-trust deny-by-default) */
594
+ mode?: PolicyMode;
595
+ /** Default action when no rule matches (overridden to "deny" in strict mode) */
596
+ defaultAction?: RuleAction;
597
+ /** Global rate limit across all tools */
598
+ globalRateLimit?: RateLimitConfig;
599
+ /** Response scanning configuration */
600
+ responseScanning?: ResponseScannerPolicyConfig;
601
+ /** Security modules (injection detection, egress control, kill switch, chain detection) */
602
+ security?: SecurityConfig;
603
+ /** Ordered list of rules — first match wins */
604
+ rules: PolicyRule[];
605
+ }
606
+ /** Response scanning section in the YAML policy. */
607
+ interface ResponseScannerPolicyConfig {
608
+ enabled?: boolean;
609
+ maxResponseSize?: number;
610
+ oversizeAction?: "block" | "redact";
611
+ detectSecrets?: boolean;
612
+ detectPII?: boolean;
613
+ /** Action for base64 blob detection: "pass" (default), "redact", or "block" */
614
+ base64Action?: "pass" | "redact" | "block";
615
+ /** Maximum number of custom patterns allowed (default: 100) */
616
+ maxPatterns?: number;
617
+ patterns?: Array<{
618
+ name: string;
619
+ pattern: string;
620
+ flags?: string;
621
+ action: "pass" | "redact" | "block";
622
+ message?: string;
623
+ category?: string;
624
+ }>;
625
+ }
626
+ interface PolicyVerdict {
627
+ action: RuleAction;
628
+ rule: string | null;
629
+ message: string;
630
+ }
631
+ declare class PolicyEngine {
632
+ private config;
633
+ private rateLimiter;
634
+ constructor(config: PolicyConfig);
635
+ /**
636
+ * Update the policy configuration (e.g., after file reload).
637
+ */
638
+ updateConfig(config: PolicyConfig): void;
639
+ /**
640
+ * Evaluate a tool call against the policy rules.
641
+ * Returns the verdict: allow, deny, or prompt.
642
+ */
643
+ evaluate(toolCall: ToolCallParams): PolicyVerdict;
644
+ /**
645
+ * Check if a rule matches a tool call.
646
+ */
647
+ private matchesRule;
648
+ /**
649
+ * Match a tool name against a pattern.
650
+ * Pattern can contain "|" for multiple alternatives.
651
+ */
652
+ private matchToolName;
653
+ /**
654
+ * Match rule argument patterns against actual tool arguments.
655
+ * Each key in ruleArgs is an argument name, value is a glob pattern.
656
+ * ALL specified argument patterns must match for the rule to match.
657
+ *
658
+ * Security: normalizes paths and unicode before matching.
659
+ * Security: checks key aliases (path → file, filepath, etc.)
660
+ */
661
+ private matchArguments;
662
+ /**
663
+ * Get the current policy config.
664
+ */
665
+ getConfig(): PolicyConfig;
666
+ /**
667
+ * Get all rule names.
668
+ */
669
+ getRuleNames(): string[];
670
+ }
671
+
672
+ /**
673
+ * Agent Wall Policy Loader
674
+ *
675
+ * Loads and validates policy configuration from YAML files.
676
+ * Supports loading from a specific path or auto-discovering
677
+ * agent-wall.yaml in the current directory or parent directories.
678
+ */
679
+
680
+ /**
681
+ * Load a policy config from a specific file path.
682
+ */
683
+ declare function loadPolicyFile(filePath: string): PolicyConfig;
684
+ /**
685
+ * Parse a YAML string into a validated PolicyConfig.
686
+ */
687
+ declare function parsePolicyYaml(yamlContent: string): PolicyConfig;
688
+ /**
689
+ * Auto-discover the nearest agent-wall.yaml by walking up
690
+ * from the given directory (defaults to cwd).
691
+ * Returns the file path if found, null otherwise.
692
+ */
693
+ declare function discoverPolicyFile(startDir?: string): string | null;
694
+ /**
695
+ * Load the policy config by auto-discovering the config file.
696
+ * Falls back to default policy if no config file found.
697
+ */
698
+ declare function loadPolicy(configPath?: string): {
699
+ config: PolicyConfig;
700
+ filePath: string | null;
701
+ };
702
+ /**
703
+ * Get the default policy config.
704
+ * Ships with Agent Wall — provides reasonable security out of the box.
705
+ */
706
+ declare function getDefaultPolicy(): PolicyConfig;
707
+ /**
708
+ * Generate the default agent-wall.yaml content for `agent-wall init`.
709
+ */
710
+ declare function generateDefaultConfigYaml(): string;
711
+
712
+ /**
713
+ * Agent Wall Response Scanner
714
+ *
715
+ * Inspects MCP server responses BEFORE they reach the AI agent.
716
+ * This is the second half of the firewall:
717
+ * - Policy Engine controls what goes IN (tool calls)
718
+ * - Response Scanner controls what comes OUT (tool results)
719
+ *
720
+ * Detects:
721
+ * 1. Secret leaks (API keys, tokens, passwords in response text)
722
+ * 2. Data exfiltration markers (base64 blobs, large hex dumps)
723
+ * 3. Sensitive file content (private keys, certificates, AWS creds)
724
+ * 4. Oversized responses (context window stuffing)
725
+ * 5. Custom patterns (user-defined via YAML config)
726
+ *
727
+ * Security:
728
+ * - ReDoS-safe pattern validation (rejects dangerous regex)
729
+ * - Max pattern count enforcement
730
+ * - Configurable base64 blob action
731
+ * - Generic redaction markers (no pattern name leak)
732
+ */
733
+ type ResponseAction = "pass" | "redact" | "block";
734
+ /** A single pattern to match against response content. */
735
+ interface ResponsePattern {
736
+ /** Unique name for this pattern */
737
+ name: string;
738
+ /** Regex pattern to match against response text */
739
+ pattern: string;
740
+ /** Flags for the regex (default: "gi") */
741
+ flags?: string;
742
+ /** What to do when the pattern matches */
743
+ action: ResponseAction;
744
+ /** Human-readable description */
745
+ message?: string;
746
+ /** Category for grouping in audit logs */
747
+ category?: string;
748
+ }
749
+ /** Configuration for response scanning. */
750
+ interface ResponseScannerConfig {
751
+ /** Enable/disable the response scanner (default: true) */
752
+ enabled?: boolean;
753
+ /** Maximum response size in bytes before blocking (0 = no limit) */
754
+ maxResponseSize?: number;
755
+ /** Action when response exceeds maxResponseSize: "block" or "redact" (truncate) */
756
+ oversizeAction?: "block" | "redact";
757
+ /** Built-in secret detection (default: true) */
758
+ detectSecrets?: boolean;
759
+ /** Built-in PII detection (default: false — opt-in) */
760
+ detectPII?: boolean;
761
+ /** Action for base64 blob detection: "pass" (default), "redact", or "block" */
762
+ base64Action?: ResponseAction;
763
+ /** Maximum number of custom patterns allowed (default: 100) */
764
+ maxPatterns?: number;
765
+ /** Custom patterns to match against response content */
766
+ patterns?: ResponsePattern[];
767
+ }
768
+ /** Result of scanning a response. */
769
+ interface ScanResult {
770
+ /** Whether the response is clean */
771
+ clean: boolean;
772
+ /** The final action to take */
773
+ action: ResponseAction;
774
+ /** All findings */
775
+ findings: ScanFinding[];
776
+ /** If action is "redact", the redacted response text */
777
+ redactedText?: string;
778
+ /** Original size in bytes */
779
+ originalSize: number;
780
+ }
781
+ /** A single finding from the scanner. */
782
+ interface ScanFinding {
783
+ /** Name of the pattern that matched */
784
+ pattern: string;
785
+ /** Category */
786
+ category: string;
787
+ /** The action this pattern requests */
788
+ action: ResponseAction;
789
+ /** Human-readable message */
790
+ message: string;
791
+ /** Number of matches found */
792
+ matchCount: number;
793
+ /** Preview of matched content (redacted) */
794
+ preview?: string;
795
+ }
796
+ /**
797
+ * Validate a regex pattern is safe from ReDoS attacks.
798
+ * Returns true if the pattern is safe, false if potentially dangerous.
799
+ */
800
+ declare function isRegexSafe(pattern: string): boolean;
801
+ declare class ResponseScanner {
802
+ private config;
803
+ private compiledPatterns;
804
+ private rejectedPatterns;
805
+ constructor(config?: ResponseScannerConfig);
806
+ /**
807
+ * Compile all regex patterns upfront for performance.
808
+ * Validates each pattern for ReDoS safety before compilation.
809
+ */
810
+ private compilePatterns;
811
+ /**
812
+ * Safely compile a pattern. For user patterns, validate ReDoS safety first.
813
+ */
814
+ private safeCompile;
815
+ /**
816
+ * Scan a response text for sensitive content.
817
+ */
818
+ scan(text: string): ScanResult;
819
+ /**
820
+ * Scan the content array from an MCP tools/call response.
821
+ * MCP responses have the shape: { content: [{ type: "text", text: "..." }, ...] }
822
+ */
823
+ scanMcpResponse(result: unknown): ScanResult;
824
+ /**
825
+ * Extract all text content from an MCP response result.
826
+ */
827
+ private extractText;
828
+ /**
829
+ * Resolve the highest-severity action from all findings.
830
+ * Priority: block > redact > pass
831
+ */
832
+ private resolveAction;
833
+ /**
834
+ * Redact matched patterns from the text.
835
+ * Uses generic [REDACTED] marker — never leaks pattern names.
836
+ */
837
+ private redactText;
838
+ /**
839
+ * Create a safe preview of matched content (first 4 chars + last 4).
840
+ */
841
+ private createPreview;
842
+ /**
843
+ * Get the current configuration.
844
+ */
845
+ getConfig(): ResponseScannerConfig;
846
+ /**
847
+ * Get the number of compiled patterns.
848
+ */
849
+ getPatternCount(): number;
850
+ /**
851
+ * Get list of patterns that were rejected during compilation.
852
+ */
853
+ getRejectedPatterns(): string[];
854
+ /**
855
+ * Update configuration (e.g., after policy file reload).
856
+ */
857
+ updateConfig(config: ResponseScannerConfig): void;
858
+ }
859
+ /**
860
+ * Create a scanner with sensible defaults (secrets ON, PII OFF).
861
+ */
862
+ declare function createDefaultScanner(): ResponseScanner;
863
+
864
+ /**
865
+ * Agent Wall Audit Logger
866
+ *
867
+ * Structured logging of every tool call and its policy verdict.
868
+ * Logs to stderr (JSON) and optionally to a file.
869
+ * This is the audit trail — proof of what the agent did and what was blocked.
870
+ *
871
+ * Security:
872
+ * - HMAC-SHA256 chain signing (tamper-evident log entries)
873
+ * - Log rotation (max file size with automatic rotation)
874
+ * - File permission checks on policy files
875
+ */
876
+
877
+ interface AuditEntry {
878
+ timestamp: string;
879
+ sessionId: string;
880
+ direction: "request" | "response";
881
+ method: string;
882
+ tool?: string;
883
+ arguments?: Record<string, unknown>;
884
+ verdict?: {
885
+ action: RuleAction;
886
+ rule: string | null;
887
+ message: string;
888
+ };
889
+ responsePreview?: string;
890
+ latencyMs?: number;
891
+ error?: string;
892
+ }
893
+ /** Signed audit entry — includes HMAC chain for tamper evidence. */
894
+ interface SignedAuditEntry extends AuditEntry {
895
+ /** HMAC-SHA256 of this entry + previous hash (chain) */
896
+ _sig?: string;
897
+ /** Sequence number in the chain */
898
+ _seq?: number;
899
+ }
900
+ interface AuditLoggerOptions {
901
+ /** Log to stdout as JSON lines (default: true) */
902
+ stdout?: boolean;
903
+ /** Log to a file (JSON lines) */
904
+ filePath?: string;
905
+ /** Redact sensitive values in arguments (default: true) */
906
+ redact?: boolean;
907
+ /** Maximum argument value length before truncation */
908
+ maxArgLength?: number;
909
+ /** Silent mode — no output */
910
+ silent?: boolean;
911
+ /** Enable HMAC-SHA256 chain signing */
912
+ signing?: boolean;
913
+ /** HMAC signing key (auto-generated per session if not provided) */
914
+ signingKey?: string;
915
+ /** Max log file size in bytes before rotation (default: 50MB, 0 = no limit) */
916
+ maxFileSize?: number;
917
+ /** Number of rotated log files to keep (default: 5) */
918
+ maxFiles?: number;
919
+ /** Callback fired after every log entry (for dashboard streaming) */
920
+ onEntry?: (entry: AuditEntry) => void;
921
+ }
922
+ declare class AuditLogger {
923
+ private options;
924
+ private fileFd;
925
+ private entries;
926
+ private prevHash;
927
+ private seqCounter;
928
+ private currentFileSize;
929
+ constructor(options?: AuditLoggerOptions);
930
+ /**
931
+ * Open or reopen the log file using synchronous fd (reliable on Windows).
932
+ */
933
+ private openLogFile;
934
+ /**
935
+ * Log a tool call with its policy verdict.
936
+ */
937
+ log(entry: AuditEntry): void;
938
+ /**
939
+ * Compute HMAC-SHA256 for a log entry in the chain.
940
+ * Chain: HMAC(entry_json + prev_hash)
941
+ */
942
+ private computeHmac;
943
+ /**
944
+ * Rotate log files: current → .1, .1 → .2, etc.
945
+ * Oldest file beyond maxFiles is deleted.
946
+ */
947
+ private rotateLogFile;
948
+ /**
949
+ * Log a denied tool call (convenience method).
950
+ */
951
+ logDeny(sessionId: string, tool: string, args: Record<string, unknown>, ruleName: string | null, message: string): void;
952
+ /**
953
+ * Log an allowed tool call (convenience method).
954
+ */
955
+ logAllow(sessionId: string, tool: string, args: Record<string, unknown>, ruleName: string | null, message: string): void;
956
+ /**
957
+ * Get all logged entries (for the audit command).
958
+ */
959
+ getEntries(): AuditEntry[];
960
+ /**
961
+ * Get summary statistics.
962
+ */
963
+ getStats(): {
964
+ total: number;
965
+ allowed: number;
966
+ denied: number;
967
+ prompted: number;
968
+ };
969
+ /**
970
+ * Redact sensitive argument values.
971
+ */
972
+ private redactEntry;
973
+ /**
974
+ * Verify the HMAC chain integrity of a log file.
975
+ * Returns { valid: boolean, entries: number, firstBroken: number | null }
976
+ */
977
+ static verifyChain(logFilePath: string, signingKey: string): {
978
+ valid: boolean;
979
+ entries: number;
980
+ firstBroken: number | null;
981
+ };
982
+ /**
983
+ * Set or replace the onEntry callback (for dashboard streaming).
984
+ */
985
+ setOnEntry(callback: ((entry: AuditEntry) => void) | undefined): void;
986
+ /**
987
+ * Close the logger (flush file stream).
988
+ */
989
+ close(): void;
990
+ }
991
+ interface FilePermissionCheckResult {
992
+ safe: boolean;
993
+ warnings: string[];
994
+ }
995
+ /**
996
+ * Check if a policy file has safe permissions.
997
+ * Warns if world-writable, group-writable, or owned by different user.
998
+ * On Windows this is best-effort (no Unix permission model).
999
+ */
1000
+ declare function checkFilePermissions(filePath: string): FilePermissionCheckResult;
1001
+
1002
+ /**
1003
+ * Agent Wall Stdio Proxy
1004
+ *
1005
+ * The core of Agent Wall. Sits between an MCP client and server,
1006
+ * intercepting every JSON-RPC message over stdio (stdin/stdout).
1007
+ *
1008
+ * Architecture:
1009
+ * MCP Client (e.g. Claude Code)
1010
+ * ↕ stdin/stdout
1011
+ * Agent Wall Proxy (this file)
1012
+ * ↕ stdin/stdout (child process)
1013
+ * MCP Server (e.g. filesystem server)
1014
+ *
1015
+ * The proxy:
1016
+ * 1. Spawns the real MCP server as a child process
1017
+ * 2. Reads JSON-RPC from its own stdin (from the MCP client)
1018
+ * 3. For tools/call requests: evaluates against the policy engine
1019
+ * 4. If allowed: forwards to child's stdin
1020
+ * 5. If denied: returns a JSON-RPC error without forwarding
1021
+ * 6. Pipes child's stdout back to its own stdout (to the MCP client)
1022
+ */
1023
+
1024
+ interface ProxyOptions {
1025
+ /** The command to spawn (e.g. "npx") */
1026
+ command: string;
1027
+ /** Arguments for the command (e.g. ["@modelcontextprotocol/server-filesystem", "/path"]) */
1028
+ args?: string[];
1029
+ /** Environment variables for the child process */
1030
+ env?: Record<string, string>;
1031
+ /** Working directory for the child process */
1032
+ cwd?: string;
1033
+ /** The policy engine instance */
1034
+ policyEngine: PolicyEngine;
1035
+ /** The response scanner instance (optional — if not provided, responses pass through) */
1036
+ responseScanner?: ResponseScanner;
1037
+ /** The audit logger instance */
1038
+ logger: AuditLogger;
1039
+ /** Session ID for audit logging */
1040
+ sessionId?: string;
1041
+ /** Callback for prompt actions — return true to allow, false to deny */
1042
+ onPrompt?: (tool: string, args: Record<string, unknown>, message: string) => Promise<boolean>;
1043
+ /** Called when the proxy is ready (child process spawned) */
1044
+ onReady?: () => void;
1045
+ /** Called when the proxy exits */
1046
+ onExit?: (code: number | null) => void;
1047
+ /** Called on error */
1048
+ onError?: (error: Error) => void;
1049
+ /** Maximum buffer size in bytes (default: 10MB) */
1050
+ maxBufferSize?: number;
1051
+ /** TTL for pending tool calls in ms (default: 30000) — prevents memory leaks */
1052
+ pendingCallTtlMs?: number;
1053
+ /** Prompt injection detector (optional) */
1054
+ injectionDetector?: InjectionDetector;
1055
+ /** Egress/SSRF control (optional) */
1056
+ egressControl?: EgressControl;
1057
+ /** Emergency kill switch (optional) */
1058
+ killSwitch?: KillSwitch;
1059
+ /** Tool call chain detector (optional) */
1060
+ chainDetector?: ChainDetector;
1061
+ }
1062
+ interface ProxyEvents {
1063
+ ready: [];
1064
+ exit: [code: number | null];
1065
+ error: [error: Error];
1066
+ denied: [tool: string, message: string];
1067
+ allowed: [tool: string];
1068
+ prompted: [tool: string, message: string];
1069
+ responseBlocked: [tool: string, findings: string];
1070
+ responseRedacted: [tool: string, findings: string];
1071
+ injectionDetected: [tool: string, summary: string];
1072
+ egressBlocked: [tool: string, summary: string];
1073
+ killSwitchActive: [tool: string];
1074
+ chainDetected: [tool: string, summary: string];
1075
+ }
1076
+ declare class StdioProxy extends EventEmitter {
1077
+ private child;
1078
+ private clientBuffer;
1079
+ private serverBuffer;
1080
+ private options;
1081
+ private sessionId;
1082
+ private running;
1083
+ private stats;
1084
+ /** Track pending tools/call requests by JSON-RPC id, so we can correlate responses */
1085
+ private pendingToolCalls;
1086
+ /** Cleanup timer for expired pending calls */
1087
+ private pendingCleanupTimer;
1088
+ private pendingCallTtlMs;
1089
+ constructor(options: ProxyOptions);
1090
+ /**
1091
+ * Start the proxy — spawn the child MCP server and begin intercepting.
1092
+ */
1093
+ start(): Promise<void>;
1094
+ /**
1095
+ * Stop the proxy — gracefully shut down the child process.
1096
+ * Follows the MCP SDK pattern: stdin.end() → SIGTERM → SIGKILL
1097
+ */
1098
+ stop(): Promise<void>;
1099
+ /**
1100
+ * Get proxy statistics.
1101
+ */
1102
+ getStats(): {
1103
+ forwarded: number;
1104
+ denied: number;
1105
+ prompted: number;
1106
+ total: number;
1107
+ scanned: number;
1108
+ responseBlocked: number;
1109
+ responseRedacted: number;
1110
+ };
1111
+ /**
1112
+ * Wire up the stdin/stdout pipelines with interception.
1113
+ */
1114
+ private setupPipelines;
1115
+ /**
1116
+ * Start periodic cleanup of expired pending tool calls.
1117
+ */
1118
+ private startPendingCallCleanup;
1119
+ /**
1120
+ * Stop the pending call cleanup timer.
1121
+ */
1122
+ private stopPendingCallCleanup;
1123
+ /**
1124
+ * Process buffered messages from the MCP client.
1125
+ * This is where policy enforcement happens.
1126
+ */
1127
+ private processClientMessages;
1128
+ /**
1129
+ * Process buffered messages from the MCP server.
1130
+ * Applies response scanning before forwarding to the client.
1131
+ */
1132
+ private processServerMessages;
1133
+ /**
1134
+ * Handle a single message from the MCP server.
1135
+ * If it's a response to a tools/call we tracked, scan it.
1136
+ */
1137
+ private handleServerMessage;
1138
+ /**
1139
+ * Build a redacted MCP response by replacing text content.
1140
+ */
1141
+ private buildRedactedResponse;
1142
+ /**
1143
+ * Handle a single message from the MCP client.
1144
+ *
1145
+ * Security check order (defense in depth):
1146
+ * 1. Kill switch — if active, deny ALL calls immediately
1147
+ * 2. Injection detection — scan arguments for prompt injection
1148
+ * 3. Egress control — check for blocked URLs/IPs
1149
+ * 4. Policy engine — evaluate rules (existing behavior)
1150
+ * 5. Chain detection — record call and check for suspicious sequences
1151
+ */
1152
+ private handleClientMessage;
1153
+ /**
1154
+ * Handle an allowed tool call — forward to server.
1155
+ */
1156
+ private handleAllow;
1157
+ /**
1158
+ * Handle a denied tool call — return error to client, never forward.
1159
+ */
1160
+ private handleDeny;
1161
+ /**
1162
+ * Handle a prompt tool call — ask for human approval.
1163
+ */
1164
+ private handlePrompt;
1165
+ /**
1166
+ * Extract scannable text from a JSON-RPC error response.
1167
+ */
1168
+ private extractErrorText;
1169
+ /**
1170
+ * Write a JSON-RPC message to the MCP server (child's stdin).
1171
+ */
1172
+ private writeToServer;
1173
+ /**
1174
+ * Write a JSON-RPC message to the MCP client (our stdout).
1175
+ */
1176
+ private writeToClient;
1177
+ }
1178
+ /**
1179
+ * Create a terminal-based prompt handler.
1180
+ *
1181
+ * IMPORTANT: stdin/stdout are reserved for MCP JSON-RPC protocol.
1182
+ * We open /dev/tty (Unix) or CON (Windows) directly to read from
1183
+ * the controlling terminal. This prevents conflicts with the
1184
+ * MCP message stream on stdin.
1185
+ *
1186
+ * Output goes to stderr (safe — MCP only uses stdout).
1187
+ */
1188
+ declare function createTerminalPromptHandler(): (tool: string, args: Record<string, unknown>, message: string) => Promise<boolean>;
1189
+
1190
+ /**
1191
+ * Agent Wall Dashboard Server
1192
+ *
1193
+ * HTTP + WebSocket server that bridges proxy events to a browser-based
1194
+ * security dashboard. Serves the built React SPA and streams real-time
1195
+ * events over WebSocket.
1196
+ *
1197
+ * Usage:
1198
+ * const dashboard = new DashboardServer({ port: 61100, proxy, killSwitch });
1199
+ * await dashboard.start();
1200
+ */
1201
+
1202
+ type WsMessageType = "event" | "stats" | "audit" | "killSwitch" | "ruleHits" | "config" | "welcome";
1203
+ interface WsMessage<T = unknown> {
1204
+ type: WsMessageType;
1205
+ ts: string;
1206
+ payload: T;
1207
+ }
1208
+ interface ProxyEventPayload {
1209
+ event: string;
1210
+ tool: string;
1211
+ detail: string;
1212
+ severity: "info" | "warn" | "critical";
1213
+ }
1214
+ interface StatsPayload {
1215
+ forwarded: number;
1216
+ denied: number;
1217
+ prompted: number;
1218
+ total: number;
1219
+ scanned: number;
1220
+ responseBlocked: number;
1221
+ responseRedacted: number;
1222
+ uptime: number;
1223
+ killSwitchActive: boolean;
1224
+ }
1225
+ interface RuleHitsPayload {
1226
+ rules: Array<{
1227
+ name: string;
1228
+ action: string;
1229
+ hits: number;
1230
+ }>;
1231
+ }
1232
+ interface ConfigPayload {
1233
+ defaultAction: string;
1234
+ ruleCount: number;
1235
+ mode: string;
1236
+ security: {
1237
+ injection: boolean;
1238
+ egress: boolean;
1239
+ killSwitch: boolean;
1240
+ chain: boolean;
1241
+ signing: boolean;
1242
+ };
1243
+ }
1244
+ type ClientWsMessage = {
1245
+ type: "toggleKillSwitch";
1246
+ } | {
1247
+ type: "getStats";
1248
+ } | {
1249
+ type: "getConfig";
1250
+ } | {
1251
+ type: "getAuditLog";
1252
+ limit?: number;
1253
+ filter?: string;
1254
+ };
1255
+ interface DashboardServerOptions {
1256
+ /** Port to listen on */
1257
+ port: number;
1258
+ /** The proxy instance to observe */
1259
+ proxy: StdioProxy;
1260
+ /** Kill switch instance (for toggle control) */
1261
+ killSwitch?: KillSwitch;
1262
+ /** Policy engine (for config summary) */
1263
+ policyEngine?: PolicyEngine;
1264
+ /** Audit logger (for log queries) */
1265
+ logger?: AuditLogger;
1266
+ /** Directory containing the built React SPA */
1267
+ staticDir?: string;
1268
+ /** Stats broadcast interval in ms (default: 2000) */
1269
+ statsIntervalMs?: number;
1270
+ }
1271
+ declare class DashboardServer {
1272
+ private httpServer;
1273
+ private wss;
1274
+ private statsTimer;
1275
+ private ruleHitCounts;
1276
+ private startTime;
1277
+ private options;
1278
+ constructor(options: DashboardServerOptions);
1279
+ start(): Promise<void>;
1280
+ stop(): Promise<void>;
1281
+ /** Get the actual port (useful when port 0 is used for testing) */
1282
+ getPort(): number;
1283
+ private wireProxyEvents;
1284
+ /** Called by the audit logger's onEntry callback */
1285
+ handleAuditEntry(entry: AuditEntry): void;
1286
+ private handleClientMessage;
1287
+ private broadcast;
1288
+ private sendTo;
1289
+ private broadcastStats;
1290
+ private sendStats;
1291
+ private buildStats;
1292
+ private sendConfig;
1293
+ private sendRuleHits;
1294
+ private handleHttpRequest;
1295
+ }
1296
+
1297
+ export { type AuditEntry, AuditLogger, type AuditLoggerOptions, BufferOverflowError, type ChainDetectionResult, ChainDetector, type ChainDetectorConfig, type ChainMatchInfo, type ChainPattern, type ClientWsMessage, type ConfigPayload, DashboardServer, type DashboardServerOptions, type EgressCheckResult, EgressControl, type EgressControlConfig, type EgressUrlInfo, type FilePermissionCheckResult, InjectionDetector, type InjectionDetectorConfig, type InjectionMatch, type InjectionScanResult, type JsonRpcMessage, JsonRpcMessageSchema, type JsonRpcNotification, JsonRpcNotificationSchema, type JsonRpcRequest, JsonRpcRequestSchema, type JsonRpcResponse, JsonRpcResponseSchema, KillSwitch, type KillSwitchConfig, type KillSwitchStatus, type McpContentBlock, type PolicyConfig, PolicyEngine, type PolicyMode, type PolicyRule, type PolicyVerdict, type ProxyEventPayload, type ProxyEvents, type ProxyOptions, type RateLimitConfig, ReadBuffer, type ResponseAction, type ResponsePattern, ResponseScanner, type ResponseScannerConfig, type ResponseScannerPolicyConfig, type RuleAction, type RuleHitsPayload, type RuleMatch, type ScanFinding, type ScanResult, type SecurityConfig, type SignedAuditEntry, type StatsPayload, StdioProxy, type ToolCallParams, type ToolListResult, type WsMessage, type WsMessageType, checkFilePermissions, createDefaultScanner, createDenyResponse, createPromptResponse, createTerminalPromptHandler, deserializeMessage, discoverPolicyFile, generateDefaultConfigYaml, getDefaultPolicy, getToolCallParams, isNotification, isRegexSafe, isRequest, isResponse, isToolCall, isToolList, loadPolicy, loadPolicyFile, parsePolicyYaml, serializeMessage };