Agent tool missing 'model' parameter for team agent model selection

Status Fixed / completed
Reported on v2.1.66
Maintainer reply ✓ Yes — wolffiex
Activity 10 comments · opened Mar 5, 2026 · closed Mar 10, 2026
💡 Likely answer: A maintainer (wolffiex, collaborator) responded on this thread — see the highlighted reply below.

Bug Report: Agent tool schema missing model parameter

Summary

The Agent tool does not expose a model parameter in its JSON schema (additionalProperties: false), making it impossible to specify which Claude model (opus, sonnet, haiku) a spawned team agent should use. This prevents proper model selection for team workflows where different roles need different model tiers.

Expected Behavior

The Agent tool should accept a model parameter (e.g., "opus", "sonnet", "haiku") so that team leads can control which model each spawned teammate uses. For example:

  • Architects and implementers → opus (complex reasoning)
  • Assistants/verifiers → sonnet (verification work)
  • Utility/ephemeral agents → haiku (lightweight tasks)

Current Behavior

  • The Agent tool schema has additionalProperties: false and does not include model in its properties
  • Passing model as a parameter is silently stripped by schema validation
  • PreToolUse hooks that read .tool_input.model always see an empty value
  • All spawned agents inherit the parent's model with no override mechanism

Impact

  • Teams that need mixed-model agents (opus for complex work, sonnet for verification) cannot enforce model selection
  • PreToolUse guard hooks cannot validate model requirements because the parameter never reaches them
  • No alternative mechanism exists to control teammate model selection

Reproduction

  1. Create a PreToolUse hook on Agent that reads .tool_input.model:

``bash
MODEL=$(echo "$INPUT" | jq -r '.tool_input.model // empty')
echo "Model: '$MODEL'" # Always empty
``

  1. Spawn an agent with model parameter:

``
Agent(name="test", subagent_type="general-purpose", model="opus", team_name="my-team", prompt="hello")
``

  1. The hook receives MODEL="" because the schema strips the unknown property

Proposed Fix

Add model to the Agent tool's JSON schema:

{
  "model": {
    "description": "The Claude model to use for this agent (e.g., 'opus', 'sonnet', 'haiku'). If omitted, inherits the parent model.",
    "type": "string"
  }
}

Comparison

For comparison, here's what 2.1.66 returned:

|Parameter|Type|Required|Description|
|:-|:-|:-|:-|
|subagent\_type|string|Yes|The type of specialized agent to use|
|prompt|string|Yes|The task for the agent to perform|
|description|string|Yes|A short (3-5 word) description of the task|
|name|string|No|Name for the spawned agent|
|team\_name|string|No|Team name for spawning; uses current team context if omitted|
|resume|string|No|Agent ID to resume from a previous execution|
|run\_in\_background|boolean|No|Run agent in background; you'll be notified when it completes|
|mode|enum|No|Permission mode: "acceptEdits", "bypassPermissions", "default", "dontAsk", "plan"|
|model|enum|No|Model override: "sonnet", "opus", "haiku"|
|isolation|enum|No|Set to "worktree" to run in an isolated git worktree|
|max\_turns|integer|No|Max agentic turns before stopping (internal use)|

And here's what 2.1.69 returns:

|Parameter|Type|Required|Description|
|:-|:-|:-|:-|
|description|string|Yes|Short (3-5 word) description of the task|
|prompt|string|Yes|The task for the agent to perform|
|subagent\_type|string|Yes|The type of specialized agent to use|
|name|string|No|Name for the spawned agent|
|mode|string|No|Permission mode: acceptEdits, bypassPermissions, default, dontAsk, plan|
|isolation|string|No|Set to "worktree" to run in an isolated git worktree|
|resume|string|No|Agent ID to resume a previous execution|
|run\_in\_background|boolean|No|Run agent in background (returns output file path)|
|team\_name|string|No|Team name for spawning; uses current team context if omitted|

The \model\ parameter is missing from the schema.

Environment

  • Claude Code CLI (agent teams feature enabled)
  • CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1
  • macOS, March 2026

View original on GitHub ↗

10 Comments

emmahyde · 5 months ago

This is true not only in Teammate Mode, but in general, for ALL subagents fired in or out of Teammate Mode (although I do have the beta flag on).

Our organization uses Claude Code agents extensively and routes to different models based on the type of problem being solved, not based on a static agent definition.

Architects and implementers → opus (complex reasoning) Assistants/verifiers → sonnet (verification work) Utility/ephemeral agents → haiku (lightweight tasks)

This is consistent with how we have our Orchestrators work.

This routing decision is made dynamically at invocation time by the parent agent based on the problem context. Without a model parameter on the Agent tool, we cannot do this. The only current workaround is to define a separate agent .md file for every model variant (e.g., researcher-haiku.md, researcher-sonnet.md, researcher-opus.md), which is brittle, duplicative, and defeats the purpose of dynamic routing (not to mention incompatible with 3rd party plugins).

Expected behavior

The Agent tool schema should accept an optional model parameter (e.g., "model": "opus", "model": "haiku", "model": "sonnet") that overrides the default model for that subagent invocation. This would allow the parent agent to make cost/capability-appropriate model selections at runtime.

I support the idea of being ABLE to declare a DEFAULT model for an agent, but that is not mutually exclusive with overriding a model in response to dynamic issues, and this is a big miss for us.

aaronleming · 5 months ago

This behavior of assigning a specific model on teammate spawn was working just fine for me up until this morning.

Now all teammates spawned via Agent Team prompt use same model (originally it was using Opus, now something has changed in the last hour and everything is spawning with Sonnet), no matter what is specified in the team definition.

jamestelfer · 5 months ago

Spawning generic subagents with the a given model has been a really useful way to do adhoc tasks without needing to create agent definitions up-front (outside agent teams).

This is a pretty significant regression, not an enhancement.

FIM43-Redeye · 5 months ago

Took a crack at this with Claude Code and it has a report:

---

Reverse-engineering findings from binary analysis (2.1.68 vs 2.1.70)

I did a comparative binary analysis between 2.1.68 (where model worked) and 2.1.70 (where it doesn't). Here's exactly what changed and where the fix points are.

1. Schema removal

In 2.1.68, the Agent tool's Zod schema included:

subagent_type: N.string().describe("The type of specialized agent to use for this task"),
model: N.enum(["sonnet","opus","haiku"]).optional().describe("Optional model to use for this agent..."),
resume: N.string().optional()...

In 2.1.70, the model field is gone from the schema entirely. additionalProperties: false causes Zod to strip it before the handler ever sees it.

2. Override slot hardcoded to void 0

The model resolution function (minified as Vv$ in 2.1.70, WV$ in 2.1.68) accepts an override parameter in its third argument slot:

// Deobfuscated signature:
function resolveAgentModel(agentDefModel, mainLoopModel, override, permissionMode, agentType) {
  if (process.env.CLAUDE_CODE_SUBAGENT_MODEL) return resolveAlias(process.env.CLAUDE_CODE_SUBAGENT_MODEL);
  // ... bedrock handling ...
  if (override) return resolveAlias(override);  // <-- This is the per-call override path
  let model = agentDefModel ?? "inherit";
  if (model === "inherit") return resolveInherit({permissionMode, mainLoopModel, exceeds200kTokens: false});
  return resolveAlias(model);
}

In 2.1.68, the call site passed the tool call's model parameter as the override:

// 2.1.68 - L is the model from the Agent tool call
let j = WV$(z.model, U.options.mainLoopModel, L, P, z.agentType);

In 2.1.70, that slot is hardcoded to void 0:

// 2.1.70 - override hardcoded to undefined
let C = Vv$(v.model, q.options.mainLoopModel, void 0, E, v.agentType);

The resolver function itself is unchanged and fully functional -- it's just never given the override value anymore.

3. Disabled feature flag for new infrastructure

There's a new getAgentModel function in 2.1.69+ that doesn't exist in 2.1.68. It's gated behind a hardcoded feature flag in the UI renderer:

let A = !1;  // hardcoded false
if (H.model || A) {
  let L = getCurrentModel();
  let D = A ? getAgentModel(void 0, L, H.model, void 0, H.subagent_type) : resolveAlias(H.model);
  if (A || D !== L) /* show model badge */
}

When A is false: no model badge is displayed, getAgentModel is never called. This looks like a staged rollout flag for new model resolution infrastructure that hasn't been enabled yet.

4. Teams path has separate model handling

The Teams spawn path (handleSpawnInProcess) reads model from the agent definition and defaults to NgA() which resolves to a provider-specific map:

{ firstParty: "claude-opus-4-6", bedrock: "us.anthropic.claude-opus-4-6-v1", ... }

So teammates without an explicit model default to Opus, not to the parent model. This is different from the non-team path where general-purpose agents inherit from the parent.

Summary

This appears to be a "remove old, build new, reconnect later" refactor where the reconnection hasn't shipped:

  • The old path (tool call model param -> override slot in resolver) was removed
  • The new path (getAgentModel + feature flag) was added but disabled
  • The resolver function itself is intact and would work immediately if the override slot received a value again
  • The simplest fix is re-adding model to the Zod schema and passing it to the third argument of the resolver instead of void 0

The internal plumbing is all there. This is a one-line schema fix and a one-line call site fix to restore the old behavior while the new infrastructure is completed.

---

I genuinely wish I could finish whatever internal implementation is planned, 'cause it looks good but it's... clunky.

wolffiex collaborator · 5 months ago

Ok thanks for this report. The intention was to use custom subagent definitions that specify the model in the front-matter. I understand this is inconvenient with the general subagent (since you'd need a few different ones) but it didn't seem insurmountable. For those of you relying on this for teammates, can you say how this works?

asn007 · 5 months ago
Ok thanks for this report. The intention was to use custom subagent definitions that specify the model in the front-matter. I understand this is inconvenient with the general subagent (since you'd need a few different ones) but it didn't seem insurmountable. For those of you relying on this for teammates, can you say how this works?

This was actually supported in February and stopped being supported around Feb 26 (when I was setting up my Claude Code environment in January, it explicitly asked me what agent I'd like to use for the model and respected that setting)
Now when I launch subagents from the root session they inherit the default session model by default

The regression was a two-step failure
On the first time that appeared Claude stopped respecting the model field in frontmatter, but I was able to work around it by storing the agent launch configurations in memory
Now when the Agent tool does not have a model field this has become impossible to work around

wolffiex collaborator · 5 months ago

We're going to put back this param, thanks again for the feedback. I was hoping to have this in today's release but it missed the cut. I'll make sure it goes out with 2.1.72

asn007 · 5 months ago

Is there any way for you guys to actually start respecting agent
frontmatter model field as well?
Because right now it's ignored, and I feel it shouldn't be, since Claude
asks what model would I like to use with the agent once it's created

wolffiex collaborator · 5 months ago

yes, the break on model param in agent front matter is a separate issue. sorry about that! this field is back in the SDK as of 2.1.72

let's chase down the issue with agent front matter in #32415

github-actions[bot] · 5 months ago

This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.