@provos/ironcurtain 0.4.1 → 0.5.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.
- package/README.md +219 -248
- package/dist/cli.js +10 -0
- package/dist/cli.js.map +1 -1
- package/dist/config/config-command.js +144 -6
- package/dist/config/config-command.js.map +1 -1
- package/dist/config/constitution-user-base.md +2 -0
- package/dist/config/first-start.js +77 -12
- package/dist/config/first-start.js.map +1 -1
- package/dist/config/generated/compiled-policy.json +199 -72
- package/dist/config/generated/tool-annotations.json +1018 -36
- package/dist/config/mcp-servers.json +10 -0
- package/dist/config/paths.d.ts +31 -0
- package/dist/config/paths.js +52 -2
- package/dist/config/paths.js.map +1 -1
- package/dist/config/types.d.ts +8 -0
- package/dist/config/user-config.d.ts +21 -3
- package/dist/config/user-config.js +34 -3
- package/dist/config/user-config.js.map +1 -1
- package/dist/docker/adapters/claude-code.js +64 -8
- package/dist/docker/adapters/claude-code.js.map +1 -1
- package/dist/docker/agent-adapter.d.ts +18 -1
- package/dist/docker/docker-agent-session.d.ts +15 -8
- package/dist/docker/docker-agent-session.js +61 -88
- package/dist/docker/docker-agent-session.js.map +1 -1
- package/dist/docker/docker-infrastructure.d.ts +51 -0
- package/dist/docker/docker-infrastructure.js +261 -0
- package/dist/docker/docker-infrastructure.js.map +1 -0
- package/dist/docker/docker-manager.js +3 -0
- package/dist/docker/docker-manager.js.map +1 -1
- package/dist/docker/mitm-proxy.js +4 -0
- package/dist/docker/mitm-proxy.js.map +1 -1
- package/dist/docker/oauth-credentials.d.ts +81 -0
- package/dist/docker/oauth-credentials.js +178 -0
- package/dist/docker/oauth-credentials.js.map +1 -0
- package/dist/docker/orientation.js +1 -1
- package/dist/docker/orientation.js.map +1 -1
- package/dist/docker/provider-config.d.ts +3 -0
- package/dist/docker/provider-config.js +28 -0
- package/dist/docker/provider-config.js.map +1 -1
- package/dist/docker/pty-session.d.ts +27 -0
- package/dist/docker/pty-session.js +420 -0
- package/dist/docker/pty-session.js.map +1 -0
- package/dist/docker/pty-types.d.ts +24 -0
- package/dist/docker/pty-types.js +12 -0
- package/dist/docker/pty-types.js.map +1 -0
- package/dist/docker/types.d.ts +6 -0
- package/dist/escalation/escalation-watcher.d.ts +44 -0
- package/dist/escalation/escalation-watcher.js +92 -0
- package/dist/escalation/escalation-watcher.js.map +1 -0
- package/dist/escalation/listener-command.d.ts +10 -0
- package/dist/escalation/listener-command.js +407 -0
- package/dist/escalation/listener-command.js.map +1 -0
- package/dist/escalation/listener-state.d.ts +49 -0
- package/dist/escalation/listener-state.js +104 -0
- package/dist/escalation/listener-state.js.map +1 -0
- package/dist/escalation/session-registry.d.ts +17 -0
- package/dist/escalation/session-registry.js +98 -0
- package/dist/escalation/session-registry.js.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -1
- package/dist/logger.js +2 -3
- package/dist/logger.js.map +1 -1
- package/dist/pipeline/annotate.js +35 -15
- package/dist/pipeline/annotate.js.map +1 -1
- package/dist/pipeline/constitution-customizer.d.ts +2 -1
- package/dist/pipeline/constitution-customizer.js +36 -3
- package/dist/pipeline/constitution-customizer.js.map +1 -1
- package/dist/pipeline/github-identity.d.ts +38 -0
- package/dist/pipeline/github-identity.js +64 -0
- package/dist/pipeline/github-identity.js.map +1 -0
- package/dist/pipeline/handwritten-scenarios.js +34 -0
- package/dist/pipeline/handwritten-scenarios.js.map +1 -1
- package/dist/sandbox/index.js +12 -3
- package/dist/sandbox/index.js.map +1 -1
- package/dist/servers/fetch-server.js +2 -2
- package/dist/session/base-transport.d.ts +19 -0
- package/dist/session/base-transport.js +36 -0
- package/dist/session/base-transport.js.map +1 -0
- package/dist/session/cli-transport.d.ts +3 -3
- package/dist/session/cli-transport.js +7 -4
- package/dist/session/cli-transport.js.map +1 -1
- package/dist/session/index.js +24 -78
- package/dist/session/index.js.map +1 -1
- package/dist/session/interaction-log.d.ts +13 -0
- package/dist/session/interaction-log.js +28 -0
- package/dist/session/interaction-log.js.map +1 -0
- package/dist/session/preflight.d.ts +3 -0
- package/dist/session/preflight.js +39 -18
- package/dist/session/preflight.js.map +1 -1
- package/dist/session/types.d.ts +2 -0
- package/dist/signal/bot-command.js +5 -0
- package/dist/signal/bot-command.js.map +1 -1
- package/dist/signal/format.d.ts +21 -1
- package/dist/signal/format.js +41 -3
- package/dist/signal/format.js.map +1 -1
- package/dist/signal/signal-bot-daemon.d.ts +53 -18
- package/dist/signal/signal-bot-daemon.js +344 -106
- package/dist/signal/signal-bot-daemon.js.map +1 -1
- package/dist/signal/signal-config.d.ts +4 -0
- package/dist/signal/signal-config.js +2 -0
- package/dist/signal/signal-config.js.map +1 -1
- package/dist/signal/signal-transport.d.ts +13 -7
- package/dist/signal/signal-transport.js +36 -12
- package/dist/signal/signal-transport.js.map +1 -1
- package/dist/trusted-process/audit-log.d.ts +6 -1
- package/dist/trusted-process/audit-log.js +22 -2
- package/dist/trusted-process/audit-log.js.map +1 -1
- package/dist/trusted-process/audit-redactor.d.ts +25 -0
- package/dist/trusted-process/audit-redactor.js +121 -0
- package/dist/trusted-process/audit-redactor.js.map +1 -0
- package/dist/trusted-process/escalation.d.ts +1 -1
- package/dist/trusted-process/escalation.js +6 -1
- package/dist/trusted-process/escalation.js.map +1 -1
- package/dist/trusted-process/index.d.ts +2 -1
- package/dist/trusted-process/index.js +24 -6
- package/dist/trusted-process/index.js.map +1 -1
- package/dist/trusted-process/mcp-client-manager.js +14 -2
- package/dist/trusted-process/mcp-client-manager.js.map +1 -1
- package/dist/trusted-process/mcp-error-utils.d.ts +11 -0
- package/dist/trusted-process/mcp-error-utils.js +50 -0
- package/dist/trusted-process/mcp-error-utils.js.map +1 -0
- package/dist/trusted-process/mcp-proxy-server.d.ts +4 -0
- package/dist/trusted-process/mcp-proxy-server.js +14 -4
- package/dist/trusted-process/mcp-proxy-server.js.map +1 -1
- package/dist/trusted-process/sandbox-integration.d.ts +5 -0
- package/dist/trusted-process/sandbox-integration.js +1 -1
- package/dist/trusted-process/sandbox-integration.js.map +1 -1
- package/dist/trusted-process/server-context.d.ts +21 -0
- package/dist/trusted-process/server-context.js +42 -0
- package/dist/trusted-process/server-context.js.map +1 -0
- package/dist/types/argument-roles.d.ts +5 -2
- package/dist/types/argument-roles.js +25 -2
- package/dist/types/argument-roles.js.map +1 -1
- package/docker/Dockerfile.claude-code +8 -3
- package/docker/entrypoint-claude-code.sh +64 -4
- package/package.json +16 -8
- package/src/config/constitution-user-base.md +2 -0
- package/src/config/generated/compiled-policy.json +199 -72
- package/src/config/generated/tool-annotations.json +1018 -36
- package/src/config/mcp-servers.json +10 -0
package/README.md
CHANGED
|
@@ -42,295 +42,247 @@ The agent clones a repository and edits a file. The policy engine escalates `git
|
|
|
42
42
|
|
|
43
43
|
IronCurtain supports two session modes with different trust models:
|
|
44
44
|
|
|
45
|
-
- **Builtin Agent (Code Mode)** — IronCurtain's own LLM agent writes TypeScript snippets that execute in a V8 sandbox. IronCurtain controls the agent, the sandbox, and the policy engine.
|
|
46
|
-
- **Docker Agent Mode** — An external agent (Claude Code, Goose, etc.) runs inside a Docker container with no network access. IronCurtain doesn't control the agent — it only mediates the agent's external access through policy-enforced proxies.
|
|
45
|
+
- **Builtin Agent (Code Mode)** — IronCurtain's own LLM agent writes TypeScript snippets that execute in a V8 sandbox. IronCurtain controls the agent, the sandbox, and the policy engine. Every tool call exits the sandbox as a structured MCP request, passes through the policy engine (allow / deny / escalate), and only then reaches the real MCP server.
|
|
47
46
|
|
|
48
|
-
|
|
47
|
+
- **Docker Agent Mode** — An external agent (Claude Code, Goose, etc.) runs inside a Docker container with no network access. IronCurtain mediates the external effects: LLM API calls pass through a TLS-terminating MITM proxy (host allowlist, fake-to-real key swap), and MCP tool calls pass through the same policy engine used by Code Mode.
|
|
49
48
|
|
|
50
|
-
|
|
51
|
-
┌─────────────────────────────────────────────┐
|
|
52
|
-
│ Agent (LLM) │
|
|
53
|
-
│ Generates TypeScript to accomplish tasks │
|
|
54
|
-
└──────────────────┬──────────────────────────┘
|
|
55
|
-
│ TypeScript code
|
|
56
|
-
▼
|
|
57
|
-
┌─────────────────────────────────────────────┐
|
|
58
|
-
│ V8 Isolated Sandbox │
|
|
59
|
-
│ Code executes in isolation. │
|
|
60
|
-
│ Only interface to the world: typed │
|
|
61
|
-
│ function stubs that produce MCP requests. │
|
|
62
|
-
│ │
|
|
63
|
-
│ filesystem.read_file({path: '...'}) │
|
|
64
|
-
│ git.status({repo_path: '...'}) │
|
|
65
|
-
└──────────────────┬──────────────────────────┘
|
|
66
|
-
│ MCP tool-call requests
|
|
67
|
-
▼
|
|
68
|
-
┌─────────────────────────────────────────────┐
|
|
69
|
-
│ Trusted Process (MCP Proxy) │
|
|
70
|
-
│ │
|
|
71
|
-
│ ┌───────────────────────────────────────┐ │
|
|
72
|
-
│ │ Policy Engine │ │
|
|
73
|
-
│ │ 1. Structural invariants (hardcoded) │ │
|
|
74
|
-
│ │ 2. Compiled constitution rules │ │
|
|
75
|
-
│ │ → allow / deny / escalate │ │
|
|
76
|
-
│ └───────────────────────────────────────┘ │
|
|
77
|
-
│ ┌──────────────┐ ┌─────────────────────┐ │
|
|
78
|
-
│ │ Audit Log │ │ Escalation Handler │ │
|
|
79
|
-
│ │ (JSONL) │ │ (human approval) │ │
|
|
80
|
-
│ └──────────────┘ └─────────────────────┘ │
|
|
81
|
-
└──────────────────┬──────────────────────────┘
|
|
82
|
-
│ approved calls only
|
|
83
|
-
▼
|
|
84
|
-
┌──────────┐ ┌──────────┐ ┌──────────┐
|
|
85
|
-
│Filesystem│ │ Git │ │ Other │
|
|
86
|
-
│MCP Server│ │MCP Server│ │MCP Server│
|
|
87
|
-
└──────────┘ └──────────┘ └──────────┘
|
|
88
|
-
```
|
|
49
|
+
In both modes, the agent is **untrusted**. Security does not depend on the model following instructions — it is enforced at the boundary.
|
|
89
50
|
|
|
90
|
-
|
|
51
|
+
See [SANDBOXING.md](SANDBOXING.md) for the full architecture with diagrams, layer-by-layer trust analysis, and macOS platform notes.
|
|
91
52
|
|
|
92
|
-
|
|
93
|
-
2. **Sandbox** -- A V8 isolate ([UTCP Code Mode](https://utcp.dev/)) that executes the agent's TypeScript. The only way to interact with the outside world is through typed function stubs that produce structured MCP requests.
|
|
94
|
-
3. **Trusted Process** -- The security kernel. Every MCP request from the sandbox passes through a two-phase policy engine before reaching any real server. Structural checks enforce hardcoded invariants (protected paths, unknown tool denial). Compiled rule evaluation evaluates the compiled constitution rules. Denied calls are blocked; escalated calls are presented to the user for approval.
|
|
95
|
-
4. **MCP Servers** -- Standard [Model Context Protocol](https://modelcontextprotocol.io/) servers that provide filesystem access, git operations, and other capabilities. Only approved requests reach them.
|
|
53
|
+
## Quick Start
|
|
96
54
|
|
|
97
|
-
|
|
55
|
+
### Prerequisites
|
|
98
56
|
|
|
99
|
-
|
|
57
|
+
- Node.js 22+ (required by `isolated-vm`; maximum Node 25)
|
|
58
|
+
- Docker (required for Docker Agent Mode and PTY mode)
|
|
59
|
+
- An API key for at least one LLM provider (Anthropic, Google, or OpenAI)
|
|
100
60
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
61
|
+
### Install
|
|
62
|
+
|
|
63
|
+
**As a global CLI tool (end users):**
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
npm install -g @provos/ironcurtain
|
|
107
67
|
```
|
|
108
68
|
|
|
109
|
-
|
|
110
|
-
2. **Compile** -- Translate the English constitution into deterministic if/then rules. Categorical references ("major news sites", "my contacts") are emitted as `@list-name` symbolic references with list definitions.
|
|
111
|
-
3. **Resolve Lists** -- Resolve dynamic list definitions to concrete values via LLM knowledge or MCP tool-use (e.g., querying a contacts database). Resolved values are written to `dynamic-lists.json` and can be user-inspected/edited. Skipped when no lists are present.
|
|
112
|
-
4. **Generate Scenarios** -- Create test scenarios from the constitution, combined with mandatory handwritten invariant tests.
|
|
113
|
-
5. **Verify & Repair** -- Execute scenarios against the real policy engine. An LLM judge analyzes failures and generates targeted repairs (up to 2 rounds). The build fails if the policy cannot be verified.
|
|
69
|
+
**From source (development):**
|
|
114
70
|
|
|
115
|
-
|
|
71
|
+
```bash
|
|
72
|
+
git clone https://github.com/provos/ironcurtain.git
|
|
73
|
+
cd ironcurtain
|
|
74
|
+
npm install
|
|
75
|
+
```
|
|
116
76
|
|
|
117
|
-
###
|
|
77
|
+
### One-time setup
|
|
118
78
|
|
|
119
|
-
|
|
79
|
+
**1. Set your API key:**
|
|
120
80
|
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
- The agent must receive human approval before git push, pull, fetch, or any remote-contacting operation.
|
|
81
|
+
```bash
|
|
82
|
+
export ANTHROPIC_API_KEY=sk-ant-...
|
|
124
83
|
```
|
|
125
84
|
|
|
126
|
-
|
|
85
|
+
Or add it to `~/.ironcurtain/config.json` via `ironcurtain config`. Environment variables take precedence. Supported: `ANTHROPIC_API_KEY`, `GOOGLE_GENERATIVE_AI_API_KEY`, `OPENAI_API_KEY`.
|
|
127
86
|
|
|
128
|
-
|
|
129
|
-
[
|
|
130
|
-
{
|
|
131
|
-
"tool": "git_status",
|
|
132
|
-
"decision": "allow",
|
|
133
|
-
"condition": { "directory": { "within": "$SANDBOX" } }
|
|
134
|
-
},
|
|
135
|
-
{
|
|
136
|
-
"tool": "git_diff",
|
|
137
|
-
"decision": "allow",
|
|
138
|
-
"condition": { "directory": { "within": "$SANDBOX" } }
|
|
139
|
-
},
|
|
140
|
-
{
|
|
141
|
-
"tool": "git_push",
|
|
142
|
-
"decision": "escalate",
|
|
143
|
-
"reason": "Remote-contacting git operations require human approval"
|
|
144
|
-
}
|
|
145
|
-
]
|
|
146
|
-
```
|
|
87
|
+
**2. Run the first-start wizard** (runs automatically on first `ironcurtain start`, or explicitly):
|
|
147
88
|
|
|
148
|
-
|
|
89
|
+
```bash
|
|
90
|
+
ironcurtain setup
|
|
91
|
+
```
|
|
149
92
|
|
|
150
|
-
|
|
93
|
+
Walks you through GitHub token setup, web search provider, model selection, and other settings. Creates `~/.ironcurtain/config.json` with your choices.
|
|
151
94
|
|
|
152
|
-
|
|
95
|
+
**3. Customize your policy** (optional but recommended):
|
|
153
96
|
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
│ Docker Container (--network=none) │
|
|
157
|
-
│ │
|
|
158
|
-
│ ┌────────────────────────────────────────┐ │
|
|
159
|
-
│ │ External Agent │ │
|
|
160
|
-
│ │ (Claude Code, Goose, etc.) │ │
|
|
161
|
-
│ │ Own LLM loop, tools, execution │ │
|
|
162
|
-
│ └──────┬──────────────────┬──────────────┘ │
|
|
163
|
-
│ │ LLM API calls │ MCP tool calls │
|
|
164
|
-
│ ▼ ▼ │
|
|
165
|
-
│ [UDS] [UDS] │
|
|
166
|
-
└─────────┬──────────────────┬─────────────────┘
|
|
167
|
-
│ │
|
|
168
|
-
▼ ▼
|
|
169
|
-
┌──────────────────┐ ┌─────────────────────────┐
|
|
170
|
-
│ MITM Proxy │ │ MCP Proxy │
|
|
171
|
-
│ (host process) │ │ (host process) │
|
|
172
|
-
│ │ │ │
|
|
173
|
-
│ Host allowlist │ │ Policy Engine │
|
|
174
|
-
│ Endpoint filter │ │ allow / deny / │
|
|
175
|
-
│ Fake→real key │ │ escalate │
|
|
176
|
-
│ swap │ │ │
|
|
177
|
-
└────────┬─────────┘ └────────────┬────────────┘
|
|
178
|
-
│ │
|
|
179
|
-
▼ ▼
|
|
180
|
-
LLM Provider MCP Servers
|
|
181
|
-
(Anthropic, etc.) (filesystem, git, etc.)
|
|
97
|
+
```bash
|
|
98
|
+
ironcurtain customize-policy
|
|
182
99
|
```
|
|
183
100
|
|
|
184
|
-
|
|
101
|
+
An LLM-assisted conversation that generates a constitution tailored to your workflow, saved to `~/.ironcurtain/constitution-user.md`. You can also edit this file directly.
|
|
185
102
|
|
|
186
|
-
|
|
103
|
+
**4. Compile the policy:**
|
|
187
104
|
|
|
188
|
-
|
|
105
|
+
```bash
|
|
106
|
+
ironcurtain compile-policy
|
|
107
|
+
```
|
|
189
108
|
|
|
190
|
-
|
|
109
|
+
Translates your constitution into deterministic rules, generates test scenarios, and verifies them. Compiled artifacts go to `~/.ironcurtain/generated/`. The package ships with pre-compiled defaults — skip this step until you customize.
|
|
191
110
|
|
|
192
|
-
|
|
193
|
-
- An API key for at least one supported LLM provider (Anthropic, Google, or OpenAI)
|
|
111
|
+
## Running Modes
|
|
194
112
|
|
|
195
|
-
###
|
|
113
|
+
### Interactive mode
|
|
196
114
|
|
|
197
|
-
|
|
115
|
+
A multi-turn session where you type tasks and the agent responds:
|
|
198
116
|
|
|
199
117
|
```bash
|
|
200
|
-
|
|
118
|
+
ironcurtain start
|
|
201
119
|
```
|
|
202
120
|
|
|
203
|
-
|
|
121
|
+
Escalated tool calls pause the agent and prompt you with `/approve` or `/deny`.
|
|
122
|
+
|
|
123
|
+
### Single-shot mode
|
|
124
|
+
|
|
125
|
+
Send one task and exit when the agent finishes:
|
|
204
126
|
|
|
205
127
|
```bash
|
|
206
|
-
|
|
207
|
-
cd ironcurtain
|
|
208
|
-
npm install
|
|
128
|
+
ironcurtain start "Summarize the files in the current directory"
|
|
209
129
|
```
|
|
210
130
|
|
|
211
|
-
###
|
|
131
|
+
### Session resume
|
|
212
132
|
|
|
213
|
-
|
|
133
|
+
Resume a previous session's conversation history:
|
|
214
134
|
|
|
215
135
|
```bash
|
|
216
|
-
|
|
136
|
+
ironcurtain start --resume <session-id>
|
|
217
137
|
```
|
|
218
138
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
```json
|
|
222
|
-
{
|
|
223
|
-
"anthropicApiKey": "sk-ant-..."
|
|
224
|
-
}
|
|
225
|
-
```
|
|
139
|
+
Session IDs are printed on session start and stored under `~/.ironcurtain/sessions/`.
|
|
226
140
|
|
|
227
|
-
|
|
141
|
+
### PTY mode and the escalation listener
|
|
228
142
|
|
|
229
|
-
|
|
143
|
+
PTY mode attaches your terminal directly to Claude Code running inside the Docker container. You get Claude Code's full interactive TUI — spinners, diffs, file previews, slash commands — while IronCurtain still mediates every tool call through its policy engine.
|
|
230
144
|
|
|
231
145
|
```bash
|
|
232
|
-
ironcurtain
|
|
146
|
+
ironcurtain start --pty
|
|
233
147
|
```
|
|
234
148
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
### 3. Customize your policy
|
|
238
|
-
|
|
239
|
-
Run the interactive policy customizer to create a constitution tailored to your workflow:
|
|
149
|
+
**Why a separate escalation listener?** In PTY mode your terminal is fully occupied by Claude Code's raw TTY — there is no channel left to inject escalation prompts inline. Each PTY session registers itself in `~/.ironcurtain/pty-registry/` and a separate companion process handles approvals:
|
|
240
150
|
|
|
241
151
|
```bash
|
|
242
|
-
|
|
152
|
+
# Terminal 1 — interactive Claude Code session
|
|
153
|
+
ironcurtain start --pty
|
|
154
|
+
|
|
155
|
+
# Terminal 2 — approve or deny escalations from all active PTY sessions
|
|
156
|
+
ironcurtain escalation-listener
|
|
243
157
|
```
|
|
244
158
|
|
|
245
|
-
|
|
159
|
+
**How session discovery works:**
|
|
246
160
|
|
|
247
|
-
|
|
248
|
-
|
|
161
|
+
1. When `--pty` starts, it writes a registration file to `~/.ironcurtain/pty-registry/session-<id>.json` containing the session ID, escalation directory path, display label, and process PID.
|
|
162
|
+
2. The escalation listener polls `pty-registry/` every second. It discovers new sessions, attaches an escalation watcher to each one, and detaches watchers when sessions end. Stale registrations (process PID no longer alive) are removed automatically, so a crashed PTY session doesn't leave phantom entries.
|
|
249
163
|
|
|
250
|
-
|
|
164
|
+
**The escalation flow:**
|
|
251
165
|
|
|
252
|
-
|
|
253
|
-
2. **No destruction**: Delete operations outside the sandbox are never permitted,
|
|
254
|
-
unless an explicit exception is granted by the user guidance.
|
|
255
|
-
3. **Human oversight**: Operations outside the sandbox require explicit human approval.
|
|
256
|
-
```
|
|
166
|
+
When a tool call is escalated, the PTY session emits a **BEL character** (`\x07`) — your terminal bell or visual flash signals that attention is needed. Switch to the listener terminal. The listener TUI re-renders in place: active sessions are listed with sequential display numbers, and each pending escalation shows the tool name, arguments, and the policy reason for the escalation.
|
|
257
167
|
|
|
258
|
-
|
|
168
|
+
**Listener commands:**
|
|
259
169
|
|
|
260
|
-
|
|
261
|
-
|
|
170
|
+
| Command | Description |
|
|
171
|
+
| -------------- | ----------------------------------------- |
|
|
172
|
+
| `/approve N` | Approve escalation #N |
|
|
173
|
+
| `/deny N` | Deny escalation #N |
|
|
174
|
+
| `/approve all` | Approve all pending escalations |
|
|
175
|
+
| `/deny all` | Deny all pending escalations |
|
|
176
|
+
| `/sessions` | Show detailed session information |
|
|
177
|
+
| `/quit` | Exit the listener |
|
|
262
178
|
|
|
263
|
-
|
|
179
|
+
Escalation numbers are sequential across all sessions. If you have two active PTY sessions, their escalations share a single numbered list in the dashboard — escalation #3 might be from session [1] and #4 from session [2]. Use the number shown in the dashboard to resolve them.
|
|
264
180
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
-
|
|
268
|
-
|
|
269
|
-
-
|
|
270
|
-
```
|
|
181
|
+
**Multiple concurrent sessions:** The listener aggregates escalations from all active PTY sessions in one dashboard. Each session is assigned a display number (e.g., `Claude Code [1]`, `Claude Code [2]`) when it first appears.
|
|
182
|
+
|
|
183
|
+
**Single-instance lock:** Only one escalation listener may run at a time, enforced via a PID-checked lock file at `~/.ironcurtain/escalation-listener.lock`. A stale lock from a crashed process is cleaned up automatically on the next start.
|
|
184
|
+
|
|
185
|
+
**Emergency exit and terminal recovery:** Press `Ctrl-\` to trigger a graceful shutdown of the PTY session (stops containers, proxies, and performs async cleanup). If the process is killed ungracefully (e.g., `kill -9`), run `reset` in that terminal to restore normal terminal mode.
|
|
271
186
|
|
|
272
|
-
|
|
187
|
+
### Signal messaging transport
|
|
273
188
|
|
|
274
|
-
|
|
189
|
+
Run IronCurtain sessions via Signal messages — send tasks, receive responses, and approve or deny escalations from your phone. All communication is end-to-end encrypted via the Signal protocol.
|
|
275
190
|
|
|
276
191
|
```bash
|
|
277
|
-
ironcurtain
|
|
278
|
-
ironcurtain
|
|
279
|
-
ironcurtain refresh-lists # re-resolve dynamic lists without full recompilation
|
|
192
|
+
ironcurtain setup-signal # One-time setup wizard
|
|
193
|
+
ironcurtain bot # Start the Signal bot daemon
|
|
280
194
|
```
|
|
281
195
|
|
|
282
|
-
|
|
196
|
+
See [TRANSPORT.md](TRANSPORT.md) for setup instructions, architecture details, and why we chose Signal over alternatives like Telegram.
|
|
283
197
|
|
|
284
|
-
|
|
198
|
+
## Session Commands
|
|
285
199
|
|
|
286
|
-
|
|
200
|
+
Commands available during an **interactive** or **single-shot** session:
|
|
287
201
|
|
|
288
|
-
|
|
202
|
+
| Command | Description |
|
|
203
|
+
| ---------- | ------------------------------------------------ |
|
|
204
|
+
| `/approve` | Approve the pending escalation |
|
|
205
|
+
| `/deny` | Deny the pending escalation |
|
|
206
|
+
| `/budget` | Show resource consumption (tokens, steps, cost) |
|
|
207
|
+
| `/logs` | Display diagnostic events |
|
|
208
|
+
| `/quit` | End the session |
|
|
289
209
|
|
|
290
|
-
**
|
|
210
|
+
In **PTY mode**, use the escalation listener instead (see above) — the PTY terminal is occupied by Claude Code's TUI.
|
|
291
211
|
|
|
292
|
-
|
|
293
|
-
ironcurtain start
|
|
294
|
-
```
|
|
212
|
+
## Policy: Constitution → Enforcement
|
|
295
213
|
|
|
296
|
-
|
|
214
|
+
You write intent in plain English; IronCurtain compiles it into deterministic rules:
|
|
297
215
|
|
|
298
|
-
```bash
|
|
299
|
-
ironcurtain start "Summarize the files in the current directory"
|
|
300
216
|
```
|
|
217
|
+
constitution.md → [Annotate] → [Compile] → [Resolve Lists] → [Generate Scenarios] → [Verify & Repair]
|
|
218
|
+
│ │ │ │ │
|
|
219
|
+
▼ ▼ ▼ ▼ ▼
|
|
220
|
+
tool-annotations compiled-policy dynamic-lists test-scenarios verified policy
|
|
221
|
+
.json .json .json .json (or build failure)
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
1. **Annotate** — Classify each MCP tool's arguments by role (read-path, write-path, delete-path, none).
|
|
225
|
+
2. **Compile** — Translate the English constitution into deterministic if/then rules. Categorical references ("major news sites", "my contacts") are emitted as `@list-name` symbolic references.
|
|
226
|
+
3. **Resolve Lists** — Resolve symbolic lists to concrete values via LLM knowledge or MCP tool-use (e.g., querying a contacts database). Written to `dynamic-lists.json`, user-editable. Skipped when no lists are present.
|
|
227
|
+
4. **Generate Scenarios** — Create test scenarios from the constitution plus mandatory handwritten invariant tests.
|
|
228
|
+
5. **Verify & Repair** — Run scenarios against the real policy engine. An LLM judge analyzes failures and generates targeted repairs (up to 2 rounds). Build fails if the policy cannot be verified.
|
|
229
|
+
|
|
230
|
+
All artifacts are content-hash cached — only changed inputs trigger recompilation.
|
|
301
231
|
|
|
302
|
-
|
|
232
|
+
### What compiled rules look like
|
|
233
|
+
|
|
234
|
+
A constitution clause like:
|
|
235
|
+
|
|
236
|
+
```markdown
|
|
237
|
+
- The agent may perform read-only git operations (status, diff, log) within the sandbox without approval.
|
|
238
|
+
- The agent must receive human approval before git push, pull, fetch, or any remote-contacting operation.
|
|
239
|
+
```
|
|
303
240
|
|
|
304
|
-
|
|
241
|
+
compiles to:
|
|
305
242
|
|
|
306
|
-
|
|
243
|
+
```json
|
|
244
|
+
[
|
|
245
|
+
{ "tool": "git_status", "decision": "allow", "condition": { "directory": { "within": "$SANDBOX" } } },
|
|
246
|
+
{ "tool": "git_diff", "decision": "allow", "condition": { "directory": { "within": "$SANDBOX" } } },
|
|
247
|
+
{ "tool": "git_push", "decision": "escalate", "reason": "Remote-contacting git operations require human approval" }
|
|
248
|
+
]
|
|
249
|
+
```
|
|
307
250
|
|
|
308
|
-
|
|
251
|
+
Any call that doesn't match an explicit `allow` or `escalate` rule is **denied by default**.
|
|
309
252
|
|
|
310
253
|
```bash
|
|
311
|
-
ironcurtain
|
|
312
|
-
ironcurtain
|
|
254
|
+
ironcurtain annotate-tools # Classify MCP tool arguments (re-run when servers change)
|
|
255
|
+
ironcurtain compile-policy # Compile constitution into rules and verify
|
|
256
|
+
ironcurtain refresh-lists # Re-resolve dynamic lists without full recompilation
|
|
257
|
+
ironcurtain refresh-lists --list major-news # Refresh a single list
|
|
313
258
|
```
|
|
314
259
|
|
|
315
|
-
|
|
260
|
+
Review the generated `~/.ironcurtain/generated/compiled-policy.json` — these are the exact rules enforced at runtime.
|
|
316
261
|
|
|
317
|
-
|
|
262
|
+
## Web Search
|
|
318
263
|
|
|
319
|
-
|
|
264
|
+
IronCurtain's fetch server includes a `web_search` tool backed by your choice of provider. Configure via `ironcurtain config` → **Web Search**, or directly in `~/.ironcurtain/config.json`:
|
|
320
265
|
|
|
321
|
-
|
|
266
|
+
```json
|
|
267
|
+
{
|
|
268
|
+
"webSearch": {
|
|
269
|
+
"provider": "brave",
|
|
270
|
+
"brave": { "apiKey": "BSA..." }
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
```
|
|
322
274
|
|
|
323
|
-
|
|
|
324
|
-
|
|
325
|
-
|
|
|
326
|
-
|
|
|
327
|
-
|
|
|
328
|
-
|
|
329
|
-
|
|
275
|
+
| Provider | `provider` value | API key field | Sign up |
|
|
276
|
+
| -------- | ---------------- | -------------------------- | ----------------------------- |
|
|
277
|
+
| Brave | `"brave"` | `webSearch.brave.apiKey` | https://brave.com/search/api/ |
|
|
278
|
+
| Tavily | `"tavily"` | `webSearch.tavily.apiKey` | https://tavily.com/ |
|
|
279
|
+
| SerpAPI | `"serpapi"` | `webSearch.serpapi.apiKey` | https://serpapi.com/ |
|
|
280
|
+
|
|
281
|
+
`web_search` is available in both builtin and Docker Agent modes. If no provider is configured, calls to `web_search` return an error explaining how to set one up via `ironcurtain config`.
|
|
330
282
|
|
|
331
283
|
## Configuration
|
|
332
284
|
|
|
333
|
-
IronCurtain stores
|
|
285
|
+
IronCurtain stores configuration and session data in `~/.ironcurtain/`:
|
|
334
286
|
|
|
335
287
|
```
|
|
336
288
|
~/.ironcurtain/
|
|
@@ -339,6 +291,7 @@ IronCurtain stores its configuration and session data in `~/.ironcurtain/`:
|
|
|
339
291
|
├── constitution-user.md # Your policy customizations (generated by customize-policy)
|
|
340
292
|
├── generated/ # User-compiled policy artifacts (overrides package defaults)
|
|
341
293
|
├── signal-data/ # Signal transport persistent data (registration keys)
|
|
294
|
+
├── pty-registry/ # Active PTY session registrations (auto-managed)
|
|
342
295
|
├── sessions/
|
|
343
296
|
│ └── {sessionId}/
|
|
344
297
|
│ ├── sandbox/ # Per-session filesystem sandbox
|
|
@@ -347,16 +300,24 @@ IronCurtain stores its configuration and session data in `~/.ironcurtain/`:
|
|
|
347
300
|
│ └── session.log # Diagnostics
|
|
348
301
|
```
|
|
349
302
|
|
|
303
|
+
Edit configuration interactively:
|
|
304
|
+
|
|
305
|
+
```bash
|
|
306
|
+
ironcurtain config
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
See [CONFIG.md](CONFIG.md) for the full configuration reference covering all fields, defaults, and environment variable overrides.
|
|
310
|
+
|
|
350
311
|
### Resource Budgets
|
|
351
312
|
|
|
352
313
|
Sessions enforce configurable limits to prevent runaway agents:
|
|
353
314
|
|
|
354
|
-
| Limit
|
|
355
|
-
|
|
356
|
-
| Max tokens
|
|
357
|
-
| Max steps
|
|
358
|
-
| Session timeout | 30 minutes | `resourceBudget.maxSessionSeconds`
|
|
359
|
-
| Cost cap
|
|
315
|
+
| Limit | Default | Config Key |
|
|
316
|
+
| --------------- | ---------- | ------------------------------------ |
|
|
317
|
+
| Max tokens | 1,000,000 | `resourceBudget.maxTotalTokens` |
|
|
318
|
+
| Max steps | 200 | `resourceBudget.maxSteps` |
|
|
319
|
+
| Session timeout | 30 minutes | `resourceBudget.maxSessionSeconds` |
|
|
320
|
+
| Cost cap | $5.00 | `resourceBudget.maxEstimatedCostUsd` |
|
|
360
321
|
|
|
361
322
|
Set any limit to `null` in `config.json` to disable it.
|
|
362
323
|
|
|
@@ -373,7 +334,11 @@ By default, all escalations require manual `/approve` or `/deny`. You can option
|
|
|
373
334
|
}
|
|
374
335
|
```
|
|
375
336
|
|
|
376
|
-
The auto-approver is conservative — it only approves when intent is unambiguous (e.g., "push my changes to origin" clearly authorizes `git_push`). Vague messages like "go ahead" or "fix the tests" always fall through to human
|
|
337
|
+
The auto-approver is conservative — it only approves when intent is unambiguous (e.g., "push my changes to origin" clearly authorizes `git_push`). Vague messages like "go ahead" or "fix the tests" always fall through to human review. It can never deny — only approve or escalate. All auto-approved actions are recorded in the audit log with `autoApproved: true`.
|
|
338
|
+
|
|
339
|
+
### Audit Redaction
|
|
340
|
+
|
|
341
|
+
Audit log entries may contain sensitive data passed through tool arguments or results. By default, recognized patterns (credit card numbers, SSNs, API keys) are automatically redacted before entries are written to `audit.jsonl`. Disable with `"auditRedaction": { "enabled": false }` in `config.json` for full forensic logging.
|
|
377
342
|
|
|
378
343
|
### Multi-Provider Support
|
|
379
344
|
|
|
@@ -387,37 +352,38 @@ IronCurtain supports multiple LLM providers. Use the `provider:model-name` forma
|
|
|
387
352
|
}
|
|
388
353
|
```
|
|
389
354
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
|
393
|
-
|
|
394
|
-
|
|
|
395
|
-
| Google | `googleApiKey` | `GOOGLE_GENERATIVE_AI_API_KEY` |
|
|
396
|
-
| OpenAI | `openaiApiKey` | `OPENAI_API_KEY` |
|
|
355
|
+
| Provider | Config Key | Environment Variable |
|
|
356
|
+
| --------- | ----------------- | ------------------------------ |
|
|
357
|
+
| Anthropic | `anthropicApiKey` | `ANTHROPIC_API_KEY` |
|
|
358
|
+
| Google | `googleApiKey` | `GOOGLE_GENERATIVE_AI_API_KEY` |
|
|
359
|
+
| OpenAI | `openaiApiKey` | `OPENAI_API_KEY` |
|
|
397
360
|
|
|
398
361
|
Environment variables take precedence over config file values.
|
|
399
362
|
|
|
400
363
|
### Adding MCP Servers
|
|
401
364
|
|
|
402
|
-
IronCurtain ships with filesystem and
|
|
365
|
+
IronCurtain ships with filesystem, git, fetch, and GitHub MCP servers pre-configured. Adding a new server is a developer-level task:
|
|
403
366
|
|
|
404
367
|
1. **Register the server** in `src/config/mcp-servers.json` with its command, arguments, and optional environment variables or sandbox settings.
|
|
405
|
-
2. **Extend the argument role registry** in `src/types/argument-roles.ts` if the new server's tools have argument semantics not covered by existing roles (e.g., `read-path`, `write-path`, `fetch-url`).
|
|
368
|
+
2. **Extend the argument role registry** in `src/types/argument-roles.ts` if the new server's tools have argument semantics not covered by existing roles (e.g., `read-path`, `write-path`, `fetch-url`).
|
|
406
369
|
3. **Update the constitution** in `src/config/constitution.md` to cover the new server's capabilities.
|
|
407
|
-
4. **Re-run `ironcurtain annotate-tools`**
|
|
408
|
-
5. **Re-run `ironcurtain compile-policy`**
|
|
370
|
+
4. **Re-run `ironcurtain annotate-tools`** to classify the new server's tool arguments by role.
|
|
371
|
+
5. **Re-run `ironcurtain compile-policy`** to compile policy rules from your constitution. The verification stage will flag gaps.
|
|
409
372
|
|
|
410
373
|
After compilation, review the updated `tool-annotations.json` and `compiled-policy.json` to verify the new tools are correctly classified and covered by policy.
|
|
411
374
|
|
|
412
375
|
## Built-in Capabilities
|
|
413
376
|
|
|
414
|
-
IronCurtain ships with
|
|
415
|
-
|
|
416
|
-
**Filesystem** (14 tools) — `read_file`, `read_multiple_files`, `write_file`, `edit_file`, `create_directory`, `list_directory`, `directory_tree`, `move_file`, `search_files`, `get_file_info`, `list_allowed_directories`, `file_exists`, `read_file_lines`, `calculate_diff`.
|
|
377
|
+
IronCurtain ships with four pre-configured MCP servers. All tool calls are governed by your compiled policy.
|
|
417
378
|
|
|
418
|
-
|
|
379
|
+
| Server | Tools | Key capabilities |
|
|
380
|
+
| -------------- | ----- | ---------------------------------------------------------------------------------------------- |
|
|
381
|
+
| **Filesystem** | 14 | Read, write, edit, search files; directory tree; move; diff calculation |
|
|
382
|
+
| **Git** | 27 | Full git workflow: status, diff, log, commit, branch, push/pull/fetch, clone, stash, blame |
|
|
383
|
+
| **Fetch** | 2 | HTTP GET with HTML-to-markdown conversion; `web_search` (see [Web Search](#web-search)) |
|
|
384
|
+
| **GitHub** | 41 | Issues, PRs, code search, reviews via `ghcr.io/github/github-mcp-server`; requires a GitHub personal access token |
|
|
419
385
|
|
|
420
|
-
|
|
386
|
+
Read-only operations are allowed by default policy; mutations (writes, pushes, PR creation) escalate for human approval.
|
|
421
387
|
|
|
422
388
|
## Security Model
|
|
423
389
|
|
|
@@ -425,42 +391,44 @@ IronCurtain is designed around a specific threat model: **the LLM goes rogue.**
|
|
|
425
391
|
|
|
426
392
|
### What IronCurtain enforces
|
|
427
393
|
|
|
428
|
-
- **Filesystem containment**
|
|
429
|
-
- **Per-tool policy**
|
|
430
|
-
- **Structural invariants**
|
|
431
|
-
- **Human escalation**
|
|
432
|
-
- **Audit trail**
|
|
433
|
-
- **Resource limits**
|
|
394
|
+
- **Filesystem containment** — Symlink-aware path resolution prevents path traversal and symlink-escape attacks.
|
|
395
|
+
- **Per-tool policy** — Each MCP tool call is evaluated against compiled rules. The policy engine classifies tool arguments by role (read-path, write-path, delete-path) to make fine-grained decisions.
|
|
396
|
+
- **Structural invariants** — Certain protections are hardcoded and cannot be overridden by the constitution: the agent can never modify its own policy files, audit logs, or configuration.
|
|
397
|
+
- **Human escalation** — When policy says "escalate," the agent pauses and the user must explicitly approve or deny. Optionally, an [LLM-based auto-approver](#auto-approve-escalations) handles unambiguous cases.
|
|
398
|
+
- **Audit trail** — Every tool call and policy decision is logged to an append-only JSONL audit log.
|
|
399
|
+
- **Resource limits** — Token, step, time, and cost budgets prevent runaway sessions.
|
|
434
400
|
|
|
435
401
|
### Known limitations
|
|
436
402
|
|
|
437
403
|
This is a research prototype. Known gaps include:
|
|
438
404
|
|
|
439
|
-
- **Policy compilation fidelity**
|
|
440
|
-
- **V8 isolate boundaries**
|
|
441
|
-
- **No outbound content inspection**
|
|
442
|
-
- **Escalation fatigue**
|
|
405
|
+
- **Policy compilation fidelity** — The LLM-based compiler can misinterpret constitution intent. The verification pipeline catches many errors but is not exhaustive. Always review the compiled `compiled-policy.json`.
|
|
406
|
+
- **V8 isolate boundaries** — Code Mode uses V8 isolates, not OS-level virtualization. A V8 zero-day could allow escape.
|
|
407
|
+
- **No outbound content inspection** — An agent allowed to write files could encode sensitive data to bypass content-level controls. Planned: LLM-based intelligibility checks on outbound content.
|
|
408
|
+
- **Escalation fatigue** — Too many false-positive escalations can lead to habitual approval. Tune your constitution to minimize unnecessary prompts.
|
|
443
409
|
|
|
444
410
|
See [docs/SECURITY_CONCERNS.md](docs/SECURITY_CONCERNS.md) for a detailed threat analysis.
|
|
445
411
|
|
|
446
412
|
## Troubleshooting
|
|
447
413
|
|
|
448
|
-
| Issue
|
|
449
|
-
|
|
450
|
-
| **Missing API key**
|
|
451
|
-
| **Sandbox unavailable**
|
|
452
|
-
| **Budget exhausted**
|
|
453
|
-
| **Node version errors**
|
|
454
|
-
| **Policy doesn't match intent**
|
|
455
|
-
| **Auto-approve not triggering**
|
|
456
|
-
| **
|
|
414
|
+
| Issue | Guidance |
|
|
415
|
+
| ------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
416
|
+
| **Missing API key** | Set the environment variable (`ANTHROPIC_API_KEY`, `GOOGLE_GENERATIVE_AI_API_KEY`, or `OPENAI_API_KEY`) or add the corresponding key to `~/.ironcurtain/config.json`. |
|
|
417
|
+
| **Sandbox unavailable** | OS-level sandboxing requires `bubblewrap` and `socat`. Install both, or set `"sandboxPolicy": "warn"` in your MCP server config for development. |
|
|
418
|
+
| **Budget exhausted** | Adjust limits in `~/.ironcurtain/config.json` under `resourceBudget`. Set any individual limit to `null` to disable it. |
|
|
419
|
+
| **Node version errors** | Node.js 22+ is required (`isolated-vm` needs `>=22.0.0`). Maximum supported is Node 25 (`<26`). |
|
|
420
|
+
| **Policy doesn't match intent** | Review `compiled-policy.json` to see the generated rules. Run `ironcurtain customize-policy` to refine your constitution, then `ironcurtain compile-policy` to recompile. Specific wording produces better rules — vague phrasing leads to vague policy. |
|
|
421
|
+
| **Auto-approve not triggering** | The auto-approver only approves when the user's message explicitly authorizes the action (e.g., "push to origin" for `git_push`). Vague messages always escalate to human review. Verify `autoApprove.enabled` is `true` in `config.json`. |
|
|
422
|
+
| **PTY terminal garbled after exit** | Run `reset` in the PTY terminal to restore normal mode. This is needed when the process is killed ungracefully and raw mode is not restored. |
|
|
423
|
+
| **Escalation listener: "already running"** | Another listener holds the lock at `~/.ironcurtain/escalation-listener.lock`. The lock is auto-cleared if the previous process is dead. If it persists, check the PID in the lock file. |
|
|
424
|
+
| **Signal bot not responding** | Verify the signal-cli container is running (`docker ps \| grep ironcurtain-signal`). Check that Signal is configured (`ironcurtain setup-signal`). See [TRANSPORT.md](TRANSPORT.md) for detailed troubleshooting. |
|
|
457
425
|
|
|
458
426
|
## Development
|
|
459
427
|
|
|
460
428
|
```bash
|
|
461
429
|
npm test # Run all tests
|
|
462
430
|
npm test -- test/policy-engine.test.ts # Run a single test file
|
|
463
|
-
|
|
431
|
+
npm test -- -t "denies delete_file" # Run a single test by name
|
|
464
432
|
npm run lint # Lint
|
|
465
433
|
npm run build # TypeScript compilation + asset copy
|
|
466
434
|
```
|
|
@@ -472,16 +440,19 @@ See [TESTING.md](TESTING.md) for the full testing guide, including integration t
|
|
|
472
440
|
```
|
|
473
441
|
src/
|
|
474
442
|
├── index.ts # Entry point
|
|
443
|
+
├── cli.ts # CLI command dispatcher
|
|
475
444
|
├── config/ # Configuration loading, constitution, MCP server definitions
|
|
476
|
-
│ ├── constitution.md #
|
|
445
|
+
│ ├── constitution.md # Base security policy in plain English
|
|
477
446
|
│ ├── mcp-servers.json # MCP server definitions
|
|
478
447
|
│ └── generated/ # Compiled policy artifacts (do not edit manually)
|
|
479
448
|
├── session/ # Multi-turn session management, budgets, loop detection
|
|
480
449
|
├── sandbox/ # V8 isolated execution environment
|
|
481
|
-
├── trusted-process/ # Policy engine, MCP proxy, audit log, escalation
|
|
450
|
+
├── trusted-process/ # Policy engine, MCP proxy, audit log, escalation handler
|
|
482
451
|
├── pipeline/ # Constitution → policy compilation pipeline
|
|
452
|
+
├── escalation/ # Escalation listener: session registry, TUI dashboard, state
|
|
483
453
|
├── signal/ # Signal messaging transport (bot daemon, setup, formatting)
|
|
484
|
-
├── docker/ # Docker agent mode,
|
|
454
|
+
├── docker/ # Docker agent mode, PTY session, MITM proxy, adapters
|
|
455
|
+
├── servers/ # Built-in MCP servers (fetch, web search providers)
|
|
485
456
|
└── types/ # Shared type definitions
|
|
486
457
|
```
|
|
487
458
|
|