154 Commits

Author SHA1 Message Date
Luis Pater
5ff4a31eaf feat(claude): support advisor tool beta header
- Add `advisor-tool-2026-03-01` beta header support when advisor server tools are declared or explicitly requested.
- Ensure proper ordering of the advisor tool beta before advanced tool use and effort betas.
- Inject and preserve advisor tool beta across token counting and fingerprint preservation paths.

Closes: #5330
2026-08-29 14:47:51 +08:00
Luis Pater
b5cde4ba42 fix(gemini): add default items schema for tool array definitions
- Populate a default string `items` schema on tool array definitions missing an items property for Gemini and Antigravity.
- Add helper to identify array declarations across single strings and type lists during schema normalization.

Closes: #5292
2026-08-28 13:21:50 +08:00
Luis Pater
d36b776c79 fix(test): improve cross-platform compatibility across unit tests
- Support CRLF line endings and skip hidden dot directories in nocopy invariant tests.
- Handle environment variable overrides and skip case-sensitive token priority tests on Windows.
- Use static OS and architecture values in Claude header fingerprint assertions for deterministic test results.

Closes: #5295
2026-08-28 05:01:42 +08:00
Luis Pater
adac1e5816 fix(gemini): improve schema normalization for unions and unsupported constraints
- Avoid overwriting parent properties when resolving `anyOf`/`oneOf` unions on object schemas, merging branch properties instead.
- Add `contains` to unsupported constraints and preserve object/array constraint hints in descriptions.
- Strip `required` arrays when the schema does not define a `properties` object.

Closes: #5219
2026-08-27 02:14:40 +08:00
Luis Pater
2555cde2f1 fix(xai): inline local refs and broaden codex app tool normalization
- Export `InlineLocalRefs` utility to resolve local JSON Pointer references.
- Inline local definitions and remove `$defs`/`definitions` in tool function parameters.
- Support `mcp__` prefixes and `codex_apps` namespace variations when identifying Codex app automation update tools.
- Handle `$ref` entries when inspecting and normalizing root union schema branches.
2026-08-26 23:41:44 +08:00
Luis Pater
e1bf893956 feat(github): resolve GitHub token for release checks and asset updates
- Add `util.ResolveGitHubToken` to resolve GitHub API tokens with priority order (`GITHUB_TOKEN`, `github_token`, and `GITSTORE_GIT_TOKEN` for GitHub repositories).
- Set GitHub `Authorization` headers in management version check and asset updater requests when a token is resolved.

Closes: #5189
2026-08-25 04:22:46 +08:00
Luis Pater
a7e3596b7e fix(gemini): normalize malformed schema nodes before cleanup
- Added a preprocessing pass to repair malformed MCP-style JSON schemas.
- Wraps bare property maps as object schemas, promotes `required: true` flags to parent `required` arrays, and removes boolean `required` from properties.
- Recurses through nested schema containers and skips known API request envelopes to avoid rewriting non-schema docs.
- Switched schema decoding/serialization path to preserve large numeric values and avoid HTML-escaping side effects.

Closes: #5178
2026-08-22 22:39:03 +08:00
Luis Pater
556328c122 feat(gemini): add namespace-aware OpenAI Responses tool resolution and custom tool call conversion
- Add shared tool descriptor collection and winner selection for Responses tools (top-level vs `additional_tools`, direct vs namespace child, and ordering rules).
- Introduce sanitized Gemini function name mapping with collision disambiguation and 64-char-safe truncation.
- Build forward/reverse tool identity maps for restoring original tool identity (`name`, `namespace`, `custom`) during translation.
- Update Gemini→Responses streaming conversion to emit proper custom tool call events and identity-aware function call events.
- Add helpers for translating `tool_choice` to Gemini config and unwrapping custom tool input payloads.

Closes: #5088
2026-08-20 13:47:15 +08:00
sususu
ac0d1888c0 fix(gemini): strip 'encrypted' metadata from tool parameters schemas (#5065) 2026-08-19 19:13:56 +08:00
Luis Pater
e424bfad00 feat(executor): support $-based custom headers from downstream request headers
- 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
2026-08-18 19:33:50 +08:00
sususu98
f1b0431c77 feat(claude): add fingerprint-profile=claude-code-cli for API keys and delegated providers (#5047)
* 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
2026-08-18 17:29:26 +08:00
shengyy
7d55d0da0d fix(antigravity): preserve schema semantics 2026-08-16 13:00:59 +08:00
shengyy
e23395a92b test(antigravity): cover schema sanitizer semantics 2026-08-16 13:00:50 +08:00
Luis Pater
f53a2b6e80 fix(gemini): merge conditional schema branches during JSON schema cleanup
- 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
2026-08-15 17:24:48 +08:00
Luis Pater
61c4fd87f5 fix(gemini): preserve additionalProperties:false for Antigravity response schemas
- 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
2026-08-15 04:48:42 +08:00
sususu
b921b5d032 Review: tighten the in-place byte write guard
Reviewing the guard against itself surfaced two defects. The allowlist was keyed
by file, so a new in-place write inside an already reviewed file passed silently,
and internal/wsrelay/manager.go was listed without ever matching a pattern.

Record the reviewed occurrence count per file and reject any deviation in either
direction: an added write in a reviewed file now fails, and a stale entry whose
writes disappeared fails too. Drop the entry that never matched.
2026-08-09 00:40:57 +08:00
sususu
1737596e02 Make the payload-reuse guards able to fail
TestConvertGeminiRequestToGeminiReusesLargeNormalizedPayload asserted the reuse
invariant with b.Fatal inside testing.Benchmark. A failing benchmark aborts
before completing an iteration and returns a zero BenchmarkResult, so
AllocedBytesPerOp reported 0 and the allocation check passed exactly when the
payload was being copied. Injecting a full copy into the translator left the
test green. Assert the invariant with t.Fatal and reject a zero-iteration
benchmark result; both injected regressions now fail the test.

Add two static guards for the no-copy invariant. Results handed out by the
no-copy helpers alias the caller's buffer, and mutating those bytes corrupts
already-derived values in a way the race detector cannot see. One guard rejects
in-place sjson options, the other keeps the set of in-place byte writes small
and documented: each reviewed site records why it cannot corrupt a no-copy
result, and a new site fails the test until it is justified.
2026-08-09 00:40:57 +08:00
sususu
ea8882bf26 Add a no-copy GJSON parse helper
gjson.ParseBytes copies the entire document before returning a result, which
is prohibitive for multi-megabyte requests. ParseGJSONBytesNoCopy mirrors the
existing GetGJSONBytesNoCopy contract: the returned result references the
caller's bytes, so callers must neither retain it nor mutate the buffer while
it is in use.
2026-08-09 00:40:57 +08:00
sususu
3904c40d65 fix(antigravity): strip propertyNames inside a property named "properties"
A Claude Messages request carrying MCP-style tool schemas failed before
inference when routed to Antigravity/Gemini: the private backend parses
function declarations as a limited proto-JSON Schema and rejects unknown
fields, so `propertyNames` produced

  Unknown name "propertyNames" at
  'request.tools[0].function_declarations[0].parameters.properties[0].value'

The cleaner already lists `propertyNames` as unsupported, but every
cleaning pass is skipped for nodes classified as property maps, and that
classification matched any path ending in ".properties". A tool may
declare a property named "properties" — Notion's page tools do — and the
schema for that property then sits at ".properties.properties", so it was
mistaken for a property map and nothing inside it was cleaned. The 400
above points at exactly that node.

Replace the suffix match with a parity check over the trailing run of
name-map keywords: the node a keyword names is a map only when its own
parent is a schema, so "properties" is a map, "properties.properties" is
the schema of a property named "properties", and a third repetition is a
map again. Only the trailing run is inspected, so a schema nested under
any prefix by the caller is classified the same way.

Codex/OpenAI egress is untouched: it converts input_schema separately,
uses non-strict tool mode, and accepts this shape.
2026-08-03 16:09:22 +08:00
Luis Pater
2b63d6bcda refactor(util): use structured options for JSON schema cleaning
- 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
2026-07-30 03:40:04 +08:00
Luis Pater
5dedb303f1 feat(util, executor, translator): enhance Antigravity schema handling and response formatting
- Introduced `CleanJSONSchemaForAntigravityResponse` to process schemas without adding tool-specific placeholders.
- Updated `cleanJSONSchema` with `removeGeminiMetadata` parameter for improved schema control.
- Refactored Antigravity schema sanitization to handle declaration and generation paths independently.
- Mapped OpenAI `response_format` to Antigravity settings, ensuring proper response schema cleaning.
- Expanded tests to verify proper placeholder-free response schema handling and metadata preservation.

Closes: #4652
2026-07-29 17:35:29 +08:00
Luis Pater
59aa35a434 feat(util): add NormalizeClaudeToolInputSchema for input schema normalization
- Introduced `NormalizeClaudeToolInputSchema` in `util` package to standardize tool input schemas for compatibility with Claude.
- Replaced local `normalizeClaudeToolInputSchema` implementations in translator functions with the new utility method.
- Improved handling of schema validation, union elimination, and property merging.
- Removed redundant code in response and chat-completions translators, streamlining schema normalization logic.

Closes: #4428
2026-07-27 06:06:26 +08:00
sususu98
a4d18cb04a fix(antigravity): scope schema sanitization (#4571) 2026-07-26 01:52:20 +08:00
sususu98
72886356dd fix(antigravity): preserve complete reasoning replay (#4525) 2026-07-25 14:07:31 +08:00
Luis Pater
0296600be6 feat(models): add Claude client model catalog and response builder
- Introduced a new `models` package for organizing Claude client model templates and building responses.
- Migrated Claude response handling to `claudemodels.BuildResponse`.
- Added comprehensive tests for model ID transformation, sorting, and metadata validation.
- Removed redundant utility functions and simplified integration with the API server.
2026-07-25 01:41:07 +08:00
Luis Pater
53c1e7e2dd perf(util): introduce GetGJSONBytesNoCopy for efficient JSON parsing without data duplication
- Added `GetGJSONBytesNoCopy` in `internal/util` for safe, no-copy JSON parse operations leveraging `unsafe`.
- Replaced `gjson.GetBytes` with the new helper in key payload processing paths (`kimi_executor`, `vertex_payload_helpers`, etc.) to improve performance.
- Added unit tests for behavior validation, including edge cases with empty input.
2026-07-20 15:37:55 +08:00
Luis Pater
c8803713c9 feat(translator): disambiguate and deduplicate tool names in request/response workflows
- Introduced `SanitizedFunctionNameMap` and `DisambiguatedToolNameMap` for consistent mapping between sanitized and original tool names.
- Added collision handling with deterministic hash suffixes for name disambiguation within character limits.
- Implemented logic to deduplicate and map tool and function names across translator components, including Interactions, Claude, and Gemini.
- Updated test cases to validate name restoration, deduplication, disambiguation, and mapping behaviors.

Closes: #4244
2026-07-15 04:49:30 +08:00
Luis Pater
ee71dc52b7 feat(auth/models): implement Claude model ID prefix handling and plugin auth disabled state support
- Added `EnsureClaudeModelIDPrefix` and `ResolveClaudeModelIDPrefix` utilities for standardizing and decoding Claude model IDs.
- Updated handling of `claude-fable-5-dd-<reversed>` model IDs for request routing and response formatting.
- Incorporated logic to enforce disabled states for plugin virtual auths and their expanded children.
- Refactored code for applying and persisting disabled state metadata in plugin multi-auth scenarios.
- Implemented and extended unit tests to validate expected behavior.
2026-07-09 22:39:59 +08:00
Luis Pater
4c78e40da4 feat(auth): unify provider key handling with OpenAI compatibility support
- Replaced direct `strings.ToLower` usage with `util.OpenAICompatibleProviderKey` for generating provider keys.
- Updated auth and executor workflows to use namespaced keys for OpenAI-compatible providers.
- Adjusted tests to validate namespaced key handling, including new test cases for provider registration and execution logic.
- Added `OpenAICompatibleProviderKey` helper in `util` for consistent key transformations.

Closes: #3600
2026-06-20 00:58:20 +08:00
Luis Pater
c44d4fcc7c feat(schema): add removal of $comment and enumDescriptions in JSON schema processing
- Updated JSON schema handling to remove `$comment` and `enumDescriptions` fields during schema transformations.
- Adjusted test cases to validate the removal of these fields both at root and nested levels.
- Expanded unsupported schema keywords to include `$comment` and `enumDescriptions` for Gemini compatibility.

Closes: #3512
2026-06-20 00:22:08 +08:00
Luis Pater
2406daf3ef feat(util): normalize Claude tool_result content and improve Gemini integration
- Added `ConvertClaudeToolResultContent` to standardize Claude tool_result content, preserving JSON structure and splitting out base64-encoded images.
- Updated Gemini and Gemini-CLI translators to use the new utility for generating deterministic function responses and inline image parts.
- Added comprehensive test cases for content types and edge cases, ensuring correct handling of string, JSON, and image blocks.

Closes: #2781
2026-06-16 08:09:30 +08:00
Mad Wiki
d606faa99c fix: strip Claude Code attribution from non-Anthropic translations 2026-05-17 04:21:53 +08:00
Luis Pater
a44e5eb1ab Merge branch 'v7' into dev 2026-05-10 02:33:42 +08:00
lihan3238
4cbe172934 refactor: extract DefaultAuthDir constant per review feedback 2026-05-08 22:28:38 +08:00
lihan3238
4071fdef84 fix: apply default auth-dir when config value is empty
When auth-dir is not specified in config.yaml, ResolveAuthDir returns
an empty string which causes os.MkdirAll to fail with no path. Use
the documented default ~/.cli-proxy-api instead.

Fixes #3272
2026-05-08 21:47:41 +08:00
Luis Pater
e50cabac4b chore: upgrade CLIProxyAPI dependency to v7 across the project
- Updated all references from v6 to v7 for `github.com/router-for-me/CLIProxyAPI`.
- Ensured consistency in imports within core libraries, tests, and integration tests.
- Added missing tests for new features in Redis Protocol integration.
2026-05-08 11:46:46 +08:00
Luis Pater
38573050aa feat(config): add support for disabling OpenAI compatibility providers
- Introduced a `Disabled` flag to OpenAI compatibility configurations.
- Updated routing, auth selection, and API handling logic to respect the `Disabled` state.
- Extended relevant APIs, YAML configurations, and data structures to include the `Disabled` field.
- Adjusted all relevant loops and filters to skip disabled providers.

Closes: #3060 #3059 #2977
2026-04-26 21:49:36 +08:00
muzhi1991
eba561bf6f fix(util): also keep Host in header map for synthetic requests
Addressing the P1 note from the Codex reviewer: applyCustomHeaders is
also called with a synthetic &http.Request{Header: ...} from the
websockets executors (aistudio_executor.go, codex_websockets_executor.go),
which forward only the header map. The previous continue meant a custom
Host was dropped from that map, regressing virtual-host overrides on
those flows. Mirror the value to both r.Host (for real net/http) and
r.Header (for header-map-only consumers).
2026-04-17 09:28:59 +08:00
muzhi1991
8f9e6622b0 fix(util): forward custom Host header to upstream
Custom headers configured under openai-compatibility (and any other
provider passing through applyCustomHeaders) were silently dropped for
the Host key, because Go's net/http reads the wire Host from
req.Host, not req.Header["Host"]. As a result, virtual-host routed
upstreams (e.g. LiteLLM behind an ingress) saw the base-url's host
instead of the user-configured override and returned 404.

Detect the Host key with http.CanonicalHeaderKey and assign it to
req.Host so it is actually written on the wire. Other headers continue
to use Header.Set as before.

Fixes #2833
2026-04-16 20:45:37 +08:00
Luis Pater
8fac29631d chore: remove Qwen support from SDK and internal components
- Deleted `QwenAuthenticator`, internal `qwen_auth`, and `qwen_executor` implementations.
- Removed all Qwen-related OAuth flows, token handling, and execution logic.
- Cleaned up dependencies and references to Qwen across the codebase.
2026-04-15 12:16:08 +08:00
sususu98
e8bb350467 fix: extend tool name sanitization to all remaining Gemini-bound translators
Apply SanitizeFunctionName on request and RestoreSanitizedToolName on
response for: gemini/claude, gemini/openai/chat-completions,
gemini/openai/responses, antigravity/openai/chat-completions,
gemini-cli/openai/chat-completions.

Also update SanitizedToolNameMap to handle OpenAI format
(tools[].function.name) in addition to Claude format (tools[].name).
2026-03-22 14:06:46 +08:00
sususu98
755ca75879 fix: address review feedback - init ToolNameMap eagerly, log collisions, add collision test 2026-03-22 13:24:03 +08:00
sususu98
2398ebad55 fix(translator): sanitize tool names for Gemini function_declarations compatibility
Claude Code and MCP clients may send tool names containing characters
invalid for Gemini's function_declarations (e.g. '/', '@', spaces).
Sanitize on request via SanitizeFunctionName and restore original names
on response for both antigravity/claude and gemini-cli/claude translators.
2026-03-22 13:10:53 +08:00
Luis Pater
2bd646ad70 refactor: replace sjson.Set usage with sjson.SetBytes to optimize mutable JSON transformations 2026-03-19 17:58:54 +08:00
Muran-prog
152c310bb7 test: add uniqueItems stripping test
Covers the fix from the previous commit — verifies uniqueItems is
removed from the schema and moved to the description hint.
2026-03-14 21:22:14 +02:00
Muran-prog
f6bbca35ab fix: strip uniqueItems from Gemini function_declarations (#2123)
Gemini API rejects uniqueItems in tool schemas with 400. Add it to
unsupportedConstraints alongside minItems/maxItems where it belongs.

Same class of fix as #1424 and #1531.
2026-03-14 21:18:06 +02:00
Luis Pater
ddaa9d2436 Fixed: #2034
feat(proxy): centralize proxy handling with `proxyutil` package and enhance test coverage

- Added `proxyutil` package to simplify proxy handling across the codebase.
- Refactored various components (`executor`, `cliproxy`, `auth`, etc.) to use `proxyutil` for consistent and reusable proxy logic.
- Introduced support for "direct" proxy mode to explicitly bypass all proxies.
- Updated tests to validate proxy behavior (e.g., `direct`, HTTP/HTTPS, and SOCKS5).
- Enhanced YAML configuration documentation for proxy options.
2026-03-11 11:08:02 +08:00
Xu Hong
553d6f50ea fix: sanitize tool_use.id to comply with Claude API regex ^[a-zA-Z0-9_-]+$
Add util.SanitizeClaudeToolID() to replace non-conforming characters in
tool_use.id fields across all five response translators (gemini, codex,
openai, antigravity, gemini-cli).

Upstream tool names may contain dots or other special characters
(e.g. "fs.readFile") that violate Claude's ID validation regex.
The sanitizer replaces such characters with underscores and provides
a generated fallback for empty IDs.

Fixes #1872, Fixes #1849

Made-with: Cursor
2026-03-06 00:10:09 +08:00
Luis Pater
4e1d09809d Fixed: #1741
fix(translator): handle tool name mappings and improve tool call handling in OpenAI and Claude integrations
2026-03-05 22:24:50 +08:00
Luis Pater
fdbd4041ca Fixed: #1531
fix(gemini): add `deprecated` to unsupported schema keywords

Add `deprecated` to the list of unsupported schema metadata fields in Gemini and update tests to verify its removal.
2026-03-05 11:48:15 +08:00