Feature Request: SubagentStart hook should support updatedInput to enable deterministic model routing

Open 💬 4 comments Opened Jun 19, 2026 by Josef-Le

Problem

There is currently no deterministic way to enforce model routing for subagents spawned via the Agent() tool. The only available mechanisms are advisory (rely on the orchestrating model choosing the right model: param), or blunt (a single CLAUDE_CODE_SUBAGENT_MODEL env var that forces all agents to one model regardless of task).

What was tried and why it doesn't work

I spent significant time empirically testing every available hook and mechanism on CC 2.1.183:

| Mechanism | Fires for Agent? | Can modify model? |
|---|---|---|
| PreToolUse(matcher: "Agent") | ❌ Does not fire | — |
| SubagentStart | ✅ Fires | ❌ updatedInput silently ignored |
| PermissionRequest(matcher: "Agent") | ❌ Does not fire | — |
| permissions.deny | ✅ Fires | ❌ Can block, not reroute |
| CLAUDE_CODE_SUBAGENT_MODEL env var | ✅ (overrides everything) | Blunt — no task awareness |

SubagentStart fires correctly and receives the session_id, transcript_path, cwd, agent_id, and agent_type. However:

  • The hook input does not include the requested model
  • Returning {"hookSpecificOutput": {"updatedInput": {"model": "claude-haiku-4-5-20251001"}}} is silently ignored — subagent runs with whatever model was originally requested
  • Returning {"decision": "block"} or exiting with code 2 is also silently ignored — subagent runs anyway
  • Returning {"hookSpecificOutput": {"additionalContext": "..."}} is not injected into the subagent

CLAUDE_CODE_SUBAGENT_MODEL does work as a full override (tested empirically: setting it in the process environment forces haiku even when model: "sonnet" is explicitly specified in the Agent call). But it's a session-wide blunt instrument — it prevents using Sonnet agents for legitimate parallel judgment tasks.

Requested behavior

Either (or both) of the following would solve the problem:

Option A: SubagentStart hook supports updatedInput.model

When a SubagentStart hook returns updatedInput, CC should apply the modified model field before initializing the subagent context. The hook should also receive the requested model in its input so hooks can make informed decisions:

{
  "session_id": "...",
  "agent_id": "...",
  "agent_type": "general-purpose",
  "model": "claude-sonnet-4-6",          // ← add this
  "task_description": "...",              // ← add this (first user message or task prompt)
  "hook_event_name": "SubagentStart"
}

Return value to override the model:

{
  "hookSpecificOutput": {
    "updatedInput": {
      "model": "claude-haiku-4-5-20251001"
    }
  }
}

Option B: PreToolUse fires for Agent tool calls

Allow PreToolUse hooks with matcher: "Agent" to intercept Agent spawns, with the ability to modify tool_input.model via updatedInput — consistent with how PreToolUse works for all other tools.

Use case

This enables building a deterministic model router: a small classifier (keyword heuristic or local 3B model) that runs in a hook script, classifies the task as mechanical (→ haiku) or judgment-based (→ sonnet), and rewrites the model accordingly. The LLM orchestrator does not need to be involved in the routing decision at all.

Current workaround: all routing is advisory — the mandate is injected via UserPromptSubmit hook but the orchestrating model still decides whether to follow it. This produces inconsistent behavior in practice.

Environment

  • Claude Code: 2.1.183
  • Platform: macOS (arm64)
  • Testing methodology: empirical hook wiring + subagent spawning, not inference from docs

View original on GitHub ↗

This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗