summonr

Policy

What asks first, what --strict enforces, and when to use a container.

Policy

summonr has no sandbox. Shell commands, file tools, MCP servers, and language servers run as your account, with its files and network. Run summonr in a container when a session should not touch the rest of the machine.

Two protections hold in every session. search never reads a protected file or folder. Secret values never reach the model: before a tool result is shown or sent, summonr replaces with [secret] every value that looks like a secret in the workspace's .env and .env.* files (three folders deep), in .npmrc and .pypirc, in ~/.aws/credentials, ~/.netrc, ~/.npmrc, ~/.pypirc, ~/.git-credentials, ~/.config/gh/hosts.yml, and ~/.docker/config.json, and every PEM private key block. A value looks like a secret when its key names one (KEY, SECRET, TOKEN, PASSWORD, AUTH, and similar words, in KEY=value, key: value, or JSON form), when it is a URL with a password, or when it is a long mix of letters and digits. Commands still read the real files, so a test suite that loads .env runs as before.

Irreversible commands ask before they run in every session (see Confirmation). The prompt shows the whole command, wrapped, and says why it needs approval, as <command> — <reason>.

--strict turns on the command and file policy. read and search reach any path except the protected ones, and the summonr home is protected wherever SUMMONR_HOME puts it. A command that reads a protected path is refused from any workdir or after cd. Reads of .env and .env.*, files named credentials or credentials.*, .netrc, .git-credentials, and .pypirc, and the home folders .ssh, .gnupg, .aws, .summonr, .docker, .kube, .config/gh, and .config/gcloud are blocked, and so is a shell content search such as grep -r or rg, or a copy, over a folder that holds one of those directories. A recursive shell search of a folder that holds a .env still runs, and the secret values in what it prints are replaced.

Under --strict, edit and write stay inside the writable roots: the launch directory, write_roots in ~/.summonr/config.toml, and --add-dir. For work that spans sibling repositories, put the parent on that list, launch summonr in the parent directory, or pass --add-dir. Auto mode asks before it grants a write outside the writable roots, then retries the tool. Without an interactive channel the write stays denied. /permissions write PATH persists to that list without a prompt. /permissions write --session PATH grants for this session only. /permissions write alone lists the extra roots. ~ and $HOME expand before the check. A write to a protected path (.git/hooks, .git/config, .git/HEAD, .git/index, .git/refs, .git/logs, ~/.ssh, ~/.gnupg, ~/.summonr, shell startup files, .env) asks before it is granted, and a protected file inside the workspace stays denied.

Under --strict, irreversible commands never run, and neither do force pushes, sudo, pipes into a shell, history rewrites, or writes to protected paths. Matches on your own [policy] deny list are refused without a prompt. With write globs in policy.toml, shell commands do not run.

write_roots = ["~/.cargo/registry", "~/code"]

A ~/ prefix expands to your home directory. A listed path that does not exist is skipped at startup; it is still a writable root for policy checks.

A provider reply of 429, a 5xx status, or a connection error is retried up to three times with a wait that doubles from 1 s to 8 s. A Retry-After header replaces that wait; one over 60 s stops the retries so the turn can fall back to the next endpoint. A reply that already streamed output is retried once, then the turn falls back. A rate limit, or a connection failure before any output, also gets one more completion after those retries, unless its Retry-After is over 60 s.

A language server gets 30 s to start and 30 s to answer an lsp call. The diagnostics check after an edit waits 5 s and is skipped when the server is slower. A server that misses a deadline keeps running, so a slow first index can finish in the background.

summonr does not steer the model. Tool results reach it unchanged, and it decides when a turn is done; only a token or wall-clock budget that you set ends a turn early. A reply cut off at the output limit gets one note saying so. The compaction summary request sends the session's tools and history unchanged, so it reuses the prompt cache; a failed summary retries after 8 rounds.

User policy is ~/.summonr/policy.toml; repository policy is .summonr/policy.toml. A nonempty command allow list must match on every overlay (intersection). Deny lists accumulate. write globs in policy.toml are a narrowing filter on paths relative to a writable root; extra roots themselves come only from write_roots, --add-dir, and /write. Empty lists add no restriction, except that strict = true with an empty allow list denies all commands. Repository policy can narrow user permissions, never widen them. Children inherit these restrictions.

confirm picks which commands ask first. confirm = "destructive", the default, asks only before irreversible destruction (see Confirmation). confirm = "risky" also asks before sudo, force pushes, pipes into a shell, git commands that discard work or rewrite history, and writes to protected paths. The stricter value wins across overlays. Shift+Tab toggles this session between auto approval and approval for risky commands. Auto approval still asks before destructive commands and configured ask patterns. The toggle cannot lower a configured confirm = "risky" or relax strict mode, command denies, or write restrictions. The composer labels approval separately from auto, work, and goal execution modes. During a turn, the change is queued until the turn ends and the active label stays unchanged.

confirm = "risky"

ask lists commands that always ask first, whatever confirm says. A command matches when it contains a pattern, the same rule as deny. A match shows [ allow ] / [ deny ], and under -p it does not run. User and repository lists add together; a repository can add patterns, never remove them. Strict mode does not read ask.

ask = ["git push", "git tag", "cargo publish", "npm publish", "gh release"]

In a read-only research child, edit, write, and run_command are unavailable. A restricted write list refuses shell commands; file tools still use the list.