You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Agent mode phase 1: real sandbox and permission system
Agent mode's only protection against run_command/run_code/background commands was a regex blocklist against command text — no OS-level isolation, no resource limits, and the code's own comments already admitted it "can't catch everything a shell is capable of." This adds two independent layers on top of it:
- Network policy (100% enforceable on every platform, since it just refuses
to run the tool at all): a new networkToolsEnabled setting gates
web_search, fetch_url, http_request, capture_page_screenshot, and the
three GitHub tools in executeTool(). Separately, run_command/run_code/
start_background_command gained a per-call `network` argument (default
false) so the model has to explicitly ask for network access on a
command-by-command basis, visible in the approval card like any other
argument.
- OS-level containment (app/src/command-sandbox.ts): on Linux, wraps the
command with bubblewrap (bwrap) when installed — confines filesystem
writes to the workspace and denies network unless the network argument
above was set. On macOS, generates a sandbox-exec profile doing the same
(built into macOS, no install needed). On Windows there's no lightweight
equivalent primitive (Windows Sandbox needs Pro/Enterprise and is a full
VM-like container; Job Objects/restricted tokens don't confine filesystem
or network) — stays on the blocklist plus the resource limits below, and
the new Settings sandbox-status row says so honestly rather than implying
protection that isn't there. Every call site falls back to running
unwrapped when no mechanism is available, never a hard failure.
Deliberately does NOT offer a plain `unshare --net` fallback on Linux:
creating a network namespace that way commonly requires privileges a
normal user doesn't have, which would make sandboxing *break* commands
instead of just not confining them.
- Resource limits (app/src/resource-monitor.ts, cross-platform via
pidusage): a safety net against a runaway process — configurable memory
and CPU caps, generous defaults, killing the whole process tree
(app/src/process-tree.ts, reused from phase 0) on breach. Complements the
existing 60s run_command timeout, which never applied to background
commands at all.
New Settings section (Chat & Prompts → Agent runtime) exposes all of this:
the network-tools toggle, the resource limit inputs, and a read-only status
row reporting exactly what's enforced on the current OS.
34 new tests across command-sandbox.test.ts, resource-monitor.test.ts, and
process-tree.test.ts, plus 2 new agent-tools.test.ts cases for the
workspace-scoped background-task cleanup from phase 0.
"Execute a shell command in the workspace (or a subdirectory of it) and return its stdout/stderr/exit code. Use for builds, tests, git, npm, etc. Commands that could affect the system outside the workspace (deleting elsewhere, shutting down the machine, privilege escalation, etc.) are rejected.",
137
+
"Execute a shell command in the workspace (or a subdirectory of it) and return its stdout/stderr/exit code. Use for builds, tests, git, npm, etc. Commands that could affect the system outside the workspace (deleting elsewhere, shutting down the machine, privilege escalation, etc.) are rejected. Runs inside an OS-level sandbox confined to the workspace where the platform supports it (Linux with bubblewrap installed, macOS always) — on Windows this containment isn't available and only the command-text checks above apply.",
135
138
parameters: {
136
139
type: "object",
137
140
properties: {
138
141
command: {type: "string",description: "The shell command to run."},
139
142
cwd: {type: "string",description: 'Working directory for the command, relative to the workspace root. Defaults to "."'},
143
+
network: {type: "boolean",description: "Whether this command needs network access (e.g. npm install, curl). Defaults to false — most commands don't need it."},
140
144
},
141
145
required: ["command"],
142
146
},
143
147
},
144
148
{
145
149
name: "run_code",
146
150
description:
147
-
"Run a Python or JavaScript code snippet in the workspace and return its stdout/stderr/exit code. A convenience over run_command for multi-line code (no shell-quoting to worry about) — it is not a sandbox: the code runs with the same permissions as run_command and is subject to the same safety checks.",
151
+
"Run a Python or JavaScript code snippet in the workspace and return its stdout/stderr/exit code. A convenience over run_command for multi-line code (no shell-quoting to worry about) — subject to the same sandboxing (where available) and safety checks as run_command.",
148
152
parameters: {
149
153
type: "object",
150
154
properties: {
151
155
language: {type: "string",enum: ["python","javascript"],description: "Which interpreter to run the code with."},
152
156
code: {type: "string",description: "The full source code to execute."},
153
157
cwd: {type: "string",description: 'Working directory, relative to the workspace root. Defaults to "."'},
158
+
network: {type: "boolean",description: "Whether this code needs network access. Defaults to false."},
154
159
},
155
160
required: ["language","code"],
156
161
},
157
162
},
158
163
{
159
164
name: "start_background_command",
160
165
description:
161
-
"Start a long-running command (dev server, build watcher, long test run) in the background and return immediately with a task id. Use get_background_output to check on it later and stop_background_command when done — unlike run_command, this doesn't block or time out. Subject to the same safety checks as run_command.",
166
+
"Start a long-running command (dev server, build watcher, long test run) in the background and return immediately with a task id. Use get_background_output to check on it later and stop_background_command when done — unlike run_command, this doesn't block or time out. Subject to the same safety checks and sandboxing as run_command.",
162
167
parameters: {
163
168
type: "object",
164
169
properties: {
165
170
command: {type: "string",description: "The shell command to run."},
166
171
cwd: {type: "string",description: 'Working directory, relative to the workspace root. Defaults to "."'},
167
172
name: {type: "string",description: "Short human-readable label for this task (e.g. \"dev server\")."},
173
+
network: {type: "boolean",description: "Whether this command needs network access (e.g. a dev server that fetches data). Defaults to false."},
168
174
},
169
175
required: ["command"],
170
176
},
@@ -784,24 +790,45 @@ function formatCommandResult(stdout: string, stderr: string, exitCode: number |
0 commit comments