From f4a866b484679ccae191de8749f0ca2936dde456 Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Wed, 19 Aug 2026 17:39:49 -0700 Subject: [PATCH] fix(cli): /config displays the live agent credential, not the env-var constructor seed --- cli.py | 18 +++++++-- tests/cli/test_show_config_credential.py | 51 ++++++++++++++++++++++++ tests/run_agent/test_callable_api_key.py | 4 +- 3 files changed, 68 insertions(+), 5 deletions(-) create mode 100644 tests/cli/test_show_config_credential.py diff --git a/cli.py b/cli.py index d41ad55877..45b9b17ebc 100644 --- a/cli.py +++ b/cli.py @@ -9246,10 +9246,22 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin, CLIBillingMixin): # ``self.api_key`` may be a callable (Azure Foundry Entra ID bearer # provider). Never invoke it; just identify the auth surface. from agent.azure_identity_adapter import is_token_provider - if is_token_provider(self.api_key): + + # Prefer the LIVE agent's credential when one exists: HermesCLI's + # constructor seeds self.api_key from OPENAI/OPENROUTER env vars + # before provider resolution runs, so on non-OpenAI providers (Nous, + # Anthropic, ...) the constructor value is a different vendor's key + # than the one actually authenticating requests. /config displaying + # an sk-proj-... OpenAI key next to a Nous base URL was the visible + # symptom (full-surface CLI QA sweep, Aug 2026). + display_key = self.api_key + agent = getattr(self, "agent", None) + if agent is not None and getattr(agent, "api_key", None): + display_key = agent.api_key + if is_token_provider(display_key): api_key_display = "Microsoft Entra ID" - elif isinstance(self.api_key, str) and len(self.api_key) > 12: - api_key_display = f"{self.api_key[:8]}...{self.api_key[-4:]}" + elif isinstance(display_key, str) and len(display_key) > 12: + api_key_display = f"{display_key[:8]}...{display_key[-4:]}" else: api_key_display = "Not set!" diff --git a/tests/cli/test_show_config_credential.py b/tests/cli/test_show_config_credential.py new file mode 100644 index 0000000000..abb5f5dacd --- /dev/null +++ b/tests/cli/test_show_config_credential.py @@ -0,0 +1,51 @@ +"""Regression test: /config must show the LIVE agent credential. + +HermesCLI.__init__ seeds ``self.api_key`` from OPENAI_API_KEY / +OPENROUTER_API_KEY env vars before provider resolution runs. On any +non-OpenAI provider (Nous, Anthropic, ...) the constructor value is a +different vendor's key than the one actually used for requests, so +``/config`` displayed e.g. an ``sk-proj-...`` OpenAI key next to a Nous +base URL. ``show_config`` must prefer ``self.agent.api_key`` when an +agent exists. +""" + +from datetime import datetime +from types import SimpleNamespace + +from cli import HermesCLI + + +def _run_show_config(stand_in, capsys): + HermesCLI.show_config(stand_in) + return capsys.readouterr().out + + +def _make_stand_in(cli_key, agent_key): + agent = SimpleNamespace(api_key=agent_key) if agent_key is not None else None + return SimpleNamespace( + api_key=cli_key, + agent=agent, + model="test/model", + base_url="https://example.invalid/v1", + enabled_toolsets=[], + max_turns=5, + verbose=False, + session_start=datetime(2026, 8, 19, 12, 0, 0), + ) + + +class TestShowConfigCredentialSource: + def test_prefers_live_agent_key(self, capsys): + out = _run_show_config( + _make_stand_in(cli_key="sk-proj-WRONGVENDORKEY1234", agent_key="nous-REALKEY-abcdef9876"), + capsys, + ) + assert "nous-REA" in out + assert "sk-proj-" not in out + + def test_falls_back_to_cli_key_without_agent(self, capsys): + out = _run_show_config( + _make_stand_in(cli_key="sk-proj-CONSTRUCTORKEY5678", agent_key=None), + capsys, + ) + assert "sk-proj-" in out diff --git a/tests/run_agent/test_callable_api_key.py b/tests/run_agent/test_callable_api_key.py index a1d1e8ef18..6588da27a1 100644 --- a/tests/run_agent/test_callable_api_key.py +++ b/tests/run_agent/test_callable_api_key.py @@ -302,8 +302,8 @@ class TestInlinedDisplayMasks: from pathlib import Path src = (Path(__file__).resolve().parent.parent.parent / "cli.py").read_text() - assert "is_token_provider(self.api_key)" in src, ( - "cli.HermesCLI.show_config must guard self.api_key via " + assert "is_token_provider(display_key)" in src, ( + "cli.HermesCLI.show_config must guard the displayed key via " "is_token_provider so callable Entra ID providers don't " "crash /config." )