# NousResearch/hermes-agent · Agent provider headers missing or incorrect intermittently

## Open now

- Nothing open on GitHub.

These issues and fixes relate to HTTP provider headers (such as OpenRouter HTTP-Referer) not being correctly set or reapplied for agent requests, particularly after model switches.

## TL;DR

- 3 member(s): 0 open issue(s), 1 closed issue(s), 0 open PR(s), 1 merged PR(s), 1 PR(s) closed without merging.
- A fix was merged to main in PR #61732. Verify it addresses your case.
- **Recommended:** read PR #61732 (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: `resolved` · deciding PR(s): `pr-61732`. This is distinct from the fix status above. Weigh it yourself.

## How this cluster was grouped

- Link evidence (`STRUCTURAL_SPANNING`): every member is connected by structural evidence: shared changed files, or a closing/superseded reference.
- Pairwise link density `0.67`: 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

- #61099: \[Bug\]: OpenRouter logs show 'Unknown' App for Hermes Agent requests intermittently

## Prior & competing PRs

- **#61732: fix(agent): reapply provider headers after model switch (#61099)**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`
  - files: `agent/agent_runtime_helpers.py`, `scripts/release.py`, `tests/run_agent/test_switch_model_reapplies_headers.py`
  - fix hunks:
  - `agent/agent_runtime_helpers.py`

    ```diff
    @@ -1953,6 +1953,11 @@ def switch_model(agent, new_model, new_provider, api_key='', base_url='', api_mo
                 _sm_timeout = get_provider_request_timeout(agent.provider, agent.model)
                 if _sm_timeout is not None:
                     agent._client_kwargs["timeout"] = _sm_timeout
    +            # Reapply provider-specific headers (e.g. OpenRouter HTTP-Referer,
    +            # X-Title) that were lost when _client_kwargs was rebuilt from
    +            # scratch.  Without this, model switches clear attribution headers
    +            # and OpenRouter logs show "Unknown" for subsequent requests.
    +            agent._apply_client_headers_for_base_url(effective_base)
                 agent.client = agent._create_openai_client(
                     dict(agent._client_kwargs),
                     reason="switch_model",
    ```
  - `scripts/release.py`

    ```diff
    @@ -55,6 +55,7 @@
         "[redacted:email]": "isheng-eqi",  # PR #59428 salvage (cron: reject past one-shot timestamps in update_job fallback + resume_job; #59395). Also PR #59446 salvage (cron: advance one-shot next_run_at before dispatch so concurrent gateway+desktop schedulers can't double-execute; #59229).
         "[redacted:email]": "derek2000139",  # PR #57838 salvage (desktop/windows: pre-write update marker before quit dwell so the renderer's waitForUpdateToFinish gate parks instead of respawning a backend that re-locks venv .pyd files mid-update)
         "AndreasHiltner@users.noreply.github.com": "AndreasHiltner",  # PR #56854 salvage (gateway: route multiplex profile responses through the profile's own adapter — 53-site _adapter_for_source sweep)
    +    "AlexFucuson9@users.noreply.github.com": "AlexFucuson9",  # PR #61347 salvage (agent: reapply provider headers after model switch; #61099)
         "allenliang2022@users.noreply.github.com": "allenliang2022",  # PR #56932 test coverage folded into #56909 salvage (408 → retryable timeout)
         "[redacted:email]": "ManniBr",  # PR #57417 partial salvage (gateway: fail-closed adapter resolution for unregistered secondary profiles)
         "[redacted:email]": "Umi4Life",  # PR #47377 salvage (agent: emit one-shot fallback switch notice on successful fallback so gateway users see model/provider change; #35419)
    ```
  - `tests/run_agent/test_switch_model_reapplies_headers.py`

    ```diff
    @@ -0,0 +1,100 @@
    +"""Regression tests for #61099: switch_model must reapply provider-specific
    +default headers when it rebuilds _client_kwargs from scratch.
    +
    +Without _apply_client_headers_for_base_url() in the rebuild path, a /model
    +switch drops OpenRouter attribution headers (HTTP-Referer / X-Title → logs
    +show "Unknown") and, worse, functional headers like Kimi's User-Agent
    +sentinel (403 without it).
    +"""
    +
    +from unittest.mock import MagicMock, patch
    +
    +from run_agent import AIAgent
    +from agent.context_compressor import ContextCompressor
    +
    +
    +def _make_agent(provider="copilot", base_url="https://api.githubcopilot.com") -> AIAgent:
    +    """Minimal AIAgent with a context_compressor, skipping __init__."""
    +    agent = AIAgent.__new__(AIAgent)
    +
    +    agent.model = "claude-opus-4.8"
    +    agent.provider = provider
    +    agent.base_url = base_url
    +    agent.api_key = "[redacted:secret-assignment]"
    +    agent.api_mode = "chat_completions"
    +    agent.client = MagicMock()
    +    agent.quiet_mode = True
    +    agent._config_context_length = None
    +    agent._client_kwargs = {"api_key": "[redacted:secret-assignment]", "base_url": base_url}
    +
    +    compressor = ContextCompressor(
    +        model=agent.model,
    +        threshold_percent=0.50,
    +        base_url=base_url,
    +        api_key="[redacted:secret-assignment]",
    +        provider=provider,
    +        quiet_mode=True,
    +        config_context_length=None,
    +    )
    +    agent.context_compressor = compressor
    +    agent._primary_runtime = {}
    +
    +    return agent
    +
    +
    +@patch("agent.model_metadata.get_model_context_length", return_value=131_072)
    +def test_switch_to_openrouter_reapplies_attribution_headers(mock_ctx_len):
    +    """Switching to an openrouter.ai base_url must attach the OpenRouter
    +    attribution headers (HTTP-Referer / X-Title) to the rebuilt client
    +    kwargs — not ship a bare api_key+base_url client (#61099)."""
    +    agent = _make_agent(provider="copilot", base_url="https://api.githubcopilot.com")
    +
    +    agent.switch_model(
    +        "deepseek/deepseek-chat",
    +        "openrouter",
    +        api_key="[redacted:secret-assignment]",
    +        base_url="https://openrouter.ai/api/v1",
    +    )
    +
    +    headers = agent._client_kwargs.get("default_headers") or {}
    +    assert "HTTP-Referer" in headers
    +    assert headers.get("X-Title")
    +
    +
    +@patch("agent.model_metadata.get_model_context_length", return_value=131_072)
    +def test_switch_to_kimi_reapplies_user_agent_sentinel(mock_ctx_len):
    +    """Kimi requires a User-Agent sentinel; a switch to api.kimi.com must
    +    carry it or every request 403s."""
    +    agent = _make_agent(provider="openrouter", base_url="https://openrouter.ai/api/v1")
    +
    +    agent.switch_model(
    +        "kimi-k2",
    +        "kimi",
    +        api_key="[redacted:secret-assignment]",
    +        base_url="https://api.kimi.com/v1",
    +    )
    +
    +    headers = agent._client_kwargs.get("default_headers") or {}
    +    assert headers.get("User-Agent", "").startswith("claude-code/")
    +
    +
    +@patch("agent.model_metadata.get_model_context_length", return_value=131_072)
    +def test_switch_away_from_headered_provider_clears_stale_headers(mock_ctx_len):
    +    """Switching FROM a headered provider TO one with no URL-specific headers
    +    must not carry the old provider's headers along."""
    +    agent = _make_agent(provider="openrouter", base_url="https://openrouter.ai/api/v1")
    +    agent._client_kwargs["default_headers"] = {
    +        "HTTP-Referer": "https://hermes-agent.nousresearch.com",
    +        "X-Title": "Hermes Agent",
    +    }
    +
    +    agent.switch_model(
    +        "MiniMax-M3",
    +        "custom:minimax",
    +        api_key="[redacted:secret-assignment]",
    +        base_url="https://api.minimax.io/v1",
    +    )
    +
    +    headers = agent._client_kwargs.get("default_headers") or {}
    +    assert "HTTP-Referer" not in headers
    +    assert "X-Title" not in headers
    ```
- **#61347: fix(agent): reapply provider headers after model switch (#61099)**. closed without merge
  - maintainer direction: category `merged_via`; implication `superseded`; refs `#61732`

## Fix status

- Merged to main in PR #61732 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-07-10`.
