# NousResearch/hermes-agent · Header propagation bugs across routed providers and agents

## Open now

- Nothing open on GitHub.

Multiple bugs and fixes around SDK client headers being read from wrong attributes (_default_headers vs _custom_headers vs public default_headers), causing custom or routed provider headers to be silently dropped before requests. Touches Copilot, Codex, NVIDIA NIM, Kimi, and agent transport layers.

## TL;DR

- 9 member(s): 0 open issue(s), 3 closed issue(s), 0 open PR(s), 1 merged PR(s), 5 PR(s) closed without merging.
- A fix was merged to main in PR #50298. Verify it addresses your case.
- **Recommended:** read PR #50298 (and any competing PRs below) before filing. Confirm the merged change covers your symptom.

## Maintainer resolution verdict

- Internal-tracker verdict over this cluster's issue members: `partially_resolved` · deciding PR(s): `pr-50298`. This is distinct from the fix status above. Weigh it yourself.

## How this cluster was grouped

- Link evidence (`STRUCTURAL_PARTIAL`): some members are connected by structural evidence (shared changed files, or a closing/superseded reference); the rest are joined by an automated relevance judgement over their text.
- Members outside that structural core may belong to a different underlying problem.
- Pairwise link density `0.20`: the share of member pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. `1.00` means every pair is linked.

## Closed / duplicate issues

- #8779: \[Bug\]: run_agent.py uses _default_headers instead of default_headers, dropping routed client headers and breaking Kimi API
- #12066: \[Bug\]: Copilot Claude chat-completions fails in Hermes with model_not_supported when direct requests succeed
- #19981: \[Bug\]: openai-codex headers from PR #12664 are read off wrong SDK attribute (_default_headers vs _custom_headers), silently dropped before request

## Prior & competing PRs

- **#8799: fix(run_agent): use public default_headers attr when preserving routed client headers**. closed without merge · fix tier `suggested`
  - maintainer direction: category `implemented_elsewhere`; implication `superseded`
  - _suggested-tier fix detail withheld; re-request with `?tier=all` to include it._
- **#19985: fix(codex): copy headers from _custom_headers (SDK >= 1.x) not _default_headers**. closed without merge · fix tier `suggested`
  - maintainer direction: category `implemented_elsewhere`; implication `superseded`; refs `27df249564b3ce6fa4d1db883df0329bfda01593`
  - _suggested-tier fix detail withheld; re-request with `?tier=all` to include it._
- **#50298: fix(agent): Copilot Claude chat-completions — plain httpx transport + preserve routed headers (#12066)**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`; implication `superseded`
  - files: `agent/agent_init.py`, `run_agent.py`, `tests/run_agent/test_create_openai_client_proxy_env.py`, `tests/run_agent/test_provider_attribution_headers.py`
  - fix hunks:
  - `agent/agent_init.py`

    ```diff
    @@ -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:
    ```

    ```diff
    @@ -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:
    ```
  - `run_agent.py`

    ```diff
    @@ -3557,6 +3557,9 @@ def _build_keepalive_http_client(base_url: str = "") -> Any:
                 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))
    ```
  - `tests/run_agent/test_create_openai_client_proxy_env.py`

    ```diff
    @@ -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.
     
    ```
  - `tests/run_agent/test_provider_attribution_headers.py`

    ```diff
    @@ -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="[redacted:secret-assignment]",
    +        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.
    ```
- **#8784: Bugfix: preserve routed provider default headers**. closed without merge
  - maintainer direction: category `implemented_elsewhere`; implication `superseded`; refs `3e354b61dbe7ae0870efcf0158bb0bb3c9538eeb`, `#8799`
- **#15185: fix(copilot): honor SDK v2 _custom_headers + skip keepalive transport for Claude (#12066)**. closed without merge
  - maintainer direction: category `merged_via`; implication `superseded`; refs `3e354b61d`, `#50298`, `#39159`
- **#39159: fix(agent): preserve copilot routed headers**. closed without merge
  - maintainer direction: category `merged_via`; implication `superseded`; refs `3e354b61d`, `#50298`, `#10324`

## Fix status

- Merged to main in PR #50298 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-06-21`.
- Subsystem: `agent`
