How to stop an agent doing something, and what each harness gives you to control it. Permission rules decide what the agent may do; a sandbox limits what a command can reach even when it is allowed.
Block a command, for example git push
| Codex CLI | Claude Code | Cursor Agent | OpenCode | pi | |
|---|---|---|---|---|---|
| How | A command rule file | A deny rule in the permission settings | A deny rule in the permission settings | A deny rule in the permission settings | An extension that blocks the tool call Only with an extension |
| File | .codex/rules/deny-push.rules | .claude/settings.json | .cursor/cli.json | opencode.jsonc | .pi/extensions/block-git-push.ts |
| Example | | | | | |
| How it matches | ordered argv-token prefix matching; .rules files are auto-discovered below the rules directory of each configuration layer | shell-operator-aware Bash command matching with wrapper stripping and leading environment-assignment handling | Shell(commandBase) with an optional command:args glob; exact argument-string behavior is not fully documented | shell resources ending in space-star also match the command without arguments | extension-defined string matching over bash tool input |
| What wins | forbidden is stricter than prompt and allow and cannot be approved interactively | deny, then ask, then allow; a matching deny survives every permission mode and cannot be overridden by a hook allow | deny takes precedence over allow and survives --force/--yolo | last configured match wins; configured deny is evaluated before saved approvals and hooks | no native policy precedence; extension ordering and later input mutation matter |
| Watch out for | Command rules belong in .codex/rules/*.rules, not config.toml. --ignore-rules skips user and project rule files; use a managed layer or OS containment when the invoking user must not be able to bypass the rule. Wrappers and nested interpreters require separate coverage unless Codex's nested-command parser recognizes the invocation. | Environment runners and nested interpreters such as sh -c can hide the inner command; deny those runners too or use OS containment for a hard boundary. | Matching details are only partly documented. Shell(git) is the unambiguous declarative form but blocks every git command. For exact git-push matching, use a beforeShellExecution hook that returns permission deny or exits 2; other non-zero hook exits fail open. | Shell directory inference is best effort and shell runs with host authority. A later configured broad allow can override an earlier configured deny; saved approvals cannot. | Stock pi has no declarative deny rule or built-in sandbox. The extension owns shell parsing and must explicitly cover wrappers, interpreters, aliases, and scripts. Project extensions load only after project trust. |
What you can control
| Codex CLI | Claude Code | Cursor Agent | OpenCode | pi | |
|---|---|---|---|---|---|
| Decisions | allow, ask, deny | allow, ask, deny | allow, ask, deny | allow, ask, deny | None built in |
| Permission rules | Built in Cover: sandbox escalation, command rules, MCP elicitation, permission request, skill approval Match by: approval_policy plus command rules Docs | Built in Cover: tool, command, path, network, MCP Match by: tool rules with tool-specific specifiers Conflicts: deny, then ask, then allow; first match within the winning class Docs | Built in Cover: tool, command, path, external directory, skill, subagent, MCP Match by: whole-value wildcards over action and resource Conflicts: last configured match wins; agent rules append after global rules; configured deny survives saved approvals and permission hooks Docs | Not supported Stock pi has no built-in tool permission model; extensions run with the user's authority. Docs | |
| Sandbox | Built in Limits: filesystem, network Modes: read-only, workspace-write, danger-full-access Enforced separately from permission rules. Docs | Built in Limits: filesystem, network Enforced separately from permission rules. 2.1.304 fixes Linux sandbox startup for denyRead globs with literal bracket segments, and extends the deletion safety prompt to rm -r of mounted-drive roots. Publisher notes; no sandbox probe. Docs | Not established | Not established external_directory is permission policy, not proof of OS containment. | Not supported No built-in execution sandbox is claimed. |
| Approval prompts | Built in Modes: untrusted, on-request, never approval_policy and sandbox_mode are separate inputs 0.163.0 release notes: saved-thread resume restores permissions and network approvals while respecting explicit overrides and current managed policy. The proxy checks permission before DNS lookup, rejects duplicate Host headers, and prevents credential aliases from bypassing inspection hooks. Docs | Built in Modes: default, acceptEdits, plan, auto, dontAsk, bypassPermissions Remember an approval: once, session, project-command 2.1.302 published changes: claude -p and Agent SDK sessions with no permission mode start in default mode on third-party providers, with telemetry off or on a fresh install. Explicit --permission-mode auto retains auto. This is a launch default, not a change to configured-rule precedence. Docs | Not established | Built in Remember an approval: once, project Allow always stores durable project-scoped proposed patterns; reject declines all pending requests in the session. 2.1 adds client.permission.saved.update({ id, action?, resource? }) / PATCH /api/permission/saved/:id for changing a saved grant’s tool action or resource pattern. action names the permission subject, not allow/ask/deny; this does not add an editable decision effect. Duplicate project/action/resource combinations return conflict. Docs | Not supported An extension must implement its own approval UI and policy. |
| Skills granting tools | Not supported Codex does not interpret SKILL.md allowed-tools as a skill-scoped permission grant. Docs | Built in Field: allowed-tools turn-scoped pre-approval while the skill is active; not a tool-availability restriction 2.1.304: under a policy allowing hooks from plugins only, a main-conversation skill whose hooks are excluded no longer receives its allowed-tools grant. Docs | Not established | Not supported The skill permission controls loading; skill frontmatter does not grant tool access. Docs | Not supported |
| Hooks that decide | Built in
Can: allow, deny PreToolUse can only deny; PermissionRequest can allow or deny, and any deny wins. Hook-definition trust and tool approval are separate controls. Docs | Built in
Can: allow, ask, deny A hook allow does not override matching ask or deny rules; a blocking hook takes precedence over allow rules. In 2.1.301, prompts for an ask decision from a plugin or skill hook identify its origin. Published telemetry adds classifier_verdict and permission_mode to tool_decision, and classifier_category when OTEL_LOG_TOOL_DETAILS=1. 2.1.302 published changes fix missing classifier_category on most auto-mode classifier blocks and preserve tool_use_id, tool_source and tool_parameters when the OpenTelemetry attribute limit is low; telemetry delivery remains unprobed. 2.1.304 mod tool.check can return decider: "person": only the permission dialog or host callback may approve; when neither can ask, the request is refused with the mod’s reason. The event also reports e.isReadOnly. This is the mod surface, separate from declarative PreToolUse hooks. Docs | Built in
Can: allow, deny, ask Hook permission decisions are a separate surface from CLI permission files. ask is not enforced for preToolUse. Docs | Built in
Can: allow, ask, deny The hook runs after configured-deny evaluation, so it cannot override a configured deny. Since 2.0.30, MCP tool calls include their input in permission-evaluation metadata.input; absent input becomes {}. This does not change configured-deny precedence. Docs | Only with an extension
An extension can implement a permission gate, but it must emit its own evidence. Docs |
| Where rules live |
Priority between these files is not recorded here. |
Listed highest priority first. |
Project settings override global ones. |
| No permission settings |
Sources
Codex CLI
- Codex configuration reference, Documentation, checked 2026-08-27
- Codex: Agent Skills, Documentation, checked 2026-08-27
- Codex hooks, Documentation, checked 2026-10-15
- Codex execpolicy rules, Documentation, checked 2026-08-28
- Codex 0.163.0 published changes, Documentation, checked 2026-10-17
Claude Code
- Claude Code permissions, Documentation, checked 2026-10-15
- Claude Code 2.1.301 published changes, Documentation, checked 2026-10-16
- Claude Code 2.1.302 published changes; no runtime probe, Documentation, checked 2026-10-19
- Claude Code 2.1.304 published changes; no runtime probe, Documentation, checked 2026-10-21
Cursor Agent
- Cursor CLI permissions, Documentation, checked 2026-10-15
- Cursor hooks, Documentation, checked 2026-10-15
OpenCode
- OpenCode 2.0.29 permission evaluation and saved approval boundary, Source code, checked 2026-10-15
- OpenCode 2.0.29 configured denies precede saved grants and permission hooks, Source code, checked 2026-10-15
- Released OpenCode 2.0.29 permission documentation, Documentation, checked 2026-10-15
- OpenCode 2.0.29 skill parser, Source code, checked 2026-10-15
- OpenCode 2.0.30 MCP tool input passed to permission evaluation, Source code, checked 2026-10-16
- OpenCode v2.1.1: packages/protocol/src/groups/permission.ts, Source code, checked 2026-10-21
pi
- pi extensions documentation at 853a80d2, Documentation, checked 2026-08-29