# Command Execution

The rules that apply when the agent runs a shell command.

`run_command` never sees a raw shell string. Argv-only input, bare-command-name enforcement, and a parsed (not pattern-matched) `sh -c` script are the three layers — see the Sandbox page for the exact rules. The same rules cover the background variant (v1.3.1); `run_command_background` was renamed `background_command` (Removed in v1.3.2). Commands on the read-only allowlist (532 embedded entries since v1.3.3, 409 after `cd` was dropped in v1.3.0; matched on the binary plus up to two arguments since v1.0.25: `git status`, `docker ps`, `ls`, ...) skip the confirm gate unless an argument is a sensitive path or the call sets `network: true`; everything else is gated by the active permission mode, and under `auto` an irreversible command (destructive Git, infrastructure, cloud, deploy, migration or SQL operations) still asks even when an allow rule matches. Since v1.3.3 the allowlist check lives in `internal/agents/exec/readonly`: commands that can set values or load plugins (`date`, `hostname`, `sysctl`, `nm`, `objdump`) get per-command argument checks, `--version` / `-v` entries must match the whole argv, git global flags are limited to an explicit list and git options that start an external program (`--ext-diff`, `--textconv`, `-O`) are refused, and an `sh -c` / `bash -c` script matches only when every inner command does and it has no variable assignment, `export`, `printf -v`, arithmetic or parameter expansion. Matched calls run concurrently. Since v1.3.3 a `run_command` / `background_command` confirmation also shows a one-sentence explanation written by the dispatcher model. Networking inside the sandbox is off unless the call sets `network: true` (v1.0.25), which confirms under `safe` and runs under `auto`. Since v1.3.0 `sandbox: false` runs one command outside the sandbox, and only after the user's system password is verified, whatever the permission mode or allow rules. Since v1.3.2 an argv `['sudo', '<binary>', ...]` is accepted too: it raises a `sudo: <cmd>` confirmation that always asks for the system password (none is shown while the sudo ticket is still valid), then runs as `sudo -n <binary> ...` outside the sandbox; `sudo` inside `sh -c` is still rejected. Since v1.3.11 an argv that contains a secret mask this daemon produced (see [Security Model](/security)) is refused, so a command that needs a key reads it inside the same call, such as `['sh', '-c', 'set -a; . ./.env; set +a; curl ...']`.

Since v1.3.0 a TUI line starting with `!` runs the rest as a shell command typed by the user: it goes to `$SHELL -c` (else `/bin/sh`) in the TUI's work directory with the user's own permissions, outside the sandbox and without the confirm gate, capped at 30 minutes; the exit code and output (up to 32 KiB) are then sent to the agent to report on. Since v1.3.2 a `!` command containing `sudo` first opens a password popup when no sudo ticket is valid, verifies the password with `sudo -S -v`, then runs; a wrong password shows `[Denied] password: incorrect password`.

The command policy is a **denylist**, not an allowlist: `denied_command` in `config.json` is the only list, and anything not on it runs subject to sandbox and confirmation. The former `white_list` / `path_white_list` keys are no longer read, and `sensitive_map` was renamed to `sensitive_path` — a config still carrying any of these keys is ignored silently (the startup warning was dropped in v1.1.2).
