Commit Graph

1665 Commits

  • fix(proxy): improve cache hit rate for Codex/Responses requests
    prompt_cache_key was falling back to provider.id when the client did not
    supply a session, which collapsed every conversation onto a single key
    and defeated upstream prefix caching. Only emit the key when a real
    client-provided session/thread identity is available; otherwise let the
    upstream use its default matching behaviour.
    
    Additional fixes that affect cache stability:
    - Canonicalise (sort) JSON keys in outgoing request bodies and in
      tool_call arguments / tool_result content so semantically identical
      requests produce identical byte sequences for upstream prefix caches.
    - Exempt JSON Schema property maps (properties, patternProperties,
      definitions, \$defs) from the underscore-prefix filter so user-defined
      schema keys like _id and _meta survive.
    - Add a [CacheTrace] debug log with stable hashes for instructions,
      tools, input and include to help diagnose cache misses.
    - Thread session_id into the usage logger for request correlation.
  • fix(proxy): drop empty pages from Read tool input (#2472)
    * fix(proxy): drop empty pages from Read tool input
    
    * fix(proxy): preserve Read args across duplicate tool starts
  • - fix(ci): restore frontend formatting and Linux clippy
    - Format Claude Desktop provider presets with Prettier
    
    - Gate platform-specific Claude Desktop path helpers behind cfg
  • chore(icons): add ClaudeCN and RunAPI raster icons
    Resized to 512x512 to match the existing extracted-icons size convention
    (hermes.png / lemondata.png). Source uploads were oversized (7.3 MB
    8635x8635 for ClaudeCN; 800x800 for RunAPI) and would have bloated the
    bundle if imported as-is.
    
    Note: not yet wired into index.ts / metadata.ts; register there when
    they need to surface in the app UI.
  • docs: add RunAPI sponsor entry to README (en/zh/ja)
    Insert sponsor row in all three README locales linking to runapi.co.
    EN/JA copy adapted from the Chinese original. Banner center-cropped to
    the project standard 1920x798 aspect ratio (preserves the logo and
    slogan, trims the decorative top/bottom padding) and saved as JPEG to
    keep file size reasonable (1.3 MB PNG -> 206 KB JPEG).
  • docs: add ClaudeCN sponsor entry to README (en/zh/ja)
    Insert sponsor row in all three README locales linking to claudecn.top.
    EN/JA copy adapted from the Chinese original. Banner normalized to the
    project standard sponsor image spec (1920x798 RGB on white background)
    and recompressed; alt text unified to "ClaudeCN" across all three files.
  • docs: use Volcengine logo for Chinese README sponsor entry
    Replace byteplus.png with localized huoshan.png (Volcengine/火山引擎)
    in README_ZH.md so Chinese readers see the regional brand. EN/JA
    README continue to use the BytePlus logo and link to byteplus.com.
    
    The new logo is normalized to the project's standard sponsor image
    spec: 1920x798 RGB on a white background.
  • docs: add BytePlus sponsor entry to README (en/zh/ja)
    Insert sponsor row in all three README locales. EN/JA point to
    byteplus.com/modelark; ZH points to volcengine.com/agentplan
    (Volcengine being the China-region counterpart). Logo normalized
    to the project-standard 1920x798 RGB white background and
    recompressed (432K -> 84K).
  • feat(claude-desktop): add 44 provider presets translated from Claude Code
    - New src/config/claudeDesktopProviderPresets.ts with the Claude Code
      preset list re-shaped into Desktop's three-segment route format
      (routeId / upstreamModel / displayName); excludes OAuth providers,
      AWS Bedrock (no SigV4 support) and KAT-Coder (placeholder URL).
    - Non-Claude upstream presets show upstream model id as displayName
      (e.g. deepseek-v4-pro) so the Desktop model list reflects what is
      actually being requested. OpenRouter/TheRouter/PIPELLM keep
      Sonnet/Opus/Haiku since their upstream really is Anthropic Claude.
    - Wire ProviderPresetSelector into ClaudeDesktopProviderForm so
      selecting a preset back-fills baseUrl, mode, routes and apiFormat.
    - Drop the hard-coded ANTHROPIC_AUTH_TOKEN write in handleSubmit so
      ANTHROPIC_API_KEY presets (LemonData / AiHubMix / Gemini Native)
      save under the correct env key, and clear the opposite key on switch.
    - Hide the universal-providers tab for claude-desktop because its
      meta-driven routing has no analogue in the universal flat-env shape.
    - Add apps."claude-desktop" i18n key (zh/en/ja) so the dialog tab
      label resolves instead of showing the literal key.
  • chore(backend): satisfy cargo fmt and clippy --all-targets
    - Apply rustfmt diffs in claude_desktop_config.rs
    - Allow needless_return on current_platform_paths (cfg-mirrored arms)
    - Allow too_many_arguments on RequestForwarder::forward
    - Replace `let mut + reassign` with struct literals in tests
      (settings, backup, provider, response_processor)
    - Use Path::new instead of PathBuf::from to fix cmp_owned in misc tests
    - Replace 3.14 with 3.5 in config test to avoid approx_constant lint
  • docs: update sponsor logos and listings
    - Add ClaudeAPI as new sponsor (all 3 languages)
    - Remove ChefShop sponsor entry (all 3 languages)
    - Convert shengsuanyun logo from SVG to PNG
    - Standardize partner logos to 1920x798 canvas
    - Fix ClaudeAPI alt text typo and spacing
    - Sync sponsor order across zh/en/ja READMEs
  • refactor(claude-desktop): show badge only for providers requiring routing
    Direct-mode providers no longer display a badge since routing is
    optional for them. Proxy-mode providers now show "需要路由" / "Requires
    routing" to clarify that local routing must be active.
  • fix(claude-desktop): match proxy model route without [1M] suffix
    Claude Desktop strips the [1M] suffix from model IDs when sending
    requests, causing route lookup to fail with "model route is not
    configured". Fall back to base-name comparison when exact match misses.
  • refactor(claude-desktop): simplify model mapping UX
    - Remove "Import from Claude" button from main provider list (keep in empty state)
    - Remove "Desktop model" column from proxy mode mapping table; route names are now auto-generated
    - Rename "upstream model" label to "requested model" and equalize column widths
    - Rewrite model mapping hint and toggle description for end-user clarity
    - Update validation messages and remove dead i18n keys (routeModelLabel)
  • refactor(claude-desktop): align provider form UI with Claude Code
    - Rename field labels: "Gateway Base URL" → "API Endpoint", "Bearer Token" → "API Key"
    - Change layout from 2-column grid to vertical sections matching Claude Code
    - Reuse shared EndpointField component with format-aware amber hint box
    - Replace native <datalist> with vendor-grouped ModelDropdown (OpenCode pattern)
    - Move Fetch/Add buttons to section headers with compact sm styling
    - Extract ModelDropdown to shared module, deduplicate from OpenCodeFormFields
    - Extract renderActionButtons helper to eliminate proxy/direct button duplication
    - Remove dead i18n keys (gatewayBaseUrl, bearerToken) from all 3 locales
  • fix(claude-desktop): remove proxy-stopped status alert
    The route toggle in the top-right corner already communicates proxy
    state; the extra warning banner was redundant and inconsistent with
    other managed apps.
  • refactor(claude-desktop): trim duplication in proxy and switch flows
    - services/proxy.rs: collapse 10 repeated `OpenCode | OpenClaw | Hermes |
      ClaudeDesktop` match arms into `_` fallthroughs.
    - claude_desktop_config.rs: extract a `with_rollback` closure shared by
      apply_provider_to_paths and restore_official_at_paths.
    - useProviderActions.ts: replace the triple-nested ternary picking the
      switch-success toast message with a flat let/if/else block.
    
    Net -36 lines. No behavior change; cargo test and pnpm typecheck pass.
  • feat(claude-desktop): add 3P provider switching with proxy gateway
    Adds a new ClaudeDesktop AppType that writes Claude Desktop's third-party
    inference profile under configLibrary/, sharing _meta.json with other
    launchers (Ollama-compatible) so cc-switch can coexist with them.
    
    Two switch modes:
    - direct: provider already exposes claude-* / anthropic/claude-* model
      ids on Anthropic Messages, Claude Desktop connects to it directly.
    - proxy: cc-switch's local proxy acts as the inference gateway,
      presenting only claude-* route names to Claude Desktop and mapping
      them to real upstream models. Required after Anthropic restricted
      Claude Desktop to claude-family ids.
    
    Backend:
    - New module claude_desktop_config with snapshot/rollback, official seed
      bypass, /claude-desktop/v1/{models,messages} routes, and a single
      source of truth for default proxy routes.
    - Gateway token persisted in SQLite, validated on every proxied request.
    - get_claude_desktop_status surfaces drift signals (stale models,
      missing routes, proxy stopped, base URL mismatch, missing token).
    
    Frontend:
    - Slim ClaudeDesktopProviderForm independent from ProviderForm,
      controlled by a top-level appId guard.
    - ProviderList banner consumes the status query (5s polling) and
      renders actionable diagnostics.
    - ClaudeDesktopRouteToggle in the header to start/stop the local
      gateway without touching takeover state.
    - Three-locale i18n synchronised.
  • fix(proxy): reuse pooled HTTPS connections for non-Anthropic backends
    The hyper raw-write path preserves original header casing but rebuilds
    TCP+TLS on every request — there is no connection pool — which was the
    root cause of slow reverse-proxy throughput.
    
    Only Anthropic-native requests actually need exact header-case
    preservation. Route OpenAI/Copilot/Codex/Gemini/codex_oauth requests
    through the pooled reqwest client (pool_max_idle_per_host=10,
    tcp_keepalive=60s) instead, so warm connections get reused.
    
    Streaming requests get a precise first-byte timeout via
    tokio::time::timeout around reqwest's send() (which resolves on
    response headers), with the body phase handed off to response_processor.
    The streaming-detection helper now also covers Gemini SSE endpoints
    and Accept: text/event-stream, not just body.stream.
  • Fix Codex startup live import duplication (#2590)
    * Fix Codex startup live import duplication
    
    * Fix: Prevent duplicate Codex default provider on restart & add startup import tests
  • feat: return reasoning_content with tool_calls for DeepSeek models (#2543)
    * feat: return reasoning_content with tool_calls for DeepSeek models
    
    * fix: correct reasoning_content handling for DeepSeek tool_calls
    
    * test: cover DeepSeek reasoning content round trip
    
    ---------
    
    Co-authored-by: Jason <farion1231@gmail.com>
  • fix(ci): pin Claude review checkout to PR head sha
    Prevents `git fetch origin pull/<N>/head:main` failures on fork PRs
    whose head branch is also named `main` — using a SHA puts the runner
    in detached HEAD so the main ref is free for the action's internal fetch.
  • refactor(theme): drop unused MouseEvent param from setTheme
    Now that the view transition animation is gone, setTheme no longer
    needs click coordinates. Reduce the API surface to (theme: Theme) =>
    void and simplify the call sites in mode-toggle and ThemeSettings.
  • refactor(theme): remove circular reveal animation for theme switching
    The View Transitions API used here crashes WebKitGTK with SIGSEGV on
    Linux. Rather than gating document.startViewTransition per platform
    (see PR #2502), drop the animation entirely — it's a low-value visual
    flourish on a low-frequency action that doesn't justify a permanent
    platform branch.
    
    Removes the ::view-transition-* CSS block and the coordinate plumbing
    in setTheme. The optional event parameter is kept on the API surface
    to keep call sites compiling; they'll be cleaned up in a follow-up
    commit.
  • fix(ci): drop --max-turns 5 from Claude review args
    Cap was too tight — review tasks need 8-15 turns to read files, analyze,
    and post results. The first run after the previous prompt change failed
    with error_max_turns at turn 6. The disallowedTools list already keeps
    the agent in read-only mode, so an explicit turn cap is redundant.
  • chore(ci): tune Claude review prompt to reduce nitpick noise
    Previous prompt produced 5-10 findings per PR including dead-code/style
    nits mixed with real bugs. New prompt adds severity tiering, an 80
    confidence threshold, an explicit anti-pattern list, a 5-nit cap, and
    permits zero-comment LGTM as a valid outcome. Calibration follows
    Anthropic Code Review docs and the claude-plugins-official prompt.
  • chore(deps): bump actions/stale from 9 to 10 (#2520)
    Bumps [actions/stale](https://github.com/actions/stale) from 9 to 10.
    - [Release notes](https://github.com/actions/stale/releases)
    - [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md)
    - [Commits](https://github.com/actions/stale/compare/v9...v10)
    
    ---
    updated-dependencies:
    - dependency-name: actions/stale
      dependency-version: '10'
      dependency-type: direct:production
      update-type: version-update:semver-major
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
  • chore(deps): bump softprops/action-gh-release from 2 to 3 (#2519)
    Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2 to 3.
    - [Release notes](https://github.com/softprops/action-gh-release/releases)
    - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
    - [Commits](https://github.com/softprops/action-gh-release/compare/v2...v3)
    
    ---
    updated-dependencies:
    - dependency-name: softprops/action-gh-release
      dependency-version: '3'
      dependency-type: direct:production
      update-type: version-update:semver-major
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
  • chore(deps): bump pnpm/action-setup from 5 to 6 (#2518)
    Bumps [pnpm/action-setup](https://github.com/pnpm/action-setup) from 5 to 6.
    - [Release notes](https://github.com/pnpm/action-setup/releases)
    - [Commits](https://github.com/pnpm/action-setup/compare/v5...v6)
    
    ---
    updated-dependencies:
    - dependency-name: pnpm/action-setup
      dependency-version: '6'
      dependency-type: direct:production
      update-type: version-update:semver-major
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
  • chore(deps): bump actions/checkout from 4 to 6 (#2517)
    Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 6.
    - [Release notes](https://github.com/actions/checkout/releases)
    - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
    - [Commits](https://github.com/actions/checkout/compare/v4...v6)
    
    ---
    updated-dependencies:
    - dependency-name: actions/checkout
      dependency-version: '6'
      dependency-type: direct:production
      update-type: version-update:semver-major
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
  • chore(ci): add Claude Code Action for review-only @claude mentions
    - Restrict to repo collaborators via author_association check
    - Use OAuth token for subscription billing
    - Disable Edit/Write tools and set contents:read permission to enforce review-only
  • fix(proxy): derive Claude auth strategy from ANTHROPIC env var name
    Anthropic SDK assigns distinct semantics to the two env vars:
    
    - ANTHROPIC_API_KEY    -> x-api-key
    - ANTHROPIC_AUTH_TOKEN -> Authorization: Bearer
    
    The Claude adapter previously collapsed both into AuthStrategy::Anthropic
    and then emitted Authorization: Bearer regardless, breaking strict
    Anthropic-protocol endpoints (Anthropic official, Cloudflare AI Gateway,
    OpenCode Go, DashScope) and silently overriding the user's intended auth
    scheme.
    
    - claude::extract_auth: infer strategy from env var name
      (ANTHROPIC_AUTH_TOKEN -> ClaudeAuth, ANTHROPIC_API_KEY -> Anthropic),
      matching the precedence already used by extract_key.
    - claude::get_auth_headers: split the Anthropic arm so it emits
      x-api-key, while ClaudeAuth and Bearer continue to use Bearer.
    - stream_check: reuse ClaudeAdapter::get_auth_headers as the single
      source of truth, replacing the prior "always Bearer + maybe x-api-key"
      double injection that produced auth conflicts and false-negative
      health checks.
    - Cover each strategy -> header mapping and env-var precedence with
      new unit tests in claude.rs.
    
    Refs #2368, #2380
  • fix(proxy): strip leading billing header from system content (#2350)
    Claude Code injects a dynamic `x-anthropic-billing-header` line at the
    start of `system` content. Its rotating `cch=` token was forwarded into
    OpenAI Responses `instructions` and Chat system messages, which broke
    upstream prefix prompt cache reuse — a stable ~95k-token prefix was
    getting re-charged on every request.
    
    Strip only the leading occurrence in both anthropic_to_openai and
    anthropic_to_responses; later occurrences are preserved so user-authored
    prompt text containing the same string is not lost.
  • chore(usage): drop Hermes Agent tracking integration
    Hermes aggregates all in-process API calls into a single sessions row
    with the `model` field locked to the initial model, so the usage
    dashboard cannot cleanly surface per-call billing context. Two rounds
    of UI workarounds (raw mapping, then `<model> @ <host>` display) did
    not resolve the user-facing confusion, so the whole tracking
    integration is dropped for now.
    
    Removes session_usage_hermes service (and its 17 tests), sync wiring
    in commands/usage.rs and lib.rs, _hermes_session/hermes_session
    entries in usage_stats SQL (provider_name_coalesce CASE and
    effective_usage_log_filter IN clause), frontend Tab/banner/dropdown/
    icon entries, and four i18n keys per locale.
    
    Hermes app integration outside usage tracking (proxy routing,
    session manager, config) is preserved. Pre-existing hermes rows in
    proxy_request_logs are left as orphans — filtered out by the
    updated SQL and never surfaced in the UI.
  • feat: persist Tauri window state (#2377)
    Add the window-state plugin and explicitly save size and position across app exit, restart, and lightweight-mode transitions.
  • feat(providers): add Baidu Qianfan Coding Plan for Claude Code (#2322)
    * feat(providers): add baidu qianfan coding plan presets
    
    * refactor(providers): align qianfan presets with existing format
    
    * chore(providers): narrow qianfan coding plan scope
  • fix(coding-plan): correct zhipu weekly tier name by reset time (#2420)
    Zhipu's `data.limits[]` returns 1 entry for legacy plans (subscribed
    before 2026-02-12) and 2 entries for current plans. Previously every
    TOKENS_LIMIT entry was hardcoded as `five_hour`, so the weekly bucket
    was rendered with the 5-hour i18n label.
    
    Sort TOKENS_LIMIT entries by nextResetTime ascending and assign
    `five_hour` to index 0, `weekly_limit` to index 1. Legacy plans
    naturally degrade to a single five_hour tier.
    
    Also harden the parser: case-insensitive type match (defends against
    upstream casing changes), reuse TIER_FIVE_HOUR/TIER_WEEKLY_LIMIT
    constants, and add 8 unit tests covering both plan shapes plus
    defensive edge cases.
  • fix(dashscope): enhance usage parsing robustness to prevent VSCode cr… (#2425)
    * fix(dashscope): enhance usage parsing robustness to prevent VSCode crashes
    
    Enhanced build_anthropic_usage_from_responses() to handle null, missing, empty,
    and partial usage fields gracefully. This prevents VSCode Extension crashes with
    "Cannot read properties of null (reading 'output_tokens')" when connecting to
    DashScope (Alibaba Cloud Bailian) models.
    
    Changes:
    - Added defensive null checks and empty object detection
    - Implemented OpenAI field name fallbacks (prompt_tokens/completion_tokens)
    - Added comprehensive logging for malformed usage scenarios
    - Fixed streaming SSE event handlers with null-safe usage access
    - Preserved cache token fields even when input/output tokens are missing
    
    This ensures the proxy never crashes on malformed Responses API usage objects,
    returning valid Anthropic-compatible usage structures (input_tokens/output_tokens)
    in all cases.
    
    * fix(proxy): tighten Responses API usage fix per review
    
    - Drop redundant fallback in streaming.rs Chat Completions path; the
      existing if-let-Some guard already prevents usage:null, so the extra
      layer was dead code and caused a fmt-breaking indentation issue.
    - Demote partial-usage warn to debug. Streaming chunks legitimately
      arrive with partial token counts and the warn-level log was noisy.
    - Rewrite CHANGELOG entry: reference #2422, broaden scope from
      DashScope-only to all api_format=openai_responses users (Codex OAuth
      is the strongest signal; DashScope compatible-mode/v1/responses is
      the original report).
    - cargo fmt to clear 12 formatting differences vs main.
    
    ---------
    
    Co-authored-by: Jason <farion1231@gmail.com>