Skip to content
Open
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
18 changes: 15 additions & 3 deletions agent/auxiliary_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -4585,16 +4585,28 @@ def _wrap_if_needed(client_obj, final_model_str: str, base_url_str: str = "",
return None, None
if raw_codex:
# Return the raw OpenAI client for callers that need direct
# access to responses.stream() (e.g., the main agent loop).
codex_token = _read_codex_access_token()
# access to responses.stream() (e.g., the main agent loop). Use
# Hermes' runtime credential resolver so refreshes and base URL
# overrides (HERMES_CODEX_BASE_URL) are honored consistently with
# the primary Codex runtime path.
codex_token = ""
base_url = _CODEX_AUX_BASE_URL
try:
from hermes_cli.auth import resolve_codex_runtime_credentials

creds = resolve_codex_runtime_credentials(refresh_if_expiring=True)
codex_token = str(creds.get("api_key") or "").strip()
base_url = str(creds.get("base_url") or base_url).strip().rstrip("/") or base_url
except Exception:
codex_token = _read_codex_access_token() or ""
if not codex_token:
logger.warning("resolve_provider_client: openai-codex requested "
"but no Codex OAuth token found (run: hermes model)")
return None, None
final_model = _normalize_resolved_model(model, provider)
raw_client = _create_openai_client(
api_key=codex_token,
base_url=_CODEX_AUX_BASE_URL,
base_url=base_url,
default_headers=_codex_cloudflare_headers(codex_token),
)
return (raw_client, final_model)
Expand Down
28 changes: 28 additions & 0 deletions tests/agent/test_auxiliary_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,34 @@ def test_pool_without_selected_entry_falls_back_to_auth_store(self):
assert mock_openai.call_args.kwargs["api_key"] == "codex-auth-token"
assert mock_openai.call_args.kwargs["base_url"] == "https://chatgpt.com/backend-api/codex"

def test_resolve_provider_client_raw_codex_uses_runtime_resolved_base_url(self, monkeypatch):
class DummyClient:
def __init__(self, *, api_key, base_url, default_headers=None):
self.api_key = api_key
self.base_url = base_url
self.default_headers = default_headers

monkeypatch.setattr("agent.auxiliary_client.OpenAI", DummyClient)
monkeypatch.setattr(
"hermes_cli.auth.resolve_codex_runtime_credentials",
lambda refresh_if_expiring=True: {
"api_key": "codex-runtime-token",
"base_url": "https://runtime.example/codex",
},
)

client, model = resolve_provider_client(
"openai-codex",
"gpt-5.4",
async_mode=False,
raw_codex=True,
)

assert isinstance(client, DummyClient)
assert client.api_key == "codex-runtime-token"
assert client.base_url == "https://runtime.example/codex"
assert model == "gpt-5.4"

def test_rejects_missing_model(self):
"""Callers must pass an explicit model; no hardcoded default."""
from agent.auxiliary_client import _build_codex_client
Expand Down
Loading