From 3e354b61dbe7ae0870efcf0158bb0bb3c9538eeb Mon Sep 17 00:00:00 2001 From: konsisumer Date: Thu, 4 Jun 2026 17:20:58 +0200 Subject: [PATCH] fix(agent): preserve copilot routed headers --- agent/agent_init.py | 4 +++ run_agent.py | 3 +++ .../test_create_openai_client_proxy_env.py | 21 ++++++++++++++++ .../test_provider_attribution_headers.py | 25 +++++++++++++++++++ 4 files changed, 53 insertions(+) diff --git a/agent/agent_init.py b/agent/agent_init.py index c0bc3c441c4..6f0edf4fb4c 100644 --- a/agent/agent_init.py +++ b/agent/agent_init.py @@ -808,6 +808,8 @@ def init_agent( # _custom_headers; older/mocked clients may expose # _default_headers instead. _routed_headers = getattr(_routed_client, "_custom_headers", None) + if not _routed_headers: + _routed_headers = getattr(_routed_client, "default_headers", None) if not _routed_headers: _routed_headers = getattr(_routed_client, "_default_headers", None) if _routed_headers: @@ -861,6 +863,8 @@ def init_agent( if _provider_timeout is not None: client_kwargs["timeout"] = _provider_timeout _fb_headers = getattr(_fb_client, "_custom_headers", None) + if not _fb_headers: + _fb_headers = getattr(_fb_client, "default_headers", None) if not _fb_headers: _fb_headers = getattr(_fb_client, "_default_headers", None) if _fb_headers: diff --git a/run_agent.py b/run_agent.py index 6f0d9cb1d56..b086400b6c4 100644 --- a/run_agent.py +++ b/run_agent.py @@ -3557,6 +3557,9 @@ class AIAgent: import httpx as _httpx import socket as _socket + if "api.githubcopilot.com" in str(base_url or "").lower(): + return _httpx.Client() + _sock_opts = [(_socket.SOL_SOCKET, _socket.SO_KEEPALIVE, 1)] if hasattr(_socket, "TCP_KEEPIDLE"): _sock_opts.append((_socket.IPPROTO_TCP, _socket.TCP_KEEPIDLE, 30)) diff --git a/tests/run_agent/test_create_openai_client_proxy_env.py b/tests/run_agent/test_create_openai_client_proxy_env.py index 9bd4ab92912..494a4919e88 100644 --- a/tests/run_agent/test_create_openai_client_proxy_env.py +++ b/tests/run_agent/test_create_openai_client_proxy_env.py @@ -145,6 +145,27 @@ def test_create_openai_client_no_proxy_when_env_unset(mock_openai, monkeypatch): http_client.close() +@patch("run_agent.OpenAI") +def test_create_openai_client_uses_plain_httpx_client_for_copilot(mock_openai, monkeypatch): + """Copilot Claude chat-completions rejects the custom socket-options transport.""" + for key in ("HTTPS_PROXY", "HTTP_PROXY", "ALL_PROXY", + "https_proxy", "http_proxy", "all_proxy"): + monkeypatch.delenv(key, raising=False) + + agent = _make_agent() + kwargs = { + "api_key": "test-key", + "base_url": "https://api.githubcopilot.com", + } + agent._create_openai_client(kwargs, reason="test", shared=False) + + forwarded = mock_openai.call_args.kwargs + http_client = _extract_http_client(forwarded) + assert isinstance(http_client, httpx.Client) + assert getattr(http_client._transport._pool, "_socket_options", None) is None + http_client.close() + + def test_get_proxy_for_base_url_returns_none_when_host_bypassed(monkeypatch): """NO_PROXY must suppress the proxy for matching base_urls. diff --git a/tests/run_agent/test_provider_attribution_headers.py b/tests/run_agent/test_provider_attribution_headers.py index 2784ba178d2..dab69d57b3d 100644 --- a/tests/run_agent/test_provider_attribution_headers.py +++ b/tests/run_agent/test_provider_attribution_headers.py @@ -109,6 +109,31 @@ def test_routed_client_preserves_openai_sdk_custom_headers(mock_openai): assert headers["X-BILLING-INVOKE-ORIGIN"] == "HermesAgent" +@patch("run_agent.OpenAI") +def test_routed_client_preserves_openai_sdk_default_headers(mock_openai): + mock_openai.return_value = MagicMock() + routed_client = SimpleNamespace( + api_key="test-key", + base_url="https://api.githubcopilot.com", + default_headers={"copilot-integration-id": "vscode-chat"}, + ) + + with patch("agent.auxiliary_client.resolve_provider_client", return_value=( + routed_client, + "claude-opus-4.7", + )): + agent = AIAgent( + provider="copilot", + model="claude-opus-4.7", + quiet_mode=True, + skip_context_files=True, + skip_memory=True, + ) + + headers = agent._client_kwargs["default_headers"] + assert headers["copilot-integration-id"] == "vscode-chat" + + @patch("run_agent.OpenAI") def test_gmi_base_url_picks_up_profile_user_agent(mock_openai): """GMI declares User-Agent on its ProviderProfile.default_headers.