Repository navigation
Use the Claude subscription through the official Claude Code CLI (ported from Hermes) - #101
Merged
arefbehboudi merged 2 commits intoOct 27, 2026
Conversation
…ted from Hermes Replaces the old Claude Code integration, which read the OAuth token from the Keychain / ~/.claude/.credentials.json and called the API with Claude Code's headers and system prompt. The new providers/claude-code module is a Java port of the Hermes "Claude Subscription DirectSDK" provider (NousResearch/hermes-official-plugins, MIT): each model call runs a fresh `claude -p` process, a loopback relay admits exactly one upstream request and keeps the prompt cache stable, and JavaClaw's own ToolCallingAdvisor keeps the agent loop. JavaClaw never reads or sends the subscription credential. Onboarding checks `claude auth status`, offers the Claude models as a dropdown, and the restart after onboarding now reads the config file ConfigurationManager writes instead of a stale classpath copy.
Switching the default provider kept the previous provider's base-url and api-key in the config file, and the credentials step prefilled the previous provider's model, so moving to the Claude subscription could end with a base URL it rejects and a model it cannot run. ConfigurationManager now removes a key passed with a null value, onboarding uses that to drop unused settings, and saved values only prefill the form for the same provider. The private config is now imported only from the file ConfigurationManager writes, so a removed key no longer comes back from the stale classpath copy.
arefbehboudi
self-requested a review
October 27, 2026 06:02
arefbehboudi
approved these changes
Oct 27, 2026
Collaborator
|
Thanks @iNicholasBE, That's what I really need it 😄 💯 |
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.
Why
JavaClaw's Claude Code support read the OAuth token straight out of the macOS Keychain (or
~/.claude/.credentials.json) and called the API itself, impersonating Claude Code with its beta headers and system-prompt prefix. Our own onboarding page warned that this "violates the terms of your subscription and may result in account suspension".What
This PR replaces that with a port of the Hermes Claude Subscription DirectSDK provider (NousResearch/hermes-official-plugins, MIT). JavaClaw never reads or sends the subscription credential. Everything goes through the official
claudeCLI, which handles login, token refresh and authentication itself.How it works (new
providers/claude-codemodule, provider typeclaude-code):claude -pprocess in stream-json mode. Native tools, skills, settings, memory and compaction are off, and--max-turns 1limits it to one turn.ToolCallingAdvisorruns the tools.AdmissionRelayis Claude Code'sANTHROPIC_BASE_URL. It forwards only the first Messages request upstream and denies Claude Code's own retries locally, so one JavaClaw call can never become several billed requests.RelayPayloadskeeps the cached prefix stable across tool rounds. It fails open, ported from the Hermes admission transforms.ANTHROPIC_API_KEY/ANTHROPIC_BASE_URL/ Bedrock / Vertex overrides are refused rather than silently billing an API key, and a custom base URL is rejected.Deviations from Hermes:
initialize,tools/list)./usageplan limits, the telemetry setting, and reasoning-effort / structured-output passthrough.Onboarding:
AgentOnboardingProviderdropssystemWideToken()and gainsreadinessProblem(),supportsBaseUrl()andmodels().claude auth statusbefore saving, hides the base URL field, and shows the models as a dropdown. Full ids are stored, and a saved model that isn't in the list stays selectable.Bug fix found while testing:
classpath:application.private.yaml, which is a stale build copy when running from Gradle or the IDE. That left the app on the "no model configured" placeholder, which fails withstreaming is not supported.application.yamlnow also imports the fileConfigurationManageractually writes, so the restart picks up the new config.Migration: a config that still holds
api-key: <claude-code-bearer-token>underprovider: anthropicnow fails with a clear message. Switch it toprovider: claude-codeand remove theapi-key.Testing
ClaudeCodeChatModelIntegrationTestdrives the realclaudeCLI (2.1.287) against a fake loopback Anthropic upstream: no credentials, no inference. It covers text, tool calls, history replay with signed thinking, the fullChatClient+ToolCallingAdvisorloop (blocking and streaming), upstream error mapping (429), truncation and cancellation. It's skipped whenclaudeisn't installed.RelayPayloadsTestholds 29 cases ported from the Hermes admission tests.ClaudeCodeRequestTest,SseCaptureTest,ClaudeCodeCliTest,ClaudeCodeModelsTestand three newOnboardingControllerTestcases cover the rest.ChatClient+ToolCallingAdvisorworked. Onboarding and web chat were also tested by hand.Note: when Gradle runs on JDK 27,
:ui:vaadinBuildFrontendfails with "Unsupported class file major version 71". That is unrelated to this PR; run Gradle on JDK 25.