Skip to content

fix(auth): make the first pooled credential the active provider on every auth add branch - #82083

Open
briandevans wants to merge 1 commit into
NousResearch:mainfrom
briandevans:fix/auth-first-pooled-credential-active-provider
Open

fix(auth): make the first pooled credential the active provider on every auth add branch#82083
briandevans wants to merge 1 commit into
NousResearch:mainfrom
briandevans:fix/auth-first-pooled-credential-active-provider

Conversation

@briandevans

Copy link
Copy Markdown
Contributor

What does this PR do?

hermes auth add puts a credential in the pool and then, on most branches, marks that provider active. On two branches it doesn't — so the credential lands, hermes auth list shows it, and hermes setup still says "No inference provider configured".

auth_add_command has six pool.add_entry() sites. Four establish an active provider:

line branch marks active_provider how
220 generic API-key (all providers) no
246 anthropic OAuth no
339 openai-codex yes first_credential guard → mark_provider_active_if_unset
380 xai-oauth yes same guarded pattern
406 qwen-oauth yes _mark_qwen_oauth_active_save_provider_state
430 minimax-oauth yes _minimax_oauth_login_minimax_save_auth_state_save_provider_state
279/303 nous yes persist_nous_credentials_save_provider_state

The two gaps are the widest-reach paths:

  • API-key branch — serves every provider. A manually pasted key writes no environment variable.
  • anthropic OAuth branchrun_hermes_oauth_login_pure() is pure: it returns tokens and persists nothing, so the pool insert is the only write on that path. OAuth is the default auth type for anthropic (_OAUTH_CAPABLE_PROVIDERS), so a bare hermes auth add anthropic lands here.

_model_section_has_credentials() (hermes_cli/setup.py:2445) returns True only on get_active_provider() or an API-key env var. Neither of the above sets either, so the wizard reports the provider missing.

This is not a new invariant — the repo states it against itself. mark_provider_active_if_unset's docstring (hermes_cli/auth.py:1485):

Adding the very first credential for a provider should make it the active provider so the setup wizard's _model_section_has_credentials() check (which consults get_active_provider()) does not report "No inference provider configured". Subsequent adds for an already-active setup leave the user's chosen active provider untouched.

And test_auth_add_xai_oauth_sets_active_provider (tests/hermes_cli/test_auth_commands.py:328) records the same bug on the branch it was already fixed on:

Early path called pool.add_entry() without active_provider, so the setup wizard reported "No inference provider configured".

The PROVIDER_REGISTRY guard on the API-key branch is load-bearing

The API-key branch also accepts custom:* pool keys and openrouter, neither of which is a PROVIDER_REGISTRY member. mark_provider_active_if_unset does no validation, and _model_section_has_credentials returns True for any truthy get_active_provider() — but resolve_provider (hermes_cli/auth.py:2118) only honours an active_provider when _maybe in PROVIDER_REGISTRY.

So marking a custom:foo pool active would silence the wizard while resolution still failed — strictly worse than the current behaviour. Hence if first_credential and provider in PROVIDER_REGISTRY:. openrouter is deliberately outside the registry (see the provider != "openrouter" special case at auth_commands.py:166) and already resolves through its own credential-pool tier at auth.py:2103-2109, so skipping it is correct rather than an omission. anthropic is a static registry member, so the OAuth branch needs no registry guard.

Scope of the claim

This restores the wizard/status signal — _model_section_has_credentials(). resolve_provider's active-provider fallback additionally requires get_auth_status(<provider>).get("logged_in"); this PR does not change that, and the tests do not claim it.

Known follow-up, declared

hermes_cli/web_server.py:10297 (dashboard anthropic PKCE pool insert) and :12813 (POST /api/credentials/pool, whose own comment says it "Mirrors the hermes auth add behaviour in auth_commands.py") share this root cause. They are deliberately excluded here: I already have an open PR against that file (#74356), and stacking an unrelated concern onto it would make both harder to review. I'll file the mirror as a sibling PR once that one closes.

Related Issue

No filed issue — found while tracing why a fresh hermes auth add anthropic leaves hermes setup reporting no provider. The strongest precedent is that this exact fix was already shipped on the sibling branches, merged by @teknium1:

This PR completes that series on the two branches it never reached.

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix
  • 📝 Documentation update
  • ✅ Tests (adding or improving test coverage)
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

Changes Made

  • hermes_cli/auth_commands.py — API-key branch: capture first_credential = not pool.entries() before the insert, then mark_provider_active_if_unset(provider) when it is the first credential and provider in PROVIDER_REGISTRY.
  • hermes_cli/auth_commands.py — anthropic OAuth branch: same first_credential guard, no registry check needed (anthropic is statically in the registry).
  • tests/hermes_cli/test_auth_commands.py — four tests, inserted next to the existing xai-oauth active-provider tests and following their template (assert on the auth.json payload rather than through get_active_provider()):
    • test_auth_add_anthropic_oauth_sets_active_provider
    • test_auth_add_api_key_sets_active_provider
    • test_auth_add_does_not_steal_an_existing_active_provider — control: with active_provider already set to openai-codex, neither new marking call overrides it, while both credentials still land in their pools. This is what keeps the change from degenerating into "last add wins".
    • test_auth_add_custom_pool_api_key_leaves_active_provider_unset — pins the PROVIDER_REGISTRY guard so a custom:* pool can never be written as an unresolvable active_provider.

No import churn — auth_mod and PROVIDER_REGISTRY are already imported in this module and used at :344 / :165.

Sibling-site sweep

git grep -n "\.add_entry(" -- '*.py' on production code returns 8 sites: the 6 in auth_commands.py in the table above (2 fixed here, 4 already correct) and the 2 in web_server.py excluded above with reasons. No other caller of pool.add_entry exists.

How to Test

Repro on a clean profile:

export HERMES_HOME=$(mktemp -d)
hermes auth add anthropic        # OAuth is the default type for anthropic
hermes auth list                 # shows the credential
hermes setup                     # before: "No inference provider configured"

Same for hermes auth add deepseek --type api-key.

Automated, and verified in both directions:

pytest tests/hermes_cli/test_auth_commands.py -v

Before the production change (test file applied to clean main) — the two positive tests fail on exactly the reported symptom, with the success message printed and no active_provider written:

FAILED test_auth_add_anthropic_oauth_sets_active_provider - KeyError: 'active_provider'
FAILED test_auth_add_api_key_sets_active_provider          - KeyError: 'active_provider'
2 failed, 3 passed
----- Captured stdout -----
Added anthropic OAuth credential #1: "anthropic@example.com"
Added deepseek credential #1: "work"

The control and the custom:* guard test pass in both directions by design — they are invariants, not regressions, and are there so the fix cannot be loosened later.

After: 22 passed for the whole file. Adjacent auth/provider/setup suites also green (211 passed):

tests/hermes_cli/test_setup_model_provider.py
tests/hermes_cli/test_setup_blank_slate.py
tests/hermes_cli/test_resolve_provider_openrouter_pool.py
tests/hermes_cli/test_runtime_provider_resolution.py
tests/hermes_cli/test_anthropic_oauth_flow.py
tests/hermes_cli/test_anthropic_provider_persistence.py
tests/hermes_cli/test_api_key_providers.py
tests/hermes_cli/test_auth_provider_gate.py
tests/hermes_cli/test_auth_xai_oauth_provider.py
tests/hermes_cli/test_auth_codex_provider.py

test_auth_xai_oauth_provider.py and test_auth_commands.py are the only two test files in the repo that invoke auth_add_command, so the behavioural blast radius is fully covered.

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run pytest tests/ -q and all tests pass — ran the focused + adjacent suites listed under "How to Test" (233 tests, all green) rather than the full suite; happy to run anything wider on request
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform: macOS 15 (Darwin 25.4.0), Python 3.11

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — N/A, no user-facing surface changes; the reasoning lives in code comments at both sites
  • I've updated cli-config.yaml.example if I added/changed config keys — N/A, no config keys
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — N/A
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide — N/A, pure auth-store dict logic with no path, process, or terminal handling; tests set HERMES_HOME rather than assuming a home layout
  • I've updated tool descriptions/schemas if I changed tool behavior — N/A

Related / Positioning

Several open PRs touch auth_add_command in nearby regions. None addresses this defect, and none touches pool.add_entry or anything after it, so this is not a duplicate of any of them — though a three-way merge may be needed on the first two if they land first:

…ery `auth add` branch

`auth_add_command` has six `pool.add_entry()` sites. Four establish an
active provider — openai-codex and xai-oauth via the guarded
`mark_provider_active_if_unset` call, qwen-oauth and minimax-oauth via
login helpers that reach `_save_provider_state` — but two do not:

  * the generic API-key branch, which serves every provider, and
  * the anthropic OAuth branch, whose `run_hermes_oauth_login_pure()` is
    pure and persists nothing, so the pool insert is the only write.

On those two paths the credential lands in the pool and `hermes auth list`
shows it, while `hermes setup` still reports "No inference provider
configured". `_model_section_has_credentials()` in hermes_cli/setup.py
consults `get_active_provider()` or an API-key env var, and neither a
manual API key nor an OAuth login writes an env var. OAuth is the default
auth type for anthropic, so a bare `hermes auth add anthropic` hits this.

`mark_provider_active_if_unset`'s own docstring states the invariant this
restores: adding the very first credential for a provider should make it
the active provider so the setup wizard does not report "No inference
provider configured". Subsequent adds leave the user's choice untouched.

The API-key branch guards on `provider in PROVIDER_REGISTRY` because that
branch also accepts `custom:*` pool keys and `openrouter`, neither of which
is a registry member. `resolve_provider` only honours an `active_provider`
it can find in the registry, so marking a custom pool alias active would
satisfy the wizard while resolution still failed. `openrouter` already
resolves from its own credential-pool tier.

Scope: this restores the wizard/status signal. `resolve_provider`'s
active-provider fallback additionally requires
`get_auth_status(<provider>).get("logged_in")`.

`hermes_cli/web_server.py:10297` and `:12813` share this root cause and are
left for a follow-up; that file has an open PR of mine already.
Copilot AI lite review requested due to automatic review settings August 8, 2026 23:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a credential-activation gap in hermes auth add where two add paths (generic API-key and Anthropic OAuth) wrote credential-pool entries but failed to set active_provider, causing hermes setup / _model_section_has_credentials() to incorrectly report “No inference provider configured” even though hermes auth list showed the credential.

Changes:

  • API-key add path now captures “first credential for this provider pool” and, when it’s the first and the provider is in PROVIDER_REGISTRY, calls mark_provider_active_if_unset(provider).
  • Anthropic OAuth add path now mirrors the first-credential activation pattern (no registry guard needed since anthropic is registry-backed).
  • Adds targeted regression tests covering both positive activation cases plus two invariants: don’t override an existing active provider, and never set active_provider to a custom:* pool key.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
hermes_cli/auth_commands.py Sets active_provider on first credential add for API-key (registry-only) and Anthropic OAuth branches.
tests/hermes_cli/test_auth_commands.py Adds regression tests ensuring active_provider is set for the two affected branches and remains correct in edge cases.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@alt-glitch alt-glitch added type/bug Something isn't working comp/cli CLI entry point, hermes_cli/, setup wizard area/auth Authentication, OAuth, credential pools P2 Medium — degraded but workaround exists sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data labels Aug 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/auth Authentication, OAuth, credential pools comp/cli CLI entry point, hermes_cli/, setup wizard P2 Medium — degraded but workaround exists sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants