Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions agent/auxiliary_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -6650,9 +6650,20 @@ def _wrap_if_needed(client_obj, final_model_str: str, base_url_str: str = "",
raw_base_for_wrap = custom_base
_clean_base2, _dq2 = _extract_url_query_params(openai_base)
_extra2 = {"default_query": _dq2} if _dq2 else {}
if base_url_host_matches(openai_base, "chatgpt.com"):
_extra2["default_headers"] = _codex_cloudflare_headers(custom_key)
_headers2 = _apply_user_default_headers(_extra2.get("default_headers"))
if _headers2:
_extra2["default_headers"] = _headers2
# Match the main-agent construction path: named providers may
# require endpoint-specific auth or routing headers. Apply them
# last so the most specific config wins over host and global
# defaults. Values may be credentials, so never log them.
from hermes_cli.config import apply_custom_provider_extra_headers_to_client_kwargs
apply_custom_provider_extra_headers_to_client_kwargs(
_extra2,
_clean_base2,
)
logger.debug(
"resolve_provider_client: named custom provider %r (%s, api_mode=%s)",
provider, final_model, entry_api_mode or "chat_completions")
Expand Down
35 changes: 35 additions & 0 deletions tests/agent/test_auxiliary_named_custom_providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,41 @@ def test_named_custom_no_api_key_uses_fallback(self, tmp_path):
assert client is not None
# no-key-required should be used

def test_command_backed_codex_provider_keeps_required_headers(self, tmp_path):
"""Auxiliary calls must preserve host and per-provider auth headers."""
_write_config(tmp_path, {
"model": {"default": "gpt-test"},
"providers": {
"codex-passive": {
"api": "https://chatgpt.com/backend-api/codex",
"transport": "codex_responses",
"key_cmd": "print-token",
"models": {"gpt-test": {}},
"extra_headers": {"ChatGPT-Account-ID": "acct-test"},
},
},
})
token_provider = lambda: "header.payload.signature"
with (
patch(
"agent.command_token_source.build_command_token_provider",
return_value=token_provider,
),
patch("agent.auxiliary_client.OpenAI") as mock_openai,
):
mock_openai.return_value = MagicMock()
from agent.auxiliary_client import resolve_provider_client

client, model = resolve_provider_client("codex-passive", "gpt-test")

assert client is not None
assert model == "gpt-test"
assert mock_openai.call_args.kwargs["api_key"] is token_provider
headers = mock_openai.call_args.kwargs["default_headers"]
assert headers["User-Agent"].startswith("codex_cli_rs/")
assert headers["originator"] == "codex_cli_rs"
assert headers["ChatGPT-Account-ID"] == "acct-test"



class TestResolveProviderClientModelNormalization:
Expand Down