PR #12 shipped oauthToken support with optimistic framing, but testing
revealed @anthropic-ai/claude-agent-sdk has a macOS-specific bug: the
SDK isolates CLAUDE_CONFIG_DIR per invocation and tries to copy
~/.claude/.credentials.json, which doesn't exist on macOS (creds live
in the Keychain). The OAuth token gets misclassified as an API key
and requests are billed against API credits instead of the
subscription — subscribers without API credits see "Credit balance is
too low" errors.
Direct `claude -p` works fine, so the upstream SDK is the broken layer.
Changes:
- README Claude Code section: lead with the macOS status note and
point subscribers at ClaudeProvider + ANTHROPIC_API_KEY as the
current workaround.
- JSDoc on ClaudeCodeProvider: same status note.
- billing_error message: explain the bug and recommend the workaround
so users can self-diagnose from the error alone.
No code removal — when the SDK fix lands upstream, oauthToken users
will Just Work without any code changes here.
`claude login` alone does not authorize SDK / non-interactive
(`claude -p`) usage. Anthropic gates that behind a separate long-lived
token minted by `claude setup-token`. Without it, subscription users
hit `billing_error` from the SDK even though interactive Claude Code
works fine.
Changes:
- ClaudeCodeConfig.oauthToken: new optional field. When set, the
provider exports it as CLAUDE_CODE_OAUTH_TOKEN before invoking the
SDK, which makes the SDK bill against the user's Pro/Max
subscription instead of API credits.
- apiKey continues to work for users with a console API key. If both
are present, oauthToken takes precedence.
- billing_error / authentication_failed messages now point users at
`claude setup-token` so the fix is obvious from the error alone.
- README: rewrite the Claude Code section to document both modes and
the `claude setup-token` step explicitly.
- examples/claude-code.ts: read CLAUDE_CODE_OAUTH_TOKEN from env so
the smoke test actually works for subscribers.
Minor release adding the ClaudeCodeProvider for Claude Pro/Max
subscription users (PR #10) along with a rewritten README (PR #10)
and a repaired test suite (PR #11).
New public exports:
- ClaudeCodeProvider, ClaudeCodeConfig
- createClaudeCodeProvider
- 'claude-code' added to PROVIDER_REGISTRY and SUPPORTED_PROVIDERS
No breaking changes.
The R1-R3 refactors removed the per-provider handle*Error methods,
consolidated validators into validateNumberInRange, renamed Gemini's
buildGenerationConfig to buildConfig, and split OpenWebUI's message
conversion and response formatting into openwebui-strategies.ts.
44 of 91 existing tests broke as a result.
Changes:
claude.test.ts (8 -> 13 passing)
- Switch error mapping tests from handleAnthropicError to the base
normalizeError method.
- Update temperature error-message expectation to match the new
validateNumberInRange wording.
openai.test.ts (16 -> 22 passing)
- Same handleOpenAIError -> normalizeError switch.
- Add a regression test for the 429+"quota" path that goes through
mapProviderError instead of the base mapping.
gemini.test.ts (17 -> 27 passing)
- Update getInfo expectations to the new default model (gemini-2.5)
and provider version 2.0.0.
- Switch to normalizeError.
- Rename buildGenerationConfig -> buildConfig (renamed during R2).
- Update formatCompletionResponse mocks: the @google/genai SDK
exposes response.text as a getter rather than walking
candidates[0].content.parts.
openwebui.test.ts (4 -> 19 passing)
- Drop tests for convertMessages / convertMessagesToPrompt /
formatChatResponse / formatOllamaResponse / makeRequest: these
moved into openwebui-strategies.ts and openwebui-http.ts during R3
and warrant their own test files.
- Add error-mapping tests covering the new mapProviderError +
providerErrorMessages plumbing.
claude-code.test.ts (new, 18 tests)
- Constructor accepts subscription mode (empty config).
- getInfo advertises subscriptionAuth + requiresLocalCli.
- mapProviderError catches ENOENT and "not logged in".
- errorForSdkAssistantError covers each documented SDK error code.
- buildPrompt handles single-turn pass-through, multi-system
combination, multi-turn flattening with role labels, and rejects
empty turn lists.
Full suite: 100 pass / 0 fail across 5 files.
Full rewrite focused on what consumers actually need: install, a
working snippet per provider, and the public API surface.
Changes vs the previous version:
- Document ClaudeCodeProvider, including the subscription-via-CLI
auth path that the new provider enables.
- Remove the ProviderRegistry section (the class was removed in R1).
- Drop the stale Provider Comparison and Detailed Capabilities
tables; vendor capabilities and model lists move too fast for a
README to track.
- Remove the inaccurate Zero Dependencies and Comprehensive Testing
claims (post-refactor 44/91 tests need updating).
- Refresh default models (Gemini 1.5 -> 2.5) and the package list to
match the current build.
- Add a brief Architecture note covering the base hooks introduced
in R2 and the OpenWebUI strategy split from R3.
README is 315 lines (was 701).
Wraps @anthropic-ai/claude-agent-sdk so Claude Pro/Max subscribers (who
do not have a console API key) can use this library by authenticating
through the local `claude` CLI. Subscribers run `claude login` once and
the provider picks up the credentials automatically.
Provider behavior:
- query() is invoked in single-turn mode (maxTurns: 1, allowedTools: [])
to behave as plain text completion by default. Both knobs are
configurable for agentic use cases.
- Conversation history is flattened to a single prompt with role labels.
- doStream() emits text deltas as the SDK yields successive assistant
messages. doComplete() accumulates and returns the final result.
- SDKAssistantMessageError codes (authentication_failed, billing_error,
rate_limit, model_not_found, ...) map to AIErrorType variants.
- ClaudeCodeConfig.apiKey is optional — when omitted the SDK falls back
to ANTHROPIC_API_KEY or local subscription credentials. The base
validator is overridden to allow this.
Tradeoffs:
- Requires the `claude` CLI installed and logged in on the host. This
is not suitable for typical server-side production deployments; it is
the official path for subscription accounts.
- Higher latency (CLI process spawn) than the direct Anthropic API.
- The agent SDK is heavy. Bundle grows ~750KB; bun build now uses
--target node so the SDK's Node built-in imports resolve correctly.
Wired into PROVIDER_REGISTRY ('claude-code'), createClaudeCodeProvider,
SUPPORTED_PROVIDERS, and re-exported from the package entry.
Patch release covering three internal refactors (see PRs #7, #8, #9):
- Extract shared constants, parameterize validators, simplify factory
- Pull validateConnection / validateModelName / error mapping into base
- Split OpenWebUI into strategy classes by backend
Public API surface and method signatures are unchanged. A handful of
error message strings were reworded (error type and status code remain
identical), and BaseAIProvider gained new protected hooks with no-op
defaults.
Replace Conditional with Polymorphism from the Refactoring Guru catalog.
The OpenWebUIProvider previously contained two parallel implementations
(completeWithChat / completeWithOllama, streamWithChat / streamWithOllama,
plus branching in validateConnection) selected by a `useOllamaProxy`
boolean. The 987-line file is split into:
- openwebui-types.ts wire-format response types
- openwebui-http.ts shared HTTP client (auth, timeout)
- openwebui-strategies.ts OpenWebUIStrategy interface plus
OpenWebUIChatStrategy and
OpenWebUIOllamaStrategy implementations
- openwebui.ts thin provider that picks one strategy at
construction and delegates
OpenWebUIProvider and OpenWebUIConfig (including useOllamaProxy) stay
in their original locations, so consumer imports are unchanged.
Side effects:
- Error mapping now goes through the base mapProviderError / providerErrorMessages
hooks added in R2, removing handleOpenWebUIError and its duplicated status switch.
- providerInfo.version bumped to 2.0.0 to reflect the rewrite.
Form Template Method / Pull Up Method from the Refactoring Guru catalog.
The three SDK-based providers (Claude, OpenAI, Gemini) each had their own
near-identical implementations of:
- validateConnection: try a tiny request, catch errors, map a handful
of patterns, warn on the rest
- validateModelName: iterate regex patterns, warn on no match
- handle{X}Error: switch on HTTP status, map to AIProviderError with
brand-flavored messages
These collapse into three protected hooks on BaseAIProvider:
- getModelNamePatterns(): RegExp[]
- sendValidationProbe(): Promise<void>
- mapProviderError(error): AIProviderError | null
- providerErrorMessages(): Partial<Record<number, string>>
The base owns the wrapping logic (warning, status -> error-type map,
common message patterns). Providers only contribute their unique parts.
Side effects:
- normalizeError now consults mapProviderError before generic mapping,
so provider-specific patterns work via the existing try/catch in
BaseAIProvider.complete/stream. The per-provider try/catch wrappers
around doComplete/doStream are removed.
- The unknown-error message becomes `${providerName} error: ${msg}`
instead of the hardcoded `Provider error:` / `Claude API error:` etc.
- OpenWebUI's HTTP-based validateConnection is preserved (override),
since its non-SDK shape doesn't fit the SDK probe pattern.
Quick-win refactors from the Refactoring Guru catalog:
- Replace Magic Number with Symbolic Constant: extract DEFAULT_TIMEOUT_MS,
DEFAULT_MAX_RETRIES, DEFAULT_MAX_TOKENS, DEFAULT_TEMPERATURE, default
models per provider, and validation prompt into src/constants.ts.
- Parameterize Method: collapse validateTemperature / validateTopP /
validateMaxTokens (and the inline timeout/maxRetries checks) into a
single validateNumberInRange helper with bounds metadata.
- Inline Class / Remove Middle Man: drop the unused ProviderRegistry
class from utils/factory.ts. createProvider now dispatches through
the PROVIDER_REGISTRY const directly instead of a parallel switch.
Also fixes stale VERSION constant in src/index.ts (was 1.3.1).
Major release: @google/generative-ai → @google/genai migration changes
the type signature of GeminiConfig.safetySettings, which is a breaking
change for consumers using that option.
Other changes accumulated since 1.3.3:
- @anthropic-ai/sdk 0.65 → 0.97
- openai 4 → 6
- @types/node 24 → 25
- TypeScript 6 support (peer widened to ^5 || ^6)
- Default Gemini model: gemini-1.5-flash → gemini-2.5-flash
The @google/generative-ai SDK has been deprecated by Google. This switches
to the successor @google/genai package and rewrites the Gemini provider
against the new stateless models/chats API.
BREAKING CHANGE: GeminiConfig.safetySettings now uses the SafetySetting
type from @google/genai. Consumers passing this field must update their
import from '@google/generative-ai' to '@google/genai'. The shape of the
type is similar but not identical.
Notable simplifications enabled by the new SDK:
- No per-model client caching: generateContent specifies model per-call
- Single code path for both single- and multi-turn (full contents array
is passed to generateContent directly; no more startChat branching)
- Stream chunks expose .text as a property (was .text() method)
- Stream iteration is direct on the response (no .stream sub-property)
Default model bumped from gemini-1.5-flash to gemini-2.5-flash.
- Add typescript ^6.0.0 as devDependency for verified builds
- Widen peerDependency to ^5 || ^6 to keep TS 5 consumers supported
- tsconfig.build.json: set explicit rootDir (required by TS 6 when outDir is set)
- tsconfig.build.json: replace deprecated moduleResolution "node" with "bundler"
Patch release covering the dependency refresh from the previous commit.
Local version was at 1.3.1 while npm registry already had 1.3.2 published,
so this bumps to 1.3.3.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Also refreshes bun.lock to pick up @anthropic-ai/sdk 0.65.0 and
@types/node 24.12.4 within existing version ranges. All bumps stay
within the same major to avoid breaking changes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>