Partially addresses #112. The streaming reader in openaiStreamToAnthropic
had no error handling - if an error occurred during streaming, the reader
lock was never released. Wrapped the while loop in try/finally to ensure
reader.releaseLock() is always called.
Fixes#111. normalizeSchemaForOpenAI only processed the top-level
object schema, leaving nested objects untouched. OpenAI strict mode
rejects schemas where nested objects have properties not listed in
their required array, causing 400 errors on tools with nested params.
Now recurses into properties, items, and anyOf/oneOf/allOf combinators
(matching the pattern used by enforceStrictSchema in codexShim.ts).
Also adds additionalProperties: false to nested objects in strict mode.
Build verified passing.
Azure OpenAI API rejects the max_tokens parameter and requires
max_completion_tokens instead. This change ensures the conversion
is robust by validating that max_tokens is a positive number before
using it, preventing edge cases like null or "null" string values
from being incorrectly sent.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Two bugs in convertTools() caused Gemini's OpenAI-compatible endpoint
to reject tool schemas with 400 "schema requires unspecified property":
1. The Agent tool patch unconditionally pushed 'message' into required[]
even though 'message' is not a property of the Agent schema. Gemini
strictly validates that every key in required[] exists in properties.
2. normalizeSchemaForOpenAI() added all property keys to required[] for
OpenAI strict mode, but this conflicts with Gemini's stricter schema
validation which rejects required keys absent from properties.
Fix:
- Agent tool patch now only adds a key to required[] if it exists in
schema.properties (fixes the 'message' 400 error on Gemini)
- normalizeSchemaForOpenAI() accepts a strict flag: true for OpenAI
(promotes all property keys into required[]), false for Gemini
(filters required[] to only keys present in properties)
- convertTools() detects CLAUDE_CODE_USE_GEMINI and passes strict=false
Fixes#82
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Update the stale test expectation to match current behavior where
normalizeSchemaForOpenAI() promotes all properties into required[]
and marks the schema as strict: true.
Same fix as PR #72 — included here so PR #80 passes CI independently.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Azure OpenAI and newer OpenAI models (o1, o3, o4...) reject `max_tokens`
with a 400 error and require `max_completion_tokens` instead.
Maps `params.max_tokens` → `max_completion_tokens` in the request body,
which is the current standard across OpenAI-compatible providers.
This commit addresses strict schema validation limitations when running subagents under OpenAI backend shims.
- Drops empty properties from payloads (like Record<string, string>) that break OpenAI's Structured Outputs validation.
- Handles edge cases for automated initial teams when subagents bypass standard creation routines.
- Aborts sending unsupported experimental backend parameters like temperature and top_p for GPT-5 derivatives.
Some providers send an empty string as the first delta to signal
streaming start. The falsy check `if (delta.content)` treated "" as
absent, skipping content_block_start. The next delta with actual
content was emitted without it, violating the Anthropic protocol.
Changed to `delta.content != null` to distinguish between absent field
and empty string.
Relates to #42
Co-Authored-By: Juan Camilo <juancamilo.auriti@gmail.com>
OpenAI and Codex enforce strict JSON Schema validation — every key in
`properties` must also appear in `required`. Anthropic schemas often
mark fields as optional (omitted from `required`), which causes 400
errors on OpenAI/Codex endpoints.
Example: the Agent tool has `subagent_type` in `properties` but not
in `required`, producing:
"Invalid schema for function 'Agent': Missing 'subagent_type'
in required array"
Fix: add `normalizeSchemaForOpenAI()` in `convertTools()` that ensures
`required` is a superset of all `properties` keys before the schema is
sent to the API. Existing `required` entries are preserved; missing
ones are appended. Schemas without `properties` pass through unchanged.
Fixes#46.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The Anthropic-to-OpenAI tool_choice mapping handled 'auto', 'any', and
'tool' but not 'none'. When 'none' was passed, the request was sent
without tool_choice, defaulting to 'auto' — the opposite of the
intended behavior (disable tool use).
Relates to #30
Co-Authored-By: Juan Camilo <juancamilo.auriti@gmail.com>
When certain OpenAI-compatible APIs (LM Studio, some proxies) send
multiple stream chunks with finish_reason set, the finish block ran
multiple times — emitting content_block_stop and message_delta for
each one. Each content_block_stop caused claude.ts to create and yield
a new assistant message, making every response appear twice in the UI.
Fix: add hasProcessedFinishReason flag (same pattern as the existing
hasEmittedFinalUsage flag) so the finish block only executes once per
response regardless of how many chunks contain finish_reason.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds Google Gemini as a first-class provider using Gemini's OpenAI-compatible
endpoint, supporting gemini-2.0-flash, gemini-2.5-pro, and gemini-2.0-flash-lite
across all three model tiers (opus/sonnet/haiku).
- Add 'gemini' to APIProvider type with CLAUDE_CODE_USE_GEMINI env detection
- Map all 11 model configs to appropriate Gemini models per tier
- Route Gemini through existing OpenAI shim (generativelanguage.googleapis.com)
- Support GEMINI_API_KEY and GOOGLE_API_KEY for authentication
- Fix model display name to show actual Gemini model instead of Claude fallback
- Add Gemini support to provider-launch, provider-bootstrap, system-check scripts
- Add dev:gemini npm script for local development
Bootstrap: bun run profile:init -- --provider gemini --api-key <key>
Launch: bun run dev:gemini
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a new 'openai' API provider that translates Anthropic SDK calls to
OpenAI chat completions format, enabling Claude Code's full tool system
(bash, file read/write/edit, grep, glob, agents) with any OpenAI-compatible
model: GPT-4o, DeepSeek, Gemini, Llama, Ollama, OpenRouter, and 200+ more.
Set CLAUDE_CODE_USE_OPENAI=1, OPENAI_API_KEY, and OPENAI_MODEL to use.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Squash the current repository state back into one baseline commit while
preserving the README reframing and repository contents.
Constraint: User explicitly requested a single squashed commit with subject "asdf"
Confidence: high
Scope-risk: broad
Reversibility: clean
Directive: This commit intentionally rewrites published history; coordinate before future force-pushes
Tested: git status clean; local history rewritten to one commit; force-pushed main to origin and instructkr
Not-tested: Fresh clone verification after push