From 492ddcd4e43ce90b192a39bc71f547e03f770f6e Mon Sep 17 00:00:00 2001 From: anish Date: Sat, 13 Jun 2026 09:51:31 +0000 Subject: [PATCH] fix(client): infer global model from agent definitions when no model is set When agents are defined with explicit model specifications but no global model is configured, the CLI would fall back to its default model, potentially ignoring the user's intent. This fix adds logic in both `ClaudeSDKClient.call_agent` and `InternalClient.call_agent_async` to infer a global model from agent definitions. If all agents use the same model, that model becomes the global default. If agents use different models, the first non-inherit model is selected to ensure at least one user-specified model is respected globally. This prevents the CLI from overriding user-specified agent models with its own defaults. Signed-off-by: anish --- src/claude_agent_sdk/_internal/client.py | 20 +++++++++++++++++++ src/claude_agent_sdk/client.py | 25 +++++++++++++++++++++--- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/src/claude_agent_sdk/_internal/client.py b/src/claude_agent_sdk/_internal/client.py index 010025b94..78d161ca9 100644 --- a/src/claude_agent_sdk/_internal/client.py +++ b/src/claude_agent_sdk/_internal/client.py @@ -97,6 +97,26 @@ async def _process_query_inner( ) -> AsyncGenerator[Message, None]: # Validate and configure permission settings (matching TypeScript SDK logic) configured_options = options + + # When agents are defined with model specifications but no global model is set, + # infer a global model to ensure the CLI respects user-specified models rather + # than falling back to its default (which may differ from agent specifications). + if options.agents and options.model is None: + agent_models = { + agent_def.model + for agent_def in options.agents.values() + if agent_def.model is not None and agent_def.model != "inherit" + } + if agent_models: + # If all agents use the same model, use that as the global default + if len(agent_models) == 1: + inferred_model = agent_models.pop() + else: + # Multiple models specified: use the first non-inherit model as fallback + # This ensures at least one user-specified model is used globally + inferred_model = next(iter(agent_models)) + configured_options = replace(options, model=inferred_model) + if options.can_use_tool: # canUseTool callback requires streaming mode (AsyncIterable prompt) if isinstance(prompt, str): diff --git a/src/claude_agent_sdk/client.py b/src/claude_agent_sdk/client.py index 3ddf4c9f9..aae12218f 100644 --- a/src/claude_agent_sdk/client.py +++ b/src/claude_agent_sdk/client.py @@ -157,6 +157,27 @@ async def _connect_inner( from ._internal.transport.subprocess_cli import SubprocessCLITransport # Validate and configure permission settings (matching TypeScript SDK logic) + options = self.options + + # When agents are defined with model specifications but no global model is set, + # infer a global model to ensure the CLI respects user-specified models rather + # than falling back to its default (which may differ from agent specifications). + if options.agents and options.model is None: + agent_models = { + agent_def.model + for agent_def in options.agents.values() + if agent_def.model is not None and agent_def.model != "inherit" + } + if agent_models: + # If all agents use the same model, use that as the global default + if len(agent_models) == 1: + inferred_model = agent_models.pop() + else: + # Multiple models specified: use the first non-inherit model as fallback + # This ensures at least one user-specified model is used globally + inferred_model = next(iter(agent_models)) + options = replace(options, model=inferred_model) + if self.options.can_use_tool: # canUseTool callback requires streaming mode (AsyncIterable prompt) if isinstance(prompt, str): @@ -173,9 +194,7 @@ async def _connect_inner( ) # Automatically set permission_prompt_tool_name to "stdio" for control protocol - options = replace(self.options, permission_prompt_tool_name="stdio") - else: - options = self.options + options = replace(options, permission_prompt_tool_name="stdio") if self._materialized is not None: options = apply_materialized_options(options, self._materialized)