Skip to content

[codex] Add Anthropic provider with API key and Claude CLI modes#229

Open
furukama wants to merge 5 commits intomainfrom
codex/add-anthropic-provider
Open

[codex] Add Anthropic provider with API key and Claude CLI modes#229
furukama wants to merge 5 commits intomainfrom
codex/add-anthropic-provider

Conversation

@furukama
Copy link
Copy Markdown
Contributor

@furukama furukama commented Apr 5, 2026

What changed

This adds Anthropic as a first-class HybridClaw provider with two login methods:

  • --method api-key for direct Anthropic Messages API access
  • --method claude-cli for the official claude -p transport

The Claude CLI path was simplified to the common-case design:

  • it is host-sandbox only
  • it shells out to the locally installed claude binary
  • it reuses the user's existing claude auth login session
  • container sandbox mode fails fast with a clear error instead of trying to synthesize Claude credentials inside Docker

The PR also fixes a few real Claude CLI compatibility issues discovered during manual smoke testing:

  • current Claude login detection on macOS keychain
  • no unsupported --cwd flag
  • --verbose added for --output-format stream-json

Why

Direct reuse of Claude Code login material inside a third-party Anthropic API transport is no longer a good assumption. The simpler and more correct split is:

  • api-key for direct API access
  • claude-cli for official Claude Code execution semantics

Restricting claude-cli to host sandbox mode keeps the implementation smaller and aligned with the main user path of an already-installed Claude CLI on the host.

Impact

Users can now configure Anthropic with either:

  • hybridclaw auth login anthropic --method api-key --set-default
  • hybridclaw auth login anthropic --method claude-cli --set-default

For claude-cli, the intended runtime is:

  • hybridclaw gateway start --foreground --sandbox=host

Container sandbox mode remains supported for Anthropic API-key usage, but not for the Claude CLI transport.

Validation

  • npm run format
  • npm run typecheck
  • npm --prefix container run lint
  • npm run build
  • npm run test:unit -- tests/container.anthropic-claude-cli.test.ts tests/providers.task-routing.test.ts tests/cli.test.ts

Remaining risk

This was not fully live-tested end-to-end against every Claude CLI version. The remaining risk is CLI contract drift across installed Claude versions, although the current implementation was adjusted against a real local Claude install during smoke testing.

@furukama furukama changed the title [codex] Add Anthropic provider with Claude Code auth [codex] Add Anthropic provider with API key and Claude CLI modes Apr 5, 2026
@furukama furukama marked this pull request as ready for review April 5, 2026 19:37
Copilot AI review requested due to automatic review settings April 5, 2026 19:37
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds Anthropic as a first-class HybridClaw provider, supporting both direct Anthropic Messages API access via API key and the official claude -p CLI transport, with end-to-end wiring through config, CLI auth/onboarding, routing, container/host runners, and doctor probes.

Changes:

  • Implement Anthropic provider resolution (api-key + claude-cli methods) and propagate providerMethod through routing/runtime credentials and worker signatures.
  • Add container-side Anthropic provider implementation (Messages API + streaming + claude -p execution in host sandbox mode).
  • Update CLI help/auth/onboarding and doctor checks/probes; expand unit tests to cover the new behavior.

Reviewed changes

Copilot reviewed 39 out of 40 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
tests/providers.task-routing.test.ts Updates task routing tests to expect resolved Anthropic policies/credentials and error handling.
tests/providers.factory.test.ts Verifies factory resolves Anthropic runtime credentials from env/runtime secrets.
tests/container.model-router.test.ts Adds test coverage for routing Anthropic direct Messages API calls in the container router.
tests/container.anthropic-claude-cli.test.ts New tests for claude -p transport behavior and sandbox-mode constraints.
tests/cli.test.ts Adds CLI tests for Anthropic auth login/status/logout flows (api-key + claude-cli).
src/types/models.ts Introduces AnthropicMethod and adds providerMethod to task model policy typing.
src/types/container.ts Extends container input shape to include providerMethod.
src/security/runtime-secrets.ts Allows ANTHROPIC_API_KEY to be stored in runtime secrets.
src/providers/types.ts Adds providerMethod to resolved runtime credentials.
src/providers/task-routing.ts Adds Anthropic model prefix detection and propagates providerMethod into policies.
src/providers/provider-ids.ts Promotes anthropic into runtime provider IDs and updates provider ID sets.
src/providers/model-catalog.ts Adds Anthropic prefix support to the model catalog filter mapping.
src/providers/auxiliary.ts Threads providerMethod through auxiliary model resolution and validation.
src/providers/anthropic.ts Implements host-side Anthropic credential resolution for api-key and claude-cli modes.
src/providers/anthropic-utils.ts Adds Anthropic constants and helpers (base URL/model normalization, headers, token detection).
src/onboarding.ts Adds Anthropic onboarding flow and integrates Anthropic into auth-method selection and credential checks.
src/infra/worker-signature.ts Includes providerMethod in worker signature normalization/hashing inputs.
src/infra/host-runner.ts Forces HYBRIDCLAW_AGENT_SANDBOX_MODE=host for host processes and passes providerMethod.
src/infra/container-runner.ts Sets HYBRIDCLAW_AGENT_SANDBOX_MODE=container for containers and passes providerMethod.
src/doctor/provider-probes.ts Adds Anthropic probe (CLI version check for claude-cli; HTTP models probe for api-key).
src/doctor/checks/providers.ts Adds Anthropic to provider diagnostics and probes.
src/doctor/checks/credentials.ts Includes ANTHROPIC_API_KEY when evaluating env-only secret presence.
src/config/runtime-config.ts Adds anthropic section to runtime config schema/defaults and bumps config version.
src/config/config.ts Exposes Anthropic config/env exports and includes Anthropic models in configured model list.
src/cli/help.ts Updates help text and adds Anthropic auth usage documentation.
src/cli/auth-command.ts Implements auth login/status/logout anthropic and associated argument parsing.
src/cli.ts Adds Anthropic-specific missing-env-var messaging.
src/auth/anthropic-auth.ts New Anthropic auth module (API key + Claude CLI credential discovery).
container/src/types.ts Extends container task model policy and input types to include Anthropic + providerMethod.
container/src/tools.ts Threads providerMethod into model context setup.
container/src/providers/shared.ts Updates call-arg normalization to include providerMethod in the raw argument layout.
container/src/providers/router.ts Routes Anthropic provider calls to the new Anthropic provider implementation.
container/src/providers/provider-ids.ts Adds anthropic to container runtime provider IDs.
container/src/providers/auxiliary.ts Propagates providerMethod through auxiliary task routing in the container.
container/src/providers/anthropic.ts New Anthropic provider implementation (Messages API + SSE streaming + claude -p).
container/src/index.ts Includes providerMethod in task model comparisons and request processing plumbing.
container/src/browser-tools.ts Extends browser model context to include providerMethod.
container/shared/provider-context.js Allows skipping API key requirement for Anthropic when providerMethod=claude-cli.
container/shared/provider-context.d.ts Adds providerMethod to provider-context validation params typing.
config.example.json Documents new anthropic runtime config section defaults.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

...auth.headers,
};
if (auth.method === 'api-key') {
headers['x-api-key'] = auth.apiKey;
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probeAnthropic() always sets x-api-key when requireAnthropicApiKey() succeeds, even if the stored value is an OAuth-style token (e.g. sk-ant-oat...). For OAuth tokens Anthropic expects Authorization: Bearer <token> instead; using x-api-key will make the doctor probe fail even when the credential is valid.

Adjust the header construction to choose Authorization vs x-api-key based on the token format (or return an explicit authHeader from requireAnthropicApiKey()).

Suggested change
headers['x-api-key'] = auth.apiKey;
const isOAuthToken = auth.apiKey.startsWith('sk-ant-oat');
if (isOAuthToken) {
headers.Authorization = `Bearer ${auth.apiKey}`;
delete headers['x-api-key'];
} else {
headers['x-api-key'] = auth.apiKey;
delete headers.Authorization;
}

Copilot uses AI. Check for mistakes.
Comment on lines +861 to +867
console.log(`Configured method: ${config.anthropic.method}`);
if (status.method) {
console.log(`Detected auth source: ${status.method}`);
}
if (status.source) {
console.log(`Source: ${status.source}`);
}
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The line console.log(Detected auth source: ${status.method}) prints the method (api-key/claude-cli) under a label that says "source". This is misleading given you also print Source: ${status.source} right below.

Consider either changing this label to something like "Detected auth method" (keeping status.method), or change the value printed here to status.source and keep a separate method-specific label elsewhere.

Copilot uses AI. Check for mistakes.
const anthropicConfiguredMethod = runtimeConfig.anthropic.method;
const anthropicReady =
anthropicConfiguredMethod === 'claude-cli'
? anthropicStatus.method === 'claude-cli'
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

anthropicReady is determined only by comparing anthropicStatus.method to the configured method. For claude-cli, getAnthropicAuthStatus() sets method: 'claude-cli' as long as any Claude credential is present, even when authenticated is false (e.g. token expired).

This can cause onboarding to treat expired Claude CLI credentials as “ready” and skip prompting / erroring, only to fail later when requireAnthropicClaudeCliCredential() is called. Consider incorporating anthropicStatus.authenticated (and/or an expiresAt > Date.now() check) into the readiness calculation for claude-cli.

Suggested change
? anthropicStatus.method === 'claude-cli'
? anthropicStatus.method === 'claude-cli' &&
anthropicStatus.authenticated === true &&
(anthropicStatus.expiresAt == null || anthropicStatus.expiresAt > Date.now())

Copilot uses AI. Check for mistakes.
refreshedRuntimeConfig.anthropic.method;
const refreshedAnthropicReady =
refreshedAnthropicConfiguredMethod === 'claude-cli'
? refreshedAnthropicStatus.method === 'claude-cli'
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

refreshedAnthropicReady is determined only by comparing refreshedAnthropicStatus.method to the configured method. For claude-cli, getAnthropicAuthStatus() reports method: 'claude-cli' whenever a Claude credential is present, even if it’s expired (authenticated: false).

This can allow onboarding to proceed as if Anthropic is ready after refresh, but then fail later when requireAnthropicClaudeCliCredential() is invoked. Consider incorporating refreshedAnthropicStatus.authenticated (or an explicit expiry check) into the readiness check for claude-cli.

Suggested change
? refreshedAnthropicStatus.method === 'claude-cli'
? refreshedAnthropicStatus.method === 'claude-cli' &&
refreshedAnthropicStatus.authenticated

Copilot uses AI. Check for mistakes.
Comment on lines +12 to +14
const originalSandboxMode = process.env.HYBRIDCLAW_AGENT_SANDBOX_MODE;
process.env.HYBRIDCLAW_AGENT_SANDBOX_MODE = 'host';
const spawnMock = vi.fn(() => {
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test mutates process.env.HYBRIDCLAW_AGENT_SANDBOX_MODE but restores it only at the end of the test body. If an assertion throws before reaching the restore block, the env var can leak into subsequent tests.

Wrap the body in a try/finally (like the other tests in this file) to guarantee restoration even on failure.

Copilot uses AI. Check for mistakes.
@furukama furukama force-pushed the codex/add-anthropic-provider branch from 879acd5 to a6c8ecd Compare April 8, 2026 14:28
@furukama furukama force-pushed the codex/add-anthropic-provider branch from a6c8ecd to e12ad81 Compare April 10, 2026 07:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants