[Feature Request] Add per-subagent effort/thinking control to align with Agent SDK

Status Fixed / completed
Reported on v2.1.173
Maintainer reply ✓ Yes — bcherny
Activity 4 comments · opened Jun 11, 2026 · closed Aug 17, 2026
💡 Likely answer: A maintainer (bcherny, collaborator) responded on this thread — see the highlighted reply below.

Bug Description
Feature request: per-subagent effort/thinking control — align Claude Code with the Agent SDK

We build a multi-agent plugin (dev-squad) on the Dynamic Workflow tool. It routes ~16 agent roles
per run to different models (haiku/sonnet/opus/fable via agent({model})), but there is NO way to
control reasoning effort per agent or per stage:

  1. Workflow agent() silently ignores everything except model/schema/phase/label/isolation/agentType.

We probed this empirically: passing effort:'banana' (an invalid enum) completes with no error,
proving effort, thinking, budget_tokens, and output_config are dropped before reaching
the API. Silent acceptance of unknown opts also hides the limitation — an error would be kinder.

  1. Subagent frontmatter only supports model: inherit|sonnet|opus|haiku — no effort field either

(same gap as github.com/anthropics/claude-code/issues/25669).

  1. The session-level /effort setting propagates to all subagents but lands non-uniformly because it

is model-gated (haiku: none; sonnet: xhigh→high silent fallback; opus/fable: full range). So one
session knob cannot express "deep critic, shallow mechanical steps" — the model tier becomes the
only effort lever, which conflates capability, context window, and cost.

The Agent SDK already exposes thinking control when launching agents (options.thinking / adaptive
thinking + output_config.effort on the Messages API). Please align Claude Code's subagent surfaces
with that:

  • accept effort (low/medium/high/xhigh/max) in Workflow agent() opts and in the Agent tool /

subagent frontmatter, with the same documented per-model silent-fallback semantics as /effort;

  • or at minimum, error on unknown agent() opts instead of swallowing them.

Concrete impact: in a single /squad run our fable-tier critic (commit gate) and our haiku-tier
mechanical steps are forced to share the session's effort. Running the session at low to save cost
degrades exactly the high-leverage roles; running at high overpays on roles that don't need it.
Per-agent effort would let orchestrators spend reasoning where the leverage is — the same argument
that justified per-agent model selection, which already exists.

Environment Info

  • Platform: darwin
  • Terminal: vscode
  • Version: 2.1.173
  • Feedback ID: 5021e3a5-c07b-4800-b4bf-ba3eee8f6185

Errors

[]

View original on GitHub ↗

4 Comments

nossonl · 2 months ago

Yes!

Butanium · 2 months ago

Code-level root cause (2.1.187), a regression bisection, and the MAX_THINKING_TOKENS override tested — adding to confirm & sharpen this report.

effort isn't "dropped before the API" — the thinking resolver structurally ignores it

The per-turn resolver that builds the request's thinking field is:

function yil(e){ let t = e.options.thinkingConfig;
  for (const n of e.permissionLayers ?? []) if (n.kind === "max_thinking_tokens") t = Nil(n.maxThinkingTokens);
  return t }                       // Nil(0) -> {type:"disabled"};  Nil(n) -> {type:"enabled", budgetTokens:n}

It reads only options.thinkingConfig and max_thinking_tokens permission layers. It never reads effort. effort is plumbed as a separate top-level request field (default high), and per the binary's embedded API-guide string it's a depth dial that only does anything once thinking is already adaptive/enabled ("adaptive thinking is off by default"). Grepping for any code mapping effort → an enabled thinkingConfig returns nothing — so effort cannot turn thinking on by itself.

The interactive session sets {type:"adaptive"} at startup. The Task/workflow spawn path threads in a config that resolves to disabled/undefined. And subagent frontmatter exposes only effort, never thinking/thinkingConfig — so there is no frontmatter (or agent() opt) that can set the one value yil actually reads. The result: the request carries effort but no enabled thinking, and the model doesn't think.

This is a regression, not a never-implemented feature

Workflow agent() spawns did produce thinking blocks on 2.1.170 (verified in stored transcripts), and produce zero on 2.1.179 and 2.1.187. So it broke somewhere in the 2.1.170 → 2.1.179 window — there's a specific range to bisect.

MAX_THINKING_TOKENS does NOT reach spawns (tested)

yil does honor a max_thinking_tokens layer, which MAX_THINKING_TOKENS (env) feeds. I tested whether that override propagates to a spawned subagent on 2.1.187:

MAX_THINKING_TOKENS=16000 claude -p --model opus  "spawn a Task subagent that reasons …"
  • Main headless agent → thinking blocks present (env var took effect).
  • Spawned subagent in the same run → 0 thinking blocks.

So the only architecturally-supported override enables the main loop but is not applied to spawned agents. There is currently no supported way to get a non-fork subagent to think.

Reproduce in one line

Grep any spawned agent's .jsonl for "type":"thinking": 0 for Task/workflow spawns at every effort (low…xhigh), while forks/teammates inherit the parent's adaptive config and do think. (Detection validated: same grep finds thinking blocks in the main-loop transcript and in fork transcripts on the same build, so 0 isn't a stripping artifact.)

What stays inferred

The exact caller assignment that hands the disabled config to the Task/workflow spawn — traced to the spawn site and matched to the observed 0-thinking, but not isolated to a single line.

Related: #43083 (frontmatter effort no-op on Task spawns), #14321 (enable extended thinking for subagents), #31536 (closed — per-agent effortLevel, closed once it was realized thinking itself isn't enabled for subagents).

Investigated with Claude Code (Opus 4.8) by reading the 2.1.187 bundle + transcript probing. Co-authored-by: Claude <noreply@anthropic.com>

guyversa74 · 1 month ago

Adding a concrete production repro for this gap, since it maps directly to point 2 above (no per-subagent thinking override in .claude/agents/*.md frontmatter).

Setup: a .claude/agents/deep-research.md subagent with frontmatter:

model: opus
effort: xhigh

No thinking field (there is no such frontmatter key to set).

What happened: when the parent interactive session had extended thinking disabled, spawning this subagent failed with:

API Error: 400 output_config.effort 'xhigh' is not supported when thinking is disabled on this model. Use effort 'high' or below, or enable thinking.

Per code.claude.com/docs/en/sub-agents.md (v2.1.198+): subagents inherit the parent session's thinking on/off state and cannot override it. So the subagent's own effort: xhigh combined with an inherited thinking: disabled triggers the model's documented constraint (Opus 5 rejects thinking:disabled at effort: xhigh/max, legal only at high or below) — even though nothing in the subagent's own definition asked for thinking to be off.

This means any agent template pinning effort: xhigh/max on an opus-tier model is a landmine that depends entirely on the parent session's thinking state at spawn time — a piece of state the agent author has no visibility into and the harness gives no way to override per-agent. We worked around it on our side by capping affected agents to effort: high (unconditionally legal regardless of thinking state) and adding a static lint to prevent reintroducing the combination, but that's a capability downgrade, not a fix — the actual fix is what this issue is asking for: a per-subagent way to set/force thinking (or at minimum, a per-agent frontmatter field), so effort and thinking aren't silently coupled to whatever the parent session happens to be in.

bcherny collaborator · 13 days ago

Both surfaces support per-agent effort now:

  • Subagent frontmatter (and --agents JSON) accepts effort: low|medium|high|xhigh|max, overriding the session level while that subagent runs. Docs: https://code.claude.com/docs/en/sub-agents
  • Workflow scripts accept agent(prompt, { effort: 'low' | 'medium' | 'high' | 'xhigh' | 'max' }) per call, with the same per-model fallback semantics as /effort.

This is in a recent release: https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md. Closing; please reopen if it's not taking effect on your build.

🤖 Generated with Claude Code