diff --git a/agent/process_bootstrap.py b/agent/process_bootstrap.py index 2fbaa1ad33..85dccfc737 100644 --- a/agent/process_bootstrap.py +++ b/agent/process_bootstrap.py @@ -18,6 +18,7 @@ from typing import Any, Optional from hermes_bootstrap import _happy_eyeballs_create_connection from utils import base_url_hostname, normalize_proxy_url from agent.proxy_bypass import first_proxy_env_value, should_bypass_proxy +from agent.thread_scoped_output import _ThreadRoutingStream _OPENAI_CLS_CACHE = None @@ -334,7 +335,8 @@ def _install_safe_stdio() -> None: """Wrap stdout/stderr so best-effort console output cannot crash the agent.""" for stream_name in ("stdout", "stderr"): stream = getattr(sys, stream_name, None) - if stream is not None and not isinstance(stream, _SafeWriter): + # The routing proxy already absorbs these errors; wrapping it stacks a new proxy per build (#139687). + if stream is not None and not isinstance(stream, (_SafeWriter, _ThreadRoutingStream)): setattr(sys, stream_name, _SafeWriter(stream)) diff --git a/tests/agent/test_thread_scoped_output.py b/tests/agent/test_thread_scoped_output.py index dc57064d13..7660f92621 100644 --- a/tests/agent/test_thread_scoped_output.py +++ b/tests/agent/test_thread_scoped_output.py @@ -130,6 +130,26 @@ def test_temporary_global_redirects_do_not_allocate_new_sinks(monkeypatch): sys.stdout, sys.stderr = original_stdout, original_stderr +def test_agent_builds_keep_the_installed_routing_proxy(monkeypatch): + """Agent builds between silences must not stack wrappers on sys.stdout/stderr (#139687).""" + from agent.process_bootstrap import _install_safe_stdio + + monkeypatch.setattr(thread_output, "_installed", {}) + monkeypatch.setattr(sys, "stdout", io.StringIO()) + monkeypatch.setattr(sys, "stderr", io.StringIO()) + _install_safe_stdio() + with thread_scoped_silence(): + pass + routers = (sys.stdout, sys.stderr) + + for _ in range(3): + _install_safe_stdio() + with thread_scoped_silence(): + pass + + assert (sys.stdout, sys.stderr) == routers + + def test_silence_survives_redirect_restoring_an_older_proxy(monkeypatch): """Silencing is stream-wide, even when a redirect swaps proxy generations.""" monkeypatch.setattr(thread_output, "_installed", {})