diff --git a/src/clean_agent_preview.py b/src/clean_agent_preview.py index e6fc63f6c..fb67f7808 100644 --- a/src/clean_agent_preview.py +++ b/src/clean_agent_preview.py @@ -5120,12 +5120,21 @@ async def stream_preview(*, endpoint_url, model, messages, headers, turn_contrac calls += 1 duplicate_count = successful_duplicate_counts.get(call_signature, 0) + 1 successful_duplicate_counts[call_signature] = duplicate_count - if evidence_tool_keeps_distinct_requests_available(name): + if ( + evidence_tool_keeps_distinct_requests_available(name) + and duplicate_count < 2 + ): suppression = ( 'rejected only for this exact request; the tool remains ' 'available with different arguments' ) elif duplicate_count >= 2: + # A first duplicate leaves evidence tools available so a + # corrected page/range/URL/query can execute immediately. + # Repeating that exact successful call after the reminder + # proves the model is not taking that route; continuing to + # advertise it produces no-op loops and starves completion. + terminal_suppression_violation = True permanently_suppressed_tools.add(canonical(name)) suppression = 'disabled for the rest of this turn' else: diff --git a/tests/test_clean_agent_preview.py b/tests/test_clean_agent_preview.py index 3f0e3d11b..a570f4598 100644 --- a/tests/test_clean_agent_preview.py +++ b/tests/test_clean_agent_preview.py @@ -5251,7 +5251,10 @@ async def test_native_stream_permanently_withholds_tool_when_model_ignores_dupli and "already returned evidence" in event.get("output", "") ] assert len(duplicate_errors) == 2 - assert 'tools' not in requests[2] + # The first exact duplicate leaves the evidence tool available so a + # changed request can run. Ignoring that correction once more suppresses + # the looping tool and forces a completion-only turn. + assert any(item['function']['name'] == 'inspect_media' for item in requests[2]['tools']) assert 'tools' not in requests[3]