Clicking the tray icon on macOS and Windows opens the panel again by
default. A settings.json without nativeTrayMenu now reads as the panel;
an explicitly saved true keeps the native menu, which stays available
under Settings > General > Tray style.
Add AllTokens SI (alltokens.si), the enterprise new-api deployment, as
a basic-tier sponsor separate from the alltokens.cc entry, with its own
icon and partner promotion copy in all four locales.
Models were checked against the public catalog (/api/pricing):
- Claude IDs use dots (claude-sonnet-5.5) and there is no Haiku, so the
Claude Code preset spells out all four tiers with Haiku on Sonnet,
and Claude Desktop maps its routes instead of passing them through.
- The GPT group only declares Chat Completions, so OpenCode, OpenClaw,
Hermes and Pi use Chat; Codex keeps Responses, which new-api's OpenAI
channels forward.
- Gemini and Grok are outside the default auto groups and need a key
from their own group; Gemini uses gemini-3.8-flash-high and Grok
Build grok-4.7, as the group has no grok-4.5.
OpenRouter, Vercel AI Gateway, Cloudflare AI Gateway and DigitalOcean
bill GPT through the OpenAI API, so the 272K subscription cap from
v4.0.13 understated their windows. Restore the values those presets had
before the cap: Codex DigitalOcean, Hermes OpenRouter / Vercel /
Cloudflare and OpenClaw Vercel / Cloudflare. Pi presets read windows from
the shared model catalog, which stays capped, so the Pi Vercel preset
overrides GPT-6 Astra explicitly.
The preset guard now allows GPT windows above 272K only for these four
hosts, and checks that each of them still uses the exception. Catalogs
without an address (Pi catalog, OpenCode variants) get no exception.
The 272K GPT cap (v4.0.13) applied to every window the metadata lookup
returned, so platforms that really serve the 1.05M OpenAI API window
(OpenRouter and other providers models.dev recognizes by address) were
capped too. The risk the cap guards against is narrower: a relay that
models.dev does not know falls back to the vendor's own entry, and its
1.05M lands on a model that is often served from a ChatGPT subscription
with a 272K window.
- Same-endpoint presets and the models.dev provider matched by address
keep their own windows; only the window borrowed from the vendor entry
(including when the matched provider lists none) is capped at 272K.
- A window the user types is kept as is. When a GPT model's window is
above 272K, the Codex, Hermes, OpenClaw, OpenCode and Pi forms show a
note under the field: relay GPT usually has the subscription window, and
an oversized one delays compaction until the upstream rejects the turn.
Text added in zh / en / ja / zh-TW.
Existing rows are not migrated. Codex GPT rows saved before v4.0.13 with
an auto-filled 1.05M now take effect since #8385; the note shows when the
form is opened.
StorageLocationGuard writes settings.json both when it is set and when it
is dropped. Eight skill tests created it before TestHomeGuard, so the setup
write and the drop-time restore (locals drop in reverse order) hit the
runner's real ~/.cc-switch/settings.json, which intermittently failed on
Windows with os error 32 / 1176 when another process held the file.
Make the guard borrow an active TestHomeGuard so it can only be created
after HOME isolation and is always dropped before HOME is restored, and
assert that the settings directory lives under the test home.
OpenCode picks its startup model from the top-level `model`
("<provider>/<model>") in opencode.json, but CC Switch had no way to set
it, so users had to edit the file by hand after adding providers.
Reuse the OpenClaw "Set as default" card action for OpenCode:
- Backend: `opencode_config::{get,set}_default_model` read/write only the
top-level `model` through the existing format-preserving edit path
(comments kept, `small_model` untouched), rejecting values that are not
"<provider>/<model>". New commands `get_opencode_default_model` and
`set_opencode_default_model`.
- Frontend: the additive section builder's `openclawDefault/openclawModels`
become generic `defaultModel/defaultModelOptions`; the default card is
marked and other added cards get "Set as default" with a model menu.
Model options come from the same fragment the backend writes (a stored
full config is unwrapped) and skip natively disabled models. Built-in
providers that declare no models get no button. Unlike OpenClaw, the
default provider can still be removed, matching the old behavior.
Existing generic notification texts are reused, so no new strings.
Refs #8509
Deleting a switch-mode provider was refused if the device-local direct
pointer, the DB is_current flag or the proxy route pointed at it. The
UI only disables delete for the effective direct pointer and the route,
so a card that only DB is_current still pointed at looked deletable but
failed with "无法删除当前正在使用的供应商".
This happens after upgrading with an old Claude Desktop mapping card:
the startup migration moves the local Desktop pointer to the official
card and routes to the mapping card, but leaves is_current on it. Once
the user routes to another provider, that card is idle yet undeletable.
is_referenced now checks the effective direct pointer (provider_for
Direct: the local pointer when valid, else DB is_current) plus the
route, the same set the UI disables. The fallback still protects
is_current when there is no valid local pointer.
Refs #8506
On Windows fnm only exposes Node through `fnm env`, which prepends a
per-shell %LOCALAPPDATA%\fnm_multishells\<id> link to that shell's PATH.
The registry PATH never contains it, so the effective PATH CC Switch builds
for CLI detection and lifecycle commands had no node, npm or npm global
shims: fnm-installed Codex/Claude/OpenCode showed as not installed, and the
in-app install failed with "'npm' is not recognized". The only fnm search
path we had is the Unix multishell layout.
Append fnm's stable <fnm dir>\aliases\default (FNM_DIR, legacy ~\.fnm, then
%APPDATA%\fnm; only if it exists) as the last segment of the effective
PATH. On Windows that alias points at the default Node installation, which
holds node.exe, npm.cmd and the global CLI shims directly, so detection,
where.exe resolution and install/update commands all see it, while every
explicit PATH entry keeps precedence. Multishell links are not scanned
since they grow without bound (#8313).
Refs #8484
The About card showed src/assets/icons/app-icon.png, a 32x32 bitmap,
in a 40px box, so it was upscaled and looked blurry, especially on
Retina displays (80 device pixels).
Use the existing logo.svg (already used by the sidebar header) instead.
The PNG carried about 4px of transparent padding while the SVG is
tight, so keep the 40px box and add p-1 to keep the mark at its
previous visual size. The PNG had no other references and is removed.
Refs #8470
The white rounded-square tile baked into icon.icns in v4.0.15 reads too
bright in the Dock, especially in dark mode. Recolor the tile to #e8e8e8
while keeping the tile geometry and the artwork pixels unchanged: the
artwork coverage of each pixel is solved from the white-tile icon itself,
and only the exposed tile area is moved from white to grey, so edges keep
their original anti-aliasing at every size in the iconset.
Windows (.ico) and Linux (PNG) icons stay transparent.
Refs #8495
Volcengine's plan quota is queried through its OpenAPI with an
account-level AccessKey ID + Secret, not the inference API key. Adding a
Volcengine Agent/Coding Plan provider (or importing one via deep link)
auto-enabled the token_plan query anyway, so every new card showed a red
"quota not found" right away and users read it as the provider not
working.
Preselect Volcengine in the usage settings but leave the query off until
the user fills in the AccessKey. Deep links that set usageEnabled are
still honored; the import dialog badge and the backend agree.
Refs #8494
Codex desktop 26.1013 refuses to resume a chat unless Codex is signed in
to ChatGPT. Users kept the login and moved to routing mode, but had to
sign in again after every restart of CC Switch. With the direct pointer
on a third-party card and "keep official login" off, quitting detaches
the client back to direct config, which stashed the login and deleted
auth.json; attaching on the next launch leaves auth.json alone for a
third-party route, so the login never came back.
Detaching is temporary, so it now always keeps the login regardless of
the setting; a real exit from routing still follows it. Entering or
attaching the proxy for a third-party route also gives back the most
recently stashed login when auth.json has none, which recovers logins
already lost to the old detach or to a direct switch made before
entering routing. The stash entry is taken, not copied, so a later
logout in Codex sticks across route switches and restarts.
Fixes#8431
Claude Desktop's route mode publishes the mapping table's keys as model
names, and the Opus / Sonnet keys are fixed to claude-opus-5 /
claude-sonnet-5. When the target is a 5.5 model, Desktop builds its
requests for the 5 model and the 5.5 upstream rejects them; editing the
generated profile by hand is overwritten on the next write.
Publish claude-opus-5-5 / claude-sonnet-5-5 instead when the target is a
5.5 model, using the same version matching as the Claude Code routing
contract. The mapping table's keys are unchanged, so requests from an
older profile that still name the original key keep resolving to the
same target. A route the user configured under the catalog name is left
alone so the two don't collide.
Fixes#8082
Editing a Codex official card showed the row's stored `auth`, which is the
OAuth snapshot older versions backfilled. Switching no longer backfills, so
once Codex rotates its tokens the editor displayed an access token that no
longer matched ~/.codex/auth.json. Follow-login cards (empty row auth)
showed an empty object instead of the current login.
For official cards not bound to a managed account, the auth.json box now
shows what auth.json will contain after switching to the card, predicted
read-only from the same login plan the switch uses. Saving without touching
the box keeps the row's original auth, so opening and saving neither binds a
follow-login card to the current account nor stores soon-to-expire tokens.
Switching never wrote the stale snapshot over the same account's login, so
the live file was not affected.
MiniMax Code only fills the model column of its token usage table when the
turn carries an execution model, so many imported rows showed up as
"unknown" and could not be priced. Each usage row's timestamp comes from the
assistant message that produced it, and that message in the pi history
carries its model, so look the model up by session and timestamp when the
column is empty.
The current history table is matched on its created_at_ms and role columns,
so only the matching message is parsed; the legacy pi_history_json blob is
parsed once per session and reused. Rows that were already imported are left
as they are.
Kimi K3 (kimi-k3 and the bare k3 id) now records the 5-minute cache write
price of $3/M listed on Moonshot's pricing page; it was seeded as 0, so cache
creation tokens were billed at nothing. Step 5 Preview cache reads follow
StepFun's 2026-10-14 raise from 0.35 to 0.70 CNY/M (0.10 USD). Both changes
also go into the repair list so existing installs pick them up unless the row
was edited by hand.
Seed qwen3.7-flash (entry tier), qwen3.6-27b and qwen3.8-omni-flash from
Alibaba Cloud's international price page.
In aggregation mode the route provider's catalog rows kept their template
comp_hash (official GPT mirrors carry "3000") while Stack member rows used
"cc-switch". Switching a session between the two made Codex run a
pre-turn compaction with the previous model; when that provider was out
of quota the compaction failed, custom providers get no retry with the
current model, and every later turn hit the same provider again.
Third-party route rows in the merged catalog now share the Stack
comp_hash, which also unsticks sessions already stuck this way. The
official route keeps its native rows: Codex retries with the current
model there and the official compaction semantics stay intact.
Closes#8485
The system glass darkens or lightens with whatever is behind it while the
text follows the app theme, so a light panel over a dark window ended up
as dark text on a dark backdrop. Lay a mostly opaque theme fill over the
glass so only a hint of the blurred background shows through.
Chinese and Japanese compact notation keeps 万/亿 up to four digits
(5000万, 2000亿), which made the token axis much wider than in English.
Write those ranges as 5千万 / 2千亿 instead, and let the Y axis in both the
usage dashboard and the tray panel size itself to its widest tick rather
than a fixed 44px, so short labels no longer leave a gap and long money
ticks are no longer clipped.
- When the sub panel switches to another app, fade the new content in from
half opacity over 90 ms; no fade-out, and the card and its native glass
stay put, so nothing waits and no empty glass shows. The first expand
keeps its own card animation. Disabled under reduced motion.
- Like native menus, hovering another item of the main panel (header,
tabs, problem rows, footer) closes the sub panel, still holding while the
pointer heads for the sub panel; leaving the panel entirely keeps it open.
- Switch the provider sub panel right away when the pointer moves between
app rows; only hold the switch while the pointer is heading for the open
sub panel (menu-aim triangle check), instead of a fixed 180 ms wait
- Collect the panel state on a blocking worker instead of the main thread;
the burst of usage refreshes after opening used to stall hover and paint
- Throttle panel change notifications to one per 250 ms (leading edge, so a
provider switch still shows at once) and skip re-rendering unchanged data
- Skip native submenu title updates while the tray uses the panel
- Drop the 150 ms color fade on row hover so highlights follow the pointer
On macOS/Linux the conflict check also scanned the current process
environment, which is inherited from whatever launched CC Switch. Claude
Desktop injects ANTHROPIC_BASE_URL=https://api.anthropic.com into the
terminals it spawns, so `pnpm dev` started there always showed a false
conflict. Such variables cannot be removed from inside the app either
(the Unix delete path for them is a no-op).
Only shell config file definitions are reported on Unix now; the Windows
registry check is unchanged.
The app rows in the tray panel now show Direct / Route / Aggregation in
the same mode colors as the main window (direct blue, route teal,
aggregation purple, with the dimmed dark-theme variants), so the modes
are easy to tell apart at a glance. The panel model carries a
machine-readable modeKind next to the localized mode name.
The tray panel becomes opt-in under Settings > General > Tray style.
A settings.json without nativeTrayMenu now reads as the native menu;
an explicitly saved false keeps the panel.
The panel's Usage tab now draws the trend as bars or an area, whichever
is picked in the main window's usage settings. Changing the currency or
the chart style in the main window also shows up the next time the
panel opens; before, the panel kept the old values until you switched
tabs, because its window stays alive while hidden.
Settings > General > Window and terminal gets a "Tray style" option:
Panel (default) or Menu. With Menu, clicking the tray icon on macOS and
Windows shows the native menu again, as before the glass panel: macOS
opens it on either button, Windows opens the main window on left click
and the menu on right click. The switch applies right away, no restart.
The choice is a device setting (nativeTrayMenu in settings.json).
Linux only has the native menu, so the option is hidden there.
Providers don't remember which preset they came from, so the edit form had
no preset and only linked the "Website" field; with that field empty the
link vanished. When no preset is selected, look the preset up by the
form's current base URL and use its apiKeyUrl and partner promotion, same
as the add form. If presets sharing that address disagree on the link or
the promotion, don't guess and fall back to the Website field.
The v7 pass turned the link into underlined body text, which read like a
plain label. Bring back the pre-v7 blue (blue-400 light / blue-500 dark,
swapped on hover) and drop the underline; keep the caption size and the
↗ arrow.
Remove the ?ic= referral parameter from the Zhipu GLM (bigmodel.cn) and
Zhipu GLM en (z.ai) apiKeyUrl in all seven presets; the landing pages stay
the same.
Since the token plan template became the default for Zhipu cards, pay-as-you-go
accounts showed "query failed": the quota endpoint answers HTTP 200 with
{"code":500,"msg":"当前用户不存在coding plan","success":false}, which looked like
the provider was broken (#8465).
Show the plan when there is one, otherwise the account balance:
- coding_plan: query_coding_plan returns CodingPlanOutcome, with ZhipuNoPlan
for the personal plan when msg says the account has no coding plan. Auth
failures, network errors and other business errors are unchanged.
get_coding_plan_quota keeps its old result (used by the modal test button).
The team plan does not fall back: the balance endpoint ignores the
organization/project headers.
- balance: query_zhipu_balance reads availableBalance from the undocumented
/api/biz/account/query-customer-account-report, which accepts the inference
API key (verified on open.bigmodel.cn, CNY). api.z.ai is assumed to share
the shape and is shown in USD; not verified. Not added to detect_provider so
it does not compete with the injected token plan template.
- The token_plan branch of query_provider_usage returns the balance result on
ZhipuNoPlan; the tray already renders it as a balance line.
- UsageFooter only renders token plan data as tiers when every row is a
percentage, so the fallback balance uses the balance layout.
Hermes routes model.provider `minimax` to its built-in provider and ignores a
same-named custom_provider, so no request is sent. The MiniMax presets now
prefill cc-switch-minimax / cc-switch-minimax-en, and MiniMax-M3.1-Flash
declares supports_vision so images go to the main request.
Make MiniMax-M3.1-Flash the default model on the official MiniMax / MiniMax en presets across Claude Code, Claude Desktop, Codex, Hermes, OpenCode, OpenClaw and Pi, keeping MiniMax-M3 selectable, and seed its pricing.
Claude Code keeps M3 in the Opus slot so it stays selectable through the local route. Hermes pins the background title model to M3 (M3.1 cannot turn thinking off) only when the user has not chosen their own title model or route, and releases the pin when no active card declares it.
Explicit provider-scoped or per-model context windows now apply to mirrored official GPT catalog entries (context_window and max_context_window), with the auto-compact limit kept in sync; official defaults remain when nothing is configured. Gemini input-overflow errors on the Codex Chat path are normalized to context_length_exceeded and returned as a response.failed stream so Codex can compact, and non-string SSE error codes are stringified so upstream messages survive.
Fixes#8366
Treat mid-history system/developer items (e.g. Codex's image_resize_notice) as hoisted rather than turn boundaries in the Responses -> Chat conversion: pending tool calls are still flushed, but tool media and orphan results wait until the whole tool-output batch ends, so strict Chat upstreams no longer see tool, user, tool. Commentary merging now looks past trailing hoisted system messages, and restored missing calls are inserted before the first output that belongs to them.
Fixes#7190Fixes#7569
In routing takeover, Opus/Sonnet slots whose upstream is a 5.5 model now get Claude Code's catalog ids (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 the proxy maps the catalog form back to the configured subagent model when its tier is empty.
On macOS and Windows, clicking the tray icon opens a panel with a Configuration tab (apps, in-use providers, modes, quota) and a Usage tab. Every row reuses the native menu's event id and goes through handle_tray_menu_event. Linux keeps the native menu and adds an "Open quick panel" item.
macOS uses native glass under each card. Windows keeps a fixed canvas per popup and clips it with a window region, with opaque cards.
Co-authored-by: funkpopo <funkpopoisme@gmail.com>
Add AllTokens (alltokens.cc) as a basic-tier sponsor across all nine
switchable and additive apps except MiniMax Code, with partner
promotion copy in all four locales and its icon.
AllTokens runs Sub2API, where each API key is bound to a single group
and every group serves one platform, so each preset notes which group's
key it needs:
- Claude Code and Claude Desktop use the Claude group; Desktop passes
the route IDs through since the group serves claude-sonnet-5,
claude-opus-5 and claude-haiku-4-5.
- Codex, OpenCode, OpenClaw, Hermes and Pi use the GPT group over the
Responses API.
- Gemini CLI uses gemini-3.8-flash, the only model in the Gemini group;
Grok Build uses the default grok-4.5.
Models were checked against the public model plaza
(/api/v1/model-plaza).
Add EveryAPI (everyapi.ai) as a basic-tier sponsor across Claude Code,
Claude Desktop, Codex, OpenCode, OpenClaw, Hermes and Pi, with
api-cn.everyapi.ai as the China-priority endpoint candidate.
- GPT models on the gateway only serve the Responses API, so OpenCode,
OpenClaw, Hermes and Pi use their Responses protocols.
- The gateway has no claude-haiku-4-5, so Claude Desktop maps the three
roles to the 5.5 models instead of passing route IDs through.
- No Gemini CLI or Grok Build preset: the catalog has no Gemini or Grok
models and no /v1beta endpoint.
- Add the partner promotion copy in all four locales and the icon.
The four Display controls sat in a wrapping flex row, so the fourth one
dropped onto its own line and labels and fields of different widths never
lined up. Put them in a two-pair grid: each label column takes its widest
label and every control shares one width, so both rows align. Currency and
trend chart style share the first row, the sidebar Today items and request
log columns the second, and the exchange rate (non-USD only) gets its own
row under the currency. Narrow viewports fall back to a single pair per row.
The bar / area toggle added in #8439 sat on the trend chart header. Move it
to the Display section of the usage Settings tab, next to currency, the
sidebar Today items and request log columns, and store it with them as
usageDisplay.trendChartType in settings.json (bar when unset or unknown).
The chart header keeps only the metric switch. The localStorage key is
dropped; the toggle was never released, so there is nothing to migrate.
Add a bar / area segmented toggle to the usage trend chart. Bar stays the default; the choice is remembered locally and unknown values fall back to bar. Both styles share the same metrics, axes (RFC3339 bucket keys), tooltip and currency conversion; the area chart draws a dot when there is a single data point. Fixes#8406.
When Codex replays tool-call history, empty assistant messages, progress text or reasoning can sit between a function_call and its results. Converted to Chat, these become standalone assistant messages and DeepSeek rejects the request with HTTP 400. For explicit DeepSeek model ids only, fold such messages into the open tool-call message after conversion (running after the malformed-call recovery from #8241); well-formed histories stay byte-identical. Refs #8331.