Files
oh-my-pi/packages/ai/CHANGELOG.md
T
2026-06-08 19:11:24 +02:00

239 KiB
Raw Blame History

Changelog

[Unreleased]

[15.10.5] - 2026-06-08

Breaking Changes

  • Renamed the OAuth subpath export @oh-my-pi/pi-ai/utils/oauth → @oh-my-pi/pi-ai/oauth (and @oh-my-pi/pi-ai/utils/oauth/* → @oh-my-pi/pi-ai/oauth/*, e.g. oauth/types, oauth/callback-server, oauth/openai-codex) after relocating the OAuth implementation out of utils/oauth/ into registry/oauth/. The high-level OAuth API (getOAuthProviders, refreshOAuthToken, getOAuthApiKey, registerOAuthProvider, unregisterOAuthProviders, getOAuthProvider) and the OAuth* types stay exported from the package root, unchanged.

Changed

  • Changed Anthropic retry handling to avoid retrying 4xx responses other than 408 and 429
  • Optimized the Anthropic cch attestation patch to locate the billing-header placeholder with native Buffer.indexOf (memmem) instead of a hand-rolled byte loop. The marker sits ~99% through the body (messages serializes before system), so the old scan walked almost the entire payload; output bytes are unchanged but the patch is ~7.5x faster (563µs -> 75µs on a 1MB body).
  • Refactored provider configuration to a single-source registry (registry/, renamed from provider-registry/ with its providers/ subdir flattened up). The KnownProvider/OAuthProvider type unions, PROVIDER_DESCRIPTORS, DEFAULT_MODEL_PER_PROVIDER, the serviceProviderMap env-key fallbacks, the /login provider list (builtInOAuthProviders), and the refreshOAuthToken/AuthStorage.login dispatch are all derived from it. Provider defs live directly under registry/; thin provider-specific login flows are inlined into the def file, while heavier provider-local OAuth flows and the shared OAuth flow infra (callback-server, pkce, google-oauth-shared, types, runtime index) now live together under registry/oauth/ (previously split across provider-registry/providers/oauth/ and utils/oauth/). The non-OAuth API-key paste/validation helpers (api-key-login, api-key-validation) sit beside the defs in registry/. Adding a provider that reuses an existing wire API is now one new provider def plus one registry entry in the common case. Exposes PROVIDER_REGISTRY, getProviderDefinition, ProviderDefinition, and PASTE_CODE_LOGIN_PROVIDERS.

Fixed

  • Disabled OpenAI Codex Responses stream obfuscation by sending stream_options.include_obfuscation=false, reducing raw WebSocket/SSE debug noise and bandwidth.
  • Interrupted OpenAI Codex Responses streams that emit long runs of whitespace-only tool-call argument deltas, preventing degenerate WebSocket/SSE responses from filling the raw stream buffer indefinitely.
  • Preserved streaming responses when Anthropic emits unrecognized content_block envelopes by ignoring unknown blocks and continuing to emit known content
  • Applied cache control to the most recent tool result block when building Anthropic OAuth payloads without a preceding text block, enabling ephemeral caching for tool-result-only messages
  • Kept Anthropic sampling parameters (temperature, top_p, top_k) when thinking is explicitly disabled
  • Fixed raw Anthropic SSE handling by parsing event frames with strict JSON parsing and matching event-type validation, surfacing malformed frames as stream errors instead of repairing them
  • Fixed Anthropic stream envelope handling to reject duplicate content_block_start indexes and block deltas/stops for unopened blocks, preventing malformed envelope states from producing partial output
  • Fixed Anthropic image conversion to normalize image/jpg to image/jpeg and emit a placeholder for unsupported image MIME types
  • Fixed Anthropic thinking request preparation by clamping max_tokens to provider/model limits and adjusting thinking budgets to a valid value
  • Fixed Anthropic request shaping around forced tool choice, unsigned thinking replay, prompt-cache marker placement, non-Anthropic bearer gateways, Foundry TLS loading, and strict tool-schema normalization so malformed or incompatible request payloads are rejected locally or shaped consistently before streaming
  • Fixed the Anthropic stream parser shipping a truncated tool call as a completed turn. When a transport drop cut the SSE stream mid-tool_use and a transparent reconnect spliced a fresh message envelope onto the same stream, the duplicate message_start was deduped but the orphaned tool block — which never received its content_block_stop — survived in the assistant message with its seed {} (or partially-parsed) arguments. The terminal stop signal from the reconnect then let it flow through as a normal tool call, so e.g. a read dispatched with {} failed downstream validation (path: expected string, received undefined). The parser now treats any tool block left open at stream end as a truncated envelope and routes it through the existing retry/error path instead of emitting bogus arguments.
  • Fixed the Zhipu Coding Plan login prompt advertising a misleading sk-... placeholder. Zhipu API keys are formatted <id>.<secret> (no sk- prefix), so the placeholder now matches the actual format instead of suggesting the wrong shape. (#2106)
  • Fixed Moonshot kimi-k2.6 (and any future kimi-k2.x) discovered via MOONSHOT_API_KEY stalling on first turn with no output. The moonshotModelManagerOptions discovery mapper only marked ids containing "thinking" as reasoning: true, so dynamic kimi-k2.6 entries fell through with reasoning: false; the openai-completions z.ai branch was then skipped and the request reached Moonshot with no thinking parameter at all. Moonshot K2.6 requires an explicit thinking: {type} field (the same native-API wire shape #1838 introduced thinking.keep for), so the server held the stream silently. The mapper now stamps reasoning: true, vision input, and default thinking metadata on every kimi-k2.x id, restoring the explicit thinking: {type: "disabled"|"enabled"} wire body the Moonshot endpoint expects. (#2113)

[15.10.4] - 2026-06-08

Added

  • Added anthropic-client-platform (desktop_app) and anthropic-client-version (1.11187.4) headers to the Anthropic request fingerprint for OAuth sessions

Changed

  • Changed non-built-in tool names sent to Anthropic from proxy_ prefixing to _ prefixing (for example bash to _bash) while built-in tool names remain unchanged
  • Updated the Anthropic OAuth stealth fingerprint to track Claude Code 2.1.165: claudeCodeVersion bumped to 2.1.165 (flows into both the cc_version billing header and the claude-cli/<version> user-agent), claudeCodeSystemInstruction changed to "You are a Claude agent, built on Anthropic's Claude Agent SDK.", and the billing-header cc_entrypoint changed from cli to local-agent.
  • Clamped the Anthropic request max_tokens to Math.min(CLAUDE_CODE_MAX_OUTPUT_TOKENS, options.maxTokens || model.maxTokens) (64k) so OAuth requests match Claude Code's requested output cap instead of sending the model's full ceiling (e.g. 128k for Opus 4.8).

[15.10.3] - 2026-06-08

Removed

  • Removed the synthetic <turn-aborted> developer guidance note that transformMessages injected after an aborted/errored assistant turn (and its turn-aborted-guidance.md prompt). The per-call synthetic "aborted" tool results already tell the model the turn's tools were terminated, so the extra "verify current state before retrying" note was redundant — and it biased the model toward second-guessing a deliberate user interrupt when the turn was resumed.
  • Removed the legacy Anthropic first-user-message skip for <system-reminder> blocks now that synthetic reminders no longer travel as user messages.

[15.10.2] - 2026-06-08

Added

  • Added support for impersonated_service_account Application Default Credentials (ADC) in Vertex AI to enable chained impersonation without failing via 401 invalid_client.
  • Added AuthStorage.getCredentialOrigin(provider) (returning a structured CredentialOrigin / CredentialOriginKind) and getEnvApiKeyName(provider), so callers can render where a provider's auth comes from — runtime override, config, stored OAuth/api-key, env var (with the backing variable name), or fallback resolver — without parsing the prose of describeCredentialSource.

Changed

  • Changed onSseEvent recording for OpenAI Responses, Azure OpenAI Responses, OpenAI Completions, and Anthropic stream providers to emit reconstructed SSE events from decoded SDK stream items instead of wrapping raw fetch responses
  • Changed OpenAI Completions SSE diagnostics to include event: "chat.completion.chunk" in onSseEvent records for chunked responses
  • Changed the default Anthropic model in DEFAULT_MODEL_PER_PROVIDER from claude-sonnet-4-6 to claude-opus-4-6, so sessions that fall back to the provider default (no configured default role, no --model, no restored session) now start on Claude Opus 4.6.

Fixed

  • Fixed duplicate upstream tool_call_id values collapsing distinct tool calls during message transformation, preserving one call/result pairing per emitted tool call before provider replay and keeping generated duplicate IDs distinct after OpenAI/Mistral wire-length caps. (#2055)
  • Fixed the Anthropic provider retrying persistent account usage/quota limits (e.g. 429 "This request would exceed your account's rate limit", usage_limit_reached) as if they were transient. Because the error text contains "rate limit", isProviderRetryableError matched it and the stream retry loop looped through its 2s/4s/8s backoff (then the streamSimple a/b/c policy re-minted the credential and ran the whole thing again) before surfacing the failure — even though the server's retry-after parked the account for minutes-to-hours. These errors are now recognized via isUsageLimitError and surfaced immediately to the credential-rotation layer, so e.g. omp dry-balance --bench reports a rate-limited account as failed at once instead of appearing to hang.
  • Fixed MiniMax-compatible OpenAI-completions hosts losing tool-call argument content when function.arguments is streamed as an object across more than one delta. The accumulator added in #1776 wrote block.partialArgs = rawArgs per chunk, so every chunk but the last was overwritten — for an edit call this surfaced as a tail-slice of the patch text being applied (e.g. a single-line replace 91..91: body extending the deletion across the surrounding rows). Chunks are now shallow-merged; for shared string keys, startsWith distinguishes cumulative restatements (take the latest) from per-chunk-delta fragments (concatenate). Per-chunk toolcall_delta emission for the object branch is suppressed (the previous code emitted JSON.stringify(rawArgs) per chunk, which fed downstream concat consumers — packages/agent/src/proxy.ts, openai-chat-server, openai-responses-server, anthropic-messages-server — an invalid sequence like {"input":"a"}{"input":"b"}); the merged object is flushed instead as a single concat-safe delta in finishToolCallBlock before toolcall_end, so accumulators reconstruct the args correctly. The single-chunk shape covered by the existing #1776 regression test stays correct end-to-end. (#2080)
  • Fixed the OpenAI Responses compatibility server misrouting late toolcall_delta events for earlier parallel tool calls after a later toolcall_start. The encoder now keeps OpenFunctionCall state by content index, allocates output indexes at item start, and closes each tool item by its own toolcall_end, preserving deferred MiniMax object-argument flushes for the matching call. (#2080)

[15.10.1] - 2026-06-07

Breaking Changes

  • Removed the onAuthError option from stream request options and shifted auth retry handling to resolver-based apiKey behavior, requiring callers using custom auth-retry hooks to migrate

Added

  • Added ApiKeyResolver and ApiKey auth helpers, including isApiKeyResolver, isAuthRetryableError, resolveApiKeyOnce, and withAuth, and exported them from the package root
  • Added support for a function-valued apiKey in SimpleStreamOptions so a single stream request can refresh or rotate credentials during retry
  • Added forceRefresh credential option to AuthStorage.getApiKey and rotateSessionCredential support for session-level credential rotation after auth failures
  • Added AuthStorage.resolver(provider, options) method that builds an ApiKeyResolver implementing the a/b/c auth-retry policy directly on the storage instance

Changed

  • Changed gateway and stream auth flows to share the a/b/c retry policy, refreshing the same session credential first and then switching to a sibling credential on repeated auth failures

Fixed

  • Fixed streaming auth retries to handle 401 and usage-limit errors before replay-unsafe content is emitted, including failures surfaced only via errorStatus
  • Fixed tool argument validation to coerce singleton non-string values into arrays when the schema expects an array, preventing Anthropic-compatible models that emit todo.ops as an object from getting stuck in repeated validation-error loops. (#2026)
  • Fixed streaming retries to buffer and suppress partial start events from failed auth attempts so only clean retried events are delivered
  • Fixed the HTTP 400 raw-request dumper (appendRawHttpRequestDumpFor400) littering the real ~/.omp/logs/http-400-requests directory during tests. Provider suites exercise the 400 error path with mocked fetch responses, which the dumper could not distinguish from genuine failures; it now skips persistence under the Bun test runner (isBunTestRuntime()).
  • Fixed Anthropic Opus requests unnecessarily forcing tool_choice.disable_parallel_tool_use, allowing Claude Opus to use the provider's default parallel tool-calling behavior again.
  • Fixed parallel function_call items losing arguments against llama.cpp's OpenAI Responses endpoint (/v1/responses), where every call but the last finalized with {} and the agent rejected them with path: Invalid input: expected string, received undefined. llama.cpp's to_json_oaicompat_resp emits output_item.added with only item.call_id (no item.id, no output_index) while the matching function_call_arguments.delta carries item_id: "fc_<call_id>". processResponsesStream now registers function-call and custom-tool-call items under item.call_id as a secondary lookup key (alongside item.id/output_index) so identifier-deviant hosts route deltas and done events to the right block. (#2015)
  • Fixed PI_REQ_DEBUG response recording truncating the captured body when a streamed response was cancelled mid-flight. The response tee in wrapResponse could call FileRequestDebugResponseLog.close() from both the cancel callback and the resumed pull (which observes done once the source reader is cancelled); the second caller saw the handle already nulled and returned before the first caller's pending write flushed, so the .res.log lost the already-buffered chunk. close() now memoizes its flush-and-close promise so every caller awaits the same completion.

[15.10.0] - 2026-06-06

Added

  • Added a dependency-free @oh-my-pi/pi-ai/effort module exporting the Effort enum and THINKING_EFFORTS, split out of model-thinking so hot-path consumers can import the thinking levels without pulling in model-thinking and its provider-compat dependency graph. The package barrel still re-exports both names, so existing imports are unaffected.

Fixed

  • Fixed Antigravity usage provider emitting one bar per model instead of deduplicating by tier — a single account's 15+ model entries now collapse to one bar per tier, matching the shared-quota reality of the upstream API.

  • Fixed Antigravity usage reports missing email and accountId in metadata, so the /usage display and the deduplicator can associate reports with their credentials.

  • Fixed usage-report dedup ignoring projectId for Google Cloud providers, preventing duplicate credential entries from being recognized as the same account.

  • Fixed Cloud Code Assist (Antigravity / Gemini CLI) rejecting the github tool with HTTP 400 when the pr parameter schema contained anyOf: [string, array]. The CCA mixed-type combiner collapse picked the first non-null type (string) but indiscriminately copied type-specific keys from variant branches — items from the array variant leaked onto the string-typed result, producing {type: "string", items: {...}} which Google's API rejects as invalid. The collapse now filters merged variant fields against the winning type's allowed key set. (#2002)

  • Fixed OpenAI Responses-family providers (Codex, OpenAI Responses, Azure Responses) rejecting requests with 400 No tool output found for function call … after the user branched/navigated the session tree to a node that ends on a tool call (the tool-result child is dropped from the reconstructed history) or after a turn was aborted/crashed between the call streaming and its result persisting. The converters now synthesize a placeholder function_call_output/custom_tool_call_output immediately after any unpaired function_call/custom_tool_call, symmetric to the existing orphan-output repair, so the model still sees the call and can recover instead of the whole request 400ing.

  • Fixed Anthropic-compatible reasoning endpoints losing prior-turn reasoning on continuation requests when they emit unsigned thinking blocks. convertAnthropicMessages treated unknown endpoints as signature-enforcing and demoted unsigned reasoning to type: "text", which destabilized tool-call argument serialization on the next turn — the upstream symptom behind the args?.ops?.map is not a function crash reported against the todo tool. Official api.anthropic.com keeps the conservative text fallback; non-official anthropic-messages reasoning models now replay unsigned reasoning as native type: "thinking" (#2005).

[15.9.67] - 2026-06-06

Fixed

  • Fixed llama.cpp/OpenAI Responses parallel tool calls losing arguments when function_call_arguments.done events omit output_index and item_id, by routing those identifierless final-argument events through the open function calls in item order. (#1970)
  • Fixed local Ollama (openai-responses) turns failing with HTTP 400 invalid reasoning value: "minimal" when a discovered model ran with minimal (or xhigh) thinking. Ollama's OpenAI-compatible reasoning.effort only accepts high|medium|low|max|none, so discovered reasoning-capable Ollama models now carry a compat.reasoningEffortMap remapping minimal → low and xhigh → max; non-reasoning models are left untouched.

[15.9.2] - 2026-06-05

Added

  • Added an AES-256-GCM auth-broker snapshot cache module and RemoteAuthCredentialStoreOptions.onSnapshot so broker clients can persist broker-sourced full snapshots without blocking startup on every run.
  • Added Model.omitMaxOutputTokens so providers (notably Ollama proxies fronting cloud catalogs) can suppress max_output_tokens (Responses) and max_tokens/max_completion_tokens (Completions) on the wire while still using the catalog maxTokens for local budgeting. Without it, applyCommonResponsesSamplingParams unconditionally sent the catalog cap and HTTP-400'd against upstream APIs whose true output limit was unknown to OMP. (#1881)

Changed

  • Changed usage-ranked OAuth credential selection to pick deterministic session-sticky weighted buckets instead of always choosing the top-ranked account, capping the best account at 2x the baseline session likelihood while keeping equal-priority accounts evenly balanced.

Fixed

  • Fixed parallel function_call items on the OpenAI Responses API losing arguments on every call except the last when the upstream server interleaves their stream events (observed against llama.cpp and other local Responses-compat hosts). processResponsesStream no longer routes function_call_arguments.{delta,done}, output_item.done, content_part/text/refusal/reasoning events through a singleton currentItem/currentBlock reference; it now tracks every open item in registries keyed by output_index and item_id so each event is folded into the matching block and the emitted toolcall_end carries the correct contentIndex. (#1880)

[15.9.1] - 2026-06-04

Added

  • Added regional Xiaomi Token Plan login/provider entries (xiaomi-token-plan-sgp, xiaomi-token-plan-ams, xiaomi-token-plan-cn) so omp login can store token-plan keys against the selected region. (#1846)

Fixed

  • Removed the context-1m-2025-08-07 (1M long-context) beta from the Anthropic agent request headers, the OAuth model-discovery header, and the Claude usage-API header. Sending it caused subscription/OAuth requests without long-context credits to fail with 429 Usage credits are required for long context requests, breaking Sonnet. The remaining betas are unchanged.
  • Fixed Kimi K2.x maxTokens on Fireworks and Fire Pass (fireworks/kimi-k2.5, fireworks/kimi-k2.6, firepass/kimi-k2.6-turbo) being inherited from Fireworks /v1/models discovery (max_completion_tokens: 65536) rather than the published Kimi-on-Fireworks output budget, which let callers (and the openai-completions default-injection safety net) ship a budget the router cannot honor and made runaway reasoning traces more likely. The Fireworks resolver now clamps every Kimi K2.x id (public catalog ids and the canonical accounts/fireworks/{models,routers}/kimi-k2… wire form) to 32,768 output tokens, and the generator applies the same cap as a post-processing safety net so the firepass static fallback and the bundled fireworks entries stay in sync across regens. (#1849)
  • Fixed Xiaomi Token Plan MiMo OpenAI-compatible tool-call continuations omitting required reasoning_content replay. (#1846)
  • Fixed Anthropic prompt caching for OpenAI-compatible Claude proxies by honoring compat.cacheControlFormat: "anthropic" outside OpenRouter. (#1845)
  • Fixed Moonshot Kimi K2.6 silently pausing for many seconds between tool calls because the server discarded the reasoning_content that omp was already sending with every assistant tool-call replay. The K2.6 thinking parameter takes an extra keep field whose default (null) ignores historical reasoning, so K2.6 had to re-derive its full chain-of-thought from the user prompt on every iteration of the agent loop. The Moonshot direct (api.moonshot.ai) and Kimi Code (api.kimi.com) wire bodies now send thinking: { type: "enabled", keep: "all" } for kimi-k2.6 requests with reasoning enabled, matching Moonshot's documented best practice for multi-step tool-calling agents. The flag is gated on the K2.6 id and the two native hosts because earlier Moonshot models (K2.5 and below) 400 on the unknown field and every Kimi gateway (OpenRouter, OpenCode, Kilo, Fireworks, …) speaks its own thinking shape. (#1838)
  • Fixed Alibaba DashScope (Bailian) compatible-mode endpoint 400 InternalError.Algo.InvalidParameter: The provided messages input is invalid. The error info is [Unexpected item type in content.] when a screenshot or other image-producing tool result was folded into a known text-only Qwen turn (e.g. qwen3.7-max, qwen-max, qwen3-coder-*) hosted at dashscope.aliyuncs.com/compatible-mode/v1. convertMessages in openai-completions no longer forwards image_url content parts for those text-only id families even when a misconfigured custom provider claims input: ["text", "image"]; multimodal compatible-mode ids such as qwen3.7-plus and qwen-vl-max still rely on the catalog input field. The tool-result branch and the user-content branch both fall back to the standard [image omitted: model does not support vision] placeholder for text-only ids so the model still sees the attachment intent. (#1859)

[15.9.0] - 2026-06-04

Fixed

  • Fixed MiniMax-compatible OpenAI-completions hosts (e.g. minimax-code-cn/MiniMax-M3) losing tool-call arguments when the stream delivers function.arguments as a complete object instead of the OpenAI JSON-string contract. The streaming buffer previously concatenated the object into a string, coercing it to [object Object] and leaving bash/edit calls with empty or malformed inputs; the tool-call block now holds the object payload directly. (#1776)
  • Fixed Cloud Code Assist (Gemini / Antigravity) rejecting tool schemas with Invalid JSON payload received. Unknown name "propertyNames" (HTTP 400) when a tool exposed a property literally named properties (e.g. the Resend MCP create_contact tool). The schema normalizer's insideProperties flag was re-asserted when descending into such a property's value schema, so Google-unsupported keywords (propertyNames, additionalProperties, …) nested inside it were never stripped. The flag is now only set when entering a real properties map from a schema node, not from within another properties map.
  • Fixed local/self-hosted providers leaking machine-specific endpoints into the bundled models.json. A generate-models run on a machine with a LiteLLM proxy baked 1202 litellm models pinned to http://localhost:4000/v1 into the committed catalog. litellm (and lm-studio) now join ollama/vllm in the generator's discovery-only exclusion set, so local providers are never fetched during generation nor written to models.json — they are discovered dynamically at runtime instead. LiteLLM model discovery now enriches metadata against models.dev (the same reference source the other gateway providers use) rather than a bundled reference map. Added a regression test pinning the invariant (no local provider blocks, no loopback/private-network baseUrls in the bundled catalog).

[15.8.2] - 2026-06-03

Fixed

  • Fixed opencode-zen/minimax-m3-free (and forward-compat opencode-zen/minimax-m3) and opencode-go/minimax-m3 being routed to anthropic-messages despite the OpenCode Zen/Go gateways only serving these ids at /v1/chat/completions, which surfaced raw MiniMax/tool-call markup (<invoke name="bash">, <tool_call>, <description>, <cwd>, <|minimax|>) in the UI. Resolver overrides now pin these ids to openai-completions and the bundled models.json entries are flipped to match. (#1617)
  • Fixed MiniMax Coding Plan China login opening the international platform.minimax.io subscription page instead of the China platform.minimaxi.com page.

[15.8.0] - 2026-06-02

Added

  • Added AnthropicMessagesClient and related Anthropic wire types/errors via anthropic-client export so callers can build a standalone Anthropic Messages client without depending on @anthropic-ai/sdk
  • Added parseClaudeRateLimitHeaders and AuthStorage.ingestUsageHeaders so Anthropic rate-limit response headers can warm the per-credential usage cache with throttling while preserving per-tier data from the last full usage report.

Changed

  • Changed Anthropic request handling to use the package-local AnthropicMessagesClient implementation instead of @anthropic-ai/sdk as the default transport
  • Updated the AnthropicOptions.client surface to accept any AnthropicMessagesClientLike implementation with messages.create, enabling custom compatible clients
  • Changed generated OAuth metadata user_id to use a deterministic device_id derived from the install ID instead of a random value
  • claudeCodeVersion bumped to 2.1.148 to match current Claude Code release.
  • X-Stainless-Package-Version updated to 0.94.0 (matches the bundled @anthropic-ai/sdk version); X-Stainless-Runtime-Version pinned to v24.3.0 (Bun version bundled with CC 2.1.148); X-Stainless-Os header key corrected to X-Stainless-OS.
  • createClaudeBillingHeader now emits a deterministic billing header (cc_version=<claudeCodeVersion>.<suffix>; cc_entrypoint=cli; cch=00000;), where <suffix> is the first 3 hex chars of SHA-256(salt + msg[4] + msg[7] + msg[20] + version) instead of random bytes. The fingerprint seed is taken from the first user message (skipping synthetic/developer injections), mirroring Claude Code's computeFingerprintFromMessages.
  • cch attestation implemented: cch=00000 is a placeholder that, for OAuth requests, wrapFetchForCch rewrites on the wire to XXHash64(body, 0x4D659218E32A3268) & 0xFFFFF formatted as 5 lowercase hex chars, computed in-place via Bun.hash.xxHash64. The rewrite is anchored to the system[0] billing-header prefix so user content is never mutated, and is installed only when a billing-header prefix is present (OAuth turns).
  • anthropic-beta header set for OAuth model discovery and Claude usage-API requests expanded to add context-1m-2025-08-07, redact-thinking-2026-02-12, mid-conversation-system-2026-04-07, advanced-tool-use-2025-11-20, effort-2025-11-24, and extended-cache-ttl-2025-04-11. The usage-API user-agent is bumped to claude-cli/2.1.158 (external, cli).
  • Reasoning models now append effort-2025-11-24 to the per-request Anthropic-Beta header (matches Claude Code).
  • buildAnthropicSystemBlocks (CC-instruction mode) now emits the same 3-block layout as Claude Code: billing header (never cached), system instruction (cached), all user content merged into one block with \n\n (cached). Previously emitted one block per item with cache only on the last, which fingerprinted the caller by block count.
  • applyPromptCaching now matches Claude Code's breakpoint layout: 2 system (instruction + merged content) + 2 message, with no tool breakpoint. The tool breakpoint was redundant — tools follow system in the token sequence, so when system changes the tool cache prefix also changes. The instruction block (system[1]) is stable across every request and now gets its own guaranteed-hit breakpoint.
  • applyPromptCaching now caches the last two messages regardless of role instead of the last two user messages. The penultimate assistant message (tool calls + response from the previous turn) is larger and more recently created than the penultimate user message, making it the higher-value cache target.
  • OAuth scope set expanded: added user:sessions:claude_code, user:mcp_servers, user:file_upload. AUTHORIZE_URL stays at claude.ai/oauth/authorize and TOKEN_URL stays at api.anthropic.com/v1/oauth/token — the platform.claude.com equivalents are CC's console-credential flow and do not grant user:inference, which OMP requires for direct OAuth-token inference.
  • Token refresh POST now sends anthropic-beta: oauth-2025-04-20 and User-Agent: anthropic-sdk-typescript/0.94.0 userOAuthProvider (CC sends these on refresh but not on the initial code exchange).

Fixed

  • Fixed tool argument validation to wrap a plain string in a singleton array when the schema requires an array, allowing tool-level path/list normalization to recover from bare string arguments.
  • Restored eager_input_streaming and strict flags on OAuth Anthropic tool definitions when model compatibility allows eager streaming.
  • Fixed OAuth stream calls with injected custom clients missing a beta client by falling back to client.messages.create instead of requiring client.beta.messages.create
  • Fixed direct use of internal API client typing so retry/timeouts and malformed-error classification remain compatible while not requiring the external SDK
  • Fixed Cursor provider requests failing with Cannot send empty user message to Cursor API after tool-result history by selecting the latest user/developer turn instead of assuming the final context message is the active user turn.
  • Fixed Anthropic web search dropping ANTHROPIC_CUSTOM_HEADERS when CLAUDE_CODE_USE_FOUNDRY was unset, causing 401s from corporate API gateways. resolveAnthropicCustomHeadersForBaseUrl now forwards the parsed headers whenever the base URL is non-Anthropic (or Foundry is enabled), and buildAnthropicSearchHeaders threads them through buildAnthropicHeaders so the search and streaming paths behave identically (#1693).
  • Fixed OpenCode Go Anthropic-format models such as qwen3.7-max sending Anthropic X-Api-Key auth alongside the OpenCode bearer token, avoiding spurious Alibaba 401 Invalid API-key provided errors. (#1661)
  • Fixed OAuth token exchange and refresh flows to fetch Claude CLI bootstrap identity when token responses omit account information, so accountId and email are now recovered when available
  • Fixed Anthropic thinking traces being lost on direct OAuth requests. OAuth requests no longer send redact-thinking-2026-02-12 unless thinking is explicitly hidden, Opus 4.7+ adaptive thinking opts into display: "summarized", and the top user-facing thinking tier now sends Anthropic's output_config.effort = "max" rather than the next-lower "xhigh" tier.

Removed

  • Removed the @anthropic-ai/sdk runtime dependency. The Anthropic provider now uses the package-local AnthropicMessagesClient and hand-maintained wire types in providers/anthropic-wire.ts; the SDK was only ever used for URL assembly, auth-header injection, bounded retries, the pre-response timeout, and HTTP-error-to-status mapping, all of which are reproduced with identical observable behavior.

[15.7.5] - 2026-06-01

Added

  • Added Anthropic task budget support, forwarding taskBudget as output_config.task_budget with the required task-budgets-2026-03-13 beta header and accepting Anthropic gateway requests that send output_config.task_budget.

Fixed

  • Fixed OpenAI-family first-event timeouts so PI_OPENAI_STREAM_IDLE_TIMEOUT_MS cannot be undercut by a lower generic PI_STREAM_FIRST_EVENT_TIMEOUT_MS while local OpenAI-compatible servers are still processing large prompts. PI_OPENAI_STREAM_FIRST_EVENT_TIMEOUT_MS is now available for an explicit OpenAI-specific first-event override. (#1603)

[15.7.4] - 2026-05-31

Fixed

  • Fixed Anthropic stream idle-timeout retries after the provider stream has already begun.
  • Fixed Xiaomi MiMo /login rejecting token-plan (tp-) keys with 401 Invalid API Key. The validation request was still sending the legacy Anthropic x-api-key header against the OpenAI-compatible /v1/chat/completions endpoint; switched to Authorization: Bearer, matching the runtime path. (#1580)
  • Fixed OpenAI-compatible tool-call replay to send empty assistant content instead of null, avoiding strict custom backends that crash with str/NoneType concatenation after subagent tool results. (#1585)

[15.7.3] - 2026-05-31

Changed

  • Throttled per-delta streaming JSON re-parsing of OpenAI Responses/Codex tool-call arguments (bounding mid-stream parse cost from O(N²) to O(N)). Finalization via response.output_item.done now writes the authoritative full arguments back to the persisted assistant-message block, so tool calls finalized without a trailing response.function_call_arguments.done no longer retain stale/empty ({}) arguments. (#1507)

[15.6.0] - 2026-05-30

Fixed

  • Fixed Anthropic adaptive-thinking replay preserving signed thinking blocks on the latest abandoned tool-use assistant message, avoiding thinking blocks in the latest assistant message cannot be modified 400s. (#1531)

[15.5.15] - 2026-05-30

Added

  • Added PI_REQ_DEBUG=1 request/response recording for provider transports. Each request writes rr-session-N.json; each received response writes rr-session-N.res.log with response headers followed by raw body bytes.

Fixed

  • Fixed OpenCode-Go dynamic model refresh downgrading qwen3.7-max from Anthropic Messages to OpenAI-compatible transport, which caused 401 Model qwen3.7-max is not supported for format oa-compat after /v1/models cache refreshes.

[15.5.12] - 2026-05-29

Removed

  • Removed ANTML stream markup healing for antml:function_calls and antml:thinking envelopes, so Anthropic-compatible providers no longer parse those tags into toolCall/thinking events

Fixed

  • Fixed GLM-5.x coding-plan OpenAI-compatible streams to use a longer default watchdog window, avoiding spurious OpenAI completions stream stalled while waiting for the next event errors during slow glm-5.1 thinking/output phases. (#1494)
  • Fixed zhipu-coding-plan model discovery and credential validation to use the dedicated GLM Coding Plan endpoint (https://open.bigmodel.cn/api/coding/paas/v4) instead of the general BigModel endpoint, preventing requests from consuming ordinary account balance. (#1494)
  • Fixed DeepSeek tool calls failing on NanoGPT (e.g. nanogpt/deepseek/deepseek-v4-pro with reasoning enabled) by routing tool-bearing DeepSeek requests through NanoGPT's :tools model route and adding nanogpt to the DSML leak allowlist so streamed <|DSML|tool_calls>...</|DSML|tool_calls> envelopes are healed into structured tool calls instead of being passed through as visible text. (#1488)
  • Fixed DeepSeek tool calls failing on NanoGPT (e.g. nanogpt/deepseek/deepseek-v4-pro with reasoning enabled) by adding nanogpt to the DSML leak allowlist so streamed <|DSML|tool_calls>...</|DSML|tool_calls> envelopes are healed into structured tool calls instead of being passed through as visible text. The :tools model suffix is no longer appended on NanoGPT; that route triggered NanoGPT's server-side tool-call parser and 502'd with code: "malformed_tool_call" on complex tool schemas (todo_write) — the default route forwards delta.content (including DSML envelopes) which is healed client-side. (#1488)
  • Fixed OpenAI-compatible streamed parallel tool calls losing indexed argument deltas by tracking active tool-call blocks by the provider's tool_calls[].index; this keeps parallel NanoGPT read calls from merging or dropping their path arguments. (#1488)

[15.5.11] - 2026-05-29

Added

  • Added mid-conversation system message support for Anthropic Messages by upgrading eligible developer turns to role: "system" on first-party Claude API with Claude Opus 4.8+ and newer
  • Added supportsMidConversationSystem to Anthropic compatibility settings so consumers can opt in to or disable mid-conversation system role handling per model
  • Added anthropic.claude-opus-4-8 model metadata in the model registry for Bedrock Converse streaming with effort-based thinking support through xhigh

Changed

  • Changed Anthropic adaptive-thinking effort mapping for Opus 4.7+ on the Messages API to use the model's full five-tier scale: user-facing efforts now shift up one notch (minimal→low, low→medium, medium→high, high→xhigh, xhigh→max) so the top tier reaches the genuine max level and high lands on Anthropic's recommended xhigh coding/agentic default. Older adaptive models (Opus 4.6) and Bedrock Converse keep the four-tier legacy mapping where xhigh aliases to max.

Fixed

  • Fixed OpenCode Zen 400 thinking is enabled but reasoning_content is missing in assistant tool call message for every model behind opencode-go/opencode-zen (Kimi K2.x, DeepSeek V4 Pro/Flash, GLM-5.x, Qwen3.x, MiMo, MiniMax) by reactivating requiresReasoningContentForToolCalls and pinning the wire field to reasoning_content for any opencode request in thinking mode. The static compat default still omits the field for thinking-disabled turns to preserve the Extra inputs are not permitted guard from #1071; forced-tool turns also stay off because the existing disableReasoningOnForcedToolChoice guard strips thinking from the wire body. (#1484)

[15.5.8] - 2026-05-28

Added

  • Added CheckCredentialsOptions.completionProbe (and completionTimeoutMs) so AuthStorage.checkCredentials can additionally exercise each credential against the provider's chat-completion endpoint after refresh-on-expiry. Result lands on CredentialHealthResult.completion ({ok, reason?, modelId?, latencyMs?}) without disturbing the usage ok field. Public types: CompletionProbe, CompletionProbeInput, CompletionProbeCredential, CredentialCompletionResult. The probe is invoked even when no UsageProvider is registered for the row, and is skipped when OAuth refresh fails (the stale bytes would only mask the upstream failure).
  • Added Wafer Pass and Wafer Serverless providers (wafer-pass, wafer-serverless). OpenAI-compatible (https://pass.wafer.ai/v1), bearer auth, wfr_… keys. /login wafer-pass and /login wafer-serverless paste-and-validate the key against /v1/models. WAFER_PASS_API_KEY and WAFER_SERVERLESS_API_KEY environment variables wired into getEnvApiKey. Bundled catalog seeds wafer-pass/{GLM-5.1, Qwen3.5-397B-A17B} and wafer-serverless/{GLM-5.1, Kimi-K2.6, Qwen3.5-397B-A17B, Qwen3.6-35B-A3B, qwen3.7-max, deepseek-v4-flash, deepseek-v4-pro}; dynamic discovery via /v1/models overlays additional models at runtime. Pass-tier discovery filters wafer.tier === "pass_included". Pass-SKU costs are seeded at 0 (flat-rate subscription, no per-token charge — matches kimi-code/firepass/alibaba-coding-plan). Serverless costs are the wafer.ai retail rate, derived from the *_cents_per_million envelope via value × 125 / 10000 (e.g. GLM-5.1 120 → $1.50/M, Kimi-K2.6 88 → $1.10/M). Reasoning entries get a thinking compat picked from the wafer.provider envelope: zai/moonshotai → zai-style thinking: { type }, qwen → top-level enable_thinking, deepseek and unknown upstreams stay unset so detectOpenAICompat can pick reasoning_effort from the id pattern at request time.

Changed

  • Changed auth-gateway credential resolution to use per-conversation promptCacheKey/sessionId when calling AuthStorage.getApiKey, so repeated turns can keep the same credential until it becomes unavailable
  • Changed auth-gateway and pi-native request handling to align sessionId with prompt/context identity before credential lookup
  • Changed Anthropic prompt preparation to downscale image blocks over 2000px when a request includes 20+ images, reducing oversized payloads automatically
  • Changed OpenAI chat request parsing to accept name on tool messages and fall back to the matching assistant tool_calls name, so parsed tool results now carry a proper tool name when the wire omits it
  • Changed checkCredentials to skip running completionProbe when OAuth refresh fails, so stale bearer tokens are never probed and the refresh failure remains the returned reason
  • Changed completion reporting to return completion: { ok: null, reason: ... } when a credential has no usable bearer bytes instead of attempting the probe
  • Refactored AuthStorage.checkCredentials so OAuth refresh-on-expiry runs up-front and the refreshed credential is shared between the usage probe and the new completion probe; rows without a registered UsageProvider no longer short-circuit before the completion probe runs.

Fixed

  • Fixed DeepSeek DSML tool-call envelope leaks on Ollama Cloud and OpenAI-compatible streams by healing leaked envelopes into structured tool calls without displaying raw DSML markers. (#1462)
  • Fixed auth-gateway to classify usage-limit messages such as usage_limit_reached, resource_exhausted, and Codex-style Try again in ~X min text as 429 rate_limit_error responses
  • Fixed auth-gateway usage-limit handling to honor parsed retry hints and switch to a sibling credential via markUsageLimitReached instead of invalidating the rate-limited credential
  • Fixed streamSimple to retry on usage-limit errors (including message-only error events) before any content is emitted, so onAuthError can rotate credentials automatically
  • Fixed auth-gateway error classification to extract embedded status codes and use word-boundary matching, so GenerateContentRequest and similar messages are no longer misreported as rate-limit errors
  • Fixed checkCredentials to handle completionProbe exceptions by recording the failure in CredentialHealthResult.completion.reason while still returning the usage probe result
  • Fixed Google Vertex's bundled model list to use the authoritative models.dev catalog, including MaaS entries such as deepseek-ai/deepseek-v3.2-maas and removing retired Gemini 1.5 fallbacks. (#1456)

[15.5.7] - 2026-05-27

Added

  • SimpleStreamOptions.openrouterVariant ("nitro", "floor", "online", "exacto", …) — when set, appends :<variant> to OpenRouter model IDs at request time, leaving ids that already carry an explicit :suffix untouched. Plumbed through openai-completions and the pi-native gateway forwarder.

  • xAI Grok OAuth (SuperGrok Subscription) provider in /login. Loopback PKCE flow on 127.0.0.1:56121; the token unlocks Grok-4.x chat. Ported from NousResearch/hermes-agent (MIT).

  • OpenRouter provider in /login. API-key paste flow validated against https://openrouter.ai/api/v1/auth/key (the /models endpoint is public and cannot validate auth). The pasted key is stored under the existing openrouter provider id used by OPENROUTER_API_KEY.

  • XAI_OAUTH_TOKEN environment variable accepted as a headless fallback for the xAI Grok OAuth provider.

Changed

  • OpenAIResponsesOptions gains four optional, provider-agnostic fields that adapter wrappers can use to compose provider-specific behavior on top of the generic transport: includeEncryptedReasoning (gates include: ["reasoning.encrypted_content"]; default true, preserves current behavior), filterReasoningHistory (strips replayed type: "reasoning" items from conversation history; default false), headers (merged onto the client's default headers), and extraBody (merged into the request payload).
  • The existing XAI_API_KEY path is unchanged — it continues to use the OpenAI-completions transport.

Fixed

  • Fixed OpenRouter DeepSeek V4 tool-call follow-up requests replaying normalized reasoning as-is instead of DeepSeek's required reasoning_content, which caused HTTP 400 errors in thinking mode. (#1445)

[15.5.6] - 2026-05-27

Added

  • Added PI_CODEX_WEBSOCKET_MAX_IDLE_REUSE_MS to control how long an idle Codex WebSocket stays eligible for reuse, with 0 disabling the check

Fixed

  • Fixed reused Codex WebSocket connections that had gone silent without activity to be dropped and replaced with a fresh handshake after the idle-reuse threshold, preventing stalled next requests
  • Fixed stale response frames left in the websocket queue from a completed turn so subsequent requests no longer process terminal frames from the previous response
  • Fixed websocket dead-socket detection to fail a stale connection when no inbound traffic or pong is observed after a ping timeout, improving recovery on runtimes that do not emit pong events

[15.5.5] - 2026-05-27

Added

  • Added PI_CODEX_WEBSOCKET_PING_INTERVAL_MS to configure the interval for Codex WebSocket protocol ping heartbeats
  • Added PI_CODEX_WEBSOCKET_PONG_TIMEOUT_MS to configure the Codex WebSocket pong timeout used to detect unresponsive connections
  • Added PI_CODEX_WEBSOCKET_MESSAGE_QUEUE_CAPACITY to configure the maximum buffered Codex WebSocket inbound queue size before transport fallback
  • Added parseStreamingJsonThrottled to @oh-my-pi/pi-ai/utils/json-parse — a per-delta wrapper around parseStreamingJson that skips re-parses until the buffer has grown by minGrowthBytes (default 256). Wired into the streaming hot path of every provider's tool-call argument accumulator (anthropic, amazon-bedrock, openai-completions, openai-codex-responses, openai-responses-shared) so per-delta cost is O(N) in total buffer length instead of O(N²). Each provider's toolcall_end still runs a final unthrottled parse, so the published block.arguments is unchanged.
  • Added named-tool routing support to Google providers: GoogleSharedStreamOptions.toolChoice and GoogleGeminiCliOptions.toolChoice now accept { mode: "ANY"; allowedFunctionNames: [string, ...string[]] } in addition to the string forms. mapGoogleToolChoice converts ToolChoice objects of shape { type: "tool" | "function", name } to the wire form. Mirrors the equivalent Anthropic mapper.

Changed

  • Improved Codex WebSocket timeout diagnostics to include last event type and time since last progress event
  • Enhanced Codex WebSocket error classification to recognize ping, pong, send, and queue-overflow failures as retryable
  • Changed mapGoogleToolChoice to be exported from @oh-my-pi/pi-ai/stream so callers can build the wire-shape allow-list directly without re-deriving it.

Fixed

  • Fixed Codex WebSocket send failures by wrapping socket.send() in try-catch and surfacing errors as retryable transport errors
  • Fixed Codex WebSocket inbound queue overflow by adding capacity bounds and triggering fallback to SSE when exceeded
  • Fixed Codex WebSocket pong timeout detection by tracking pong events and failing the connection when no pong is received within the configured timeout
  • Fixed Anthropic streaming to suppress hallucinated meta-prompt thinking blocks (the recent "I don't see any current rewritten thinking..." regression). When the marker phrase rewritten thinking appears in a streamed thinking summary the block is collapsed to a plain Thinking... placeholder and its signature is dropped so subsequent turns can't re-anchor on the garbled chain.
  • Fixed Codex WebSocket silent stalls by adding protocol pings, inbound queue bounding, clearer idle-timeout diagnostics, and SDK retry clamping for first-event timeouts.
  • Fixed Synthetic model discovery to treat the provider /models response as authoritative so deprecated bundled IDs are pruned from the runtime cache, and changed Synthetic login validation to avoid probing a specific model (#1417).

[15.5.0] - 2026-05-26

Added

  • Added zhipu-coding-plan provider for Zhipu (智谱) BigModel's domestic coding-plan SKU at https://open.bigmodel.cn/api/coding/paas/v4, with dynamic model discovery (ZHIPU_API_KEY), zai-format thinking, reasoning_content field, and OAuth login flow (#1340).

Removed

  • Removed the pi-ai CLI binary (packages/ai/src/cli.ts) and its bin entry. Use the in-process equivalent in the omp coding-agent CLI: omp auth-broker login [provider], omp auth-broker logout [provider], and omp auth-broker list. The library API (AuthStorage.login(), getOAuthProviders(), etc.) is unchanged.

Fixed

  • Fixed delayed toolResult emissions so real tool results are emitted in the correct assistant toolCall window after handoff/compaction, preventing out-of-order or orphaned tool results
  • Fixed delayed toolResult handling for aborted calls so a late real result is emitted instead of a synthetic aborted result for the same toolCallId
  • Fixed usage polling to disable credentials when OAuth refresh fails definitively (for example invalid_grant) and clear cached last-good usage data so stale reports no longer remain visible

[15.4.3] - 2026-05-26

Fixed

  • Fixed Google Vertex model discovery to use the project-scoped OpenAI-compatible model list so Vertex Model Garden models such as GLM and Claude are available through ADC auth (#1412).

[15.4.2] - 2026-05-26

Fixed

  • Fixed OpenCode Zen big-pickle follow-up requests replaying assistant tool-call turns without DeepSeek-required reasoning_content, which caused HTTP 400 errors in thinking mode.

[15.4.1] - 2026-05-26

Added

  • Added isOpenAICompletionsProgressChunk export to identify real progress chunks vs. keepalives in OpenAI completions streams
  • Added per-provider stream watchdog overrides via getStreamIdleTimeoutMs(fallbackMs) and getStreamFirstEventTimeoutMs(idleTimeoutMs, fallbackMs) to allow providers like Google Gemini CLI to extend first-event timeouts without affecting global defaults
  • Added promptCacheKey to StreamOptions and passed it through stream option mapping so callers can specify an explicit prompt-cache key separate from sessionId
  • Added promptCacheKey support to the native server option whitelist so promptCacheKey is accepted by pi-native-server streams
  • Restored the per-provider stream watchdog (iterateWithIdleTimeout) on top of the abortable iterator. The lazy stream forwarder in register-builtins now wraps every provider's event stream with the first-event + steady-state idle watchdog (PI_STREAM_FIRST_EVENT_TIMEOUT_MS, PI_STREAM_IDLE_TIMEOUT_MS; aliases honored), and Anthropic / OpenAI Completions / OpenAI Responses / Azure OpenAI Responses / Codex SSE re-emit their per-provider progress predicates so empty keepalive frames cannot keep a stalled stream alive. Reverts the partial regression from #1392 that left Codex WebSocket subagent runs hanging silently for hours when the broker dropped frames between deltas. The Codex WebSocket transport additionally now resets lastProgressAt only on progress events (not keepalives), giving the 300s WS-internal idle ceiling the same liveness semantics as the SSE path.

Changed

  • Enabled OpenAI Codex WebSocket streams to apply streamIdleTimeoutMs and streamFirstEventTimeoutMs from StreamOptions per request instead of fixed internal defaults
  • Changed stream idle watchdog implementation from iterateUntilAbort to iterateWithIdleTimeout, which now enforces maximum idle gaps between streamed events and distinguishes between first-event and steady-state timeouts
  • Changed Anthropic, OpenAI Responses, OpenAI Completions, Azure OpenAI Responses, and OpenAI Codex Responses providers to use the new idle-timeout iterator with per-provider progress predicates so empty keepalive frames cannot keep a stalled stream alive
  • Changed Codex WebSocket transport to reset lastProgressAt only on progress events (not keepalives), giving the 300s WS-internal idle ceiling the same liveness semantics as the SSE path
  • Changed Google Gemini CLI stream forwarding defaults to use a 5-minute first-event floor via per-provider lazy-stream limits to avoid premature first-event timeouts on slow startup
  • Changed OpenAI Responses and OpenAI Codex request handling to keep sessionId for provider routing and conversation headers while promptCacheKey controls the prompt_cache_key payload independently
  • Changed StreamOptions.streamIdleTimeoutMs documentation to clarify it is now wired into every built-in provider and the lazy stream forwarder, and that streamFirstEventTimeoutMs is honored at both the SDK-request layer and the iterator-watchdog layer
  • Changed OpenAI Responses and OpenAI Codex request handling so sessionId continues to drive provider routing and state while promptCacheKey controls the prompt_cache_key payload
  • Changed Google Gemini CLI stream forwarding defaults to use a 5-minute first-event floor to avoid premature first-event timeouts on slow startup
  • Changed auth-gateway request mapping to preserve incoming prompt_cache_key as both promptCacheKey and sessionId when routing OpenAI-compatible sessions
  • Un-deprecated StreamOptions.streamIdleTimeoutMs; the option is wired into every built-in provider and the lazy stream forwarder again. streamFirstEventTimeoutMs is now honored at both the SDK-request layer (via createSdkStreamRequestOptions) and the iterator-watchdog layer, in cooperation.

Removed

  • Removed installH2Fetch and the fetch patch that forced HTTP/2 on HTTPS requests; callers now use the default Bun fetch transport

Fixed

  • Fixed first-item timeout handling so iterateWithIdleTimeout no longer keeps first-event timers active after the source throws or the consumer stops before semantic progress
  • Fixed silent multi-hour hangs on Codex WebSocket subagent runs when the broker dropped frames between deltas by restoring per-provider stream watchdogs with progress-event filtering
  • Fixed z.ai/GLM-via-OpenRouter subagent stalls where no-op keepalive chunks reset the idle watchdog indefinitely by filtering non-progress items before resetting the deadline

[15.4.0] - 2026-05-26

Breaking Changes

  • Removed findAnthropicAuth from anthropic-auth and replaced store-driven auth discovery with buildAnthropicAuthConfig, requiring callers to provide an already-resolved API key before building Anthropic auth config

Added

  • Added PI_CODEX_WEBSOCKET_FIRST_EVENT_TIMEOUT_MS and PI_CODEX_WEBSOCKET_IDLE_TIMEOUT_MS options to tune Codex WebSocket timeout behavior before fallback
  • Added AuthStorage.getOAuthAccess to return a refreshed OAuth access token with identity metadata (accountId, email, projectId, enterpriseUrl) for callers that need bearer-token headers together
  • Added Codex WebSocket forwarding to the onSseEvent observer so the raw provider-stream debug viewer captures the inbound JSON frames and the outbound request frame from the WS transport using the same synthesized SSE-wire shape (event: + data: lines, prefixed with a : ws ← <type> (inbound) or : ws → <type> (outbound) comment).

Changed

  • Changed OAuth selection in AuthStorage to treat credentials as stale when they are within 60 seconds of expiry and rotate them preemptively
  • Changed Google Gemini CLI, Google Gemini usage, Antigravity usage, and Kimi usage flows to stop refreshing OAuth tokens directly and rely on AuthStorage for token rotation

Deprecated

  • Deprecated streamIdleTimeoutMs in StreamOptions as a compatibility-only field that is no longer used by providers

Removed

  • Removed provider-local OAuth refresh helpers from Google Gemini CLI and Google/Kimi/Antigravity usage probes, preventing direct refresh calls from those usage paths

Fixed

  • Dropped truncated, thinking-only assistant turns with only thinking/redacted_thinking blocks and no text or tool content during message transformation, preventing Anthropic requests from sending consecutive assistant messages after a max_tokens/error/aborted interruption
  • Fixed Amazon Bedrock bearer-token authentication to honor AWS_BEARER_TOKEN_BEDROCK before resolving AWS profiles or running credential_process, matching Bedrock API-key precedence. (#1399)
  • Updated isRetryableError to treat Bun HTTP/2 transport errors (HTTP2StreamReset, HTTP2RefusedStream) as retryable so transient stream-reset failures can be retried
  • Fixed Codex WebSocket streaming to recover from stalled sessions by falling back to SSE when the first event or subsequent progress is delayed beyond the configured websocket timeout
  • Fixed expired OAuth handling so provider-level paths no longer attempt direct token refresh calls for expired credentials and instead rely on AuthStorage for rotation
  • Fixed provider streams aborting slow-but-valid first tokens or silent inter-event gaps with OMP-owned first-event/idle watchdog errors. Built-in lazy streams, OpenAI/Anthropic/Azure/Codex SSE, and Codex WebSocket streams now wait for provider output, provider/socket errors, caller aborts, or explicit request-layer timeouts instead of treating provider silence as failure (#1392).
  • Fixed Claude Opus 4.7 on Amazon Bedrock streaming no reasoning output (and appearing to hang on long reasoning runs) because Anthropic silently switched the adaptive-thinking display default to "omitted". The Bedrock provider now sends thinking.display = "summarized" by default on Opus 4.7+ adaptive models and on budget-based Claude models, mirroring the existing direct-Anthropic behavior. BedrockOptions.thinkingDisplay ("summarized" | "omitted") is exposed for callers that want to opt out, and hideThinkingSummary now wires through to the Bedrock case (#1373).
  • Fixed Cursor Composer resume/tool-continuation turns failing with Cannot send empty user message to Cursor API. Empty current user turns now use Cursor's resumeAction instead of constructing an invalid userMessageAction (#1376).
  • Fixed pi-ai login moonshot failing with invalid temperature: only 1 is allowed for this model (HTTP 400) because the API-key validator probed kimi-k2.5 with temperature: 0. Moonshot login now validates against GET /v1/models, matching the DeepSeek/Fireworks/NanoGPT/ZenMux pattern and authenticating the key without invoking model-specific parameter restrictions.

[15.3.2] - 2026-05-25

Added

  • Added GET /v1/snapshot/stream for live auth-broker snapshot updates via SSE with snapshot, entry, and removed event frames
  • Added AuthBrokerClient.openSnapshotStream() for consuming SSE snapshot streams from /v1/snapshot/stream
  • Added streamSnapshots option to RemoteAuthCredentialStore (default true) to enable or disable SSE-based snapshot synchronization
  • Added streamKeepaliveMs to startAuthBroker() to tune heartbeat frequency for the SSE stream
  • Added AuthStorage.checkCredentials({ signal?, timeoutMs?, baseUrlResolver? }) that returns a per-credential CredentialHealthResult with tri-state ok (true / false / null-unverifiable), the credential's identity (provider, type, email/accountId, broker-refresh flag), and the upstream error string when the probe fails. Iterates sequentially over listAuthCredentials(), exercises OAuth refresh on expiry, then calls the per-provider UsageProvider.fetchUsage without swallowing errors — so callers can identify which row in a multi-account broker is producing 401s instead of getting a silently-deduplicated fetchUsageReports list.
  • Added GET /v1/credentials/check to startAuthGateway() that forwards to AuthStorage.checkCredentials and returns { generatedAt, credentials }. Gated by the same bearer as the rest of the gateway.

Changed

  • Changed RemoteAuthCredentialStore to prefer SSE snapshot streaming and automatically fall back to long-polling when a broker returns 404 for /v1/snapshot/stream
  • Changed snapshot write-refresh flow so RemoteAuthCredentialStore skips immediate /v1/snapshot refreshes when SSE streaming is active
  • Changed broker SSE stream behavior to keep connections open with periodic keepalives and an increased server idle timeout

[15.3.0] - 2026-05-25

Added

  • Added DeepSeek to the built-in API-key login provider catalog so omp login deepseek stores a reusable DEEPSEEK_API_KEY credential for the bundled DeepSeek models.

Fixed

  • Fixed openai-responses requests intermittently 400ing with No tool call found for function call output with call_id … after an aborted turn or a locally-rejected tool call (e.g. argument-validation failure). convertConversationMessages now folds orphan function_call_output / custom_tool_call_output items — those whose matching function_call was wiped by an earlier dt: false snapshot splice or never landed in any persisted provider payload — into assistant text notes, preserving the payload while keeping the request grammatically valid (#1351).

[15.2.4] - 2026-05-22

Fixed

  • Fixed ChatGPT Plus/Pro (Codex) OAuth login returning Token exchange failed: 403 on Windows. When port 1455 was in use, the callback server silently fell back to a random port; OpenAI's authorization endpoint accepts any localhost redirect URI (loose validation), so the browser callback succeeds and shows "Authentication Successful", but the token endpoint rejects the non-registered port with 403. The OpenAICodexOAuthFlow now enforces a fixed redirectUri option so a busy port immediately surfaces as "port unavailable" instead of producing a confusing 403 (#1277).
  • Improved exchangeCodeForToken error diagnostics: the 403 response body (error / error_description fields) is now included in the thrown message, matching the existing refreshOpenAICodexToken behaviour.

Added

  • Added ChatGPT Plus/Pro (Codex, headless/device) (openai-codex-device) as an alternative login method for the Codex provider. Uses OpenAI's device-code flow (/api/accounts/deviceauth/usercode → poll /api/accounts/deviceauth/token), which avoids a local callback server and port 1455 entirely. Credentials are stored under the existing openai-codex provider key so all models and tooling continue to work without reconfiguration (#1277).

[15.2.2] - 2026-05-22

Fixed

  • Fixed gemini-3.1-pro-high and gemini-3.1-pro-low on the google-antigravity provider always returning HTTP 400 from Cloud Code Assist. The ANTIGRAVITY_SYSTEM_INSTRUCTION identity header was not injected for these models because the internal check matched the string "gemini-3-pro-high" (hyphen) instead of the versioned "gemini-3.1-pro-..." form. The guard now matches all gemini-3 model variants (#1274).

[15.2.0] - 2026-05-21

Fixed

  • Fixed /login (and /logout, plus any AuthStorage.set / remove call) against a remote auth-broker throwing RemoteAuthCredentialStore is read-only on the client. Use 'omp auth-broker login <provider>' to mutate credentials. Added three optional async write hooks to AuthCredentialStore (upsertAuthCredentialRemote, replaceAuthCredentialsRemote, deleteAuthCredentialsRemote); RemoteAuthCredentialStore implements them via the broker's POST /v1/credential and POST /v1/credential/:id/disable endpoints and applies the broker's authoritative post-write entries to the local snapshot. AuthStorage routes through the hooks when present, so OAuth and API-key logins (and logouts) initiated from a broker-backed client now persist server-side and surface immediately without waiting for the long-poll snapshot tick.

[15.1.9] - 2026-05-21

Fixed

  • Fixed Ollama named tool forcing to send only the requested tool when the caller passes a named toolChoice, preserving tool_choice: "required" while preventing local models from selecting a different tool. (#1236)
  • Fixed /btw (and IRC background replies) returning a BedrockException 400 (The toolConfig field must be defined when using toolUse and toolResult content blocks.) on LiteLLM → Bedrock once the session has tool-call history. Two source fixes in buildParams: (1) if (context.tools) → if (context.tools?.length) so an explicit context.tools = [] (the /btw opt-out) never routes through convertTools and never emits an empty "tools" array; (2) else if (hasToolHistory(...)) → else if (context.tools === undefined && hasToolHistory(...)) so the Anthropic-proxy sentinel that injects tools: [] for tool-history turns is suppressed when the caller explicitly opted out, preventing it from re-introducing the empty array. As defence-in-depth, tool_choice: "none" is also dropped when the resolved tools list is missing or empty. (#1227)

[15.1.8] - 2026-05-20

Added

  • Added Fireworks Fire Pass as a separate firepass provider with API-key login flow, bundled kimi-k2.6-turbo model entry (Kimi K2.6 Turbo), and wire-id translation from the friendly catalog id to the accounts/fireworks/routers/kimi-k2p6-turbo router endpoint. Fire Pass keys (fpk_…) authorize only the dedicated router and reject /v1/models, so login validation pings chat completions against the router id directly. Extended the openai-completions Kimi-family safety net so the firepass entry inherits the per-Fireworks-docs "always send max_tokens" default (Kimi K2 guide); the router's accepted reasoning_effort set includes xhigh, so it is forwarded verbatim rather than remapped. See https://docs.fireworks.ai/firepass.

Fixed

  • Fixed DeepSeek V4 direct API requests with tools to keep documented thinking mode instead of dropping reasoning: lower OMP efforts now map to DeepSeek's supported high, tool_choice is omitted, thinking: { type: "enabled" } and max_tokens are sent, and partial user reasoningEffortMap overrides merge with DeepSeek defaults. (#1207)
  • Fixed model cache schema v2 databases so offline refreshes preserve cached provider discoveries after upgrading to schema v3 and subsequent online refreshes can overwrite the cache. (#1219)
  • Fixed Perplexity OAuth credentials being treated as expired one hour after login. getJwtExpiry was fabricating expires = now + 1h whenever the JWT had no exp claim (the common case — Perplexity sessions are server-side). Once the hour elapsed, getOAuthApiKey would mark the cred expired and the search provider's loader would silently skip it, surfacing as "logged out". Logins with no exp now persist a far-future sentinel; getOAuthApiKey also normalizes any stale expires written by older builds.

[15.1.7] - 2026-05-19

Added

  • Added Anthropic realization of serviceTier: "priority". The anthropic-messages provider now sets speed: "fast" on the request and appends the fast-mode-2026-02-01 beta to Anthropic-Beta whenever the caller passes serviceTier: "priority". When the server rejects an unsupported model with invalid_request_error, the provider transparently retries the same turn without the fast-mode signal (mirroring the strict-tools fallback pattern), persists the disable via a new providerSessionState.fastModeDisabled flag so subsequent requests in the session skip the field, and surfaces the action via the new AssistantMessage.disabledFeatures array (id "priority") so callers can sync user-facing toggles. A new clearAnthropicFastModeFallback(providerSessionState) helper lets callers re-arm priority after the auto-fallback fired.
  • Added scoped ServiceTier values: "openai-only" (priority on openai/openai-codex, ignored elsewhere) and "claude-only" (priority on direct anthropic, ignored on Bedrock/Vertex Claude and elsewhere). A new resolveServiceTier(serviceTier, provider) helper computes the effective tier for the provider; existing OpenAI/Anthropic provider code routes through it, so service_tier and Anthropic fast-mode emission both respect scope. getPriorityPremiumRequests now counts Anthropic+priority as one premium request (previously zero) and continues to ignore providers that drop the field on the wire.

Fixed

  • Fixed Anthropic fast mode (serviceTier: "priority") looping on 429 rate_limit_error: "Extra usage is required for fast mode." for accounts without the extra-usage entitlement. isAnthropicFastModeUnsupportedError now matches the 429 phrasing in addition to the 400 invalid_request_error "does not support the speed parameter" case, so the provider drops speed: "fast" on the in-turn retry, sets providerSessionState.fastModeDisabled for the remainder of the session, and surfaces disabledFeatures: ["priority"] to the caller instead of retrying with the same payload until PROVIDER_MAX_RETRIES is exhausted.
  • Fixed MiniMax Coding Plan CN streaming <think>...</think> reasoning as visible assistant text. The OpenAI-compatible stream parser now enables the existing MiniMax tag parser for both minimax-code and minimax-code-cn, so CN responses become structured thinking blocks instead of raw text. (#1203)

[15.1.6] - 2026-05-19

Fixed

  • Fixed {} (empty JSON Schema, the wire representation of z.unknown()) being passed verbatim to grammar-constrained samplers (llama.cpp, etc.) in additionalProperties, items, and other schema-valued positions across every provider (OpenAI, Anthropic, Google, Ollama, Bedrock, Cursor). Grammar builders treat {} as "generate an empty object" rather than "any JSON value", causing open-typed fields (e.g. extra.title from z.record(z.string(), z.unknown())) to always emit {} instead of the intended string/number/etc. toolWireSchema now applies a new normalizeEmptySchemas pass (exported) to both the Zod and TypeBox/raw-JSON-Schema branches, converting {} → true (semantically identical per JSON Schema draft 2020-12 §4.3.1) in all schema-valued positions. Strict-mode opt-out is preserved across all providers: OpenAI's hasUnrepresentableStrictObjectMap hits the === true branch instead of the isJsonObject({}) branch (same result); Anthropic's normalizeAnthropicStrictSchemaNode opts out via additionalProperties !== false (still true for true); Google's normalizeSchemaForGoogle strips additionalProperties regardless (pre-existing). (#1179)
  • Fixed pi-ai login <provider> crashing with Unknown provider for providers that only the auth-storage login() switch knew about (perplexity, alibaba-coding-plan, gitlab-duo, huggingface, opencode-zen/go, lm-studio, ollama, cerebras, fireworks, qianfan, synthetic, venice, litellm, moonshot, together, cloudflare/vercel ai gateways, vllm, qwen-portal, nvidia, xiaomi, and any custom OAuth provider). The CLI now delegates to SqliteAuthCredentialStore.login() instead of duplicating a smaller switch, so the auth-broker omp auth-broker login <provider> flow works for every registered OAuth provider.

[15.1.4] - 2026-05-19

Changed

  • Updated auth-gateway format and pi-native request handling to invalidate the failed API key and retry the provider request with a replacement key when authentication fails

Fixed

  • Fixed OpenCode-Go and OpenCode-Zen chat-completions replay to omit stored reasoning fields on Kimi assistant tool-call messages, avoiding provider 400s for rejected messages[].reasoning payloads. (#1157)
  • Fixed OpenAI Responses and Codex tool schema normalization to emit properties: {} for no-argument object schemas without rewriting literal payloads. (#1147)
  • Fixed Anthropic 400 (unexpected tool_use_id found in tool_result blocks ... Each tool_result block must have a corresponding tool_use block in the previous message) when handoff/compaction folds an assistant tool_use into the handoff summary string but leaves the matching user-side tool_result message in the history. transformMessages now indexes every tool_use id surviving the first pass and drops orphan tool_result messages whose originator was compacted away, preserving the text payload as a user-level <stale-tool-result> note so the model still sees what the tool returned. The note is emitted with role: "user" rather than role: "developer" so providers that elevate developer-role messages (Ollama: developer → system; OpenAI chat-completions reasoning models: developer → developer) cannot lift stale tool output to an instruction-priority tier above the surrounding user/developer messages.
  • Fixed streaming authentication retry to trigger when a provider emits a 401 error event after a start event but before any replay-unsafe content is emitted
  • Added credential_process support to the Bedrock provider's AWS credential resolver so profiles delegating to external brokers (aws-vault, granted, in-house tools) resolve instead of falling through to Unable to resolve AWS credentials. Parses the AWS SDK Version: 1 JSON envelope, honors Expiration in the per-profile cache, propagates AbortSignal to the spawned helper, routes Windows .cmd/.bat helpers through cmd.exe /c, and ships a POSIX-shell-style tokenizer that preserves backslashes inside double quotes so Windows paths survive (#1142)

[15.1.3] - 2026-05-17

Breaking Changes

  • Changed AuthBrokerClient.fetchSnapshot() to return status-based results (200 or 304) instead of always returning a raw snapshot body, so callers now need to branch on status
  • Renamed public schema utilities in @oh-my-pi/pi-ai/utils/schema by replacing sanitizeSchemaForGoogle, sanitizeSchemaForCCA, prepareSchemaForCCA, and sanitizeSchemaForMCP with normalizeSchemaForGoogle, normalizeSchemaForCCA, and normalizeSchemaForMCP
  • Added MCP schema normalization via normalizeSchemaForMCP for compatibility checks
  • Removed the StringEnum helper from @oh-my-pi/pi-ai/utils/schema. Use z.enum([...]) directly; Zod's emitted JSON Schema is already wire-compatible with Google and other providers.
  • Renamed the concrete SQLite credential store class from AuthCredentialStore to SqliteAuthCredentialStore. AuthCredentialStore is now the persistence interface implemented by both the SQLite store and the new RemoteAuthCredentialStore. Update new AuthCredentialStore(db) / AuthCredentialStore.open(...) call-sites to SqliteAuthCredentialStore; type-position uses (store: AuthCredentialStore) continue to work unchanged.

Added

  • Added onAuthError to StreamOptions and wired streamSimple() to retry once with a replacement API key when the first provider response is a 401 before any assistant events are emitted
  • Added generation-aware snapshot metadata (generation, serverNowMs, refresher, and rotatesInMs) to auth-broker snapshot responses to support client-side credential-rotation planning
  • Added transport: "pi-native" on Model and the matching streamPiNative client. When model.transport === "pi-native", streamSimple short-circuits the per-provider dispatch and POSTs the canonical Context to the auth-gateway's POST /v1/pi/stream endpoint. The response is SSE-framed AssistantMessageEvents parsed by readSseJson and pushed verbatim into the local AssistantMessageEventStream — no wire-format translation, no partial-stripping reconstruction. Used by containerized omp installs (robomp slots, swarm extension, etc.) to route every LLM call through a credential-holding sidecar; the slot itself never sees the real provider tokens. Server-controlled fields (apiKey, signal, fetch, lifecycle callbacks, the provider-session map) are stripped from the wire body — apiKey rides in the Authorization header as the gateway bearer.
  • Added POST /v1/pi/stream to the auth-gateway. Same auth + abort + model-resolution + codex-compat + prefix-cache plumbing as the foreign-wire routes; only the wire-format translation is skipped. Request body is { modelId, context, options?, stream? } where context is the canonical pi-ai Context and options is SimpleStreamOptions with non-serializable fields stripped. Response is SSE-framed AssistantMessageEvent (terminated by data: [DONE]) when streaming, or { message: AssistantMessage } JSON when stream: false.
  • Added Vertex AI authentication via Google Application Default Credentials from GOOGLE_APPLICATION_CREDENTIALS, ~/.config/gcloud/application_default_credentials.json, or metadata server tokens, with token caching and refresh skew control via GOOGLE_VERTEX_REFRESH_SKEW_MS
  • Added support for Anthropic image message parts with type: "url" and type: "file" sources
  • Added stopSequences and frequencyPenalty to shared stream options and wired them through to OpenAI request translation
  • Added optional request cancellation support to auth-broker interactions by propagating AbortSignal into health, snapshot, usage, and refresh calls
  • Added AuthStorage.setConfigApiKey / removeConfigApiKey / clearConfigApiKeys for config-sourced per-provider bearers (e.g. models.yml providers.<name>.apiKey). The new tier sits between runtime --api-key and stored credentials in getApiKey/peekApiKey resolution, so a bearer pinned in config now beats the broker's OAuth access token. Also suppresses OAuth account_uuid attribution when active, since outbound auth is the explicit config bearer, not OAuth. describeCredentialSource reports "config override (models.yml)" for visibility.
  • Added per-model additional_rate_limits parsing to openaiCodexUsageProvider. The Codex wham/usage endpoint surfaces a separate GPT-5.3-Codex-Spark rate limit (metered_feature: codex_bengalfox) on Pro accounts; these now emit dedicated openai-codex:spark:{primary,secondary} UsageLimit entries with scope.tier = "spark", mirroring how Anthropic exposes anthropic:7d:sonnet separately from the umbrella anthropic:7d bucket. The osx-widgets client already keyed spark detection off limit.id.includes("spark"); this populates that contract end-to-end.
  • Added GET /v1/usage to the auth-broker API to expose aggregated usage reports from AuthStorage.fetchUsageReports
  • Added auth-broker usage polling response handling that returns normalized usage reports plus generation timestamp for clients (5-min per-credential cache via AuthStorage)
  • Added the auth-broker subsystem (@oh-my-pi/pi-ai/auth-broker) for sharing OAuth credentials across machines without leaking refresh tokens.
  • startAuthBroker(...) boots a Bun.serve HTTP server exposing GET /v1/healthz, GET /v1/snapshot, POST /v1/credential (upsert), POST /v1/credential/:id/refresh, and POST /v1/credential/:id/disable.
  • AuthBrokerClient is the matching HTTP client used by remote clients.
  • RemoteAuthCredentialStore is a client-side AuthCredentialStore that mirrors a broker snapshot in memory; mutating methods (replace*, upsert*, delete*ForProvider) throw because writes are server-side only.
  • AuthBrokerRefresher is the background refresh loop that pre-refreshes credentials within refreshSkewMs and disables on definitive failure (invalid_grant / non-network 401-403).
  • Added AuthStorage.exportSnapshot(), AuthStorage.upsertCredential(provider, credential), AuthStorage.forceRefreshCredentialById(id), and AuthStorage.disableCredentialById(id, cause) public methods consumed by the auth-broker server.
  • Added AuthStorageOptions.refreshOAuthCredential override so a remote-store client can route every OAuth refresh through the broker instead of the local OAuth endpoint.
  • Added REMOTE_REFRESH_SENTINEL ("__remote__") — the wire placeholder substituted for OAuth refresh tokens in broker snapshots; clients never see the real refresh token.
  • Exposed the OAuth provider catalog (getOAuthProviders, OAuthProvider, OAuthProviderInfo) and refreshOAuthToken through the package barrel so the coding-agent CLI can target them without reaching into utils/oauth.
  • Added the auth-gateway subsystem (@oh-my-pi/pi-ai/auth-gateway) — a forward-proxy that sits between unauthenticated clients (the macOS usage widget, llm-git, robomp containers, …) and the broker. Clients send standard provider-format requests; the gateway parses them into omp's canonical Context, dispatches through pi-ai's streamSimple(), and translates the canonical event stream back to the matching wire format. Authorization is injected server-side so access tokens never leave the gateway host. Wire surface:
  • GET /healthz — unauth liveness.
  • GET /v1/usage — aggregated provider usage; 5-min per-credential cache via AuthStorage.fetchUsageReports.
  • GET /v1/models — model catalog (scoped to providers with credentials).
  • POST /v1/chat/completions — OpenAI chat-completions in/out.
  • POST /v1/messages — Anthropic messages in/out (text + thinking + tool_use blocks, SSE event taxonomy preserved).
  • POST /v1/responses — OpenAI Responses in/out (reasoning items + function_call output items, SSE pass-through).
  • Added exports from @oh-my-pi/pi-ai/auth-gateway: startAuthGateway, AuthGatewayServerOptions, AuthGatewayBootOptions, AuthGatewayServerHandle, ModelResolver, DEFAULT_AUTH_GATEWAY_BIND. Per-format parseRequest / encodeResponse / encodeStream triples are reachable via the ./providers/* subpath as openai-chat-server, anthropic-messages-server, and openai-responses-server.
  • Added listProvidersWithEnvKey() to enumerate every provider with an env-var fallback (used by the new migrate command in coding-agent).

Changed

  • Changed GET /v1/snapshot to support generation-based polling with If-None-Match and wait for long-poll updates and to return 304 when no snapshot changes are available
  • Changed Bedrock credential resolution for streaming calls to prefer environment keys, AWS profile/SSO credentials, and IMDSv2 fallback when available
  • Changed auth-gateway parsing for OpenAI chat-completions and Responses to ignore unsupported SDK-only fields instead of rejecting requests
  • Changed auth-gateway protocol handling to include CORS headers on responses and support browser-origin requests
  • Changed prompt-cache handling to resolve cache keys from request metadata and headers and preserve them through protocol translation
  • Changed Anthropic messages parsing to forward request metadata through to downstream execution
  • Changed usage report caching to use a 5-minute per-credential TTL with jittered refresh timing to reduce usage endpoint rate-limit collisions
  • Changed usage polling failure handling so transient errors continue serving the last known report instead of returning null and dropping the credential from usage aggregates after cache expiry
  • Changed sanitizeSchemaForGoogle to normalize snake_case schema keys (such as any_of and additional_properties) to camelCase and auto-generate propertyOrdering for multi-property objects
  • Changed strict-mode sanitization to resolve $ref nodes with sibling keys by inlining and merging referenced local definitions
  • Changed strict-mode sanitization to flatten single-entry allOf nodes and remove the allOf wrapper
  • Changed Anthropic tool schema normalization to preserve supported metadata keywords such as $ref, $defs, $schema, enum, const, default, title, and nullable instead of stripping them
  • Changed string schema processing to retain only supported format values (date-time, time, date, duration, email, hostname, uri, ipv4, ipv6, uuid) and demote unsupported format values to description hints

Fixed

  • Fixed OAuth credential refresh flow so concurrent manual and background refreshes now share one in-flight attempt per credential, and RemoteAuthCredentialStore now re-synchronizes before using near-expiring OAuth credentials
  • Fixed stale-credential handling after auth failures by waiting for updated broker snapshots and refreshing suspect credentials through broker endpoints before continuing
  • Fixed Google Generative AI startup behavior to throw a clear API-key-required error when no key is configured
  • Fixed AWS Bedrock image message serialization to preserve base64 source.bytes payloads instead of decoding and rebuilding them
  • Fixed Google provider error handling to extract the API-reported error.message from JSON response bodies when available
  • Fixed RemoteAuthCredentialStore.getUsageReport to return the matching credential-specific usage report and coalesce parallel callers into one broker /v1/usage fetch
  • Fixed auth-broker credential upload validation to reject the remote refresh-token sentinel and prevent storing a non-refresh value
  • Fixed OpenAI Responses streaming output to emit reasoning_summary_text events and parse/send summary_text reasoning payloads
  • Fixed Anthropic stop-sequence handling by trimming requests to the API limit of four entries before forwarding
  • Fixed prompt caching behavior across protocol translations so cached-token usage is preserved when Anthropic and OpenAI requests are routed through each other
  • Fixed Claude usage fetching to retry transient 429 and 5xx responses with exponential backoff, respecting Retry-After before returning failure
  • Fixed auth-gateway request translation to preserve OpenAI Responses string/system message content, reasoning replay payloads, completed item text in stream item-done events, Anthropic tool-result ordering, and OpenAI Chat/Responses cached-token usage totals
  • Fixed auth-gateway failure handling so unsupported request controls, upstream terminal errors, non-streaming aborts, and already-aborted client requests fail explicitly instead of being accepted, ignored, or encoded as successful HTTP 200 responses
  • Fixed Gemini CLI / Antigravity tool schema normalization to run the full Cloud Code Assist pipeline, matching shared Google schema handling for union/object merging and nullable extraction
  • Fixed stripped validation hints to be preserved as description spill text ({key: value} blocks) when normalizeSchemaForGoogle and normalizeSchemaForCCA drop unsupported schema keywords
  • Fixed sanitizeSchemaForGoogle to collapse nullability forms (type:'null' and null-bearing anyOf variants) into nullable while preserving remaining variants
  • Fixed sanitizeSchemaForGoogle to inline local $defs references instead of dropping $ref/$defs structure during Google schema sanitization
  • Fixed normalizeAnthropicToolSchema to handle self-referential schemas without infinite recursion
  • Fixed object schema normalization so explicit open-map declarations (additionalProperties: true and schema-valued additionalProperties) are preserved instead of being converted to closed objects
  • Fixed unsupported schema constraints on arrays and strings (maxItems, uniqueItems, pattern, minLength, maxLength, and minItems when greater than 1) by demoting them into description rather than dropping them

Security

  • Hardened auth-gateway bearer-token checks with constant-time comparison to avoid timing-side-channel leaks

[15.1.2] - 2026-05-15

Breaking Changes

  • Rejected draft-07 tuple and dependency keywords (items arrays, dependencies, additionalItems) in JSON Schema validation

Added

  • Added responseHeaders, responseStatus, and responseRequestId fields to MockResponse so mock providers can provide synthetic ProviderResponseMetadata
  • Added onResponse metadata emission for mocks that sends lowercased headers and a default status of 200 before streaming when response headers are configured
  • Added recursive strict-mode sanitization for array prefixItems entries so tuple schemas now enforce object constraints per item

Changed

  • Normalized legacy draft-07 JSON Schema constructs used in tool parameters (items arrays, additionalItems, definitions, dependencies) to draft 2020-12 before OpenAI/Google/CCA sanitization, wire conversion, and argument validation
  • Reworked OpenAI response schema adaptation to rewrite oneOf into anyOf while preserving existing anyOf branches
  • Changed tuple array validation to validate per-index schemas from prefixItems and apply items only to remaining elements

Fixed

  • Fixed validation of plain JSON Schema tool arguments that omitted a $schema URI so draft-07-shaped schemas now pass validation instead of being rejected
  • Fixed tuple-array validation for legacy JSON Schema tool schemas to enforce additionalItems: false and per-position constraints after automatic draft upgrade
  • Fixed Anthropic tool schema normalization to recurse into prefixItems so unsupported constraints inside tuple items are stripped in the generated input schema
  • Fixed Anthropic tool-schema normalization stripping the body of explicit open additionalProperties (e.g. Zod's z.record(z.string(), z.unknown()) compiling to additionalProperties: {}) by unconditionally overwriting it with false, which closed record-style fields and prevented models from supplying any key. The coding-agent's resolve tool exposes plan-approval titles via such a field, so Kimi K2 (and any other Anthropic-shaped provider) could not pass extra: { title }, blocking plan mode entirely (#1104)
  • Fixed Anthropic strict tool planning to leave tools with open additionalProperties maps non-strict instead of sending schemas Anthropic rejects.

[15.1.0] - 2026-05-15

Breaking Changes

  • Removed TypeBox root exports (Type, Static, and TSchema) from the package entrypoint, so callers importing those symbols from @oh-my-pi/pi-ai must migrate to zod or @oh-my-pi/pi-ai/types

Added

  • Added support for defining tool schemas with Zod (z.object, z.string, etc.) by allowing Tool.parameters to be either Zod schemas or legacy JSON Schema objects and converting them to provider wire format automatically
  • Added package-level schema helpers in the zod/v4 style by exporting z and ZodType from the root entrypoint
  • Added a mock API provider via createMockModel to build Model<"mock"> instances for fully in-memory, deterministic assistant streams in tests
  • Added streamMock and registerMockApi so mock responses can be consumed through stream() and the global custom API registry without an external model backend
  • Added async/sync response scripting with optional context-based handlers, and new push()/reset() controls to drive multi-turn mock interactions and inspect per-call invocation state
  • Added support in mock responses for simulating tool calls, usage metadata, custom stop reasons, delayed emissions, and terminal error/aborted outcomes

Changed

  • Changed Azure OpenAI Responses tool schema conversion to sanitize tool parameter schemas and rewrite oneOf branches as anyOf so tool calls remain compatible with Azure's schema expectations
  • Changed Static<S> to extract a schema object’s static type when present, improving inferred tool argument types for non-Zod parameter definitions
  • Changed Static typing behavior so it now infers argument types from Zod schemas and defaults to unknown for non-Zod JSON Schema parameter definitions
  • Restored the default steady-state stream idle timeout to 120s (regressed in 15.0.0). 30s was too aggressive for reasoning models, slow proxies, and tool-call planning gaps, surfacing as repeated Provider stream stalled while waiting for the next event errors. Existing PI_STREAM_IDLE_TIMEOUT_MS / PI_OPENAI_STREAM_IDLE_TIMEOUT_MS overrides are unchanged.

Fixed

  • Preserved top-level unknown fields in validated tool-call arguments so extra root properties are retained after schema coercion
  • Fixed coercion for Zod record fields by parsing JSON-stringified record arguments into objects
  • Validated legacy draft-07 JSON Schema tool parameters directly instead of converting through Zod, improving support for features like $ref, definitions, nullable, and uniqueItems
  • Fixed Cloud Code Assist schema preparation to strip unsupported propertyNames and fall back to a minimal tool schema when schema meta-validation detects malformed keywords
  • Fixed OpenAI Completions streaming to avoid treating non-output chunks (including role-only preambles) as progress events so idle-timeout watchdog behavior no longer hangs on no-op streamed chunks
  • Fixed Cloud Code Assist schema compatibility checks by replacing strict AJV meta-schema validation with structural JSON Schema validation to avoid rejecting structurally valid tool schemas
  • Fixed lazy built-in provider streams (anthropic-messages, bedrock-converse-stream, cursor-agent, google-*, ollama-chat, openai-*) prematurely aborting slow first-token responses with Provider stream stalled while waiting for the next event. The lazy-stream watchdog wrapper was treating the synthetic start event (yielded immediately by every provider before the model emits any tokens) as the first real item, which caused the watchdog to drop from firstItemTimeoutMs (100s) to idleTimeoutMs (30s) before the upstream model had produced anything. The shared iterateWithIdleTimeout now keeps awaitingFirstItem true until a real progress item arrives, and the lazy-stream wrapper marks start as a non-progress keepalive (#1073 regression).
  • Heal leaked Kimi K2 chat-template tool-call tokens (<|tool_calls_section_begin|> … <|tool_call_argument_begin|> … <|tool_calls_section_end|>) that some hosts (native kimi-code API, OpenRouter, Fireworks, etc.) emit into delta.content instead of structured tool_calls. The OpenAI-completions stream consumer now strips the markers from visible text, reconstructs the embedded calls as proper toolCall content blocks (stream-aware, token-boundary-safe), and promotes finish_reason: stop to toolUse when calls were healed.
  • Fixed OpenAI-completions Kimi K2 healed-call promotion clobbering non-stop terminal finish reasons (error, length, aborted); promotion now only fires when the prior stop reason is the natural-completion stop
  • Fixed OpenAI-completions duplicate Kimi tool calls when a single chunk delivers both leaked markers and a structured delta.tool_calls; the healer now strips visible markers but discards its synthesized calls so structured payloads remain the single source of truth
  • Fixed Kimi tool-call healer synthesizing a bogus empty call when assistant text mentions a literal <|tool_call_end|> (or <|tool_call_begin|> / <|tool_call_argument_begin|>) outside an active <|tool_calls_section_begin|>…<|tool_calls_section_end|> section; the tokens now survive as text
  • Fixed OpenAI-completions ignoring per-request StreamOptions.streamFirstEventTimeoutMs when configuring the underlying OpenAI SDK HTTP timeout, causing slow-before-headers providers to be aborted at the env default before the wrapping watchdog armed
  • Fixed JSON Schema validator silently accepting values that violate propertyNames, patternProperties, dependentRequired, dependencies, if/then/else, contains, and prefixItems; the in-tree validator now enforces these keywords instead of falling through. unevaluatedProperties/unevaluatedItems remain permissive but log a one-time warning so tool authors are not surprised.
  • Fixed recursive $ref schemas being treated as universally valid: the validator previously short-circuited on the second occurrence of any ref it had already seen, so nested values violating the referenced sub-schema passed. Cycle detection now keys on (ref, value-identity) pairs with a depth cap for primitive values, so genuine sub-tree violations are still caught.
  • Fixed JSON Schema meta-validator accepting malformed if/then/else and dependencies keywords; each conditional sub-schema is now structurally validated and draft-07 dependencies accepts either a schema or a string array of dependent keys.
  • Fixed Zod-emitted wire schemas dropping null-valued unknown root fields before preserveUnknownRootFields could snapshot them, so callers like task.simple no longer lose a schema: null argument and downstream rejection paths fire as intended.
  • Fixed mock provider partial Usage to recompute totalTokens (and cost.total when cost components are supplied) when omitted, instead of reporting 0
  • Fixed mock provider auto-generated tool-call IDs to use a per-instance counter (now reset by reset()), so test order no longer affects IDs across createMockModel() instances

[15.0.2] - 2026-05-15

Fixed

  • Fixed StreamOptions.fetch typing to accept fetch-compatible override functions that do not expose preconnect, allowing custom fetch implementations to be used without type errors across runtimes
  • Fixed Moonshot Kimi K2.6 forced tool calls to send thinking: { type: "disabled" }, avoiding tool_choice 'specified' is incompatible with thinking enabled 400s while preserving the requested named tool (#1077).

[15.0.1] - 2026-05-14

Breaking Changes

  • Increased the minimum Bun runtime version to >=1.3.14 for the @aws-? package

Added

  • Added installH2Fetch to patch globalThis.fetch so HTTPS requests attempt HTTP/2 over ALPN with automatic HTTP/1.1 fallback when HTTP/2 is unsupported
  • Added priority service-tier traffic to the premiumRequests accounting on OpenAI and OpenAI Codex providers. Sending serviceTier: "priority" now increments usage.premiumRequests by 1 per request, matching the existing GitHub Copilot premium-request budget semantics so downstream consumers (e.g. the omp stats "Premium Reqs" card and /usage) reflect priority traffic alongside Copilot premium calls.

[15.0.0] - 2026-05-13

Added

  • Added AuthStorage.onCredentialDisabled(listener) — a multi-subscriber on/off API for credential_disabled events. Returns an unsubscribe function; calling it more than once is a no-op. Multiple subscribers all receive every disable event, with synchronous and async exceptions isolated per-listener so a misbehaving subscriber cannot starve the rest of the chain. Buffer-and-replay semantics are preserved: events emitted while no listener is subscribed are buffered (FIFO, capped at 32) and replayed once to the listener that triggers the empty→non-empty transition. After every subscriber unsubscribes, subsequent disable events buffer again until the next subscribe.

Fixed

  • Fixed OAuth credentials being silently disabled when two omp processes (or any two AuthStorage instances sharing a agent.db) race on token refresh. Anthropic rotates refresh tokens on every use, so the loser's invalid_grant response previously soft-deleted the row that the winner just rotated, forcing the user to /login again. #tryOAuthCredential now re-reads the row from disk before declaring a definitive failure: if the persisted refresh differs from the snapshot it tried, the peer-rotated credential is reloaded and the request retries against the fresh token instead of disabling the live row.
  • Closed a remaining race window in OAuth refresh-failure handling: between re-reading the credential row to check for peer rotation and the subsequent soft-delete, another process could still complete a refresh and rotate the row, leaving us to disable the freshly-rotated credential by id. The disable now runs as a single CAS update conditioned on the row's data still matching the snapshot we tried to refresh, and on disabled_cause IS NULL. If the CAS reports 0 rows changed (peer rotation, or row already disabled by a concurrent failure on the same snapshot), we reload from disk and retry instead of mutating the wrong row or emitting a spurious credential_disabled event.
  • Lazy built-in provider streams now enforce the shared idle watchdog and abort stalled provider requests, so session auto-retry can continue after transient network drops instead of remaining stuck. Caller aborts still terminate as aborted.

Changed

  • Lowered the default steady-state stream idle timeout from 120s to 30s while preserving the existing environment overrides.

[14.9.3] - 2026-05-10

Fixed

  • Anthropic provider now retries generic transient connect failures (unable to connect, fetch failed, connection error, etc.) by falling back to the shared isRetryableError allowlist after the provider-specific patterns. Previously these errors bypassed the hand-curated regex in isProviderRetryableError and aborted the stream on the first attempt, while the OpenAI SDK and Codex fetchWithRetry paths already handled them.

[14.9.0] - 2026-05-10

Fixed

  • Fixed silent forwarding of image content (for example Python plot output rendered in the terminal) to models without vision support, which produced opaque 404 errors from upstream. Image blocks are now stripped and replaced with a [image omitted: model does not support vision] placeholder for non-vision models, including tool-result payloads (#967, #968).

  • Added AuthStorage onCredentialDisabled callback (sync or async) so embedders can react when a credential is automatically disabled (e.g. OAuth refresh fails with invalid_grant) — useful for surfacing a banner or auto-launching a re-login flow instead of letting the credential silently disappear. Sync throws and async rejections are both caught and logged so a misbehaving subscriber cannot break the disable path.

  • Added Anthropic OAuth account.uuid and account.email_address extraction from the /v1/oauth/token exchange and refresh responses; both AnthropicOAuthFlow.exchangeToken() and refreshAnthropicToken() now populate OAuthCredentials.{accountId, email} so downstream consumers can attribute requests to the authenticated account without a separate /api/oauth/profile round-trip.

  • Added onSseEvent stream diagnostics so HTTP SSE providers can expose raw SSE frames without changing parsed model output.

  • Added streamIdleTimeoutMs option (and PI_STREAM_IDLE_TIMEOUT_MS env override; PI_OPENAI_STREAM_IDLE_TIMEOUT_MS remains a backward-compatible alias) for a steady-state inter-event watchdog. Set to 0 to disable.

  • Added a semantic-progress predicate to OpenAI Responses and Codex SSE/WebSocket transports so response.in_progress-style keepalives no longer reset the idle deadline on stalled tool calls.

Changed

  • Anthropic streams now enforce a steady-state idle timeout (defaults to 120s, same control as PI_STREAM_IDLE_TIMEOUT_MS) in addition to the first-event watchdog. Long-running responses that go fully silent between events will now surface as Anthropic stream stalled while waiting for the next event instead of hanging.
  • Fixed resolveAnthropicMetadataUserId() to accept JSON-format user_id values that match real Claude Code's payload shape ({ device_id, account_uuid, session_id, ... } from services/api/claude.ts:getAPIMetadata). Previously only the synthetic user_<hex>_account_<uuid>_session_<uuid> cloaking format was accepted on OAuth, which caused stable session-keyed metadata supplied by callers to be discarded and replaced with fresh random entropy on every request — defeating session-count attribution on the Claude OAuth path.

[14.8.0] - 2026-05-09

Fixed

  • Fixed Gemini 3 Pro thinking metadata so medium effort is rejected with the expected error instead of being silently accepted: ThinkingConfig now carries an optional explicit levels list that survives expandEffortRange, letting non-contiguous supported sets (e.g. [low, high]) round-trip through enrichment.
  • Fixed Kimi Code OAuth expiry handling to refresh access tokens 5 minutes before server expiry, avoiding daily 401s from using tokens right up to the cutoff.
  • Fixed OpenAI Responses custom tool replay to preserve custom tool call item IDs with the ctc_ prefix instead of rewriting them as fc_ function-call IDs (#977).

[14.7.6] - 2026-05-07

Added

  • Added hideThinkingSummary option to SimpleStreamOptions. When true, streamSimple requests that the underlying provider omit reasoning/thinking summaries: Anthropic receives thinking.display = "omitted" (where supported), and OpenAI Responses / Azure / Codex providers leave reasoning.summary unset so the server skips emitting the human-readable summary stream entirely.

Changed

  • Changed OpenAI Responses, Azure OpenAI Responses, and OpenAI Codex providers to omit reasoning.summary from requests when reasoningSummary is explicitly null (previously fell back to "auto").

[14.7.5] - 2026-05-07

Added

  • Added OpenAICompat.supportsMultipleSystemMessages so chat-completions hosts can opt out of separate leading system blocks. Auto-detected as true for OpenAI, Azure, OpenRouter, Cerebras, Together, Fireworks, Groq, DeepSeek, Mistral, xAI, Z.ai, GitHub Copilot, and Zenmux; false for MiniMax, Alibaba Dashscope, and Qwen Portal whose chat templates reject follow-up system messages. Unknown OpenAI-compatible hosts (custom vLLM/local) default to false; users can opt back in via compat.supportsMultipleSystemMessages: true.

Fixed

  • Fixed strict-template OpenAI-compatible hosts (e.g. Qwen 3.5+ via vLLM, MiniMax) rejecting follow-up system/developer messages by coalescing ordered system prompts into a single block joined by \n\n when compat.supportsMultipleSystemMessages is false. Canonical hosts continue to receive separate blocks so KV-cache reuse stays effective when only the trailing prompt changes (#958).

[14.7.2] - 2026-05-06

Fixed

  • Fixed VLLM model discovery to use max_model_len as the context window when the endpoint reports it.
  • Fixed custom Ollama Cloud/local-proxy model aliases (for example deepseek-v4-pro:cloud) to inherit bundled cache-pricing metadata when the upstream model is known (#937).
  • Fixed local Ollama model discovery to apply /api/show thinking and vision capabilities in addition to native context windows (#928).

[14.7.0] - 2026-05-04

Breaking Changes

  • Changed Context.systemPrompt from a string to string[], so callers must now pass an array of prompts instead of a single string
  • Changed behavior will throw at runtime for non-array system prompts because request builders now normalize system prompts as an array

Added

  • Added support for multiple system prompts by changing Context.systemPrompt to an ordered string array and preserving provider-appropriate instruction precedence

Changed

  • Changed request builders for Anthropic, OpenAI, Bedrock, Azure, Cursor, Google, and Ollama to propagate every non-empty system prompt entry without demoting durable instructions into ordinary conversation turns

Fixed

  • Filtered out empty normalized system prompts so blank entries are no longer sent to providers
  • Removed blank system prompt strings from provider payloads to avoid unnecessary empty instruction messages

[14.6.6] - 2026-05-04

Added

  • Added always-on OpenRouter response caching (1h TTL) by sending X-OpenRouter-Cache: true and X-OpenRouter-Cache-TTL: 3600 on every OpenRouter request — identical requests replay from OpenRouter's edge cache for free. https://openrouter.ai/docs/features/response-caching

[14.6.4] - 2026-05-03

Fixed

  • Fixed OpenAI Codex websocket continuations to retry with full context when previous_response_id expires server-side instead of surfacing previous_response_not_found.

[14.6.2] - 2026-05-03

Added

  • Added EventStream.fail(err) method to terminate the async iterator with an error, enabling consumers to catch stream-level failures via for await without hanging

Fixed

  • Fixed OpenAI Responses tool schema conversion to rewrite non-strict oneOf unions to anyOf before sending tools to the Responses API (#920)

[14.6.0] - 2026-05-02

Added

  • Added disableReasoning to stream and OpenAI completion options to force reasoning off for models that support it, sending reasoning: { enabled: false } for OpenRouter-compatible requests
  • Added thinkingDisplay option to Anthropic options to control whether adaptive and explicit reasoning is returned as summarized or omitted
  • Added Anthropic model compatibility flags supportsEagerToolInputStreaming and supportsLongCacheRetention for API-capability-specific request behavior

Changed

  • Changed Anthropic request payloads to send thinking: { type: "disabled" } when thinkingEnabled is explicitly false on reasoning-enabled models
  • Changed Anthropic cache retention handling so cacheRetention: "long" now uses ttl: "1h" only for canonical Anthropic endpoints with long-cache support
  • Changed Anthropic tool schema generation to include eager_input_streaming only on models that advertise support
  • Changed Anthropic OAuth login flow to include browser fallback guidance and richer error context when token exchange or refresh fails

Fixed

  • Fixed Anthropic non-thinking requests to include the caller-provided temperature value in request payloads
  • Fixed Anthropic claude-opus-4-7 non-thinking payloads to omit sampling fields (temperature, top_p, and top_k)
  • Fixed OpenAI Codex base URL normalization so configured base URLs with or without /codex or /codex/responses now resolve to /codex/responses
  • Fixed OpenAI Codex websocket handling to parse JSON from non-string message payloads including ArrayBuffer, typed arrays, and Blob values
  • Fixed OpenAI Codex websocket handshakes to replace stale openai-beta values with the websocket beta and avoid sending request-body headers over websocket transport
  • Fixed abort tracking so caller-initiated cancellations are treated as user aborts even after local watchdog timeouts, preventing unintended automatic retries
  • Fixed Anthropic stream handling to parse raw SSE envelopes directly, ignore unrelated events, and repair malformed JSON in SSE payloads
  • Fixed Anthropic streaming to emit an explicit error when the SSE stream ends without a message_stop event
  • Fixed OpenAI Codex websocket continuations to send true previous_response_id deltas for store: false transcripts, expose request stats, and default text verbosity to low unless explicitly overridden.
  • Fixed OpenAI Codex websocket append reuse after response.completed terminal events.

[14.5.14] - 2026-05-01

Added

  • Added package-level google-gemini-headers exports (getGeminiCliHeaders, getGeminiCliUserAgent, getAntigravityHeaders, extractRetryDelay, and ANTIGRAVITY_SYSTEM_INSTRUCTION) for header and retry handling reuse without importing full Google providers

Changed

  • Changed package exports and streaming/provider wiring to load heavy Google/Kimi/GitLab/synthetic provider modules lazily through register-builtins, reducing startup import overhead from optional provider SDKs

Fixed

  • Fixed DeepSeek V4 tool-call follow-up 400 errors from three root causes:
    • Mapped reasoning_effort "xhigh" to "max" for DeepSeek-family models on any provider (NVIDIA, OpenCode-Go, etc.), not just deepseek
    • Recovered reasoning_content from thinking blocks with valid signatures that were filtered by the non-empty-text check
  • Added empty-string fallback when reasoning_content is genuinely absent (e.g. proxy-stripped) but the provider requires the field

[14.5.13] - 2026-05-01

Breaking Changes

  • Removed utils/oauth re-exports from the package entrypoint, so OAuth helper imports from the root module must be updated

[14.5.10] - 2026-04-30

Added

  • Added provider response metadata callbacks for Anthropic and OpenAI streaming requests.

[14.5.9] - 2026-04-30

Added

  • Added usage.reasoningTokens to OpenAI and Google usage output when providers report reasoning/thinking tokens
  • Added usage.cttl.ephemeral5m and usage.cttl.ephemeral1h to report Anthropic cache-write TTL token buckets
  • Added usage.server.webSearch and usage.server.webFetch to report Anthropic server tool-call request counts

Fixed

  • Fixed OpenAI usage attribution to avoid double-counting reasoning_tokens in output totals
  • Fixed Anthropic streaming usage handling so a previously populated cache TTL breakdown is preserved when later events omit cache_creation

[14.5.4] - 2026-04-28

Changed

  • Changed OpenAI custom Lark grammar payloads to strip comments and blank lines before sending provider requests.

Fixed

  • Fixed OpenAI Codex GPT model pricing by inheriting matching OpenAI catalog rates for zero-priced discovered Codex entries.

[14.5.3] - 2026-04-27

Added

  • Added fireworks as a supported provider with API key login flow and credential storage
  • Added Fireworks model catalog support with fireworks-scoped openai-completions models glm-5, glm-5.1, kimi-k2.5, kimi-k2.6, and minimax-m2.7
  • Added built-in discovery wiring so providers with base URL api.fireworks.ai are recognized as OpenAI-compatible and can use streaming token control

Changed

  • Updated the built-in model catalog to use corrected contextWindow and maxTokens values for many existing models instead of placeholder limits
  • Updated several model cost entries, including cache-read pricing, to corrected values

Fixed

  • Fixed Fireworks request formatting by translating between public model IDs and API wire IDs when sending OpenAI-completions requests
  • Fixed OpenAI-compatible model parameter handling for Fireworks by allowing max_tokens to be sent during requests

[14.5.1] - 2026-04-26

Fixed

  • Fixed NVIDIA NIM DeepSeek-V4 models leaking chat-template tool-call markers (e.g. <|DSML|tool_calls|>) into visible response text by stripping the special tokens from streamed delta.content (#798)

[14.4.0] - 2026-04-26

Added

  • Added an examples option to StringEnum to include example values in the generated schema

Changed

  • Changed Anthropic tool schema generation to strip unsupported schema fields (including patternProperties), add additionalProperties: false for object types, and apply Anthropic strict-mode limits when marking tools as strict
  • Changed Anthropic strict tool planning to cap strict tools at twenty entries and convert excess optional/union parameters to nullable schemas to stay within provider constraints

Fixed

  • Fixed Anthropic tool schema compilation failures by keeping the write tool out of the strict-tool allowlist when the full coding-agent tool set is active
  • Fixed Anthropic 400 tools.*.custom: For 'object' type, property 'minItems' is not supported by stripping minItems from object-shaped JSON schema nodes (array nodes still keep supported minItems values)
  • Fixed Anthropic tool schemas that used tuple-style arrays by stripping unsupported maxItems and only preserving provider-supported minItems values
  • Fixed Anthropic and OpenRouter Anthropic tool calls that previously failed with compiled grammar is too large by retrying automatically without strict tool schemas and reusing non-strict mode for subsequent requests in the same provider session
  • Fixed parsing of JSON tool arguments containing raw control characters inside string values (such as embedded newlines) by escaping them before JSON parsing
  • Fixed validateToolArguments to accept stringified objects and arrays that include literal control characters inside string fields
  • Fixed OpenAI Codex Spark OAuth selection to fall back to non-Pro accounts when no ChatGPT Pro account is connected, so users without a Pro account can still attempt Spark requests in case the server permits access.

[14.3.0] - 2026-04-25

Added

  • Added support for Claude Opus 4.7 (claude-opus-4-7) model (#726)
    • Suppresses sampling parameters (temperature/top_p/top_k) that Opus 4.7 rejects
    • Enables display: "summarized" for adaptive thinking to restore visible thinking content

Fixed

  • Fixed Cursor provider losing conversation history on follow-up turns (model responding "this appears to be the start of our session") by populating ConversationStateStructure.rootPromptMessagesJson with JSON blob IDs for the system prompt plus prior user/assistant/tool-result messages. Cursor's server builds the model prompt from rootPromptMessagesJson, not from the protobuf turns[] tree, so sending only the system prompt there caused prior turns to be dropped
  • Fixed Cursor provider multi-turn conversations failing with Connect error internal: Blob not found on the second message by storing ConversationStateStructure.turns, AgentConversationTurnStructure.user_message, and AgentConversationTurnStructure.steps as content-addressed blob IDs in the KV store (matching the existing handling for rootPromptMessagesJson) rather than sending the raw serialized bytes inline (#678)

[14.2.1] - 2026-04-24

Fixed

  • Fixed OpenAI Codex Spark OAuth selection to require a verified ChatGPT Pro account instead of falling back to Plus or unknown-plan accounts.

[14.2.0] - 2026-04-23

Added

  • Added gpt-5.5 to the built-in model catalog for both OpenAI Responses (openai) and local litellm (openai-completions) providers
  • Added gpt-image-2 to the litellm built-in model catalog
  • Added isCopilotTransientModelError() and callWithCopilotModelRetry() helpers in utils/retry that detect GitHub Copilot's intermittent HTTP 400 model_not_supported responses for preview models (gpt-5.3-codex, gpt-5.4, gpt-5.4-mini, ...) and retry the request up to three times with backoff. OpenAI Responses, OpenAI Completions, and Anthropic provider paths now participate in this retry when the model is served through Copilot.
  • Added OpenAI Responses custom-tool grammar support for Codex-style apply_patch calls, including freeform streaming, history replay, and forced tool-choice mapping to the custom wire name.

Changed

  • Updated built-in model metadata with revised contextWindow, maxTokens, and pricing values for existing entries
  • Changed generated model policies to assign applyPatchToolType: "freeform" for first-party GPT-5 OpenAI Responses and Codex models, so regenerated models.json preserves the apply_patch custom-tool metadata.
  • Renamed rewriteCopilotAuthError to rewriteCopilotError and extended it to rewrite HTTP 400 model_not_supported after retries are exhausted with guidance about Copilot's OAuth-client-specific rollout gap (see opencode#13313).

Fixed

  • Fixed Amazon Bedrock proxy handling to honor lowercase http_proxy, https_proxy, and all_proxy environment variables when using HTTP/1 fallback
  • Fixed Amazon Bedrock streaming behind corporate HTTP proxies by using a proxy-aware HTTP/1 transport when HTTPS_PROXY, HTTP_PROXY, or ALL_PROXY is configured, including AWS SSO credential calls.
  • Fixed Amazon Bedrock requests to retry once with HTTP/1 when the AWS SDK's default HTTP/2 transport fails before streaming begins.
  • Fixed OpenAI Responses streaming to display thinking tokens from local providers (llama.cpp, etc.) that send raw reasoning_text.delta events and empty summary arrays in output_item.done. Previously, thinking content was silently dropped during streaming while non-streaming mode worked correctly.
  • Synced the bundled OpenCode Go catalog with the current docs so kimi-k2.6, mimo-v2.5, and mimo-v2.5-pro appear in offline/default model lists.

[14.1.3] - 2026-04-17

Fixed

  • Preserved user-provided session_id and x-client-request-id headers in OpenAI Responses requests instead of overriding them with automatic session-derived values
  • Stopped sending session_id and x-client-request-id headers for OpenAI Responses requests when cacheRetention is set to none
  • Fixed direct OpenAI Responses requests to send session_id and x-client-request-id from the same session-derived value as prompt_cache_key, improving prompt cache affinity for append-only sessions

[14.1.1] - 2026-04-14

Added

  • Added toolStrictMode compatibility option ("all_strict" or "none") to OpenAI-compatible model config to force tool schemas to be sent uniformly strict, uniformly non-strict, or keep mixed per-tool behavior

Changed

  • Changed Cerebras OpenAI-compatible providers to default toolStrictMode to "all_strict" unless explicitly overridden

Fixed

  • Fixed OpenAI Completions handling for providers that reject mixed strict flags by automatically retrying with non-strict tool schemas when an initial all-strict tool request fails with strict-format 400/422 errors
  • Fixed OpenAI-completions error reporting by including captured JSON error body details such as type, param, and code when a request fails without a body in the thrown SDK error
  • Fixed shell execution failure responses to preserve all result fields when sanitizing, preventing truncated metadata in stream results
  • Fixed context overflow detection to recognize model_context_window_exceeded from z.ai / GLM providers, preventing infinite retry loops when context window is exceeded (#638)
  • Fixed strict tool schema enforcement to preserve additionalProperties: false and required keys for reused nested object schemas, preventing invalid todo_write function schemas in Codex/OpenAI requests
  • Fixed GitHub Copilot reasoning regressions by preserving GPT-5.x / Claude 4.x reasoning controls instead of stripping them from requests (#773)

[14.1.0] - 2026-04-11

Added

  • Added accountId to usage report metadata

Changed

  • Changed usage parsing to emit a usage report with available fields when parsing fails, rather than returning null

Fixed

  • Fixed planType resolution to fall back to the raw payload plan_type when parsed value is absent
  • Fixed usage metadata raw fallback to preserve the original payload when parsed raw output is missing

[14.0.5] - 2026-04-11

Changed

  • Replaced GitHub Copilot authentication from VSCode extension impersonation to the opencode OAuth flow, eliminating TOS concerns. Existing users will need to re-authenticate once with /login github-copilot.
  • Simplified Copilot token handling: GitHub OAuth token is used directly for all API requests (no JWT exchange or refresh cycle).
  • Changed GitHub Copilot API base URL from api.individual.githubcopilot.com to api.githubcopilot.com.
  • Updated default OpenAI stream idle timeout to 120,000 milliseconds to keep stream generation alive longer

Fixed

  • Fixed duplicate synthetic tool results being generated when a real tool result appears later in message history
  • Fixed GitHub Copilot /models discovery to unwrap structured OAuth credentials before sending the bearer token, preserving dynamic catalog refresh for OAuth-backed callers.

Removed

  • Removed Copilot JWT proxy-ep base URL resolution (no longer needed with opencode auth).

[14.0.3] - 2026-04-09

Fixed

  • Fixed Ollama discovery cache normalization so cached models upgrade to the OpenAI Responses transport after the provider change

[14.0.0] - 2026-04-08

Breaking Changes

  • Removed coerceNullStrings function and its automatic null-string coercion behavior from JSON parsing

Added

  • Added support for OpenRouter provider with strict mode detection
  • Added automatic cleaning of literal escape sequences (\n, \t, \r) in JSON parsing to handle LLM encoding confusion
  • Added support for healing JSON with trailing junk after balanced containers (e.g., ]\n</invoke>)
  • Added CODEX_STARTUP_EVENT_CHANNEL constant and CodexStartupEvent type for monitoring Codex provider initialization status
  • Added automatic healing of malformed JSON with single-character bracket errors at the end of strings, improving LLM tool argument parsing robustness

[13.19.0] - 2026-04-05

Fixed

  • Fixed GitHub Copilot model context window detection by correcting fallback priority for maxContextWindowTokens and maxPromptTokens

  • Fixed Gemini 2.5 Pro context window detection in GitHub Copilot model limits test

  • Fixed Claude Opus 4.6 context window detection in GitHub Copilot model limits test

  • Fixed Anthropic streaming to suppress transient SDK console errors for malformed SSE keep-alive frames so the TUI only shows surfaced provider errors

  • Added environment-based credential fallback for the OpenAI Codex provider.

[13.17.6] - 2026-04-01

Fixed

  • Fixed Anthropic first-event timeouts to exclude stream connection setup from the watchdog, preserve timeout-specific retry classification after local aborts, and reset retry state cleanly between attempts

[13.17.5] - 2026-04-01

Changed

  • Increased default first-event timeout from 15s to 45s to better accommodate longer request setup times
  • Modified first-event watchdog to inherit idle timeout when it exceeds the default, ensuring consistent timeout behavior across different configurations

Fixed

  • Fixed first-event watchdog initialization timing so it no longer starts before the actual stream request is created, preventing premature timeouts during request setup
  • Fixed first-event watchdog timing so OpenAI-family providers no longer count slow request setup against the first streamed event timeout, and raised the default first-event timeout to avoid false aborts after long tool turns

[13.17.2] - 2026-04-01

Fixed

  • Fixed OpenAI-family first-event timeouts to preserve provider-specific timeout errors for retry classification instead of flattening them to generic aborts (#591)

[13.17.1] - 2026-04-01

Added

  • Added thinkingSignature field to thinking content blocks to preserve the original reasoning field name (e.g., reasoning_text, reasoning_content) for accurate follow-up requests
  • Added first-event timeout detection for streaming responses to abort stuck requests before user-visible content arrives
  • Added PI_STREAM_FIRST_EVENT_TIMEOUT_MS environment variable to configure first-event timeout (defaults to 15 seconds or idle timeout, whichever is lower)
  • Added Vercel AI Gateway to /login providers for interactive API key setup

Changed

  • Changed thinking block handling to track and distinguish between different reasoning field types, enabling proper field name preservation across multiple turns

Fixed

  • Fixed Anthropic stream timeout errors to be properly retried by recognizing first-event timeout messages
  • Fixed stream stall detection to distinguish between first-event timeouts and idle timeouts, enabling faster recovery for stuck connections
  • Fixed omp commit failing with HTTP 400 errors when using reasoning-enabled models on OpenAI-compatible endpoints that don't support the developer role (e.g., GitHub Copilot, custom proxies). Now falls back to system role when developer is unsupported.

[13.17.0] - 2026-03-30

Changed

  • Bumped zai provider default model from glm-4.6 to glm-5.1

[13.16.5] - 2026-03-29

Added

  • Added Gemma 3 27B model support for Google Generative AI

Changed

  • Updated Kwaipilot KAT-Coder-Pro V2 model display name and pricing information
  • Updated Kwaipilot KAT-Coder-Pro V2 context window from 222,222 to 256,000 tokens and max tokens from 8,888 to 80,000

Fixed

  • Fixed normalizeAnthropicBaseUrl returning empty string instead of undefined when baseUrl is empty

[13.16.4] - 2026-03-28

Added

  • Added support for Groq Compound and Compound Mini models with extended context window (131K tokens) and configurable thinking levels
  • Added support for OpenAI GPT-OSS-Safeguard-20B model with reasoning capabilities across multiple providers
  • Added support for Kwaipilot KAT-Coder-Pro V2 model across Kilo, NanoGPT, and OpenRouter providers
  • Added support for GLM-5.1 model with extended context window (200K tokens) and max output of 131K tokens
  • Added support for Qwen3.5-27B-Musica-v1 model
  • Added support for zai-org/glm-5.1 model with reasoning capabilities
  • Added support for Sapiens AI Agnes-1.5-Lite model with multimodal input (text and image) and reasoning
  • Added support for Venice openai-gpt-54-mini model

Changed

  • Updated Qwen QwQ 32B max tokens from 16,384 to 40,960 across multiple providers
  • Updated OpenAI GPT-OSS-Safeguard-20B model name to 'Safety GPT OSS 20B' and enabled reasoning capabilities
  • Updated OpenAI GPT-OSS-Safeguard-20B context window from 222,222 to 131,072 tokens and max tokens from 8,888 to 65,536
  • Updated OpenRouter Qwen QwQ 32B pricing: input from 0.2 to 0.19, output from 1.17 to 1.15, cache read from 0.1 to 0.095
  • Updated OpenRouter Claude 3.5 Sonnet pricing: input from 0.45 to 0.42, cache read from 0.225 to 0.21

[13.16.3] - 2026-03-28

Changed

  • Modified OAuth credential saving to preserve unrelated identities instead of replacing all credentials for a provider
  • Updated credential identity resolution to use provider context for more accurate email deduplication

Fixed

  • Fixed OAuth credential updates to replace matching credentials in-place rather than creating disabled rows, preventing unbounded accumulation of soft-deleted credentials

[13.15.0] - 2026-03-23

Added

  • Added isUsageLimitError() to rate-limit-utils as a single source of truth for detecting usage/quota limit errors across all providers

Fixed

  • Fixed lazy stream forwarding to properly handle final results from source streams with result() methods
  • Fixed lazy stream error handling to convert iterator failures into terminal error results instead of silently failing
  • Fixed parseRateLimitReason to recognize "usage limit" in error messages and correctly classify them as QUOTA_EXHAUSTED
  • Fixed Codex fetchWithRetry retrying 429 responses for usage_limit_reached errors for up to 5 minutes instead of returning immediately for credential switching
  • Removed usage.?limit from TRANSIENT_MESSAGE_PATTERN in retry utils since usage limits are not transient and require credential rotation
  • Fixed parseRateLimitReason not recognizing "usage limit" in Codex error messages, causing incorrect fallback to UNKNOWN classification instead of QUOTA_EXHAUSTED

[13.14.2] - 2026-03-21

Changed

  • Updated thinking configuration format from levels array to minLevel and maxLevel properties for improved clarity
  • Corrected context window from 400000 to 272000 tokens for GPT-5.4 mini and nano variants on Codex transport
  • Normalized GPT-5.4 variant priority handling to use parsed variant instead of special-casing raw model IDs
  • Added support for mini variant in OpenAI model parsing regex

Fixed

  • Fixed inconsistent thinking level configuration across multiple model definitions

[13.14.0] - 2026-03-20

Fixed

  • Fixed resumed OpenAI Responses sessions to avoid replaying stale same-provider native history on the first follow-up after process restart (#488)

Added

  • Added bundled GPT-5.4 mini model metadata for OpenAI, OpenAI Codex, and GitHub Copilot, including low-to-xhigh thinking support and GitHub Copilot premium multiplier metadata
  • Added bundled GPT-5.4 nano model metadata for OpenAI and OpenAI Codex, including low-to-xhigh thinking support

[13.13.2] - 2026-03-18

Changed

  • Modified tool result handling for aborted assistant messages to preserve existing tool results when already recorded, instead of always replacing them with synthetic 'aborted' results

[13.13.0] - 2026-03-18

Changed

  • Changed tool argument validation to always normalize optional null values before type coercion, ensuring consistent handling of LLM-generated 'null' strings

Fixed

  • Fixed tool argument validation to properly handle string 'null' values from LLMs on optional fields by stripping them during normalization
  • Improved type safety of validateToolCall and validateToolArguments functions by returning properly typed ToolCall["arguments"] instead of any

[13.12.9] - 2026-03-17

Changed

  • Extracted OpenAI compatibility detection and resolution logic into dedicated openai-completions-compat module for improved maintainability and reusability

Fixed

  • Fixed openai-responses manual history replay to strip replay-only item IDs and preserve normalized tool call_id values for GitHub Copilot follow-up turns (#457)

[13.12.0] - 2026-03-14

Added

  • Added support for qwen-chat-template thinking format to enable reasoning via chat_template_kwargs.enable_thinking
  • Added reasoningEffortMap option to OpenAICompat for mapping pi-ai reasoning levels to provider-specific reasoning_effort values
  • Added extraBody to OpenAICompat to support provider-specific request body routing fields in OpenAI-completions requests
  • Added support for reading token usage from choice-level usage field as fallback when root-level usage is unavailable
  • Added new models: DeepSeek-V3.2 (Bedrock), Llama 3.1 405B Instruct, Magistral Small 1.2, Ministral 3 3B, Mistral Large 3, Pixtral Large (25.02), NVIDIA Nemotron Nano 3 30B, and Qwen3-5-9b
  • Added close() method to AuthStorage for properly closing the underlying credential store
  • Added initiatorOverride option in OpenAI and Anthropic providers to customize message attribution

Changed

  • Changed assistant message content serialization to always use plain string format instead of text block arrays to prevent recursive nesting in OpenAI-compatible backends
  • Changed Bedrock Opus 4.6 context window from 1M to 1M and added max tokens limit of 128K
  • Changed OpenCode Zen/Go Sonnet 4.0/4.5 context window from 1M to 200K
  • Changed GitHub Copilot context windows from 200K to 128K for both gpt-4o and gpt-4o-mini
  • Changed Claude 3.5 Sonnet (Anthropic API) pricing: input from $0.5 to $0.25, output from $3 to $1.5, cache read from $0.05 to $0.025, cache write from $0 to $1
  • Changed Devstral 2 model name from '135B' to '123B'
  • Changed ByteDance Seed 2.0-Lite to support reasoning with effort-based thinking mode and image inputs
  • Changed Qwen3-32b (Groq) reasoning effort mapping to normalize all levels to 'default'
  • Changed finish_reason 'end' to map to 'stop' for improved compatibility with additional providers
  • Changed Anthropic reference model merging to prioritize bundled metadata for known models while using models.dev for newly discovered IDs

Fixed

  • Fixed reasoning_effort parameter handling to use provider-specific mappings instead of raw effort values
  • Fixed assistant content serialization for GitHub Copilot and other OpenAI-compatible backends that mirror array payloads
  • Fixed token usage calculation to properly extract cached tokens from both root and nested prompt_tokens_details fields
  • Fixed stop reason mapping to handle string values and unknown finish reasons gracefully
  • Fixed resource cleanup in AuthCredentialStore.close() to properly finalize all prepared statements before closing the database

[13.11.1] - 2026-03-13

Fixed

  • Added llama.cpp as local provider
  • Fixed auth schema V0-to-V1 migration crash when the V0 table lacks a disabled column

[13.11.0] - 2026-03-12

Added

  • Added support for Parallel AI provider with API key authentication
  • Added PARALLEL_API_KEY environment variable support for Parallel provider configuration
  • Added automatic websocket reconnection handling for connection limit errors, with fallback to SSE replay when content has already been emitted

Changed

  • Enhanced CodexProviderStreamError to include an optional error code field for better error categorization and handling

Fixed

  • Improved retry logic to handle HTTP/2 stream errors and internal_error responses from Anthropic API

[13.9.16] - 2026-03-10

Added

  • Support for onPayload callback to replace provider request payloads before sending, enabling request interception and modification
  • Support for structured text signature metadata with phase information (commentary/final_answer) in OpenAI and Azure OpenAI Responses providers
  • Support for OpenAI Codex Spark model selection with plan-based account prioritization
  • Added modelId option to getApiKey() to enable model-specific credential ranking

Changed

  • Enhanced onPayload callback signature to accept model parameter and support async payload replacement
  • Improved error messages for response.failed events to include detailed error codes, messages, and incomplete reasons
  • Refactored OpenAI Codex response streaming to improve code organization and maintainability with extracted helper functions and type definitions
  • Enhanced websocket fallback logic to safely replay buffered output over SSE when websocket connections fail mid-stream
  • Improved error recovery for websocket streams by distinguishing between fatal connection errors and retryable stream errors
  • Updated credential ranking strategy to prioritize Pro plan accounts when requesting OpenAI Codex Spark models

Fixed

  • Fixed websocket stream recovery to properly reset output state and clear buffered items when falling back to SSE after partial output
  • Fixed handling of malformed JSON messages in websocket streams to trigger immediate fallback to SSE without retry attempts

[13.9.13] - 2026-03-10

Added

  • Added isSpecialServiceTier utility function to validate OpenAI service tier values

[13.9.12] - 2026-03-09

Added

  • Added Tavily web search provider support with API key authentication

Fixed

  • Fixed OpenAI-family streaming transports to fail with an explicit idle-timeout error instead of hanging indefinitely when the provider stops sending events mid-response
  • Fixed OpenAI Codex OAuth refresh and usage-limit lookups to respect request timeouts instead of waiting indefinitely during account selection or rotation
  • Fixed OpenAI Codex prewarmed websocket requests to fall back quickly when the socket connects but never starts the response stream

[13.9.10] - 2026-03-08

Added

  • Added identity_key column to auth credentials storage for improved credential deduplication
  • Added schema versioning system to auth credentials database for safer migrations
  • Added automatic backfilling of identity keys during database schema migrations

Changed

  • Changed credential deduplication logic to use single identity key instead of multiple identifiers for better performance
  • Changed database schema to store normalized identity keys alongside credentials
  • Changed auth schema migration to support upgrading from legacy database versions with automatic data backfill

Fixed

  • Fixed API key credential matching to correctly identify when the same key is re-stored, preventing unnecessary row duplication on re-login
  • Fixed credential deduplication to correctly handle OAuth accounts with matching emails but different account IDs
  • Fixed API key replacement to reuse existing stored rows instead of accumulating disabled duplicates
  • Fixed auth storage to preserve newer recorded schema versions when opened by older binaries

[13.9.8] - 2026-03-08

Fixed

  • Fixed WebSocket stream fallback logic to safely replay buffered output over SSE when WebSocket fails after partial content has been streamed

[13.9.4] - 2026-03-07

Changed

  • Simplified API key credential storage to always replace existing credentials on re-login instead of accumulating multiple keys
  • Updated Kagi API key placeholder from kagi_... to KG_... to match current API key format
  • Updated Kagi login instructions to clarify Search API access is beta-only and provide support contact
  • Disabled usage reporting in streaming responses for Cerebras models due to compatibility issues

Fixed

  • Fixed Cerebras model compatibility by preventing stream_options usage requests in chat completions

[13.9.3] - 2026-03-07

Breaking Changes

  • Changed reasoning parameter from ThinkingLevel | undefined to Effort | undefined in SimpleStreamOptions; 'off' is no longer valid (omit the field instead)
  • Removed supportsXhigh() function; check model.thinking?.maxLevel instead
  • Removed ThinkingLevel and ThinkingEffort types; use Effort enum
  • Removed getAvailableThinkingLevels() and getAvailableThinkingEfforts() functions
  • Changed transformRequestBody() signature to require Model parameter as second argument for effort validation
  • Removed thinking.ts module export; import from model-thinking.ts instead

Added

  • Added incremental flag to OpenAIResponsesHistoryPayload to support building conversation history from multiple assistant messages instead of replacing it
  • Added dt flag to OpenAIResponsesHistoryPayload for transport-level metadata
  • Added ThinkingConfig interface to models for canonical thinking transport metadata with min/max effort levels and provider-specific mode
  • Added thinking field to Model type containing per-model thinking capabilities used to clamp and map user-facing effort levels
  • Added Effort enum (minimal, low, medium, high, xhigh) as canonical user-facing thinking levels replacing ThinkingLevel
  • Added enrichModelThinking() function to automatically populate thinking metadata on models based on their capabilities
  • Added mapEffortToAnthropicAdaptiveEffort() function to map user effort levels to Anthropic adaptive thinking effort
  • Added mapEffortToGoogleThinkingLevel() function to map user effort levels to Google thinking levels
  • Added requireSupportedEffort() function to validate and clamp effort levels per model, throwing errors for unsupported combinations
  • Added clampThinkingLevelForModel() function to clamp thinking levels to model-supported range
  • Added applyGeneratedModelPolicies() and linkSparkPromotionTargets() exports from model-thinking module
  • Added serviceTier option to control OpenAI processing priority and cost (auto, default, flex, scale, priority)
  • Added providerPayload field to messages and responses for reconstructing transport-native history
  • Added Gemini usage provider for tracking quota and tier information
  • Added getCodexAccountId() utility to extract account ID from Codex JWT tokens
  • Added email extraction from OpenAI Codex OAuth tokens for credential deduplication

Changed

  • Changed credential disabling mechanism from boolean disabled flag to disabled_cause text field for tracking why credentials were disabled
  • Changed deleteAuthCredential() and deleteAuthCredentialsForProvider() methods to require a disabledCause parameter explaining the reason for disabling
  • Changed Gemini model parsing to strip -preview suffix for consistent model identification
  • Changed OpenAI Codex websocket error handling to detect fatal connection errors and immediately fall back to SSE without retrying
  • Changed OpenAI Codex to always use websockets v2 protocol (removed v1 support)
  • Changed reasoning parameter type from ThinkingLevel to Effort in SimpleStreamOptions, removing 'off' value (callers should omit the field instead)
  • Changed thinking configuration to use model-specific metadata instead of hardcoded provider logic for effort mapping
  • Changed OpenAI Codex request transformer to accept Model parameter for effort validation instead of string model ID
  • Changed Anthropic provider to use model thinking metadata for determining adaptive thinking support instead of model ID pattern matching
  • Changed Google Vertex and Google providers to use shorter variable names for thinking config construction
  • Moved thinking-related utilities from thinking.ts to new model-thinking.ts module with expanded functionality
  • Moved model policy functions from provider-models/model-policies.ts to model-thinking.ts
  • Moved googleGeminiCliUsageProvider from providers/google-gemini-cli-usage.ts to usage/gemini.ts
  • Changed default OpenAI model from gpt-5.1-codex to gpt-5.4 across all providers
  • Changed UsageFetchContext to remove cache and now() dependencies—usage fetchers now use Date.now() directly
  • Removed resetInMs field from usage windows; consumers should calculate from resetsAt timestamp
  • Changed OpenAI Codex credential ranking to deduplicate by email when accountId matches
  • Improved OpenAI Codex error handling with retryable error detection

Removed

  • Removed thinking.ts module; use model-thinking.ts instead
  • Removed provider-models/model-policies.ts module; functionality moved to model-thinking.ts
  • Removed supportsXhigh() function from models.ts; use model.thinking metadata instead
  • Removed ThinkingLevel and ThinkingEffort types; use Effort enum instead
  • Removed getAvailableThinkingLevels() and getAvailableThinkingEfforts() functions
  • Removed model-policies export from provider-models/index.ts
  • Removed hardcoded thinking level clamping logic from OpenAI Codex request transformer; now uses model metadata
  • Removed UsageCache and UsageCacheEntry interfaces—caching is now handled internally by AuthStorage
  • Removed google-gemini-cli-usage export; use new gemini usage provider instead
  • Removed resetInMs computation from all usage providers
  • Removed cache TTL constants and cache management from usage fetchers (claude, github-copilot, google-antigravity, kimi, openai-codex, zai)

Fixed

  • Fixed credential purging to respect disabled credentials when deduplicating by email, preventing re-enablement of intentionally disabled credentials
  • Fixed OpenAI Codex websocket error reporting to include detailed error messages from error events
  • Fixed conversation history reconstruction to support incremental updates from multiple assistant messages while maintaining backward compatibility with full-snapshot payloads
  • Fixed OpenAI Codex to reject unsupported effort levels instead of silently clamping them, providing clear error messages about supported efforts
  • Fixed model cache normalization to properly apply thinking enrichment when loading cached models
  • Fixed dynamic model merging to apply thinking enrichment to merged model results
  • Fixed OpenAI Codex streaming to properly include service_tier in SSE payloads
  • Fixed type safety in OpenAI responses by removing unsafe type casts on image content blocks
  • Fixed credential purging to respect disabled credentials when deduplicating by email
  • Fixed API-key provider re-login to replace the active stored key instead of appending stale credentials that were still selected first
  • Fixed Kagi login guidance to use the correct KG_... key format and mention Search API beta access requirements

[13.9.2] - 2026-03-05

Added

  • Support for redacted thinking blocks in Anthropic messages, enabling secure handling of encrypted reasoning content
  • Preservation of latest Anthropic thinking blocks and redacted thinking content during message transformation, even when switching between Anthropic models

Changed

  • Assistant message content now includes RedactedThinkingContent type alongside existing text, thinking, and tool call blocks
  • Message transformation logic now preserves signed thinking blocks and redacted thinking for the latest assistant message in Anthropic conversations

Fixed

  • Fixed Unicode normalization to consistently apply toWellFormed() to all text content, including thinking blocks, ensuring proper handling of malformed UTF-16 sequences

[13.9.1] - 2026-03-05

Breaking Changes

  • Removed THINKING_LEVELS, ALL_THINKING_LEVELS, ALL_THINKING_MODES, THINKING_MODE_DESCRIPTIONS, and THINKING_MODE_LABELS exports
  • Renamed formatThinking() to getThinkingMetadata() with changed return type from string to ThinkingMetadata object
  • Renamed getAvailableThinkingLevel() to getAvailableThinkingLevels() and added default parameter
  • Renamed getAvailableEffort() to getAvailableEfforts() and added default parameter

Added

  • Added ThinkingMetadata type to provide structured access to thinking mode information (value, label, description)

[13.9.0] - 2026-03-05

Added

  • Exported new thinking module with Effort, ThinkingLevel, and ThinkingMode types for managing reasoning effort levels
  • Added getAvailableEffort() function to determine supported thinking effort levels based on model capabilities
  • Added parseEffort(), parseThinkingLevel(), and parseThinkingMode() functions for parsing thinking configuration strings
  • Added THINKING_LEVELS, ALL_THINKING_LEVELS, and ALL_THINKING_MODES constants for iterating over available thinking options
  • Added THINKING_MODE_DESCRIPTIONS and THINKING_MODE_LABELS for displaying thinking modes in user interfaces
  • Added formatThinking() function to format thinking modes as compact display labels

Changed

  • Refactored thinking level handling to distinguish between Effort (provider-level, no "off") and ThinkingLevel (user-facing, includes "off")
  • Updated ThinkingBudgets type to use Effort instead of ThinkingLevel for more precise token budget configuration
  • Improved reasoning option handling to explicitly support "off" value for disabling reasoning across all providers
  • Simplified thinking effort mapping logic by centralizing provider-specific clamping behavior

[13.7.8] - 2026-03-04

Added

  • Added ZenMux provider support with mixed API routing: Anthropic-owned models discovered from https://zenmux.ai/api/v1/models now use the Anthropic transport (https://zenmux.ai/api/anthropic), while other ZenMux models use the OpenAI-compatible transport.

[13.7.7] - 2026-03-04

Changed

  • Modified response ID normalization to preserve existing item ID prefixes when truncating oversized IDs
  • Updated tool call ID normalization to use fc_ prefix for generated item IDs instead of item_ prefix

Fixed

  • Fixed handling of reasoning item IDs to remain untouched during response normalization while function call IDs are properly normalized

[13.7.2] - 2026-03-04

Added

  • Added support for Kagi API key authentication via login kagi command
  • Added Kagi to the list of available OAuth providers

Fixed

  • MCP tool schemas with $ref/$defs are now dereferenced before being sent to LLM providers, fixing dangling references that left models without type definitions
  • Ajv schema validation no longer emits console.warn() for non-standard format keywords (e.g. "uint") from MCP servers, preventing TUI corruption
  • Tool schema compilation is now cached per schema identity, eliminating redundant recompilation on every tool call

[13.6.0] - 2026-03-03

Added

  • Added Anthropic Foundry gateway mode controlled by CLAUDE_CODE_USE_FOUNDRY, with support for FOUNDRY_BASE_URL, ANTHROPIC_FOUNDRY_API_KEY, ANTHROPIC_CUSTOM_HEADERS, and optional mTLS material (CLAUDE_CODE_CLIENT_CERT, CLAUDE_CODE_CLIENT_KEY, NODE_EXTRA_CA_CERTS)
  • Added LM Studio provider support with OpenAI-compatible model discovery and OAuth login.
  • Added support for LM_STUDIO_API_KEY and LM_STUDIO_BASE_URL environment variables for authentication and custom host configuration.

Changed

  • Anthropic key resolution now prefers ANTHROPIC_FOUNDRY_API_KEY over ANTHROPIC_OAUTH_TOKEN and ANTHROPIC_API_KEY when Foundry mode is enabled
  • Anthropic auth base-URL fallback now prefers FOUNDRY_BASE_URL when CLAUDE_CODE_USE_FOUNDRY is enabled

[13.5.8] - 2026-03-02

Fixed

  • Fixed schema compatibility issue where patternProperties in tool parameters caused failures when converting to legacy Antigravity format

[13.5.5] - 2026-03-01

Changed

  • Anthropic Claude system-block cloaking now leaves the agent identity block uncached and applies cache_control: { type: "ephemeral" } to injected user system blocks without forcing ttl: "1h"

Fixed

  • Anthropic request payload construction now enforces a maximum of 4 cache_control breakpoints (tools/system/messages priority order) before dispatch
  • Anthropic cache-control normalization now removes later ttl: "1h" entries when a default/5m block has already appeared earlier in evaluation order

[13.5.3] - 2026-03-01

Fixed

  • Fixed tool argument coercion to handle malformed JSON with trailing wrapper braces by parsing leading JSON containers

[13.4.0] - 2026-03-01

Breaking Changes

  • Removed TInput generic parameter from ToolResultMessage interface and removed $normative property

Added

  • hasUnrepresentableStrictObjectMap() pre-flight check in tryEnforceStrictSchema: schemas with patternProperties or schema-valued additionalProperties now degrade gracefully to non-strict mode instead of throwing during enforcement
  • generateClaudeCloakingUserId() generates structured user IDs for Anthropic OAuth metadata (user_{hex64}_account_{uuid}_session_{uuid})
  • isClaudeCloakingUserId() validates whether a string matches the cloaking user-ID format
  • mapStainlessOs() and mapStainlessArch() map process.platform/process.arch to Stainless header values; X-Stainless-Os and X-Stainless-Arch in claudeCodeHeaders are now runtime-computed
  • buildClaudeCodeTlsFetchOptions() attaches SNI and default TLS ciphers for direct api.anthropic.com connections
  • createClaudeBillingHeader() generates the x-anthropic-billing-header block (SHA-256 payload fingerprint + random build hash)
  • buildAnthropicSystemBlocks() now injects a billing header block and the Claude Agent SDK identity block with ephemeral 1h cache-control when includeClaudeCodeInstruction is set
  • resolveAnthropicMetadataUserId() auto-generates a cloaking user ID for OAuth requests when metadata.user_id is absent or invalid
  • AnthropicOAuthFlow is now exported for direct use
  • OAuth callback server timeout extended from 2 min to 5 min
  • parseGeminiCliCredentials() parses Google Cloud credential JSON with support for legacy ({token,projectId}), alias (project_id/refresh/expires), and enriched formats
  • shouldRefreshGeminiCliCredentials() and proactive token refresh before requests for both Gemini CLI and Antigravity providers (60s pre-expiry buffer)
  • normalizeAntigravityTools() converts parametersJsonSchema → parameters in function declarations for Antigravity compatibility
  • ANTIGRAVITY_SYSTEM_INSTRUCTION is now exported for use by search and other consumers
  • ANTIGRAVITY_LOAD_CODE_ASSIST_METADATA constant exported from OAuth module with ANTIGRAVITY ideType
  • Antigravity project onboarding: onboardProjectWithRetries() provisions a new project via onboardUser LRO when loadCodeAssist returns no existing project (up to 5 attempts, 2s interval)
  • getOAuthApiKey now includes refreshToken, expiresAt, email, and accountId in the Gemini/Antigravity JSON credential payload to enable proactive refresh
  • Antigravity model discovery now tries the production daily endpoint first, with sandbox as fallback
  • ANTIGRAVITY_DISCOVERY_DENYLIST filters low-quality/internal models from discovery results

Changed

  • Replaced sanitizeSurrogates() utility with native String.prototype.toWellFormed() for handling unpaired Unicode surrogates across all providers
  • Extended ANTHROPIC_OAUTH_BETA constant in the OpenAI-compat Anthropic route with interleaved-thinking-2025-05-14, context-management-2025-06-27, and prompt-caching-scope-2026-01-05 beta flags
  • claudeCodeVersion bumped to 2.1.63; claudeCodeSystemInstruction updated to identify as Claude Agent SDK
  • claudeCodeHeaders: removed X-Stainless-Helper-Method, updated package version to 0.74.0, runtime version to v24.3.0
  • applyClaudeToolPrefix / stripClaudeToolPrefix now accept an optional prefix override and skip Anthropic built-in tool names (web_search, code_execution, text_editor, computer)
  • Accept-Encoding header updated to gzip, deflate, br, zstd
  • Non-Anthropic base URLs now receive Authorization: Bearer regardless of OAuth status
  • Prompt-caching logic now skips applying breakpoints when any block already carries cache_control, instead of stripping then re-applying
  • fine-grained-tool-streaming-2025-05-14 removed from default beta set
  • Anthropic OAuth token URL changed from platform.claude.com to api.anthropic.com
  • Anthropic OAuth scopes reduced to org:create_api_key user:profile user:inference
  • OAuth code exchange now strips URL fragment from callback code, using the fragment as state override when present
  • Claude usage headers aligned: user-agent updated to claude-cli/2.1.63 (external, cli), anthropic-beta extended with full beta set
  • Antigravity session ID format changed to signed decimal (negative int63 derived from SHA-256 of first user message, or random bounded int63)
  • Antigravity requestId now uses agent-{uuid} format; non-Antigravity requests no longer include requestId/userAgent/requestType in the payload
  • ANTIGRAVITY_DAILY_ENDPOINT corrected to daily-cloudcode-pa.googleapis.com; sandbox endpoint kept as fallback only
  • Antigravity discovery: removed recommended/agentModelSorts filter; now includes all non-internal, non-denylisted models
  • Antigravity discovery no longer sends project in the request body
  • Gemini/Antigravity OAuth flows no longer use PKCE (code_challenge removed)
  • Antigravity loadCodeAssist metadata ideType changed from IDE_UNSPECIFIED to ANTIGRAVITY
  • Antigravity discoverProject now uses a single canonical production endpoint; falls back to project onboarding instead of a hardcoded default project ID
  • VALIDATED tool calling config applied to Antigravity requests with Claude models
  • maxOutputTokens removed from Antigravity generation config for non-Claude models
  • System instruction injection for Antigravity scoped to Claude and gemini-3-pro-high models only

Removed

  • Removed sanitizeSurrogates() utility function; use native String.prototype.toWellFormed() instead

[13.3.14] - 2026-02-28

Added

  • Exported schema utilities from new ./utils/schema module, consolidating JSON Schema handling across providers
  • Added CredentialRankingStrategy interface for providers to implement usage-based credential selection
  • Added claudeRankingStrategy for Anthropic OAuth credentials to enable smart multi-account selection based on usage windows
  • Added codexRankingStrategy for OpenAI Codex OAuth credentials with priority boost for fresh 5-hour window starts
  • Added adaptSchemaForStrict() helper for unified OpenAI strict schema enforcement across providers
  • Added schema equality and merging utilities: areJsonValuesEqual(), mergeCompatibleEnumSchemas(), mergePropertySchemas()
  • Added Cloud Code Assist schema normalization: copySchemaWithout(), stripResidualCombiners(), prepareSchemaForCCA()
  • Added sanitizeSchemaForGoogle() and sanitizeSchemaForCCA() for provider-specific schema sanitization
  • Added StringEnum() helper for creating string enum schemas compatible with Google and other providers
  • Added enforceStrictSchema() and sanitizeSchemaForStrictMode() for OpenAI strict mode schema validation
  • Added package exports for ./utils/schema and ./utils/schema/* subpaths
  • Added validateSchemaCompatibility() to statically audit a JSON Schema against provider-specific rules (openai-strict, google, cloud-code-assist-claude) and return structured violations
  • Added validateStrictSchemaEnforcement() to verify the strict-fail-open contract: enforced schemas pass strict validation, failed schemas return the original object identity
  • Added COMBINATOR_KEYS (anyOf, allOf, oneOf) and CCA_UNSUPPORTED_SCHEMA_FIELDS as exported constants in fields.ts to eliminate duplication across modules
  • Added tryEnforceStrictSchema result cache (WeakMap) to avoid redundant sanitize + enforce work for the same schema object
  • Added comprehensive schema normalization test suite (schema-normalization.test.ts) covering strict mode, Google, and Cloud Code Assist normalization paths
  • Added schema compatibility validation test suite (schema-compatibility.test.ts) covering all three provider targets

Changed

  • Moved schema utilities from ./utils/typebox-helpers to new ./utils/schema module with expanded functionality
  • Refactored OpenAI provider tool conversion to use unified adaptSchemaForStrict() helper across codex, completions, and responses
  • Updated AuthStorage to support generic credential ranking via CredentialRankingStrategy instead of Codex-only logic
  • Moved Google schema sanitization functions from google-shared.ts to ./utils/schema module
  • Changed export path: ./utils/typebox-helpers → ./utils/schema in main index
  • sanitizeSchemaForGoogle() / sanitizeSchemaForCCA() now accept a parameterized unsupportedFields set internally, enabling code reuse between the two sanitizers
  • copySchemaWithout() rewritten using object-rest destructuring for clarity

Fixed

  • Fixed cycle detection: WeakSet guards added to all recursive schema traversals (sanitizeSchemaForStrictMode, enforceStrictSchema, normalizeSchemaForCCA, normalizeNullablePropertiesForCloudCodeAssist, stripResidualCombiners, sanitizeSchemaImpl, hasResidualCloudCodeAssistIncompatibilities) — circular schemas no longer cause infinite loops or stack overflows
  • Fixed hasResidualCloudCodeAssistIncompatibilities: cycle detection now returns false (not true) for already-visited nodes, eliminating false positives that forced the CCA fallback schema on valid recursive inputs
  • Fixed stripResidualCombiners to iterate to a fixpoint rather than making a single pass, ensuring chained combiner reductions (where one reduction enables another) are fully resolved
  • Fixed mergeObjectCombinerVariants required-field computation: the flattened object now takes the intersection of all variants' required arrays (unioned with own-level required properties that exist in the merged schema), preventing required fields from being silently dropped or over-included
  • Fixed mergeCompatibleEnumSchemas to use deep structural equality (areJsonValuesEqual) instead of Object.is when deduplicating object-valued enum members
  • Fixed sanitizeSchemaForGoogle const-to-enum deduplication to use deep equality instead of reference equality
  • Fixed sanitizeSchemaForGoogle type inference for anyOf/oneOf-flattened const enums: type is now derived from all variants (must agree), falling back to inference from enum values; mixed null/non-null infers the non-null type and sets nullable
  • Fixed sanitizeSchemaForGoogle recursion to spread options when descending (previously only insideProperties, normalizeTypeArrayToNullable, stripNullableKeyword were forwarded; new fields unsupportedFields and seen were silently dropped)
  • Fixed sanitizeSchemaForGoogle array-valued type filtering to exclude non-string entries before processing
  • Removed incorrect additionalProperties: false stripping from sanitizeSchemaForGoogle (the field is valid in Google schemas when false)
  • Fixed sanitizeSchemaForStrictMode to strip the nullable keyword and expand it into anyOf: [schema, {type: "null"}] in the output, matching what OpenAI strict mode actually expects
  • Fixed sanitizeSchemaForStrictMode to infer type: "array" when items is present but type is absent
  • Fixed sanitizeSchemaForStrictMode to infer a scalar type from uniform enum values when type is not explicitly set
  • Fixed sanitizeSchemaForStrictMode const-to-enum merge to use deep equality, preventing duplicate enum entries when const and enum both exist with the same value
  • Fixed enforceStrictSchema to drop additionalProperties unconditionally (previously only object-valued additionalProperties was recursed into; non-object values were passed through, violating strict schema requirements)
  • Fixed enforceStrictSchema to recurse into $defs and definitions blocks so referenced sub-schemas are also made strict-compliant
  • Fixed enforceStrictSchema to handle tuple-style items arrays (previously only single-schema items objects were recursed)
  • Fixed enforceStrictSchema double-wrapping: optional properties already expressed as anyOf: [..., {type: "null"}] are not wrapped again
  • Fixed enforceStrictSchema Array.isArray type-narrowing for type field to filter non-string entries before checking for "object"

[13.3.8] - 2026-02-28

Fixed

  • Fixed response body reuse error when handling 429 rate limit responses with retry logic

[13.3.7] - 2026-02-27

Added

  • Added tryEnforceStrictSchema function that gracefully downgrades to non-strict mode when schema enforcement fails, enabling better compatibility with malformed or circular schemas
  • Added sanitizeSchemaForStrictMode function to normalize JSON schemas by stripping non-structural keywords, converting const to enum, and expanding type arrays into anyOf variants
  • Added Kilo Gateway provider support with OpenAI-compatible model discovery, OAuth /login kilo, and KILO_API_KEY environment variable support (#193)

Changed

  • Changed strict mode handling in OpenAI providers to use tryEnforceStrictSchema for safer schema enforcement with automatic fallback to non-strict mode
  • Enhanced enforceStrictSchema to properly handle schemas with type arrays containing object (e.g., type: ["object", "null"])

Fixed

  • Fixed enforceStrictSchema to properly handle malformed object schemas with required keys but missing properties
  • Fixed enforceStrictSchema to correctly process nested object schemas within anyOf, allOf, and oneOf combinators

[13.3.1] - 2026-02-26

Added

  • Added topP, topK, minP, presencePenalty, and repetitionPenalty options to StreamOptions for fine-grained control over model sampling behavior

[13.3.0] - 2026-02-26

Changed

  • Allowed OAuth provider logins to supply a manual authorization code handler with a default prompt when none is provided

[13.2.0] - 2026-02-23

Added

  • Added support for GitHub Copilot provider in strict mode for both openai-completions and openai-responses tool schemas

Fixed

  • Fixed tool descriptions being rejected when undefined by providing empty string fallback across all providers

[12.19.1] - 2026-02-22

Added

  • Exported isProviderRetryableError function for detecting rate-limit and transient stream errors
  • Support for retrying malformed JSON stream-envelope parse errors from Anthropic-compatible proxy endpoints

Changed

  • Expanded retry detection to include JSON parse errors (unterminated strings, unexpected end of input) in addition to rate-limit errors

[12.19.0] - 2026-02-22

Added

  • Added GitLab Duo provider with support for Claude, GPT-5, and other models via GitLab AI Gateway
  • Added OAuth authentication for GitLab Duo with automatic token refresh and direct access caching
  • Added 16 new GitLab Duo models including Claude Opus/Sonnet/Haiku variants and GPT-5 series models
  • Added isOAuth option to Anthropic provider to force OAuth bearer auth mode for proxy tokens
  • Added streamGitLabDuo function to route requests through GitLab AI Gateway with direct access tokens
  • Added getGitLabDuoModels function to retrieve available GitLab Duo model configurations
  • Added clearGitLabDuoDirectAccessCache function to manually clear cached direct access tokens

Changed

  • Enhanced getModelMapping() to support both GitLab Duo alias IDs (e.g., duo-chat-gpt-5-codex) and canonical model IDs (e.g., gpt-5-codex) for improved model resolution flexibility
  • Migrated AuthCredentialStore and AuthStorage into @oh-my-pi/pi-ai as shared credential primitives for downstream packages
  • Moved Anthropic auth helpers (findAnthropicAuth, isOAuthToken, buildAnthropicSearchHeaders, buildAnthropicUrl) into shared AI utilities for reuse across providers
  • Replaced CliAuthStorage with AuthCredentialStore for improved credential management with multiple credentials per provider
  • Updated models.json pricing for Claude 3.5 Sonnet (input: 0.23→0.45, output: 3→2.2, added cache read: 0.225) and Claude 3 Opus (input: 0.3→0.95)
  • Moved mapAnthropicToolChoice function from gitlab-duo provider to stream module for broader reusability
  • Enhanced HTTP status code extraction to handle string-formatted status codes in error objects

Removed

  • Removed CliAuthStorage class in favor of new AuthCredentialStore with enhanced functionality

[12.17.2] - 2026-02-21

Added

  • Exported getAntigravityUserAgent() function for constructing Antigravity User-Agent headers

Changed

  • Updated default Antigravity version from 1.15.8 to 1.18.3
  • Unified User-Agent header generation across Antigravity API calls to use centralized getAntigravityUserAgent() function

[12.17.1] - 2026-02-21

Added

  • Added new export paths for provider models via ./provider-models and ./provider-models/*
  • Added new export paths for Cursor and OpenAI Codex providers via ./providers/cursor/gen/* and ./providers/openai-codex/*
  • Added new export paths for usage utilities via ./usage/*
  • Added new export paths for discovery and OAuth utilities via ./utils/discovery and ./utils/oauth with subpath exports

Changed

  • Simplified main export path to use wildcard pattern ./src/*.ts for broader module access
  • Updated models.json export to include TypeScript declaration file at ./src/models.json.d.ts
  • Reorganized package.json field ordering for improved readability

[12.17.0] - 2026-02-21

Fixed

  • Cursor provider: bind execHandlers when passing handler methods to the exec protocol so handlers receive correct this context (fixes "undefined is not an object (evaluating 'this.options')" when using exec tools such as web search with Cursor)

[12.16.0] - 2026-02-21

Added

  • Exported readModelCache and writeModelCache functions for direct SQLite-backed model cache access
  • Added <turn_aborted> guidance marker as synthetic user message when assistant messages are aborted or errored, informing the model that tools may have partially executed
  • Added support for Sonnet 4.6 models in adaptive thinking detection

Changed

  • Updated model cache schema version to support improved global model fallback resolution
  • Improved GitHub Copilot model resolution to prefer provider-specific model definitions over global references when context window is larger, ensuring optimal model capabilities
  • Migrated model cache from per-provider JSON files to unified SQLite database (models.db) for atomic cross-process access
  • Renamed cachePath option to cacheDbPath in ModelManagerOptions to reflect database-backed storage
  • Improved non-authoritative cache handling with 5-minute retry backoff instead of retrying on every startup
  • Modified handling of aborted/errored assistant messages to preserve tool call structure instead of converting to text summaries, with synthetic 'aborted' tool results injected
  • Updated tool call tracking to use status map (Resolved/Aborted) instead of separate sets for better handling of duplicate and aborted tool results

[12.15.0] - 2026-02-20

Fixed

  • Improved error messages for OAuth token refresh failures by including detailed error information from the provider
  • Separated rate limit and usage limit error handling to provide distinct user-friendly messages for ChatGPT rate limits vs subscription usage limits

Changed

  • Increased SDK retry attempts to 5 for OpenAI, Azure OpenAI, and Anthropic clients (was SDK default of 2)
  • Changed 429 retry strategy for OpenAI Codex and Google Gemini CLI to use a 5-minute time budget when the server provides a retry delay, instead of a fixed attempt cap

[12.14.0] - 2026-02-19

Added

  • Added gemini-3.1-pro model to opencode provider with text and image input support
  • Added trinity-large-preview-free model to opencode provider
  • Added google/gemini-3.1-pro-preview model to nanogpt provider
  • Added google/gemini-3.1-pro-preview model to openrouter provider with text and image input support
  • Added gemini-3.1-pro model to cursor provider
  • Added optional intent field to ToolCall interface for harness-level intent metadata

Changed

  • Changed big-pickle model API from openai-completions to anthropic-messages
  • Changed big-pickle model baseUrl from https://opencode.ai/zen/v1 to https://opencode.ai/zen
  • Changed minimax-m2.5-free model API from openai-completions to anthropic-messages
  • Changed minimax-m2.5-free model baseUrl from https://opencode.ai/zen/v1 to https://opencode.ai/zen

Fixed

  • Fixed tool argument validation to iteratively coerce nested JSON strings across multiple passes, enabling proper handling of deeply nested JSON-serialized objects and arrays

[12.13.0] - 2026-02-19

Added

  • Added NanoGPT provider support with API-key login, dynamic model discovery from https://nano-gpt.com/api/v1/models, and text-model filtering for catalog/runtime discovery (#111)

[12.12.3] - 2026-02-19

Fixed

  • Fixed retry logic to recognize 'unable to connect' errors as transient failures

[12.11.3] - 2026-02-19

Fixed

  • Fixed OpenAI Codex streaming to fail truncated responses that end without a terminal completion event, preventing partial outputs from being treated as successful completions.
  • Fixed Codex websocket append fallback by resetting stale turn-state/model-etag session metadata when request shape diverges from appendable history.

[12.11.1] - 2026-02-19

Added

  • Added support for Claude 4.6 Opus and Sonnet models via Cursor API
  • Added support for Composer 1.5 model via Cursor API
  • Added support for GPT-5.1 Codex Mini and GPT-5.1 High models via Cursor API
  • Added support for GPT-5.2 and GPT-5.3 Codex variants (Fast, High, Low, Extra High) via Cursor API
  • Added HTTP/2 transport support for Cursor API requests (required by Cursor API)

Changed

  • Updated pricing for Claude 3.5 Sonnet model
  • Updated Claude 3.5 Sonnet context window from 262,144 to 131,072 tokens
  • Simplified Cursor model display names by removing '(Cursor)' suffix
  • Changed Cursor API timeout from 15 seconds to 5 seconds
  • Switched Cursor API transport from HTTP/1.1 to HTTP/2

[12.11.0] - 2026-02-19

Added

  • Added priority field to Model interface for provider-assigned model prioritization
  • Added CatalogDiscoveryConfig interface to standardize catalog discovery configuration across providers
  • Added type guards isCatalogDescriptor() and allowsUnauthenticatedCatalogDiscovery() for safer descriptor handling
  • Added DEFAULT_MODEL_PER_PROVIDER export from descriptors module for centralized default model management
  • Support for 11 new AI providers: Cloudflare AI Gateway, Hugging Face Inference, LiteLLM, Moonshot, NVIDIA, Ollama, Qianfan, Qwen Portal, Together, Venice, vLLM, and Xiaomi MiMo
  • Login flows for new providers with API key validation and OAuth token support
  • Extended KnownProvider type to include all newly supported providers
  • API key environment variable mappings for all new providers in service provider map
  • Model discovery and configuration for Cloudflare AI Gateway, Hugging Face, LiteLLM, Moonshot, NVIDIA, Ollama, Qianfan, Qwen Portal, Together, Venice, vLLM, and Xiaomi MiMo

Changed

  • Refactored OAuth credential retrieval to simplify storage lifecycle management in model generation script
  • Parallelized special model discovery sources (Antigravity, Codex) for improved generation performance
  • Reorganized model JSON structure to place contextWindow and maxTokens before compat field for consistency
  • Added priority field to OpenAI Codex models for provider-assigned model prioritization
  • Refactored provider descriptors to use helper functions (descriptor, catalog, catalogDescriptor) for reduced code duplication
  • Refactored models.dev provider descriptors to use helper functions (simpleModelsDevDescriptor, openAiCompletionsDescriptor, anthropicMessagesDescriptor) for improved maintainability
  • Unified provider descriptors into single source of truth in descriptors.ts for both runtime model discovery and catalog generation, improving maintainability
  • Refactored model generation script to use declarative CatalogProviderDescriptor interface instead of separate descriptor types, reducing code duplication
  • Reorganized models.dev provider descriptors into logical groups (Bedrock, Core, Coding Plans, Specialized) for better code organization
  • Simplified API resolution for OpenCode and GitHub Copilot providers using rule-based matching instead of inline conditionals
  • Refactored model generation script to use declarative provider descriptors instead of inline provider-specific logic, improving maintainability and reducing code duplication
  • Extracted model post-processing policies (cache pricing corrections, context window normalization) into dedicated model-policies.ts module for better testability and clarity
  • Removed static bundled models for Ollama and vLLM from models.json to rely on dynamic discovery instead, reducing static catalog size
  • Updated OAuthProvider type to include new provider identifiers
  • Expanded model registry (models.json) with thousands of new model entries across all new providers
  • Modified environment variable resolution to use $pickenv for providers with multiple possible env var names
  • Updated README documentation to list all newly supported providers and their authentication requirements

[12.10.1] - 2026-02-18

  • Added Synthetic provider
  • Added API-key login helpers for Synthetic and Cerebras providers

[12.10.0] - 2026-02-18

Breaking Changes

  • Renamed public API functions: getModel() → getBundledModel(), getModels() → getBundledModels(), getProviders() → getBundledProviders()

Added

  • Exported ModelManager API for runtime-aware model resolution with dynamic endpoint discovery
  • Exported provider-specific model manager configuration helpers for Google, OpenAI-compatible, Codex, and Cursor providers
  • Exported discovery utilities for fetching models from Antigravity, Codex, Cursor, Gemini, and OpenAI-compatible endpoints
  • Added createModelManager() function to manage bundled and dynamically discovered models with configurable refresh strategies
  • Added support for on-disk model caching with TTL-based invalidation
  • Added resolveProviderModels() function for runtime model resolution across multiple providers
  • Added EU cross-region inference variants for Claude Haiku 3.5 on Bedrock
  • Added Claude Sonnet 4.6 and Claude Sonnet 4.6 Thinking models to Antigravity provider
  • Added GLM-5 Free model via OpenCode provider
  • Added GLM-4.7-FlashX model via ZAI provider
  • Added MiniMax-M2.5-highspeed model across multiple providers (minimax-code, minimax-code-cn, minimax, minimax-cn)
  • Added Claude Sonnet 4.6 model to OpenRouter provider
  • Added Qwen 3.5 Plus model to Vercel AI Gateway provider
  • Added Claude Sonnet 4.6 model to Vercel AI Gateway provider

Changed

  • Renamed getModel() to getBundledModel() to clarify it returns compile-time bundled models only
  • Renamed getModels() to getBundledModels() for consistency
  • Renamed getProviders() to getBundledProviders() for consistency
  • Refactored model generation script to use modular discovery functions instead of monolithic provider-specific logic
  • Updated models.json with new model entries and pricing updates across multiple providers
  • Updated pricing for deepseek/deepseek-v3 model on OpenRouter
  • Updated maxTokens from 65536 to 4096 for deepseek/deepseek-v3 on OpenRouter
  • Updated pricing and maxTokens for mistralai/mistral-large-2411 on OpenRouter
  • Updated pricing for qwen/qwen-max on Together AI
  • Updated pricing for qwen/qwen-vl-plus on Together AI
  • Updated pricing for qwen/qwen-plus on Together AI
  • Updated pricing for qwen/qwen-turbo on Together AI
  • Expanded EU cross-region inference variant support to all Claude models on Bedrock (previously limited to Haiku, Sonnet, and Opus 4.5)

[12.8.0] - 2026-02-16

Added

  • Added contextPromotionTarget model property to specify preferred fallback model when context promotion is triggered
  • Added automatic context promotion target assignment for Spark models to their base model equivalents
  • Added support for Brave search provider with BRAVE_API_KEY environment variable

Changed

  • Updated Qwen model context window and max token limits for improved accuracy

[12.7.0] - 2026-02-16

Added

  • Added DeepSeek-V3.2 model support via Amazon Bedrock
  • Added GLM-5 model support via OpenCode
  • Added MiniMax M2.5 model support via OpenCode

Changed

  • Updated GLM-4.5, GLM-4.5-Air, GLM-4.5-Flash, GLM-4.5V, GLM-4.6, GLM-4.6V, GLM-4.7, GLM-4.7-Flash, and GLM-5 models to use anthropic-messages API instead of openai-completions
  • Updated GLM models base URL from https://api.z.ai/api/coding/paas/v4 to https://api.z.ai/api/anthropic
  • Updated pricing for multiple models including Mistral, Moonshot, and Qwen variants
  • Updated context window and max tokens for several models to reflect accurate specifications

Removed

  • Removed compat field with supportsDeveloperRole and thinkingFormat properties from GLM models

[12.6.0] - 2026-02-16

Added

  • Added source-scoped custom API and OAuth provider registration helpers for extension-defined providers.

Changed

  • Expanded Api typing to allow extension-defined API identifiers while preserving built-in API exhaustiveness checks.

Fixed

  • Fixed custom API registration to reject built-in API identifiers and prevent accidental provider overrides.

[12.2.0] - 2026-02-13

Added

  • Added automatic retry logic for WebSocket stream closures before response completion, with configurable retry budget to improve reliability on flaky connections
  • Added providerSessionState option to enable provider-scoped mutable state persistence across agent turns
  • Added WebSocket retry logic with configurable retry budget and delay via PI_CODEX_WEBSOCKET_RETRY_BUDGET and PI_CODEX_WEBSOCKET_RETRY_DELAY_MS environment variables
  • Added WebSocket idle timeout detection via PI_CODEX_WEBSOCKET_IDLE_TIMEOUT_MS environment variable to fail stalled connections
  • Added WebSocket v2 beta header support via PI_CODEX_WEBSOCKET_V2 environment variable for newer OpenAI API versions
  • Added WebSocket handshake header capture to extract and replay session metadata (turn state, models etag, reasoning flags) across SSE fallback requests
  • Added preferWebsockets option to enable WebSocket transport for OpenAI Codex responses when supported
  • Added prewarmOpenAICodexResponses() function to establish and reuse WebSocket connections across multiple requests
  • Added getOpenAICodexTransportDetails() function to inspect transport layer details including WebSocket status and fallback information
  • Added getProviderDetails() function to retrieve formatted provider configuration and transport information
  • Added automatic fallback from WebSocket to SSE when connection fails, with transparent retry logic
  • Added session state management to reuse WebSocket connections and enable request appending across turns
  • Added support for x-codex-turn-state header to maintain conversation state across SSE requests

Changed

  • Changed WebSocket session state storage from global maps to provider-scoped session state for multi-agent isolation
  • Changed WebSocket connection initialization to accept idle timeout configuration and handshake header callbacks
  • Changed WebSocket error handling to use standardized transport error messages with Codex websocket transport error prefix
  • Changed WebSocket retry behavior to retry transient failures before activating sticky fallback, improving reliability on flaky connections
  • Changed OpenAI Codex model configuration to prefer WebSocket transport by default with preferWebsockets: true
  • Changed header handling to use appropriate OpenAI-Beta header values for WebSocket vs SSE transports
  • Perplexity OAuth token refresh now uses JWT expiry extraction instead of Socket.IO RPC, improving reliability when server is unreachable
  • Removed Socket.IO client implementation for Perplexity token refresh; tokens are now validated using embedded JWT expiry claims

Removed

  • Removed refreshPerplexityToken export; token refresh is now handled internally via JWT expiry detection

Fixed

  • Fixed WebSocket stream retry logic to properly handle mid-stream connection closures and retry before falling back to SSE transport
  • Fixed preferWebsockets option handling to correctly respect explicit false values when determining transport preference
  • Fixed WebSocket append state not being reset after aborted requests, preventing stale state from affecting subsequent turns
  • Fixed WebSocket append state not being reset after stream errors, preventing failed append attempts from blocking future requests
  • Fixed Codex model context window metadata to use 272000 input tokens (instead of 400000 total budget) for non-Spark Codex variants

[12.0.0] - 2026-02-12

Added

  • Added GPT-5.3 Codex Spark model with 128K context window and extended reasoning capabilities
  • Added MiniMax M2.5 and M2.5 Lightning models via OpenAI-compatible API (minimax-code provider)
  • Added MiniMax M2.5 and M2.5 Lightning models via OpenAI-compatible API (minimax-code-cn provider for China region)
  • Added MiniMax M2.5 and M2.5 Lightning models via Anthropic API (minimax and minimax-cn providers)
  • Added Llama 3.1 8B model via Cerebras API
  • Added MiniMax M2.5 model via OpenRouter
  • Added MiniMax M2.5 model via Vercel AI Gateway
  • Added MiniMax M2.5 Free model via OpenCode
  • Added Qwen3 VL 32B Instruct multimodal model via OpenRouter

Changed

  • Updated Z.ai GLM-5 pricing and context window configuration on OpenRouter
  • Updated Qwen3 Max Thinking max tokens from 32768 to 65536 on OpenRouter
  • Updated OpenAI GPT-5 Image Mini pricing on OpenRouter
  • Updated OpenAI GPT-5 Pro pricing and context window on OpenRouter
  • Updated OpenAI o4-mini pricing and context window on OpenRouter
  • Updated Claude Opus 4.5 Thinking model name formatting (removed parentheses)
  • Updated Claude Opus 4.6 Thinking model name formatting (removed parentheses)
  • Updated Claude Sonnet 4.5 Thinking model name formatting (removed parentheses)
  • Updated Gemini 2.5 Flash Thinking model name formatting (removed parentheses)
  • Updated Gemini 3 Pro High and Low model name formatting (removed parentheses)
  • Updated GPT-OSS 120B Medium model name formatting (removed parentheses) and context window to 131072

Removed

  • Removed GLM-5 model from Z.ai provider
  • Removed Trinity Large Preview Free model from OpenCode provider
  • Removed MiniMax M2.1 Free model from OpenCode provider
  • Removed deprecated Anthropic model entries: claude-3-5-haiku-latest, claude-3-5-haiku-20241022, claude-3-7-sonnet-20250219, claude-3-7-sonnet-latest, claude-3-opus-20240229, claude-3-sonnet-20240229 (#33)

Fixed

  • Added deprecation filter in model generation script to prevent re-adding deprecated Anthropic models (#33)

[11.14.1] - 2026-02-12

Added

  • Added prompt-caching-scope-2026-01-05 beta feature support

Changed

  • Updated Claude Code version header to 2.1.39
  • Updated runtime version header to v24.13.1 and package version to 0.73.0
  • Increased request timeout from 60s to 600s
  • Reordered Accept-Encoding header values for compression preference
  • Updated OAuth authorization and token endpoints to use platform.claude.com
  • Expanded OAuth scopes to include user:sessions:claude_code and user:mcp_servers

Removed

  • Removed claude-code-20250219 beta feature from default models
  • Removed fine-grained-tool-streaming-2025-05-14 beta feature

[11.13.1] - 2026-02-12

Added

  • Added Perplexity (Pro/Max) OAuth login support via native macOS app extraction or email OTP authentication
  • Added loginPerplexity and refreshPerplexityToken functions for Perplexity account integration
  • Added Socket.IO v4 client implementation for authenticated WebSocket communication with Perplexity API

[11.12.0] - 2026-02-11

Changed

  • Increased maximum retry attempts for Codex requests from 2 to 5 to improve reliability on transient failures

Fixed

  • Fixed tool result content handling in Anthropic provider to provide fallback error message when content is empty
  • Improved retry delay calculation to parse delay values from error response bodies (e.g., 'Please try again in 225ms')

[11.11.0] - 2026-02-10

Breaking Changes

  • Replaced ./models.generated export with ./models.json - update imports from import { MODELS } from './models.generated' to import MODELS from './models.json' with { type: 'json' }

Added

  • Added TypeScript type declarations for models.json to enable proper type inference when importing the JSON file

Changed

  • Updated available models in google-antigravity provider with new model variants and updated context window/token limits
  • Simplified type signatures for getModel() and getModels() functions for improved usability
  • Changed models export from TypeScript module to JSON format for improved performance and reduced bundle size
  • Updated @anthropic-ai/sdk dependency from ^0.72.1 to ^0.74.0

[11.10.0] - 2026-02-10

Added

  • Added support for Kimi K2, K2 Turbo Preview, and K2.5 models with reasoning capabilities

Fixed

  • Fixed Claude Opus 4.6 context window to 200K across all providers (was incorrectly set to 1M)
  • Fixed Claude Sonnet 4 context window to 200K across multiple providers (was incorrectly set to 1M)

[11.8.0] - 2026-02-10

Added

  • Added auto model alias for OpenRouter with automatic model routing
  • Added openrouter/aurora-alpha model with reasoning capabilities
  • Added qwen/qwen3-max-thinking model with extended context window support
  • Added support for parametersJsonSchema in Google Gemini tool definitions for improved JSON Schema compatibility

Changed

  • Updated Claude Sonnet 4 and 4.5 context window from 1M to 200K tokens to reflect actual limits
  • Updated Claude Opus 4.6 context window to 200K tokens across providers
  • Changed default reasoningSummary for OpenAI Codex from undefined to auto
  • Updated Qwen model pricing and context window specifications across multiple variants
  • Modified Google Gemini CLI system instruction to use compact format
  • Changed tool parameter handling for Claude models on Google Cloud Code Assist to use legacy parameters field for API translation

Removed

  • Removed glm-4.7-free model from OpenCode provider
  • Removed qwen3-coder model from OpenCode provider
  • Removed ai21/jamba-mini-1.7 model from OpenRouter
  • Removed stepfun-ai/step3 model from OpenRouter
  • Removed duplicate test suite for Google Antigravity Provider with gemini-3-pro-high

Fixed

  • Fixed Amazon Bedrock HTTP/1.1 handler import to use direct import instead of dynamic import
  • Fixed Qwen model context window and pricing inconsistencies across OpenRouter
  • Fixed cache read pricing for multiple Qwen models
  • Fixed OpenAI Codex reasoning effort clamping for gpt-5.3-codex model

[11.7.1] - 2026-02-07

Added

  • Added Claude Opus 4.6 Thinking model for Antigravity provider
  • Added Gemini 2.5 Flash, Gemini 2.5 Flash Thinking, and Gemini 2.5 Pro models for Antigravity provider
  • Added Pony Alpha model via OpenRouter

Changed

  • Updated Antigravity models to use free tier pricing (0 cost) across all models
  • Changed Antigravity model fetching to dynamically load from API when credentials are available, with hardcoded fallback models
  • Updated Claude Opus 4.6 context window from 200,000 to 1,000,000 tokens across Bedrock regions
  • Updated Claude Opus 4.6 cache pricing from 1.5/18.75 to 0.5/6.25 for EU and US regions
  • Updated Antigravity model pricing to free tier (0 cost) for Claude Opus 4.5 Thinking, Claude Sonnet 4.5 Thinking, Gemini 3 Flash, Gemini 3 Pro variants, and GPT-OSS 120B Medium
  • Updated GPT-OSS 120B Medium reasoning capability from false to true
  • Updated Gemini 3 Flash max tokens from 65,535 to 65,536
  • Updated Claude Opus 4.5 Thinking display name formatting to include parentheses
  • Updated various model pricing and context window parameters across OpenRouter and other providers
  • Removed Claude Opus 4.6 20260205 model from Anthropic provider

Fixed

  • Fixed Claude Opus 4.6 model ID format by removing version suffix (:0) in Bedrock configurations
  • Fixed Llama 3.1 70B Instruct pricing and context window parameters
  • Fixed Mistral model pricing and cache read costs
  • Fixed DeepSeek and other model pricing inconsistencies
  • Fixed Qwen model pricing and token limits
  • Fixed GLM model pricing and context window specifications

[11.6.0] - 2026-02-07

Added

  • Added Bedrock cache retention support with PI_CACHE_RETENTION env var and per-request cacheRetention option
  • Added adaptive thinking support for Bedrock Opus 4.6+ models
  • Added AWS_BEDROCK_SKIP_AUTH env var to support unauthenticated Bedrock proxies
  • Added AWS_BEDROCK_FORCE_HTTP1 env var to force HTTP/1.1 for custom Bedrock endpoints
  • Re-exported Static, TSchema, and Type from @sinclair/typebox

Fixed

  • Fixed OpenAI Responses storage disabled by default (store: false)
  • Fixed reasoning effort clamping for gpt-5.3 Codex models (minimal -> low)
  • Fixed Bedrock supportsPromptCaching to also check model cost fields

[11.5.1] - 2026-02-07

Fixed

  • Fixed schema normalization to handle array-valued type fields by converting them to a single type with nullable flag for Google provider compatibility

[11.3.0] - 2026-02-06

Added

  • Added cacheRetention option to control prompt cache retention preference ('none', 'short', 'long') across providers
  • Added maxRetryDelayMs option to cap server-requested retry delays and fail fast when delays exceed the limit
  • Added effort option for Anthropic Opus 4.6+ models to control adaptive thinking effort levels ('low', 'medium', 'high', 'max')
  • Added support for Anthropic Opus 4.6+ adaptive thinking mode that lets Claude decide when and how much to think
  • Added PI_AI_ANTIGRAVITY_VERSION environment variable to customize Antigravity sandbox endpoint version
  • Exported convertAnthropicMessages function for converting message formats to Anthropic API
  • Automatic fallback for Anthropic assistant-prefill requests: appends synthetic user "Continue." message when conversation ends with assistant turn to maintain API compatibility

Changed

  • Changed supportsXhigh() to include GPT-5.1 Codex Max and broaden Anthropic support to all Anthropic Messages API models with budget-based thinking capability
  • Changed Anthropic thinking mode to use adaptive thinking for Opus 4.6+ models instead of budget-based thinking
  • Changed supportsXhigh() to support GPT-5.2/5.3 and Anthropic Opus 4.6+ models with adaptive thinking
  • Changed prompt caching to respect cacheRetention option and support TTL configuration for Anthropic
  • Changed OpenAI tool definitions to conditionally include strict field only when provider supports it
  • Changed Qwen model support to use enable_thinking boolean parameter instead of OpenAI-style reasoning_effort

Fixed

  • Fixed indentation and formatting in convertAnthropicMessages function
  • Fixed handling of conversations ending with assistant messages on Anthropic-routed models that reject assistant prefill requests

[11.2.3] - 2026-02-05

Added

  • Added Claude Opus 4.6 model support across multiple providers (Anthropic, Amazon Bedrock, GitHub Copilot, OpenRouter, OpenCode, Vercel AI Gateway)
  • Added GPT-5.3 Codex model support for OpenAI
  • Added readSseJson utility import for improved SSE stream handling in Google Gemini CLI provider

Changed

  • Updated Google Gemini CLI provider to use readSseJson utility for cleaner SSE stream parsing
  • Updated pricing for Llama 3.1 405B model on Vercel AI Gateway (cache read rate adjusted)
  • Updated Llama 3.1 405B context window and max tokens on Vercel AI Gateway (256000 for both)

Removed

  • Removed Kimi K2, Kimi K2 Turbo Preview, and Kimi K2.5 models
  • Removed Deep Cogito Cogito V2 Preview models from OpenRouter

[11.0.0] - 2026-02-05

Changed

  • Replaced direct Bun.env access with getEnv() utility from @oh-my-pi/pi-utils for consistent environment variable handling across all providers
  • Updated environment variable names from OMP_* prefix to PI_* prefix for consistency (e.g., OMP_CODING_AGENT_DIR → PI_CODING_AGENT_DIR)

Removed

  • Removed automatic environment variable migration from PI_* to OMP_* prefixes via migrate-env.ts module

[10.5.0] - 2026-02-04

Changed

  • Updated @anthropic-ai/sdk to ^0.72.1
  • Updated @aws-sdk/client-bedrock-runtime to ^3.982.0
  • Updated @google/genai to ^1.39.0
  • Updated @smithy/node-http-handler to ^4.4.9
  • Updated openai to ^6.17.0
  • Updated @types/node to ^25.2.0

Removed

  • Removed proxy-agent dependency
  • Removed undici dependency

[9.4.0] - 2026-01-31

Added

  • Added getEnv() function to retrieve environment variables from Bun.env, cwd/.env, or ~/.env
  • Added support for reading .env files from home directory and current working directory
  • Added support for exa and perplexity as known providers in getEnvApiKey()

Changed

  • Changed getEnvApiKey() to check Bun.env, cwd/.env, and ~/.env files in order of precedence
  • Refactored provider API key resolution to use a declarative service provider map

[9.2.2] - 2026-01-31

Added

  • Added OpenCode Zen provider with API key authentication for accessing multiple AI models
  • Added 4 new free models via OpenCode: glm-4.7-free, kimi-k2.5-free, minimax-m2.1-free, trinity-large-preview-free
  • Added glm-4.7-flash model via Zai provider
  • Added Kimi Code provider with OpenAI and Anthropic API format support
  • Added prompt cache retention support with PI_CACHE_RETENTION env var
  • Added overflow patterns for Bedrock, MiniMax, Kimi; reclassified 429 as rate limiting
  • Added profile endpoint integration to resolve user emails with 24-hour caching
  • Added automatic token refresh for expired Kimi OAuth credentials
  • Added Kimi Code OAuth handler with device authorization flow
  • Added Kimi Code usage provider with quota caching
  • Added 4 new Kimi Code models (kimi-for-coding, kimi-k2, kimi-k2-turbo-preview, kimi-k2.5)
  • Added Kimi Code provider integration with OAuth and token management
  • Added tool-choice utility for mapping unified ToolChoice to provider-specific formats
  • Added ToolChoice type for controlling tool selection (auto, none, any, required, function)

Changed

  • Updated Kimi K2.5 cache read pricing from 0.1 to 0.08
  • Updated MiniMax M2 pricing: input 0.6→0.6, output 3→3, cache read 0.1→0.09999999999999999
  • Updated OpenRouter DeepSeek V3.1 pricing and max tokens: input 0.6→0.5, output 3→2.8, maxTokens 262144→4096
  • Updated OpenRouter DeepSeek R1 pricing and max tokens: input 0.06→0.049999999999999996, output 0.24→0.19999999999999998, maxTokens 262144→4096
  • Updated Anthropic Claude 3.5 Sonnet max tokens from 256000 to 65536 on OpenRouter
  • Updated Vercel AI Gateway Claude 3.5 Sonnet cache read pricing from 0.125 to 0.13
  • Updated Vercel AI Gateway Claude 3.5 Sonnet New cache read pricing from 0.125 to 0.13
  • Updated Vercel AI Gateway GPT-5.2 cache read pricing from 0.175 to 0.18 and display name to 'GPT 5.2'
  • Updated Zai GLM-4.6 cache read pricing from 0.024999999999999998 to 0.03
  • Updated Zai Qwen QwQ max tokens from 66000 to 16384
  • Added delta event batching and throttling (50ms, 20 updates/sec max) to AssistantMessageEventStream
  • Updated MiniMax-M2 pricing: input 1.2→0.6, output 1.2→3, cacheRead 0.6→0.1

Removed

  • Removed OpenRouter google/gemini-2.0-flash-exp:free model
  • Removed Vercel AI Gateway stealth/sonoma-dusk-alpha and stealth/sonoma-sky-alpha models

Fixed

  • Fixed rate limit issues with Kimi models by always sending max_tokens
  • Added handling for sensitive stop reason from Anthropic API safety filters
  • Added optional chaining for safer JSON schema property access in Anthropic provider

[8.6.0] - 2026-01-27

Changed

  • Replaced JSON5 dependency with Bun.JSON5 parsing

Fixed

  • Filtered empty user text blocks for OpenAI-compatible completions and normalized Kimi reasoning_content for OpenRouter tool-call messages

[8.4.0] - 2026-01-25

Added

  • Added Azure OpenAI Responses provider with deployment mapping and resource-based base URL support

Changed

  • Added OpenRouter routing preferences for OpenAI-compatible completions

Fixed

  • Defaulted Google tool call arguments to empty objects when providers omit args
  • Guarded Responses/Codex streaming deltas against missing content parts and handled arguments.done events

[8.2.1] - 2026-01-24

Fixed

  • Fixed handling of streaming function call arguments in OpenAI responses to properly parse arguments when sent via response.function_call_arguments.done events

[8.2.0] - 2026-01-24

Changed

  • Migrated node module imports from named to namespace imports across all packages for consistency with project guidelines

[8.0.0] - 2026-01-23

Fixed

  • Fixed OpenAI Responses API 400 error "function_call without required reasoning item" when switching between models (same provider, different model). The fix omits the id field for function_calls from different models to avoid triggering OpenAI's reasoning/function_call pairing validation
  • Fixed 400 errors when reading multiple images via GitHub Copilot's Claude models. Claude requires tool_use -> tool_result adjacency with no user messages interleaved. Images from consecutive tool results are now batched into a single user message

[7.0.0] - 2026-01-21

Added

  • Added usage tracking system with normalized schema for provider quota/limit endpoints
  • Added Claude usage provider for 5-hour and 7-day quota windows
  • Added GitHub Copilot usage provider for chat, completions, and premium requests
  • Added Google Antigravity usage provider for model quota tracking
  • Added Google Gemini CLI usage provider for tier-based quota monitoring
  • Added OpenAI Codex usage provider for primary and secondary rate limit windows
  • Added ZAI usage provider for token and request quota tracking

Changed

  • Updated Claude usage provider to extract account identifiers from response headers
  • Updated GitHub Copilot usage provider to include account identifiers in usage reports
  • Updated Google Gemini CLI usage provider to handle missing reset time gracefully

Fixed

  • Fixed GitHub Copilot usage provider to simplify token handling and improve reliability
  • Fixed GitHub Copilot usage provider to properly resolve account identifiers for OAuth credentials
  • Fixed API validation errors when sending empty user messages (resume with .) across all providers:
  • Google Cloud Code Assist (google-shared.ts)
  • OpenAI Responses API (openai-responses.ts)
  • OpenAI Codex Responses API (openai-codex-responses.ts)
  • Cursor (cursor.ts)
  • Amazon Bedrock (amazon-bedrock.ts)
  • Clamped OpenAI Codex reasoning effort "minimal" to "low" for gpt-5.2 models to avoid API errors
  • Fixed GitHub Copilot usage fallback to internal quota endpoints when billing usage is unavailable
  • Fixed GitHub Copilot usage metadata to include account identifiers for report dedupe
  • Fixed Anthropic usage metadata extraction to include account identifiers when provided by the usage endpoint
  • Fixed Gemini CLI usage windows to consistently label quota windows for display suppression

[6.9.69] - 2026-01-21

Added

  • Added duration and time-to-first-token (ttft) metrics to all AI provider responses
  • Added performance tracking for streaming responses across all providers

[6.9.0] - 2026-01-21

Removed

  • Removed openai-codex provider exports from main package index
  • Removed openai-codex prompt utilities and moved them inline
  • Removed vitest configuration file

[6.8.4] - 2026-01-21

Changed

  • Updated prompt caching strategy to follow Anthropic's recommended hierarchy
  • Fixed token usage tracking to properly handle cumulative output tokens from message_delta events
  • Improved message validation to filter out empty or invalid content blocks
  • Increased OAuth callback timeout from 120 seconds to 120,000 milliseconds

[6.8.3] - 2026-01-21

Added

  • Added headers option to all providers for custom request headers
  • Added onPayload hook to observe provider request payloads before sending
  • Added strictResponsesPairing option for Azure OpenAI Responses API compatibility
  • Added originator option to loginOpenAICodex for custom OAuth flow identification
  • Added per-request headers and onPayload hooks to StreamOptions
  • Added originator option to loginOpenAICodex

Fixed

  • Fixed tool call ID normalization for OpenAI Responses API cross-provider handoffs
  • Skipped errored or aborted assistant messages during cross-provider transforms
  • Detected AWS ECS/IRSA credentials for Bedrock authentication checks
  • Detected AWS ECS/IRSA credentials for Bedrock authentication checks
  • Normalized Responses API tool call IDs during handoffs and refreshed handoff tests
  • Enforced strict tool call/result pairing for Azure OpenAI Responses API
  • Skipped errored or aborted assistant messages during cross-provider transforms

Security

  • Enhanced AWS credential detection to support ECS task roles and IRSA web identity tokens

[6.8.2] - 2026-01-21

Fixed

  • Improved error handling for aborted requests in Google Gemini CLI provider
  • Enhanced OAuth callback flow to handle manual input errors gracefully
  • Fixed login cancellation handling in GitHub Copilot OAuth flow
  • Removed fallback manual input from OpenAI Codex OAuth flow

Security

  • Hardened database file permissions to prevent credential leakage
  • Set secure directory permissions (0o700) for credential storage

[6.8.0] - 2026-01-20

Added

  • Added logout command to CLI for OAuth provider logout
  • Added status command to show logged-in providers and token expiry
  • Added persistent credential storage using SQLite database
  • Added OAuth callback server with automatic port fallback
  • Added HTML callback page with success/error states
  • Added support for Cursor OAuth provider

Changed

  • Updated Promise.withResolvers usage for better compatibility
  • Replaced custom sleep implementations with Bun.sleep and abortableSleep
  • Simplified SSE stream parsing using readLines utility
  • Updated test framework from vitest to bun:test
  • Replaced temp directory creation with TempDir API
  • Changed credential storage from auth.json to ~/.omp/agent/agent.db
  • Changed CLI command examples from npx to bunx
  • Refactored OAuth flows to use common callback server base class
  • Updated OAuth provider interfaces to use controller pattern

Fixed

  • Fixed OAuth callback handling with improved error states
  • Fixed token refresh for all OAuth providers

[6.7.670] - 2026-01-19

Changed

  • Updated Claude Code compatibility headers and version
  • Improved OAuth token handling with proper state generation
  • Enhanced cache control for tool and user message blocks
  • Simplified tool name prefixing for OAuth traffic
  • Updated PKCE verifier generation for better security

[5.7.67] - 2026-01-18

Fixed

  • Added error handling for unknown OAuth providers

[5.6.77] - 2026-01-18

Fixed

  • Prevented duplicate tool results for errored or aborted messages when results already exist

[5.6.7] - 2026-01-18

Added

  • Added automatic retry logic for OpenAI Codex responses with configurable delay and max retries
  • Added tool call ID sanitization for Amazon Bedrock to ensure valid characters
  • Added tool argument validation that coerces JSON-encoded strings for expected non-string types

Changed

  • Updated environment variable prefix from PI* to OMP* for better consistency
  • Added automatic migration for legacy PI* environment variables to OMP* equivalents
  • Adjusted Bedrock Claude thinking budgets to reserve output tokens when maxTokens is too low

Fixed

  • Fixed orphaned tool call handling to ensure proper tool_use/tool_result pairing for all assistant messages
  • Fixed message transformation to insert synthetic tool results for errored/aborted assistant messages with tool calls
  • Fixed tool prefix handling in Claude provider to use case-insensitive comparison
  • Fixed Gemini 3 model handling to treat unsigned tool calls as context-only with anti-mimicry context
  • Fixed message transformation to filter out empty error messages from conversation history
  • Fixed OpenAI completions provider compatibility detection to use provider metadata
  • Fixed OpenAI completions provider to avoid using developer role for opencode provider
  • Fixed orphaned tool call handling to skip synthetic results for errored assistant messages

[5.5.0] - 2026-01-18

Changed

  • Updated User-Agent header from 'opencode' to 'pi' for OpenAI Codex requests
  • Simplified Codex system prompt instructions
  • Removed bridge text override from Codex system prompt builder

[5.3.0] - 2026-01-15

Changed

  • Replaced detailed Codex system instructions with simplified pi assistant instructions
  • Updated internal documentation references to use pi-internal:// protocol

[5.1.0] - 2026-01-14

Added

  • Added Amazon Bedrock provider with bedrock-converse-stream API for Claude models via AWS
  • Added MiniMax provider with OpenAI-compatible API
  • Added EU cross-region inference model variants for Claude models on Bedrock

Fixed

  • Fixed Gemini CLI provider retries with proper error handling, retry delays from headers, and empty stream retry logic
  • Fixed numbered list items showing "1." for all items when code blocks break list continuity (via start property)

[5.0.0] - 2026-01-12

Added

  • Added support for xhigh thinking level in thinkingBudgets configuration

Changed

  • Changed Anthropic thinking token budgets: minimal (1024→3072), low (2048→6144), medium (8192→12288), high (16384→24576)
  • Changed Google thinking token budgets: minimal (1024), low (2048→4096), medium (8192), high (16384), xhigh (24575)
  • Changed supportsXhigh() to return true for all Anthropic models

[4.6.0] - 2026-01-12

Fixed

  • Fixed incorrect classification of thought signatures in Google Gemini responses—thought signatures are now correctly treated as metadata rather than thinking content indicators
  • Fixed thought signature handling in Google Gemini CLI and Vertex AI streaming to properly preserve signatures across text deltas
  • Fixed Google schema sanitization stripping property names that match schema keywords (e.g., "pattern", "format") from tool definitions

[4.4.9] - 2026-01-12

Fixed

  • Fixed Google provider schema sanitization to strip additional unsupported JSON Schema fields (patternProperties, additionalProperties, min/max constraints, pattern, format)

[4.4.8] - 2026-01-12

Fixed

  • Fixed Google provider schema sanitization to properly collapse anyOf/oneOf with const values into enum arrays
  • Fixed const-to-enum conversion to infer type from the const value when type is not specified

[4.4.6] - 2026-01-11

Fixed

  • Fixed tool parameter schema sanitization to only apply Google-specific transformations for Gemini models, preserving original schemas for other model types

[4.4.5] - 2026-01-11

Changed

  • Exported sanitizeSchemaForGoogle utility function for external use

Fixed

  • Fixed Google provider schema sanitization to strip additional unsupported JSON Schema fields ($schema, $ref, $defs, format, examples, and others)
  • Fixed Google provider to ignore additionalProperties: false which is unsupported by the API

[4.4.4] - 2026-01-11

Fixed

  • Fixed Cursor todo updates to bridge update_todos tool calls to the local todo_write tool

[4.3.0] - 2026-01-11

Added

  • Added debug log filtering and display script for Cursor JSONL logs with follow mode and coalescing support
  • Added protobuf definition extractor script to reconstruct .proto files from bundled JavaScript
  • Added conversation state caching to persist context across multiple Cursor API requests in the same session
  • Added shell streaming support for real-time stdout/stderr output during command execution
  • Added JSON5 parsing for MCP tool arguments with Python-style boolean and None value normalization
  • Added Cursor provider with support for Claude, GPT, and Gemini models via Cursor's agent API
  • Added OAuth authentication flow for Cursor including login, token refresh, and expiry detection
  • Added cursor-agent API type with streaming support and tool execution handlers
  • Added Cursor model definitions including Claude 4.5, GPT-5.x, Gemini 3, and Grok variants
  • Added model generation script to automatically fetch and update AI model definitions from models.dev and OpenRouter APIs

Changed

  • Changed Cursor debug logging to use structured JSONL format with automatic MCP argument decoding
  • Changed MCP tool argument decoding to use protobuf Value schema for improved type handling
  • Changed tool advertisement to filter Cursor native tools (bash, read, write, delete, ls, grep, lsp) instead of only exposing mcp_ prefixed tools

Fixed

  • Fixed Cursor conversation history serialization so subagents retain task context and can call complete

[4.2.1] - 2026-01-11

Changed

  • Updated reasoningSummary option to accept only "auto", "concise", "detailed", or null (removed "off" and "on" values)
  • Changed default reasoningSummary from "auto" to "detailed"
  • OpenAI Codex: switched to bundled system prompt matching opencode, changed originator to "opencode", simplified prompt handling

Fixed

  • Fixed Cloud Code Assist tool schema conversion to avoid unsupported const fields

[4.0.0] - 2026-01-10

Added

  • Added betas option in AnthropicOptions for passing custom Anthropic beta feature flags
  • OpenCode Zen provider support with 26 models (Claude, GPT, Gemini, Grok, Kimi, GLM, Qwen, etc.). Set OPENCODE_API_KEY env var to use.
  • thinkingBudgets option in SimpleStreamOptions for customizing token budgets per thinking level on token-based providers
  • sessionId option in StreamOptions for providers that support session-based caching. OpenAI Codex provider uses this to set prompt_cache_key and routing headers.
  • supportsUsageInStreaming compatibility flag for OpenAI-compatible providers that reject stream_options: { include_usage: true }. Defaults to true. Set to false in model config for providers like gatewayz.ai.
  • GOOGLE_APPLICATION_CREDENTIALS env var support for Vertex AI credential detection (standard for CI/production)
  • Exported OpenAI Codex utilities: CacheMetadata, getCodexInstructions, getModelFamily, ModelFamily, buildCodexPiBridge, buildCodexSystemPrompt, CodexSystemPrompt
  • Headless OAuth support for all callback-server providers (Google Gemini CLI, Antigravity, OpenAI Codex): paste redirect URL when browser callback is unreachable
  • Cancellable GitHub Copilot device code polling via AbortSignal
  • Improved error messages for OpenRouter providers by including raw metadata from upstream errors

Changed

  • Changed Anthropic provider to include Claude Code system instruction for all API key types, not just OAuth tokens (except Haiku models)
  • Changed Anthropic OAuth tool naming to use proxy_ prefix instead of mapping to Claude Code tool names, avoiding potential name collisions
  • Changed Anthropic provider to include Claude Code headers for all requests, not just OAuth tokens
  • Anthropic provider now maps tool names to Claude Code's exact tool names (Read, Write, Edit, Bash, Grep, Glob) instead of using prefixed names
  • OpenAI Completions provider now disables strict mode on tools to allow optional parameters without null unions

Fixed

  • Fixed Anthropic OAuth code parsing to accept full redirect URLs in addition to raw authorization codes
  • Fixed Anthropic token refresh to preserve existing refresh token when server doesn't return a new one
  • Fixed thinking mode being enabled when tool_choice forces a specific tool, which is unsupported
  • Fixed max_tokens being too low when thinking budget is set, now auto-adjusts to model's maxTokens
  • Google Cloud Code Assist OAuth for paid subscriptions: properly handles long-running operations for project provisioning, supports GOOGLE_CLOUD_PROJECT / GOOGLE_CLOUD_PROJECT_ID env vars for paid tiers
  • os.homedir() calls at module load time; now resolved lazily when needed
  • OpenAI Responses tool strict flag to use a boolean for LM Studio compatibility
  • Gemini CLI abort handling: detect native AbortError in retry catch block, cancel SSE reader when abort signal fires
  • Antigravity provider 429 errors by aligning request payload with CLIProxyAPI v6.6.89
  • Thinking block handling for cross-model conversations: thinking blocks are now converted to plain text when switching models
  • OpenAI Codex context window from 400,000 to 272,000 tokens to match Codex CLI defaults
  • Codex SSE error events to surface message, code, and status
  • Context overflow detection for context_length_exceeded error codes
  • Codex provider now always includes reasoning.encrypted_content even when custom include options are passed
  • Codex requests now omit the reasoning field entirely when thinking is off
  • Crash when pasting text with trailing whitespace exceeding terminal width

[3.37.1] - 2026-01-10

Added

  • Added automatic type coercion for tool arguments when LLMs return JSON-encoded strings instead of native types (numbers, booleans, arrays, objects)

Changed

  • Changed tool argument validation to attempt JSON parsing and type coercion before rejecting mismatched types
  • Changed validation error messages to include both original and normalized arguments when coercion was attempted

[3.37.0] - 2026-01-10

Changed

  • Enabled type coercion in JSON schema validation to automatically convert compatible types

[3.35.0] - 2026-01-09

Added

  • Enhanced error messages to include retry-after timing information from API rate limit headers

[0.42.0] - 2026-01-09

Added

  • Added OpenCode Zen provider support with 26 models (Claude, GPT, Gemini, Grok, Kimi, GLM, Qwen, etc.). Set OPENCODE_API_KEY env var to use.

[0.39.0] - 2026-01-08

Fixed

  • Fixed Gemini CLI abort handling: detect native AbortError in retry catch block, cancel SSE reader when abort signal fires (#568 by @tmustier)
  • Fixed Antigravity provider 429 errors by aligning request payload with CLIProxyAPI v6.6.89: inject Antigravity system instruction with role: "user", set requestType: "agent", and use antigravity userAgent. Added bridge prompt to override Antigravity behavior (identity, paths, web dev guidelines) with Pi defaults. (#571 by @ben-vargas)
  • Fixed thinking block handling for cross-model conversations: thinking blocks are now converted to plain text (no <thinking> tags) when switching models. Previously, <thinking> tags caused models to mimic the pattern and output literal tags. Also fixed empty thinking blocks causing API errors. (#561)

[0.38.0] - 2026-01-08

Added

  • thinkingBudgets option in SimpleStreamOptions for customizing token budgets per thinking level on token-based providers (#529 by @melihmucuk)

Breaking Changes

  • Removed OpenAI Codex model aliases (gpt-5, gpt-5-mini, gpt-5-nano, codex-mini-latest, gpt-5-codex, gpt-5.1-codex, gpt-5.1-chat-latest). Use canonical model IDs: gpt-5.1, gpt-5.1-codex-max, gpt-5.1-codex-mini, gpt-5.2, gpt-5.2-codex. (#536 by @ghoulr)

Fixed

  • Fixed OpenAI Codex context window from 400,000 to 272,000 tokens to match Codex CLI defaults and prevent 400 errors. (#536 by @ghoulr)
  • Fixed Codex SSE error events to surface message, code, and status. (#551 by @tmustier)
  • Fixed context overflow detection for context_length_exceeded error codes.

[0.37.6] - 2026-01-06

Added

  • Exported OpenAI Codex utilities: CacheMetadata, getCodexInstructions, getModelFamily, ModelFamily, buildCodexPiBridge, buildCodexSystemPrompt, CodexSystemPrompt (#510 by @mitsuhiko)

[0.37.3] - 2026-01-06

Added

  • sessionId option in StreamOptions for providers that support session-based caching. OpenAI Codex provider uses this to set prompt_cache_key and routing headers.

[0.37.2] - 2026-01-05

Fixed

  • Codex provider now always includes reasoning.encrypted_content even when custom include options are passed (#484 by @kim0)

[0.37.0] - 2026-01-05

Breaking Changes

  • OpenAI Codex models no longer have per-thinking-level variants (e.g., gpt-5.2-codex-high). Use the base model ID and set thinking level separately. The Codex provider clamps reasoning effort to what each model supports internally. (initial implementation by @ben-vargas in #472)

Added

  • Headless OAuth support for all callback-server providers (Google Gemini CLI, Antigravity, OpenAI Codex): paste redirect URL when browser callback is unreachable (#428 by @ben-vargas, #468 by @crcatala)
  • Cancellable GitHub Copilot device code polling via AbortSignal

Fixed

  • Codex requests now omit the reasoning field entirely when thinking is off, letting the backend use its default instead of forcing a value. (#472)

[0.36.0] - 2026-01-05

Added

  • OpenAI Codex OAuth provider with Responses API streaming support: openai-codex-responses streaming provider with SSE parsing, tool-call handling, usage/cost tracking, and PKCE OAuth flow (#451 by @kim0)

Fixed

  • Vertex AI dummy value for getEnvApiKey(): Returns "<authenticated>" when Application Default Credentials are configured (~/.config/gcloud/application_default_credentials.json exists) and both GOOGLE_CLOUD_PROJECT (or GCLOUD_PROJECT) and GOOGLE_CLOUD_LOCATION are set. This allows streamSimple() to work with Vertex AI without explicit apiKey option. The ADC credentials file existence check is cached per-process to avoid repeated filesystem access.

[0.32.3] - 2026-01-03

Fixed

  • Google Vertex AI models no longer appear in available models list without explicit authentication. Previously, getEnvApiKey() returned a dummy value for google-vertex, causing models to show up even when Google Cloud ADC was not configured.

[0.32.0] - 2026-01-03

Added

  • Vertex AI provider with ADC (Application Default Credentials) support. Authenticate with gcloud auth application-default login, set GOOGLE_CLOUD_PROJECT and GOOGLE_CLOUD_LOCATION, and access Gemini models via Vertex AI. (#300 by @default-anton)

Fixed

  • Gemini CLI rate limit handling: Added automatic retry with server-provided delay for 429 errors. Parses delay from error messages like "Your quota will reset after 39s" and waits accordingly. Falls back to exponential backoff for other transient errors. (#370)

[0.31.0] - 2026-01-02

Breaking Changes

  • Agent API moved: All agent functionality (agentLoop, agentLoopContinue, AgentContext, AgentEvent, AgentTool, AgentToolResult, etc.) has moved to @mariozechner/pi-agent-core. Import from that package instead of @oh-my-pi/pi-ai.

Added

  • GoogleThinkingLevel type: Exported type that mirrors Google's ThinkingLevel enum values ("THINKING_LEVEL_UNSPECIFIED" | "MINIMAL" | "LOW" | "MEDIUM" | "HIGH"). Allows configuring Gemini thinking levels without importing from @google/genai.
  • ANTHROPIC_OAUTH_TOKEN env var: Now checked before ANTHROPIC_API_KEY in getEnvApiKey(), allowing OAuth tokens to take precedence.
  • event-stream.js export: AssistantMessageEventStream utility now exported from package index.

Changed

  • OAuth uses Web Crypto API: PKCE generation and OAuth flows now use Web Crypto API (crypto.subtle) instead of Node.js crypto module. This improves browser compatibility while still working in Node.js 20+.
  • Deterministic model generation: generate-models.ts now sorts providers and models alphabetically for consistent output across runs. (#332 by @mrexodia)

Fixed

  • OpenAI completions empty content blocks: Empty text or thinking blocks in assistant messages are now filtered out before sending to the OpenAI completions API, preventing validation errors. (#344 by @default-anton)
  • Thinking token duplication: Fixed thinking content duplication with chutes.ai provider. The provider was returning thinking content in both reasoning_content and reasoning fields, causing each chunk to be processed twice. Now only the first non-empty reasoning field is used.
  • zAi provider API mapping: Fixed zAi models to use openai-completions API with correct base URL (https://api.z.ai/api/coding/paas/v4) instead of incorrect Anthropic API mapping. (#344, #358 by @default-anton)

[0.28.0] - 2025-12-25

Breaking Changes

  • OAuth storage removed (#296): All storage functions (loadOAuthCredentials, saveOAuthCredentials, setOAuthStorage, etc.) removed. Callers are responsible for storing credentials.
  • OAuth login functions: loginAnthropic, loginGitHubCopilot, loginGeminiCli, loginAntigravity now return OAuthCredentials instead of saving to disk.
  • refreshOAuthToken: Now takes (provider, credentials) and returns new OAuthCredentials instead of saving.
  • getOAuthApiKey: Now takes (provider, credentials) and returns { newCredentials, apiKey } or null.
  • OAuthCredentials type: No longer includes type: "oauth" discriminator. Callers add discriminator when storing.
  • setApiKey, resolveApiKey: Removed. Callers must manage their own API key storage/resolution.
  • getApiKey: Renamed to getEnvApiKey. Only checks environment variables for known providers.

[0.27.7] - 2025-12-24

Fixed

  • Thinking tag leakage: Fixed Claude mimicking literal </thinking> tags in responses. Unsigned thinking blocks (from aborted streams) are now converted to plain text without <thinking> tags. The TUI still displays them as thinking blocks. (#302 by @nicobailon)

[0.25.1] - 2025-12-21

Added

  • xhigh thinking level support: Added supportsXhigh() function to check if a model supports xhigh reasoning level. Also clamps xhigh to high for OpenAI models that don't support it. (#236 by @theBucky)

Fixed

  • Gemini multimodal tool results: Fixed images in tool results causing flaky/broken responses with Gemini models. For Gemini 3, images are now nested inside functionResponse.parts per the docs. For older models (which don't support multimodal function responses), images are sent in a separate user message.

  • Queued message steering: When getQueuedMessages is provided, the agent loop now checks for queued user messages after each tool call and skips remaining tool calls in the current assistant message when a queued message arrives (emitting error tool results).

  • Double API version path in Google provider URL: Fixed Gemini API calls returning 404 after baseUrl support was added. The SDK was appending its default apiVersion to baseUrl which already included the version path. (#251 by @shellfyred)

  • Anthropic SDK retries disabled: Re-enabled SDK-level retries (default 2) for transient HTTP failures. (#252)

[0.23.5] - 2025-12-19

Added

  • Gemini 3 Flash thinking support: Extended thinking level support for Gemini 3 Flash models (MINIMAL, LOW, MEDIUM, HIGH) to match Pro models' capabilities. (#212 by @markusylisiurunen)

  • GitHub Copilot thinking models: Added thinking support for additional Copilot models (o3-mini, o1-mini, o1-preview). (#234 by @aadishv)

Fixed

  • Gemini tool result format: Fixed tool result format for Gemini 3 Flash Preview which strictly requires { output: value } for success and { error: value } for errors. Previous format using { result, isError } was rejected by newer Gemini models. Also improved type safety by removing as any casts. (#213, #220)

  • Google baseUrl configuration: Google provider now respects baseUrl configuration for custom endpoints or API proxies. (#216, #221 by @theBucky)

  • GitHub Copilot vision requests: Added Copilot-Vision-Request header when sending images to GitHub Copilot models. (#222)

  • GitHub Copilot X-Initiator header: Fixed X-Initiator logic to check last message role instead of any message in history. This ensures proper billing when users send follow-up messages. (#209)

[0.22.3] - 2025-12-16

Added

  • Image limits test suite: Added comprehensive tests for provider-specific image limitations (max images, max size, max dimensions). Discovered actual limits: Anthropic (100 images, 5MB, 8000px), OpenAI (500 images, ≥25MB), Gemini (~2500 images, ≥40MB), Mistral (8 images, ~15MB), OpenRouter (~40 images context-limited, ~15MB). (#120)

  • Tool result streaming: Added tool_execution_update event and optional onUpdate callback to AgentTool.execute() for streaming tool output during execution. Tools can now emit partial results (e.g., bash stdout) that are forwarded to subscribers. (#44)

  • X-Initiator header for GitHub Copilot: Added X-Initiator header handling for GitHub Copilot provider to ensure correct call accounting (agent calls are not deducted from quota). Sets initiator based on last message role. (#200 by @kim0)

Changed

  • Normalized tool_execution_end result: tool_execution_end event now always contains AgentToolResult (no longer AgentToolResult | string). Errors are wrapped in the standard result format.

Fixed

  • Reasoning disabled by default: When reasoning option is not specified, thinking is now explicitly disabled for all providers. Previously, some providers like Gemini with "dynamic thinking" would use their default (thinking ON), causing unexpected token usage. This was the original intended behavior. (#180 by @markusylisiurunen)

[0.22.2] - 2025-12-15

Added

  • Interleaved thinking for Anthropic: Added interleavedThinking option to AnthropicOptions. When enabled, Claude 4 models can think between tool calls and reason after receiving tool results. Enabled by default (no extra token cost, just unlocks the capability). Set interleavedThinking: false to disable.

[0.22.1] - 2025-12-15

Dedicated to Peter's shoulder (@steipete)

Added

  • Interleaved thinking for Anthropic: Enabled interleaved thinking in the Anthropic provider, allowing Claude models to output thinking blocks interspersed with text responses.

[0.22.0] - 2025-12-15

Added

  • GitHub Copilot provider: Added github-copilot as a known provider with models sourced from models.dev. Includes Claude, GPT, Gemini, Grok, and other models available through GitHub Copilot. (#191 by @cau1k)

Fixed

  • GitHub Copilot gpt-5 models: Fixed API selection for gpt-5 models to use openai-responses instead of openai-completions (gpt-5 models are not accessible via completions endpoint)

  • GitHub Copilot cross-model context handoff: Fixed context handoff failing when switching between GitHub Copilot models using different APIs (e.g., gpt-5 to claude-sonnet-4). Tool call IDs from OpenAI Responses API were incompatible with other models. (#198)

  • Gemini 3 Pro thinking levels: Thinking level configuration now works correctly for Gemini 3 Pro models. Previously all levels mapped to -1 (minimal thinking). Now LOW/MEDIUM/HIGH properly control test-time computation. (#176 by @markusylisiurunen)

[0.18.2] - 2025-12-11

Changed

  • Anthropic SDK retries disabled: Set maxRetries: 0 on Anthropic client to allow application-level retry handling. The SDK's built-in retries were interfering with coding-agent's retry logic. (#157)

[0.18.1] - 2025-12-10

Added

  • Mistral provider: Added support for Mistral AI models via the OpenAI-compatible API. Includes automatic handling of Mistral-specific requirements (tool call ID format). Set MISTRAL_API_KEY environment variable to use.

Fixed

  • Fixed Mistral 400 errors after aborted assistant messages by skipping empty assistant messages (no content, no tool calls) (#165)

  • Removed synthetic assistant bridge message after tool results for Mistral (no longer required as of Dec 2025) (#165)

  • Fixed bug where ANTHROPIC_API_KEY environment variable was deleted globally after first OAuth token usage, causing subsequent prompts to fail (#164)

[0.17.0] - 2025-12-09

Added

  • agentLoopContinue function: Continue an agent loop from existing context without adding a new user message. Validates that the last message is user or toolResult. Useful for retry after context overflow or resuming from manually-added tool results.

  • Added validateToolCall(tools, toolCall) helper that finds the tool by name and validates arguments.

  • OpenAI compatibility overrides: Added compat field to Model for openai-completions API, allowing explicit configuration of provider quirks (supportsStore, supportsDeveloperRole, supportsReasoningEffort, maxTokensField). Falls back to URL-based detection if not set. Useful for LiteLLM, custom proxies, and other non-standard endpoints. (#133, thanks @fink-andreas for the initial idea and PR)

  • xhigh reasoning level: Added xhigh to ReasoningEffort type for OpenAI codex-max models. For non-OpenAI providers (Anthropic, Google), xhigh is automatically mapped to high. (#143)

Breaking Changes

  • Removed provider-level tool argument validation. Validation now happens in agentLoop via executeToolCalls, allowing models to retry on validation errors. For manual tool execution, use validateToolCall(tools, toolCall) or validateToolArguments(tool, toolCall).

Changed

  • Updated SDK versions: OpenAI SDK 5.21.0 → 6.10.0, Anthropic SDK 0.61.0 → 0.71.2, Google GenAI SDK 1.30.0 → 1.31.0

[0.13.0] - 2025-12-06

Breaking Changes

  • Added totalTokens field to Usage type: All code that constructs Usage objects must now include the totalTokens field. This field represents the total tokens processed by the LLM (input + output + cache). For OpenAI and Google, this uses native API values (total_tokens, totalTokenCount). For Anthropic, it's computed as input + output + cacheRead + cacheWrite.

[0.12.10] - 2025-12-04

Added

  • Added gpt-5.1-codex-max model support

Fixed

  • OpenAI Token Counting: Fixed usage.input to exclude cached tokens for OpenAI providers. Previously, input included cached tokens, causing double-counting when calculating total context size via input + cacheRead. Now input represents non-cached input tokens across all providers, making input + output + cacheRead + cacheWrite the correct formula for total context size.

  • Fixed Claude Opus 4.5 cache pricing (was 3x too expensive)

    • Corrected cache_read: $1.50 → $0.50 per MTok
    • Corrected cache_write: $18.75 → $6.25 per MTok
    • Added manual override in scripts/generate-models.ts until upstream fix is merged
    • Submitted PR to models.dev: https://github.com/sst/models.dev/pull/439

[0.9.4] - 2025-11-26

Initial release with multi-provider LLM support.