From 84efe1fb5bfd14c535884b1b12d75b83ea9c05f4 Mon Sep 17 00:00:00 2001 From: Jason Date: Tue, 22 Sep 2026 22:17:05 +0800 Subject: [PATCH] chore(release): v3.20.4 --- CHANGELOG.md | 83 +++++++++++++++++++++++++++++++++++++++ package.json | 2 +- src-tauri/Cargo.lock | 2 +- src-tauri/Cargo.toml | 2 +- src-tauri/tauri.conf.json | 2 +- 5 files changed, 87 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e22a2e2c..9c70ab4b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,89 @@ All notable changes to CC Switch will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [3.20.4] - 2026-09-22 + +Development since v3.20.3 adds a tenth managed app and a wave of contributed correctness fixes, most of them for Codex through the local proxy. MiniMax Code joins as an additive-mode app (#7383): providers are written into its native `~/.minimax/config.yaml` alongside MiniMax Code's own settings, with bidirectional MCP sync, Skills, an `AGENTS.md` prompt library, a read-only session browser with resume, and session usage in the dashboard, while model selection, login and session deletion stay with MiniMax Code. Claude Desktop 3P configuration now works on Linux, Flatpak builds included (#7331). On the proxy, Codex 0.154+ requests carrying an `additional_tools` item no longer 400 on strict Chat gateways, where the converter had turned it into a `content: null` system message (#7454), a single `detail: "original"` screenshot no longer breaks a Codex session for good (#7476), tools without a description are no longer sent as `null` (#7319, #7378), Claude Code's auto mode classifier works through GitHub Copilot (#5404), models mapped to Grok 4.6 get their reasoning effort again and `/effort xhigh` is no longer dropped (#7318), and `/effort max` stays `max` on GPT-5.6 and GPT-6 Astra instead of being lowered to `xhigh` (#7531). Two Codex account bugs are closed: editing or switching away from a Codex provider that authenticates through its own provider table no longer silently erases its stored API key (#7434), and cards still bound to a ChatGPT account removed from the Auth Center can be rebound or switched away instead of failing every switch, takeover toggle and startup restore (#7395). The request log shows output tokens per second (#3369), the OpenCode provider form can search and bulk-add fetched models (#7515), skills.sh skills whose id differs from their directory and repositories with more than 10,000 files now install (#6381, #7489), the enabled prompt picks up external edits to `CLAUDE.md`/`AGENTS.md` (#7194), OMO with a WSL-hosted OpenCode directory edits the unified config it actually reads (#7550), the About card's tool probes stop hanging on npm metadata and misreading the WSL MOTD as a version (#7346, #7348), and the Windows main window no longer drops out of the taskbar after a silent start (#6348). On the preset side, the domestic Codex presets are refreshed from a Responses API audit (Kimi For Coding on K2.8 Preview with a 1M window, DeepSeek defaulting to `deepseek-flash`), MiniMax CN and BaiLing move to their current official endpoints, AICodeWith's Codex-family endpoint is corrected to `/v1`, Claude Desktop offers the 1M variant on DeepSeek V4 routes, and a handful of presets are added (Kimi Global, FluxA Token Plan, Soshow and four Codex Responses presets). DeepSeek V4 Pro pricing returns to its peak list price after DeepSeek withdrew the V4.1 Flash cutover that v3.20.3 had priced in early, and five previously unpriced models get pricing rows. This release changes the database schema: `SCHEMA_VERSION` 18 → 19 adds an `enabled_mcode` flag to `mcp_servers` and `skills`. + +**Stats**: 46 commits | 156 files changed | +8,673 insertions | -684 deletions + +### Added + +- **MiniMax Code Joins CC Switch as the Tenth Managed App** (#7383, #7578): MiniMax Code, whose `mcode` TUI and desktop app share one data directory (`~/.minimax`, overridable with `MINIMAX_DATA_DIR` or the legacy `MAVIS_DATA_DIR`), is now a managed app (`app_type` `mcode`). + - **Providers**: MiniMax Code runs in additive mode like OpenCode, Hermes and Pi: each provider is an entry under `custom_provider` in `~/.minimax/config.yaml` (`kind: custom`, `api`, `options.baseURL`/`apiKey`/`headers`, `models`), several providers coexist, and a card counts as enabled exactly when its key is present in that file. The provider form is the OpenCode-style structured editor plus a raw JSON editor and offers three API formats (`anthropic-messages`, `openai-completions`, `openai-responses`); its preset list is derived from the Pi catalog — every Pi preset that uses one of those formats and needs no Pi-specific `compat` options, 41 at release (33 Chat Completions, 5 Responses, 3 Anthropic Messages). Custom providers created inside MiniMax Code are imported whenever the list loads and edits made there flow back into CC Switch; account providers (any `kind` other than `custom`) are never imported or overwritten. Writes keep every other key in `config.yaml` (`defaultModel`, `minimax_api`, unknown fields) and respect MiniMax Code's own `config.yaml.lock` directory lock — a lock older than 10 seconds is reclaimed, otherwise the save fails with a busy/retry conflict — and the database change is rolled back if the native write fails. Model selection stays with MiniMax Code: CC Switch never writes `defaultModel`/`defaultLightModel`, and refuses to remove or disable a provider or model that either field points to (`custom_provider:/`) until another default is picked in MiniMax Code. Removing a provider from `config.yaml` updates its card immediately: it stays in the list, marked as not added, and can be added again (#7578). + - **MCP**: MCP servers sync both ways with `~/.minimax/mcp.json` (`mcpServers`): managed entries are written with `enabled: true`; an edit replaces only the transport fields (`command`, `args`, `env`, `url`, `headers`, `type`), so native options such as `timeout` survive; imports map `streamable-http` and URL-only entries to `http` and command-only entries to `stdio`, and a native `enabled: false` entry imports with the MiniMax Code flag off; a same-id server with a different transport is skipped and reported instead of overwritten; and a full resync never removes entries whose flag is off, because that flag also covers servers CC Switch does not manage — only an explicit disable or delete removes one. The native file is restored when the database commit fails. + - **Skills and prompts**: Skills deploy to `~/.minimax/skills/` with the ownership checks Pi uses: sync, storage migration and uninstall leave alone any same-named Skill CC Switch did not deploy, an imported external symlink is re-pointed at the managed copy only when the contents match, and updates are staged and rolled back if the metadata commit fails. The prompt library writes the active prompt to `~/.minimax/AGENTS.md`: at startup it imports that file only while the prompt library is still empty for every app (in practice a fresh install); otherwise, before switching, it copies live edits back into the active prompt or, when no MiniMax Code prompt is active yet, saves the file's unmatched content as a backup library entry. It empties the file when the last active prompt is disabled and enforces a 32 KiB (UTF-8 bytes) limit on save, enable, sync and import. As with MCP, `AGENTS.md` is restored when the database commit fails. + - **Deep links and scope**: MCP deep links accept `mcode` in `apps`; provider deep links for MiniMax Code are rejected. Deliberately out of scope: native login and cloud features, proxy takeover and failover, tray entries, common config snippets, universal providers, profiles (the profile switcher is hidden on the MiniMax Code page), the per-card provider check, a config-directory override in Settings, and install/upgrade from Settings → About. The UI is localized in zh/en/ja/zh-TW and the README tool lists include MiniMax Code. The new `enabled_mcode` columns on `mcp_servers` and `skills` are this release's v18 → v19 schema migration. +- **MiniMax Code Sessions and Usage in the Dashboard** (#7383): The session browser lists MiniMax Code conversations from `~/.minimax/v2/sqlite/runtime-state.sqlite`, which it opens read-only, excluding hidden, archived, child and peek/channel/cron sessions and reading messages from both migrated rows and display JSON MiniMax Code has not migrated yet; each session offers the resume command `env MINIMAX_DATA_DIR= mcode --session ` (a PowerShell form on Windows), and deletion is disabled in the UI and refused by the backend because sessions are tied to MiniMax Code's runtime state. Usage from the same database's `local_runtime_token_usage` table is imported incrementally under a "MiniMax Code (Session)" source with its own app filter: reasoning tokens count as output; the model id is the part after the first `/` (`custom_provider:router/vendor/model` is recorded as `vendor/model`); at import MiniMax Code's own `cost_usd` is used when it is a finite non-negative number, and a missing, negative or non-finite cost is priced from CC Switch's pricing table instead; a native cost of exactly $0 is stored as $0, but like every zero-cost row with tokens it is re-priced by the shared cost backfill (at startup, before log pruning, and when the request list or a request detail is loaded) as soon as the model has a price in the table; a row-id watermark committed together with the imported rows keeps pruned logs from being imported again; and totals that include MiniMax Code carry the partial cache-write caveat, as with Pi. +- **Claude Desktop 3P Configuration on Linux** (#7331; fixes #4855): CC Switch previously refused to write Claude Desktop 3P configuration on Linux ("Phase 1 only supports macOS and Windows"), so the Claude Desktop panel reported the platform as unsupported. Linux is now supported with the same file layout as macOS and Windows: the config root is `$XDG_CONFIG_HOME` when it is set to an absolute path, otherwise `~/.config` (a relative value is ignored), and under it CC Switch maintains `Claude/claude_desktop_config.json`, `Claude-3p/claude_desktop_config.json`, `Claude-3p/configLibrary/_meta.json` and the `Claude-3p/configLibrary/00000000-0000-4000-8000-000000157210.json` profile. When CC Switch itself runs as a Flatpak (detected through `/.flatpak-info`) it always uses the host `~/.config` rather than the sandbox-private `XDG_CONFIG_HOME`; there is deliberately no config-directory override and no attempt to recover a custom host `XDG_CONFIG_HOME`, because Flatpak replaces that variable inside the sandbox — users whose Claude Desktop lives under a custom XDG directory should run the native package. The bundled Flatpak manifest already grants home-directory access; stricter custom Flatpak builds must grant read/write access to `xdg-config/Claude` and `xdg-config/Claude-3p`. macOS and Windows paths are unchanged, and the unsupported-platform message on any other OS now lists macOS, Windows and Linux. The user manual (en/zh/ja) documents the Linux paths, the Flatpak boundary and the updated support-scope table. +- **Output Tokens per Second in the Request Log** (#3369; fixes #5936): The request log table now shows the generation rate next to the output token count (`1,234/56 tps`), and the request detail panel shows it in parentheses after the output tokens. The rate is output tokens divided by the generation window: for streaming requests that recorded a first-token time the window is `latency − first-token time`, so it measures generation after the first token; non-streaming requests fall back to the full latency, so their rate includes time to first token. An explicit `durationMs` would be preferred when positive, but the proxy does not currently record one. Rates of 1 or more are rounded to whole numbers, smaller ones keep one decimal. Rows with no positive output token count or no positive window show nothing — including rows imported from Claude Code, Codex and Gemini session logs, which carry no latency. The value is computed at display time from stored fields, so no schema or logging change is involved. +- **Search and Bulk-Add Fetched Models in the OpenCode Provider Form** (#7515; fixes #7423): After "Fetch models", the OpenCode provider form used to report only a count and offer a dropdown beside each existing model row, so with no models configured users had to add an empty row before they could see any candidates, and adding several models meant one row at a time. A searchable multi-select list now appears above the model list: filter by model ID or owner, tick models (selections survive changing the search), and "Add selected (N)" adds them in one step with the raw model ID as both the config key and the initial display name. Models already configured show as checked, disabled and marked "Already added", and existing entries keep their names, limits and options. Fetched results are de-duplicated by ID; changing the base URL or API key clears the candidate list and discards late responses from the previous endpoint; pressing Enter in the search box does not submit the provider form. The per-row dropdown is kept, and the MiniMax Code provider form, which reuses the OpenCode fields, gets the same picker. Labels are added in all four locales. +- **New Provider Presets: Kimi Global, Kimi For Coding Global, FluxA Token Plan, Soshow, StepFun API, Baidu Qianfan (Pay-As-You-Go), Astron Coding Plan**: Kimi Global and Kimi For Coding Global (#7526), the international-site (kimi.ai) twins of the domestic Kimi presets (identical apart from hosts and links, except that the Codex Kimi For Coding Global catalog's `kimi-for-coding` row defaults to `max` reasoning where the domestic row defaults to `high`, keeps the plain "Kimi For Coding" display name and declares no input modalities), FluxA Token Plan (a sponsor, with a row in the four README sponsor tables) and the Soshow aggregator are added across Claude Code, Claude Desktop, Codex, Hermes, OpenClaw, OpenCode and Pi. Codex alone gains native Responses presets for StepFun API (CN and international), Baidu Qianfan pay-as-you-go and iFlytek Astron Coding Plan; the StepFun Step Plan and Qianfan Coding Plan/Token Plan presets stay on Chat. +- **Pricing Rows for Grok 4.7, GLM-5.3-FlashX, Hunyuan Hy4 Preview and Two Open-Weight Qwen3.8 Models**: None of these ids had a seed row, and the `LIKE '{id}-%'` prefix fallback only matches longer ids, so their requests were recorded at $0. The new rows, per million tokens as input/output/cache read/cache write: `grok-4.7` at $2/$6/$0.50/$0, recorded at the base (<200K prompt) tier like `grok-4.5` and `grok-4.6` (xAI doubles unit prices, cached input included, for prompts of 200K and above, and the table has no tier column); `glm-5.3-flashx` at $0.37/$1.25/$0.075/$0; `hy4-preview` (Hunyuan Hy4 Preview) at $0.84/$2.52/$0.042/$0, converted from Tencent's Guangzhou-region list price of CNY 6/18/0.3 at 7.14 with no tiering; `qwen3.8-2.4t-a95b` at $2/$6/$0.25/$2.50; and `qwen3.8-27b` at $0.50/$3/$0.10/$0.625. Both Qwen rows use Alibaba Cloud's international (Singapore) list prices, with the two cache columns holding the implicit cache-hit price and the explicit cache-creation price, the same convention as `qwen3.8-max`. +- **GitHub Star Prompt on the About Card**: Downloads and updates now go through ccswitch.io, so few users pass through the GitHub repository any more. The About card shows a one-line prompt in the primary color between the app title and the action buttons, asking users to star the project and opening the repository when clicked. The GitHub button moves to the front of the button row, ahead of Official Website; it is tinted with the primary color (still visually below the solid "check for updates" action) and carries a slowly spinning star that stops under `prefers-reduced-motion`. The Official Website button still opens `https://ccswitch.io`. `settings.starPrompt` is added in all four locales. + +### Changed + +- **Codex CN Presets Refreshed from the Responses API Audit**: Several existing domestic Codex presets are brought in line with the vendors' current Responses documentation. Kimi For Coding: `kimi-for-coding` now serves K2.8 Preview, so its catalog row is renamed "Kimi For Coding (K2.8 Preview)", its context window goes from 262144 to 1048576, it declares text+image input, and its reasoning levels widen from `high` only to `low`/`high`/`max` (default `high`); `kimi-for-coding-highspeed` stays at 262144 with `high` only, and the `k3`/`k3-256k` rows are unchanged. DeepSeek: the preset now defaults to `deepseek-flash` (V4.1 Flash, shown as "DeepSeek V4.1 Flash", text+image) instead of the legacy `deepseek-v4-flash` alias, and its `deepseek-v4-pro` row is declared text-only, so both rows match the bundled official catalog (`codex_deepseek_catalog_template.json`); cards still on `deepseek-v4-flash` keep working because the vendor routes that alias to `deepseek-flash`, and `deepseek-v4-pro` stays out of the global text-only registry, so other gateways hosting it still fail open. Tencent Hunyuan gains `hy4-preview` (960000 max input, text-only, `none`/`high`, default `high`) while `hy3` stays the default model. The 千问AI平台 and QwenCloud pay-as-you-go Codex presets add the open-weight `qwen3.8-2.4t-a95b` (text-only) and `qwen3.8-27b` (text+image), both with a 983616 window, parallel tool calls off and `low`/`medium`/`xhigh` effort (default `xhigh`); the two Token Plan presets deliberately do not get them. No existing preset changes protocol: the four Tencent Token Plan Enterprise presets (Pro and Lite, domestic and Intl) stay on `openai_chat` even though `/plan/v3/responses` is listed, because TokenHub marks Kimi K2.7 Code as unsupported on Responses and serves DeepSeek V4 and GLM-5.2 there only through server-side Chat conversion. Because Baidu Qianfan and StepFun now have native Responses presets for their pay-as-you-go APIs, the zh/en/ja user manual names Baidu Qianfan Coding Plan and StepFun Step Plan as the Chat-only examples in the Codex local-routing section, the Stream Check page and the v3.16.0 highlights. +- **MiniMax CN and BaiLing Presets Move to Their Current Official Endpoints**: MiniMax's domestic presets move from `api.minimaxi.com` to `api.minimax.cn` in all seven apps that carry them — `https://api.minimax.cn/anthropic` for Claude Code and Claude Desktop, `https://api.minimax.cn/v1` for Codex, Hermes, OpenClaw, OpenCode and Pi — and website and key links move to `platform.minimax.cn`. Coding Plan usage detection recognizes both the new and the legacy host; the frontend and the backend now match on host labels, including the global `api.minimax.io`, so look-alike hosts such as `api.minimax.cn.example.com` and URLs that carry the domain only in their path no longer trigger it. The quota query stays on `api.minimaxi.com`, the only CN host documented for that endpoint. On Codex, `minimax.cn` joins `minimaxi.com` in both `CODEX_NATIVE_RESPONSES_HOSTS` and the `web_search` reject list, so a card on the new host is treated like one on the legacy host: it gets the native Responses catalog even with a stale stored format, and `web_search = "disabled"`. BaiLing moves from `api.tbox.cn` to `api.ant-ling.com` in the same seven apps: `/anthropic` for Claude Code, Claude Desktop and Hermes, and `/v1` for Codex (replacing `/api/llm/v1`), OpenClaw, OpenCode and Pi. The six apps still pinned to `Ling-2.5-1T`, which the vendor's docs no longer list, now default to `Ling-2.6-1T` as Codex already did; OpenClaw's row gets a 262144 context and a 0.63/2.52 cost, and Pi's catalog entry a 262144 context and 32768 max tokens. `Ling-2.6-1T` is added to the confirmed text-only registry, since Ant Ling ships vision as separate `-VL` models, so generated Codex catalogs declare it text-only and the proxy's media fallback replaces its images with a text marker before sending. +- **SudoCode.chat Presets Offer `api.sudorelay.com` as an Alternate Endpoint**: The SudoCode.chat presets for Claude Code, Claude Desktop, Codex and Grok Build now list `https://api.sudorelay.com` (`https://api.sudorelay.com/v1` for Codex and Grok Build) as a second `endpointCandidates` entry next to `api.sudocode.chat`, so it shows up in the endpoint speed test and is recorded as one of the card's endpoints when a SudoCode.chat card is created. The default base URL stays on `api.sudocode.chat`. The Hermes, OpenClaw, OpenCode and Pi SudoCode.chat presets have no endpoint candidate list and are unchanged, as are the SudoCode.us presets. +- **Codex Disables `web_search` on StepFun, Baidu Qianfan and iFlytek Astron Responses Gateways**: Codex sends its hosted `web_search` tool by default, but StepFun's Responses API accepts only `function` tools, and the Responses docs for Qianfan pay-as-you-go and Astron document no hosted tools. `stepfun.com`, `stepfun.ai`, `qianfan.baidubce.com` and `xf-yun.com` are added to `CODEX_WEB_SEARCH_REJECT_HOSTS` (matched on host labels), so a native-Responses card on these hosts gets `web_search = "disabled"` written to `~/.codex/config.toml`; Qianfan and Astron are listed as a precaution, not as confirmed rejects. These hosts are deliberately not added to `CODEX_NATIVE_RESPONSES_HOSTS`, because Chat offerings share them — StepFun Step Plan on `/step_plan/v1`, Qianfan Coding Plan and Token Plan on `/v2/coding` and `/v2/tokenplan/personal`, Astron Chat on `/v2` — so a Chat card on the same host is never reclassified as native and its `web_search` setting is left alone. The new presets connect natively only because they set `apiFormat: "openai_responses"` explicitly. +- **APIKEY.FUN Presets Move to `apikey.fan`**: In all nine apps, the APIKEY.FUN presets switch website, registration link and base URL from `apikey.fun` to `apikey.fan` (`https://api.apikey.fan`, with `/v1` for Codex, Grok Build and OpenCode). Claude Code, Claude Desktop, Codex, Gemini and Grok Build keep the old `api.apikey.fun` and `slb.apikey.fun` as alternate endpoint candidates. The sponsor links in the four READMEs are updated too. +- **README Kimi Sponsor Section Leads With the Kimi Code Plan** (#7522): The Kimi sponsor block in all four READMEs (en/zh/ja/de) now puts the Kimi Code plan first and the Kimi Open Platform API second, each with a domestic (kimi.com) and a Global (kimi.ai) link. The `aff` and `track_id` parameters are unchanged. +- **Expired OpenCode Go Referral Offer Removed**: The `opencode_go` partner promotion text shown when the OpenCode Go preset is selected drops the expired first-month offer; in zh/en/ja/zh-TW it now just describes the subscription and points to the plan details. The preset, its link and its partner flag are unchanged. + +### Fixed + +- **Codex Requests With an `additional_tools` Carrier No Longer 400 on Chat Gateways** (#7454; fixes #7451, #6158; supersedes #6159): Codex 0.154 and later ship a request's extra tools (the `functions`/`collaboration` exec namespaces, plugins) in an `additional_tools` input item — `{"type":"additional_tools","role":"developer","tools":[…]}`, which has a role but no `content`. The Codex Responses-to-Chat converter treated every item with a role as a message and emitted `{"role":"system","content":null}` at `messages[1]`, which strict Chat gateways reject with `messages[1]: missing field "content"` (or `data did not match any variant of …SystemMessageContent`), and the tools inside the carrier were silently dropped on both the Chat and the Anthropic conversions. The carrier is now skipped in the message walk and its tools are collected through the same registry that already handles `tool_search_output` groups: they are deduplicated against top-level `tools` (the top-level declaration wins), namespaces are flattened (`functions` → `exec_command` becomes `functions__exec_command`), and names round-trip back to Codex on the response side. Requests without a carrier convert byte-for-byte as before. Deliberate boundary: `custom`-typed children inside a namespace tool (e.g. the grammar-format `functions.exec`) are still not lifted, since they need their own response-side name round-trip. +- **One `detail: "original"` Image No Longer Breaks Codex Sessions on Chat Gateways** (#7476; fixes #6495, #7458; supersedes #6515): When a model's Codex catalog entry advertises `supports_image_detail_original`, Codex marks tool-returned images (e.g. `view_image` screenshots) with `detail: "original"`, a Responses-only value. When that media was moved out of `function_call_output` for a Chat upstream, the value was copied verbatim into `image_url.detail`, and strict OpenAI-compatible Chat gateways (Ark/Doubao, SenseNova, GLM, vLLM, …) reject the whole request with 400 `invalid_request_error` on `messages.N.content`. Because Codex replays the full rollout on every turn, a single such image made the session fail permanently, plain-text turns included. The fix has two layers: the proxy downgrades `original` to `auto` in the Chat image part (`auto`/`low`/`high` pass through unchanged), so already-poisoned sessions and Codex versions before 0.155.0, which do not normalize replayed history themselves, recover immediately; and generated Codex catalogs for the Chat (proxy-conversion) profile now always write `supports_image_detail_original: false`, so Codex stops emitting `original` at the source. Native Responses and Anthropic catalog profiles and the bundled DeepSeek official catalog are unchanged. Top-level user images were never affected, because the Chat conversion drops their part-level `detail`. +- **Editing or Switching Away From a Codex Provider No Longer Erases Its Stored API Key** (#7434; fixes #7433; supersedes #7492): Some Codex providers declare their own credential source in the `[model_providers.*]` table (`env_key`, an `auth`/`aws` table, or an explicit `Authorization` header in `http_headers`). For these no bearer token is injected into `config.toml`, and default mode deletes the shared `~/.codex/auth.json` on every third-party switch, so the live snapshot is permanently `{ auth: {}, config }` and `auth.OPENAI_API_KEY` in the database is the only copy of the key. Two paths treated that empty live auth as authoritative — the edit dialog for the current provider, which seeds the form from the live snapshot, and the switch-away backfill, which writes the live snapshot back into the database row — so opening the editor and saving without any change, or just switching away, silently wiped the key, after which the usage-script dialog read the keyless card as an official subscription and reset its saved script. Both layers now treat a live auth with no credential (a missing `auth.json`, or only the `auth_mode` logout marker) as an absent field: they keep the stored auth and still capture the live `config.toml`. Live auth still wins whenever it carries material, which covers manual edits to `~/.codex/auth.json`, and official providers keep the live login as their source of truth. Whether a provider counts as official is now decided by official identity (`is_codex_official_provider` on the backend, `resolveCodexOfficialIdentity` in the dialog) rather than `category` alone, so a legacy fixed official card with no stored category still honors a logout instead of bringing back the old login on switch-back. +- **Codex Cards Bound to a Deleted ChatGPT Account Can Be Rebound or Switched Away** (#7395; fixes #7392): Removing a managed ChatGPT account in the Auth Center and logging in again creates a new local account id, even for the same ChatGPT user and workspace. A Codex card still bound to the old id then failed every route switch, takeover toggle, startup restore and rebind with `账号不存在: `, because the switch-away step reconciles the outgoing account's live `auth.json` before writing and hard-failed when that account no longer existed — the user could neither move to another provider nor pick the new account on the same card. Switch-away preparation now tells a deleted outgoing account apart from an existing account that simply has no matching live token: a deleted account only releases its cc-switch ownership marker and leaves `~/.codex/auth.json` untouched, so a later native Codex CLI login of the same user is not deleted as if cc-switch managed it, and the target provider's normal write policy still applies. An account counts as deleted only after the persisted store `codex_oauth_auth.json` is verified; the switch is aborted with the live files unchanged, instead of guessing, when the store is unreadable or malformed (broken JSON, no `accounts` object, a version other than 1/2, keys that don't match `account_id`), when the store file is missing while accounts are still loaded, or when the account is still present on disk. When a card's own binding is stale, the error now tells the user to click Choose account on the provider card and rebind, instead of a bare not-found error — including in the already-taken-over state, which used to return success without checking the binding. Startup restore goes through the same check. Recovery uses the existing Choose account action on the provider card and in the form, with no UI or locale changes; account deletion itself is unchanged, and nothing is rebound automatically. +- **DeepSeek V4 Pro Pricing Restored to the Peak List Price**: v3.20.3 repriced `deepseek-v4-pro` to the V4.1 Flash tier ($0.30/$1.20 per million, $0.006 cache read) because DeepSeek had announced that V4 Pro would be routed to V4.1 Flash from 2026-09-14. DeepSeek withdrew that announcement on 2026-09-12 and keeps serving V4 Pro with unchanged billing, so V4 Pro requests were being costed well below what the upstream charges. The seed row returns to $1.32 input / $3.96 output / $0.044 cache read per million (the peak tier, CNY 9/27/0.3). The v3.20.3 repair entry is replaced by its inverse, which only rewrites a `deepseek-v4-pro` row whose four price columns are still exactly 0.3/1.2/0.006/0, the values v3.20.3 seeded or repaired to; the old entry is removed rather than kept, because with both in place every startup would flip the row between the two tiers, and a regression test runs startup seeding twice and asserts the row settles at 1.32/3.96/0.044. Databases that never ran v3.20.3 still chain through the older built-in values (1.68/3.36/0.14 → 0.435/0.87/0.003625 → 1.32/3.96/0.044) and now stop at the peak tier; rows edited to any other value are left alone. `deepseek-v4-flash`, `deepseek-v4-flash-0731`, `deepseek-flash` and `deepseek-v4-flash-vision-exp` stay on the V4.1 Flash tier (0.3/1.2/0.006), and `deepseek-chat` and `deepseek-reasoner` are unchanged. +- **Tools Without a Description Are No Longer Sent as `null`** (#7319, #7378; fixes #7303): The Claude-to-OpenAI Chat and Claude-to-Responses converters copied each tool's `description` into the outbound request, so a tool without one went out as `"description": null`, and strict OpenAI-compatible upstreams rejected the entire turn with 400 `expected string, received null`. This hit Anthropic hosted tools such as `web_search`, which carry no description by definition, and any custom or MCP tool declared without one — failing the whole request rather than just the one tool. A missing or explicit-`null` description is now omitted and every other value passes through unchanged. Fields are still inserted in `name` → `description` → `parameters` order, so tools that do carry a description serialize byte-for-byte as before and prompt-cache prefixes are unaffected. The Codex Responses-to-Chat converter had the same `unwrap_or(null)` fallback for function tools and gets the same guard (#7378). +- **Claude Code Auto Mode Classifier Works Through GitHub Copilot** (#5404; fixes #5175): Claude Code's auto mode classifier sends `stop_sequences` on every safety check. On the GitHub Copilot path, the Anthropic-to-OpenAI Chat conversion mapped them to `stop`, and Copilot's Chat Completions route for Claude models answers such requests with HTTP 400, so every classifier call failed while normal chat kept working. The converted `stop` field is now removed only when the provider is GitHub Copilot; other OpenAI-compatible upstreams (e.g. OpenRouter) keep it. Copilot detection in this transform also covers cards identified only by an `env.ANTHROPIC_BASE_URL` that points at `githubcopilot.com`, in addition to the Copilot provider type and a top-level `baseUrl`. +- **Claude Code Effort Levels Reach OpenAI and Grok Upstreams Intact: `xhigh`, `max` and Grok 4.6** (#7318, #7531; fixes #7314, #6197, #3389, #5367): Three gaps in how Claude Code's reasoning effort is carried into converted OpenAI-format requests (`reasoning_effort` on Chat, `reasoning.effort` on Responses) are closed. Claude Code's `/effort xhigh` sends `output_config.effort: "xhigh"`, which the resolver did not recognise; it fell into the unknown-value branch, and because an explicit `output_config.effort` suppresses the `thinking` fallback, no effort was sent at all — `xhigh` now maps 1:1. The reasoning-capable model list only knew `grok-4.5`/`grok-4.5-*` and `grok-build-*`, so Claude models mapped to `grok-4.6` or `grok-4.6-build` lost every effort level, `max` included; `grok-4.6` and `grok-4.6-*` are added (plain `grok-4` stays excluded), and the same list also gates Codex's `reasoning.effort` passthrough on Chat upstreams that have no per-model reasoning settings. And an explicit `max` was always downgraded to `xhigh`; it is now forwarded as `max` for the models that support a distinct max tier — `gpt-5.6`, `gpt-5.6-sol`, `gpt-5.6-terra`, `gpt-5.6-luna` and `gpt-6-astra` (exact ids, case-insensitive). Every other model still falls back to `xhigh`, `thinking.type: "adaptive"` still resolves to `xhigh`, and `low`/`medium`/`high` requests are unchanged. +- **Enabled Prompt Picks Up External Edits to the Live Prompt File** (#7194): Editing the global `CLAUDE.md`, `AGENTS.md` or another app's live prompt file outside CC Switch left the Prompts list showing the old database content until a restart. Loading the Prompts list now reads the live file back into the currently enabled prompt and saves it, with a new `updated_at`, when the content differs, and the open Prompts panel also reloads when the window regains focus (still waiting while an edit, confirmation or write is in progress). Only the enabled prompt is refreshed; inactive templates are left alone. Missing, unreadable, empty or whitespace-only files leave the saved prompt as it is (matching `enable_prompt`), unchanged content causes no write, and the refresh is skipped while a sync or restore holds the sync lock, so loading the list cannot write a stale live file into a freshly restored database. Pi keeps its own `AGENTS.md`-based activation and is not part of this refresh. +- **skills.sh Skills Whose ID Differs From Their Directory Install and Update** (#6381; fixes #6365): skills.sh passes a skill's `skillId` as its directory, but the id is not always the directory name in the repository — `tencent/WeChatReading`, for example, publishes `weread-skills` at `skills/SKILL.md` with `name: weread-skills` — so installing it failed, and even a fixed install would have been missed by update checks, which matched only on the directory name. Source lookup still tries the explicit path first, then a directory named after the id; before falling back to a `SKILL.md` at the repository root, it now accepts a `SKILL.md` whose metadata `name` matches the id, as long as exactly one does, and refuses the install when several directories declare the same name instead of picking whichever the file walk found first. Update checks and manual updates first look for the skill at the source path saved in its `readme_url`, then by directory name, then by the same unique-name rule, and an update saves the path where the skill was actually found, so a skill that moved inside its repository or was renamed in its metadata keeps updating. The local install directory is still the original `skillId`, and storage, sync and the database are unchanged. +- **Large Skill Repositories Install Instead of Being Rejected as Zip Bombs** (#7489; fixes #7475): Skill installs download the whole GitHub repository archive, not just the skill directory, so installing `hugohe3/ppt-master` from skills.sh (a 13,248-entry archive) failed with `ARCHIVE_TOO_MANY_ENTRIES` against the 10,000-entry cap. `MAX_ARCHIVE_ENTRIES` rises to 30,000 for both remote archives and local ZIP installs, and WebDAV's `MAX_EXTRACT_ENTRIES` rises to 30,000 as well, so a synced `skills.zip` containing such a skill can still be restored. The entry count is only a quick check before extraction; the real bound stays the 512 MiB extraction budget, which now charges every file at least one 4 KiB block, the same way directories are charged, so an archive of empty files is still limited by the byte budget. +- **OMO Finds Its Unified Config on the WSL Side** (#7550; fixes #7363; supersedes #7367): When the OpenCode config directory points into WSL (`\\wsl.localhost\\home\\.config\opencode` or `\\wsl$\…`), OMO 4.19.3 and later keep their unified config in the WSL-side home (`~/.omo/omo.jsonc` or `omo.json`). CC Switch only looked in the Windows home, never found it, and silently fell back to the legacy per-plugin file (`oh-my-opencode.jsonc`) that OMO no longer reads, so provider switches appeared to succeed but did nothing. The WSL-side home is now worked out from the OpenCode directory's WSL path (`home/` or `root`; non-WSL paths and WSL paths outside a home directory give no candidate) and checked before the Windows home, for reads, writes and deletes. Only detection changes: when neither home has a unified config, the legacy fallback still applies. The WSL-side lookup is Windows-only. +- **Windows Main Window Stays in the Taskbar When Shown From Other Entry Points** (#6348): After a silent startup or closing to the tray, the Windows main window is hidden with `skip_taskbar(true)`, and only the tray's "show" action reset that flag before `show()`. When the window was brought back another way it appeared without a taskbar button, and clicking minimize made it disappear completely. The main window's other entry points now call `set_skip_taskbar(false)` before showing it, matching the tray path: launching CC Switch again from a shortcut or the Start menu while it is already running, a `ccswitch://` deep link that focuses the main window, the URL-open event handler, and the database-recovery screen shown on a version mismatch. +- **"Hide AI Attribution" Also Hides Claude Code Session Links** (#7053): Claude Code added a separate `attribution.sessionUrl` setting for the claude.ai session links it puts in commits and pull requests created from web and Remote Control sessions, so clearing `commit` and `pr` no longer hid every attribution surface. The Claude editor's "Hide AI Attribution" quick toggle now writes `attribution: {commit: "", pr: "", sessionUrl: false}` and shows as checked only when all three are set that way, so a config with the older two-field shape now shows it unchecked. Unchecking still removes the whole `attribution` object. The toggle table in the zh/en/ja user manual is updated. +- **Tool Version Probes Fetch Only npm Dist-Tags and Time Out After 15 Seconds** (#7346; fixes #7339): The About card's latest-version probe for npm-distributed tools downloaded the full npm packument (`registry.npmjs.org/{package}`) — metadata for every past version, 14–25 MB uncompressed for fast-releasing packages such as Codex, OpenCode and OpenClaw — just to read its `dist-tags`, and it was the only latest-version probe without `LATEST_PROBE_TIMEOUT`, so it fell back to the shared client's 600 s total timeout. On slow links the cards sat on "loading" for minutes, and the large downloads starved the Hermes GitHub probe into "unknown". The probe now calls the dedicated `registry.npmjs.org/-/package/{package}/dist-tags` endpoint, whose body is the dist-tags object itself (tens of bytes to a few KB), escapes scoped names as `%2f` (`@openai%2fcodex`), and uses the same 15 s timeout as the GitHub and PyPI probes; a timeout, network error or 404 now shows "unknown" instead of hanging. The probe still reads only `latest` and the tool's prerelease tags. +- **WSL Tool Versions No Longer Pick Up the Distro Version From Shell Startup Output** (#7348; fixes #7347): On Windows, a tool installed inside WSL is probed by running `"${SHELL:-sh}" -lic ' --version'` through `wsl.exe`, and the first `x.y.z` anywhere in stdout was taken as the version. A login and interactive shell runs the distro's startup files first and their output lands on the same stdout; on Ubuntu, `/etc/profile.d/update-motd.sh` prints the MOTD ("Welcome to Ubuntu 24.04.4 LTS …") once a day, so after the first probe of the day the Claude Code card showed `24.04.4` until a refresh. The probe now prints the sentinel `__CCSWITCH_VERSION__` before running the tool and parses only what follows its last occurrence — the last one, because the `-lic || -lc || -c` fallback chain can print it more than once, and matched as a substring so OSC escape sequences stuck to its front do not hide it. When the sentinel is missing because the shell failed before running the payload, the output is parsed as before, and tools that print their version to stderr still fall back to stderr. +- **Model List Fetch Works on Zhipu's Responses Endpoint** (#7330): Zhipu's OpenAI Responses endpoint (`https://open.bigmodel.cn/api/v1`, the base URL of the Zhipu GLM Codex preset) serves `/models` in the Codex remote-catalog shape (`models[].slug`) instead of the OpenAI/Anthropic shape (`data[].id`), so fetching models in the Codex provider form came back empty. The shared model-list parser now falls back to `models[].slug` when `data` is absent (those entries have no `owned_by`); when `data` is present the existing `data[].id`/`owned_by` handling is used unchanged, so OpenAI-compatible and Anthropic model lists behave as before. Endpoint derivation, auth headers, timeout and error handling are untouched, and a response with neither field still produces an empty list. +- **AICodeWith Codex-Family Endpoint Corrected to `/v1`**: AICodeWith serves its Responses endpoint at `https://api.aicodewith.ai/v1`; the `/chatgpt/v1` path published in the vendor's blog posts is a documentation error, confirmed by the vendor. The Codex preset (base URL and endpoint candidate) moves from `/chatgpt/v1` to `/v1`, and so do the Hermes (`codex_responses`), OpenClaw (`openai-responses`) and Pi (`openai-responses`) presets, which reuse the same Responses endpoint. Claude Code and Claude Desktop (root host), Gemini (`/gemini_cli`) and OpenCode (already on `/v1`) are unchanged. +- **Claude Desktop Offers the 1M Context Variant on DeepSeek V4 Routes**: The DeepSeek V4 family has a 1M context window, and every Codex catalog in the repo records 1048576 for these ids, but the Claude Desktop presets that pin them left `supports1m` off, so the 1M variant never appeared in Claude Desktop's model list. These presets now declare 1M on their routes: DeepSeek (`deepseek-v4-pro` for Sonnet/Opus, `deepseek-v4-flash` for Haiku), OpenCode Go (`deepseek-v4-flash`) and Baidu Qianfan Token Plan (`deepseek-v4-pro`); PPIO already declared it. The `[1m]` suffix is a marker local to Claude Desktop — translated to `supports1m` at the import boundary and stripped before upstream model matching — so nothing sent to the provider changes. +- **Provider Cards No Longer Expand Stale Usage Tiers When Script Usage Is Inactive**: A disabled React Query observer still returns the cached entry for its key, so a provider card whose usage script was turned off, or that was switched to the official-subscription template, kept the multi-plan chevron and the expanded tier list from an earlier script result; official provider cards were affected the same way. Whether a card can expand is now decided by the same condition that enables the script usage query (`scriptUsageActive`: usage script enabled, not an official provider, not the official-subscription template), and only an active script result with more than one plan can expand. Token Plan templates stay inline as before, and ordinary multi-plan script usage still expands and collapses. +- **Switching Apps Resets the Provider List Scroll Position** (#5211; supersedes #4397): The provider view reuses its scroll containers when you switch apps and only swaps the animated list inside them, so the previous app's `scrollTop`/`scrollLeft` carried over and switching away from a long, scrolled provider list could open the next app on what looked like an empty page. Before paint, both the page's `
` container and the provider-list container now reset to the top-left whenever the active app changes while the provider view is open, and the animated view wrapper is back in the flex-column layout, so the provider list is once again the container that scrolls. +- **Monochrome Preset Icons Use the Foreground Color in the Preset Selector**: Monochrome preset icons (drawn in `currentColor`) took on the preset button's `text-muted-foreground`, so the same icon looked mid-gray in the preset selector but near-black in the form's icon preview and on the provider card — most visibly on 9527CODE, whose 0.82-opacity base layer lightened the gray further. Unselected preset icons, and the icons in the universal preset row, now use `text-foreground`; a selected preset's icon still follows the button's `text-white`. Brand-colored icons are unchanged because their color is set inline. +- **Pi Providers Without an Icon Show Name Initials Instead of the Pi Logo** (#6826): A Pi provider saved with no icon from the user or its preset was stored with the `pi` app logo as its icon, so custom Pi cards all looked alike. The Pi form now saves an empty icon in that case, so the card shows the provider name's initials like other apps' custom providers. Pi providers saved earlier keep the stored `pi` icon until it is changed in the form. +- **zh-TW Pi Form Uses One Term for the Provider Key** (#7187): The zh-TW Pi form labels the field 供應商識別碼 and uses that term in its required-field message, but its duplicate-key message used 供應商標識, the term the Hermes, OpenCode and OpenClaw forms use for their own field. The Pi duplicate message now reads 該供應商識別碼已存在; `hermes.form.providerKeyDuplicate` stays as it is because its label is 供應商標識. + +### Internal + +- **CI Builds the Frontend and Limits Vitest to Source and Test Folders**: The CI frontend job now runs `pnpm build:renderer` (`vite build`) after the unit tests, and `vitest.config.ts` only collects tests from `{src,tests}/**/*.{test,spec}.{ts,tsx,js,jsx}`. A slow Pi form thinking-map test now starts from a provider with its model already set through `initialData` instead of clicking through the custom-preset flow. +- **Claude Desktop Linux Path Tests Run on Every Unix Host**: The Linux config-directory resolver and its three unit tests now compile under any unix test build, so they run on macOS as well as CI Ubuntu; Windows is excluded because the assertions use POSIX absolute paths. The provider-to-Claude Desktop profile sync integration test now also runs on Linux, with a Linux profile-path helper and `XDG_CONFIG_HOME` pinned under the temporary home, saved on setup and restored on drop. + +### Upgrade notes + +- **Database migration v18 → v19** (#7383): `mcp_servers` and `skills` each gain `enabled_mcode BOOLEAN NOT NULL DEFAULT 0`. The migration runs automatically on first launch. CC Switch first tries to back up the database, but a failed backup is only logged and does not stop the upgrade, so check that a backup exists before relying on it for a downgrade. Existing MCP servers and Skills start disabled for MiniMax Code, so nothing is written into `~/.minimax` until you turn them on. After running this version once, v3.20.3 and earlier refuse to open the database as too new — downgrading requires restoring that backup or another pre-upgrade copy of `~/.cc-switch/cc-switch.db`. +- **A MiniMax Code tab appears in the app switcher by default**, including for installs whose settings predate it. Hide it under Settings → app visibility if you don't use MiniMax Code. +- **Existing MiniMax Code data is picked up automatically**: custom providers already in `~/.minimax/config.yaml` are imported the first time the MiniMax Code page loads; an existing `~/.minimax/AGENTS.md` is imported into the prompt library at startup only when the prompt library is empty for every app (and the file is at most 32 KiB) — upgrading users who already have Claude, Codex or other prompts get no automatic import, and the file's content is instead saved as a backup library entry the first time they enable a MiniMax Code prompt; and the first usage sync backfills all existing `local_runtime_token_usage` history, so the dashboard's MiniMax Code totals include past usage right away. +- **MiniMax Code's data directory comes from environment variables only**: CC Switch reads `MINIMAX_DATA_DIR`, then `MAVIS_DATA_DIR`, from its own environment and otherwise uses `~/.minimax`. Settings has no directory override for it, so if MiniMax Code uses a custom directory, CC Switch must be launched with the same variable. +- **Model selection stays in MiniMax Code**: to remove or disable a provider or model that MiniMax Code's `defaultModel`/`defaultLightModel` currently points to, first choose another default in MiniMax Code. A save while MiniMax Code holds its config lock fails with a retry prompt instead of waiting. +- **Codex cards bound to a deleted ChatGPT account need one manual rebind** (#7395): nothing is rebound automatically — open the card's Choose account action and pick the new login. Until then, Codex takeover cannot be enabled for that card; if takeover was on when the app last exited, startup restore now fails the binding check and switches Codex takeover off, leaving the native `~/.codex/auth.json` untouched. Re-enable takeover after rebinding. +- **Codex API keys that were already erased are not restored** (#7434): the fix only stops future erasure. If a Codex provider's stored `OPENAI_API_KEY` was cleared by an earlier edit or switch, re-enter the key once, and its usage script too if that was reset as a consequence. +- **Re-check "Hide AI Attribution" on Claude providers that already use it** (#7053): the toggle now counts as on only when `attribution.sessionUrl` is `false` as well, so configs saved with the old `{commit: "", pr: ""}` shape show it unchecked after upgrading. Tick it again to add `sessionUrl: false`; existing configs are not rewritten automatically. +- **The enabled prompt adopts external edits when the Prompts list loads** (#7194): if an app's live prompt file (`CLAUDE.md`, `AGENTS.md`, …) differs from the enabled prompt's saved content and is not empty, opening the Prompts list overwrites the saved content with the file's. Inactive templates are not touched, and Pi is excluded. +- **Reasoning effort now follows the level you picked**: `/effort max` on `gpt-5.6`, `gpt-5.6-sol`, `gpt-5.6-terra`, `gpt-5.6-luna` and `gpt-6-astra` now requests the `max` tier instead of `xhigh` (#7531), and `/effort xhigh`, or any effort level on models mapped to `grok-4.6`/`grok-4.6-*`, now sends an effort where none was sent before (#7318). Reasoning-token usage and latency may rise accordingly. +- **One-time prompt-cache miss for the affected request shapes**: two groups of converted requests change their bytes once after upgrading, and the shape is stable from then on — Codex requests that carry an `additional_tools` carrier on lenient Chat or Anthropic upstreams (#7454), where the `content: null` system message disappears and the carried tools join `tools`, and requests whose tools lack a description on lenient upstreams that had accepted `"description": null` (#7319, #7378). Tools that do have a description serialize exactly as before. +- **`Ling-2.6-1T` is now a confirmed text-only model** for every provider using that id, not just the presets: with the proxy's media fallback on, images sent to it are replaced with the `[Unsupported Image]` marker before the request goes upstream. +- **Claude Desktop on Linux needs no migration**: switch to a Claude Desktop provider and restart Claude Desktop, the same as on the other platforms. A custom Flatpak build with narrower permissions than the bundled manifest must grant read/write access to `xdg-config/Claude` and `xdg-config/Claude-3p`; the Flatpak build always writes to the host `~/.config`, so if your Claude Desktop uses a custom `XDG_CONFIG_HOME`, use the native package. +- **Pi providers saved without an icon keep the Pi logo** (#6826): only providers saved from now on fall back to name initials. Change the icon in the form to update an existing card. +- **Preset changes apply only to newly created providers** — a stored card keeps the settings it was created with. In this release that covers: MiniMax CN (existing cards stay on `api.minimaxi.com`, which usage detection, the Codex native-Responses catalog and the `web_search` gate still recognize); BaiLing (existing cards keep `api.tbox.cn` and, outside Codex, `Ling-2.5-1T` — edit the base URL and model or re-add the preset); AICodeWith (edit existing Codex, Hermes, OpenClaw and Pi cards to `https://api.aicodewith.ai/v1` by hand); APIKEY.FUN (existing cards keep `api.apikey.fun`); the SudoCode.chat alternate endpoint `api.sudorelay.com` (seeded only on newly added Claude Code, Claude Desktop, Codex and Grok Build cards — add it by hand as a custom endpoint in the endpoint speed test); the Kimi For Coding K2.8 Preview catalog row (1M window, `low`/`high`/`max`), DeepSeek's `deepseek-flash` default with the text-only `deepseek-v4-pro` row, Hunyuan's `hy4-preview` and the Qwen open-weight rows (add the rows in the card's model catalog or re-add the preset); the Claude Desktop 1M variant on the DeepSeek, OpenCode Go and Baidu Qianfan Token Plan presets (tick "Declare 1M" on the route in the provider editor, or re-add the preset); and the Kimi Global presets (#7526) — existing Kimi cards keep their domestic hosts (`api.moonshot.cn` / `api.kimi.com`), so keys from the international kimi.ai platform should use the new Kimi Global / Kimi For Coding Global presets. +- **Codex catalog fixes reach Codex on the next provider switch**, when `config.toml` and the catalog file are regenerated: Chat-upstream cards stop advertising `supports_image_detail_original` (#7476); native-Responses cards on `stepfun.com`, `stepfun.ai`, `qianfan.baidubce.com`, `xf-yun.com` and `minimax.cn` get `web_search = "disabled"`; cards on `minimax.cn` get the native Responses catalog; and `Ling-2.6-1T` is declared text-only. Switch away and back once on affected cards. The proxy-side `original` → `auto` downgrade takes effect immediately, including for sessions already poisoned by an earlier image, so no rollout editing is needed. +- **Pricing**: `grok-4.7`, `glm-5.3-flashx`, `hy4-preview`, `qwen3.8-2.4t-a95b` and `qwen3.8-27b` are new seed rows inserted with `INSERT OR IGNORE` (a row you already added under the same id is kept), so the startup backfill re-prices historical requests under those ids that were recorded at $0; rows with a non-zero cost are left unchanged. The `deepseek-v4-pro` repair only returns the row to 1.32/3.96/0.044 if it is still exactly 0.3/1.2/0.006/0, the values v3.20.3 wrote; customized rows are left alone, but a row set to exactly those values by hand cannot be told apart and is reset too. V4 Pro costs recorded while v3.20.3 was installed are _not_ re-priced — the backfill only recalculates request rows whose recorded total cost is $0 or less and that have token usage — so every V4 Pro request logged under v3.20.3 keeps its Flash-price cost, and only requests after upgrading use the restored price. Grok 4.7 is costed at its <200K base tier; xAI bills prompts of 200K tokens and above at double ($4/$12), which the pricing table does not model. + ## [3.20.3] - 2026-09-11 Development since v3.20.2 is a short, dense cycle: most of it is contributed correctness fixes on the local proxy and the Codex integration, closing issues that had been open for months, plus a round of preset and pricing maintenance. On the proxy, OpenAI-compatible upstreams that keep an empty `reasoning_content` placeholder in every chunk no longer flood Claude Code with empty Thought blocks (#7227), the Codex Responses-to-Chat converter no longer splits a commentary message from its tool calls into two assistant turns — which ended long agent tasks right after a progress update (#7280) — Claude Desktop's one-token model probe is clamped to the Responses API minimum so mapped models stop reporting "not available" (#7287), and Codex image generation under routing picks up three follow-up fixes for pasted full endpoints, mixed-case suffixes and streamed usage (#7177). Two data-integrity bugs are closed: every normal shutdown copied Claude's retry and timeout settings onto the Codex, Gemini and Grok Build proxy rows (#7210), and syncing a universal provider wiped its children's usage script, common-config opt-out and endpoint auto-select and pushed the card to the bottom of the list (#7212). Codex takeover now honors the proxy address when a card omits `model_provider` (#7263), Codex usage import detects growing rollouts on Windows NTFS through a persisted byte cursor (#7219), the tray shows the bound ChatGPT account's quota for managed Codex cards (#7267), and Claude Fable's weekly limit appears in the provider card and tray. The Claude provider editor gains a "Disable Artifact Tool" quick toggle for gateways that reject Claude Code's Artifact tool schema. On the preset side, Kimi's two Codex presets move to native Responses direct-connect, the aggregator Codex presets are refreshed to current catalogs, DashScope/Bailian is rebranded as 千问AI平台 on Qwen 3.8 (#7183), MiniMax defaults move to M3 (#7255), the bundled DeepSeek Codex catalog mirrors the vision-capable `deepseek-flash` (#7286), and the DeepSeek V4 family is repriced to the V4.1 Flash tier. This release does not change the database schema. diff --git a/package.json b/package.json index 4c90e0d03..6bbad8cc2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cc-switch", - "version": "3.20.3", + "version": "3.20.4", "description": "All-in-One Assistant for Claude Code, Codex & Gemini CLI", "type": "module", "scripts": { diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 730f3e778..110be7860 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -758,7 +758,7 @@ dependencies = [ [[package]] name = "cc-switch" -version = "3.20.3" +version = "3.20.4" dependencies = [ "anyhow", "arboard", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index cfbfc90af..3e1e2fd1f 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cc-switch" -version = "3.20.3" +version = "3.20.4" description = "All-in-One Assistant for Claude Code, Codex & Gemini CLI" authors = ["Jason Young"] license = "MIT" diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 47da63548..212503b24 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "CC Switch", - "version": "3.20.3", + "version": "3.20.4", "identifier": "com.ccswitch.desktop", "build": { "frontendDist": "../dist",