Antigravity responses without a model field or a Model Selection event (subagents, ACP sessions) were imported as unknown. Recover the model from the native conversations/<id>.db: prefer the tiered model ID (field 21, matching Model Selection and pricing) and fall back to the base model name (field 19). The database is opened read-only, never waits on a writer, and keeps already recovered names when it cannot be read.
Co-authored-by: Jason <farion1231@gmail.com>
The default provider in OpenCode, OpenClaw, Hermes and Kimi used a gray
card and dot. Use the same blue as an in-use switch-mode card instead,
and drop the now-unused neutral card tone.
Kimi is additive, but both card groups only offered "Switch", which
wrote the provider into config.toml and changed default_model at once,
so the Added / Available split did nothing.
- Available cards get "Add": a new add_provider_to_live_config command
writes the provider and its models into config.toml without changing
default_model, so they show up in Kimi's /model list. A node already in
config.toml belongs to Kimi and is never overwritten from the archive.
- Added cards get a "Remove" button; "Set as default" (select the
provider's model) moves into the more menu, as for OpenCode/OpenClaw.
- The provider owning default_model is marked with the gray "Default"
status like OpenCode, and cannot be removed.
Kimi Code shows a provider's key as its name in the model picker, so
separate accounts appeared as "kimi-code-ccs-<hash>". New separate
accounts are now keyed "Kimi 订阅 <nickname>" ("Kimi Subscription" when
CC Switch is not in Chinese), matching Kimi's own label for its default
login and limited to the characters Kimi accepts, numbered on clashes.
The credential slot and model aliases keep the stable
"kimi-code-ccs-<hash>" name, so default_model stays an ASCII key and
ownership checks are unchanged. A returning account is also recognised
by its credential slot, not only by the local identity cache. Card names
fall back to the readable key when no nickname is cached.
Follow-up to #8726, which lets the Authorization Center hold several Kimi
official subscriptions, each with its own provider and credential slot.
Identity: after sign-in (and for logins found on disk) read /me to show
the account's nickname on its row and card. Signing in to an account that
is already listed writes back to its slot instead of adding a duplicate,
and the slot is chosen when the login completes, so a native Kimi login
made meanwhile is never overwritten. New separate slots are named from a
hash of region and user id, stable across devices. Opening the Kimi page
reads identities too, so card names carry the nickname without a visit to
the Authorization Center first.
Remove instead of sign out: a separate account is deleted together with
its provider node, models, credential and card (refused while in use);
web search/fetch pointing at it moves to Kimi's own default login. Kimi's
own login is only hidden from the list - CC Switch never logs Kimi out -
and stays hidden across Kimi's token rotations until the user signs in to
it again here. An empty list caused by that says so instead of "signed
out".
Quota: each account row and official card shows its quota. Tokens close
to expiry are refreshed under the same proper-lockfile-compatible lock
Kimi uses (mkdir <slot>.lock, 5 s stale takeover), with Kimi's revoked
token recovery. Windows, where Kimi takes no lock, only reads.
Also: re-login keeps custom fields such as custom_headers on the node;
deleting a card whose node was removed elsewhere deletes the leftover
CC Switch-owned credential; the card's switch button works for official
accounts after checking they are signed in on this machine; account rows
use the same compact layout as the other Authorization Center groups.
* feat(kimi): manage multiple official accounts in auth center
Isolate account credentials, providers and model aliases; preserve native logins and protect official OAuth nodes. Fix session image access, drop unpublished usage repair paths and the kimi-cli alias, and synchronize all four READMEs.
* fix(kimi): address account compatibility review feedback
Restore the native default credential slot for the first account, stop rewriting tool services on account switches, allow deleting profiles removed from native config, and fix official-account test fixtures and documentation.
* fix(kimi): pin OAuth endpoints to the login region
Use fixed official hosts for mainland and global sign-ins, keep existing account regions and credential slots during reauthentication, and preserve sanitized transport error causes. Add regional routing and network-error regression coverage.
Add Kimi Code as the 12th managed app (additive mode): native multi-provider config in ~/.kimi-code/config.toml (honors KIMI_CODE_HOME), presets derived from the Pi catalog, MCP (stdio/HTTP/SSE), Skills with native-directory protection, global prompts, session browsing/resume, and session-log usage import. Local routing and aggregation do not apply; official OAuth stays managed by Kimi Code. Schema v21 -> v22 adds enabled_kimi columns.
Since route and aggregation mode keep the ChatGPT login (#8431), the
route table writes requires_openai_auth = true whenever a login is on
disk, so making a third-party card the aggregation default no longer
keeps Codex from locking the send button to Luna Reserve once the
official 5-hour quota runs out.
The guide and the 4.6 manual pages now describe the method that still
works: switch to Direct, keep "Keep official login for direct switches"
off, switch to a third-party card and fully restart Codex; switch back
to Aggregation once the quota resets. The old method is marked as no
longer working.
Refs #7962
Add a "Create a desktop shortcut" checkbox to the MSI install-directory page and a public CREATEDESKTOPSHORTCUT property for silent installs. Upgrades keep the current state of the desktop shortcut, and clearing the option during an upgrade removes the old one.
The desktop shortcut is now copied from the Start Menu shortcut by custom actions with Return="ignore", so a Desktop that cannot be written no longer blocks or rolls back the install.
Fixes#5667
Co-authored-by: thefool <thefoolyuzhe@gmail.com>
agy only reads GEMINI_API_KEY and GOOGLE_GEMINI_BASE_URL from the process
environment, and modelProvider is global. The previous approach wrote the key
to a file, installed a wrapper script and edited .zshenv/.zprofile/.zshrc so
every agy picked it up.
Now the native settings.json always stays in login mode: login cards set the
model (display name) and drop modelProvider; key cards only drop
modelProvider. A key card applies to agy started from the card's "Open
Terminal": `agy --gemini_dir=<private dir>` under ~/.cc-switch/agy/<id>, whose
settings carry modelProvider and the model and whose config links back to
~/.gemini/config so MCP, Skills and prompts stay shared. The key and endpoint
reach that one process through the environment only.
The terminal launcher now takes a per-app launch description (command, env,
temp files) so Claude Code and agy share the terminal layer. On macOS/Linux
env values are read from a 0600 file that is deleted right away and passed
via `env`, so values exported by the user's rc files cannot override them;
on Windows they live in a setlocal block. Claude's temporary settings file is
now written 0600 as well, and temp files are removed before handing over to
the interactive shell (exec skipped the EXIT trap).
On startup, wrappers, env files and PATH blocks left by earlier test builds
are removed (only CC Switch-marked content) and the current provider is
reapplied so modelProvider returns to login mode.
Add Antigravity CLI (agy) as a managed app: provider switching for the native login and API-key modes, MCP and Skills sync through ~/.gemini/config, session browsing and session-log usage import.
Co-authored-by: peterLwk888 <lwk1124@outlook.com>
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
Route switches now return what was actually applied (remembered / route / stack) and whether the client configuration was rewritten, and the success toast is built from that instead of always asking Codex / Gemini CLI / Grok Build users to restart. A restart hint is shown only when client files changed. Direct-mode switches serialize unchanged. Fixes#8347.
Codex's automatic approval (Approve for me) sends every sandbox
escalation to a reviewer model on the same provider as the
conversation. Third-party cards had no way to pick it: without a model
list Codex asks the provider for its hidden codex-auto-review model
(404 on most relays), and with a list the main model reviews itself
(#8015, #5903, #3855). Codex reads the choice from the
auto_review_model_override field on the main model's catalog row, so
CC Switch now writes it there.
- Direct and Routing: a new "Auto Review model" field on the Codex card
(modelCatalog.autoReviewModel), limited to the card's own model
list. It is written on every row of the card's catalog; a value that
left the list is not written. Hidden for official cards; disabled
with an explanation when the card has no model list.
- Aggregation: a device setting (codexStackAutoReviewModel) in the
Aggregation settings sheet, picking any published ccs- model so
approvals can go to another provider. It is checked against the
merged catalog on every rewrite, like the sub-agent model, and the
card value is ignored in this mode. The default label follows the
default provider: official Auto Review, or the main model.
- Changing the catalog needs a Codex restart; the existing stale
catalog notice covers it.
- Inside a form, Radix Select's hidden native select reported an empty
value when the saved pick and the list arrived in the same render,
which cleared the field when editing a card; empty values are now
ignored.
- Docs and the four locales updated.
Aggregation mode used to strip [agents] default_subagent_model, so Codex
sub-agents always followed the main model (#8286). Users can now choose a
sub-agent model and reasoning effort for aggregation, both in the
"Switch to Aggregation" dialog and in a new Aggregation settings sheet on
the Stack tab.
- The choice is a device setting (settings.json). On every rewrite it is
checked against the merged model catalog: an id that is no longer
published (member removed, or it is now the default provider) is not
written, so sub-agents fall back to the main model instead of every
spawn failing, and it comes back when the member is re-added. An effort
the model does not support is dropped.
- Codex reads [agents] when a thread starts: new conversations pick up a
change without a restart; open ones keep the old value.
- The classic sub-agent tools toggle moves from Settings into the sheet
and is now on by default: with the newer tools the main agent's
provider encrypts the task and a sub-agent on another provider gets
nothing (#8253). It is stored under a new key, codexStackClassicTools;
the old codexStackClassicSubagents key was written as false on every
save since v4.0.10, so it is no longer read.
- Docs and the four locales updated.
The usage page's auto-refresh interval only re-read the database, so
session usage still waited for the fixed 60-second background scan, and
routed usage already updated in real time through usage-log-recorded.
The interval now controls how often session logs are scanned:
- The background scan drops to every 10 minutes as a safety net against
CLIs pruning old session files.
- While the usage page is visible it scans right away, again whenever
the window becomes visible, and then at the selected interval
(5/10/30/60s, default 60s). The first scan waits until settings load,
so it never runs when automatic scanning is off.
- The "Off" option is removed (a saved 0 falls back to 60s), and the
interval menu is hidden when automatic scanning is off.
- Statistics no longer poll. Imports refresh them through the
usage-log-recorded event, which is always on again, and time-relative
ranges re-run once a minute so "Today" rolls over at midnight and
rolling windows drop old records without new usage.
Copy is updated in zh, zh-TW, en, ja and tr, and in the zh/en/ja manual.
Section 2.1 still said the icon area lets you pick a preset icon and
customize its color. There has been no color control for a while (the
color comes from the built-in icon's metadata), and icons can now be
uploaded to My Icons. Point 2.1 at the full description in 2.3, and
drop "and color" from the icon row of the 2.3 field table.
Uploading an icon used to attach the image to the one provider being
edited, shown as a lone "Custom icon" strip above the built-in grid. It
had no name, could not be searched and had to be uploaded again for
every provider that wanted it.
The icon picker now has two groups, filtered by the same search box:
- My Icons: the first tile uploads an image. After it is resized, a
dialog asks for a name (the file name by default); "Save and Use"
stores it in the library and selects it. Library icons show their
names, can be picked by any provider in any app, and can be renamed or
deleted from the buttons that appear on hover. Uploading an image that
is already in the library reuses the existing entry. A current custom
icon that is not in the library (for example one from an import link)
shows as "Current icon" and can be saved, named after the provider.
- Built-in Icons, as before.
Clicking an icon now selects it and closes the picker, so the "Done"
button is gone; "Clear icon" next to the search box removes the icon.
The library lives in the settings table under `custom_icons`, which is
already cloud-synced. Providers still store the image itself, not a
library id, so renaming or deleting a library entry never changes a
provider that uses it, and a provider shows its icon on a device whose
library differs. Adding to the library decodes the PNG fully like
saving a provider does, caps the library at 100 icons and names at 64
characters. SQL import and cloud sync drop library entries whose image
is invalid, alongside the existing provider and universal provider
cleanup; unknown fields on the remaining entries are kept.
The naming dialog stops its submit event from propagating: React events
bubble through portals, so without it saving a name would also submit
the provider form the picker is rendered in.
The user manual's icon section (2.3, zh/en/ja) describes the new flow.
Translate the site owner integration guide into English and Japanese,
quoting UI labels from the app's own locale files and using each
language's Sub2API template fallback message. The three versions link to
each other. Manual 5.3 (en/ja) and the English, German and Japanese
READMEs now point to the guide in their own language.
Site owners running New API or Sub2API already have an "Import to CC
Switch" button in the key list. Say so up front, and point out that New
API's link carries no usage script, so users see no balance after import.
The manual linked the generator at farion1231.github.io, but GitHub Pages
was never enabled for the repository, so the link returned 404. The page
is now hosted on the website at ccswitch.io/deplink.html; the manual
(zh/en/ja), the four READMEs and the site owner guide point there.
The generator inlines its favicon and logo so the same file works both in
the repository and on the website. Manual 5.3 gains a pointer to the site
owner guide, and its usage steps list the Skill repository type.
The guide explained how to build a link but not how to put it on a page.
Add a section on fetching the link from the backend when the button is
clicked (so the key never sits in the page source), the button markup,
and what to show when CC Switch is not installed. Add a fast path at the
top and fold the sections most sites can skip.
The link generator folds the Base64 tool and the MCP, prompt and Skill
examples, opens them when reached from the sidebar or an anchor, and
gains an FAQ entry pointing to the new section.
The user manual (zh/en/ja) gains a Sub2API template section, and the link
generator and the site owner guide gain a Sub2API example. All of them use
the same script as the in-app template.
deplink.html becomes a generator for site owners instead of a test page:
a sidebar layout with light and dark themes, tabs for provider, MCP,
prompt and Skill links, per-app `apiFormat` choices, usage script
templates, and examples that follow the v4.0.10 rules (Pi providers,
the activation toggle, `apiFormat`).
iconUrl:
- The generator gets an `iconUrl` field and checks it with the same
shape rules as the app (https, a domain, default port, no
credentials), so a link is never generated with an address CC Switch
would drop. Built-in `icon` names are checked against the same
whitelist as the parser.
- The parser marks `icon` and `iconUrl` values that would be dropped.
- New example and FAQ entry for a site's own icon.
- The integration guide adds `iconUrl` to the Node.js sample and a
debugging step pointing at the log line. The zh and ja manuals list
`iconUrl` and say what `icon` accepts.
- Add a single `apiFormat` parameter (anthropic / openai_chat /
openai_responses / gemini_native) that maps to each app's own field:
meta.apiFormat for Claude Code, Codex and Grok Build (non-native formats
need local routing), `npm` for OpenCode, `api` for OpenClaw and Pi,
`api_mode` for Hermes. Unsupported combinations are rejected while
parsing; links without it keep the previous defaults byte for byte.
- Support Pi provider links. They write a models.json node and must carry
a model.
- The confirmation dialog now has a switch for "switch after import"
(providers), "add to the app config" (additive apps, on by default) and
"enable after import" (prompts). The link's `enabled` only sets its
initial state.
- Turning the switch off really only saves the row: a new
`ProviderService::add_without_switching` skips the first-provider path
in `add` that would otherwise write the client config. The deep-link
golden rows now have is_current false accordingly.
- Reject link values that additive apps would expand: Pi keys starting
with `!` and `$NAME` / `${NAME}`, OpenCode `{env:}` / `{file:}`, and
`${...}` for OpenClaw and Hermes. These would run a command or send
local secrets and files to the link's endpoint. Checked when parsing,
when merging `config`, and when building the provider.
- Update the deep-link manual in zh/en/ja.
Session titles: when `models.session_summary` is unset, Grok Build
(verified on 1.0.50) generates titles with the built-in `grok-4.6`
model but sends the request to the default model table's endpoint, so
third-party upstreams reject it and title generation fails. Switching
never wrote this key, so fixing the row templates alone would not
reach live. The Grok switch patch now makes session_summary follow the
provider when it is unset or names a table CC Switch wrote (the
target, the write record, or a table retired in this switch): it points
at the target table, or is removed when switching to the official
account. A user's own choice (a built-in model or their own table) is
left alone.
Deep links: a Grok Build link carrying a config.toml no longer gets
rebuilt into a fixed template that dropped the profile's display name,
description, API backend and context window. The table named by
`[models] default` is kept through an allowlist of Grok 1.0.50 profile
fields; the endpoint, key and model come from the link parameters.
Links whose table uses auth_provider, env_http_headers or
mtls_cert_dir are rejected, and env_key is still dropped. Only one
model table is imported, since a provider owns one table in live.
Links without a config keep the old template byte for byte.
Fixes#7003
Refs #6457
A Codex provider link could carry a full config.toml, but import only
pulled the key, endpoint and model out of it and rebuilt a fixed
template, silently dropping the provider's auth mode, headers, websocket
support and model catalog URL.
When a link carries a config, keep the parts that belong to the
provider, using the same field lists the live projection writes on
switch:
- from the selected [model_providers.*] table: connection and auth
fields (wire_api, requires_openai_auth, http_headers, query_params,
supports_websockets, retries/timeouts, capabilities, ...); the route
is always renamed to `custom`
- model_catalog_url only on the same origin as the endpoint, since
Codex sends the provider's credentials when fetching it
- top-level key and provider-exclusive fields (model, reasoning effort,
web_search, context window, subagent model)
Global settings ([features], [tools], MCP, approval_policy, notify,
local file paths) are not imported. Links whose provider table makes
Codex run a command or read local credentials (auth, aws,
gateway_oauth, env_http_headers) are rejected; env_key is dropped in
favor of the link's own key, and an env_key-only link is rejected.
URL params still take priority, and a table's bearer token is reset to
the final key.
Links without a config keep the old template byte for byte; a new
golden snapshot locks the shape of config-carrying imports. The confirm
dialog notes that only provider settings are imported, and the manual
documents the rules (and that configUrl is rejected).
Refs #7438, #7942, #5510
A provider link that carries a usage script now enables usage query
unless it says `usageEnabled=false`. Site owners attach the script so
users see their balance; making them switch it on again after import
was an extra step with little safety benefit: the script runs in the
QuickJS sandbox, sends a single request, and deep-link scripts have no
template type, so that request must be same-origin HTTPS with the base
URL.
Links without a script still import usage fields disabled. The
confirmation badge follows the same rule as the backend, and the
script body is still shown in full before the user imports.
高級オプション reads as "luxury options" in Japanese. Rename the toggle and the docs that quote it (README_JA, the Japanese routing guides and the user manual FAQ).
The proxy URL written into client configs (including Claude Desktop's gateway URL) was always derived from the listen address, with 0.0.0.0 written as 127.0.0.1, so clients outside the host's loopback, such as Claude Desktop sessions in WSL2 NAT mode, got an unreachable address.
Add an optional per-device "connection address" in Settings > Routing, stored in ~/.cc-switch/settings.json so cloud sync never carries it to another machine. When set it replaces the host in every proxy URL written to clients; saving rewrites routed clients without restarting the service and is safe to retry.
Fixes#7877
The three dark-mode text tiers all sat close to white (15.5 / 11.1 / 8.4
contrast on the app background), so screens full of table data and labels
looked harsh and the hierarchy flattened.
- --text-1 #f4f4f5 -> #e4e4e7, and the shadcn --foreground family follows
so inherited body text matches
- --text-2 #d0d0d8 -> #ababb3, --text-3 #b5b5bd -> #9a9aa2
- --muted-foreground aligned with --text-2
Contrast is now about 13.4 / 7.5 / 6.1; the secondary and tertiary tiers
stay at or above 4.5:1 on card and selected backgrounds. Light mode is
unchanged.
Explain how the switch maps to config.toml, when Codex actually compresses
sessions, how resuming and CC Switch's session list and usage handle
compressed rollouts, and what to watch out for (Codex version, startup
notice, turning it off, other tools that read .jsonl). Move it to the top
of the new features.