- Propagate request headers into custom-header resolution for OpenAI/Gemini/XAI/Codex execution and websocket flows.
- Resolve auth `header:` values like `$ABC` from incoming request headers at request time and omit headers when no value is available.
- Add documentation for the dynamic custom-header behavior in `config.example.yaml`.
Closes: #5053
- Extract the credential-identity block shared by the streaming and
non-streaming Claude paths into applyClaudeCLIIdentity, so the identity
seed choice (API key versus stable Kimi auth identity) cannot drift
between the two paths
- Move stripDefaultKimiClaudeCodeAttribution next to the other attribution
and CCH helpers in claude_signing.go; it is only called from the Claude
executor paths and never from the Kimi executor itself
- Reattach the addConfigHeadersToAttrs doc comment to its function in the
watcher synthesizer helpers
Claude Code 2.1.220 through 2.1.234 emit the cch attribution only for
firstParty on api.anthropic.com and for vertex; every other backend sends
the billing header unsigned. CPA had dropped its endpoint check, so an
opted-in API key signed a per-request hash on any gateway and could bust
that gateway's prompt cache.
- Restore the endpoint gate in claudeCCHSigningEnabled: a real Claude OAuth
credential still signs on every upstream, because a downstream Claude Code
pointed at CPA cannot produce that value itself, while a claude-code-cli
API key signs only on api.anthropic.com or Vertex
- Drop the unused origin parameter from Claude fingerprint policy resolution
and restore the original resolveClaudeWirePolicy signature; the wire profile
follows the credential and only CCH follows the origin
- Add config.NormalizeClaudeFingerprintProfile / ValidateClaudeFingerprintProfile
as the single source of truth for fingerprint-profile values
- Reject unknown fingerprint-profile values in the Management API, and warn
once per distinct value at request time instead of on every resolution,
which previously logged about four warnings per request for one typo
- Preserve unrecognized values through config sanitization so rewriting a
config file never discards operator input
- Update config.example.yaml and tests for the origin-scoped CCH behavior
* feat(config): add fingerprint-profile to Claude keys and auth JSON
- Add FingerprintProfile to ClaudeKey configuration struct and normalizer
- Track fingerprint-profile in config diff
- Map fingerprint-profile / fingerprint_profile to auth attributes in file and config synthesizers
- Support fingerprint-profile in Management API PatchClaudeKey and normalization
- Add Claude billing attribution string manipulation utilities in internal/util
- Document fingerprint-profile options in config.example.yaml
* feat(claude): add fingerprint policy and request-local CLI identity
- Centralize Claude fingerprint policy resolution in claude_fingerprint_policy.go
- Support stable Claude CLI identity synthesis (UUIDv5 account_uuid and SHA-256 device_id)
seeded from API keys or stable OAuth IDs, keeping access tokens isolated
- Warn on unrecognized fingerprint-profile values
* feat(claude): apply CLI fingerprint to Messages and keep API keys caller-owned
- Wire centralized fingerprint policy into Claude and Kimi executors
- Keep first-party Anthropic API keys and delegated providers caller-owned by default
- Apply Claude Code CLI wire profile (betas, metadata, diagnostics, MCP aliases)
when fingerprint-profile=claude-code-cli is configured
- Strictly align CCH signing with native Claude Code 2.1.220: only first-party
api.anthropic.com and Vertex sign dynamic CCH; third-party gateways and Kimi
receive billing header without cch= to avoid prompt cache busting
- Respect caller-owned count_tokens bodies by default while aligning CLI shape on opt-in
- Fall back to CLIProxyAPI/<version> User-Agent when caller sends no UA in caller-owned mode
- Scope custom operator header overrides accurately in caller-owned mode
- Add comprehensive test coverage for policy resolution, gateway opt-in, Kimi, and token counting
- Prefer `max_tokens` when both `max_tokens` and `max_completion_tokens` are present, otherwise use whichever exists.
- Default to existing template `max_tokens` limit when neither field is provided.
- Add regression coverage for all token-limit source/preference paths.
Closes: #5040
After remote compaction succeeds, the normalized previous request still contains the consumed compaction_trigger. The next WebSocket-to-HTTP merge replays that trigger before the compaction output and current input, causing the upstream 400 error.
Remove compaction_trigger only from previous request items when the previous response contains compaction or compaction_summary. Add a two-step regression test covering trigger normalization followed by compact replay.
Fixes#5041
- Raise GPT 5.6 Sol/Terra/Luna `context_length` values to `921000` in `internal/registry/models/models.json`.
- Update matching Codex client model settings to `context_window: 272000` and `max_context_window: 921000` in `internal/registry/models/codex_client_models.json`.
- Changed `DisableCooling` from a boolean to a pointer in various config types to allow explicit inheritance.
- Updated tests to reflect the new pointer usage for `DisableCooling`.
- Enhanced the `BuildConfigChangeDetails` function to handle optional boolean changes for `DisableCooling`.
- Added new tests to ensure proper handling of cooling overrides in configurations.
- Refactored the `SetQuotaCooldownDisabled` function and related logic to clarify the purpose of cooldown management.
- Introduced new tests for cooling override precedence in the auth manager.
- Ensured that all relevant handlers and synthesizers correctly manage the `DisableCooling` setting.
- Add `isSameSelector` using type-aware comparable checks to avoid unnecessary selector replacement.
- Update `Manager.SetSelector` to:
- serialize swaps with a dedicated selector mutex,
- no-op when replacing with the same selector instance/type,
- stop the previous selector when it implements `StoppableSelector`.
- Protect `SessionCache.Stop()` with `sync.Once` and nil-check to make repeated/concurrent stops safe and idempotent.
Closes: #5018
- Remove the **Playful Proxy API Panel (PPAP)** and **Alex** entries from `README.md`, `README_CN.md`, and `README_JA.md`.
- Keep the remaining project list and note section unchanged.
- Canonicalize model IDs when building session-affinity cache/fallback keys so variant suffixes (for example thinking modes) map to the same binding.
- Normalize model values from result metadata/on-result release paths to release and rebind bindings consistently across canonical model keys.
Closes: #5016
- Add request-scoped error rule extraction from auth metadata or runtime provider config (including OpenAI compatibility fallback)
- Match rules by HTTP/status-code plus error body substring or regex patterns
- Support `stop`, `stop-and-cooldown`, `continue`, `continue-and-cooldown` actions with normalized validation
- Apply matched actions to execution results via request-scoped vs force-cooldown error codes and stop/continue flow control
- Introduce request-stop error wrappers/helpers for matching and unwrapping scoped stop state
Closes: #5006
- Return buffered pending stream errors from `errChan` when the data channel closes in streaming handlers.
- Emit error response instead of silently falling back to normal SSE completion paths.
- Replace Claude’s local `pendingClaudeStreamError` usage with shared `handlers.PendingStreamError`.
Closes: #4710
- Use original `call_id` / `tool_call_id` values as keys for tool-result/message caches and emitted-id tracking in Claude OpenAI request translators.
- Avoid mixed sanitized-vs-raw ID lookups that could cause duplicate or missed deduplication of repeated tool outputs.
- Refactor Gemini/OpenAI/Codex/Claude/Antigravity interaction and response translators to collect `parts`, `content`, `steps`, `tool_calls`, and `output` items in slices, then set each array once via `translatorcommon` helpers.
- Replace repeated incremental `sjson.SetRawBytes(..., ".-1", ...)` mutations with single-shot raw-array updates to avoid repeated rewrites.
- Keep existing step/content semantics while reducing ordering/overwrite risks when merging multi-fragment data (including thought/signature and web-search/function-call blocks).
- Emit plain text directly for Gemini→OpenAI chat responses when the message contains only one text part and no reasoning/tool/image content.
- Replace `choices` assignment via `JoinRawArray` with `translatorcommon.SetRawArrayItems` for direct raw-array insertion.
- Refactor multiple OpenAI/Gemini/Codex/Claude/Antigravity/Interactions response translators to collect output elements in slices and set arrays once via `translatorcommon.JoinRawArray`.
- Fix non-stream OpenAI→Gemini part handling so fields (`reasoning`, `content`, `functionCall`) from multiple choices overlay correctly on shared parts instead of being overwritten.
- Preserve explicit empty message fields in Gemini→OpenAI chat-completion conversion (`content`/`reasoning_content`) and add tests covering empty-text and cross-choice overlay cases.
- Add shared `GenerateClaudeToolCallID` in `common` and replace duplicated local `toolu_` generators across Claude request translators for consistent, uniform ID generation.
- Replace `bufio.Scanner`-based SSE line handling with manual newline parsing and use `JoinRawArray` for array assembly to avoid scanner buffer limits and extra wrapping allocations.
- Improve carrier/thought signature scrubbing to safely strip internal metadata only when present and detect spoofed/duplicate `thoughtSignature` keys (including raw/unicode-escaped forms) during sanitized JSON traversal.
- Precompute the last tool message/output per `tool_call_id` in chat-completions and responses requests, then emit only one `tool_result` per call ID at its first position.
- Use the final payload for each duplicate tool call/output and preserve attachment of cache-control metadata from the final tool item.
Closes: #4997
- Normalize conversation-context fields across OpenAI↔Interactions request/response translation by mapping both `previous_response_id`/`previous_interaction_id` and resolving `environment_id` (including nested `environment.id`) in both directions.
- Forward `agent_config` through translation and, for antigravity models, route output-token limits to `agent_config.max_total_tokens` while removing antigravity-incompatible `generation_config` knobs from emitted requests.
- Preserve `environment_id` through stream state so chat-completions and responses (stream/non-stream) outputs consistently carry it in final payloads.
Closes: #4995
- Add a cleanup pass to hoist `then`/`else` properties into the containing schema path (including nested and `allOf` cases).
- Remove `if`/`then`/`else` from the unsupported keyword list so sanitized schemas no longer retain conditional keywords.
Closes: #4984
- Propagate session-affinity provider/model metadata through selection and execution results so affinity cleanup uses the same namespace/model keys as selection.
- Forward execution `Options` in `Result` across normal, home, and stream paths, then hook manager cooldown handling to notify the affinity selector.
- Add `Touch`/`CompareAndDelete` to session cache and switch affinity release logic to compare-and-delete semantics, preventing stale failures from deleting a session already rebound to another auth.
Closes: #4989
- add shared `EnsureResponsesUsageDetails` helper to patch `usage` objects with:
- `output_tokens_details.reasoning_tokens = 0`
- `input_tokens_details.cached_tokens = 0`
- for both plain JSON and SSE `data:` frames, including multi-line frames
- apply the helper to OpenAI Response format outputs in non-stream and stream paths across executors/plugins so translated payloads consistently include required usage details
- update websocket/completion payload builders to emit default `usage` detail fields for prewarm/finish responses
Closes: #4985
injectClaudeCodeCurrentDate inserted the reminder at index 0 of the first user message. Anthropic requires the message after an assistant tool_use turn to lead with its tool_result blocks, so the request was rejected with 400. Advance the insert index past leading tool_result blocks, matching the existing guard in prependClaudeSystemRemindersToFirstUserMessage. Every other content shape keeps the current index-0 placement.
- Treat request-invalid/invalid-parameter 401 faults as request-scoped so auth/model cooldown is skipped.
- Export request-scoped/connection-lifecycle error codes and add helpers to explicitly create or mark request-scoped `Error`s.
- Improve request-fault detection by checking the raw message body when `Error.Code` formatting hides JSON payloads.
Closes: #4976
- Add a sanitizer option to keep `additionalProperties: false` in response paths instead of converting it to description hints.
- Skip dropping `additionalProperties: false` in `removeUnsupportedKeywords` when response mode is enabled, while still removing non-false `additionalProperties`.
- Keep tool-schema behavior unchanged by continuing to remove `additionalProperties` and emit compatibility hints for non-response conversions.
Closes: #4961
- Update OpenAI Responses conversion to map finish reasons (`length`/`max_tokens`/`content_filter`) to `response.incomplete` with proper `incomplete_details`.
- Propagate `incomplete` status to response, message, and function/custom tool items in both streaming and non-stream paths.
- Prevent premature completion of tool-call items when a stream ends without a terminal finish reason or with incomplete/partial tool arguments.
Closes: #4873
- Add registry generation tracking and a Codex catalog revision accessor to provide stable cache keys.
- Cache parsed model templates and rendered spawn-agent markdown, and reuse them across requests when revision/generation are unchanged.
- Invalidate/recompute caches when model registrations or catalog content change, and separate rewrite handling for spawn tool descriptions vs. message encryption stripping.
Closes: #4967