fix(router): bind Claude Code background sessions to their auto-router (#43767)

Claude Code background sessions (claude --bg) stamp x-app: cli-bg on every
request, including main-loop turns. The session router binding only
accepted x-app: cli, so a background session never bound and its
subagents' concrete-model calls bypassed the router.

The binding write already requires the requested model to resolve to a
pre-routing strategy, so background side calls naming plain models still
never bind. Since f6eff1bde0 removed the clear path, the x-app check
guarded nothing else.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
tin-berri 2026-09-29 15:17:14 -07:00 • committed by GitHub
parent 24a7e89738
commit 92c0d6f5c8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 4 additions and 5 deletions

View file

@ -13716,8 +13716,6 @@ class Router:
self._stamp_or_clear_metadata_key(request_kwargs, "model_group", bound_model)
return bound_registered_model
if self._request_header(request_kwargs, "x-app") != "cli":
return registered_model_name
if self._select_pre_routing_strategy(registered_model_name, request_kwargs) is None:
return registered_model_name
await self._claude_code_session_router_cache.async_set_cache(

View file

@ -11515,15 +11515,16 @@ class TestClaudeCodeSubagentSessionRouterBinding:
}
@pytest.mark.asyncio
async def test_subagent_concrete_model_uses_the_main_sessions_router(self):
@pytest.mark.parametrize("app", ["cli", "cli-bg"])
async def test_subagent_concrete_model_uses_the_main_sessions_router(self, app):
router = self._router()
await router.acompletion(
model="smart-router",
messages=[{"role": "user", "content": "main turn"}],
**self._request_kwargs(),
**self._request_kwargs(app=app),
)
subagent_kwargs = self._request_kwargs(agent_id="agent-1234")
subagent_kwargs = self._request_kwargs(app=app, agent_id="agent-1234")
response = await router.acompletion(
model="expensive-model",