diff --git a/agent/chat_completion_helpers.py b/agent/chat_completion_helpers.py index 85fdf40bcb..4f91ec1c36 100644 --- a/agent/chat_completion_helpers.py +++ b/agent/chat_completion_helpers.py @@ -1978,14 +1978,20 @@ def _reasoning_config_for_wire(agent): applies its own default. """ cfg = agent.reasoning_config - if _consume_ephemeral_reasoning_off(agent): + ephemeral_off = _consume_ephemeral_reasoning_off(agent) + if getattr(agent, "_reasoning_disable_rejected", False): + # The route rejects disables. Resend exactly what the session has + # been sending — the user's own config — so the retry lands on the + # same provider cache key as every prior request. Only a config that + # is itself a disable is dropped (omitted → route default), and that + # session has never sent anything else, so nothing warm is lost. + if isinstance(cfg, dict) and ( + cfg.get("enabled") is False or cfg.get("effort") == "none" + ): + return None + return cfg + if ephemeral_off: cfg = {**(cfg or {}), "enabled": False, "effort": "none"} - if ( - getattr(agent, "_reasoning_disable_rejected", False) - and isinstance(cfg, dict) - and (cfg.get("enabled") is False or cfg.get("effort") == "none") - ): - return None return cfg diff --git a/tests/run_agent/test_length_continuation_thinking_exhaustion.py b/tests/run_agent/test_length_continuation_thinking_exhaustion.py index ddf4580a68..983d726e88 100644 --- a/tests/run_agent/test_length_continuation_thinking_exhaustion.py +++ b/tests/run_agent/test_length_continuation_thinking_exhaustion.py @@ -68,21 +68,21 @@ class TestReasoningOffOneShotOverride: cfg = _reasoning_config_for_wire(agent) assert cfg == {"enabled": False, "effort": "none"} - def test_rejected_disable_drops_every_disable_but_keeps_enabled(self): - """After a 'reasoning is mandatory' 400, no disable reaches the wire — - neither the configured one nor the one-shot continuation override — - while an enabled config still passes through.""" + def test_rejected_disable_resends_users_config_verbatim(self): + """After a 'reasoning is mandatory' 400 the retry must land on the + SAME provider cache key as every prior request: the ephemeral + continuation override is discarded and the user's own config goes + out unchanged. A config that is itself a disable is omitted.""" from agent.chat_completion_helpers import _reasoning_config_for_wire - agent = _AgentStandIn({"enabled": False}) + agent = _AgentStandIn({"enabled": True, "effort": "high"}) agent._reasoning_disable_rejected = True - assert _reasoning_config_for_wire(agent) is None agent._ephemeral_reasoning_off = True - assert _reasoning_config_for_wire(agent) is None + assert _reasoning_config_for_wire(agent) == {"enabled": True, "effort": "high"} assert agent._ephemeral_reasoning_off is False - agent.reasoning_config = {"enabled": True, "effort": "high"} - assert _reasoning_config_for_wire(agent) == {"enabled": True, "effort": "high"} + agent.reasoning_config = {"enabled": False} + assert _reasoning_config_for_wire(agent) is None @pytest.fixture()