Reduce token overhead: Task tool injects all subagent descriptions into system prompt #4396

Closed
opened 2026-02-16 17:43:40 -05:00 by yindo · 7 comments
Owner

Originally created by @marcusquinn on GitHub (Jan 7, 2026).

Originally assigned to: @rekram1-node on GitHub.

Feature hasn't been suggested before.

  • I have verified this feature I'm about to request hasn't been suggested before.

Describe the enhancement you want to request

Problem

The task tool definition includes the full list of all configured subagents with their descriptions, injected into every conversation's system prompt. With many subagents configured, this adds significant token overhead before the user even sends their first message.

Example: With 150 subagents configured in ~/.config/opencode/agent/, the task tool description alone adds ~6,000-10,000 tokens to every conversation, regardless of whether the agent will actually use subagents.

Current Behavior

The task tool description contains entries like:

Available agent types and the tools they have access to:
- general: General-purpose agent...
- explore: Fast agent specialized for exploring codebases...
- dev-browser: Read ~/.aidevops/agents/tools/browser/dev-browser.md
- authentication: Read ~/.aidevops/agents/tools/git/authentication.md
... (150+ entries)

This is injected for every agent that has task: true in their tools config.

Impact

  • ~6,000-10,000 tokens added to system prompt per conversation
  • Affects cost (especially with reasoning models - relates to #924, #6728)
  • Reduces effective context window for actual work
  • No way to scope which subagents are available per primary agent
  • Agents that rarely spawn subagents still pay the full token cost

Measured Example

Comparing two agents with identical instructions but different tool access:

Agent Task Tool First Message Tokens
Plan+ Disabled ~X tokens
Build+ Enabled ~X + 11,000 tokens

The ~11k difference is primarily the task tool's subagent list.

Proposed Solutions

Option A: Per-agent subagent filtering (preferred)

Allow primary agents to specify which subagents they can spawn:

{
  "agent": {
    "Build+": {
      "tools": {
        "task": ["explore", "dev-browser", "code-review"]
      }
    }
  }
}

Only listed subagents would appear in that agent's task tool definition. This also improves focus - the LLM only sees relevant options.

Option B: Lazy loading / on-demand lookup

Instead of injecting all descriptions upfront, provide a minimal task tool that can look up subagent details on demand:

task tool: "Launch subagents. Use `task list` to see available agents, or `task run <name> <prompt>` to launch."

The LLM would call task list only when it needs to spawn a subagent, paying the token cost only when needed.

Option C: Compact mode flag

Add a config option to use minimal descriptions:

{
  "task_tool_mode": "compact"
}

Compact mode would list only agent names without descriptions, or use a much shorter format.

Option D: Hierarchical/categorized listing

Group subagents by category with collapsed descriptions:

Available agent categories: browser (5), git (4), code-review (6), seo (7)...
Use task(category="browser") to see browser agents, or task(agent="explore") directly.

Related Issues

  • #924 - Dramatic increase in token consumption
  • #6728 - High Token Consumption Observed with Opus 4.5
  • #6651 - Dynamic model selection for subagents via Task tool (related architecture)
  • #4960 - Add visibility flags for merged config and system prompt
  • #5005 - Unexpected system prompt parts despite disabling default agents

Environment

  • OpenCode version: 1.x
  • Subagents configured: 150+
  • Framework: aidevops (generates many specialized subagents)
Originally created by @marcusquinn on GitHub (Jan 7, 2026). Originally assigned to: @rekram1-node on GitHub. ### Feature hasn't been suggested before. - [x] I have verified this feature I'm about to request hasn't been suggested before. ### Describe the enhancement you want to request ## Problem The `task` tool definition includes the full list of all configured subagents with their descriptions, injected into every conversation's system prompt. With many subagents configured, this adds significant token overhead before the user even sends their first message. **Example:** With 150 subagents configured in `~/.config/opencode/agent/`, the task tool description alone adds **~6,000-10,000 tokens** to every conversation, regardless of whether the agent will actually use subagents. ### Current Behavior The task tool description contains entries like: ``` Available agent types and the tools they have access to: - general: General-purpose agent... - explore: Fast agent specialized for exploring codebases... - dev-browser: Read ~/.aidevops/agents/tools/browser/dev-browser.md - authentication: Read ~/.aidevops/agents/tools/git/authentication.md ... (150+ entries) ``` This is injected for every agent that has `task: true` in their tools config. ### Impact - **~6,000-10,000 tokens** added to system prompt per conversation - Affects cost (especially with reasoning models - relates to #924, #6728) - Reduces effective context window for actual work - No way to scope which subagents are available per primary agent - Agents that rarely spawn subagents still pay the full token cost ### Measured Example Comparing two agents with identical instructions but different tool access: | Agent | Task Tool | First Message Tokens | |-------|-----------|---------------------| | Plan+ | Disabled | ~X tokens | | Build+ | Enabled | ~X + 11,000 tokens | The ~11k difference is primarily the task tool's subagent list. ### Proposed Solutions **Option A: Per-agent subagent filtering (preferred)** Allow primary agents to specify which subagents they can spawn: ```json { "agent": { "Build+": { "tools": { "task": ["explore", "dev-browser", "code-review"] } } } } ``` Only listed subagents would appear in that agent's task tool definition. This also improves focus - the LLM only sees relevant options. **Option B: Lazy loading / on-demand lookup** Instead of injecting all descriptions upfront, provide a minimal task tool that can look up subagent details on demand: ``` task tool: "Launch subagents. Use `task list` to see available agents, or `task run <name> <prompt>` to launch." ``` The LLM would call `task list` only when it needs to spawn a subagent, paying the token cost only when needed. **Option C: Compact mode flag** Add a config option to use minimal descriptions: ```json { "task_tool_mode": "compact" } ``` Compact mode would list only agent names without descriptions, or use a much shorter format. **Option D: Hierarchical/categorized listing** Group subagents by category with collapsed descriptions: ``` Available agent categories: browser (5), git (4), code-review (6), seo (7)... Use task(category="browser") to see browser agents, or task(agent="explore") directly. ``` ### Related Issues - #924 - Dramatic increase in token consumption - #6728 - High Token Consumption Observed with Opus 4.5 - #6651 - Dynamic model selection for subagents via Task tool (related architecture) - #4960 - Add visibility flags for merged config and system prompt - #5005 - Unexpected system prompt parts despite disabling default agents ### Environment - OpenCode version: 1.x - Subagents configured: 150+ - Framework: aidevops (generates many specialized subagents)
yindo added the perf label 2026-02-16 17:43:40 -05:00
yindo closed this issue 2026-02-16 17:43:40 -05:00
Author
Owner

@github-actions[bot] commented on GitHub (Jan 7, 2026):

This issue might be a duplicate of existing issues. Please check:

  • #924: Dramatic increase in token consumption
  • #6728: High Token Consumption Observed with Opus 4.5 in OpenCode
  • #5224: System environment prompt causes cache invalidation
  • #4960: Add visibility flags for merged config and system prompt
  • #5005: Unexpected system prompt parts despite disabling default agents
  • #7101: Allow custom prompts in global, project or custom directories
  • #1894: Add optional system prompt environment injection

These issues relate to token overhead, system prompt optimization, and configuration visibility that may address your concerns about token efficiency.

Feel free to ignore if none of these address your specific case.

@github-actions[bot] commented on GitHub (Jan 7, 2026): This issue might be a duplicate of existing issues. Please check: - #924: Dramatic increase in token consumption - #6728: High Token Consumption Observed with Opus 4.5 in OpenCode - #5224: System environment prompt causes cache invalidation - #4960: Add visibility flags for merged config and system prompt - #5005: Unexpected system prompt parts despite disabling default agents - #7101: Allow custom prompts in global, project or custom directories - #1894: Add optional system prompt environment injection These issues relate to token overhead, system prompt optimization, and configuration visibility that may address your concerns about token efficiency. Feel free to ignore if none of these address your specific case.
Author
Owner

@marcusquinn commented on GitHub (Jan 7, 2026):

Thanks for the duplicate check! This issue is distinct from the flagged ones:

  • #924, #6728: These report symptoms (high token usage). This issue identifies a specific cause - the task tool's subagent list injection.
  • #5224: Addresses file list in environment prompt. This issue addresses the task tool definition containing all subagent descriptions.
  • #4960, #5005, #7101, #1894: These are about system prompt visibility/customization. This issue proposes per-agent subagent filtering to reduce tool definition size.

The key distinction: this isn't about the system prompt content, but about the tool schema/description that gets injected. With 150 subagents, the task tool's description field alone is ~6-10k tokens - and there's currently no way to scope which subagents appear per primary agent.

The proposed solutions (per-agent filtering, lazy loading, compact mode) are specific to the task tool architecture and haven't been addressed in the related issues.

@marcusquinn commented on GitHub (Jan 7, 2026): Thanks for the duplicate check! This issue is distinct from the flagged ones: - **#924, #6728**: These report *symptoms* (high token usage). This issue identifies a *specific cause* - the task tool's subagent list injection. - **#5224**: Addresses file list in environment prompt. This issue addresses the **task tool definition** containing all subagent descriptions. - **#4960, #5005, #7101, #1894**: These are about system prompt visibility/customization. This issue proposes **per-agent subagent filtering** to reduce tool definition size. The key distinction: this isn't about the system prompt content, but about the **tool schema/description** that gets injected. With 150 subagents, the task tool's `description` field alone is ~6-10k tokens - and there's currently no way to scope which subagents appear per primary agent. The proposed solutions (per-agent filtering, lazy loading, compact mode) are specific to the task tool architecture and haven't been addressed in the related issues.
Author
Owner

@lorenzo-colpani commented on GitHub (Jan 8, 2026):

I found a temporary workaround for this!

The issue is that if a subagent isn't explicitly restricted, OpenCode injects its full description into every system prompt. You can 'prune' the system prompt by globally denying all subagents and then locally allowing only the ones specific agents need.

This effectively hides the denied subagents from the system prompt, drastically reducing the token overhead.

Simplified Example
If you have a build-specialist and a project-manager subagent, but don't want them both showing up in every agent:

{
  "$schema": "https://opencode.ai/config.json",
  "permission": {
    "task": {
      "build-specialist": "deny",
      "project-manager": "deny"
    }
  },
  "agent": {
    "builder": {
      "permission": {
        "task": {
          "build-specialist": "allow"
        }
      }
    },
    "planner": {
      "permission": {
        "task": {
          "project-manager": "allow"
        }
      }
    }
  }
}
@lorenzo-colpani commented on GitHub (Jan 8, 2026): I found a temporary workaround for this! The issue is that if a subagent isn't explicitly restricted, OpenCode injects its full description into every system prompt. You can 'prune' the system prompt by globally denying all subagents and then locally allowing only the ones specific agents need. This effectively hides the denied subagents from the system prompt, drastically reducing the token overhead. Simplified Example If you have a build-specialist and a project-manager subagent, but don't want them both showing up in every agent: ```json { "$schema": "https://opencode.ai/config.json", "permission": { "task": { "build-specialist": "deny", "project-manager": "deny" } }, "agent": { "builder": { "permission": { "task": { "build-specialist": "allow" } } }, "planner": { "permission": { "task": { "project-manager": "allow" } } } } } ```
Author
Owner

@rekram1-node commented on GitHub (Jan 8, 2026):

That description is a literal prompt for the agent so it knows when to call the subagents, this is expected and intentional, you can remove certain agents from the task tool by denying them example if I only want my agent to call explore subagent:

{
  "agent": {
     "build": {
        "permission": {
          "task": {"*": "deny", "explore": "allow"} 
        }
     }
  }
}

You can also ofc decrease your agent descriptions too

@rekram1-node commented on GitHub (Jan 8, 2026): That description is a literal prompt for the agent so it knows when to call the subagents, this is expected and intentional, you can remove certain agents from the task tool by denying them example if I only want my agent to call explore subagent: ``` { "agent": { "build": { "permission": { "task": {"*": "deny", "explore": "allow"} } } } } ``` You can also ofc decrease your agent descriptions too
Author
Owner

@marcusquinn commented on GitHub (Jan 8, 2026):

Following up with additional context on why per-agent filtering is preferable to lazy-loading approaches.

I'm aware of the existing options for managing context (like #4119 for subagent context control), but they don't solve this specific problem: main agent awareness of its delegation options.

Amp recently shipped a lazy-load MCP with skills feature where MCP tools only load when a skill is invoked. While this saves tokens, it creates a chicken-and-egg problem for subagents:

Without upfront awareness, the main agent:

  • Reinvents solutions that specialized subagents already handle
  • Misses parallelization opportunities across subagents
  • Can't make informed decisions about delegation

The filtered subagents list in this PR preserves awareness while reducing overhead. The main agent still knows "I have explore, dev-browser, and code-review available" - it just doesn't see 150 irrelevant options.

Lazy-loading makes sense for MCP tools within subagents (the subagent knows what it needs), but subagent awareness should remain upfront in the main agent's context.

Happy to discuss alternative approaches if there are concerns with this implementation.

@marcusquinn commented on GitHub (Jan 8, 2026): Following up with additional context on why per-agent filtering is preferable to lazy-loading approaches. I'm aware of the existing options for managing context (like #4119 for subagent context control), but they don't solve this specific problem: **main agent awareness of its delegation options**. Amp recently shipped a [lazy-load MCP with skills](https://ampcode.com/news/lazy-load-mcp-with-skills) feature where MCP tools only load when a skill is invoked. While this saves tokens, it creates a chicken-and-egg problem for subagents: **Without upfront awareness, the main agent:** - Reinvents solutions that specialized subagents already handle - Misses parallelization opportunities across subagents - Can't make informed decisions about delegation The filtered `subagents` list in this PR preserves awareness while reducing overhead. The main agent still knows "I have `explore`, `dev-browser`, and `code-review` available" - it just doesn't see 150 irrelevant options. Lazy-loading makes sense for **MCP tools within subagents** (the subagent knows what it needs), but subagent awareness should remain upfront in the main agent's context. Happy to discuss alternative approaches if there are concerns with this implementation.
Author
Owner

@lorenzo-colpani commented on GitHub (Jan 9, 2026):

That description is a literal prompt for the agent so it knows when to call the subagents, this is expected and intentional, you can remove certain agents from the task tool by denying them example if I only want my agent to call explore subagent:

{
  "agent": {
     "build": {
        "permission": {
          "task": {"*": "deny", "explore": "allow"} 
        }
     }
  }
}

You can also ofc decrease your agent descriptions too

Just an update: I was facing this in v1.1.6. Even when explicitly denying subagents, the agent could see the prompts, resulting in permission errors upon execution. I had to use a workaround where I denied everyone in the System permissions and then whitelisted them in the Agent permissions which then worked fine.

However, v1.1.8 seems to have fixed this. I can now deny specific subagents normally, and they are properly excluded from the prompt context. Thank you!

@lorenzo-colpani commented on GitHub (Jan 9, 2026): > That description is a literal prompt for the agent so it knows when to call the subagents, this is expected and intentional, you can remove certain agents from the task tool by denying them example if I only want my agent to call explore subagent: > > ``` > { > "agent": { > "build": { > "permission": { > "task": {"*": "deny", "explore": "allow"} > } > } > } > } > ``` > > You can also ofc decrease your agent descriptions too Just an update: I was facing this in v1.1.6. Even when explicitly denying subagents, the agent could see the prompts, resulting in permission errors upon execution. I had to use a workaround where I denied everyone in the System permissions and then whitelisted them in the Agent permissions which then worked fine. However, v1.1.8 seems to have fixed this. I can now deny specific subagents normally, and they are properly excluded from the prompt context. Thank you!
Author
Owner

@marcusquinn commented on GitHub (Jan 9, 2026):

Thanks @lorenzo-colpani for confirming the fix!

I've tested on v1.1.10 and can confirm the permission filtering is working correctly:

Agent Task Permission Visible Subagents
TestFiltered {"*": "deny", "explore": "allow", "general": "allow"} 2
Build+ unrestricted 108

The denied subagents are now properly excluded from the prompt context, not just blocked at execution time.

For anyone with many subagents (I have ~158 from aidevops), this is the pattern to reduce token overhead:

{
  "agent": {
    "SEO": {
      "permission": {
        "task": {
          "*": "deny",
          "explore": "allow",
          "seo": "allow",
          "keyword-research": "allow"
        }
      },
      "tools": {
        "task": true
      }
    }
  }
}

This reduces the task tool description from all subagents to just the allowed ones.

@marcusquinn commented on GitHub (Jan 9, 2026): Thanks @lorenzo-colpani for confirming the fix! I've tested on **v1.1.10** and can confirm the permission filtering is working correctly: | Agent | Task Permission | Visible Subagents | |-------|-----------------|-------------------| | TestFiltered | `{"*": "deny", "explore": "allow", "general": "allow"}` | **2** | | Build+ | unrestricted | **108** | The denied subagents are now properly excluded from the prompt context, not just blocked at execution time. For anyone with many subagents (I have ~158 from [aidevops](https://github.com/marcusquinn/aidevops)), this is the pattern to reduce token overhead: ```json { "agent": { "SEO": { "permission": { "task": { "*": "deny", "explore": "allow", "seo": "allow", "keyword-research": "allow" } }, "tools": { "task": true } } } } ``` This reduces the task tool description from all subagents to just the allowed ones.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: anomalyco/opencode#4396