Summary
claude-code is currently registered as an alias of anthropic (hermes_cli/providers.py:282, plugins/model-providers/anthropic/__init__.py:44), so provider: claude-code in config.yaml routes to https://api.anthropic.com with API-key/OAuth-token auth. There is no path for the local claude --print subprocess transport that the official Claude Code CLI uses to authenticate against a flat-fee Claude Max plan (Keychain OAuth, no metered key, no API quota wall).
This forces users on Claude Max to either:
- Set
provider: claude-code and silently hit api.anthropic.com with whatever ANTHROPIC_API_KEY/ANTHROPIC_TOKEN/CLAUDE_CODE_OAUTH_TOKEN is in env (metered billing, no Max benefit), or
- Stand up a local OpenAI-compatible reverse-proxy that shells out to
claude --print per request, and point config.yaml at it via a custom claude-code-proxy provider block. This is the workaround most Max users converge on.
Every conversation turn currently logs:
agent.conversation_loop: conversation turn: model=claude-opus-4-7 provider=anthropic
agent.chat_completion_helpers: Streaming failed before delivery: Error code: 400 - "You're out of extra usage..."
agent.conversation_loop: API call failed provider=anthropic base_url=https://api.anthropic.com
agent.chat_completion_helpers: Fallback activated: claude-opus-4-7 → claude-opus-4-7 (claude-code-proxy)
i.e. the primary attempt is a useless 400 round-trip to api.anthropic.com, every turn, on every Max user's gateway.
Proposed change
Register claude-code as a first-class provider with its own profile (not an alias), backed by a subprocess driver around claude --print --output-format stream-json. Sketch:
claude_code = SubprocessProfile(
name="claude-code",
api_mode="claude_cli_stream_json",
binary="claude",
args=["--print", "--output-format", "stream-json", "--input-format", "stream-json"],
auth_type="none", # OAuth is handled by ~/.claude/.credentials.json
auth_probe=lambda: shutil.which("claude") is not None,
default_aux_model="claude-haiku-4-5",
)
register_provider(claude_code)
…plus a translator from Claude CLI stream-json → OpenAI SSE chunks with tool_calls deltas so the existing agent loop doesn't need changes.
Why this matters
- Cost: Max users currently waste one failed
api.anthropic.com request per turn; some never notice and end up on metered billing for the primary lane.
- Auth hygiene: Removes the need to keep
CLAUDE_CODE_OAUTH_TOKEN in env (which currently shadows Keychain OAuth and silently switches to metered).
- UX: Today Max users get cold UX (chat-completions translation only). Native
tool_use blocks and 1M-token context would Just Work if Hermes drove the CLI subprocess directly.
- Fallback chain: Allows
claude-code (Max OAuth, $0) → anthropic (metered API, capped) as a clean primary/fallback pair instead of users running a private fork or a reverse proxy.
Acceptance criteria
Workaround until then
Add a custom proxy provider that shells out to claude --print:
providers:
claude-code-proxy:
api_mode: chat_completions
base_url: http://127.0.0.1:18796/v1
api_key: dummy-key
model:
provider: claude-code-proxy
default: claude-opus-4-7
Happy to upstream the subprocess provider + stream-json→SSE translator as a PR if there's interest.
Hermes version: v0.14.0 (2026.5.16)
Filed by: @zkhalpey
Summary
claude-codeis currently registered as an alias ofanthropic(hermes_cli/providers.py:282,plugins/model-providers/anthropic/__init__.py:44), soprovider: claude-codeinconfig.yamlroutes tohttps://api.anthropic.comwith API-key/OAuth-token auth. There is no path for the localclaude --printsubprocess transport that the official Claude Code CLI uses to authenticate against a flat-fee Claude Max plan (Keychain OAuth, no metered key, no API quota wall).This forces users on Claude Max to either:
provider: claude-codeand silently hitapi.anthropic.comwith whateverANTHROPIC_API_KEY/ANTHROPIC_TOKEN/CLAUDE_CODE_OAUTH_TOKENis in env (metered billing, no Max benefit), orclaude --printper request, and pointconfig.yamlat it via a customclaude-code-proxyprovider block. This is the workaround most Max users converge on.Every conversation turn currently logs:
i.e. the primary attempt is a useless 400 round-trip to
api.anthropic.com, every turn, on every Max user's gateway.Proposed change
Register
claude-codeas a first-class provider with its own profile (not an alias), backed by a subprocess driver aroundclaude --print --output-format stream-json. Sketch:…plus a translator from Claude CLI stream-json → OpenAI SSE chunks with
tool_callsdeltas so the existing agent loop doesn't need changes.Why this matters
api.anthropic.comrequest per turn; some never notice and end up on metered billing for the primary lane.CLAUDE_CODE_OAUTH_TOKENin env (which currently shadows Keychain OAuth and silently switches to metered).tool_useblocks and 1M-token context would Just Work if Hermes drove the CLI subprocess directly.claude-code(Max OAuth, $0) →anthropic(metered API, capped) as a clean primary/fallback pair instead of users running a private fork or a reverse proxy.Acceptance criteria
provider: claude-codeinconfig.yamlresolves to a distinct provider (not aliased toanthropic).claudeCLI subprocess;api.anthropic.comis not contacted unlessprovider: anthropicis explicitly set.hermes doctorreportsclaude-codeas a separate provider with its own auth probe.claude/claude-oauthaliases (HTTP path), butclaude-codeis removed from that alias tuple.Workaround until then
Add a custom proxy provider that shells out to
claude --print:Happy to upstream the subprocess provider + stream-json→SSE translator as a PR if there's interest.
Hermes version: v0.14.0 (2026.5.16)
Filed by: @zkhalpey