Skip to content

Claude (and other pool-based) cards disappear when Desktop is on a named profile that has no auth.json of its own #36

Closed

Description

@yy75pvkjvz-star

Summary

When Hermes Desktop is switched to a named profile (~/.hermes/profiles/<name>) that has no auth.json of its own, the Claude card disappears, even though that profile is actively running on the base install's Claude Pro OAuth login. Switching back to the default profile brings the card back.

Environment

  • hermes-resetwatch 0.2.21 (sha d460334), installed via Desktop catalog
  • macOS (Apple Silicon), Hermes Desktop with three profiles: default, curry, him
  • Claude Pro login stored only in the base ~/.hermes/auth.json → credential_pool.anthropic (auth_type: oauth, source: manual:hermes_pkce)
  • profiles/curry/auth.json and profiles/him/auth.json do not exist. Hermes falls back to the base credentials, and the curry profile runs on Claude Pro without problems.

Steps to reproduce

  1. Log in to Claude via Hermes on the default profile (credentials land in ~/.hermes/auth.json).
  2. Create a named profile without its own auth.json and set its provider to Anthropic.
  3. Open ResetWatch on the default profile: the Claude card shows.
  4. Switch Desktop to the named profile and reload: the Claude card is gone, and only the cards from outside Hermes (e.g. Codex via ~/.codex) remain.

Evidence

Per-profile probe caches from the same machine, minutes apart:

Cache Claude card Codex card
~/.hermes/cache/resetwatch/probe_snapshots.full.json ✅ Pro ✅
~/.hermes/profiles/curry/cache/resetwatch/probe_snapshots.full.json ❌ ✅
~/.hermes/profiles/him/cache/resetwatch/probe_snapshots.full.json ❌ ✅

No anthropic_usage.*.ratelimit files were present, so this isn't throttling.

Root cause

With --profile <name>, _hermes_homes() returns only the profile home:

def _hermes_homes() -> list[Path]:
    if _profile_home is not None:
        return [_profile_home]

_pool_entries() therefore reads only profiles/<name>/auth.json, which doesn't exist, so _pool_entries("anthropic") returns [] and the card is dropped. Hermes itself falls back to the base home's credentials in this case, so the probe and the agent disagree about which login the profile uses.

Suggested fix

Fall back to the base home for auth.json credential pools only, and only when the profile has no auth.json of its own. This mirrors Hermes's behaviour while keeping the existing profile isolation for cache dirs and .env lookups. Tested locally: after the patch, --profile curry, --profile him and --profile default all return the Anthropic Pro card plus Codex.

@@ def _pool_entries(provider: str) -> list[dict]:
-    for home in _hermes_homes():
+    for home in _auth_store_homes():
         store = _read_auth_store(home / "auth.json")

+def _auth_store_homes() -> list[Path]:
+    """Homes to search for auth.json credential pools.
+
+    Hermes profiles without their own auth.json use the base home's
+    credentials, so mirror that here. Profile first; base only as a
+    fallback when the profile has no auth.json.
+    """
+    homes = _hermes_homes()
+    if _profile_home is not None and not (_profile_home / "auth.json").is_file():
+        if _profile_home.parent.name == "profiles":
+            base = _profile_home.parent.parent
+            if base not in homes and (base / "auth.json").is_file():
+                homes = [*homes, base]
+    return homes

Happy to open a PR if that's useful.

Activity

  1. Adolanium commented on Oct 9, 2026

    @Adolanium
    Owner

    Thanks for the thorough report. You're right about the cause, and yes, a PR would be great.

    One change to the approach, though. Hermes decides the fallback per provider, not per file. read_credential_pool() in hermes_cli/auth.py uses the base home's rows for a provider whenever the profile has no rows for that provider, even if the profile has its own auth.json. With the suggested patch, a profile whose auth.json only holds an OpenRouter key, for example, would still lose the Claude card, while Hermes runs that profile on the base Claude login.

    _pool_entries() already returns the first home that has rows for the provider, so adding the base home after the profile home gives the same rule:

    homes = _hermes_homes()
    if _profile_home is not None and _profile_home.parent.name == "profiles":
        homes = [*homes, _profile_home.parent.parent]
    for home in homes:

    Please keep the change inside _pool_entries(), so .env lookups, caches and .anthropic_oauth.json stay per-profile. Hermes also reads that OAuth file only from the profile's own home.

    These three test cases would cover it:

    • A profile with no auth.json gets the base home's Claude rows.
    • A profile whose auth.json only has another provider still gets the base home's Claude rows.
    • A profile with its own Claude rows uses only those.

    Then run python scripts/build_catalog.py so catalog/desktop/probe.py gets the same change. CI runs it with --check.

  2. Adolanium commented on Oct 17, 2026

    @Adolanium
    Owner

    Fixed in #43 with the per-provider fallback described above, and released in catalog-v0.2.23. A profile with no Claude rows of its own now shows the base login's Claude card. Catalog installs get it once the catalog update (NousResearch/hermes-agent#140366) is merged, through hermes plugins update.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions