Commit Graph

1512 Commits

  • Merge branch 'main' into feat/usage-dashboard-refinement
    Resolve conflicts in usage dashboard components:
    
    - RequestLogTable.tsx: keep PR's new `range: UsageRangeSelection` API
      (replaces the old timeMode/rollingWindowSeconds mechanism), while
      preserving main's advanced pagination (ellipsis pages + jump input)
      and page-reset effect. Removed obsolete handleRefresh and datetime
      converters that are no longer needed under the new range API.
    
    - UsageDashboard.tsx: keep PR's redesigned filter panel with calendar
      picker and preset buttons. Drop the stale `timeRange={timeRange}`
      prop that was auto-preserved from main but references a variable
      that no longer exists in the PR's design.
  • fix(opencode): use json5 parser for trailing comma tolerance (#2023)
    * fix(opencode): use json5 parser for trailing comma tolerance
    
    OpenCode CLI writes opencode.json with trailing commas (valid JSONC),
    but CC Switch parsed it with serde_json (strict JSON), causing errors
    like 'trailing comma at line 35 column 3'.
    
    Switch to json5::from_str which accepts both JSON and JSONC. The json5
    crate is already a project dependency. Change error type from
    AppError::json() to AppError::Config() since json5::Error differs from
    serde_json::Error.
    
    * style(opencode): apply rustfmt to satisfy cargo fmt --check
    
    The previous commit's .map_err(...) chain exceeded rustfmt's default
    100-char max_width, breaking CI's `cargo fmt --check`. Let rustfmt
    wrap the closure body as a multi-line block. No behavior change.
    
    ---------
    
    Co-authored-by: 18067889926 <ming.flute@outlook.com>
    Co-authored-by: Jason <farion1231@gmail.com>
  • fix: preserve env vars when saving Google Official Gemini provider (#2087)
    write_gemini_live() unconditionally cleared env_map for GoogleOfficial
    auth type, discarding user-configured env vars (e.g. GEMINI_MODEL).
    Remove the env_map.clear() call so the user's settings_config.env is
    written as-is, and merge identical Packycode/Generic match arms.
  • feat: classify stream check errors with color-coded toasts
    Distinguish between "provider rejects probe" (yellow warning) and
    "genuinely broken" (red error) in health check results.
    
    Backend: add AppError::HttpStatus variant to carry structured HTTP
    status codes, populate http_status on error results, classify codes
    into short labels (e.g. "Auth rejected (401)"), and truncate overly
    long response bodies.
    
    Frontend: route 401/403/400/429/5xx to toast.warning with localized
    hints explaining the error may not indicate actual unusability; route
    404/402/connection errors to toast.error. Add i18n keys for all three
    locales (zh/en/ja).
    
    Also deduplicate check_once by reusing build_stream_check_result.
  • fix: auto-expand collapsed messages when search matches hidden content
    When a search query matches text beyond the collapse point, the message
    automatically expands to show the highlighted match. Also adds
    aria-expanded for accessibility.
  • perf: collapse long session messages to reduce text layout cost
    Messages over 3000 characters are now truncated to 1500 characters by
    default, with an expand/collapse toggle. This avoids expensive browser
    text layout for large AI responses containing code or tool output.
  • perf: virtualize session message list for long conversations
    Replace full DOM rendering with @tanstack/react-virtual to only render
    visible messages (~25 DOM nodes instead of N). Wrap SessionMessageItem
    in React.memo to prevent unnecessary re-renders on state changes.
  • fix: handle root-level skill repos during installation
    When a repo itself is a single skill (SKILL.md at repo root), the
    discovery phase sets directory to the repo name, but after ZIP
    extraction (which strips the root folder), no matching subdirectory
    exists. Add a fallback to check if SKILL.md exists directly in the
    extracted temp directory before reporting SKILL_DIR_NOT_FOUND.
    
    Fixes installation of repos like zlbigger/Google-SEOs.skill.
  • fix: remove duplicate usage summary from app filter bar
    The request count and total cost were displayed both in the app filter
    bar and in the UsageSummaryCards below it. Remove the redundant inline
    summary and its unused imports.
  • refactor: remove per-provider proxy config feature
    Replace all remaining "代理服务/Proxy Service/プロキシサービス" references
    in the local routing feature context with "路由服务/Routing Service/
    ルーティングサービス". This covers service settings, status messages,
    tooltips, field descriptions, and tab labels.
    
    Global Proxy, HTTP proxy hints, and AI Agent references are unchanged.
  • docs: rename takeover docs to routing across all languages
    Rename 4.2-takeover.md to 4.2-routing.md in zh/en/ja user manuals,
    replacing all "接管/takeover" terminology with "路由/routing" to match
    the rebranded feature name. Update README index links accordingly.
  • rename: rebrand "Local Proxy Takeover" to "Local Routing" in all locales
    Replace all user-facing references to "本地代理接管/Local Proxy/Takeover"
    with "本地路由/Local Routing/ローカルルーティング" across zh/en/ja to
    eliminate naming confusion with the separate "Global Proxy" feature.
    
    Only i18n string values are changed; keys, code identifiers, and
    database schema remain untouched.
  • refactor: remove per-provider proxy config feature
    The per-provider proxy configuration (meta.proxyConfig) is removed
    because its scope is too narrow and covered by global proxy settings
    and proxy takeover mode. Users can achieve the same result via the
    global proxy panel.
    
    Changes:
    - Remove ProviderProxyConfig type (frontend TS + backend Rust)
    - Remove ProviderAdvancedConfig proxy UI block, keep testConfig/pricingConfig
    - Simplify http_client: delete build_proxy_url_from_config,
      build_client_for_provider, get_for_provider
    - Simplify forwarder/stream_check/model_fetch to use global client
    - Remove i18n keys (en/zh/ja)
    - Fix pre-existing test bug in transform.rs (extra None arg)
  • Add LemonData sponsor and update partner logo formats
    Add new sponsor LemonData to partner section. Update Crazyrouter logo
    from JPG to PNG format.
  • Update partner logos and sync across languages
    Synchronize Crazyrouter logo format and partner section updates across
    EN/JA/ZH README files.
  • fix(clippy): remove redundant closure in session ID parsing
    Replace `|uid| parse_session_from_user_id(uid)` with direct
    function reference to satisfy clippy::redundant_closure.
  • fix(proxy): reduce unnecessary Copilot premium interaction consumption
    - Fix request classification: treat messages containing tool_result as
      agent continuation instead of user-initiated, preventing false premium
      charges on every tool call
    - Add subagent detection via __SUBAGENT_MARKER__ and metadata._agent_
      fallback, setting x-interaction-type=conversation-subagent
    - Add deterministic x-interaction-id derived from session ID to group
      requests into a single billing interaction
    - Add orphan tool_result sanitization to prevent upstream API errors
      that could cause retries and duplicate billing
    - Reorder pipeline: classify (on original body) → sanitize → merge →
      warmup, ensuring classification sees raw tool_result semantics
    - Enable warmup downgrade by default with gpt-5-mini model
    - Enhance session ID extraction priority chain for Copilot cache keys
    - Detect infinite whitespace bug in streaming tool call arguments
  • fix(usage): remove unnecessary private IP restrictions from usage script
    SSRF protection (private IP blocking, suspicious hostname detection) was
    originally added for web-server threat models but is unnecessary for a
    local desktop app where the user already has full network access. This
    removal unblocks legitimate use cases like enterprise intranet APIs,
    Docker container addresses, and self-hosted services.
    
    Retained: HTTPS enforcement and same-origin checks which still provide
    meaningful security (protecting API keys in transit and preventing
    scripts from leaking keys to unrelated domains).
  • fix(usage): sync request log time range with dashboard 1d/7d/30d selector
    The RequestLogTable had a hardcoded 24-hour rolling window, ignoring the
    dashboard's time range selector. Now it accepts a timeRange prop and
    dynamically adjusts the query window, so users can view logs beyond just
    the last day.
  • feat(usage): improve pagination with first/last 3 pages and page jump input
    Show first 3 and last 3 page buttons instead of just first/last, with
    Set-based deduplication for clean edge merging. Add a page number input
    field with Go button for direct page navigation.
  • fix(sessions): strip OpenClaw message_id suffix and allow 2-line titles
    OpenClaw gateway injects `[message_id: UUID]` metadata at the end of
    every message, wasting display space. Strip this suffix from both title
    and summary fields.
    
    Also change session title display from single-line truncate to
    line-clamp-2, so longer titles (e.g. OpenClaw's timestamp-prefixed
    messages) can show more meaningful content across two lines.
  • feat(sessions): extract meaningful titles for Codex and OpenClaw sessions
    Previously Codex and OpenClaw sessions only showed the working directory
    basename as the title, making it hard to distinguish sessions in the same
    project. Now both providers extract the first real user message as the
    session title, matching the existing Claude Code behavior.
    
    - Codex: first user message → dir basename (skips AGENTS.md injection)
    - OpenClaw: displayName (sessions.json) → first user message → dir basename
    - Move TITLE_MAX_CHARS constant to shared utils.rs
    - Use Option<&HashMap> for OpenClaw parse_session to avoid leaky abstraction
  • fix(usage): deduplicate proxy and session log usage records
    Extract message_id from Claude API responses (msg_xxx) and use it to
    generate a shared request_id format (session:{msg_xxx}) between the
    proxy logger and session log sync. When session sync encounters the
    same request_id via INSERT OR IGNORE, it skips the duplicate.
    
    - Add message_id field to TokenUsage, extracted from Claude responses
    - Add TokenUsage::dedup_request_id() to generate shared request IDs
    - Define SESSION_REQUEST_ID_PREFIX constant to eliminate magic strings
    - Change proxy logger to INSERT OR REPLACE for richer-data-wins semantics
  • feat(pricing): add ~50 new model pricing entries and fix outdated prices
    Add pricing data for 4 new providers (Qwen, xAI Grok, Mistral, Cohere)
    and supplement existing providers (MiniMax M2.5/M2.7, GLM-5/5.1,
    Doubao Seed 2.0, MiMo V2 Pro, OpenAI o1/o3/codex-mini/gpt-5-mini/nano).
    
    Fix outdated prices for deepseek-chat, deepseek-reasoner, and kimi-k2.5.
    Fix display_name casing "Mimo" → "MiMo" for consistency.
    
    Use prepared statement in seed_model_pricing() to avoid recompiling SQL
    on each of ~130 INSERT iterations.
    
    Schema migration v8→v9: DELETE + re-seed model_pricing for existing users.
  • feat: block official provider switching during proxy takeover
    Prevent users from switching to official providers (Anthropic/OpenAI/Google)
    when proxy takeover is active, as using a proxy with official APIs may cause
    account bans.
    
    Defense-in-depth across 4 layers:
    - Backend: ProviderService::switch(), hot_switch_provider(), switch_proxy_provider command
    - Frontend: useProviderActions soft guard with error toast
    - UI: ProviderActions button disabled with ShieldAlert icon
    - Tray menu: official provider items disabled with  indicator
    
    Also warns when enabling proxy takeover while current provider is official.
  • [codex] Preserve cache_control when merging system prompts (#1946)
    * Preserve cache hints when collapsing system prompts
    
    Strict OpenAI-compatible chat backends still need fragmented Claude\nsystem prompts collapsed into one leading system message, but that\nnormalization should not silently drop stable cache hints. Preserve\nmessage-level cache_control when the merged system fragments agree,\nand fall back to omitting it when the fragments conflict.\n\nConstraint: Must keep single-system normalization for Nvidia/Qwen-style chat backends\nRejected: Always copy the first cache_control | could misrepresent conflicting cache boundaries\nConfidence: high\nScope-risk: narrow\nReversibility: clean\nDirective: If system prompt merging changes again, preserve cache_control whenever the merged metadata is unambiguous\nTested: cargo test proxy::providers::transform --manifest-path src-tauri/Cargo.toml\nNot-tested: End-to-end prompt caching behavior against cache-aware OpenAI-compatible upstreams\nRelated: #1881
    
    * Tighten cache hint inheritance for merged system prompts
    
    The follow-up cache hint fix still treated mixed present/absent\ncache_control across fragmented system prompts as inheritable, which\nexpanded the cache scope after prompt collapse. Treat that mix as\nambiguous and only preserve cache_control when every merged fragment\nexplicitly agrees on the same value.\n\nConstraint: Must preserve strict-backend system prompt normalization from #1942\nRejected: Inherit first present cache_control | widens cache scope when later fragments were intentionally uncached\nConfidence: high\nScope-risk: narrow\nReversibility: clean\nDirective: Any future merged-system cache hint logic should treat missing cache_control as semantically significant\nTested: cargo test proxy::providers::transform --manifest-path src-tauri/Cargo.toml\nNot-tested: End-to-end upstream caching behavior against cache-aware relays\nRelated: #1881\nRelated: #1946
    
    * Keep cache-control merge regressions easy to review
    
    Reflow the two long cache-control regression assertions in transform.rs so the neighboring merge cases stay rustfmt-aligned and easier to scan.
    
    This keeps the preserved code change separate from the untracked Markdown design notes the user did not want committed.
    
    Constraint: Exclude Markdown design files from the commit while preserving the local code change
    Rejected: Include docs in the same commit | user explicitly asked to leave Markdown files out
    Confidence: high
    Scope-risk: narrow
    Reversibility: clean
    Directive: Treat this as a readability-only test change; do not infer runtime behavior changes from it
    Tested: cargo test --manifest-path src-tauri/Cargo.toml test_anthropic_to_openai_drops_ --lib
    Tested: cargo check --manifest-path src-tauri/Cargo.toml --tests
    Tested: pnpm format:check
    Tested: pnpm typecheck
    Not-tested: Full application integration and manual flows
  • Stop sending prompt cache keys on Claude chat conversions (#2003)
    Responses conversions still use promptCacheKey, but chat completions now stay a pure shape transform. This keeps Claude -> chat requests aligned with providers that do not understand the field and keeps stream checks consistent with production behavior.
    
    Constraint: Issue #1919 requires removing prompt_cache_key from Claude -> OpenAI Chat requests
    Rejected: Add a runtime toggle for chat injection | requested behavior is unconditional removal
    Confidence: high
    Scope-risk: narrow
    Reversibility: clean
    Directive: Keep promptCacheKey limited to Claude -> Responses conversions unless a provider-specific contract is proven
    Tested: cargo test anthropic_to_openai
    Tested: cargo test anthropic_to_responses_with_cache_key
    Tested: cargo test transform_claude_request_for_api_format_responses
    Not-tested: Full src-tauri test suite
    Related: #1919
  • 添加应用级别窗口按钮,以改善linux wayland下系统窗口按钮失效的问题 (#1119)
    * feat(window): add app-level window controls with settings toggle
    
    Add a persistent settings toggle to enable app-level minimize/maximize/close controls and hide system decorations when enabled, providing a Wayland-friendly fallback for broken native titlebar interactions.
    
    Co-authored-by: Cursor <cursoragent@cursor.com>
    
    * fix(window): restrict app-level window controls to Linux only and fix startup flicker
    
    - Guard useAppWindowControls with isLinux() in App.tsx so it's always
      false on macOS/Windows even if persisted as true
    - Wrap set_decorations call in lib.rs with #[cfg(target_os = "linux")]
    - Only show the toggle in WindowSettings on Linux
    - Skip setDecorations effect while settingsData is still loading to
      prevent the Rust-side decoration state from being overridden by the
      undefined->false fallback, which caused a brief title bar flicker
    
    ---------
    
    Co-authored-by: wzk <wx13571681304@outlook.com>
    Co-authored-by: Cursor <cursoragent@cursor.com>
    Co-authored-by: Jason <farion1231@gmail.com>
  • docs(release-notes): sync user edits across en/zh/ja and add PR credits
    - Simplify "ChatGPT Plus / Pro" → "ChatGPT" across all three languages
    - Clarify Codex OAuth description to highlight Claude Code usage
    - Add "requires manual activation" note for Token Plan and third-party balances
    - Add Copilot API consumption caveat to the interaction optimizer section
    - Update overview with skills.sh search, usage tracking, and onboarding mentions
    - Add PR credits for TheRouter (@cmzz), Kaku/OMO Slim/Thinking fallback/auth tab (@yovinchen)
  • chore(release): bump version to v3.13.0 and sync changelog/release notes
    Backfill post-draft changes into CHANGELOG and three-language release
    notes (en/zh/ja): 16 new Added entries, 6 Fixed entries, 1 Docs entry,
    and updated header stats (139 commits, 280 files, +31627/-3042).
  • feat: update PIPELLM preset with full config, add Codex support and icon
    Update base URL to cc-api.pipellm.ai, add complete model definitions
    (Opus/Sonnet/Haiku), add Codex preset with custom TOML config, add
    pipellm PNG icon, and set apiKeyUrl to referral link.
  • feat: add E-FlowCode provider preset across all five apps
    Multi-protocol provider: Anthropic for Claude, OpenAI Responses for
    Codex/OpenClaw, OpenAI for OpenCode, custom Gemini config. Includes
    PNG icon via URL import and provider-specific settings like effortLevel,
    enabledPlugins, and custom TOML config for Codex.
  • feat: add Shengsuanyun provider preset with partner promotion
    Add Shengsuanyun (胜算云) as an aggregator partner across all five apps,
    positioned right after official providers. Uses anthropic-messages
    protocol for OpenCode/OpenClaw. Includes URL-based icon import (217KB
    SVG), partner promotion i18n for zh/en/ja, and localized display name.
  • chore: remove generate-icon-index.js to prevent accidental regeneration
    The icon index (index.ts) is hand-curated with optimized SVG content
    and custom name mappings. Running the generation script destroys these
    optimizations. Remove the script entirely to eliminate the risk.