- Introduced a new `support-prompt-cache-key` configuration option to enable optional prompt cache key derivation for supported models.
- Updated relevant API handlers and internal logic to handle the new configuration.
- Enhanced executor workflows to apply prompt cache keys conditionally during request processing.
- Added tests to validate prompt cache key behavior across various scenarios and providers.
- Updated configuration examples and documentation for the new option.
Closes: #4700
- Introduced `claudeFastModeBeta` constant and logic to append it conditionally based on the `speed` attribute in the request body.
- Ensured `fast-mode-2026-02-01` beta inclusion only when explicitly requested or if the `speed` is set to `fast`.
- Updated `applyClaudeHeaders` to dynamically handle fast-mode beta and adjust headers accordingly.
- Enhanced beta extraction and addition with a new helper `appendClaudeFastModeBeta`.
- Improved flexibility in beta handling by preserving requested overrides while ensuring compatibility with default configurations.
Closes: #4703
- Added logic for caching and restoring Kimi "Thinking Replay" states to improve continuity across requests.
- Implemented `kimiThinkingReplayScope`, content replay preparation, and conditional cache storage based on replay validity.
- Introduced helper methods for replay content restoration, caching, and cleanup on errors.
- Enhanced executor workflows to utilize the "Thinking Replay" cache for both immediate and streaming response handling.
- Integrated periodic cleanup of expired and oversized replay cache entries.
Closes: #4695
- Introduced `disable-codex-cloaking` to allow disabling enforced `User-Agent` and `Originator` headers for Codex requests.
- Updated header application logic to conditionally include `codexUserAgent` and `codexOriginator` based on configuration.
- Enhanced config diff tracking to detect changes in `disable-codex-cloaking`.
- Expanded tests to cover new config behavior and header application scenarios.
- Adjusted `responseSchema` logic to only set schema for `json_schema` type while ensuring it is excluded for `json_object`.
- Updated tests to reflect changes in `responseSchema` handling, ensuring correct validation for both response formats.
- Renamed test function for better clarity regarding `json_object` behavior.
Closes: #4667
- Replaced boolean parameters with a `jsonSchemaCleanOptions` struct in `cleanJSONSchema` to improve readability and scalability.
- Updated `CleanJSONSchemaForAntigravity` and related methods to utilize the new options struct.
- Enhanced flexibility for schema transformations with fine-grained control over operations like union flattening, enum type enforcement, and metadata removal.
- Added comprehensive tests to verify correct handling of unions and enum types in schemas.
Closes: #4666
- Replaced instances of `thinking.ApplyThinking` with `helps.ApplyRequestThinking` across all executors for consistency.
- Updated `applyGeminiInteractionsThinking` to accept `cliproxyexecutor.Request` and `Options`.
- Centralized logic for request thinking application to `helps` package for improved maintainability.
Closes: #4618
Client.KVCompareAndSwap sent Redis EVAL with a Lua script, but the Home RESP
subset does not implement EVAL, so Home replied "ERR unknown command 'eval'".
That broke the Antigravity and Codex reasoning replay caches in Home mode.
Switch the transport to Home's dedicated CAS command:
CAS <key> <expected-exists 0|1> <expected-value> <new-value> [PX <ttl-ms>]
The semantics match the old script argument for argument, so KVCompareAndSwap's
signature and all its callers are unchanged. Omitting PX when ttl <= 0 mirrors
the script's SET-without-PX branch, which clears the TTL.
Deployments that predate CAS reject the command. Detect that by matching the
unsupported-command error, latch ErrCompareAndSwapUnsupported for the client
lifetime so later calls skip the round trip, and warn exactly once. The latch is
deliberately not carried across NewLifetime, so a Home upgrade takes effect on
the next reconnect rather than requiring a CPA restart.
Also stop replay-state failures from failing the request. A bare replay error has
no HTTP status, so resultErrorFromError does not classify it as request-scoped and
MarkResult marks the credential unavailable for that model, walking every
candidate credential until alias resolution has nothing left and returns 503. A
ledger miss is already a tolerated outcome, so degrade to "no replay this turn"
instead. The pairing failure still returns its 400.
Verified end to end against a real Home over RESP/mTLS on PostgreSQL with
Antigravity OAuth credentials: patched Home recreates the replay row through CAS
with its TTL, while a pre-CAS Home latches once, keeps returning 200 instead of
503, and records no credential error attributable to the replay path.
Refs router-for-me/CLIProxyAPIHome#79
A cached text signature is pinned to its part by that part's own content
fingerprint. Gemini validates a signature's own integrity and never its
binding to the surrounding history, so drift elsewhere in the conversation
cannot invalidate it. Gating the fingerprinted lookup on the context hash
only discarded reasoning the model then had to redo. The legacy positional
fallback has no such proof and stays gated.
Reasoning replay treated a changed history as proof that a cached thought
signature had become invalid, so a drifted context or a ledger miss dropped the
signature and left the call on the bypass sentinel. Once that happened the
damage cascaded: every later ledger item verifies its contextHash against the
restored bytes of all preceding contents, so one broken link cost the whole tail
of the conversation its reasoning.
Testing the assumption directly against daily-cloudcode-pa.googleapis.com shows
it does not hold. Gemini validates a thought signature's own integrity and
nothing else -- corrupting one byte returns "Corrupted thought signature", while
changing the system instruction, adding a tool, rewriting an earlier call's
args, rewriting a tool result, or swapping two turns' signatures are all
accepted. Only the newest functionCall group has to carry a signature at all,
and the bypass sentinel satisfies that.
Keeping the signature is worth doing rather than merely harmless: on an
otherwise identical request, replacing every signature with the sentinel raises
thoughtsTokenCount from 11-15 to 41-53, so a broken chain makes the model
re-reason from scratch.
Restore the signature on the identity-only path, and stop deleting the client's
in-band signature while degrading unresolved provenance IDs. Argument integrity
is unaffected: that is the opaque digest ID's job, not the signature's.
Since #4525 a Claude Code session against Antigravity Gemini could die
permanently with HTTP 400 "missing Claude tool provenance". The reserved
cpa_gemini_<digest> IDs live in the client transcript forever, so once the
replay ledger cannot resolve them every later request fails the same way and
the request never reaches upstream.
Live testing against gemini-3.6-flash-high confirmed four independent ways the
ledger lookup breaks, all producing the same 400:
A contents mutated earlier in the history -> contextHash rejects an exact
opaque-ID match, so proven identity is discarded along with the signature
B one extra system block -> the session key embeds the
system lane, so the whole ledger bucket switches and comes back empty
C process restart or TTL expiry -> empty bucket
D one mid-stream client abort -> that turn never commits
D needs nothing but a single interrupted stream and kills the session from its
very first tool turn, which matches the reported symptom most closely. Parallel
tool calls are not the trigger; they only lengthen the history.
Split tool identity recovery from context-bound signature replay:
- antigravityFunctionCallProvenanceLocation resolves a call when the payload ID
equals the opaque digest derived from a ledger item. That digest is
sha256(call_id, name, args), so an exact match already proves the identity and
the context hash adds nothing to it. Such a call is restored to its native
id/name/args with its paired functionResponse, but deliberately stays
unsigned - the cached signature belongs to a different history.
- Unresolved reserved IDs are now rewritten to deterministic neutral IDs rather
than aborting the request. Pairing is preserved, untrusted signatures are
dropped, and only genuinely malformed histories still error.
- antigravityRepairUnsignedFirstFunctionCalls re-asserts the leading-call
signature invariant after replay, because the request-level sanitizer runs
before it. Native signatures are never touched.
Also fixes a corruption the fail-closed check was masking: when a client changed
a call's arguments, replay inserted a duplicate native functionCall next to the
mutated one, leaving more calls than responses.
Adds forensic debug logging (hashed session keys and counts only) that
distinguishes a ledger miss from a context-hash rejection.
Verified live: happy path unchanged over 20 upstream requests and 100 parallel
call groups (0 residual reserved IDs, every group 1 native-signed + 4 unsigned);
all four failure modes now return 200; and a session survives a mid-stream abort
plus 5 further requests that previously died.
- Introduced `InjectXSearch` in `XAIConfig` to enable automatic injection of the native `x_search` tool when not explicitly declared.
- Updated `XAIExecutor` to honor the `InjectXSearch` configuration, ensuring consistent tool availability.
- Enhanced configuration handling with support for dynamic diffing to track changes in `InjectXSearch`.
- Added comprehensive tests to validate `InjectXSearch` behavior, including preparation and tool choice synchronization.
- Updated example config and documentation to outline `InjectXSearch` usage.
Closes: #4339
- Added `CountClaudeInputTokens` for estimating token usage with O200kBase tokenizer.
- Enhanced `ClaudeExecutor` to handle token counting locally, reducing dependency on upstream services.
- Introduced validation for token count requests for improved error handling.
- Updated tests to cover local token counting behavior and invalid request handling.
Closes: #4103
- Removed deprecated interceptor and executor-related methods, including `callRequestInterceptor`, `callResponseInterceptor`, and `callStreamChunkInterceptor`.
- Consolidated unused logic and pruned redundant imports to streamline `adapters.go`.
- No functional changes.
`prependToFirstUserMessage` always inserted the `<system-reminder>` text block at
index 0 of the first user message's content array. When a client sends a history
that begins with an assistant `tool_use` turn, that first user message is the
`tool_result` carrier, and Anthropic requires those blocks to stay at the head of
the message. Prepending pushed them out of first position, so the upstream
rejected the whole request with:
messages.N: `tool_use` ids were found without `tool_result` blocks
immediately after: <id>
Append the reminder instead when the content array already leads with a
`tool_result` block; behaviour is unchanged for every other message shape.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Added logic to `preserveXAIResponsesOutputControls` for retaining supported controls (`max_output_tokens`, `temperature`, `top_p`, `top_k`) during translation.
- Removed unsupported fields such as `stop` where necessary to align with xAI's Responses API.
- Updated tests to validate the preservation and removal of fields across various input scenarios.
- Improved payload configuration handling with additional unit tests for ensuring compatibility with model-specific overrides.
Closes: #4464
- Added `UsesConfig` to `XAIAutoExecutor` for determining config-based binding.
- Refactored `Service` to support thread-safe executor registration, using a new mutex (`executorRegistrationMu`).
- Improved executor rebind logic to prevent unnecessary replacements unless forced or required by config updates.
- Updated handling of config updates to correctly replace stale XAI executors.
- Enhanced the test suite for edge cases in executor binding and replacement for multiple providers.
Closes: #4567
- Updated `ensureCacheControl` to inject cache_control into the last non-deferred tool instead of the last tool.
- Adjusted `injectToolsCacheControl` to ignore tools marked with `defer_loading` for prompt caching.
- Added unit tests to validate behavior for deferred tools, existing cache_control, and mixed tool arrays.
Closes: #4561
codexReasoningReplayTurnAnchorIndex re-hashed the entire input prefix for
every probed index while scanning anchors downward, turning the anchor
search into O(n^2) SHA-256 over the conversation. On long-context (1M)
sessions the gateway spent minutes hashing per request before contacting
upstream, which surfaced as client-side 'operation timed out' errors and
~11 cores of steady CPU on the gorillaclaw candidate.
Replace the per-probe recomputation with a lazily extended incremental
digest that snapshots each prefix sum once, keeping every probe O(1)
after a single O(n) pass. Output is bit-identical to
codexReplayInputPrefixFingerprint; equivalence covered by a new test.
- Updated Kimi and Gemini executors to use `TranslateRequestWithCodexMultiAgentV2` for improved multi-agent v2 optimization.
- Enhanced configuration and API server to support `CodexOptimizeMultiAgentV2`.
- Added codex multi-agent V2 optimizations in `config`, `executor`, and `watcher` modules.
- Introduced `ClaudeInputTokenState` to track and estimate input token usage for Claude message_start events.
- Added `TranslateStreamWithClaudeInputTokens` for token-aware stream translation.
- Updated executors (XAI, Kimi, Gemini) to leverage the new logic.
- Included robust test cases for token counting, JSON validation, and concurrent tokenization scenarios.
- Refactored token counting logic into a reusable `countXAIInputTokens` function for better readability and maintainability.
- Replaced direct encoder usage with structured input handling, including JSON parsing and segment collection.
- Switched tokenizer from `Cl100kBase` to `O200kBase` to support larger contexts.
Closes: #4492
Preserve Codex and XAI upstream message-too-big closes across the Responses WebSocket boundary while treating them as request-scoped errors. Prevent CPA reconnect and credential cooldown/fallback, scope close state to the active connection, retain terminal errors under backpressure, and ensure downstream disconnect cleanup cannot wait behind a blocked writer.