feat: claude_agent_sdk runtime — Claude via subscription OAuth (Agent SDK) - #1
Open
snagnever wants to merge 23 commits into
Open
feat: claude_agent_sdk runtime — Claude via subscription OAuth (Agent SDK)#1snagnever wants to merge 23 commits into
snagnever wants to merge 23 commits into
Conversation
Verified the real Hermes approval-callback signature (command, description, allow_permanent=False) -> once|session|always|deny against tools/terminal_tool.py + codex_app_server_session.py, correcting the plan's assumed (tool_name, preview, args) shape.
Also registers claude_agent_sdk in agent_init's explicit-api_mode set so a passed api_mode='claude_agent_sdk' is honored instead of falling through to anthropic_messages (parity with codex_app_server).
Adds [claude-agent] optional-dependency (claude-agent-sdk==0.2.110, the version the runtime is verified against) and a provider.claude_agent LAZY_DEPS entry, mirroring how provider.anthropic is handled. Keeps the runtime a lazy import; the extra just pins the floor and lets hermes update refresh it.
Bridges claude-agent into the picker registry (HermesOverlay + a claude_agent_sdk transport → api_mode, 'Claude (subscription)' label, claude-sdk/claude-subscription aliases) so the Claude-subscription models are selectable via /model instead of only as a config default. Selecting the provider always routes through the SDK (provider is self-describing), independent of the anthropic_runtime flag, which stays as the make-it-default reroute for the plain anthropic provider. agent_init sets up the keyless no-client path for the claude_agent_sdk api_mode.
… switch resolve_runtime_provider short-circuits claude-agent (and aliases) to a no-credentials runtime dict (api_mode=claude_agent_sdk, no base_url) before the credential pool, which previously rejected it as 'Unknown provider'. The Claude Code subprocess owns auth, so Hermes needs no in-process key.
Real E2E showed the SDK reports auth failures (not-logged-in) as is_error=True with subtype='success' and the message in result — so the error string was the useless 'claude-agent-sdk: success'. Now append the result text (e.g. 'Not logged in · Please run /login').
The claude-agent runtime has no REST /models endpoint (the SDK validates the model at turn time), so the switch validator's live probe always failed and printed a misleading 'could not reach the claude-agent API ... may not be valid' warning (and cost a slow timeout). Early-accept claude-agent (and its aliases) with no probe — instant switch, no false alarm.
The per-turn credential setup rejected an empty base URL, but the claude_agent_sdk runtime has no HTTP endpoint by design (the Claude Code subprocess owns the connection). Exempt api_mode=claude_agent_sdk from the base-URL guard; every other runtime still requires one. run_conversation short-circuits to the SDK runtime before any client/base_url machinery, so nothing downstream needs it.
…h probe Root cause of the ~10-45s freeze on 'hermes chat' open and /model: the claude-agent runtime has no HTTP endpoint, but context-length detection probed the resolved base_url — which, after switching from another provider, was a stale leftover URL (e.g. an LM Studio LAN address). The probe hung on that host's TCP connect timeout (measured 44s). - model_metadata.get_model_context_length: early-return Claude's 200K window for claude-agent (+aliases) before any probe. - model_switch: force base_url='' + api_mode=claude_agent_sdk when switching to claude-agent so it never inherits the previous provider's endpoint.
…alled The /model picker only surfaces providers with detectable credentials, but claude-agent's auth lives in the Claude Code CLI (subscription login / Keychain), not an env var or Hermes auth store — so it was silently dropped. Detect availability via check_claude_binary and populate its model list from the provider profile's fallback_models. Hidden when the CLI isn't installed so it doesn't clutter the picker for users who haven't set it up.
Auxiliary helpers (title generation, auto-compression, vision, memory) need a separate API-key-backed provider; the subscription runtime has no HTTP client for them, so they're skipped non-fatally. Document configuring an auxiliary provider to enable them.
Auxiliary helpers (title generation, compression, memory) have no HTTP client on the subscription runtime. Add a one-shot SDK path in call_llm: route when the aux task resolves to claude-agent (config/inherited), and as a last resort when no HTTP provider is configured but the claude CLI is present. Runs a stateless ClaudeAgentSession on Haiku (no hermes tools, no MCP boot) and returns an OpenAI-ChatCompletion-shaped response so callers are unchanged. Verified live: title generated in ~6.8s. Streaming/tool aux tasks fall through to the normal HTTP path.
Startup hung ~33s in show_banner: the vision-capability check probes the model base_url to detect a local Ollama server, and a stale leftover URL (a previous provider's LM Studio LAN address, still in config because claude-agent has no endpoint of its own) hung on the TCP connect timeout. _resolve_inference_base_url now returns '' for claude-agent and _should_probe_ollama_vision skips it — no probe path sees a base_url for the endpoint-less runtime. (Same disease as the earlier context-length freeze.)
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
Adds a
claude-agentprovider +claude_agent_sdkruntime so Claude models run through the Claude Agent SDK, authenticated by a Claude subscription (Claude Code OAuth) instead of a pay-per-tokenANTHROPIC_API_KEY. Mirrors the existingcodex_app_serverruntime 1:1. Implements the intent of NousResearch#25267.Claude Code owns the tool loop (terminal/file-ops run in its runtime); Hermes' own tool surface is injected via the existing
hermes_tools_mcp_serverstdio MCP; events/usage project back into Hermes' accounting. Opt-in and additive — default paths are unchanged.How to use
…or switch to it at runtime:
/model opus --provider claude-agent(shows as "Claude (subscription)" in the picker). Requiresnpm i -g @anthropic-ai/claude-code(logged in) andpip install hermes-agent[claude-agent].Architecture
plugins/model-providers/claude-agent/) — registersclaude-agent(aliasesclaude-sdk,claude-subscription),api_mode=claude_agent_sdk,auth_type=oauth_external, no REST catalog.ClaudeAgentSession(agent/transports/claude_agent_session.py) — drives the asyncClaudeSDKClientfrom Hermes' sync loop via one event loop on a dedicated thread. Lifecycle,run_turn(text + tool-use projection + usage),can_use_tool→ Hermes approval bridge, interrupt.ANTHROPIC_API_KEYis stripped from the child env so subscription auth is never silently bypassed.run_claude_agent_sdk_turn(agent/claude_runtime.py) — turn orchestrator, projects messages + usage exactly likecodex_runtime.py.run_conversationshort-circuits to the SDK runtime whenapi_mode == "claude_agent_sdk";agent_initsets up the keyless no-HTTP-client path.hermes_cli(HermesOverlay+claude_agent_sdktransport, credential resolver short-circuit, keyless validation). Selecting the provider always routes through the SDK.claudeCLI is present (subscription-only setups get them for free).Auth hygiene / ToS
Uses the Agent SDK + Claude Code subscription login — the sanctioned subscription path.
ANTHROPIC_API_KEYis deliberately blanked in the SDK subprocess so a stray key can't flip billing to pay-per-token.Testing
claude_agent_sdkfixture — no package/binary/network needed), plus codex-runtime regression suites green.claude-agent-sdk 0.2.110+claudeCLI 2.1.193:ClaudeAgentOptionsfields, message/permission classes,ResultMessage.usageshape./modelswitch + turn inhermes chat, and aux title generation on Haiku (~6.8s).Notes / limitations
temperature/top_p(SDK doesn't expose sampling params).delegate_task/memory/todoagent-loop tools unavailable (same ascodex_app_server).base_urlmust be null forclaude-agent— the switch now clears it so no probe path (context-length, vision) hangs on a stale URL.Commits
🤖 Generated with Claude Code