@provos/ironcurtain 0.3.1 → 0.4.1

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 (101) hide show
  1. package/README.md +20 -1
  2. package/dist/cli.js +15 -0
  3. package/dist/cli.js.map +1 -1
  4. package/dist/config/index.js +12 -1
  5. package/dist/config/index.js.map +1 -1
  6. package/dist/config/mcp-servers.json +3 -0
  7. package/dist/config/types.d.ts +2 -0
  8. package/dist/config/user-config.d.ts +11 -0
  9. package/dist/config/user-config.js +56 -2
  10. package/dist/config/user-config.js.map +1 -1
  11. package/dist/docker/adapters/claude-code.d.ts +4 -0
  12. package/dist/docker/adapters/claude-code.js +35 -21
  13. package/dist/docker/adapters/claude-code.js.map +1 -1
  14. package/dist/docker/agent-adapter.d.ts +6 -12
  15. package/dist/docker/audit-log-tailer.d.ts +4 -3
  16. package/dist/docker/audit-log-tailer.js +9 -7
  17. package/dist/docker/audit-log-tailer.js.map +1 -1
  18. package/dist/docker/code-mode-proxy.d.ts +34 -0
  19. package/dist/docker/code-mode-proxy.js +137 -0
  20. package/dist/docker/code-mode-proxy.js.map +1 -0
  21. package/dist/docker/docker-agent-session.d.ts +17 -5
  22. package/dist/docker/docker-agent-session.js +171 -36
  23. package/dist/docker/docker-agent-session.js.map +1 -1
  24. package/dist/docker/docker-manager.js +106 -15
  25. package/dist/docker/docker-manager.js.map +1 -1
  26. package/dist/docker/mitm-proxy.d.ts +7 -4
  27. package/dist/docker/mitm-proxy.js +43 -13
  28. package/dist/docker/mitm-proxy.js.map +1 -1
  29. package/dist/docker/orientation.d.ts +3 -2
  30. package/dist/docker/orientation.js +6 -3
  31. package/dist/docker/orientation.js.map +1 -1
  32. package/dist/docker/platform.d.ts +20 -0
  33. package/dist/docker/platform.js +24 -0
  34. package/dist/docker/platform.js.map +1 -0
  35. package/dist/docker/types.d.ts +59 -7
  36. package/dist/sandbox/index.d.ts +25 -1
  37. package/dist/sandbox/index.js +78 -9
  38. package/dist/sandbox/index.js.map +1 -1
  39. package/dist/servers/fetch-server.d.ts +6 -1
  40. package/dist/servers/fetch-server.js +24 -12
  41. package/dist/servers/fetch-server.js.map +1 -1
  42. package/dist/session/agent-session.js +9 -5
  43. package/dist/session/agent-session.js.map +1 -1
  44. package/dist/session/index.js +18 -36
  45. package/dist/session/index.js.map +1 -1
  46. package/dist/session/preflight.js +6 -0
  47. package/dist/session/preflight.js.map +1 -1
  48. package/dist/session/prompts.d.ts +9 -3
  49. package/dist/session/prompts.js +28 -10
  50. package/dist/session/prompts.js.map +1 -1
  51. package/dist/signal/bot-command.d.ts +13 -0
  52. package/dist/signal/bot-command.js +54 -0
  53. package/dist/signal/bot-command.js.map +1 -0
  54. package/dist/signal/format.d.ts +31 -0
  55. package/dist/signal/format.js +101 -0
  56. package/dist/signal/format.js.map +1 -0
  57. package/dist/signal/index.d.ts +16 -0
  58. package/dist/signal/index.js +13 -0
  59. package/dist/signal/index.js.map +1 -0
  60. package/dist/signal/markdown-to-signal.d.ts +29 -0
  61. package/dist/signal/markdown-to-signal.js +154 -0
  62. package/dist/signal/markdown-to-signal.js.map +1 -0
  63. package/dist/signal/setup-signal.d.ts +48 -0
  64. package/dist/signal/setup-signal.js +443 -0
  65. package/dist/signal/setup-signal.js.map +1 -0
  66. package/dist/signal/signal-bot-daemon.d.ts +142 -0
  67. package/dist/signal/signal-bot-daemon.js +504 -0
  68. package/dist/signal/signal-bot-daemon.js.map +1 -0
  69. package/dist/signal/signal-config.d.ts +64 -0
  70. package/dist/signal/signal-config.js +53 -0
  71. package/dist/signal/signal-config.js.map +1 -0
  72. package/dist/signal/signal-container.d.ts +37 -0
  73. package/dist/signal/signal-container.js +112 -0
  74. package/dist/signal/signal-container.js.map +1 -0
  75. package/dist/signal/signal-transport.d.ts +34 -0
  76. package/dist/signal/signal-transport.js +70 -0
  77. package/dist/signal/signal-transport.js.map +1 -0
  78. package/dist/trusted-process/mcp-proxy-server.d.ts +111 -1
  79. package/dist/trusted-process/mcp-proxy-server.js +319 -247
  80. package/dist/trusted-process/mcp-proxy-server.js.map +1 -1
  81. package/dist/trusted-process/path-utils.d.ts +5 -1
  82. package/dist/trusted-process/path-utils.js +47 -4
  83. package/dist/trusted-process/path-utils.js.map +1 -1
  84. package/dist/trusted-process/policy-engine.d.ts +3 -1
  85. package/dist/trusted-process/policy-engine.js +17 -6
  86. package/dist/trusted-process/policy-engine.js.map +1 -1
  87. package/dist/trusted-process/sandbox-integration.d.ts +11 -4
  88. package/dist/trusted-process/sandbox-integration.js +13 -7
  89. package/dist/trusted-process/sandbox-integration.js.map +1 -1
  90. package/dist/trusted-process/tcp-server-transport.d.ts +43 -0
  91. package/dist/trusted-process/tcp-server-transport.js +119 -0
  92. package/dist/trusted-process/tcp-server-transport.js.map +1 -0
  93. package/dist/types/argument-roles.d.ts +7 -0
  94. package/dist/types/argument-roles.js +30 -5
  95. package/dist/types/argument-roles.js.map +1 -1
  96. package/docker/Dockerfile.base.arm64 +23 -0
  97. package/package.json +9 -4
  98. package/src/config/mcp-servers.json +3 -0
  99. package/dist/docker/managed-proxy.d.ts +0 -27
  100. package/dist/docker/managed-proxy.js +0 -175
  101. package/dist/docker/managed-proxy.js.map +0 -1
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Manages the signal-cli-rest-api Docker container.
3
+ *
4
+ * Unlike agent containers (per-session, ephemeral, --network=none),
5
+ * the signal-cli container is a long-lived background service that
6
+ * needs network access to communicate with Signal servers.
7
+ *
8
+ * Delegates to DockerManager for all Docker CLI operations.
9
+ */
10
+ import type { DockerManager } from '../docker/types.js';
11
+ /** Configuration for the signal-cli Docker container. */
12
+ export interface SignalContainerConfig {
13
+ /** Docker image name with tag. */
14
+ readonly image: string;
15
+ /** Host port to bind the REST API to. */
16
+ readonly port: number;
17
+ /** Host directory for signal-cli persistent data. */
18
+ readonly dataDir: string;
19
+ /** Container name for identification. */
20
+ readonly containerName: string;
21
+ }
22
+ /** Manages the signal-cli-rest-api Docker container. */
23
+ export interface SignalContainerManager {
24
+ /** Ensures the container is running. Returns the REST API base URL. Idempotent. */
25
+ ensureRunning(): Promise<string>;
26
+ /** Polls GET /v1/health until the REST API responds. */
27
+ waitForHealthy(baseUrl: string, timeoutMs?: number): Promise<void>;
28
+ /** Stops and removes the container. */
29
+ teardown(): Promise<void>;
30
+ /** Pulls the latest version of the configured Docker image. */
31
+ pullImage(): Promise<void>;
32
+ /** Returns true if the container exists (running or stopped). */
33
+ exists(): Promise<boolean>;
34
+ /** Returns true if the container is currently running. */
35
+ isRunning(): Promise<boolean>;
36
+ }
37
+ export declare function createSignalContainerManager(docker: DockerManager, config: SignalContainerConfig): SignalContainerManager;
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Manages the signal-cli-rest-api Docker container.
3
+ *
4
+ * Unlike agent containers (per-session, ephemeral, --network=none),
5
+ * the signal-cli container is a long-lived background service that
6
+ * needs network access to communicate with Signal servers.
7
+ *
8
+ * Delegates to DockerManager for all Docker CLI operations.
9
+ */
10
+ import { mkdirSync } from 'node:fs';
11
+ import { getSignalDataDir } from './signal-config.js';
12
+ function sleep(ms) {
13
+ return new Promise((resolve) => setTimeout(resolve, ms));
14
+ }
15
+ export function createSignalContainerManager(docker, config) {
16
+ const resolvedDataDir = config.dataDir || getSignalDataDir();
17
+ return {
18
+ async ensureRunning() {
19
+ const baseUrl = `http://127.0.0.1:${config.port}`;
20
+ // Pull latest image (fast no-op when already up to date)
21
+ try {
22
+ await docker.pullImage(config.image);
23
+ }
24
+ catch {
25
+ // Offline or registry unavailable - continue with cached image
26
+ }
27
+ // If container exists, check whether its image is current.
28
+ // If outdated, remove it so we recreate with the new image.
29
+ if (await docker.containerExists(config.containerName)) {
30
+ const containerId = await docker.getImageId(config.containerName);
31
+ const imageId = await docker.getImageId(config.image);
32
+ const outdated = containerId && imageId && containerId !== imageId;
33
+ if (outdated) {
34
+ if (await docker.isRunning(config.containerName)) {
35
+ await docker.stop(config.containerName);
36
+ }
37
+ await docker.remove(config.containerName);
38
+ // Fall through to create below
39
+ }
40
+ else if (await docker.isRunning(config.containerName)) {
41
+ return baseUrl;
42
+ }
43
+ else {
44
+ await docker.start(config.containerName);
45
+ return baseUrl;
46
+ }
47
+ }
48
+ // Create new container via DockerManager.create()
49
+ mkdirSync(resolvedDataDir, { recursive: true });
50
+ await docker.create({
51
+ image: config.image,
52
+ name: config.containerName,
53
+ network: 'bridge',
54
+ ports: [`127.0.0.1:${config.port}:8080`],
55
+ restartPolicy: 'unless-stopped',
56
+ // The signal-cli-rest-api entrypoint needs capabilities for:
57
+ // CHOWN, FOWNER - jsonrpc2-helper creates FIFOs and sets permissions
58
+ // DAC_OVERRIDE - root writes config to UID-1000-owned volume
59
+ // SETUID/SETGID - usermod/groupmod + setpriv to drop privileges
60
+ // KILL - supervisor manages signal-cli child process
61
+ capAdd: ['CHOWN', 'DAC_OVERRIDE', 'FOWNER', 'SETUID', 'SETGID', 'KILL'],
62
+ mounts: [
63
+ {
64
+ source: resolvedDataDir,
65
+ target: '/home/.local/share/signal-cli',
66
+ readonly: false,
67
+ },
68
+ ],
69
+ env: {
70
+ MODE: 'json-rpc',
71
+ // Skip recursive chown on data dir - it's already owned by
72
+ // UID 1000 (matching the container's signal-api user).
73
+ SIGNAL_CLI_CHOWN_ON_STARTUP: 'false',
74
+ },
75
+ command: [],
76
+ });
77
+ await docker.start(config.containerName);
78
+ return baseUrl;
79
+ },
80
+ async waitForHealthy(baseUrl, timeoutMs = 30_000) {
81
+ const start = Date.now();
82
+ let delay = 500;
83
+ while (Date.now() - start < timeoutMs) {
84
+ try {
85
+ const resp = await fetch(`${baseUrl}/v1/health`);
86
+ if (resp.status === 204)
87
+ return;
88
+ }
89
+ catch {
90
+ // Container starting up
91
+ }
92
+ await sleep(delay);
93
+ delay = Math.min(delay * 1.5, 3000);
94
+ }
95
+ throw new Error(`signal-cli container did not become healthy within ${timeoutMs}ms`);
96
+ },
97
+ async teardown() {
98
+ await docker.stop(config.containerName);
99
+ await docker.remove(config.containerName);
100
+ },
101
+ async pullImage() {
102
+ await docker.pullImage(config.image);
103
+ },
104
+ async exists() {
105
+ return docker.containerExists(config.containerName);
106
+ },
107
+ async isRunning() {
108
+ return docker.isRunning(config.containerName);
109
+ },
110
+ };
111
+ }
112
+ //# sourceMappingURL=signal-container.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signal-container.js","sourceRoot":"","sources":["../../src/signal/signal-container.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Signal session transport - thin adapter implementing the Transport interface.
3
+ *
4
+ * One SignalSessionTransport exists per session. It does not own the
5
+ * WebSocket or manage identity verification - those responsibilities
6
+ * belong to the SignalBotDaemon. The transport's job is to:
7
+ *
8
+ * 1. Block run() until close() is called (session lifetime)
9
+ * 2. Create callback factories that bridge session events to the daemon
10
+ */
11
+ import type { Transport } from '../session/transport.js';
12
+ import type { Session, DiagnosticEvent, EscalationRequest } from '../session/types.js';
13
+ import type { SignalBotDaemon } from './signal-bot-daemon.js';
14
+ export declare class SignalSessionTransport implements Transport {
15
+ private session;
16
+ private readonly daemon;
17
+ private exitResolve;
18
+ constructor(daemon: SignalBotDaemon);
19
+ /**
20
+ * Starts the transport. The returned promise resolves when
21
+ * close() is called, which signals that the session is done.
22
+ * This follows the same contract as CliTransport.run().
23
+ */
24
+ run(session: Session): Promise<void>;
25
+ /**
26
+ * Signals the transport to stop. Resolves the run() promise.
27
+ * Does NOT call daemon.endCurrentSession() - the daemon owns
28
+ * session lifecycle and calls this method, not the other way around.
29
+ */
30
+ close(): void;
31
+ createDiagnosticHandler(): (event: DiagnosticEvent) => void;
32
+ createEscalationHandler(): (request: EscalationRequest) => void;
33
+ createEscalationExpiredHandler(): () => void;
34
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Signal session transport - thin adapter implementing the Transport interface.
3
+ *
4
+ * One SignalSessionTransport exists per session. It does not own the
5
+ * WebSocket or manage identity verification - those responsibilities
6
+ * belong to the SignalBotDaemon. The transport's job is to:
7
+ *
8
+ * 1. Block run() until close() is called (session lifetime)
9
+ * 2. Create callback factories that bridge session events to the daemon
10
+ */
11
+ import { formatEscalationBanner } from './format.js';
12
+ export class SignalSessionTransport {
13
+ session = null;
14
+ daemon;
15
+ exitResolve = null;
16
+ constructor(daemon) {
17
+ this.daemon = daemon;
18
+ }
19
+ /**
20
+ * Starts the transport. The returned promise resolves when
21
+ * close() is called, which signals that the session is done.
22
+ * This follows the same contract as CliTransport.run().
23
+ */
24
+ async run(session) {
25
+ this.session = session;
26
+ return new Promise((resolve) => {
27
+ this.exitResolve = resolve;
28
+ });
29
+ }
30
+ /**
31
+ * Signals the transport to stop. Resolves the run() promise.
32
+ * Does NOT call daemon.endCurrentSession() - the daemon owns
33
+ * session lifecycle and calls this method, not the other way around.
34
+ */
35
+ close() {
36
+ this.session = null;
37
+ this.exitResolve?.();
38
+ this.exitResolve = null;
39
+ }
40
+ // --- Callback factories (wired into SessionOptions by the daemon) ---
41
+ createDiagnosticHandler() {
42
+ return (event) => {
43
+ switch (event.kind) {
44
+ case 'tool_call':
45
+ // Don't send every tool call - too noisy for messaging
46
+ break;
47
+ case 'budget_warning':
48
+ this.daemon.sendSignalMessage(`[Budget warning] ${event.message}`).catch(() => { });
49
+ break;
50
+ case 'budget_exhausted':
51
+ this.daemon.sendSignalMessage(`[Budget exhausted] ${event.message}`).catch(() => { });
52
+ break;
53
+ }
54
+ };
55
+ }
56
+ createEscalationHandler() {
57
+ return (request) => {
58
+ this.daemon.setPendingEscalation(request.escalationId);
59
+ const banner = formatEscalationBanner(request);
60
+ this.daemon.sendSignalMessage(banner).catch(() => { });
61
+ };
62
+ }
63
+ createEscalationExpiredHandler() {
64
+ return () => {
65
+ this.daemon.clearPendingEscalation();
66
+ this.daemon.sendSignalMessage('Escalation expired (timed out).').catch(() => { });
67
+ };
68
+ }
69
+ }
70
+ //# sourceMappingURL=signal-transport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signal-transport.js","sourceRoot":"","sources":["../../src/signal/signal-transport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAErD,MAAM,OAAO,sBAAsB;IACzB,OAAO,GAAmB,IAAI,CAAC;IACtB,MAAM,CAAkB;IACjC,WAAW,GAAwB,IAAI,CAAC;IAEhD,YAAY,MAAuB;QACjC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,GAAG,CAAC,OAAgB;QACxB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YACnC,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC;QAC7B,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;OAIG;IACH,KAAK;QACH,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;QACrB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;IAC1B,CAAC;IAED,uEAAuE;IAEvE,uBAAuB;QACrB,OAAO,CAAC,KAAK,EAAE,EAAE;YACf,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;gBACnB,KAAK,WAAW;oBACd,uDAAuD;oBACvD,MAAM;gBACR,KAAK,gBAAgB;oBACnB,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,oBAAoB,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;oBACnF,MAAM;gBACR,KAAK,kBAAkB;oBACrB,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,sBAAsB,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;oBACrF,MAAM;YACV,CAAC;QACH,CAAC,CAAC;IACJ,CAAC;IAED,uBAAuB;QACrB,OAAO,CAAC,OAAO,EAAE,EAAE;YACjB,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;YACvD,MAAM,MAAM,GAAG,sBAAsB,CAAC,OAAO,CAAC,CAAC;YAC/C,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACxD,CAAC,CAAC;IACJ,CAAC;IAED,8BAA8B;QAC5B,OAAO,GAAG,EAAE;YACV,IAAI,CAAC,MAAM,CAAC,sBAAsB,EAAE,CAAC;YACrC,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,iCAAiC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACnF,CAAC,CAAC;IACJ,CAAC;CACF"}
@@ -25,4 +25,114 @@
25
25
  * AUTO_APPROVE_API_KEY -- (optional) API key for the auto-approve model's provider
26
26
  * AUTO_APPROVE_LLM_LOG_PATH -- (optional) path to JSONL file for auto-approve LLM logging
27
27
  */
28
- export {};
28
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
29
+ import { PolicyEngine } from './policy-engine.js';
30
+ import { AuditLog } from './audit-log.js';
31
+ import { type ResolvedSandboxConfig } from './sandbox-integration.js';
32
+ import type { ToolCallRequest, PolicyDecision } from '../types/mcp.js';
33
+ import type { AuditEntry } from '../types/audit.js';
34
+ import { type McpRoot } from './mcp-client-manager.js';
35
+ import { CallCircuitBreaker } from './call-circuit-breaker.js';
36
+ import type { LanguageModelV3 } from '@ai-sdk/provider';
37
+ import type { MCPServerConfig, SandboxAvailabilityPolicy } from '../config/types.js';
38
+ export interface ProxiedTool {
39
+ serverName: string;
40
+ name: string;
41
+ description?: string;
42
+ inputSchema: Record<string, unknown>;
43
+ }
44
+ export interface ClientState {
45
+ client: Client;
46
+ roots: McpRoot[];
47
+ rootsRefreshed?: () => void;
48
+ }
49
+ /** Parsed environment configuration for the proxy server. */
50
+ export interface ProxyEnvConfig {
51
+ auditLogPath: string;
52
+ serversConfig: Record<string, MCPServerConfig>;
53
+ generatedDir: string;
54
+ protectedPaths: string[];
55
+ sessionLogPath: string | undefined;
56
+ allowedDirectory: string | undefined;
57
+ containerWorkspaceDir: string | undefined;
58
+ escalationDir: string | undefined;
59
+ serverCredentials: Record<string, string>;
60
+ sandboxPolicy: SandboxAvailabilityPolicy;
61
+ }
62
+ /** Result of sandbox availability validation. */
63
+ export interface SandboxValidationResult {
64
+ sandboxAvailable: boolean;
65
+ }
66
+ /** MCP tool call response shape returned by handleCallTool. */
67
+ export interface ToolCallResponse {
68
+ content: unknown;
69
+ isError?: boolean;
70
+ [key: string]: unknown;
71
+ }
72
+ /** Dependencies injected into handleCallTool for testability. */
73
+ export interface CallToolDeps {
74
+ toolMap: Map<string, ProxiedTool>;
75
+ policyEngine: PolicyEngine;
76
+ auditLog: AuditLog;
77
+ circuitBreaker: CallCircuitBreaker;
78
+ clientStates: Map<string, ClientState>;
79
+ resolvedSandboxConfigs: Map<string, ResolvedSandboxConfig>;
80
+ allowedDirectory: string | undefined;
81
+ containerWorkspaceDir: string | undefined;
82
+ escalationDir: string | undefined;
83
+ autoApproveModel: LanguageModelV3 | null;
84
+ }
85
+ /**
86
+ * Reads and validates proxy environment variables.
87
+ * Returns a typed config object or calls process.exit(1) on missing required vars.
88
+ */
89
+ export declare function parseProxyEnvConfig(): ProxyEnvConfig;
90
+ /**
91
+ * Validates sandbox availability against the configured policy.
92
+ * Logs warnings to session log. Throws when enforce mode is active
93
+ * but sandboxing is unavailable.
94
+ */
95
+ export declare function validateSandboxAvailability(sandboxPolicy: SandboxAvailabilityPolicy, sessionLogPath: string | undefined, platform: string): SandboxValidationResult;
96
+ /**
97
+ * Resolves per-server sandbox configurations and writes srt settings
98
+ * files for sandboxed servers. Returns the config map and the temp
99
+ * settings directory path.
100
+ */
101
+ export declare function resolveServerSandboxConfigs(serversConfig: Record<string, MCPServerConfig>, allowedDirectory: string | undefined, sandboxAvailable: boolean, sandboxPolicy: SandboxAvailabilityPolicy): {
102
+ resolvedSandboxConfigs: Map<string, ResolvedSandboxConfig>;
103
+ settingsDir: string;
104
+ serverCwdPaths: Map<string, string>;
105
+ };
106
+ /** Builds a lookup map from tool name to ProxiedTool for routing. */
107
+ export declare function buildToolMap(allTools: ProxiedTool[]): Map<string, ProxiedTool>;
108
+ /**
109
+ * Creates an audit entry and logs it. Extracted so the audit shape
110
+ * construction is testable independently.
111
+ */
112
+ export declare function buildAuditEntry(request: ToolCallRequest, argsForTransport: Record<string, unknown>, policyDecision: PolicyDecision, result: AuditEntry['result'], durationMs: number, options: {
113
+ escalationResult?: 'approved' | 'denied';
114
+ sandboxed?: boolean;
115
+ autoApproved?: boolean;
116
+ }): AuditEntry;
117
+ /**
118
+ * Handles a single tool call request: policy evaluation, escalation,
119
+ * circuit breaker check, and forwarding to the real MCP server.
120
+ *
121
+ * This is the core security logic extracted from the CallTool handler
122
+ * for independent unit testing.
123
+ */
124
+ export declare function handleCallTool(toolName: string, rawArgs: Record<string, unknown>, deps: CallToolDeps): Promise<ToolCallResponse>;
125
+ /**
126
+ * Selects and validates the proxy transport based on environment variables.
127
+ * Returns 'tcp', 'uds', or 'stdio' along with the transport options.
128
+ */
129
+ export declare function selectTransportConfig(): {
130
+ kind: 'tcp';
131
+ port: number;
132
+ portFilePath: string | undefined;
133
+ } | {
134
+ kind: 'uds';
135
+ socketPath: string;
136
+ } | {
137
+ kind: 'stdio';
138
+ };