Skip to content

feat(agents): per-subagent permission sets#187

Merged
emal-avala merged 1 commit intomainfrom
feat/per-agent-permissions
Apr 23, 2026
Merged

feat(agents): per-subagent permission sets#187
emal-avala merged 1 commit intomainfrom
feat/per-agent-permissions

Conversation

@emal-avala
Copy link
Copy Markdown
Member

Summary

Subagents spawned via the coordinator can now declare their own allow / deny / ask permission rules — independent of the parent session's global permissions config. This closes roadmap item 5.5.

Why

The coordinator already supports custom agent types (.agent/agents/<name>.md) and a coarse read_only flag that forwards --permission-mode plan. But there was no way to say "this subagent may run git * but never rm" without either:

  • mutating the user's global config (wrong — pollutes the parent),
  • writing a separate project config (wrong — leaks between unrelated subagent spawns),
  • maintaining a parallel permission check inside the coordinator (wrong — duplicates the existing enforcement path).

Per-agent overlays let the coordinator hand each child its own scoped rule set while reusing the existing PermissionsConfig / PermissionChecker pipeline end-to-end.

How it works

Agent markdown files can now carry permission fields in their YAML frontmatter:

---
name: search-only
description: Read-only searcher
include_tools: [Grep, Glob, FileRead]
permission_mode: deny
allow: ["Grep", "Glob", "FileRead(src/**)"]
deny: ["Bash(rm *)"]
ask: ["Bash(git *)"]
---

System prompt additions...
  • Entries use Tool(pattern) syntax; bare tool names yield (tool, None) pattern
  • Missing permission_mode defaults to ask
  • Files with no permission fields get permissions: None (and inherit the parent's config, as before)

When the coordinator spawns the subagent:

  1. If permissions is Some, serialise it to a temp TOML at $TMPDIR/agent-code-perms-<uuid>.toml
  2. Append --permissions-overlay <path> to the child command
  3. The child reads the file at startup and replaces its effective config.permissions (not merges — semantics match /profile load on config replacement)

The overlay flag is gated by security.disable_bypass_permissions, so a locked-down host can't be loosened by passing a file path.

API surface changes

  • AgentDefinition gains permissions: Option<PermissionsConfig> (#[serde(default)], backwards compatible)
  • New public helper services::coordinator::permissions_to_toml(&PermissionsConfig) -> Result<String, String>
  • New CLI flag --permissions-overlay <path> on the agent binary

Test plan

  • cargo fmt --all — clean
  • cargo clippy --workspace --all-targets -- -D warnings — clean
  • cargo test -p agent-code-lib --lib services::coordinator — 15/15 pass (8 new)
    • parse_permission_entry_bare_tool
    • parse_permission_entry_with_pattern
    • parse_permission_entry_strips_surrounding_whitespace
    • build_permissions_returns_none_when_empty
    • build_permissions_default_mode_falls_back_to_ask
    • build_permissions_orders_allow_deny_ask
    • parse_agent_file_reads_permission_frontmatter
    • parse_agent_file_without_permissions_yields_none
    • permissions_to_toml_round_trip
  • cargo test -p agent-code --test smoke — 4/4 pass (new: permissions_overlay_flag_appears_in_help)
  • Manual: agent --help | grep permissions-overlay — flag documented
  • Manual: cat > /tmp/p.toml <<EOF ... EOF; agent --permissions-overlay /tmp/p.toml --dump-system-prompt — overlay read without panic

Pre-existing sandbox::tests::auto_detect_off_macos_is_noop failure on Linux is unrelated (environment-dependent cfg test that fails on main too).

Subagents can now be launched with their own allow/deny/ask rules,
independent of the parent session's global permissions config.

Agent markdown files (`.agent/agents/<name>.md`) may declare
permission fields in their frontmatter:

    ---
    name: search-only
    description: Read-only searcher
    include_tools: [Grep, Glob, FileRead]
    permission_mode: deny
    allow: ["Grep", "Glob", "FileRead(src/**)"]
    deny: ["Bash(rm *)"]
    ask: ["Bash(git *)"]
    ---

Entries are parsed as `Tool(pattern)` — bare tool names bypass the
pattern. The collected `PermissionsConfig` is serialised to a temp
TOML file and handed to the spawned subagent as
`--permissions-overlay <path>`, which replaces the child's effective
permissions wholesale.

The overlay flag is gated by `security.disable_bypass_permissions`
so a locked-down host cannot be loosened by passing a file path.

Closes ROADMAP 5.5.

Tests: 8 new unit tests in coordinator_tests, 1 smoke test for the
new --permissions-overlay flag appearing in --help.
@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@emal-avala emal-avala merged commit 910f2cb into main Apr 23, 2026
13 of 14 checks passed
@emal-avala emal-avala deleted the feat/per-agent-permissions branch April 23, 2026 02:23
@emal-avala emal-avala mentioned this pull request Apr 23, 2026
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant