Which coding agent has the best native controls?
Every harness now ships something — approval prompts, an allowlist, a sandbox, hooks. None ships everything, the defaults differ wildly, and the docs rarely say what happens in the cases that matter (approval times out; agent runs headless; someone passes the yolo flag). This page compares the native control surfaces of the major harnesses as of August 2026, judged on seven questions:
- Approvals — is there a native ask, and what are its modes?
- Rules — can you write allow/deny policy, and how expressive is it?
- Sandbox — is there OS-level isolation, and is it on by default?
- Interception — can a third party stand on the call path, and does it see every tool call?
- Audit — what record exists that the agent didn’t author?
- Escape hatch — what single flag disables the controls?
- Empty chair — what happens to an approval when nobody is there to answer it?
This survey is the per-harness data behind our harness control patterns page, which names the eight control mechanisms these columns instantiate. It is also the native-controls companion to our coverage matrix (which documents what our layer intercepts per harness); the same survey for the other side of the wire is the MCP server controls comparison. Deep pages per harness are linked in each row as we publish them. Corrections welcome — every claim links to its source, and this page changes when the harnesses do.
The short version
- Strongest defaults: Muse Code and DeepSeek Harness. Muse Code ships an OS sandbox (Seatbelt/bubblewrap) and an append-only event log, both on by default — with the documented caveat that hooks and MCP servers run outside its sandbox (the deep page has the full model). dsh defaults its native sandbox to read-only, fails unanswerable approvals closed, and keeps an append-only session log with a runtime-enforced invariant: model-visible means logged.
- Most mature rule system: Claude Code. Layered settings with managed (org) scope, pattern rules, hooks on every call, and now a classifier-based auto mode that derives a trust profile from your own usage.
- Cleanest architecture: DeepSeek Harness. One pipeline, typed interception points covering everything including MCP tools and Code Mode sub-calls, the only native ask-denies-itself headless posture, and subagents whose prompts auto-reject by default. Core ships architecture, not rules — the deep page explains the trade.
- Most expressive rules, least available: Gemini CLI. Its TOML policy engine (per-mode rules, five priority tiers, root-owned admin policy files, and
ask_userresolving to deny in non-interactive mode) was the high-water mark for rule design — Google retired it with the tool in June 2026. Its successor Antigravity ships a simpleraction(target)permissions engine, and the only hook vocabulary with a nativeask. - Best approval hardening: OpenClaw — approvals bind exact command/cwd, approved scripts bind to on-disk snapshots, inline-interpreter forms force approval, and prompts detect invisible-Unicode obfuscation. Also the most permissive default posture: exec
security: "full",ask: "off", on an agent built to run unattended. Both facts on the deep page. - The honest zero: pi. Mario Zechner and Armin Ronacher’s ~80k-star minimal harness ships four tools and no permission system at all — deliberately, documented, with containment patterns (micro-VM extension, Docker, OpenShell) offered instead. It’s the cleanest possible statement that in a minimal-core design, control is an extension — which also means the extension seam is where control has to arrive. Its hard fork Prime Agent (Prime Intellect, ~18k stars) carries the same zero into always-on autonomous territory — one ipython kernel, daemon-resident sessions, and an inherited
hasUIsignal that misreports headless runs (deep page). - Most dangerous defaults elsewhere: Amp and Cline. Amp defaults to no approval prompts; Cline’s yolo is a literal approve-everything, and its “safe commands” tier trusts the model’s own
requires_approvalself-classification — the classifier is the thing being governed.
The comparison
| Harness | Approvals | Rules | Sandbox | Interception | Native audit | Escape hatch | Empty chair |
|---|---|---|---|---|---|---|---|
| Claude Code (deep page) | Six modes incl. auto — classifier-screened, default for Pro/Max/Team since 08-14 |
permissions.allow/ask/deny patterns; user + project + local + managed scopes; deny at any scope wins; autoMode.* prose rules |
Available, off by default (Seatbelt / bubblewrap; credential masking) | Hooks on every tool call; deny overrides allow; fail-open on timeout | JSONL transcripts + opt-in OTel tool_decision (decision + source) |
--dangerously-skip-permissions — but deny rules, hooks, and an rm -rf circuit breaker still hold; refuses root |
Defined: dontAsk mode auto-denies; unresolvable headless asks deny and continue |
| Codex CLI (deep page) | untrusted / on-request / never (on-failure removed 0.143) + Guardian reviewer agent (--approve-for-me, fail-closed, 0.147) |
Layered TOML + permission profiles (path→read/write/deny maps, network domains) + enterprise requirements.toml; approved_with_amendment persists approvals as reviewable rules |
Native on all three OSes — Seatbelt / bubblewrap+seccomp / native Windows sandbox; workspace-write networks off by default | Full hooks system, on by default — PreToolUse / PermissionRequest / PostToolUse, Claude Code-compatible schema, /import migrates |
Session rollouts (JSONL + state DB); --ephemeral and codex delete erase |
--yolo (sandbox + approvals both off) |
Defined: exec never prompts — blocked actions fail back to the model |
| Cursor / Cursor CLI (deep page) | Run Modes: Auto-review (allowlist → sandbox → LLM classifier; recommended default), Allowlist, Run Everything | permissions.json takes natural-language allow/block instructions; allowlist officially “best-effort, not a security boundary” (3 bypass CVEs); .cursor/rules are steering, not enforcement |
Seatbelt / Landlock+seccomp (WSL2 on Windows); network blocked by default; sandbox.json |
Rich hook events; ask honored only for shell/MCP events; failClosed defaults to false |
None on any tier — DIY via hooks | Run Everything mode; CLI --force (alias --yolo) |
Cloud agents skip Run Modes entirely — auto-run inside a VM |
| DeepSeek Harness (dsh) (deep page) | Approval service, mounted per composition | None shipped — policy arrives as a plugin | Native, default read-only (ctx.sandbox; also workspace-write, danger-full-access); bwrap-compatible local runner, remote E2B backend exists |
Typed tools/pre-execute / post-execute on one pipeline covering everything, incl. Code Mode sub-calls; post can rewrite output |
Trajectory log | danger-full-access preset (sandbox open, approval never) — named, visible profile choice |
Ask resolves to deny, natively |
| Muse Code (Meta) (deep page · ACP install guide) | On by default + LLM approval judge (--approval-judge off routes all to human); staged review of compound shell commands; workspace trust prompt |
on-request / untrusted / never modes; saved prefix rules per workspace root; deny always beats allow; interpreter prefixes can’t be broad-allowed |
Seatbelt / bubblewrap, on by default; refuses shell if sandbox unverifiable; .git/.muse/.agents read-only even in-workspace; network default proxy-only (per-destination review) |
Hooks on the full lifecycle incl. model calls, permission requests, subagent events; fleet-managed hooks via managed_hooks_path (pre-approved) — caveat: hooks + MCP run outside the sandbox |
Append-only local event log of every model call, tool run, approval, edit | --yolo (approval + sandbox off, trusts the workspace — loads the checkout’s AGENTS.md); --disable-sandbox also unconfines file tools + forces full network egress |
Not documented |
| Grok Build (xAI) (deep page) | default / auto (classifier) / plan / always-approve / dontAsk (headless, fail-closed) |
TOML [permission] rules (action/tool/pattern), deny always wins; explicit allow auto-approves |
5 profiles (off default → strict), Landlock/Seatbelt; child-network block Linux-only |
PreToolUse hooks fire in every mode incl. always-approve; loads hooks from ~/.claude/settings.json but doesn’t parse Claude’s output vocab; updatedInput rewrite; HTTP hooks; fail-open core (5s timeout) |
Session log (~/.grok/sessions, format undocumented) |
--always-approve / --yolo (deny rules + hooks still fire) |
dontAsk: silent deny without an allow rule |
| Antigravity (Google, absorbed Gemini CLI) (deep page) | TUI approval cards; accept-edits / plan modes; headless (-p) soft-denies unobtainable approvals (exit 0 + stderr notice naming the needed rule) |
permissions.allow/ask/deny action(target) rules in settings.json — files, commands (prefix/regex), URLs, browser actuation, mcp(server/tool); deny > ask > allow; workspace files auto-allowed, everything else defaults to ask |
enableTerminalSandbox (off by default; nsjail / sandbox-exec / AppContainer); per-prompt opt-in/out; unsandboxed() rule escape |
hooks.json hooks, 5 events, regex matchers; native ask/force_ask in the hook vocabulary — the only harness with a first-class hook-issued ask; fail-closed on hook failure |
Per-conversation transcript JSONL; enterprise tier: central audit logging, MCP restrictions, spend caps (Gemini Enterprise) | --dangerously-skip-permissions — total, verified: hooks are not invoked at all under the flag (unlike Claude Code / Grok Build, where denies + hooks survive bypass) |
Defined: headless soft-deny, run continues |
| Goose (Block / LF) | auto / smart_approve (LLM judge + read_only_hint) / approve / chat |
Extension allowlist via GOOSE_ALLOWLIST URL; ~35 danger patterns exist but are off by default and prompt-only |
None | None — extensibility is MCP-shaped, no tool-call hook | — | GOOSE_MODE=auto (the default) |
Undefined |
| opencode (deep page) | Native ask via permission config |
allow/ask/deny, last-match-wins, per-tool + bash patterns ("rm *": "deny") |
None | JS/TS plugin hooks incl. the permission system (reliability caveats reported) | Session log | --auto (denies still enforced) |
Undefined |
| Hermes Agent (deep page) | Inline prompt on ~47 dangerous patterns | Published blocklist: 12 hardline patterns that hold in every mode, quoting-hardened (our read) | None | pre_tool_call / post_tool_call on every tool, in-process; hook API is deny-only, post is observational |
Session log | yolo mode (hardline tier survives it) | Prompt with no answerer — halt |
| Amp (Sourcegraph) | Default: no approval prompts | amp.permissions (allow/ask/reject, glob matches, per-thread/subagent context); amp.guardedFiles.allowlist |
None | Plugin API | — | amp.dangerouslyAllowAll |
Undefined |
| Cline (guide) | Per-category auto-approve toggles | Safe-command classification | None | Hooks via stdin (open bug: hooks execute even when permissions disabled) | — | CLI default: auto-approve ON | Auto-approve |
| Crush (Charm) | Permission service on side-effectful calls | permissions.allowed_tools; options.disabled_tools |
None | No hook system | — | --yolo (still hard-blocks ssh/scp/rsync) |
Undefined |
| OpenHands | Confirmation mode default-on; --llm-approve risk analyzer |
ConfirmRisky policy, configurable threshold | Sandboxed runtime | SDK-level pluggable SecurityAnalyzer + ConfirmationPolicy |
— | --always-approve / --yolo |
Undefined |
| pi (earendil-works) (deep page) | None — by design. No permission prompts of any kind | None. “Pi does not include a built-in permission system” — the docs say so plainly and point you at containment | None built in; documented patterns: Gondolin micro-VM extension, Docker, OpenShell | TypeScript extensions can mediate tool execution (Gondolin reroutes the built-in tools; hook API undocumented) | — | The default is full access | N/A — there is nothing to resolve |
| Prime Agent (Prime Intellect) (deep page) | None — by design (pi fork; kept the honest zero) | None. Extension docs list the permission gate as the canonical extension to write | None built in; one ipython kernel runs everything with the launching user’s access |
pi’s typed events kept intact: tool_call (blockable, input-mutable, fails closed on handler error) + tool_result (rewritable); no MCP second path (MCP = Python skills inside ipython) |
Branchable session tree (the run’s own account) | The default is full access; /autonomous limits bound spend, not actions |
ctx.hasUI lies in print mode (reports true headless, 0.8.x; all CLI sessions run in daemon workers) — the no-op UI’s unanswerable confirm is the reliable empty-chair signal |
| fx (Vercel Labs) (deep page) | ask / auto (default) / yolo; auto = rules first, then a hardcoded model review of unresolved sensitive calls (docs say openai/gpt-5.4; launch-day source hardcodes zai/glm-5.2 — not configurable either way), human fallback when interactive |
Wildcard permission JSON, user-global + per-workspace, last match wins; /permissions remember stores exact confirmed rules; repo .fx.json can’t set rules or model |
os mode (macOS only: workspace-scoped writes), default none; widening access is a separate approval |
fx acp — as an Agent Client Protocol server, fx sends every approval-gated call to the client (session/request_permission, full raw arguments) and fails closed; no hooks/plugin surface otherwise |
— | yolo (permission checks + sandbox off; requires yolo_acknowledged; FX_PERMISSION_MODE=yolo env does the same) |
Undefined — the reviewer’s human fallback is documented for interactive mode only |
Sources: each harness’s official docs, linked from its deep page or row; adoption and behavior claims for harnesses we haven’t integrated come from vendor documentation and are marked where uncertain. Kilo (Cline lineage), Qwen Code (Gemini CLI fork), and Mistral Vibe track their parents’ models and are omitted from the table until we’ve verified their divergences; Devin CLI ships JSON hooks in .devin/ and reads .claude/ hook directories. Aider remains git-diff-review by design — with one elegant exception worth stealing: under --yes-always, shell execution is auto-denied, not auto-approved. This table extends our July primary-source read of eleven harnesses’ guardrails, where the escape-hatch column is examined in depth.
What the table actually says
Interception is the dividing line. Read the Interception column and the ecosystem splits in two: harnesses where a third party can stand on the call path and see everything (Claude Code, Codex as of its 2026 hooks system, dsh, Hermes, Muse Code, Grok Build, Cursor), and harnesses where it can’t (Goose and Crush, principally). Every other control can be added from outside if interception is complete; nothing can be added if it isn’t. If you’re choosing a harness with control in mind, this column outweighs the rest of the table.
Convergence on Claude Code’s hook contract. Codex’s new hooks are deliberately Claude Code-compatible and codex /import migrates Claude Code configs wholesale; Cursor and Hermes honor exit-code-2 blocking and the JSON decision shapes; Grok Build loads hooks straight out of .claude/settings.json (but parses only its own top-level decision vocabulary — the trap); Devin CLI auto-discovers .claude/ hooks; dsh ships a hooks-claude-code bridge. The PreToolUse contract is becoming the ecosystem’s POSIX — which means a control layer built against it ports cheaply, and so does a permission-model bug (the same settings-format bypass has been reproduced in two different harnesses).
Classifiers took over the approval loop this season — ledgers didn’t follow. All three majors now put a model where the human prompt was: Claude Code’s auto mode (default-on for paid personal accounts since August 14), Codex’s Guardian reviewer (0.147), Cursor’s Auto-review (3.6). Hermes made LLM smart approvals its default in July; Grok Build, Goose, and OpenHands have their own variants; Cline goes furthest and trusts the governed model’s own risk flag. The fatigue math is real — Anthropic’s published numbers have humans catching 13.6% of dangerous commands at the prompt versus 89% for the classifier. But exactly two harnesses (dsh, Muse Code) added an independent append-only record of what ran, and none added a queryable trail of what their classifier decided or why. The ecosystem is spending fast on making attended sessions quieter and almost nothing on making decisions explainable afterwards.
The empty chair is being answered — by the newest designs. dsh resolves unanswerable asks to deny; Codex exec never prompts and fails blocked actions back to the model; Claude Code ships dontAsk; Gemini CLI’s policy engine resolves ask_user to deny non-interactively; Hermes denies flagged commands in cron and one-shot modes. That’s real convergence on the right answer, and it’s recent. The counterexamples are the ones to check before unattended use: Cline’s yolo, Cursor’s cloud agents (auto-run inside a VM), and OpenClaw’s default posture on an agent designed to run unattended. The reasoning is in the empty-chair test.
Where every native model ends
Grade any row generously and the same four gaps remain, because they’re structural rather than missing features:
- The record is self-authored. Transcripts and trajectories are written by the process they describe, on the machine that ran it. Only Muse Code hardens this locally; nothing ships it off-machine.
- Policy is per-machine. Settings files, profiles, and config live with each install. Two engineers on one codebase run whatever their machines accumulated. Managed settings (Claude Code) and fleet-managed hooks (Muse Code) are the first cracks in this, for those two harnesses, org-wide.
- One flag undoes it. Nearly every row has an escape hatch that disables the controls and the record of the controls being disabled.
- Rules don’t travel. A deny you wrote in
settings.jsondoesn’t exist inconfig.toml,crush.json, orpermission.yaml. Every harness added means re-deriving your policy in a new dialect — or carrying one policy across all of them.
Those four are the job of a layer above the harness. The native controls are genuinely good now, and getting better fast — this page exists because choosing well matters. What none of them try to be is the same policy, enforced identically, with an independent record, on every machine and every harness you run. That’s the layer we build; the per-harness deep pages linked above say precisely where each handoff happens.
Run agents? ACP lets you see, control, and price every tool call they make — allow/ask/deny policy, per-session cost, and a full audit log for Claude Code, Cursor, Codex, and OpenClaw, in one command:
curl -sf https://agenticcontrolplane.com/install.sh | bash
Getting started → · see your first governed call → · free up to 5 agents · prefer fully on-device? add --local