Skip to content

[FEATURE]: Lazy-load agent/skill lists instead of inlining into tool descriptions #13188

Closed as not planned

Description

@Tefx

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 and skill tool descriptions are statically inlined with the full list of all available agents/skills on every API call. This means every turn pays a fixed token cost proportional to the number of configured agents and skills, regardless of whether the user needs them.

How it works today

task.ts — on every init(), fetches all non-primary agents and concatenates their names + descriptions into the tool's description field:

const agents = await Agent.list().then(x => x.filter(a => a.mode !== "primary"))
const description = DESCRIPTION.replace("{agents}",
  accessibleAgents.map(a => `- ${a.name}: ${a.description}`).join("\n")
)

skill.ts — similarly inlines all skills as XML blocks into the tool description:

...accessibleSkills.flatMap((skill) => [
  `  <skill>`,
  `    <name>${skill.name}</name>`,
  `    <description>${skill.description}</description>`,
  `    <location>${pathToFileURL(skill.location).href}</location>`,
  `  </skill>`,
])

Token cost

With 21 custom agents and 3 skills, this adds ~2,000 tokens per turn just in tool descriptions. For power users with 27+ agents and 7+ skills (common in multi-project setups), this can reach ~3,500 tokens per turn — entirely wasted when the user doesn't need to launch a subagent or load a skill in that particular turn.

Combined with MCP tools (see #7399) and other built-in tool descriptions (see #9858), the fixed overhead can consume 15-20% of the context window before any conversation even starts.

Proposed Solution

Make agent/skill lists dynamically loaded on demand instead of statically inlined.

Option A: Two-step tool call (recommended)

The task tool description becomes a fixed ~200 tokens:

Launch a new agent to handle complex, multistep tasks autonomously.
Call with action="list" to see available agent types, or provide a
subagent_type to launch an agent directly.
[usage notes...]

When the LLM needs to pick an agent, it calls task(action="list") → returns the full agent catalog as a tool result. This result lives in conversation history and is naturally available for subsequent turns without re-sending.

Same pattern for skill:

Load a specialized skill. Call with action="list" to see available
skills, or provide a skill name to load it.

Option B: Summary in description, detail on demand

Keep a one-line-per-agent summary in the description (just names, no descriptions), and add a task(action="describe", agent="foo") to get the full description when needed.

Benefits

  • Saves ~2-3.5k tokens per turn for users with many agents/skills
  • Scales gracefully — 100 agents costs the same as 3 until you actually need the list
  • Consistent with how MCP tools themselves work (tools are registered, not described inline)
  • The agent/skill list is typically only needed once per conversation (first time a subagent is spawned)

Related issues

Activity

  1. github-actions commented on Feb 11, 2026

    @github-actions
    Contributor

    Thanks for the detailed feature request! This complements #8277 (lazy loading for MCP tools) nicely — while #8277 tackles context bloat from external MCP servers, this issue addresses the same problem for internal agents and skills.

    Both follow similar patterns and could potentially share implementation approaches. Worth keeping an eye on the discussion in #8277 for any relevant insights.

  2. eugenio commented on Apr 2, 2026

    @eugenio

    Additional evidence: 1,500+ skills exhaust the entire context window

    Adding data from my environment that shows this issue at scale.

    Environment

    • OpenCode with opencode-skills-antigravity plugin (~1,330 skills) + LabClaw (~240 nested skills)
    • NadirClaw proxy routing to AWS Bedrock (eu-west-2)
    • Models: Kimi K2.5, Qwen3 480B, Qwen3 30B (all 262K token context)
    • LiteLLM 1.83.0

    Measurements from NadirClaw raw request logs

    Metric Value
    system_prompt_length 595,269 chars (595KB)
    Skill <skill> XML blocks 1,539
    Skill descriptions payload ~580KB
    OpenCode own instructions ~9KB
    User CLAUDE.md ~5KB
    Approximate tokens ~148,817
    % of 262K context window 56%

    Impact

    • Every Bedrock model returns BadRequestError — even simple prompts like "hi" fail on auto mode
    • All models in the fallback chain exhausted on multi-turn conversations with tool results
    • Users see: "All configured models are currently unavailable"
    • The 56% context consumption leaves insufficient room for conversation + tool results, so even models that accept the initial request fail on turn 2-3

    Workaround

    Moved all skills from ~/.config/opencode/skills/ to a separate directory and use @zenobius/opencode-skillful plugin for lazy discovery. System prompt dropped from ~595KB to ~9KB.

    This confirms the same root cause identified in this issue — skill descriptions should not be statically inlined into the system prompt.

    ref: #20647

  3. malhashemi commented on Apr 14, 2026

    @malhashemi
    Contributor

    This was the way OpenCode worked for both skills and subagents some time ago, see #7042 and #7165. If it is no longer working as intended this seems like a regression. Not sure if it was intentional decision or an introduced bug.

  4. HeyChriss commented on Apr 22, 2026

    @HeyChriss

    I had the same problem installing the opencode-skills-antigravity plugin (~1,330 skills). The alert shows up, and the workaround of moving the skills to ~/.config/opencode/skills/ works; however, it is not ideal.

    Image
  5. assigned and unassigned on Apr 26, 2026
  6. iatxako commented on May 8, 2026

    @iatxako

    Real-world impact: 32K tokens just to start a session

    Running OpenCode with:

    • Model: qwen3.6-plus (via OpenCode Zen)
    • Skills: ~60 skills installed across ~/.agents/skills/ and ~/.claude/skills/
    • Session start: 32,691 tokens consumed before any user message
    • Context usage: 12% used at session start

    Breakdown

    Source Approx tokens
    Model system prompt ~15-20K
    Tool descriptions (60+ tools) ~8-10K
    Skill descriptions (60 skills) ~4-6K
    AGENTS.md ~1-2K

    The problem

    I want skills available on-demand but not paying for their descriptions every single session. The current design forces a trade-off:

    1. Keep all skills → pay 5-10K tokens per session for descriptions I may never use
    2. Delete unused skills → save tokens but lose access permanently (have to reinstall)

    There is no middle ground: no config to disable skills per-session, no lazy loading of the skill list, no way to have skills installed but turned off.

    Request

    Implement one of:

    • Lazy-load skill descriptions — only inject name+description when the model signals it might need one
    • Config to disable skills without deleting them — e.g., opencode.json allows marking skills as inactive
    • TUI toggle — enable/disable skills from the interface per session

    This matters most for users with many skills who do not use all of them in every session.

  7. github-actions commented on Jul 9, 2026

    @github-actions
    Contributor

    To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions