From 3cfad382a2e6961ae41669e0546bdf39cc631e4d Mon Sep 17 00:00:00 2001 From: Coy Geek <65363919+coygeek@users.noreply.github.com> Date: Sun, 15 Mar 2026 22:49:39 -0700 Subject: [PATCH] docs(agents,permissions,config): warn about infinite subagent recursion with global task permissions Fixes #17722 --- packages/web/src/content/docs/agents.mdx | 39 +++++++++++++++++++ packages/web/src/content/docs/config.mdx | 4 ++ packages/web/src/content/docs/permissions.mdx | 2 +- 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/packages/web/src/content/docs/agents.mdx b/packages/web/src/content/docs/agents.mdx index 876464212487..33edad5c36b1 100644 --- a/packages/web/src/content/docs/agents.mdx +++ b/packages/web/src/content/docs/agents.mdx @@ -597,6 +597,45 @@ Rules are evaluated in order, and the **last matching rule wins**. In the exampl Users can always invoke any subagent directly via the `@` autocomplete menu, even if the agent's task permissions would deny it. ::: +:::caution[Recursive subagent nesting] +Setting `permission.task` rules **globally** (in the top-level `permission` block) or directly on a subagent definition enables that subagent to spawn further subagents. OpenCode has **no depth limit** on this recursion. + +Each child session runs independently with its own `steps` counter and context window. If subagents recursively spawn more subagents, token usage grows exponentially — 10 levels of binary branching creates over 2,000 independent sessions, each consuming API tokens. + +Neither `steps` nor `doom_loop` protect against this. `steps` limits iterations per session (each child gets its own counter), and `doom_loop` detects repeated calls within a single session, not across parent-child sessions. + +**Safe pattern** — set `permission.task` only on primary agents, targeting specific subagents: + +```json title="opencode.json" +{ + "agent": { + "orchestrator": { + "mode": "primary", + "permission": { + "task": { + "*": "deny", + "explore": "allow", + "general": "allow" + } + } + } + } +} +``` + +**Unsafe pattern** — global `task` permission enables all agents (including subagents) to spawn recursively: + +```json title="opencode.json" +{ + "permission": { + "task": "allow" + } +} +``` + +You do **not** need an explicit `"task"` permission for primary agents to spawn subagents — the default `"*": "allow"` already covers this. Adding an explicit `"task": "allow"` globally is what overrides the built-in nesting guard. +::: + --- ### Color diff --git a/packages/web/src/content/docs/config.mdx b/packages/web/src/content/docs/config.mdx index d2770ee20948..3af1aa330a54 100644 --- a/packages/web/src/content/docs/config.mdx +++ b/packages/web/src/content/docs/config.mdx @@ -482,6 +482,10 @@ For example, to ensure that the `edit` and `bash` tools require user approval: } ``` +:::caution +Be cautious with `"task": "allow"` in the global permission block. Unlike other permissions, this enables subagents to spawn further subagents recursively with no depth limit. See [Agents — Recursive subagent nesting](/docs/agents#recursive-subagent-nesting). +::: + [Learn more about permissions here](/docs/permissions). --- diff --git a/packages/web/src/content/docs/permissions.mdx b/packages/web/src/content/docs/permissions.mdx index d48c2a084b44..db1c48b189e2 100644 --- a/packages/web/src/content/docs/permissions.mdx +++ b/packages/web/src/content/docs/permissions.mdx @@ -135,7 +135,7 @@ OpenCode permissions are keyed by tool name, plus a couple of safety guards: - `grep` — content search (matches the regex pattern) - `list` — listing files in a directory (matches the directory path) - `bash` — running shell commands (matches parsed commands like `git status --porcelain`) -- `task` — launching subagents (matches the subagent type) +- `task` — launching subagents (matches the subagent type). See [Agents — Recursive subagent nesting](/docs/agents#recursive-subagent-nesting) for important caveats when setting this globally. - `skill` — loading a skill (matches the skill name) - `lsp` — running LSP queries (currently non-granular) - `todoread`, `todowrite` — reading/updating the todo list