Saving any disabled prompt emptied the app's prompt file whenever no prompt
was enabled, so adding or importing a prompt wiped a hand-written AGENTS.md
or Hermes SOUL.md without a backup. A deeplink import also lost the file
before enable_prompt could back it up.
Follow the MiniMax Code branch: read the previous row before saving and
clear the file only when this prompt was enabled, is now disabled, and no
other prompt is enabled.
A vendor that also hosts other vendors' models (NVIDIA lists
minimaxai/minimax-m2.7 without a canonical id) was counted as a second
vendor next to the entry relays point at, so the fallback gave up and
models like MiniMax-M2.7, gpt-5.4 and kimi-k2.6 got no metadata through
unrecognized relays. Use the entries relays point at first and only fall
back to a vendor's own entry when nothing points anywhere.
- Export modelsDevQueryOptions so the metadata fill hook and both usage
pricing dialogs use one query definition, including the one-hour
gcTime the fill hook needs for its prefetch-only cache.
- Add useCommittableRef for the "latest value + synchronous commit"
pattern repeated in the Codex, Hermes, OpenClaw, and OpenCode forms.
- Drop an unused field from the models.dev index.
Without a thinking map, Pi shows off..high for a custom model and hides
xhigh and max, even when the upstream supports them. When a fetched
model is picked and has no map yet, fill one:
- Use the reviewed map of the Pi preset at the same endpoint, but only
under the same API and when the compat it relies on does not conflict
with what the user wrote. Missing compat keys are added to the model,
and for openai-completions the thinking format and reasoning_effort
support that Pi detects must match the preset's.
- Otherwise, when Pi sends levels verbatim as reasoning_effort
(openai-responses, or openai-completions detected as the OpenAI
thinking format), derive the map from models.dev effort levels using
Pi's own getEffortThinkingLevelMap rule.
A model with its own baseUrl is now looked up by that address. Update
the Pi design docs accordingly.
Apply the same lookup to Pi custom providers: picking a fetched model
fills contextWindow, maxTokens, reasoning, and image input when they are
still empty. thinkingLevelMap is left untouched.
Update the Pi design docs, which previously forbade inferring model
capabilities. That rule came from an earlier implementation that voted
across providers by model name and produced wrong values; the docs now
keep the lesson as constraints (match the provider by endpoint first,
fill only empty fields, never generate a thinking map).
A provider's /models endpoint only returns model IDs, so a model picked
from the fetched list left its context window, output limit, reasoning
levels, and modalities empty, while the same model in a preset had them.
When a model is picked in the Codex, OpenClaw, Hermes, OpenCode, or
MiniMax Code form, look up its metadata field by field:
1. the preset model with the same endpoint (hand-verified values);
2. the models.dev provider whose API endpoint matches;
3. the vendor's own models.dev entry, without its price.
Endpoints match only when host and path are equal after dropping a
trailing version segment or request path, so plans that share a host
(z.ai standard vs Coding Plan, OpenCode Zen vs Go) never borrow each
other's values. Models are never matched by name across providers: the
same model ID often has very different context windows per deployment.
Only empty fields are filled, and reasoning or image input is only ever
turned on. models.dev is prefetched once the model list is fetched and
shared with the usage pricing import; when it arrives late the row is
filled again, located by its stable row key. A single toast asks the
user to review what was filled.
Move the models.dev download, response types, and model ID normalization
out of the pricing module into src/lib/modelsDev.ts so other features can
reuse the same data. The query key becomes ["models-dev"] because the
cache now holds the raw catalog rather than pricing-only data.
Switch the Kimi Open Platform invite links to the new campaign trackers:
mainland platform.kimi.com links now use track-7cf2b91dcde043eda6ef9a95951a042c
and global platform.kimi.ai links use track-674ed6e2af924a5682a87421f7cf753a.
In the four READMEs this replaces the previous track IDs in the sponsor banner
and API call to action. The Kimi and Kimi Global presets for Claude Code,
Claude Desktop, Codex, OpenCode, OpenClaw, Hermes and Pi now carry the same
tracked links in websiteUrl, so sign-ups from inside the app are attributed as
well. The aff=cc-switch tag is kept everywhere; the console API-key deep links
and the Kimi Code subscription links are unchanged.
Success, provider failure, neutral failure and terminal failure each
had copies of the breaker/status/in-use/failover-switch updates inside
forward_with_retry_inner. Move them into note_attempt, finish_success,
record_provider_failure, finish_neutral_failure and
record_failed_request so the loop no longer writes routing state
directly.
Add characterization tests that drive forward_with_retry against a
scripted upstream and assert ProxyStatus, current_providers and
circuit breaker counts on every exit path. No behavior change.
MiniMax Code shows custom_provider:<key>/<model> in its model picker, so the
UUID CC Switch used as the key leaked into the UI. New MCode providers now
take a user-entered key, prefilled from the preset, like OpenCode. Existing
providers keep their UUID and the key is locked when editing, since MCode's
default-model references embed it.
- Duplicating an MCode provider generates an unused <key>-copy key.
- Adding rejects a key already used by a database row or any live
custom_provider node, including MCode's own account providers. The check
and save run under the MCode switch lock, and the live write re-checks the
key while holding config.yaml.lock so it never replaces a node MCode
created in the meantime.
The reachability probe treats any HTTP response as reachable, so its timeout, retry and slow-threshold knobs barely change the outcome. Remove the Advanced settings panel and its get/save commands; the probe now always uses the built-in defaults (8s timeout, 1 retry, 6000ms slow threshold).
Also remove dead code: the batch check command had no frontend caller, and stream_check_logs was written on every check but never read. Stop writing it and drop the always-empty model_used/error_category result fields. The table, its migrations and the periodic cleanup stay so existing rows age out.
- Resolve three-way editor conflicts in one place (ConflictPolicy::resolve)
and share JsonChange between the Claude and Gemini editors
- Move the duplicated Codex/Grok editor helpers into editor_toml and chain
TOML steps through TomlSteps instead of per-app wrapper patches
- Start every engine write through AppWrite::begin so the lock and the
pre-write recovery check cannot be skipped (the Codex edits-only path
missed it)
- Look up the direct provider, the in-use provider and the proxy origin
through single helpers; read the mode file once per status poll, settle
mode and written records in one state update, and query one row for the
provider existence check
- Drop the common-config merge chain in live.rs that no longer changed
anything, the unused config API and snippet helpers on the frontend
- Share the live-edit conflict retry flow, the inactive-fields panel and
the editor-view app list across dialogs and editors
- A multi-file operation records that it has started publishing in
live-state.json before the first file is replaced; if that cannot be
recorded nothing is published. Recovery rolls such an operation forward
even when a published file was changed again afterwards, so an
unpublished Codex login stash is no longer discarded.
- controller::lock_settled fails when the previous operation cannot be
finished, and entry points refuse to save or switch instead of writing
rows the leftover operation cannot restore. Startup, detach and exit
log the failure per app and continue.
- Saving, adding and syncing the current provider take the proxy switch
lock before reading mode and pointer, so entering proxy mode can no
longer be overwritten by direct key fields. A new proxy address is
written to every attached app even if one of them fails.
- Codex: the row-level model catalog pointer leaves with its provider; a
profile selecting the built-in openai provider counts as the official
route; exclusive fields the editor shows only because live has them are
compared by value, so an untouched field changed by the client stays
with the client. The add dialog sends the preset draft it projected, and
the backend uses it as the field origin like an existing row.
- Claude editor keeps a legacy Bedrock apiKey at the top level where older
versions read it; the Gemini editor recognizes a legacy Google card the
same way switching does.
- The request path passes the provider it already resolved to the router
instead of reading live-state.json twice per request.
- Exclusive fields shown in the editor only because live already had them
are no longer adopted into the row when left untouched; deleting one now
removes it from live instead of being a no-op. Modified values still
belong to the row. The Codex add dialog compares against a projection of
live with an empty card, since its base already carries the preset.
- Codex editor diffs skip the route tables selected by both the base and the
edited document, so a table renamed away from `custom` no longer leaks
into config.toml as a global setting.
- The Claude editor takes the proxy switch lock, saves the row before
writing live and restores the row when the write fails without leaving a
pending operation, matching the other editors. Row rollback is shared
through ProviderService::restore_row.
- commit_staged keeps the temp file when the rename fails, so a pending
operation can still be finished later; one-shot StagedWrite::commit
removes it itself.
- Recovery rolls forward once any file carries its planned bytes: files
still at their old bytes are published, files changed by someone else
are left alone, and the pointer/mode still land (RolledForwardExcept).
Only an operation that never published anything is discarded.
- Entry points take the switch lock and settle the previous operation
before reading mode, pointer, auth or stash (controller::lock_settled);
each app writer refuses to continue after finishing a leftover
operation so the caller retries with fresh state.
- Codex proxy contract includes the official login identity, so two
official routes for different accounts swap auth.json.
- An unreadable Codex login stash is never overwritten by a write that
would store a login into it.
Gemini CLI loads ~/.gemini/.env only at startup (and when validating the
auth method), and never overwrites a variable already in its environment,
so a running session keeps the old key, endpoint and model after a
switch. The switch toast now asks for a restart, as it already does for
Codex and Grok Build.
The user manual said the opposite in three places (the quick start
table, the switch page and the FAQ), claiming Gemini CLI re-reads .env on
every request; all three languages now say a restart is needed. The
README and the routing page now list Gemini CLI among the apps that show
a restart notice.
Brings the README FAQ (English, Japanese, German) and the English and
Japanese user manuals in line with the Chinese master text.
- README FAQ: switching no longer touches plugins, hooks and other shared
settings; a model changed in the client resets on switching back;
routing keeps the direct provider and writes it back on exit; the
device-local files; the restart note now covers Gemini CLI.
- Manual: the config editor section replaces backfill; the add dialog
follows the same rules; quick toggles are marked global or per
provider; the keys each app's switch changes; config ownership,
malformed files and the first-write backup; routing writes back the
direct provider; the Codex login is restored when switching back to an
official card; Gemini auth follows the provider type.
- The routing feature bullet and the quick start still said only Codex
and Grok Build may need a restart when a switch changes the model;
Gemini CLI does too, as the FAQ already says.
- The routing page still said routing backs up the original config and
restores it; disabling routing now writes back the direct provider,
and the only backup is the one taken before the first write.
Chinese master text for the new switching behavior; translations follow
once it is final.
- README FAQ: switching no longer touches plugins, hooks and other shared
settings (replaces the common-config snippet answer); a model changed
in the client resets on switching back; routing keeps the direct
provider and writes it back on exit; the device-local files
(live-state.json, the Codex login stash, first-write backups) and the
restart note now covering Gemini CLI.
- Manual: the editor section (what it shows, global vs per-provider
fields, edit conflicts, inactive fields) replaces backfill; the add
dialog follows the same rules; quick toggles are marked global or
per-provider; the keys each app's switch changes; the config ownership
and malformed-file sections; routing writes back the direct provider;
the Codex login is restored when switching back to an official card;
Gemini auth follows the provider type.
The add dialog for these apps now follows the same rules as the editor
and Claude Code's add dialog. Picking a preset (or the custom template)
projects it onto the current client files, so the editor shows what the
files will look like after switching to the new provider. On save, key
fields and provider-exclusive fields go to the row and other edits go to
live as global settings with a three-way check. Before, anything typed
there besides key fields was stored in the row and never took effect.
- Adding and editing share one backend path per app. When the new
provider is the first one, its key fields and the direct pointer are
committed in the same write; the Gemini and Grok editor writers take a
set_pointer flag for this, as Codex's already did.
- The form reports each projection to the dialog as the three-way base.
Only the latest request counts; while a projection is pending or after
it fails, the add saves the row only. Every reset of the shown content
re-projects or clears the base, otherwise global settings missing from
the editor would be written to live as deletions. Grok's official card
has no editor and keeps its seed flow.
- Remove six unused common-config strings and rewrite the empty-state
hint that still described the snippet; it now explains key-field
switching and also shows for Grok Build.
Switching, adding, editing, sync and entering or leaving local routing now
write Gemini CLI and Grok Build through the write engine. Whole-file
rewrites, backfill before switching and snippet merging are gone.
Gemini CLI:
- Only the key-field lines in .env change; comments, order and the user's
own variables stay. In settings.json only security.auth.selectedType and
model.name change. Both files commit as one operation.
- selectedType follows the provider type (Google login for official cards,
API key otherwise) instead of the row, so a /auth choice an older version
stored in a row can no longer send a third-party card through Google
login.
- Local routing writes the route's model name along with the local address
and placeholder key, so switching to a route with another model rewrites
the client, as Claude Code does.
- Vertex rows pass validation with GOOGLE_API_KEY or
GOOGLE_GENAI_USE_VERTEXAI instead of GEMINI_API_KEY.
- The common-config snippet is frozen like Claude's and Codex's; its UI and
the explainer dialog are removed. New rows keep commonConfigEnabled: true
for older versions that sync them.
Grok Build:
- Only models.default and the [model."<name>"] table CC Switch wrote
change; MCP servers and the user's other tables stay.
- The written table name is recorded in live-state.json in the same
operation, and switching away removes the recorded table rather than
whatever models.default points to now, so a default changed in Grok's
/settings no longer leaves the old provider's table behind. Without a
record (just upgraded), the table is inferred from the current row.
Tables holding the proxy placeholder key are always removed.
- Local routing writes the route's table pointing at the local
/grokbuild/v1 endpoint with the placeholder key through the engine. An
official card cannot be a route.
Editors:
- Both editors show what the client files will look like after switching.
On save, key fields go to the row and other edits go to live as global
settings with a three-way check. Gemini row fields that never reach live
can be added to the global settings; Grok's can be copied, as in Codex.
The TOML diffing shared with Codex moves to editor_toml.rs.
Also:
- First-run import no longer moves key fields (GEMINI_MODEL, Codex
model_reasoning_effort and the like) from the default row into the
snippet, where a frozen snippet would lose them.
- The .env patch can remove keys unconditionally and parse entries.
Switching, adding, editing, sync, universal providers and entering or
leaving local routing now write Codex through the write engine. Only
routing, credential, model, reasoning, context-window and compatibility
fields are replaced; MCP servers, [projects], comments and the user's own
provider tables stay byte-for-byte. auth.json, config.toml, the model
catalog, the managed-account marker and a device-local login stash commit
as one operation, so a crash at any point rolls forward on startup.
- Third-party routes are always written as [model_providers.custom] with
the key in experimental_bearer_token. Rows that use another id, a
top-level openai_base_url or a legacy reserved table are normalized on
the way out. Switching to an official card leaves a dormant custom table
(local proxy address plus placeholder credential) so older third-party
sessions can still resume.
- requires_openai_auth follows the route's auth and whether a login is on
disk; keyring and ephemeral credential stores are handled separately.
- A switch is refused before anything is written when the active profile
overrides the route, the selected table is missing or built in, a
keyless row would fall back to the official login, or the row has no
auth.
- Provider tables an older version wrote are removed only when they can
be proven to be CC Switch's. Tables a profile references are kept, and
reserved ids that cannot be proven are renamed.
- Provider-exclusive fields, including web_search and a row's own
model_catalog_json, are removed on the way out only when live still
holds the previous provider's value.
- No more backfill. A native ChatGPT login that CC Switch deletes or
overwrites is kept in ~/.cc-switch/codex-login-stash.json (0600, not
synced) and restored when switching back to an official card; logging
out on an official card discards it. Login snapshots already in
official rows seed the stash once. A leftover API key in auth.json is
deleted only when it matches a third-party row's key.
- Managed accounts fetch their token before taking the lock. If the CLI
refreshes the login during a switch, the switch stops before writing
anything. Once files are published, a later failure no longer rolls the
row back; startup finishes the switch.
- Local routing: third-party routes get a fixed local-proxy table and the
official route uses cc-switch-official plus the client's own login. The
contract covers every field written, so a route switch with the same
contract touches no files.
- The Codex editor shows what config.toml will look like after switching.
On save, key fields go to the row and other edits go to live as global
settings with a three-way check. Row fields that never reach live are
listed read-only and can be copied.
- The Codex common-config snippet is frozen like Claude's; its UI and the
TOML snippet command are removed. New Codex rows keep
commonConfigEnabled: true for older versions that sync them.
- The write engine can delete a file as part of an operation.
- Integration-test mutexes tolerate poisoning so one failure no longer
cascades into unrelated tests.
Local routing no longer takes over a client by backing up its live config
and restoring it later. Each routable app (Claude Code, Codex, Gemini CLI,
Grok Build) now has a device-local mode in live-state.json: direct or
proxy, whether it is attached, the proxy route, and the contract written
into the client. proxy_config.enabled is kept only as a mirror for older
versions.
- Entering proxy mode writes the route's contract (local proxy address,
placeholder credential, model aliases, provider-exclusive fields) into
the client; leaving re-projects the direct provider. No backups.
- The proxy route is independent of the direct provider. Switching while
routing changes only the route; if the contract is unchanged the client
file is neither read nor written. Failover records the new route and
never touches client files.
- Current-provider reads go through mode::current::provider_for with an
explicit purpose (direct pointer vs. the provider in use); a unit test
rejects direct calls to the old getters.
- Quitting detaches (writes the direct config back, keeps the mode);
startup re-attaches. Legacy takeover state is migrated on startup:
backup rows are saved to ~/.cc-switch/backups/proxy-live-backup/ and
deleted so an older build cannot replay them.
- "Stop routing" returns every app to direct and then stops the server.
Applying a project no longer disables routing; it moves the direct
provider or the route depending on the mode. A provider used as the
route cannot be deleted.
- Codex and Grok Build still use their whole-file writers for proxy
writes, and all three non-Claude apps for leaving; the direct row is
backfilled from live before entering (never for the official Codex
card), and leaving to an unbound official Codex card writes config.toml
only.
- The provider list marks the direct provider while routing.
- Removes the backup/restore/hot-switch/crash-recovery paths from the
proxy service, the unused tray Auto item, and the downgrade golden test.
Switching, adding the first provider, editing the current provider,
sync, universal providers and restoring after the local router all
write settings.json through the write engine now. Only endpoint,
credential, model and protocol fields plus the provider's own switches
are replaced; hooks, plugins, permissions and everything else stay
byte-for-byte. The file write and the current-provider pointer commit
as one operation, so a crash in between rolls forward on startup.
- No more backfill, no common-config snippet and no context-window
injection for Claude. Window values left behind by the old injection
are cleaned up from a frozen list.
- The provider editor shows what settings.json will look like after
switching. On save, provider fields go back to the row and other
edits go to live as global settings, with a three-way check against
external changes and a dialog to resolve conflicts. Row fields that
never reach live are listed with a one-click add.
- New Claude rows keep commonConfigEnabled: true so older versions that
sync the row still merge their snippet.
- Presets drop timeout and telemetry keys; a test now requires Claude
presets to contain only key or provider-owned fields.
Client config files are now changed through one engine instead of being
rebuilt from provider snapshots:
- Key-field definitions live in live/floor.rs and are shared with the
common-config extraction.
- JSON, TOML and dotenv patchers change only the keys they name. Other
keys, values, order, comments, indentation, line endings and BOM stay as
they are, and writing the same target twice is byte-stable.
- A file that cannot be parsed is refused and left untouched; it is never
rebuilt from an empty document.
- Each write is staged next to the target, re-read and compared before the
rename, and replanned on top of the new content when another writer got
there first. The first engine write of every file keeps a byte-for-byte
backup under ~/.cc-switch/backups/live-first-write/.
- Multi-file operations record their intent in ~/.cc-switch/live-state.json
(0600) before publishing. After a crash the operation is rolled forward
from the staged files, discarded if nothing was published, or abandoned
if the files were changed externally. Recovery runs at startup and before
every operation.
Claude Desktop is the first client moved onto the engine. Its four files
are written in one operation; the gateway profile keeps the user's own
settings, seeds disableDeploymentModeChooser and coworkEgressAllowedHosts
only when missing, and is written owner-only. Restoring the official app
now clears only the CC Switch key fields instead of deleting the profile,
and a meta file that cannot be parsed is refused instead of replaced.
PRAGMA incremental_vacuum frees one page per result row, and the pages
are only released while the statement is being stepped. execute_batch
steps it once, so startup cleanup and periodic maintenance reclaimed a
single page each time and the database file never shrank after pruning.
Add Database::incremental_vacuum_on_conn, which drains every result
row, and use it in both places.
Relays already price in "official price x their own multiplier", and the
real spend is visible from the relay's own balance or usage script. A
second copy of that multiplier in CC Switch only drifts out of sync, and
it only ever applied to requests going through local routing (session
log imports always recorded 1.0), so users setting it in direct mode saw
no effect at all.
- Remove the pricing section from the provider editor (it was also shown
for Pi and MiniMax Code, which never go through local routing).
- Remove the default multiplier column from the usage pricing defaults;
keep the global pricing model source (request vs response model) and
say it only applies to routed requests.
- Remove the get/set_default_cost_multiplier commands and DAO methods.
- Routed requests are now always recorded at multiplier 1. Existing log
rows keep the multiplier they were recorded with.
- Keep costMultiplier / pricingModelSource in ProviderMeta, unread, so
older devices syncing the same rows do not lose their values; the
proxy_config column stays as well, so no schema migration is needed.
The upcoming refactor replaces whole-file live writes with key-field
replacement and splits direct and proxy modes. These tests pin what must
survive it, and pass on the current code.
Backend (`cargo test --test golden`, snapshots under
src-tauri/tests/golden/snapshots, refresh with CC_SWITCH_UPDATE_GOLDEN=1):
- mcp_bytes: MCP projection bytes in ~/.claude.json, Codex config.toml
and Gemini settings.json, and that switching providers leaves the MCP
section untouched.
- import_rows: raw DB rows written by deep-link import and first-run
import (including seeded official rows and the generated snippet).
- file_modes (Unix): which client files a switch writes and their
permission bits; existing 0600 files are never loosened.
- claude_key_fields: after a switch, live key fields are exactly the
target provider's, across third-party, Bedrock, Vertex, apiKeyHelper
and official providers, with or without common config.
- codex_red_lines: 25 Codex invariants (index at the top of the file),
asserted as outcomes: the config loads on Codex 0.149, the official
login never reaches a third-party URL, refused switches leave files,
current provider and rows untouched.
- downgrade_contract: the previous release detects takeover by the
PROXY_MANAGED literal, rebuilds live from current rows without backup
rows, and replays leftover backup rows.
Frontend: ProviderForm submit payloads for ten representative Claude and
Codex presets.
Three spots are sorted before comparing because their output order is
not deterministic today (MCP maps written from a HashMap, Gemini .env
parsed into a HashMap). One ignored test records a known gap: exiting
the proxy restores a login the user logged out of while on a
third-party Codex route.
Common config snippet (Claude Code):
- Treat custom request headers (`*_CUSTOM_HEADERS`, `HEADERS`) and any
key containing COOKIE or AUTHORIZATION as credentials. Header names in
general stay shareable (CLAUDE_CODE_ATTRIBUTION_HEADER,
OTEL_EXPORTER_OTLP_HEADERS).
- Never share key fields: all ANTHROPIC_* settings, the protocol
selectors (CLAUDE_CODE_USE_BEDROCK/VERTEX/...), AWS_* and Vertex
regions, and top-level model/apiKeyHelper/fallbackModel. Switching
from Bedrock to the official provider no longer keeps Claude Code on
Bedrock, and a /model choice no longer follows every switch.
- Entries an existing snippet already shared are stripped from the
current provider's row on its next switch-away, so they cannot get
stuck in the wrong provider. A provider carrying AWS or Google
credentials keeps that family's selectors and regions.
Live files holding keys are now written owner-only (0600): Codex
auth.json and config.toml, Claude Code settings.json and Grok Build
config.toml. New files used to follow the umask (0644).
Presets:
- E-FlowCode and PIPELLM no longer ship personal preferences; Codex
E-FlowCode drops `personality`.
- AWS Bedrock (API Key) stores the key in env.AWS_BEARER_TOKEN_BEDROCK,
which Claude Code reads. The key field and the proxy read it too;
existing rows with a top-level apiKey keep working and are edited in
place.
- A preset scan keeps preference keys out of Claude and Codex presets.
Remove dead code only reached from tests: ProxyService::start_with_takeover
and its helpers, and the ConfigService sync_current_providers_to_live
family.
Behavior changes: ANTHROPIC_* settings such as ANTHROPIC_LOG are no
longer shared; a Bedrock or Vertex provider whose selectors only lived in
the shared snippet may need them re-entered once.
Grok Build's /settings "Default model" rewrites models.default in
~/.grok/config.toml. Selecting a built-in model while a third-party
provider was active left the provider's own [model.*] table
unreferenced; the switch backfill stored that config verbatim, and
switching back then failed validation with "missing [model.\"...\"]".
When models.default is missing or dangling during backfill and the
provider's original default table is still present, restore it.
- Cover Grok Build, Pi and MiniMax Code across install, interface,
settings, presets, MCP, Skills, prompts, sessions, local routing,
config files and env conflict pages
- Rename proxy/takeover wording to local routing and update settings
paths to the Routing / Auth / Usage Statistics tabs
- Rewrite 4.5 as the connectivity check that replaced model test in
v3.16.3 (no real model request, no test model to configure)
- Fix stale facts: official subscription quota is opt-in via the
Official Subscription template since v3.16.2, Codex keys live only in
config.toml experimental_bearer_token, deep link content/config must
be Base64, SQL export includes usage logs, onboarding skip writes
hasCompletedOnboarding in ~/.claude.json
- Backfill the website-only Codex preset section (native Responses vs
Chat Completions) and align UI labels with the v3.20.4 locales
- Retranslate en/ja from the approved zh master, keeping all three
languages line-aligned with matching anchors
- Backfill website-only edits: Kimi direct-connection section, desktop FAQ cross-links, stray </content>
- Retitle the DeepSeek guide around Chat-format providers; v4-pro, Kimi, GLM and MiniMax now connect directly
- Rename the toggle to "Keep official login for direct switches"; document auth.json removal and takeover preservation
- Prefill gpt-5.6-sol, mention the Kimi Global presets, and use routing takeover terminology
- Remove the outdated, unreferenced proxy-guide-zh.md
- Reorder sections (download and quick start before features), add a per-tool
capability table, and document local routing, projects, the OAuth auth
center (with a ToS risk note), quota display, usage without routing,
S3 sync, and the CLI tool manager
- Rewrite the data-storage FAQ; add FAQs for routing addresses, OpenAI/Gemini
upstreams, connectivity checks, WSL, and the community CLI
- Fix first-launch import (Claude Desktop is not imported), Pi's Enable
button, and the non-existent Markdown preview; unify on "local routing"
- Move architecture/dev guide/project structure into CONTRIBUTING.md and
refresh it (Node/pnpm/Rust versions, CI-aligned checks, test isolation,
four locales, no ESLint/pnpm lint)
- Add Pi (10 managed apps), scope tray/MCP/Skills per app, document additive apps
- Fix "signature bypass" wording (Hide AI Attribution), common-config FAQ labels,
official-login/restart FAQs, and first-launch auto-import note
- Correct Linux requirements (glibc 2.35 + WebKitGTK 4.1) and release asset names
- Update dev env (Node 20.19+/22.12+, pnpm 10, Rust 1.95), drop broken coverage cmd
- Point release notes to v3.20.4 and SSSAiCode to sssaicodeapi.com
- JA: replace the stale unsigned-macOS instructions; fix Chinese FAQ anchors in
SUPPORT.md and issue templates
Updating a CLI installed outside npm fell back to a bare `npm i -g`, which
silently installed a second, npm-managed copy instead of upgrading the one
the command line actually runs.
- Codex standalone installer (install.sh / install.ps1): rerun the official
installer with CODEX_INSTALL_DIR / CODEX_HOME pinned to the detected
install. `codex update` is not used: it pipes `curl | sh` without
pipefail and reports success even when the download fails.
- Keep the npm self-heal for broken Codex installs away from standalone
installs whose entry lives in a Homebrew/nvm bin directory.
- When the default install is a native binary outside node_modules and its
channel is unknown (winget, Scoop, Nix, manual download), stop falling
back to npm: the probe reports it as unmanaged, the About page skips it
with a localized hint, and the backend refuses to run it.
- Add a Claude Code cask regression test on top of the Homebrew cask
support from #6572.
Fixes#7650
MiniMax Code 0.5.x reads a whitelist of `models.<id>.compat` keys from
config.yaml and layers them over the compat it synthesizes, so Pi's
per-model compat can be reused instead of dropping every preset that
carries one.
- Keep a model when every compat key is one MiniMax Code reads, and write
the compat verbatim. kimi-k3 stays out: `deferredToolsMode` is unknown
to MiniMax Code's embedded pi-ai.
- Map adaptive-thinking Claude models to `thinking.effortOptions`, since
MiniMax Code ignores Pi's thinkingLevelMap and compat-blind versions
would otherwise fall back to budget_tokens.
- Presets grow from 40 to 77; only AWS Bedrock remains excluded.
MiniMax Code intentionally has no "Import Current Config" button (same as
Pi): custom providers in config.yaml are merged into the list on every
load, and the official MiniMax account provider plus its sign-in state
are owned by MiniMax Code, which CC Switch refuses to write. The empty
state still showed the generic copy asking users to click that button.
Give mcode its own empty-state title and description in all four
locales, route Pi and mcode through their own namespaces in
ProviderEmptyState, and add a regression test.
Add MiniMax Code (mcode) to the About page tool list: version probing,
latest version from the npm registry (@minimax-ai/code), and silent
install/update actions.
- Install runs the official installer (install.sh / install.ps1) with an
npm fallback. npm installs pass --ignore-scripts=false,
--include=optional and --allow-scripts for @minimax-ai/code and
better-sqlite3, since npm 12 blocks dependency install scripts by
default and SQLite breaks without them.
- `mcode update` is deliberately not used: without a TTY it only prints
the command and exits 0 without installing, which would mask failures
in silent lifecycle runs.
- Script installs are updated in place by re-running the official
installer with MCODE_INSTALL_DIR set to the detected root. Custom
install dirs are claimed via the installer's install.json receipt
(UTF-8 BOM tolerated, as Windows PowerShell 5.1 writes one), falling
back to the default .minimax-code directory name. The Windows layout
keeps mcode.cmd at the install root, so only the POSIX layout strips
a bin segment.
- npm global installs anchor to the sibling npm with the same flags.
- Search the installer launcher dirs ($MCODE_INSTALL_DIR,
~/.minimax-code) ahead of generic Node locations.
- Update the one-click install hint in all four locales, also adding the
previously missing Grok Build.
Colored brand icons (Claude, Gemini, OpenCode, OpenClaw, Hermes,
MiniMax Code) kept their full color when inactive, so only the pill
background distinguished selected vs unselected. Monochrome icons
(Codex, Grok Build, Pi) already dimmed via currentColor.
Apply grayscale + reduced opacity to inactive colored icons, restoring
full color on hover or when active.
Both confirmed against the vendors' official pricing pages (docs.x.ai,
docs.z.ai) and match models.dev. Grok 4.7 records the base (<200K) tier
like 4.5/4.6.
Downloads and updates now go through ccswitch.io, so users no longer
pass through the GitHub repo and rarely star it. Surface the ask where
they still look: between the app title and the action buttons.
- Show a one-line prompt in the primary color that opens the repo,
followed by a pointer toward the GitHub button
- Move the GitHub button to the front of the row so the pointer lands
on it, tint it with the primary color (kept below the solid "check
for updates" action), and add a slowly spinning star that stops
under prefers-reduced-motion
- Add settings.starPrompt to all four locales
Add non-sponsor presets for Soshow Model Market (maas.so-show.com) across
Claude Code, Claude Desktop, Codex, OpenCode, OpenClaw, Hermes and Pi, plus
its icon.
- Claude Code sets CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, which the vendor's
Model API doc requires for the Claude protocol, and points the Haiku tier at
claude-sonnet-5 since the market offers no Haiku model.
- Claude Desktop declares only the sonnet/opus routes: direct mode cannot map
models, so passthroughRoutes() would expose a Haiku route that 404s.
- Codex uses native Responses at /v1. The route is not in the vendor docs but
exists (unknown paths return 404, /v1/responses returns 401 without a key).
- OpenCode, OpenClaw, Hermes and Pi use the Anthropic protocol with the two
Claude models, matching the vendor's OpenClaw guide.
- Gemini CLI is skipped: the native generateContent routes return 404.
The FluxA sponsor row only linked from the banner image. Append the same
"via this link" call to action the other sponsor rows use, pointing at
the TokenPlan marketplace page, and collapse the blank line between the
two paragraphs into a single line break to save vertical space.
The DeepSeek V4 family has a 1M context window - every Codex catalog in
this 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. The [1m] suffix is a Claude Desktop local
marker: it is translated to the supports1m field at the import boundary
and stripped before upstream matching, so turning it on does not change
what is sent to the provider.
Affects Baidu Qianfan Token Plan, DeepSeek and OpenCode Go. PPIO already
had it on.
FluxA AgentMarket resells Baidu AI Cloud's international Token Plan at
partner pricing. The key it issues is used directly against Baidu's
overseas endpoint, which is a separate deployment from the existing
domestic personal plan on qianfan.baidubce.com.
- Claude and Claude Desktop use the Anthropic-compatible base
api.baiduqianfan.ai/anthropic/tokenplan/team; Codex, OpenCode,
OpenClaw, Hermes and Pi use the OpenAI-compatible
api.baiduqianfan.ai/v2/tokenplan/team.
- The model lineup follows FluxA's product page table, excluding the two
entries it marks "Coming soon". kimi-k2.6 is added on top because the
sponsor copy names Kimi; its id and 262144 context window come from
FluxA's own baidu-ai-cloud catalog and the domestic Token Plan preset.
- glm-5.2 is pinned to the 500k window FluxA publishes for this
international deployment, not the 1M the domestic platform reports.
- DeepSeek V4 rows are declared text-only, matching the other hosted
Token Plan presets, and the preset joins the existing regression guard
that keeps those rows from failing open to image input.
- Registers the fluxa icon, the partner promotion string in all four
locales, and the sponsor row in all four READMEs.
The AICodeWith site owner confirmed the Responses endpoint is
https://api.aicodewith.ai/v1. The /chatgpt/v1 path published in their
blog posts is a documentation error.
Update the Codex preset (base_url and endpointCandidates) and the
Hermes, OpenClaw and Pi presets, which reuse the same Responses
endpoint.
Existing providers created from the old preset keep the stored URL and
need to be edited manually.
Icons that paint with currentColor inherited 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. The mismatch hit all 28 monochrome icons and was most visible on
9527CODE, whose 0.82-opacity base layer lightened the gray further.
Unselected preset icons now take text-foreground; selected ones still
inherit the button's text-white. The universal preset row gets the same
treatment. Brand-colored icons are unaffected because ProviderIcon sets
their color inline.
Baidu Qianfan and StepFun now ship native Responses presets for their
pay-as-you-go APIs, so the Chat-only examples name the offerings that
still route through Chat: Baidu Qianfan Coding Plan and StepFun Step
Plan. Synced across the zh/en/ja manuals.
- deepseek-v4-pro returns to its peak list price 1.32/3.96/0.044. The
2026-09-11 move to the V4.1 Flash price anticipated a cutover that
DeepSeek retracted on 2026-09-12. Replace that repair entry with one
that restores rows still at 0.3/1.2/0.006; keeping both would rewrite
the row back and forth on every start.
- Seed hy4-preview (CNY 6/18/0.3 at 7.14), qwen3.8-2.4t-a95b and
qwen3.8-27b (Alibaba Cloud international list prices).
- MiniMax CN: the remaining apps switch from api.minimaxi.com to
api.minimax.cn (Anthropic root /anthropic). Usage detection recognizes
both hosts and now matches on host labels (frontend, and the global
host in the backend), so look-alike hosts and URLs that only carry the
domain in their path are ignored. The quota query keeps
api.minimaxi.com, the only CN host documented for that endpoint.
- BaiLing: the remaining apps switch from api.tbox.cn to
api.ant-ling.com (/v1 and /anthropic) and default to Ling-2.6-1T,
since Ling-2.5-1T is no longer listed in the current docs. Ling-2.6-1T
is registered as text-only (vision ships as separate -VL models).
- Reword the DeepSeek V4 Pro note in the text-only registry: the vendor
keeps serving V4 Pro after September 14, 2026.
- Kimi For Coding: kimi-for-coding now serves K2.8 Preview (up to 1M,
text+image, low/high/max); the highspeed variant stays at 256K.
- DeepSeek: default to deepseek-flash (V4.1 Flash, text+image) and
declare deepseek-v4-pro text-only, matching the bundled official
catalog.
- Add native Responses presets for StepFun API (CN/intl,
step-3.7-flash), Baidu Qianfan pay-as-you-go (/v2) and iFlytek Astron
Coding Plan; subscription plans on the same hosts stay on Chat.
- Add hy4-preview to Tencent Hunyuan, and the open-weight
qwen3.8-2.4t-a95b / qwen3.8-27b to both pay-as-you-go Qwen platforms.
- MiniMax CN moves to api.minimax.cn and BaiLing to api.ant-ling.com.
- Disable Codex web_search on StepFun (function tools only), and
conservatively on Qianfan and Astron, whose Responses docs document no
hosted tools. The new hosts stay out of CODEX_NATIVE_RESPONSES_HOSTS
so Chat plans on the same hosts are never reclassified as native.
- Tencent Enterprise Token Plans stay on Chat: /plan/v3/responses is
listed, but TokenHub marks Kimi K2.7 Code unsupported and several
models as server-side Chat conversions, and the catalog levels were
verified on Chat only.
Follow-up to #7331 (Claude Desktop 3P configuration on Linux):
- Compile the Linux config-dir resolver and its three unit tests under
any unix test build so they run on macOS as well as CI ubuntu. The
gate is unix rather than test because the assertions use POSIX
absolute paths, which are not absolute on Windows.
- Open the provider -> Claude Desktop profile sync integration tests to
Linux: add the Linux profile path helper and pin XDG_CONFIG_HOME under
the temporary home (saved on construction, restored on drop).
- Update the support-scope table in the en/zh/ja user manuals to list
Linux and note that the Flatpak build always uses the host ~/.config.
A disabled React Query observer still returns the existing cache entry for
the same key, so a provider switched to the official subscription template
(or with its usage script disabled) kept showing the multi-plan chevron and
the expanded tiers left over from a previous script result.
Gate hasMultiplePlans with the same predicate that enables the usage query,
extracted as scriptUsageActive so the two cannot drift apart again.
DeepSeek retired V4 Flash and announced that V4 Pro is routed to V4.1
Flash and billed at the Flash price from 2026-09-14 12:00 (Beijing).
Executing that callback early per maintainer decision.
- Seed `deepseek-flash` (the only currently recommended id) and
`deepseek-v4-flash-vision-exp` so the `LIKE '{id}-%'` prefix fallback
cannot silently bill them at zero.
- Reprice `deepseek-v4-flash` / `-0731` / `-pro` to 0.3 / 1.2 / 0.006
with chained repair guards ordered after the 2026-08-16 peak/off-peak
rows; `deepseek-chat` / `deepseek-reasoner` stay untouched for lack of
an authoritative source.
- Drop `deepseek-v4-pro` from the confirmed text-only registry: it now
lands on a vision-capable model, so the image sanitizer must fail open.
Fixtures that relied on it being text-only switch to `qwen3-coder-plus`,
including the two media_sanitizer tests #7286 had just pointed at it.
DeepSeek's official endpoint now routes the legacy deepseek-v4-flash id to
the vision-capable V4.1 Flash, so #7286 removed it from the confirmed
text-only registry and the id fails open everywhere. Baidu Qianfan and
Tencent Token Plan still host the text-only V4 deployments (Qianfan's
Coding Plan doc states image input returns 400; Tencent's 2026-09-10 plan
rosters list V4 only), so their preset rows must carry an explicit
inputModalities: ["text"] declaration instead of relying on the registry.
Covers the flash and pro families incl. -0731/-0813/-202605/-202606 rows,
and adds a preset test that also locks the official DeepSeek preset to
stay undeclared (fail-open).
Follow-up to #7283.
Rename the DouBaoSeed provider preset across all seven app preset files
and localize its display name: zh/zh-TW show "火山 豆包AI", en/ja show
"Volcengine Doubao" via the new providerForm.presets.doubaoseed key.
Following the existing Qiniu/Compshare convention, the top-level name
holds the latin fallback (so the preset stays searchable in both
scripts) and the localized name lives in the locale files. OpenCode and
Pi settingsConfig.name, which is written into the client config files,
keeps the ASCII form.
Live-config identifiers are deliberately untouched: the Codex TOML
provider name, the Hermes node key doubao_seed, Pi's providerKey, the
OpenClaw model-ref prefix, partnerPromotionKey and icon.
Follow-up to #7219. The parser counted only fully consumed records toward
the persisted byte length, so a rollout that ends mid-record or in
whitespace (a crashed session that was never resumed) always looked
shorter than its file size and was fully reparsed on every sync pass.
Count the bytes of the incomplete tail as observed too. The line cursor
still stops before the incomplete record, so it is retried as soon as the
file grows, while an unchanged file is skipped on the mtime+size gate.
Tests prove the skip via total_changes() rather than a zero import count.
Since #6537 the tray deliberately dropped the usage suffix for Codex
official cards bound to a managed ChatGPT account: the tray only had an
app-wide subscription cache fed by whatever login the Codex CLI holds
(macOS Keychain first, then auth.json), which cannot represent
account-scoped quota safely once several accounts are bound. The provider
card queried the bound account through `get_codex_oauth_quota`, but that
result never reached the tray, so users with multiple ChatGPT logins saw
`Codex · <name>` with no quota at all.
- UsageCache: add an account-keyed `codex_oauth` snapshot map
(`put_codex_oauth` / `with_codex_oauth`), cleared by `invalidate_all`.
- `get_codex_oauth_quota`: write the `Ok` snapshot through to the cache
under the resolved account id and schedule a tray label refresh.
Transport errors keep the last good snapshot; auth/HTTP failures replace
it so the tray hides an invalid quota instead of a stale one.
- Tray: introduce `TrayUsageSource` so the refresh path and the display
path share one decision. Managed Codex cards read only their bound
account's snapshot (never a provider-scoped or CLI snapshot as fallback),
default to enabled when no usage toggle has been saved (matching
ProviderCard), and the fixed official card takes the managed path too
when it carries a binding. Rebinding a card switches to the new
account's snapshot immediately and a late response for the previous
account cannot overwrite the label.
- Tests: source selection across binding/toggle/legacy shapes, label
rendering across switch, rebind, late response, disabled and expired
states, and the native fixed card still using the CLI subscription.
Fixes#7267
Some third-party Anthropic-compatible gateways (e.g. DeepSeek) validate
tool JSON Schemas with a strict regex checker and reject the Unicode
property escapes (\p{Cc}...) that Claude Code's Artifact tool emits when
its str_replace db capability is rolled out, failing every request with
400 "Invalid schema for function 'Artifact'". The failure is independent
of the model selected.
Add a sixth checkbox next to the existing Claude quick toggles that sets
env.CLAUDE_CODE_DISABLE_ARTIFACT="1" on the provider, which keeps the
Artifact tool out of the tools array entirely. Unchecking removes the key
like the other toggles do. Labels added for zh/en/ja/zh-TW and the user
manual toggle table updated in zh/en/ja.
Both Kimi open platform (api.moonshot.cn/v1) and Kimi For Coding
(api.kimi.com/coding/v1) now serve /v1/responses natively and their
official Codex guides require wire_api = "responses", so the two presets
move from openai_chat (proxy Responses->Chat conversion, routing takeover
required) to openai_responses (Codex connects directly). Verified against
both endpoints with real keys using the exact Codex 0.153.4 request shape
(include reasoning.encrypted_content, reasoning.summary, hosted web_search,
replayed reasoning items) and with codex exec end-to-end tool loops.
- kimi-k3 becomes the default open-platform model and first catalog row;
kimi-k2.7-code stays as a second row (it also answers on /v1/responses,
although the OpenAPI only lists kimi-k3)
- drop codexChatReasoning (Chat-conversion only) from both presets and
promptCacheRouting from Kimi For Coding (Codex sends prompt_cache_key
itself on the native path; the proxy auto-detects the host anyway)
- declare supportsParallelToolCalls on every Kimi row, mirroring the
official Kimi Code models.json
- no reasoning default is declared: config.toml model_reasoning_effort
wins over the catalog default, which only marks the /model picker
- move the two presets from the Chat list to the native list in the
preset tests; invert the prompt-cache-routing assertion
- refresh the user manual (zh/en/ja) so the "Chat-only" examples name
providers that still use Chat routing, and correct the stale
userAgentPresets note about Kimi Code rejecting codex-cli
No backend change. Existing Kimi cards keep their stored openai_chat
format and continue to work through routing; switch a card's upstream
format to Responses (or re-add the preset) to connect directly.
The Claude OAuth usage API now reports model-scoped weekly limits in a
top-level `limits[]` array (`kind: "weekly_scoped"`, `scope.model.display_name`,
`percent`) instead of dedicated top-level windows. The parser only read the
legacy windows, so the Fable limit never showed up in the provider card or
the tray.
- Extract `parse_claude_quota` and parse `limits[]` into `seven_day_fable`,
`seven_day_opus` and `seven_day_sonnet`; scoped rows override the legacy
window of the same name, duplicates and malformed rows are skipped, and
legacy windows / extra usage / unknown windows are preserved.
- Tray: give Fable its own label group so it is not merged into the weekly
max value.
- Frontend: add the `seven_day_fable` label ("Fable") in all four locales.
- Tests: parser edge cases, tray summary for both quota and flattened script
paths, and footer rendering for inline/expanded layouts and locales.
Atlas Cloud is no longer a sponsor. Drop the sponsor identity everywhere
while keeping the provider presets themselves intact:
- README (en/zh/ja/de): remove the Atlas Cloud row from the sponsor table
and delete the now-unreferenced banner image
- Presets (Claude Code, Claude Desktop, Codex, OpenCode, OpenClaw, Hermes,
Pi): strip `isPartner` / `partnerPromotionKey` from the AtlasCloud entry
and move it from the sponsor group to the non-sponsor group (endpoints,
models and icon unchanged)
- i18n (zh/en/ja/zh-TW): remove `providerForm.partnerPromotion.atlascloud`
SoleAPI (soleapi.com) joins as a sponsor, placed directly above Micu in
the sponsor order (preset files and the four README sponsor tables stay
in sync).
- Presets for Claude Code, Claude Desktop, Codex, Gemini CLI, Grok
Build, OpenCode, OpenClaw, Hermes and Pi. Endpoints follow the
vendor's CC Switch setup page: Anthropic/Gemini-style clients use the
bare host, OpenAI-style clients (Codex, Grok, Responses) use /v1.
- Haiku's upstream ID carries a date suffix
(claude-haiku-4-5-20251001), so the Desktop preset uses mappedRoutes
and the multi-model presets list the dated ID.
- Codex defaults to gpt-5.6-sol (vendor config.toml), Gemini to
gemini-3.8-flash.
- No OpenClaw/Hermes cost fields: site prices are official rates times
a per-channel multiplier and change with promotions.
- Referral link only in apiKeyUrl and README hrefs; request endpoints
stay bare.
- Inline currentColor icon traced from the vendor SVG, metadata entry,
four-locale partnerPromotion copy, and a 1920x786 banner.
Codex's built-in ImageGen switches from `/images/generations` to
`/images/edits` whenever it references existing images (explicit paths or
the last N generated images). After #7036 the generations route is proxied,
but edits still hit an empty local route and the tool fails with 404.
Add `CodexStandaloneEndpoint::ImagesEdits` and route the four local aliases
through the same standalone passthrough as generations. The full-URL sibling
derivation now uses a shared suffix table for the Images family, so a
provider configured with either `/images/generations` or `/images/edits` as
a complete URL derives the other route; opaque URLs still fail closed.
Tests cover the effective-endpoint match, full-URL rewrites in both
directions, and an end-to-end alias test that asserts auth replacement,
body preservation (data-URL images) and usage logging with the real Images
API `input_tokens_details` shape.
Both serve the Anthropic-compatible API under /anthropic but expose the
OpenAI-compatible model list under /openai/v1. The candidates derived
from the base URL (/anthropic/v1/models, and /v1/models and /models at
the stripped root) all return 404, so "Fetch models" could never
succeed for either provider.
Also drop the JieKou assertion that pinned the absent modelsUrl, which
locked the broken behaviour in.
Audit run of /jason-update-model against models.dev, every change confirmed
on the vendor's own pricing page.
New row:
- qwen3.8-flash: 0.15 / 0.47 flat across the 1M window, cache 0.016 / 0.20.
The other two rows this audit found have since landed through contributed
PRs, so only the annotations remain here:
- gpt-6-astra came in via #7162; this commit keeps its literal "12.5" and
adds the note that low/medium/high/xhigh fall back to the base row.
- gemini-3.8-flash came in via #7164; this commit adds the note that
0.75 / 3.75 / 0.075 is introductory through 2026-12-31 and reverts to
the 1.50 / 7.50 / 0.15 list price on 2027-01-01.
Price changes (seed + repair, guarded on the previous values):
- gemini-3.6-flash: Google now lists the same 0.75 / 3.75 / 0.075
introductory price through 2026-12-31.
- gpt-5.6-sol plus the bare gpt-5.6 row and its five effort-suffix rows:
promotional 4 / 20 / 0.40 / 5, "available at least through November 21,
2026". The alias rows must move in lockstep because exact-id lookup
runs before suffix stripping.
- minimax-m2, minimax-m2.1, minimax-m2.5: the official pay-as-you-go
page lists all three at 0.3 / 1.2 with cache read 0.03 and write 0.375.
The new repair entries are appended after the existing chain so old
databases hop through the intermediate prices (gpt-5.6-sol cache-write
backfill, minimax-m2.5 0.12 -> 0.15). The repair-order test now locks
both chains alongside the DeepSeek one.
With official-login preservation off (the default), a direct switch to a
third-party provider deletes ~/.codex/auth.json. Enabling proxy takeover
afterwards rebuilt config.toml from the stored card, which still carried the
pre-0.149 `requires_openai_auth = true`, so Codex >= 0.149 trapped the TUI in
the login screen even though the proxy placeholder bearer token was in place.
The same gap hit hot switches after `codex logout` mid-takeover.
The takeover writer now stamps the active custom table's flag from the login
state Codex itself would observe, mirroring the direct-switch plan:
- Resolve the auth mode exactly like `AuthDotJson::resolved_mode` (explicit
`auth_mode` first, then personal_access_token > bedrock_api_key >
bedrock_access_keys > OPENAI_API_KEY > ChatGPT) before checking the
credential, so Bedrock credentials next to a stale API key are never
promoted to an OpenAI login (`account_state` would fail with
UnsupportedBedrockApiKeyAuth and abort TUI startup).
- Decide `cli_auth_credentials_store` before touching auth.json: keyring and
auto stores are undecidable from disk (the card's flag is left alone),
ephemeral is always signed out, and only the file store reads the file.
A missing, unreadable or corrupt file means signed out, as it does for
Codex, and no longer fails the takeover write.
- Proxy-injected OAuth cards (xai_oauth, github_copilot) keep the neutralized
`false` regardless of what is on disk; the official login is never their
credential.
Official passthrough and managed-official branches are untouched.
The tools panel asked PyPI for Hermes' latest version, but upstream
stopped publishing there after 0.19.0 (2026-07-20) and now ships only
through GitHub Releases. Users saw "latest 0.19.0" forever, often below
their installed version, and the upgrade button never appeared because
it only shows when latest is strictly newer than current.
PyPI was never the channel cc-switch installs or upgrades Hermes
through: install runs the official install.sh (git clone of main) and
update runs `hermes update` (git pull). Probe the same channel.
- Query GitHub Releases first and fall back to PyPI only when GitHub is
unreachable or rate-limited.
- Parse the semver from the release name ("Hermes Agent v0.21.0
(v2026.8.31)") since the tag is calendar-versioned; reject calendar
numbers on both paths so the frontend never treats 2026.8.31 as a
version and reports a permanent update.
- Hide the PyPI fallback value when the local version already leads it,
so a degraded probe shows "unknown" rather than "latest < current".
- Add a 15s request-level timeout to the GitHub and PyPI probes; the
shared proxy client allows 600s, which would stall the Hermes card
and the refresh/upgrade-all buttons when api.github.com hangs.
Refs: #6475, #6618, #7033
Supersedes: #6621
The nine 9527CODE presets pinned iconColor to #4F46E5, which came from
the docs site favicon rather than the logo itself. The vendor mark is
two-tone black (#000000 over #2D2D2D).
The fix is not to swap in #000000. ProviderIcon resolves colour as
preset iconColor -> metadata defaultColor -> undefined, and only the
undefined case leaves style.color unset, letting the inline SVG's
fill="currentColor" inherit the surrounding text colour. Any hex opts
out of theming, and a black one disappears in dark mode: the icon sits
in a bg-muted container at 18% lightness there, against a --foreground
of 98%, and nothing in the stylesheet inverts icons for dark mode.
Dropping iconColor and marking the metadata defaultColor currentColor
renders near-black on light, matching the official logo, and near-white
on dark. The traced SVG already suits this — its two tones are a
0.82-opacity base layer plus an opaque overlay, so the depth
relationship is relative and survives the inversion. Hard-coding both
tones would have foreclosed it.
This follows packycode and the 26 other currentColor icons.
9527CODE (9527.codes) is a sponsored enterprise-grade relay running New
API, fronting Claude, GPT, Gemini, Grok and Chinese models behind a
single key. It lands across Claude Code, Claude Desktop, Codex, Gemini
CLI, Grok Build, OpenCode, OpenClaw, Hermes and Pi, and is inserted
after APIKEY.FUN in both the preset files and the sponsor table so the
README order stays the ordering SSOT.
The gateway accepts every protocol with or without the /v1 prefix, but
the presets are written to match what each client actually appends
rather than relying on that fallback:
- Claude Code / Desktop / Gemini CLI: 9527.codes (client adds /v1)
- Codex / Grok Build: 9527.codes/v1 (responses)
- OpenCode: 9527.codes/v1 (@ai-sdk/anthropic)
- OpenClaw / Hermes / Pi: 9527.codes (anthropic-messages)
OpenCode and the other Anthropic-protocol clients look inconsistent but
are not: the AI SDK appends /messages to its baseURL while OpenClaw,
Hermes and Pi append /v1/messages themselves.
Three non-obvious details:
- endpointCandidates carry only the two live backups, api.9527.codes
(overseas) and cdn.9527.codes (Asia-Pacific). The vendor docs list a
fourth route, txcdn.9527.codes, which does not resolve; including it
would offer users an endpoint that cannot connect.
- No cost is recorded for the OpenClaw and Hermes models. What a user
actually pays is the official price times a token-group multiplier
that varies per group and plan, so quoting list prices would mislead.
- The icon is an inline SVG traced from the vendor logo, drawn as a
full silhouette at 0.82 opacity with the black regions layered on top.
Stroking the two tones as separate adjacent paths leaves a hairline
seam between them; the base layer covers it.
AICodeWith (aicodewith.ai) is an aggregator exposing 30 models — Claude,
GPT, Gemini, GLM, Kimi, DeepSeek and Grok — behind a single key and
balance. It lands across Claude Code, Claude Desktop, Codex, Gemini CLI,
OpenCode, OpenClaw, Hermes and Pi.
The vendor exposes one endpoint family per protocol and its own docs warn
against reusing the generic /v1. All four are transcribed from the
official client guides:
- Claude Code / Desktop: api.aicodewith.ai (Anthropic, no /v1)
- Codex: api.aicodewith.ai/chatgpt/v1 (responses)
- Gemini CLI: api.aicodewith.ai/gemini_cli
- OpenCode: api.aicodewith.ai/v1 (@ai-sdk/anthropic)
The first and last look inconsistent but are not: Claude Code appends
/v1/messages itself while the AI SDK appends /messages to its baseURL,
so both land on the same route.
Three non-obvious details:
- Claude Desktop uses mappedRoutes rather than passthroughRoutes. The
upstream Haiku id is claude-haiku-4-5-20251001, which differs from the
claude-haiku-4-5 role route id, so passthrough would point the Haiku
route at a model that does not exist.
- OpenClaw, Hermes and Pi have no official integration guide. They reuse
the Codex endpoint with their respective Responses protocol instead of
guessing at /v1/chat/completions, for which the vendor publishes no
example anywhere. Endpoint probing cannot settle this either: the auth
middleware runs before route matching, so every path returns 401
unauthenticated.
- Model ids come from GET /v1/models, which is unauthenticated and more
current than the pricing page; the OpenCode guide still advertises
kimi-k2 and glm-4.6, both retired.
The only logo the vendor ships is a 128KB PNG whose 2664 colours are
almost entirely compression noise over a two-tone image. It is vectorised
to a 4.9KB inline SVG using currentColor, so the icon follows the theme
rather than carrying an opaque white background.
QwenCloud is DashScope's international site — a separate deployment from
the existing Bailian presets, with its own domain, console and API keys.
It lands as new entries rather than a rename, so existing Bailian users
keep working unchanged.
Each of the three plans gets its own preset because their base URLs and
keys are not interchangeable; the vendor docs state that a mismatched
key/base_url pair fails with 401:
- Pay-as-you-go: dashscope-intl.aliyuncs.com
- Coding Plan: coding-intl.dashscope.aliyuncs.com
- Token Plan: token-plan.ap-southeast-1.maas.aliyuncs.com
Endpoints and model metadata are transcribed from the official client
docs. Two non-obvious details:
- The Coding Plan's OpenAI endpoint is a bare /v1 with no compatible-mode
segment, and only supports Chat Completions, so its Codex preset is
marked openai_chat rather than inheriting the Responses default.
- qwen3.8 models expose a 983616-token context window and accept only
low/medium/xhigh reasoning levels, so the Claude Desktop Token Plan
routes are deliberately not flagged 1M-capable.
c08040e92 declared xhigh for the grok-4.5 xAI presets but missed the
test expectations, leaving main's frontend checks red and dragging
every PR's Frontend Checks down with the same two failures.
u64::MAX as f64 rounds up to 2^64, so the > guard let exactly 2^64
through; the saturating cast then produced u64::MAX, which rounds back
to 2^64 and passes the round-trip check - silently rewriting the value
off by one. Guard with >= instead.
create_namespace_restore_sse_stream and restore_sse_block lost their
last caller when the xAI response path moved to the sanitize rewrite
stream; delete them instead of shipping #[allow(dead_code)].
- Remap unknown request models before sanitizing, so a subagent SKU
that lands on grok-4.5 gets its unsupported sampling fields stripped.
- Intersect union-branch required lists instead of unioning them, so a
flattened oneOf does not force fields the chosen branch lacks.
- Let grok-prefixed request models bypass the catalog remap: real Grok
SKUs the catalog has not caught up with must pass through.
Probed 2026-08-30 against native /v1/responses: grok-4.5 accepts
low/medium/high/xhigh and rejects max with HTTP 400 "Invalid reasoning
effort". Codex does not clamp out-of-catalog efforts, so the preset
list must match what the upstream actually accepts.
The v3.20.1 config-only switch refactor extended the keyless safety gate
to all write paths, which broke switching to proxy-managed OAuth cards
(xAI Grok OAuth): they are keyless by design — the local proxy injects
the real token per request — yet their preset snapshot inherited the
pre-0.149 template's requires_openai_auth = true, so the gate refused
the switch with provider.codex.config.official_auth_fallback.
Fix the snapshot instead of exempting the gate: force the flag to false
on the active custom table for proxy-injected OAuth providers
(uses_proxy_injected_oauth: xai_oauth, github_copilot — codex_oauth is
deliberately excluded, the official login IS its credential). The
neutralization hooks into build_effective_provider_for_live so
preflight, normal writes, and proxy backup/takeover projections all see
the same safe shape, and 0.149 resolves the provider as unauthenticated
without ever reading auth.json. The OAuth preset now emits
requires_openai_auth = false at the source; stored cards self-heal via
backfill on the next switch.
The session sync card sat in the page-level space-y-8 stack while the
pricing/maintenance cards below use space-y-4 inside the Accordion,
leaving an inconsistent 32px vs 16px gap. Group the sync card with the
Accordion under a shared space-y-4 wrapper so all three cards align.
Also show the Sync Now button only when auto-scan is off: in auto mode
the background timer already covers scanning, so the manual entry point
is redundant there.
When an external truncation or rewrite pins the cursor to EOF, the
skipped range is permanently dropped (replaying it would double-count
entries whose detail rows were already pruned into the rollups). That
was only logged; manual sync showed plain success. Surface it through
the result's errors instead — deliberately not deferred_files, whose
semantics promise a retry next round that will never happen.
Follow-up hardening on the byte-cursor scan:
- session_log_sync gains last_tail_fingerprint (added to the still
unreleased v17->v18 migration): a hash of the bytes just before the
cursor boundary. On each incremental read the stored fingerprint is
verified against the file, catching same-size or larger rewrites
that a size check cannot see. The verification read doubles as the
seek to the cursor and seeds the rolling tail buffer, so the append
fast path costs no extra IO.
- truncation and fingerprint-mismatch rewrites now pin the cursor to
the current EOF instead of rescanning from byte 0. A rescan would
re-import entries whose detail rows were already pruned into the
daily rollups, double-counting them forever; skipping the rewritten
range loses at most externally-modified lines, which is the lesser
harm. Appends after the pin resume normal incremental reads.
- mid-file IO errors are now reported through the sync result
(errors + deferred count) instead of silently returning success;
manual sync has no next round to quietly retry in.
Replace the line-number cursor with a byte offset so unchanged file
prefixes are skipped via seek instead of re-reading whole files each
round (12MB active file: 6.04s full scan -> 9.3ms incremental), and
prefetch all sync cursors in one query per round across the five
file-based importers instead of one row lookup per file.
Details:
- session_log_sync gains last_byte_offset via a standalone v17->v18
migration (v17 already ran on live databases; appending the column
to it would leave them permanently missing the column)
- cursor only advances past complete (newline-terminated) lines; a
parseable unterminated tail is imported but deferred, fixing the
legacy bug where a half-written line was counted and then skipped
forever once completed
- legacy line cursors are converted by skipping N lines byte-wise,
never by full rescan: rollup_and_prune deletes detail rows that
request_id dedup relies on, so a rescan would double-count pruned
history into the rollups
- load_sync_cursors now returns Result and aborts the round on
failure instead of falling back to an empty map (same rescan
double-count hazard); Pi reports the error through its result
- on mid-file read errors the cursor keeps the old mtime so the next
round actually resumes from the committed offset
- imports and cursor updates commit atomically in one transaction
Verified with a frozen snapshot replay (1017 files / 409MB): old and
new code produce identical aggregates.
Users reported that the constant background scanning of client session
logs (every 60s) was intrusive. Add a sessionAutoSyncEnabled setting
(default on, so existing behavior is unchanged) that switches session
usage collection between two modes:
- Auto (default): background timer scans client session logs as before
- Manual: background scanning stops entirely (including the startup
scan pass); logs are only scanned when the user clicks the new
"Sync Now" button on the usage dashboard
The toggle only governs scan timing. Proxy request accounting is
real-time and never reads session files, so it is unaffected, and the
startup cost backfill (which only patches existing DB rows) still runs.
Also wires up the previously unused sync_session_usage command as the
manual sync entry point, and adds usage.sessionSync.* strings in all
four locales.
Query the official first-party (undocumented) endpoint
GET https://opencode.ai/zen/go/v1/usage — Bearer-only auth, the opposite
of the inference-side /messages which only accepts x-api-key. The 200
response shape was verified against a real Go subscription key.
- Map rolling/weekly/monthly windows onto the existing five_hour /
weekly_limit / monthly tiers: no new i18n keys or tray label groups
- Parse each window defensively (upstream reshaped the response within
an hour of launching it); report an explicit error when no window
parses instead of rendering an empty card
- Drop resetsAt when percent is 0: the upstream value is a now+window
placeholder, not a real reset time
- Treat 403 as "valid key but no Go subscription" (Zen and Go share
the same workspace key), distinct from 401 auth failures
- Auto-enable the usage script on newly added providers across all five
apps shipping the OpenCode Go preset; claude keeps its existing
all-coding-plans behavior while claude-desktop/codex/opencode/pi only
opt in for OpenCode Go