diff --git a/hermes_cli/cli_agent_setup_mixin.py b/hermes_cli/cli_agent_setup_mixin.py index 3fedeb9be7..8ef87db4f3 100644 --- a/hermes_cli/cli_agent_setup_mixin.py +++ b/hermes_cli/cli_agent_setup_mixin.py @@ -183,9 +183,13 @@ class CLIAgentSetupMixin: _primary_exc = None runtime = None try: + # target_model: the ladder's model-keyed rungs (OpenCode free tier, Zen/Go api_mode, + # Copilot/Nous api_mode) must see the model this CLI will actually send, not + # config's `default` -- otherwise `hermes -m mimo-v2.5 --provider opencode-go` with a + # *-free default is routed to the keyless Zen relay (#112600). runtime = resolve_runtime_provider( requested=self.requested_provider, explicit_api_key=self._explicit_api_key, - explicit_base_url=self._explicit_base_url) + explicit_base_url=self._explicit_base_url, target_model=self.model or None) except Exception as exc: _primary_exc = exc if _primary_exc is not None: diff --git a/tests/hermes_cli/test_cli_provider_resolution.py b/tests/hermes_cli/test_cli_provider_resolution.py index fefd8c0b3b..fb206c77a2 100644 --- a/tests/hermes_cli/test_cli_provider_resolution.py +++ b/tests/hermes_cli/test_cli_provider_resolution.py @@ -261,6 +261,32 @@ def test_runtime_resolution_failure_is_not_sticky(monkeypatch): assert shell.agent is not None +def test_ensure_runtime_credentials_passes_cli_model_as_target_model(monkeypatch): + """`hermes -m mimo-v2.5 --provider opencode-go` must resolve credentials for the model the + CLI will send: the OpenCode free-tier rung keys off the effective model, and without + target_model a `*-free` config default routes an explicit paid model to the keyless Zen + relay (#112600).""" + cli = _import_cli() + seen = {} + + def _runtime_resolve(**kwargs): + seen.update(kwargs) + return { + "provider": "opencode-go", + "api_mode": "chat_completions", + "base_url": "https://opencode.ai/zen/go/v1", + "api_key": "test-key", + "source": "env", + } + + monkeypatch.setattr("hermes_cli.runtime_provider.resolve_runtime_provider", _runtime_resolve) + shell = cli.HermesCLI(model="mimo-v2.5", provider="opencode-go", compact=True, max_turns=1) + + assert shell._ensure_runtime_credentials() is True + assert seen["requested"] == "opencode-go" + assert seen["target_model"] == "mimo-v2.5" + + def test_cli_turn_routing_uses_primary_when_disabled(monkeypatch):