# Execution Guards

The write guard, argv-only subprocess schema and timeouts that sit alongside the sandbox.

## Filesystem write guard

`go-pkg/filesystem` write APIs (`WriteFile`, `WriteJSON`, `AppendText`, `CheckDir`) all enforce `IsDenied` internally. Any agenvoy code that bypasses `go-pkg/filesystem` and writes via `os.WriteFile` directly **escapes the policy** — this is forbidden.

The `internal/filesystem` package retains only path computation, runtime limits, and domain wrappers. It does not duplicate read/write logic.

Outside `$HOME`, `run_command` needs the paths declared up front: the call carries `write_paths` (absolute paths only), the user approves them with the system password, and only then are they bound read-write into the sandbox for that session. A write to an unapproved path fails with an explicit message, and a permission error on an unbound path outside `$HOME` gets a hint to re-run with `write_paths` rather than a bare error the model would misread as an ownership problem.

## Subprocess argv-only schema

`run_command` accepts only `argv: string[]` (minItems 1) plus optional `write_paths` and, since v1.0.25, `network` (boolean, default off). It does **not** accept a `command: string` with auto-tokenization. This zero-parsing approach removes shell-injection surface in the agent layer.

At the top level, `sudo` is rejected (declare `write_paths` instead), `rm` is routed to the trash, and `cd` switches the work directory after verifying the path.

Shell features (pipes, redirects) require an explicit `["sh", "-c", "cmd | pipe"]` (or `bash -c`). That script is **parsed** with `mvdan.cc/sh` rather than string-matched, and every command node inside it is checked:

| Rule | Effect |
|---|---|
| Bare command names only | `/usr/bin/curl` is rejected — the binary must be written as `curl` |
| No dynamic commands | A command built from a variable or command substitution is rejected outright |
| No `rm` or `sudo` | Both are rejected inside `sh -c` |
| Denied binaries | Anything on `denied_command` is rejected, inside `sh -c` as well |
| Shell builtins | A fixed set (`cd`, `echo`, `test`, `export`, ...) passes without an allowlist entry |
| Nested `sh -c` | Recursively validated with the same rules; a nested script that is not a static string is rejected |

## Timeouts

Every tool carries its own timeout, defaulting to one minute and overridden per tool at registration (`open_file` 10 s, `html_template` 30 s, `fetch_page` and `search_web` 90 s, `generate_audio` 5 min, `download_file` 10 min, `generate_image` 15 min, `run_command` 60 min; `ask_user` has none). Subagent invocations, including slot-wait time, are capped by `MaxSubagentTimeoutMin` (30 min).

These are package-level values — the environment-variable overrides that used to control them were removed.
