feat(agent): pin_anthropic_token — let a static setup-token win over a refreshable Claude Code credential - #82095
Open
adurham wants to merge 2 commits into
Open
Conversation
…a refreshable Claude Code credential resolve_anthropic_token() already prefers a refreshable Claude Code credential (~/.claude/.credentials.json or macOS Keychain) over a static ANTHROPIC_TOKEN/CLAUDE_CODE_OAUTH_TOKEN env var, so a stale persisted token never silently blocks auto-refresh. That's the right default, but it has no escape hatch for a real, common setup: a dedicated long-lived `claude setup-token` for Hermes, on a machine where the user also runs interactive `claude` CLI logins for daily-driver Claude Code work. macOS Keychain isn't scoped by CLAUDE_CONFIG_DIR, so both credentials collide in the same Keychain slot -- every interactive `claude` login silently overwrites Hermes's dedicated setup-token, and there was no way to tell resolve_anthropic_token() "no, keep using my static token on this machine regardless of what the interactive login just wrote." Adds an opt-in `agent.pin_anthropic_token: true` config key (default false, preserving the existing safety-net behavior). When set, the resolver skips the refreshable-credential preference check for both env var sources and returns the static token directly. 4 new tests in tests/agent/test_anthropic_adapter.py::TestResolveAnthropicToken: pin=true makes the static token win over a refreshable credential, pin=false (default) is unaffected (regression guard), and a config-load exception degrades to the safe default rather than propagating. Verified: ruff clean; 97/97 in test_anthropic_adapter.py; 93/93 across adjacent credential-resolution test files (runtime_provider_resolution, credential_pool_oat_authtype, minimax_provider, anthropic_token_scope_isolation, anthropic_third_party_oauth_guard, hermetic_side_effect_guards); the new pin=true test confirmed to fail against pre-fix code via git stash.
adurham
pushed a commit
to adurham/hermes-agent
that referenced
this pull request
Aug 9, 2026
Filed agent.pin_anthropic_token as PR NousResearch#82095 -- verified upstream's resolve_anthropic_token()/_prefer_refreshable_claude_code_token() already exist in the converged shape, no competing PR found. Checked the other two Tier-1 candidates (trafilatura web_extract, tool_search core-toolset deferral) and found both already have real open competing PRs upstream; per standing rule, did not file duplicates for either.
Contributor
|
This was generated by AI during triage. Summary: Problems:
Solution: Checked against |
…y present Per AI triage review on PR NousResearch#82095: 1. _pin_static_anthropic_token() (and its config read) was called unconditionally at the top of resolve_anthropic_token() on every invocation, even when neither ANTHROPIC_TOKEN nor CLAUDE_CODE_OAUTH_TOKEN is set and the pin can never apply. Moved the call into the #1/#2 branches where it's actually consulted, so the config read is skipped entirely for the common case. 2. Switched from load_config() (deepcopy variant) to load_config_readonly() -- this helper only reads the flag, never mutates it, and load_config_readonly() skips the ~265us deepcopy load_config() pays on every cache hit. Updated the 3 existing pin_anthropic_token tests' mock targets to load_config_readonly, and added 2 new regression tests: one asserting _pin_static_anthropic_token() is never called when no static env token is present (confirmed fails pre-fix via git stash), and one asserting the readonly loader is used instead of the mutable one.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
resolve_anthropic_token()already prefers a refreshable Claude Code credential (~/.claude/.credentials.jsonor macOS Keychain) over a staticANTHROPIC_TOKEN/CLAUDE_CODE_OAUTH_TOKENenv var, so a stale persisted token never silently blocks auto-refresh. That's the right default — but it has no escape hatch for a real, common setup: a dedicated long-livedclaude setup-tokenfor Hermes, on a machine where the user also runs interactiveclaudeCLI logins for daily-driver Claude Code work.macOS Keychain isn't scoped by
CLAUDE_CONFIG_DIR, so both credentials collide in the same Keychain slot — every interactiveclaudelogin silently overwrites Hermes's dedicated setup-token, and there was no config lever to tellresolve_anthropic_token()"keep using my static token on this machine regardless of what the interactive login just wrote."Fix
Adds an opt-in
agent.pin_anthropic_token: trueconfig key (defaultfalse, preserving the existing safety-net behavior). When set, the resolver skips the refreshable-credential preference check for both env var sources (ANTHROPIC_TOKENandCLAUDE_CODE_OAUTH_TOKEN) and returns the static token directly.Test plan
tests/agent/test_anthropic_adapter.py::TestResolveAnthropicToken:pin_anthropic_token: truemakes the static token win over a refreshable credentialpin_anthropic_token: false(default) is unaffected — regression guard confirming the opt-in doesn't change existing behaviorgit stash.ruff checkclean.test_anthropic_adapter.py.test_runtime_provider_resolution.py,test_credential_pool_oat_authtype.py,test_minimax_provider.py,test_anthropic_token_scope_isolation.py,test_anthropic_third_party_oauth_guard.py,test_hermetic_side_effect_guards.py.Searched existing issues/PRs first — no existing report or competing PR for this specific gap.