# Changelog 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). ## [4.0.19] - 2026-10-23 This release adds Kimi Code as the twelfth managed app (the CLI and the desktop app share one configuration, so both are covered): providers coexist in `~/.kimi-code/config.toml` with add/remove/set-default actions, MCP, Skills, prompts, sessions and session-log usage are supported, and official Kimi subscriptions can be signed in to (several accounts in the Auth Center, each with its own credential slot, provider, nickname and quota). The usage page is reorganized around one scope bar, a selectable metric strip driving the trend chart and heatmap, and a tabbed detail panel with a rows-per-page choice. Other fixes cover Codex `unsupported call: exec` after switching between third-party GPT and direct models in aggregation, Settings → About freezing when launched from a terminal on Linux/WSL, Codex image input turned off by model-metadata fill, the "Unsupported content type: redacted_thinking" message in Claude Code's VS Code extension on Responses upstreams, macOS Claude subscription quota reading the wrong keychain item, Antigravity quota missing the Gemini group and Windows console windows, the Hermes dashboard on WSL, and Escape closing every stacked panel. **Stats**: 45 commits | 206 files changed | +15661 insertions | -2347 deletions ### Added - **Kimi Code Support** (#8687): Kimi Code (command `kimi`) is a new additive app reading `~/.kimi-code/config.toml` (or `KIMI_CODE_HOME`). The Kimi Code CLI and desktop app share this directory, so everything below applies to both. Providers and their models are written as native `[providers]`/`[models]` entries without touching other nodes, environment variables or OAuth references; presets come from the Pi catalog grouped like the other apps. MCP (stdio, HTTP, SSE), Skills with native-directory protection, the global prompt, session browsing/resume and session-log usage import (input, output, cache, first-token time and duration) are supported. The Apps page installs, upgrades and uninstalls Kimi Code with the official installer. Local routing and aggregation do not apply. Database schema 21 → 22 adds `enabled_kimi` columns. - **Kimi Provider Cards** (d6a232419, 1c52fa918): Available cards get "Add", which writes the provider and its models into `config.toml` without changing `default_model`; added cards get "Remove", and "Set as default" lives in the more menu. The provider owning `default_model` (or the subagent model) is marked as default and cannot be removed; a refused removal rereads `config.toml` and explains why in all four locales. Native nodes already in `config.toml` are never overwritten from the stored copy. - **Kimi Provider Form** (b61e2767c, 0f7682900, 8969264b9, b96ff6182): "Fetch models" and the per-row picker fill context window, thinking and image input from the matching preset or models.dev; models are one row each with a display name column (`display_name`, shown in Kimi's `/model`); "Always thinking" and "Load tools on demand" have help tips; request headers move under "Advanced options". - **Kimi Official Accounts** (#8710, #8726, cdd2190df, c49c2be94, 44e0fc963): Sign in with Kimi (mainland or global, device code in the browser) from the Auth Center; the "Kimi Code official login" preset links there. Official cards are managed only in the Auth Center. The first login uses Kimi's own `managed:kimi-code` slot; further accounts get their own credential file, provider node and model aliases (`kimi-code-ccs-`), and their provider key is "Kimi 订阅 " so Kimi's model picker shows a readable name. Each row and official card shows the account's nickname and quota; tokens close to expiry are refreshed under Kimi's own lock (`.lock`, read-only on Windows). Re-login updates the same slot and keeps custom fields; removing a separate account deletes its node, models, credential and card (refused while in use); Kimi's own login is only hidden, never logged out. OAuth hosts are pinned per region and connection errors name the host and cause. ### Changed - **Usage Page Layout** (#8711): One sync button showing the last sync time next to a settings sheet (data sources, display, pricing as a sub-page); a scope bar with date first, app chips and a cascading provider/model menu; a selectable metric strip drives the trend chart and heatmap, with the four token parts in an expandable row; the detail panel has tabs with counts, natural-width tables that scroll sideways with pinned time/app columns, and a rows-per-page menu (10/20/50) for request logs; the old Settings tab is gone and request log columns are chosen from a menu beside the status filter. Empty values show an em dash. Trend and heatmap keep their previous colors (c6f85464e), and request log columns are tighter so the first-token column fits (e53ca7e82). - **Additive Default Provider** (e12415b5b, 7cc3141ca): OpenCode and OpenClaw move "Set as default" from the star icon into the card's more menu (with a model submenu for multi-model providers); the default provider in OpenCode, OpenClaw, Hermes and Kimi is shown in the same blue as an in-use card. - **Pi Provider Key** (1e2559ef9, 03707c400): The key is always shown, prefilled for presets, and a key already used by another Pi card is flagged and blocks saving. - **MCP Display Names** (#8692): Servers with a custom display name show it as the title, with the id beside it, also in the delete confirmation. - **Session Group Headers** (#8709): The whole project group header row toggles the group. - **Presets**: Add BuyToken and V-API (with backup endpoints) sponsor presets; DIT.ai links point at the referral URL; FluxA TokenPlan and Shengsuanyun swap positions. ### Fixed - **Codex `unsupported call: exec` After Switching Models in Aggregation** (fixes #8676): Every third-party aggregation row shared one `comp_hash`, so switching from a third-party GPT row (`code_mode_only`, shell calls recorded as `exec`) to a direct model skipped compaction and the direct model copied `exec` calls. Rows are now grouped by tool protocol (`cc-switch-`; direct rows keep `cc-switch`), so cross-protocol switches compact first. - **Settings → About Froze When Launched from a Terminal on Linux/WSL** (fixes #3241): The `$SHELL -lic` version probe kept the controlling tty and was stopped by SIGTTIN. It now runs with stdin null, its own session and the 10 s deadline. - **Codex Image Input Turned Off by Model Metadata Fill** (refs #8682): Picking a fetched model the lookup reported as text-only wrote `inputModalities: ["text"]`, which Codex treats as no images, with no control to undo it. Modalities are only filled when the metadata includes image. - **Relay Models Borrowed Another Vendor's Hosted Entry** (refs #8765, #8682): When no same-name entry resolved, the metadata fallback took any entry listed under a vendor-like provider, so `deepseek-v4.1-flash-1017` picked thinkingmachines' text-only LoRA entry (262144 window). Entries whose `canonical_model_id` names another vendor are now skipped and the fields stay empty. - **"Unsupported content type: redacted_thinking" in Claude Code's VS Code Extension** (refs #8691): Encrypted-only reasoning from OpenAI Responses upstreams is emitted as an empty `thinking` block carrying the signature instead of `redacted_thinking`; replay is byte-identical. Summary text that only arrives in `output_item.done` is recovered. - **macOS Claude Subscription Quota Read the Wrong Keychain Item** (#8764, fixes #8762): Lookup now queries `-a $USER` first, keeps the service-only query for compatibility, and skips entries without a Claude OAuth token so the credentials-file fallback runs. - **Antigravity Quota Missing Gemini on macOS**: Google returns the Gemini group only to `antigravity/cli/` from 1.2.0, so the User-Agent is now `cc-switch/ (antigravity/cli/)` from the local `agy --version` (fallback 1.4.6). - **Black Console Windows When Refreshing Antigravity Quota on Windows** (refs #8700): `agy --print /usage` and `agy models` run with `CREATE_NO_WINDOW`. - **Hermes Dashboard Started on Windows When Hermes Lives in WSL** (refs #8689): With a `\\wsl$` / `\\wsl.localhost` config directory, "launch dashboard" runs `hermes dashboard` inside the distribution through the login shell. - **Codex Session Header Aliases** (#8718): `session-id` and `thread-id` headers are recognized after `session_id` and `x-session-id`. - **Bailian / Qianwen Token Plan Model List** (#8755): `/apps/anthropic` base URLs also try `{root}/compatible-mode/v1/models`. - **OpenCode Zen / Go Addresses Not Matched to models.dev**: The old `/zen/...` addresses map to the moved `/inference/...` entries, so models such as `deepseek-v4.1-flash` fill again. - **Escape Closed Every Stacked Panel**: Only the topmost full-screen panel reacts. - **A Finished Deep-Link Import Closed a Newer Link's Confirmation** (refs #8696). - **Usage 7/14/30-Day Presets Shifted a Day Across DST** (refs #8719): The start is computed from calendar days. - **Empty Request Log After the Total Shrank on a Later Page**: The page index is clamped back. - **Redundant "not installed or not executable" Line on the Apps Page**. - **Advanced Options Toggles Jumped When Pressed** in the Claude, Codex and Kimi Code forms. ### Upgrade notes - **Database schema 21 → 22** (adds `enabled_kimi` to `mcp_servers` and `skills`); the database is backed up before upgrading. Older versions refuse a version-22 database. - Kimi Code is shown in the sidebar by default; turn it off on the Apps page. ## [4.0.18] - 2026-10-21 This release adds Antigravity CLI (`agy`) as the eleventh managed app. Official-login and third-party API-key cards each open a terminal running agy with that provider; a provider only applies to agy started from its card's Open Terminal, never to a plain `agy` in another terminal. MCP servers, Skills and prompts sync to agy's shared customization folder, sessions can be browsed and resumed, usage is imported from session logs, and the Apps page installs, updates and uninstalls the CLI. On macOS the Auth Center manages several Antigravity accounts: switch agy's sign-in, see each account's quota and bind official cards to an account. Windows in-app updates no longer reboot the machine when a file is in use, the MSI installer makes the desktop shortcut optional, and the header shows the actual connection as a compact mode-colored badge. Fixes cover the router exhausting file descriptors under bursts on macOS, "Restart Codex daemon" failing with `env: node` when launched from Finder, Claude Code sessions staying under their old project after `/cd`, Windows Open Terminal spinning until the console is closed, countdowns on unused Codex quota windows, and unpriced DeepSeek V4.1 Flash requests. **Stats**: 38 commits | 206 files changed | +10711 insertions | -1253 deletions ### Added - **Antigravity CLI Support** (#8268, with follow-ups ported from #8608): `agy` is a new managed app with its own provider list, MCP and Skills flags, prompt file, sessions and usage, separate from Gemini CLI. - Providers: the official login card starts `agy --model=`, applying the model to that session only; API-key cards start `agy --gemini_dir=~/.cc-switch/agy/`, a private profile whose settings carry `modelProvider: "gemini"` and the model and whose `config` links back to `~/.gemini/config`. The key and endpoint reach that one process through the environment only (a 0600 file read and deleted right away on macOS/Linux, a `setlocal` block on Windows); they are never written to agy's files, shell rc files or a wrapper. Every card's action is "Open Terminal with This Provider" and there is no "in use" state; agy's own `~/.gemini/antigravity-cli/settings.json` is never written, apart from removing the `modelProvider` and wrapper/PATH blocks left by #8268 preview builds on startup. Custom cards need an API key; an empty endpoint uses Google's default API. Model lists come from agy's offline catalog (display names), and only a settings file with a model is imported as the official card. - MCP (`~/.gemini/config/mcp_config.json`, stdio and Streamable HTTP, `disabled: true` for off; a zero-byte file is treated as empty), Skills (`~/.gemini/config/skills`) and the prompt (`~/.gemini/config/AGENTS.md`, shared by key profiles). Database schema 20 → 21 adds `enabled_agy` to `mcp_servers` and `skills`. - Sessions from the CLI and Antigravity IDE `brain` folders and from key profiles, showing only the text inside ``; key-profile sessions resume with the matching `--gemini_dir` and key. Usage is imported from `transcript_full.jsonl`, attributed to the model from the `Model Selection` step, and priced at Gemini API prices; session-log rows show as "Session log". - Official quota (Gemini and Claude/GPT, each with a 5-hour and a weekly window) from agy's `/usage`; the card shows the tightest window per group and a dropdown lists all four with their reset times. - The Apps page installs agy with the official script (`install.ps1` on Windows), updates it with `agy update` (Homebrew cask included) and uninstalls script and cask installs. Antigravity has its own icon. Deep links accept `agy` for providers, MCP and prompts. - **Antigravity Accounts in the Auth Center** (macOS): Sign in with Google in the browser using agy's own OAuth client (PKCE, loopback callback). CC Switch keeps a copy of each account and switches agy by writing it into the `gemini`/`antigravity` keychain entry through `/usr/bin/security` (`-U`, read back and checked); the current login is absorbed first and an unreadable one is stashed to `agy-login-stash.json`. Removing an account never signs agy out. After a switch CC Switch warns when agy is running. Each row shows its quota through Google's `retrieveUserQuotaSummary` with the account's own token (User-Agent `cc-switch/ (antigravity)`, read-only); refreshed tokens are saved only to CC Switch's copy. A 403 from an ineligible account shows Google's reason from `loadCodeAssist`. Official cards can bind an account (`authBinding`, `agy_oauth`): Open Terminal switches agy's sign-in first and refuses when the account is missing on this computer, and the card shows the bound account's quota. Unbound official cards read the signed-in account from the keychain instead of starting agy for `/usage`. ### Changed - **Header Connection Badge** (#8634): The actual connection now sits beside the app title as a compact outlined badge in the direct/route/aggregation color, stays visible while browsing other mode tabs, truncates long names and shows details on hover and focus. - **Trimmed Helper Copy** (#8633): Helper text that repeated nearby labels, headings or defaults is removed from provider forms, OpenClaw, daily memory, sync and rectifier settings, with supplementary guidance moved into the existing help cards. - **Usage App Filter Follows the Apps Page**: The usage dashboard only lists apps shown on the Apps page (Claude Code stays while Claude Desktop is shown; the selected app is kept even when hidden). - **Presets**: Add DIT.ai and hao.ai sponsor presets; EveryAPI becomes a full sponsor (presets unchanged); ZetaAPI presets are removed (the icon stays for existing cards). ### Fixed - **Windows In-App Updates Could Reboot the Machine** (#8628, fixes #8534): The updater runs the MSI with `/passive`, and with no restart option Windows Installer rebooted as soon as a file was in use. The MSI now carries `REBOOT=ReallySuppress` (covering updates driven by older updaters), the updater passes it too, a leftover instance is closed on the updater path only, and the release workflow fails if the MSI lacks the property. - **Windows Desktop Shortcut Not Optional** (#8601, fixes #5667): The MSI install-directory page has a "Create a desktop shortcut" checkbox and `CREATEDESKTOPSHORTCUT=0` skips it in silent installs. Upgrades keep the current shortcut state, and a Desktop that cannot be written no longer blocks the install. - **Router Ran Out of File Descriptors on macOS** (refs #8668): GUI apps inherit launchd's soft `RLIMIT_NOFILE` of 256, so bursts of parallel requests failed with EMFILE in accept, upstream connects, `live-state.json` reads and SQLite. The soft limit is raised to the hard limit (capped at 10240) at startup on Unix. - **"Restart Codex Daemon" Failed with `env: node` When Launched from Finder** (refs #8653): Runtime CLI commands (also `opencode models`) ran with launchd's narrow PATH plus the tool's own directory, so an npm CLI could not find node in another directory. The login-shell PATH is now merged in front, honoring the caller's deadline. - **Claude Code Sessions Stayed Under Their Old Project After `/cd`** (#8643, fixes #6862): The last `relocated` record in the final 64 KiB of the transcript now decides the project, as in Claude Code, so grouping and the resume command follow `/cd` and worktree moves; ordinary drifting `cwd` values are ignored. - **Windows Open Terminal Kept Spinning Until the Console Closed** (refs #6712): `cmd /C start` inherited the wrapper's stdout pipe, which only reached EOF when the console closed. Stdio now goes to NUL and only the exit status is awaited (the approach of #6716). - **Countdowns on Unused Codex Quota Windows** (#8631): A window with `used_percent` exactly 0 returns a placeholder reset time of now plus the window length, so the countdown restarted on every refresh. Such windows no longer show a reset time. - **DeepSeek V4.1 Flash Unpriced**: `deepseek-v4.1-flash` (OpenCode Go, OpenRouter, Hermes presets) was shown as unpriced and counted at $0; it now gets a pricing row at the `deepseek-flash` peak price; requests already logged are not repriced. - **Aggregation Exhausted-Quota Workaround** (docs, refs #7962): The guide and manual 4.6 now describe the method that still works since #8431 (switch to Direct, keep "Keep official login for direct switches" off, use a third-party card and fully restart Codex) and mark the old one as no longer working. ### Upgrade notes - **Database schema 20 → 21** (adds `enabled_agy` to `mcp_servers` and `skills`); the database is backed up before upgrading. Older versions refuse a version-21 database. - Antigravity providers only apply to agy started from the card's "Open Terminal with This Provider"; running `agy` in any other terminal keeps agy's own Google sign-in and settings. - Antigravity is shown on the Apps page by default and can be hidden there. - Antigravity account management in the Auth Center is macOS only; other platforms show no account picker, official cards follow agy's own sign-in, and bindings synced from a Mac are ignored. ## [4.0.17] - 2026-10-20 This release fixes two regressions from 4.0.16 and polishes the tray panel. Lightweight mode frees the tray panel's WebView again, so WebView2 processes exit on Windows, and Codex no longer gets stuck on the sign-in page after a long-expired stashed ChatGPT login was given back on entering routing. The tray panel keeps its rows in place while hovering between apps, fits high-DPI and small Windows displays, lines up quota bars with a time-until-reset, and shows remaining balances in green; on Windows a left click opens the main window again. The Hermes form now warns when a provider key is one of Hermes' built-in provider ids. Other fixes cover the proxy port staying taken after quitting on Windows, Codex remote compaction usage recorded as zero, MCP servers lost when several are toggled at once, URL-only MCP servers synced to Hermes and OpenCode as empty entries, smart quotes inside valid TOML strings being rewritten, WebDAV sync on Data Capsule, API keys capitalized by macOS, and MSI installs over a portable folder still treated as portable. **Stats**: 16 commits | 34 files changed | +1674 insertions | -164 deletions ### Changed - **Hermes Built-in Provider Key Warning**: Hermes routes `model.provider` to its built-in provider whenever the name is a canonical built-in id, so a card keyed `deepseek` or `openrouter` was ignored and Hermes asked for the built-in's own API key env var. #8538 only fixed the two MiniMax presets. The provider key field now warns for any of the 52 canonical ids (generated from hermes-agent rc.3-v0.22.0) and suggests `cc-switch-`; a locked key gets a variant that says to re-add the provider under another key. Aliases such as `kimi` still reach the custom provider and are not flagged. Saving is not blocked. - **Tray Panel Quota Lines**: Each quota block is one grid (label, bar, reset), so bars start and end at the same x across tiers and extend under the "In use" column. The absolute reset date is replaced by a compact time until reset (at most two units, e.g. "1d 23h"), and labels no longer wrap. A pay-as-you-go balance colors only the amount green, like the provider card; a used-up balance keeps its warning color. The panel payload carries a new `emphasis` field for this. - **Windows Tray Left Click Opens the Main Window** (#8572): Left click always opens the main window; right click shows the selected tray style (panel or menu). The "Tray style" help text says so. macOS is unchanged: both buttons open the selected style. ### Fixed - **Lightweight Mode Kept the Tray Panel WebView Alive** (refs #8612): Since the panel became the default in 4.0.16, it is a second WebView window that was only hidden after the first tray click, so WebView2 processes never exited in lightweight mode. In lightweight mode the panel is now destroyed whenever it is hidden (and right away when entering lightweight mode with it hidden), deferred to a later main-thread turn and re-checked before tearing down; the next tray click creates it again with a fresh theme state and, on macOS, fresh glass views. - **Expired Stashed ChatGPT Login Given Back on Entering Routing** (refs #8573): The #8431 restore in 4.0.16 could hand back a login stashed months earlier, which made the route table require OpenAI auth and left Codex desktop stuck refreshing a dead token on every enter/exit. The restore now skips a login whose `access_token` JWT has expired; undecodable tokens are still restored, and the skipped login stays in the stash for switching back to an official card. - **Tray Panel Rows Jumping and Clipped on Scaled Windows Displays** (#8572, #8595, fixes #8549, #8553, refs #8564): Both columns now follow the main panel's height and long provider lists scroll inside the sub panel, so hovering between apps no longer moves rows under the cursor or leaves blank space with a single app. On Windows, WebView CSS pixels and screen logical pixels are converted with the monitor scale factor, the panel is narrowed and clamped to the monitor work area, footer buttons can shrink, and a `devicePixelRatio` change after moving to another monitor relays out the panel and its clip region on the first show. - **Proxy Port Still Taken After Quitting on Windows** (refs #8552): The locked mio 1.1.1 created inheritable listener sockets, and terminals or CLI tools started by CC Switch inherited them, so the port stayed LISTENING under the dead PID and the next start failed with os error 10048. mio is bumped to 1.2.1 (`WSA_FLAG_NO_HANDLE_INHERIT`), with a Windows test asserting the listener handle is not inheritable. - **Codex Remote Compaction Usage Recorded as Zero** (#8479): Codex 0.153+ writes a `token_usage_record` per response, and a remote compaction follows it with an all-zero `last_token_usage`. Such records are now imported as `codex_thread:{root_thread_id}:usage:{response_id}`; matching `token_count` rows stay the only row, existing `event_index` values and cursors are kept, and lines scanned before the upgrade are not re-imported. - **MCP Servers Lost When Toggled Concurrently** (refs #8580): `toggle_mcp_app` and `set_mcp_enabled` edited the live config without the per-app switch lock, so parallel toggles, or a toggle racing a provider switch or MCP resync, could drop servers from the live file while the database showed them enabled. Both commands now go through `McpService::toggle_app_locked`. - **URL-only MCP Servers Synced as Empty Entries to Hermes and OpenCode** (refs #8579): A spec with a `url` but no `type` (from JSON mode or a deep link) was treated as stdio, giving Hermes an entry with only `enabled: true` and OpenCode a `local` entry with an empty command. Codex's transport inference moves to `mcp/validation.rs` and all three projections use it. - **Smart Quotes Inside Valid TOML Strings Rewritten** (refs #8559): Normalization replaced every curly or fullwidth quote, so MCP import and validation and the Codex config editor turned `"don’t"` into `"don't"` and rejected valid values such as `["say“hello”"]`. Only smart quotes used as string delimiters become ASCII now; quotes inside ASCII-quoted strings are kept, multi-line strings and comments are handled, and the Chinese IME case still parses. Text without smart quotes is unchanged. - **WebDAV Sync Failed on Data Capsule** (refs #8554): CSTCloud Data Capsule answered `PUT manifest.json` with 500 for multi-line JSON sent as `application/json`. The manifest is uploaded as `application/octet-stream`; its bytes and hash are unchanged and readers ignore Content-Type, so old and new versions read each other's manifests. S3 is unchanged. - **macOS Capitalized Typed API Keys**: The API key field lacked the autocorrect, auto-capitalization and spellcheck opt-outs of the shared input, so a typed `sk-…` key shown in plain text could be saved as `Sk-…`. Pasted keys were not affected. - **MSI Install Over a Portable Folder Still Treated as Portable** (#8529, refs #3132): A leftover `portable.ini` kept the app in portable mode, so the update button only opened the Releases page. The MSI now removes `INSTALLDIR/portable.ini` on install (not on uninstall, no wildcard). ### Upgrade notes - **No database schema change** (still 20). - On Windows a left click on the tray icon opens the main window again (it opened the panel in 4.0.16); right click shows the tray style chosen in Settings → General. - Codex compaction lines scanned before upgrading stay unbilled; only lines read after the upgrade use the new path. ## [4.0.16] - 2026-10-19 This release turns the tray into a quick panel and fixes a batch of Codex and routing issues. Clicking the tray icon now opens a panel with each app's in-use provider, mode, quota and usage; the native menu is still available under Settings → General → Tray style. In Codex aggregation mode, a session no longer gets stuck compacting on the provider it came from, and the ChatGPT login is no longer lost every time CC Switch restarts while Codex is routed. Codex through Chat upstreams keeps parallel tool results together around image notices, configured GPT context windows now apply to official GPT catalog entries, and the 272K GPT cap from 4.0.13 only applies to windows borrowed from the vendor entry. Claude Code routing and Claude Desktop route mode publish the 5.5 catalog ids for 5.5 upstreams. Other fixes cover Zhipu pay-as-you-go accounts showing "query failed", Volcengine plan quota turning red on new cards, fnm-managed Node CLIs on Windows, an idle card that could not be deleted, the "Get API Key" link in the edit form, and a softer grey macOS icon tile. OpenCode cards can now set OpenCode's default model. **Stats**: 40 commits | 121 files changed | +8525 insertions | -552 deletions ### Added - **Tray Panel** (#8432): On macOS and Windows, clicking the tray icon opens a panel by default; Settings → General → Window and terminal adds a "Tray style" option, Panel (default) or Menu. The panel has a Configuration tab (apps, in-use providers with search and switching, modes in their theme colors, quota) and a Usage tab whose trend follows the main window's currency and chart style. Every row reuses the native menu's event id and goes through `handle_tray_menu_event`. macOS draws native glass under each card with a mostly opaque theme fill so text stays readable over dark windows; Windows uses opaque cards on a clipped canvas. Switching provider rows follows the pointer immediately (with a menu-aim check toward the open sub panel), panel state is collected off the main thread and change notifications are throttled. The choice is a device setting (`nativeTrayMenu` in `settings.json`; unset means the panel) and applies without restarting. Linux keeps the native menu and gets an "Open quick panel" item at the top. - **OpenCode Default Model From a Card** (refs #8509): Added OpenCode cards get the "Set as default" action already used by OpenClaw, with a model menu built from the same fragment CC Switch writes. It writes only the top-level `model` (`/`) in `opencode.json` through the format-preserving path, leaving comments and `small_model` alone. New commands `get_opencode_default_model` / `set_opencode_default_model`. ### Changed - **MiniMax-M3.1-Flash** (#8518): The official MiniMax / MiniMax en presets default to MiniMax-M3.1-Flash in Claude Code, Claude Desktop, Codex, Hermes, OpenCode, OpenClaw and Pi, with MiniMax-M3 still selectable (Claude Code keeps it in the Opus slot) and M3.1 Flash pricing seeded. Hermes pins its background title model to M3, since M3.1 cannot turn thinking off, only when the user has not chosen their own title model or route. - **"Get API Key" Link Style**: The link is light blue again without an underline, keeping the caption size and the ↗ arrow. - **Sidebar Today Badge Prefix**: The "Today" prefix from before 4.0.15 is back. - **Zhipu API Key Links**: The `?ic=` invite parameter is removed from the Zhipu GLM / Zhipu GLM en links in all seven presets. - **Pricing**: Kimi K3 records the $3/M 5-minute cache write price (was 0); Step 5 Preview cache reads follow StepFun's raise to 0.70 CNY/M. Both go into the repair list so existing installs pick them up unless edited by hand. Seeds qwen3.7-flash, qwen3.6-27b and qwen3.8-omni-flash. - **Turkish**: 17 UI strings reworded (#8481). - **Presets**: AllTokens SI joins as a sponsor preset. ### Fixed - **Codex Aggregation Sessions Stuck Compacting on the Previous Provider** (fixes #8485): Route provider catalog rows kept their template `comp_hash` (official GPT mirrors carry `3000`) while Stack member rows used `cc-switch`, so moving a session between them made Codex run a pre-turn compaction on the previous model; when that provider was out of quota every later turn failed the same way. Third-party route rows in the merged catalog now share the Stack `comp_hash`, which also unsticks sessions already caught this way. The official route keeps its native rows. - **Codex ChatGPT Login Lost After Restarting CC Switch in Routing** (fixes #8431): With the direct pointer on a third-party card and "keep official login" off, quitting detached back to direct config, which stashed the login and deleted `auth.json`, and the next attach never restored it, so Codex desktop 26.1013+ refused to resume chats. Detaching now always keeps the login; a real exit from routing still follows the setting. Entering or attaching a third-party route also takes back the most recently stashed login when `auth.json` has none, recovering logins already lost this way. - **Chat Tool Outputs Split by Hoisted Developer Notices** (#8445, fixes #7190, #7569): Mid-history system/developer items such as Codex's `image_resize_notice` were treated as turn boundaries in the Responses → Chat conversion, so strict Chat upstreams saw tool, user, tool and returned 400 "No tool output found". They are now hoisted: tool media and orphan results wait until the tool-output batch ends, commentary merging looks past trailing hoisted messages, and restored missing calls go before their first output. - **Configured GPT Context Windows Ignored for Official Catalog Entries** (#8385, fixes #8366, #8350): Provider-scoped or per-model context windows now apply to mirrored official GPT entries (`context_window`, `max_context_window`) with the auto-compact limit kept in sync; official defaults stay when nothing is configured. Gemini input overflow on the Codex Chat path is normalized to `context_length_exceeded` as a `response.failed` stream so Codex can compact, and non-string SSE error codes are stringified. - **272K GPT Cap Applied to Real 1.05M Windows**: The 4.0.13 cap applied to every window from the metadata lookup. Same-endpoint presets and the models.dev provider matched by address now keep their own windows; only a window borrowed from the vendor entry is capped. A typed window is kept as is, and the Codex, Hermes, OpenClaw, OpenCode and Pi forms show a note when a GPT window is above 272K. The OpenRouter, Vercel AI Gateway, Cloudflare AI Gateway and DigitalOcean presets get their 1.05M windows back, and the preset guard allows GPT windows above 272K only for those hosts. Existing rows are not migrated. - **Claude Code Routing Used 5 Ids for 5.5 Upstreams** (#8450): In routing takeover, Opus / Sonnet slots whose upstream is a 5.5 model get `claude-opus-5-5` / `claude-sonnet-5-5`, so Claude Code shows the right model and uses the 128K output cap instead of 64K. `CLAUDE_CODE_SUBAGENT_MODEL` is projected the same way and mapped back when its tier is empty. - **Claude Desktop Route Mode Sent 5 Requests to 5.5 Targets** (fixes #8082): Route mode published the fixed mapping keys `claude-opus-5` / `claude-sonnet-5`, so Desktop built requests for the 5 models and 5.5 upstreams rejected them. It now publishes the 5.5 catalog ids for 5.5 targets using the same version matching as Claude Code; mapping keys are unchanged so older profiles keep resolving, and a route configured under the catalog name is left alone. - **Unforced Claude WebSearch Rejected on Codex OAuth** (#7936): A WebSearch tool with `max_uses` was refused unless it was the forced tool. It now passes with a soft instruction cap, and the existing response-side counter cuts off excess `web_search_call` items. - **Zhipu Pay-as-you-go Accounts Showed "Query Failed"** (refs #8465): The token plan endpoint answers HTTP 200 with business code 500 "no coding plan" for such accounts. The personal plan now falls back to the account balance (`availableBalance`, CNY on bigmodel.cn, USD assumed on z.ai), shown in the balance layout; auth, network and other errors are unchanged and the team plan does not fall back. - **Volcengine Plan Quota Red on New Cards** (refs #8494): The quota needs an account AccessKey ID + Secret, not the inference key, but adding a Volcengine Agent / Coding Plan card or importing one by deep link enabled the query anyway. Volcengine is preselected in the usage settings but stays off until the AccessKey is filled in; deep links that set `usageEnabled` are still honored, and the import badge matches the backend. - **Codex Official Card Editor Showed a Stale `auth.json`**: For official cards not bound to a managed account, the box now shows what `auth.json` will contain after switching, predicted read-only from the switch's login plan, instead of the row's old OAuth snapshot (or an empty object for follow-login cards). Saving without touching it keeps the row's original auth. The live file was never affected. - **fnm-managed Node CLIs Not Found on Windows** (refs #8484): fnm only adds a per-shell multishell link to PATH, so detection and in-app installs saw no node or npm. The effective PATH now ends with fnm's `aliases\default` (`FNM_DIR`, legacy `~\.fnm`, then `%APPDATA%\fnm`; only if present); explicit PATH entries keep precedence. - **Idle Card Could Not Be Deleted** (refs #8506): Deletion also refused cards that only the stale DB `is_current` pointed at, which happens after the startup migration of an old Claude Desktop mapping card. It now checks the effective direct pointer plus the route, the same set the UI disables. - **"Get API Key" Link Missing When Editing a Provider**: With no preset selected, the form looks up the preset by its current base URL for the link and partner promotion, as the add form does; presets that disagree fall back to the Website field. - **False Environment Conflicts on macOS / Linux**: The check also scanned the inherited process environment (for example `ANTHROPIC_BASE_URL` that Claude Desktop injects into its terminals), which could not be removed from the app. Only shell config definitions are reported on Unix now; the Windows registry check is unchanged. - **MiniMax Code Usage Rows Recorded as `unknown`**: When the usage table has no model, it is looked up from the assistant message with the same session and timestamp in the pi history. Rows already imported are unchanged. - **Hermes MiniMax Provider Shadowed by the Built-in** (#8538): Hermes routes `model.provider` equal to a canonical built-in id (`minimax`, `minimax-cn`, `minimax-oauth`) to the built-in provider and ignores a same-named `custom_providers` entry, so a card keyed `minimax` asked for `MINIMAX_API_KEY` and sent nothing. The MiniMax / MiniMax en Hermes presets now prefill `cc-switch-minimax` / `cc-switch-minimax-en` as the provider key, and MiniMax-M3.1-Flash declares `supports_vision: true`. Existing cards keyed `minimax` are not migrated. - **Wide CJK Usage Axis**: Chinese and Japanese ticks write 5千万 / 2千亿 instead of 5000万 / 2000亿, and the Y axis in the dashboard and the tray panel sizes itself to its widest tick instead of a fixed 44px. - **macOS Icon Tile Too Bright** (refs #8495): The white tile from 4.0.15 is recolored to `#e8e8e8` with the same geometry and anti-aliasing. Windows and Linux icons stay transparent. - **Blurry About Card Logo** (refs #8470): The About card renders the vector `logo.svg` instead of a 32×32 PNG. ### Upgrade notes - **No database schema change** (still 20). - On macOS and Windows the tray icon opens the panel after upgrading; the native menu can be picked under Settings → General → Tray style. On Windows a left click used to open the main window and now opens the panel too; "Open main window" sits at the bottom of the panel. - Existing GPT model rows are not migrated. Codex GPT rows saved before 4.0.13 with an auto-filled 1.05M window take effect since #8385; the form shows a note when opened. ## [4.0.15] - 2026-10-17 This release is mainly fixes. On Windows, settings such as hidden apps and config dir overrides no longer reset to defaults after an update, and updating Codex no longer removes a healthy install when `--version` is slow. The macOS app icon gets a white rounded-square background so macOS 26 stops placing it in a grey tile. Requests through the local route keep Claude Code's session identity again, which restores the stable cache key on the Codex OAuth path and the Gemini signature replay for Claude Desktop. Codex through Chat upstreams recovers from a malformed tool call in history, and DeepSeek no longer rejects replayed tool history. Other fixes cover slow WebDAV/S3 transfers cut off at 300 seconds, "'claude' is not recognized" in Windows provider terminals, Hermes sessions in WSL, unconditional restart prompts on routed switches, the routing marker after failover, and drag sorting. New: a bar / area style for the usage trend chart, an image input switch per Hermes model, and model fetching for managed OAuth providers in the Claude Desktop form. **Stats**: 31 commits | 86 files changed | +5214 insertions | -380 deletions ### Added - **Usage Trend Chart Style** (#8439, fixes #8406): The Display section of the usage Settings tab adds a trend chart style, bar (default) or area. It is stored with the other display settings as `usageDisplay.trendChartType` in `~/.cc-switch/settings.json` (bar when unset or unknown). Both styles share the metrics, axes, tooltip and currency conversion; the area chart draws a dot for a single data point. The Display controls are now laid out as an aligned two-pair grid, with the exchange rate on its own row under the currency. - **Hermes Per-model Image Input** (refs #8464): The expanded model details get a "Supports image input" switch. On writes `supports_vision: true`; off removes the key so Hermes falls back to its own detection instead of being forced to text-only. Picking a fetched model whose metadata lists image input pre-fills `true` (never `false`), and an explicit value is left alone. - **Claude Desktop Fetches Models for Managed OAuth Providers** (refs #8426): The "Fetch models" button now shows for GitHub Copilot, Codex OAuth and xAI OAuth providers and calls the account-scoped model APIs, asking to log in first when needed. The fetched list is reset and in-flight results are dropped when the provider type or account changes. ### Changed - **Routed Switch Messages** (#8378, fixes #8347): Route switches report what was applied (remembered, route target, routed, or aggregation default) and whether client files were rewritten. The success toast follows that, and Codex / Gemini CLI / Grok Build are asked to restart only when their configuration actually changed. Direct switches are unchanged. - **Sidebar Today Badge Shows Only the Numbers**: The "Today" prefix is dropped even when a single item is shown; the full wording stays in the tooltip and accessible name. - **Claude Usage Import Caches Pricing per Transaction** (#8226, fixes #8224): Resolved and missing model prices are reused within one session-log import transaction and dropped before the next, so price edits still apply. - **Turkish**: New uninstall and plan quota strings are translated (#8291). - **Presets**: EveryAPI and AllTokens join as sponsor presets. ### Fixed - **Windows Settings Reset to Defaults After Updates** (refs #8429): An older non-atomic save could leave leftover bytes after a shorter `settings.json`, and a failed load silently used defaults that the next full save (for example the What's New dialog) wrote back. Saves now go through a temp file and rename; reads retry transient errors, strip a UTF-8 BOM, keep every field that still deserializes, and read only the first JSON value when a trailing tail is present. A file that did not fully load is backed up to `settings.json.broken-