Repository navigation
fix(codex): proxy image generations endpoint - #7036
Conversation
|
@codex review |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: db08ea5dbd
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
db08ea5 to
4c006c1
Compare
|
To use Codex here, create a Codex account and connect to github. |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4c006c1004
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
4c006c1 to
5f495e7
Compare
|
To use Codex here, create a Codex account and connect to github. |
|
@codex review |
|
Codex Review: Didn't find any major issues. You're on a roll. Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
|
@farion1231 大佬空了看下这个 PR,主要解决 #5429 #6745 这两个 bug issue |
@farion1231 空了帮忙看看能合并吗 |
|
同样遇到了这个问题,求修复 @farion1231 |
|
@farion1231 相同问题 求修复 🥺 |
farion1231
left a comment
There was a problem hiding this comment.
感谢 @thisTom 的修复,也感谢 #5429 / #6745 的报告者和一路 +1 的朋友们。这个 PR 我完整审过了,直接合并。
核实过的几点:
- 4 个本地 alias 归到同一个透传 handler,与 Alpha Search 共用(handlers.rs#L974-L986)。Responses→Chat / Anthropic 桥的闸门是
/responses白名单,/images/generations不会误进转换。 - 对照 openai/codex 上游(codex-api/src/endpoint/images.rs):ImageGen 是非流式
POST {base_url}/images/generations,整体 JSON 解码,与这里的透传形状一致;usage.input_tokens能被 Codex 解析器正确记账。 - full-URL 卡的 sibling 推导与 opaque fail-closed(forwarder.rs#L3294-L3365)逻辑清楚,既有 Alpha Search 三条测试只改了调用签名,行为不变。
- 合并树对当前 main 干净,全量 Rust 测试与 clippy 通过。
合并后可以作为 follow-up 的几点(不需要在本 PR 处理,欢迎接着提):
[P2] /images/edits 未覆盖。 同一个 imagegen 工具在带 referenced_image_paths 或 num_last_images_to_include 时,Codex 会 POST images/edits(JSON body,images: [{image_url}],见上游 ImagesClient::edit 与 tool.rs 的 request_for_call_args),本地路由仍会回空 404,现象与本次修的一样。修法大致:CodexStandaloneEndpoint 加 ImagesEdits 变体(source_suffix 里补 /images/edits 与 /images/generations)、4 个路由 alias、一个 handler wrapper。
[P3] 定价表没有 gpt-image-* 条目,生图用量会记 token 但费用为 0。Codex 当前默认模型是 gpt-image-2(tool.rs#L58),补定价时注意图片 token 与文本 token 单价不同。
[P3] base_url 直接贴了完整 /chat/completions 或 /responses、但没开「完整 URL」开关的卡,standalone 请求会经 build_url 拼成 …/chat/completions/images/generations。Alpha Search 早有同样的缺口,不是本 PR 的回归;可以考虑把 standalone 改写也扩到 base_url_is_full_endpoint 命中的情况。
[P3] 流式生图的用量。 如果以后要把这个端点当通用 OpenAI Images 网关用:stream: true 时上游通过 image_generation.completed 事件顶层的 usage 回报用量,而 from_codex_stream_events_auto 找不到 response.completed 会回退到 Chat 解析并记 0。Codex 客户端本身不发流式生图请求,对本 PR 的场景没有影响;修法是在回退前扫一遍带 usage.input_tokens 的事件走 from_codex_response。
再次感谢,/images/edits 的 follow-up 提上来我会尽快复审。
Follow-ups to farion1231#7036 for Codex's built-in ImageGen over the local proxy: - Register `/images/edits` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`) and forward them through the shared Codex standalone passthrough. ImageGen posts to `images/edits` whenever a call references existing images, and the router returned an empty 404 for it. - Add `CodexStandaloneEndpoint::ImagesEdits`. Both Images endpoints share one full-URL suffix table, so a complete Responses / Compact / Chat Completions / Images URL derives the sibling endpoint (and vice versa) while opaque full URLs keep failing closed. - Apply the standalone sibling rewrite when the base URL is pasted as a complete endpoint with the full-URL switch off, mirroring `codex_chat_base_is_full_endpoint`; standalone requests no longer become `.../chat/completions/images/generations`. - Parse Images API streaming usage: `image_generation.completed` carries `usage` on the event top level, so scan for `usage.input_tokens` before falling back to the Chat Completions stream parser. Related farion1231#7036
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.
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 farion1231#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.
* fix(codex): proxy image edits and Images API follow-ups Follow-ups to #7036 for Codex's built-in ImageGen over the local proxy: - Register `/images/edits` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`) and forward them through the shared Codex standalone passthrough. ImageGen posts to `images/edits` whenever a call references existing images, and the router returned an empty 404 for it. - Add `CodexStandaloneEndpoint::ImagesEdits`. Both Images endpoints share one full-URL suffix table, so a complete Responses / Compact / Chat Completions / Images URL derives the sibling endpoint (and vice versa) while opaque full URLs keep failing closed. - Apply the standalone sibling rewrite when the base URL is pasted as a complete endpoint with the full-URL switch off, mirroring `codex_chat_base_is_full_endpoint`; standalone requests no longer become `.../chat/completions/images/generations`. - Parse Images API streaming usage: `image_generation.completed` carries `usage` on the event top level, so scan for `usage.input_tokens` before falling back to the Chat Completions stream parser. Related #7036 * fix(codex): match standalone URL suffixes case-insensitively Keep endpoint detection and sibling rewriting consistent while preserving the original URL prefix and query. Cover mixed-case Images and Alpha Search endpoints through URL and proxy regression cases. --------- Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com> Co-authored-by: Jason <farion1231@gmail.com>
feat: gate dev panel behind dev-only env flag
chore(comet): archive ccs-sidebar-panels-visibility change
fix: apply skills visibility guard to hermes branch toolbar button
feat: include prompts in sidebar panel visibility settings
fix: persist visibleSidebarPanels setting in backend AppSettings
style: use compact pill toggles for sidebar panel visibility
feat: add sidebar panel visibility switches to settings
feat: add sidebar panel visibility settings and wire into toolbar buttons
chore(comet): archive ccs-home-model-display change
fix(fork): read grokbuild api key from config TOML in quick switch dialog
feat(fork): align model badge with upstream extractModelBadge
feat(fork): show current model badge and quick-switch entry on provider cards
feat(fork): add model quick switch dialog
feat(fork): add provider model read/write utils with tests
chore(comet): archive ccs-model-search-port change
feat(i18n): add model search picker keys for all locales
feat(form): port SearchableModelPicker from upstream for searchable model selection
chore(comet): archive ccs-claude-form-layout change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-claude-form-layout/
- Sync claude-form-layout spec to main specs
- Annotate design doc as archived
- Add verification report
fix(i18n): update advanced options hint, add ja/zh-TW toggle-all, drop dead apiFormatHint key
fix(form): correct 1M toggle-all alignment direction (ml-1)
fix(form): align 1M toggle-all checkbox with row checkboxes
refactor(form): move api format select into EndpointField rightSlot
fix(form): widen api format select to fit option text
feat(form): add 1M toggle-all checkbox in model mapping header
feat(form): move api format select next to endpoint input
chore(comet): archive ccs-provider-presets-trim change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-provider-presets-trim/
- Sync provider-presets-visibility spec to main specs
- Annotate design doc as archived
- Add verification report
docs(fork): add presets-trim files to rebase whitelist
test(fork): mock IS_FORK_BUILD false in unit tests for upstream-form semantics
feat(fork): wire official preset filter into provider forms
feat(fork): add hidden field to preset interfaces
feat(fork): add official preset allowlist and filter util
chore(comet): archive ccs-fork-scaffolding change
- Move change artifacts to docs/openspec/changes/archive/2026-08-18-ccs-fork-scaffolding/
- Sync fork-build-identity spec to main specs
- Annotate design doc as archived
- Add verification report
feat(fork): add dev config with isolated identifier and config dir
docs(fork): complete rebase whitelist with all fork-touched files
fix(fork): guard setTitle with isTauri in test/non-tauri env
fix(fork): define __CCS_FORK_BUILD__ in vitest config
feat(fork): add DevPanel and settings footer entry
feat(fork): set macOS window title to CC Switch (Fork) on startup
feat(fork): inject __CCS_FORK_BUILD__ compile-time constant
feat(fork): bump version to 3.19.2-fork.1 and rename product to CC Switch (Fork)
docs(fork): add upstream rebase workflow and ignore tool artifacts
fix(proxy): accept the whole grok-4.x (x>=5) family in the reasoning-effort whitelist (#7369)
Replace the verbatim grok-4.5 / grok-4.6 entries in supports_reasoning_effort with a rule that parses the grok-4.x minor version and accepts x >= 5, mirroring the existing GPT-5+ rule. This covers grok-4.7 (released 2026-09-21), whose reasoning effort was previously dropped on the Claude -> Chat, Claude -> Responses and Codex Responses -> Chat conversion paths, and lets future releases pass without another whitelist edit. The grok-build-* family is retained for saved providers.
Co-authored-by: allenxu09 <171831965+allenxu09@users.noreply.github.com>
docs(release): add v3.20.4 release notes
chore(release): v3.20.4
feat(pricing): add Grok 4.7 and GLM-5.3-FlashX
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.
fix(i18n): remove outdated OpenCode Go referral offer
fix: add SudoCode.chat alternate API endpoint
feat(settings): add a GitHub star prompt to the About card
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
feat(presets): add Soshow aggregator presets
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.
fix(ci): stabilize frontend tests and verify renderer build
fix(mcode): refresh provider state after removal (#7578)
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
fix(lib): reset skip_taskbar before window.show() across entry points (#6348)
5处 window.show() 调用中 4处没有在 show() 前重置 Windows 的 skip_taskbar,导致以下场景出现窗口可见但不在任务栏、点最小化后窗口消失的 bug:
- single_instance handler (lib.rs:360-361):当原进程因 silent_startup / 关闭到托盘等处于 skip_taskbar(true) 状态时,从快捷方式/开始菜单唤起已运行实例触发。
- handle_deeplink_url 的 focus_main_window 分支 (lib.rs:256):通过 ccswitch:// URL 唤起时。
- DB 恢复流程 (lib.rs:556):数据库版本不匹配时显示恢复界面。
- RunEvent::Opened 处理器 (lib.rs:1803):系统接收到 deep-link URL 时。
修复后的行为与 tray.rs::handle_tray_menu_event::show_main 分支一致(那里已有相同的 set_skip_taskbar(false) 调用)。
经用户测试验证:autostart + silent_startup + 从开始菜单/快捷方式启动的场景下,最小化按钮恢复正常工作。
fix(proxy): omit missing tool descriptions in the Codex responses-to-chat converter (#7378)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): preserve max effort for GPT-5.6 and GPT-6 Astra (#7531)
fix(misc): ignore shell startup output when probing tool versions in WSL (#7348)
try_get_version_wsl runs "${SHELL:-sh}" -lic '<tool> --version' through
wsl.exe and takes the first x.y.z anywhere in stdout. A login+interactive
shell runs the distro's startup files first, so their output shares that
stdout. On Ubuntu, /etc/profile.d/update-motd.sh prints the MOTD once a day,
starting with "Welcome to Ubuntu 24.04.4 LTS", and the card then showed
24.04.4 as the Claude Code version. The once-a-day stamp makes it appear
after the first probe of the day and vanish on refresh.
The payload now prints a sentinel before running the tool, and parsing looks
only at what follows its last occurrence. The last occurrence handles the
-lic || -lc || -c fallback chain printing it several times; matching it as a
substring handles OSC sequences glued in front of it. If the sentinel is
absent (the shell failed before the payload ran), the output is used as
before. The stderr fallback for tools that print their version there is kept.
Fixes #7347
Co-authored-by: lyfuci <lyfuci11@gmail.com>
fix(claude): hide session URL attribution (#7053)
* fix(claude): hide session URL attribution
* docs(claude): document session URL attribution
fix(ui): reset provider scroll when switching apps (#5211)
fix(pi): fix provider logo (#6826)
fix(i18n): use the Pi form's own term for the zh-TW duplicate-key message (#7187)
Follow-up to the nit on #6768. In zh-TW the Pi form labels the field
供應商識別碼 and says 供應商識別碼為必填 when it is empty, but the
duplicate message I added said 供應商標識 — the term Hermes, OpenCode and
OpenClaw use for their own field. One form, two names for the same thing.
The other three locales already keep the label and both messages on one
term, so this makes zh-TW match the pattern rather than introduce one:
en Provider key / Provider key is required / This provider key already exists
ja プロバイダーキー / プロバイダーキーを入力してください / このプロバイダーキーは既に存在します
zh 供应商标识 / 供应商标识不能为空 / 该供应商标识已存在
zh-TW 供應商識別碼 / 供應商識別碼為必填 / 該供應商標識已存在 <- the odd one out
hermes.form.providerKeyDuplicate carries the same sentence and is left
alone: its own label is 供應商標識, so it is already consistent.
Verified: managementListLocales 7/7. Full unit suite 1085/1087, the same
two App.test.tsx integration cases failing before and after.
fix(proxy): strip stop param for GitHub Copilot to unblock auto mode classifier (#5404)
Claude Code's auto mode classifier sends `stop_sequences` on every
safety check. On the GitHub Copilot path the Anthropic -> OpenAI Chat
conversion mapped it to `stop`, and Copilot's Chat Completions route
for Claude models answers that request with HTTP 400, so every
classifier call failed while normal chat kept working.
- Remove the converted `stop` field only when the provider is GitHub
Copilot; other OpenAI-compatible upstreams keep it.
- Copilot detection in the transform now also covers providers that
are recognised only through `env.ANTHROPIC_BASE_URL`.
- Regression tests for the Copilot (meta and env URL) and generic
provider cases.
Fixes #5175
fix(codex): keep stored auth when the live snapshot has no credential (#7434)
Editing the current Codex provider seeds the form from the live ~/.codex
snapshot, and switching away backfills that snapshot into the DB row. When a
provider table declares its own credential source (an Authorization header,
env_key, auth/aws), no bearer token is injected into config.toml and the
shared auth.json is deleted on third-party switches, so the live snapshot is
permanently `{ auth: {}, config }` while auth.OPENAI_API_KEY in the DB is the
only copy of the key. Both paths treated that empty live auth as
authoritative and silently erased the stored key, which in turn made the
usage-script modal misread the provider as an official subscription and
reset the saved script.
- Treat a credential-less live auth (missing auth.json or the bare
auth_mode logout marker) as an absent field in both the edit dialog and
the switch-away backfill, keeping the stored auth template. Live still
wins whenever it carries material, and official providers keep the live
login as their source of truth.
- Gate the official exemption on is_codex_official_provider /
resolveCodexOfficialIdentity rather than the category alone: a legacy
fixed official card without a persisted category is still written as
official, so the fallback must not resurrect a logged-out login on
switch-back.
- Cover both layers with regression tests, including the category-less
official shapes.
Fixes #7433
Supersedes #7492
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): lift additional_tools input carriers on the Codex chat conversion (#7454)
Codex talks to third-party providers over its Responses Lite transport,
which ships the request's extra tools in an `additional_tools` input
carrier: `{"type":"additional_tools","role":"developer","tools":[...]}`,
a role but no `content`. The Responses->Chat conversion let that carrier
fall through the generic "item with a role is a message" arm and emitted
`{"role":"system","content":null}` at messages[1], which strict Chat
gateways reject with `messages[1]: missing field "content"` (or
`data did not match any variant of ...SystemMessageContent`). The tools
carried inside it were also dropped on the Chat and Anthropic conversions.
- Skip `additional_tools` items in the Chat message walk; a carrier
declares tools, it is not a message.
- Collect carrier tools through the existing CodexToolContext registry
(dedup, namespace flattening, custom-tool conversion), which also feeds
the Anthropic conversion and the response-side name round-trip.
- Requests without a carrier convert byte-for-byte as before.
Known boundary: `custom` children inside a namespace tool
(`functions.exec`) are still not lifted; that needs its own response-side
name round-trip and is left for a follow-up.
Diagnosed independently in #6158 by BigStrongSun (fix in #6159) and in
fixture.
Fixes #7451
Fixes #6158
Refs #6033
Refs #6776
Supersedes #6159
fix(proxy): normalize Codex detail:original images for Chat gateways (#7476)
Codex marks tool-returned images with detail:"original" whenever the model
catalog advertises supports_image_detail_original, a Responses-only value.
The Responses->Chat conversion copied it verbatim into image_url.detail, and
strict OpenAI-compatible Chat gateways (Ark/Doubao, SenseNova, GLM, vLLM, ...)
reject the whole request with 400. Because Codex replays the full rollout on
every turn, one poisoned view_image result made the session fail permanently,
including plain-text turns.
Two layers:
- tool_media::merge_top_level_detail downgrades original -> auto, so already
poisoned rollouts and Codex versions before 0.155.0 (which do not normalize
replayed history themselves) recover immediately. auto/low/high are untouched.
- codex_catalog_model_entry always writes supports_image_detail_original:false
on the ProxyChat profile, so Codex stops emitting original at the source once
the catalog is regenerated. NativeResponses/Anthropic profiles and the DeepSeek
official catalog are unaffected.
Top-level user images were never affected: responses_content_to_chat_content
drops the part-level detail entirely.
Fixes #6495
Fixes #7458
Refs #6569
Refs #5805
fix(omo): detect unified config on the WSL side when the OpenCode dir is in WSL (#7550)
When the OpenCode config directory points into WSL
(\\wsl.localhost\<distro>\home\<user>\.config\opencode), OMO >= 4.19.3 keeps
its unified config in the WSL-side home (~/.omo/omo.jsonc). Detection only
probed the Windows home, never found it, and silently fell back to the legacy
per-plugin file that OMO no longer reads, so provider switches looked
successful but had no effect.
- Add derive_wsl_home_dir to derive the WSL-side home from a WSL UNC path
(home/<user> or root); non-WSL and non-home paths return None.
- Probe a list of home candidates for the unified config, WSL-side home
first and the OS home as a fallback, shared by the read, write and delete
paths.
- Detection only: when no unified config exists on either side the legacy
fallback is unchanged.
Fixes #7363
Supersedes #7367
docs(partner): add a FluxA purchase link and tighten its sponsor blurb
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.
fix(claude-desktop): expose the 1M context variant for DeepSeek V4 routes
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.
feat(presets): add FluxA Token Plan partner presets
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.
fix(presets): correct AICodeWith Codex-family endpoint to /v1
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.
fix(ui): render monochrome preset icons in the foreground color
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.
docs(user-manual): point Chat-only Codex examples at plan endpoints
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.
fix(pricing): restore DeepSeek V4 Pro price and seed Qwen3.8 and Hy4 models
- 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).
fix(presets): move MiniMax CN and BaiLing to their current official endpoints
- 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.
feat(codex): refresh CN presets from the Responses API audit
- 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.
test(claude-desktop): run Linux path tests on unix hosts and cover Linux in provider sync tests
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.
fix(ui): stop expanding stale cached usage tiers on ineligible provider cards
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.
fix(skills): 支持 skillId 与目录名不一致的技能安装和更新 (#6381)
* fix(skills): resolve mismatched skillId directories
* fix(skills): preserve source path during updates
* fix(skills): refresh source URL after repository relocation
* test(skills): isolate relocation fixture from legacy home
feat(opencode): 支持从获取的模型列表中搜索并批量添加所选模型 (#7515)
* feat: implement fetched models selection and UI components
* fix(opencode): prevent model search from submitting provider form
Add Kimi Global preset variants (kimi.ai platform + api endpoints) (#7526)
Add a "Kimi Global" twin immediately after the domestic "Kimi" API preset and a "Kimi For Coding Global" twin after the domestic "Kimi For Coding" preset, in every preset file that ships them (claude, claude-desktop, codex, hermes, openclaw, opencode, pi).
"Kimi Global" points to the international open platform https://platform.kimi.ai?aff=cc-switch (apiKeyUrl mirrored as https://platform.kimi.ai/console/api-keys?aff=cc-switch) and swaps the endpoint host api.moonshot.cn for api.moonshot.ai with each file's path unchanged (/v1 or /anthropic).
"Kimi For Coding Global" points to the international site https://www.kimi.ai/code/?aff=cc-switch and swaps the endpoint host api.kimi.com for api.kimi.ai with the path unchanged (e.g. https://api.kimi.ai/coding/v1).
Domestic presets are unchanged; category, icons, apiFormat, model catalogs, endpointCandidates (host swapped), partnerPromotionKey and settings otherwise mirror the domestic blocks exactly.
primePartner stays only on the domestic presets so the new variants do not alter prime-partner ordering.
The Pi twins get distinct providerKey values ("cc-switch-kimi-global", "cc-switch-kimi-for-coding-global") because providerKey is the preset-level primary key and must not collide with the domestic presets.
Rebalance sponsor CTAs around Kimi Code plan with dual-region links (#7522)
Kimi Code plan purchases have resumed, so the README sponsor sections go back to leading with the coding plan instead of the API-only flow written during the purchase pause.
All four locales (EN/ZH/JA/DE) now present Kimi Code plan (中文站 | Global) as the primary CTA and the Kimi Open Platform API (中文站 | Global, with the first-top-up bonus) as the secondary one, mirroring the domestic|global split already used for API links.
The aff and track_id values are unchanged.
fix(prompts): refresh active prompts after external file edits (#7194)
Reload the enabled prompt from its live file when the Prompts list loads, and refresh the open panel when the window regains focus, so external edits to CLAUDE.md / AGENTS.md show up without a restart.
- Keep saved templates available when the live file cannot be read.
- Skip the backfill for empty or whitespace-only files, matching enable_prompt.
- Skip the backfill while a sync/restore holds the sync lock, so a list load cannot write a stale live file into a freshly restored database.
fix(skills): raise archive entry limit so large skill repos install (#7489)
Installing hugohe3/ppt-master from skills.sh failed with
ARCHIVE_TOO_MANY_ENTRIES: the GitHub archive we download is the whole
repository (13,248 entries), and the 10,000-entry cap added in #5811
treated it as a zip bomb.
- Raise MAX_ARCHIVE_ENTRIES to 30,000 for both the remote archive and
local ZIP paths, and align WebDAV's MAX_EXTRACT_ENTRIES so a skills.zip
containing such a skill still restores.
- Charge every file entry at least one 4 KiB block against the existing
512 MiB budget, mirroring directory accounting, so empty-file archives
are bounded by the byte budget rather than by the entry count alone.
- Add tests for a 13k-entry repo archive and for the per-file minimum
charge.
Fixes: https://github.com/farion1231/cc-switch/issues/7475
feat: add MiniMax Code harness support (#7383)
Add MiniMax Code (mcode) as a managed application: provider and model management through the native custom_provider configuration, MCP servers, Skills, global instructions (AGENTS.md), read-only local session history with resume, and usage import from the native runtime database. Native files shared by the TUI and desktop are written under the native lock with rollback when the database commit fails, unrelated native configuration is preserved, and the data directory honors MINIMAX_DATA_DIR and MAVIS_DATA_DIR. Deleting sessions stays in the native application because they are coupled to runtime state.
fix(codex): recover stale account bindings during takeover (#7395)
After deleting a managed ChatGPT account and logging in again, a Codex provider can still reference the old local account ID, which made route switching, takeover, startup restoration and rebinding fail permanently.
Distinguish a missing outgoing account from an existing account with no matching live token. A missing account only releases its ownership marker after the persisted account store is verified; the target write policy stays in force. A stale target now reports an account-selection instruction instead of a bare not-found error, including when takeover is already enabled.
Fixes #7392
feat(claude-desktop): support 3P configuration on Linux (#7331)
Add Claude Desktop 3P configuration support on Linux. Resolve the config root from an absolute XDG_CONFIG_HOME, falling back to ~/.config; when CC Switch itself runs as a Flatpak, use the host ~/.config instead of the sandbox-private XDG_CONFIG_HOME. Update the en/zh/ja user manuals with Linux paths and the Flatpak boundary.
Fixes #4855
fix(proxy): omit missing tool descriptions instead of serializing null (#7319)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): keep reasoning effort for grok-4.6 and pass xhigh through verbatim (#7318)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(models): support Zhipu OpenAI Responses model lists (#7330)
Zhipu's Responses endpoint (open.bigmodel.cn/api/v1) serves its model list in the Codex remote catalog shape (models[].slug) instead of the OpenAI-compatible data[].id shape, so the Codex form's model fetch came back empty. The parser now falls back to models[].slug when data is absent.
fix(misc): fetch npm dist-tags from the dedicated endpoint with the probe timeout (#7346)
The tool version probe requested the full npm packument for each package (metadata for every historical version, 14-25 MB uncompressed for codex / opencode / openclaw) only to read its dist-tags, and it was the only latest-version probe without LATEST_PROBE_TIMEOUT, so it fell back to the proxy client's 600 s total timeout and the cards sat on "loading" for minutes on slow links while the large downloads starved the Hermes GitHub probe into "unknown".
Switch fetch_npm_dist_tags to registry.npmjs.org/-/package/{package}/dist-tags, whose body is the dist-tags object itself (tens of bytes to a few KB), escape scoped names as %2f, and apply the same 15 s probe timeout as the GitHub and PyPI probes. No data is lost: the only consumer reads latest and the tool's prerelease tags.
Fixes #7339
feat(usage): display output tokens per second in request log table and detail panel (#3369)
Show tokens/s next to the output token count in the request log table and the request detail panel.
The generation window prefers an explicit duration when present, otherwise latency minus first-token time for streaming requests, and falls back to full latency for non-streaming requests. Rows without a positive token count or window show nothing.
Closes #5936
fix(presets): update APIKEY.FUN URLs to apikey.fan
docs(release): add v3.20.3 release notes
chore(release): v3.20.3
fix(pricing): move retired DeepSeek V4 family to the V4.1 Flash tier
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.
fix(presets): declare third-party Token Plan DeepSeek V4 rows text-only
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.
feat(presets): rename DouBaoSeed preset to localized Volcengine Doubao
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.
fix(codex): skip unchanged incomplete rollout tails
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.
fix(claude): align quick model selection with form order
fix(tray): show bound ChatGPT account quota for managed Codex cards
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
feat(claude): add "Disable Artifact Tool" quick toggle to provider editor
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.
feat(codex): refresh aggregator model presets and catalogs
feat(codex): switch Kimi presets to native Responses direct-connect
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.
fix(usage): show Claude Fable weekly limit from the usage API limits array
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.
fix(codex): mirror vision-capable DeepSeek catalog and un-gate the legacy v4-flash alias (#7286)
Refresh the bundled DeepSeek official Codex catalog to the vendor's current models.json (slug deepseek-flash, image input modality, supports_image_detail_original) and remove deepseek-v4-flash from the confirmed text-only registry: the legacy id is still accepted by DeepSeek and routed to the vision-capable V4.1 Flash, so both the proxy media sanitizer and the generated Codex catalog must fail open for it. deepseek-v4-pro stays text-only per the vendor catalog.
Fixes #7283
fix(proxy): clamp sub-floor max_tokens to the Responses API minimum (#7287)
The Responses API rejects max_output_tokens below 16, but Anthropic clients legitimately send tiny probe budgets (Claude Desktop's model-availability probe uses max_tokens=1). The Anthropic→Responses conversion copied the value verbatim, so strict upstreams returned 400 and Claude Desktop reported the mapped model as unavailable under local routing.
Clamp 1..=15 up to 16 during conversion; 16+, 0 and non-integer values keep their pass-through semantics. Normal session requests convert byte-for-byte as before, and the Codex OAuth path still strips max_output_tokens afterwards.
Fixes #7103
fix(codex): coalesce adjacent commentary with pending tool calls (#7280)
The Codex Responses -> Chat Completions converter emitted two consecutive
assistant messages when one Responses model turn contained a commentary
message directly followed by function_call items. Chat upstreams imitate
the text-only assistant message as a complete turn and return
finish_reason=stop before the expected tool call, so long agent tasks end
after a progress update.
Merge pending tool calls into the directly adjacent assistant message when
it carries no tool_calls yet, attach pending reasoning per segment so
parallel calls do not duplicate it, and keep the new-message path for every
other boundary shape (user/tool boundaries, prior tool-call batches, media
flushes). The reasoning placeholder backfill still applies to coalesced
calls without reasoning.
Fixes #6529
Supersedes #6530
Co-authored-by: BigStrongSun <BigStrongSun@users.noreply.github.com>
fix(proxy): skip empty reasoning_content placeholders in Anthropic SSE streaming (#7227)
Some OpenAI-compatible upstreams (GLM, Qwen, DeepSeek-V4-Pro via ModelScope/DashScope,
Bailian, JD Cloud, ...) keep a `reasoning_content: ""` placeholder in every content
chunk instead of omitting the field. The reasoning branch of the OpenAI Chat -> Anthropic
SSE converter only checked for presence, and it shares `current_non_tool_block_type`
with the content branch, so every content chunk closed the open text block, opened an
empty thinking block, then reopened a text block. N content chunks became 2N content
blocks, half of them empty thinking blocks, which Claude Code rendered as one line per
token with dozens of empty Thought blocks and bloated the session JSONL.
Filter empty reasoning before entering the branch, matching the `!content.is_empty()`
guard the content branch already had and the non-streaming `openai_to_anthropic`
converter. Two regression tests pin block index, type and order: the placeholder shape
must yield a single text block and no thinking_delta at all, and real reasoning must
still yield one thinking block followed by one text block.
Fixes #5028
Fixes #4404
fix(codex): detect growing rollouts with a persisted byte cursor (#7219)
Codex keeps the rollout append handle open for the whole session, so on Windows NTFS the file mtime does not advance while the file grows and the pure-mtime gate skipped growing rollouts forever. Persist the observed byte length in session_log_sync.last_byte_offset for Codex rows and skip a file only when both mtime and size are unchanged. The parser now splits records on newlines manually so an incomplete trailing record is retried on the next pass without advancing the line cursor.
Fixes #6060
fix(codex): proxy image edits and Images API follow-ups (#7177)
* fix(codex): proxy image edits and Images API follow-ups
Follow-ups to #7036 for Codex's built-in ImageGen over the local proxy:
- Register `/images/edits` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them through the shared Codex standalone passthrough.
ImageGen posts to `images/edits` whenever a call references existing
images, and the router returned an empty 404 for it.
- Add `CodexStandaloneEndpoint::ImagesEdits`. Both Images endpoints share
one full-URL suffix table, so a complete Responses / Compact / Chat
Completions / Images URL derives the sibling endpoint (and vice versa)
while opaque full URLs keep failing closed.
- Apply the standalone sibling rewrite when the base URL is pasted as a
complete endpoint with the full-URL switch off, mirroring
`codex_chat_base_is_full_endpoint`; standalone requests no longer become
`.../chat/completions/images/generations`.
- Parse Images API streaming usage: `image_generation.completed` carries
`usage` on the event top level, so scan for `usage.input_tokens` before
falling back to the Chat Completions stream parser.
Related #7036
* fix(codex): match standalone URL suffixes case-insensitively
Keep endpoint detection and sibling rewriting consistent while preserving the original URL prefix and query. Cover mixed-case Images and Alpha Search endpoints through URL and proxy regression cases.
---------
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): preserve per-app settings on shutdown and port allocation (#7210)
On normal exit, stop_with_restore_keep_state() called the legacy global
proxy-config writer to clear the long-retired live_takeover_active flag.
That writer reads the Claude row and runs an UPDATE without a WHERE clause,
so every shutdown copied Claude's max_retries and the three timeout fields
onto the Codex, Gemini and Grok Build rows — and the UPDATE never carried
live_takeover_active in the first place, so the step had no effect beyond
the clobber. The ephemeral listen-port path did the same on every proxy
start.
Drop the dead write-back (live-config restore, backup cleanup and health
reset are unchanged) and persist the resolved ephemeral port through the
global-config interface, which only touches the shared listen/logging
columns. Two regression tests seed distinct per-app settings for all four
apps and assert they survive both paths.
Fixes #7204
fix(provider): preserve child metadata during universal sync (#7212)
Syncing a universal provider regenerates its Claude/Codex/Gemini child
providers. The child's `settings_config` was merged with the existing row,
but `meta`, `created_at` and `sort_index` were taken from the generated
object, and the universal provider never carries any of them from the UI.
The UPDATE therefore wiped the child's usage script, common-config opt-out,
endpoint auto-select and other per-app settings to `{}` and reset the sort
index to NULL, which pushed the card to the bottom of the list and looked
like the provider had been deleted and re-created.
Keep the existing child's `meta`, `created_at` and `sort_index` when the
row already exists. Name, base URL, key, models, website and notes are
still driven by the universal provider, and first-time creation still
inherits the parent's metadata.
Fixes #7134
feat(presets): rebrand DashScope as 千问AI平台 and refresh Qwen models (#7183)
Rename the domestic Bailian presets to 千问AI平台 with a dedicated icon, refresh the Qwen family to the 3.8 generation across Claude Code, Claude Desktop, Codex, Hermes, OpenClaw, OpenCode and Pi, add the domestic Token Plan presets, restore the international QwenCloud Coding Plan presets, and align OpenClaw/OpenCode/Pi model metadata with the official client docs.
fix(codex): honor proxy URL when model_provider is omitted (#7263)
When a Codex provider card's TOML has no `model_provider`, Codex falls back to
its built-in `openai` provider, but takeover wrote the proxy address as a
top-level `base_url` that Codex never reads, so requests bypassed the local
proxy and went straight to api.openai.com.
Treat the missing selector as the built-in `openai` provider and write the
proxy address to `openai_base_url`; the existing legacy-reroute migration then
turns it into a `cc-switch` custom provider table carrying the `PROXY_MANAGED`
bearer, the same shape every other third-party takeover uses. Explicit
provider selections are unchanged.
Refs #6256, #7217
fix(provider): update MiniMax defaults to M3 and remove expired offers (#7255)
* fix(provider): refresh MiniMax M3 presets and remove expired promotions
* fix(provider): complete MiniMax M3 capability declarations
---------
Co-authored-by: saladday <1203511142@qq.com>
chore(provider): remove Atlas Cloud sponsor status, keep presets
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`
docs(release): add v3.20.2 release notes
chore(release): v3.20.2
feat(provider): add SoleAPI sponsor presets across all nine apps
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.
fix(codex): proxy image edits endpoint
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.
fix(provider): pin models URL for JieKou and Novita Claude presets
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.
feat(pricing): refresh seed for September 2026 vendor price changes
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.
fix(codex): stamp requires_openai_auth on takeover writes to match the live login state
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.
fix(updater): show the real reason when an update check fails (#6482)
The updater plugin rejects with a plain string, so `err instanceof Error`
never matched and the real failure reason was replaced by the generic
fallback message. Use the existing extractErrorMessage helper, and pass
the recovered message into the check-update failure toast so it actually
reaches the user instead of stopping at an unread context state.
Co-authored-by: Jason <farion1231@gmail.com>
docs: fix the locales path in the README directory trees (#6100)
All four READMEs showed locales/ as a direct child of src/, but the
translations live at src/i18n/locales/. Split it into two tree nodes so it
matches how the rest of the tree is drawn. Also fixes a CLaude -> Claude
typo in README_ZH.md.
Co-authored-by: Jason <farion1231@gmail.com>
fix(claude): exclude workflow journal.jsonl from session list
Claude Code's workflow feature writes subagents/workflows/wf_*/journal.jsonl inside the session directory. The Claude session scanner only excluded agent-* files, so each journal parsed as an empty ghost session titled "journal". Exclude journal.jsonl by file name and add regression tests.
Fixes #6042
fix(i18n): add the missing pi.form.providerKeyDuplicate and common.collapse keys
translatePiProviderMutationError returns pi.form.providerKeyDuplicate for the Pi backend's duplicate-key error, but the key existed in none of the four locale files, so the toast showed the raw key. common.collapse (aria-label of the thinking-map toggle) was missing the same way. Add both keys to zh/en/ja/zh-TW and lock them in the locale coverage test.
fix(proxy): stop mask_url panicking on multi-byte UTF-8 near the truncation boundary
mask_url's fallback branch (used when the proxy URL fails Url::parse) sliced the raw string at byte 20 and panicked when a multi-byte UTF-8 character straddled that offset, e.g. a CJK string pasted into the global proxy address. Snap the cut to the nearest char boundary at or before 20 and add a regression test.
fix(tests): isolate LOCALAPPDATA so tests stop writing the real Claude Desktop config
On Windows, Claude Desktop config paths resolve from LOCALAPPDATA, which the test home override in tests/support.rs did not redirect, so profile_roundtrip wrote the https://desktop.test fixture into the developer's real Claude Desktop config. Point LOCALAPPDATA at the isolated test home alongside HOME and USERPROFILE.
Fixes #6077
fix(usage): widen pricing model source select to fit localized labels
The pricing model source SelectTrigger in the usage cost settings table was w-28, which leaves about 70px for the label; the English and Japanese labels need about 107px and overflowed the trigger. Widen it to w-40 and drop the h-7 overrides so the trigger and the adjacent Input use the shared h-9 height.
feat(pi): add Tencent TokenHub and Token Plan presets (#7159)
Add eight Tencent presets for Pi, covering the TokenHub pay-as-you-go line
(domestic and international) and the six Token Plan subscription products —
Pi was the only managed app still missing them. Model IDs, ordering and
endpoints follow Tencent's own model lists verbatim, and the tests assert each
product's line-up in order so future drift is caught.
Declare thinking control alongside the capabilities rather than leaving it to
defaults: the 31 DeepSeek entries carry a Tencent-specific compat so selecting
"off" actually disables thinking instead of silently billing reasoning tokens,
the eight Kimi K2.7 Code entries no longer offer an "off" level the model
cannot honour, and kimi-k2.7-code-highspeed is marked image-capable so
tool-returned screenshots are no longer dropped.
Drop minimax-m2.5 from every app: it is gone from Tencent's model tables and
the platform is retiring it.
fix(codex): proxy image generations endpoint (#7036)
Codex's built-in ImageGen calls the legacy OpenAI Images API, but the local
proxy only registered Responses / Chat Completions / Compact / Alpha Search
routes, so `/v1/images/generations` returned an empty 404 under Codex routing.
- Register `/images/generations` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them as a Codex standalone passthrough shared with Alpha Search.
- Generalize the Alpha Search full-URL rewrite into
`rewrite_codex_standalone_full_url`: derive the sibling
`/images/generations` from full Responses / Compact / Chat Completions URLs,
keep an existing Images URL as-is, and fail closed on opaque full URLs.
- Record `usage.input_tokens` / `output_tokens` through the Codex usage parser.
Fixes #5429
Fixes #6745
feat(pricing): add Gemini 3.8 Flash pricing (#7164)
Seeds the built-in pricing row for Gemini 3.8 Flash at 0.75 / 3.75 input/output per million tokens, 0.075 cache read, no cache-write charge, plus a regression test locking all four fields.
Values verified against Google's own pricing page (introductory price through 2026-12-31; list price 1.50 / 7.50 / 0.15 from 2027-01-01) and models.dev.
feat(provider): extend PPIO presets for Pi (#6870)
Add a PPIO provider preset for Pi using the OpenAI-compatible endpoint,
and pin an explicit models URL on the Claude Code PPIO preset so model
discovery resolves against https://api.ppio.com/openai/v1/models instead
of the derived candidates, which all return 404.
Co-authored-by: Jason <farion1231@gmail.com>
feat(pricing): add GLM-5.3 Flash pricing (#7163)
fix(proxy): enable parallel tool calls for Codex OAuth (#7024)
Codex OAuth requests defaulted parallel_tool_calls to false when the incoming
Anthropic request carried no explicit value, forcing Claude Code into one tool
call per model turn. Default it to true (matching codex-rs), map Anthropic
tool_choice.disable_parallel_tool_use to the inverse Responses value so explicit
serial execution is preserved, and add regression coverage for both directions.
Carries #5722 forward on top of current main.
Fixes #5719
Co-authored-by: 太一 <taiyi@douyu.tv>
feat(pricing): add GPT-6 Astra pricing (#7162)
fix(codex): resolve input_modalities for unknown models in vendor catalog (#6750)
When a model slug is not in the bundled DeepSeek official catalog, the vendor path clones the flagship entry and previously inherited its text-only input_modalities. Resolve modalities for unmatched slugs through the same registry/fail-open logic the non-vendor path uses, so a vision variant such as deepseek-v4-flash-vision-exp is no longer declared text-only. Explicit user inputModalities still win; matched models keep the vendor's declaration verbatim.
Fixes #6725
fix(claude): expose Opus 5 and Sonnet 5 in takeover mode (#5882)
Bump the stable client-facing role aliases that takeover mode writes to
Claude Code: Opus claude-opus-4-8 -> claude-opus-5 and Sonnet
claude-sonnet-4-6 -> claude-sonnet-5, so the /model menu matches the
Claude Desktop default routes and preset defaults that already moved to
Opus 5 / Sonnet 5. Add "opus-5" to the adaptive-thinking classifier so
the Bedrock thinking optimizer, which runs on the client alias before
model mapping, keeps sending adaptive thinking instead of the removed
budget_tokens.
Routing is unaffected: the proxy maps client aliases by role keyword, and
the aliases are rewritten automatically the next time the proxy starts.
Fixes #5876
Co-authored-by: Jason <farion1231@gmail.com>
feat(pricing): add GLM-5.3 to built-in model pricing table (#6591)
Seed glm-5.3 at $1.4/$4.4 per MTok with $0.26 cache read, matching the
official Z.ai list price (identical to the GLM-5.2 tier):
https://docs.z.ai/guides/overview/pricing
New model id, so seed only; no repair entry and no SCHEMA_VERSION bump.
Existing installs pick the row up on next launch via INSERT OR IGNORE, and
the startup cost backfill re-prices historical glm-5.3 rows recorded at $0.
fix(codex): align OAuth client identity for GPT-6 (#7132)
Requests for gpt-6-astra through the Claude-to-Codex OAuth takeover route advertised Codex 0.144.1, below the model's 0.153.0 minimal_client_version, so ChatGPT's backend rejected them with HTTP 400 "requires a newer version of Codex". Bump the advertised client version to 0.153.4 and share the originator/version constants between generation and model discovery, which previously sent cc-switch's own package version and originator.
Two regression tests pin the generation headers and the model-list request to a version at or above the gpt-6-astra floor.
Refs #7129
fix(codex): disable supports_search_tool for DeepSeek catalog models (#6653)
DeepSeek's Responses API has no tool_search support, but the bundled
official catalog declared supports_search_tool=true for deepseek-v4-pro
and deepseek-v4-flash. Codex gates deferred/namespaced MCP tools on
this flag (search_tool_enabled = supports_search_tool &&
namespace_tools), so MCP tools were hidden behind a tool_search the
gateway cannot serve — no MCP tool could ever be called.
Set the flag to false for both models so Codex lists MCP tools inline.
Hosted web_search is unaffected (gated by provider capabilities, not
this flag) and DeepSeek's server-side web_search still works.
Fixes #6647
fix(codex): move $ref siblings into allOf for Moonshot/Kimi chat upstreams (#6863)
Moonshot's Chat Completions validator (api.moonshot.cn, api.moonshot.ai and the Kimi For Coding endpoint api.kimi.com) rejects a `$ref` that carries sibling keywords. Codex Desktop's built-in tool schemas carry exactly that shape, so every desktop turn routed through the local proxy to Kimi failed with HTTP 400.
When the resolved upstream host is Moonshot/Kimi, each `$ref` with siblings is moved into `allOf` after the Responses -> Chat conversion and the siblings stay in place. The traversal only enters schema-valued keywords and the rewrite is idempotent. Every other provider keeps byte-identical tool schemas, and the Codex -> Anthropic path is left alone because Moonshot's Anthropic-compatible endpoint accepts the original shape.
Fixes #6867
Co-authored-by: Jason <farion1231@gmail.com>
fix(codex): point Zhipu GLM presets at the official Responses endpoint (#6957)
Zhipu documents three separate base URLs per site (Anthropic /api/anthropic,
Chat /api/coding/paas/v4, Responses /api/v1) and warns that the wrong one
cannot use Coding Plan quota. Codex direct-connect speaks Responses, so the
presets must target /api/v1; the 400 in #6944 (`unknown variant custom`)
came from the Chat endpoint's strict legacy gateway.
- Switch the Zhipu GLM / Zhipu GLM en presets to apiFormat openai_responses
(NativeResponses catalog: shell_command edits, no freeform apply_patch)
…
移除 tauri-plugin-updater 依赖与自动更新链路,改为统一引导用户到
GitHub Releases 手动下载新版。
- 前端:updater.ts 恒返回 up-to-date;UpdateContext 取消启动自检;
AboutSection「检查更新」直接打开 fork releases 页;DatabaseUpgrade
恢复界面去掉下载进度条,改为打开发布页。
- 后端:移除 updater 插件注册与 UpdaterExt 调用;
install_update_and_restart 降级为空壳,check_app_update_available
返回当前版本号;check_for_updates 指向 fork releases;capabilities
去掉 updater:default 权限。
- 打包:tauri.conf.json 删 plugins.updater、createUpdaterArtifacts=false;
Cargo.toml / package.json 删 updater 依赖,Cargo.lock / pnpm-lock
同步重生成。
- CI:release.yml 移除签名收集与 assemble-latest-json job,删除
.tar.gz/.sig 处理,release body 去掉 updater 说明。
版本号统一为 3.20.0-1(合法 semver 且 MSI 第 4 位为 1)。
Cargo.lock / pnpm-lock.yaml 已随依赖移除重生成,cargo check 通过。
ci: build only Windows x64 and macOS arm64, unsigned
feat: gate dev panel behind dev-only env flag
chore(comet): archive ccs-sidebar-panels-visibility change
fix: apply skills visibility guard to hermes branch toolbar button
feat: include prompts in sidebar panel visibility settings
fix: persist visibleSidebarPanels setting in backend AppSettings
style: use compact pill toggles for sidebar panel visibility
feat: add sidebar panel visibility switches to settings
feat: add sidebar panel visibility settings and wire into toolbar buttons
chore(comet): archive ccs-home-model-display change
fix(fork): read grokbuild api key from config TOML in quick switch dialog
feat(fork): align model badge with upstream extractModelBadge
feat(fork): show current model badge and quick-switch entry on provider cards
feat(fork): add model quick switch dialog
feat(fork): add provider model read/write utils with tests
chore(comet): archive ccs-model-search-port change
feat(i18n): add model search picker keys for all locales
feat(form): port SearchableModelPicker from upstream for searchable model selection
chore(comet): archive ccs-claude-form-layout change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-claude-form-layout/
- Sync claude-form-layout spec to main specs
- Annotate design doc as archived
- Add verification report
fix(i18n): update advanced options hint, add ja/zh-TW toggle-all, drop dead apiFormatHint key
fix(form): correct 1M toggle-all alignment direction (ml-1)
fix(form): align 1M toggle-all checkbox with row checkboxes
refactor(form): move api format select into EndpointField rightSlot
fix(form): widen api format select to fit option text
feat(form): add 1M toggle-all checkbox in model mapping header
feat(form): move api format select next to endpoint input
chore(comet): archive ccs-provider-presets-trim change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-provider-presets-trim/
- Sync provider-presets-visibility spec to main specs
- Annotate design doc as archived
- Add verification report
docs(fork): add presets-trim files to rebase whitelist
test(fork): mock IS_FORK_BUILD false in unit tests for upstream-form semantics
feat(fork): wire official preset filter into provider forms
feat(fork): add hidden field to preset interfaces
feat(fork): add official preset allowlist and filter util
chore(comet): archive ccs-fork-scaffolding change
- Move change artifacts to docs/openspec/changes/archive/2026-08-18-ccs-fork-scaffolding/
- Sync fork-build-identity spec to main specs
- Annotate design doc as archived
- Add verification report
feat(fork): add dev config with isolated identifier and config dir
docs(fork): complete rebase whitelist with all fork-touched files
fix(fork): guard setTitle with isTauri in test/non-tauri env
fix(fork): define __CCS_FORK_BUILD__ in vitest config
feat(fork): add DevPanel and settings footer entry
feat(fork): set macOS window title to CC Switch (Fork) on startup
feat(fork): inject __CCS_FORK_BUILD__ compile-time constant
feat(fork): bump version to 3.19.2-fork.1 and rename product to CC Switch (Fork)
docs(fork): add upstream rebase workflow and ignore tool artifacts
fix(proxy): accept the whole grok-4.x (x>=5) family in the reasoning-effort whitelist (#7369)
Replace the verbatim grok-4.5 / grok-4.6 entries in supports_reasoning_effort with a rule that parses the grok-4.x minor version and accepts x >= 5, mirroring the existing GPT-5+ rule. This covers grok-4.7 (released 2026-09-21), whose reasoning effort was previously dropped on the Claude -> Chat, Claude -> Responses and Codex Responses -> Chat conversion paths, and lets future releases pass without another whitelist edit. The grok-build-* family is retained for saved providers.
Co-authored-by: allenxu09 <171831965+allenxu09@users.noreply.github.com>
docs(release): add v3.20.4 release notes
chore(release): v3.20.4
feat(pricing): add Grok 4.7 and GLM-5.3-FlashX
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.
fix(i18n): remove outdated OpenCode Go referral offer
fix: add SudoCode.chat alternate API endpoint
feat(settings): add a GitHub star prompt to the About card
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
feat(presets): add Soshow aggregator presets
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.
fix(ci): stabilize frontend tests and verify renderer build
fix(mcode): refresh provider state after removal (#7578)
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
fix(lib): reset skip_taskbar before window.show() across entry points (#6348)
5处 window.show() 调用中 4处没有在 show() 前重置 Windows 的 skip_taskbar,导致以下场景出现窗口可见但不在任务栏、点最小化后窗口消失的 bug:
- single_instance handler (lib.rs:360-361):当原进程因 silent_startup / 关闭到托盘等处于 skip_taskbar(true) 状态时,从快捷方式/开始菜单唤起已运行实例触发。
- handle_deeplink_url 的 focus_main_window 分支 (lib.rs:256):通过 ccswitch:// URL 唤起时。
- DB 恢复流程 (lib.rs:556):数据库版本不匹配时显示恢复界面。
- RunEvent::Opened 处理器 (lib.rs:1803):系统接收到 deep-link URL 时。
修复后的行为与 tray.rs::handle_tray_menu_event::show_main 分支一致(那里已有相同的 set_skip_taskbar(false) 调用)。
经用户测试验证:autostart + silent_startup + 从开始菜单/快捷方式启动的场景下,最小化按钮恢复正常工作。
fix(proxy): omit missing tool descriptions in the Codex responses-to-chat converter (#7378)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): preserve max effort for GPT-5.6 and GPT-6 Astra (#7531)
fix(misc): ignore shell startup output when probing tool versions in WSL (#7348)
try_get_version_wsl runs "${SHELL:-sh}" -lic '<tool> --version' through
wsl.exe and takes the first x.y.z anywhere in stdout. A login+interactive
shell runs the distro's startup files first, so their output shares that
stdout. On Ubuntu, /etc/profile.d/update-motd.sh prints the MOTD once a day,
starting with "Welcome to Ubuntu 24.04.4 LTS", and the card then showed
24.04.4 as the Claude Code version. The once-a-day stamp makes it appear
after the first probe of the day and vanish on refresh.
The payload now prints a sentinel before running the tool, and parsing looks
only at what follows its last occurrence. The last occurrence handles the
-lic || -lc || -c fallback chain printing it several times; matching it as a
substring handles OSC sequences glued in front of it. If the sentinel is
absent (the shell failed before the payload ran), the output is used as
before. The stderr fallback for tools that print their version there is kept.
Fixes #7347
Co-authored-by: lyfuci <lyfuci11@gmail.com>
fix(claude): hide session URL attribution (#7053)
* fix(claude): hide session URL attribution
* docs(claude): document session URL attribution
fix(ui): reset provider scroll when switching apps (#5211)
fix(pi): fix provider logo (#6826)
fix(i18n): use the Pi form's own term for the zh-TW duplicate-key message (#7187)
Follow-up to the nit on #6768. In zh-TW the Pi form labels the field
供應商識別碼 and says 供應商識別碼為必填 when it is empty, but the
duplicate message I added said 供應商標識 — the term Hermes, OpenCode and
OpenClaw use for their own field. One form, two names for the same thing.
The other three locales already keep the label and both messages on one
term, so this makes zh-TW match the pattern rather than introduce one:
en Provider key / Provider key is required / This provider key already exists
ja プロバイダーキー / プロバイダーキーを入力してください / このプロバイダーキーは既に存在します
zh 供应商标识 / 供应商标识不能为空 / 该供应商标识已存在
zh-TW 供應商識別碼 / 供應商識別碼為必填 / 該供應商標識已存在 <- the odd one out
hermes.form.providerKeyDuplicate carries the same sentence and is left
alone: its own label is 供應商標識, so it is already consistent.
Verified: managementListLocales 7/7. Full unit suite 1085/1087, the same
two App.test.tsx integration cases failing before and after.
fix(proxy): strip stop param for GitHub Copilot to unblock auto mode classifier (#5404)
Claude Code's auto mode classifier sends `stop_sequences` on every
safety check. On the GitHub Copilot path the Anthropic -> OpenAI Chat
conversion mapped it to `stop`, and Copilot's Chat Completions route
for Claude models answers that request with HTTP 400, so every
classifier call failed while normal chat kept working.
- Remove the converted `stop` field only when the provider is GitHub
Copilot; other OpenAI-compatible upstreams keep it.
- Copilot detection in the transform now also covers providers that
are recognised only through `env.ANTHROPIC_BASE_URL`.
- Regression tests for the Copilot (meta and env URL) and generic
provider cases.
Fixes #5175
fix(codex): keep stored auth when the live snapshot has no credential (#7434)
Editing the current Codex provider seeds the form from the live ~/.codex
snapshot, and switching away backfills that snapshot into the DB row. When a
provider table declares its own credential source (an Authorization header,
env_key, auth/aws), no bearer token is injected into config.toml and the
shared auth.json is deleted on third-party switches, so the live snapshot is
permanently `{ auth: {}, config }` while auth.OPENAI_API_KEY in the DB is the
only copy of the key. Both paths treated that empty live auth as
authoritative and silently erased the stored key, which in turn made the
usage-script modal misread the provider as an official subscription and
reset the saved script.
- Treat a credential-less live auth (missing auth.json or the bare
auth_mode logout marker) as an absent field in both the edit dialog and
the switch-away backfill, keeping the stored auth template. Live still
wins whenever it carries material, and official providers keep the live
login as their source of truth.
- Gate the official exemption on is_codex_official_provider /
resolveCodexOfficialIdentity rather than the category alone: a legacy
fixed official card without a persisted category is still written as
official, so the fallback must not resurrect a logged-out login on
switch-back.
- Cover both layers with regression tests, including the category-less
official shapes.
Fixes #7433
Supersedes #7492
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): lift additional_tools input carriers on the Codex chat conversion (#7454)
Codex talks to third-party providers over its Responses Lite transport,
which ships the request's extra tools in an `additional_tools` input
carrier: `{"type":"additional_tools","role":"developer","tools":[...]}`,
a role but no `content`. The Responses->Chat conversion let that carrier
fall through the generic "item with a role is a message" arm and emitted
`{"role":"system","content":null}` at messages[1], which strict Chat
gateways reject with `messages[1]: missing field "content"` (or
`data did not match any variant of ...SystemMessageContent`). The tools
carried inside it were also dropped on the Chat and Anthropic conversions.
- Skip `additional_tools` items in the Chat message walk; a carrier
declares tools, it is not a message.
- Collect carrier tools through the existing CodexToolContext registry
(dedup, namespace flattening, custom-tool conversion), which also feeds
the Anthropic conversion and the response-side name round-trip.
- Requests without a carrier convert byte-for-byte as before.
Known boundary: `custom` children inside a namespace tool
(`functions.exec`) are still not lifted; that needs its own response-side
name round-trip and is left for a follow-up.
Diagnosed independently in #6158 by BigStrongSun (fix in #6159) and in
fixture.
Fixes #7451
Fixes #6158
Refs #6033
Refs #6776
Supersedes #6159
fix(proxy): normalize Codex detail:original images for Chat gateways (#7476)
Codex marks tool-returned images with detail:"original" whenever the model
catalog advertises supports_image_detail_original, a Responses-only value.
The Responses->Chat conversion copied it verbatim into image_url.detail, and
strict OpenAI-compatible Chat gateways (Ark/Doubao, SenseNova, GLM, vLLM, ...)
reject the whole request with 400. Because Codex replays the full rollout on
every turn, one poisoned view_image result made the session fail permanently,
including plain-text turns.
Two layers:
- tool_media::merge_top_level_detail downgrades original -> auto, so already
poisoned rollouts and Codex versions before 0.155.0 (which do not normalize
replayed history themselves) recover immediately. auto/low/high are untouched.
- codex_catalog_model_entry always writes supports_image_detail_original:false
on the ProxyChat profile, so Codex stops emitting original at the source once
the catalog is regenerated. NativeResponses/Anthropic profiles and the DeepSeek
official catalog are unaffected.
Top-level user images were never affected: responses_content_to_chat_content
drops the part-level detail entirely.
Fixes #6495
Fixes #7458
Refs #6569
Refs #5805
fix(omo): detect unified config on the WSL side when the OpenCode dir is in WSL (#7550)
When the OpenCode config directory points into WSL
(\\wsl.localhost\<distro>\home\<user>\.config\opencode), OMO >= 4.19.3 keeps
its unified config in the WSL-side home (~/.omo/omo.jsonc). Detection only
probed the Windows home, never found it, and silently fell back to the legacy
per-plugin file that OMO no longer reads, so provider switches looked
successful but had no effect.
- Add derive_wsl_home_dir to derive the WSL-side home from a WSL UNC path
(home/<user> or root); non-WSL and non-home paths return None.
- Probe a list of home candidates for the unified config, WSL-side home
first and the OS home as a fallback, shared by the read, write and delete
paths.
- Detection only: when no unified config exists on either side the legacy
fallback is unchanged.
Fixes #7363
Supersedes #7367
docs(partner): add a FluxA purchase link and tighten its sponsor blurb
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.
fix(claude-desktop): expose the 1M context variant for DeepSeek V4 routes
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.
feat(presets): add FluxA Token Plan partner presets
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.
fix(presets): correct AICodeWith Codex-family endpoint to /v1
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.
fix(ui): render monochrome preset icons in the foreground color
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.
docs(user-manual): point Chat-only Codex examples at plan endpoints
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.
fix(pricing): restore DeepSeek V4 Pro price and seed Qwen3.8 and Hy4 models
- 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).
fix(presets): move MiniMax CN and BaiLing to their current official endpoints
- 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.
feat(codex): refresh CN presets from the Responses API audit
- 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.
test(claude-desktop): run Linux path tests on unix hosts and cover Linux in provider sync tests
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.
fix(ui): stop expanding stale cached usage tiers on ineligible provider cards
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.
fix(skills): 支持 skillId 与目录名不一致的技能安装和更新 (#6381)
* fix(skills): resolve mismatched skillId directories
* fix(skills): preserve source path during updates
* fix(skills): refresh source URL after repository relocation
* test(skills): isolate relocation fixture from legacy home
feat(opencode): 支持从获取的模型列表中搜索并批量添加所选模型 (#7515)
* feat: implement fetched models selection and UI components
* fix(opencode): prevent model search from submitting provider form
Add Kimi Global preset variants (kimi.ai platform + api endpoints) (#7526)
Add a "Kimi Global" twin immediately after the domestic "Kimi" API preset and a "Kimi For Coding Global" twin after the domestic "Kimi For Coding" preset, in every preset file that ships them (claude, claude-desktop, codex, hermes, openclaw, opencode, pi).
"Kimi Global" points to the international open platform https://platform.kimi.ai?aff=cc-switch (apiKeyUrl mirrored as https://platform.kimi.ai/console/api-keys?aff=cc-switch) and swaps the endpoint host api.moonshot.cn for api.moonshot.ai with each file's path unchanged (/v1 or /anthropic).
"Kimi For Coding Global" points to the international site https://www.kimi.ai/code/?aff=cc-switch and swaps the endpoint host api.kimi.com for api.kimi.ai with the path unchanged (e.g. https://api.kimi.ai/coding/v1).
Domestic presets are unchanged; category, icons, apiFormat, model catalogs, endpointCandidates (host swapped), partnerPromotionKey and settings otherwise mirror the domestic blocks exactly.
primePartner stays only on the domestic presets so the new variants do not alter prime-partner ordering.
The Pi twins get distinct providerKey values ("cc-switch-kimi-global", "cc-switch-kimi-for-coding-global") because providerKey is the preset-level primary key and must not collide with the domestic presets.
Rebalance sponsor CTAs around Kimi Code plan with dual-region links (#7522)
Kimi Code plan purchases have resumed, so the README sponsor sections go back to leading with the coding plan instead of the API-only flow written during the purchase pause.
All four locales (EN/ZH/JA/DE) now present Kimi Code plan (中文站 | Global) as the primary CTA and the Kimi Open Platform API (中文站 | Global, with the first-top-up bonus) as the secondary one, mirroring the domestic|global split already used for API links.
The aff and track_id values are unchanged.
fix(prompts): refresh active prompts after external file edits (#7194)
Reload the enabled prompt from its live file when the Prompts list loads, and refresh the open panel when the window regains focus, so external edits to CLAUDE.md / AGENTS.md show up without a restart.
- Keep saved templates available when the live file cannot be read.
- Skip the backfill for empty or whitespace-only files, matching enable_prompt.
- Skip the backfill while a sync/restore holds the sync lock, so a list load cannot write a stale live file into a freshly restored database.
fix(skills): raise archive entry limit so large skill repos install (#7489)
Installing hugohe3/ppt-master from skills.sh failed with
ARCHIVE_TOO_MANY_ENTRIES: the GitHub archive we download is the whole
repository (13,248 entries), and the 10,000-entry cap added in #5811
treated it as a zip bomb.
- Raise MAX_ARCHIVE_ENTRIES to 30,000 for both the remote archive and
local ZIP paths, and align WebDAV's MAX_EXTRACT_ENTRIES so a skills.zip
containing such a skill still restores.
- Charge every file entry at least one 4 KiB block against the existing
512 MiB budget, mirroring directory accounting, so empty-file archives
are bounded by the byte budget rather than by the entry count alone.
- Add tests for a 13k-entry repo archive and for the per-file minimum
charge.
Fixes: https://github.com/farion1231/cc-switch/issues/7475
feat: add MiniMax Code harness support (#7383)
Add MiniMax Code (mcode) as a managed application: provider and model management through the native custom_provider configuration, MCP servers, Skills, global instructions (AGENTS.md), read-only local session history with resume, and usage import from the native runtime database. Native files shared by the TUI and desktop are written under the native lock with rollback when the database commit fails, unrelated native configuration is preserved, and the data directory honors MINIMAX_DATA_DIR and MAVIS_DATA_DIR. Deleting sessions stays in the native application because they are coupled to runtime state.
fix(codex): recover stale account bindings during takeover (#7395)
After deleting a managed ChatGPT account and logging in again, a Codex provider can still reference the old local account ID, which made route switching, takeover, startup restoration and rebinding fail permanently.
Distinguish a missing outgoing account from an existing account with no matching live token. A missing account only releases its ownership marker after the persisted account store is verified; the target write policy stays in force. A stale target now reports an account-selection instruction instead of a bare not-found error, including when takeover is already enabled.
Fixes #7392
feat(claude-desktop): support 3P configuration on Linux (#7331)
Add Claude Desktop 3P configuration support on Linux. Resolve the config root from an absolute XDG_CONFIG_HOME, falling back to ~/.config; when CC Switch itself runs as a Flatpak, use the host ~/.config instead of the sandbox-private XDG_CONFIG_HOME. Update the en/zh/ja user manuals with Linux paths and the Flatpak boundary.
Fixes #4855
fix(proxy): omit missing tool descriptions instead of serializing null (#7319)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): keep reasoning effort for grok-4.6 and pass xhigh through verbatim (#7318)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(models): support Zhipu OpenAI Responses model lists (#7330)
Zhipu's Responses endpoint (open.bigmodel.cn/api/v1) serves its model list in the Codex remote catalog shape (models[].slug) instead of the OpenAI-compatible data[].id shape, so the Codex form's model fetch came back empty. The parser now falls back to models[].slug when data is absent.
fix(misc): fetch npm dist-tags from the dedicated endpoint with the probe timeout (#7346)
The tool version probe requested the full npm packument for each package (metadata for every historical version, 14-25 MB uncompressed for codex / opencode / openclaw) only to read its dist-tags, and it was the only latest-version probe without LATEST_PROBE_TIMEOUT, so it fell back to the proxy client's 600 s total timeout and the cards sat on "loading" for minutes on slow links while the large downloads starved the Hermes GitHub probe into "unknown".
Switch fetch_npm_dist_tags to registry.npmjs.org/-/package/{package}/dist-tags, whose body is the dist-tags object itself (tens of bytes to a few KB), escape scoped names as %2f, and apply the same 15 s probe timeout as the GitHub and PyPI probes. No data is lost: the only consumer reads latest and the tool's prerelease tags.
Fixes #7339
feat(usage): display output tokens per second in request log table and detail panel (#3369)
Show tokens/s next to the output token count in the request log table and the request detail panel.
The generation window prefers an explicit duration when present, otherwise latency minus first-token time for streaming requests, and falls back to full latency for non-streaming requests. Rows without a positive token count or window show nothing.
Closes #5936
fix(presets): update APIKEY.FUN URLs to apikey.fan
docs(release): add v3.20.3 release notes
chore(release): v3.20.3
fix(pricing): move retired DeepSeek V4 family to the V4.1 Flash tier
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.
fix(presets): declare third-party Token Plan DeepSeek V4 rows text-only
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.
feat(presets): rename DouBaoSeed preset to localized Volcengine Doubao
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.
fix(codex): skip unchanged incomplete rollout tails
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.
fix(claude): align quick model selection with form order
fix(tray): show bound ChatGPT account quota for managed Codex cards
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
feat(claude): add "Disable Artifact Tool" quick toggle to provider editor
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.
feat(codex): refresh aggregator model presets and catalogs
feat(codex): switch Kimi presets to native Responses direct-connect
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.
fix(usage): show Claude Fable weekly limit from the usage API limits array
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.
fix(codex): mirror vision-capable DeepSeek catalog and un-gate the legacy v4-flash alias (#7286)
Refresh the bundled DeepSeek official Codex catalog to the vendor's current models.json (slug deepseek-flash, image input modality, supports_image_detail_original) and remove deepseek-v4-flash from the confirmed text-only registry: the legacy id is still accepted by DeepSeek and routed to the vision-capable V4.1 Flash, so both the proxy media sanitizer and the generated Codex catalog must fail open for it. deepseek-v4-pro stays text-only per the vendor catalog.
Fixes #7283
fix(proxy): clamp sub-floor max_tokens to the Responses API minimum (#7287)
The Responses API rejects max_output_tokens below 16, but Anthropic clients legitimately send tiny probe budgets (Claude Desktop's model-availability probe uses max_tokens=1). The Anthropic→Responses conversion copied the value verbatim, so strict upstreams returned 400 and Claude Desktop reported the mapped model as unavailable under local routing.
Clamp 1..=15 up to 16 during conversion; 16+, 0 and non-integer values keep their pass-through semantics. Normal session requests convert byte-for-byte as before, and the Codex OAuth path still strips max_output_tokens afterwards.
Fixes #7103
fix(codex): coalesce adjacent commentary with pending tool calls (#7280)
The Codex Responses -> Chat Completions converter emitted two consecutive
assistant messages when one Responses model turn contained a commentary
message directly followed by function_call items. Chat upstreams imitate
the text-only assistant message as a complete turn and return
finish_reason=stop before the expected tool call, so long agent tasks end
after a progress update.
Merge pending tool calls into the directly adjacent assistant message when
it carries no tool_calls yet, attach pending reasoning per segment so
parallel calls do not duplicate it, and keep the new-message path for every
other boundary shape (user/tool boundaries, prior tool-call batches, media
flushes). The reasoning placeholder backfill still applies to coalesced
calls without reasoning.
Fixes #6529
Supersedes #6530
Co-authored-by: BigStrongSun <BigStrongSun@users.noreply.github.com>
fix(proxy): skip empty reasoning_content placeholders in Anthropic SSE streaming (#7227)
Some OpenAI-compatible upstreams (GLM, Qwen, DeepSeek-V4-Pro via ModelScope/DashScope,
Bailian, JD Cloud, ...) keep a `reasoning_content: ""` placeholder in every content
chunk instead of omitting the field. The reasoning branch of the OpenAI Chat -> Anthropic
SSE converter only checked for presence, and it shares `current_non_tool_block_type`
with the content branch, so every content chunk closed the open text block, opened an
empty thinking block, then reopened a text block. N content chunks became 2N content
blocks, half of them empty thinking blocks, which Claude Code rendered as one line per
token with dozens of empty Thought blocks and bloated the session JSONL.
Filter empty reasoning before entering the branch, matching the `!content.is_empty()`
guard the content branch already had and the non-streaming `openai_to_anthropic`
converter. Two regression tests pin block index, type and order: the placeholder shape
must yield a single text block and no thinking_delta at all, and real reasoning must
still yield one thinking block followed by one text block.
Fixes #5028
Fixes #4404
fix(codex): detect growing rollouts with a persisted byte cursor (#7219)
Codex keeps the rollout append handle open for the whole session, so on Windows NTFS the file mtime does not advance while the file grows and the pure-mtime gate skipped growing rollouts forever. Persist the observed byte length in session_log_sync.last_byte_offset for Codex rows and skip a file only when both mtime and size are unchanged. The parser now splits records on newlines manually so an incomplete trailing record is retried on the next pass without advancing the line cursor.
Fixes #6060
fix(codex): proxy image edits and Images API follow-ups (#7177)
* fix(codex): proxy image edits and Images API follow-ups
Follow-ups to #7036 for Codex's built-in ImageGen over the local proxy:
- Register `/images/edits` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them through the shared Codex standalone passthrough.
ImageGen posts to `images/edits` whenever a call references existing
images, and the router returned an empty 404 for it.
- Add `CodexStandaloneEndpoint::ImagesEdits`. Both Images endpoints share
one full-URL suffix table, so a complete Responses / Compact / Chat
Completions / Images URL derives the sibling endpoint (and vice versa)
while opaque full URLs keep failing closed.
- Apply the standalone sibling rewrite when the base URL is pasted as a
complete endpoint with the full-URL switch off, mirroring
`codex_chat_base_is_full_endpoint`; standalone requests no longer become
`.../chat/completions/images/generations`.
- Parse Images API streaming usage: `image_generation.completed` carries
`usage` on the event top level, so scan for `usage.input_tokens` before
falling back to the Chat Completions stream parser.
Related #7036
* fix(codex): match standalone URL suffixes case-insensitively
Keep endpoint detection and sibling rewriting consistent while preserving the original URL prefix and query. Cover mixed-case Images and Alpha Search endpoints through URL and proxy regression cases.
---------
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): preserve per-app settings on shutdown and port allocation (#7210)
On normal exit, stop_with_restore_keep_state() called the legacy global
proxy-config writer to clear the long-retired live_takeover_active flag.
That writer reads the Claude row and runs an UPDATE without a WHERE clause,
so every shutdown copied Claude's max_retries and the three timeout fields
onto the Codex, Gemini and Grok Build rows — and the UPDATE never carried
live_takeover_active in the first place, so the step had no effect beyond
the clobber. The ephemeral listen-port path did the same on every proxy
start.
Drop the dead write-back (live-config restore, backup cleanup and health
reset are unchanged) and persist the resolved ephemeral port through the
global-config interface, which only touches the shared listen/logging
columns. Two regression tests seed distinct per-app settings for all four
apps and assert they survive both paths.
Fixes #7204
fix(provider): preserve child metadata during universal sync (#7212)
Syncing a universal provider regenerates its Claude/Codex/Gemini child
providers. The child's `settings_config` was merged with the existing row,
but `meta`, `created_at` and `sort_index` were taken from the generated
object, and the universal provider never carries any of them from the UI.
The UPDATE therefore wiped the child's usage script, common-config opt-out,
endpoint auto-select and other per-app settings to `{}` and reset the sort
index to NULL, which pushed the card to the bottom of the list and looked
like the provider had been deleted and re-created.
Keep the existing child's `meta`, `created_at` and `sort_index` when the
row already exists. Name, base URL, key, models, website and notes are
still driven by the universal provider, and first-time creation still
inherits the parent's metadata.
Fixes #7134
feat(presets): rebrand DashScope as 千问AI平台 and refresh Qwen models (#7183)
Rename the domestic Bailian presets to 千问AI平台 with a dedicated icon, refresh the Qwen family to the 3.8 generation across Claude Code, Claude Desktop, Codex, Hermes, OpenClaw, OpenCode and Pi, add the domestic Token Plan presets, restore the international QwenCloud Coding Plan presets, and align OpenClaw/OpenCode/Pi model metadata with the official client docs.
fix(codex): honor proxy URL when model_provider is omitted (#7263)
When a Codex provider card's TOML has no `model_provider`, Codex falls back to
its built-in `openai` provider, but takeover wrote the proxy address as a
top-level `base_url` that Codex never reads, so requests bypassed the local
proxy and went straight to api.openai.com.
Treat the missing selector as the built-in `openai` provider and write the
proxy address to `openai_base_url`; the existing legacy-reroute migration then
turns it into a `cc-switch` custom provider table carrying the `PROXY_MANAGED`
bearer, the same shape every other third-party takeover uses. Explicit
provider selections are unchanged.
Refs #6256, #7217
fix(provider): update MiniMax defaults to M3 and remove expired offers (#7255)
* fix(provider): refresh MiniMax M3 presets and remove expired promotions
* fix(provider): complete MiniMax M3 capability declarations
---------
Co-authored-by: saladday <1203511142@qq.com>
chore(provider): remove Atlas Cloud sponsor status, keep presets
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`
docs(release): add v3.20.2 release notes
chore(release): v3.20.2
feat(provider): add SoleAPI sponsor presets across all nine apps
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.
fix(codex): proxy image edits endpoint
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.
fix(provider): pin models URL for JieKou and Novita Claude presets
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.
feat(pricing): refresh seed for September 2026 vendor price changes
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.
fix(codex): stamp requires_openai_auth on takeover writes to match the live login state
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.
fix(updater): show the real reason when an update check fails (#6482)
The updater plugin rejects with a plain string, so `err instanceof Error`
never matched and the real failure reason was replaced by the generic
fallback message. Use the existing extractErrorMessage helper, and pass
the recovered message into the check-update failure toast so it actually
reaches the user instead of stopping at an unread context state.
Co-authored-by: Jason <farion1231@gmail.com>
docs: fix the locales path in the README directory trees (#6100)
All four READMEs showed locales/ as a direct child of src/, but the
translations live at src/i18n/locales/. Split it into two tree nodes so it
matches how the rest of the tree is drawn. Also fixes a CLaude -> Claude
typo in README_ZH.md.
Co-authored-by: Jason <farion1231@gmail.com>
fix(claude): exclude workflow journal.jsonl from session list
Claude Code's workflow feature writes subagents/workflows/wf_*/journal.jsonl inside the session directory. The Claude session scanner only excluded agent-* files, so each journal parsed as an empty ghost session titled "journal". Exclude journal.jsonl by file name and add regression tests.
Fixes #6042
fix(i18n): add the missing pi.form.providerKeyDuplicate and common.collapse keys
translatePiProviderMutationError returns pi.form.providerKeyDuplicate for the Pi backend's duplicate-key error, but the key existed in none of the four locale files, so the toast showed the raw key. common.collapse (aria-label of the thinking-map toggle) was missing the same way. Add both keys to zh/en/ja/zh-TW and lock them in the locale coverage test.
fix(proxy): stop mask_url panicking on multi-byte UTF-8 near the truncation boundary
mask_url's fallback branch (used when the proxy URL fails Url::parse) sliced the raw string at byte 20 and panicked when a multi-byte UTF-8 character straddled that offset, e.g. a CJK string pasted into the global proxy address. Snap the cut to the nearest char boundary at or before 20 and add a regression test.
fix(tests): isolate LOCALAPPDATA so tests stop writing the real Claude Desktop config
On Windows, Claude Desktop config paths resolve from LOCALAPPDATA, which the test home override in tests/support.rs did not redirect, so profile_roundtrip wrote the https://desktop.test fixture into the developer's real Claude Desktop config. Point LOCALAPPDATA at the isolated test home alongside HOME and USERPROFILE.
Fixes #6077
fix(usage): widen pricing model source select to fit localized labels
The pricing model source SelectTrigger in the usage cost settings table was w-28, which leaves about 70px for the label; the English and Japanese labels need about 107px and overflowed the trigger. Widen it to w-40 and drop the h-7 overrides so the trigger and the adjacent Input use the shared h-9 height.
feat(pi): add Tencent TokenHub and Token Plan presets (#7159)
Add eight Tencent presets for Pi, covering the TokenHub pay-as-you-go line
(domestic and international) and the six Token Plan subscription products —
Pi was the only managed app still missing them. Model IDs, ordering and
endpoints follow Tencent's own model lists verbatim, and the tests assert each
product's line-up in order so future drift is caught.
Declare thinking control alongside the capabilities rather than leaving it to
defaults: the 31 DeepSeek entries carry a Tencent-specific compat so selecting
"off" actually disables thinking instead of silently billing reasoning tokens,
the eight Kimi K2.7 Code entries no longer offer an "off" level the model
cannot honour, and kimi-k2.7-code-highspeed is marked image-capable so
tool-returned screenshots are no longer dropped.
Drop minimax-m2.5 from every app: it is gone from Tencent's model tables and
the platform is retiring it.
fix(codex): proxy image generations endpoint (#7036)
Codex's built-in ImageGen calls the legacy OpenAI Images API, but the local
proxy only registered Responses / Chat Completions / Compact / Alpha Search
routes, so `/v1/images/generations` returned an empty 404 under Codex routing.
- Register `/images/generations` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them as a Codex standalone passthrough shared with Alpha Search.
- Generalize the Alpha Search full-URL rewrite into
`rewrite_codex_standalone_full_url`: derive the sibling
`/images/generations` from full Responses / Compact / Chat Completions URLs,
keep an existing Images URL as-is, and fail closed on opaque full URLs.
- Record `usage.input_tokens` / `output_tokens` through the Codex usage parser.
Fixes #5429
Fixes #6745
feat(pricing): add Gemini 3.8 Flash pricing (#7164)
Seeds the built-in pricing row for Gemini 3.8 Flash at 0.75 / 3.75 input/output per million tokens, 0.075 cache read, no cache-write charge, plus a regression test locking all four fields.
Values verified against Google's own pricing page (introductory price through 2026-12-31; list price 1.50 / 7.50 / 0.15 from 2027-01-01) and models.dev.
feat(provider): extend PPIO presets for Pi (#6870)
Add a PPIO provider preset for Pi using the OpenAI-compatible endpoint,
and pin an explicit models URL on the Claude Code PPIO preset so model
discovery resolves against https://api.ppio.com/openai/v1/models instead
of the derived candidates, which all return 404.
Co-authored-by: Jason <farion1231@gmail.com>
feat(pricing): add GLM-5.3 Flash pricing (#7163)
fix(proxy): enable parallel tool calls for Codex OAuth (#7024)
Codex OAuth requests defaulted parallel_tool_calls to false when the incoming
Anthropic request carried no explicit value, forcing Claude Code into one tool
call per model turn. Default it to true (matching codex-rs), map Anthropic
tool_choice.disable_parallel_tool_use to the inverse Responses value so explicit
serial execution is preserved, and add regression coverage for both directions.
Carries #5722 forward on top of current main.
Fixes #5719
Co-authored-by: 太一 <taiyi@douyu.tv>
feat(pricing): add GPT-6 Astra pricing (#7162)
fix(codex): resolve input_modalities for unknown models in vendor catalog (#6750)
When a model slug is not in the bundled DeepSeek official catalog, the vendor path clones the flagship entry and previously inherited its text-only input_modalities. Resolve modalities for unmatched slugs through the same registry/fail-open logic the non-vendor path uses, so a vision variant such as deepseek-v4-flash-vision-exp is no longer declared text-only. Explicit user inputModalities still win; matched models keep the vendor's declaration verbatim.
Fixes #6725
fix(claude): expose Opus 5 and Sonnet 5 in takeover mode (#5882)
Bump the stable client-facing role aliases that takeover mode writes to
Claude Code: Opus claude-opus-4-8 -> claude-opus-5 and Sonnet
claude-sonnet-4-6 -> claude-sonnet-5, so the /model menu matches the
Claude Desktop default routes and preset defaults that already moved to
Opus 5 / Sonnet 5. Add "opus-5" to the adaptive-thinking classifier so
the Bedrock thinking optimizer, which runs on the client alias before
model mapping, keeps sending adaptive thinking instead of the removed
budget_tokens.
Routing is unaffected: the proxy maps client aliases by role keyword, and
the aliases are rewritten automatically the next time the proxy starts.
Fixes #5876
Co-authored-by: Jason <farion1231@gmail.com>
feat(pricing): add GLM-5.3 to built-in model pricing table (#6591)
Seed glm-5.3 at $1.4/$4.4 per MTok with $0.26 cache read, matching the
official Z.ai list price (identical to the GLM-5.2 tier):
https://docs.z.ai/guides/overview/pricing
New model id, so seed only; no repair entry and no SCHEMA_VERSION bump.
Existing installs pick the row up on next launch via INSERT OR IGNORE, and
the startup cost backfill re-prices historical glm-5.3 rows recorded at $0.
fix(codex): align OAuth client identity for GPT-6 (#7132)
Requests for gpt-6-astra through the Claude-to-Codex OAuth takeover route advertised Codex 0.144.1, below the model's 0.153.0 minimal_client_version, so ChatGPT's backend rejected them with HTTP 400 "requires a newer version of Codex". Bump the advertised client version to 0.153.4 and share the originator/version constants between generation and model discovery, which previously sent cc-switch's own package version and originator.
Two regression tests pin the generation headers and the model-list request to a version at or above the gpt-6-astra floor.
Refs #7129
fix(codex): disable supports_search_tool for DeepSeek catalog models (#6653)
DeepSeek's Responses API has no tool_search support, but the bundled
official catalog declared supports_search_tool=true for deepseek-v4-pro
and deepseek-v4-flash. Codex gates deferred/namespaced MCP tools on
this flag (search_tool_enabled = supports_search_tool &&
namespace_tools), so MCP tools were hidden behind a tool_search the
gateway cannot serve — no MCP tool could ever be called.
Set the flag to false for both models so Codex lists MCP tools inline.
Hosted web_search is unaffected (gated by provider capabilities, not
this flag) and DeepSeek's server-side web_search still works.
Fixes #6647
fix(codex): move $ref siblings into allOf for Moonshot/Kimi chat upstreams (#6863)
Moonshot's Chat Completions validator (api.moonshot.cn, api.moonshot.ai and the Kimi For Coding endpoint api.kimi.com) rejects a `$ref` that carries sibling keywords. Codex Desktop's built-in tool schemas carry exactly that shape, so every desktop turn rou…
重放 fork 提交到 upstream/main(f3b18df1,v3.20.2)后统一版本号。
Cargo.lock 同步移除 updater 依赖树(fork 已禁用应用内更新器)。
chore(native): archive tray-left-click-toggle-window
chore(comet): 记录 tray-left-click-toggle-window 验收通过与归档状态
feat(tray): 托盘图标左键单击切换主窗口显示/隐藏
- tray.rs 新增 show_main_window/hide_main_window/toggle_main_window 助手:
显示语义与"打开主界面"一致,隐藏与"关闭到托盘"平台处理一致
(Windows set_skip_taskbar、macOS apply_tray_policy)
- lib.rs 托盘 show_menu_on_left_click 改为 false,左键 Click(Left, Up)
触发切换(过滤 Down/Up 双事件),右键仍弹菜单;Enter/Click 用量刷新保持
- show_main 菜单事件与 macOS Reopen 复用 show_main_window
docs(readme): 更新日志补充连通性测试、双官网链接与托盘左键切换
feat(model-quick-switch): 获取模型列表成功后自动展开选择器
SearchableModelPicker 增加可选 open/onOpenChange 受控展开(不传保持内部自管,
表单等既有用法不受影响);快捷切换弹窗拉取成功且列表非空时直接展开,关闭弹窗复位。
chore(native): archive fix-quick-switch-1m
chore(comet): 记录 fix-quick-switch-1m 验收通过与归档状态
fix(model-quick-switch): 补齐 Haiku 显示名同步,支持不选模型仅翻转 1M
- 显示名同步映射补上 ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME(沿用空/旧base/旧base[1M] 覆盖、自定义保留语义)
- 新增 setClaudeOneMInSettings:仅原地翻转 1M 标记,不改各角色模型 base 与显示名
- 弹窗打开时 1M 开关按供应商当前状态初始化,避免已开 1M 被静默剥离
- claude 未选模型且 1M 状态有变化时允许保存;无可应用变化时保存禁用
- 补单测与四语言 toast 文案
feat(provider-website-links): 供应商支持第二个官网链接,主页横排双链接
- Rust Provider/UniversalProvider 新增 website_url_2(serde websiteUrl2,skip none)
- SQLite providers 表新增 website_url_2 列(schema v18→v19 幂等迁移 + create_tables 补列 + 旧 config.json 迁移)
- DAO SELECT/INSERT/UPDATE/replace 全链路带出新列
- 表单共享 BasicFormFields + 统一供应商表单第二输入框;zod 同第一链接规则校验与回填
- ProviderCard 主页双链接横排、两空格分隔、逐链接 openExternal 打开默认浏览器
- 搜索匹配、复制供应商、统一供应商生成 claude/codex/gemini 随行透传
- gemini_auth packycode 认证检测纳入 website_url_2;ClaudeDesktop/GrokBuild 预设切换清空第二链接;卡片同值链接去重
- 4 语言 i18n + 前端/前端单测覆盖
Comet change dual-website-links-homepage(19/19 验收通过,归档)
feat(connectivity-test): 拉取模型列表改为「搜索添加」选择器交互
- 新增共享组件 SearchableModelMultiPicker:搜索过滤 + 多选 + 已在列表标记 + 已选计数
- ConnectivityTestDialog 获取模型列表后不再全量追加合并去重,改为会话内缓存完整列表并自动弹出一次选择器;用户搜索挑选后添加进表格并自动勾选
- 重新拉取只刷新缓存,已添加进表格的行保留;拉取结果仍仅会话内有效,不写回供应商配置
- 四语言 i18n 新增 searchAdd / addToTest / alreadyInList / selectedCount
- 同步更新 ConnectivityTestDialog 测试并新增 SearchableModelMultiPicker 测试
feat(connectivity-test): 探测目标按供应商 API 格式对齐真实转发链路
- build_claude 按 get_claude_api_format 分派端点与请求体:openai_chat → /v1/chat/completions(复用 transform_claude_request_for_api_format,流式注入 stream_options.include_usage)、openai_responses → /v1/responses、gemini_native → /v1beta/models/{model}:{method}(resolve_gemini_native_url)、anthropic 保持 /v1/messages 并补 anthropic-version/beta 头
- build_codex 按 should_convert_codex_responses_to_chat/anthropic 分派:chat → /chat/completions(responses_to_chat_completions_with_reasoning)、anthropic → /v1/messages(responses_request_to_anthropic,含 max_output_tokens 注入与 [1m] 剥离 + context-1m beta 头)
- 鉴权改用 ClaudeAdapter/CodexAdapter 的 extract_auth + get_auth_headers(与真实出站请求同源:AUTH_TOKEN→Bearer、ANTHROPIC_API_KEY→x-api-key、gemini→x-goog-api-key、GeminiCli OAuth→Bearer、OpenRouter→Bearer、Codex 按 apiKeyField)
- 转换前剥离 [1m] 标记;meta.isFullUrl 或 base 已是完整端点时直接使用 base_url;自定义 body 浅合并移至转换后的最终协议体
- 新增 18 个目标构造断言单测;Comet 归档产物同步提交
chore: archive connectivity-test-optimizations
feat(connectivity-test): 单一模型表格 + 实时拉取模型 + 勾选即测试范围 + 新默认提示词
- 弹窗合并为单一模型表格:打开即全部模型行(初始待测试)、行勾选 + 表头全选(含半选态)、5 格统计卡,移除上方独立选择区
- 获取模型列表:凭据提取抽为共享模块 src/utils/providerCredentials.ts(ModelQuickSwitchDialog 同步改引用),复用 fetch_models_for_config 拉取 /v1/models,按 ID 追加合并去重、仅会话内有效
- 默认提示词改为「你好,你可以帮我做什么事情」:前后端常量同步,merge 差异判断基准跟随
- 勾选即测试范围:后端 connectivity_test_provider_models 新增 modelIds 参数(None/空白回退静态清单),前端逐模型独立 invoke 并行 + 行级流式更新
- 一轮测完自动勾选失败项,全部成功保持原勾选
fix(connectivity-test): UI 四项修复
- 弹窗抬至 alert 层(z-[60]):打开连通性测试时 header 切换栏正确变灰,
详情子弹窗同步抬层(同 z + 更晚挂载 portal 在上)
- 批量检测入口移至 header 图标组(状态提升到 App,ProviderList 经
props 读取探针结果),不再另起一行
- 批量检测可停止:runId 代次守卫(Tauri invoke 无法真正取消),再次
点击即清除 waiting/running 徽标、丢弃在途迟到写入、不再拉起排队项
- 弹窗打开自动勾选模型:已保存默认测试模型 → 供应商当前模型
(claude env.ANTHROPIC_MODEL / codex config.toml model,新增
currentProviderModelId 与后端同口径)→ 清单首个
前端全量 141 文件 / 1106 测试全绿;typecheck clean
chore: archive per-model-connectivity-test
fix(connectivity-test): shouldShowTestEntry 增判 providerType + 收尾清理
chore(connectivity-test): 勾选 Task 9 / OpenSpec 5.1-5.2 + verify 报告
chore(connectivity-test): 勾选 Task 8 / OpenSpec 4.1-4.3
refactor(connectivity-test): 移除旧 stream_check 可达性探测全链路(保留表结构)
- 删除 6 个文件:commands/stream_check.rs、services/stream_check.rs、
database/dao/stream_check.rs、hooks/useStreamCheck.ts、lib/api/connectivity-check.ts、
components/usage/ConnectivityCheckConfigPanel.tsx
- 移除 mod.rs 声明、lib.rs generate_handler! 注册、cleanup_old_stream_check_logs 调用
- 从 SYNC_SKIP_TABLES / SYNC_PRESERVE_TABLES 移除 stream_check_logs(表结构保留在 schema.rs)
- 更新 backup.rs 测试:移除 stream_check_logs fixture 与断言
- 更新 SettingsPage:移除 ConnectivityCheckConfigPanel AccordionItem 与 FlaskConical 导入
- i18n:删除 streamCheck.* 命名空间(zh/en/ja/zh-TW 四语言)
- 更新注释:移除 stream_check 旧引用(forwarder.rs、provider.rs、userAgentPresets.ts、
connectivity_test.rs)
- 修复 4 个 pre-existing clippy lint(connectivity_test: matches! 宏、unused_unit、
derive Default)
- 移除 ProviderList.test.tsx 中已失效的 useStreamCheck mock
chore(connectivity-test): 勾选 Task 7 / OpenSpec 2.4 3.1-3.3
feat(connectivity-test): 供应商列表集成(弹窗入口 + 批量探针徽标)
chore(connectivity-test): 勾选 Task 6 / OpenSpec 2.2-2.3
fix(connectivity-test): 模型列表解析与后端对齐(每条目首个匹配,提取共享 helper)
feat(connectivity-test): 连通性测试弹窗/详情/徽标组件与四语言文案
chore(connectivity-test): 勾选 Task 5 / OpenSpec 2.1
test(connectivity-test): 锁定 toBackendParams 字段映射(customHeaders/customBody 等)
feat(connectivity-test): 前端命令绑定与测试/探针状态 hooks
chore(connectivity-test): 勾选 Task 4 / OpenSpec 1.4
feat(connectivity-test): 测试参数持久化纯函数(缺省即默认值)
chore(connectivity-test): 勾选 Task 3 / OpenSpec 1.3
feat(connectivity-test): Tauri 命令与注册,官方/动态端点过滤,sanitize 隔离私有字段
chore(connectivity-test): 勾选 Task 2 / OpenSpec 1.2
feat(connectivity-test): 后端直连执行器(reqwest+SSE,总/空闲超时,度量与错误识别)
feat(connectivity-test): 后端直连执行器(reqwest+SSE,总/空闲超时,度量与错误识别)
chore(connectivity-test): 勾选 Task 1 / OpenSpec 1.1
fix(connectivity-test): claude 鉴权对齐 adapter 顶层 apiKey/api_key 兜底
feat(connectivity-test): 新增后端连通性测试服务类型/模型解析/协议构造
chore: archive model-quick-switch-picker-scroll
chore: model-quick-switch-picker-scroll 验证报告与状态元数据
fix: 模型快捷切换弹窗内模型列表滚轮无法滚动
模态 Dialog 的 RemoveScroll 会对 shards 外的 portaled PopoverContent
wheel 事件无条件 preventDefault。在 SearchableModelPicker 的
PopoverContent 捕获阶段阻断 wheel 传播,恢复原生滚动。
chore: bump fork version to 3.20.1-1 after upstream v3.20.1 rebase
fix(form): only auto-expand Claude advanced options on divergent model mapping
Uniform mapping produced by the quick-set button (all roles equal to the
fallback model) no longer counts as an advanced value, so the advanced
section stays collapsed on edit. A 1M marker is stripped before comparing
so marked roles are not misjudged as divergent. Truly customized mappings
still auto-expand the section.
feat: align fallback-model quick-set buttons with model input right edge
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
docs: update README with fork build notes and quick-move provider changes
chore: archive comet-native claude-model-quick-access
feat: surface Claude fallback model as quick-access section below endpoint
Move the default fallback model (ANTHROPIC_MODEL) block out of the
collapsed Advanced Options into a quick-access section directly below
the request endpoint: input + 1M checkbox + quick-set + fetch-models
buttons are all reachable without expanding.
- header toggle-all "declare 1M" now includes the fallback model
(Haiku still excluded); partial selection shows indeterminate state
- drop the duplicated quick-set/fetch buttons and the old fallback
block inside Advanced Options
- exclude claudeModel from hasAnyAdvancedValue so a fallback-only
value no longer auto-expands Advanced Options
- quick-set priority, env write semantics, and OAuth preset branches
unchanged; add 4 component tests covering the new layout
chore: archive comet-native provider-right-click-top-bottom
Enable native workflow in .comet/config.yaml and move the Comet managed
state block in .gitignore so the exclusion rules stay grouped with the
other fork tool artifacts.
feat: add provider right-click move-to-top/bottom and insert new providers at second position
- ProviderList: context menu with quick move-to-top/move-to-bottom, reusing
update_providers_sort_order and refreshing tray menu like drag sort
- useAddProviderMutation: default insert at index 1 with explicit sortIndex
materialization for existing items (avoids defined-first sort trap)
- i18n: quickMove* strings for zh/en/zh-TW/ja
ci: stop flaky WSL2 linker job from blocking main
The backend-windows-wsl2 job intermittently fails with LNK1327 /
mt.exe c1010070 when the Windows runner's mt.exe cannot parse a
manifest temp file on the WSL2 UNC TEMP path. The same flaky failure
occurs upstream and is unrelated to fork changes. Mark the job
continue-on-error so main stops going red on it; the result stays
visible in the Actions UI.
style: apply prettier to files touched by recent comet archives
Frontend CI format:check failed on 6 files modified by the app-name
and model-quick-switch archive commits plus the updater-disable commit.
Run prettier --write so format:check passes again.
chore: archive ccs-fork-app-name-cc-switch
chore: archive ccs-model-quick-switch-ui-polish
docs: rewrite README to document fork differences vs upstream
README 从上游完整功能介绍改为仅记录 fork 与上游的差别,完整功能
文档指回上游。涵盖 8 项 fork 魔改:构建标识/开发预览、预设精简、
Claude 表单调整、模型搜索移植、主页模型快速切换、侧边栏面板显隐、
关闭应用内更新、打包范围,并说明版本号与 rebase 同步流程。
feat: disable in-app updater, point users to GitHub releases
移除 tauri-plugin-updater 依赖与自动更新链路,改为统一引导用户到
GitHub Releases 手动下载新版。
- 前端:updater.ts 恒返回 up-to-date;UpdateContext 取消启动自检;
AboutSection「检查更新」直接打开 fork releases 页;DatabaseUpgrade
恢复界面去掉下载进度条,改为打开发布页。
- 后端:移除 updater 插件注册与 UpdaterExt 调用;
install_update_and_restart 降级为空壳,check_app_update_available
返回当前版本号;check_for_updates 指向 fork releases;capabilities
去掉 updater:default 权限。
- 打包:tauri.conf.json 删 plugins.updater、createUpdaterArtifacts=false;
Cargo.toml / package.json 删 updater 依赖,Cargo.lock / pnpm-lock
同步重生成。
- CI:release.yml 移除签名收集与 assemble-latest-json job,删除
.tar.gz/.sig 处理,release body 去掉 updater 说明。
版本号统一为 3.20.0-1(合法 semver 且 MSI 第 4 位为 1)。
Cargo.lock / pnpm-lock.yaml 已随依赖移除重生成,cargo check 通过。
ci: build only Windows x64 and macOS arm64, unsigned
feat: gate dev panel behind dev-only env flag
chore(comet): archive ccs-sidebar-panels-visibility change
fix: apply skills visibility guard to hermes branch toolbar button
feat: include prompts in sidebar panel visibility settings
fix: persist visibleSidebarPanels setting in backend AppSettings
style: use compact pill toggles for sidebar panel visibility
feat: add sidebar panel visibility switches to settings
feat: add sidebar panel visibility settings and wire into toolbar buttons
chore(comet): archive ccs-home-model-display change
fix(fork): read grokbuild api key from config TOML in quick switch dialog
feat(fork): align model badge with upstream extractModelBadge
feat(fork): show current model badge and quick-switch entry on provider cards
feat(fork): add model quick switch dialog
feat(fork): add provider model read/write utils with tests
chore(comet): archive ccs-model-search-port change
feat(i18n): add model search picker keys for all locales
feat(form): port SearchableModelPicker from upstream for searchable model selection
chore(comet): archive ccs-claude-form-layout change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-claude-form-layout/
- Sync claude-form-layout spec to main specs
- Annotate design doc as archived
- Add verification report
fix(i18n): update advanced options hint, add ja/zh-TW toggle-all, drop dead apiFormatHint key
fix(form): correct 1M toggle-all alignment direction (ml-1)
fix(form): align 1M toggle-all checkbox with row checkboxes
refactor(form): move api format select into EndpointField rightSlot
fix(form): widen api format select to fit option text
feat(form): add 1M toggle-all checkbox in model mapping header
feat(form): move api format select next to endpoint input
chore(comet): archive ccs-provider-presets-trim change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-provider-presets-trim/
- Sync provider-presets-visibility spec to main specs
- Annotate design doc as archived
- Add verification report
docs(fork): add presets-trim files to rebase whitelist
test(fork): mock IS_FORK_BUILD false in unit tests for upstream-form semantics
feat(fork): wire official preset filter into provider forms
feat(fork): add hidden field to preset interfaces
feat(fork): add official preset allowlist and filter util
chore(comet): archive ccs-fork-scaffolding change
- Move change artifacts to docs/openspec/changes/archive/2026-08-18-ccs-fork-scaffolding/
- Sync fork-build-identity spec to main specs
- Annotate design doc as archived
- Add verification report
feat(fork): add dev config with isolated identifier and config dir
docs(fork): complete rebase whitelist with all fork-touched files
fix(fork): guard setTitle with isTauri in test/non-tauri env
fix(fork): define __CCS_FORK_BUILD__ in vitest config
feat(fork): add DevPanel and settings footer entry
feat(fork): set macOS window title to CC Switch (Fork) on startup
feat(fork): inject __CCS_FORK_BUILD__ compile-time constant
feat(fork): bump version to 3.19.2-fork.1 and rename product to CC Switch (Fork)
docs(fork): add upstream rebase workflow and ignore tool artifacts
fix(proxy): accept the whole grok-4.x (x>=5) family in the reasoning-effort whitelist (#7369)
Replace the verbatim grok-4.5 / grok-4.6 entries in supports_reasoning_effort with a rule that parses the grok-4.x minor version and accepts x >= 5, mirroring the existing GPT-5+ rule. This covers grok-4.7 (released 2026-09-21), whose reasoning effort was previously dropped on the Claude -> Chat, Claude -> Responses and Codex Responses -> Chat conversion paths, and lets future releases pass without another whitelist edit. The grok-build-* family is retained for saved providers.
Co-authored-by: allenxu09 <171831965+allenxu09@users.noreply.github.com>
docs(release): add v3.20.4 release notes
chore(release): v3.20.4
feat(pricing): add Grok 4.7 and GLM-5.3-FlashX
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.
fix(i18n): remove outdated OpenCode Go referral offer
fix: add SudoCode.chat alternate API endpoint
feat(settings): add a GitHub star prompt to the About card
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
feat(presets): add Soshow aggregator presets
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.
fix(ci): stabilize frontend tests and verify renderer build
fix(mcode): refresh provider state after removal (#7578)
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
fix(lib): reset skip_taskbar before window.show() across entry points (#6348)
5处 window.show() 调用中 4处没有在 show() 前重置 Windows 的 skip_taskbar,导致以下场景出现窗口可见但不在任务栏、点最小化后窗口消失的 bug:
- single_instance handler (lib.rs:360-361):当原进程因 silent_startup / 关闭到托盘等处于 skip_taskbar(true) 状态时,从快捷方式/开始菜单唤起已运行实例触发。
- handle_deeplink_url 的 focus_main_window 分支 (lib.rs:256):通过 ccswitch:// URL 唤起时。
- DB 恢复流程 (lib.rs:556):数据库版本不匹配时显示恢复界面。
- RunEvent::Opened 处理器 (lib.rs:1803):系统接收到 deep-link URL 时。
修复后的行为与 tray.rs::handle_tray_menu_event::show_main 分支一致(那里已有相同的 set_skip_taskbar(false) 调用)。
经用户测试验证:autostart + silent_startup + 从开始菜单/快捷方式启动的场景下,最小化按钮恢复正常工作。
fix(proxy): omit missing tool descriptions in the Codex responses-to-chat converter (#7378)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): preserve max effort for GPT-5.6 and GPT-6 Astra (#7531)
fix(misc): ignore shell startup output when probing tool versions in WSL (#7348)
try_get_version_wsl runs "${SHELL:-sh}" -lic '<tool> --version' through
wsl.exe and takes the first x.y.z anywhere in stdout. A login+interactive
shell runs the distro's startup files first, so their output shares that
stdout. On Ubuntu, /etc/profile.d/update-motd.sh prints the MOTD once a day,
starting with "Welcome to Ubuntu 24.04.4 LTS", and the card then showed
24.04.4 as the Claude Code version. The once-a-day stamp makes it appear
after the first probe of the day and vanish on refresh.
The payload now prints a sentinel before running the tool, and parsing looks
only at what follows its last occurrence. The last occurrence handles the
-lic || -lc || -c fallback chain printing it several times; matching it as a
substring handles OSC sequences glued in front of it. If the sentinel is
absent (the shell failed before the payload ran), the output is used as
before. The stderr fallback for tools that print their version there is kept.
Fixes #7347
Co-authored-by: lyfuci <lyfuci11@gmail.com>
fix(claude): hide session URL attribution (#7053)
* fix(claude): hide session URL attribution
* docs(claude): document session URL attribution
fix(ui): reset provider scroll when switching apps (#5211)
fix(pi): fix provider logo (#6826)
fix(i18n): use the Pi form's own term for the zh-TW duplicate-key message (#7187)
Follow-up to the nit on #6768. In zh-TW the Pi form labels the field
供應商識別碼 and says 供應商識別碼為必填 when it is empty, but the
duplicate message I added said 供應商標識 — the term Hermes, OpenCode and
OpenClaw use for their own field. One form, two names for the same thing.
The other three locales already keep the label and both messages on one
term, so this makes zh-TW match the pattern rather than introduce one:
en Provider key / Provider key is required / This provider key already exists
ja プロバイダーキー / プロバイダーキーを入力してください / このプロバイダーキーは既に存在します
zh 供应商标识 / 供应商标识不能为空 / 该供应商标识已存在
zh-TW 供應商識別碼 / 供應商識別碼為必填 / 該供應商標識已存在 <- the odd one out
hermes.form.providerKeyDuplicate carries the same sentence and is left
alone: its own label is 供應商標識, so it is already consistent.
Verified: managementListLocales 7/7. Full unit suite 1085/1087, the same
two App.test.tsx integration cases failing before and after.
fix(proxy): strip stop param for GitHub Copilot to unblock auto mode classifier (#5404)
Claude Code's auto mode classifier sends `stop_sequences` on every
safety check. On the GitHub Copilot path the Anthropic -> OpenAI Chat
conversion mapped it to `stop`, and Copilot's Chat Completions route
for Claude models answers that request with HTTP 400, so every
classifier call failed while normal chat kept working.
- Remove the converted `stop` field only when the provider is GitHub
Copilot; other OpenAI-compatible upstreams keep it.
- Copilot detection in the transform now also covers providers that
are recognised only through `env.ANTHROPIC_BASE_URL`.
- Regression tests for the Copilot (meta and env URL) and generic
provider cases.
Fixes #5175
fix(codex): keep stored auth when the live snapshot has no credential (#7434)
Editing the current Codex provider seeds the form from the live ~/.codex
snapshot, and switching away backfills that snapshot into the DB row. When a
provider table declares its own credential source (an Authorization header,
env_key, auth/aws), no bearer token is injected into config.toml and the
shared auth.json is deleted on third-party switches, so the live snapshot is
permanently `{ auth: {}, config }` while auth.OPENAI_API_KEY in the DB is the
only copy of the key. Both paths treated that empty live auth as
authoritative and silently erased the stored key, which in turn made the
usage-script modal misread the provider as an official subscription and
reset the saved script.
- Treat a credential-less live auth (missing auth.json or the bare
auth_mode logout marker) as an absent field in both the edit dialog and
the switch-away backfill, keeping the stored auth template. Live still
wins whenever it carries material, and official providers keep the live
login as their source of truth.
- Gate the official exemption on is_codex_official_provider /
resolveCodexOfficialIdentity rather than the category alone: a legacy
fixed official card without a persisted category is still written as
official, so the fallback must not resurrect a logged-out login on
switch-back.
- Cover both layers with regression tests, including the category-less
official shapes.
Fixes #7433
Supersedes #7492
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): lift additional_tools input carriers on the Codex chat conversion (#7454)
Codex talks to third-party providers over its Responses Lite transport,
which ships the request's extra tools in an `additional_tools` input
carrier: `{"type":"additional_tools","role":"developer","tools":[...]}`,
a role but no `content`. The Responses->Chat conversion let that carrier
fall through the generic "item with a role is a message" arm and emitted
`{"role":"system","content":null}` at messages[1], which strict Chat
gateways reject with `messages[1]: missing field "content"` (or
`data did not match any variant of ...SystemMessageContent`). The tools
carried inside it were also dropped on the Chat and Anthropic conversions.
- Skip `additional_tools` items in the Chat message walk; a carrier
declares tools, it is not a message.
- Collect carrier tools through the existing CodexToolContext registry
(dedup, namespace flattening, custom-tool conversion), which also feeds
the Anthropic conversion and the response-side name round-trip.
- Requests without a carrier convert byte-for-byte as before.
Known boundary: `custom` children inside a namespace tool
(`functions.exec`) are still not lifted; that needs its own response-side
name round-trip and is left for a follow-up.
Diagnosed independently in #6158 by BigStrongSun (fix in #6159) and in
fixture.
Fixes #7451
Fixes #6158
Refs #6033
Refs #6776
Supersedes #6159
fix(proxy): normalize Codex detail:original images for Chat gateways (#7476)
Codex marks tool-returned images with detail:"original" whenever the model
catalog advertises supports_image_detail_original, a Responses-only value.
The Responses->Chat conversion copied it verbatim into image_url.detail, and
strict OpenAI-compatible Chat gateways (Ark/Doubao, SenseNova, GLM, vLLM, ...)
reject the whole request with 400. Because Codex replays the full rollout on
every turn, one poisoned view_image result made the session fail permanently,
including plain-text turns.
Two layers:
- tool_media::merge_top_level_detail downgrades original -> auto, so already
poisoned rollouts and Codex versions before 0.155.0 (which do not normalize
replayed history themselves) recover immediately. auto/low/high are untouched.
- codex_catalog_model_entry always writes supports_image_detail_original:false
on the ProxyChat profile, so Codex stops emitting original at the source once
the catalog is regenerated. NativeResponses/Anthropic profiles and the DeepSeek
official catalog are unaffected.
Top-level user images were never affected: responses_content_to_chat_content
drops the part-level detail entirely.
Fixes #6495
Fixes #7458
Refs #6569
Refs #5805
fix(omo): detect unified config on the WSL side when the OpenCode dir is in WSL (#7550)
When the OpenCode config directory points into WSL
(\\wsl.localhost\<distro>\home\<user>\.config\opencode), OMO >= 4.19.3 keeps
its unified config in the WSL-side home (~/.omo/omo.jsonc). Detection only
probed the Windows home, never found it, and silently fell back to the legacy
per-plugin file that OMO no longer reads, so provider switches looked
successful but had no effect.
- Add derive_wsl_home_dir to derive the WSL-side home from a WSL UNC path
(home/<user> or root); non-WSL and non-home paths return None.
- Probe a list of home candidates for the unified config, WSL-side home
first and the OS home as a fallback, shared by the read, write and delete
paths.
- Detection only: when no unified config exists on either side the legacy
fallback is unchanged.
Fixes #7363
Supersedes #7367
docs(partner): add a FluxA purchase link and tighten its sponsor blurb
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.
fix(claude-desktop): expose the 1M context variant for DeepSeek V4 routes
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.
feat(presets): add FluxA Token Plan partner presets
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.
fix(presets): correct AICodeWith Codex-family endpoint to /v1
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.
fix(ui): render monochrome preset icons in the foreground color
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.
docs(user-manual): point Chat-only Codex examples at plan endpoints
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.
fix(pricing): restore DeepSeek V4 Pro price and seed Qwen3.8 and Hy4 models
- 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).
fix(presets): move MiniMax CN and BaiLing to their current official endpoints
- 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.
feat(codex): refresh CN presets from the Responses API audit
- 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.
test(claude-desktop): run Linux path tests on unix hosts and cover Linux in provider sync tests
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.
fix(ui): stop expanding stale cached usage tiers on ineligible provider cards
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.
fix(skills): 支持 skillId 与目录名不一致的技能安装和更新 (#6381)
* fix(skills): resolve mismatched skillId directories
* fix(skills): preserve source path during updates
* fix(skills): refresh source URL after repository relocation
* test(skills): isolate relocation fixture from legacy home
feat(opencode): 支持从获取的模型列表中搜索并批量添加所选模型 (#7515)
* feat: implement fetched models selection and UI components
* fix(opencode): prevent model search from submitting provider form
Add Kimi Global preset variants (kimi.ai platform + api endpoints) (#7526)
Add a "Kimi Global" twin immediately after the domestic "Kimi" API preset and a "Kimi For Coding Global" twin after the domestic "Kimi For Coding" preset, in every preset file that ships them (claude, claude-desktop, codex, hermes, openclaw, opencode, pi).
"Kimi Global" points to the international open platform https://platform.kimi.ai?aff=cc-switch (apiKeyUrl mirrored as https://platform.kimi.ai/console/api-keys?aff=cc-switch) and swaps the endpoint host api.moonshot.cn for api.moonshot.ai with each file's path unchanged (/v1 or /anthropic).
"Kimi For Coding Global" points to the international site https://www.kimi.ai/code/?aff=cc-switch and swaps the endpoint host api.kimi.com for api.kimi.ai with the path unchanged (e.g. https://api.kimi.ai/coding/v1).
Domestic presets are unchanged; category, icons, apiFormat, model catalogs, endpointCandidates (host swapped), partnerPromotionKey and settings otherwise mirror the domestic blocks exactly.
primePartner stays only on the domestic presets so the new variants do not alter prime-partner ordering.
The Pi twins get distinct providerKey values ("cc-switch-kimi-global", "cc-switch-kimi-for-coding-global") because providerKey is the preset-level primary key and must not collide with the domestic presets.
Rebalance sponsor CTAs around Kimi Code plan with dual-region links (#7522)
Kimi Code plan purchases have resumed, so the README sponsor sections go back to leading with the coding plan instead of the API-only flow written during the purchase pause.
All four locales (EN/ZH/JA/DE) now present Kimi Code plan (中文站 | Global) as the primary CTA and the Kimi Open Platform API (中文站 | Global, with the first-top-up bonus) as the secondary one, mirroring the domestic|global split already used for API links.
The aff and track_id values are unchanged.
fix(prompts): refresh active prompts after external file edits (#7194)
Reload the enabled prompt from its live file when the Prompts list loads, and refresh the open panel when the window regains focus, so external edits to CLAUDE.md / AGENTS.md show up without a restart.
- Keep saved templates available when the live file cannot be read.
- Skip the backfill for empty or whitespace-only files, matching enable_prompt.
- Skip the backfill while a sync/restore holds the sync lock, so a list load cannot write a stale live file into a freshly restored database.
fix(skills): raise archive entry limit so large skill repos install (#7489)
Installing hugohe3/ppt-master from skills.sh failed with
ARCHIVE_TOO_MANY_ENTRIES: the GitHub archive we download is the whole
repository (13,248 entries), and the 10,000-entry cap added in #5811
treated it as a zip bomb.
- Raise MAX_ARCHIVE_ENTRIES to 30,000 for both the remote archive and
local ZIP paths, and align WebDAV's MAX_EXTRACT_ENTRIES so a skills.zip
containing such a skill still restores.
- Charge every file entry at least one 4 KiB block against the existing
512 MiB budget, mirroring directory accounting, so empty-file archives
are bounded by the byte budget rather than by the entry count alone.
- Add tests for a 13k-entry repo archive and for the per-file minimum
charge.
Fixes: https://github.com/farion1231/cc-switch/issues/7475
feat: add MiniMax Code harness support (#7383)
Add MiniMax Code (mcode) as a managed application: provider and model management through the native custom_provider configuration, MCP servers, Skills, global instructions (AGENTS.md), read-only local session history with resume, and usage import from the native runtime database. Native files shared by the TUI and desktop are written under the native lock with rollback when the database commit fails, unrelated native configuration is preserved, and the data directory honors MINIMAX_DATA_DIR and MAVIS_DATA_DIR. Deleting sessions stays in the native application because they are coupled to runtime state.
fix(codex): recover stale account bindings during takeover (#7395)
After deleting a managed ChatGPT account and logging in again, a Codex provider can still reference the old local account ID, which made route switching, takeover, startup restoration and rebinding fail permanently.
Distinguish a missing outgoing account from an existing account with no matching live token. A missing account only releases its ownership marker after the persisted account store is verified; the target write policy stays in force. A stale target now reports an account-selection instruction instead of a bare not-found error, including when takeover is already enabled.
Fixes #7392
feat(claude-desktop): support 3P configuration on Linux (#7331)
Add Claude Desktop 3P configuration support on Linux. Resolve the config root from an absolute XDG_CONFIG_HOME, falling back to ~/.config; when CC Switch itself runs as a Flatpak, use the host ~/.config instead of the sandbox-private XDG_CONFIG_HOME. Update the en/zh/ja user manuals with Linux paths and the Flatpak boundary.
Fixes #4855
fix(proxy): omit missing tool descriptions instead of serializing null (#7319)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): keep reasoning effort for grok-4.6 and pass xhigh through verbatim (#7318)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(models): support Zhipu OpenAI Responses model lists (#7330)
Zhipu's Responses endpoint (open.bigmodel.cn/api/v1) serves its model list in the Codex remote catalog shape (models[].slug) instead of the OpenAI-compatible data[].id shape, so the Codex form's model fetch came back empty. The parser now falls back to models[].slug when data is absent.
fix(misc): fetch npm dist-tags from the dedicated endpoint with the probe timeout (#7346)
The tool version probe requested the full npm packument for each package (metadata for every historical version, 14-25 MB uncompressed for codex / opencode / openclaw) only to read its dist-tags, and it was the only latest-version probe without LATEST_PROBE_TIMEOUT, so it fell back to the proxy client's 600 s total timeout and the cards sat on "loading" for minutes on slow links while the large downloads starved the Hermes GitHub probe into "unknown".
Switch fetch_npm_dist_tags to registry.npmjs.org/-/package/{package}/dist-tags, whose body is the dist-tags object itself (tens of bytes to a few KB), escape scoped names as %2f, and apply the same 15 s probe timeout as the GitHub and PyPI probes. No data is lost: the only consumer reads latest and the tool's prerelease tags.
Fixes #7339
feat(usage): display output tokens per second in request log table and detail panel (#3369)
Show tokens/s next to the output token count in the request log table and the request detail panel.
The generation window prefers an explicit duration when present, otherwise latency minus first-token time for streaming requests, and falls back to full latency for non-streaming requests. Rows without a positive token count or window show nothing.
Closes #5936
fix(presets): update APIKEY.FUN URLs to apikey.fan
docs(release): add v3.20.3 release notes
chore(release): v3.20.3
fix(pricing): move retired DeepSeek V4 family to the V4.1 Flash tier
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.
fix(presets): declare third-party Token Plan DeepSeek V4 rows text-only
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.
feat(presets): rename DouBaoSeed preset to localized Volcengine Doubao
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.
fix(codex): skip unchanged incomplete rollout tails
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.
fix(claude): align quick model selection with form order
fix(tray): show bound ChatGPT account quota for managed Codex cards
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
feat(claude): add "Disable Artifact Tool" quick toggle to provider editor
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.
feat(codex): refresh aggregator model presets and catalogs
feat(codex): switch Kimi presets to native Responses direct-connect
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.
fix(usage): show Claude Fable weekly limit from the usage API limits array
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.
fix(codex): mirror vision-capable DeepSeek catalog and un-gate the legacy v4-flash alias (#7286)
Refresh the bundled DeepSeek official Codex catalog to the vendor's current models.json (slug deepseek-flash, image input modality, supports_image_detail_original) and remove deepseek-v4-flash from the confirmed text-only registry: the legacy id is still accepted by DeepSeek and routed to the vision-capable V4.1 Flash, so both the proxy media sanitizer and the generated Codex catalog must fail open for it. deepseek-v4-pro stays text-only per the vendor catalog.
Fixes #7283
fix(proxy): clamp sub-floor max_tokens to the Responses API minimum (#7287)
The Responses API rejects max_output_tokens below 16, but Anthropic clients legitimately send tiny probe budgets (Claude Desktop's model-availability probe uses max_tokens=1). The Anthropic→Responses conversion copied the value verbatim, so strict upstreams returned 400 and Claude Desktop reported the mapped model as unavailable under local routing.
Clamp 1..=15 up to 16 during conversion; 16+, 0 and non-integer values keep their pass-through semantics. Normal session requests convert byte-for-byte as before, and the Codex OAuth path still strips max_output_tokens afterwards.
Fixes #7103
fix(codex): coalesce adjacent commentary with pending tool calls (#7280)
The Codex Responses -> Chat Completions converter emitted two consecutive
assistant messages when one Responses model turn contained a commentary
message directly followed by function_call items. Chat upstreams imitate
the text-only assistant message as a complete turn and return
finish_reason=stop before the expected tool call, so long agent tasks end
after a progress update.
Merge pending tool calls into the directly adjacent assistant message when
it carries no tool_calls yet, attach pending reasoning per segment so
parallel calls do not duplicate it, and keep the new-message path for every
other boundary shape (user/tool boundaries, prior tool-call batches, media
flushes). The reasoning placeholder backfill still applies to coalesced
calls without reasoning.
Fixes #6529
Supersedes #6530
Co-authored-by: BigStrongSun <BigStrongSun@users.noreply.github.com>
fix(proxy): skip empty reasoning_content placeholders in Anthropic SSE streaming (#7227)
Some OpenAI-compatible upstreams (GLM, Qwen, DeepSeek-V4-Pro via ModelScope/DashScope,
Bailian, JD Cloud, ...) keep a `reasoning_content: ""` placeholder in every content
chunk instead of omitting the field. The reasoning branch of the OpenAI Chat -> Anthropic
SSE converter only checked for presence, and it shares `current_non_tool_block_type`
with the content branch, so every content chunk closed the open text block, opened an
empty thinking block, then reopened a text block. N content chunks became 2N content
blocks, half of them empty thinking blocks, which Claude Code rendered as one line per
token with dozens of empty Thought blocks and bloated the session JSONL.
Filter empty reasoning before entering the branch, matching the `!content.is_empty()`
guard the content branch already had and the non-streaming `openai_to_anthropic`
converter. Two regression tests pin block index, type and order: the placeholder shape
must yield a single text block and no thinking_delta at all, and real reasoning must
still yield one thinking block followed by one text block.
Fixes #5028
Fixes #4404
fix(codex): detect growing rollouts with a persisted byte cursor (#7219)
Codex keeps the rollout append handle open for the whole session, so on Windows NTFS the file mtime does not advance while the file grows and the pure-mtime gate skipped growing rollouts forever. Persist the observed byte length in session_log_sync.last_byte_offset for Codex rows and skip a file only when both mtime and size are unchanged. The parser now splits records on newlines manually so an incomplete trailing record is retried on the next pass without advancing the line cursor.
Fixes #6060
fix(codex): proxy image edits and Images API follow-ups (#7177)
* fix(codex): proxy image edits and Images API follow-ups
Follow-ups to #7036 for Codex's built-in ImageGen over the local proxy:
- Register `/images/edits` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them through the shared Codex standalone passthrough.
ImageGen posts to `images/edits` whenever a call references existing
images, and the router returned an empty 404 for it.
- Add `CodexStandaloneEndpoint::ImagesEdits`. Both Images endpoints share
one full-URL suffix table, so a complete Responses / Compact / Chat
Completions / Images URL derives the sibling endpoint (and vice versa)
while opaque full URLs keep failing closed.
- Apply the standalone sibling rewrite when the base URL is pasted as a
complete endpoint with the full-URL switch off, mirroring
`codex_chat_base_is_full_endpoint`; standalone requests no longer become
`.../chat/completions/images/generations`.
- Parse Images API streaming usage: `image_generation.completed` carries
`usage` on the event top level, so scan for `usage.input_tokens` before
falling back to the Chat Completions stream parser.
Related #7036
* fix(codex): match standalone URL suffixes case-insensitively
Keep endpoint detection and sibling rewriting consistent while preserving the original URL prefix and query. Cover mixed-case Images and Alpha Search endpoints through URL and proxy regression cases.
---------
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): preserve per-app settings on shutdown and port allocation (#7210)
On normal exit, stop_with_restore_keep_state() called the legacy global
proxy-config writer to clear the long-retired live_takeover_active flag.
That writer reads the Claude row and runs an UPDATE without a WHERE clause,
so every shutdown copied Claude's max_retries and the three timeout fields
onto the Codex, Gemini and Grok Build rows — and the UPDATE never carried
live_takeover_active in the first place, so the step had no effect beyond
the clobber. The ephemeral listen-port path did the same on every proxy
start.
Drop the dead write-back (live-config restore, backup cleanup and health
reset are unchanged) and persist the resolved ephemeral port through the
global-config interface, which only touches the shared listen/logging
columns. Two regression tests seed distinct per-app settings for all four
apps and assert they survive both paths.
Fixes #7204
fix(provider): preserve child metadata during universal sync (#7212)
Syncing a universal provider regenerates its Claude/Codex/Gemini child
providers. The child's `settings_config` was merged with the existing row,
but `meta`, `created_at` and `sort_index` were taken from the generated
object, and the universal provider never carries any of them from the UI.
The UPDATE therefore wiped the child's usage script, common-config opt-out,
endpoint auto-select and other per-app settings to `{}` and reset the sort
index to NULL, which pushed the card to the bottom of the list and looked
like the provider had been deleted and re-created.
Keep the existing child's `meta`, `created_at` and `sort_index` when the
row already exists. Name, base URL, key, models, website and notes are
still driven by the universal provider, and first-time creation still
inherits the parent's metadata.
Fixes #7134
feat(presets): rebrand DashScope as 千问AI平台 and refresh Qwen models (#7183)
Rename the domestic Bailian presets to 千问AI平台 with a dedicated icon, refresh the Qwen family to the 3.8 generation across Claude Code, Claude Desktop, Codex, Hermes, OpenClaw, OpenCode and Pi, add the domestic Token Plan presets, restore the international QwenCloud Coding Plan presets, and align OpenClaw/OpenCode/Pi model metadata with the official client docs.
fix(codex): honor proxy URL when model_provider is omitted (#7263)
When a Codex provider card's TOML has no `model_provider`, Codex falls back to
its built-in `openai` provider, but takeover wrote the proxy address as a
top-level `base_url` that Codex never reads, so requests bypassed the local
proxy and went straight to api.openai.com.
Treat the missing selector as the built-in `openai` provider and write the
proxy address to `openai_base_url`; the existing legacy-reroute migration then
turns it into a `cc-switch` custom provider table carrying the `PROXY_MANAGED`
bearer, the same shape every other third-party takeover uses. Explicit
provider selections are unchanged.
Refs #6256, #7217
fix(provider): update MiniMax defaults to M3 and remove expired offers (#7255)
* fix(provider): refresh MiniMax M3 presets and remove expired promotions
* fix(provider): complete MiniMax M3 capability declarations
---------
Co-authored-by: saladday <1203511142@qq.com>
chore(provider): remove Atlas Cloud sponsor status, keep presets
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`
docs(release): add v3.20.2 release notes
chore(release): v3.20.2
feat(provider): add SoleAPI sponsor presets across all nine apps
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.
fix(codex): proxy image edits endpoint
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.
fix(pro…
修复 CI Frontend Checks 的 Check formatting 步骤失败
(App.tsx、ConnectivityTestDialog、ProviderCard、ProviderList、
provider schema 共 5 个文件)。
style: apply cargo fmt to connectivity-test era backend files
修复 CI 的 Check Rust formatting 步骤失败(connectivity_test、
backup、tests、provider 等文件格式不符合 rustfmt 规范)。
chore(native): archive sync-upstream
docs(fork): update rebase whitelist for connectivity-test era fork features
- 白名单补充连通性测试前后端、模型快捷切换、双官网链接测试、
CI 裁剪版、README/CHANGELOG、.comet/superpowers 产物等 fork 专属文件
- 新增共享文件 fork 专属语义表(托盘左键切换、禁用更新器、hidden 字段等)
- 修正 §3.1 rebase 方向说明:rebase 中 fork 侧是 --theirs 而非 --ours
(v3.20.2 同步实测确认,与 merge 语义相反)
chore: bump fork version to 3.20.2-1 after upstream v3.20.2 rebase
重放 fork 提交到 upstream/main(f3b18df1,v3.20.2)后统一版本号。
Cargo.lock 同步移除 updater 依赖树(fork 已禁用应用内更新器)。
chore(native): archive tray-left-click-toggle-window
chore(comet): 记录 tray-left-click-toggle-window 验收通过与归档状态
feat(tray): 托盘图标左键单击切换主窗口显示/隐藏
- tray.rs 新增 show_main_window/hide_main_window/toggle_main_window 助手:
显示语义与"打开主界面"一致,隐藏与"关闭到托盘"平台处理一致
(Windows set_skip_taskbar、macOS apply_tray_policy)
- lib.rs 托盘 show_menu_on_left_click 改为 false,左键 Click(Left, Up)
触发切换(过滤 Down/Up 双事件),右键仍弹菜单;Enter/Click 用量刷新保持
- show_main 菜单事件与 macOS Reopen 复用 show_main_window
docs(readme): 更新日志补充连通性测试、双官网链接与托盘左键切换
feat(model-quick-switch): 获取模型列表成功后自动展开选择器
SearchableModelPicker 增加可选 open/onOpenChange 受控展开(不传保持内部自管,
表单等既有用法不受影响);快捷切换弹窗拉取成功且列表非空时直接展开,关闭弹窗复位。
chore(native): archive fix-quick-switch-1m
chore(comet): 记录 fix-quick-switch-1m 验收通过与归档状态
fix(model-quick-switch): 补齐 Haiku 显示名同步,支持不选模型仅翻转 1M
- 显示名同步映射补上 ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME(沿用空/旧base/旧base[1M] 覆盖、自定义保留语义)
- 新增 setClaudeOneMInSettings:仅原地翻转 1M 标记,不改各角色模型 base 与显示名
- 弹窗打开时 1M 开关按供应商当前状态初始化,避免已开 1M 被静默剥离
- claude 未选模型且 1M 状态有变化时允许保存;无可应用变化时保存禁用
- 补单测与四语言 toast 文案
feat(provider-website-links): 供应商支持第二个官网链接,主页横排双链接
- Rust Provider/UniversalProvider 新增 website_url_2(serde websiteUrl2,skip none)
- SQLite providers 表新增 website_url_2 列(schema v18→v19 幂等迁移 + create_tables 补列 + 旧 config.json 迁移)
- DAO SELECT/INSERT/UPDATE/replace 全链路带出新列
- 表单共享 BasicFormFields + 统一供应商表单第二输入框;zod 同第一链接规则校验与回填
- ProviderCard 主页双链接横排、两空格分隔、逐链接 openExternal 打开默认浏览器
- 搜索匹配、复制供应商、统一供应商生成 claude/codex/gemini 随行透传
- gemini_auth packycode 认证检测纳入 website_url_2;ClaudeDesktop/GrokBuild 预设切换清空第二链接;卡片同值链接去重
- 4 语言 i18n + 前端/前端单测覆盖
Comet change dual-website-links-homepage(19/19 验收通过,归档)
feat(connectivity-test): 拉取模型列表改为「搜索添加」选择器交互
- 新增共享组件 SearchableModelMultiPicker:搜索过滤 + 多选 + 已在列表标记 + 已选计数
- ConnectivityTestDialog 获取模型列表后不再全量追加合并去重,改为会话内缓存完整列表并自动弹出一次选择器;用户搜索挑选后添加进表格并自动勾选
- 重新拉取只刷新缓存,已添加进表格的行保留;拉取结果仍仅会话内有效,不写回供应商配置
- 四语言 i18n 新增 searchAdd / addToTest / alreadyInList / selectedCount
- 同步更新 ConnectivityTestDialog 测试并新增 SearchableModelMultiPicker 测试
feat(connectivity-test): 探测目标按供应商 API 格式对齐真实转发链路
- build_claude 按 get_claude_api_format 分派端点与请求体:openai_chat → /v1/chat/completions(复用 transform_claude_request_for_api_format,流式注入 stream_options.include_usage)、openai_responses → /v1/responses、gemini_native → /v1beta/models/{model}:{method}(resolve_gemini_native_url)、anthropic 保持 /v1/messages 并补 anthropic-version/beta 头
- build_codex 按 should_convert_codex_responses_to_chat/anthropic 分派:chat → /chat/completions(responses_to_chat_completions_with_reasoning)、anthropic → /v1/messages(responses_request_to_anthropic,含 max_output_tokens 注入与 [1m] 剥离 + context-1m beta 头)
- 鉴权改用 ClaudeAdapter/CodexAdapter 的 extract_auth + get_auth_headers(与真实出站请求同源:AUTH_TOKEN→Bearer、ANTHROPIC_API_KEY→x-api-key、gemini→x-goog-api-key、GeminiCli OAuth→Bearer、OpenRouter→Bearer、Codex 按 apiKeyField)
- 转换前剥离 [1m] 标记;meta.isFullUrl 或 base 已是完整端点时直接使用 base_url;自定义 body 浅合并移至转换后的最终协议体
- 新增 18 个目标构造断言单测;Comet 归档产物同步提交
chore: archive connectivity-test-optimizations
feat(connectivity-test): 单一模型表格 + 实时拉取模型 + 勾选即测试范围 + 新默认提示词
- 弹窗合并为单一模型表格:打开即全部模型行(初始待测试)、行勾选 + 表头全选(含半选态)、5 格统计卡,移除上方独立选择区
- 获取模型列表:凭据提取抽为共享模块 src/utils/providerCredentials.ts(ModelQuickSwitchDialog 同步改引用),复用 fetch_models_for_config 拉取 /v1/models,按 ID 追加合并去重、仅会话内有效
- 默认提示词改为「你好,你可以帮我做什么事情」:前后端常量同步,merge 差异判断基准跟随
- 勾选即测试范围:后端 connectivity_test_provider_models 新增 modelIds 参数(None/空白回退静态清单),前端逐模型独立 invoke 并行 + 行级流式更新
- 一轮测完自动勾选失败项,全部成功保持原勾选
fix(connectivity-test): UI 四项修复
- 弹窗抬至 alert 层(z-[60]):打开连通性测试时 header 切换栏正确变灰,
详情子弹窗同步抬层(同 z + 更晚挂载 portal 在上)
- 批量检测入口移至 header 图标组(状态提升到 App,ProviderList 经
props 读取探针结果),不再另起一行
- 批量检测可停止:runId 代次守卫(Tauri invoke 无法真正取消),再次
点击即清除 waiting/running 徽标、丢弃在途迟到写入、不再拉起排队项
- 弹窗打开自动勾选模型:已保存默认测试模型 → 供应商当前模型
(claude env.ANTHROPIC_MODEL / codex config.toml model,新增
currentProviderModelId 与后端同口径)→ 清单首个
前端全量 141 文件 / 1106 测试全绿;typecheck clean
chore: archive per-model-connectivity-test
fix(connectivity-test): shouldShowTestEntry 增判 providerType + 收尾清理
chore(connectivity-test): 勾选 Task 9 / OpenSpec 5.1-5.2 + verify 报告
chore(connectivity-test): 勾选 Task 8 / OpenSpec 4.1-4.3
refactor(connectivity-test): 移除旧 stream_check 可达性探测全链路(保留表结构)
- 删除 6 个文件:commands/stream_check.rs、services/stream_check.rs、
database/dao/stream_check.rs、hooks/useStreamCheck.ts、lib/api/connectivity-check.ts、
components/usage/ConnectivityCheckConfigPanel.tsx
- 移除 mod.rs 声明、lib.rs generate_handler! 注册、cleanup_old_stream_check_logs 调用
- 从 SYNC_SKIP_TABLES / SYNC_PRESERVE_TABLES 移除 stream_check_logs(表结构保留在 schema.rs)
- 更新 backup.rs 测试:移除 stream_check_logs fixture 与断言
- 更新 SettingsPage:移除 ConnectivityCheckConfigPanel AccordionItem 与 FlaskConical 导入
- i18n:删除 streamCheck.* 命名空间(zh/en/ja/zh-TW 四语言)
- 更新注释:移除 stream_check 旧引用(forwarder.rs、provider.rs、userAgentPresets.ts、
connectivity_test.rs)
- 修复 4 个 pre-existing clippy lint(connectivity_test: matches! 宏、unused_unit、
derive Default)
- 移除 ProviderList.test.tsx 中已失效的 useStreamCheck mock
chore(connectivity-test): 勾选 Task 7 / OpenSpec 2.4 3.1-3.3
feat(connectivity-test): 供应商列表集成(弹窗入口 + 批量探针徽标)
chore(connectivity-test): 勾选 Task 6 / OpenSpec 2.2-2.3
fix(connectivity-test): 模型列表解析与后端对齐(每条目首个匹配,提取共享 helper)
feat(connectivity-test): 连通性测试弹窗/详情/徽标组件与四语言文案
chore(connectivity-test): 勾选 Task 5 / OpenSpec 2.1
test(connectivity-test): 锁定 toBackendParams 字段映射(customHeaders/customBody 等)
feat(connectivity-test): 前端命令绑定与测试/探针状态 hooks
chore(connectivity-test): 勾选 Task 4 / OpenSpec 1.4
feat(connectivity-test): 测试参数持久化纯函数(缺省即默认值)
chore(connectivity-test): 勾选 Task 3 / OpenSpec 1.3
feat(connectivity-test): Tauri 命令与注册,官方/动态端点过滤,sanitize 隔离私有字段
chore(connectivity-test): 勾选 Task 2 / OpenSpec 1.2
feat(connectivity-test): 后端直连执行器(reqwest+SSE,总/空闲超时,度量与错误识别)
feat(connectivity-test): 后端直连执行器(reqwest+SSE,总/空闲超时,度量与错误识别)
chore(connectivity-test): 勾选 Task 1 / OpenSpec 1.1
fix(connectivity-test): claude 鉴权对齐 adapter 顶层 apiKey/api_key 兜底
feat(connectivity-test): 新增后端连通性测试服务类型/模型解析/协议构造
chore: archive model-quick-switch-picker-scroll
chore: model-quick-switch-picker-scroll 验证报告与状态元数据
fix: 模型快捷切换弹窗内模型列表滚轮无法滚动
模态 Dialog 的 RemoveScroll 会对 shards 外的 portaled PopoverContent
wheel 事件无条件 preventDefault。在 SearchableModelPicker 的
PopoverContent 捕获阶段阻断 wheel 传播,恢复原生滚动。
chore: bump fork version to 3.20.1-1 after upstream v3.20.1 rebase
fix(form): only auto-expand Claude advanced options on divergent model mapping
Uniform mapping produced by the quick-set button (all roles equal to the
fallback model) no longer counts as an advanced value, so the advanced
section stays collapsed on edit. A 1M marker is stripped before comparing
so marked roles are not misjudged as divergent. Truly customized mappings
still auto-expand the section.
feat: align fallback-model quick-set buttons with model input right edge
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
docs: update README with fork build notes and quick-move provider changes
chore: archive comet-native claude-model-quick-access
feat: surface Claude fallback model as quick-access section below endpoint
Move the default fallback model (ANTHROPIC_MODEL) block out of the
collapsed Advanced Options into a quick-access section directly below
the request endpoint: input + 1M checkbox + quick-set + fetch-models
buttons are all reachable without expanding.
- header toggle-all "declare 1M" now includes the fallback model
(Haiku still excluded); partial selection shows indeterminate state
- drop the duplicated quick-set/fetch buttons and the old fallback
block inside Advanced Options
- exclude claudeModel from hasAnyAdvancedValue so a fallback-only
value no longer auto-expands Advanced Options
- quick-set priority, env write semantics, and OAuth preset branches
unchanged; add 4 component tests covering the new layout
chore: archive comet-native provider-right-click-top-bottom
Enable native workflow in .comet/config.yaml and move the Comet managed
state block in .gitignore so the exclusion rules stay grouped with the
other fork tool artifacts.
feat: add provider right-click move-to-top/bottom and insert new providers at second position
- ProviderList: context menu with quick move-to-top/move-to-bottom, reusing
update_providers_sort_order and refreshing tray menu like drag sort
- useAddProviderMutation: default insert at index 1 with explicit sortIndex
materialization for existing items (avoids defined-first sort trap)
- i18n: quickMove* strings for zh/en/zh-TW/ja
ci: stop flaky WSL2 linker job from blocking main
The backend-windows-wsl2 job intermittently fails with LNK1327 /
mt.exe c1010070 when the Windows runner's mt.exe cannot parse a
manifest temp file on the WSL2 UNC TEMP path. The same flaky failure
occurs upstream and is unrelated to fork changes. Mark the job
continue-on-error so main stops going red on it; the result stays
visible in the Actions UI.
style: apply prettier to files touched by recent comet archives
Frontend CI format:check failed on 6 files modified by the app-name
and model-quick-switch archive commits plus the updater-disable commit.
Run prettier --write so format:check passes again.
chore: archive ccs-fork-app-name-cc-switch
chore: archive ccs-model-quick-switch-ui-polish
docs: rewrite README to document fork differences vs upstream
README 从上游完整功能介绍改为仅记录 fork 与上游的差别,完整功能
文档指回上游。涵盖 8 项 fork 魔改:构建标识/开发预览、预设精简、
Claude 表单调整、模型搜索移植、主页模型快速切换、侧边栏面板显隐、
关闭应用内更新、打包范围,并说明版本号与 rebase 同步流程。
feat: disable in-app updater, point users to GitHub releases
移除 tauri-plugin-updater 依赖与自动更新链路,改为统一引导用户到
GitHub Releases 手动下载新版。
- 前端:updater.ts 恒返回 up-to-date;UpdateContext 取消启动自检;
AboutSection「检查更新」直接打开 fork releases 页;DatabaseUpgrade
恢复界面去掉下载进度条,改为打开发布页。
- 后端:移除 updater 插件注册与 UpdaterExt 调用;
install_update_and_restart 降级为空壳,check_app_update_available
返回当前版本号;check_for_updates 指向 fork releases;capabilities
去掉 updater:default 权限。
- 打包:tauri.conf.json 删 plugins.updater、createUpdaterArtifacts=false;
Cargo.toml / package.json 删 updater 依赖,Cargo.lock / pnpm-lock
同步重生成。
- CI:release.yml 移除签名收集与 assemble-latest-json job,删除
.tar.gz/.sig 处理,release body 去掉 updater 说明。
版本号统一为 3.20.0-1(合法 semver 且 MSI 第 4 位为 1)。
Cargo.lock / pnpm-lock.yaml 已随依赖移除重生成,cargo check 通过。
ci: build only Windows x64 and macOS arm64, unsigned
feat: gate dev panel behind dev-only env flag
chore(comet): archive ccs-sidebar-panels-visibility change
fix: apply skills visibility guard to hermes branch toolbar button
feat: include prompts in sidebar panel visibility settings
fix: persist visibleSidebarPanels setting in backend AppSettings
style: use compact pill toggles for sidebar panel visibility
feat: add sidebar panel visibility switches to settings
feat: add sidebar panel visibility settings and wire into toolbar buttons
chore(comet): archive ccs-home-model-display change
fix(fork): read grokbuild api key from config TOML in quick switch dialog
feat(fork): align model badge with upstream extractModelBadge
feat(fork): show current model badge and quick-switch entry on provider cards
feat(fork): add model quick switch dialog
feat(fork): add provider model read/write utils with tests
chore(comet): archive ccs-model-search-port change
feat(i18n): add model search picker keys for all locales
feat(form): port SearchableModelPicker from upstream for searchable model selection
chore(comet): archive ccs-claude-form-layout change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-claude-form-layout/
- Sync claude-form-layout spec to main specs
- Annotate design doc as archived
- Add verification report
fix(i18n): update advanced options hint, add ja/zh-TW toggle-all, drop dead apiFormatHint key
fix(form): correct 1M toggle-all alignment direction (ml-1)
fix(form): align 1M toggle-all checkbox with row checkboxes
refactor(form): move api format select into EndpointField rightSlot
fix(form): widen api format select to fit option text
feat(form): add 1M toggle-all checkbox in model mapping header
feat(form): move api format select next to endpoint input
chore(comet): archive ccs-provider-presets-trim change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-provider-presets-trim/
- Sync provider-presets-visibility spec to main specs
- Annotate design doc as archived
- Add verification report
docs(fork): add presets-trim files to rebase whitelist
test(fork): mock IS_FORK_BUILD false in unit tests for upstream-form semantics
feat(fork): wire official preset filter into provider forms
feat(fork): add hidden field to preset interfaces
feat(fork): add official preset allowlist and filter util
chore(comet): archive ccs-fork-scaffolding change
- Move change artifacts to docs/openspec/changes/archive/2026-08-18-ccs-fork-scaffolding/
- Sync fork-build-identity spec to main specs
- Annotate design doc as archived
- Add verification report
feat(fork): add dev config with isolated identifier and config dir
docs(fork): complete rebase whitelist with all fork-touched files
fix(fork): guard setTitle with isTauri in test/non-tauri env
fix(fork): define __CCS_FORK_BUILD__ in vitest config
feat(fork): add DevPanel and settings footer entry
feat(fork): set macOS window title to CC Switch (Fork) on startup
feat(fork): inject __CCS_FORK_BUILD__ compile-time constant
feat(fork): bump version to 3.19.2-fork.1 and rename product to CC Switch (Fork)
docs(fork): add upstream rebase workflow and ignore tool artifacts
fix(proxy): accept the whole grok-4.x (x>=5) family in the reasoning-effort whitelist (#7369)
Replace the verbatim grok-4.5 / grok-4.6 entries in supports_reasoning_effort with a rule that parses the grok-4.x minor version and accepts x >= 5, mirroring the existing GPT-5+ rule. This covers grok-4.7 (released 2026-09-21), whose reasoning effort was previously dropped on the Claude -> Chat, Claude -> Responses and Codex Responses -> Chat conversion paths, and lets future releases pass without another whitelist edit. The grok-build-* family is retained for saved providers.
Co-authored-by: allenxu09 <171831965+allenxu09@users.noreply.github.com>
docs(release): add v3.20.4 release notes
chore(release): v3.20.4
feat(pricing): add Grok 4.7 and GLM-5.3-FlashX
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.
fix(i18n): remove outdated OpenCode Go referral offer
fix: add SudoCode.chat alternate API endpoint
feat(settings): add a GitHub star prompt to the About card
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
feat(presets): add Soshow aggregator presets
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.
fix(ci): stabilize frontend tests and verify renderer build
fix(mcode): refresh provider state after removal (#7578)
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
fix(lib): reset skip_taskbar before window.show() across entry points (#6348)
5处 window.show() 调用中 4处没有在 show() 前重置 Windows 的 skip_taskbar,导致以下场景出现窗口可见但不在任务栏、点最小化后窗口消失的 bug:
- single_instance handler (lib.rs:360-361):当原进程因 silent_startup / 关闭到托盘等处于 skip_taskbar(true) 状态时,从快捷方式/开始菜单唤起已运行实例触发。
- handle_deeplink_url 的 focus_main_window 分支 (lib.rs:256):通过 ccswitch:// URL 唤起时。
- DB 恢复流程 (lib.rs:556):数据库版本不匹配时显示恢复界面。
- RunEvent::Opened 处理器 (lib.rs:1803):系统接收到 deep-link URL 时。
修复后的行为与 tray.rs::handle_tray_menu_event::show_main 分支一致(那里已有相同的 set_skip_taskbar(false) 调用)。
经用户测试验证:autostart + silent_startup + 从开始菜单/快捷方式启动的场景下,最小化按钮恢复正常工作。
fix(proxy): omit missing tool descriptions in the Codex responses-to-chat converter (#7378)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): preserve max effort for GPT-5.6 and GPT-6 Astra (#7531)
fix(misc): ignore shell startup output when probing tool versions in WSL (#7348)
try_get_version_wsl runs "${SHELL:-sh}" -lic '<tool> --version' through
wsl.exe and takes the first x.y.z anywhere in stdout. A login+interactive
shell runs the distro's startup files first, so their output shares that
stdout. On Ubuntu, /etc/profile.d/update-motd.sh prints the MOTD once a day,
starting with "Welcome to Ubuntu 24.04.4 LTS", and the card then showed
24.04.4 as the Claude Code version. The once-a-day stamp makes it appear
after the first probe of the day and vanish on refresh.
The payload now prints a sentinel before running the tool, and parsing looks
only at what follows its last occurrence. The last occurrence handles the
-lic || -lc || -c fallback chain printing it several times; matching it as a
substring handles OSC sequences glued in front of it. If the sentinel is
absent (the shell failed before the payload ran), the output is used as
before. The stderr fallback for tools that print their version there is kept.
Fixes #7347
Co-authored-by: lyfuci <lyfuci11@gmail.com>
fix(claude): hide session URL attribution (#7053)
* fix(claude): hide session URL attribution
* docs(claude): document session URL attribution
fix(ui): reset provider scroll when switching apps (#5211)
fix(pi): fix provider logo (#6826)
fix(i18n): use the Pi form's own term for the zh-TW duplicate-key message (#7187)
Follow-up to the nit on #6768. In zh-TW the Pi form labels the field
供應商識別碼 and says 供應商識別碼為必填 when it is empty, but the
duplicate message I added said 供應商標識 — the term Hermes, OpenCode and
OpenClaw use for their own field. One form, two names for the same thing.
The other three locales already keep the label and both messages on one
term, so this makes zh-TW match the pattern rather than introduce one:
en Provider key / Provider key is required / This provider key already exists
ja プロバイダーキー / プロバイダーキーを入力してください / このプロバイダーキーは既に存在します
zh 供应商标识 / 供应商标识不能为空 / 该供应商标识已存在
zh-TW 供應商識別碼 / 供應商識別碼為必填 / 該供應商標識已存在 <- the odd one out
hermes.form.providerKeyDuplicate carries the same sentence and is left
alone: its own label is 供應商標識, so it is already consistent.
Verified: managementListLocales 7/7. Full unit suite 1085/1087, the same
two App.test.tsx integration cases failing before and after.
fix(proxy): strip stop param for GitHub Copilot to unblock auto mode classifier (#5404)
Claude Code's auto mode classifier sends `stop_sequences` on every
safety check. On the GitHub Copilot path the Anthropic -> OpenAI Chat
conversion mapped it to `stop`, and Copilot's Chat Completions route
for Claude models answers that request with HTTP 400, so every
classifier call failed while normal chat kept working.
- Remove the converted `stop` field only when the provider is GitHub
Copilot; other OpenAI-compatible upstreams keep it.
- Copilot detection in the transform now also covers providers that
are recognised only through `env.ANTHROPIC_BASE_URL`.
- Regression tests for the Copilot (meta and env URL) and generic
provider cases.
Fixes #5175
fix(codex): keep stored auth when the live snapshot has no credential (#7434)
Editing the current Codex provider seeds the form from the live ~/.codex
snapshot, and switching away backfills that snapshot into the DB row. When a
provider table declares its own credential source (an Authorization header,
env_key, auth/aws), no bearer token is injected into config.toml and the
shared auth.json is deleted on third-party switches, so the live snapshot is
permanently `{ auth: {}, config }` while auth.OPENAI_API_KEY in the DB is the
only copy of the key. Both paths treated that empty live auth as
authoritative and silently erased the stored key, which in turn made the
usage-script modal misread the provider as an official subscription and
reset the saved script.
- Treat a credential-less live auth (missing auth.json or the bare
auth_mode logout marker) as an absent field in both the edit dialog and
the switch-away backfill, keeping the stored auth template. Live still
wins whenever it carries material, and official providers keep the live
login as their source of truth.
- Gate the official exemption on is_codex_official_provider /
resolveCodexOfficialIdentity rather than the category alone: a legacy
fixed official card without a persisted category is still written as
official, so the fallback must not resurrect a logged-out login on
switch-back.
- Cover both layers with regression tests, including the category-less
official shapes.
Fixes #7433
Supersedes #7492
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): lift additional_tools input carriers on the Codex chat conversion (#7454)
Codex talks to third-party providers over its Responses Lite transport,
which ships the request's extra tools in an `additional_tools` input
carrier: `{"type":"additional_tools","role":"developer","tools":[...]}`,
a role but no `content`. The Responses->Chat conversion let that carrier
fall through the generic "item with a role is a message" arm and emitted
`{"role":"system","content":null}` at messages[1], which strict Chat
gateways reject with `messages[1]: missing field "content"` (or
`data did not match any variant of ...SystemMessageContent`). The tools
carried inside it were also dropped on the Chat and Anthropic conversions.
- Skip `additional_tools` items in the Chat message walk; a carrier
declares tools, it is not a message.
- Collect carrier tools through the existing CodexToolContext registry
(dedup, namespace flattening, custom-tool conversion), which also feeds
the Anthropic conversion and the response-side name round-trip.
- Requests without a carrier convert byte-for-byte as before.
Known boundary: `custom` children inside a namespace tool
(`functions.exec`) are still not lifted; that needs its own response-side
name round-trip and is left for a follow-up.
Diagnosed independently in #6158 by BigStrongSun (fix in #6159) and in
fixture.
Fixes #7451
Fixes #6158
Refs #6033
Refs #6776
Supersedes #6159
fix(proxy): normalize Codex detail:original images for Chat gateways (#7476)
Codex marks tool-returned images with detail:"original" whenever the model
catalog advertises supports_image_detail_original, a Responses-only value.
The Responses->Chat conversion copied it verbatim into image_url.detail, and
strict OpenAI-compatible Chat gateways (Ark/Doubao, SenseNova, GLM, vLLM, ...)
reject the whole request with 400. Because Codex replays the full rollout on
every turn, one poisoned view_image result made the session fail permanently,
including plain-text turns.
Two layers:
- tool_media::merge_top_level_detail downgrades original -> auto, so already
poisoned rollouts and Codex versions before 0.155.0 (which do not normalize
replayed history themselves) recover immediately. auto/low/high are untouched.
- codex_catalog_model_entry always writes supports_image_detail_original:false
on the ProxyChat profile, so Codex stops emitting original at the source once
the catalog is regenerated. NativeResponses/Anthropic profiles and the DeepSeek
official catalog are unaffected.
Top-level user images were never affected: responses_content_to_chat_content
drops the part-level detail entirely.
Fixes #6495
Fixes #7458
Refs #6569
Refs #5805
fix(omo): detect unified config on the WSL side when the OpenCode dir is in WSL (#7550)
When the OpenCode config directory points into WSL
(\\wsl.localhost\<distro>\home\<user>\.config\opencode), OMO >= 4.19.3 keeps
its unified config in the WSL-side home (~/.omo/omo.jsonc). Detection only
probed the Windows home, never found it, and silently fell back to the legacy
per-plugin file that OMO no longer reads, so provider switches looked
successful but had no effect.
- Add derive_wsl_home_dir to derive the WSL-side home from a WSL UNC path
(home/<user> or root); non-WSL and non-home paths return None.
- Probe a list of home candidates for the unified config, WSL-side home
first and the OS home as a fallback, shared by the read, write and delete
paths.
- Detection only: when no unified config exists on either side the legacy
fallback is unchanged.
Fixes #7363
Supersedes #7367
docs(partner): add a FluxA purchase link and tighten its sponsor blurb
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.
fix(claude-desktop): expose the 1M context variant for DeepSeek V4 routes
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.
feat(presets): add FluxA Token Plan partner presets
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.
fix(presets): correct AICodeWith Codex-family endpoint to /v1
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.
fix(ui): render monochrome preset icons in the foreground color
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.
docs(user-manual): point Chat-only Codex examples at plan endpoints
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.
fix(pricing): restore DeepSeek V4 Pro price and seed Qwen3.8 and Hy4 models
- 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).
fix(presets): move MiniMax CN and BaiLing to their current official endpoints
- 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.
feat(codex): refresh CN presets from the Responses API audit
- 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.
test(claude-desktop): run Linux path tests on unix hosts and cover Linux in provider sync tests
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.
fix(ui): stop expanding stale cached usage tiers on ineligible provider cards
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.
fix(skills): 支持 skillId 与目录名不一致的技能安装和更新 (#6381)
* fix(skills): resolve mismatched skillId directories
* fix(skills): preserve source path during updates
* fix(skills): refresh source URL after repository relocation
* test(skills): isolate relocation fixture from legacy home
feat(opencode): 支持从获取的模型列表中搜索并批量添加所选模型 (#7515)
* feat: implement fetched models selection and UI components
* fix(opencode): prevent model search from submitting provider form
Add Kimi Global preset variants (kimi.ai platform + api endpoints) (#7526)
Add a "Kimi Global" twin immediately after the domestic "Kimi" API preset and a "Kimi For Coding Global" twin after the domestic "Kimi For Coding" preset, in every preset file that ships them (claude, claude-desktop, codex, hermes, openclaw, opencode, pi).
"Kimi Global" points to the international open platform https://platform.kimi.ai?aff=cc-switch (apiKeyUrl mirrored as https://platform.kimi.ai/console/api-keys?aff=cc-switch) and swaps the endpoint host api.moonshot.cn for api.moonshot.ai with each file's path unchanged (/v1 or /anthropic).
"Kimi For Coding Global" points to the international site https://www.kimi.ai/code/?aff=cc-switch and swaps the endpoint host api.kimi.com for api.kimi.ai with the path unchanged (e.g. https://api.kimi.ai/coding/v1).
Domestic presets are unchanged; category, icons, apiFormat, model catalogs, endpointCandidates (host swapped), partnerPromotionKey and settings otherwise mirror the domestic blocks exactly.
primePartner stays only on the domestic presets so the new variants do not alter prime-partner ordering.
The Pi twins get distinct providerKey values ("cc-switch-kimi-global", "cc-switch-kimi-for-coding-global") because providerKey is the preset-level primary key and must not collide with the domestic presets.
Rebalance sponsor CTAs around Kimi Code plan with dual-region links (#7522)
Kimi Code plan purchases have resumed, so the README sponsor sections go back to leading with the coding plan instead of the API-only flow written during the purchase pause.
All four locales (EN/ZH/JA/DE) now present Kimi Code plan (中文站 | Global) as the primary CTA and the Kimi Open Platform API (中文站 | Global, with the first-top-up bonus) as the secondary one, mirroring the domestic|global split already used for API links.
The aff and track_id values are unchanged.
fix(prompts): refresh active prompts after external file edits (#7194)
Reload the enabled prompt from its live file when the Prompts list loads, and refresh the open panel when the window regains focus, so external edits to CLAUDE.md / AGENTS.md show up without a restart.
- Keep saved templates available when the live file cannot be read.
- Skip the backfill for empty or whitespace-only files, matching enable_prompt.
- Skip the backfill while a sync/restore holds the sync lock, so a list load cannot write a stale live file into a freshly restored database.
fix(skills): raise archive entry limit so large skill repos install (#7489)
Installing hugohe3/ppt-master from skills.sh failed with
ARCHIVE_TOO_MANY_ENTRIES: the GitHub archive we download is the whole
repository (13,248 entries), and the 10,000-entry cap added in #5811
treated it as a zip bomb.
- Raise MAX_ARCHIVE_ENTRIES to 30,000 for both the remote archive and
local ZIP paths, and align WebDAV's MAX_EXTRACT_ENTRIES so a skills.zip
containing such a skill still restores.
- Charge every file entry at least one 4 KiB block against the existing
512 MiB budget, mirroring directory accounting, so empty-file archives
are bounded by the byte budget rather than by the entry count alone.
- Add tests for a 13k-entry repo archive and for the per-file minimum
charge.
Fixes: https://github.com/farion1231/cc-switch/issues/7475
feat: add MiniMax Code harness support (#7383)
Add MiniMax Code (mcode) as a managed application: provider and model management through the native custom_provider configuration, MCP servers, Skills, global instructions (AGENTS.md), read-only local session history with resume, and usage import from the native runtime database. Native files shared by the TUI and desktop are written under the native lock with rollback when the database commit fails, unrelated native configuration is preserved, and the data directory honors MINIMAX_DATA_DIR and MAVIS_DATA_DIR. Deleting sessions stays in the native application because they are coupled to runtime state.
fix(codex): recover stale account bindings during takeover (#7395)
After deleting a managed ChatGPT account and logging in again, a Codex provider can still reference the old local account ID, which made route switching, takeover, startup restoration and rebinding fail permanently.
Distinguish a missing outgoing account from an existing account with no matching live token. A missing account only releases its ownership marker after the persisted account store is verified; the target write policy stays in force. A stale target now reports an account-selection instruction instead of a bare not-found error, including when takeover is already enabled.
Fixes #7392
feat(claude-desktop): support 3P configuration on Linux (#7331)
Add Claude Desktop 3P configuration support on Linux. Resolve the config root from an absolute XDG_CONFIG_HOME, falling back to ~/.config; when CC Switch itself runs as a Flatpak, use the host ~/.config instead of the sandbox-private XDG_CONFIG_HOME. Update the en/zh/ja user manuals with Linux paths and the Flatpak boundary.
Fixes #4855
fix(proxy): omit missing tool descriptions instead of serializing null (#7319)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): keep reasoning effort for grok-4.6 and pass xhigh through verbatim (#7318)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(models): support Zhipu OpenAI Responses model lists (#7330)
Zhipu's Responses endpoint (open.bigmodel.cn/api/v1) serves its model list in the Codex remote catalog shape (models[].slug) instead of the OpenAI-compatible data[].id shape, so the Codex form's model fetch came back empty. The parser now falls back to models[].slug when data is absent.
fix(misc): fetch npm dist-tags from the dedicated endpoint with the probe timeout (#7346)
The tool version probe requested the full npm packument for each package (metadata for every historical version, 14-25 MB uncompressed for codex / opencode / openclaw) only to read its dist-tags, and it was the only latest-version probe without LATEST_PROBE_TIMEOUT, so it fell back to the proxy client's 600 s total timeout and the cards sat on "loading" for minutes on slow links while the large downloads starved the Hermes GitHub probe into "unknown".
Switch fetch_npm_dist_tags to registry.npmjs.org/-/package/{package}/dist-tags, whose body is the dist-tags object itself (tens of bytes to a few KB), escape scoped names as %2f, and apply the same 15 s probe timeout as the GitHub and PyPI probes. No data is lost: the only consumer reads latest and the tool's prerelease tags.
Fixes #7339
feat(usage): display output tokens per second in request log table and detail panel (#3369)
Show tokens/s next to the output token count in the request log table and the request detail panel.
The generation window prefers an explicit duration when present, otherwise latency minus first-token time for streaming requests, and falls back to full latency for non-streaming requests. Rows without a positive token count or window show nothing.
Closes #5936
fix(presets): update APIKEY.FUN URLs to apikey.fan
docs(release): add v3.20.3 release notes
chore(release): v3.20.3
fix(pricing): move retired DeepSeek V4 family to the V4.1 Flash tier
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.
fix(presets): declare third-party Token Plan DeepSeek V4 rows text-only
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.
feat(presets): rename DouBaoSeed preset to localized Volcengine Doubao
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.
fix(codex): skip unchanged incomplete rollout tails
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.
fix(claude): align quick model selection with form order
fix(tray): show bound ChatGPT account quota for managed Codex cards
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
feat(claude): add "Disable Artifact Tool" quick toggle to provider editor
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.
feat(codex): refresh aggregator model presets and catalogs
feat(codex): switch Kimi presets to native Responses direct-connect
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.
fix(usage): show Claude Fable weekly limit from the usage API limits array
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.
fix(codex): mirror vision-capable DeepSeek catalog and un-gate the legacy v4-flash alias (#7286)
Refresh the bundled DeepSeek official Codex catalog to the vendor's current models.json (slug deepseek-flash, image input modality, supports_image_detail_original) and remove deepseek-v4-flash from the confirmed text-only registry: the legacy id is still accepted by DeepSeek and routed to the vision-capable V4.1 Flash, so both the proxy media sanitizer and the generated Codex catalog must fail open for it. deepseek-v4-pro stays text-only per the vendor catalog.
Fixes #7283
fix(proxy): clamp sub-floor max_tokens to the Responses API minimum (#7287)
The Responses API rejects max_output_tokens below 16, but Anthropic clients legitimately send tiny probe budgets (Claude Desktop's model-availability probe uses max_tokens=1). The Anthropic→Responses conversion copied the value verbatim, so strict upstreams returned 400 and Claude Desktop reported the mapped model as unavailable under local routing.
Clamp 1..=15 up to 16 during conversion; 16+, 0 and non-integer values keep their pass-through semantics. Normal session requests convert byte-for-byte as before, and the Codex OAuth path still strips max_output_tokens afterwards.
Fixes #7103
fix(codex): coalesce adjacent commentary with pending tool calls (#7280)
The Codex Responses -> Chat Completions converter emitted two consecutive
assistant messages when one Responses model turn contained a commentary
message directly followed by function_call items. Chat upstreams imitate
the text-only assistant message as a complete turn and return
finish_reason=stop before the expected tool call, so long agent tasks end
after a progress update.
Merge pending tool calls into the directly adjacent assistant message when
it carries no tool_calls yet, attach pending reasoning per segment so
parallel calls do not duplicate it, and keep the new-message path for every
other boundary shape (user/tool boundaries, prior tool-call batches, media
flushes). The reasoning placeholder backfill still applies to coalesced
calls without reasoning.
Fixes #6529
Supersedes #6530
Co-authored-by: BigStrongSun <BigStrongSun@users.noreply.github.com>
fix(proxy): skip empty reasoning_content placeholders in Anthropic SSE streaming (#7227)
Some OpenAI-compatible upstreams (GLM, Qwen, DeepSeek-V4-Pro via ModelScope/DashScope,
Bailian, JD Cloud, ...) keep a `reasoning_content: ""` placeholder in every content
chunk instead of omitting the field. The reasoning branch of the OpenAI Chat -> Anthropic
SSE converter only checked for presence, and it shares `current_non_tool_block_type`
with the content branch, so every content chunk closed the open text block, opened an
empty thinking block, then reopened a text block. N content chunks became 2N content
blocks, half of them empty thinking blocks, which Claude Code rendered as one line per
token with dozens of empty Thought blocks and bloated the session JSONL.
Filter empty reasoning before entering the branch, matching the `!content.is_empty()`
guard the content branch already had and the non-streaming `openai_to_anthropic`
converter. Two regression tests pin block index, type and order: the placeholder shape
must yield a single text block and no thinking_delta at all, and real reasoning must
still yield one thinking block followed by one text block.
Fixes #5028
Fixes #4404
fix(codex): detect growing rollouts with a persisted byte cursor (#7219)
Codex keeps the rollout append handle open for the whole session, so on Windows NTFS the file mtime does not advance while the file grows and the pure-mtime gate skipped growing rollouts forever. Persist the observed byte length in session_log_sync.last_byte_offset for Codex rows and skip a file only when both mtime and size are unchanged. The parser now splits records on newlines manually so an incomplete trailing record is retried on the next pass without advancing the line cursor.
Fixes #6060
fix(codex): proxy image edits and Images API follow-ups (#7177)
* fix(codex): proxy image edits and Images API follow-ups
Follow-ups to #7036 for Codex's built-in ImageGen over the local proxy:
- Register `/images/edits` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them through the shared Codex standalone passthrough.
ImageGen posts to `images/edits` whenever a call references existing
images, and the router returned an empty 404 for it.
- Add `CodexStandaloneEndpoint::ImagesEdits`. Both Images endpoints share
one full-URL suffix table, so a complete Responses / Compact / Chat
Completions / Images URL derives the sibling endpoint (and vice versa)
while opaque full URLs keep failing closed.
- Apply the standalone sibling rewrite when the base URL is pasted as a
complete endpoint with the full-URL switch off, mirroring
`codex_chat_base_is_full_endpoint`; standalone requests no longer become
`.../chat/completions/images/generations`.
- Parse Images API streaming usage: `image_generation.completed` carries
`usage` on the event top level, so scan for `usage.input_tokens` before
falling back to the Chat Completions stream parser.
Related #7036
* fix(codex): match standalone URL suffixes case-insensitively
Keep endpoint detection and sibling rewriting consistent while preserving the original URL prefix and query. Cover mixed-case Images and Alpha Search endpoints through URL and proxy regression cases.
---------
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): preserve per-app settings on shutdown and port allocation (#7210)
On normal exit, stop_with_restore_keep_state() called the legacy global
proxy-config writer to clear the long-retired live_takeover_active flag.
That writer reads the Claude row and runs an UPDATE without a WHERE clause,
so every shutdown copied Claude's max_retries and the three timeout fields
onto the Codex, Gemini and Grok Build rows — and the UPDATE never carried
live_takeover_active in the first place, so the step had no effect beyond
the clobber. The ephemeral listen-port path did the same on every proxy
start.
Drop the dead write-back (live-config restore, backup cleanup and health
reset are unchanged) and persist the resolved ephemeral port through the
global-config interface, which only touches the shared listen/logging
columns. Two regression tests seed distinct per-app settings for all four
apps and assert they survive both paths.
Fixes #7204
fix(provider): preserve child metadata during universal sync (#7212)
Syncing a universal provider regenerates its Claude/Codex/Gemini child
providers. The child's `settings_config` was merged with the existing row,
but `meta`, `created_at` and `sort_index` were taken from the generated
object, and the universal provider never carries any of them from the UI.
The UPDATE therefore wiped the child's usage script, common-config opt-out,
endpoint auto-select and other per-app settings to `{}` and reset the sort
index to NULL, which pushed the card to the bottom of the list and looked
like the provider had been deleted and re-created.
Keep the existing child's `meta`, `created_at` and `sort_index` when the
row already exists. Name, base URL, key, models, website and notes are
still driven by the universal provider, and first-time creation still
inherits the parent's metadata.
Fixes #7134
feat(presets): rebrand DashScope as 千问AI平台 and refresh Qwen models (#7183)
Rename the domestic Bailian presets to 千问AI平台 with a dedicated icon, refresh the Qwen family to the 3.8 generation across Claude Code, Claude Desktop, Codex, Hermes, OpenClaw, OpenCode and Pi, add the domestic Token Plan presets, restore the international QwenCloud Coding Plan presets, and align OpenClaw/OpenCode/Pi model metadata with the official client docs.
fix(codex): honor proxy URL when model_provider is omitted (#7263)
When a Codex provider card's TOML has no `model_provider`, Codex falls back to
its built-in `openai` provider, but takeover wrote the proxy address as a
top-level `base_url` that Codex never reads, so requests bypassed the local
proxy and went straight to api.openai.com.
Treat the missing selector as the built-in `openai` provider and write the
proxy address to `openai_base_url`; the existing legacy-reroute migration then
turns it into a `cc-switch` custom provider table carrying the `PROXY_MANAGED`
bearer, the same shape every other third-party takeover uses. Explicit
provider selections are unchanged.
Refs #6256, #7217
fix(provider): update MiniMax defaults to M3 and remove expired offers (#7255)
* fix(provider): refresh MiniMax M3 presets and remove expired promotions
* fix(provider): complete MiniMax M3 capability declarations
---------
Co-authored-by: saladday <1203511142@qq.com>
chore(provider): remove Atlas Cloud sponsor status, keep presets
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`
docs(release): add v3.20.2 release notes
chore(release): v3.20.2
feat(provider): add SoleAPI sponsor presets across all nine apps
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 19…
…itHub
Rebase fork main onto upstream/main (v3.20.3, 06082e18, 33 new commits)
while keeping all fork modifications: connectivity test, model quick
switch, sidebar panel visibility, preset filtering, tray left-click
toggle, dual website links, disabled in-app updater, fork CI.
- Bump fork version to 3.20.3-1 (package.json, tauri.conf.json,
Cargo.toml, Cargo.lock).
- Merged upstream's v18->v19 schema migration with the fork's
website_url_2 column migration in the same migration arm.
- Added mcode entry to forkOfficialAllowlist (no official presets,
same treatment as hermes/openclaw/opencode/pi).
- Changed the top-left CC Switch brand link target from
https://ccswitch.io to https://github.com/farion1231/cc-switch.
- Updated HOW_TO_REBASE_UPSTREAM.md whitelist with the brand link
override.
style: apply prettier to files flagged by CI format check
修复 CI Frontend Checks 的 Check formatting 步骤失败
(App.tsx、ConnectivityTestDialog、ProviderCard、ProviderList、
provider schema 共 5 个文件)。
style: apply cargo fmt to connectivity-test era backend files
修复 CI 的 Check Rust formatting 步骤失败(connectivity_test、
backup、tests、provider 等文件格式不符合 rustfmt 规范)。
chore(native): archive sync-upstream
docs(fork): update rebase whitelist for connectivity-test era fork features
- 白名单补充连通性测试前后端、模型快捷切换、双官网链接测试、
CI 裁剪版、README/CHANGELOG、.comet/superpowers 产物等 fork 专属文件
- 新增共享文件 fork 专属语义表(托盘左键切换、禁用更新器、hidden 字段等)
- 修正 §3.1 rebase 方向说明:rebase 中 fork 侧是 --theirs 而非 --ours
(v3.20.2 同步实测确认,与 merge 语义相反)
chore: bump fork version to 3.20.2-1 after upstream v3.20.2 rebase
重放 fork 提交到 upstream/main(f3b18df1,v3.20.2)后统一版本号。
Cargo.lock 同步移除 updater 依赖树(fork 已禁用应用内更新器)。
chore(native): archive tray-left-click-toggle-window
chore(comet): 记录 tray-left-click-toggle-window 验收通过与归档状态
feat(tray): 托盘图标左键单击切换主窗口显示/隐藏
- tray.rs 新增 show_main_window/hide_main_window/toggle_main_window 助手:
显示语义与"打开主界面"一致,隐藏与"关闭到托盘"平台处理一致
(Windows set_skip_taskbar、macOS apply_tray_policy)
- lib.rs 托盘 show_menu_on_left_click 改为 false,左键 Click(Left, Up)
触发切换(过滤 Down/Up 双事件),右键仍弹菜单;Enter/Click 用量刷新保持
- show_main 菜单事件与 macOS Reopen 复用 show_main_window
docs(readme): 更新日志补充连通性测试、双官网链接与托盘左键切换
feat(model-quick-switch): 获取模型列表成功后自动展开选择器
SearchableModelPicker 增加可选 open/onOpenChange 受控展开(不传保持内部自管,
表单等既有用法不受影响);快捷切换弹窗拉取成功且列表非空时直接展开,关闭弹窗复位。
chore(native): archive fix-quick-switch-1m
chore(comet): 记录 fix-quick-switch-1m 验收通过与归档状态
fix(model-quick-switch): 补齐 Haiku 显示名同步,支持不选模型仅翻转 1M
- 显示名同步映射补上 ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME(沿用空/旧base/旧base[1M] 覆盖、自定义保留语义)
- 新增 setClaudeOneMInSettings:仅原地翻转 1M 标记,不改各角色模型 base 与显示名
- 弹窗打开时 1M 开关按供应商当前状态初始化,避免已开 1M 被静默剥离
- claude 未选模型且 1M 状态有变化时允许保存;无可应用变化时保存禁用
- 补单测与四语言 toast 文案
feat(provider-website-links): 供应商支持第二个官网链接,主页横排双链接
- Rust Provider/UniversalProvider 新增 website_url_2(serde websiteUrl2,skip none)
- SQLite providers 表新增 website_url_2 列(schema v18→v19 幂等迁移 + create_tables 补列 + 旧 config.json 迁移)
- DAO SELECT/INSERT/UPDATE/replace 全链路带出新列
- 表单共享 BasicFormFields + 统一供应商表单第二输入框;zod 同第一链接规则校验与回填
- ProviderCard 主页双链接横排、两空格分隔、逐链接 openExternal 打开默认浏览器
- 搜索匹配、复制供应商、统一供应商生成 claude/codex/gemini 随行透传
- gemini_auth packycode 认证检测纳入 website_url_2;ClaudeDesktop/GrokBuild 预设切换清空第二链接;卡片同值链接去重
- 4 语言 i18n + 前端/前端单测覆盖
Comet change dual-website-links-homepage(19/19 验收通过,归档)
feat(connectivity-test): 拉取模型列表改为「搜索添加」选择器交互
- 新增共享组件 SearchableModelMultiPicker:搜索过滤 + 多选 + 已在列表标记 + 已选计数
- ConnectivityTestDialog 获取模型列表后不再全量追加合并去重,改为会话内缓存完整列表并自动弹出一次选择器;用户搜索挑选后添加进表格并自动勾选
- 重新拉取只刷新缓存,已添加进表格的行保留;拉取结果仍仅会话内有效,不写回供应商配置
- 四语言 i18n 新增 searchAdd / addToTest / alreadyInList / selectedCount
- 同步更新 ConnectivityTestDialog 测试并新增 SearchableModelMultiPicker 测试
feat(connectivity-test): 探测目标按供应商 API 格式对齐真实转发链路
- build_claude 按 get_claude_api_format 分派端点与请求体:openai_chat → /v1/chat/completions(复用 transform_claude_request_for_api_format,流式注入 stream_options.include_usage)、openai_responses → /v1/responses、gemini_native → /v1beta/models/{model}:{method}(resolve_gemini_native_url)、anthropic 保持 /v1/messages 并补 anthropic-version/beta 头
- build_codex 按 should_convert_codex_responses_to_chat/anthropic 分派:chat → /chat/completions(responses_to_chat_completions_with_reasoning)、anthropic → /v1/messages(responses_request_to_anthropic,含 max_output_tokens 注入与 [1m] 剥离 + context-1m beta 头)
- 鉴权改用 ClaudeAdapter/CodexAdapter 的 extract_auth + get_auth_headers(与真实出站请求同源:AUTH_TOKEN→Bearer、ANTHROPIC_API_KEY→x-api-key、gemini→x-goog-api-key、GeminiCli OAuth→Bearer、OpenRouter→Bearer、Codex 按 apiKeyField)
- 转换前剥离 [1m] 标记;meta.isFullUrl 或 base 已是完整端点时直接使用 base_url;自定义 body 浅合并移至转换后的最终协议体
- 新增 18 个目标构造断言单测;Comet 归档产物同步提交
chore: archive connectivity-test-optimizations
feat(connectivity-test): 单一模型表格 + 实时拉取模型 + 勾选即测试范围 + 新默认提示词
- 弹窗合并为单一模型表格:打开即全部模型行(初始待测试)、行勾选 + 表头全选(含半选态)、5 格统计卡,移除上方独立选择区
- 获取模型列表:凭据提取抽为共享模块 src/utils/providerCredentials.ts(ModelQuickSwitchDialog 同步改引用),复用 fetch_models_for_config 拉取 /v1/models,按 ID 追加合并去重、仅会话内有效
- 默认提示词改为「你好,你可以帮我做什么事情」:前后端常量同步,merge 差异判断基准跟随
- 勾选即测试范围:后端 connectivity_test_provider_models 新增 modelIds 参数(None/空白回退静态清单),前端逐模型独立 invoke 并行 + 行级流式更新
- 一轮测完自动勾选失败项,全部成功保持原勾选
fix(connectivity-test): UI 四项修复
- 弹窗抬至 alert 层(z-[60]):打开连通性测试时 header 切换栏正确变灰,
详情子弹窗同步抬层(同 z + 更晚挂载 portal 在上)
- 批量检测入口移至 header 图标组(状态提升到 App,ProviderList 经
props 读取探针结果),不再另起一行
- 批量检测可停止:runId 代次守卫(Tauri invoke 无法真正取消),再次
点击即清除 waiting/running 徽标、丢弃在途迟到写入、不再拉起排队项
- 弹窗打开自动勾选模型:已保存默认测试模型 → 供应商当前模型
(claude env.ANTHROPIC_MODEL / codex config.toml model,新增
currentProviderModelId 与后端同口径)→ 清单首个
前端全量 141 文件 / 1106 测试全绿;typecheck clean
chore: archive per-model-connectivity-test
fix(connectivity-test): shouldShowTestEntry 增判 providerType + 收尾清理
chore(connectivity-test): 勾选 Task 9 / OpenSpec 5.1-5.2 + verify 报告
chore(connectivity-test): 勾选 Task 8 / OpenSpec 4.1-4.3
refactor(connectivity-test): 移除旧 stream_check 可达性探测全链路(保留表结构)
- 删除 6 个文件:commands/stream_check.rs、services/stream_check.rs、
database/dao/stream_check.rs、hooks/useStreamCheck.ts、lib/api/connectivity-check.ts、
components/usage/ConnectivityCheckConfigPanel.tsx
- 移除 mod.rs 声明、lib.rs generate_handler! 注册、cleanup_old_stream_check_logs 调用
- 从 SYNC_SKIP_TABLES / SYNC_PRESERVE_TABLES 移除 stream_check_logs(表结构保留在 schema.rs)
- 更新 backup.rs 测试:移除 stream_check_logs fixture 与断言
- 更新 SettingsPage:移除 ConnectivityCheckConfigPanel AccordionItem 与 FlaskConical 导入
- i18n:删除 streamCheck.* 命名空间(zh/en/ja/zh-TW 四语言)
- 更新注释:移除 stream_check 旧引用(forwarder.rs、provider.rs、userAgentPresets.ts、
connectivity_test.rs)
- 修复 4 个 pre-existing clippy lint(connectivity_test: matches! 宏、unused_unit、
derive Default)
- 移除 ProviderList.test.tsx 中已失效的 useStreamCheck mock
chore(connectivity-test): 勾选 Task 7 / OpenSpec 2.4 3.1-3.3
feat(connectivity-test): 供应商列表集成(弹窗入口 + 批量探针徽标)
chore(connectivity-test): 勾选 Task 6 / OpenSpec 2.2-2.3
fix(connectivity-test): 模型列表解析与后端对齐(每条目首个匹配,提取共享 helper)
feat(connectivity-test): 连通性测试弹窗/详情/徽标组件与四语言文案
chore(connectivity-test): 勾选 Task 5 / OpenSpec 2.1
test(connectivity-test): 锁定 toBackendParams 字段映射(customHeaders/customBody 等)
feat(connectivity-test): 前端命令绑定与测试/探针状态 hooks
chore(connectivity-test): 勾选 Task 4 / OpenSpec 1.4
feat(connectivity-test): 测试参数持久化纯函数(缺省即默认值)
chore(connectivity-test): 勾选 Task 3 / OpenSpec 1.3
feat(connectivity-test): Tauri 命令与注册,官方/动态端点过滤,sanitize 隔离私有字段
chore(connectivity-test): 勾选 Task 2 / OpenSpec 1.2
feat(connectivity-test): 后端直连执行器(reqwest+SSE,总/空闲超时,度量与错误识别)
feat(connectivity-test): 后端直连执行器(reqwest+SSE,总/空闲超时,度量与错误识别)
chore(connectivity-test): 勾选 Task 1 / OpenSpec 1.1
fix(connectivity-test): claude 鉴权对齐 adapter 顶层 apiKey/api_key 兜底
feat(connectivity-test): 新增后端连通性测试服务类型/模型解析/协议构造
chore: archive model-quick-switch-picker-scroll
chore: model-quick-switch-picker-scroll 验证报告与状态元数据
fix: 模型快捷切换弹窗内模型列表滚轮无法滚动
模态 Dialog 的 RemoveScroll 会对 shards 外的 portaled PopoverContent
wheel 事件无条件 preventDefault。在 SearchableModelPicker 的
PopoverContent 捕获阶段阻断 wheel 传播,恢复原生滚动。
chore: bump fork version to 3.20.1-1 after upstream v3.20.1 rebase
fix(form): only auto-expand Claude advanced options on divergent model mapping
Uniform mapping produced by the quick-set button (all roles equal to the
fallback model) no longer counts as an advanced value, so the advanced
section stays collapsed on edit. A 1M marker is stripped before comparing
so marked roles are not misjudged as divergent. Truly customized mappings
still auto-expand the section.
feat: align fallback-model quick-set buttons with model input right edge
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
docs: update README with fork build notes and quick-move provider changes
chore: archive comet-native claude-model-quick-access
feat: surface Claude fallback model as quick-access section below endpoint
Move the default fallback model (ANTHROPIC_MODEL) block out of the
collapsed Advanced Options into a quick-access section directly below
the request endpoint: input + 1M checkbox + quick-set + fetch-models
buttons are all reachable without expanding.
- header toggle-all "declare 1M" now includes the fallback model
(Haiku still excluded); partial selection shows indeterminate state
- drop the duplicated quick-set/fetch buttons and the old fallback
block inside Advanced Options
- exclude claudeModel from hasAnyAdvancedValue so a fallback-only
value no longer auto-expands Advanced Options
- quick-set priority, env write semantics, and OAuth preset branches
unchanged; add 4 component tests covering the new layout
chore: archive comet-native provider-right-click-top-bottom
Enable native workflow in .comet/config.yaml and move the Comet managed
state block in .gitignore so the exclusion rules stay grouped with the
other fork tool artifacts.
feat: add provider right-click move-to-top/bottom and insert new providers at second position
- ProviderList: context menu with quick move-to-top/move-to-bottom, reusing
update_providers_sort_order and refreshing tray menu like drag sort
- useAddProviderMutation: default insert at index 1 with explicit sortIndex
materialization for existing items (avoids defined-first sort trap)
- i18n: quickMove* strings for zh/en/zh-TW/ja
ci: stop flaky WSL2 linker job from blocking main
The backend-windows-wsl2 job intermittently fails with LNK1327 /
mt.exe c1010070 when the Windows runner's mt.exe cannot parse a
manifest temp file on the WSL2 UNC TEMP path. The same flaky failure
occurs upstream and is unrelated to fork changes. Mark the job
continue-on-error so main stops going red on it; the result stays
visible in the Actions UI.
style: apply prettier to files touched by recent comet archives
Frontend CI format:check failed on 6 files modified by the app-name
and model-quick-switch archive commits plus the updater-disable commit.
Run prettier --write so format:check passes again.
chore: archive ccs-fork-app-name-cc-switch
chore: archive ccs-model-quick-switch-ui-polish
docs: rewrite README to document fork differences vs upstream
README 从上游完整功能介绍改为仅记录 fork 与上游的差别,完整功能
文档指回上游。涵盖 8 项 fork 魔改:构建标识/开发预览、预设精简、
Claude 表单调整、模型搜索移植、主页模型快速切换、侧边栏面板显隐、
关闭应用内更新、打包范围,并说明版本号与 rebase 同步流程。
feat: disable in-app updater, point users to GitHub releases
移除 tauri-plugin-updater 依赖与自动更新链路,改为统一引导用户到
GitHub Releases 手动下载新版。
- 前端:updater.ts 恒返回 up-to-date;UpdateContext 取消启动自检;
AboutSection「检查更新」直接打开 fork releases 页;DatabaseUpgrade
恢复界面去掉下载进度条,改为打开发布页。
- 后端:移除 updater 插件注册与 UpdaterExt 调用;
install_update_and_restart 降级为空壳,check_app_update_available
返回当前版本号;check_for_updates 指向 fork releases;capabilities
去掉 updater:default 权限。
- 打包:tauri.conf.json 删 plugins.updater、createUpdaterArtifacts=false;
Cargo.toml / package.json 删 updater 依赖,Cargo.lock / pnpm-lock
同步重生成。
- CI:release.yml 移除签名收集与 assemble-latest-json job,删除
.tar.gz/.sig 处理,release body 去掉 updater 说明。
版本号统一为 3.20.0-1(合法 semver 且 MSI 第 4 位为 1)。
Cargo.lock / pnpm-lock.yaml 已随依赖移除重生成,cargo check 通过。
ci: build only Windows x64 and macOS arm64, unsigned
feat: gate dev panel behind dev-only env flag
chore(comet): archive ccs-sidebar-panels-visibility change
fix: apply skills visibility guard to hermes branch toolbar button
feat: include prompts in sidebar panel visibility settings
fix: persist visibleSidebarPanels setting in backend AppSettings
style: use compact pill toggles for sidebar panel visibility
feat: add sidebar panel visibility switches to settings
feat: add sidebar panel visibility settings and wire into toolbar buttons
chore(comet): archive ccs-home-model-display change
fix(fork): read grokbuild api key from config TOML in quick switch dialog
feat(fork): align model badge with upstream extractModelBadge
feat(fork): show current model badge and quick-switch entry on provider cards
feat(fork): add model quick switch dialog
feat(fork): add provider model read/write utils with tests
chore(comet): archive ccs-model-search-port change
feat(i18n): add model search picker keys for all locales
feat(form): port SearchableModelPicker from upstream for searchable model selection
chore(comet): archive ccs-claude-form-layout change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-claude-form-layout/
- Sync claude-form-layout spec to main specs
- Annotate design doc as archived
- Add verification report
fix(i18n): update advanced options hint, add ja/zh-TW toggle-all, drop dead apiFormatHint key
fix(form): correct 1M toggle-all alignment direction (ml-1)
fix(form): align 1M toggle-all checkbox with row checkboxes
refactor(form): move api format select into EndpointField rightSlot
fix(form): widen api format select to fit option text
feat(form): add 1M toggle-all checkbox in model mapping header
feat(form): move api format select next to endpoint input
chore(comet): archive ccs-provider-presets-trim change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-provider-presets-trim/
- Sync provider-presets-visibility spec to main specs
- Annotate design doc as archived
- Add verification report
docs(fork): add presets-trim files to rebase whitelist
test(fork): mock IS_FORK_BUILD false in unit tests for upstream-form semantics
feat(fork): wire official preset filter into provider forms
feat(fork): add hidden field to preset interfaces
feat(fork): add official preset allowlist and filter util
chore(comet): archive ccs-fork-scaffolding change
- Move change artifacts to docs/openspec/changes/archive/2026-08-18-ccs-fork-scaffolding/
- Sync fork-build-identity spec to main specs
- Annotate design doc as archived
- Add verification report
feat(fork): add dev config with isolated identifier and config dir
docs(fork): complete rebase whitelist with all fork-touched files
fix(fork): guard setTitle with isTauri in test/non-tauri env
fix(fork): define __CCS_FORK_BUILD__ in vitest config
feat(fork): add DevPanel and settings footer entry
feat(fork): set macOS window title to CC Switch (Fork) on startup
feat(fork): inject __CCS_FORK_BUILD__ compile-time constant
feat(fork): bump version to 3.19.2-fork.1 and rename product to CC Switch (Fork)
docs(fork): add upstream rebase workflow and ignore tool artifacts
fix(proxy): accept the whole grok-4.x (x>=5) family in the reasoning-effort whitelist (#7369)
Replace the verbatim grok-4.5 / grok-4.6 entries in supports_reasoning_effort with a rule that parses the grok-4.x minor version and accepts x >= 5, mirroring the existing GPT-5+ rule. This covers grok-4.7 (released 2026-09-21), whose reasoning effort was previously dropped on the Claude -> Chat, Claude -> Responses and Codex Responses -> Chat conversion paths, and lets future releases pass without another whitelist edit. The grok-build-* family is retained for saved providers.
Co-authored-by: allenxu09 <171831965+allenxu09@users.noreply.github.com>
docs(release): add v3.20.4 release notes
chore(release): v3.20.4
feat(pricing): add Grok 4.7 and GLM-5.3-FlashX
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.
fix(i18n): remove outdated OpenCode Go referral offer
fix: add SudoCode.chat alternate API endpoint
feat(settings): add a GitHub star prompt to the About card
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
feat(presets): add Soshow aggregator presets
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.
fix(ci): stabilize frontend tests and verify renderer build
fix(mcode): refresh provider state after removal (#7578)
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
fix(lib): reset skip_taskbar before window.show() across entry points (#6348)
5处 window.show() 调用中 4处没有在 show() 前重置 Windows 的 skip_taskbar,导致以下场景出现窗口可见但不在任务栏、点最小化后窗口消失的 bug:
- single_instance handler (lib.rs:360-361):当原进程因 silent_startup / 关闭到托盘等处于 skip_taskbar(true) 状态时,从快捷方式/开始菜单唤起已运行实例触发。
- handle_deeplink_url 的 focus_main_window 分支 (lib.rs:256):通过 ccswitch:// URL 唤起时。
- DB 恢复流程 (lib.rs:556):数据库版本不匹配时显示恢复界面。
- RunEvent::Opened 处理器 (lib.rs:1803):系统接收到 deep-link URL 时。
修复后的行为与 tray.rs::handle_tray_menu_event::show_main 分支一致(那里已有相同的 set_skip_taskbar(false) 调用)。
经用户测试验证:autostart + silent_startup + 从开始菜单/快捷方式启动的场景下,最小化按钮恢复正常工作。
fix(proxy): omit missing tool descriptions in the Codex responses-to-chat converter (#7378)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): preserve max effort for GPT-5.6 and GPT-6 Astra (#7531)
fix(misc): ignore shell startup output when probing tool versions in WSL (#7348)
try_get_version_wsl runs "${SHELL:-sh}" -lic '<tool> --version' through
wsl.exe and takes the first x.y.z anywhere in stdout. A login+interactive
shell runs the distro's startup files first, so their output shares that
stdout. On Ubuntu, /etc/profile.d/update-motd.sh prints the MOTD once a day,
starting with "Welcome to Ubuntu 24.04.4 LTS", and the card then showed
24.04.4 as the Claude Code version. The once-a-day stamp makes it appear
after the first probe of the day and vanish on refresh.
The payload now prints a sentinel before running the tool, and parsing looks
only at what follows its last occurrence. The last occurrence handles the
-lic || -lc || -c fallback chain printing it several times; matching it as a
substring handles OSC sequences glued in front of it. If the sentinel is
absent (the shell failed before the payload ran), the output is used as
before. The stderr fallback for tools that print their version there is kept.
Fixes #7347
Co-authored-by: lyfuci <lyfuci11@gmail.com>
fix(claude): hide session URL attribution (#7053)
* fix(claude): hide session URL attribution
* docs(claude): document session URL attribution
fix(ui): reset provider scroll when switching apps (#5211)
fix(pi): fix provider logo (#6826)
fix(i18n): use the Pi form's own term for the zh-TW duplicate-key message (#7187)
Follow-up to the nit on #6768. In zh-TW the Pi form labels the field
供應商識別碼 and says 供應商識別碼為必填 when it is empty, but the
duplicate message I added said 供應商標識 — the term Hermes, OpenCode and
OpenClaw use for their own field. One form, two names for the same thing.
The other three locales already keep the label and both messages on one
term, so this makes zh-TW match the pattern rather than introduce one:
en Provider key / Provider key is required / This provider key already exists
ja プロバイダーキー / プロバイダーキーを入力してください / このプロバイダーキーは既に存在します
zh 供应商标识 / 供应商标识不能为空 / 该供应商标识已存在
zh-TW 供應商識別碼 / 供應商識別碼為必填 / 該供應商標識已存在 <- the odd one out
hermes.form.providerKeyDuplicate carries the same sentence and is left
alone: its own label is 供應商標識, so it is already consistent.
Verified: managementListLocales 7/7. Full unit suite 1085/1087, the same
two App.test.tsx integration cases failing before and after.
fix(proxy): strip stop param for GitHub Copilot to unblock auto mode classifier (#5404)
Claude Code's auto mode classifier sends `stop_sequences` on every
safety check. On the GitHub Copilot path the Anthropic -> OpenAI Chat
conversion mapped it to `stop`, and Copilot's Chat Completions route
for Claude models answers that request with HTTP 400, so every
classifier call failed while normal chat kept working.
- Remove the converted `stop` field only when the provider is GitHub
Copilot; other OpenAI-compatible upstreams keep it.
- Copilot detection in the transform now also covers providers that
are recognised only through `env.ANTHROPIC_BASE_URL`.
- Regression tests for the Copilot (meta and env URL) and generic
provider cases.
Fixes #5175
fix(codex): keep stored auth when the live snapshot has no credential (#7434)
Editing the current Codex provider seeds the form from the live ~/.codex
snapshot, and switching away backfills that snapshot into the DB row. When a
provider table declares its own credential source (an Authorization header,
env_key, auth/aws), no bearer token is injected into config.toml and the
shared auth.json is deleted on third-party switches, so the live snapshot is
permanently `{ auth: {}, config }` while auth.OPENAI_API_KEY in the DB is the
only copy of the key. Both paths treated that empty live auth as
authoritative and silently erased the stored key, which in turn made the
usage-script modal misread the provider as an official subscription and
reset the saved script.
- Treat a credential-less live auth (missing auth.json or the bare
auth_mode logout marker) as an absent field in both the edit dialog and
the switch-away backfill, keeping the stored auth template. Live still
wins whenever it carries material, and official providers keep the live
login as their source of truth.
- Gate the official exemption on is_codex_official_provider /
resolveCodexOfficialIdentity rather than the category alone: a legacy
fixed official card without a persisted category is still written as
official, so the fallback must not resurrect a logged-out login on
switch-back.
- Cover both layers with regression tests, including the category-less
official shapes.
Fixes #7433
Supersedes #7492
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): lift additional_tools input carriers on the Codex chat conversion (#7454)
Codex talks to third-party providers over its Responses Lite transport,
which ships the request's extra tools in an `additional_tools` input
carrier: `{"type":"additional_tools","role":"developer","tools":[...]}`,
a role but no `content`. The Responses->Chat conversion let that carrier
fall through the generic "item with a role is a message" arm and emitted
`{"role":"system","content":null}` at messages[1], which strict Chat
gateways reject with `messages[1]: missing field "content"` (or
`data did not match any variant of ...SystemMessageContent`). The tools
carried inside it were also dropped on the Chat and Anthropic conversions.
- Skip `additional_tools` items in the Chat message walk; a carrier
declares tools, it is not a message.
- Collect carrier tools through the existing CodexToolContext registry
(dedup, namespace flattening, custom-tool conversion), which also feeds
the Anthropic conversion and the response-side name round-trip.
- Requests without a carrier convert byte-for-byte as before.
Known boundary: `custom` children inside a namespace tool
(`functions.exec`) are still not lifted; that needs its own response-side
name round-trip and is left for a follow-up.
Diagnosed independently in #6158 by BigStrongSun (fix in #6159) and in
fixture.
Fixes #7451
Fixes #6158
Refs #6033
Refs #6776
Supersedes #6159
fix(proxy): normalize Codex detail:original images for Chat gateways (#7476)
Codex marks tool-returned images with detail:"original" whenever the model
catalog advertises supports_image_detail_original, a Responses-only value.
The Responses->Chat conversion copied it verbatim into image_url.detail, and
strict OpenAI-compatible Chat gateways (Ark/Doubao, SenseNova, GLM, vLLM, ...)
reject the whole request with 400. Because Codex replays the full rollout on
every turn, one poisoned view_image result made the session fail permanently,
including plain-text turns.
Two layers:
- tool_media::merge_top_level_detail downgrades original -> auto, so already
poisoned rollouts and Codex versions before 0.155.0 (which do not normalize
replayed history themselves) recover immediately. auto/low/high are untouched.
- codex_catalog_model_entry always writes supports_image_detail_original:false
on the ProxyChat profile, so Codex stops emitting original at the source once
the catalog is regenerated. NativeResponses/Anthropic profiles and the DeepSeek
official catalog are unaffected.
Top-level user images were never affected: responses_content_to_chat_content
drops the part-level detail entirely.
Fixes #6495
Fixes #7458
Refs #6569
Refs #5805
fix(omo): detect unified config on the WSL side when the OpenCode dir is in WSL (#7550)
When the OpenCode config directory points into WSL
(\\wsl.localhost\<distro>\home\<user>\.config\opencode), OMO >= 4.19.3 keeps
its unified config in the WSL-side home (~/.omo/omo.jsonc). Detection only
probed the Windows home, never found it, and silently fell back to the legacy
per-plugin file that OMO no longer reads, so provider switches looked
successful but had no effect.
- Add derive_wsl_home_dir to derive the WSL-side home from a WSL UNC path
(home/<user> or root); non-WSL and non-home paths return None.
- Probe a list of home candidates for the unified config, WSL-side home
first and the OS home as a fallback, shared by the read, write and delete
paths.
- Detection only: when no unified config exists on either side the legacy
fallback is unchanged.
Fixes #7363
Supersedes #7367
docs(partner): add a FluxA purchase link and tighten its sponsor blurb
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.
fix(claude-desktop): expose the 1M context variant for DeepSeek V4 routes
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.
feat(presets): add FluxA Token Plan partner presets
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.
fix(presets): correct AICodeWith Codex-family endpoint to /v1
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.
fix(ui): render monochrome preset icons in the foreground color
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.
docs(user-manual): point Chat-only Codex examples at plan endpoints
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.
fix(pricing): restore DeepSeek V4 Pro price and seed Qwen3.8 and Hy4 models
- 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).
fix(presets): move MiniMax CN and BaiLing to their current official endpoints
- 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.
feat(codex): refresh CN presets from the Responses API audit
- 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.
test(claude-desktop): run Linux path tests on unix hosts and cover Linux in provider sync tests
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.
fix(ui): stop expanding stale cached usage tiers on ineligible provider cards
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.
fix(skills): 支持 skillId 与目录名不一致的技能安装和更新 (#6381)
* fix(skills): resolve mismatched skillId directories
* fix(skills): preserve source path during updates
* fix(skills): refresh source URL after repository relocation
* test(skills): isolate relocation fixture from legacy home
feat(opencode): 支持从获取的模型列表中搜索并批量添加所选模型 (#7515)
* feat: implement fetched models selection and UI components
* fix(opencode): prevent model search from submitting provider form
Add Kimi Global preset variants (kimi.ai platform + api endpoints) (#7526)
Add a "Kimi Global" twin immediately after the domestic "Kimi" API preset and a "Kimi For Coding Global" twin after the domestic "Kimi For Coding" preset, in every preset file that ships them (claude, claude-desktop, codex, hermes, openclaw, opencode, pi).
"Kimi Global" points to the international open platform https://platform.kimi.ai?aff=cc-switch (apiKeyUrl mirrored as https://platform.kimi.ai/console/api-keys?aff=cc-switch) and swaps the endpoint host api.moonshot.cn for api.moonshot.ai with each file's path unchanged (/v1 or /anthropic).
"Kimi For Coding Global" points to the international site https://www.kimi.ai/code/?aff=cc-switch and swaps the endpoint host api.kimi.com for api.kimi.ai with the path unchanged (e.g. https://api.kimi.ai/coding/v1).
Domestic presets are unchanged; category, icons, apiFormat, model catalogs, endpointCandidates (host swapped), partnerPromotionKey and settings otherwise mirror the domestic blocks exactly.
primePartner stays only on the domestic presets so the new variants do not alter prime-partner ordering.
The Pi twins get distinct providerKey values ("cc-switch-kimi-global", "cc-switch-kimi-for-coding-global") because providerKey is the preset-level primary key and must not collide with the domestic presets.
Rebalance sponsor CTAs around Kimi Code plan with dual-region links (#7522)
Kimi Code plan purchases have resumed, so the README sponsor sections go back to leading with the coding plan instead of the API-only flow written during the purchase pause.
All four locales (EN/ZH/JA/DE) now present Kimi Code plan (中文站 | Global) as the primary CTA and the Kimi Open Platform API (中文站 | Global, with the first-top-up bonus) as the secondary one, mirroring the domestic|global split already used for API links.
The aff and track_id values are unchanged.
fix(prompts): refresh active prompts after external file edits (#7194)
Reload the enabled prompt from its live file when the Prompts list loads, and refresh the open panel when the window regains focus, so external edits to CLAUDE.md / AGENTS.md show up without a restart.
- Keep saved templates available when the live file cannot be read.
- Skip the backfill for empty or whitespace-only files, matching enable_prompt.
- Skip the backfill while a sync/restore holds the sync lock, so a list load cannot write a stale live file into a freshly restored database.
fix(skills): raise archive entry limit so large skill repos install (#7489)
Installing hugohe3/ppt-master from skills.sh failed with
ARCHIVE_TOO_MANY_ENTRIES: the GitHub archive we download is the whole
repository (13,248 entries), and the 10,000-entry cap added in #5811
treated it as a zip bomb.
- Raise MAX_ARCHIVE_ENTRIES to 30,000 for both the remote archive and
local ZIP paths, and align WebDAV's MAX_EXTRACT_ENTRIES so a skills.zip
containing such a skill still restores.
- Charge every file entry at least one 4 KiB block against the existing
512 MiB budget, mirroring directory accounting, so empty-file archives
are bounded by the byte budget rather than by the entry count alone.
- Add tests for a 13k-entry repo archive and for the per-file minimum
charge.
Fixes: https://github.com/farion1231/cc-switch/issues/7475
feat: add MiniMax Code harness support (#7383)
Add MiniMax Code (mcode) as a managed application: provider and model management through the native custom_provider configuration, MCP servers, Skills, global instructions (AGENTS.md), read-only local session history with resume, and usage import from the native runtime database. Native files shared by the TUI and desktop are written under the native lock with rollback when the database commit fails, unrelated native configuration is preserved, and the data directory honors MINIMAX_DATA_DIR and MAVIS_DATA_DIR. Deleting sessions stays in the native application because they are coupled to runtime state.
fix(codex): recover stale account bindings during takeover (#7395)
After deleting a managed ChatGPT account and logging in again, a Codex provider can still reference the old local account ID, which made route switching, takeover, startup restoration and rebinding fail permanently.
Distinguish a missing outgoing account from an existing account with no matching live token. A missing account only releases its ownership marker after the persisted account store is verified; the target write policy stays in force. A stale target now reports an account-selection instruction instead of a bare not-found error, including when takeover is already enabled.
Fixes #7392
feat(claude-desktop): support 3P configuration on Linux (#7331)
Add Claude Desktop 3P configuration support on Linux. Resolve the config root from an absolute XDG_CONFIG_HOME, falling back to ~/.config; when CC Switch itself runs as a Flatpak, use the host ~/.config instead of the sandbox-private XDG_CONFIG_HOME. Update the en/zh/ja user manuals with Linux paths and the Flatpak boundary.
Fixes #4855
fix(proxy): omit missing tool descriptions instead of serializing null (#7319)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): keep reasoning effort for grok-4.6 and pass xhigh through verbatim (#7318)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(models): support Zhipu OpenAI Responses model lists (#7330)
Zhipu's Responses endpoint (open.bigmodel.cn/api/v1) serves its model list in the Codex remote catalog shape (models[].slug) instead of the OpenAI-compatible data[].id shape, so the Codex form's model fetch came back empty. The parser now falls back to models[].slug when data is absent.
fix(misc): fetch npm dist-tags from the dedicated endpoint with the probe timeout (#7346)
The tool version probe requested the full npm packument for each package (metadata for every historical version, 14-25 MB uncompressed for codex / opencode / openclaw) only to read its dist-tags, and it was the only latest-version probe without LATEST_PROBE_TIMEOUT, so it fell back to the proxy client's 600 s total timeout and the cards sat on "loading" for minutes on slow links while the large downloads starved the Hermes GitHub probe into "unknown".
Switch fetch_npm_dist_tags to registry.npmjs.org/-/package/{package}/dist-tags, whose body is the dist-tags object itself (tens of bytes to a few KB), escape scoped names as %2f, and apply the same 15 s probe timeout as the GitHub and PyPI probes. No data is lost: the only consumer reads latest and the tool's prerelease tags.
Fixes #7339
feat(usage): display output tokens per second in request log table and detail panel (#3369)
Show tokens/s next to the output token count in the request log table and the request detail panel.
The generation window prefers an explicit duration when present, otherwise latency minus first-token time for streaming requests, and falls back to full latency for non-streaming requests. Rows without a positive token count or window show nothing.
Closes #5936
fix(presets): update APIKEY.FUN URLs to apikey.fan
docs(release): add v3.20.3 release notes
chore(release): v3.20.3
fix(pricing): move retired DeepSeek V4 family to the V4.1 Flash tier
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.
fix(presets): declare third-party Token Plan DeepSeek V4 rows text-only
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.
feat(presets): rename DouBaoSeed preset to localized Volcengine Doubao
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.
fix(codex): skip unchanged incomplete rollout tails
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.
fix(claude): align quick model selection with form order
fix(tray): show bound ChatGPT account quota for managed Codex cards
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
feat(claude): add "Disable Artifact Tool" quick toggle to provider editor
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.
feat(codex): refresh aggregator model presets and catalogs
feat(codex): switch Kimi presets to native Responses direct-connect
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.
fix(usage): show Claude Fable weekly limit from the usage API limits array
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.
fix(codex): mirror vision-capable DeepSeek catalog and un-gate the legacy v4-flash alias (#7286)
Refresh the bundled DeepSeek official Codex catalog to the vendor's current models.json (slug deepseek-flash, image input modality, supports_image_detail_original) and remove deepseek-v4-flash from the confirmed text-only registry: the legacy id is still accepted by DeepSeek and routed to the vision-capable V4.1 Flash, so both the proxy media sanitizer and the generated Codex catalog must fail open for it. deepseek-v4-pro stays text-only per the vendor catalog.
Fixes #7283
fix(proxy): clamp sub-floor max_tokens to the Responses API minimum (#7287)
The Responses API rejects max_output_tokens below 16, but Anthropic clients legitimately send tiny probe budgets (Claude Desktop's model-availability probe uses max_tokens=1). The Anthropic→Responses conversion copied the value verbatim, so strict upstreams returned 400 and Claude Desktop reported the mapped model as unavailable under local routing.
Clamp 1..=15 up to 16 during conversion; 16+, 0 and non-integer values keep their pass-through semantics. Normal session requests convert byte-for-byte as before, and the Codex OAuth path still strips max_output_tokens afterwards.
Fixes #7103
fix(codex): coalesce adjacent commentary with pending tool calls (#7280)
The Codex Responses -> Chat Completions converter emitted two consecutive
assistant messages when one Responses model turn contained a commentary
message directly followed by function_call items. Chat upstreams imitate
the text-only assistant message as a complete turn and return
finish_reason=stop before the expected tool call, so long agent tasks end
after a progress update.
Merge pending tool calls into the directly adjacent assistant message when
it carries no tool_calls yet, attach pending reasoning per segment so
parallel calls do not duplicate it, and keep the new-message path for every
other boundary shape (user/tool boundaries, prior tool-call batches, media
flushes). The reasoning placeholder backfill still applies to coalesced
calls without reasoning.
Fixes #6529
Supersedes #6530
Co-authored-by: BigStrongSun <BigStrongSun@users.noreply.github.com>
fix(proxy): skip empty reasoning_content placeholders in Anthropic SSE streaming (#7227)
Some OpenAI-compatible upstreams (GLM, Qwen, DeepSeek-V4-Pro via ModelScope/DashScope,
Bailian, JD Cloud, ...) keep a `reasoning_content: ""` placeholder in every content
chunk instead of omitting the field. The reasoning branch of the OpenAI Chat -> Anthropic
SSE converter only checked for presence, and it shares `current_non_tool_block_type`
with the content branch, so every content chunk closed the open text block, opened an
empty thinking block, then reopened a text block. N content chunks became 2N content
blocks, half of them empty thinking blocks, which Claude Code rendered as one line per
token with dozens of empty Thought blocks and bloated the session JSONL.
Filter empty reasoning before entering the branch, matching the `!content.is_empty()`
guard the content branch already had and the non-streaming `openai_to_anthropic`
converter. Two regression tests pin block index, type and order: the placeholder shape
must yield a single text block and no thinking_delta at all, and real reasoning must
still yield one thinking block followed by one text block.
Fixes #5028
Fixes #4404
fix(codex): detect growing rollouts with a persisted byte cursor (#7219)
Codex keeps the rollout append handle open for the whole session, so on Windows NTFS the file mtime does not advance while the file grows and the pure-mtime gate skipped growing rollouts forever. Persist the observed byte length in session_log_sync.last_byte_offset for Codex rows and skip a file only when both mtime and size are unchanged. The parser now splits records on newlines manually so an incomplete trailing record is retried on the next pass without advancing the line cursor.
Fixes #6060
fix(codex): proxy image edits and Images API follow-ups (#7177)
* fix(codex): proxy image edits and Images API follow-ups
Follow-ups to #7036 for Codex's built-in ImageGen over the local proxy:
- Register `/images/edits` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them through the shared Codex standalone passthrough.
ImageGen posts to `images/edits` whenever a call references existing
images, and the router returned an empty 404 for it.
- Add `CodexStandaloneEndpoint::ImagesEdits`. Both Images endpoints share
one full-URL suffix table, so a complete Responses / Compact / Chat
Completions / Images URL derives the sibling endpoint (and vice versa)
while opaque full URLs keep failing closed.
- Apply the standalone sibling rewrite when the base URL is pasted as a
complete endpoint with the full-URL switch off, mirroring
`codex_chat_base_is_full_endpoint`; standalone requests no longer become
`.../chat/completions/images/generations`.
- Parse Images API streaming usage: `image_generation.completed` carries
`usage` on the event top level, so scan for `usage.input_tokens` before
falling back to the Chat Completions stream parser.
Related #7036
* fix(codex): match standalone URL suffixes case-insensitively
Keep endpoint detection and sibling rewriting consistent while preserving the original URL prefix and query. Cover mixed-case Images and Alpha Search endpoints through URL and proxy regression cases.
---------
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): preserve per-app settings on shutdown and port allocation (#7210)
On normal exit, stop_with_restore_keep_state() called the legacy global
proxy-config writer to clear the long-retired live_takeover_active flag.
That writer reads the Claude row and runs an UPDATE without a WHERE clause,
so every shutdown copied Claude's max_retries and the three timeout fields
onto the Codex, Gemini and Grok Build rows — and the UPDATE never carried
live_takeover_active in the first place, so the step had no effect beyond
the clobber. The ephemeral listen-port path did the same on every proxy
start.
Drop the dead write-back (live-config restore, backup cleanup and health
reset are unchanged) and persist the resolved ephemeral port through the
global-config interface, which only touches the shared listen/logging
columns. Two regression tests seed distinct per-app settings for all four
apps and assert they survive both paths.
Fixes #7204
fix(provider): preserve child metadata during universal sync (#7212)
Syncing a universal provider regenerates its Claude/Codex/Gemini child
providers. The child's `settings_config` was merged with the existing row,
but `meta`, `created_at` and `sort_index` were taken from the generated
object, and the universal provider never carries any of them from the UI.
The UPDATE therefore wiped the child's usage script, common-config opt-out,
endpoint auto-select and other per-app settings to `{}` and reset the sort
index to NULL, which pushed the card to the bottom of the list and looked
like the provider had been deleted and re-created.
Keep the existing child's `meta`, `created_at` and `sort_index` when the
row already exists. Name, base URL, key, models, website and notes are
still driven by the universal provider, and first-time creation still
inherits the parent's metadata.
Fixes #7134
feat(presets): rebrand DashScope as 千问AI平台 and refresh Qwen models (#7183)
Rename the domestic Bailian presets to 千问AI平台 with a dedicated icon, refresh the Qwen family to the 3.8 generation across Claude Code, Claude Desktop, Codex, Hermes, OpenClaw, OpenCode and Pi, add the domestic Token Plan presets, restore the international QwenCloud Coding Plan presets, and align OpenClaw/OpenCode/Pi model metadata with the official client docs.
fix(codex): honor proxy URL when model_provider is omitted (#7263)
When a Codex provider card's TOML has no `model_provider`, Codex falls back to
its built-in `openai` provider, but takeover wrote the proxy address as a
top-level `base_url` that Codex never reads, so requests bypassed the local
proxy and went straight to api.openai.com.
Treat the missing selector as the built-in `openai` provider and write the
proxy address to `openai_base_url`; the existing legacy-reroute migration then
turns it into a `cc-switch` custom provider table carrying the `PROXY_MANAGED`
bearer, the same shape every other third-party takeover uses. Explicit
provider selections are unchanged.
Refs #6256, #7217
fix(provider): update MiniMax defaults to M3 and remove expired offers (#7255)
* fix(provider): refresh MiniMax M3 presets and remove expired promotions
* fix(provider): complete MiniMax M3 capability declarations
---------
Co-authored-by: saladday <1203511142@qq.com>
chore(provider): remove Atlas Cloud sponsor status, keep presets
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`
docs(release): add v3.20.2 release notes
chore(release): v3.20.2
feat(provider): add SoleAPI sponsor presets across all nine apps
SoleAPI (soleapi.com) joins as a sponsor, placed directly above Micu in
the sponsor order (preset files and the four READ…
新建/编辑供应商表单里,可从其他已启用应用的已有供应商一次性带入
名称、备注、官网链接、API Key、请求地址、默认(兜底)模型名与上游
API 格式,其余配置由用户自行填写。10 个应用的配置形状各不相同,
映射层按目标应用分别落键;编辑模式只覆盖这三个键位,配置对象其余
部分(providerKey、模型表、Codex modelCatalog、Gemini 顶层 config、
env/options/auth 其他键含 Codex 登录态键)原样保留。
- src/utils/providerImport.ts:读侧按应用取可迁移字段;写侧拆成
buildImportedSettingsConfig(新建,按目标应用「自定义」模板重建)
与 patchImportedSettingsConfig(编辑,就地覆盖)两条路径
- src/components/providers/forms/ProviderImportEntry.tsx:来源应用
按钮行 + 供应商选择弹窗,预告「本次将写入」的字段,不渲染 API Key
- useProviderImportSources / useProviderImportApply:来源枚举与表单
写入;ProviderPresetSelector 增加 extraActions 插槽
- src/utils/piProviderConfig.ts:buildPiSettingsConfig 自
PiProviderForm 抽出,供映射层复用
- i18n 四语言新增 providerImport 文案
release 工作流改为直接以正式版发布(prerelease: false),不再先挂
预发布等人工提升;注意这同时移除了那道人工放行关卡,tag 一推发布
即对 /releases/latest、下载页和应用内更新器可见。sync-r2 不受影响,
其 released 触发器对直接以 stable 发布同样触发。
chore(native): archive cross-app-provider-import
chore(native): archive sync-upstream-3203
chore: sync fork with upstream v3.20.3 and rebrand top-left link to GitHub
Rebase fork main onto upstream/main (v3.20.3, 06082e18, 33 new commits)
while keeping all fork modifications: connectivity test, model quick
switch, sidebar panel visibility, preset filtering, tray left-click
toggle, dual website links, disabled in-app updater, fork CI.
- Bump fork version to 3.20.3-1 (package.json, tauri.conf.json,
Cargo.toml, Cargo.lock).
- Merged upstream's v18->v19 schema migration with the fork's
website_url_2 column migration in the same migration arm.
- Added mcode entry to forkOfficialAllowlist (no official presets,
same treatment as hermes/openclaw/opencode/pi).
- Changed the top-left CC Switch brand link target from
https://ccswitch.io to https://github.com/farion1231/cc-switch.
- Updated HOW_TO_REBASE_UPSTREAM.md whitelist with the brand link
override.
style: apply prettier to files flagged by CI format check
修复 CI Frontend Checks 的 Check formatting 步骤失败
(App.tsx、ConnectivityTestDialog、ProviderCard、ProviderList、
provider schema 共 5 个文件)。
style: apply cargo fmt to connectivity-test era backend files
修复 CI 的 Check Rust formatting 步骤失败(connectivity_test、
backup、tests、provider 等文件格式不符合 rustfmt 规范)。
chore(native): archive sync-upstream
docs(fork): update rebase whitelist for connectivity-test era fork features
- 白名单补充连通性测试前后端、模型快捷切换、双官网链接测试、
CI 裁剪版、README/CHANGELOG、.comet/superpowers 产物等 fork 专属文件
- 新增共享文件 fork 专属语义表(托盘左键切换、禁用更新器、hidden 字段等)
- 修正 §3.1 rebase 方向说明:rebase 中 fork 侧是 --theirs 而非 --ours
(v3.20.2 同步实测确认,与 merge 语义相反)
chore: bump fork version to 3.20.2-1 after upstream v3.20.2 rebase
重放 fork 提交到 upstream/main(f3b18df1,v3.20.2)后统一版本号。
Cargo.lock 同步移除 updater 依赖树(fork 已禁用应用内更新器)。
chore(native): archive tray-left-click-toggle-window
chore(comet): 记录 tray-left-click-toggle-window 验收通过与归档状态
feat(tray): 托盘图标左键单击切换主窗口显示/隐藏
- tray.rs 新增 show_main_window/hide_main_window/toggle_main_window 助手:
显示语义与"打开主界面"一致,隐藏与"关闭到托盘"平台处理一致
(Windows set_skip_taskbar、macOS apply_tray_policy)
- lib.rs 托盘 show_menu_on_left_click 改为 false,左键 Click(Left, Up)
触发切换(过滤 Down/Up 双事件),右键仍弹菜单;Enter/Click 用量刷新保持
- show_main 菜单事件与 macOS Reopen 复用 show_main_window
docs(readme): 更新日志补充连通性测试、双官网链接与托盘左键切换
feat(model-quick-switch): 获取模型列表成功后自动展开选择器
SearchableModelPicker 增加可选 open/onOpenChange 受控展开(不传保持内部自管,
表单等既有用法不受影响);快捷切换弹窗拉取成功且列表非空时直接展开,关闭弹窗复位。
chore(native): archive fix-quick-switch-1m
chore(comet): 记录 fix-quick-switch-1m 验收通过与归档状态
fix(model-quick-switch): 补齐 Haiku 显示名同步,支持不选模型仅翻转 1M
- 显示名同步映射补上 ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME(沿用空/旧base/旧base[1M] 覆盖、自定义保留语义)
- 新增 setClaudeOneMInSettings:仅原地翻转 1M 标记,不改各角色模型 base 与显示名
- 弹窗打开时 1M 开关按供应商当前状态初始化,避免已开 1M 被静默剥离
- claude 未选模型且 1M 状态有变化时允许保存;无可应用变化时保存禁用
- 补单测与四语言 toast 文案
feat(provider-website-links): 供应商支持第二个官网链接,主页横排双链接
- Rust Provider/UniversalProvider 新增 website_url_2(serde websiteUrl2,skip none)
- SQLite providers 表新增 website_url_2 列(schema v18→v19 幂等迁移 + create_tables 补列 + 旧 config.json 迁移)
- DAO SELECT/INSERT/UPDATE/replace 全链路带出新列
- 表单共享 BasicFormFields + 统一供应商表单第二输入框;zod 同第一链接规则校验与回填
- ProviderCard 主页双链接横排、两空格分隔、逐链接 openExternal 打开默认浏览器
- 搜索匹配、复制供应商、统一供应商生成 claude/codex/gemini 随行透传
- gemini_auth packycode 认证检测纳入 website_url_2;ClaudeDesktop/GrokBuild 预设切换清空第二链接;卡片同值链接去重
- 4 语言 i18n + 前端/前端单测覆盖
Comet change dual-website-links-homepage(19/19 验收通过,归档)
feat(connectivity-test): 拉取模型列表改为「搜索添加」选择器交互
- 新增共享组件 SearchableModelMultiPicker:搜索过滤 + 多选 + 已在列表标记 + 已选计数
- ConnectivityTestDialog 获取模型列表后不再全量追加合并去重,改为会话内缓存完整列表并自动弹出一次选择器;用户搜索挑选后添加进表格并自动勾选
- 重新拉取只刷新缓存,已添加进表格的行保留;拉取结果仍仅会话内有效,不写回供应商配置
- 四语言 i18n 新增 searchAdd / addToTest / alreadyInList / selectedCount
- 同步更新 ConnectivityTestDialog 测试并新增 SearchableModelMultiPicker 测试
feat(connectivity-test): 探测目标按供应商 API 格式对齐真实转发链路
- build_claude 按 get_claude_api_format 分派端点与请求体:openai_chat → /v1/chat/completions(复用 transform_claude_request_for_api_format,流式注入 stream_options.include_usage)、openai_responses → /v1/responses、gemini_native → /v1beta/models/{model}:{method}(resolve_gemini_native_url)、anthropic 保持 /v1/messages 并补 anthropic-version/beta 头
- build_codex 按 should_convert_codex_responses_to_chat/anthropic 分派:chat → /chat/completions(responses_to_chat_completions_with_reasoning)、anthropic → /v1/messages(responses_request_to_anthropic,含 max_output_tokens 注入与 [1m] 剥离 + context-1m beta 头)
- 鉴权改用 ClaudeAdapter/CodexAdapter 的 extract_auth + get_auth_headers(与真实出站请求同源:AUTH_TOKEN→Bearer、ANTHROPIC_API_KEY→x-api-key、gemini→x-goog-api-key、GeminiCli OAuth→Bearer、OpenRouter→Bearer、Codex 按 apiKeyField)
- 转换前剥离 [1m] 标记;meta.isFullUrl 或 base 已是完整端点时直接使用 base_url;自定义 body 浅合并移至转换后的最终协议体
- 新增 18 个目标构造断言单测;Comet 归档产物同步提交
chore: archive connectivity-test-optimizations
feat(connectivity-test): 单一模型表格 + 实时拉取模型 + 勾选即测试范围 + 新默认提示词
- 弹窗合并为单一模型表格:打开即全部模型行(初始待测试)、行勾选 + 表头全选(含半选态)、5 格统计卡,移除上方独立选择区
- 获取模型列表:凭据提取抽为共享模块 src/utils/providerCredentials.ts(ModelQuickSwitchDialog 同步改引用),复用 fetch_models_for_config 拉取 /v1/models,按 ID 追加合并去重、仅会话内有效
- 默认提示词改为「你好,你可以帮我做什么事情」:前后端常量同步,merge 差异判断基准跟随
- 勾选即测试范围:后端 connectivity_test_provider_models 新增 modelIds 参数(None/空白回退静态清单),前端逐模型独立 invoke 并行 + 行级流式更新
- 一轮测完自动勾选失败项,全部成功保持原勾选
fix(connectivity-test): UI 四项修复
- 弹窗抬至 alert 层(z-[60]):打开连通性测试时 header 切换栏正确变灰,
详情子弹窗同步抬层(同 z + 更晚挂载 portal 在上)
- 批量检测入口移至 header 图标组(状态提升到 App,ProviderList 经
props 读取探针结果),不再另起一行
- 批量检测可停止:runId 代次守卫(Tauri invoke 无法真正取消),再次
点击即清除 waiting/running 徽标、丢弃在途迟到写入、不再拉起排队项
- 弹窗打开自动勾选模型:已保存默认测试模型 → 供应商当前模型
(claude env.ANTHROPIC_MODEL / codex config.toml model,新增
currentProviderModelId 与后端同口径)→ 清单首个
前端全量 141 文件 / 1106 测试全绿;typecheck clean
chore: archive per-model-connectivity-test
fix(connectivity-test): shouldShowTestEntry 增判 providerType + 收尾清理
chore(connectivity-test): 勾选 Task 9 / OpenSpec 5.1-5.2 + verify 报告
chore(connectivity-test): 勾选 Task 8 / OpenSpec 4.1-4.3
refactor(connectivity-test): 移除旧 stream_check 可达性探测全链路(保留表结构)
- 删除 6 个文件:commands/stream_check.rs、services/stream_check.rs、
database/dao/stream_check.rs、hooks/useStreamCheck.ts、lib/api/connectivity-check.ts、
components/usage/ConnectivityCheckConfigPanel.tsx
- 移除 mod.rs 声明、lib.rs generate_handler! 注册、cleanup_old_stream_check_logs 调用
- 从 SYNC_SKIP_TABLES / SYNC_PRESERVE_TABLES 移除 stream_check_logs(表结构保留在 schema.rs)
- 更新 backup.rs 测试:移除 stream_check_logs fixture 与断言
- 更新 SettingsPage:移除 ConnectivityCheckConfigPanel AccordionItem 与 FlaskConical 导入
- i18n:删除 streamCheck.* 命名空间(zh/en/ja/zh-TW 四语言)
- 更新注释:移除 stream_check 旧引用(forwarder.rs、provider.rs、userAgentPresets.ts、
connectivity_test.rs)
- 修复 4 个 pre-existing clippy lint(connectivity_test: matches! 宏、unused_unit、
derive Default)
- 移除 ProviderList.test.tsx 中已失效的 useStreamCheck mock
chore(connectivity-test): 勾选 Task 7 / OpenSpec 2.4 3.1-3.3
feat(connectivity-test): 供应商列表集成(弹窗入口 + 批量探针徽标)
chore(connectivity-test): 勾选 Task 6 / OpenSpec 2.2-2.3
fix(connectivity-test): 模型列表解析与后端对齐(每条目首个匹配,提取共享 helper)
feat(connectivity-test): 连通性测试弹窗/详情/徽标组件与四语言文案
chore(connectivity-test): 勾选 Task 5 / OpenSpec 2.1
test(connectivity-test): 锁定 toBackendParams 字段映射(customHeaders/customBody 等)
feat(connectivity-test): 前端命令绑定与测试/探针状态 hooks
chore(connectivity-test): 勾选 Task 4 / OpenSpec 1.4
feat(connectivity-test): 测试参数持久化纯函数(缺省即默认值)
chore(connectivity-test): 勾选 Task 3 / OpenSpec 1.3
feat(connectivity-test): Tauri 命令与注册,官方/动态端点过滤,sanitize 隔离私有字段
chore(connectivity-test): 勾选 Task 2 / OpenSpec 1.2
feat(connectivity-test): 后端直连执行器(reqwest+SSE,总/空闲超时,度量与错误识别)
feat(connectivity-test): 后端直连执行器(reqwest+SSE,总/空闲超时,度量与错误识别)
chore(connectivity-test): 勾选 Task 1 / OpenSpec 1.1
fix(connectivity-test): claude 鉴权对齐 adapter 顶层 apiKey/api_key 兜底
feat(connectivity-test): 新增后端连通性测试服务类型/模型解析/协议构造
chore: archive model-quick-switch-picker-scroll
chore: model-quick-switch-picker-scroll 验证报告与状态元数据
fix: 模型快捷切换弹窗内模型列表滚轮无法滚动
模态 Dialog 的 RemoveScroll 会对 shards 外的 portaled PopoverContent
wheel 事件无条件 preventDefault。在 SearchableModelPicker 的
PopoverContent 捕获阶段阻断 wheel 传播,恢复原生滚动。
chore: bump fork version to 3.20.1-1 after upstream v3.20.1 rebase
fix(form): only auto-expand Claude advanced options on divergent model mapping
Uniform mapping produced by the quick-set button (all roles equal to the
fallback model) no longer counts as an advanced value, so the advanced
section stays collapsed on edit. A 1M marker is stripped before comparing
so marked roles are not misjudged as divergent. Truly customized mappings
still auto-expand the section.
feat: align fallback-model quick-set buttons with model input right edge
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
docs: update README with fork build notes and quick-move provider changes
chore: archive comet-native claude-model-quick-access
feat: surface Claude fallback model as quick-access section below endpoint
Move the default fallback model (ANTHROPIC_MODEL) block out of the
collapsed Advanced Options into a quick-access section directly below
the request endpoint: input + 1M checkbox + quick-set + fetch-models
buttons are all reachable without expanding.
- header toggle-all "declare 1M" now includes the fallback model
(Haiku still excluded); partial selection shows indeterminate state
- drop the duplicated quick-set/fetch buttons and the old fallback
block inside Advanced Options
- exclude claudeModel from hasAnyAdvancedValue so a fallback-only
value no longer auto-expands Advanced Options
- quick-set priority, env write semantics, and OAuth preset branches
unchanged; add 4 component tests covering the new layout
chore: archive comet-native provider-right-click-top-bottom
Enable native workflow in .comet/config.yaml and move the Comet managed
state block in .gitignore so the exclusion rules stay grouped with the
other fork tool artifacts.
feat: add provider right-click move-to-top/bottom and insert new providers at second position
- ProviderList: context menu with quick move-to-top/move-to-bottom, reusing
update_providers_sort_order and refreshing tray menu like drag sort
- useAddProviderMutation: default insert at index 1 with explicit sortIndex
materialization for existing items (avoids defined-first sort trap)
- i18n: quickMove* strings for zh/en/zh-TW/ja
ci: stop flaky WSL2 linker job from blocking main
The backend-windows-wsl2 job intermittently fails with LNK1327 /
mt.exe c1010070 when the Windows runner's mt.exe cannot parse a
manifest temp file on the WSL2 UNC TEMP path. The same flaky failure
occurs upstream and is unrelated to fork changes. Mark the job
continue-on-error so main stops going red on it; the result stays
visible in the Actions UI.
style: apply prettier to files touched by recent comet archives
Frontend CI format:check failed on 6 files modified by the app-name
and model-quick-switch archive commits plus the updater-disable commit.
Run prettier --write so format:check passes again.
chore: archive ccs-fork-app-name-cc-switch
chore: archive ccs-model-quick-switch-ui-polish
docs: rewrite README to document fork differences vs upstream
README 从上游完整功能介绍改为仅记录 fork 与上游的差别,完整功能
文档指回上游。涵盖 8 项 fork 魔改:构建标识/开发预览、预设精简、
Claude 表单调整、模型搜索移植、主页模型快速切换、侧边栏面板显隐、
关闭应用内更新、打包范围,并说明版本号与 rebase 同步流程。
feat: disable in-app updater, point users to GitHub releases
移除 tauri-plugin-updater 依赖与自动更新链路,改为统一引导用户到
GitHub Releases 手动下载新版。
- 前端:updater.ts 恒返回 up-to-date;UpdateContext 取消启动自检;
AboutSection「检查更新」直接打开 fork releases 页;DatabaseUpgrade
恢复界面去掉下载进度条,改为打开发布页。
- 后端:移除 updater 插件注册与 UpdaterExt 调用;
install_update_and_restart 降级为空壳,check_app_update_available
返回当前版本号;check_for_updates 指向 fork releases;capabilities
去掉 updater:default 权限。
- 打包:tauri.conf.json 删 plugins.updater、createUpdaterArtifacts=false;
Cargo.toml / package.json 删 updater 依赖,Cargo.lock / pnpm-lock
同步重生成。
- CI:release.yml 移除签名收集与 assemble-latest-json job,删除
.tar.gz/.sig 处理,release body 去掉 updater 说明。
版本号统一为 3.20.0-1(合法 semver 且 MSI 第 4 位为 1)。
Cargo.lock / pnpm-lock.yaml 已随依赖移除重生成,cargo check 通过。
ci: build only Windows x64 and macOS arm64, unsigned
feat: gate dev panel behind dev-only env flag
chore(comet): archive ccs-sidebar-panels-visibility change
fix: apply skills visibility guard to hermes branch toolbar button
feat: include prompts in sidebar panel visibility settings
fix: persist visibleSidebarPanels setting in backend AppSettings
style: use compact pill toggles for sidebar panel visibility
feat: add sidebar panel visibility switches to settings
feat: add sidebar panel visibility settings and wire into toolbar buttons
chore(comet): archive ccs-home-model-display change
fix(fork): read grokbuild api key from config TOML in quick switch dialog
feat(fork): align model badge with upstream extractModelBadge
feat(fork): show current model badge and quick-switch entry on provider cards
feat(fork): add model quick switch dialog
feat(fork): add provider model read/write utils with tests
chore(comet): archive ccs-model-search-port change
feat(i18n): add model search picker keys for all locales
feat(form): port SearchableModelPicker from upstream for searchable model selection
chore(comet): archive ccs-claude-form-layout change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-claude-form-layout/
- Sync claude-form-layout spec to main specs
- Annotate design doc as archived
- Add verification report
fix(i18n): update advanced options hint, add ja/zh-TW toggle-all, drop dead apiFormatHint key
fix(form): correct 1M toggle-all alignment direction (ml-1)
fix(form): align 1M toggle-all checkbox with row checkboxes
refactor(form): move api format select into EndpointField rightSlot
fix(form): widen api format select to fit option text
feat(form): add 1M toggle-all checkbox in model mapping header
feat(form): move api format select next to endpoint input
chore(comet): archive ccs-provider-presets-trim change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-provider-presets-trim/
- Sync provider-presets-visibility spec to main specs
- Annotate design doc as archived
- Add verification report
docs(fork): add presets-trim files to rebase whitelist
test(fork): mock IS_FORK_BUILD false in unit tests for upstream-form semantics
feat(fork): wire official preset filter into provider forms
feat(fork): add hidden field to preset interfaces
feat(fork): add official preset allowlist and filter util
chore(comet): archive ccs-fork-scaffolding change
- Move change artifacts to docs/openspec/changes/archive/2026-08-18-ccs-fork-scaffolding/
- Sync fork-build-identity spec to main specs
- Annotate design doc as archived
- Add verification report
feat(fork): add dev config with isolated identifier and config dir
docs(fork): complete rebase whitelist with all fork-touched files
fix(fork): guard setTitle with isTauri in test/non-tauri env
fix(fork): define __CCS_FORK_BUILD__ in vitest config
feat(fork): add DevPanel and settings footer entry
feat(fork): set macOS window title to CC Switch (Fork) on startup
feat(fork): inject __CCS_FORK_BUILD__ compile-time constant
feat(fork): bump version to 3.19.2-fork.1 and rename product to CC Switch (Fork)
docs(fork): add upstream rebase workflow and ignore tool artifacts
fix(proxy): accept the whole grok-4.x (x>=5) family in the reasoning-effort whitelist (#7369)
Replace the verbatim grok-4.5 / grok-4.6 entries in supports_reasoning_effort with a rule that parses the grok-4.x minor version and accepts x >= 5, mirroring the existing GPT-5+ rule. This covers grok-4.7 (released 2026-09-21), whose reasoning effort was previously dropped on the Claude -> Chat, Claude -> Responses and Codex Responses -> Chat conversion paths, and lets future releases pass without another whitelist edit. The grok-build-* family is retained for saved providers.
Co-authored-by: allenxu09 <171831965+allenxu09@users.noreply.github.com>
docs(release): add v3.20.4 release notes
chore(release): v3.20.4
feat(pricing): add Grok 4.7 and GLM-5.3-FlashX
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.
fix(i18n): remove outdated OpenCode Go referral offer
fix: add SudoCode.chat alternate API endpoint
feat(settings): add a GitHub star prompt to the About card
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
feat(presets): add Soshow aggregator presets
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.
fix(ci): stabilize frontend tests and verify renderer build
fix(mcode): refresh provider state after removal (#7578)
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
fix(lib): reset skip_taskbar before window.show() across entry points (#6348)
5处 window.show() 调用中 4处没有在 show() 前重置 Windows 的 skip_taskbar,导致以下场景出现窗口可见但不在任务栏、点最小化后窗口消失的 bug:
- single_instance handler (lib.rs:360-361):当原进程因 silent_startup / 关闭到托盘等处于 skip_taskbar(true) 状态时,从快捷方式/开始菜单唤起已运行实例触发。
- handle_deeplink_url 的 focus_main_window 分支 (lib.rs:256):通过 ccswitch:// URL 唤起时。
- DB 恢复流程 (lib.rs:556):数据库版本不匹配时显示恢复界面。
- RunEvent::Opened 处理器 (lib.rs:1803):系统接收到 deep-link URL 时。
修复后的行为与 tray.rs::handle_tray_menu_event::show_main 分支一致(那里已有相同的 set_skip_taskbar(false) 调用)。
经用户测试验证:autostart + silent_startup + 从开始菜单/快捷方式启动的场景下,最小化按钮恢复正常工作。
fix(proxy): omit missing tool descriptions in the Codex responses-to-chat converter (#7378)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): preserve max effort for GPT-5.6 and GPT-6 Astra (#7531)
fix(misc): ignore shell startup output when probing tool versions in WSL (#7348)
try_get_version_wsl runs "${SHELL:-sh}" -lic '<tool> --version' through
wsl.exe and takes the first x.y.z anywhere in stdout. A login+interactive
shell runs the distro's startup files first, so their output shares that
stdout. On Ubuntu, /etc/profile.d/update-motd.sh prints the MOTD once a day,
starting with "Welcome to Ubuntu 24.04.4 LTS", and the card then showed
24.04.4 as the Claude Code version. The once-a-day stamp makes it appear
after the first probe of the day and vanish on refresh.
The payload now prints a sentinel before running the tool, and parsing looks
only at what follows its last occurrence. The last occurrence handles the
-lic || -lc || -c fallback chain printing it several times; matching it as a
substring handles OSC sequences glued in front of it. If the sentinel is
absent (the shell failed before the payload ran), the output is used as
before. The stderr fallback for tools that print their version there is kept.
Fixes #7347
Co-authored-by: lyfuci <lyfuci11@gmail.com>
fix(claude): hide session URL attribution (#7053)
* fix(claude): hide session URL attribution
* docs(claude): document session URL attribution
fix(ui): reset provider scroll when switching apps (#5211)
fix(pi): fix provider logo (#6826)
fix(i18n): use the Pi form's own term for the zh-TW duplicate-key message (#7187)
Follow-up to the nit on #6768. In zh-TW the Pi form labels the field
供應商識別碼 and says 供應商識別碼為必填 when it is empty, but the
duplicate message I added said 供應商標識 — the term Hermes, OpenCode and
OpenClaw use for their own field. One form, two names for the same thing.
The other three locales already keep the label and both messages on one
term, so this makes zh-TW match the pattern rather than introduce one:
en Provider key / Provider key is required / This provider key already exists
ja プロバイダーキー / プロバイダーキーを入力してください / このプロバイダーキーは既に存在します
zh 供应商标识 / 供应商标识不能为空 / 该供应商标识已存在
zh-TW 供應商識別碼 / 供應商識別碼為必填 / 該供應商標識已存在 <- the odd one out
hermes.form.providerKeyDuplicate carries the same sentence and is left
alone: its own label is 供應商標識, so it is already consistent.
Verified: managementListLocales 7/7. Full unit suite 1085/1087, the same
two App.test.tsx integration cases failing before and after.
fix(proxy): strip stop param for GitHub Copilot to unblock auto mode classifier (#5404)
Claude Code's auto mode classifier sends `stop_sequences` on every
safety check. On the GitHub Copilot path the Anthropic -> OpenAI Chat
conversion mapped it to `stop`, and Copilot's Chat Completions route
for Claude models answers that request with HTTP 400, so every
classifier call failed while normal chat kept working.
- Remove the converted `stop` field only when the provider is GitHub
Copilot; other OpenAI-compatible upstreams keep it.
- Copilot detection in the transform now also covers providers that
are recognised only through `env.ANTHROPIC_BASE_URL`.
- Regression tests for the Copilot (meta and env URL) and generic
provider cases.
Fixes #5175
fix(codex): keep stored auth when the live snapshot has no credential (#7434)
Editing the current Codex provider seeds the form from the live ~/.codex
snapshot, and switching away backfills that snapshot into the DB row. When a
provider table declares its own credential source (an Authorization header,
env_key, auth/aws), no bearer token is injected into config.toml and the
shared auth.json is deleted on third-party switches, so the live snapshot is
permanently `{ auth: {}, config }` while auth.OPENAI_API_KEY in the DB is the
only copy of the key. Both paths treated that empty live auth as
authoritative and silently erased the stored key, which in turn made the
usage-script modal misread the provider as an official subscription and
reset the saved script.
- Treat a credential-less live auth (missing auth.json or the bare
auth_mode logout marker) as an absent field in both the edit dialog and
the switch-away backfill, keeping the stored auth template. Live still
wins whenever it carries material, and official providers keep the live
login as their source of truth.
- Gate the official exemption on is_codex_official_provider /
resolveCodexOfficialIdentity rather than the category alone: a legacy
fixed official card without a persisted category is still written as
official, so the fallback must not resurrect a logged-out login on
switch-back.
- Cover both layers with regression tests, including the category-less
official shapes.
Fixes #7433
Supersedes #7492
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): lift additional_tools input carriers on the Codex chat conversion (#7454)
Codex talks to third-party providers over its Responses Lite transport,
which ships the request's extra tools in an `additional_tools` input
carrier: `{"type":"additional_tools","role":"developer","tools":[...]}`,
a role but no `content`. The Responses->Chat conversion let that carrier
fall through the generic "item with a role is a message" arm and emitted
`{"role":"system","content":null}` at messages[1], which strict Chat
gateways reject with `messages[1]: missing field "content"` (or
`data did not match any variant of ...SystemMessageContent`). The tools
carried inside it were also dropped on the Chat and Anthropic conversions.
- Skip `additional_tools` items in the Chat message walk; a carrier
declares tools, it is not a message.
- Collect carrier tools through the existing CodexToolContext registry
(dedup, namespace flattening, custom-tool conversion), which also feeds
the Anthropic conversion and the response-side name round-trip.
- Requests without a carrier convert byte-for-byte as before.
Known boundary: `custom` children inside a namespace tool
(`functions.exec`) are still not lifted; that needs its own response-side
name round-trip and is left for a follow-up.
Diagnosed independently in #6158 by BigStrongSun (fix in #6159) and in
fixture.
Fixes #7451
Fixes #6158
Refs #6033
Refs #6776
Supersedes #6159
fix(proxy): normalize Codex detail:original images for Chat gateways (#7476)
Codex marks tool-returned images with detail:"original" whenever the model
catalog advertises supports_image_detail_original, a Responses-only value.
The Responses->Chat conversion copied it verbatim into image_url.detail, and
strict OpenAI-compatible Chat gateways (Ark/Doubao, SenseNova, GLM, vLLM, ...)
reject the whole request with 400. Because Codex replays the full rollout on
every turn, one poisoned view_image result made the session fail permanently,
including plain-text turns.
Two layers:
- tool_media::merge_top_level_detail downgrades original -> auto, so already
poisoned rollouts and Codex versions before 0.155.0 (which do not normalize
replayed history themselves) recover immediately. auto/low/high are untouched.
- codex_catalog_model_entry always writes supports_image_detail_original:false
on the ProxyChat profile, so Codex stops emitting original at the source once
the catalog is regenerated. NativeResponses/Anthropic profiles and the DeepSeek
official catalog are unaffected.
Top-level user images were never affected: responses_content_to_chat_content
drops the part-level detail entirely.
Fixes #6495
Fixes #7458
Refs #6569
Refs #5805
fix(omo): detect unified config on the WSL side when the OpenCode dir is in WSL (#7550)
When the OpenCode config directory points into WSL
(\\wsl.localhost\<distro>\home\<user>\.config\opencode), OMO >= 4.19.3 keeps
its unified config in the WSL-side home (~/.omo/omo.jsonc). Detection only
probed the Windows home, never found it, and silently fell back to the legacy
per-plugin file that OMO no longer reads, so provider switches looked
successful but had no effect.
- Add derive_wsl_home_dir to derive the WSL-side home from a WSL UNC path
(home/<user> or root); non-WSL and non-home paths return None.
- Probe a list of home candidates for the unified config, WSL-side home
first and the OS home as a fallback, shared by the read, write and delete
paths.
- Detection only: when no unified config exists on either side the legacy
fallback is unchanged.
Fixes #7363
Supersedes #7367
docs(partner): add a FluxA purchase link and tighten its sponsor blurb
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.
fix(claude-desktop): expose the 1M context variant for DeepSeek V4 routes
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.
feat(presets): add FluxA Token Plan partner presets
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.
fix(presets): correct AICodeWith Codex-family endpoint to /v1
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.
fix(ui): render monochrome preset icons in the foreground color
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.
docs(user-manual): point Chat-only Codex examples at plan endpoints
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.
fix(pricing): restore DeepSeek V4 Pro price and seed Qwen3.8 and Hy4 models
- 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).
fix(presets): move MiniMax CN and BaiLing to their current official endpoints
- 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.
feat(codex): refresh CN presets from the Responses API audit
- 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.
test(claude-desktop): run Linux path tests on unix hosts and cover Linux in provider sync tests
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.
fix(ui): stop expanding stale cached usage tiers on ineligible provider cards
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.
fix(skills): 支持 skillId 与目录名不一致的技能安装和更新 (#6381)
* fix(skills): resolve mismatched skillId directories
* fix(skills): preserve source path during updates
* fix(skills): refresh source URL after repository relocation
* test(skills): isolate relocation fixture from legacy home
feat(opencode): 支持从获取的模型列表中搜索并批量添加所选模型 (#7515)
* feat: implement fetched models selection and UI components
* fix(opencode): prevent model search from submitting provider form
Add Kimi Global preset variants (kimi.ai platform + api endpoints) (#7526)
Add a "Kimi Global" twin immediately after the domestic "Kimi" API preset and a "Kimi For Coding Global" twin after the domestic "Kimi For Coding" preset, in every preset file that ships them (claude, claude-desktop, codex, hermes, openclaw, opencode, pi).
"Kimi Global" points to the international open platform https://platform.kimi.ai?aff=cc-switch (apiKeyUrl mirrored as https://platform.kimi.ai/console/api-keys?aff=cc-switch) and swaps the endpoint host api.moonshot.cn for api.moonshot.ai with each file's path unchanged (/v1 or /anthropic).
"Kimi For Coding Global" points to the international site https://www.kimi.ai/code/?aff=cc-switch and swaps the endpoint host api.kimi.com for api.kimi.ai with the path unchanged (e.g. https://api.kimi.ai/coding/v1).
Domestic presets are unchanged; category, icons, apiFormat, model catalogs, endpointCandidates (host swapped), partnerPromotionKey and settings otherwise mirror the domestic blocks exactly.
primePartner stays only on the domestic presets so the new variants do not alter prime-partner ordering.
The Pi twins get distinct providerKey values ("cc-switch-kimi-global", "cc-switch-kimi-for-coding-global") because providerKey is the preset-level primary key and must not collide with the domestic presets.
Rebalance sponsor CTAs around Kimi Code plan with dual-region links (#7522)
Kimi Code plan purchases have resumed, so the README sponsor sections go back to leading with the coding plan instead of the API-only flow written during the purchase pause.
All four locales (EN/ZH/JA/DE) now present Kimi Code plan (中文站 | Global) as the primary CTA and the Kimi Open Platform API (中文站 | Global, with the first-top-up bonus) as the secondary one, mirroring the domestic|global split already used for API links.
The aff and track_id values are unchanged.
fix(prompts): refresh active prompts after external file edits (#7194)
Reload the enabled prompt from its live file when the Prompts list loads, and refresh the open panel when the window regains focus, so external edits to CLAUDE.md / AGENTS.md show up without a restart.
- Keep saved templates available when the live file cannot be read.
- Skip the backfill for empty or whitespace-only files, matching enable_prompt.
- Skip the backfill while a sync/restore holds the sync lock, so a list load cannot write a stale live file into a freshly restored database.
fix(skills): raise archive entry limit so large skill repos install (#7489)
Installing hugohe3/ppt-master from skills.sh failed with
ARCHIVE_TOO_MANY_ENTRIES: the GitHub archive we download is the whole
repository (13,248 entries), and the 10,000-entry cap added in #5811
treated it as a zip bomb.
- Raise MAX_ARCHIVE_ENTRIES to 30,000 for both the remote archive and
local ZIP paths, and align WebDAV's MAX_EXTRACT_ENTRIES so a skills.zip
containing such a skill still restores.
- Charge every file entry at least one 4 KiB block against the existing
512 MiB budget, mirroring directory accounting, so empty-file archives
are bounded by the byte budget rather than by the entry count alone.
- Add tests for a 13k-entry repo archive and for the per-file minimum
charge.
Fixes: https://github.com/farion1231/cc-switch/issues/7475
feat: add MiniMax Code harness support (#7383)
Add MiniMax Code (mcode) as a managed application: provider and model management through the native custom_provider configuration, MCP servers, Skills, global instructions (AGENTS.md), read-only local session history with resume, and usage import from the native runtime database. Native files shared by the TUI and desktop are written under the native lock with rollback when the database commit fails, unrelated native configuration is preserved, and the data directory honors MINIMAX_DATA_DIR and MAVIS_DATA_DIR. Deleting sessions stays in the native application because they are coupled to runtime state.
fix(codex): recover stale account bindings during takeover (#7395)
After deleting a managed ChatGPT account and logging in again, a Codex provider can still reference the old local account ID, which made route switching, takeover, startup restoration and rebinding fail permanently.
Distinguish a missing outgoing account from an existing account with no matching live token. A missing account only releases its ownership marker after the persisted account store is verified; the target write policy stays in force. A stale target now reports an account-selection instruction instead of a bare not-found error, including when takeover is already enabled.
Fixes #7392
feat(claude-desktop): support 3P configuration on Linux (#7331)
Add Claude Desktop 3P configuration support on Linux. Resolve the config root from an absolute XDG_CONFIG_HOME, falling back to ~/.config; when CC Switch itself runs as a Flatpak, use the host ~/.config instead of the sandbox-private XDG_CONFIG_HOME. Update the en/zh/ja user manuals with Linux paths and the Flatpak boundary.
Fixes #4855
fix(proxy): omit missing tool descriptions instead of serializing null (#7319)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): keep reasoning effort for grok-4.6 and pass xhigh through verbatim (#7318)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(models): support Zhipu OpenAI Responses model lists (#7330)
Zhipu's Responses endpoint (open.bigmodel.cn/api/v1) serves its model list in the Codex remote catalog shape (models[].slug) instead of the OpenAI-compatible data[].id shape, so the Codex form's model fetch came back empty. The parser now falls back to models[].slug when data is absent.
fix(misc): fetch npm dist-tags from the dedicated endpoint with the probe timeout (#7346)
The tool version probe requested the full npm packument for each package (metadata for every historical version, 14-25 MB uncompressed for codex / opencode / openclaw) only to read its dist-tags, and it was the only latest-version probe without LATEST_PROBE_TIMEOUT, so it fell back to the proxy client's 600 s total timeout and the cards sat on "loading" for minutes on slow links while the large downloads starved the Hermes GitHub probe into "unknown".
Switch fetch_npm_dist_tags to registry.npmjs.org/-/package/{package}/dist-tags, whose body is the dist-tags object itself (tens of bytes to a few KB), escape scoped names as %2f, and apply the same 15 s probe timeout as the GitHub and PyPI probes. No data is lost: the only consumer reads latest and the tool's prerelease tags.
Fixes #7339
feat(usage): display output tokens per second in request log table and detail panel (#3369)
Show tokens/s next to the output token count in the request log table and the request detail panel.
The generation window prefers an explicit duration when present, otherwise latency minus first-token time for streaming requests, and falls back to full latency for non-streaming requests. Rows without a positive token count or window show nothing.
Closes #5936
fix(presets): update APIKEY.FUN URLs to apikey.fan
docs(release): add v3.20.3 release notes
chore(release): v3.20.3
fix(pricing): move retired DeepSeek V4 family to the V4.1 Flash tier
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.
fix(presets): declare third-party Token Plan DeepSeek V4 rows text-only
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.
feat(presets): rename DouBaoSeed preset to localized Volcengine Doubao
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.
fix(codex): skip unchanged incomplete rollout tails
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.
fix(claude): align quick model selection with form order
fix(tray): show bound ChatGPT account quota for managed Codex cards
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
feat(claude): add "Disable Artifact Tool" quick toggle to provider editor
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.
feat(codex): refresh aggregator model presets and catalogs
feat(codex): switch Kimi presets to native Responses direct-connect
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.
fix(usage): show Claude Fable weekly limit from the usage API limits array
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.
fix(codex): mirror vision-capable DeepSeek catalog and un-gate the legacy v4-flash alias (#7286)
Refresh the bundled DeepSeek official Codex catalog to the vendor's current models.json (slug deepseek-flash, image input modality, supports_image_detail_original) and remove deepseek-v4-flash from the confirmed text-only registry: the legacy id is still accepted by DeepSeek and routed to the vision-capable V4.1 Flash, so both the proxy media sanitizer and the generated Codex catalog must fail open for it. deepseek-v4-pro stays text-only per the vendor catalog.
Fixes #7283
fix(proxy): clamp sub-floor max_tokens to the Responses API minimum (#7287)
The Responses API rejects max_output_tokens below 16, but Anthropic clients legitimately send tiny probe budgets (Claude Desktop's model-availability probe uses max_tokens=1). The Anthropic→Responses conversion copied the value verbatim, so strict upstreams returned 400 and Claude Desktop reported the mapped model as unavailable under local routing.
Clamp 1..=15 up to 16 during conversion; 16+, 0 and non-integer values keep their pass-through semantics. Normal session requests convert byte-for-byte as before, and the Codex OAuth path still strips max_output_tokens afterwards.
Fixes #7103
fix(codex): coalesce adjacent commentary with pending tool calls (#7280)
The Codex Responses -> Chat Completions converter emitted two consecutive
assistant messages when one Responses model turn contained a commentary
message directly followed by function_call items. Chat upstreams imitate
the text-only assistant message as a complete turn and return
finish_reason=stop before the expected tool call, so long agent tasks end
after a progress update.
Merge pending tool calls into the directly adjacent assistant message when
it carries no tool_calls yet, attach pending reasoning per segment so
parallel calls do not duplicate it, and keep the new-message path for every
other boundary shape (user/tool boundaries, prior tool-call batches, media
flushes). The reasoning placeholder backfill still applies to coalesced
calls without reasoning.
Fixes #6529
Supersedes #6530
Co-authored-by: BigStrongSun <BigStrongSun@users.noreply.github.com>
fix(proxy): skip empty reasoning_content placeholders in Anthropic SSE streaming (#7227)
Some OpenAI-compatible upstreams (GLM, Qwen, DeepSeek-V4-Pro via ModelScope/DashScope,
Bailian, JD Cloud, ...) keep a `reasoning_content: ""` placeholder in every content
chunk instead of omitting the field. The reasoning branch of the OpenAI Chat -> Anthropic
SSE converter only checked for presence, and it shares `current_non_tool_block_type`
with the content branch, so every content chunk closed the open text block, opened an
empty thinking block, then reopened a text block. N content chunks became 2N content
blocks, half of them empty thinking blocks, which Claude Code rendered as one line per
token with dozens of empty Thought blocks and bloated the session JSONL.
Filter empty reasoning before entering the branch, matching the `!content.is_empty()`
guard the content branch already had and the non-streaming `openai_to_anthropic`
converter. Two regression tests pin block index, type and order: the placeholder shape
must yield a single text block and no thinking_delta at all, and real reasoning must
still yield one thinking block followed by one text block.
Fixes #5028
Fixes #4404
fix(codex): detect growing rollouts with a persisted byte cursor (#7219)
Codex keeps the rollout append handle open for the whole session, so on Windows NTFS the file mtime does not advance while the file grows and the pure-mtime gate skipped growing rollouts forever. Persist the observed byte length in session_log_sync.last_byte_offset for Codex rows and skip a file only when both mtime and size are unchanged. The parser now splits records on newlines manually so an incomplete trailing record is retried on the next pass without advancing the line cursor.
Fixes #6060
fix(codex): proxy image edits and Images API follow-ups (#7177)
* fix(codex): proxy image edits and Images API follow-ups
Follow-ups to #7036 for Codex's built-in ImageGen over the local proxy:
- Register `/images/edits` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them through the shared Codex standalone passthrough.
ImageGen posts to `images/edits` whenever a call references existing
images, and the router returned an empty 404 for it.
- Add `CodexStandaloneEndpoint::ImagesEdits`. Both Images endpoints share
one full-URL suffix table, so a complete Responses / Compact / Chat
Completions / Images URL derives the sibling endpoint (and vice versa)
while opaque full URLs keep failing closed.
- Apply the standalone sibling rewrite when the base URL is pasted as a
complete endpoint with the full-URL switch off, mirroring
`codex_chat_base_is_full_endpoint`; standalone requests no longer become
`.../chat/completions/images/generations`.
- Parse Images API streaming usage: `image_generation.completed` carries
`usage` on the event top level, so scan for `usage.input_tokens` before
falling back to the Chat Completions stream parser.
Related #7036
* fix(codex): match standalone URL suffixes case-insensitively
Keep endpoint detection and sibling rewriting consistent while preserving the original URL prefix and query. Cover mixed-case Images and Alpha Search endpoints through URL and proxy regression cases.
---------
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): preserve per-app settings on shutdown and port allocation (#7210)
On normal exit, stop_with_restore_keep_state() called the legacy global
proxy-config writer to clear the long-retired live_takeover_active flag.
That writer reads the Claude row and runs an UPDATE without a WHERE clause,
so every shutdown copied Claude's max_retries and the three timeout fields
onto the Codex, Gemini and Grok Build rows — and the UPDATE never carried
live_takeover_active in the first place, so the step had no effect beyond
the clobber. The ephemeral listen-port path did the same on every proxy
start.
Drop the dead write-back (live-config restore, backup cleanup and health
reset are unchanged) and persist the resolved ephemeral port through the
global-config interface, which only touches the shared listen/logging
columns. Two regression tests seed distinct per-app settings for all four
apps and assert they survive both paths.
Fixes #7204
fix(provider): preserve child metadata during universal sync (#7212)
Syncing a universal provider regenerates its Claude/Codex/Gemini child
providers. The child's `settings_config` was merged with the existing row,
but `meta`, `created_at` and `sort_index` were taken from the generated
object, and the universal provider never carries any of them from the UI.
The UPDATE therefore wiped the child's usage script, common-config opt-out,
endpoint auto-select and other per-app settings to `{}` and reset the sort
index to NULL, which pushed the card to the bottom of the list and looked
like the provider had been deleted and re-created.
Keep the existing child's `meta`, `created_at` and `sort_index` when the
row already exists. Name, base URL, key, models, website and notes are
still driven by the universal provider, and first-time creation still
inherits the parent's metadata.
Fixes #7134
feat(presets): rebrand DashScope as 千问AI平台 and refresh Qwen models (#7183)
Rename the domestic Bailian presets to 千问AI平台 with a dedicated icon, refresh the Qwen family to the 3.8 generation across Claude Code, Claude Desktop, Codex, Hermes, OpenClaw, OpenCode and Pi, add the domestic Token Plan presets, restore the international QwenCloud Coding Plan presets, and align OpenClaw/OpenCode/Pi model metadata with the official client docs.
fix(codex): honor proxy URL when model_provider is omitted (#7263)
When a Codex provider card's TOML has no `model_provider`, Codex falls back to
it…
设置页可隐藏主页顶栏批量检测入口;关闭时停止进行中的探测。旧数据缺字段时默认显示。README 补上该开关,以及从其他应用导入供应商。
chore(native): archive sync-upstream-latest
chore(native): record sync-upstream-latest verification state
Commit the Runtime-managed comet-state.yaml and verification.md so the
working tree is clean after the v3.20.4 upstream sync.
chore: sync fork with upstream v3.20.4
Rebase fork main onto upstream/main (v3.20.4, 8e478b2b, 40 new commits)
while keeping all fork modifications: connectivity test, model quick
switch, sidebar panel visibility, preset filtering, tray left-click
toggle, dual website links, cross-app provider import, disabled in-app
updater, fork CI.
- Bump fork version to 3.20.4-1 (package.json, tauri.conf.json,
Cargo.toml, Cargo.lock).
- Keep vitest testTimeout at 30s so full provider-form jsdom tests stop
flaking past the 5s default.
- Resolved conflicts: version files kept on the fork side, dropped the
duplicate GitHub button in AboutSection (upstream now ships its own
star prompt), and restored .comet/config.yaml with native enabled.
- Updated HOW_TO_REBASE_UPSTREAM.md whitelist with the cross-app import
files, searchable model pickers, and the dev config.
feat(provider-import): 从其他已启用应用导入供应商配置
新建/编辑供应商表单里,可从其他已启用应用的已有供应商一次性带入
名称、备注、官网链接、API Key、请求地址、默认(兜底)模型名与上游
API 格式,其余配置由用户自行填写。10 个应用的配置形状各不相同,
映射层按目标应用分别落键;编辑模式只覆盖这三个键位,配置对象其余
部分(providerKey、模型表、Codex modelCatalog、Gemini 顶层 config、
env/options/auth 其他键含 Codex 登录态键)原样保留。
- src/utils/providerImport.ts:读侧按应用取可迁移字段;写侧拆成
buildImportedSettingsConfig(新建,按目标应用「自定义」模板重建)
与 patchImportedSettingsConfig(编辑,就地覆盖)两条路径
- src/components/providers/forms/ProviderImportEntry.tsx:来源应用
按钮行 + 供应商选择弹窗,预告「本次将写入」的字段,不渲染 API Key
- useProviderImportSources / useProviderImportApply:来源枚举与表单
写入;ProviderPresetSelector 增加 extraActions 插槽
- src/utils/piProviderConfig.ts:buildPiSettingsConfig 自
PiProviderForm 抽出,供映射层复用
- i18n 四语言新增 providerImport 文案
release 工作流改为直接以正式版发布(prerelease: false),不再先挂
预发布等人工提升;注意这同时移除了那道人工放行关卡,tag 一推发布
即对 /releases/latest、下载页和应用内更新器可见。sync-r2 不受影响,
其 released 触发器对直接以 stable 发布同样触发。
chore(native): archive cross-app-provider-import
chore(native): archive sync-upstream-3203
chore: sync fork with upstream v3.20.3 and rebrand top-left link to GitHub
Rebase fork main onto upstream/main (v3.20.3, 06082e18, 33 new commits)
while keeping all fork modifications: connectivity test, model quick
switch, sidebar panel visibility, preset filtering, tray left-click
toggle, dual website links, disabled in-app updater, fork CI.
- Bump fork version to 3.20.3-1 (package.json, tauri.conf.json,
Cargo.toml, Cargo.lock).
- Merged upstream's v18->v19 schema migration with the fork's
website_url_2 column migration in the same migration arm.
- Added mcode entry to forkOfficialAllowlist (no official presets,
same treatment as hermes/openclaw/opencode/pi).
- Changed the top-left CC Switch brand link target from
https://ccswitch.io to https://github.com/farion1231/cc-switch.
- Updated HOW_TO_REBASE_UPSTREAM.md whitelist with the brand link
override.
style: apply prettier to files flagged by CI format check
修复 CI Frontend Checks 的 Check formatting 步骤失败
(App.tsx、ConnectivityTestDialog、ProviderCard、ProviderList、
provider schema 共 5 个文件)。
style: apply cargo fmt to connectivity-test era backend files
修复 CI 的 Check Rust formatting 步骤失败(connectivity_test、
backup、tests、provider 等文件格式不符合 rustfmt 规范)。
chore(native): archive sync-upstream
docs(fork): update rebase whitelist for connectivity-test era fork features
- 白名单补充连通性测试前后端、模型快捷切换、双官网链接测试、
CI 裁剪版、README/CHANGELOG、.comet/superpowers 产物等 fork 专属文件
- 新增共享文件 fork 专属语义表(托盘左键切换、禁用更新器、hidden 字段等)
- 修正 §3.1 rebase 方向说明:rebase 中 fork 侧是 --theirs 而非 --ours
(v3.20.2 同步实测确认,与 merge 语义相反)
chore: bump fork version to 3.20.2-1 after upstream v3.20.2 rebase
重放 fork 提交到 upstream/main(f3b18df1,v3.20.2)后统一版本号。
Cargo.lock 同步移除 updater 依赖树(fork 已禁用应用内更新器)。
chore(native): archive tray-left-click-toggle-window
chore(comet): 记录 tray-left-click-toggle-window 验收通过与归档状态
feat(tray): 托盘图标左键单击切换主窗口显示/隐藏
- tray.rs 新增 show_main_window/hide_main_window/toggle_main_window 助手:
显示语义与"打开主界面"一致,隐藏与"关闭到托盘"平台处理一致
(Windows set_skip_taskbar、macOS apply_tray_policy)
- lib.rs 托盘 show_menu_on_left_click 改为 false,左键 Click(Left, Up)
触发切换(过滤 Down/Up 双事件),右键仍弹菜单;Enter/Click 用量刷新保持
- show_main 菜单事件与 macOS Reopen 复用 show_main_window
docs(readme): 更新日志补充连通性测试、双官网链接与托盘左键切换
feat(model-quick-switch): 获取模型列表成功后自动展开选择器
SearchableModelPicker 增加可选 open/onOpenChange 受控展开(不传保持内部自管,
表单等既有用法不受影响);快捷切换弹窗拉取成功且列表非空时直接展开,关闭弹窗复位。
chore(native): archive fix-quick-switch-1m
chore(comet): 记录 fix-quick-switch-1m 验收通过与归档状态
fix(model-quick-switch): 补齐 Haiku 显示名同步,支持不选模型仅翻转 1M
- 显示名同步映射补上 ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME(沿用空/旧base/旧base[1M] 覆盖、自定义保留语义)
- 新增 setClaudeOneMInSettings:仅原地翻转 1M 标记,不改各角色模型 base 与显示名
- 弹窗打开时 1M 开关按供应商当前状态初始化,避免已开 1M 被静默剥离
- claude 未选模型且 1M 状态有变化时允许保存;无可应用变化时保存禁用
- 补单测与四语言 toast 文案
feat(provider-website-links): 供应商支持第二个官网链接,主页横排双链接
- Rust Provider/UniversalProvider 新增 website_url_2(serde websiteUrl2,skip none)
- SQLite providers 表新增 website_url_2 列(schema v18→v19 幂等迁移 + create_tables 补列 + 旧 config.json 迁移)
- DAO SELECT/INSERT/UPDATE/replace 全链路带出新列
- 表单共享 BasicFormFields + 统一供应商表单第二输入框;zod 同第一链接规则校验与回填
- ProviderCard 主页双链接横排、两空格分隔、逐链接 openExternal 打开默认浏览器
- 搜索匹配、复制供应商、统一供应商生成 claude/codex/gemini 随行透传
- gemini_auth packycode 认证检测纳入 website_url_2;ClaudeDesktop/GrokBuild 预设切换清空第二链接;卡片同值链接去重
- 4 语言 i18n + 前端/前端单测覆盖
Comet change dual-website-links-homepage(19/19 验收通过,归档)
feat(connectivity-test): 拉取模型列表改为「搜索添加」选择器交互
- 新增共享组件 SearchableModelMultiPicker:搜索过滤 + 多选 + 已在列表标记 + 已选计数
- ConnectivityTestDialog 获取模型列表后不再全量追加合并去重,改为会话内缓存完整列表并自动弹出一次选择器;用户搜索挑选后添加进表格并自动勾选
- 重新拉取只刷新缓存,已添加进表格的行保留;拉取结果仍仅会话内有效,不写回供应商配置
- 四语言 i18n 新增 searchAdd / addToTest / alreadyInList / selectedCount
- 同步更新 ConnectivityTestDialog 测试并新增 SearchableModelMultiPicker 测试
feat(connectivity-test): 探测目标按供应商 API 格式对齐真实转发链路
- build_claude 按 get_claude_api_format 分派端点与请求体:openai_chat → /v1/chat/completions(复用 transform_claude_request_for_api_format,流式注入 stream_options.include_usage)、openai_responses → /v1/responses、gemini_native → /v1beta/models/{model}:{method}(resolve_gemini_native_url)、anthropic 保持 /v1/messages 并补 anthropic-version/beta 头
- build_codex 按 should_convert_codex_responses_to_chat/anthropic 分派:chat → /chat/completions(responses_to_chat_completions_with_reasoning)、anthropic → /v1/messages(responses_request_to_anthropic,含 max_output_tokens 注入与 [1m] 剥离 + context-1m beta 头)
- 鉴权改用 ClaudeAdapter/CodexAdapter 的 extract_auth + get_auth_headers(与真实出站请求同源:AUTH_TOKEN→Bearer、ANTHROPIC_API_KEY→x-api-key、gemini→x-goog-api-key、GeminiCli OAuth→Bearer、OpenRouter→Bearer、Codex 按 apiKeyField)
- 转换前剥离 [1m] 标记;meta.isFullUrl 或 base 已是完整端点时直接使用 base_url;自定义 body 浅合并移至转换后的最终协议体
- 新增 18 个目标构造断言单测;Comet 归档产物同步提交
chore: archive connectivity-test-optimizations
feat(connectivity-test): 单一模型表格 + 实时拉取模型 + 勾选即测试范围 + 新默认提示词
- 弹窗合并为单一模型表格:打开即全部模型行(初始待测试)、行勾选 + 表头全选(含半选态)、5 格统计卡,移除上方独立选择区
- 获取模型列表:凭据提取抽为共享模块 src/utils/providerCredentials.ts(ModelQuickSwitchDialog 同步改引用),复用 fetch_models_for_config 拉取 /v1/models,按 ID 追加合并去重、仅会话内有效
- 默认提示词改为「你好,你可以帮我做什么事情」:前后端常量同步,merge 差异判断基准跟随
- 勾选即测试范围:后端 connectivity_test_provider_models 新增 modelIds 参数(None/空白回退静态清单),前端逐模型独立 invoke 并行 + 行级流式更新
- 一轮测完自动勾选失败项,全部成功保持原勾选
fix(connectivity-test): UI 四项修复
- 弹窗抬至 alert 层(z-[60]):打开连通性测试时 header 切换栏正确变灰,
详情子弹窗同步抬层(同 z + 更晚挂载 portal 在上)
- 批量检测入口移至 header 图标组(状态提升到 App,ProviderList 经
props 读取探针结果),不再另起一行
- 批量检测可停止:runId 代次守卫(Tauri invoke 无法真正取消),再次
点击即清除 waiting/running 徽标、丢弃在途迟到写入、不再拉起排队项
- 弹窗打开自动勾选模型:已保存默认测试模型 → 供应商当前模型
(claude env.ANTHROPIC_MODEL / codex config.toml model,新增
currentProviderModelId 与后端同口径)→ 清单首个
前端全量 141 文件 / 1106 测试全绿;typecheck clean
chore: archive per-model-connectivity-test
fix(connectivity-test): shouldShowTestEntry 增判 providerType + 收尾清理
chore(connectivity-test): 勾选 Task 9 / OpenSpec 5.1-5.2 + verify 报告
chore(connectivity-test): 勾选 Task 8 / OpenSpec 4.1-4.3
refactor(connectivity-test): 移除旧 stream_check 可达性探测全链路(保留表结构)
- 删除 6 个文件:commands/stream_check.rs、services/stream_check.rs、
database/dao/stream_check.rs、hooks/useStreamCheck.ts、lib/api/connectivity-check.ts、
components/usage/ConnectivityCheckConfigPanel.tsx
- 移除 mod.rs 声明、lib.rs generate_handler! 注册、cleanup_old_stream_check_logs 调用
- 从 SYNC_SKIP_TABLES / SYNC_PRESERVE_TABLES 移除 stream_check_logs(表结构保留在 schema.rs)
- 更新 backup.rs 测试:移除 stream_check_logs fixture 与断言
- 更新 SettingsPage:移除 ConnectivityCheckConfigPanel AccordionItem 与 FlaskConical 导入
- i18n:删除 streamCheck.* 命名空间(zh/en/ja/zh-TW 四语言)
- 更新注释:移除 stream_check 旧引用(forwarder.rs、provider.rs、userAgentPresets.ts、
connectivity_test.rs)
- 修复 4 个 pre-existing clippy lint(connectivity_test: matches! 宏、unused_unit、
derive Default)
- 移除 ProviderList.test.tsx 中已失效的 useStreamCheck mock
chore(connectivity-test): 勾选 Task 7 / OpenSpec 2.4 3.1-3.3
feat(connectivity-test): 供应商列表集成(弹窗入口 + 批量探针徽标)
chore(connectivity-test): 勾选 Task 6 / OpenSpec 2.2-2.3
fix(connectivity-test): 模型列表解析与后端对齐(每条目首个匹配,提取共享 helper)
feat(connectivity-test): 连通性测试弹窗/详情/徽标组件与四语言文案
chore(connectivity-test): 勾选 Task 5 / OpenSpec 2.1
test(connectivity-test): 锁定 toBackendParams 字段映射(customHeaders/customBody 等)
feat(connectivity-test): 前端命令绑定与测试/探针状态 hooks
chore(connectivity-test): 勾选 Task 4 / OpenSpec 1.4
feat(connectivity-test): 测试参数持久化纯函数(缺省即默认值)
chore(connectivity-test): 勾选 Task 3 / OpenSpec 1.3
feat(connectivity-test): Tauri 命令与注册,官方/动态端点过滤,sanitize 隔离私有字段
chore(connectivity-test): 勾选 Task 2 / OpenSpec 1.2
feat(connectivity-test): 后端直连执行器(reqwest+SSE,总/空闲超时,度量与错误识别)
feat(connectivity-test): 后端直连执行器(reqwest+SSE,总/空闲超时,度量与错误识别)
chore(connectivity-test): 勾选 Task 1 / OpenSpec 1.1
fix(connectivity-test): claude 鉴权对齐 adapter 顶层 apiKey/api_key 兜底
feat(connectivity-test): 新增后端连通性测试服务类型/模型解析/协议构造
chore: archive model-quick-switch-picker-scroll
chore: model-quick-switch-picker-scroll 验证报告与状态元数据
fix: 模型快捷切换弹窗内模型列表滚轮无法滚动
模态 Dialog 的 RemoveScroll 会对 shards 外的 portaled PopoverContent
wheel 事件无条件 preventDefault。在 SearchableModelPicker 的
PopoverContent 捕获阶段阻断 wheel 传播,恢复原生滚动。
chore: bump fork version to 3.20.1-1 after upstream v3.20.1 rebase
fix(form): only auto-expand Claude advanced options on divergent model mapping
Uniform mapping produced by the quick-set button (all roles equal to the
fallback model) no longer counts as an advanced value, so the advanced
section stays collapsed on edit. A 1M marker is stripped before comparing
so marked roles are not misjudged as divergent. Truly customized mappings
still auto-expand the section.
feat: align fallback-model quick-set buttons with model input right edge
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
docs: update README with fork build notes and quick-move provider changes
chore: archive comet-native claude-model-quick-access
feat: surface Claude fallback model as quick-access section below endpoint
Move the default fallback model (ANTHROPIC_MODEL) block out of the
collapsed Advanced Options into a quick-access section directly below
the request endpoint: input + 1M checkbox + quick-set + fetch-models
buttons are all reachable without expanding.
- header toggle-all "declare 1M" now includes the fallback model
(Haiku still excluded); partial selection shows indeterminate state
- drop the duplicated quick-set/fetch buttons and the old fallback
block inside Advanced Options
- exclude claudeModel from hasAnyAdvancedValue so a fallback-only
value no longer auto-expands Advanced Options
- quick-set priority, env write semantics, and OAuth preset branches
unchanged; add 4 component tests covering the new layout
chore: archive comet-native provider-right-click-top-bottom
Enable native workflow in .comet/config.yaml and move the Comet managed
state block in .gitignore so the exclusion rules stay grouped with the
other fork tool artifacts.
feat: add provider right-click move-to-top/bottom and insert new providers at second position
- ProviderList: context menu with quick move-to-top/move-to-bottom, reusing
update_providers_sort_order and refreshing tray menu like drag sort
- useAddProviderMutation: default insert at index 1 with explicit sortIndex
materialization for existing items (avoids defined-first sort trap)
- i18n: quickMove* strings for zh/en/zh-TW/ja
ci: stop flaky WSL2 linker job from blocking main
The backend-windows-wsl2 job intermittently fails with LNK1327 /
mt.exe c1010070 when the Windows runner's mt.exe cannot parse a
manifest temp file on the WSL2 UNC TEMP path. The same flaky failure
occurs upstream and is unrelated to fork changes. Mark the job
continue-on-error so main stops going red on it; the result stays
visible in the Actions UI.
style: apply prettier to files touched by recent comet archives
Frontend CI format:check failed on 6 files modified by the app-name
and model-quick-switch archive commits plus the updater-disable commit.
Run prettier --write so format:check passes again.
chore: archive ccs-fork-app-name-cc-switch
chore: archive ccs-model-quick-switch-ui-polish
docs: rewrite README to document fork differences vs upstream
README 从上游完整功能介绍改为仅记录 fork 与上游的差别,完整功能
文档指回上游。涵盖 8 项 fork 魔改:构建标识/开发预览、预设精简、
Claude 表单调整、模型搜索移植、主页模型快速切换、侧边栏面板显隐、
关闭应用内更新、打包范围,并说明版本号与 rebase 同步流程。
feat: disable in-app updater, point users to GitHub releases
移除 tauri-plugin-updater 依赖与自动更新链路,改为统一引导用户到
GitHub Releases 手动下载新版。
- 前端:updater.ts 恒返回 up-to-date;UpdateContext 取消启动自检;
AboutSection「检查更新」直接打开 fork releases 页;DatabaseUpgrade
恢复界面去掉下载进度条,改为打开发布页。
- 后端:移除 updater 插件注册与 UpdaterExt 调用;
install_update_and_restart 降级为空壳,check_app_update_available
返回当前版本号;check_for_updates 指向 fork releases;capabilities
去掉 updater:default 权限。
- 打包:tauri.conf.json 删 plugins.updater、createUpdaterArtifacts=false;
Cargo.toml / package.json 删 updater 依赖,Cargo.lock / pnpm-lock
同步重生成。
- CI:release.yml 移除签名收集与 assemble-latest-json job,删除
.tar.gz/.sig 处理,release body 去掉 updater 说明。
版本号统一为 3.20.0-1(合法 semver 且 MSI 第 4 位为 1)。
Cargo.lock / pnpm-lock.yaml 已随依赖移除重生成,cargo check 通过。
ci: build only Windows x64 and macOS arm64, unsigned
feat: gate dev panel behind dev-only env flag
chore(comet): archive ccs-sidebar-panels-visibility change
fix: apply skills visibility guard to hermes branch toolbar button
feat: include prompts in sidebar panel visibility settings
fix: persist visibleSidebarPanels setting in backend AppSettings
style: use compact pill toggles for sidebar panel visibility
feat: add sidebar panel visibility switches to settings
feat: add sidebar panel visibility settings and wire into toolbar buttons
chore(comet): archive ccs-home-model-display change
fix(fork): read grokbuild api key from config TOML in quick switch dialog
feat(fork): align model badge with upstream extractModelBadge
feat(fork): show current model badge and quick-switch entry on provider cards
feat(fork): add model quick switch dialog
feat(fork): add provider model read/write utils with tests
chore(comet): archive ccs-model-search-port change
feat(i18n): add model search picker keys for all locales
feat(form): port SearchableModelPicker from upstream for searchable model selection
chore(comet): archive ccs-claude-form-layout change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-claude-form-layout/
- Sync claude-form-layout spec to main specs
- Annotate design doc as archived
- Add verification report
fix(i18n): update advanced options hint, add ja/zh-TW toggle-all, drop dead apiFormatHint key
fix(form): correct 1M toggle-all alignment direction (ml-1)
fix(form): align 1M toggle-all checkbox with row checkboxes
refactor(form): move api format select into EndpointField rightSlot
fix(form): widen api format select to fit option text
feat(form): add 1M toggle-all checkbox in model mapping header
feat(form): move api format select next to endpoint input
chore(comet): archive ccs-provider-presets-trim change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-provider-presets-trim/
- Sync provider-presets-visibility spec to main specs
- Annotate design doc as archived
- Add verification report
docs(fork): add presets-trim files to rebase whitelist
test(fork): mock IS_FORK_BUILD false in unit tests for upstream-form semantics
feat(fork): wire official preset filter into provider forms
feat(fork): add hidden field to preset interfaces
feat(fork): add official preset allowlist and filter util
chore(comet): archive ccs-fork-scaffolding change
- Move change artifacts to docs/openspec/changes/archive/2026-08-18-ccs-fork-scaffolding/
- Sync fork-build-identity spec to main specs
- Annotate design doc as archived
- Add verification report
feat(fork): add dev config with isolated identifier and config dir
docs(fork): complete rebase whitelist with all fork-touched files
fix(fork): guard setTitle with isTauri in test/non-tauri env
fix(fork): define __CCS_FORK_BUILD__ in vitest config
feat(fork): add DevPanel and settings footer entry
feat(fork): set macOS window title to CC Switch (Fork) on startup
feat(fork): inject __CCS_FORK_BUILD__ compile-time constant
feat(fork): bump version to 3.19.2-fork.1 and rename product to CC Switch (Fork)
docs(fork): add upstream rebase workflow and ignore tool artifacts
fix(proxy): accept the whole grok-4.x (x>=5) family in the reasoning-effort whitelist (#7369)
Replace the verbatim grok-4.5 / grok-4.6 entries in supports_reasoning_effort with a rule that parses the grok-4.x minor version and accepts x >= 5, mirroring the existing GPT-5+ rule. This covers grok-4.7 (released 2026-09-21), whose reasoning effort was previously dropped on the Claude -> Chat, Claude -> Responses and Codex Responses -> Chat conversion paths, and lets future releases pass without another whitelist edit. The grok-build-* family is retained for saved providers.
Co-authored-by: allenxu09 <171831965+allenxu09@users.noreply.github.com>
docs(release): add v3.20.4 release notes
chore(release): v3.20.4
feat(pricing): add Grok 4.7 and GLM-5.3-FlashX
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.
fix(i18n): remove outdated OpenCode Go referral offer
fix: add SudoCode.chat alternate API endpoint
feat(settings): add a GitHub star prompt to the About card
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
feat(presets): add Soshow aggregator presets
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.
fix(ci): stabilize frontend tests and verify renderer build
fix(mcode): refresh provider state after removal (#7578)
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
fix(lib): reset skip_taskbar before window.show() across entry points (#6348)
5处 window.show() 调用中 4处没有在 show() 前重置 Windows 的 skip_taskbar,导致以下场景出现窗口可见但不在任务栏、点最小化后窗口消失的 bug:
- single_instance handler (lib.rs:360-361):当原进程因 silent_startup / 关闭到托盘等处于 skip_taskbar(true) 状态时,从快捷方式/开始菜单唤起已运行实例触发。
- handle_deeplink_url 的 focus_main_window 分支 (lib.rs:256):通过 ccswitch:// URL 唤起时。
- DB 恢复流程 (lib.rs:556):数据库版本不匹配时显示恢复界面。
- RunEvent::Opened 处理器 (lib.rs:1803):系统接收到 deep-link URL 时。
修复后的行为与 tray.rs::handle_tray_menu_event::show_main 分支一致(那里已有相同的 set_skip_taskbar(false) 调用)。
经用户测试验证:autostart + silent_startup + 从开始菜单/快捷方式启动的场景下,最小化按钮恢复正常工作。
fix(proxy): omit missing tool descriptions in the Codex responses-to-chat converter (#7378)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): preserve max effort for GPT-5.6 and GPT-6 Astra (#7531)
fix(misc): ignore shell startup output when probing tool versions in WSL (#7348)
try_get_version_wsl runs "${SHELL:-sh}" -lic '<tool> --version' through
wsl.exe and takes the first x.y.z anywhere in stdout. A login+interactive
shell runs the distro's startup files first, so their output shares that
stdout. On Ubuntu, /etc/profile.d/update-motd.sh prints the MOTD once a day,
starting with "Welcome to Ubuntu 24.04.4 LTS", and the card then showed
24.04.4 as the Claude Code version. The once-a-day stamp makes it appear
after the first probe of the day and vanish on refresh.
The payload now prints a sentinel before running the tool, and parsing looks
only at what follows its last occurrence. The last occurrence handles the
-lic || -lc || -c fallback chain printing it several times; matching it as a
substring handles OSC sequences glued in front of it. If the sentinel is
absent (the shell failed before the payload ran), the output is used as
before. The stderr fallback for tools that print their version there is kept.
Fixes #7347
Co-authored-by: lyfuci <lyfuci11@gmail.com>
fix(claude): hide session URL attribution (#7053)
* fix(claude): hide session URL attribution
* docs(claude): document session URL attribution
fix(ui): reset provider scroll when switching apps (#5211)
fix(pi): fix provider logo (#6826)
fix(i18n): use the Pi form's own term for the zh-TW duplicate-key message (#7187)
Follow-up to the nit on #6768. In zh-TW the Pi form labels the field
供應商識別碼 and says 供應商識別碼為必填 when it is empty, but the
duplicate message I added said 供應商標識 — the term Hermes, OpenCode and
OpenClaw use for their own field. One form, two names for the same thing.
The other three locales already keep the label and both messages on one
term, so this makes zh-TW match the pattern rather than introduce one:
en Provider key / Provider key is required / This provider key already exists
ja プロバイダーキー / プロバイダーキーを入力してください / このプロバイダーキーは既に存在します
zh 供应商标识 / 供应商标识不能为空 / 该供应商标识已存在
zh-TW 供應商識別碼 / 供應商識別碼為必填 / 該供應商標識已存在 <- the odd one out
hermes.form.providerKeyDuplicate carries the same sentence and is left
alone: its own label is 供應商標識, so it is already consistent.
Verified: managementListLocales 7/7. Full unit suite 1085/1087, the same
two App.test.tsx integration cases failing before and after.
fix(proxy): strip stop param for GitHub Copilot to unblock auto mode classifier (#5404)
Claude Code's auto mode classifier sends `stop_sequences` on every
safety check. On the GitHub Copilot path the Anthropic -> OpenAI Chat
conversion mapped it to `stop`, and Copilot's Chat Completions route
for Claude models answers that request with HTTP 400, so every
classifier call failed while normal chat kept working.
- Remove the converted `stop` field only when the provider is GitHub
Copilot; other OpenAI-compatible upstreams keep it.
- Copilot detection in the transform now also covers providers that
are recognised only through `env.ANTHROPIC_BASE_URL`.
- Regression tests for the Copilot (meta and env URL) and generic
provider cases.
Fixes #5175
fix(codex): keep stored auth when the live snapshot has no credential (#7434)
Editing the current Codex provider seeds the form from the live ~/.codex
snapshot, and switching away backfills that snapshot into the DB row. When a
provider table declares its own credential source (an Authorization header,
env_key, auth/aws), no bearer token is injected into config.toml and the
shared auth.json is deleted on third-party switches, so the live snapshot is
permanently `{ auth: {}, config }` while auth.OPENAI_API_KEY in the DB is the
only copy of the key. Both paths treated that empty live auth as
authoritative and silently erased the stored key, which in turn made the
usage-script modal misread the provider as an official subscription and
reset the saved script.
- Treat a credential-less live auth (missing auth.json or the bare
auth_mode logout marker) as an absent field in both the edit dialog and
the switch-away backfill, keeping the stored auth template. Live still
wins whenever it carries material, and official providers keep the live
login as their source of truth.
- Gate the official exemption on is_codex_official_provider /
resolveCodexOfficialIdentity rather than the category alone: a legacy
fixed official card without a persisted category is still written as
official, so the fallback must not resurrect a logged-out login on
switch-back.
- Cover both layers with regression tests, including the category-less
official shapes.
Fixes #7433
Supersedes #7492
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): lift additional_tools input carriers on the Codex chat conversion (#7454)
Codex talks to third-party providers over its Responses Lite transport,
which ships the request's extra tools in an `additional_tools` input
carrier: `{"type":"additional_tools","role":"developer","tools":[...]}`,
a role but no `content`. The Responses->Chat conversion let that carrier
fall through the generic "item with a role is a message" arm and emitted
`{"role":"system","content":null}` at messages[1], which strict Chat
gateways reject with `messages[1]: missing field "content"` (or
`data did not match any variant of ...SystemMessageContent`). The tools
carried inside it were also dropped on the Chat and Anthropic conversions.
- Skip `additional_tools` items in the Chat message walk; a carrier
declares tools, it is not a message.
- Collect carrier tools through the existing CodexToolContext registry
(dedup, namespace flattening, custom-tool conversion), which also feeds
the Anthropic conversion and the response-side name round-trip.
- Requests without a carrier convert byte-for-byte as before.
Known boundary: `custom` children inside a namespace tool
(`functions.exec`) are still not lifted; that needs its own response-side
name round-trip and is left for a follow-up.
Diagnosed independently in #6158 by BigStrongSun (fix in #6159) and in
fixture.
Fixes #7451
Fixes #6158
Refs #6033
Refs #6776
Supersedes #6159
fix(proxy): normalize Codex detail:original images for Chat gateways (#7476)
Codex marks tool-returned images with detail:"original" whenever the model
catalog advertises supports_image_detail_original, a Responses-only value.
The Responses->Chat conversion copied it verbatim into image_url.detail, and
strict OpenAI-compatible Chat gateways (Ark/Doubao, SenseNova, GLM, vLLM, ...)
reject the whole request with 400. Because Codex replays the full rollout on
every turn, one poisoned view_image result made the session fail permanently,
including plain-text turns.
Two layers:
- tool_media::merge_top_level_detail downgrades original -> auto, so already
poisoned rollouts and Codex versions before 0.155.0 (which do not normalize
replayed history themselves) recover immediately. auto/low/high are untouched.
- codex_catalog_model_entry always writes supports_image_detail_original:false
on the ProxyChat profile, so Codex stops emitting original at the source once
the catalog is regenerated. NativeResponses/Anthropic profiles and the DeepSeek
official catalog are unaffected.
Top-level user images were never affected: responses_content_to_chat_content
drops the part-level detail entirely.
Fixes #6495
Fixes #7458
Refs #6569
Refs #5805
fix(omo): detect unified config on the WSL side when the OpenCode dir is in WSL (#7550)
When the OpenCode config directory points into WSL
(\\wsl.localhost\<distro>\home\<user>\.config\opencode), OMO >= 4.19.3 keeps
its unified config in the WSL-side home (~/.omo/omo.jsonc). Detection only
probed the Windows home, never found it, and silently fell back to the legacy
per-plugin file that OMO no longer reads, so provider switches looked
successful but had no effect.
- Add derive_wsl_home_dir to derive the WSL-side home from a WSL UNC path
(home/<user> or root); non-WSL and non-home paths return None.
- Probe a list of home candidates for the unified config, WSL-side home
first and the OS home as a fallback, shared by the read, write and delete
paths.
- Detection only: when no unified config exists on either side the legacy
fallback is unchanged.
Fixes #7363
Supersedes #7367
docs(partner): add a FluxA purchase link and tighten its sponsor blurb
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.
fix(claude-desktop): expose the 1M context variant for DeepSeek V4 routes
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.
feat(presets): add FluxA Token Plan partner presets
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.
fix(presets): correct AICodeWith Codex-family endpoint to /v1
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.
fix(ui): render monochrome preset icons in the foreground color
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.
docs(user-manual): point Chat-only Codex examples at plan endpoints
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.
fix(pricing): restore DeepSeek V4 Pro price and seed Qwen3.8 and Hy4 models
- 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).
fix(presets): move MiniMax CN and BaiLing to their current official endpoints
- 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.
feat(codex): refresh CN presets from the Responses API audit
- 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.
test(claude-desktop): run Linux path tests on unix hosts and cover Linux in provider sync tests
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.
fix(ui): stop expanding stale cached usage tiers on ineligible provider cards
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.
fix(skills): 支持 skillId 与目录名不一致的技能安装和更新 (#6381)
* fix(skills): resolve mismatched skillId directories
* fix(skills): preserve source path during updates
* fix(skills): refresh source URL after repository relocation
* test(skills): isolate relocation fixture from legacy home
feat(opencode): 支持从获取的模型列表中搜索并批量添加所选模型 (#7515)
* feat: implement fetched models selection and UI components
* fix(opencode): prevent model search from submitting provider form
Add Kimi Global preset variants (kimi.ai platform + api endpoints) (#7526)
Add a "Kimi Global" twin immediately after the domestic "Kimi" API preset and a "Kimi For Coding Global" twin after the domestic "Kimi For Coding" preset, in every preset file that ships them (claude, claude-desktop, codex, hermes, openclaw, opencode, pi).
"Kimi Global" points to the international open platform https://platform.kimi.ai?aff=cc-switch (apiKeyUrl mirrored as https://platform.kimi.ai/console/api-keys?aff=cc-switch) and swaps the endpoint host api.moonshot.cn for api.moonshot.ai with each file's path unchanged (/v1 or /anthropic).
"Kimi For Coding Global" points to the international site https://www.kimi.ai/code/?aff=cc-switch and swaps the endpoint host api.kimi.com for api.kimi.ai with the path unchanged (e.g. https://api.kimi.ai/coding/v1).
Domestic presets are unchanged; category, icons, apiFormat, model catalogs, endpointCandidates (host swapped), partnerPromotionKey and settings otherwise mirror the domestic blocks exactly.
primePartner stays only on the domestic presets so the new variants do not alter prime-partner ordering.
The Pi twins get distinct providerKey values ("cc-switch-kimi-global", "cc-switch-kimi-for-coding-global") because providerKey is the preset-level primary key and must not collide with the domestic presets.
Rebalance sponsor CTAs around Kimi Code plan with dual-region links (#7522)
Kimi Code plan purchases have resumed, so the README sponsor sections go back to leading with the coding plan instead of the API-only flow written during the purchase pause.
All four locales (EN/ZH/JA/DE) now present Kimi Code plan (中文站 | Global) as the primary CTA and the Kimi Open Platform API (中文站 | Global, with the first-top-up bonus) as the secondary one, mirroring the domestic|global split already used for API links.
The aff and track_id values are unchanged.
fix(prompts): refresh active prompts after external file edits (#7194)
Reload the enabled prompt from its live file when the Prompts list loads, and refresh the open panel when the window regains focus, so external edits to CLAUDE.md / AGENTS.md show up without a restart.
- Keep saved templates available when the live file cannot be read.
- Skip the backfill for empty or whitespace-only files, matching enable_prompt.
- Skip the backfill while a sync/restore holds the sync lock, so a list load cannot write a stale live file into a freshly restored database.
fix(skills): raise archive entry limit so large skill repos install (#7489)
Installing hugohe3/ppt-master from skills.sh failed with
ARCHIVE_TOO_MANY_ENTRIES: the GitHub archive we download is the whole
repository (13,248 entries), and the 10,000-entry cap added in #5811
treated it as a zip bomb.
- Raise MAX_ARCHIVE_ENTRIES to 30,000 for both the remote archive and
local ZIP paths, and align WebDAV's MAX_EXTRACT_ENTRIES so a skills.zip
containing such a skill still restores.
- Charge every file entry at least one 4 KiB block against the existing
512 MiB budget, mirroring directory accounting, so empty-file archives
are bounded by the byte budget rather than by the entry count alone.
- Add tests for a 13k-entry repo archive and for the per-file minimum
charge.
Fixes: https://github.com/farion1231/cc-switch/issues/7475
feat: add MiniMax Code harness support (#7383)
Add MiniMax Code (mcode) as a managed application: provider and model management through the native custom_provider configuration, MCP servers, Skills, global instructions (AGENTS.md), read-only local session history with resume, and usage import from the native runtime database. Native files shared by the TUI and desktop are written under the native lock with rollback when the database commit fails, unrelated native configuration is preserved, and the data directory honors MINIMAX_DATA_DIR and MAVIS_DATA_DIR. Deleting sessions stays in the native application because they are coupled to runtime state.
fix(codex): recover stale account bindings during takeover (#7395)
After deleting a managed ChatGPT account and logging in again, a Codex provider can still reference the old local account ID, which made route switching, takeover, startup restoration and rebinding fail permanently.
Distinguish a missing outgoing account from an existing account with no matching live token. A missing account only releases its ownership marker after the persisted account store is verified; the target write policy stays in force. A stale target now reports an account-selection instruction instead of a bare not-found error, including when takeover is already enabled.
Fixes #7392
feat(claude-desktop): support 3P configuration on Linux (#7331)
Add Claude Desktop 3P configuration support on Linux. Resolve the config root from an absolute XDG_CONFIG_HOME, falling back to ~/.config; when CC Switch itself runs as a Flatpak, use the host ~/.config instead of the sandbox-private XDG_CONFIG_HOME. Update the en/zh/ja user manuals with Linux paths and the Flatpak boundary.
Fixes #4855
fix(proxy): omit missing tool descriptions instead of serializing null (#7319)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): keep reasoning effort for grok-4.6 and pass xhigh through verbatim (#7318)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(models): support Zhipu OpenAI Responses model lists (#7330)
Zhipu's Responses endpoint (open.bigmodel.cn/api/v1) serves its model list in the Codex remote catalog shape (models[].slug) instead of the OpenAI-compatible data[].id shape, so the Codex form's model fetch came back empty. The parser now falls back to models[].slug when data is absent.
fix(misc): fetch npm dist-tags from the dedicated endpoint with the probe timeout (#7346)
The tool version probe requested the full npm packument for each package (metadata for every historical version, 14-25 MB uncompressed for codex / opencode / openclaw) only to read its dist-tags, and it was the only latest-version probe without LATEST_PROBE_TIMEOUT, so it fell back to the proxy client's 600 s total timeout and the cards sat on "loading" for minutes on slow links while the large downloads starved the Hermes GitHub probe into "unknown".
Switch fetch_npm_dist_tags to registry.npmjs.org/-/package/{package}/dist-tags, whose body is the dist-tags object itself (tens of bytes to a few KB), escape scoped names as %2f, and apply the same 15 s probe timeout as the GitHub and PyPI probes. No data is lost: the only consumer reads latest and the tool's prerelease tags.
Fixes #7339
feat(usage): display output tokens per second in request log table and detail panel (#3369)
Show tokens/s next to the output token count in the request log table and the request detail panel.
The generation window prefers an explicit duration when present, otherwise latency minus first-token time for streaming requests, and falls back to full latency for non-streaming requests. Rows without a positive token count or window show nothing.
Closes #5936
fix(presets): update APIKEY.FUN URLs to apikey.fan
docs(release): add v3.20.3 release notes
chore(release): v3.20.3
fix(pricing): move retired DeepSeek V4 family to the V4.1 Flash tier
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.
fix(presets): declare third-party Token Plan DeepSeek V4 rows text-only
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.
feat(presets): rename DouBaoSeed preset to localized Volcengine Doubao
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.
fix(codex): skip unchanged incomplete rollout tails
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.
fix(claude): align quick model selection with form order
fix(tray): show bound ChatGPT account quota for managed Codex cards
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
feat(claude): add "Disable Artifact Tool" quick toggle to provider editor
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.
feat(codex): refresh aggregator model presets and catalogs
feat(codex): switch Kimi presets to native Responses direct-connect
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.
fix(usage): show Claude Fable weekly limit from the usage API limits array
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.
fix(codex): mirror vision-capable DeepSeek catalog and un-gate the legacy v4-flash alias (#7286)
Refresh the bundled DeepSeek official Codex catalog to the vendor's current models.json (slug deepseek-flash, image input modality, supports_image_detail_original) and remove deepseek-v4-flash from the confirmed text-only registry: the legacy id is still accepted by DeepSeek and routed to the vision-capable V4.1 Flash, so both the proxy media sanitizer and the generated Codex catalog must fail open for it. deepseek-v4-pro stays text-only per the vendor catalog.
Fixes #7283
fix(proxy): clamp sub-floor max_tokens to the Responses API minimum (#7287)
The Responses API rejects max_output_tokens below 16, but Anthropic clients legitimately send tiny probe budgets (Claude Desktop's model-availability probe uses max_tokens=1). The Anthropic→Responses conversion copied the value verbatim, so strict upstreams returned 400 and Claude Desktop reported the mapped model as unavailable under local routing.
Clamp 1..=15 up to 16 during conversion; 16+, 0 and non-integer values keep their pass-through semantics. Normal session requests convert byte-for-byte as before, and the Codex OAuth path still strips max_output_tokens afterwards.
Fixes #7103
fix(codex): coalesce adjacent commentary with pending tool calls (#7280)
The Codex Responses -> Chat Completions converter emitted two consecutive
assistant messages when one Responses model turn contained a commentary
message directly followed by function_call items. Chat upstreams imitate
the text-only assistant message as a complete turn and return
finish_reason=stop before the expected tool call, so long agent tasks end
after a progress update.
Merge pending tool calls into the directly adjacent assistant message when
it carries no tool_calls yet, attach pending reasoning per segment so
parallel calls do not duplicate it, and keep the new-message path for every
other boundary shape (user/tool boundaries, prior tool-call batches, media
flushes). The reasoning placeholder backfill still applies to coalesced
calls without reasoning.
Fixes #6529
Supersedes #6530
Co-authored-by: BigStrongSun <BigStrongSun@users.noreply.github.com>
fix(proxy): skip empty reasoning_content placeholders in Anthropic SSE streaming (#7227)
Some OpenAI-compatible upstreams (GLM, Qwen, DeepSeek-V4-Pro via ModelScope/DashScope,
Bailian, JD Cloud, ...) keep a `reasoning_content: ""` placeholder in every content
chunk instead of omitting the field. The reasoning branch of the OpenAI Chat -> Anthropic
SSE converter only checked for presence, and it shares `current_non_tool_block_type`
with the content branch, so every content chunk closed the open text block, opened an
empty thinking block, then reopened a text block. N content chunks became 2N content
blocks, half of them empty thinking blocks, which Claude Code rendered as one line per
token with dozens of empty Thought blocks and bloated the session JSONL.
Filter empty reasoning before entering the branch, matching the `!content.is_empty()`
guard the content branch already had and the non-streaming `openai_to_anthropic`
converter. Two regression tests pin block index, type and order: the placeholder shape
must yield a single text block and no thinking_delta at all, and real reasoning must
still yield one thinking block followed by one text block.
Fixes #5028
Fixes #4404
fix(codex): detect growing rollouts with a persisted byte cursor (#7219)
Codex keeps the rollout append handle open for the whole session, so on Windows NTFS the file mtime does not advance while the file grows and the pure-mtime gate skipped growing rollouts forever. Persist the observed byte length in session_log_sync.last_byte_offset for Codex rows and skip a file only when both mtime and size are unchanged. The parser now splits records on newlines manually so an incomplete trailing record is retried on the next pass without advancing the line cursor.
Fixes #6060
fix(codex): proxy image edits and Images API follow-ups (#7177)
* fix(codex): proxy image edits and Images API follow-ups
Follow-ups to #7036 for Codex's built-in ImageGen over the local proxy:
- Register `/images/edits` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them through the shared Codex standalone passthrough.
ImageGen posts to `images/edits` whenever a call references existing
images, and the router returned an empty 404 for it.
- Add `CodexStandaloneEndpoint::ImagesEdits`. Both Images endpoints share
one full-URL suffix table, so a complete Responses / Compact / Chat
Completions / Images URL derives the sibling endpoint (and vice versa)
while opaque full URLs keep failing closed.
- Apply the standalone sibling rewrite when the base URL is pasted as a
complete endpoint with the full-URL switch off, mirroring
`codex_chat_base_is_full_endpoint`; standalone requests no longer become
`.../chat/completions/images/generations`.
- Parse Images API streaming usage: `image_generation.completed` carries
`usage` on the event top level, so scan for `usage.input_tokens` before
falling back to the Chat Completions stream parser.
Related #7036
* fix(codex): match standalone URL suffixes case-insensitively
Keep endpoint detection and sibling rewriting consistent while preserving the original URL prefix and query. Cover mixed-case Images and Alpha Search endpoints through URL and proxy regression cases.
---------
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): preserve per-app settings on shutdown and port allocation (#7210)
On normal exit, stop_with_restore_keep_state() called the legacy global
proxy-config writer to clear the long-retired live_takeover_active flag.
That writer reads the Claude row and runs an UPDATE without a WHERE clause,
so every shutdown copied Claude's max_retries and the three timeout fields
onto the Codex, Gemini and Grok Build rows — and the UPDATE never carried
live_takeover_active in the first place, so the step had no effect beyond
the clobber. The ephemeral listen-port path did the same on every proxy
start.
Drop the dead write-back (live-config restore, backup cleanup and health
reset are unchanged) and persist the resolved ephemeral port through the
global-config interface, which only touches the shared listen/logging
columns. Two regression tests seed distinct per-app settings for all four
apps and assert they survive both paths.
Fixes #7204
fix(provider): preserve child metadata during universal sync (#7212)
Syncing a universal provider regenerates its Claude/Codex/Gemini c…
feat: gate dev panel behind dev-only env flag
chore(comet): archive ccs-sidebar-panels-visibility change
fix: apply skills visibility guard to hermes branch toolbar button
feat: include prompts in sidebar panel visibility settings
fix: persist visibleSidebarPanels setting in backend AppSettings
style: use compact pill toggles for sidebar panel visibility
feat: add sidebar panel visibility switches to settings
feat: add sidebar panel visibility settings and wire into toolbar buttons
chore(comet): archive ccs-home-model-display change
fix(fork): read grokbuild api key from config TOML in quick switch dialog
feat(fork): align model badge with upstream extractModelBadge
feat(fork): show current model badge and quick-switch entry on provider cards
feat(fork): add model quick switch dialog
feat(fork): add provider model read/write utils with tests
chore(comet): archive ccs-model-search-port change
feat(i18n): add model search picker keys for all locales
feat(form): port SearchableModelPicker from upstream for searchable model selection
chore(comet): archive ccs-claude-form-layout change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-claude-form-layout/
- Sync claude-form-layout spec to main specs
- Annotate design doc as archived
- Add verification report
fix(i18n): update advanced options hint, add ja/zh-TW toggle-all, drop dead apiFormatHint key
fix(form): correct 1M toggle-all alignment direction (ml-1)
fix(form): align 1M toggle-all checkbox with row checkboxes
refactor(form): move api format select into EndpointField rightSlot
fix(form): widen api format select to fit option text
feat(form): add 1M toggle-all checkbox in model mapping header
feat(form): move api format select next to endpoint input
chore(comet): archive ccs-provider-presets-trim change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-provider-presets-trim/
- Sync provider-presets-visibility spec to main specs
- Annotate design doc as archived
- Add verification report
docs(fork): add presets-trim files to rebase whitelist
test(fork): mock IS_FORK_BUILD false in unit tests for upstream-form semantics
feat(fork): wire official preset filter into provider forms
feat(fork): add hidden field to preset interfaces
feat(fork): add official preset allowlist and filter util
chore(comet): archive ccs-fork-scaffolding change
- Move change artifacts to docs/openspec/changes/archive/2026-08-18-ccs-fork-scaffolding/
- Sync fork-build-identity spec to main specs
- Annotate design doc as archived
- Add verification report
feat(fork): add dev config with isolated identifier and config dir
docs(fork): complete rebase whitelist with all fork-touched files
fix(fork): guard setTitle with isTauri in test/non-tauri env
fix(fork): define __CCS_FORK_BUILD__ in vitest config
feat(fork): add DevPanel and settings footer entry
feat(fork): set macOS window title to CC Switch (Fork) on startup
feat(fork): inject __CCS_FORK_BUILD__ compile-time constant
feat(fork): bump version to 3.19.2-fork.1 and rename product to CC Switch (Fork)
docs(fork): add upstream rebase workflow and ignore tool artifacts
fix(proxy): accept the whole grok-4.x (x>=5) family in the reasoning-effort whitelist (#7369)
Replace the verbatim grok-4.5 / grok-4.6 entries in supports_reasoning_effort with a rule that parses the grok-4.x minor version and accepts x >= 5, mirroring the existing GPT-5+ rule. This covers grok-4.7 (released 2026-09-21), whose reasoning effort was previously dropped on the Claude -> Chat, Claude -> Responses and Codex Responses -> Chat conversion paths, and lets future releases pass without another whitelist edit. The grok-build-* family is retained for saved providers.
Co-authored-by: allenxu09 <171831965+allenxu09@users.noreply.github.com>
docs(release): add v3.20.4 release notes
chore(release): v3.20.4
feat(pricing): add Grok 4.7 and GLM-5.3-FlashX
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.
fix(i18n): remove outdated OpenCode Go referral offer
fix: add SudoCode.chat alternate API endpoint
feat(settings): add a GitHub star prompt to the About card
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
feat(presets): add Soshow aggregator presets
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.
fix(ci): stabilize frontend tests and verify renderer build
fix(mcode): refresh provider state after removal (#7578)
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
fix(lib): reset skip_taskbar before window.show() across entry points (#6348)
5处 window.show() 调用中 4处没有在 show() 前重置 Windows 的 skip_taskbar,导致以下场景出现窗口可见但不在任务栏、点最小化后窗口消失的 bug:
- single_instance handler (lib.rs:360-361):当原进程因 silent_startup / 关闭到托盘等处于 skip_taskbar(true) 状态时,从快捷方式/开始菜单唤起已运行实例触发。
- handle_deeplink_url 的 focus_main_window 分支 (lib.rs:256):通过 ccswitch:// URL 唤起时。
- DB 恢复流程 (lib.rs:556):数据库版本不匹配时显示恢复界面。
- RunEvent::Opened 处理器 (lib.rs:1803):系统接收到 deep-link URL 时。
修复后的行为与 tray.rs::handle_tray_menu_event::show_main 分支一致(那里已有相同的 set_skip_taskbar(false) 调用)。
经用户测试验证:autostart + silent_startup + 从开始菜单/快捷方式启动的场景下,最小化按钮恢复正常工作。
fix(proxy): omit missing tool descriptions in the Codex responses-to-chat converter (#7378)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): preserve max effort for GPT-5.6 and GPT-6 Astra (#7531)
fix(misc): ignore shell startup output when probing tool versions in WSL (#7348)
try_get_version_wsl runs "${SHELL:-sh}" -lic '<tool> --version' through
wsl.exe and takes the first x.y.z anywhere in stdout. A login+interactive
shell runs the distro's startup files first, so their output shares that
stdout. On Ubuntu, /etc/profile.d/update-motd.sh prints the MOTD once a day,
starting with "Welcome to Ubuntu 24.04.4 LTS", and the card then showed
24.04.4 as the Claude Code version. The once-a-day stamp makes it appear
after the first probe of the day and vanish on refresh.
The payload now prints a sentinel before running the tool, and parsing looks
only at what follows its last occurrence. The last occurrence handles the
-lic || -lc || -c fallback chain printing it several times; matching it as a
substring handles OSC sequences glued in front of it. If the sentinel is
absent (the shell failed before the payload ran), the output is used as
before. The stderr fallback for tools that print their version there is kept.
Fixes #7347
Co-authored-by: lyfuci <lyfuci11@gmail.com>
fix(claude): hide session URL attribution (#7053)
* fix(claude): hide session URL attribution
* docs(claude): document session URL attribution
fix(ui): reset provider scroll when switching apps (#5211)
fix(pi): fix provider logo (#6826)
fix(i18n): use the Pi form's own term for the zh-TW duplicate-key message (#7187)
Follow-up to the nit on #6768. In zh-TW the Pi form labels the field
供應商識別碼 and says 供應商識別碼為必填 when it is empty, but the
duplicate message I added said 供應商標識 — the term Hermes, OpenCode and
OpenClaw use for their own field. One form, two names for the same thing.
The other three locales already keep the label and both messages on one
term, so this makes zh-TW match the pattern rather than introduce one:
en Provider key / Provider key is required / This provider key already exists
ja プロバイダーキー / プロバイダーキーを入力してください / このプロバイダーキーは既に存在します
zh 供应商标识 / 供应商标识不能为空 / 该供应商标识已存在
zh-TW 供應商識別碼 / 供應商識別碼為必填 / 該供應商標識已存在 <- the odd one out
hermes.form.providerKeyDuplicate carries the same sentence and is left
alone: its own label is 供應商標識, so it is already consistent.
Verified: managementListLocales 7/7. Full unit suite 1085/1087, the same
two App.test.tsx integration cases failing before and after.
fix(proxy): strip stop param for GitHub Copilot to unblock auto mode classifier (#5404)
Claude Code's auto mode classifier sends `stop_sequences` on every
safety check. On the GitHub Copilot path the Anthropic -> OpenAI Chat
conversion mapped it to `stop`, and Copilot's Chat Completions route
for Claude models answers that request with HTTP 400, so every
classifier call failed while normal chat kept working.
- Remove the converted `stop` field only when the provider is GitHub
Copilot; other OpenAI-compatible upstreams keep it.
- Copilot detection in the transform now also covers providers that
are recognised only through `env.ANTHROPIC_BASE_URL`.
- Regression tests for the Copilot (meta and env URL) and generic
provider cases.
Fixes #5175
fix(codex): keep stored auth when the live snapshot has no credential (#7434)
Editing the current Codex provider seeds the form from the live ~/.codex
snapshot, and switching away backfills that snapshot into the DB row. When a
provider table declares its own credential source (an Authorization header,
env_key, auth/aws), no bearer token is injected into config.toml and the
shared auth.json is deleted on third-party switches, so the live snapshot is
permanently `{ auth: {}, config }` while auth.OPENAI_API_KEY in the DB is the
only copy of the key. Both paths treated that empty live auth as
authoritative and silently erased the stored key, which in turn made the
usage-script modal misread the provider as an official subscription and
reset the saved script.
- Treat a credential-less live auth (missing auth.json or the bare
auth_mode logout marker) as an absent field in both the edit dialog and
the switch-away backfill, keeping the stored auth template. Live still
wins whenever it carries material, and official providers keep the live
login as their source of truth.
- Gate the official exemption on is_codex_official_provider /
resolveCodexOfficialIdentity rather than the category alone: a legacy
fixed official card without a persisted category is still written as
official, so the fallback must not resurrect a logged-out login on
switch-back.
- Cover both layers with regression tests, including the category-less
official shapes.
Fixes #7433
Supersedes #7492
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): lift additional_tools input carriers on the Codex chat conversion (#7454)
Codex talks to third-party providers over its Responses Lite transport,
which ships the request's extra tools in an `additional_tools` input
carrier: `{"type":"additional_tools","role":"developer","tools":[...]}`,
a role but no `content`. The Responses->Chat conversion let that carrier
fall through the generic "item with a role is a message" arm and emitted
`{"role":"system","content":null}` at messages[1], which strict Chat
gateways reject with `messages[1]: missing field "content"` (or
`data did not match any variant of ...SystemMessageContent`). The tools
carried inside it were also dropped on the Chat and Anthropic conversions.
- Skip `additional_tools` items in the Chat message walk; a carrier
declares tools, it is not a message.
- Collect carrier tools through the existing CodexToolContext registry
(dedup, namespace flattening, custom-tool conversion), which also feeds
the Anthropic conversion and the response-side name round-trip.
- Requests without a carrier convert byte-for-byte as before.
Known boundary: `custom` children inside a namespace tool
(`functions.exec`) are still not lifted; that needs its own response-side
name round-trip and is left for a follow-up.
Diagnosed independently in #6158 by BigStrongSun (fix in #6159) and in
fixture.
Fixes #7451
Fixes #6158
Refs #6033
Refs #6776
Supersedes #6159
fix(proxy): normalize Codex detail:original images for Chat gateways (#7476)
Codex marks tool-returned images with detail:"original" whenever the model
catalog advertises supports_image_detail_original, a Responses-only value.
The Responses->Chat conversion copied it verbatim into image_url.detail, and
strict OpenAI-compatible Chat gateways (Ark/Doubao, SenseNova, GLM, vLLM, ...)
reject the whole request with 400. Because Codex replays the full rollout on
every turn, one poisoned view_image result made the session fail permanently,
including plain-text turns.
Two layers:
- tool_media::merge_top_level_detail downgrades original -> auto, so already
poisoned rollouts and Codex versions before 0.155.0 (which do not normalize
replayed history themselves) recover immediately. auto/low/high are untouched.
- codex_catalog_model_entry always writes supports_image_detail_original:false
on the ProxyChat profile, so Codex stops emitting original at the source once
the catalog is regenerated. NativeResponses/Anthropic profiles and the DeepSeek
official catalog are unaffected.
Top-level user images were never affected: responses_content_to_chat_content
drops the part-level detail entirely.
Fixes #6495
Fixes #7458
Refs #6569
Refs #5805
fix(omo): detect unified config on the WSL side when the OpenCode dir is in WSL (#7550)
When the OpenCode config directory points into WSL
(\\wsl.localhost\<distro>\home\<user>\.config\opencode), OMO >= 4.19.3 keeps
its unified config in the WSL-side home (~/.omo/omo.jsonc). Detection only
probed the Windows home, never found it, and silently fell back to the legacy
per-plugin file that OMO no longer reads, so provider switches looked
successful but had no effect.
- Add derive_wsl_home_dir to derive the WSL-side home from a WSL UNC path
(home/<user> or root); non-WSL and non-home paths return None.
- Probe a list of home candidates for the unified config, WSL-side home
first and the OS home as a fallback, shared by the read, write and delete
paths.
- Detection only: when no unified config exists on either side the legacy
fallback is unchanged.
Fixes #7363
Supersedes #7367
docs(partner): add a FluxA purchase link and tighten its sponsor blurb
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.
fix(claude-desktop): expose the 1M context variant for DeepSeek V4 routes
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.
feat(presets): add FluxA Token Plan partner presets
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.
fix(presets): correct AICodeWith Codex-family endpoint to /v1
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.
fix(ui): render monochrome preset icons in the foreground color
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.
docs(user-manual): point Chat-only Codex examples at plan endpoints
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.
fix(pricing): restore DeepSeek V4 Pro price and seed Qwen3.8 and Hy4 models
- 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).
fix(presets): move MiniMax CN and BaiLing to their current official endpoints
- 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.
feat(codex): refresh CN presets from the Responses API audit
- 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.
test(claude-desktop): run Linux path tests on unix hosts and cover Linux in provider sync tests
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.
fix(ui): stop expanding stale cached usage tiers on ineligible provider cards
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.
fix(skills): 支持 skillId 与目录名不一致的技能安装和更新 (#6381)
* fix(skills): resolve mismatched skillId directories
* fix(skills): preserve source path during updates
* fix(skills): refresh source URL after repository relocation
* test(skills): isolate relocation fixture from legacy home
feat(opencode): 支持从获取的模型列表中搜索并批量添加所选模型 (#7515)
* feat: implement fetched models selection and UI components
* fix(opencode): prevent model search from submitting provider form
Add Kimi Global preset variants (kimi.ai platform + api endpoints) (#7526)
Add a "Kimi Global" twin immediately after the domestic "Kimi" API preset and a "Kimi For Coding Global" twin after the domestic "Kimi For Coding" preset, in every preset file that ships them (claude, claude-desktop, codex, hermes, openclaw, opencode, pi).
"Kimi Global" points to the international open platform https://platform.kimi.ai?aff=cc-switch (apiKeyUrl mirrored as https://platform.kimi.ai/console/api-keys?aff=cc-switch) and swaps the endpoint host api.moonshot.cn for api.moonshot.ai with each file's path unchanged (/v1 or /anthropic).
"Kimi For Coding Global" points to the international site https://www.kimi.ai/code/?aff=cc-switch and swaps the endpoint host api.kimi.com for api.kimi.ai with the path unchanged (e.g. https://api.kimi.ai/coding/v1).
Domestic presets are unchanged; category, icons, apiFormat, model catalogs, endpointCandidates (host swapped), partnerPromotionKey and settings otherwise mirror the domestic blocks exactly.
primePartner stays only on the domestic presets so the new variants do not alter prime-partner ordering.
The Pi twins get distinct providerKey values ("cc-switch-kimi-global", "cc-switch-kimi-for-coding-global") because providerKey is the preset-level primary key and must not collide with the domestic presets.
Rebalance sponsor CTAs around Kimi Code plan with dual-region links (#7522)
Kimi Code plan purchases have resumed, so the README sponsor sections go back to leading with the coding plan instead of the API-only flow written during the purchase pause.
All four locales (EN/ZH/JA/DE) now present Kimi Code plan (中文站 | Global) as the primary CTA and the Kimi Open Platform API (中文站 | Global, with the first-top-up bonus) as the secondary one, mirroring the domestic|global split already used for API links.
The aff and track_id values are unchanged.
fix(prompts): refresh active prompts after external file edits (#7194)
Reload the enabled prompt from its live file when the Prompts list loads, and refresh the open panel when the window regains focus, so external edits to CLAUDE.md / AGENTS.md show up without a restart.
- Keep saved templates available when the live file cannot be read.
- Skip the backfill for empty or whitespace-only files, matching enable_prompt.
- Skip the backfill while a sync/restore holds the sync lock, so a list load cannot write a stale live file into a freshly restored database.
fix(skills): raise archive entry limit so large skill repos install (#7489)
Installing hugohe3/ppt-master from skills.sh failed with
ARCHIVE_TOO_MANY_ENTRIES: the GitHub archive we download is the whole
repository (13,248 entries), and the 10,000-entry cap added in #5811
treated it as a zip bomb.
- Raise MAX_ARCHIVE_ENTRIES to 30,000 for both the remote archive and
local ZIP paths, and align WebDAV's MAX_EXTRACT_ENTRIES so a skills.zip
containing such a skill still restores.
- Charge every file entry at least one 4 KiB block against the existing
512 MiB budget, mirroring directory accounting, so empty-file archives
are bounded by the byte budget rather than by the entry count alone.
- Add tests for a 13k-entry repo archive and for the per-file minimum
charge.
Fixes: https://github.com/farion1231/cc-switch/issues/7475
feat: add MiniMax Code harness support (#7383)
Add MiniMax Code (mcode) as a managed application: provider and model management through the native custom_provider configuration, MCP servers, Skills, global instructions (AGENTS.md), read-only local session history with resume, and usage import from the native runtime database. Native files shared by the TUI and desktop are written under the native lock with rollback when the database commit fails, unrelated native configuration is preserved, and the data directory honors MINIMAX_DATA_DIR and MAVIS_DATA_DIR. Deleting sessions stays in the native application because they are coupled to runtime state.
fix(codex): recover stale account bindings during takeover (#7395)
After deleting a managed ChatGPT account and logging in again, a Codex provider can still reference the old local account ID, which made route switching, takeover, startup restoration and rebinding fail permanently.
Distinguish a missing outgoing account from an existing account with no matching live token. A missing account only releases its ownership marker after the persisted account store is verified; the target write policy stays in force. A stale target now reports an account-selection instruction instead of a bare not-found error, including when takeover is already enabled.
Fixes #7392
feat(claude-desktop): support 3P configuration on Linux (#7331)
Add Claude Desktop 3P configuration support on Linux. Resolve the config root from an absolute XDG_CONFIG_HOME, falling back to ~/.config; when CC Switch itself runs as a Flatpak, use the host ~/.config instead of the sandbox-private XDG_CONFIG_HOME. Update the en/zh/ja user manuals with Linux paths and the Flatpak boundary.
Fixes #4855
fix(proxy): omit missing tool descriptions instead of serializing null (#7319)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): keep reasoning effort for grok-4.6 and pass xhigh through verbatim (#7318)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(models): support Zhipu OpenAI Responses model lists (#7330)
Zhipu's Responses endpoint (open.bigmodel.cn/api/v1) serves its model list in the Codex remote catalog shape (models[].slug) instead of the OpenAI-compatible data[].id shape, so the Codex form's model fetch came back empty. The parser now falls back to models[].slug when data is absent.
fix(misc): fetch npm dist-tags from the dedicated endpoint with the probe timeout (#7346)
The tool version probe requested the full npm packument for each package (metadata for every historical version, 14-25 MB uncompressed for codex / opencode / openclaw) only to read its dist-tags, and it was the only latest-version probe without LATEST_PROBE_TIMEOUT, so it fell back to the proxy client's 600 s total timeout and the cards sat on "loading" for minutes on slow links while the large downloads starved the Hermes GitHub probe into "unknown".
Switch fetch_npm_dist_tags to registry.npmjs.org/-/package/{package}/dist-tags, whose body is the dist-tags object itself (tens of bytes to a few KB), escape scoped names as %2f, and apply the same 15 s probe timeout as the GitHub and PyPI probes. No data is lost: the only consumer reads latest and the tool's prerelease tags.
Fixes #7339
feat(usage): display output tokens per second in request log table and detail panel (#3369)
Show tokens/s next to the output token count in the request log table and the request detail panel.
The generation window prefers an explicit duration when present, otherwise latency minus first-token time for streaming requests, and falls back to full latency for non-streaming requests. Rows without a positive token count or window show nothing.
Closes #5936
fix(presets): update APIKEY.FUN URLs to apikey.fan
docs(release): add v3.20.3 release notes
chore(release): v3.20.3
fix(pricing): move retired DeepSeek V4 family to the V4.1 Flash tier
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.
fix(presets): declare third-party Token Plan DeepSeek V4 rows text-only
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.
feat(presets): rename DouBaoSeed preset to localized Volcengine Doubao
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.
fix(codex): skip unchanged incomplete rollout tails
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.
fix(claude): align quick model selection with form order
fix(tray): show bound ChatGPT account quota for managed Codex cards
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
feat(claude): add "Disable Artifact Tool" quick toggle to provider editor
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.
feat(codex): refresh aggregator model presets and catalogs
feat(codex): switch Kimi presets to native Responses direct-connect
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.
fix(usage): show Claude Fable weekly limit from the usage API limits array
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.
fix(codex): mirror vision-capable DeepSeek catalog and un-gate the legacy v4-flash alias (#7286)
Refresh the bundled DeepSeek official Codex catalog to the vendor's current models.json (slug deepseek-flash, image input modality, supports_image_detail_original) and remove deepseek-v4-flash from the confirmed text-only registry: the legacy id is still accepted by DeepSeek and routed to the vision-capable V4.1 Flash, so both the proxy media sanitizer and the generated Codex catalog must fail open for it. deepseek-v4-pro stays text-only per the vendor catalog.
Fixes #7283
fix(proxy): clamp sub-floor max_tokens to the Responses API minimum (#7287)
The Responses API rejects max_output_tokens below 16, but Anthropic clients legitimately send tiny probe budgets (Claude Desktop's model-availability probe uses max_tokens=1). The Anthropic→Responses conversion copied the value verbatim, so strict upstreams returned 400 and Claude Desktop reported the mapped model as unavailable under local routing.
Clamp 1..=15 up to 16 during conversion; 16+, 0 and non-integer values keep their pass-through semantics. Normal session requests convert byte-for-byte as before, and the Codex OAuth path still strips max_output_tokens afterwards.
Fixes #7103
fix(codex): coalesce adjacent commentary with pending tool calls (#7280)
The Codex Responses -> Chat Completions converter emitted two consecutive
assistant messages when one Responses model turn contained a commentary
message directly followed by function_call items. Chat upstreams imitate
the text-only assistant message as a complete turn and return
finish_reason=stop before the expected tool call, so long agent tasks end
after a progress update.
Merge pending tool calls into the directly adjacent assistant message when
it carries no tool_calls yet, attach pending reasoning per segment so
parallel calls do not duplicate it, and keep the new-message path for every
other boundary shape (user/tool boundaries, prior tool-call batches, media
flushes). The reasoning placeholder backfill still applies to coalesced
calls without reasoning.
Fixes #6529
Supersedes #6530
Co-authored-by: BigStrongSun <BigStrongSun@users.noreply.github.com>
fix(proxy): skip empty reasoning_content placeholders in Anthropic SSE streaming (#7227)
Some OpenAI-compatible upstreams (GLM, Qwen, DeepSeek-V4-Pro via ModelScope/DashScope,
Bailian, JD Cloud, ...) keep a `reasoning_content: ""` placeholder in every content
chunk instead of omitting the field. The reasoning branch of the OpenAI Chat -> Anthropic
SSE converter only checked for presence, and it shares `current_non_tool_block_type`
with the content branch, so every content chunk closed the open text block, opened an
empty thinking block, then reopened a text block. N content chunks became 2N content
blocks, half of them empty thinking blocks, which Claude Code rendered as one line per
token with dozens of empty Thought blocks and bloated the session JSONL.
Filter empty reasoning before entering the branch, matching the `!content.is_empty()`
guard the content branch already had and the non-streaming `openai_to_anthropic`
converter. Two regression tests pin block index, type and order: the placeholder shape
must yield a single text block and no thinking_delta at all, and real reasoning must
still yield one thinking block followed by one text block.
Fixes #5028
Fixes #4404
fix(codex): detect growing rollouts with a persisted byte cursor (#7219)
Codex keeps the rollout append handle open for the whole session, so on Windows NTFS the file mtime does not advance while the file grows and the pure-mtime gate skipped growing rollouts forever. Persist the observed byte length in session_log_sync.last_byte_offset for Codex rows and skip a file only when both mtime and size are unchanged. The parser now splits records on newlines manually so an incomplete trailing record is retried on the next pass without advancing the line cursor.
Fixes #6060
fix(codex): proxy image edits and Images API follow-ups (#7177)
* fix(codex): proxy image edits and Images API follow-ups
Follow-ups to #7036 for Codex's built-in ImageGen over the local proxy:
- Register `/images/edits` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them through the shared Codex standalone passthrough.
ImageGen posts to `images/edits` whenever a call references existing
images, and the router returned an empty 404 for it.
- Add `CodexStandaloneEndpoint::ImagesEdits`. Both Images endpoints share
one full-URL suffix table, so a complete Responses / Compact / Chat
Completions / Images URL derives the sibling endpoint (and vice versa)
while opaque full URLs keep failing closed.
- Apply the standalone sibling rewrite when the base URL is pasted as a
complete endpoint with the full-URL switch off, mirroring
`codex_chat_base_is_full_endpoint`; standalone requests no longer become
`.../chat/completions/images/generations`.
- Parse Images API streaming usage: `image_generation.completed` carries
`usage` on the event top level, so scan for `usage.input_tokens` before
falling back to the Chat Completions stream parser.
Related #7036
* fix(codex): match standalone URL suffixes case-insensitively
Keep endpoint detection and sibling rewriting consistent while preserving the original URL prefix and query. Cover mixed-case Images and Alpha Search endpoints through URL and proxy regression cases.
---------
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): preserve per-app settings on shutdown and port allocation (#7210)
On normal exit, stop_with_restore_keep_state() called the legacy global
proxy-config writer to clear the long-retired live_takeover_active flag.
That writer reads the Claude row and runs an UPDATE without a WHERE clause,
so every shutdown copied Claude's max_retries and the three timeout fields
onto the Codex, Gemini and Grok Build rows — and the UPDATE never carried
live_takeover_active in the first place, so the step had no effect beyond
the clobber. The ephemeral listen-port path did the same on every proxy
start.
Drop the dead write-back (live-config restore, backup cleanup and health
reset are unchanged) and persist the resolved ephemeral port through the
global-config interface, which only touches the shared listen/logging
columns. Two regression tests seed distinct per-app settings for all four
apps and assert they survive both paths.
Fixes #7204
fix(provider): preserve child metadata during universal sync (#7212)
Syncing a universal provider regenerates its Claude/Codex/Gemini child
providers. The child's `settings_config` was merged with the existing row,
but `meta`, `created_at` and `sort_index` were taken from the generated
object, and the universal provider never carries any of them from the UI.
The UPDATE therefore wiped the child's usage script, common-config opt-out,
endpoint auto-select and other per-app settings to `{}` and reset the sort
index to NULL, which pushed the card to the bottom of the list and looked
like the provider had been deleted and re-created.
Keep the existing child's `meta`, `created_at` and `sort_index` when the
row already exists. Name, base URL, key, models, website and notes are
still driven by the universal provider, and first-time creation still
inherits the parent's metadata.
Fixes #7134
feat(presets): rebrand DashScope as 千问AI平台 and refresh Qwen models (#7183)
Rename the domestic Bailian presets to 千问AI平台 with a dedicated icon, refresh the Qwen family to the 3.8 generation across Claude Code, Claude Desktop, Codex, Hermes, OpenClaw, OpenCode and Pi, add the domestic Token Plan presets, restore the international QwenCloud Coding Plan presets, and align OpenClaw/OpenCode/Pi model metadata with the official client docs.
fix(codex): honor proxy URL when model_provider is omitted (#7263)
When a Codex provider card's TOML has no `model_provider`, Codex falls back to
its built-in `openai` provider, but takeover wrote the proxy address as a
top-level `base_url` that Codex never reads, so requests bypassed the local
proxy and went straight to api.openai.com.
Treat the missing selector as the built-in `openai` provider and write the
proxy address to `openai_base_url`; the existing legacy-reroute migration then
turns it into a `cc-switch` custom provider table carrying the `PROXY_MANAGED`
bearer, the same shape every other third-party takeover uses. Explicit
provider selections are unchanged.
Refs #6256, #7217
fix(provider): update MiniMax defaults to M3 and remove expired offers (#7255)
* fix(provider): refresh MiniMax M3 presets and remove expired promotions
* fix(provider): complete MiniMax M3 capability declarations
---------
Co-authored-by: saladday <1203511142@qq.com>
chore(provider): remove Atlas Cloud sponsor status, keep presets
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`
docs(release): add v3.20.2 release notes
chore(release): v3.20.2
feat(provider): add SoleAPI sponsor presets across all nine apps
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.
fix(codex): proxy image edits endpoint
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.
fix(provider): pin models URL for JieKou and Novita Claude presets
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.
feat(pricing): refresh seed for September 2026 vendor price changes
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.
fix(codex): stamp requires_openai_auth on takeover writes to match the live login state
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.
fix(updater): show the real reason when an update check fails (#6482)
The updater plugin rejects with a plain string, so `err instanceof Error`
never matched and the real failure reason was replaced by the generic
fallback message. Use the existing extractErrorMessage helper, and pass
the recovered message into the check-update failure toast so it actually
reaches the user instead of stopping at an unread context state.
Co-authored-by: Jason <farion1231@gmail.com>
docs: fix the locales path in the README directory trees (#6100)
All four READMEs showed locales/ as a direct child of src/, but the
translations live at src/i18n/locales/. Split it into two tree nodes so it
matches how the rest of the tree is drawn. Also fixes a CLaude -> Claude
typo in README_ZH.md.
Co-authored-by: Jason <farion1231@gmail.com>
fix(claude): exclude workflow journal.jsonl from session list
Claude Code's workflow feature writes subagents/workflows/wf_*/journal.jsonl inside the session directory. The Claude session scanner only excluded agent-* files, so each journal parsed as an empty ghost session titled "journal". Exclude journal.jsonl by file name and add regression tests.
Fixes #6042
fix(i18n): add the missing pi.form.providerKeyDuplicate and common.collapse keys
translatePiProviderMutationError returns pi.form.providerKeyDuplicate for the Pi backend's duplicate-key error, but the key existed in none of the four locale files, so the toast showed the raw key. common.collapse (aria-label of the thinking-map toggle) was missing the same way. Add both keys to zh/en/ja/zh-TW and lock them in the locale coverage test.
fix(proxy): stop mask_url panicking on multi-byte UTF-8 near the truncation boundary
mask_url's fallback branch (used when the proxy URL fails Url::parse) sliced the raw string at byte 20 and panicked when a multi-byte UTF-8 character straddled that offset, e.g. a CJK string pasted into the global proxy address. Snap the cut to the nearest char boundary at or before 20 and add a regression test.
fix(tests): isolate LOCALAPPDATA so tests stop writing the real Claude Desktop config
On Windows, Claude Desktop config paths resolve from LOCALAPPDATA, which the test home override in tests/support.rs did not redirect, so profile_roundtrip wrote the https://desktop.test fixture into the developer's real Claude Desktop config. Point LOCALAPPDATA at the isolated test home alongside HOME and USERPROFILE.
Fixes #6077
fix(usage): widen pricing model source select to fit localized labels
The pricing model source SelectTrigger in the usage cost settings table was w-28, which leaves about 70px for the label; the English and Japanese labels need about 107px and overflowed the trigger. Widen it to w-40 and drop the h-7 overrides so the trigger and the adjacent Input use the shared h-9 height.
feat(pi): add Tencent TokenHub and Token Plan presets (#7159)
Add eight Tencent presets for Pi, covering the TokenHub pay-as-you-go line
(domestic and international) and the six Token Plan subscription products —
Pi was the only managed app still missing them. Model IDs, ordering and
endpoints follow Tencent's own model lists verbatim, and the tests assert each
product's line-up in order so future drift is caught.
Declare thinking control alongside the capabilities rather than leaving it to
defaults: the 31 DeepSeek entries carry a Tencent-specific compat so selecting
"off" actually disables thinking instead of silently billing reasoning tokens,
the eight Kimi K2.7 Code entries no longer offer an "off" level the model
cannot honour, and kimi-k2.7-code-highspeed is marked image-capable so
tool-returned screenshots are no longer dropped.
Drop minimax-m2.5 from every app: it is gone from Tencent's model tables and
the platform is retiring it.
fix(codex): proxy image generations endpoint (#7036)
Codex's built-in ImageGen calls the legacy OpenAI Images API, but the local
proxy only registered Responses / Chat Completions / Compact / Alpha Search
routes, so `/v1/images/generations` returned an empty 404 under Codex routing.
- Register `/images/generations` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them as a Codex standalone passthrough shared with Alpha Search.
- Generalize the Alpha Search full-URL rewrite into
`rewrite_codex_standalone_full_url`: derive the sibling
`/images/generations` from full Responses / Compact / Chat Completions URLs,
keep an existing Images URL as-is, and fail closed on opaque full URLs.
- Record `usage.input_tokens` / `output_tokens` through the Codex usage parser.
Fixes #5429
Fixes #6745
feat(pricing): add Gemini 3.8 Flash pricing (#7164)
Seeds the built-in pricing row for Gemini 3.8 Flash at 0.75 / 3.75 input/output per million tokens, 0.075 cache read, no cache-write charge, plus a regression test locking all four fields.
Values verified against Google's own pricing page (introductory price through 2026-12-31; list price 1.50 / 7.50 / 0.15 from 2027-01-01) and models.dev.
feat(provider): extend PPIO presets for Pi (#6870)
Add a PPIO provider preset for Pi using the OpenAI-compatible endpoint,
and pin an explicit models URL on the Claude Code PPIO preset so model
discovery resolves against https://api.ppio.com/openai/v1/models instead
of the derived candidates, which all return 404.
Co-authored-by: Jason <farion1231@gmail.com>
feat(pricing): add GLM-5.3 Flash pricing (#7163)
fix(proxy): enable parallel tool calls for Codex OAuth (#7024)
Codex OAuth requests defaulted parallel_tool_calls to false when the incoming
Anthropic request carried no explicit value, forcing Claude Code into one tool
call per model turn. Default it to true (matching codex-rs), map Anthropic
tool_choice.disable_parallel_tool_use to the inverse Responses value so explicit
serial execution is preserved, and add regression coverage for both directions.
Carries #5722 forward on top of current main.
Fixes #5719
Co-authored-by: 太一 <taiyi@douyu.tv>
feat(pricing): add GPT-6 Astra pricing (#7162)
fix(codex): resolve input_modalities for unknown models in vendor catalog (#6750)
When a model slug is not in the bundled DeepSeek official catalog, the vendor path clones the flagship entry and previously inherited its text-only input_modalities. Resolve modalities for unmatched slugs through the same registry/fail-open logic the non-vendor path uses, so a vision variant such as deepseek-v4-flash-vision-exp is no longer declared text-only. Explicit user inputModalities still win; matched models keep the vendor's declaration verbatim.
Fixes #6725
fix(claude): expose Opus 5 and Sonnet 5 in takeover mode (#5882)
Bump the stable client-facing role aliases that takeover mode writes to
Claude Code: Opus claude-opus-4-8 -> claude-opus-5 and Sonnet
claude-sonnet-4-6 -> claude-sonnet-5, so the /model menu matches the
Claude Desktop default routes and preset defaults that already moved to
Opus 5 / Sonnet 5. Add "opus-5" to the adaptive-thinking classifier so
the Bedrock thinking optimizer, which runs on the client alias before
model mapping, keeps sending adaptive thinking instead of the removed
budget_tokens.
Routing is unaffected: the proxy maps client aliases by role keyword, and
the aliases are rewritten automatically the next time the proxy starts.
Fixes #5876
Co-authored-by: Jason <farion1231@gmail.com>
feat(pricing): add GLM-5.3 to built-in model pricing table (#6591)
Seed glm-5.3 at $1.4/$4.4 per MTok with $0.26 cache read, matching the
official Z.ai list price (identical to the GLM-5.2 tier):
https://docs.z.ai/guides/overview/pricing
New model id, so seed only; no repair entry and no SCHEMA_VERSION bump.
Existing installs pick the row up on next launch via INSERT OR IGNORE, and
the startup cost backfill re-prices historical glm-5.3 rows recorded at $0.
fix(codex): align OAuth client identity for GPT-6 (#7132)
Requests for gpt-6-astra through the Claude-to-Codex OAuth takeover route advertised Codex 0.144.1, below the model's 0.153.0 minimal_client_version, so ChatGPT's backend rejected them with HTTP 400 "requires a newer version of Codex". Bump the advertised client version to 0.153.4 and share the originator/version constants between generation and model discovery, which previously sent cc-switch's own package version and originator.
Two regression tests pin the generation headers and the model-list request to a version at or above the gpt-6-astra floor.
Refs #7129
fix(codex): disable supports_search_tool for DeepSeek catalog models (#6653)
DeepSeek's Responses API has no tool_search support, but the bundled
official catalog declared supports_search_tool=true for deepseek-v4-pro
and deepseek-v4-flash. Codex gates deferred/namespaced MCP tools on
this flag (search_tool_enabled = supports_search_tool &&
namespace_tools), so MCP tools were hidden behind a tool_search the
gateway cannot serve — no MCP tool could ever be called.
Set the flag to false for both models so Codex lists MCP tools inline.
Hosted web_search is unaffected (gated by provider capabilities, not
this flag) and DeepSeek's server-side web_search still works.
Fixes #6647
fix(codex): move $ref siblings into allOf for Moonshot/Kimi chat upstreams (#6863)
Moonshot's Chat Completions validator (api.moonshot.cn, api.moonshot.ai and the Kimi For Coding endpoint api.kimi.com) rejects a `$ref` that carries sibling keywords. Codex Desktop's built-in tool schemas carry exactly that shape, so every desktop turn routed through the local proxy to Kimi failed with HTTP 400.
When the resolved upstream host is Moonshot/Kimi, each `$ref` with siblings is moved into `allOf` after the Responses -> Chat conversion and the siblings stay in place. The traversal only enters schema-valued keywords and the rewrite is idempotent. Every other provider keeps byte-identical tool schemas, and the Codex -> Anthropic path is left alone because Moonshot's Anthropic-compatible endpoint accepts the original shape.
Fixes #6867
Co-authored-by: Jason <farion1231@gmail.com>
fix(codex): point Zhipu GLM presets at the official Responses endpoint (#6957)
Zhipu documents three separate base URLs per site (Anthropic /api/anthropic,
Chat /api/coding/paas/v4, Responses /api/v1) and warns that the wrong one
cannot use Coding Plan quota. Codex direct-connect speaks Responses, so the
presets must target /api/v1; the 400 in #6944 (`unknown variant custom`)
came from the Chat endpoint's strict legacy gateway.
- Switch the Zhipu GLM / Zhipu GLM en presets to apiFormat openai_responses
(NativeResponses catalog: shell_command edits, no freeform apply_patch)
…
移除 tauri-plugin-updater 依赖与自动更新链路,改为统一引导用户到
GitHub Releases 手动下载新版。
- 前端:updater.ts 恒返回 up-to-date;UpdateContext 取消启动自检;
AboutSection「检查更新」直接打开 fork releases 页;DatabaseUpgrade
恢复界面去掉下载进度条,改为打开发布页。
- 后端:移除 updater 插件注册与 UpdaterExt 调用;
install_update_and_restart 降级为空壳,check_app_update_available
返回当前版本号;check_for_updates 指向 fork releases;capabilities
去掉 updater:default 权限。
- 打包:tauri.conf.json 删 plugins.updater、createUpdaterArtifacts=false;
Cargo.toml / package.json 删 updater 依赖,Cargo.lock / pnpm-lock
同步重生成。
- CI:release.yml 移除签名收集与 assemble-latest-json job,删除
.tar.gz/.sig 处理,release body 去掉 updater 说明。
版本号统一为 3.20.0-1(合法 semver 且 MSI 第 4 位为 1)。
Cargo.lock / pnpm-lock.yaml 已随依赖移除重生成,cargo check 通过。
ci: build only Windows x64 and macOS arm64, unsigned
feat: gate dev panel behind dev-only env flag
chore(comet): archive ccs-sidebar-panels-visibility change
fix: apply skills visibility guard to hermes branch toolbar button
feat: include prompts in sidebar panel visibility settings
fix: persist visibleSidebarPanels setting in backend AppSettings
style: use compact pill toggles for sidebar panel visibility
feat: add sidebar panel visibility switches to settings
feat: add sidebar panel visibility settings and wire into toolbar buttons
chore(comet): archive ccs-home-model-display change
fix(fork): read grokbuild api key from config TOML in quick switch dialog
feat(fork): align model badge with upstream extractModelBadge
feat(fork): show current model badge and quick-switch entry on provider cards
feat(fork): add model quick switch dialog
feat(fork): add provider model read/write utils with tests
chore(comet): archive ccs-model-search-port change
feat(i18n): add model search picker keys for all locales
feat(form): port SearchableModelPicker from upstream for searchable model selection
chore(comet): archive ccs-claude-form-layout change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-claude-form-layout/
- Sync claude-form-layout spec to main specs
- Annotate design doc as archived
- Add verification report
fix(i18n): update advanced options hint, add ja/zh-TW toggle-all, drop dead apiFormatHint key
fix(form): correct 1M toggle-all alignment direction (ml-1)
fix(form): align 1M toggle-all checkbox with row checkboxes
refactor(form): move api format select into EndpointField rightSlot
fix(form): widen api format select to fit option text
feat(form): add 1M toggle-all checkbox in model mapping header
feat(form): move api format select next to endpoint input
chore(comet): archive ccs-provider-presets-trim change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-provider-presets-trim/
- Sync provider-presets-visibility spec to main specs
- Annotate design doc as archived
- Add verification report
docs(fork): add presets-trim files to rebase whitelist
test(fork): mock IS_FORK_BUILD false in unit tests for upstream-form semantics
feat(fork): wire official preset filter into provider forms
feat(fork): add hidden field to preset interfaces
feat(fork): add official preset allowlist and filter util
chore(comet): archive ccs-fork-scaffolding change
- Move change artifacts to docs/openspec/changes/archive/2026-08-18-ccs-fork-scaffolding/
- Sync fork-build-identity spec to main specs
- Annotate design doc as archived
- Add verification report
feat(fork): add dev config with isolated identifier and config dir
docs(fork): complete rebase whitelist with all fork-touched files
fix(fork): guard setTitle with isTauri in test/non-tauri env
fix(fork): define __CCS_FORK_BUILD__ in vitest config
feat(fork): add DevPanel and settings footer entry
feat(fork): set macOS window title to CC Switch (Fork) on startup
feat(fork): inject __CCS_FORK_BUILD__ compile-time constant
feat(fork): bump version to 3.19.2-fork.1 and rename product to CC Switch (Fork)
docs(fork): add upstream rebase workflow and ignore tool artifacts
fix(proxy): accept the whole grok-4.x (x>=5) family in the reasoning-effort whitelist (#7369)
Replace the verbatim grok-4.5 / grok-4.6 entries in supports_reasoning_effort with a rule that parses the grok-4.x minor version and accepts x >= 5, mirroring the existing GPT-5+ rule. This covers grok-4.7 (released 2026-09-21), whose reasoning effort was previously dropped on the Claude -> Chat, Claude -> Responses and Codex Responses -> Chat conversion paths, and lets future releases pass without another whitelist edit. The grok-build-* family is retained for saved providers.
Co-authored-by: allenxu09 <171831965+allenxu09@users.noreply.github.com>
docs(release): add v3.20.4 release notes
chore(release): v3.20.4
feat(pricing): add Grok 4.7 and GLM-5.3-FlashX
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.
fix(i18n): remove outdated OpenCode Go referral offer
fix: add SudoCode.chat alternate API endpoint
feat(settings): add a GitHub star prompt to the About card
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
feat(presets): add Soshow aggregator presets
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.
fix(ci): stabilize frontend tests and verify renderer build
fix(mcode): refresh provider state after removal (#7578)
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
fix(lib): reset skip_taskbar before window.show() across entry points (#6348)
5处 window.show() 调用中 4处没有在 show() 前重置 Windows 的 skip_taskbar,导致以下场景出现窗口可见但不在任务栏、点最小化后窗口消失的 bug:
- single_instance handler (lib.rs:360-361):当原进程因 silent_startup / 关闭到托盘等处于 skip_taskbar(true) 状态时,从快捷方式/开始菜单唤起已运行实例触发。
- handle_deeplink_url 的 focus_main_window 分支 (lib.rs:256):通过 ccswitch:// URL 唤起时。
- DB 恢复流程 (lib.rs:556):数据库版本不匹配时显示恢复界面。
- RunEvent::Opened 处理器 (lib.rs:1803):系统接收到 deep-link URL 时。
修复后的行为与 tray.rs::handle_tray_menu_event::show_main 分支一致(那里已有相同的 set_skip_taskbar(false) 调用)。
经用户测试验证:autostart + silent_startup + 从开始菜单/快捷方式启动的场景下,最小化按钮恢复正常工作。
fix(proxy): omit missing tool descriptions in the Codex responses-to-chat converter (#7378)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): preserve max effort for GPT-5.6 and GPT-6 Astra (#7531)
fix(misc): ignore shell startup output when probing tool versions in WSL (#7348)
try_get_version_wsl runs "${SHELL:-sh}" -lic '<tool> --version' through
wsl.exe and takes the first x.y.z anywhere in stdout. A login+interactive
shell runs the distro's startup files first, so their output shares that
stdout. On Ubuntu, /etc/profile.d/update-motd.sh prints the MOTD once a day,
starting with "Welcome to Ubuntu 24.04.4 LTS", and the card then showed
24.04.4 as the Claude Code version. The once-a-day stamp makes it appear
after the first probe of the day and vanish on refresh.
The payload now prints a sentinel before running the tool, and parsing looks
only at what follows its last occurrence. The last occurrence handles the
-lic || -lc || -c fallback chain printing it several times; matching it as a
substring handles OSC sequences glued in front of it. If the sentinel is
absent (the shell failed before the payload ran), the output is used as
before. The stderr fallback for tools that print their version there is kept.
Fixes #7347
Co-authored-by: lyfuci <lyfuci11@gmail.com>
fix(claude): hide session URL attribution (#7053)
* fix(claude): hide session URL attribution
* docs(claude): document session URL attribution
fix(ui): reset provider scroll when switching apps (#5211)
fix(pi): fix provider logo (#6826)
fix(i18n): use the Pi form's own term for the zh-TW duplicate-key message (#7187)
Follow-up to the nit on #6768. In zh-TW the Pi form labels the field
供應商識別碼 and says 供應商識別碼為必填 when it is empty, but the
duplicate message I added said 供應商標識 — the term Hermes, OpenCode and
OpenClaw use for their own field. One form, two names for the same thing.
The other three locales already keep the label and both messages on one
term, so this makes zh-TW match the pattern rather than introduce one:
en Provider key / Provider key is required / This provider key already exists
ja プロバイダーキー / プロバイダーキーを入力してください / このプロバイダーキーは既に存在します
zh 供应商标识 / 供应商标识不能为空 / 该供应商标识已存在
zh-TW 供應商識別碼 / 供應商識別碼為必填 / 該供應商標識已存在 <- the odd one out
hermes.form.providerKeyDuplicate carries the same sentence and is left
alone: its own label is 供應商標識, so it is already consistent.
Verified: managementListLocales 7/7. Full unit suite 1085/1087, the same
two App.test.tsx integration cases failing before and after.
fix(proxy): strip stop param for GitHub Copilot to unblock auto mode classifier (#5404)
Claude Code's auto mode classifier sends `stop_sequences` on every
safety check. On the GitHub Copilot path the Anthropic -> OpenAI Chat
conversion mapped it to `stop`, and Copilot's Chat Completions route
for Claude models answers that request with HTTP 400, so every
classifier call failed while normal chat kept working.
- Remove the converted `stop` field only when the provider is GitHub
Copilot; other OpenAI-compatible upstreams keep it.
- Copilot detection in the transform now also covers providers that
are recognised only through `env.ANTHROPIC_BASE_URL`.
- Regression tests for the Copilot (meta and env URL) and generic
provider cases.
Fixes #5175
fix(codex): keep stored auth when the live snapshot has no credential (#7434)
Editing the current Codex provider seeds the form from the live ~/.codex
snapshot, and switching away backfills that snapshot into the DB row. When a
provider table declares its own credential source (an Authorization header,
env_key, auth/aws), no bearer token is injected into config.toml and the
shared auth.json is deleted on third-party switches, so the live snapshot is
permanently `{ auth: {}, config }` while auth.OPENAI_API_KEY in the DB is the
only copy of the key. Both paths treated that empty live auth as
authoritative and silently erased the stored key, which in turn made the
usage-script modal misread the provider as an official subscription and
reset the saved script.
- Treat a credential-less live auth (missing auth.json or the bare
auth_mode logout marker) as an absent field in both the edit dialog and
the switch-away backfill, keeping the stored auth template. Live still
wins whenever it carries material, and official providers keep the live
login as their source of truth.
- Gate the official exemption on is_codex_official_provider /
resolveCodexOfficialIdentity rather than the category alone: a legacy
fixed official card without a persisted category is still written as
official, so the fallback must not resurrect a logged-out login on
switch-back.
- Cover both layers with regression tests, including the category-less
official shapes.
Fixes #7433
Supersedes #7492
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): lift additional_tools input carriers on the Codex chat conversion (#7454)
Codex talks to third-party providers over its Responses Lite transport,
which ships the request's extra tools in an `additional_tools` input
carrier: `{"type":"additional_tools","role":"developer","tools":[...]}`,
a role but no `content`. The Responses->Chat conversion let that carrier
fall through the generic "item with a role is a message" arm and emitted
`{"role":"system","content":null}` at messages[1], which strict Chat
gateways reject with `messages[1]: missing field "content"` (or
`data did not match any variant of ...SystemMessageContent`). The tools
carried inside it were also dropped on the Chat and Anthropic conversions.
- Skip `additional_tools` items in the Chat message walk; a carrier
declares tools, it is not a message.
- Collect carrier tools through the existing CodexToolContext registry
(dedup, namespace flattening, custom-tool conversion), which also feeds
the Anthropic conversion and the response-side name round-trip.
- Requests without a carrier convert byte-for-byte as before.
Known boundary: `custom` children inside a namespace tool
(`functions.exec`) are still not lifted; that needs its own response-side
name round-trip and is left for a follow-up.
Diagnosed independently in #6158 by BigStrongSun (fix in #6159) and in
fixture.
Fixes #7451
Fixes #6158
Refs #6033
Refs #6776
Supersedes #6159
fix(proxy): normalize Codex detail:original images for Chat gateways (#7476)
Codex marks tool-returned images with detail:"original" whenever the model
catalog advertises supports_image_detail_original, a Responses-only value.
The Responses->Chat conversion copied it verbatim into image_url.detail, and
strict OpenAI-compatible Chat gateways (Ark/Doubao, SenseNova, GLM, vLLM, ...)
reject the whole request with 400. Because Codex replays the full rollout on
every turn, one poisoned view_image result made the session fail permanently,
including plain-text turns.
Two layers:
- tool_media::merge_top_level_detail downgrades original -> auto, so already
poisoned rollouts and Codex versions before 0.155.0 (which do not normalize
replayed history themselves) recover immediately. auto/low/high are untouched.
- codex_catalog_model_entry always writes supports_image_detail_original:false
on the ProxyChat profile, so Codex stops emitting original at the source once
the catalog is regenerated. NativeResponses/Anthropic profiles and the DeepSeek
official catalog are unaffected.
Top-level user images were never affected: responses_content_to_chat_content
drops the part-level detail entirely.
Fixes #6495
Fixes #7458
Refs #6569
Refs #5805
fix(omo): detect unified config on the WSL side when the OpenCode dir is in WSL (#7550)
When the OpenCode config directory points into WSL
(\\wsl.localhost\<distro>\home\<user>\.config\opencode), OMO >= 4.19.3 keeps
its unified config in the WSL-side home (~/.omo/omo.jsonc). Detection only
probed the Windows home, never found it, and silently fell back to the legacy
per-plugin file that OMO no longer reads, so provider switches looked
successful but had no effect.
- Add derive_wsl_home_dir to derive the WSL-side home from a WSL UNC path
(home/<user> or root); non-WSL and non-home paths return None.
- Probe a list of home candidates for the unified config, WSL-side home
first and the OS home as a fallback, shared by the read, write and delete
paths.
- Detection only: when no unified config exists on either side the legacy
fallback is unchanged.
Fixes #7363
Supersedes #7367
docs(partner): add a FluxA purchase link and tighten its sponsor blurb
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.
fix(claude-desktop): expose the 1M context variant for DeepSeek V4 routes
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.
feat(presets): add FluxA Token Plan partner presets
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.
fix(presets): correct AICodeWith Codex-family endpoint to /v1
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.
fix(ui): render monochrome preset icons in the foreground color
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.
docs(user-manual): point Chat-only Codex examples at plan endpoints
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.
fix(pricing): restore DeepSeek V4 Pro price and seed Qwen3.8 and Hy4 models
- 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).
fix(presets): move MiniMax CN and BaiLing to their current official endpoints
- 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.
feat(codex): refresh CN presets from the Responses API audit
- 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.
test(claude-desktop): run Linux path tests on unix hosts and cover Linux in provider sync tests
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.
fix(ui): stop expanding stale cached usage tiers on ineligible provider cards
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.
fix(skills): 支持 skillId 与目录名不一致的技能安装和更新 (#6381)
* fix(skills): resolve mismatched skillId directories
* fix(skills): preserve source path during updates
* fix(skills): refresh source URL after repository relocation
* test(skills): isolate relocation fixture from legacy home
feat(opencode): 支持从获取的模型列表中搜索并批量添加所选模型 (#7515)
* feat: implement fetched models selection and UI components
* fix(opencode): prevent model search from submitting provider form
Add Kimi Global preset variants (kimi.ai platform + api endpoints) (#7526)
Add a "Kimi Global" twin immediately after the domestic "Kimi" API preset and a "Kimi For Coding Global" twin after the domestic "Kimi For Coding" preset, in every preset file that ships them (claude, claude-desktop, codex, hermes, openclaw, opencode, pi).
"Kimi Global" points to the international open platform https://platform.kimi.ai?aff=cc-switch (apiKeyUrl mirrored as https://platform.kimi.ai/console/api-keys?aff=cc-switch) and swaps the endpoint host api.moonshot.cn for api.moonshot.ai with each file's path unchanged (/v1 or /anthropic).
"Kimi For Coding Global" points to the international site https://www.kimi.ai/code/?aff=cc-switch and swaps the endpoint host api.kimi.com for api.kimi.ai with the path unchanged (e.g. https://api.kimi.ai/coding/v1).
Domestic presets are unchanged; category, icons, apiFormat, model catalogs, endpointCandidates (host swapped), partnerPromotionKey and settings otherwise mirror the domestic blocks exactly.
primePartner stays only on the domestic presets so the new variants do not alter prime-partner ordering.
The Pi twins get distinct providerKey values ("cc-switch-kimi-global", "cc-switch-kimi-for-coding-global") because providerKey is the preset-level primary key and must not collide with the domestic presets.
Rebalance sponsor CTAs around Kimi Code plan with dual-region links (#7522)
Kimi Code plan purchases have resumed, so the README sponsor sections go back to leading with the coding plan instead of the API-only flow written during the purchase pause.
All four locales (EN/ZH/JA/DE) now present Kimi Code plan (中文站 | Global) as the primary CTA and the Kimi Open Platform API (中文站 | Global, with the first-top-up bonus) as the secondary one, mirroring the domestic|global split already used for API links.
The aff and track_id values are unchanged.
fix(prompts): refresh active prompts after external file edits (#7194)
Reload the enabled prompt from its live file when the Prompts list loads, and refresh the open panel when the window regains focus, so external edits to CLAUDE.md / AGENTS.md show up without a restart.
- Keep saved templates available when the live file cannot be read.
- Skip the backfill for empty or whitespace-only files, matching enable_prompt.
- Skip the backfill while a sync/restore holds the sync lock, so a list load cannot write a stale live file into a freshly restored database.
fix(skills): raise archive entry limit so large skill repos install (#7489)
Installing hugohe3/ppt-master from skills.sh failed with
ARCHIVE_TOO_MANY_ENTRIES: the GitHub archive we download is the whole
repository (13,248 entries), and the 10,000-entry cap added in #5811
treated it as a zip bomb.
- Raise MAX_ARCHIVE_ENTRIES to 30,000 for both the remote archive and
local ZIP paths, and align WebDAV's MAX_EXTRACT_ENTRIES so a skills.zip
containing such a skill still restores.
- Charge every file entry at least one 4 KiB block against the existing
512 MiB budget, mirroring directory accounting, so empty-file archives
are bounded by the byte budget rather than by the entry count alone.
- Add tests for a 13k-entry repo archive and for the per-file minimum
charge.
Fixes: https://github.com/farion1231/cc-switch/issues/7475
feat: add MiniMax Code harness support (#7383)
Add MiniMax Code (mcode) as a managed application: provider and model management through the native custom_provider configuration, MCP servers, Skills, global instructions (AGENTS.md), read-only local session history with resume, and usage import from the native runtime database. Native files shared by the TUI and desktop are written under the native lock with rollback when the database commit fails, unrelated native configuration is preserved, and the data directory honors MINIMAX_DATA_DIR and MAVIS_DATA_DIR. Deleting sessions stays in the native application because they are coupled to runtime state.
fix(codex): recover stale account bindings during takeover (#7395)
After deleting a managed ChatGPT account and logging in again, a Codex provider can still reference the old local account ID, which made route switching, takeover, startup restoration and rebinding fail permanently.
Distinguish a missing outgoing account from an existing account with no matching live token. A missing account only releases its ownership marker after the persisted account store is verified; the target write policy stays in force. A stale target now reports an account-selection instruction instead of a bare not-found error, including when takeover is already enabled.
Fixes #7392
feat(claude-desktop): support 3P configuration on Linux (#7331)
Add Claude Desktop 3P configuration support on Linux. Resolve the config root from an absolute XDG_CONFIG_HOME, falling back to ~/.config; when CC Switch itself runs as a Flatpak, use the host ~/.config instead of the sandbox-private XDG_CONFIG_HOME. Update the en/zh/ja user manuals with Linux paths and the Flatpak boundary.
Fixes #4855
fix(proxy): omit missing tool descriptions instead of serializing null (#7319)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): keep reasoning effort for grok-4.6 and pass xhigh through verbatim (#7318)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(models): support Zhipu OpenAI Responses model lists (#7330)
Zhipu's Responses endpoint (open.bigmodel.cn/api/v1) serves its model list in the Codex remote catalog shape (models[].slug) instead of the OpenAI-compatible data[].id shape, so the Codex form's model fetch came back empty. The parser now falls back to models[].slug when data is absent.
fix(misc): fetch npm dist-tags from the dedicated endpoint with the probe timeout (#7346)
The tool version probe requested the full npm packument for each package (metadata for every historical version, 14-25 MB uncompressed for codex / opencode / openclaw) only to read its dist-tags, and it was the only latest-version probe without LATEST_PROBE_TIMEOUT, so it fell back to the proxy client's 600 s total timeout and the cards sat on "loading" for minutes on slow links while the large downloads starved the Hermes GitHub probe into "unknown".
Switch fetch_npm_dist_tags to registry.npmjs.org/-/package/{package}/dist-tags, whose body is the dist-tags object itself (tens of bytes to a few KB), escape scoped names as %2f, and apply the same 15 s probe timeout as the GitHub and PyPI probes. No data is lost: the only consumer reads latest and the tool's prerelease tags.
Fixes #7339
feat(usage): display output tokens per second in request log table and detail panel (#3369)
Show tokens/s next to the output token count in the request log table and the request detail panel.
The generation window prefers an explicit duration when present, otherwise latency minus first-token time for streaming requests, and falls back to full latency for non-streaming requests. Rows without a positive token count or window show nothing.
Closes #5936
fix(presets): update APIKEY.FUN URLs to apikey.fan
docs(release): add v3.20.3 release notes
chore(release): v3.20.3
fix(pricing): move retired DeepSeek V4 family to the V4.1 Flash tier
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.
fix(presets): declare third-party Token Plan DeepSeek V4 rows text-only
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.
feat(presets): rename DouBaoSeed preset to localized Volcengine Doubao
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.
fix(codex): skip unchanged incomplete rollout tails
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.
fix(claude): align quick model selection with form order
fix(tray): show bound ChatGPT account quota for managed Codex cards
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
feat(claude): add "Disable Artifact Tool" quick toggle to provider editor
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.
feat(codex): refresh aggregator model presets and catalogs
feat(codex): switch Kimi presets to native Responses direct-connect
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.
fix(usage): show Claude Fable weekly limit from the usage API limits array
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.
fix(codex): mirror vision-capable DeepSeek catalog and un-gate the legacy v4-flash alias (#7286)
Refresh the bundled DeepSeek official Codex catalog to the vendor's current models.json (slug deepseek-flash, image input modality, supports_image_detail_original) and remove deepseek-v4-flash from the confirmed text-only registry: the legacy id is still accepted by DeepSeek and routed to the vision-capable V4.1 Flash, so both the proxy media sanitizer and the generated Codex catalog must fail open for it. deepseek-v4-pro stays text-only per the vendor catalog.
Fixes #7283
fix(proxy): clamp sub-floor max_tokens to the Responses API minimum (#7287)
The Responses API rejects max_output_tokens below 16, but Anthropic clients legitimately send tiny probe budgets (Claude Desktop's model-availability probe uses max_tokens=1). The Anthropic→Responses conversion copied the value verbatim, so strict upstreams returned 400 and Claude Desktop reported the mapped model as unavailable under local routing.
Clamp 1..=15 up to 16 during conversion; 16+, 0 and non-integer values keep their pass-through semantics. Normal session requests convert byte-for-byte as before, and the Codex OAuth path still strips max_output_tokens afterwards.
Fixes #7103
fix(codex): coalesce adjacent commentary with pending tool calls (#7280)
The Codex Responses -> Chat Completions converter emitted two consecutive
assistant messages when one Responses model turn contained a commentary
message directly followed by function_call items. Chat upstreams imitate
the text-only assistant message as a complete turn and return
finish_reason=stop before the expected tool call, so long agent tasks end
after a progress update.
Merge pending tool calls into the directly adjacent assistant message when
it carries no tool_calls yet, attach pending reasoning per segment so
parallel calls do not duplicate it, and keep the new-message path for every
other boundary shape (user/tool boundaries, prior tool-call batches, media
flushes). The reasoning placeholder backfill still applies to coalesced
calls without reasoning.
Fixes #6529
Supersedes #6530
Co-authored-by: BigStrongSun <BigStrongSun@users.noreply.github.com>
fix(proxy): skip empty reasoning_content placeholders in Anthropic SSE streaming (#7227)
Some OpenAI-compatible upstreams (GLM, Qwen, DeepSeek-V4-Pro via ModelScope/DashScope,
Bailian, JD Cloud, ...) keep a `reasoning_content: ""` placeholder in every content
chunk instead of omitting the field. The reasoning branch of the OpenAI Chat -> Anthropic
SSE converter only checked for presence, and it shares `current_non_tool_block_type`
with the content branch, so every content chunk closed the open text block, opened an
empty thinking block, then reopened a text block. N content chunks became 2N content
blocks, half of them empty thinking blocks, which Claude Code rendered as one line per
token with dozens of empty Thought blocks and bloated the session JSONL.
Filter empty reasoning before entering the branch, matching the `!content.is_empty()`
guard the content branch already had and the non-streaming `openai_to_anthropic`
converter. Two regression tests pin block index, type and order: the placeholder shape
must yield a single text block and no thinking_delta at all, and real reasoning must
still yield one thinking block followed by one text block.
Fixes #5028
Fixes #4404
fix(codex): detect growing rollouts with a persisted byte cursor (#7219)
Codex keeps the rollout append handle open for the whole session, so on Windows NTFS the file mtime does not advance while the file grows and the pure-mtime gate skipped growing rollouts forever. Persist the observed byte length in session_log_sync.last_byte_offset for Codex rows and skip a file only when both mtime and size are unchanged. The parser now splits records on newlines manually so an incomplete trailing record is retried on the next pass without advancing the line cursor.
Fixes #6060
fix(codex): proxy image edits and Images API follow-ups (#7177)
* fix(codex): proxy image edits and Images API follow-ups
Follow-ups to #7036 for Codex's built-in ImageGen over the local proxy:
- Register `/images/edits` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them through the shared Codex standalone passthrough.
ImageGen posts to `images/edits` whenever a call references existing
images, and the router returned an empty 404 for it.
- Add `CodexStandaloneEndpoint::ImagesEdits`. Both Images endpoints share
one full-URL suffix table, so a complete Responses / Compact / Chat
Completions / Images URL derives the sibling endpoint (and vice versa)
while opaque full URLs keep failing closed.
- Apply the standalone sibling rewrite when the base URL is pasted as a
complete endpoint with the full-URL switch off, mirroring
`codex_chat_base_is_full_endpoint`; standalone requests no longer become
`.../chat/completions/images/generations`.
- Parse Images API streaming usage: `image_generation.completed` carries
`usage` on the event top level, so scan for `usage.input_tokens` before
falling back to the Chat Completions stream parser.
Related #7036
* fix(codex): match standalone URL suffixes case-insensitively
Keep endpoint detection and sibling rewriting consistent while preserving the original URL prefix and query. Cover mixed-case Images and Alpha Search endpoints through URL and proxy regression cases.
---------
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): preserve per-app settings on shutdown and port allocation (#7210)
On normal exit, stop_with_restore_keep_state() called the legacy global
proxy-config writer to clear the long-retired live_takeover_active flag.
That writer reads the Claude row and runs an UPDATE without a WHERE clause,
so every shutdown copied Claude's max_retries and the three timeout fields
onto the Codex, Gemini and Grok Build rows — and the UPDATE never carried
live_takeover_active in the first place, so the step had no effect beyond
the clobber. The ephemeral listen-port path did the same on every proxy
start.
Drop the dead write-back (live-config restore, backup cleanup and health
reset are unchanged) and persist the resolved ephemeral port through the
global-config interface, which only touches the shared listen/logging
columns. Two regression tests seed distinct per-app settings for all four
apps and assert they survive both paths.
Fixes #7204
fix(provider): preserve child metadata during universal sync (#7212)
Syncing a universal provider regenerates its Claude/Codex/Gemini child
providers. The child's `settings_config` was merged with the existing row,
but `meta`, `created_at` and `sort_index` were taken from the generated
object, and the universal provider never carries any of them from the UI.
The UPDATE therefore wiped the child's usage script, common-config opt-out,
endpoint auto-select and other per-app settings to `{}` and reset the sort
index to NULL, which pushed the card to the bottom of the list and looked
like the provider had been deleted and re-created.
Keep the existing child's `meta`, `created_at` and `sort_index` when the
row already exists. Name, base URL, key, models, website and notes are
still driven by the universal provider, and first-time creation still
inherits the parent's metadata.
Fixes #7134
feat(presets): rebrand DashScope as 千问AI平台 and refresh Qwen models (#7183)
Rename the domestic Bailian presets to 千问AI平台 with a dedicated icon, refresh the Qwen family to the 3.8 generation across Claude Code, Claude Desktop, Codex, Hermes, OpenClaw, OpenCode and Pi, add the domestic Token Plan presets, restore the international QwenCloud Coding Plan presets, and align OpenClaw/OpenCode/Pi model metadata with the official client docs.
fix(codex): honor proxy URL when model_provider is omitted (#7263)
When a Codex provider card's TOML has no `model_provider`, Codex falls back to
its built-in `openai` provider, but takeover wrote the proxy address as a
top-level `base_url` that Codex never reads, so requests bypassed the local
proxy and went straight to api.openai.com.
Treat the missing selector as the built-in `openai` provider and write the
proxy address to `openai_base_url`; the existing legacy-reroute migration then
turns it into a `cc-switch` custom provider table carrying the `PROXY_MANAGED`
bearer, the same shape every other third-party takeover uses. Explicit
provider selections are unchanged.
Refs #6256, #7217
fix(provider): update MiniMax defaults to M3 and remove expired offers (#7255)
* fix(provider): refresh MiniMax M3 presets and remove expired promotions
* fix(provider): complete MiniMax M3 capability declarations
---------
Co-authored-by: saladday <1203511142@qq.com>
chore(provider): remove Atlas Cloud sponsor status, keep presets
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`
docs(release): add v3.20.2 release notes
chore(release): v3.20.2
feat(provider): add SoleAPI sponsor presets across all nine apps
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.
fix(codex): proxy image edits endpoint
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.
fix(provider): pin models URL for JieKou and Novita Claude presets
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.
feat(pricing): refresh seed for September 2026 vendor price changes
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.
fix(codex): stamp requires_openai_auth on takeover writes to match the live login state
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.
fix(updater): show the real reason when an update check fails (#6482)
The updater plugin rejects with a plain string, so `err instanceof Error`
never matched and the real failure reason was replaced by the generic
fallback message. Use the existing extractErrorMessage helper, and pass
the recovered message into the check-update failure toast so it actually
reaches the user instead of stopping at an unread context state.
Co-authored-by: Jason <farion1231@gmail.com>
docs: fix the locales path in the README directory trees (#6100)
All four READMEs showed locales/ as a direct child of src/, but the
translations live at src/i18n/locales/. Split it into two tree nodes so it
matches how the rest of the tree is drawn. Also fixes a CLaude -> Claude
typo in README_ZH.md.
Co-authored-by: Jason <farion1231@gmail.com>
fix(claude): exclude workflow journal.jsonl from session list
Claude Code's workflow feature writes subagents/workflows/wf_*/journal.jsonl inside the session directory. The Claude session scanner only excluded agent-* files, so each journal parsed as an empty ghost session titled "journal". Exclude journal.jsonl by file name and add regression tests.
Fixes #6042
fix(i18n): add the missing pi.form.providerKeyDuplicate and common.collapse keys
translatePiProviderMutationError returns pi.form.providerKeyDuplicate for the Pi backend's duplicate-key error, but the key existed in none of the four locale files, so the toast showed the raw key. common.collapse (aria-label of the thinking-map toggle) was missing the same way. Add both keys to zh/en/ja/zh-TW and lock them in the locale coverage test.
fix(proxy): stop mask_url panicking on multi-byte UTF-8 near the truncation boundary
mask_url's fallback branch (used when the proxy URL fails Url::parse) sliced the raw string at byte 20 and panicked when a multi-byte UTF-8 character straddled that offset, e.g. a CJK string pasted into the global proxy address. Snap the cut to the nearest char boundary at or before 20 and add a regression test.
fix(tests): isolate LOCALAPPDATA so tests stop writing the real Claude Desktop config
On Windows, Claude Desktop config paths resolve from LOCALAPPDATA, which the test home override in tests/support.rs did not redirect, so profile_roundtrip wrote the https://desktop.test fixture into the developer's real Claude Desktop config. Point LOCALAPPDATA at the isolated test home alongside HOME and USERPROFILE.
Fixes #6077
fix(usage): widen pricing model source select to fit localized labels
The pricing model source SelectTrigger in the usage cost settings table was w-28, which leaves about 70px for the label; the English and Japanese labels need about 107px and overflowed the trigger. Widen it to w-40 and drop the h-7 overrides so the trigger and the adjacent Input use the shared h-9 height.
feat(pi): add Tencent TokenHub and Token Plan presets (#7159)
Add eight Tencent presets for Pi, covering the TokenHub pay-as-you-go line
(domestic and international) and the six Token Plan subscription products —
Pi was the only managed app still missing them. Model IDs, ordering and
endpoints follow Tencent's own model lists verbatim, and the tests assert each
product's line-up in order so future drift is caught.
Declare thinking control alongside the capabilities rather than leaving it to
defaults: the 31 DeepSeek entries carry a Tencent-specific compat so selecting
"off" actually disables thinking instead of silently billing reasoning tokens,
the eight Kimi K2.7 Code entries no longer offer an "off" level the model
cannot honour, and kimi-k2.7-code-highspeed is marked image-capable so
tool-returned screenshots are no longer dropped.
Drop minimax-m2.5 from every app: it is gone from Tencent's model tables and
the platform is retiring it.
fix(codex): proxy image generations endpoint (#7036)
Codex's built-in ImageGen calls the legacy OpenAI Images API, but the local
proxy only registered Responses / Chat Completions / Compact / Alpha Search
routes, so `/v1/images/generations` returned an empty 404 under Codex routing.
- Register `/images/generations` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them as a Codex standalone passthrough shared with Alpha Search.
- Generalize the Alpha Search full-URL rewrite into
`rewrite_codex_standalone_full_url`: derive the sibling
`/images/generations` from full Responses / Compact / Chat Completions URLs,
keep an existing Images URL as-is, and fail closed on opaque full URLs.
- Record `usage.input_tokens` / `output_tokens` through the Codex usage parser.
Fixes #5429
Fixes #6745
feat(pricing): add Gemini 3.8 Flash pricing (#7164)
Seeds the built-in pricing row for Gemini 3.8 Flash at 0.75 / 3.75 input/output per million tokens, 0.075 cache read, no cache-write charge, plus a regression test locking all four fields.
Values verified against Google's own pricing page (introductory price through 2026-12-31; list price 1.50 / 7.50 / 0.15 from 2027-01-01) and models.dev.
feat(provider): extend PPIO presets for Pi (#6870)
Add a PPIO provider preset for Pi using the OpenAI-compatible endpoint,
and pin an explicit models URL on the Claude Code PPIO preset so model
discovery resolves against https://api.ppio.com/openai/v1/models instead
of the derived candidates, which all return 404.
Co-authored-by: Jason <farion1231@gmail.com>
feat(pricing): add GLM-5.3 Flash pricing (#7163)
fix(proxy): enable parallel tool calls for Codex OAuth (#7024)
Codex OAuth requests defaulted parallel_tool_calls to false when the incoming
Anthropic request carried no explicit value, forcing Claude Code into one tool
call per model turn. Default it to true (matching codex-rs), map Anthropic
tool_choice.disable_parallel_tool_use to the inverse Responses value so explicit
serial execution is preserved, and add regression coverage for both directions.
Carries #5722 forward on top of current main.
Fixes #5719
Co-authored-by: 太一 <taiyi@douyu.tv>
feat(pricing): add GPT-6 Astra pricing (#7162)
fix(codex): resolve input_modalities for unknown models in vendor catalog (#6750)
When a model slug is not in the bundled DeepSeek official catalog, the vendor path clones the flagship entry and previously inherited its text-only input_modalities. Resolve modalities for unmatched slugs through the same registry/fail-open logic the non-vendor path uses, so a vision variant such as deepseek-v4-flash-vision-exp is no longer declared text-only. Explicit user inputModalities still win; matched models keep the vendor's declaration verbatim.
Fixes #6725
fix(claude): expose Opus 5 and Sonnet 5 in takeover mode (#5882)
Bump the stable client-facing role aliases that takeover mode writes to
Claude Code: Opus claude-opus-4-8 -> claude-opus-5 and Sonnet
claude-sonnet-4-6 -> claude-sonnet-5, so the /model menu matches the
Claude Desktop default routes and preset defaults that already moved to
Opus 5 / Sonnet 5. Add "opus-5" to the adaptive-thinking classifier so
the Bedrock thinking optimizer, which runs on the client alias before
model mapping, keeps sending adaptive thinking instead of the removed
budget_tokens.
Routing is unaffected: the proxy maps client aliases by role keyword, and
the aliases are rewritten automatically the next time the proxy starts.
Fixes #5876
Co-authored-by: Jason <farion1231@gmail.com>
feat(pricing): add GLM-5.3 to built-in model pricing table (#6591)
Seed glm-5.3 at $1.4/$4.4 per MTok with $0.26 cache read, matching the
official Z.ai list price (identical to the GLM-5.2 tier):
https://docs.z.ai/guides/overview/pricing
New model id, so seed only; no repair entry and no SCHEMA_VERSION bump.
Existing installs pick the row up on next launch via INSERT OR IGNORE, and
the startup cost backfill re-prices historical glm-5.3 rows recorded at $0.
fix(codex): align OAuth client identity for GPT-6 (#7132)
Requests for gpt-6-astra through the Claude-to-Codex OAuth takeover route advertised Codex 0.144.1, below the model's 0.153.0 minimal_client_version, so ChatGPT's backend rejected them with HTTP 400 "requires a newer version of Codex". Bump the advertised client version to 0.153.4 and share the originator/version constants between generation and model discovery, which previously sent cc-switch's own package version and originator.
Two regression tests pin the generation headers and the model-list request to a version at or above the gpt-6-astra floor.
Refs #7129
fix(codex): disable supports_search_tool for DeepSeek catalog models (#6653)
DeepSeek's Responses API has no tool_search support, but the bundled
official catalog declared supports_search_tool=true for deepseek-v4-pro
and deepseek-v4-flash. Codex gates deferred/namespaced MCP tools on
this flag (search_tool_enabled = supports_search_tool &&
namespace_tools), so MCP tools were hidden behind a tool_search the
gateway cannot serve — no MCP tool could ever be called.
Set the flag to false for both models so Codex lists MCP tools inline.
Hosted web_search is unaffected (gated by provider capabilities, not
this flag) and DeepSeek's server-side web_search still works.
Fixes #6647
fix(codex): move $ref siblings into allOf for Moonshot/Kimi chat upstreams (#6863)
Moonshot's Chat Completions validator (api.moonshot.cn, api.moonshot.ai and the Kimi For Coding endpoint api.kimi.com) rejects a `$ref` that carries sibling keywords. Codex Desktop's built-in tool schemas carry exactly that shape, so every desktop turn rou…
README 从上游完整功能介绍改为仅记录 fork 与上游的差别,完整功能
文档指回上游。涵盖 8 项 fork 魔改:构建标识/开发预览、预设精简、
Claude 表单调整、模型搜索移植、主页模型快速切换、侧边栏面板显隐、
关闭应用内更新、打包范围,并说明版本号与 rebase 同步流程。
feat: disable in-app updater, point users to GitHub releases
移除 tauri-plugin-updater 依赖与自动更新链路,改为统一引导用户到
GitHub Releases 手动下载新版。
- 前端:updater.ts 恒返回 up-to-date;UpdateContext 取消启动自检;
AboutSection「检查更新」直接打开 fork releases 页;DatabaseUpgrade
恢复界面去掉下载进度条,改为打开发布页。
- 后端:移除 updater 插件注册与 UpdaterExt 调用;
install_update_and_restart 降级为空壳,check_app_update_available
返回当前版本号;check_for_updates 指向 fork releases;capabilities
去掉 updater:default 权限。
- 打包:tauri.conf.json 删 plugins.updater、createUpdaterArtifacts=false;
Cargo.toml / package.json 删 updater 依赖,Cargo.lock / pnpm-lock
同步重生成。
- CI:release.yml 移除签名收集与 assemble-latest-json job,删除
.tar.gz/.sig 处理,release body 去掉 updater 说明。
版本号统一为 3.20.0-1(合法 semver 且 MSI 第 4 位为 1)。
Cargo.lock / pnpm-lock.yaml 已随依赖移除重生成,cargo check 通过。
ci: build only Windows x64 and macOS arm64, unsigned
feat: gate dev panel behind dev-only env flag
chore(comet): archive ccs-sidebar-panels-visibility change
fix: apply skills visibility guard to hermes branch toolbar button
feat: include prompts in sidebar panel visibility settings
fix: persist visibleSidebarPanels setting in backend AppSettings
style: use compact pill toggles for sidebar panel visibility
feat: add sidebar panel visibility switches to settings
feat: add sidebar panel visibility settings and wire into toolbar buttons
chore(comet): archive ccs-home-model-display change
fix(fork): read grokbuild api key from config TOML in quick switch dialog
feat(fork): align model badge with upstream extractModelBadge
feat(fork): show current model badge and quick-switch entry on provider cards
feat(fork): add model quick switch dialog
feat(fork): add provider model read/write utils with tests
chore(comet): archive ccs-model-search-port change
feat(i18n): add model search picker keys for all locales
feat(form): port SearchableModelPicker from upstream for searchable model selection
chore(comet): archive ccs-claude-form-layout change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-claude-form-layout/
- Sync claude-form-layout spec to main specs
- Annotate design doc as archived
- Add verification report
fix(i18n): update advanced options hint, add ja/zh-TW toggle-all, drop dead apiFormatHint key
fix(form): correct 1M toggle-all alignment direction (ml-1)
fix(form): align 1M toggle-all checkbox with row checkboxes
refactor(form): move api format select into EndpointField rightSlot
fix(form): widen api format select to fit option text
feat(form): add 1M toggle-all checkbox in model mapping header
feat(form): move api format select next to endpoint input
chore(comet): archive ccs-provider-presets-trim change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-provider-presets-trim/
- Sync provider-presets-visibility spec to main specs
- Annotate design doc as archived
- Add verification report
docs(fork): add presets-trim files to rebase whitelist
test(fork): mock IS_FORK_BUILD false in unit tests for upstream-form semantics
feat(fork): wire official preset filter into provider forms
feat(fork): add hidden field to preset interfaces
feat(fork): add official preset allowlist and filter util
chore(comet): archive ccs-fork-scaffolding change
- Move change artifacts to docs/openspec/changes/archive/2026-08-18-ccs-fork-scaffolding/
- Sync fork-build-identity spec to main specs
- Annotate design doc as archived
- Add verification report
feat(fork): add dev config with isolated identifier and config dir
docs(fork): complete rebase whitelist with all fork-touched files
fix(fork): guard setTitle with isTauri in test/non-tauri env
fix(fork): define __CCS_FORK_BUILD__ in vitest config
feat(fork): add DevPanel and settings footer entry
feat(fork): set macOS window title to CC Switch (Fork) on startup
feat(fork): inject __CCS_FORK_BUILD__ compile-time constant
feat(fork): bump version to 3.19.2-fork.1 and rename product to CC Switch (Fork)
docs(fork): add upstream rebase workflow and ignore tool artifacts
fix(proxy): accept the whole grok-4.x (x>=5) family in the reasoning-effort whitelist (#7369)
Replace the verbatim grok-4.5 / grok-4.6 entries in supports_reasoning_effort with a rule that parses the grok-4.x minor version and accepts x >= 5, mirroring the existing GPT-5+ rule. This covers grok-4.7 (released 2026-09-21), whose reasoning effort was previously dropped on the Claude -> Chat, Claude -> Responses and Codex Responses -> Chat conversion paths, and lets future releases pass without another whitelist edit. The grok-build-* family is retained for saved providers.
Co-authored-by: allenxu09 <171831965+allenxu09@users.noreply.github.com>
docs(release): add v3.20.4 release notes
chore(release): v3.20.4
feat(pricing): add Grok 4.7 and GLM-5.3-FlashX
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.
fix(i18n): remove outdated OpenCode Go referral offer
fix: add SudoCode.chat alternate API endpoint
feat(settings): add a GitHub star prompt to the About card
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
feat(presets): add Soshow aggregator presets
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.
fix(ci): stabilize frontend tests and verify renderer build
fix(mcode): refresh provider state after removal (#7578)
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
fix(lib): reset skip_taskbar before window.show() across entry points (#6348)
5处 window.show() 调用中 4处没有在 show() 前重置 Windows 的 skip_taskbar,导致以下场景出现窗口可见但不在任务栏、点最小化后窗口消失的 bug:
- single_instance handler (lib.rs:360-361):当原进程因 silent_startup / 关闭到托盘等处于 skip_taskbar(true) 状态时,从快捷方式/开始菜单唤起已运行实例触发。
- handle_deeplink_url 的 focus_main_window 分支 (lib.rs:256):通过 ccswitch:// URL 唤起时。
- DB 恢复流程 (lib.rs:556):数据库版本不匹配时显示恢复界面。
- RunEvent::Opened 处理器 (lib.rs:1803):系统接收到 deep-link URL 时。
修复后的行为与 tray.rs::handle_tray_menu_event::show_main 分支一致(那里已有相同的 set_skip_taskbar(false) 调用)。
经用户测试验证:autostart + silent_startup + 从开始菜单/快捷方式启动的场景下,最小化按钮恢复正常工作。
fix(proxy): omit missing tool descriptions in the Codex responses-to-chat converter (#7378)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): preserve max effort for GPT-5.6 and GPT-6 Astra (#7531)
fix(misc): ignore shell startup output when probing tool versions in WSL (#7348)
try_get_version_wsl runs "${SHELL:-sh}" -lic '<tool> --version' through
wsl.exe and takes the first x.y.z anywhere in stdout. A login+interactive
shell runs the distro's startup files first, so their output shares that
stdout. On Ubuntu, /etc/profile.d/update-motd.sh prints the MOTD once a day,
starting with "Welcome to Ubuntu 24.04.4 LTS", and the card then showed
24.04.4 as the Claude Code version. The once-a-day stamp makes it appear
after the first probe of the day and vanish on refresh.
The payload now prints a sentinel before running the tool, and parsing looks
only at what follows its last occurrence. The last occurrence handles the
-lic || -lc || -c fallback chain printing it several times; matching it as a
substring handles OSC sequences glued in front of it. If the sentinel is
absent (the shell failed before the payload ran), the output is used as
before. The stderr fallback for tools that print their version there is kept.
Fixes #7347
Co-authored-by: lyfuci <lyfuci11@gmail.com>
fix(claude): hide session URL attribution (#7053)
* fix(claude): hide session URL attribution
* docs(claude): document session URL attribution
fix(ui): reset provider scroll when switching apps (#5211)
fix(pi): fix provider logo (#6826)
fix(i18n): use the Pi form's own term for the zh-TW duplicate-key message (#7187)
Follow-up to the nit on #6768. In zh-TW the Pi form labels the field
供應商識別碼 and says 供應商識別碼為必填 when it is empty, but the
duplicate message I added said 供應商標識 — the term Hermes, OpenCode and
OpenClaw use for their own field. One form, two names for the same thing.
The other three locales already keep the label and both messages on one
term, so this makes zh-TW match the pattern rather than introduce one:
en Provider key / Provider key is required / This provider key already exists
ja プロバイダーキー / プロバイダーキーを入力してください / このプロバイダーキーは既に存在します
zh 供应商标识 / 供应商标识不能为空 / 该供应商标识已存在
zh-TW 供應商識別碼 / 供應商識別碼為必填 / 該供應商標識已存在 <- the odd one out
hermes.form.providerKeyDuplicate carries the same sentence and is left
alone: its own label is 供應商標識, so it is already consistent.
Verified: managementListLocales 7/7. Full unit suite 1085/1087, the same
two App.test.tsx integration cases failing before and after.
fix(proxy): strip stop param for GitHub Copilot to unblock auto mode classifier (#5404)
Claude Code's auto mode classifier sends `stop_sequences` on every
safety check. On the GitHub Copilot path the Anthropic -> OpenAI Chat
conversion mapped it to `stop`, and Copilot's Chat Completions route
for Claude models answers that request with HTTP 400, so every
classifier call failed while normal chat kept working.
- Remove the converted `stop` field only when the provider is GitHub
Copilot; other OpenAI-compatible upstreams keep it.
- Copilot detection in the transform now also covers providers that
are recognised only through `env.ANTHROPIC_BASE_URL`.
- Regression tests for the Copilot (meta and env URL) and generic
provider cases.
Fixes #5175
fix(codex): keep stored auth when the live snapshot has no credential (#7434)
Editing the current Codex provider seeds the form from the live ~/.codex
snapshot, and switching away backfills that snapshot into the DB row. When a
provider table declares its own credential source (an Authorization header,
env_key, auth/aws), no bearer token is injected into config.toml and the
shared auth.json is deleted on third-party switches, so the live snapshot is
permanently `{ auth: {}, config }` while auth.OPENAI_API_KEY in the DB is the
only copy of the key. Both paths treated that empty live auth as
authoritative and silently erased the stored key, which in turn made the
usage-script modal misread the provider as an official subscription and
reset the saved script.
- Treat a credential-less live auth (missing auth.json or the bare
auth_mode logout marker) as an absent field in both the edit dialog and
the switch-away backfill, keeping the stored auth template. Live still
wins whenever it carries material, and official providers keep the live
login as their source of truth.
- Gate the official exemption on is_codex_official_provider /
resolveCodexOfficialIdentity rather than the category alone: a legacy
fixed official card without a persisted category is still written as
official, so the fallback must not resurrect a logged-out login on
switch-back.
- Cover both layers with regression tests, including the category-less
official shapes.
Fixes #7433
Supersedes #7492
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): lift additional_tools input carriers on the Codex chat conversion (#7454)
Codex talks to third-party providers over its Responses Lite transport,
which ships the request's extra tools in an `additional_tools` input
carrier: `{"type":"additional_tools","role":"developer","tools":[...]}`,
a role but no `content`. The Responses->Chat conversion let that carrier
fall through the generic "item with a role is a message" arm and emitted
`{"role":"system","content":null}` at messages[1], which strict Chat
gateways reject with `messages[1]: missing field "content"` (or
`data did not match any variant of ...SystemMessageContent`). The tools
carried inside it were also dropped on the Chat and Anthropic conversions.
- Skip `additional_tools` items in the Chat message walk; a carrier
declares tools, it is not a message.
- Collect carrier tools through the existing CodexToolContext registry
(dedup, namespace flattening, custom-tool conversion), which also feeds
the Anthropic conversion and the response-side name round-trip.
- Requests without a carrier convert byte-for-byte as before.
Known boundary: `custom` children inside a namespace tool
(`functions.exec`) are still not lifted; that needs its own response-side
name round-trip and is left for a follow-up.
Diagnosed independently in #6158 by BigStrongSun (fix in #6159) and in
fixture.
Fixes #7451
Fixes #6158
Refs #6033
Refs #6776
Supersedes #6159
fix(proxy): normalize Codex detail:original images for Chat gateways (#7476)
Codex marks tool-returned images with detail:"original" whenever the model
catalog advertises supports_image_detail_original, a Responses-only value.
The Responses->Chat conversion copied it verbatim into image_url.detail, and
strict OpenAI-compatible Chat gateways (Ark/Doubao, SenseNova, GLM, vLLM, ...)
reject the whole request with 400. Because Codex replays the full rollout on
every turn, one poisoned view_image result made the session fail permanently,
including plain-text turns.
Two layers:
- tool_media::merge_top_level_detail downgrades original -> auto, so already
poisoned rollouts and Codex versions before 0.155.0 (which do not normalize
replayed history themselves) recover immediately. auto/low/high are untouched.
- codex_catalog_model_entry always writes supports_image_detail_original:false
on the ProxyChat profile, so Codex stops emitting original at the source once
the catalog is regenerated. NativeResponses/Anthropic profiles and the DeepSeek
official catalog are unaffected.
Top-level user images were never affected: responses_content_to_chat_content
drops the part-level detail entirely.
Fixes #6495
Fixes #7458
Refs #6569
Refs #5805
fix(omo): detect unified config on the WSL side when the OpenCode dir is in WSL (#7550)
When the OpenCode config directory points into WSL
(\\wsl.localhost\<distro>\home\<user>\.config\opencode), OMO >= 4.19.3 keeps
its unified config in the WSL-side home (~/.omo/omo.jsonc). Detection only
probed the Windows home, never found it, and silently fell back to the legacy
per-plugin file that OMO no longer reads, so provider switches looked
successful but had no effect.
- Add derive_wsl_home_dir to derive the WSL-side home from a WSL UNC path
(home/<user> or root); non-WSL and non-home paths return None.
- Probe a list of home candidates for the unified config, WSL-side home
first and the OS home as a fallback, shared by the read, write and delete
paths.
- Detection only: when no unified config exists on either side the legacy
fallback is unchanged.
Fixes #7363
Supersedes #7367
docs(partner): add a FluxA purchase link and tighten its sponsor blurb
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.
fix(claude-desktop): expose the 1M context variant for DeepSeek V4 routes
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.
feat(presets): add FluxA Token Plan partner presets
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.
fix(presets): correct AICodeWith Codex-family endpoint to /v1
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.
fix(ui): render monochrome preset icons in the foreground color
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.
docs(user-manual): point Chat-only Codex examples at plan endpoints
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.
fix(pricing): restore DeepSeek V4 Pro price and seed Qwen3.8 and Hy4 models
- 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).
fix(presets): move MiniMax CN and BaiLing to their current official endpoints
- 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.
feat(codex): refresh CN presets from the Responses API audit
- 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.
test(claude-desktop): run Linux path tests on unix hosts and cover Linux in provider sync tests
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.
fix(ui): stop expanding stale cached usage tiers on ineligible provider cards
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.
fix(skills): 支持 skillId 与目录名不一致的技能安装和更新 (#6381)
* fix(skills): resolve mismatched skillId directories
* fix(skills): preserve source path during updates
* fix(skills): refresh source URL after repository relocation
* test(skills): isolate relocation fixture from legacy home
feat(opencode): 支持从获取的模型列表中搜索并批量添加所选模型 (#7515)
* feat: implement fetched models selection and UI components
* fix(opencode): prevent model search from submitting provider form
Add Kimi Global preset variants (kimi.ai platform + api endpoints) (#7526)
Add a "Kimi Global" twin immediately after the domestic "Kimi" API preset and a "Kimi For Coding Global" twin after the domestic "Kimi For Coding" preset, in every preset file that ships them (claude, claude-desktop, codex, hermes, openclaw, opencode, pi).
"Kimi Global" points to the international open platform https://platform.kimi.ai?aff=cc-switch (apiKeyUrl mirrored as https://platform.kimi.ai/console/api-keys?aff=cc-switch) and swaps the endpoint host api.moonshot.cn for api.moonshot.ai with each file's path unchanged (/v1 or /anthropic).
"Kimi For Coding Global" points to the international site https://www.kimi.ai/code/?aff=cc-switch and swaps the endpoint host api.kimi.com for api.kimi.ai with the path unchanged (e.g. https://api.kimi.ai/coding/v1).
Domestic presets are unchanged; category, icons, apiFormat, model catalogs, endpointCandidates (host swapped), partnerPromotionKey and settings otherwise mirror the domestic blocks exactly.
primePartner stays only on the domestic presets so the new variants do not alter prime-partner ordering.
The Pi twins get distinct providerKey values ("cc-switch-kimi-global", "cc-switch-kimi-for-coding-global") because providerKey is the preset-level primary key and must not collide with the domestic presets.
Rebalance sponsor CTAs around Kimi Code plan with dual-region links (#7522)
Kimi Code plan purchases have resumed, so the README sponsor sections go back to leading with the coding plan instead of the API-only flow written during the purchase pause.
All four locales (EN/ZH/JA/DE) now present Kimi Code plan (中文站 | Global) as the primary CTA and the Kimi Open Platform API (中文站 | Global, with the first-top-up bonus) as the secondary one, mirroring the domestic|global split already used for API links.
The aff and track_id values are unchanged.
fix(prompts): refresh active prompts after external file edits (#7194)
Reload the enabled prompt from its live file when the Prompts list loads, and refresh the open panel when the window regains focus, so external edits to CLAUDE.md / AGENTS.md show up without a restart.
- Keep saved templates available when the live file cannot be read.
- Skip the backfill for empty or whitespace-only files, matching enable_prompt.
- Skip the backfill while a sync/restore holds the sync lock, so a list load cannot write a stale live file into a freshly restored database.
fix(skills): raise archive entry limit so large skill repos install (#7489)
Installing hugohe3/ppt-master from skills.sh failed with
ARCHIVE_TOO_MANY_ENTRIES: the GitHub archive we download is the whole
repository (13,248 entries), and the 10,000-entry cap added in #5811
treated it as a zip bomb.
- Raise MAX_ARCHIVE_ENTRIES to 30,000 for both the remote archive and
local ZIP paths, and align WebDAV's MAX_EXTRACT_ENTRIES so a skills.zip
containing such a skill still restores.
- Charge every file entry at least one 4 KiB block against the existing
512 MiB budget, mirroring directory accounting, so empty-file archives
are bounded by the byte budget rather than by the entry count alone.
- Add tests for a 13k-entry repo archive and for the per-file minimum
charge.
Fixes: https://github.com/farion1231/cc-switch/issues/7475
feat: add MiniMax Code harness support (#7383)
Add MiniMax Code (mcode) as a managed application: provider and model management through the native custom_provider configuration, MCP servers, Skills, global instructions (AGENTS.md), read-only local session history with resume, and usage import from the native runtime database. Native files shared by the TUI and desktop are written under the native lock with rollback when the database commit fails, unrelated native configuration is preserved, and the data directory honors MINIMAX_DATA_DIR and MAVIS_DATA_DIR. Deleting sessions stays in the native application because they are coupled to runtime state.
fix(codex): recover stale account bindings during takeover (#7395)
After deleting a managed ChatGPT account and logging in again, a Codex provider can still reference the old local account ID, which made route switching, takeover, startup restoration and rebinding fail permanently.
Distinguish a missing outgoing account from an existing account with no matching live token. A missing account only releases its ownership marker after the persisted account store is verified; the target write policy stays in force. A stale target now reports an account-selection instruction instead of a bare not-found error, including when takeover is already enabled.
Fixes #7392
feat(claude-desktop): support 3P configuration on Linux (#7331)
Add Claude Desktop 3P configuration support on Linux. Resolve the config root from an absolute XDG_CONFIG_HOME, falling back to ~/.config; when CC Switch itself runs as a Flatpak, use the host ~/.config instead of the sandbox-private XDG_CONFIG_HOME. Update the en/zh/ja user manuals with Linux paths and the Flatpak boundary.
Fixes #4855
fix(proxy): omit missing tool descriptions instead of serializing null (#7319)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): keep reasoning effort for grok-4.6 and pass xhigh through verbatim (#7318)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(models): support Zhipu OpenAI Responses model lists (#7330)
Zhipu's Responses endpoint (open.bigmodel.cn/api/v1) serves its model list in the Codex remote catalog shape (models[].slug) instead of the OpenAI-compatible data[].id shape, so the Codex form's model fetch came back empty. The parser now falls back to models[].slug when data is absent.
fix(misc): fetch npm dist-tags from the dedicated endpoint with the probe timeout (#7346)
The tool version probe requested the full npm packument for each package (metadata for every historical version, 14-25 MB uncompressed for codex / opencode / openclaw) only to read its dist-tags, and it was the only latest-version probe without LATEST_PROBE_TIMEOUT, so it fell back to the proxy client's 600 s total timeout and the cards sat on "loading" for minutes on slow links while the large downloads starved the Hermes GitHub probe into "unknown".
Switch fetch_npm_dist_tags to registry.npmjs.org/-/package/{package}/dist-tags, whose body is the dist-tags object itself (tens of bytes to a few KB), escape scoped names as %2f, and apply the same 15 s probe timeout as the GitHub and PyPI probes. No data is lost: the only consumer reads latest and the tool's prerelease tags.
Fixes #7339
feat(usage): display output tokens per second in request log table and detail panel (#3369)
Show tokens/s next to the output token count in the request log table and the request detail panel.
The generation window prefers an explicit duration when present, otherwise latency minus first-token time for streaming requests, and falls back to full latency for non-streaming requests. Rows without a positive token count or window show nothing.
Closes #5936
fix(presets): update APIKEY.FUN URLs to apikey.fan
docs(release): add v3.20.3 release notes
chore(release): v3.20.3
fix(pricing): move retired DeepSeek V4 family to the V4.1 Flash tier
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.
fix(presets): declare third-party Token Plan DeepSeek V4 rows text-only
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.
feat(presets): rename DouBaoSeed preset to localized Volcengine Doubao
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.
fix(codex): skip unchanged incomplete rollout tails
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.
fix(claude): align quick model selection with form order
fix(tray): show bound ChatGPT account quota for managed Codex cards
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
feat(claude): add "Disable Artifact Tool" quick toggle to provider editor
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.
feat(codex): refresh aggregator model presets and catalogs
feat(codex): switch Kimi presets to native Responses direct-connect
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.
fix(usage): show Claude Fable weekly limit from the usage API limits array
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.
fix(codex): mirror vision-capable DeepSeek catalog and un-gate the legacy v4-flash alias (#7286)
Refresh the bundled DeepSeek official Codex catalog to the vendor's current models.json (slug deepseek-flash, image input modality, supports_image_detail_original) and remove deepseek-v4-flash from the confirmed text-only registry: the legacy id is still accepted by DeepSeek and routed to the vision-capable V4.1 Flash, so both the proxy media sanitizer and the generated Codex catalog must fail open for it. deepseek-v4-pro stays text-only per the vendor catalog.
Fixes #7283
fix(proxy): clamp sub-floor max_tokens to the Responses API minimum (#7287)
The Responses API rejects max_output_tokens below 16, but Anthropic clients legitimately send tiny probe budgets (Claude Desktop's model-availability probe uses max_tokens=1). The Anthropic→Responses conversion copied the value verbatim, so strict upstreams returned 400 and Claude Desktop reported the mapped model as unavailable under local routing.
Clamp 1..=15 up to 16 during conversion; 16+, 0 and non-integer values keep their pass-through semantics. Normal session requests convert byte-for-byte as before, and the Codex OAuth path still strips max_output_tokens afterwards.
Fixes #7103
fix(codex): coalesce adjacent commentary with pending tool calls (#7280)
The Codex Responses -> Chat Completions converter emitted two consecutive
assistant messages when one Responses model turn contained a commentary
message directly followed by function_call items. Chat upstreams imitate
the text-only assistant message as a complete turn and return
finish_reason=stop before the expected tool call, so long agent tasks end
after a progress update.
Merge pending tool calls into the directly adjacent assistant message when
it carries no tool_calls yet, attach pending reasoning per segment so
parallel calls do not duplicate it, and keep the new-message path for every
other boundary shape (user/tool boundaries, prior tool-call batches, media
flushes). The reasoning placeholder backfill still applies to coalesced
calls without reasoning.
Fixes #6529
Supersedes #6530
Co-authored-by: BigStrongSun <BigStrongSun@users.noreply.github.com>
fix(proxy): skip empty reasoning_content placeholders in Anthropic SSE streaming (#7227)
Some OpenAI-compatible upstreams (GLM, Qwen, DeepSeek-V4-Pro via ModelScope/DashScope,
Bailian, JD Cloud, ...) keep a `reasoning_content: ""` placeholder in every content
chunk instead of omitting the field. The reasoning branch of the OpenAI Chat -> Anthropic
SSE converter only checked for presence, and it shares `current_non_tool_block_type`
with the content branch, so every content chunk closed the open text block, opened an
empty thinking block, then reopened a text block. N content chunks became 2N content
blocks, half of them empty thinking blocks, which Claude Code rendered as one line per
token with dozens of empty Thought blocks and bloated the session JSONL.
Filter empty reasoning before entering the branch, matching the `!content.is_empty()`
guard the content branch already had and the non-streaming `openai_to_anthropic`
converter. Two regression tests pin block index, type and order: the placeholder shape
must yield a single text block and no thinking_delta at all, and real reasoning must
still yield one thinking block followed by one text block.
Fixes #5028
Fixes #4404
fix(codex): detect growing rollouts with a persisted byte cursor (#7219)
Codex keeps the rollout append handle open for the whole session, so on Windows NTFS the file mtime does not advance while the file grows and the pure-mtime gate skipped growing rollouts forever. Persist the observed byte length in session_log_sync.last_byte_offset for Codex rows and skip a file only when both mtime and size are unchanged. The parser now splits records on newlines manually so an incomplete trailing record is retried on the next pass without advancing the line cursor.
Fixes #6060
fix(codex): proxy image edits and Images API follow-ups (#7177)
* fix(codex): proxy image edits and Images API follow-ups
Follow-ups to #7036 for Codex's built-in ImageGen over the local proxy:
- Register `/images/edits` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them through the shared Codex standalone passthrough.
ImageGen posts to `images/edits` whenever a call references existing
images, and the router returned an empty 404 for it.
- Add `CodexStandaloneEndpoint::ImagesEdits`. Both Images endpoints share
one full-URL suffix table, so a complete Responses / Compact / Chat
Completions / Images URL derives the sibling endpoint (and vice versa)
while opaque full URLs keep failing closed.
- Apply the standalone sibling rewrite when the base URL is pasted as a
complete endpoint with the full-URL switch off, mirroring
`codex_chat_base_is_full_endpoint`; standalone requests no longer become
`.../chat/completions/images/generations`.
- Parse Images API streaming usage: `image_generation.completed` carries
`usage` on the event top level, so scan for `usage.input_tokens` before
falling back to the Chat Completions stream parser.
Related #7036
* fix(codex): match standalone URL suffixes case-insensitively
Keep endpoint detection and sibling rewriting consistent while preserving the original URL prefix and query. Cover mixed-case Images and Alpha Search endpoints through URL and proxy regression cases.
---------
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): preserve per-app settings on shutdown and port allocation (#7210)
On normal exit, stop_with_restore_keep_state() called the legacy global
proxy-config writer to clear the long-retired live_takeover_active flag.
That writer reads the Claude row and runs an UPDATE without a WHERE clause,
so every shutdown copied Claude's max_retries and the three timeout fields
onto the Codex, Gemini and Grok Build rows — and the UPDATE never carried
live_takeover_active in the first place, so the step had no effect beyond
the clobber. The ephemeral listen-port path did the same on every proxy
start.
Drop the dead write-back (live-config restore, backup cleanup and health
reset are unchanged) and persist the resolved ephemeral port through the
global-config interface, which only touches the shared listen/logging
columns. Two regression tests seed distinct per-app settings for all four
apps and assert they survive both paths.
Fixes #7204
fix(provider): preserve child metadata during universal sync (#7212)
Syncing a universal provider regenerates its Claude/Codex/Gemini child
providers. The child's `settings_config` was merged with the existing row,
but `meta`, `created_at` and `sort_index` were taken from the generated
object, and the universal provider never carries any of them from the UI.
The UPDATE therefore wiped the child's usage script, common-config opt-out,
endpoint auto-select and other per-app settings to `{}` and reset the sort
index to NULL, which pushed the card to the bottom of the list and looked
like the provider had been deleted and re-created.
Keep the existing child's `meta`, `created_at` and `sort_index` when the
row already exists. Name, base URL, key, models, website and notes are
still driven by the universal provider, and first-time creation still
inherits the parent's metadata.
Fixes #7134
feat(presets): rebrand DashScope as 千问AI平台 and refresh Qwen models (#7183)
Rename the domestic Bailian presets to 千问AI平台 with a dedicated icon, refresh the Qwen family to the 3.8 generation across Claude Code, Claude Desktop, Codex, Hermes, OpenClaw, OpenCode and Pi, add the domestic Token Plan presets, restore the international QwenCloud Coding Plan presets, and align OpenClaw/OpenCode/Pi model metadata with the official client docs.
fix(codex): honor proxy URL when model_provider is omitted (#7263)
When a Codex provider card's TOML has no `model_provider`, Codex falls back to
its built-in `openai` provider, but takeover wrote the proxy address as a
top-level `base_url` that Codex never reads, so requests bypassed the local
proxy and went straight to api.openai.com.
Treat the missing selector as the built-in `openai` provider and write the
proxy address to `openai_base_url`; the existing legacy-reroute migration then
turns it into a `cc-switch` custom provider table carrying the `PROXY_MANAGED`
bearer, the same shape every other third-party takeover uses. Explicit
provider selections are unchanged.
Refs #6256, #7217
fix(provider): update MiniMax defaults to M3 and remove expired offers (#7255)
* fix(provider): refresh MiniMax M3 presets and remove expired promotions
* fix(provider): complete MiniMax M3 capability declarations
---------
Co-authored-by: saladday <1203511142@qq.com>
chore(provider): remove Atlas Cloud sponsor status, keep presets
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`
docs(release): add v3.20.2 release notes
chore(release): v3.20.2
feat(provider): add SoleAPI sponsor presets across all nine apps
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.
fix(codex): proxy image edits endpoint
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.
fix(provider): pin models URL for JieKou and Novita Claude presets
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.
feat(pricing): refresh seed for September 2026 vendor price changes
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.
fix(codex): stamp requires_openai_auth on takeover writes to match the live login state
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.
fix(updater): show the real reason when an update check fails (#6482)
The updater plugin rejects with a plain string, so `err instanceof Error`
never matched and the real failure reason was replaced by the generic
fallback message. Use the existing extractErrorMessage helper, and pass
the recovered message into the check-update failure toast so it actually
reaches the user instead of stopping at an unread context state.
Co-authored-by: Jason <farion1231@gmail.com>
docs: fix the locales path in the README directory trees (#6100)
All four READMEs showed locales/ as a direct child of src/, but the
translations live at src/i18n/locales/. Split it into two tree nodes so it
matches how the rest of the tree is drawn. Also fixes a CLaude -> Claude
typo in README_ZH.md.
Co-authored-by: Jason <farion1231@gmail.com>
fix(claude): exclude workflow journal.jsonl from session list
Claude Code's workflow feature writes subagents/workflows/wf_*/journal.jsonl inside the session directory. The Claude session scanner only excluded agent-* files, so each journal parsed as an empty ghost session titled "journal". Exclude journal.jsonl by file name and add regression tests.
Fixes #6042
fix(i18n): add the missing pi.form.providerKeyDuplicate and common.collapse keys
translatePiProviderMutationError returns pi.form.providerKeyDuplicate for the Pi backend's duplicate-key error, but the key existed in none of the four locale files, so the toast showed the raw key. common.collapse (aria-label of the thinking-map toggle) was missing the same way. Add both keys to zh/en/ja/zh-TW and lock them in the locale coverage test.
fix(proxy): stop mask_url panicking on multi-byte UTF-8 near the truncation boundary
mask_url's fallback branch (used when the proxy URL fails Url::parse) sliced the raw string at byte 20 and panicked when a multi-byte UTF-8 character straddled that offset, e.g. a CJK string pasted into the global proxy address. Snap the cut to the nearest char boundary at or before 20 and add a regression test.
fix(tests): isolate LOCALAPPDATA so tests stop writing the real Claude Desktop config
On Windows, Claude Desktop config paths resolve from LOCALAPPDATA, which the test home override in tests/support.rs did not redirect, so profile_roundtrip wrote the https://desktop.test fixture into the developer's real Claude Desktop config. Point LOCALAPPDATA at the isolated test home alongside HOME and USERPROFILE.
Fixes #6077
fix(usage): widen pricing model source select to fit localized labels
The pricing model source SelectTrigger in the usage cost settings table was w-28, which leaves about 70px for the label; the English and Japanese labels need about 107px and overflowed the trigger. Widen it to w-40 and drop the h-7 overrides so the trigger and the adjacent Input use the shared h-9 height.
feat(pi): add Tencent TokenHub and Token Plan presets (#7159)
Add eight Tencent presets for Pi, covering the TokenHub pay-as-you-go line
(domestic and international) and the six Token Plan subscription products —
Pi was the only managed app still missing them. Model IDs, ordering and
endpoints follow Tencent's own model lists verbatim, and the tests assert each
product's line-up in order so future drift is caught.
Declare thinking control alongside the capabilities rather than leaving it to
defaults: the 31 DeepSeek entries carry a Tencent-specific compat so selecting
"off" actually disables thinking instead of silently billing reasoning tokens,
the eight Kimi K2.7 Code entries no longer offer an "off" level the model
cannot honour, and kimi-k2.7-code-highspeed is marked image-capable so
tool-returned screenshots are no longer dropped.
Drop minimax-m2.5 from every app: it is gone from Tencent's model tables and
the platform is retiring it.
fix(codex): proxy image generations endpoint (#7036)
Codex's built-in ImageGen calls the legacy OpenAI Images API, but the local
proxy only registered Responses / Chat Completions / Compact / Alpha Search
routes, so `/v1/images/generations` returned an empty 404 under Codex routing.
- Register `/images/generations` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them as a Codex standalone passthrough shared with Alpha Search.
- Generalize the Alpha Search full-URL rewrite into
`rewrite_codex_standalone_full_url`: derive the sibling
`/images/generations` from full Responses / Compact / Chat Completions URLs,
keep an existing Images URL as-is, and fail closed on opaque full URLs.
- Record `usage.input_tokens` / `output_tokens` through the Codex usage parser.
Fixes #5429
Fixes #6745
feat(pricing): add Gemini 3.8 Flash pricing (#7164)
Seeds the built-in pricing row for Gemini 3.8 Flash at 0.75 / 3.75 input/output per million tokens, 0.075 cache read, no cache-write charge, plus a regression test locking all four fields.
Values verified against Google's own pricing page (introductory price through 2026-12-31; list price 1.50 / 7.50 / 0.15 from 2027-01-01) and models.dev.
feat(provider): extend PPIO presets for Pi (#6870)
Add a PPIO provider preset for Pi using the OpenAI-compatible endpoint,
and pin an explicit models URL on the Claude Code PPIO preset so model
discovery resolves against https://api.ppio.com/openai/v1/models instead
of the derived candidates, which all return 404.
Co-authored-by: Jason <farion1231@gmail.com>
feat(pricing): add GLM-5.3 Flash pricing (#7163)
fix(proxy): enable parallel tool calls for Codex OAuth (#7024)
Codex OAuth requests defaulted parallel_tool_calls to false when the incoming
Anthropic request carried no explicit value, forcing Claude Code into one tool
call per model turn. Default it to true (matching codex-rs), map Anthropic
tool_choice.disable_parallel_tool_use to the inverse Responses value so explicit
serial execution is preserved, and add regression coverage for both directions.
Carries #5722 forward on top of current main.
Fixes #5719
Co-authored-by: 太一 <taiyi@douyu.tv>
feat(pricing): add GPT-6 Astra pricing (#7162)
fix(codex): resolve input_modalities for unknown models in vendor catalog (#6750)
When a model slug is not in the bundled DeepSeek official catalog, the vendor path clones the flagship entry and previously inherited its text-only input_modalities. Resolve modalities for unmatched slugs through the same registry/fail-open logic the non-vendor path uses, so a vision variant such as deepseek-v4-flash-vision-exp is no longer declared text-only. Explicit user inputModalities still win; matched models keep the vendor's declaration verbatim.
Fixes #6725
fix(claude): expose Opus 5 and Sonnet 5 in takeover mode (#5882)
Bump the stable client-facing role aliases that takeover mode writes to
Claude Code: Opus claude-opus-4-8 -> claude-opus-5 and Sonnet
claude-sonnet-4-6 -> claude-sonnet-5, so the /model menu matches the
Claude Desktop default routes and preset defaults that already moved to
Opus 5 / Sonnet 5. Add "opus-5" to the adaptive-thinking classifier so
the Bedrock thinking optimizer, which runs on the client alias before
model mapping, keeps sending adaptive thinking instead of the removed
budget_tokens.
Routing is unaffected: the proxy maps client aliases by role keyword, and
the aliases are rewritten automatically the next time the proxy starts.
Fixes #5876
Co-authored-by: Jason <farion1231@gmail.com>
feat(pricing): add GLM-5.3 to built-in model pricing table (#6591)
Seed glm-5.3 at $1.4/$4.4 per MTok with $0.26 cache read, matching the
official Z.ai list price (identical to the GLM-5.2 tier):
https://docs.z.ai/guides/overview/pricing
New model id, so seed only; no repair entry and no SCHEMA_VERSION bump.
Existing installs pick the row up on next launch via INSERT OR IGNORE, and
the startup cost backfill re-prices historical glm-5.3 rows recorded at $0.
fix(codex): align OAuth client identity for GPT-6 (#7132)
Requests for gpt-6-astra through the Claude-to-Codex OAuth takeover route advertised Codex 0.144.1, below the model's 0.153.0 minimal_client_version, so ChatGPT's backend rejected them with HTTP 400 "requires a newer version of Codex". Bump the advertised client version to 0.153.4 and share the originator/version constants between generation and model discovery, which previously sent cc-switch's own package version and originator.
Two regression tests pin the generation headers and the model-list request to a version at or above the gpt-6-astra floor.
Refs #7129
fix(codex): disable supports_search_tool for DeepSeek catalog models (#6653)
DeepSeek's Responses API has no tool_search support, but the bundled
official catalog declared supports_search_tool=true for deepseek-v4-pro
and deepseek-v4-flash. Codex gates deferred/namespaced MCP tools on
this flag (search_tool_enabled = supports_search_tool &&
namespace_tools), so MCP tools were hidden behind a tool_search the
gateway cannot serve — no MCP tool could ever be called.
Set the flag to false for both models so Codex lists MCP tools inline.
Hosted web_search is unaffected (gated by provider capabilit…
Frontend CI format:check failed on 6 files modified by the app-name
and model-quick-switch archive commits plus the updater-disable commit.
Run prettier --write so format:check passes again.
chore: archive ccs-fork-app-name-cc-switch
chore: archive ccs-model-quick-switch-ui-polish
docs: rewrite README to document fork differences vs upstream
README 从上游完整功能介绍改为仅记录 fork 与上游的差别,完整功能
文档指回上游。涵盖 8 项 fork 魔改:构建标识/开发预览、预设精简、
Claude 表单调整、模型搜索移植、主页模型快速切换、侧边栏面板显隐、
关闭应用内更新、打包范围,并说明版本号与 rebase 同步流程。
feat: disable in-app updater, point users to GitHub releases
移除 tauri-plugin-updater 依赖与自动更新链路,改为统一引导用户到
GitHub Releases 手动下载新版。
- 前端:updater.ts 恒返回 up-to-date;UpdateContext 取消启动自检;
AboutSection「检查更新」直接打开 fork releases 页;DatabaseUpgrade
恢复界面去掉下载进度条,改为打开发布页。
- 后端:移除 updater 插件注册与 UpdaterExt 调用;
install_update_and_restart 降级为空壳,check_app_update_available
返回当前版本号;check_for_updates 指向 fork releases;capabilities
去掉 updater:default 权限。
- 打包:tauri.conf.json 删 plugins.updater、createUpdaterArtifacts=false;
Cargo.toml / package.json 删 updater 依赖,Cargo.lock / pnpm-lock
同步重生成。
- CI:release.yml 移除签名收集与 assemble-latest-json job,删除
.tar.gz/.sig 处理,release body 去掉 updater 说明。
版本号统一为 3.20.0-1(合法 semver 且 MSI 第 4 位为 1)。
Cargo.lock / pnpm-lock.yaml 已随依赖移除重生成,cargo check 通过。
ci: build only Windows x64 and macOS arm64, unsigned
feat: gate dev panel behind dev-only env flag
chore(comet): archive ccs-sidebar-panels-visibility change
fix: apply skills visibility guard to hermes branch toolbar button
feat: include prompts in sidebar panel visibility settings
fix: persist visibleSidebarPanels setting in backend AppSettings
style: use compact pill toggles for sidebar panel visibility
feat: add sidebar panel visibility switches to settings
feat: add sidebar panel visibility settings and wire into toolbar buttons
chore(comet): archive ccs-home-model-display change
fix(fork): read grokbuild api key from config TOML in quick switch dialog
feat(fork): align model badge with upstream extractModelBadge
feat(fork): show current model badge and quick-switch entry on provider cards
feat(fork): add model quick switch dialog
feat(fork): add provider model read/write utils with tests
chore(comet): archive ccs-model-search-port change
feat(i18n): add model search picker keys for all locales
feat(form): port SearchableModelPicker from upstream for searchable model selection
chore(comet): archive ccs-claude-form-layout change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-claude-form-layout/
- Sync claude-form-layout spec to main specs
- Annotate design doc as archived
- Add verification report
fix(i18n): update advanced options hint, add ja/zh-TW toggle-all, drop dead apiFormatHint key
fix(form): correct 1M toggle-all alignment direction (ml-1)
fix(form): align 1M toggle-all checkbox with row checkboxes
refactor(form): move api format select into EndpointField rightSlot
fix(form): widen api format select to fit option text
feat(form): add 1M toggle-all checkbox in model mapping header
feat(form): move api format select next to endpoint input
chore(comet): archive ccs-provider-presets-trim change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-provider-presets-trim/
- Sync provider-presets-visibility spec to main specs
- Annotate design doc as archived
- Add verification report
docs(fork): add presets-trim files to rebase whitelist
test(fork): mock IS_FORK_BUILD false in unit tests for upstream-form semantics
feat(fork): wire official preset filter into provider forms
feat(fork): add hidden field to preset interfaces
feat(fork): add official preset allowlist and filter util
chore(comet): archive ccs-fork-scaffolding change
- Move change artifacts to docs/openspec/changes/archive/2026-08-18-ccs-fork-scaffolding/
- Sync fork-build-identity spec to main specs
- Annotate design doc as archived
- Add verification report
feat(fork): add dev config with isolated identifier and config dir
docs(fork): complete rebase whitelist with all fork-touched files
fix(fork): guard setTitle with isTauri in test/non-tauri env
fix(fork): define __CCS_FORK_BUILD__ in vitest config
feat(fork): add DevPanel and settings footer entry
feat(fork): set macOS window title to CC Switch (Fork) on startup
feat(fork): inject __CCS_FORK_BUILD__ compile-time constant
feat(fork): bump version to 3.19.2-fork.1 and rename product to CC Switch (Fork)
docs(fork): add upstream rebase workflow and ignore tool artifacts
fix(proxy): accept the whole grok-4.x (x>=5) family in the reasoning-effort whitelist (#7369)
Replace the verbatim grok-4.5 / grok-4.6 entries in supports_reasoning_effort with a rule that parses the grok-4.x minor version and accepts x >= 5, mirroring the existing GPT-5+ rule. This covers grok-4.7 (released 2026-09-21), whose reasoning effort was previously dropped on the Claude -> Chat, Claude -> Responses and Codex Responses -> Chat conversion paths, and lets future releases pass without another whitelist edit. The grok-build-* family is retained for saved providers.
Co-authored-by: allenxu09 <171831965+allenxu09@users.noreply.github.com>
docs(release): add v3.20.4 release notes
chore(release): v3.20.4
feat(pricing): add Grok 4.7 and GLM-5.3-FlashX
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.
fix(i18n): remove outdated OpenCode Go referral offer
fix: add SudoCode.chat alternate API endpoint
feat(settings): add a GitHub star prompt to the About card
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
feat(presets): add Soshow aggregator presets
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.
fix(ci): stabilize frontend tests and verify renderer build
fix(mcode): refresh provider state after removal (#7578)
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
fix(lib): reset skip_taskbar before window.show() across entry points (#6348)
5处 window.show() 调用中 4处没有在 show() 前重置 Windows 的 skip_taskbar,导致以下场景出现窗口可见但不在任务栏、点最小化后窗口消失的 bug:
- single_instance handler (lib.rs:360-361):当原进程因 silent_startup / 关闭到托盘等处于 skip_taskbar(true) 状态时,从快捷方式/开始菜单唤起已运行实例触发。
- handle_deeplink_url 的 focus_main_window 分支 (lib.rs:256):通过 ccswitch:// URL 唤起时。
- DB 恢复流程 (lib.rs:556):数据库版本不匹配时显示恢复界面。
- RunEvent::Opened 处理器 (lib.rs:1803):系统接收到 deep-link URL 时。
修复后的行为与 tray.rs::handle_tray_menu_event::show_main 分支一致(那里已有相同的 set_skip_taskbar(false) 调用)。
经用户测试验证:autostart + silent_startup + 从开始菜单/快捷方式启动的场景下,最小化按钮恢复正常工作。
fix(proxy): omit missing tool descriptions in the Codex responses-to-chat converter (#7378)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): preserve max effort for GPT-5.6 and GPT-6 Astra (#7531)
fix(misc): ignore shell startup output when probing tool versions in WSL (#7348)
try_get_version_wsl runs "${SHELL:-sh}" -lic '<tool> --version' through
wsl.exe and takes the first x.y.z anywhere in stdout. A login+interactive
shell runs the distro's startup files first, so their output shares that
stdout. On Ubuntu, /etc/profile.d/update-motd.sh prints the MOTD once a day,
starting with "Welcome to Ubuntu 24.04.4 LTS", and the card then showed
24.04.4 as the Claude Code version. The once-a-day stamp makes it appear
after the first probe of the day and vanish on refresh.
The payload now prints a sentinel before running the tool, and parsing looks
only at what follows its last occurrence. The last occurrence handles the
-lic || -lc || -c fallback chain printing it several times; matching it as a
substring handles OSC sequences glued in front of it. If the sentinel is
absent (the shell failed before the payload ran), the output is used as
before. The stderr fallback for tools that print their version there is kept.
Fixes #7347
Co-authored-by: lyfuci <lyfuci11@gmail.com>
fix(claude): hide session URL attribution (#7053)
* fix(claude): hide session URL attribution
* docs(claude): document session URL attribution
fix(ui): reset provider scroll when switching apps (#5211)
fix(pi): fix provider logo (#6826)
fix(i18n): use the Pi form's own term for the zh-TW duplicate-key message (#7187)
Follow-up to the nit on #6768. In zh-TW the Pi form labels the field
供應商識別碼 and says 供應商識別碼為必填 when it is empty, but the
duplicate message I added said 供應商標識 — the term Hermes, OpenCode and
OpenClaw use for their own field. One form, two names for the same thing.
The other three locales already keep the label and both messages on one
term, so this makes zh-TW match the pattern rather than introduce one:
en Provider key / Provider key is required / This provider key already exists
ja プロバイダーキー / プロバイダーキーを入力してください / このプロバイダーキーは既に存在します
zh 供应商标识 / 供应商标识不能为空 / 该供应商标识已存在
zh-TW 供應商識別碼 / 供應商識別碼為必填 / 該供應商標識已存在 <- the odd one out
hermes.form.providerKeyDuplicate carries the same sentence and is left
alone: its own label is 供應商標識, so it is already consistent.
Verified: managementListLocales 7/7. Full unit suite 1085/1087, the same
two App.test.tsx integration cases failing before and after.
fix(proxy): strip stop param for GitHub Copilot to unblock auto mode classifier (#5404)
Claude Code's auto mode classifier sends `stop_sequences` on every
safety check. On the GitHub Copilot path the Anthropic -> OpenAI Chat
conversion mapped it to `stop`, and Copilot's Chat Completions route
for Claude models answers that request with HTTP 400, so every
classifier call failed while normal chat kept working.
- Remove the converted `stop` field only when the provider is GitHub
Copilot; other OpenAI-compatible upstreams keep it.
- Copilot detection in the transform now also covers providers that
are recognised only through `env.ANTHROPIC_BASE_URL`.
- Regression tests for the Copilot (meta and env URL) and generic
provider cases.
Fixes #5175
fix(codex): keep stored auth when the live snapshot has no credential (#7434)
Editing the current Codex provider seeds the form from the live ~/.codex
snapshot, and switching away backfills that snapshot into the DB row. When a
provider table declares its own credential source (an Authorization header,
env_key, auth/aws), no bearer token is injected into config.toml and the
shared auth.json is deleted on third-party switches, so the live snapshot is
permanently `{ auth: {}, config }` while auth.OPENAI_API_KEY in the DB is the
only copy of the key. Both paths treated that empty live auth as
authoritative and silently erased the stored key, which in turn made the
usage-script modal misread the provider as an official subscription and
reset the saved script.
- Treat a credential-less live auth (missing auth.json or the bare
auth_mode logout marker) as an absent field in both the edit dialog and
the switch-away backfill, keeping the stored auth template. Live still
wins whenever it carries material, and official providers keep the live
login as their source of truth.
- Gate the official exemption on is_codex_official_provider /
resolveCodexOfficialIdentity rather than the category alone: a legacy
fixed official card without a persisted category is still written as
official, so the fallback must not resurrect a logged-out login on
switch-back.
- Cover both layers with regression tests, including the category-less
official shapes.
Fixes #7433
Supersedes #7492
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): lift additional_tools input carriers on the Codex chat conversion (#7454)
Codex talks to third-party providers over its Responses Lite transport,
which ships the request's extra tools in an `additional_tools` input
carrier: `{"type":"additional_tools","role":"developer","tools":[...]}`,
a role but no `content`. The Responses->Chat conversion let that carrier
fall through the generic "item with a role is a message" arm and emitted
`{"role":"system","content":null}` at messages[1], which strict Chat
gateways reject with `messages[1]: missing field "content"` (or
`data did not match any variant of ...SystemMessageContent`). The tools
carried inside it were also dropped on the Chat and Anthropic conversions.
- Skip `additional_tools` items in the Chat message walk; a carrier
declares tools, it is not a message.
- Collect carrier tools through the existing CodexToolContext registry
(dedup, namespace flattening, custom-tool conversion), which also feeds
the Anthropic conversion and the response-side name round-trip.
- Requests without a carrier convert byte-for-byte as before.
Known boundary: `custom` children inside a namespace tool
(`functions.exec`) are still not lifted; that needs its own response-side
name round-trip and is left for a follow-up.
Diagnosed independently in #6158 by BigStrongSun (fix in #6159) and in
fixture.
Fixes #7451
Fixes #6158
Refs #6033
Refs #6776
Supersedes #6159
fix(proxy): normalize Codex detail:original images for Chat gateways (#7476)
Codex marks tool-returned images with detail:"original" whenever the model
catalog advertises supports_image_detail_original, a Responses-only value.
The Responses->Chat conversion copied it verbatim into image_url.detail, and
strict OpenAI-compatible Chat gateways (Ark/Doubao, SenseNova, GLM, vLLM, ...)
reject the whole request with 400. Because Codex replays the full rollout on
every turn, one poisoned view_image result made the session fail permanently,
including plain-text turns.
Two layers:
- tool_media::merge_top_level_detail downgrades original -> auto, so already
poisoned rollouts and Codex versions before 0.155.0 (which do not normalize
replayed history themselves) recover immediately. auto/low/high are untouched.
- codex_catalog_model_entry always writes supports_image_detail_original:false
on the ProxyChat profile, so Codex stops emitting original at the source once
the catalog is regenerated. NativeResponses/Anthropic profiles and the DeepSeek
official catalog are unaffected.
Top-level user images were never affected: responses_content_to_chat_content
drops the part-level detail entirely.
Fixes #6495
Fixes #7458
Refs #6569
Refs #5805
fix(omo): detect unified config on the WSL side when the OpenCode dir is in WSL (#7550)
When the OpenCode config directory points into WSL
(\\wsl.localhost\<distro>\home\<user>\.config\opencode), OMO >= 4.19.3 keeps
its unified config in the WSL-side home (~/.omo/omo.jsonc). Detection only
probed the Windows home, never found it, and silently fell back to the legacy
per-plugin file that OMO no longer reads, so provider switches looked
successful but had no effect.
- Add derive_wsl_home_dir to derive the WSL-side home from a WSL UNC path
(home/<user> or root); non-WSL and non-home paths return None.
- Probe a list of home candidates for the unified config, WSL-side home
first and the OS home as a fallback, shared by the read, write and delete
paths.
- Detection only: when no unified config exists on either side the legacy
fallback is unchanged.
Fixes #7363
Supersedes #7367
docs(partner): add a FluxA purchase link and tighten its sponsor blurb
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.
fix(claude-desktop): expose the 1M context variant for DeepSeek V4 routes
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.
feat(presets): add FluxA Token Plan partner presets
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.
fix(presets): correct AICodeWith Codex-family endpoint to /v1
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.
fix(ui): render monochrome preset icons in the foreground color
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.
docs(user-manual): point Chat-only Codex examples at plan endpoints
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.
fix(pricing): restore DeepSeek V4 Pro price and seed Qwen3.8 and Hy4 models
- 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).
fix(presets): move MiniMax CN and BaiLing to their current official endpoints
- 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.
feat(codex): refresh CN presets from the Responses API audit
- 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.
test(claude-desktop): run Linux path tests on unix hosts and cover Linux in provider sync tests
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.
fix(ui): stop expanding stale cached usage tiers on ineligible provider cards
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.
fix(skills): 支持 skillId 与目录名不一致的技能安装和更新 (#6381)
* fix(skills): resolve mismatched skillId directories
* fix(skills): preserve source path during updates
* fix(skills): refresh source URL after repository relocation
* test(skills): isolate relocation fixture from legacy home
feat(opencode): 支持从获取的模型列表中搜索并批量添加所选模型 (#7515)
* feat: implement fetched models selection and UI components
* fix(opencode): prevent model search from submitting provider form
Add Kimi Global preset variants (kimi.ai platform + api endpoints) (#7526)
Add a "Kimi Global" twin immediately after the domestic "Kimi" API preset and a "Kimi For Coding Global" twin after the domestic "Kimi For Coding" preset, in every preset file that ships them (claude, claude-desktop, codex, hermes, openclaw, opencode, pi).
"Kimi Global" points to the international open platform https://platform.kimi.ai?aff=cc-switch (apiKeyUrl mirrored as https://platform.kimi.ai/console/api-keys?aff=cc-switch) and swaps the endpoint host api.moonshot.cn for api.moonshot.ai with each file's path unchanged (/v1 or /anthropic).
"Kimi For Coding Global" points to the international site https://www.kimi.ai/code/?aff=cc-switch and swaps the endpoint host api.kimi.com for api.kimi.ai with the path unchanged (e.g. https://api.kimi.ai/coding/v1).
Domestic presets are unchanged; category, icons, apiFormat, model catalogs, endpointCandidates (host swapped), partnerPromotionKey and settings otherwise mirror the domestic blocks exactly.
primePartner stays only on the domestic presets so the new variants do not alter prime-partner ordering.
The Pi twins get distinct providerKey values ("cc-switch-kimi-global", "cc-switch-kimi-for-coding-global") because providerKey is the preset-level primary key and must not collide with the domestic presets.
Rebalance sponsor CTAs around Kimi Code plan with dual-region links (#7522)
Kimi Code plan purchases have resumed, so the README sponsor sections go back to leading with the coding plan instead of the API-only flow written during the purchase pause.
All four locales (EN/ZH/JA/DE) now present Kimi Code plan (中文站 | Global) as the primary CTA and the Kimi Open Platform API (中文站 | Global, with the first-top-up bonus) as the secondary one, mirroring the domestic|global split already used for API links.
The aff and track_id values are unchanged.
fix(prompts): refresh active prompts after external file edits (#7194)
Reload the enabled prompt from its live file when the Prompts list loads, and refresh the open panel when the window regains focus, so external edits to CLAUDE.md / AGENTS.md show up without a restart.
- Keep saved templates available when the live file cannot be read.
- Skip the backfill for empty or whitespace-only files, matching enable_prompt.
- Skip the backfill while a sync/restore holds the sync lock, so a list load cannot write a stale live file into a freshly restored database.
fix(skills): raise archive entry limit so large skill repos install (#7489)
Installing hugohe3/ppt-master from skills.sh failed with
ARCHIVE_TOO_MANY_ENTRIES: the GitHub archive we download is the whole
repository (13,248 entries), and the 10,000-entry cap added in #5811
treated it as a zip bomb.
- Raise MAX_ARCHIVE_ENTRIES to 30,000 for both the remote archive and
local ZIP paths, and align WebDAV's MAX_EXTRACT_ENTRIES so a skills.zip
containing such a skill still restores.
- Charge every file entry at least one 4 KiB block against the existing
512 MiB budget, mirroring directory accounting, so empty-file archives
are bounded by the byte budget rather than by the entry count alone.
- Add tests for a 13k-entry repo archive and for the per-file minimum
charge.
Fixes: https://github.com/farion1231/cc-switch/issues/7475
feat: add MiniMax Code harness support (#7383)
Add MiniMax Code (mcode) as a managed application: provider and model management through the native custom_provider configuration, MCP servers, Skills, global instructions (AGENTS.md), read-only local session history with resume, and usage import from the native runtime database. Native files shared by the TUI and desktop are written under the native lock with rollback when the database commit fails, unrelated native configuration is preserved, and the data directory honors MINIMAX_DATA_DIR and MAVIS_DATA_DIR. Deleting sessions stays in the native application because they are coupled to runtime state.
fix(codex): recover stale account bindings during takeover (#7395)
After deleting a managed ChatGPT account and logging in again, a Codex provider can still reference the old local account ID, which made route switching, takeover, startup restoration and rebinding fail permanently.
Distinguish a missing outgoing account from an existing account with no matching live token. A missing account only releases its ownership marker after the persisted account store is verified; the target write policy stays in force. A stale target now reports an account-selection instruction instead of a bare not-found error, including when takeover is already enabled.
Fixes #7392
feat(claude-desktop): support 3P configuration on Linux (#7331)
Add Claude Desktop 3P configuration support on Linux. Resolve the config root from an absolute XDG_CONFIG_HOME, falling back to ~/.config; when CC Switch itself runs as a Flatpak, use the host ~/.config instead of the sandbox-private XDG_CONFIG_HOME. Update the en/zh/ja user manuals with Linux paths and the Flatpak boundary.
Fixes #4855
fix(proxy): omit missing tool descriptions instead of serializing null (#7319)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): keep reasoning effort for grok-4.6 and pass xhigh through verbatim (#7318)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(models): support Zhipu OpenAI Responses model lists (#7330)
Zhipu's Responses endpoint (open.bigmodel.cn/api/v1) serves its model list in the Codex remote catalog shape (models[].slug) instead of the OpenAI-compatible data[].id shape, so the Codex form's model fetch came back empty. The parser now falls back to models[].slug when data is absent.
fix(misc): fetch npm dist-tags from the dedicated endpoint with the probe timeout (#7346)
The tool version probe requested the full npm packument for each package (metadata for every historical version, 14-25 MB uncompressed for codex / opencode / openclaw) only to read its dist-tags, and it was the only latest-version probe without LATEST_PROBE_TIMEOUT, so it fell back to the proxy client's 600 s total timeout and the cards sat on "loading" for minutes on slow links while the large downloads starved the Hermes GitHub probe into "unknown".
Switch fetch_npm_dist_tags to registry.npmjs.org/-/package/{package}/dist-tags, whose body is the dist-tags object itself (tens of bytes to a few KB), escape scoped names as %2f, and apply the same 15 s probe timeout as the GitHub and PyPI probes. No data is lost: the only consumer reads latest and the tool's prerelease tags.
Fixes #7339
feat(usage): display output tokens per second in request log table and detail panel (#3369)
Show tokens/s next to the output token count in the request log table and the request detail panel.
The generation window prefers an explicit duration when present, otherwise latency minus first-token time for streaming requests, and falls back to full latency for non-streaming requests. Rows without a positive token count or window show nothing.
Closes #5936
fix(presets): update APIKEY.FUN URLs to apikey.fan
docs(release): add v3.20.3 release notes
chore(release): v3.20.3
fix(pricing): move retired DeepSeek V4 family to the V4.1 Flash tier
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.
fix(presets): declare third-party Token Plan DeepSeek V4 rows text-only
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.
feat(presets): rename DouBaoSeed preset to localized Volcengine Doubao
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.
fix(codex): skip unchanged incomplete rollout tails
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.
fix(claude): align quick model selection with form order
fix(tray): show bound ChatGPT account quota for managed Codex cards
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
feat(claude): add "Disable Artifact Tool" quick toggle to provider editor
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.
feat(codex): refresh aggregator model presets and catalogs
feat(codex): switch Kimi presets to native Responses direct-connect
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.
fix(usage): show Claude Fable weekly limit from the usage API limits array
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.
fix(codex): mirror vision-capable DeepSeek catalog and un-gate the legacy v4-flash alias (#7286)
Refresh the bundled DeepSeek official Codex catalog to the vendor's current models.json (slug deepseek-flash, image input modality, supports_image_detail_original) and remove deepseek-v4-flash from the confirmed text-only registry: the legacy id is still accepted by DeepSeek and routed to the vision-capable V4.1 Flash, so both the proxy media sanitizer and the generated Codex catalog must fail open for it. deepseek-v4-pro stays text-only per the vendor catalog.
Fixes #7283
fix(proxy): clamp sub-floor max_tokens to the Responses API minimum (#7287)
The Responses API rejects max_output_tokens below 16, but Anthropic clients legitimately send tiny probe budgets (Claude Desktop's model-availability probe uses max_tokens=1). The Anthropic→Responses conversion copied the value verbatim, so strict upstreams returned 400 and Claude Desktop reported the mapped model as unavailable under local routing.
Clamp 1..=15 up to 16 during conversion; 16+, 0 and non-integer values keep their pass-through semantics. Normal session requests convert byte-for-byte as before, and the Codex OAuth path still strips max_output_tokens afterwards.
Fixes #7103
fix(codex): coalesce adjacent commentary with pending tool calls (#7280)
The Codex Responses -> Chat Completions converter emitted two consecutive
assistant messages when one Responses model turn contained a commentary
message directly followed by function_call items. Chat upstreams imitate
the text-only assistant message as a complete turn and return
finish_reason=stop before the expected tool call, so long agent tasks end
after a progress update.
Merge pending tool calls into the directly adjacent assistant message when
it carries no tool_calls yet, attach pending reasoning per segment so
parallel calls do not duplicate it, and keep the new-message path for every
other boundary shape (user/tool boundaries, prior tool-call batches, media
flushes). The reasoning placeholder backfill still applies to coalesced
calls without reasoning.
Fixes #6529
Supersedes #6530
Co-authored-by: BigStrongSun <BigStrongSun@users.noreply.github.com>
fix(proxy): skip empty reasoning_content placeholders in Anthropic SSE streaming (#7227)
Some OpenAI-compatible upstreams (GLM, Qwen, DeepSeek-V4-Pro via ModelScope/DashScope,
Bailian, JD Cloud, ...) keep a `reasoning_content: ""` placeholder in every content
chunk instead of omitting the field. The reasoning branch of the OpenAI Chat -> Anthropic
SSE converter only checked for presence, and it shares `current_non_tool_block_type`
with the content branch, so every content chunk closed the open text block, opened an
empty thinking block, then reopened a text block. N content chunks became 2N content
blocks, half of them empty thinking blocks, which Claude Code rendered as one line per
token with dozens of empty Thought blocks and bloated the session JSONL.
Filter empty reasoning before entering the branch, matching the `!content.is_empty()`
guard the content branch already had and the non-streaming `openai_to_anthropic`
converter. Two regression tests pin block index, type and order: the placeholder shape
must yield a single text block and no thinking_delta at all, and real reasoning must
still yield one thinking block followed by one text block.
Fixes #5028
Fixes #4404
fix(codex): detect growing rollouts with a persisted byte cursor (#7219)
Codex keeps the rollout append handle open for the whole session, so on Windows NTFS the file mtime does not advance while the file grows and the pure-mtime gate skipped growing rollouts forever. Persist the observed byte length in session_log_sync.last_byte_offset for Codex rows and skip a file only when both mtime and size are unchanged. The parser now splits records on newlines manually so an incomplete trailing record is retried on the next pass without advancing the line cursor.
Fixes #6060
fix(codex): proxy image edits and Images API follow-ups (#7177)
* fix(codex): proxy image edits and Images API follow-ups
Follow-ups to #7036 for Codex's built-in ImageGen over the local proxy:
- Register `/images/edits` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them through the shared Codex standalone passthrough.
ImageGen posts to `images/edits` whenever a call references existing
images, and the router returned an empty 404 for it.
- Add `CodexStandaloneEndpoint::ImagesEdits`. Both Images endpoints share
one full-URL suffix table, so a complete Responses / Compact / Chat
Completions / Images URL derives the sibling endpoint (and vice versa)
while opaque full URLs keep failing closed.
- Apply the standalone sibling rewrite when the base URL is pasted as a
complete endpoint with the full-URL switch off, mirroring
`codex_chat_base_is_full_endpoint`; standalone requests no longer become
`.../chat/completions/images/generations`.
- Parse Images API streaming usage: `image_generation.completed` carries
`usage` on the event top level, so scan for `usage.input_tokens` before
falling back to the Chat Completions stream parser.
Related #7036
* fix(codex): match standalone URL suffixes case-insensitively
Keep endpoint detection and sibling rewriting consistent while preserving the original URL prefix and query. Cover mixed-case Images and Alpha Search endpoints through URL and proxy regression cases.
---------
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): preserve per-app settings on shutdown and port allocation (#7210)
On normal exit, stop_with_restore_keep_state() called the legacy global
proxy-config writer to clear the long-retired live_takeover_active flag.
That writer reads the Claude row and runs an UPDATE without a WHERE clause,
so every shutdown copied Claude's max_retries and the three timeout fields
onto the Codex, Gemini and Grok Build rows — and the UPDATE never carried
live_takeover_active in the first place, so the step had no effect beyond
the clobber. The ephemeral listen-port path did the same on every proxy
start.
Drop the dead write-back (live-config restore, backup cleanup and health
reset are unchanged) and persist the resolved ephemeral port through the
global-config interface, which only touches the shared listen/logging
columns. Two regression tests seed distinct per-app settings for all four
apps and assert they survive both paths.
Fixes #7204
fix(provider): preserve child metadata during universal sync (#7212)
Syncing a universal provider regenerates its Claude/Codex/Gemini child
providers. The child's `settings_config` was merged with the existing row,
but `meta`, `created_at` and `sort_index` were taken from the generated
object, and the universal provider never carries any of them from the UI.
The UPDATE therefore wiped the child's usage script, common-config opt-out,
endpoint auto-select and other per-app settings to `{}` and reset the sort
index to NULL, which pushed the card to the bottom of the list and looked
like the provider had been deleted and re-created.
Keep the existing child's `meta`, `created_at` and `sort_index` when the
row already exists. Name, base URL, key, models, website and notes are
still driven by the universal provider, and first-time creation still
inherits the parent's metadata.
Fixes #7134
feat(presets): rebrand DashScope as 千问AI平台 and refresh Qwen models (#7183)
Rename the domestic Bailian presets to 千问AI平台 with a dedicated icon, refresh the Qwen family to the 3.8 generation across Claude Code, Claude Desktop, Codex, Hermes, OpenClaw, OpenCode and Pi, add the domestic Token Plan presets, restore the international QwenCloud Coding Plan presets, and align OpenClaw/OpenCode/Pi model metadata with the official client docs.
fix(codex): honor proxy URL when model_provider is omitted (#7263)
When a Codex provider card's TOML has no `model_provider`, Codex falls back to
its built-in `openai` provider, but takeover wrote the proxy address as a
top-level `base_url` that Codex never reads, so requests bypassed the local
proxy and went straight to api.openai.com.
Treat the missing selector as the built-in `openai` provider and write the
proxy address to `openai_base_url`; the existing legacy-reroute migration then
turns it into a `cc-switch` custom provider table carrying the `PROXY_MANAGED`
bearer, the same shape every other third-party takeover uses. Explicit
provider selections are unchanged.
Refs #6256, #7217
fix(provider): update MiniMax defaults to M3 and remove expired offers (#7255)
* fix(provider): refresh MiniMax M3 presets and remove expired promotions
* fix(provider): complete MiniMax M3 capability declarations
---------
Co-authored-by: saladday <1203511142@qq.com>
chore(provider): remove Atlas Cloud sponsor status, keep presets
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`
docs(release): add v3.20.2 release notes
chore(release): v3.20.2
feat(provider): add SoleAPI sponsor presets across all nine apps
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.
fix(codex): proxy image edits endpoint
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.
fix(provider): pin models URL for JieKou and Novita Claude presets
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.
feat(pricing): refresh seed for September 2026 vendor price changes
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.
fix(codex): stamp requires_openai_auth on takeover writes to match the live login state
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.
fix(updater): show the real reason when an update check fails (#6482)
The updater plugin rejects with a plain string, so `err instanceof Error`
never matched and the real failure reason was replaced by the generic
fallback message. Use the existing extractErrorMessage helper, and pass
the recovered message into the check-update failure toast so it actually
reaches the user instead of stopping at an unread context state.
Co-authored-by: Jason <farion1231@gmail.com>
docs: fix the locales path in the README directory trees (#6100)
All four READMEs showed locales/ as a direct child of src/, but the
translations live at src/i18n/locales/. Split it into two tree nodes so it
matches how the rest of the tree is drawn. Also fixes a CLaude -> Claude
typo in README_ZH.md.
Co-authored-by: Jason <farion1231@gmail.com>
fix(claude): exclude workflow journal.jsonl from session list
Claude Code's workflow feature writes subagents/workflows/wf_*/journal.jsonl inside the session directory. The Claude session scanner only excluded agent-* files, so each journal parsed as an empty ghost session titled "journal". Exclude journal.jsonl by file name and add regression tests.
Fixes #6042
fix(i18n): add the missing pi.form.providerKeyDuplicate and common.collapse keys
translatePiProviderMutationError returns pi.form.providerKeyDuplicate for the Pi backend's duplicate-key error, but the key existed in none of the four locale files, so the toast showed the raw key. common.collapse (aria-label of the thinking-map toggle) was missing the same way. Add both keys to zh/en/ja/zh-TW and lock them in the locale coverage test.
fix(proxy): stop mask_url panicking on multi-byte UTF-8 near the truncation boundary
mask_url's fallback branch (used when the proxy URL fails Url::parse) sliced the raw string at byte 20 and panicked when a multi-byte UTF-8 character straddled that offset, e.g. a CJK string pasted into the global proxy address. Snap the cut to the nearest char boundary at or before 20 and add a regression test.
fix(tests): isolate LOCALAPPDATA so tests stop writing the real Claude Desktop config
On Windows, Claude Desktop config paths resolve from LOCALAPPDATA, which the test home override in tests/support.rs did not redirect, so profile_roundtrip wrote the https://desktop.test fixture into the developer's real Claude Desktop config. Point LOCALAPPDATA at the isolated test home alongside HOME and USERPROFILE.
Fixes #6077
fix(usage): widen pricing model source select to fit localized labels
The pricing model source SelectTrigger in the usage cost settings table was w-28, which leaves about 70px for the label; the English and Japanese labels need about 107px and overflowed the trigger. Widen it to w-40 and drop the h-7 overrides so the trigger and the adjacent Input use the shared h-9 height.
feat(pi): add Tencent TokenHub and Token Plan presets (#7159)
Add eight Tencent presets for Pi, covering the TokenHub pay-as-you-go line
(domestic and international) and the six Token Plan subscription products —
Pi was the only managed app still missing them. Model IDs, ordering and
endpoints follow Tencent's own model lists verbatim, and the tests assert each
product's line-up in order so future drift is caught.
Declare thinking control alongside the capabilities rather than leaving it to
defaults: the 31 DeepSeek entries carry a Tencent-specific compat so selecting
"off" actually disables thinking instead of silently billing reasoning tokens,
the eight Kimi K2.7 Code entries no longer offer an "off" level the model
cannot honour, and kimi-k2.7-code-highspeed is marked image-capable so
tool-returned screenshots are no longer dropped.
Drop minimax-m2.5 from every app: it is gone from Tencent's model tables and
the platform is retiring it.
fix(codex): proxy image generations endpoint (#7036)
Codex's built-in ImageGen calls the legacy OpenAI Images API, but the local
proxy only registered Responses / Chat Completions / Compact / Alpha Search
routes, so `/v1/images/generations` returned an empty 404 under Codex routing.
- Register `/images/generations` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them as a Codex standalone passthrough shared with Alpha Search.
- Generalize the Alpha Search full-URL rewrite into
`rewrite_codex_standalone_full_url`: derive the sibling
`/images/generations` from full Responses / Compact / Chat Completions URLs,
keep an existing Images URL as-is, and fail closed on opaque full URLs.
- Record `usage.input_tokens` / `output_tokens` through the Codex usage parser.
Fixes #5429
Fixes #6745
feat(pricing): add Gemini 3.8 Flash pricing (#7164)
Seeds the built-in pricing row for Gemini 3.8 Flash at 0.75 / 3.75 input/output per million tokens, 0.075 cache read, no cache-write charge, plus a regression test locking all four fields.
Values verified against Google's own pricing page (introductory price through 2026-12-31; list price 1.50 / 7.50 / 0.15 from 2027-01-01) and models.dev.
feat(provider): extend PPIO presets for Pi (#6870)
Add a PPIO provider preset for Pi using the OpenAI-compatible endpoint,
and pin an explicit models URL on the Claude Code PPIO preset so model
discovery resolves against https://api.ppio.com/openai/v1/models instead
of the derived candidates, which all return 404.
Co-authored-by: Jason <farion1231@gmail.com>
feat(pricing): add GLM-5.3 Flash pricing (#7163)
fix(proxy): enable parallel tool calls for Codex OAuth (#7024)
Codex OAuth requests defaulted parallel_tool_calls to false when the incoming
Anthropic request carried no explicit value, forcing Claude Code into one tool
call per model turn. Default it to true (matching codex-rs), map Anthropic
tool_choice.disable_parallel_tool_use to the inverse Responses value so explicit
serial execution is preserved, and add regression coverage for both directions.
Carries #5722 forward on top of current main.
Fixes #5719
Co-authored-by: 太一 <taiyi@douyu.tv>
feat(pricing): add GPT-6 Astra pricing (#7162)
fix(codex): resolve input_modalities for unknown models in vendor catalog (#6750)
When a model slug is not in the bundled DeepSeek official catalog, the vendor path clones the flagship entry and previously inherited its text-only input_modalities. Resolve modalities for unmatched slugs through the same registry/fail-open logic the non-vendor path uses, so a vision variant such as deepseek-v4-flash-vision-exp is no longer declared text-only. Explicit user inputModalities still win; matched models keep the vendor's declaration verbatim.
Fixes #6725
fix(claude): expose Opus 5 and Sonnet 5 in takeover mode (#5882)
Bump the stable client-facing role aliases that takeover mode writes to
Claude Code: Opus claude-opus-4-8 -> claude-opus-5 and Sonnet
claude-sonnet-4-6 -> claude-sonnet-5, so the /model menu matches the
Claude Desktop default routes and preset defaults that already moved to
Opus 5 / Sonnet 5. Add "opus-5" to the adaptive-thinking classifier so
the Bedrock thinking optimizer, which runs on the client alias before
model mapping, keeps sending adaptive thinking instead of the removed
budget_tokens.
Routing is unaffected: the proxy maps client aliases by role keyword, and
the aliases are rewritten automatically the next time the proxy starts.
Fixes #5876
Co-authored-by: Jason <farion1231@gmail.com>
feat(pricing): add GLM-5.3 to built-in model pricing table (#6591)
Seed glm-5.3 at $1.4/$4.4 per MTok with $0.26 cache read, matching the
official Z.ai list price (identical to the GLM-5.2 tier):
https://docs.z.ai/guides/overview/pricing
New model id, so seed only; no repair entry and no SCHEMA_VERSION bump.
Existing installs pick the row up on next launch via INSERT OR IGNORE, and
the startup cost backfill re-prices historical glm-5.3 rows recorded at $0.
fix(codex): align OAuth client identity for GPT-6 (#7132)
Requests for gpt-6-astra through the Claude-to-Codex OAuth takeover route advertised Codex 0.144.1, below the model's 0.153.0 minimal_client_version, so ChatGPT's backend rejected them with HTTP 400 "requires a newer version of Codex". Bump the advertised client version to 0.153.4 and share the originator/version constants between generation and model discovery, which previously sent cc-switch's own package version and originator.
Two regression tests pin the generation headers and the model-list request to a version at or above the gpt-6-astra floor.
Refs #7129
fix(codex): disable supports_search_tool for DeepSeek catalog models (#6653)
DeepSeek's Responses API has no tool_search support, but the bundled
official catalog declared supports_search_tool=true for deepseek-v4-pro
and deepseek-v4-flash. Codex gates…
…iders at second position
- ProviderList: context menu with quick move-to-top/move-to-bottom, reusing
update_providers_sort_order and refreshing tray menu like drag sort
- useAddProviderMutation: default insert at index 1 with explicit sortIndex
materialization for existing items (avoids defined-first sort trap)
- i18n: quickMove* strings for zh/en/zh-TW/ja
ci: stop flaky WSL2 linker job from blocking main
The backend-windows-wsl2 job intermittently fails with LNK1327 /
mt.exe c1010070 when the Windows runner's mt.exe cannot parse a
manifest temp file on the WSL2 UNC TEMP path. The same flaky failure
occurs upstream and is unrelated to fork changes. Mark the job
continue-on-error so main stops going red on it; the result stays
visible in the Actions UI.
style: apply prettier to files touched by recent comet archives
Frontend CI format:check failed on 6 files modified by the app-name
and model-quick-switch archive commits plus the updater-disable commit.
Run prettier --write so format:check passes again.
chore: archive ccs-fork-app-name-cc-switch
chore: archive ccs-model-quick-switch-ui-polish
docs: rewrite README to document fork differences vs upstream
README 从上游完整功能介绍改为仅记录 fork 与上游的差别,完整功能
文档指回上游。涵盖 8 项 fork 魔改:构建标识/开发预览、预设精简、
Claude 表单调整、模型搜索移植、主页模型快速切换、侧边栏面板显隐、
关闭应用内更新、打包范围,并说明版本号与 rebase 同步流程。
feat: disable in-app updater, point users to GitHub releases
移除 tauri-plugin-updater 依赖与自动更新链路,改为统一引导用户到
GitHub Releases 手动下载新版。
- 前端:updater.ts 恒返回 up-to-date;UpdateContext 取消启动自检;
AboutSection「检查更新」直接打开 fork releases 页;DatabaseUpgrade
恢复界面去掉下载进度条,改为打开发布页。
- 后端:移除 updater 插件注册与 UpdaterExt 调用;
install_update_and_restart 降级为空壳,check_app_update_available
返回当前版本号;check_for_updates 指向 fork releases;capabilities
去掉 updater:default 权限。
- 打包:tauri.conf.json 删 plugins.updater、createUpdaterArtifacts=false;
Cargo.toml / package.json 删 updater 依赖,Cargo.lock / pnpm-lock
同步重生成。
- CI:release.yml 移除签名收集与 assemble-latest-json job,删除
.tar.gz/.sig 处理,release body 去掉 updater 说明。
版本号统一为 3.20.0-1(合法 semver 且 MSI 第 4 位为 1)。
Cargo.lock / pnpm-lock.yaml 已随依赖移除重生成,cargo check 通过。
ci: build only Windows x64 and macOS arm64, unsigned
feat: gate dev panel behind dev-only env flag
chore(comet): archive ccs-sidebar-panels-visibility change
fix: apply skills visibility guard to hermes branch toolbar button
feat: include prompts in sidebar panel visibility settings
fix: persist visibleSidebarPanels setting in backend AppSettings
style: use compact pill toggles for sidebar panel visibility
feat: add sidebar panel visibility switches to settings
feat: add sidebar panel visibility settings and wire into toolbar buttons
chore(comet): archive ccs-home-model-display change
fix(fork): read grokbuild api key from config TOML in quick switch dialog
feat(fork): align model badge with upstream extractModelBadge
feat(fork): show current model badge and quick-switch entry on provider cards
feat(fork): add model quick switch dialog
feat(fork): add provider model read/write utils with tests
chore(comet): archive ccs-model-search-port change
feat(i18n): add model search picker keys for all locales
feat(form): port SearchableModelPicker from upstream for searchable model selection
chore(comet): archive ccs-claude-form-layout change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-claude-form-layout/
- Sync claude-form-layout spec to main specs
- Annotate design doc as archived
- Add verification report
fix(i18n): update advanced options hint, add ja/zh-TW toggle-all, drop dead apiFormatHint key
fix(form): correct 1M toggle-all alignment direction (ml-1)
fix(form): align 1M toggle-all checkbox with row checkboxes
refactor(form): move api format select into EndpointField rightSlot
fix(form): widen api format select to fit option text
feat(form): add 1M toggle-all checkbox in model mapping header
feat(form): move api format select next to endpoint input
chore(comet): archive ccs-provider-presets-trim change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-provider-presets-trim/
- Sync provider-presets-visibility spec to main specs
- Annotate design doc as archived
- Add verification report
docs(fork): add presets-trim files to rebase whitelist
test(fork): mock IS_FORK_BUILD false in unit tests for upstream-form semantics
feat(fork): wire official preset filter into provider forms
feat(fork): add hidden field to preset interfaces
feat(fork): add official preset allowlist and filter util
chore(comet): archive ccs-fork-scaffolding change
- Move change artifacts to docs/openspec/changes/archive/2026-08-18-ccs-fork-scaffolding/
- Sync fork-build-identity spec to main specs
- Annotate design doc as archived
- Add verification report
feat(fork): add dev config with isolated identifier and config dir
docs(fork): complete rebase whitelist with all fork-touched files
fix(fork): guard setTitle with isTauri in test/non-tauri env
fix(fork): define __CCS_FORK_BUILD__ in vitest config
feat(fork): add DevPanel and settings footer entry
feat(fork): set macOS window title to CC Switch (Fork) on startup
feat(fork): inject __CCS_FORK_BUILD__ compile-time constant
feat(fork): bump version to 3.19.2-fork.1 and rename product to CC Switch (Fork)
docs(fork): add upstream rebase workflow and ignore tool artifacts
fix(proxy): accept the whole grok-4.x (x>=5) family in the reasoning-effort whitelist (#7369)
Replace the verbatim grok-4.5 / grok-4.6 entries in supports_reasoning_effort with a rule that parses the grok-4.x minor version and accepts x >= 5, mirroring the existing GPT-5+ rule. This covers grok-4.7 (released 2026-09-21), whose reasoning effort was previously dropped on the Claude -> Chat, Claude -> Responses and Codex Responses -> Chat conversion paths, and lets future releases pass without another whitelist edit. The grok-build-* family is retained for saved providers.
Co-authored-by: allenxu09 <171831965+allenxu09@users.noreply.github.com>
docs(release): add v3.20.4 release notes
chore(release): v3.20.4
feat(pricing): add Grok 4.7 and GLM-5.3-FlashX
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.
fix(i18n): remove outdated OpenCode Go referral offer
fix: add SudoCode.chat alternate API endpoint
feat(settings): add a GitHub star prompt to the About card
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
feat(presets): add Soshow aggregator presets
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.
fix(ci): stabilize frontend tests and verify renderer build
fix(mcode): refresh provider state after removal (#7578)
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
fix(lib): reset skip_taskbar before window.show() across entry points (#6348)
5处 window.show() 调用中 4处没有在 show() 前重置 Windows 的 skip_taskbar,导致以下场景出现窗口可见但不在任务栏、点最小化后窗口消失的 bug:
- single_instance handler (lib.rs:360-361):当原进程因 silent_startup / 关闭到托盘等处于 skip_taskbar(true) 状态时,从快捷方式/开始菜单唤起已运行实例触发。
- handle_deeplink_url 的 focus_main_window 分支 (lib.rs:256):通过 ccswitch:// URL 唤起时。
- DB 恢复流程 (lib.rs:556):数据库版本不匹配时显示恢复界面。
- RunEvent::Opened 处理器 (lib.rs:1803):系统接收到 deep-link URL 时。
修复后的行为与 tray.rs::handle_tray_menu_event::show_main 分支一致(那里已有相同的 set_skip_taskbar(false) 调用)。
经用户测试验证:autostart + silent_startup + 从开始菜单/快捷方式启动的场景下,最小化按钮恢复正常工作。
fix(proxy): omit missing tool descriptions in the Codex responses-to-chat converter (#7378)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): preserve max effort for GPT-5.6 and GPT-6 Astra (#7531)
fix(misc): ignore shell startup output when probing tool versions in WSL (#7348)
try_get_version_wsl runs "${SHELL:-sh}" -lic '<tool> --version' through
wsl.exe and takes the first x.y.z anywhere in stdout. A login+interactive
shell runs the distro's startup files first, so their output shares that
stdout. On Ubuntu, /etc/profile.d/update-motd.sh prints the MOTD once a day,
starting with "Welcome to Ubuntu 24.04.4 LTS", and the card then showed
24.04.4 as the Claude Code version. The once-a-day stamp makes it appear
after the first probe of the day and vanish on refresh.
The payload now prints a sentinel before running the tool, and parsing looks
only at what follows its last occurrence. The last occurrence handles the
-lic || -lc || -c fallback chain printing it several times; matching it as a
substring handles OSC sequences glued in front of it. If the sentinel is
absent (the shell failed before the payload ran), the output is used as
before. The stderr fallback for tools that print their version there is kept.
Fixes #7347
Co-authored-by: lyfuci <lyfuci11@gmail.com>
fix(claude): hide session URL attribution (#7053)
* fix(claude): hide session URL attribution
* docs(claude): document session URL attribution
fix(ui): reset provider scroll when switching apps (#5211)
fix(pi): fix provider logo (#6826)
fix(i18n): use the Pi form's own term for the zh-TW duplicate-key message (#7187)
Follow-up to the nit on #6768. In zh-TW the Pi form labels the field
供應商識別碼 and says 供應商識別碼為必填 when it is empty, but the
duplicate message I added said 供應商標識 — the term Hermes, OpenCode and
OpenClaw use for their own field. One form, two names for the same thing.
The other three locales already keep the label and both messages on one
term, so this makes zh-TW match the pattern rather than introduce one:
en Provider key / Provider key is required / This provider key already exists
ja プロバイダーキー / プロバイダーキーを入力してください / このプロバイダーキーは既に存在します
zh 供应商标识 / 供应商标识不能为空 / 该供应商标识已存在
zh-TW 供應商識別碼 / 供應商識別碼為必填 / 該供應商標識已存在 <- the odd one out
hermes.form.providerKeyDuplicate carries the same sentence and is left
alone: its own label is 供應商標識, so it is already consistent.
Verified: managementListLocales 7/7. Full unit suite 1085/1087, the same
two App.test.tsx integration cases failing before and after.
fix(proxy): strip stop param for GitHub Copilot to unblock auto mode classifier (#5404)
Claude Code's auto mode classifier sends `stop_sequences` on every
safety check. On the GitHub Copilot path the Anthropic -> OpenAI Chat
conversion mapped it to `stop`, and Copilot's Chat Completions route
for Claude models answers that request with HTTP 400, so every
classifier call failed while normal chat kept working.
- Remove the converted `stop` field only when the provider is GitHub
Copilot; other OpenAI-compatible upstreams keep it.
- Copilot detection in the transform now also covers providers that
are recognised only through `env.ANTHROPIC_BASE_URL`.
- Regression tests for the Copilot (meta and env URL) and generic
provider cases.
Fixes #5175
fix(codex): keep stored auth when the live snapshot has no credential (#7434)
Editing the current Codex provider seeds the form from the live ~/.codex
snapshot, and switching away backfills that snapshot into the DB row. When a
provider table declares its own credential source (an Authorization header,
env_key, auth/aws), no bearer token is injected into config.toml and the
shared auth.json is deleted on third-party switches, so the live snapshot is
permanently `{ auth: {}, config }` while auth.OPENAI_API_KEY in the DB is the
only copy of the key. Both paths treated that empty live auth as
authoritative and silently erased the stored key, which in turn made the
usage-script modal misread the provider as an official subscription and
reset the saved script.
- Treat a credential-less live auth (missing auth.json or the bare
auth_mode logout marker) as an absent field in both the edit dialog and
the switch-away backfill, keeping the stored auth template. Live still
wins whenever it carries material, and official providers keep the live
login as their source of truth.
- Gate the official exemption on is_codex_official_provider /
resolveCodexOfficialIdentity rather than the category alone: a legacy
fixed official card without a persisted category is still written as
official, so the fallback must not resurrect a logged-out login on
switch-back.
- Cover both layers with regression tests, including the category-less
official shapes.
Fixes #7433
Supersedes #7492
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): lift additional_tools input carriers on the Codex chat conversion (#7454)
Codex talks to third-party providers over its Responses Lite transport,
which ships the request's extra tools in an `additional_tools` input
carrier: `{"type":"additional_tools","role":"developer","tools":[...]}`,
a role but no `content`. The Responses->Chat conversion let that carrier
fall through the generic "item with a role is a message" arm and emitted
`{"role":"system","content":null}` at messages[1], which strict Chat
gateways reject with `messages[1]: missing field "content"` (or
`data did not match any variant of ...SystemMessageContent`). The tools
carried inside it were also dropped on the Chat and Anthropic conversions.
- Skip `additional_tools` items in the Chat message walk; a carrier
declares tools, it is not a message.
- Collect carrier tools through the existing CodexToolContext registry
(dedup, namespace flattening, custom-tool conversion), which also feeds
the Anthropic conversion and the response-side name round-trip.
- Requests without a carrier convert byte-for-byte as before.
Known boundary: `custom` children inside a namespace tool
(`functions.exec`) are still not lifted; that needs its own response-side
name round-trip and is left for a follow-up.
Diagnosed independently in #6158 by BigStrongSun (fix in #6159) and in
fixture.
Fixes #7451
Fixes #6158
Refs #6033
Refs #6776
Supersedes #6159
fix(proxy): normalize Codex detail:original images for Chat gateways (#7476)
Codex marks tool-returned images with detail:"original" whenever the model
catalog advertises supports_image_detail_original, a Responses-only value.
The Responses->Chat conversion copied it verbatim into image_url.detail, and
strict OpenAI-compatible Chat gateways (Ark/Doubao, SenseNova, GLM, vLLM, ...)
reject the whole request with 400. Because Codex replays the full rollout on
every turn, one poisoned view_image result made the session fail permanently,
including plain-text turns.
Two layers:
- tool_media::merge_top_level_detail downgrades original -> auto, so already
poisoned rollouts and Codex versions before 0.155.0 (which do not normalize
replayed history themselves) recover immediately. auto/low/high are untouched.
- codex_catalog_model_entry always writes supports_image_detail_original:false
on the ProxyChat profile, so Codex stops emitting original at the source once
the catalog is regenerated. NativeResponses/Anthropic profiles and the DeepSeek
official catalog are unaffected.
Top-level user images were never affected: responses_content_to_chat_content
drops the part-level detail entirely.
Fixes #6495
Fixes #7458
Refs #6569
Refs #5805
fix(omo): detect unified config on the WSL side when the OpenCode dir is in WSL (#7550)
When the OpenCode config directory points into WSL
(\\wsl.localhost\<distro>\home\<user>\.config\opencode), OMO >= 4.19.3 keeps
its unified config in the WSL-side home (~/.omo/omo.jsonc). Detection only
probed the Windows home, never found it, and silently fell back to the legacy
per-plugin file that OMO no longer reads, so provider switches looked
successful but had no effect.
- Add derive_wsl_home_dir to derive the WSL-side home from a WSL UNC path
(home/<user> or root); non-WSL and non-home paths return None.
- Probe a list of home candidates for the unified config, WSL-side home
first and the OS home as a fallback, shared by the read, write and delete
paths.
- Detection only: when no unified config exists on either side the legacy
fallback is unchanged.
Fixes #7363
Supersedes #7367
docs(partner): add a FluxA purchase link and tighten its sponsor blurb
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.
fix(claude-desktop): expose the 1M context variant for DeepSeek V4 routes
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.
feat(presets): add FluxA Token Plan partner presets
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.
fix(presets): correct AICodeWith Codex-family endpoint to /v1
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.
fix(ui): render monochrome preset icons in the foreground color
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.
docs(user-manual): point Chat-only Codex examples at plan endpoints
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.
fix(pricing): restore DeepSeek V4 Pro price and seed Qwen3.8 and Hy4 models
- 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).
fix(presets): move MiniMax CN and BaiLing to their current official endpoints
- 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.
feat(codex): refresh CN presets from the Responses API audit
- 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.
test(claude-desktop): run Linux path tests on unix hosts and cover Linux in provider sync tests
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.
fix(ui): stop expanding stale cached usage tiers on ineligible provider cards
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.
fix(skills): 支持 skillId 与目录名不一致的技能安装和更新 (#6381)
* fix(skills): resolve mismatched skillId directories
* fix(skills): preserve source path during updates
* fix(skills): refresh source URL after repository relocation
* test(skills): isolate relocation fixture from legacy home
feat(opencode): 支持从获取的模型列表中搜索并批量添加所选模型 (#7515)
* feat: implement fetched models selection and UI components
* fix(opencode): prevent model search from submitting provider form
Add Kimi Global preset variants (kimi.ai platform + api endpoints) (#7526)
Add a "Kimi Global" twin immediately after the domestic "Kimi" API preset and a "Kimi For Coding Global" twin after the domestic "Kimi For Coding" preset, in every preset file that ships them (claude, claude-desktop, codex, hermes, openclaw, opencode, pi).
"Kimi Global" points to the international open platform https://platform.kimi.ai?aff=cc-switch (apiKeyUrl mirrored as https://platform.kimi.ai/console/api-keys?aff=cc-switch) and swaps the endpoint host api.moonshot.cn for api.moonshot.ai with each file's path unchanged (/v1 or /anthropic).
"Kimi For Coding Global" points to the international site https://www.kimi.ai/code/?aff=cc-switch and swaps the endpoint host api.kimi.com for api.kimi.ai with the path unchanged (e.g. https://api.kimi.ai/coding/v1).
Domestic presets are unchanged; category, icons, apiFormat, model catalogs, endpointCandidates (host swapped), partnerPromotionKey and settings otherwise mirror the domestic blocks exactly.
primePartner stays only on the domestic presets so the new variants do not alter prime-partner ordering.
The Pi twins get distinct providerKey values ("cc-switch-kimi-global", "cc-switch-kimi-for-coding-global") because providerKey is the preset-level primary key and must not collide with the domestic presets.
Rebalance sponsor CTAs around Kimi Code plan with dual-region links (#7522)
Kimi Code plan purchases have resumed, so the README sponsor sections go back to leading with the coding plan instead of the API-only flow written during the purchase pause.
All four locales (EN/ZH/JA/DE) now present Kimi Code plan (中文站 | Global) as the primary CTA and the Kimi Open Platform API (中文站 | Global, with the first-top-up bonus) as the secondary one, mirroring the domestic|global split already used for API links.
The aff and track_id values are unchanged.
fix(prompts): refresh active prompts after external file edits (#7194)
Reload the enabled prompt from its live file when the Prompts list loads, and refresh the open panel when the window regains focus, so external edits to CLAUDE.md / AGENTS.md show up without a restart.
- Keep saved templates available when the live file cannot be read.
- Skip the backfill for empty or whitespace-only files, matching enable_prompt.
- Skip the backfill while a sync/restore holds the sync lock, so a list load cannot write a stale live file into a freshly restored database.
fix(skills): raise archive entry limit so large skill repos install (#7489)
Installing hugohe3/ppt-master from skills.sh failed with
ARCHIVE_TOO_MANY_ENTRIES: the GitHub archive we download is the whole
repository (13,248 entries), and the 10,000-entry cap added in #5811
treated it as a zip bomb.
- Raise MAX_ARCHIVE_ENTRIES to 30,000 for both the remote archive and
local ZIP paths, and align WebDAV's MAX_EXTRACT_ENTRIES so a skills.zip
containing such a skill still restores.
- Charge every file entry at least one 4 KiB block against the existing
512 MiB budget, mirroring directory accounting, so empty-file archives
are bounded by the byte budget rather than by the entry count alone.
- Add tests for a 13k-entry repo archive and for the per-file minimum
charge.
Fixes: https://github.com/farion1231/cc-switch/issues/7475
feat: add MiniMax Code harness support (#7383)
Add MiniMax Code (mcode) as a managed application: provider and model management through the native custom_provider configuration, MCP servers, Skills, global instructions (AGENTS.md), read-only local session history with resume, and usage import from the native runtime database. Native files shared by the TUI and desktop are written under the native lock with rollback when the database commit fails, unrelated native configuration is preserved, and the data directory honors MINIMAX_DATA_DIR and MAVIS_DATA_DIR. Deleting sessions stays in the native application because they are coupled to runtime state.
fix(codex): recover stale account bindings during takeover (#7395)
After deleting a managed ChatGPT account and logging in again, a Codex provider can still reference the old local account ID, which made route switching, takeover, startup restoration and rebinding fail permanently.
Distinguish a missing outgoing account from an existing account with no matching live token. A missing account only releases its ownership marker after the persisted account store is verified; the target write policy stays in force. A stale target now reports an account-selection instruction instead of a bare not-found error, including when takeover is already enabled.
Fixes #7392
feat(claude-desktop): support 3P configuration on Linux (#7331)
Add Claude Desktop 3P configuration support on Linux. Resolve the config root from an absolute XDG_CONFIG_HOME, falling back to ~/.config; when CC Switch itself runs as a Flatpak, use the host ~/.config instead of the sandbox-private XDG_CONFIG_HOME. Update the en/zh/ja user manuals with Linux paths and the Flatpak boundary.
Fixes #4855
fix(proxy): omit missing tool descriptions instead of serializing null (#7319)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): keep reasoning effort for grok-4.6 and pass xhigh through verbatim (#7318)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(models): support Zhipu OpenAI Responses model lists (#7330)
Zhipu's Responses endpoint (open.bigmodel.cn/api/v1) serves its model list in the Codex remote catalog shape (models[].slug) instead of the OpenAI-compatible data[].id shape, so the Codex form's model fetch came back empty. The parser now falls back to models[].slug when data is absent.
fix(misc): fetch npm dist-tags from the dedicated endpoint with the probe timeout (#7346)
The tool version probe requested the full npm packument for each package (metadata for every historical version, 14-25 MB uncompressed for codex / opencode / openclaw) only to read its dist-tags, and it was the only latest-version probe without LATEST_PROBE_TIMEOUT, so it fell back to the proxy client's 600 s total timeout and the cards sat on "loading" for minutes on slow links while the large downloads starved the Hermes GitHub probe into "unknown".
Switch fetch_npm_dist_tags to registry.npmjs.org/-/package/{package}/dist-tags, whose body is the dist-tags object itself (tens of bytes to a few KB), escape scoped names as %2f, and apply the same 15 s probe timeout as the GitHub and PyPI probes. No data is lost: the only consumer reads latest and the tool's prerelease tags.
Fixes #7339
feat(usage): display output tokens per second in request log table and detail panel (#3369)
Show tokens/s next to the output token count in the request log table and the request detail panel.
The generation window prefers an explicit duration when present, otherwise latency minus first-token time for streaming requests, and falls back to full latency for non-streaming requests. Rows without a positive token count or window show nothing.
Closes #5936
fix(presets): update APIKEY.FUN URLs to apikey.fan
docs(release): add v3.20.3 release notes
chore(release): v3.20.3
fix(pricing): move retired DeepSeek V4 family to the V4.1 Flash tier
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.
fix(presets): declare third-party Token Plan DeepSeek V4 rows text-only
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.
feat(presets): rename DouBaoSeed preset to localized Volcengine Doubao
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.
fix(codex): skip unchanged incomplete rollout tails
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.
fix(claude): align quick model selection with form order
fix(tray): show bound ChatGPT account quota for managed Codex cards
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
feat(claude): add "Disable Artifact Tool" quick toggle to provider editor
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.
feat(codex): refresh aggregator model presets and catalogs
feat(codex): switch Kimi presets to native Responses direct-connect
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.
fix(usage): show Claude Fable weekly limit from the usage API limits array
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.
fix(codex): mirror vision-capable DeepSeek catalog and un-gate the legacy v4-flash alias (#7286)
Refresh the bundled DeepSeek official Codex catalog to the vendor's current models.json (slug deepseek-flash, image input modality, supports_image_detail_original) and remove deepseek-v4-flash from the confirmed text-only registry: the legacy id is still accepted by DeepSeek and routed to the vision-capable V4.1 Flash, so both the proxy media sanitizer and the generated Codex catalog must fail open for it. deepseek-v4-pro stays text-only per the vendor catalog.
Fixes #7283
fix(proxy): clamp sub-floor max_tokens to the Responses API minimum (#7287)
The Responses API rejects max_output_tokens below 16, but Anthropic clients legitimately send tiny probe budgets (Claude Desktop's model-availability probe uses max_tokens=1). The Anthropic→Responses conversion copied the value verbatim, so strict upstreams returned 400 and Claude Desktop reported the mapped model as unavailable under local routing.
Clamp 1..=15 up to 16 during conversion; 16+, 0 and non-integer values keep their pass-through semantics. Normal session requests convert byte-for-byte as before, and the Codex OAuth path still strips max_output_tokens afterwards.
Fixes #7103
fix(codex): coalesce adjacent commentary with pending tool calls (#7280)
The Codex Responses -> Chat Completions converter emitted two consecutive
assistant messages when one Responses model turn contained a commentary
message directly followed by function_call items. Chat upstreams imitate
the text-only assistant message as a complete turn and return
finish_reason=stop before the expected tool call, so long agent tasks end
after a progress update.
Merge pending tool calls into the directly adjacent assistant message when
it carries no tool_calls yet, attach pending reasoning per segment so
parallel calls do not duplicate it, and keep the new-message path for every
other boundary shape (user/tool boundaries, prior tool-call batches, media
flushes). The reasoning placeholder backfill still applies to coalesced
calls without reasoning.
Fixes #6529
Supersedes #6530
Co-authored-by: BigStrongSun <BigStrongSun@users.noreply.github.com>
fix(proxy): skip empty reasoning_content placeholders in Anthropic SSE streaming (#7227)
Some OpenAI-compatible upstreams (GLM, Qwen, DeepSeek-V4-Pro via ModelScope/DashScope,
Bailian, JD Cloud, ...) keep a `reasoning_content: ""` placeholder in every content
chunk instead of omitting the field. The reasoning branch of the OpenAI Chat -> Anthropic
SSE converter only checked for presence, and it shares `current_non_tool_block_type`
with the content branch, so every content chunk closed the open text block, opened an
empty thinking block, then reopened a text block. N content chunks became 2N content
blocks, half of them empty thinking blocks, which Claude Code rendered as one line per
token with dozens of empty Thought blocks and bloated the session JSONL.
Filter empty reasoning before entering the branch, matching the `!content.is_empty()`
guard the content branch already had and the non-streaming `openai_to_anthropic`
converter. Two regression tests pin block index, type and order: the placeholder shape
must yield a single text block and no thinking_delta at all, and real reasoning must
still yield one thinking block followed by one text block.
Fixes #5028
Fixes #4404
fix(codex): detect growing rollouts with a persisted byte cursor (#7219)
Codex keeps the rollout append handle open for the whole session, so on Windows NTFS the file mtime does not advance while the file grows and the pure-mtime gate skipped growing rollouts forever. Persist the observed byte length in session_log_sync.last_byte_offset for Codex rows and skip a file only when both mtime and size are unchanged. The parser now splits records on newlines manually so an incomplete trailing record is retried on the next pass without advancing the line cursor.
Fixes #6060
fix(codex): proxy image edits and Images API follow-ups (#7177)
* fix(codex): proxy image edits and Images API follow-ups
Follow-ups to #7036 for Codex's built-in ImageGen over the local proxy:
- Register `/images/edits` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them through the shared Codex standalone passthrough.
ImageGen posts to `images/edits` whenever a call references existing
images, and the router returned an empty 404 for it.
- Add `CodexStandaloneEndpoint::ImagesEdits`. Both Images endpoints share
one full-URL suffix table, so a complete Responses / Compact / Chat
Completions / Images URL derives the sibling endpoint (and vice versa)
while opaque full URLs keep failing closed.
- Apply the standalone sibling rewrite when the base URL is pasted as a
complete endpoint with the full-URL switch off, mirroring
`codex_chat_base_is_full_endpoint`; standalone requests no longer become
`.../chat/completions/images/generations`.
- Parse Images API streaming usage: `image_generation.completed` carries
`usage` on the event top level, so scan for `usage.input_tokens` before
falling back to the Chat Completions stream parser.
Related #7036
* fix(codex): match standalone URL suffixes case-insensitively
Keep endpoint detection and sibling rewriting consistent while preserving the original URL prefix and query. Cover mixed-case Images and Alpha Search endpoints through URL and proxy regression cases.
---------
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): preserve per-app settings on shutdown and port allocation (#7210)
On normal exit, stop_with_restore_keep_state() called the legacy global
proxy-config writer to clear the long-retired live_takeover_active flag.
That writer reads the Claude row and runs an UPDATE without a WHERE clause,
so every shutdown copied Claude's max_retries and the three timeout fields
onto the Codex, Gemini and Grok Build rows — and the UPDATE never carried
live_takeover_active in the first place, so the step had no effect beyond
the clobber. The ephemeral listen-port path did the same on every proxy
start.
Drop the dead write-back (live-config restore, backup cleanup and health
reset are unchanged) and persist the resolved ephemeral port through the
global-config interface, which only touches the shared listen/logging
columns. Two regression tests seed distinct per-app settings for all four
apps and assert they survive both paths.
Fixes #7204
fix(provider): preserve child metadata during universal sync (#7212)
Syncing a universal provider regenerates its Claude/Codex/Gemini child
providers. The child's `settings_config` was merged with the existing row,
but `meta`, `created_at` and `sort_index` were taken from the generated
object, and the universal provider never carries any of them from the UI.
The UPDATE therefore wiped the child's usage script, common-config opt-out,
endpoint auto-select and other per-app settings to `{}` and reset the sort
index to NULL, which pushed the card to the bottom of the list and looked
like the provider had been deleted and re-created.
Keep the existing child's `meta`, `created_at` and `sort_index` when the
row already exists. Name, base URL, key, models, website and notes are
still driven by the universal provider, and first-time creation still
inherits the parent's metadata.
Fixes #7134
feat(presets): rebrand DashScope as 千问AI平台 and refresh Qwen models (#7183)
Rename the domestic Bailian presets to 千问AI平台 with a dedicated icon, refresh the Qwen family to the 3.8 generation across Claude Code, Claude Desktop, Codex, Hermes, OpenClaw, OpenCode and Pi, add the domestic Token Plan presets, restore the international QwenCloud Coding Plan presets, and align OpenClaw/OpenCode/Pi model metadata with the official client docs.
fix(codex): honor proxy URL when model_provider is omitted (#7263)
When a Codex provider card's TOML has no `model_provider`, Codex falls back to
its built-in `openai` provider, but takeover wrote the proxy address as a
top-level `base_url` that Codex never reads, so requests bypassed the local
proxy and went straight to api.openai.com.
Treat the missing selector as the built-in `openai` provider and write the
proxy address to `openai_base_url`; the existing legacy-reroute migration then
turns it into a `cc-switch` custom provider table carrying the `PROXY_MANAGED`
bearer, the same shape every other third-party takeover uses. Explicit
provider selections are unchanged.
Refs #6256, #7217
fix(provider): update MiniMax defaults to M3 and remove expired offers (#7255)
* fix(provider): refresh MiniMax M3 presets and remove expired promotions
* fix(provider): complete MiniMax M3 capability declarations
---------
Co-authored-by: saladday <1203511142@qq.com>
chore(provider): remove Atlas Cloud sponsor status, keep presets
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`
docs(release): add v3.20.2 release notes
chore(release): v3.20.2
feat(provider): add SoleAPI sponsor presets across all nine apps
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.
fix(codex): proxy image edits endpoint
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.
fix(provider): pin models URL for JieKou and Novita Claude presets
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.
feat(pricing): refresh seed for September 2026 vendor price changes
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.
fix(codex): stamp requires_openai_auth on takeover writes to match the live login state
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.
fix(updater): show the real reason when an update check fails (#6482)
The updater plugin rejects with a plain string, so `err instanceof Error`
never matched and the real failure reason was replaced by the generic
fallback message. Use the existing extractErrorMessage helper, and pass
the recovered message into the check-update failure toast so it actually
reaches the user instead of stopping at an unread context state.
Co-authored-by: Jason <farion1231@gmail.com>
docs: fix the locales path in the README directory trees (#6100)
All four READMEs showed locales/ as a direct child of src/, but the
translations live at src/i18n/locales/. Split it into two tree nodes so it
matches how the rest of the tree is drawn. Also fixes a CLaude -> Claude
typo in README_ZH.md.
Co-authored-by: Jason <farion1231@gmail.com>
fix(claude): exclude workflow journal.jsonl from session list
Claude Code's workflow feature writes subagents/workflows/wf_*/journal.jsonl inside the session directory. The Claude session scanner only excluded agent-* files, so each journal parsed as an empty ghost session titled "journal". Exclude journal.jsonl by file name and add regression tests.
Fixes #6042
fix(i18n): add the missing pi.form.providerKeyDuplicate and common.collapse keys
translatePiProviderMutationError returns pi.form.providerKeyDuplicate for the Pi backend's duplicate-key error, but the key existed in none of the four locale files, so the toast showed the raw key. common.collapse (aria-label of the thinking-map toggle) was missing the same way. Add both keys to zh/en/ja/zh-TW and lock them in the locale coverage test.
fix(proxy): stop mask_url panicking on multi-byte UTF-8 near the truncation boundary
mask_url's fallback branch (used when the proxy URL fails Url::parse) sliced the raw string at byte 20 and panicked when a multi-byte UTF-8 character straddled that offset, e.g. a CJK string pasted into the global proxy address. Snap the cut to the nearest char boundary at or before 20 and add a regression test.
fix(tests): isolate LOCALAPPDATA so tests stop writing the real Claude Desktop config
On Windows, Claude Desktop config paths resolve from LOCALAPPDATA, which the test home override in tests/support.rs did not redirect, so profile_roundtrip wrote the https://desktop.test fixture into the developer's real Claude Desktop config. Point LOCALAPPDATA at the isolated test home alongside HOME and USERPROFILE.
Fixes #6077
fix(usage): widen pricing model source select to fit localized labels
The pricing model source SelectTrigger in the usage cost settings table was w-28, which leaves about 70px for the label; the English and Japanese labels need about 107px and overflowed the trigger. Widen it to w-40 and drop the h-7 overrides so the trigger and the adjacent Input use the shared h-9 height.
feat(pi): add Tencent TokenHub and Token Plan presets (#7159)
Add eight Tencent presets for Pi, covering the TokenHub pay-as-you-go line
(domestic and international) and the six Token Plan subscription products —
Pi was the only managed app still missing them. Model IDs, ordering and
endpoints follow Tencent's own model lists verbatim, and the tests assert each
product's line-up in order so future drift is caught.
Declare thinking control alongside the capabilities rather than leaving it to
defaults: the 31 DeepSeek entries carry a Tencent-specific compat so selecting
"off" actually disables thinking instead of silently billing reasoning tokens,
the eight Kimi K2.7 Code entries no longer offer an "off" level the model
cannot honour, and kimi-k2.7-code-highspeed is marked image-capable so
tool-returned screenshots are no longer dropped.
Drop minimax-m2.5 from every app: it is gone from Tencent's model tables and
the platform is retiring it.
fix(codex): proxy image generations endpoint (#7036)
Codex's built-in ImageGen calls the legacy OpenAI Images API, but the local
proxy only registered Responses / Chat Completions / Compact / Alpha Search
routes, so `/v1/images/generations` returned an empty 404 under Codex routing.
- Register `/images/generations` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them as a Codex standalone passthrough shared with Alpha Search.
- Generalize the Alpha Search full-URL rewrite into
`rewrite_codex_standalone_full_url`: derive the sibling
`/images/generations` from full Responses / Compact / Chat Completions URLs,
keep an existing Images URL as-is, and fail closed on opaque full URLs.
- Record `usage.input_tokens` / `output_tokens` through the Codex usage parser.
Fixes #5429
Fixes #6745
feat(pricing): add Gemini 3.8 Flash pricing (#7164)
Seeds the built-in pricing row for Gemini 3.8 Flash at 0.75 / 3.75 input/output per million tokens, 0.075 cache read, no cache-write charge, plus a regression test locking all four fields.
Values verified against Google's own pricing page (introductory price through 2026-12-31; list price 1.50 / 7.50 / 0.15 from 2027-01-01) and models.dev.
feat(provider): extend PPIO presets for Pi (#6870)
Add a PPIO provider preset for Pi using the OpenAI-compatible endpoint,
and pin an explicit models URL on the Claude Code PPIO preset so model
discovery resolves against https://api.ppio.com/openai/v1/models instead
of the derived candidates, which all return 404.
Co-authored-by: Jason <farion1231@gmail.com>
feat(pricing): add GLM-5.3 Flash pricing (#7163)
fix(proxy): enable parallel tool calls for Codex OAuth (#7024)
Codex OAuth requests defaulted parallel_tool_calls to false when the incoming
Anthropic request carried no explicit value, forcing Claude Code into one tool
call per model turn. Default it to true (matching codex-rs), map Anthropic
tool_choice.disable_parallel_tool_use to the inverse Responses value so explicit
serial execution is preserved, and add regression coverage for both directions.
Carries #5722 forward on top of current main.
Fixes #5719
Co-authored-by: 太一 <taiyi@douyu.tv>
feat(pricing): add GPT-6 Astra pricing (#7162)
fix(codex): resolve input_modalities for unknown models in vendor catalog (#6750)
When a model slug is not in the bundled DeepSeek official catalog, the vendor path clones the flagship entry and previously inherited its text-only input_modalities. Resolve modalities for unmatched slugs through the same registry/fail-open logic the non-vendor path uses, so a vision variant such as deepseek-v4-flash-vision-exp is no longer declared text-only. Explicit user inputModalities still win; matched models keep the vendor's declaration verbatim.
Fixes #6725
fix(claude): expose Opus 5 and Sonnet 5 in takeover mode (#5882)
Bump the stable client-facing role aliases that takeover mode writes to
Claude Code: Opus claude-opus-4-8 -> claude-opus-5 and Sonnet
claude-sonnet-4-6 -> claude-sonnet-5, so the /model menu matches the
Claude Desktop default routes and preset defaults that already moved to
Opus 5 / Sonnet 5. Add "opus-5" to the adaptive-thinking classifier so
the Bedrock thinking optimizer, which runs on the client alias before
model mapping, keeps sending adaptive thinking instead of the removed
budget_tokens.
Routing is unaffected: the proxy maps client aliases by role keyword, and
the aliases are rewritten automatically the next time the proxy starts.
Fixes #5876
Co-authored-by: Jason <farion1231@gmail.com>
feat(pricing): add GLM-5.3 to built-in model pricing table (#6591)
Seed glm-5.3 at $1.4/$4.4 per MTok with $0.26 cache read, matching the
official Z.ai list price (identical to the GLM-5.2 tier):
https://docs.z.ai/guides/overview/pricing
New model id, so seed only; no repair entry and no SCHEMA_VERSION bump.
Existing installs pick the row up on next launch via INSERT OR IGNORE, and
the startup cost backfill re-prices historical glm-5.3 rows recorded at $0.
fix(codex): align OAuth client identity for…
chore(connectivity-test): 勾选 Task 2 / OpenSpec 1.2
feat(connectivity-test): 后端直连执行器(reqwest+SSE,总/空闲超时,度量与错误识别)
feat(connectivity-test): 后端直连执行器(reqwest+SSE,总/空闲超时,度量与错误识别)
chore(connectivity-test): 勾选 Task 1 / OpenSpec 1.1
fix(connectivity-test): claude 鉴权对齐 adapter 顶层 apiKey/api_key 兜底
feat(connectivity-test): 新增后端连通性测试服务类型/模型解析/协议构造
chore: archive model-quick-switch-picker-scroll
chore: model-quick-switch-picker-scroll 验证报告与状态元数据
fix: 模型快捷切换弹窗内模型列表滚轮无法滚动
模态 Dialog 的 RemoveScroll 会对 shards 外的 portaled PopoverContent
wheel 事件无条件 preventDefault。在 SearchableModelPicker 的
PopoverContent 捕获阶段阻断 wheel 传播,恢复原生滚动。
chore: bump fork version to 3.20.1-1 after upstream v3.20.1 rebase
fix(form): only auto-expand Claude advanced options on divergent model mapping
Uniform mapping produced by the quick-set button (all roles equal to the
fallback model) no longer counts as an advanced value, so the advanced
section stays collapsed on edit. A 1M marker is stripped before comparing
so marked roles are not misjudged as divergent. Truly customized mappings
still auto-expand the section.
feat: align fallback-model quick-set buttons with model input right edge
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
docs: update README with fork build notes and quick-move provider changes
chore: archive comet-native claude-model-quick-access
feat: surface Claude fallback model as quick-access section below endpoint
Move the default fallback model (ANTHROPIC_MODEL) block out of the
collapsed Advanced Options into a quick-access section directly below
the request endpoint: input + 1M checkbox + quick-set + fetch-models
buttons are all reachable without expanding.
- header toggle-all "declare 1M" now includes the fallback model
(Haiku still excluded); partial selection shows indeterminate state
- drop the duplicated quick-set/fetch buttons and the old fallback
block inside Advanced Options
- exclude claudeModel from hasAnyAdvancedValue so a fallback-only
value no longer auto-expands Advanced Options
- quick-set priority, env write semantics, and OAuth preset branches
unchanged; add 4 component tests covering the new layout
chore: archive comet-native provider-right-click-top-bottom
Enable native workflow in .comet/config.yaml and move the Comet managed
state block in .gitignore so the exclusion rules stay grouped with the
other fork tool artifacts.
feat: add provider right-click move-to-top/bottom and insert new providers at second position
- ProviderList: context menu with quick move-to-top/move-to-bottom, reusing
update_providers_sort_order and refreshing tray menu like drag sort
- useAddProviderMutation: default insert at index 1 with explicit sortIndex
materialization for existing items (avoids defined-first sort trap)
- i18n: quickMove* strings for zh/en/zh-TW/ja
ci: stop flaky WSL2 linker job from blocking main
The backend-windows-wsl2 job intermittently fails with LNK1327 /
mt.exe c1010070 when the Windows runner's mt.exe cannot parse a
manifest temp file on the WSL2 UNC TEMP path. The same flaky failure
occurs upstream and is unrelated to fork changes. Mark the job
continue-on-error so main stops going red on it; the result stays
visible in the Actions UI.
style: apply prettier to files touched by recent comet archives
Frontend CI format:check failed on 6 files modified by the app-name
and model-quick-switch archive commits plus the updater-disable commit.
Run prettier --write so format:check passes again.
chore: archive ccs-fork-app-name-cc-switch
chore: archive ccs-model-quick-switch-ui-polish
docs: rewrite README to document fork differences vs upstream
README 从上游完整功能介绍改为仅记录 fork 与上游的差别,完整功能
文档指回上游。涵盖 8 项 fork 魔改:构建标识/开发预览、预设精简、
Claude 表单调整、模型搜索移植、主页模型快速切换、侧边栏面板显隐、
关闭应用内更新、打包范围,并说明版本号与 rebase 同步流程。
feat: disable in-app updater, point users to GitHub releases
移除 tauri-plugin-updater 依赖与自动更新链路,改为统一引导用户到
GitHub Releases 手动下载新版。
- 前端:updater.ts 恒返回 up-to-date;UpdateContext 取消启动自检;
AboutSection「检查更新」直接打开 fork releases 页;DatabaseUpgrade
恢复界面去掉下载进度条,改为打开发布页。
- 后端:移除 updater 插件注册与 UpdaterExt 调用;
install_update_and_restart 降级为空壳,check_app_update_available
返回当前版本号;check_for_updates 指向 fork releases;capabilities
去掉 updater:default 权限。
- 打包:tauri.conf.json 删 plugins.updater、createUpdaterArtifacts=false;
Cargo.toml / package.json 删 updater 依赖,Cargo.lock / pnpm-lock
同步重生成。
- CI:release.yml 移除签名收集与 assemble-latest-json job,删除
.tar.gz/.sig 处理,release body 去掉 updater 说明。
版本号统一为 3.20.0-1(合法 semver 且 MSI 第 4 位为 1)。
Cargo.lock / pnpm-lock.yaml 已随依赖移除重生成,cargo check 通过。
ci: build only Windows x64 and macOS arm64, unsigned
feat: gate dev panel behind dev-only env flag
chore(comet): archive ccs-sidebar-panels-visibility change
fix: apply skills visibility guard to hermes branch toolbar button
feat: include prompts in sidebar panel visibility settings
fix: persist visibleSidebarPanels setting in backend AppSettings
style: use compact pill toggles for sidebar panel visibility
feat: add sidebar panel visibility switches to settings
feat: add sidebar panel visibility settings and wire into toolbar buttons
chore(comet): archive ccs-home-model-display change
fix(fork): read grokbuild api key from config TOML in quick switch dialog
feat(fork): align model badge with upstream extractModelBadge
feat(fork): show current model badge and quick-switch entry on provider cards
feat(fork): add model quick switch dialog
feat(fork): add provider model read/write utils with tests
chore(comet): archive ccs-model-search-port change
feat(i18n): add model search picker keys for all locales
feat(form): port SearchableModelPicker from upstream for searchable model selection
chore(comet): archive ccs-claude-form-layout change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-claude-form-layout/
- Sync claude-form-layout spec to main specs
- Annotate design doc as archived
- Add verification report
fix(i18n): update advanced options hint, add ja/zh-TW toggle-all, drop dead apiFormatHint key
fix(form): correct 1M toggle-all alignment direction (ml-1)
fix(form): align 1M toggle-all checkbox with row checkboxes
refactor(form): move api format select into EndpointField rightSlot
fix(form): widen api format select to fit option text
feat(form): add 1M toggle-all checkbox in model mapping header
feat(form): move api format select next to endpoint input
chore(comet): archive ccs-provider-presets-trim change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-provider-presets-trim/
- Sync provider-presets-visibility spec to main specs
- Annotate design doc as archived
- Add verification report
docs(fork): add presets-trim files to rebase whitelist
test(fork): mock IS_FORK_BUILD false in unit tests for upstream-form semantics
feat(fork): wire official preset filter into provider forms
feat(fork): add hidden field to preset interfaces
feat(fork): add official preset allowlist and filter util
chore(comet): archive ccs-fork-scaffolding change
- Move change artifacts to docs/openspec/changes/archive/2026-08-18-ccs-fork-scaffolding/
- Sync fork-build-identity spec to main specs
- Annotate design doc as archived
- Add verification report
feat(fork): add dev config with isolated identifier and config dir
docs(fork): complete rebase whitelist with all fork-touched files
fix(fork): guard setTitle with isTauri in test/non-tauri env
fix(fork): define __CCS_FORK_BUILD__ in vitest config
feat(fork): add DevPanel and settings footer entry
feat(fork): set macOS window title to CC Switch (Fork) on startup
feat(fork): inject __CCS_FORK_BUILD__ compile-time constant
feat(fork): bump version to 3.19.2-fork.1 and rename product to CC Switch (Fork)
docs(fork): add upstream rebase workflow and ignore tool artifacts
fix(proxy): accept the whole grok-4.x (x>=5) family in the reasoning-effort whitelist (#7369)
Replace the verbatim grok-4.5 / grok-4.6 entries in supports_reasoning_effort with a rule that parses the grok-4.x minor version and accepts x >= 5, mirroring the existing GPT-5+ rule. This covers grok-4.7 (released 2026-09-21), whose reasoning effort was previously dropped on the Claude -> Chat, Claude -> Responses and Codex Responses -> Chat conversion paths, and lets future releases pass without another whitelist edit. The grok-build-* family is retained for saved providers.
Co-authored-by: allenxu09 <171831965+allenxu09@users.noreply.github.com>
docs(release): add v3.20.4 release notes
chore(release): v3.20.4
feat(pricing): add Grok 4.7 and GLM-5.3-FlashX
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.
fix(i18n): remove outdated OpenCode Go referral offer
fix: add SudoCode.chat alternate API endpoint
feat(settings): add a GitHub star prompt to the About card
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
feat(presets): add Soshow aggregator presets
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.
fix(ci): stabilize frontend tests and verify renderer build
fix(mcode): refresh provider state after removal (#7578)
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
fix(lib): reset skip_taskbar before window.show() across entry points (#6348)
5处 window.show() 调用中 4处没有在 show() 前重置 Windows 的 skip_taskbar,导致以下场景出现窗口可见但不在任务栏、点最小化后窗口消失的 bug:
- single_instance handler (lib.rs:360-361):当原进程因 silent_startup / 关闭到托盘等处于 skip_taskbar(true) 状态时,从快捷方式/开始菜单唤起已运行实例触发。
- handle_deeplink_url 的 focus_main_window 分支 (lib.rs:256):通过 ccswitch:// URL 唤起时。
- DB 恢复流程 (lib.rs:556):数据库版本不匹配时显示恢复界面。
- RunEvent::Opened 处理器 (lib.rs:1803):系统接收到 deep-link URL 时。
修复后的行为与 tray.rs::handle_tray_menu_event::show_main 分支一致(那里已有相同的 set_skip_taskbar(false) 调用)。
经用户测试验证:autostart + silent_startup + 从开始菜单/快捷方式启动的场景下,最小化按钮恢复正常工作。
fix(proxy): omit missing tool descriptions in the Codex responses-to-chat converter (#7378)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): preserve max effort for GPT-5.6 and GPT-6 Astra (#7531)
fix(misc): ignore shell startup output when probing tool versions in WSL (#7348)
try_get_version_wsl runs "${SHELL:-sh}" -lic '<tool> --version' through
wsl.exe and takes the first x.y.z anywhere in stdout. A login+interactive
shell runs the distro's startup files first, so their output shares that
stdout. On Ubuntu, /etc/profile.d/update-motd.sh prints the MOTD once a day,
starting with "Welcome to Ubuntu 24.04.4 LTS", and the card then showed
24.04.4 as the Claude Code version. The once-a-day stamp makes it appear
after the first probe of the day and vanish on refresh.
The payload now prints a sentinel before running the tool, and parsing looks
only at what follows its last occurrence. The last occurrence handles the
-lic || -lc || -c fallback chain printing it several times; matching it as a
substring handles OSC sequences glued in front of it. If the sentinel is
absent (the shell failed before the payload ran), the output is used as
before. The stderr fallback for tools that print their version there is kept.
Fixes #7347
Co-authored-by: lyfuci <lyfuci11@gmail.com>
fix(claude): hide session URL attribution (#7053)
* fix(claude): hide session URL attribution
* docs(claude): document session URL attribution
fix(ui): reset provider scroll when switching apps (#5211)
fix(pi): fix provider logo (#6826)
fix(i18n): use the Pi form's own term for the zh-TW duplicate-key message (#7187)
Follow-up to the nit on #6768. In zh-TW the Pi form labels the field
供應商識別碼 and says 供應商識別碼為必填 when it is empty, but the
duplicate message I added said 供應商標識 — the term Hermes, OpenCode and
OpenClaw use for their own field. One form, two names for the same thing.
The other three locales already keep the label and both messages on one
term, so this makes zh-TW match the pattern rather than introduce one:
en Provider key / Provider key is required / This provider key already exists
ja プロバイダーキー / プロバイダーキーを入力してください / このプロバイダーキーは既に存在します
zh 供应商标识 / 供应商标识不能为空 / 该供应商标识已存在
zh-TW 供應商識別碼 / 供應商識別碼為必填 / 該供應商標識已存在 <- the odd one out
hermes.form.providerKeyDuplicate carries the same sentence and is left
alone: its own label is 供應商標識, so it is already consistent.
Verified: managementListLocales 7/7. Full unit suite 1085/1087, the same
two App.test.tsx integration cases failing before and after.
fix(proxy): strip stop param for GitHub Copilot to unblock auto mode classifier (#5404)
Claude Code's auto mode classifier sends `stop_sequences` on every
safety check. On the GitHub Copilot path the Anthropic -> OpenAI Chat
conversion mapped it to `stop`, and Copilot's Chat Completions route
for Claude models answers that request with HTTP 400, so every
classifier call failed while normal chat kept working.
- Remove the converted `stop` field only when the provider is GitHub
Copilot; other OpenAI-compatible upstreams keep it.
- Copilot detection in the transform now also covers providers that
are recognised only through `env.ANTHROPIC_BASE_URL`.
- Regression tests for the Copilot (meta and env URL) and generic
provider cases.
Fixes #5175
fix(codex): keep stored auth when the live snapshot has no credential (#7434)
Editing the current Codex provider seeds the form from the live ~/.codex
snapshot, and switching away backfills that snapshot into the DB row. When a
provider table declares its own credential source (an Authorization header,
env_key, auth/aws), no bearer token is injected into config.toml and the
shared auth.json is deleted on third-party switches, so the live snapshot is
permanently `{ auth: {}, config }` while auth.OPENAI_API_KEY in the DB is the
only copy of the key. Both paths treated that empty live auth as
authoritative and silently erased the stored key, which in turn made the
usage-script modal misread the provider as an official subscription and
reset the saved script.
- Treat a credential-less live auth (missing auth.json or the bare
auth_mode logout marker) as an absent field in both the edit dialog and
the switch-away backfill, keeping the stored auth template. Live still
wins whenever it carries material, and official providers keep the live
login as their source of truth.
- Gate the official exemption on is_codex_official_provider /
resolveCodexOfficialIdentity rather than the category alone: a legacy
fixed official card without a persisted category is still written as
official, so the fallback must not resurrect a logged-out login on
switch-back.
- Cover both layers with regression tests, including the category-less
official shapes.
Fixes #7433
Supersedes #7492
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): lift additional_tools input carriers on the Codex chat conversion (#7454)
Codex talks to third-party providers over its Responses Lite transport,
which ships the request's extra tools in an `additional_tools` input
carrier: `{"type":"additional_tools","role":"developer","tools":[...]}`,
a role but no `content`. The Responses->Chat conversion let that carrier
fall through the generic "item with a role is a message" arm and emitted
`{"role":"system","content":null}` at messages[1], which strict Chat
gateways reject with `messages[1]: missing field "content"` (or
`data did not match any variant of ...SystemMessageContent`). The tools
carried inside it were also dropped on the Chat and Anthropic conversions.
- Skip `additional_tools` items in the Chat message walk; a carrier
declares tools, it is not a message.
- Collect carrier tools through the existing CodexToolContext registry
(dedup, namespace flattening, custom-tool conversion), which also feeds
the Anthropic conversion and the response-side name round-trip.
- Requests without a carrier convert byte-for-byte as before.
Known boundary: `custom` children inside a namespace tool
(`functions.exec`) are still not lifted; that needs its own response-side
name round-trip and is left for a follow-up.
Diagnosed independently in #6158 by BigStrongSun (fix in #6159) and in
fixture.
Fixes #7451
Fixes #6158
Refs #6033
Refs #6776
Supersedes #6159
fix(proxy): normalize Codex detail:original images for Chat gateways (#7476)
Codex marks tool-returned images with detail:"original" whenever the model
catalog advertises supports_image_detail_original, a Responses-only value.
The Responses->Chat conversion copied it verbatim into image_url.detail, and
strict OpenAI-compatible Chat gateways (Ark/Doubao, SenseNova, GLM, vLLM, ...)
reject the whole request with 400. Because Codex replays the full rollout on
every turn, one poisoned view_image result made the session fail permanently,
including plain-text turns.
Two layers:
- tool_media::merge_top_level_detail downgrades original -> auto, so already
poisoned rollouts and Codex versions before 0.155.0 (which do not normalize
replayed history themselves) recover immediately. auto/low/high are untouched.
- codex_catalog_model_entry always writes supports_image_detail_original:false
on the ProxyChat profile, so Codex stops emitting original at the source once
the catalog is regenerated. NativeResponses/Anthropic profiles and the DeepSeek
official catalog are unaffected.
Top-level user images were never affected: responses_content_to_chat_content
drops the part-level detail entirely.
Fixes #6495
Fixes #7458
Refs #6569
Refs #5805
fix(omo): detect unified config on the WSL side when the OpenCode dir is in WSL (#7550)
When the OpenCode config directory points into WSL
(\\wsl.localhost\<distro>\home\<user>\.config\opencode), OMO >= 4.19.3 keeps
its unified config in the WSL-side home (~/.omo/omo.jsonc). Detection only
probed the Windows home, never found it, and silently fell back to the legacy
per-plugin file that OMO no longer reads, so provider switches looked
successful but had no effect.
- Add derive_wsl_home_dir to derive the WSL-side home from a WSL UNC path
(home/<user> or root); non-WSL and non-home paths return None.
- Probe a list of home candidates for the unified config, WSL-side home
first and the OS home as a fallback, shared by the read, write and delete
paths.
- Detection only: when no unified config exists on either side the legacy
fallback is unchanged.
Fixes #7363
Supersedes #7367
docs(partner): add a FluxA purchase link and tighten its sponsor blurb
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.
fix(claude-desktop): expose the 1M context variant for DeepSeek V4 routes
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.
feat(presets): add FluxA Token Plan partner presets
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.
fix(presets): correct AICodeWith Codex-family endpoint to /v1
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.
fix(ui): render monochrome preset icons in the foreground color
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.
docs(user-manual): point Chat-only Codex examples at plan endpoints
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.
fix(pricing): restore DeepSeek V4 Pro price and seed Qwen3.8 and Hy4 models
- 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).
fix(presets): move MiniMax CN and BaiLing to their current official endpoints
- 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.
feat(codex): refresh CN presets from the Responses API audit
- 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.
test(claude-desktop): run Linux path tests on unix hosts and cover Linux in provider sync tests
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.
fix(ui): stop expanding stale cached usage tiers on ineligible provider cards
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.
fix(skills): 支持 skillId 与目录名不一致的技能安装和更新 (#6381)
* fix(skills): resolve mismatched skillId directories
* fix(skills): preserve source path during updates
* fix(skills): refresh source URL after repository relocation
* test(skills): isolate relocation fixture from legacy home
feat(opencode): 支持从获取的模型列表中搜索并批量添加所选模型 (#7515)
* feat: implement fetched models selection and UI components
* fix(opencode): prevent model search from submitting provider form
Add Kimi Global preset variants (kimi.ai platform + api endpoints) (#7526)
Add a "Kimi Global" twin immediately after the domestic "Kimi" API preset and a "Kimi For Coding Global" twin after the domestic "Kimi For Coding" preset, in every preset file that ships them (claude, claude-desktop, codex, hermes, openclaw, opencode, pi).
"Kimi Global" points to the international open platform https://platform.kimi.ai?aff=cc-switch (apiKeyUrl mirrored as https://platform.kimi.ai/console/api-keys?aff=cc-switch) and swaps the endpoint host api.moonshot.cn for api.moonshot.ai with each file's path unchanged (/v1 or /anthropic).
"Kimi For Coding Global" points to the international site https://www.kimi.ai/code/?aff=cc-switch and swaps the endpoint host api.kimi.com for api.kimi.ai with the path unchanged (e.g. https://api.kimi.ai/coding/v1).
Domestic presets are unchanged; category, icons, apiFormat, model catalogs, endpointCandidates (host swapped), partnerPromotionKey and settings otherwise mirror the domestic blocks exactly.
primePartner stays only on the domestic presets so the new variants do not alter prime-partner ordering.
The Pi twins get distinct providerKey values ("cc-switch-kimi-global", "cc-switch-kimi-for-coding-global") because providerKey is the preset-level primary key and must not collide with the domestic presets.
Rebalance sponsor CTAs around Kimi Code plan with dual-region links (#7522)
Kimi Code plan purchases have resumed, so the README sponsor sections go back to leading with the coding plan instead of the API-only flow written during the purchase pause.
All four locales (EN/ZH/JA/DE) now present Kimi Code plan (中文站 | Global) as the primary CTA and the Kimi Open Platform API (中文站 | Global, with the first-top-up bonus) as the secondary one, mirroring the domestic|global split already used for API links.
The aff and track_id values are unchanged.
fix(prompts): refresh active prompts after external file edits (#7194)
Reload the enabled prompt from its live file when the Prompts list loads, and refresh the open panel when the window regains focus, so external edits to CLAUDE.md / AGENTS.md show up without a restart.
- Keep saved templates available when the live file cannot be read.
- Skip the backfill for empty or whitespace-only files, matching enable_prompt.
- Skip the backfill while a sync/restore holds the sync lock, so a list load cannot write a stale live file into a freshly restored database.
fix(skills): raise archive entry limit so large skill repos install (#7489)
Installing hugohe3/ppt-master from skills.sh failed with
ARCHIVE_TOO_MANY_ENTRIES: the GitHub archive we download is the whole
repository (13,248 entries), and the 10,000-entry cap added in #5811
treated it as a zip bomb.
- Raise MAX_ARCHIVE_ENTRIES to 30,000 for both the remote archive and
local ZIP paths, and align WebDAV's MAX_EXTRACT_ENTRIES so a skills.zip
containing such a skill still restores.
- Charge every file entry at least one 4 KiB block against the existing
512 MiB budget, mirroring directory accounting, so empty-file archives
are bounded by the byte budget rather than by the entry count alone.
- Add tests for a 13k-entry repo archive and for the per-file minimum
charge.
Fixes: https://github.com/farion1231/cc-switch/issues/7475
feat: add MiniMax Code harness support (#7383)
Add MiniMax Code (mcode) as a managed application: provider and model management through the native custom_provider configuration, MCP servers, Skills, global instructions (AGENTS.md), read-only local session history with resume, and usage import from the native runtime database. Native files shared by the TUI and desktop are written under the native lock with rollback when the database commit fails, unrelated native configuration is preserved, and the data directory honors MINIMAX_DATA_DIR and MAVIS_DATA_DIR. Deleting sessions stays in the native application because they are coupled to runtime state.
fix(codex): recover stale account bindings during takeover (#7395)
After deleting a managed ChatGPT account and logging in again, a Codex provider can still reference the old local account ID, which made route switching, takeover, startup restoration and rebinding fail permanently.
Distinguish a missing outgoing account from an existing account with no matching live token. A missing account only releases its ownership marker after the persisted account store is verified; the target write policy stays in force. A stale target now reports an account-selection instruction instead of a bare not-found error, including when takeover is already enabled.
Fixes #7392
feat(claude-desktop): support 3P configuration on Linux (#7331)
Add Claude Desktop 3P configuration support on Linux. Resolve the config root from an absolute XDG_CONFIG_HOME, falling back to ~/.config; when CC Switch itself runs as a Flatpak, use the host ~/.config instead of the sandbox-private XDG_CONFIG_HOME. Update the en/zh/ja user manuals with Linux paths and the Flatpak boundary.
Fixes #4855
fix(proxy): omit missing tool descriptions instead of serializing null (#7319)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): keep reasoning effort for grok-4.6 and pass xhigh through verbatim (#7318)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(models): support Zhipu OpenAI Responses model lists (#7330)
Zhipu's Responses endpoint (open.bigmodel.cn/api/v1) serves its model list in the Codex remote catalog shape (models[].slug) instead of the OpenAI-compatible data[].id shape, so the Codex form's model fetch came back empty. The parser now falls back to models[].slug when data is absent.
fix(misc): fetch npm dist-tags from the dedicated endpoint with the probe timeout (#7346)
The tool version probe requested the full npm packument for each package (metadata for every historical version, 14-25 MB uncompressed for codex / opencode / openclaw) only to read its dist-tags, and it was the only latest-version probe without LATEST_PROBE_TIMEOUT, so it fell back to the proxy client's 600 s total timeout and the cards sat on "loading" for minutes on slow links while the large downloads starved the Hermes GitHub probe into "unknown".
Switch fetch_npm_dist_tags to registry.npmjs.org/-/package/{package}/dist-tags, whose body is the dist-tags object itself (tens of bytes to a few KB), escape scoped names as %2f, and apply the same 15 s probe timeout as the GitHub and PyPI probes. No data is lost: the only consumer reads latest and the tool's prerelease tags.
Fixes #7339
feat(usage): display output tokens per second in request log table and detail panel (#3369)
Show tokens/s next to the output token count in the request log table and the request detail panel.
The generation window prefers an explicit duration when present, otherwise latency minus first-token time for streaming requests, and falls back to full latency for non-streaming requests. Rows without a positive token count or window show nothing.
Closes #5936
fix(presets): update APIKEY.FUN URLs to apikey.fan
docs(release): add v3.20.3 release notes
chore(release): v3.20.3
fix(pricing): move retired DeepSeek V4 family to the V4.1 Flash tier
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.
fix(presets): declare third-party Token Plan DeepSeek V4 rows text-only
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.
feat(presets): rename DouBaoSeed preset to localized Volcengine Doubao
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.
fix(codex): skip unchanged incomplete rollout tails
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.
fix(claude): align quick model selection with form order
fix(tray): show bound ChatGPT account quota for managed Codex cards
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
feat(claude): add "Disable Artifact Tool" quick toggle to provider editor
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.
feat(codex): refresh aggregator model presets and catalogs
feat(codex): switch Kimi presets to native Responses direct-connect
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.
fix(usage): show Claude Fable weekly limit from the usage API limits array
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.
fix(codex): mirror vision-capable DeepSeek catalog and un-gate the legacy v4-flash alias (#7286)
Refresh the bundled DeepSeek official Codex catalog to the vendor's current models.json (slug deepseek-flash, image input modality, supports_image_detail_original) and remove deepseek-v4-flash from the confirmed text-only registry: the legacy id is still accepted by DeepSeek and routed to the vision-capable V4.1 Flash, so both the proxy media sanitizer and the generated Codex catalog must fail open for it. deepseek-v4-pro stays text-only per the vendor catalog.
Fixes #7283
fix(proxy): clamp sub-floor max_tokens to the Responses API minimum (#7287)
The Responses API rejects max_output_tokens below 16, but Anthropic clients legitimately send tiny probe budgets (Claude Desktop's model-availability probe uses max_tokens=1). The Anthropic→Responses conversion copied the value verbatim, so strict upstreams returned 400 and Claude Desktop reported the mapped model as unavailable under local routing.
Clamp 1..=15 up to 16 during conversion; 16+, 0 and non-integer values keep their pass-through semantics. Normal session requests convert byte-for-byte as before, and the Codex OAuth path still strips max_output_tokens afterwards.
Fixes #7103
fix(codex): coalesce adjacent commentary with pending tool calls (#7280)
The Codex Responses -> Chat Completions converter emitted two consecutive
assistant messages when one Responses model turn contained a commentary
message directly followed by function_call items. Chat upstreams imitate
the text-only assistant message as a complete turn and return
finish_reason=stop before the expected tool call, so long agent tasks end
after a progress update.
Merge pending tool calls into the directly adjacent assistant message when
it carries no tool_calls yet, attach pending reasoning per segment so
parallel calls do not duplicate it, and keep the new-message path for every
other boundary shape (user/tool boundaries, prior tool-call batches, media
flushes). The reasoning placeholder backfill still applies to coalesced
calls without reasoning.
Fixes #6529
Supersedes #6530
Co-authored-by: BigStrongSun <BigStrongSun@users.noreply.github.com>
fix(proxy): skip empty reasoning_content placeholders in Anthropic SSE streaming (#7227)
Some OpenAI-compatible upstreams (GLM, Qwen, DeepSeek-V4-Pro via ModelScope/DashScope,
Bailian, JD Cloud, ...) keep a `reasoning_content: ""` placeholder in every content
chunk instead of omitting the field. The reasoning branch of the OpenAI Chat -> Anthropic
SSE converter only checked for presence, and it shares `current_non_tool_block_type`
with the content branch, so every content chunk closed the open text block, opened an
empty thinking block, then reopened a text block. N content chunks became 2N content
blocks, half of them empty thinking blocks, which Claude Code rendered as one line per
token with dozens of empty Thought blocks and bloated the session JSONL.
Filter empty reasoning before entering the branch, matching the `!content.is_empty()`
guard the content branch already had and the non-streaming `openai_to_anthropic`
converter. Two regression tests pin block index, type and order: the placeholder shape
must yield a single text block and no thinking_delta at all, and real reasoning must
still yield one thinking block followed by one text block.
Fixes #5028
Fixes #4404
fix(codex): detect growing rollouts with a persisted byte cursor (#7219)
Codex keeps the rollout append handle open for the whole session, so on Windows NTFS the file mtime does not advance while the file grows and the pure-mtime gate skipped growing rollouts forever. Persist the observed byte length in session_log_sync.last_byte_offset for Codex rows and skip a file only when both mtime and size are unchanged. The parser now splits records on newlines manually so an incomplete trailing record is retried on the next pass without advancing the line cursor.
Fixes #6060
fix(codex): proxy image edits and Images API follow-ups (#7177)
* fix(codex): proxy image edits and Images API follow-ups
Follow-ups to #7036 for Codex's built-in ImageGen over the local proxy:
- Register `/images/edits` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them through the shared Codex standalone passthrough.
ImageGen posts to `images/edits` whenever a call references existing
images, and the router returned an empty 404 for it.
- Add `CodexStandaloneEndpoint::ImagesEdits`. Both Images endpoints share
one full-URL suffix table, so a complete Responses / Compact / Chat
Completions / Images URL derives the sibling endpoint (and vice versa)
while opaque full URLs keep failing closed.
- Apply the standalone sibling rewrite when the base URL is pasted as a
complete endpoint with the full-URL switch off, mirroring
`codex_chat_base_is_full_endpoint`; standalone requests no longer become
`.../chat/completions/images/generations`.
- Parse Images API streaming usage: `image_generation.completed` carries
`usage` on the event top level, so scan for `usage.input_tokens` before
falling back to the Chat Completions stream parser.
Related #7036
* fix(codex): match standalone URL suffixes case-insensitively
Keep endpoint detection and sibling rewriting consistent while preserving the original URL prefix and query. Cover mixed-case Images and Alpha Search endpoints through URL and proxy regression cases.
---------
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): preserve per-app settings on shutdown and port allocation (#7210)
On normal exit, stop_with_restore_keep_state() called the legacy global
proxy-config writer to clear the long-retired live_takeover_active flag.
That writer reads the Claude row and runs an UPDATE without a WHERE clause,
so every shutdown copied Claude's max_retries and the three timeout fields
onto the Codex, Gemini and Grok Build rows — and the UPDATE never carried
live_takeover_active in the first place, so the step had no effect beyond
the clobber. The ephemeral listen-port path did the same on every proxy
start.
Drop the dead write-back (live-config restore, backup cleanup and health
reset are unchanged) and persist the resolved ephemeral port through the
global-config interface, which only touches the shared listen/logging
columns. Two regression tests seed distinct per-app settings for all four
apps and assert they survive both paths.
Fixes #7204
fix(provider): preserve child metadata during universal sync (#7212)
Syncing a universal provider regenerates its Claude/Codex/Gemini child
providers. The child's `settings_config` was merged with the existing row,
but `meta`, `created_at` and `sort_index` were taken from the generated
object, and the universal provider never carries any of them from the UI.
The UPDATE therefore wiped the child's usage script, common-config opt-out,
endpoint auto-select and other per-app settings to `{}` and reset the sort
index to NULL, which pushed the card to the bottom of the list and looked
like the provider had been deleted and re-created.
Keep the existing child's `meta`, `created_at` and `sort_index` when the
row already exists. Name, base URL, key, models, website and notes are
still driven by the universal provider, and first-time creation still
inherits the parent's metadata.
Fixes #7134
feat(presets): rebrand DashScope as 千问AI平台 and refresh Qwen models (#7183)
Rename the domestic Bailian presets to 千问AI平台 with a dedicated icon, refresh the Qwen family to the 3.8 generation across Claude Code, Claude Desktop, Codex, Hermes, OpenClaw, OpenCode and Pi, add the domestic Token Plan presets, restore the international QwenCloud Coding Plan presets, and align OpenClaw/OpenCode/Pi model metadata with the official client docs.
fix(codex): honor proxy URL when model_provider is omitted (#7263)
When a Codex provider card's TOML has no `model_provider`, Codex falls back to
its built-in `openai` provider, but takeover wrote the proxy address as a
top-level `base_url` that Codex never reads, so requests bypassed the local
proxy and went straight to api.openai.com.
Treat the missing selector as the built-in `openai` provider and write the
proxy address to `openai_base_url`; the existing legacy-reroute migration then
turns it into a `cc-switch` custom provider table carrying the `PROXY_MANAGED`
bearer, the same shape every other third-party takeover uses. Explicit
provider selections are unchanged.
Refs #6256, #7217
fix(provider): update MiniMax defaults to M3 and remove expired offers (#7255)
* fix(provider): refresh MiniMax M3 presets and remove expired promotions
* fix(provider): complete MiniMax M3 capability declarations
---------
Co-authored-by: saladday <1203511142@qq.com>
chore(provider): remove Atlas Cloud sponsor status, keep presets
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`
docs(release): add v3.20.2 release notes
chore(release): v3.20.2
feat(provider): add SoleAPI sponsor presets across all nine apps
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.
fix(codex): proxy image edits endpoint
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.
fix(provider): pin models URL for JieKou and Novita Claude presets
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.
feat(pricing): refresh seed for September 2026 vendor price changes
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.
fix(codex): stamp requires_openai_auth on takeover writes to match the live login state
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.
fix(updater): show the real reason when an update check fails (#6482)
The updater plugin rejects with a plain string, so `err instanceof Error`
never matched and the real failure reason was replaced by the generic
fallback message. Use the existing extractErrorMessage helper, and pass
the recovered message into the check-update failure toast so it actually
reaches the user instead of stopping at an unread context state.
Co-authored-by: Jason <farion1231@gmail.com>
docs: fix the locales path in the README directory trees (#6100)
All four READMEs showed locales/ as a direct child of src/, but the
translations live at src/i18n/locales/. Split it into two tree nodes so it
matches how the rest of the tree is drawn. Also fixes a CLaude -> Claude
typo in README_ZH.md.
Co-authored-by: Jason <farion1231@gmail.com>
fix(claude): exclude workflow journal.jsonl from session list
Claude Code's workflow feature writes subagents/workflows/wf_*/journal.jsonl inside the session directory. The Claude session scanner only excluded agent-* files, so each journal parsed as an empty ghost session titled "journal". Exclude journal.jsonl by file name and add regression tests.
Fixes #6042
fix(i18n): add the missing pi.form.providerKeyDuplicate and common.collapse keys
translatePiProviderMutationError returns pi.form.providerKeyDuplicate for the Pi backend's duplicate-key error, but the key existed in none of the four locale files, so the toast showed the raw key. common.collapse (aria-label of the thinking-map toggle) was missing the same way. Add both keys to zh/en/ja/zh-TW and lock them in the locale coverage test.
fix(proxy): stop mask_url panicking on multi-byte UTF-8 near the truncation boundary
mask_url's fallback branch (used when the proxy URL fails Url::parse) sliced the raw string at byte 20 and panicked when a multi-byte UTF-8 character straddled that offset, e.g. a CJK string pasted into the global proxy address. Snap the cut to the nearest char boundary at or before 20 and add a regression test.
fix(tests): isolate LOCALAPPDATA so tests stop writing the real Claude Desktop config
On Windows, Claude Desktop config paths resolve from LOCALAPPDATA, which the test home override in tests/support.rs did not redirect, so profile_roundtrip wrote the https://desktop.test fixture into the developer's real Claude Desktop config. Point LOCALAPPDATA at the isolated test home alongside HOME and USERPROFILE.
Fixes #6077
fix(usage): widen pricing model source select to fit localized labels
The pricing model source SelectTrigger in the usage cost settings table was w-28, which leaves about 70px for the label; the English and Japanese labels need about 107px and overflowed the trigger. Widen it to w-40 and drop the h-7 overrides so the trigger and the adjacent Input use the shared h-9 height.
feat(pi): add Tencent TokenHub and Token Plan presets (#7159)
Add eight Tencent presets for Pi, covering the TokenHub pay-as-you-go line
(domestic and international) and the six Token Plan subscription products —
Pi was the only managed app still missing them. Model IDs, ordering and
endpoints follow Tencent's own model lists verbatim, and the tests assert each
product's line-up in order so future drift is caught.
Declare thinking control alongside the capabilities rather than leaving it to
defaults: the 31 DeepSeek entries carry a Tencent-specific compat so selecting
"off" actually disables thinking instead of silently billing reasoning tokens,
the eight Kimi K2.7 Code entries no longer offer an "off" level the model
cannot honour, and kimi-k2.7-code-highspeed is marked image-capable so
tool-returned screenshots are no longer dropped.
Drop minimax-m2.5 from every app: it is gone from Tencent's model tables and
the platform is retiring it.
fix(codex): proxy image generations endpoint (#7036)
Codex's built-in ImageGen calls the legacy OpenAI Images API, but the local
proxy only registered Responses / Chat Completions / Compact / Alpha Search
routes, so `/v1/images/generations` returned an empty 404 under Codex routing.
- Register `/images/generations` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them as a Codex standalone passthrough shared with Alpha Search.
- Generalize the Alpha Search full-URL rewrite into
`rewrite_codex_standalone_full_url`: derive the sibling
`/images/generations` from full Responses / Compact / Chat Completions URLs,
keep an existing Images URL as-is, and fail closed on opaque full URLs.
- Record `usage.input_tokens` / `output_tokens` through the Codex usage parser.
Fixes #5429
Fixes #6745
feat(pricing): add Gemini 3.8 Flash pricing (#7164)
Seeds the built-in pricing row for Gemini 3.8 Flash at 0.75 / 3.75 input/output per million tokens, 0.075 cache read, no cache-write charge, plus a regression test locking all four fields.
Values verified against Google's own pricing page (introductory price through 2026-12-31; list price 1.50 / 7.50 / 0.15 from 2027-01-01) and models.dev.
feat(provider): extend PPIO presets for Pi (#6870)
Add a PPIO provider…
chore(connectivity-test): 勾选 Task 5 / OpenSpec 2.1
test(connectivity-test): 锁定 toBackendParams 字段映射(customHeaders/customBody 等)
feat(connectivity-test): 前端命令绑定与测试/探针状态 hooks
chore(connectivity-test): 勾选 Task 4 / OpenSpec 1.4
feat(connectivity-test): 测试参数持久化纯函数(缺省即默认值)
chore(connectivity-test): 勾选 Task 3 / OpenSpec 1.3
feat(connectivity-test): Tauri 命令与注册,官方/动态端点过滤,sanitize 隔离私有字段
chore(connectivity-test): 勾选 Task 2 / OpenSpec 1.2
feat(connectivity-test): 后端直连执行器(reqwest+SSE,总/空闲超时,度量与错误识别)
feat(connectivity-test): 后端直连执行器(reqwest+SSE,总/空闲超时,度量与错误识别)
chore(connectivity-test): 勾选 Task 1 / OpenSpec 1.1
fix(connectivity-test): claude 鉴权对齐 adapter 顶层 apiKey/api_key 兜底
feat(connectivity-test): 新增后端连通性测试服务类型/模型解析/协议构造
chore: archive model-quick-switch-picker-scroll
chore: model-quick-switch-picker-scroll 验证报告与状态元数据
fix: 模型快捷切换弹窗内模型列表滚轮无法滚动
模态 Dialog 的 RemoveScroll 会对 shards 外的 portaled PopoverContent
wheel 事件无条件 preventDefault。在 SearchableModelPicker 的
PopoverContent 捕获阶段阻断 wheel 传播,恢复原生滚动。
chore: bump fork version to 3.20.1-1 after upstream v3.20.1 rebase
fix(form): only auto-expand Claude advanced options on divergent model mapping
Uniform mapping produced by the quick-set button (all roles equal to the
fallback model) no longer counts as an advanced value, so the advanced
section stays collapsed on edit. A 1M marker is stripped before comparing
so marked roles are not misjudged as divergent. Truly customized mappings
still auto-expand the section.
feat: align fallback-model quick-set buttons with model input right edge
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
docs: update README with fork build notes and quick-move provider changes
chore: archive comet-native claude-model-quick-access
feat: surface Claude fallback model as quick-access section below endpoint
Move the default fallback model (ANTHROPIC_MODEL) block out of the
collapsed Advanced Options into a quick-access section directly below
the request endpoint: input + 1M checkbox + quick-set + fetch-models
buttons are all reachable without expanding.
- header toggle-all "declare 1M" now includes the fallback model
(Haiku still excluded); partial selection shows indeterminate state
- drop the duplicated quick-set/fetch buttons and the old fallback
block inside Advanced Options
- exclude claudeModel from hasAnyAdvancedValue so a fallback-only
value no longer auto-expands Advanced Options
- quick-set priority, env write semantics, and OAuth preset branches
unchanged; add 4 component tests covering the new layout
chore: archive comet-native provider-right-click-top-bottom
Enable native workflow in .comet/config.yaml and move the Comet managed
state block in .gitignore so the exclusion rules stay grouped with the
other fork tool artifacts.
feat: add provider right-click move-to-top/bottom and insert new providers at second position
- ProviderList: context menu with quick move-to-top/move-to-bottom, reusing
update_providers_sort_order and refreshing tray menu like drag sort
- useAddProviderMutation: default insert at index 1 with explicit sortIndex
materialization for existing items (avoids defined-first sort trap)
- i18n: quickMove* strings for zh/en/zh-TW/ja
ci: stop flaky WSL2 linker job from blocking main
The backend-windows-wsl2 job intermittently fails with LNK1327 /
mt.exe c1010070 when the Windows runner's mt.exe cannot parse a
manifest temp file on the WSL2 UNC TEMP path. The same flaky failure
occurs upstream and is unrelated to fork changes. Mark the job
continue-on-error so main stops going red on it; the result stays
visible in the Actions UI.
style: apply prettier to files touched by recent comet archives
Frontend CI format:check failed on 6 files modified by the app-name
and model-quick-switch archive commits plus the updater-disable commit.
Run prettier --write so format:check passes again.
chore: archive ccs-fork-app-name-cc-switch
chore: archive ccs-model-quick-switch-ui-polish
docs: rewrite README to document fork differences vs upstream
README 从上游完整功能介绍改为仅记录 fork 与上游的差别,完整功能
文档指回上游。涵盖 8 项 fork 魔改:构建标识/开发预览、预设精简、
Claude 表单调整、模型搜索移植、主页模型快速切换、侧边栏面板显隐、
关闭应用内更新、打包范围,并说明版本号与 rebase 同步流程。
feat: disable in-app updater, point users to GitHub releases
移除 tauri-plugin-updater 依赖与自动更新链路,改为统一引导用户到
GitHub Releases 手动下载新版。
- 前端:updater.ts 恒返回 up-to-date;UpdateContext 取消启动自检;
AboutSection「检查更新」直接打开 fork releases 页;DatabaseUpgrade
恢复界面去掉下载进度条,改为打开发布页。
- 后端:移除 updater 插件注册与 UpdaterExt 调用;
install_update_and_restart 降级为空壳,check_app_update_available
返回当前版本号;check_for_updates 指向 fork releases;capabilities
去掉 updater:default 权限。
- 打包:tauri.conf.json 删 plugins.updater、createUpdaterArtifacts=false;
Cargo.toml / package.json 删 updater 依赖,Cargo.lock / pnpm-lock
同步重生成。
- CI:release.yml 移除签名收集与 assemble-latest-json job,删除
.tar.gz/.sig 处理,release body 去掉 updater 说明。
版本号统一为 3.20.0-1(合法 semver 且 MSI 第 4 位为 1)。
Cargo.lock / pnpm-lock.yaml 已随依赖移除重生成,cargo check 通过。
ci: build only Windows x64 and macOS arm64, unsigned
feat: gate dev panel behind dev-only env flag
chore(comet): archive ccs-sidebar-panels-visibility change
fix: apply skills visibility guard to hermes branch toolbar button
feat: include prompts in sidebar panel visibility settings
fix: persist visibleSidebarPanels setting in backend AppSettings
style: use compact pill toggles for sidebar panel visibility
feat: add sidebar panel visibility switches to settings
feat: add sidebar panel visibility settings and wire into toolbar buttons
chore(comet): archive ccs-home-model-display change
fix(fork): read grokbuild api key from config TOML in quick switch dialog
feat(fork): align model badge with upstream extractModelBadge
feat(fork): show current model badge and quick-switch entry on provider cards
feat(fork): add model quick switch dialog
feat(fork): add provider model read/write utils with tests
chore(comet): archive ccs-model-search-port change
feat(i18n): add model search picker keys for all locales
feat(form): port SearchableModelPicker from upstream for searchable model selection
chore(comet): archive ccs-claude-form-layout change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-claude-form-layout/
- Sync claude-form-layout spec to main specs
- Annotate design doc as archived
- Add verification report
fix(i18n): update advanced options hint, add ja/zh-TW toggle-all, drop dead apiFormatHint key
fix(form): correct 1M toggle-all alignment direction (ml-1)
fix(form): align 1M toggle-all checkbox with row checkboxes
refactor(form): move api format select into EndpointField rightSlot
fix(form): widen api format select to fit option text
feat(form): add 1M toggle-all checkbox in model mapping header
feat(form): move api format select next to endpoint input
chore(comet): archive ccs-provider-presets-trim change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-provider-presets-trim/
- Sync provider-presets-visibility spec to main specs
- Annotate design doc as archived
- Add verification report
docs(fork): add presets-trim files to rebase whitelist
test(fork): mock IS_FORK_BUILD false in unit tests for upstream-form semantics
feat(fork): wire official preset filter into provider forms
feat(fork): add hidden field to preset interfaces
feat(fork): add official preset allowlist and filter util
chore(comet): archive ccs-fork-scaffolding change
- Move change artifacts to docs/openspec/changes/archive/2026-08-18-ccs-fork-scaffolding/
- Sync fork-build-identity spec to main specs
- Annotate design doc as archived
- Add verification report
feat(fork): add dev config with isolated identifier and config dir
docs(fork): complete rebase whitelist with all fork-touched files
fix(fork): guard setTitle with isTauri in test/non-tauri env
fix(fork): define __CCS_FORK_BUILD__ in vitest config
feat(fork): add DevPanel and settings footer entry
feat(fork): set macOS window title to CC Switch (Fork) on startup
feat(fork): inject __CCS_FORK_BUILD__ compile-time constant
feat(fork): bump version to 3.19.2-fork.1 and rename product to CC Switch (Fork)
docs(fork): add upstream rebase workflow and ignore tool artifacts
fix(proxy): accept the whole grok-4.x (x>=5) family in the reasoning-effort whitelist (#7369)
Replace the verbatim grok-4.5 / grok-4.6 entries in supports_reasoning_effort with a rule that parses the grok-4.x minor version and accepts x >= 5, mirroring the existing GPT-5+ rule. This covers grok-4.7 (released 2026-09-21), whose reasoning effort was previously dropped on the Claude -> Chat, Claude -> Responses and Codex Responses -> Chat conversion paths, and lets future releases pass without another whitelist edit. The grok-build-* family is retained for saved providers.
Co-authored-by: allenxu09 <171831965+allenxu09@users.noreply.github.com>
docs(release): add v3.20.4 release notes
chore(release): v3.20.4
feat(pricing): add Grok 4.7 and GLM-5.3-FlashX
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.
fix(i18n): remove outdated OpenCode Go referral offer
fix: add SudoCode.chat alternate API endpoint
feat(settings): add a GitHub star prompt to the About card
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
feat(presets): add Soshow aggregator presets
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.
fix(ci): stabilize frontend tests and verify renderer build
fix(mcode): refresh provider state after removal (#7578)
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
fix(lib): reset skip_taskbar before window.show() across entry points (#6348)
5处 window.show() 调用中 4处没有在 show() 前重置 Windows 的 skip_taskbar,导致以下场景出现窗口可见但不在任务栏、点最小化后窗口消失的 bug:
- single_instance handler (lib.rs:360-361):当原进程因 silent_startup / 关闭到托盘等处于 skip_taskbar(true) 状态时,从快捷方式/开始菜单唤起已运行实例触发。
- handle_deeplink_url 的 focus_main_window 分支 (lib.rs:256):通过 ccswitch:// URL 唤起时。
- DB 恢复流程 (lib.rs:556):数据库版本不匹配时显示恢复界面。
- RunEvent::Opened 处理器 (lib.rs:1803):系统接收到 deep-link URL 时。
修复后的行为与 tray.rs::handle_tray_menu_event::show_main 分支一致(那里已有相同的 set_skip_taskbar(false) 调用)。
经用户测试验证:autostart + silent_startup + 从开始菜单/快捷方式启动的场景下,最小化按钮恢复正常工作。
fix(proxy): omit missing tool descriptions in the Codex responses-to-chat converter (#7378)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): preserve max effort for GPT-5.6 and GPT-6 Astra (#7531)
fix(misc): ignore shell startup output when probing tool versions in WSL (#7348)
try_get_version_wsl runs "${SHELL:-sh}" -lic '<tool> --version' through
wsl.exe and takes the first x.y.z anywhere in stdout. A login+interactive
shell runs the distro's startup files first, so their output shares that
stdout. On Ubuntu, /etc/profile.d/update-motd.sh prints the MOTD once a day,
starting with "Welcome to Ubuntu 24.04.4 LTS", and the card then showed
24.04.4 as the Claude Code version. The once-a-day stamp makes it appear
after the first probe of the day and vanish on refresh.
The payload now prints a sentinel before running the tool, and parsing looks
only at what follows its last occurrence. The last occurrence handles the
-lic || -lc || -c fallback chain printing it several times; matching it as a
substring handles OSC sequences glued in front of it. If the sentinel is
absent (the shell failed before the payload ran), the output is used as
before. The stderr fallback for tools that print their version there is kept.
Fixes #7347
Co-authored-by: lyfuci <lyfuci11@gmail.com>
fix(claude): hide session URL attribution (#7053)
* fix(claude): hide session URL attribution
* docs(claude): document session URL attribution
fix(ui): reset provider scroll when switching apps (#5211)
fix(pi): fix provider logo (#6826)
fix(i18n): use the Pi form's own term for the zh-TW duplicate-key message (#7187)
Follow-up to the nit on #6768. In zh-TW the Pi form labels the field
供應商識別碼 and says 供應商識別碼為必填 when it is empty, but the
duplicate message I added said 供應商標識 — the term Hermes, OpenCode and
OpenClaw use for their own field. One form, two names for the same thing.
The other three locales already keep the label and both messages on one
term, so this makes zh-TW match the pattern rather than introduce one:
en Provider key / Provider key is required / This provider key already exists
ja プロバイダーキー / プロバイダーキーを入力してください / このプロバイダーキーは既に存在します
zh 供应商标识 / 供应商标识不能为空 / 该供应商标识已存在
zh-TW 供應商識別碼 / 供應商識別碼為必填 / 該供應商標識已存在 <- the odd one out
hermes.form.providerKeyDuplicate carries the same sentence and is left
alone: its own label is 供應商標識, so it is already consistent.
Verified: managementListLocales 7/7. Full unit suite 1085/1087, the same
two App.test.tsx integration cases failing before and after.
fix(proxy): strip stop param for GitHub Copilot to unblock auto mode classifier (#5404)
Claude Code's auto mode classifier sends `stop_sequences` on every
safety check. On the GitHub Copilot path the Anthropic -> OpenAI Chat
conversion mapped it to `stop`, and Copilot's Chat Completions route
for Claude models answers that request with HTTP 400, so every
classifier call failed while normal chat kept working.
- Remove the converted `stop` field only when the provider is GitHub
Copilot; other OpenAI-compatible upstreams keep it.
- Copilot detection in the transform now also covers providers that
are recognised only through `env.ANTHROPIC_BASE_URL`.
- Regression tests for the Copilot (meta and env URL) and generic
provider cases.
Fixes #5175
fix(codex): keep stored auth when the live snapshot has no credential (#7434)
Editing the current Codex provider seeds the form from the live ~/.codex
snapshot, and switching away backfills that snapshot into the DB row. When a
provider table declares its own credential source (an Authorization header,
env_key, auth/aws), no bearer token is injected into config.toml and the
shared auth.json is deleted on third-party switches, so the live snapshot is
permanently `{ auth: {}, config }` while auth.OPENAI_API_KEY in the DB is the
only copy of the key. Both paths treated that empty live auth as
authoritative and silently erased the stored key, which in turn made the
usage-script modal misread the provider as an official subscription and
reset the saved script.
- Treat a credential-less live auth (missing auth.json or the bare
auth_mode logout marker) as an absent field in both the edit dialog and
the switch-away backfill, keeping the stored auth template. Live still
wins whenever it carries material, and official providers keep the live
login as their source of truth.
- Gate the official exemption on is_codex_official_provider /
resolveCodexOfficialIdentity rather than the category alone: a legacy
fixed official card without a persisted category is still written as
official, so the fallback must not resurrect a logged-out login on
switch-back.
- Cover both layers with regression tests, including the category-less
official shapes.
Fixes #7433
Supersedes #7492
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): lift additional_tools input carriers on the Codex chat conversion (#7454)
Codex talks to third-party providers over its Responses Lite transport,
which ships the request's extra tools in an `additional_tools` input
carrier: `{"type":"additional_tools","role":"developer","tools":[...]}`,
a role but no `content`. The Responses->Chat conversion let that carrier
fall through the generic "item with a role is a message" arm and emitted
`{"role":"system","content":null}` at messages[1], which strict Chat
gateways reject with `messages[1]: missing field "content"` (or
`data did not match any variant of ...SystemMessageContent`). The tools
carried inside it were also dropped on the Chat and Anthropic conversions.
- Skip `additional_tools` items in the Chat message walk; a carrier
declares tools, it is not a message.
- Collect carrier tools through the existing CodexToolContext registry
(dedup, namespace flattening, custom-tool conversion), which also feeds
the Anthropic conversion and the response-side name round-trip.
- Requests without a carrier convert byte-for-byte as before.
Known boundary: `custom` children inside a namespace tool
(`functions.exec`) are still not lifted; that needs its own response-side
name round-trip and is left for a follow-up.
Diagnosed independently in #6158 by BigStrongSun (fix in #6159) and in
fixture.
Fixes #7451
Fixes #6158
Refs #6033
Refs #6776
Supersedes #6159
fix(proxy): normalize Codex detail:original images for Chat gateways (#7476)
Codex marks tool-returned images with detail:"original" whenever the model
catalog advertises supports_image_detail_original, a Responses-only value.
The Responses->Chat conversion copied it verbatim into image_url.detail, and
strict OpenAI-compatible Chat gateways (Ark/Doubao, SenseNova, GLM, vLLM, ...)
reject the whole request with 400. Because Codex replays the full rollout on
every turn, one poisoned view_image result made the session fail permanently,
including plain-text turns.
Two layers:
- tool_media::merge_top_level_detail downgrades original -> auto, so already
poisoned rollouts and Codex versions before 0.155.0 (which do not normalize
replayed history themselves) recover immediately. auto/low/high are untouched.
- codex_catalog_model_entry always writes supports_image_detail_original:false
on the ProxyChat profile, so Codex stops emitting original at the source once
the catalog is regenerated. NativeResponses/Anthropic profiles and the DeepSeek
official catalog are unaffected.
Top-level user images were never affected: responses_content_to_chat_content
drops the part-level detail entirely.
Fixes #6495
Fixes #7458
Refs #6569
Refs #5805
fix(omo): detect unified config on the WSL side when the OpenCode dir is in WSL (#7550)
When the OpenCode config directory points into WSL
(\\wsl.localhost\<distro>\home\<user>\.config\opencode), OMO >= 4.19.3 keeps
its unified config in the WSL-side home (~/.omo/omo.jsonc). Detection only
probed the Windows home, never found it, and silently fell back to the legacy
per-plugin file that OMO no longer reads, so provider switches looked
successful but had no effect.
- Add derive_wsl_home_dir to derive the WSL-side home from a WSL UNC path
(home/<user> or root); non-WSL and non-home paths return None.
- Probe a list of home candidates for the unified config, WSL-side home
first and the OS home as a fallback, shared by the read, write and delete
paths.
- Detection only: when no unified config exists on either side the legacy
fallback is unchanged.
Fixes #7363
Supersedes #7367
docs(partner): add a FluxA purchase link and tighten its sponsor blurb
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.
fix(claude-desktop): expose the 1M context variant for DeepSeek V4 routes
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.
feat(presets): add FluxA Token Plan partner presets
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.
fix(presets): correct AICodeWith Codex-family endpoint to /v1
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.
fix(ui): render monochrome preset icons in the foreground color
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.
docs(user-manual): point Chat-only Codex examples at plan endpoints
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.
fix(pricing): restore DeepSeek V4 Pro price and seed Qwen3.8 and Hy4 models
- 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).
fix(presets): move MiniMax CN and BaiLing to their current official endpoints
- 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.
feat(codex): refresh CN presets from the Responses API audit
- 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.
test(claude-desktop): run Linux path tests on unix hosts and cover Linux in provider sync tests
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.
fix(ui): stop expanding stale cached usage tiers on ineligible provider cards
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.
fix(skills): 支持 skillId 与目录名不一致的技能安装和更新 (#6381)
* fix(skills): resolve mismatched skillId directories
* fix(skills): preserve source path during updates
* fix(skills): refresh source URL after repository relocation
* test(skills): isolate relocation fixture from legacy home
feat(opencode): 支持从获取的模型列表中搜索并批量添加所选模型 (#7515)
* feat: implement fetched models selection and UI components
* fix(opencode): prevent model search from submitting provider form
Add Kimi Global preset variants (kimi.ai platform + api endpoints) (#7526)
Add a "Kimi Global" twin immediately after the domestic "Kimi" API preset and a "Kimi For Coding Global" twin after the domestic "Kimi For Coding" preset, in every preset file that ships them (claude, claude-desktop, codex, hermes, openclaw, opencode, pi).
"Kimi Global" points to the international open platform https://platform.kimi.ai?aff=cc-switch (apiKeyUrl mirrored as https://platform.kimi.ai/console/api-keys?aff=cc-switch) and swaps the endpoint host api.moonshot.cn for api.moonshot.ai with each file's path unchanged (/v1 or /anthropic).
"Kimi For Coding Global" points to the international site https://www.kimi.ai/code/?aff=cc-switch and swaps the endpoint host api.kimi.com for api.kimi.ai with the path unchanged (e.g. https://api.kimi.ai/coding/v1).
Domestic presets are unchanged; category, icons, apiFormat, model catalogs, endpointCandidates (host swapped), partnerPromotionKey and settings otherwise mirror the domestic blocks exactly.
primePartner stays only on the domestic presets so the new variants do not alter prime-partner ordering.
The Pi twins get distinct providerKey values ("cc-switch-kimi-global", "cc-switch-kimi-for-coding-global") because providerKey is the preset-level primary key and must not collide with the domestic presets.
Rebalance sponsor CTAs around Kimi Code plan with dual-region links (#7522)
Kimi Code plan purchases have resumed, so the README sponsor sections go back to leading with the coding plan instead of the API-only flow written during the purchase pause.
All four locales (EN/ZH/JA/DE) now present Kimi Code plan (中文站 | Global) as the primary CTA and the Kimi Open Platform API (中文站 | Global, with the first-top-up bonus) as the secondary one, mirroring the domestic|global split already used for API links.
The aff and track_id values are unchanged.
fix(prompts): refresh active prompts after external file edits (#7194)
Reload the enabled prompt from its live file when the Prompts list loads, and refresh the open panel when the window regains focus, so external edits to CLAUDE.md / AGENTS.md show up without a restart.
- Keep saved templates available when the live file cannot be read.
- Skip the backfill for empty or whitespace-only files, matching enable_prompt.
- Skip the backfill while a sync/restore holds the sync lock, so a list load cannot write a stale live file into a freshly restored database.
fix(skills): raise archive entry limit so large skill repos install (#7489)
Installing hugohe3/ppt-master from skills.sh failed with
ARCHIVE_TOO_MANY_ENTRIES: the GitHub archive we download is the whole
repository (13,248 entries), and the 10,000-entry cap added in #5811
treated it as a zip bomb.
- Raise MAX_ARCHIVE_ENTRIES to 30,000 for both the remote archive and
local ZIP paths, and align WebDAV's MAX_EXTRACT_ENTRIES so a skills.zip
containing such a skill still restores.
- Charge every file entry at least one 4 KiB block against the existing
512 MiB budget, mirroring directory accounting, so empty-file archives
are bounded by the byte budget rather than by the entry count alone.
- Add tests for a 13k-entry repo archive and for the per-file minimum
charge.
Fixes: https://github.com/farion1231/cc-switch/issues/7475
feat: add MiniMax Code harness support (#7383)
Add MiniMax Code (mcode) as a managed application: provider and model management through the native custom_provider configuration, MCP servers, Skills, global instructions (AGENTS.md), read-only local session history with resume, and usage import from the native runtime database. Native files shared by the TUI and desktop are written under the native lock with rollback when the database commit fails, unrelated native configuration is preserved, and the data directory honors MINIMAX_DATA_DIR and MAVIS_DATA_DIR. Deleting sessions stays in the native application because they are coupled to runtime state.
fix(codex): recover stale account bindings during takeover (#7395)
After deleting a managed ChatGPT account and logging in again, a Codex provider can still reference the old local account ID, which made route switching, takeover, startup restoration and rebinding fail permanently.
Distinguish a missing outgoing account from an existing account with no matching live token. A missing account only releases its ownership marker after the persisted account store is verified; the target write policy stays in force. A stale target now reports an account-selection instruction instead of a bare not-found error, including when takeover is already enabled.
Fixes #7392
feat(claude-desktop): support 3P configuration on Linux (#7331)
Add Claude Desktop 3P configuration support on Linux. Resolve the config root from an absolute XDG_CONFIG_HOME, falling back to ~/.config; when CC Switch itself runs as a Flatpak, use the host ~/.config instead of the sandbox-private XDG_CONFIG_HOME. Update the en/zh/ja user manuals with Linux paths and the Flatpak boundary.
Fixes #4855
fix(proxy): omit missing tool descriptions instead of serializing null (#7319)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): keep reasoning effort for grok-4.6 and pass xhigh through verbatim (#7318)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(models): support Zhipu OpenAI Responses model lists (#7330)
Zhipu's Responses endpoint (open.bigmodel.cn/api/v1) serves its model list in the Codex remote catalog shape (models[].slug) instead of the OpenAI-compatible data[].id shape, so the Codex form's model fetch came back empty. The parser now falls back to models[].slug when data is absent.
fix(misc): fetch npm dist-tags from the dedicated endpoint with the probe timeout (#7346)
The tool version probe requested the full npm packument for each package (metadata for every historical version, 14-25 MB uncompressed for codex / opencode / openclaw) only to read its dist-tags, and it was the only latest-version probe without LATEST_PROBE_TIMEOUT, so it fell back to the proxy client's 600 s total timeout and the cards sat on "loading" for minutes on slow links while the large downloads starved the Hermes GitHub probe into "unknown".
Switch fetch_npm_dist_tags to registry.npmjs.org/-/package/{package}/dist-tags, whose body is the dist-tags object itself (tens of bytes to a few KB), escape scoped names as %2f, and apply the same 15 s probe timeout as the GitHub and PyPI probes. No data is lost: the only consumer reads latest and the tool's prerelease tags.
Fixes #7339
feat(usage): display output tokens per second in request log table and detail panel (#3369)
Show tokens/s next to the output token count in the request log table and the request detail panel.
The generation window prefers an explicit duration when present, otherwise latency minus first-token time for streaming requests, and falls back to full latency for non-streaming requests. Rows without a positive token count or window show nothing.
Closes #5936
fix(presets): update APIKEY.FUN URLs to apikey.fan
docs(release): add v3.20.3 release notes
chore(release): v3.20.3
fix(pricing): move retired DeepSeek V4 family to the V4.1 Flash tier
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.
fix(presets): declare third-party Token Plan DeepSeek V4 rows text-only
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.
feat(presets): rename DouBaoSeed preset to localized Volcengine Doubao
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.
fix(codex): skip unchanged incomplete rollout tails
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.
fix(claude): align quick model selection with form order
fix(tray): show bound ChatGPT account quota for managed Codex cards
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
feat(claude): add "Disable Artifact Tool" quick toggle to provider editor
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.
feat(codex): refresh aggregator model presets and catalogs
feat(codex): switch Kimi presets to native Responses direct-connect
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.
fix(usage): show Claude Fable weekly limit from the usage API limits array
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.
fix(codex): mirror vision-capable DeepSeek catalog and un-gate the legacy v4-flash alias (#7286)
Refresh the bundled DeepSeek official Codex catalog to the vendor's current models.json (slug deepseek-flash, image input modality, supports_image_detail_original) and remove deepseek-v4-flash from the confirmed text-only registry: the legacy id is still accepted by DeepSeek and routed to the vision-capable V4.1 Flash, so both the proxy media sanitizer and the generated Codex catalog must fail open for it. deepseek-v4-pro stays text-only per the vendor catalog.
Fixes #7283
fix(proxy): clamp sub-floor max_tokens to the Responses API minimum (#7287)
The Responses API rejects max_output_tokens below 16, but Anthropic clients legitimately send tiny probe budgets (Claude Desktop's model-availability probe uses max_tokens=1). The Anthropic→Responses conversion copied the value verbatim, so strict upstreams returned 400 and Claude Desktop reported the mapped model as unavailable under local routing.
Clamp 1..=15 up to 16 during conversion; 16+, 0 and non-integer values keep their pass-through semantics. Normal session requests convert byte-for-byte as before, and the Codex OAuth path still strips max_output_tokens afterwards.
Fixes #7103
fix(codex): coalesce adjacent commentary with pending tool calls (#7280)
The Codex Responses -> Chat Completions converter emitted two consecutive
assistant messages when one Responses model turn contained a commentary
message directly followed by function_call items. Chat upstreams imitate
the text-only assistant message as a complete turn and return
finish_reason=stop before the expected tool call, so long agent tasks end
after a progress update.
Merge pending tool calls into the directly adjacent assistant message when
it carries no tool_calls yet, attach pending reasoning per segment so
parallel calls do not duplicate it, and keep the new-message path for every
other boundary shape (user/tool boundaries, prior tool-call batches, media
flushes). The reasoning placeholder backfill still applies to coalesced
calls without reasoning.
Fixes #6529
Supersedes #6530
Co-authored-by: BigStrongSun <BigStrongSun@users.noreply.github.com>
fix(proxy): skip empty reasoning_content placeholders in Anthropic SSE streaming (#7227)
Some OpenAI-compatible upstreams (GLM, Qwen, DeepSeek-V4-Pro via ModelScope/DashScope,
Bailian, JD Cloud, ...) keep a `reasoning_content: ""` placeholder in every content
chunk instead of omitting the field. The reasoning branch of the OpenAI Chat -> Anthropic
SSE converter only checked for presence, and it shares `current_non_tool_block_type`
with the content branch, so every content chunk closed the open text block, opened an
empty thinking block, then reopened a text block. N content chunks became 2N content
blocks, half of them empty thinking blocks, which Claude Code rendered as one line per
token with dozens of empty Thought blocks and bloated the session JSONL.
Filter empty reasoning before entering the branch, matching the `!content.is_empty()`
guard the content branch already had and the non-streaming `openai_to_anthropic`
converter. Two regression tests pin block index, type and order: the placeholder shape
must yield a single text block and no thinking_delta at all, and real reasoning must
still yield one thinking block followed by one text block.
Fixes #5028
Fixes #4404
fix(codex): detect growing rollouts with a persisted byte cursor (#7219)
Codex keeps the rollout append handle open for the whole session, so on Windows NTFS the file mtime does not advance while the file grows and the pure-mtime gate skipped growing rollouts forever. Persist the observed byte length in session_log_sync.last_byte_offset for Codex rows and skip a file only when both mtime and size are unchanged. The parser now splits records on newlines manually so an incomplete trailing record is retried on the next pass without advancing the line cursor.
Fixes #6060
fix(codex): proxy image edits and Images API follow-ups (#7177)
* fix(codex): proxy image edits and Images API follow-ups
Follow-ups to #7036 for Codex's built-in ImageGen over the local proxy:
- Register `/images/edits` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them through the shared Codex standalone passthrough.
ImageGen posts to `images/edits` whenever a call references existing
images, and the router returned an empty 404 for it.
- Add `CodexStandaloneEndpoint::ImagesEdits`. Both Images endpoints share
one full-URL suffix table, so a complete Responses / Compact / Chat
Completions / Images URL derives the sibling endpoint (and vice versa)
while opaque full URLs keep failing closed.
- Apply the standalone sibling rewrite when the base URL is pasted as a
complete endpoint with the full-URL switch off, mirroring
`codex_chat_base_is_full_endpoint`; standalone requests no longer become
`.../chat/completions/images/generations`.
- Parse Images API streaming usage: `image_generation.completed` carries
`usage` on the event top level, so scan for `usage.input_tokens` before
falling back to the Chat Completions stream parser.
Related #7036
* fix(codex): match standalone URL suffixes case-insensitively
Keep endpoint detection and sibling rewriting consistent while preserving the original URL prefix and query. Cover mixed-case Images and Alpha Search endpoints through URL and proxy regression cases.
---------
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): preserve per-app settings on shutdown and port allocation (#7210)
On normal exit, stop_with_restore_keep_state() called the legacy global
proxy-config writer to clear the long-retired live_takeover_active flag.
That writer reads the Claude row and runs an UPDATE without a WHERE clause,
so every shutdown copied Claude's max_retries and the three timeout fields
onto the Codex, Gemini and Grok Build rows — and the UPDATE never carried
live_takeover_active in the first place, so the step had no effect beyond
the clobber. The ephemeral listen-port path did the same on every proxy
start.
Drop the dead write-back (live-config restore, backup cleanup and health
reset are unchanged) and persist the resolved ephemeral port through the
global-config interface, which only touches the shared listen/logging
columns. Two regression tests seed distinct per-app settings for all four
apps and assert they survive both paths.
Fixes #7204
fix(provider): preserve child metadata during universal sync (#7212)
Syncing a universal provider regenerates its Claude/Codex/Gemini child
providers. The child's `settings_config` was merged with the existing row,
but `meta`, `created_at` and `sort_index` were taken from the generated
object, and the universal provider never carries any of them from the UI.
The UPDATE therefore wiped the child's usage script, common-config opt-out,
endpoint auto-select and other per-app settings to `{}` and reset the sort
index to NULL, which pushed the card to the bottom of the list and looked
like the provider had been deleted and re-created.
Keep the existing child's `meta`, `created_at` and `sort_index` when the
row already exists. Name, base URL, key, models, website and notes are
still driven by the universal provider, and first-time creation still
inherits the parent's metadata.
Fixes #7134
feat(presets): rebrand DashScope as 千问AI平台 and refresh Qwen models (#7183)
Rename the domestic Bailian presets to 千问AI平台 with a dedicated icon, refresh the Qwen family to the 3.8 generation across Claude Code, Claude Desktop, Codex, Hermes, OpenClaw, OpenCode and Pi, add the domestic Token Plan presets, restore the international QwenCloud Coding Plan presets, and align OpenClaw/OpenCode/Pi model metadata with the official client docs.
fix(codex): honor proxy URL when model_provider is omitted (#7263)
When a Codex provider card's TOML has no `model_provider`, Codex falls back to
its built-in `openai` provider, but takeover wrote the proxy address as a
top-level `base_url` that Codex never reads, so requests bypassed the local
proxy and went straight to api.openai.com.
Treat the missing selector as the built-in `openai` provider and write the
proxy address to `openai_base_url`; the existing legacy-reroute migration then
turns it into a `cc-switch` custom provider table carrying the `PROXY_MANAGED`
bearer, the same shape every other third-party takeover uses. Explicit
provider selections are unchanged.
Refs #6256, #7217
fix(provider): update MiniMax defaults to M3 and remove expired offers (#7255)
* fix(provider): refresh MiniMax M3 presets and remove expired promotions
* fix(provider): complete MiniMax M3 capability declarations
---------
Co-authored-by: saladday <1203511142@qq.com>
chore(provider): remove Atlas Cloud sponsor status, keep presets
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`
docs(release): add v3.20.2 release notes
chore(release): v3.20.2
feat(provider): add SoleAPI sponsor presets across all nine apps
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.
fix(codex): proxy image edits endpoint
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.
fix(provider): pin models URL for JieKou and Novita Claude presets
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.
feat(pricing): refresh seed for September 2026 vendor price changes
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.
fix(codex): stamp requires_openai_auth on takeover writes to match the live login state
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.
fix(updater): show the real reason when an update check fails (#6482)
The updater plugin rejects with a plain string, so `err instanceof Error`
never matched and the real failure reason was replaced by the generic
fallback message. Use the existing extractErrorMessage helper, and pass
the recovered message into the check-update failure toast so it actually
reaches the user instead of stopping at an unread context state.
Co-authored-by: Jason <farion1231@gmail.com>
docs: fix the locales path in the README directory trees (#6100)
All four READMEs showed locales/ as a direct child of src/, but the
translations live at src/i18n/locales/. Split it into two tree nodes so it
matches how the rest of the tree is drawn. Also fixes a CLaude -> Claude
typo in README_ZH.md.
Co-authored-by: Jason <farion1231@gmail.com>
fix(claude): exclude workflow journal.jsonl from session list
Claude Code's workflow feature writes subagents/workflows/wf_*/journal.jsonl inside the session directory. The Claude session scanner only excluded agent-* files, so each journal parsed as an empty ghost session titled "journal". Exclude journal.jsonl by file name and add regression tests.
Fixes #6042
fix(i18n): add the missing pi.form.providerKeyDuplicate and common.collapse keys
translatePiProviderMutationError returns pi.form.providerKeyDuplicate for the Pi backend's duplicate-key error, but the key existed in none of the four locale files, so the toast showed the raw key. common.collapse (aria-label of the thinking-map toggle) was missing the same way. Add both keys to zh/en/ja/zh-TW and lock them in the locale coverage test.
fix(proxy): stop mask_url panicking on multi-byte UTF-8 near the truncation boundary
mask_url's fallback branch (used when the proxy URL fails Url::parse) sliced the raw string at byte 20 and panicked when a multi-byte UTF-8 character straddled that offset, e.g. a CJK string pasted into the global proxy address. Snap the cut to the nearest char boundary at or before 20 and add a regression test.
fix(tests): isolate LOCALAPPDATA so tests stop writing the real Claude Desktop config
On Windows, Claude Desktop config paths resolve from LOCALAPPDATA, which the test home override in tests/support.rs did not redirect, so profile_roundtrip wrote the https://desktop.test fixture into the developer's real Claude Desktop config. Point LOCALAPPDATA at the isolated test home alongside HOME and USERPROFILE.
Fixes #6077
fix(usage): widen pricing model source select to fit localized labels
The pricing model source SelectTrigger in the usage cost settings table was w-28, which leaves about 70px for the label; the English and Japanese labels need about 107px and overflowed the trigger. Widen it to w-40 and drop the h-7 overrides so the trigger and the adjacent Input use the shared h-9 height.
feat(pi): add Tencent TokenHub and Token Plan presets (#7159)
Add eight Tencent presets for Pi, covering the TokenHub pay-as-you-go line
(domestic and international) and the six Token Plan subscription products —
Pi was the only managed app still missing them. Model IDs, ordering and
endpoints follow Tencent's own model lists verbatim, and the tests assert each
product's line-up in order so future drift is caught.
Declare thinking control alongside the capabilities rather than leaving it to
defaults: the 31 DeepSeek entries carry a Tencent-specific compat so selecting
"off" actually disables thinking instead of silently billing reasoning tokens,
the eight Kimi K2.7 Code entries no longer offer an "off" level the model
cannot honour, and kimi-k2.7-code-highspeed is marked image-capable so
tool-returned screenshots are no longer dropped.
Drop minimax-m2.5 from every app: it is gone from Tencent's model tables and
the platform is retiring it.
fix(codex): proxy image generations endpoint (#7036)
Codex's built-in ImageGen calls the legacy OpenAI Images API, but the local
proxy only registered Responses / Chat Completions / Compact / Alpha Search
routes, so `/v1/images/generations` returned an empty 404 under Codex routing.
- Register `/images/generations` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them as a Codex standalone passthrough shared with Alpha Search.
- Generalize the Alpha Search full-URL rewrite into
`rewrite_codex_standalone_full_url`: derive the sibling
`/images/generations` from full Responses / Compact / Chat Completions URLs,
keep an existing Images URL as-is, and fail closed on opaque full URLs.
- Record `usage.input_tokens` / `output_tokens` through the Codex us…
chore(connectivity-test): 勾选 Task 6 / OpenSpec 2.2-2.3
fix(connectivity-test): 模型列表解析与后端对齐(每条目首个匹配,提取共享 helper)
feat(connectivity-test): 连通性测试弹窗/详情/徽标组件与四语言文案
chore(connectivity-test): 勾选 Task 5 / OpenSpec 2.1
test(connectivity-test): 锁定 toBackendParams 字段映射(customHeaders/customBody 等)
feat(connectivity-test): 前端命令绑定与测试/探针状态 hooks
chore(connectivity-test): 勾选 Task 4 / OpenSpec 1.4
feat(connectivity-test): 测试参数持久化纯函数(缺省即默认值)
chore(connectivity-test): 勾选 Task 3 / OpenSpec 1.3
feat(connectivity-test): Tauri 命令与注册,官方/动态端点过滤,sanitize 隔离私有字段
chore(connectivity-test): 勾选 Task 2 / OpenSpec 1.2
feat(connectivity-test): 后端直连执行器(reqwest+SSE,总/空闲超时,度量与错误识别)
feat(connectivity-test): 后端直连执行器(reqwest+SSE,总/空闲超时,度量与错误识别)
chore(connectivity-test): 勾选 Task 1 / OpenSpec 1.1
fix(connectivity-test): claude 鉴权对齐 adapter 顶层 apiKey/api_key 兜底
feat(connectivity-test): 新增后端连通性测试服务类型/模型解析/协议构造
chore: archive model-quick-switch-picker-scroll
chore: model-quick-switch-picker-scroll 验证报告与状态元数据
fix: 模型快捷切换弹窗内模型列表滚轮无法滚动
模态 Dialog 的 RemoveScroll 会对 shards 外的 portaled PopoverContent
wheel 事件无条件 preventDefault。在 SearchableModelPicker 的
PopoverContent 捕获阶段阻断 wheel 传播,恢复原生滚动。
chore: bump fork version to 3.20.1-1 after upstream v3.20.1 rebase
fix(form): only auto-expand Claude advanced options on divergent model mapping
Uniform mapping produced by the quick-set button (all roles equal to the
fallback model) no longer counts as an advanced value, so the advanced
section stays collapsed on edit. A 1M marker is stripped before comparing
so marked roles are not misjudged as divergent. Truly customized mappings
still auto-expand the section.
feat: align fallback-model quick-set buttons with model input right edge
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
docs: update README with fork build notes and quick-move provider changes
chore: archive comet-native claude-model-quick-access
feat: surface Claude fallback model as quick-access section below endpoint
Move the default fallback model (ANTHROPIC_MODEL) block out of the
collapsed Advanced Options into a quick-access section directly below
the request endpoint: input + 1M checkbox + quick-set + fetch-models
buttons are all reachable without expanding.
- header toggle-all "declare 1M" now includes the fallback model
(Haiku still excluded); partial selection shows indeterminate state
- drop the duplicated quick-set/fetch buttons and the old fallback
block inside Advanced Options
- exclude claudeModel from hasAnyAdvancedValue so a fallback-only
value no longer auto-expands Advanced Options
- quick-set priority, env write semantics, and OAuth preset branches
unchanged; add 4 component tests covering the new layout
chore: archive comet-native provider-right-click-top-bottom
Enable native workflow in .comet/config.yaml and move the Comet managed
state block in .gitignore so the exclusion rules stay grouped with the
other fork tool artifacts.
feat: add provider right-click move-to-top/bottom and insert new providers at second position
- ProviderList: context menu with quick move-to-top/move-to-bottom, reusing
update_providers_sort_order and refreshing tray menu like drag sort
- useAddProviderMutation: default insert at index 1 with explicit sortIndex
materialization for existing items (avoids defined-first sort trap)
- i18n: quickMove* strings for zh/en/zh-TW/ja
ci: stop flaky WSL2 linker job from blocking main
The backend-windows-wsl2 job intermittently fails with LNK1327 /
mt.exe c1010070 when the Windows runner's mt.exe cannot parse a
manifest temp file on the WSL2 UNC TEMP path. The same flaky failure
occurs upstream and is unrelated to fork changes. Mark the job
continue-on-error so main stops going red on it; the result stays
visible in the Actions UI.
style: apply prettier to files touched by recent comet archives
Frontend CI format:check failed on 6 files modified by the app-name
and model-quick-switch archive commits plus the updater-disable commit.
Run prettier --write so format:check passes again.
chore: archive ccs-fork-app-name-cc-switch
chore: archive ccs-model-quick-switch-ui-polish
docs: rewrite README to document fork differences vs upstream
README 从上游完整功能介绍改为仅记录 fork 与上游的差别,完整功能
文档指回上游。涵盖 8 项 fork 魔改:构建标识/开发预览、预设精简、
Claude 表单调整、模型搜索移植、主页模型快速切换、侧边栏面板显隐、
关闭应用内更新、打包范围,并说明版本号与 rebase 同步流程。
feat: disable in-app updater, point users to GitHub releases
移除 tauri-plugin-updater 依赖与自动更新链路,改为统一引导用户到
GitHub Releases 手动下载新版。
- 前端:updater.ts 恒返回 up-to-date;UpdateContext 取消启动自检;
AboutSection「检查更新」直接打开 fork releases 页;DatabaseUpgrade
恢复界面去掉下载进度条,改为打开发布页。
- 后端:移除 updater 插件注册与 UpdaterExt 调用;
install_update_and_restart 降级为空壳,check_app_update_available
返回当前版本号;check_for_updates 指向 fork releases;capabilities
去掉 updater:default 权限。
- 打包:tauri.conf.json 删 plugins.updater、createUpdaterArtifacts=false;
Cargo.toml / package.json 删 updater 依赖,Cargo.lock / pnpm-lock
同步重生成。
- CI:release.yml 移除签名收集与 assemble-latest-json job,删除
.tar.gz/.sig 处理,release body 去掉 updater 说明。
版本号统一为 3.20.0-1(合法 semver 且 MSI 第 4 位为 1)。
Cargo.lock / pnpm-lock.yaml 已随依赖移除重生成,cargo check 通过。
ci: build only Windows x64 and macOS arm64, unsigned
feat: gate dev panel behind dev-only env flag
chore(comet): archive ccs-sidebar-panels-visibility change
fix: apply skills visibility guard to hermes branch toolbar button
feat: include prompts in sidebar panel visibility settings
fix: persist visibleSidebarPanels setting in backend AppSettings
style: use compact pill toggles for sidebar panel visibility
feat: add sidebar panel visibility switches to settings
feat: add sidebar panel visibility settings and wire into toolbar buttons
chore(comet): archive ccs-home-model-display change
fix(fork): read grokbuild api key from config TOML in quick switch dialog
feat(fork): align model badge with upstream extractModelBadge
feat(fork): show current model badge and quick-switch entry on provider cards
feat(fork): add model quick switch dialog
feat(fork): add provider model read/write utils with tests
chore(comet): archive ccs-model-search-port change
feat(i18n): add model search picker keys for all locales
feat(form): port SearchableModelPicker from upstream for searchable model selection
chore(comet): archive ccs-claude-form-layout change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-claude-form-layout/
- Sync claude-form-layout spec to main specs
- Annotate design doc as archived
- Add verification report
fix(i18n): update advanced options hint, add ja/zh-TW toggle-all, drop dead apiFormatHint key
fix(form): correct 1M toggle-all alignment direction (ml-1)
fix(form): align 1M toggle-all checkbox with row checkboxes
refactor(form): move api format select into EndpointField rightSlot
fix(form): widen api format select to fit option text
feat(form): add 1M toggle-all checkbox in model mapping header
feat(form): move api format select next to endpoint input
chore(comet): archive ccs-provider-presets-trim change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-provider-presets-trim/
- Sync provider-presets-visibility spec to main specs
- Annotate design doc as archived
- Add verification report
docs(fork): add presets-trim files to rebase whitelist
test(fork): mock IS_FORK_BUILD false in unit tests for upstream-form semantics
feat(fork): wire official preset filter into provider forms
feat(fork): add hidden field to preset interfaces
feat(fork): add official preset allowlist and filter util
chore(comet): archive ccs-fork-scaffolding change
- Move change artifacts to docs/openspec/changes/archive/2026-08-18-ccs-fork-scaffolding/
- Sync fork-build-identity spec to main specs
- Annotate design doc as archived
- Add verification report
feat(fork): add dev config with isolated identifier and config dir
docs(fork): complete rebase whitelist with all fork-touched files
fix(fork): guard setTitle with isTauri in test/non-tauri env
fix(fork): define __CCS_FORK_BUILD__ in vitest config
feat(fork): add DevPanel and settings footer entry
feat(fork): set macOS window title to CC Switch (Fork) on startup
feat(fork): inject __CCS_FORK_BUILD__ compile-time constant
feat(fork): bump version to 3.19.2-fork.1 and rename product to CC Switch (Fork)
docs(fork): add upstream rebase workflow and ignore tool artifacts
fix(proxy): accept the whole grok-4.x (x>=5) family in the reasoning-effort whitelist (#7369)
Replace the verbatim grok-4.5 / grok-4.6 entries in supports_reasoning_effort with a rule that parses the grok-4.x minor version and accepts x >= 5, mirroring the existing GPT-5+ rule. This covers grok-4.7 (released 2026-09-21), whose reasoning effort was previously dropped on the Claude -> Chat, Claude -> Responses and Codex Responses -> Chat conversion paths, and lets future releases pass without another whitelist edit. The grok-build-* family is retained for saved providers.
Co-authored-by: allenxu09 <171831965+allenxu09@users.noreply.github.com>
docs(release): add v3.20.4 release notes
chore(release): v3.20.4
feat(pricing): add Grok 4.7 and GLM-5.3-FlashX
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.
fix(i18n): remove outdated OpenCode Go referral offer
fix: add SudoCode.chat alternate API endpoint
feat(settings): add a GitHub star prompt to the About card
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
feat(presets): add Soshow aggregator presets
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.
fix(ci): stabilize frontend tests and verify renderer build
fix(mcode): refresh provider state after removal (#7578)
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
fix(lib): reset skip_taskbar before window.show() across entry points (#6348)
5处 window.show() 调用中 4处没有在 show() 前重置 Windows 的 skip_taskbar,导致以下场景出现窗口可见但不在任务栏、点最小化后窗口消失的 bug:
- single_instance handler (lib.rs:360-361):当原进程因 silent_startup / 关闭到托盘等处于 skip_taskbar(true) 状态时,从快捷方式/开始菜单唤起已运行实例触发。
- handle_deeplink_url 的 focus_main_window 分支 (lib.rs:256):通过 ccswitch:// URL 唤起时。
- DB 恢复流程 (lib.rs:556):数据库版本不匹配时显示恢复界面。
- RunEvent::Opened 处理器 (lib.rs:1803):系统接收到 deep-link URL 时。
修复后的行为与 tray.rs::handle_tray_menu_event::show_main 分支一致(那里已有相同的 set_skip_taskbar(false) 调用)。
经用户测试验证:autostart + silent_startup + 从开始菜单/快捷方式启动的场景下,最小化按钮恢复正常工作。
fix(proxy): omit missing tool descriptions in the Codex responses-to-chat converter (#7378)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): preserve max effort for GPT-5.6 and GPT-6 Astra (#7531)
fix(misc): ignore shell startup output when probing tool versions in WSL (#7348)
try_get_version_wsl runs "${SHELL:-sh}" -lic '<tool> --version' through
wsl.exe and takes the first x.y.z anywhere in stdout. A login+interactive
shell runs the distro's startup files first, so their output shares that
stdout. On Ubuntu, /etc/profile.d/update-motd.sh prints the MOTD once a day,
starting with "Welcome to Ubuntu 24.04.4 LTS", and the card then showed
24.04.4 as the Claude Code version. The once-a-day stamp makes it appear
after the first probe of the day and vanish on refresh.
The payload now prints a sentinel before running the tool, and parsing looks
only at what follows its last occurrence. The last occurrence handles the
-lic || -lc || -c fallback chain printing it several times; matching it as a
substring handles OSC sequences glued in front of it. If the sentinel is
absent (the shell failed before the payload ran), the output is used as
before. The stderr fallback for tools that print their version there is kept.
Fixes #7347
Co-authored-by: lyfuci <lyfuci11@gmail.com>
fix(claude): hide session URL attribution (#7053)
* fix(claude): hide session URL attribution
* docs(claude): document session URL attribution
fix(ui): reset provider scroll when switching apps (#5211)
fix(pi): fix provider logo (#6826)
fix(i18n): use the Pi form's own term for the zh-TW duplicate-key message (#7187)
Follow-up to the nit on #6768. In zh-TW the Pi form labels the field
供應商識別碼 and says 供應商識別碼為必填 when it is empty, but the
duplicate message I added said 供應商標識 — the term Hermes, OpenCode and
OpenClaw use for their own field. One form, two names for the same thing.
The other three locales already keep the label and both messages on one
term, so this makes zh-TW match the pattern rather than introduce one:
en Provider key / Provider key is required / This provider key already exists
ja プロバイダーキー / プロバイダーキーを入力してください / このプロバイダーキーは既に存在します
zh 供应商标识 / 供应商标识不能为空 / 该供应商标识已存在
zh-TW 供應商識別碼 / 供應商識別碼為必填 / 該供應商標識已存在 <- the odd one out
hermes.form.providerKeyDuplicate carries the same sentence and is left
alone: its own label is 供應商標識, so it is already consistent.
Verified: managementListLocales 7/7. Full unit suite 1085/1087, the same
two App.test.tsx integration cases failing before and after.
fix(proxy): strip stop param for GitHub Copilot to unblock auto mode classifier (#5404)
Claude Code's auto mode classifier sends `stop_sequences` on every
safety check. On the GitHub Copilot path the Anthropic -> OpenAI Chat
conversion mapped it to `stop`, and Copilot's Chat Completions route
for Claude models answers that request with HTTP 400, so every
classifier call failed while normal chat kept working.
- Remove the converted `stop` field only when the provider is GitHub
Copilot; other OpenAI-compatible upstreams keep it.
- Copilot detection in the transform now also covers providers that
are recognised only through `env.ANTHROPIC_BASE_URL`.
- Regression tests for the Copilot (meta and env URL) and generic
provider cases.
Fixes #5175
fix(codex): keep stored auth when the live snapshot has no credential (#7434)
Editing the current Codex provider seeds the form from the live ~/.codex
snapshot, and switching away backfills that snapshot into the DB row. When a
provider table declares its own credential source (an Authorization header,
env_key, auth/aws), no bearer token is injected into config.toml and the
shared auth.json is deleted on third-party switches, so the live snapshot is
permanently `{ auth: {}, config }` while auth.OPENAI_API_KEY in the DB is the
only copy of the key. Both paths treated that empty live auth as
authoritative and silently erased the stored key, which in turn made the
usage-script modal misread the provider as an official subscription and
reset the saved script.
- Treat a credential-less live auth (missing auth.json or the bare
auth_mode logout marker) as an absent field in both the edit dialog and
the switch-away backfill, keeping the stored auth template. Live still
wins whenever it carries material, and official providers keep the live
login as their source of truth.
- Gate the official exemption on is_codex_official_provider /
resolveCodexOfficialIdentity rather than the category alone: a legacy
fixed official card without a persisted category is still written as
official, so the fallback must not resurrect a logged-out login on
switch-back.
- Cover both layers with regression tests, including the category-less
official shapes.
Fixes #7433
Supersedes #7492
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): lift additional_tools input carriers on the Codex chat conversion (#7454)
Codex talks to third-party providers over its Responses Lite transport,
which ships the request's extra tools in an `additional_tools` input
carrier: `{"type":"additional_tools","role":"developer","tools":[...]}`,
a role but no `content`. The Responses->Chat conversion let that carrier
fall through the generic "item with a role is a message" arm and emitted
`{"role":"system","content":null}` at messages[1], which strict Chat
gateways reject with `messages[1]: missing field "content"` (or
`data did not match any variant of ...SystemMessageContent`). The tools
carried inside it were also dropped on the Chat and Anthropic conversions.
- Skip `additional_tools` items in the Chat message walk; a carrier
declares tools, it is not a message.
- Collect carrier tools through the existing CodexToolContext registry
(dedup, namespace flattening, custom-tool conversion), which also feeds
the Anthropic conversion and the response-side name round-trip.
- Requests without a carrier convert byte-for-byte as before.
Known boundary: `custom` children inside a namespace tool
(`functions.exec`) are still not lifted; that needs its own response-side
name round-trip and is left for a follow-up.
Diagnosed independently in #6158 by BigStrongSun (fix in #6159) and in
fixture.
Fixes #7451
Fixes #6158
Refs #6033
Refs #6776
Supersedes #6159
fix(proxy): normalize Codex detail:original images for Chat gateways (#7476)
Codex marks tool-returned images with detail:"original" whenever the model
catalog advertises supports_image_detail_original, a Responses-only value.
The Responses->Chat conversion copied it verbatim into image_url.detail, and
strict OpenAI-compatible Chat gateways (Ark/Doubao, SenseNova, GLM, vLLM, ...)
reject the whole request with 400. Because Codex replays the full rollout on
every turn, one poisoned view_image result made the session fail permanently,
including plain-text turns.
Two layers:
- tool_media::merge_top_level_detail downgrades original -> auto, so already
poisoned rollouts and Codex versions before 0.155.0 (which do not normalize
replayed history themselves) recover immediately. auto/low/high are untouched.
- codex_catalog_model_entry always writes supports_image_detail_original:false
on the ProxyChat profile, so Codex stops emitting original at the source once
the catalog is regenerated. NativeResponses/Anthropic profiles and the DeepSeek
official catalog are unaffected.
Top-level user images were never affected: responses_content_to_chat_content
drops the part-level detail entirely.
Fixes #6495
Fixes #7458
Refs #6569
Refs #5805
fix(omo): detect unified config on the WSL side when the OpenCode dir is in WSL (#7550)
When the OpenCode config directory points into WSL
(\\wsl.localhost\<distro>\home\<user>\.config\opencode), OMO >= 4.19.3 keeps
its unified config in the WSL-side home (~/.omo/omo.jsonc). Detection only
probed the Windows home, never found it, and silently fell back to the legacy
per-plugin file that OMO no longer reads, so provider switches looked
successful but had no effect.
- Add derive_wsl_home_dir to derive the WSL-side home from a WSL UNC path
(home/<user> or root); non-WSL and non-home paths return None.
- Probe a list of home candidates for the unified config, WSL-side home
first and the OS home as a fallback, shared by the read, write and delete
paths.
- Detection only: when no unified config exists on either side the legacy
fallback is unchanged.
Fixes #7363
Supersedes #7367
docs(partner): add a FluxA purchase link and tighten its sponsor blurb
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.
fix(claude-desktop): expose the 1M context variant for DeepSeek V4 routes
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.
feat(presets): add FluxA Token Plan partner presets
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.
fix(presets): correct AICodeWith Codex-family endpoint to /v1
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.
fix(ui): render monochrome preset icons in the foreground color
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.
docs(user-manual): point Chat-only Codex examples at plan endpoints
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.
fix(pricing): restore DeepSeek V4 Pro price and seed Qwen3.8 and Hy4 models
- 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).
fix(presets): move MiniMax CN and BaiLing to their current official endpoints
- 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.
feat(codex): refresh CN presets from the Responses API audit
- 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.
test(claude-desktop): run Linux path tests on unix hosts and cover Linux in provider sync tests
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.
fix(ui): stop expanding stale cached usage tiers on ineligible provider cards
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.
fix(skills): 支持 skillId 与目录名不一致的技能安装和更新 (#6381)
* fix(skills): resolve mismatched skillId directories
* fix(skills): preserve source path during updates
* fix(skills): refresh source URL after repository relocation
* test(skills): isolate relocation fixture from legacy home
feat(opencode): 支持从获取的模型列表中搜索并批量添加所选模型 (#7515)
* feat: implement fetched models selection and UI components
* fix(opencode): prevent model search from submitting provider form
Add Kimi Global preset variants (kimi.ai platform + api endpoints) (#7526)
Add a "Kimi Global" twin immediately after the domestic "Kimi" API preset and a "Kimi For Coding Global" twin after the domestic "Kimi For Coding" preset, in every preset file that ships them (claude, claude-desktop, codex, hermes, openclaw, opencode, pi).
"Kimi Global" points to the international open platform https://platform.kimi.ai?aff=cc-switch (apiKeyUrl mirrored as https://platform.kimi.ai/console/api-keys?aff=cc-switch) and swaps the endpoint host api.moonshot.cn for api.moonshot.ai with each file's path unchanged (/v1 or /anthropic).
"Kimi For Coding Global" points to the international site https://www.kimi.ai/code/?aff=cc-switch and swaps the endpoint host api.kimi.com for api.kimi.ai with the path unchanged (e.g. https://api.kimi.ai/coding/v1).
Domestic presets are unchanged; category, icons, apiFormat, model catalogs, endpointCandidates (host swapped), partnerPromotionKey and settings otherwise mirror the domestic blocks exactly.
primePartner stays only on the domestic presets so the new variants do not alter prime-partner ordering.
The Pi twins get distinct providerKey values ("cc-switch-kimi-global", "cc-switch-kimi-for-coding-global") because providerKey is the preset-level primary key and must not collide with the domestic presets.
Rebalance sponsor CTAs around Kimi Code plan with dual-region links (#7522)
Kimi Code plan purchases have resumed, so the README sponsor sections go back to leading with the coding plan instead of the API-only flow written during the purchase pause.
All four locales (EN/ZH/JA/DE) now present Kimi Code plan (中文站 | Global) as the primary CTA and the Kimi Open Platform API (中文站 | Global, with the first-top-up bonus) as the secondary one, mirroring the domestic|global split already used for API links.
The aff and track_id values are unchanged.
fix(prompts): refresh active prompts after external file edits (#7194)
Reload the enabled prompt from its live file when the Prompts list loads, and refresh the open panel when the window regains focus, so external edits to CLAUDE.md / AGENTS.md show up without a restart.
- Keep saved templates available when the live file cannot be read.
- Skip the backfill for empty or whitespace-only files, matching enable_prompt.
- Skip the backfill while a sync/restore holds the sync lock, so a list load cannot write a stale live file into a freshly restored database.
fix(skills): raise archive entry limit so large skill repos install (#7489)
Installing hugohe3/ppt-master from skills.sh failed with
ARCHIVE_TOO_MANY_ENTRIES: the GitHub archive we download is the whole
repository (13,248 entries), and the 10,000-entry cap added in #5811
treated it as a zip bomb.
- Raise MAX_ARCHIVE_ENTRIES to 30,000 for both the remote archive and
local ZIP paths, and align WebDAV's MAX_EXTRACT_ENTRIES so a skills.zip
containing such a skill still restores.
- Charge every file entry at least one 4 KiB block against the existing
512 MiB budget, mirroring directory accounting, so empty-file archives
are bounded by the byte budget rather than by the entry count alone.
- Add tests for a 13k-entry repo archive and for the per-file minimum
charge.
Fixes: https://github.com/farion1231/cc-switch/issues/7475
feat: add MiniMax Code harness support (#7383)
Add MiniMax Code (mcode) as a managed application: provider and model management through the native custom_provider configuration, MCP servers, Skills, global instructions (AGENTS.md), read-only local session history with resume, and usage import from the native runtime database. Native files shared by the TUI and desktop are written under the native lock with rollback when the database commit fails, unrelated native configuration is preserved, and the data directory honors MINIMAX_DATA_DIR and MAVIS_DATA_DIR. Deleting sessions stays in the native application because they are coupled to runtime state.
fix(codex): recover stale account bindings during takeover (#7395)
After deleting a managed ChatGPT account and logging in again, a Codex provider can still reference the old local account ID, which made route switching, takeover, startup restoration and rebinding fail permanently.
Distinguish a missing outgoing account from an existing account with no matching live token. A missing account only releases its ownership marker after the persisted account store is verified; the target write policy stays in force. A stale target now reports an account-selection instruction instead of a bare not-found error, including when takeover is already enabled.
Fixes #7392
feat(claude-desktop): support 3P configuration on Linux (#7331)
Add Claude Desktop 3P configuration support on Linux. Resolve the config root from an absolute XDG_CONFIG_HOME, falling back to ~/.config; when CC Switch itself runs as a Flatpak, use the host ~/.config instead of the sandbox-private XDG_CONFIG_HOME. Update the en/zh/ja user manuals with Linux paths and the Flatpak boundary.
Fixes #4855
fix(proxy): omit missing tool descriptions instead of serializing null (#7319)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): keep reasoning effort for grok-4.6 and pass xhigh through verbatim (#7318)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(models): support Zhipu OpenAI Responses model lists (#7330)
Zhipu's Responses endpoint (open.bigmodel.cn/api/v1) serves its model list in the Codex remote catalog shape (models[].slug) instead of the OpenAI-compatible data[].id shape, so the Codex form's model fetch came back empty. The parser now falls back to models[].slug when data is absent.
fix(misc): fetch npm dist-tags from the dedicated endpoint with the probe timeout (#7346)
The tool version probe requested the full npm packument for each package (metadata for every historical version, 14-25 MB uncompressed for codex / opencode / openclaw) only to read its dist-tags, and it was the only latest-version probe without LATEST_PROBE_TIMEOUT, so it fell back to the proxy client's 600 s total timeout and the cards sat on "loading" for minutes on slow links while the large downloads starved the Hermes GitHub probe into "unknown".
Switch fetch_npm_dist_tags to registry.npmjs.org/-/package/{package}/dist-tags, whose body is the dist-tags object itself (tens of bytes to a few KB), escape scoped names as %2f, and apply the same 15 s probe timeout as the GitHub and PyPI probes. No data is lost: the only consumer reads latest and the tool's prerelease tags.
Fixes #7339
feat(usage): display output tokens per second in request log table and detail panel (#3369)
Show tokens/s next to the output token count in the request log table and the request detail panel.
The generation window prefers an explicit duration when present, otherwise latency minus first-token time for streaming requests, and falls back to full latency for non-streaming requests. Rows without a positive token count or window show nothing.
Closes #5936
fix(presets): update APIKEY.FUN URLs to apikey.fan
docs(release): add v3.20.3 release notes
chore(release): v3.20.3
fix(pricing): move retired DeepSeek V4 family to the V4.1 Flash tier
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.
fix(presets): declare third-party Token Plan DeepSeek V4 rows text-only
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.
feat(presets): rename DouBaoSeed preset to localized Volcengine Doubao
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.
fix(codex): skip unchanged incomplete rollout tails
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.
fix(claude): align quick model selection with form order
fix(tray): show bound ChatGPT account quota for managed Codex cards
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
feat(claude): add "Disable Artifact Tool" quick toggle to provider editor
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.
feat(codex): refresh aggregator model presets and catalogs
feat(codex): switch Kimi presets to native Responses direct-connect
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.
fix(usage): show Claude Fable weekly limit from the usage API limits array
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.
fix(codex): mirror vision-capable DeepSeek catalog and un-gate the legacy v4-flash alias (#7286)
Refresh the bundled DeepSeek official Codex catalog to the vendor's current models.json (slug deepseek-flash, image input modality, supports_image_detail_original) and remove deepseek-v4-flash from the confirmed text-only registry: the legacy id is still accepted by DeepSeek and routed to the vision-capable V4.1 Flash, so both the proxy media sanitizer and the generated Codex catalog must fail open for it. deepseek-v4-pro stays text-only per the vendor catalog.
Fixes #7283
fix(proxy): clamp sub-floor max_tokens to the Responses API minimum (#7287)
The Responses API rejects max_output_tokens below 16, but Anthropic clients legitimately send tiny probe budgets (Claude Desktop's model-availability probe uses max_tokens=1). The Anthropic→Responses conversion copied the value verbatim, so strict upstreams returned 400 and Claude Desktop reported the mapped model as unavailable under local routing.
Clamp 1..=15 up to 16 during conversion; 16+, 0 and non-integer values keep their pass-through semantics. Normal session requests convert byte-for-byte as before, and the Codex OAuth path still strips max_output_tokens afterwards.
Fixes #7103
fix(codex): coalesce adjacent commentary with pending tool calls (#7280)
The Codex Responses -> Chat Completions converter emitted two consecutive
assistant messages when one Responses model turn contained a commentary
message directly followed by function_call items. Chat upstreams imitate
the text-only assistant message as a complete turn and return
finish_reason=stop before the expected tool call, so long agent tasks end
after a progress update.
Merge pending tool calls into the directly adjacent assistant message when
it carries no tool_calls yet, attach pending reasoning per segment so
parallel calls do not duplicate it, and keep the new-message path for every
other boundary shape (user/tool boundaries, prior tool-call batches, media
flushes). The reasoning placeholder backfill still applies to coalesced
calls without reasoning.
Fixes #6529
Supersedes #6530
Co-authored-by: BigStrongSun <BigStrongSun@users.noreply.github.com>
fix(proxy): skip empty reasoning_content placeholders in Anthropic SSE streaming (#7227)
Some OpenAI-compatible upstreams (GLM, Qwen, DeepSeek-V4-Pro via ModelScope/DashScope,
Bailian, JD Cloud, ...) keep a `reasoning_content: ""` placeholder in every content
chunk instead of omitting the field. The reasoning branch of the OpenAI Chat -> Anthropic
SSE converter only checked for presence, and it shares `current_non_tool_block_type`
with the content branch, so every content chunk closed the open text block, opened an
empty thinking block, then reopened a text block. N content chunks became 2N content
blocks, half of them empty thinking blocks, which Claude Code rendered as one line per
token with dozens of empty Thought blocks and bloated the session JSONL.
Filter empty reasoning before entering the branch, matching the `!content.is_empty()`
guard the content branch already had and the non-streaming `openai_to_anthropic`
converter. Two regression tests pin block index, type and order: the placeholder shape
must yield a single text block and no thinking_delta at all, and real reasoning must
still yield one thinking block followed by one text block.
Fixes #5028
Fixes #4404
fix(codex): detect growing rollouts with a persisted byte cursor (#7219)
Codex keeps the rollout append handle open for the whole session, so on Windows NTFS the file mtime does not advance while the file grows and the pure-mtime gate skipped growing rollouts forever. Persist the observed byte length in session_log_sync.last_byte_offset for Codex rows and skip a file only when both mtime and size are unchanged. The parser now splits records on newlines manually so an incomplete trailing record is retried on the next pass without advancing the line cursor.
Fixes #6060
fix(codex): proxy image edits and Images API follow-ups (#7177)
* fix(codex): proxy image edits and Images API follow-ups
Follow-ups to #7036 for Codex's built-in ImageGen over the local proxy:
- Register `/images/edits` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them through the shared Codex standalone passthrough.
ImageGen posts to `images/edits` whenever a call references existing
images, and the router returned an empty 404 for it.
- Add `CodexStandaloneEndpoint::ImagesEdits`. Both Images endpoints share
one full-URL suffix table, so a complete Responses / Compact / Chat
Completions / Images URL derives the sibling endpoint (and vice versa)
while opaque full URLs keep failing closed.
- Apply the standalone sibling rewrite when the base URL is pasted as a
complete endpoint with the full-URL switch off, mirroring
`codex_chat_base_is_full_endpoint`; standalone requests no longer become
`.../chat/completions/images/generations`.
- Parse Images API streaming usage: `image_generation.completed` carries
`usage` on the event top level, so scan for `usage.input_tokens` before
falling back to the Chat Completions stream parser.
Related #7036
* fix(codex): match standalone URL suffixes case-insensitively
Keep endpoint detection and sibling rewriting consistent while preserving the original URL prefix and query. Cover mixed-case Images and Alpha Search endpoints through URL and proxy regression cases.
---------
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): preserve per-app settings on shutdown and port allocation (#7210)
On normal exit, stop_with_restore_keep_state() called the legacy global
proxy-config writer to clear the long-retired live_takeover_active flag.
That writer reads the Claude row and runs an UPDATE without a WHERE clause,
so every shutdown copied Claude's max_retries and the three timeout fields
onto the Codex, Gemini and Grok Build rows — and the UPDATE never carried
live_takeover_active in the first place, so the step had no effect beyond
the clobber. The ephemeral listen-port path did the same on every proxy
start.
Drop the dead write-back (live-config restore, backup cleanup and health
reset are unchanged) and persist the resolved ephemeral port through the
global-config interface, which only touches the shared listen/logging
columns. Two regression tests seed distinct per-app settings for all four
apps and assert they survive both paths.
Fixes #7204
fix(provider): preserve child metadata during universal sync (#7212)
Syncing a universal provider regenerates its Claude/Codex/Gemini child
providers. The child's `settings_config` was merged with the existing row,
but `meta`, `created_at` and `sort_index` were taken from the generated
object, and the universal provider never carries any of them from the UI.
The UPDATE therefore wiped the child's usage script, common-config opt-out,
endpoint auto-select and other per-app settings to `{}` and reset the sort
index to NULL, which pushed the card to the bottom of the list and looked
like the provider had been deleted and re-created.
Keep the existing child's `meta`, `created_at` and `sort_index` when the
row already exists. Name, base URL, key, models, website and notes are
still driven by the universal provider, and first-time creation still
inherits the parent's metadata.
Fixes #7134
feat(presets): rebrand DashScope as 千问AI平台 and refresh Qwen models (#7183)
Rename the domestic Bailian presets to 千问AI平台 with a dedicated icon, refresh the Qwen family to the 3.8 generation across Claude Code, Claude Desktop, Codex, Hermes, OpenClaw, OpenCode and Pi, add the domestic Token Plan presets, restore the international QwenCloud Coding Plan presets, and align OpenClaw/OpenCode/Pi model metadata with the official client docs.
fix(codex): honor proxy URL when model_provider is omitted (#7263)
When a Codex provider card's TOML has no `model_provider`, Codex falls back to
its built-in `openai` provider, but takeover wrote the proxy address as a
top-level `base_url` that Codex never reads, so requests bypassed the local
proxy and went straight to api.openai.com.
Treat the missing selector as the built-in `openai` provider and write the
proxy address to `openai_base_url`; the existing legacy-reroute migration then
turns it into a `cc-switch` custom provider table carrying the `PROXY_MANAGED`
bearer, the same shape every other third-party takeover uses. Explicit
provider selections are unchanged.
Refs #6256, #7217
fix(provider): update MiniMax defaults to M3 and remove expired offers (#7255)
* fix(provider): refresh MiniMax M3 presets and remove expired promotions
* fix(provider): complete MiniMax M3 capability declarations
---------
Co-authored-by: saladday <1203511142@qq.com>
chore(provider): remove Atlas Cloud sponsor status, keep presets
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`
docs(release): add v3.20.2 release notes
chore(release): v3.20.2
feat(provider): add SoleAPI sponsor presets across all nine apps
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.
fix(codex): proxy image edits endpoint
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.
fix(provider): pin models URL for JieKou and Novita Claude presets
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.
feat(pricing): refresh seed for September 2026 vendor price changes
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.
fix(codex): stamp requires_openai_auth on takeover writes to match the live login state
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.
fix(updater): show the real reason when an update check fails (#6482)
The updater plugin rejects with a plain string, so `err instanceof Error`
never matched and the real failure reason was replaced by the generic
fallback message. Use the existing extractErrorMessage helper, and pass
the recovered message into the check-update failure toast so it actually
reaches the user instead of stopping at an unread context state.
Co-authored-by: Jason <farion1231@gmail.com>
docs: fix the locales path in the README directory trees (#6100)
All four READMEs showed locales/ as a direct child of src/, but the
translations live at src/i18n/locales/. Split it into two tree nodes so it
matches how the rest of the tree is drawn. Also fixes a CLaude -> Claude
typo in README_ZH.md.
Co-authored-by: Jason <farion1231@gmail.com>
fix(claude): exclude workflow journal.jsonl from session list
Claude Code's workflow feature writes subagents/workflows/wf_*/journal.jsonl inside the session directory. The Claude session scanner only excluded agent-* files, so each journal parsed as an empty ghost session titled "journal". Exclude journal.jsonl by file name and add regression tests.
Fixes #6042
fix(i18n): add the missing pi.form.providerKeyDuplicate and common.collapse keys
translatePiProviderMutationError returns pi.form.providerKeyDuplicate for the Pi backend's duplicate-key error, but the key existed in none of the four locale files, so the toast showed the raw key. common.collapse (aria-label of the thinking-map toggle) was missing the same way. Add both keys to zh/en/ja/zh-TW and lock them in the locale coverage test.
fix(proxy): stop mask_url panicking on multi-byte UTF-8 near the truncation boundary
mask_url's fallback branch (used when the proxy URL fails Url::parse) sliced the raw string at byte 20 and panicked when a multi-byte UTF-8 character straddled that offset, e.g. a CJK string pasted into the global proxy address. Snap the cut to the nearest char boundary at or before 20 and add a regression test.
fix(tests): isolate LOCALAPPDATA so tests stop writing the real Claude Desktop config
On Windows, Claude Desktop config paths resolve from LOCALAPPDATA, which the test home override in tests/support.rs did not redirect, so profile_roundtrip wrote the https://desktop.test fixture into the developer's real Claude Desktop config. Point LOCALAPPDATA at the isolated test home alongside HOME and USERPROFILE.
Fixes #6077
fix(usage): widen pricing model source select to fit localized labels
The pricing model source SelectTrigger in the usage cost settings table was w-28, which leaves about 70px for the label; the English and Japanese labels need about 107px and overflowed the trigger. Widen it to w-40 and drop the h-7 overrides so the trigger and the adjacent Input use the shared h-9 height.
feat(pi): add Tencent TokenHub and Token Plan presets (#7159)
Add eight Tencent presets for Pi, covering the TokenHub pay-as-you-go line
(domestic and international) and the six Token Plan subscription products —
Pi was the only managed app still missing them. Model IDs, ordering and
endpoints follow Tencent's own model lists verbatim, and the tests assert each
product's line-up in order so future drift is caught.
Declare thinking control alongside the capabilities rather than leaving it to
defaults: the 31 DeepSeek entries carry a Tencent-specific compat so selecting
"off" actually disables thinking instead of silently billing reasoning tokens,
the eight Kimi K2.7 Code entries no longer offer an "off" level the model
cannot honour, and kimi-k2.7-code-highspeed is marked image-capable so
tool-returned screenshots are no longer dropped.
Drop minimax-m2.5 from every app: it is gone from Tencent's model tables and
the platform is retiring it.
fix(codex): proxy image generations endpoint (#7036)
Codex's built-in ImageGen calls the legacy OpenAI Images API, but the local
proxy only registered Responses / Chat Completions / Compact / Alpha Search
routes, so `/v1/images/generations` returned an empty 404 under Codex routing.
- Register `/images/generations` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them as a Codex standalone passthrough shared with Alpha Search.
- Generalize the Alpha Search full-URL rewrite into
`rewrite_codex_standal…
- 删除 6 个文件:commands/stream_check.rs、services/stream_check.rs、
database/dao/stream_check.rs、hooks/useStreamCheck.ts、lib/api/connectivity-check.ts、
components/usage/ConnectivityCheckConfigPanel.tsx
- 移除 mod.rs 声明、lib.rs generate_handler! 注册、cleanup_old_stream_check_logs 调用
- 从 SYNC_SKIP_TABLES / SYNC_PRESERVE_TABLES 移除 stream_check_logs(表结构保留在 schema.rs)
- 更新 backup.rs 测试:移除 stream_check_logs fixture 与断言
- 更新 SettingsPage:移除 ConnectivityCheckConfigPanel AccordionItem 与 FlaskConical 导入
- i18n:删除 streamCheck.* 命名空间(zh/en/ja/zh-TW 四语言)
- 更新注释:移除 stream_check 旧引用(forwarder.rs、provider.rs、userAgentPresets.ts、
connectivity_test.rs)
- 修复 4 个 pre-existing clippy lint(connectivity_test: matches! 宏、unused_unit、
derive Default)
- 移除 ProviderList.test.tsx 中已失效的 useStreamCheck mock
chore(connectivity-test): 勾选 Task 7 / OpenSpec 2.4 3.1-3.3
feat(connectivity-test): 供应商列表集成(弹窗入口 + 批量探针徽标)
chore(connectivity-test): 勾选 Task 6 / OpenSpec 2.2-2.3
fix(connectivity-test): 模型列表解析与后端对齐(每条目首个匹配,提取共享 helper)
feat(connectivity-test): 连通性测试弹窗/详情/徽标组件与四语言文案
chore(connectivity-test): 勾选 Task 5 / OpenSpec 2.1
test(connectivity-test): 锁定 toBackendParams 字段映射(customHeaders/customBody 等)
feat(connectivity-test): 前端命令绑定与测试/探针状态 hooks
chore(connectivity-test): 勾选 Task 4 / OpenSpec 1.4
feat(connectivity-test): 测试参数持久化纯函数(缺省即默认值)
chore(connectivity-test): 勾选 Task 3 / OpenSpec 1.3
feat(connectivity-test): Tauri 命令与注册,官方/动态端点过滤,sanitize 隔离私有字段
chore(connectivity-test): 勾选 Task 2 / OpenSpec 1.2
feat(connectivity-test): 后端直连执行器(reqwest+SSE,总/空闲超时,度量与错误识别)
feat(connectivity-test): 后端直连执行器(reqwest+SSE,总/空闲超时,度量与错误识别)
chore(connectivity-test): 勾选 Task 1 / OpenSpec 1.1
fix(connectivity-test): claude 鉴权对齐 adapter 顶层 apiKey/api_key 兜底
feat(connectivity-test): 新增后端连通性测试服务类型/模型解析/协议构造
chore: archive model-quick-switch-picker-scroll
chore: model-quick-switch-picker-scroll 验证报告与状态元数据
fix: 模型快捷切换弹窗内模型列表滚轮无法滚动
模态 Dialog 的 RemoveScroll 会对 shards 外的 portaled PopoverContent
wheel 事件无条件 preventDefault。在 SearchableModelPicker 的
PopoverContent 捕获阶段阻断 wheel 传播,恢复原生滚动。
chore: bump fork version to 3.20.1-1 after upstream v3.20.1 rebase
fix(form): only auto-expand Claude advanced options on divergent model mapping
Uniform mapping produced by the quick-set button (all roles equal to the
fallback model) no longer counts as an advanced value, so the advanced
section stays collapsed on edit. A 1M marker is stripped before comparing
so marked roles are not misjudged as divergent. Truly customized mappings
still auto-expand the section.
feat: align fallback-model quick-set buttons with model input right edge
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
docs: update README with fork build notes and quick-move provider changes
chore: archive comet-native claude-model-quick-access
feat: surface Claude fallback model as quick-access section below endpoint
Move the default fallback model (ANTHROPIC_MODEL) block out of the
collapsed Advanced Options into a quick-access section directly below
the request endpoint: input + 1M checkbox + quick-set + fetch-models
buttons are all reachable without expanding.
- header toggle-all "declare 1M" now includes the fallback model
(Haiku still excluded); partial selection shows indeterminate state
- drop the duplicated quick-set/fetch buttons and the old fallback
block inside Advanced Options
- exclude claudeModel from hasAnyAdvancedValue so a fallback-only
value no longer auto-expands Advanced Options
- quick-set priority, env write semantics, and OAuth preset branches
unchanged; add 4 component tests covering the new layout
chore: archive comet-native provider-right-click-top-bottom
Enable native workflow in .comet/config.yaml and move the Comet managed
state block in .gitignore so the exclusion rules stay grouped with the
other fork tool artifacts.
feat: add provider right-click move-to-top/bottom and insert new providers at second position
- ProviderList: context menu with quick move-to-top/move-to-bottom, reusing
update_providers_sort_order and refreshing tray menu like drag sort
- useAddProviderMutation: default insert at index 1 with explicit sortIndex
materialization for existing items (avoids defined-first sort trap)
- i18n: quickMove* strings for zh/en/zh-TW/ja
ci: stop flaky WSL2 linker job from blocking main
The backend-windows-wsl2 job intermittently fails with LNK1327 /
mt.exe c1010070 when the Windows runner's mt.exe cannot parse a
manifest temp file on the WSL2 UNC TEMP path. The same flaky failure
occurs upstream and is unrelated to fork changes. Mark the job
continue-on-error so main stops going red on it; the result stays
visible in the Actions UI.
style: apply prettier to files touched by recent comet archives
Frontend CI format:check failed on 6 files modified by the app-name
and model-quick-switch archive commits plus the updater-disable commit.
Run prettier --write so format:check passes again.
chore: archive ccs-fork-app-name-cc-switch
chore: archive ccs-model-quick-switch-ui-polish
docs: rewrite README to document fork differences vs upstream
README 从上游完整功能介绍改为仅记录 fork 与上游的差别,完整功能
文档指回上游。涵盖 8 项 fork 魔改:构建标识/开发预览、预设精简、
Claude 表单调整、模型搜索移植、主页模型快速切换、侧边栏面板显隐、
关闭应用内更新、打包范围,并说明版本号与 rebase 同步流程。
feat: disable in-app updater, point users to GitHub releases
移除 tauri-plugin-updater 依赖与自动更新链路,改为统一引导用户到
GitHub Releases 手动下载新版。
- 前端:updater.ts 恒返回 up-to-date;UpdateContext 取消启动自检;
AboutSection「检查更新」直接打开 fork releases 页;DatabaseUpgrade
恢复界面去掉下载进度条,改为打开发布页。
- 后端:移除 updater 插件注册与 UpdaterExt 调用;
install_update_and_restart 降级为空壳,check_app_update_available
返回当前版本号;check_for_updates 指向 fork releases;capabilities
去掉 updater:default 权限。
- 打包:tauri.conf.json 删 plugins.updater、createUpdaterArtifacts=false;
Cargo.toml / package.json 删 updater 依赖,Cargo.lock / pnpm-lock
同步重生成。
- CI:release.yml 移除签名收集与 assemble-latest-json job,删除
.tar.gz/.sig 处理,release body 去掉 updater 说明。
版本号统一为 3.20.0-1(合法 semver 且 MSI 第 4 位为 1)。
Cargo.lock / pnpm-lock.yaml 已随依赖移除重生成,cargo check 通过。
ci: build only Windows x64 and macOS arm64, unsigned
feat: gate dev panel behind dev-only env flag
chore(comet): archive ccs-sidebar-panels-visibility change
fix: apply skills visibility guard to hermes branch toolbar button
feat: include prompts in sidebar panel visibility settings
fix: persist visibleSidebarPanels setting in backend AppSettings
style: use compact pill toggles for sidebar panel visibility
feat: add sidebar panel visibility switches to settings
feat: add sidebar panel visibility settings and wire into toolbar buttons
chore(comet): archive ccs-home-model-display change
fix(fork): read grokbuild api key from config TOML in quick switch dialog
feat(fork): align model badge with upstream extractModelBadge
feat(fork): show current model badge and quick-switch entry on provider cards
feat(fork): add model quick switch dialog
feat(fork): add provider model read/write utils with tests
chore(comet): archive ccs-model-search-port change
feat(i18n): add model search picker keys for all locales
feat(form): port SearchableModelPicker from upstream for searchable model selection
chore(comet): archive ccs-claude-form-layout change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-claude-form-layout/
- Sync claude-form-layout spec to main specs
- Annotate design doc as archived
- Add verification report
fix(i18n): update advanced options hint, add ja/zh-TW toggle-all, drop dead apiFormatHint key
fix(form): correct 1M toggle-all alignment direction (ml-1)
fix(form): align 1M toggle-all checkbox with row checkboxes
refactor(form): move api format select into EndpointField rightSlot
fix(form): widen api format select to fit option text
feat(form): add 1M toggle-all checkbox in model mapping header
feat(form): move api format select next to endpoint input
chore(comet): archive ccs-provider-presets-trim change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-provider-presets-trim/
- Sync provider-presets-visibility spec to main specs
- Annotate design doc as archived
- Add verification report
docs(fork): add presets-trim files to rebase whitelist
test(fork): mock IS_FORK_BUILD false in unit tests for upstream-form semantics
feat(fork): wire official preset filter into provider forms
feat(fork): add hidden field to preset interfaces
feat(fork): add official preset allowlist and filter util
chore(comet): archive ccs-fork-scaffolding change
- Move change artifacts to docs/openspec/changes/archive/2026-08-18-ccs-fork-scaffolding/
- Sync fork-build-identity spec to main specs
- Annotate design doc as archived
- Add verification report
feat(fork): add dev config with isolated identifier and config dir
docs(fork): complete rebase whitelist with all fork-touched files
fix(fork): guard setTitle with isTauri in test/non-tauri env
fix(fork): define __CCS_FORK_BUILD__ in vitest config
feat(fork): add DevPanel and settings footer entry
feat(fork): set macOS window title to CC Switch (Fork) on startup
feat(fork): inject __CCS_FORK_BUILD__ compile-time constant
feat(fork): bump version to 3.19.2-fork.1 and rename product to CC Switch (Fork)
docs(fork): add upstream rebase workflow and ignore tool artifacts
fix(proxy): accept the whole grok-4.x (x>=5) family in the reasoning-effort whitelist (#7369)
Replace the verbatim grok-4.5 / grok-4.6 entries in supports_reasoning_effort with a rule that parses the grok-4.x minor version and accepts x >= 5, mirroring the existing GPT-5+ rule. This covers grok-4.7 (released 2026-09-21), whose reasoning effort was previously dropped on the Claude -> Chat, Claude -> Responses and Codex Responses -> Chat conversion paths, and lets future releases pass without another whitelist edit. The grok-build-* family is retained for saved providers.
Co-authored-by: allenxu09 <171831965+allenxu09@users.noreply.github.com>
docs(release): add v3.20.4 release notes
chore(release): v3.20.4
feat(pricing): add Grok 4.7 and GLM-5.3-FlashX
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.
fix(i18n): remove outdated OpenCode Go referral offer
fix: add SudoCode.chat alternate API endpoint
feat(settings): add a GitHub star prompt to the About card
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
feat(presets): add Soshow aggregator presets
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.
fix(ci): stabilize frontend tests and verify renderer build
fix(mcode): refresh provider state after removal (#7578)
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
fix(lib): reset skip_taskbar before window.show() across entry points (#6348)
5处 window.show() 调用中 4处没有在 show() 前重置 Windows 的 skip_taskbar,导致以下场景出现窗口可见但不在任务栏、点最小化后窗口消失的 bug:
- single_instance handler (lib.rs:360-361):当原进程因 silent_startup / 关闭到托盘等处于 skip_taskbar(true) 状态时,从快捷方式/开始菜单唤起已运行实例触发。
- handle_deeplink_url 的 focus_main_window 分支 (lib.rs:256):通过 ccswitch:// URL 唤起时。
- DB 恢复流程 (lib.rs:556):数据库版本不匹配时显示恢复界面。
- RunEvent::Opened 处理器 (lib.rs:1803):系统接收到 deep-link URL 时。
修复后的行为与 tray.rs::handle_tray_menu_event::show_main 分支一致(那里已有相同的 set_skip_taskbar(false) 调用)。
经用户测试验证:autostart + silent_startup + 从开始菜单/快捷方式启动的场景下,最小化按钮恢复正常工作。
fix(proxy): omit missing tool descriptions in the Codex responses-to-chat converter (#7378)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): preserve max effort for GPT-5.6 and GPT-6 Astra (#7531)
fix(misc): ignore shell startup output when probing tool versions in WSL (#7348)
try_get_version_wsl runs "${SHELL:-sh}" -lic '<tool> --version' through
wsl.exe and takes the first x.y.z anywhere in stdout. A login+interactive
shell runs the distro's startup files first, so their output shares that
stdout. On Ubuntu, /etc/profile.d/update-motd.sh prints the MOTD once a day,
starting with "Welcome to Ubuntu 24.04.4 LTS", and the card then showed
24.04.4 as the Claude Code version. The once-a-day stamp makes it appear
after the first probe of the day and vanish on refresh.
The payload now prints a sentinel before running the tool, and parsing looks
only at what follows its last occurrence. The last occurrence handles the
-lic || -lc || -c fallback chain printing it several times; matching it as a
substring handles OSC sequences glued in front of it. If the sentinel is
absent (the shell failed before the payload ran), the output is used as
before. The stderr fallback for tools that print their version there is kept.
Fixes #7347
Co-authored-by: lyfuci <lyfuci11@gmail.com>
fix(claude): hide session URL attribution (#7053)
* fix(claude): hide session URL attribution
* docs(claude): document session URL attribution
fix(ui): reset provider scroll when switching apps (#5211)
fix(pi): fix provider logo (#6826)
fix(i18n): use the Pi form's own term for the zh-TW duplicate-key message (#7187)
Follow-up to the nit on #6768. In zh-TW the Pi form labels the field
供應商識別碼 and says 供應商識別碼為必填 when it is empty, but the
duplicate message I added said 供應商標識 — the term Hermes, OpenCode and
OpenClaw use for their own field. One form, two names for the same thing.
The other three locales already keep the label and both messages on one
term, so this makes zh-TW match the pattern rather than introduce one:
en Provider key / Provider key is required / This provider key already exists
ja プロバイダーキー / プロバイダーキーを入力してください / このプロバイダーキーは既に存在します
zh 供应商标识 / 供应商标识不能为空 / 该供应商标识已存在
zh-TW 供應商識別碼 / 供應商識別碼為必填 / 該供應商標識已存在 <- the odd one out
hermes.form.providerKeyDuplicate carries the same sentence and is left
alone: its own label is 供應商標識, so it is already consistent.
Verified: managementListLocales 7/7. Full unit suite 1085/1087, the same
two App.test.tsx integration cases failing before and after.
fix(proxy): strip stop param for GitHub Copilot to unblock auto mode classifier (#5404)
Claude Code's auto mode classifier sends `stop_sequences` on every
safety check. On the GitHub Copilot path the Anthropic -> OpenAI Chat
conversion mapped it to `stop`, and Copilot's Chat Completions route
for Claude models answers that request with HTTP 400, so every
classifier call failed while normal chat kept working.
- Remove the converted `stop` field only when the provider is GitHub
Copilot; other OpenAI-compatible upstreams keep it.
- Copilot detection in the transform now also covers providers that
are recognised only through `env.ANTHROPIC_BASE_URL`.
- Regression tests for the Copilot (meta and env URL) and generic
provider cases.
Fixes #5175
fix(codex): keep stored auth when the live snapshot has no credential (#7434)
Editing the current Codex provider seeds the form from the live ~/.codex
snapshot, and switching away backfills that snapshot into the DB row. When a
provider table declares its own credential source (an Authorization header,
env_key, auth/aws), no bearer token is injected into config.toml and the
shared auth.json is deleted on third-party switches, so the live snapshot is
permanently `{ auth: {}, config }` while auth.OPENAI_API_KEY in the DB is the
only copy of the key. Both paths treated that empty live auth as
authoritative and silently erased the stored key, which in turn made the
usage-script modal misread the provider as an official subscription and
reset the saved script.
- Treat a credential-less live auth (missing auth.json or the bare
auth_mode logout marker) as an absent field in both the edit dialog and
the switch-away backfill, keeping the stored auth template. Live still
wins whenever it carries material, and official providers keep the live
login as their source of truth.
- Gate the official exemption on is_codex_official_provider /
resolveCodexOfficialIdentity rather than the category alone: a legacy
fixed official card without a persisted category is still written as
official, so the fallback must not resurrect a logged-out login on
switch-back.
- Cover both layers with regression tests, including the category-less
official shapes.
Fixes #7433
Supersedes #7492
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): lift additional_tools input carriers on the Codex chat conversion (#7454)
Codex talks to third-party providers over its Responses Lite transport,
which ships the request's extra tools in an `additional_tools` input
carrier: `{"type":"additional_tools","role":"developer","tools":[...]}`,
a role but no `content`. The Responses->Chat conversion let that carrier
fall through the generic "item with a role is a message" arm and emitted
`{"role":"system","content":null}` at messages[1], which strict Chat
gateways reject with `messages[1]: missing field "content"` (or
`data did not match any variant of ...SystemMessageContent`). The tools
carried inside it were also dropped on the Chat and Anthropic conversions.
- Skip `additional_tools` items in the Chat message walk; a carrier
declares tools, it is not a message.
- Collect carrier tools through the existing CodexToolContext registry
(dedup, namespace flattening, custom-tool conversion), which also feeds
the Anthropic conversion and the response-side name round-trip.
- Requests without a carrier convert byte-for-byte as before.
Known boundary: `custom` children inside a namespace tool
(`functions.exec`) are still not lifted; that needs its own response-side
name round-trip and is left for a follow-up.
Diagnosed independently in #6158 by BigStrongSun (fix in #6159) and in
fixture.
Fixes #7451
Fixes #6158
Refs #6033
Refs #6776
Supersedes #6159
fix(proxy): normalize Codex detail:original images for Chat gateways (#7476)
Codex marks tool-returned images with detail:"original" whenever the model
catalog advertises supports_image_detail_original, a Responses-only value.
The Responses->Chat conversion copied it verbatim into image_url.detail, and
strict OpenAI-compatible Chat gateways (Ark/Doubao, SenseNova, GLM, vLLM, ...)
reject the whole request with 400. Because Codex replays the full rollout on
every turn, one poisoned view_image result made the session fail permanently,
including plain-text turns.
Two layers:
- tool_media::merge_top_level_detail downgrades original -> auto, so already
poisoned rollouts and Codex versions before 0.155.0 (which do not normalize
replayed history themselves) recover immediately. auto/low/high are untouched.
- codex_catalog_model_entry always writes supports_image_detail_original:false
on the ProxyChat profile, so Codex stops emitting original at the source once
the catalog is regenerated. NativeResponses/Anthropic profiles and the DeepSeek
official catalog are unaffected.
Top-level user images were never affected: responses_content_to_chat_content
drops the part-level detail entirely.
Fixes #6495
Fixes #7458
Refs #6569
Refs #5805
fix(omo): detect unified config on the WSL side when the OpenCode dir is in WSL (#7550)
When the OpenCode config directory points into WSL
(\\wsl.localhost\<distro>\home\<user>\.config\opencode), OMO >= 4.19.3 keeps
its unified config in the WSL-side home (~/.omo/omo.jsonc). Detection only
probed the Windows home, never found it, and silently fell back to the legacy
per-plugin file that OMO no longer reads, so provider switches looked
successful but had no effect.
- Add derive_wsl_home_dir to derive the WSL-side home from a WSL UNC path
(home/<user> or root); non-WSL and non-home paths return None.
- Probe a list of home candidates for the unified config, WSL-side home
first and the OS home as a fallback, shared by the read, write and delete
paths.
- Detection only: when no unified config exists on either side the legacy
fallback is unchanged.
Fixes #7363
Supersedes #7367
docs(partner): add a FluxA purchase link and tighten its sponsor blurb
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.
fix(claude-desktop): expose the 1M context variant for DeepSeek V4 routes
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.
feat(presets): add FluxA Token Plan partner presets
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.
fix(presets): correct AICodeWith Codex-family endpoint to /v1
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.
fix(ui): render monochrome preset icons in the foreground color
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.
docs(user-manual): point Chat-only Codex examples at plan endpoints
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.
fix(pricing): restore DeepSeek V4 Pro price and seed Qwen3.8 and Hy4 models
- 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).
fix(presets): move MiniMax CN and BaiLing to their current official endpoints
- 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.
feat(codex): refresh CN presets from the Responses API audit
- 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.
test(claude-desktop): run Linux path tests on unix hosts and cover Linux in provider sync tests
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.
fix(ui): stop expanding stale cached usage tiers on ineligible provider cards
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.
fix(skills): 支持 skillId 与目录名不一致的技能安装和更新 (#6381)
* fix(skills): resolve mismatched skillId directories
* fix(skills): preserve source path during updates
* fix(skills): refresh source URL after repository relocation
* test(skills): isolate relocation fixture from legacy home
feat(opencode): 支持从获取的模型列表中搜索并批量添加所选模型 (#7515)
* feat: implement fetched models selection and UI components
* fix(opencode): prevent model search from submitting provider form
Add Kimi Global preset variants (kimi.ai platform + api endpoints) (#7526)
Add a "Kimi Global" twin immediately after the domestic "Kimi" API preset and a "Kimi For Coding Global" twin after the domestic "Kimi For Coding" preset, in every preset file that ships them (claude, claude-desktop, codex, hermes, openclaw, opencode, pi).
"Kimi Global" points to the international open platform https://platform.kimi.ai?aff=cc-switch (apiKeyUrl mirrored as https://platform.kimi.ai/console/api-keys?aff=cc-switch) and swaps the endpoint host api.moonshot.cn for api.moonshot.ai with each file's path unchanged (/v1 or /anthropic).
"Kimi For Coding Global" points to the international site https://www.kimi.ai/code/?aff=cc-switch and swaps the endpoint host api.kimi.com for api.kimi.ai with the path unchanged (e.g. https://api.kimi.ai/coding/v1).
Domestic presets are unchanged; category, icons, apiFormat, model catalogs, endpointCandidates (host swapped), partnerPromotionKey and settings otherwise mirror the domestic blocks exactly.
primePartner stays only on the domestic presets so the new variants do not alter prime-partner ordering.
The Pi twins get distinct providerKey values ("cc-switch-kimi-global", "cc-switch-kimi-for-coding-global") because providerKey is the preset-level primary key and must not collide with the domestic presets.
Rebalance sponsor CTAs around Kimi Code plan with dual-region links (#7522)
Kimi Code plan purchases have resumed, so the README sponsor sections go back to leading with the coding plan instead of the API-only flow written during the purchase pause.
All four locales (EN/ZH/JA/DE) now present Kimi Code plan (中文站 | Global) as the primary CTA and the Kimi Open Platform API (中文站 | Global, with the first-top-up bonus) as the secondary one, mirroring the domestic|global split already used for API links.
The aff and track_id values are unchanged.
fix(prompts): refresh active prompts after external file edits (#7194)
Reload the enabled prompt from its live file when the Prompts list loads, and refresh the open panel when the window regains focus, so external edits to CLAUDE.md / AGENTS.md show up without a restart.
- Keep saved templates available when the live file cannot be read.
- Skip the backfill for empty or whitespace-only files, matching enable_prompt.
- Skip the backfill while a sync/restore holds the sync lock, so a list load cannot write a stale live file into a freshly restored database.
fix(skills): raise archive entry limit so large skill repos install (#7489)
Installing hugohe3/ppt-master from skills.sh failed with
ARCHIVE_TOO_MANY_ENTRIES: the GitHub archive we download is the whole
repository (13,248 entries), and the 10,000-entry cap added in #5811
treated it as a zip bomb.
- Raise MAX_ARCHIVE_ENTRIES to 30,000 for both the remote archive and
local ZIP paths, and align WebDAV's MAX_EXTRACT_ENTRIES so a skills.zip
containing such a skill still restores.
- Charge every file entry at least one 4 KiB block against the existing
512 MiB budget, mirroring directory accounting, so empty-file archives
are bounded by the byte budget rather than by the entry count alone.
- Add tests for a 13k-entry repo archive and for the per-file minimum
charge.
Fixes: https://github.com/farion1231/cc-switch/issues/7475
feat: add MiniMax Code harness support (#7383)
Add MiniMax Code (mcode) as a managed application: provider and model management through the native custom_provider configuration, MCP servers, Skills, global instructions (AGENTS.md), read-only local session history with resume, and usage import from the native runtime database. Native files shared by the TUI and desktop are written under the native lock with rollback when the database commit fails, unrelated native configuration is preserved, and the data directory honors MINIMAX_DATA_DIR and MAVIS_DATA_DIR. Deleting sessions stays in the native application because they are coupled to runtime state.
fix(codex): recover stale account bindings during takeover (#7395)
After deleting a managed ChatGPT account and logging in again, a Codex provider can still reference the old local account ID, which made route switching, takeover, startup restoration and rebinding fail permanently.
Distinguish a missing outgoing account from an existing account with no matching live token. A missing account only releases its ownership marker after the persisted account store is verified; the target write policy stays in force. A stale target now reports an account-selection instruction instead of a bare not-found error, including when takeover is already enabled.
Fixes #7392
feat(claude-desktop): support 3P configuration on Linux (#7331)
Add Claude Desktop 3P configuration support on Linux. Resolve the config root from an absolute XDG_CONFIG_HOME, falling back to ~/.config; when CC Switch itself runs as a Flatpak, use the host ~/.config instead of the sandbox-private XDG_CONFIG_HOME. Update the en/zh/ja user manuals with Linux paths and the Flatpak boundary.
Fixes #4855
fix(proxy): omit missing tool descriptions instead of serializing null (#7319)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): keep reasoning effort for grok-4.6 and pass xhigh through verbatim (#7318)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(models): support Zhipu OpenAI Responses model lists (#7330)
Zhipu's Responses endpoint (open.bigmodel.cn/api/v1) serves its model list in the Codex remote catalog shape (models[].slug) instead of the OpenAI-compatible data[].id shape, so the Codex form's model fetch came back empty. The parser now falls back to models[].slug when data is absent.
fix(misc): fetch npm dist-tags from the dedicated endpoint with the probe timeout (#7346)
The tool version probe requested the full npm packument for each package (metadata for every historical version, 14-25 MB uncompressed for codex / opencode / openclaw) only to read its dist-tags, and it was the only latest-version probe without LATEST_PROBE_TIMEOUT, so it fell back to the proxy client's 600 s total timeout and the cards sat on "loading" for minutes on slow links while the large downloads starved the Hermes GitHub probe into "unknown".
Switch fetch_npm_dist_tags to registry.npmjs.org/-/package/{package}/dist-tags, whose body is the dist-tags object itself (tens of bytes to a few KB), escape scoped names as %2f, and apply the same 15 s probe timeout as the GitHub and PyPI probes. No data is lost: the only consumer reads latest and the tool's prerelease tags.
Fixes #7339
feat(usage): display output tokens per second in request log table and detail panel (#3369)
Show tokens/s next to the output token count in the request log table and the request detail panel.
The generation window prefers an explicit duration when present, otherwise latency minus first-token time for streaming requests, and falls back to full latency for non-streaming requests. Rows without a positive token count or window show nothing.
Closes #5936
fix(presets): update APIKEY.FUN URLs to apikey.fan
docs(release): add v3.20.3 release notes
chore(release): v3.20.3
fix(pricing): move retired DeepSeek V4 family to the V4.1 Flash tier
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.
fix(presets): declare third-party Token Plan DeepSeek V4 rows text-only
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.
feat(presets): rename DouBaoSeed preset to localized Volcengine Doubao
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.
fix(codex): skip unchanged incomplete rollout tails
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.
fix(claude): align quick model selection with form order
fix(tray): show bound ChatGPT account quota for managed Codex cards
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
feat(claude): add "Disable Artifact Tool" quick toggle to provider editor
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.
feat(codex): refresh aggregator model presets and catalogs
feat(codex): switch Kimi presets to native Responses direct-connect
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.
fix(usage): show Claude Fable weekly limit from the usage API limits array
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.
fix(codex): mirror vision-capable DeepSeek catalog and un-gate the legacy v4-flash alias (#7286)
Refresh the bundled DeepSeek official Codex catalog to the vendor's current models.json (slug deepseek-flash, image input modality, supports_image_detail_original) and remove deepseek-v4-flash from the confirmed text-only registry: the legacy id is still accepted by DeepSeek and routed to the vision-capable V4.1 Flash, so both the proxy media sanitizer and the generated Codex catalog must fail open for it. deepseek-v4-pro stays text-only per the vendor catalog.
Fixes #7283
fix(proxy): clamp sub-floor max_tokens to the Responses API minimum (#7287)
The Responses API rejects max_output_tokens below 16, but Anthropic clients legitimately send tiny probe budgets (Claude Desktop's model-availability probe uses max_tokens=1). The Anthropic→Responses conversion copied the value verbatim, so strict upstreams returned 400 and Claude Desktop reported the mapped model as unavailable under local routing.
Clamp 1..=15 up to 16 during conversion; 16+, 0 and non-integer values keep their pass-through semantics. Normal session requests convert byte-for-byte as before, and the Codex OAuth path still strips max_output_tokens afterwards.
Fixes #7103
fix(codex): coalesce adjacent commentary with pending tool calls (#7280)
The Codex Responses -> Chat Completions converter emitted two consecutive
assistant messages when one Responses model turn contained a commentary
message directly followed by function_call items. Chat upstreams imitate
the text-only assistant message as a complete turn and return
finish_reason=stop before the expected tool call, so long agent tasks end
after a progress update.
Merge pending tool calls into the directly adjacent assistant message when
it carries no tool_calls yet, attach pending reasoning per segment so
parallel calls do not duplicate it, and keep the new-message path for every
other boundary shape (user/tool boundaries, prior tool-call batches, media
flushes). The reasoning placeholder backfill still applies to coalesced
calls without reasoning.
Fixes #6529
Supersedes #6530
Co-authored-by: BigStrongSun <BigStrongSun@users.noreply.github.com>
fix(proxy): skip empty reasoning_content placeholders in Anthropic SSE streaming (#7227)
Some OpenAI-compatible upstreams (GLM, Qwen, DeepSeek-V4-Pro via ModelScope/DashScope,
Bailian, JD Cloud, ...) keep a `reasoning_content: ""` placeholder in every content
chunk instead of omitting the field. The reasoning branch of the OpenAI Chat -> Anthropic
SSE converter only checked for presence, and it shares `current_non_tool_block_type`
with the content branch, so every content chunk closed the open text block, opened an
empty thinking block, then reopened a text block. N content chunks became 2N content
blocks, half of them empty thinking blocks, which Claude Code rendered as one line per
token with dozens of empty Thought blocks and bloated the session JSONL.
Filter empty reasoning before entering the branch, matching the `!content.is_empty()`
guard the content branch already had and the non-streaming `openai_to_anthropic`
converter. Two regression tests pin block index, type and order: the placeholder shape
must yield a single text block and no thinking_delta at all, and real reasoning must
still yield one thinking block followed by one text block.
Fixes #5028
Fixes #4404
fix(codex): detect growing rollouts with a persisted byte cursor (#7219)
Codex keeps the rollout append handle open for the whole session, so on Windows NTFS the file mtime does not advance while the file grows and the pure-mtime gate skipped growing rollouts forever. Persist the observed byte length in session_log_sync.last_byte_offset for Codex rows and skip a file only when both mtime and size are unchanged. The parser now splits records on newlines manually so an incomplete trailing record is retried on the next pass without advancing the line cursor.
Fixes #6060
fix(codex): proxy image edits and Images API follow-ups (#7177)
* fix(codex): proxy image edits and Images API follow-ups
Follow-ups to #7036 for Codex's built-in ImageGen over the local proxy:
- Register `/images/edits` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them through the shared Codex standalone passthrough.
ImageGen posts to `images/edits` whenever a call references existing
images, and the router returned an empty 404 for it.
- Add `CodexStandaloneEndpoint::ImagesEdits`. Both Images endpoints share
one full-URL suffix table, so a complete Responses / Compact / Chat
Completions / Images URL derives the sibling endpoint (and vice versa)
while opaque full URLs keep failing closed.
- Apply the standalone sibling rewrite when the base URL is pasted as a
complete endpoint with the full-URL switch off, mirroring
`codex_chat_base_is_full_endpoint`; standalone requests no longer become
`.../chat/completions/images/generations`.
- Parse Images API streaming usage: `image_generation.completed` carries
`usage` on the event top level, so scan for `usage.input_tokens` before
falling back to the Chat Completions stream parser.
Related #7036
* fix(codex): match standalone URL suffixes case-insensitively
Keep endpoint detection and sibling rewriting consistent while preserving the original URL prefix and query. Cover mixed-case Images and Alpha Search endpoints through URL and proxy regression cases.
---------
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): preserve per-app settings on shutdown and port allocation (#7210)
On normal exit, stop_with_restore_keep_state() called the legacy global
proxy-config writer to clear the long-retired live_takeover_active flag.
That writer reads the Claude row and runs an UPDATE without a WHERE clause,
so every shutdown copied Claude's max_retries and the three timeout fields
onto the Codex, Gemini and Grok Build rows — and the UPDATE never carried
live_takeover_active in the first place, so the step had no effect beyond
the clobber. The ephemeral listen-port path did the same on every proxy
start.
Drop the dead write-back (live-config restore, backup cleanup and health
reset are unchanged) and persist the resolved ephemeral port through the
global-config interface, which only touches the shared listen/logging
columns. Two regression tests seed distinct per-app settings for all four
apps and assert they survive both paths.
Fixes #7204
fix(provider): preserve child metadata during universal sync (#7212)
Syncing a universal provider regenerates its Claude/Codex/Gemini child
providers. The child's `settings_config` was merged with the existing row,
but `meta`, `created_at` and `sort_index` were taken from the generated
object, and the universal provider never carries any of them from the UI.
The UPDATE therefore wiped the child's usage script, common-config opt-out,
endpoint auto-select and other per-app settings to `{}` and reset the sort
index to NULL, which pushed the card to the bottom of the list and looked
like the provider had been deleted and re-created.
Keep the existing child's `meta`, `created_at` and `sort_index` when the
row already exists. Name, base URL, key, models, website and notes are
still driven by the universal provider, and first-time creation still
inherits the parent's metadata.
Fixes #7134
feat(presets): rebrand DashScope as 千问AI平台 and refresh Qwen models (#7183)
Rename the domestic Bailian presets to 千问AI平台 with a dedicated icon, refresh the Qwen family to the 3.8 generation across Claude Code, Claude Desktop, Codex, Hermes, OpenClaw, OpenCode and Pi, add the domestic Token Plan presets, restore the international QwenCloud Coding Plan presets, and align OpenClaw/OpenCode/Pi model metadata with the official client docs.
fix(codex): honor proxy URL when model_provider is omitted (#7263)
When a Codex provider card's TOML has no `model_provider`, Codex falls back to
its built-in `openai` provider, but takeover wrote the proxy address as a
top-level `base_url` that Codex never reads, so requests bypassed the local
proxy and went straight to api.openai.com.
Treat the missing selector as the built-in `openai` provider and write the
proxy address to `openai_base_url`; the existing legacy-reroute migration then
turns it into a `cc-switch` custom provider table carrying the `PROXY_MANAGED`
bearer, the same shape every other third-party takeover uses. Explicit
provider selections are unchanged.
Refs #6256, #7217
fix(provider): update MiniMax defaults to M3 and remove expired offers (#7255)
* fix(provider): refresh MiniMax M3 presets and remove expired promotions
* fix(provider): complete MiniMax M3 capability declarations
---------
Co-authored-by: saladday <1203511142@qq.com>
chore(provider): remove Atlas Cloud sponsor status, keep presets
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`
docs(release): add v3.20.2 release notes
chore(release): v3.20.2
feat(provider): add SoleAPI sponsor presets across all nine apps
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.
fix(codex): proxy image edits endpoint
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.
fix(provider): pin models URL for JieKou and Novita Claude presets
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.
feat(pricing): refresh seed for September 2026 vendor price changes
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.
fix(codex): stamp requires_openai_auth on takeover writes to match the live login state
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.
fix(updater): show the real reason when an update check fails (#6482)
The updater plugin rejects with a plain string, so `err instanceof Error`
never matched and the real failure reason was replaced by the generic
fallback message. Use the existing extractErrorMessage helper, and pass
the recovered message into the check-update failure toast so it actually
reaches the user instead of stopping at an unread context state.
Co-authored-by: Jason <farion1231@gmail.com>
docs: fix the locales path in the README directory trees (#6100)
All four READMEs showed locales/ as a direct child of src/, but the
translations live at src/i18n/locales/. Split it into two tree nodes so it
matches how the rest of the tree is drawn. Also fixes a CLaude -> Claude
typo in README_ZH.md.
Co-authored-by: Jason <farion1231@gmail.com>
fix(claude): exclude workflow journal.jsonl from session list
Claude Code's workflow feature writes subagents/workflows/wf_*/journal.jsonl inside the session directory. The Claude session scanner only excluded agent-* files, so each journal parsed as an empty ghost session titled "journal". Exclude journal.jsonl by file name and add regression tests.
Fixes #6042
fix(i18n): add the missing pi.form.providerKeyDuplicate and common.collapse keys
translatePiProviderMutationError returns pi.form.providerKeyDuplicate for the Pi backend's duplicate-key error, but the key existed in none of the four locale files, so the toast showed the raw key. common.collapse (aria-label of the thinking-map toggle) was missing the same way. Add both keys to zh/en/ja/zh-TW and lock them in the locale coverage test.
fix(proxy): stop mask_url panicking on multi-byte UTF-8 near the truncation boundary
mask_url's fallback branch (used when the proxy URL fails Url::parse) sliced the raw string at byte 20 and panicked when a multi-byte UTF-8 character straddled that offset, e.g. a CJK string pasted into the global proxy address. Snap the cut to the nearest char boundary at or before 20 and add a regression test.
fix(tests): isolate LOCALAPPDATA so tests stop writing the real Claude Desktop config
On Windows, Claude Desktop config paths resolve from LOCALAPPDATA, which the test home override in tests/support.rs did not redirect, so profile_roundtrip wrote the https://desktop.test fixture into the developer's real Claude Desktop config. Point LOCALAPPDATA at the isolated test home alongside HOME and USERPROFILE.
Fixes #6077
fix(usage): widen pricing model source select to fit localized labels
The pricing model source SelectTrigger in the usage cost settings table was w-28, which leaves about 70px for the label; the English and Japanese labels need about 107px and overflowed the trigger. Widen it to w-40 and drop the h-7 overrides so the trigger and the adjacent Input use the shared h-9 height.
feat(pi): add Tencent TokenHub and Token Plan presets (#7159)
Add eight Tencent presets for Pi, covering the TokenHub pay-as-you-go line
(domestic and international) and the six Token Plan subscription products —
Pi was the only managed app still missing them. Model IDs, ordering and
endpoints follow Tencent's own model lists verbatim, and…
chore(connectivity-test): 勾选 Task 9 / OpenSpec 5.1-5.2 + verify 报告
chore(connectivity-test): 勾选 Task 8 / OpenSpec 4.1-4.3
refactor(connectivity-test): 移除旧 stream_check 可达性探测全链路(保留表结构)
- 删除 6 个文件:commands/stream_check.rs、services/stream_check.rs、
database/dao/stream_check.rs、hooks/useStreamCheck.ts、lib/api/connectivity-check.ts、
components/usage/ConnectivityCheckConfigPanel.tsx
- 移除 mod.rs 声明、lib.rs generate_handler! 注册、cleanup_old_stream_check_logs 调用
- 从 SYNC_SKIP_TABLES / SYNC_PRESERVE_TABLES 移除 stream_check_logs(表结构保留在 schema.rs)
- 更新 backup.rs 测试:移除 stream_check_logs fixture 与断言
- 更新 SettingsPage:移除 ConnectivityCheckConfigPanel AccordionItem 与 FlaskConical 导入
- i18n:删除 streamCheck.* 命名空间(zh/en/ja/zh-TW 四语言)
- 更新注释:移除 stream_check 旧引用(forwarder.rs、provider.rs、userAgentPresets.ts、
connectivity_test.rs)
- 修复 4 个 pre-existing clippy lint(connectivity_test: matches! 宏、unused_unit、
derive Default)
- 移除 ProviderList.test.tsx 中已失效的 useStreamCheck mock
chore(connectivity-test): 勾选 Task 7 / OpenSpec 2.4 3.1-3.3
feat(connectivity-test): 供应商列表集成(弹窗入口 + 批量探针徽标)
chore(connectivity-test): 勾选 Task 6 / OpenSpec 2.2-2.3
fix(connectivity-test): 模型列表解析与后端对齐(每条目首个匹配,提取共享 helper)
feat(connectivity-test): 连通性测试弹窗/详情/徽标组件与四语言文案
chore(connectivity-test): 勾选 Task 5 / OpenSpec 2.1
test(connectivity-test): 锁定 toBackendParams 字段映射(customHeaders/customBody 等)
feat(connectivity-test): 前端命令绑定与测试/探针状态 hooks
chore(connectivity-test): 勾选 Task 4 / OpenSpec 1.4
feat(connectivity-test): 测试参数持久化纯函数(缺省即默认值)
chore(connectivity-test): 勾选 Task 3 / OpenSpec 1.3
feat(connectivity-test): Tauri 命令与注册,官方/动态端点过滤,sanitize 隔离私有字段
chore(connectivity-test): 勾选 Task 2 / OpenSpec 1.2
feat(connectivity-test): 后端直连执行器(reqwest+SSE,总/空闲超时,度量与错误识别)
feat(connectivity-test): 后端直连执行器(reqwest+SSE,总/空闲超时,度量与错误识别)
chore(connectivity-test): 勾选 Task 1 / OpenSpec 1.1
fix(connectivity-test): claude 鉴权对齐 adapter 顶层 apiKey/api_key 兜底
feat(connectivity-test): 新增后端连通性测试服务类型/模型解析/协议构造
chore: archive model-quick-switch-picker-scroll
chore: model-quick-switch-picker-scroll 验证报告与状态元数据
fix: 模型快捷切换弹窗内模型列表滚轮无法滚动
模态 Dialog 的 RemoveScroll 会对 shards 外的 portaled PopoverContent
wheel 事件无条件 preventDefault。在 SearchableModelPicker 的
PopoverContent 捕获阶段阻断 wheel 传播,恢复原生滚动。
chore: bump fork version to 3.20.1-1 after upstream v3.20.1 rebase
fix(form): only auto-expand Claude advanced options on divergent model mapping
Uniform mapping produced by the quick-set button (all roles equal to the
fallback model) no longer counts as an advanced value, so the advanced
section stays collapsed on edit. A 1M marker is stripped before comparing
so marked roles are not misjudged as divergent. Truly customized mappings
still auto-expand the section.
feat: align fallback-model quick-set buttons with model input right edge
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
docs: update README with fork build notes and quick-move provider changes
chore: archive comet-native claude-model-quick-access
feat: surface Claude fallback model as quick-access section below endpoint
Move the default fallback model (ANTHROPIC_MODEL) block out of the
collapsed Advanced Options into a quick-access section directly below
the request endpoint: input + 1M checkbox + quick-set + fetch-models
buttons are all reachable without expanding.
- header toggle-all "declare 1M" now includes the fallback model
(Haiku still excluded); partial selection shows indeterminate state
- drop the duplicated quick-set/fetch buttons and the old fallback
block inside Advanced Options
- exclude claudeModel from hasAnyAdvancedValue so a fallback-only
value no longer auto-expands Advanced Options
- quick-set priority, env write semantics, and OAuth preset branches
unchanged; add 4 component tests covering the new layout
chore: archive comet-native provider-right-click-top-bottom
Enable native workflow in .comet/config.yaml and move the Comet managed
state block in .gitignore so the exclusion rules stay grouped with the
other fork tool artifacts.
feat: add provider right-click move-to-top/bottom and insert new providers at second position
- ProviderList: context menu with quick move-to-top/move-to-bottom, reusing
update_providers_sort_order and refreshing tray menu like drag sort
- useAddProviderMutation: default insert at index 1 with explicit sortIndex
materialization for existing items (avoids defined-first sort trap)
- i18n: quickMove* strings for zh/en/zh-TW/ja
ci: stop flaky WSL2 linker job from blocking main
The backend-windows-wsl2 job intermittently fails with LNK1327 /
mt.exe c1010070 when the Windows runner's mt.exe cannot parse a
manifest temp file on the WSL2 UNC TEMP path. The same flaky failure
occurs upstream and is unrelated to fork changes. Mark the job
continue-on-error so main stops going red on it; the result stays
visible in the Actions UI.
style: apply prettier to files touched by recent comet archives
Frontend CI format:check failed on 6 files modified by the app-name
and model-quick-switch archive commits plus the updater-disable commit.
Run prettier --write so format:check passes again.
chore: archive ccs-fork-app-name-cc-switch
chore: archive ccs-model-quick-switch-ui-polish
docs: rewrite README to document fork differences vs upstream
README 从上游完整功能介绍改为仅记录 fork 与上游的差别,完整功能
文档指回上游。涵盖 8 项 fork 魔改:构建标识/开发预览、预设精简、
Claude 表单调整、模型搜索移植、主页模型快速切换、侧边栏面板显隐、
关闭应用内更新、打包范围,并说明版本号与 rebase 同步流程。
feat: disable in-app updater, point users to GitHub releases
移除 tauri-plugin-updater 依赖与自动更新链路,改为统一引导用户到
GitHub Releases 手动下载新版。
- 前端:updater.ts 恒返回 up-to-date;UpdateContext 取消启动自检;
AboutSection「检查更新」直接打开 fork releases 页;DatabaseUpgrade
恢复界面去掉下载进度条,改为打开发布页。
- 后端:移除 updater 插件注册与 UpdaterExt 调用;
install_update_and_restart 降级为空壳,check_app_update_available
返回当前版本号;check_for_updates 指向 fork releases;capabilities
去掉 updater:default 权限。
- 打包:tauri.conf.json 删 plugins.updater、createUpdaterArtifacts=false;
Cargo.toml / package.json 删 updater 依赖,Cargo.lock / pnpm-lock
同步重生成。
- CI:release.yml 移除签名收集与 assemble-latest-json job,删除
.tar.gz/.sig 处理,release body 去掉 updater 说明。
版本号统一为 3.20.0-1(合法 semver 且 MSI 第 4 位为 1)。
Cargo.lock / pnpm-lock.yaml 已随依赖移除重生成,cargo check 通过。
ci: build only Windows x64 and macOS arm64, unsigned
feat: gate dev panel behind dev-only env flag
chore(comet): archive ccs-sidebar-panels-visibility change
fix: apply skills visibility guard to hermes branch toolbar button
feat: include prompts in sidebar panel visibility settings
fix: persist visibleSidebarPanels setting in backend AppSettings
style: use compact pill toggles for sidebar panel visibility
feat: add sidebar panel visibility switches to settings
feat: add sidebar panel visibility settings and wire into toolbar buttons
chore(comet): archive ccs-home-model-display change
fix(fork): read grokbuild api key from config TOML in quick switch dialog
feat(fork): align model badge with upstream extractModelBadge
feat(fork): show current model badge and quick-switch entry on provider cards
feat(fork): add model quick switch dialog
feat(fork): add provider model read/write utils with tests
chore(comet): archive ccs-model-search-port change
feat(i18n): add model search picker keys for all locales
feat(form): port SearchableModelPicker from upstream for searchable model selection
chore(comet): archive ccs-claude-form-layout change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-claude-form-layout/
- Sync claude-form-layout spec to main specs
- Annotate design doc as archived
- Add verification report
fix(i18n): update advanced options hint, add ja/zh-TW toggle-all, drop dead apiFormatHint key
fix(form): correct 1M toggle-all alignment direction (ml-1)
fix(form): align 1M toggle-all checkbox with row checkboxes
refactor(form): move api format select into EndpointField rightSlot
fix(form): widen api format select to fit option text
feat(form): add 1M toggle-all checkbox in model mapping header
feat(form): move api format select next to endpoint input
chore(comet): archive ccs-provider-presets-trim change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-provider-presets-trim/
- Sync provider-presets-visibility spec to main specs
- Annotate design doc as archived
- Add verification report
docs(fork): add presets-trim files to rebase whitelist
test(fork): mock IS_FORK_BUILD false in unit tests for upstream-form semantics
feat(fork): wire official preset filter into provider forms
feat(fork): add hidden field to preset interfaces
feat(fork): add official preset allowlist and filter util
chore(comet): archive ccs-fork-scaffolding change
- Move change artifacts to docs/openspec/changes/archive/2026-08-18-ccs-fork-scaffolding/
- Sync fork-build-identity spec to main specs
- Annotate design doc as archived
- Add verification report
feat(fork): add dev config with isolated identifier and config dir
docs(fork): complete rebase whitelist with all fork-touched files
fix(fork): guard setTitle with isTauri in test/non-tauri env
fix(fork): define __CCS_FORK_BUILD__ in vitest config
feat(fork): add DevPanel and settings footer entry
feat(fork): set macOS window title to CC Switch (Fork) on startup
feat(fork): inject __CCS_FORK_BUILD__ compile-time constant
feat(fork): bump version to 3.19.2-fork.1 and rename product to CC Switch (Fork)
docs(fork): add upstream rebase workflow and ignore tool artifacts
fix(proxy): accept the whole grok-4.x (x>=5) family in the reasoning-effort whitelist (#7369)
Replace the verbatim grok-4.5 / grok-4.6 entries in supports_reasoning_effort with a rule that parses the grok-4.x minor version and accepts x >= 5, mirroring the existing GPT-5+ rule. This covers grok-4.7 (released 2026-09-21), whose reasoning effort was previously dropped on the Claude -> Chat, Claude -> Responses and Codex Responses -> Chat conversion paths, and lets future releases pass without another whitelist edit. The grok-build-* family is retained for saved providers.
Co-authored-by: allenxu09 <171831965+allenxu09@users.noreply.github.com>
docs(release): add v3.20.4 release notes
chore(release): v3.20.4
feat(pricing): add Grok 4.7 and GLM-5.3-FlashX
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.
fix(i18n): remove outdated OpenCode Go referral offer
fix: add SudoCode.chat alternate API endpoint
feat(settings): add a GitHub star prompt to the About card
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
feat(presets): add Soshow aggregator presets
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.
fix(ci): stabilize frontend tests and verify renderer build
fix(mcode): refresh provider state after removal (#7578)
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
fix(lib): reset skip_taskbar before window.show() across entry points (#6348)
5处 window.show() 调用中 4处没有在 show() 前重置 Windows 的 skip_taskbar,导致以下场景出现窗口可见但不在任务栏、点最小化后窗口消失的 bug:
- single_instance handler (lib.rs:360-361):当原进程因 silent_startup / 关闭到托盘等处于 skip_taskbar(true) 状态时,从快捷方式/开始菜单唤起已运行实例触发。
- handle_deeplink_url 的 focus_main_window 分支 (lib.rs:256):通过 ccswitch:// URL 唤起时。
- DB 恢复流程 (lib.rs:556):数据库版本不匹配时显示恢复界面。
- RunEvent::Opened 处理器 (lib.rs:1803):系统接收到 deep-link URL 时。
修复后的行为与 tray.rs::handle_tray_menu_event::show_main 分支一致(那里已有相同的 set_skip_taskbar(false) 调用)。
经用户测试验证:autostart + silent_startup + 从开始菜单/快捷方式启动的场景下,最小化按钮恢复正常工作。
fix(proxy): omit missing tool descriptions in the Codex responses-to-chat converter (#7378)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): preserve max effort for GPT-5.6 and GPT-6 Astra (#7531)
fix(misc): ignore shell startup output when probing tool versions in WSL (#7348)
try_get_version_wsl runs "${SHELL:-sh}" -lic '<tool> --version' through
wsl.exe and takes the first x.y.z anywhere in stdout. A login+interactive
shell runs the distro's startup files first, so their output shares that
stdout. On Ubuntu, /etc/profile.d/update-motd.sh prints the MOTD once a day,
starting with "Welcome to Ubuntu 24.04.4 LTS", and the card then showed
24.04.4 as the Claude Code version. The once-a-day stamp makes it appear
after the first probe of the day and vanish on refresh.
The payload now prints a sentinel before running the tool, and parsing looks
only at what follows its last occurrence. The last occurrence handles the
-lic || -lc || -c fallback chain printing it several times; matching it as a
substring handles OSC sequences glued in front of it. If the sentinel is
absent (the shell failed before the payload ran), the output is used as
before. The stderr fallback for tools that print their version there is kept.
Fixes #7347
Co-authored-by: lyfuci <lyfuci11@gmail.com>
fix(claude): hide session URL attribution (#7053)
* fix(claude): hide session URL attribution
* docs(claude): document session URL attribution
fix(ui): reset provider scroll when switching apps (#5211)
fix(pi): fix provider logo (#6826)
fix(i18n): use the Pi form's own term for the zh-TW duplicate-key message (#7187)
Follow-up to the nit on #6768. In zh-TW the Pi form labels the field
供應商識別碼 and says 供應商識別碼為必填 when it is empty, but the
duplicate message I added said 供應商標識 — the term Hermes, OpenCode and
OpenClaw use for their own field. One form, two names for the same thing.
The other three locales already keep the label and both messages on one
term, so this makes zh-TW match the pattern rather than introduce one:
en Provider key / Provider key is required / This provider key already exists
ja プロバイダーキー / プロバイダーキーを入力してください / このプロバイダーキーは既に存在します
zh 供应商标识 / 供应商标识不能为空 / 该供应商标识已存在
zh-TW 供應商識別碼 / 供應商識別碼為必填 / 該供應商標識已存在 <- the odd one out
hermes.form.providerKeyDuplicate carries the same sentence and is left
alone: its own label is 供應商標識, so it is already consistent.
Verified: managementListLocales 7/7. Full unit suite 1085/1087, the same
two App.test.tsx integration cases failing before and after.
fix(proxy): strip stop param for GitHub Copilot to unblock auto mode classifier (#5404)
Claude Code's auto mode classifier sends `stop_sequences` on every
safety check. On the GitHub Copilot path the Anthropic -> OpenAI Chat
conversion mapped it to `stop`, and Copilot's Chat Completions route
for Claude models answers that request with HTTP 400, so every
classifier call failed while normal chat kept working.
- Remove the converted `stop` field only when the provider is GitHub
Copilot; other OpenAI-compatible upstreams keep it.
- Copilot detection in the transform now also covers providers that
are recognised only through `env.ANTHROPIC_BASE_URL`.
- Regression tests for the Copilot (meta and env URL) and generic
provider cases.
Fixes #5175
fix(codex): keep stored auth when the live snapshot has no credential (#7434)
Editing the current Codex provider seeds the form from the live ~/.codex
snapshot, and switching away backfills that snapshot into the DB row. When a
provider table declares its own credential source (an Authorization header,
env_key, auth/aws), no bearer token is injected into config.toml and the
shared auth.json is deleted on third-party switches, so the live snapshot is
permanently `{ auth: {}, config }` while auth.OPENAI_API_KEY in the DB is the
only copy of the key. Both paths treated that empty live auth as
authoritative and silently erased the stored key, which in turn made the
usage-script modal misread the provider as an official subscription and
reset the saved script.
- Treat a credential-less live auth (missing auth.json or the bare
auth_mode logout marker) as an absent field in both the edit dialog and
the switch-away backfill, keeping the stored auth template. Live still
wins whenever it carries material, and official providers keep the live
login as their source of truth.
- Gate the official exemption on is_codex_official_provider /
resolveCodexOfficialIdentity rather than the category alone: a legacy
fixed official card without a persisted category is still written as
official, so the fallback must not resurrect a logged-out login on
switch-back.
- Cover both layers with regression tests, including the category-less
official shapes.
Fixes #7433
Supersedes #7492
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): lift additional_tools input carriers on the Codex chat conversion (#7454)
Codex talks to third-party providers over its Responses Lite transport,
which ships the request's extra tools in an `additional_tools` input
carrier: `{"type":"additional_tools","role":"developer","tools":[...]}`,
a role but no `content`. The Responses->Chat conversion let that carrier
fall through the generic "item with a role is a message" arm and emitted
`{"role":"system","content":null}` at messages[1], which strict Chat
gateways reject with `messages[1]: missing field "content"` (or
`data did not match any variant of ...SystemMessageContent`). The tools
carried inside it were also dropped on the Chat and Anthropic conversions.
- Skip `additional_tools` items in the Chat message walk; a carrier
declares tools, it is not a message.
- Collect carrier tools through the existing CodexToolContext registry
(dedup, namespace flattening, custom-tool conversion), which also feeds
the Anthropic conversion and the response-side name round-trip.
- Requests without a carrier convert byte-for-byte as before.
Known boundary: `custom` children inside a namespace tool
(`functions.exec`) are still not lifted; that needs its own response-side
name round-trip and is left for a follow-up.
Diagnosed independently in #6158 by BigStrongSun (fix in #6159) and in
fixture.
Fixes #7451
Fixes #6158
Refs #6033
Refs #6776
Supersedes #6159
fix(proxy): normalize Codex detail:original images for Chat gateways (#7476)
Codex marks tool-returned images with detail:"original" whenever the model
catalog advertises supports_image_detail_original, a Responses-only value.
The Responses->Chat conversion copied it verbatim into image_url.detail, and
strict OpenAI-compatible Chat gateways (Ark/Doubao, SenseNova, GLM, vLLM, ...)
reject the whole request with 400. Because Codex replays the full rollout on
every turn, one poisoned view_image result made the session fail permanently,
including plain-text turns.
Two layers:
- tool_media::merge_top_level_detail downgrades original -> auto, so already
poisoned rollouts and Codex versions before 0.155.0 (which do not normalize
replayed history themselves) recover immediately. auto/low/high are untouched.
- codex_catalog_model_entry always writes supports_image_detail_original:false
on the ProxyChat profile, so Codex stops emitting original at the source once
the catalog is regenerated. NativeResponses/Anthropic profiles and the DeepSeek
official catalog are unaffected.
Top-level user images were never affected: responses_content_to_chat_content
drops the part-level detail entirely.
Fixes #6495
Fixes #7458
Refs #6569
Refs #5805
fix(omo): detect unified config on the WSL side when the OpenCode dir is in WSL (#7550)
When the OpenCode config directory points into WSL
(\\wsl.localhost\<distro>\home\<user>\.config\opencode), OMO >= 4.19.3 keeps
its unified config in the WSL-side home (~/.omo/omo.jsonc). Detection only
probed the Windows home, never found it, and silently fell back to the legacy
per-plugin file that OMO no longer reads, so provider switches looked
successful but had no effect.
- Add derive_wsl_home_dir to derive the WSL-side home from a WSL UNC path
(home/<user> or root); non-WSL and non-home paths return None.
- Probe a list of home candidates for the unified config, WSL-side home
first and the OS home as a fallback, shared by the read, write and delete
paths.
- Detection only: when no unified config exists on either side the legacy
fallback is unchanged.
Fixes #7363
Supersedes #7367
docs(partner): add a FluxA purchase link and tighten its sponsor blurb
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.
fix(claude-desktop): expose the 1M context variant for DeepSeek V4 routes
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.
feat(presets): add FluxA Token Plan partner presets
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.
fix(presets): correct AICodeWith Codex-family endpoint to /v1
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.
fix(ui): render monochrome preset icons in the foreground color
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.
docs(user-manual): point Chat-only Codex examples at plan endpoints
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.
fix(pricing): restore DeepSeek V4 Pro price and seed Qwen3.8 and Hy4 models
- 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).
fix(presets): move MiniMax CN and BaiLing to their current official endpoints
- 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.
feat(codex): refresh CN presets from the Responses API audit
- 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.
test(claude-desktop): run Linux path tests on unix hosts and cover Linux in provider sync tests
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.
fix(ui): stop expanding stale cached usage tiers on ineligible provider cards
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.
fix(skills): 支持 skillId 与目录名不一致的技能安装和更新 (#6381)
* fix(skills): resolve mismatched skillId directories
* fix(skills): preserve source path during updates
* fix(skills): refresh source URL after repository relocation
* test(skills): isolate relocation fixture from legacy home
feat(opencode): 支持从获取的模型列表中搜索并批量添加所选模型 (#7515)
* feat: implement fetched models selection and UI components
* fix(opencode): prevent model search from submitting provider form
Add Kimi Global preset variants (kimi.ai platform + api endpoints) (#7526)
Add a "Kimi Global" twin immediately after the domestic "Kimi" API preset and a "Kimi For Coding Global" twin after the domestic "Kimi For Coding" preset, in every preset file that ships them (claude, claude-desktop, codex, hermes, openclaw, opencode, pi).
"Kimi Global" points to the international open platform https://platform.kimi.ai?aff=cc-switch (apiKeyUrl mirrored as https://platform.kimi.ai/console/api-keys?aff=cc-switch) and swaps the endpoint host api.moonshot.cn for api.moonshot.ai with each file's path unchanged (/v1 or /anthropic).
"Kimi For Coding Global" points to the international site https://www.kimi.ai/code/?aff=cc-switch and swaps the endpoint host api.kimi.com for api.kimi.ai with the path unchanged (e.g. https://api.kimi.ai/coding/v1).
Domestic presets are unchanged; category, icons, apiFormat, model catalogs, endpointCandidates (host swapped), partnerPromotionKey and settings otherwise mirror the domestic blocks exactly.
primePartner stays only on the domestic presets so the new variants do not alter prime-partner ordering.
The Pi twins get distinct providerKey values ("cc-switch-kimi-global", "cc-switch-kimi-for-coding-global") because providerKey is the preset-level primary key and must not collide with the domestic presets.
Rebalance sponsor CTAs around Kimi Code plan with dual-region links (#7522)
Kimi Code plan purchases have resumed, so the README sponsor sections go back to leading with the coding plan instead of the API-only flow written during the purchase pause.
All four locales (EN/ZH/JA/DE) now present Kimi Code plan (中文站 | Global) as the primary CTA and the Kimi Open Platform API (中文站 | Global, with the first-top-up bonus) as the secondary one, mirroring the domestic|global split already used for API links.
The aff and track_id values are unchanged.
fix(prompts): refresh active prompts after external file edits (#7194)
Reload the enabled prompt from its live file when the Prompts list loads, and refresh the open panel when the window regains focus, so external edits to CLAUDE.md / AGENTS.md show up without a restart.
- Keep saved templates available when the live file cannot be read.
- Skip the backfill for empty or whitespace-only files, matching enable_prompt.
- Skip the backfill while a sync/restore holds the sync lock, so a list load cannot write a stale live file into a freshly restored database.
fix(skills): raise archive entry limit so large skill repos install (#7489)
Installing hugohe3/ppt-master from skills.sh failed with
ARCHIVE_TOO_MANY_ENTRIES: the GitHub archive we download is the whole
repository (13,248 entries), and the 10,000-entry cap added in #5811
treated it as a zip bomb.
- Raise MAX_ARCHIVE_ENTRIES to 30,000 for both the remote archive and
local ZIP paths, and align WebDAV's MAX_EXTRACT_ENTRIES so a skills.zip
containing such a skill still restores.
- Charge every file entry at least one 4 KiB block against the existing
512 MiB budget, mirroring directory accounting, so empty-file archives
are bounded by the byte budget rather than by the entry count alone.
- Add tests for a 13k-entry repo archive and for the per-file minimum
charge.
Fixes: https://github.com/farion1231/cc-switch/issues/7475
feat: add MiniMax Code harness support (#7383)
Add MiniMax Code (mcode) as a managed application: provider and model management through the native custom_provider configuration, MCP servers, Skills, global instructions (AGENTS.md), read-only local session history with resume, and usage import from the native runtime database. Native files shared by the TUI and desktop are written under the native lock with rollback when the database commit fails, unrelated native configuration is preserved, and the data directory honors MINIMAX_DATA_DIR and MAVIS_DATA_DIR. Deleting sessions stays in the native application because they are coupled to runtime state.
fix(codex): recover stale account bindings during takeover (#7395)
After deleting a managed ChatGPT account and logging in again, a Codex provider can still reference the old local account ID, which made route switching, takeover, startup restoration and rebinding fail permanently.
Distinguish a missing outgoing account from an existing account with no matching live token. A missing account only releases its ownership marker after the persisted account store is verified; the target write policy stays in force. A stale target now reports an account-selection instruction instead of a bare not-found error, including when takeover is already enabled.
Fixes #7392
feat(claude-desktop): support 3P configuration on Linux (#7331)
Add Claude Desktop 3P configuration support on Linux. Resolve the config root from an absolute XDG_CONFIG_HOME, falling back to ~/.config; when CC Switch itself runs as a Flatpak, use the host ~/.config instead of the sandbox-private XDG_CONFIG_HOME. Update the en/zh/ja user manuals with Linux paths and the Flatpak boundary.
Fixes #4855
fix(proxy): omit missing tool descriptions instead of serializing null (#7319)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): keep reasoning effort for grok-4.6 and pass xhigh through verbatim (#7318)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(models): support Zhipu OpenAI Responses model lists (#7330)
Zhipu's Responses endpoint (open.bigmodel.cn/api/v1) serves its model list in the Codex remote catalog shape (models[].slug) instead of the OpenAI-compatible data[].id shape, so the Codex form's model fetch came back empty. The parser now falls back to models[].slug when data is absent.
fix(misc): fetch npm dist-tags from the dedicated endpoint with the probe timeout (#7346)
The tool version probe requested the full npm packument for each package (metadata for every historical version, 14-25 MB uncompressed for codex / opencode / openclaw) only to read its dist-tags, and it was the only latest-version probe without LATEST_PROBE_TIMEOUT, so it fell back to the proxy client's 600 s total timeout and the cards sat on "loading" for minutes on slow links while the large downloads starved the Hermes GitHub probe into "unknown".
Switch fetch_npm_dist_tags to registry.npmjs.org/-/package/{package}/dist-tags, whose body is the dist-tags object itself (tens of bytes to a few KB), escape scoped names as %2f, and apply the same 15 s probe timeout as the GitHub and PyPI probes. No data is lost: the only consumer reads latest and the tool's prerelease tags.
Fixes #7339
feat(usage): display output tokens per second in request log table and detail panel (#3369)
Show tokens/s next to the output token count in the request log table and the request detail panel.
The generation window prefers an explicit duration when present, otherwise latency minus first-token time for streaming requests, and falls back to full latency for non-streaming requests. Rows without a positive token count or window show nothing.
Closes #5936
fix(presets): update APIKEY.FUN URLs to apikey.fan
docs(release): add v3.20.3 release notes
chore(release): v3.20.3
fix(pricing): move retired DeepSeek V4 family to the V4.1 Flash tier
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.
fix(presets): declare third-party Token Plan DeepSeek V4 rows text-only
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.
feat(presets): rename DouBaoSeed preset to localized Volcengine Doubao
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.
fix(codex): skip unchanged incomplete rollout tails
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.
fix(claude): align quick model selection with form order
fix(tray): show bound ChatGPT account quota for managed Codex cards
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
feat(claude): add "Disable Artifact Tool" quick toggle to provider editor
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.
feat(codex): refresh aggregator model presets and catalogs
feat(codex): switch Kimi presets to native Responses direct-connect
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.
fix(usage): show Claude Fable weekly limit from the usage API limits array
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.
fix(codex): mirror vision-capable DeepSeek catalog and un-gate the legacy v4-flash alias (#7286)
Refresh the bundled DeepSeek official Codex catalog to the vendor's current models.json (slug deepseek-flash, image input modality, supports_image_detail_original) and remove deepseek-v4-flash from the confirmed text-only registry: the legacy id is still accepted by DeepSeek and routed to the vision-capable V4.1 Flash, so both the proxy media sanitizer and the generated Codex catalog must fail open for it. deepseek-v4-pro stays text-only per the vendor catalog.
Fixes #7283
fix(proxy): clamp sub-floor max_tokens to the Responses API minimum (#7287)
The Responses API rejects max_output_tokens below 16, but Anthropic clients legitimately send tiny probe budgets (Claude Desktop's model-availability probe uses max_tokens=1). The Anthropic→Responses conversion copied the value verbatim, so strict upstreams returned 400 and Claude Desktop reported the mapped model as unavailable under local routing.
Clamp 1..=15 up to 16 during conversion; 16+, 0 and non-integer values keep their pass-through semantics. Normal session requests convert byte-for-byte as before, and the Codex OAuth path still strips max_output_tokens afterwards.
Fixes #7103
fix(codex): coalesce adjacent commentary with pending tool calls (#7280)
The Codex Responses -> Chat Completions converter emitted two consecutive
assistant messages when one Responses model turn contained a commentary
message directly followed by function_call items. Chat upstreams imitate
the text-only assistant message as a complete turn and return
finish_reason=stop before the expected tool call, so long agent tasks end
after a progress update.
Merge pending tool calls into the directly adjacent assistant message when
it carries no tool_calls yet, attach pending reasoning per segment so
parallel calls do not duplicate it, and keep the new-message path for every
other boundary shape (user/tool boundaries, prior tool-call batches, media
flushes). The reasoning placeholder backfill still applies to coalesced
calls without reasoning.
Fixes #6529
Supersedes #6530
Co-authored-by: BigStrongSun <BigStrongSun@users.noreply.github.com>
fix(proxy): skip empty reasoning_content placeholders in Anthropic SSE streaming (#7227)
Some OpenAI-compatible upstreams (GLM, Qwen, DeepSeek-V4-Pro via ModelScope/DashScope,
Bailian, JD Cloud, ...) keep a `reasoning_content: ""` placeholder in every content
chunk instead of omitting the field. The reasoning branch of the OpenAI Chat -> Anthropic
SSE converter only checked for presence, and it shares `current_non_tool_block_type`
with the content branch, so every content chunk closed the open text block, opened an
empty thinking block, then reopened a text block. N content chunks became 2N content
blocks, half of them empty thinking blocks, which Claude Code rendered as one line per
token with dozens of empty Thought blocks and bloated the session JSONL.
Filter empty reasoning before entering the branch, matching the `!content.is_empty()`
guard the content branch already had and the non-streaming `openai_to_anthropic`
converter. Two regression tests pin block index, type and order: the placeholder shape
must yield a single text block and no thinking_delta at all, and real reasoning must
still yield one thinking block followed by one text block.
Fixes #5028
Fixes #4404
fix(codex): detect growing rollouts with a persisted byte cursor (#7219)
Codex keeps the rollout append handle open for the whole session, so on Windows NTFS the file mtime does not advance while the file grows and the pure-mtime gate skipped growing rollouts forever. Persist the observed byte length in session_log_sync.last_byte_offset for Codex rows and skip a file only when both mtime and size are unchanged. The parser now splits records on newlines manually so an incomplete trailing record is retried on the next pass without advancing the line cursor.
Fixes #6060
fix(codex): proxy image edits and Images API follow-ups (#7177)
* fix(codex): proxy image edits and Images API follow-ups
Follow-ups to #7036 for Codex's built-in ImageGen over the local proxy:
- Register `/images/edits` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them through the shared Codex standalone passthrough.
ImageGen posts to `images/edits` whenever a call references existing
images, and the router returned an empty 404 for it.
- Add `CodexStandaloneEndpoint::ImagesEdits`. Both Images endpoints share
one full-URL suffix table, so a complete Responses / Compact / Chat
Completions / Images URL derives the sibling endpoint (and vice versa)
while opaque full URLs keep failing closed.
- Apply the standalone sibling rewrite when the base URL is pasted as a
complete endpoint with the full-URL switch off, mirroring
`codex_chat_base_is_full_endpoint`; standalone requests no longer become
`.../chat/completions/images/generations`.
- Parse Images API streaming usage: `image_generation.completed` carries
`usage` on the event top level, so scan for `usage.input_tokens` before
falling back to the Chat Completions stream parser.
Related #7036
* fix(codex): match standalone URL suffixes case-insensitively
Keep endpoint detection and sibling rewriting consistent while preserving the original URL prefix and query. Cover mixed-case Images and Alpha Search endpoints through URL and proxy regression cases.
---------
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): preserve per-app settings on shutdown and port allocation (#7210)
On normal exit, stop_with_restore_keep_state() called the legacy global
proxy-config writer to clear the long-retired live_takeover_active flag.
That writer reads the Claude row and runs an UPDATE without a WHERE clause,
so every shutdown copied Claude's max_retries and the three timeout fields
onto the Codex, Gemini and Grok Build rows — and the UPDATE never carried
live_takeover_active in the first place, so the step had no effect beyond
the clobber. The ephemeral listen-port path did the same on every proxy
start.
Drop the dead write-back (live-config restore, backup cleanup and health
reset are unchanged) and persist the resolved ephemeral port through the
global-config interface, which only touches the shared listen/logging
columns. Two regression tests seed distinct per-app settings for all four
apps and assert they survive both paths.
Fixes #7204
fix(provider): preserve child metadata during universal sync (#7212)
Syncing a universal provider regenerates its Claude/Codex/Gemini child
providers. The child's `settings_config` was merged with the existing row,
but `meta`, `created_at` and `sort_index` were taken from the generated
object, and the universal provider never carries any of them from the UI.
The UPDATE therefore wiped the child's usage script, common-config opt-out,
endpoint auto-select and other per-app settings to `{}` and reset the sort
index to NULL, which pushed the card to the bottom of the list and looked
like the provider had been deleted and re-created.
Keep the existing child's `meta`, `created_at` and `sort_index` when the
row already exists. Name, base URL, key, models, website and notes are
still driven by the universal provider, and first-time creation still
inherits the parent's metadata.
Fixes #7134
feat(presets): rebrand DashScope as 千问AI平台 and refresh Qwen models (#7183)
Rename the domestic Bailian presets to 千问AI平台 with a dedicated icon, refresh the Qwen family to the 3.8 generation across Claude Code, Claude Desktop, Codex, Hermes, OpenClaw, OpenCode and Pi, add the domestic Token Plan presets, restore the international QwenCloud Coding Plan presets, and align OpenClaw/OpenCode/Pi model metadata with the official client docs.
fix(codex): honor proxy URL when model_provider is omitted (#7263)
When a Codex provider card's TOML has no `model_provider`, Codex falls back to
its built-in `openai` provider, but takeover wrote the proxy address as a
top-level `base_url` that Codex never reads, so requests bypassed the local
proxy and went straight to api.openai.com.
Treat the missing selector as the built-in `openai` provider and write the
proxy address to `openai_base_url`; the existing legacy-reroute migration then
turns it into a `cc-switch` custom provider table carrying the `PROXY_MANAGED`
bearer, the same shape every other third-party takeover uses. Explicit
provider selections are unchanged.
Refs #6256, #7217
fix(provider): update MiniMax defaults to M3 and remove expired offers (#7255)
* fix(provider): refresh MiniMax M3 presets and remove expired promotions
* fix(provider): complete MiniMax M3 capability declarations
---------
Co-authored-by: saladday <1203511142@qq.com>
chore(provider): remove Atlas Cloud sponsor status, keep presets
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`
docs(release): add v3.20.2 release notes
chore(release): v3.20.2
feat(provider): add SoleAPI sponsor presets across all nine apps
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.
fix(codex): proxy image edits endpoint
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.
fix(provider): pin models URL for JieKou and Novita Claude presets
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.
feat(pricing): refresh seed for September 2026 vendor price changes
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.
fix(codex): stamp requires_openai_auth on takeover writes to match the live login state
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.
fix(updater): show the real reason when an update check fails (#6482)
The updater plugin rejects with a plain string, so `err instanceof Error`
never matched and the real failure reason was replaced by the generic
fallback message. Use the existing extractErrorMessage helper, and pass
the recovered message into the check-update failure toast so it actually
reaches the user instead of stopping at an unread context state.
Co-authored-by: Jason <farion1231@gmail.com>
docs: fix the locales path in the README directory trees (#6100)
All four READMEs showed locales/ as a direct child of src/, but the
translations live at src/i18n/locales/. Split it into two tree nodes so it
matches how the rest of the tree is drawn. Also fixes a CLaude -> Claude
typo in README_ZH.md.
Co-authored-by: Jason <farion1231@gmail.com>
fix(claude): exclude workflow journal.jsonl from session list
Claude Code's workflow feature writes subagents/workflows/wf_*/journal.jsonl inside the session directory. The Claude session scanner only excluded agent-* files, so each journal parsed as an empty ghost session titled "journal". Exclude journal.jsonl by file name and add regression tests.
Fixes #6042
fix(i18n): add the missing pi.form.providerKeyDuplicate and common.collapse keys
translatePiProviderMutationError returns pi.form.providerKeyDuplicate for the Pi backend's duplicate-key error, but the key existed in none of the four locale files, so the toast showed the raw key. common.collapse (aria-label of the thinking-map toggle) was missing the same way. Add both keys to zh/en/ja/zh-TW and lock them in the locale coverage test.
fix(proxy): stop mask_url panicking on multi-byte UTF-8 near the truncation boundary
mask_url's fallback branch (used when the proxy URL fails Url::parse) sliced the raw string at byte 20 and panicked when a multi-byte UTF-8 character straddled that offset, e.g. a CJK string pasted into the global proxy address. Snap the cut to the nearest char boundary at or before 20 and add a regression test.
fix(tests): isolate LOCALAPPDATA so tests stop writing the real Claude Desktop config
On Windows, Claude Desktop config paths resolve from LOCALAPPDATA, which the test home override in tests/support.rs did not redirect, so profile_roundtrip wrote the https://desktop.test fixture into the developer's real Claude Desktop config. Point LOCALAPPDATA at the isolated test home alongside HOME and USERPROFILE.
Fixes #6077
fix(usage): widen pricing model source select to fit localized labels
The pricing model source SelectTrigger in the usage cost settings table was w-28, which leaves about 70px for the label; the English and Japanese labels need about 107px and overflowed the trigger. Widen it to w-40 and drop the h-7 overrides so the trigger and the adjacent Input use the shared h-9 height.
feat(pi): add Tencent TokenHub and Token Plan presets (#7159)
Add eight Tencent presets for Pi, covering the TokenHub pay-as-yo…
- 弹窗抬至 alert 层(z-[60]):打开连通性测试时 header 切换栏正确变灰,
详情子弹窗同步抬层(同 z + 更晚挂载 portal 在上)
- 批量检测入口移至 header 图标组(状态提升到 App,ProviderList 经
props 读取探针结果),不再另起一行
- 批量检测可停止:runId 代次守卫(Tauri invoke 无法真正取消),再次
点击即清除 waiting/running 徽标、丢弃在途迟到写入、不再拉起排队项
- 弹窗打开自动勾选模型:已保存默认测试模型 → 供应商当前模型
(claude env.ANTHROPIC_MODEL / codex config.toml model,新增
currentProviderModelId 与后端同口径)→ 清单首个
前端全量 141 文件 / 1106 测试全绿;typecheck clean
chore: archive per-model-connectivity-test
fix(connectivity-test): shouldShowTestEntry 增判 providerType + 收尾清理
chore(connectivity-test): 勾选 Task 9 / OpenSpec 5.1-5.2 + verify 报告
chore(connectivity-test): 勾选 Task 8 / OpenSpec 4.1-4.3
refactor(connectivity-test): 移除旧 stream_check 可达性探测全链路(保留表结构)
- 删除 6 个文件:commands/stream_check.rs、services/stream_check.rs、
database/dao/stream_check.rs、hooks/useStreamCheck.ts、lib/api/connectivity-check.ts、
components/usage/ConnectivityCheckConfigPanel.tsx
- 移除 mod.rs 声明、lib.rs generate_handler! 注册、cleanup_old_stream_check_logs 调用
- 从 SYNC_SKIP_TABLES / SYNC_PRESERVE_TABLES 移除 stream_check_logs(表结构保留在 schema.rs)
- 更新 backup.rs 测试:移除 stream_check_logs fixture 与断言
- 更新 SettingsPage:移除 ConnectivityCheckConfigPanel AccordionItem 与 FlaskConical 导入
- i18n:删除 streamCheck.* 命名空间(zh/en/ja/zh-TW 四语言)
- 更新注释:移除 stream_check 旧引用(forwarder.rs、provider.rs、userAgentPresets.ts、
connectivity_test.rs)
- 修复 4 个 pre-existing clippy lint(connectivity_test: matches! 宏、unused_unit、
derive Default)
- 移除 ProviderList.test.tsx 中已失效的 useStreamCheck mock
chore(connectivity-test): 勾选 Task 7 / OpenSpec 2.4 3.1-3.3
feat(connectivity-test): 供应商列表集成(弹窗入口 + 批量探针徽标)
chore(connectivity-test): 勾选 Task 6 / OpenSpec 2.2-2.3
fix(connectivity-test): 模型列表解析与后端对齐(每条目首个匹配,提取共享 helper)
feat(connectivity-test): 连通性测试弹窗/详情/徽标组件与四语言文案
chore(connectivity-test): 勾选 Task 5 / OpenSpec 2.1
test(connectivity-test): 锁定 toBackendParams 字段映射(customHeaders/customBody 等)
feat(connectivity-test): 前端命令绑定与测试/探针状态 hooks
chore(connectivity-test): 勾选 Task 4 / OpenSpec 1.4
feat(connectivity-test): 测试参数持久化纯函数(缺省即默认值)
chore(connectivity-test): 勾选 Task 3 / OpenSpec 1.3
feat(connectivity-test): Tauri 命令与注册,官方/动态端点过滤,sanitize 隔离私有字段
chore(connectivity-test): 勾选 Task 2 / OpenSpec 1.2
feat(connectivity-test): 后端直连执行器(reqwest+SSE,总/空闲超时,度量与错误识别)
feat(connectivity-test): 后端直连执行器(reqwest+SSE,总/空闲超时,度量与错误识别)
chore(connectivity-test): 勾选 Task 1 / OpenSpec 1.1
fix(connectivity-test): claude 鉴权对齐 adapter 顶层 apiKey/api_key 兜底
feat(connectivity-test): 新增后端连通性测试服务类型/模型解析/协议构造
chore: archive model-quick-switch-picker-scroll
chore: model-quick-switch-picker-scroll 验证报告与状态元数据
fix: 模型快捷切换弹窗内模型列表滚轮无法滚动
模态 Dialog 的 RemoveScroll 会对 shards 外的 portaled PopoverContent
wheel 事件无条件 preventDefault。在 SearchableModelPicker 的
PopoverContent 捕获阶段阻断 wheel 传播,恢复原生滚动。
chore: bump fork version to 3.20.1-1 after upstream v3.20.1 rebase
fix(form): only auto-expand Claude advanced options on divergent model mapping
Uniform mapping produced by the quick-set button (all roles equal to the
fallback model) no longer counts as an advanced value, so the advanced
section stays collapsed on edit. A 1M marker is stripped before comparing
so marked roles are not misjudged as divergent. Truly customized mappings
still auto-expand the section.
feat: align fallback-model quick-set buttons with model input right edge
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
docs: update README with fork build notes and quick-move provider changes
chore: archive comet-native claude-model-quick-access
feat: surface Claude fallback model as quick-access section below endpoint
Move the default fallback model (ANTHROPIC_MODEL) block out of the
collapsed Advanced Options into a quick-access section directly below
the request endpoint: input + 1M checkbox + quick-set + fetch-models
buttons are all reachable without expanding.
- header toggle-all "declare 1M" now includes the fallback model
(Haiku still excluded); partial selection shows indeterminate state
- drop the duplicated quick-set/fetch buttons and the old fallback
block inside Advanced Options
- exclude claudeModel from hasAnyAdvancedValue so a fallback-only
value no longer auto-expands Advanced Options
- quick-set priority, env write semantics, and OAuth preset branches
unchanged; add 4 component tests covering the new layout
chore: archive comet-native provider-right-click-top-bottom
Enable native workflow in .comet/config.yaml and move the Comet managed
state block in .gitignore so the exclusion rules stay grouped with the
other fork tool artifacts.
feat: add provider right-click move-to-top/bottom and insert new providers at second position
- ProviderList: context menu with quick move-to-top/move-to-bottom, reusing
update_providers_sort_order and refreshing tray menu like drag sort
- useAddProviderMutation: default insert at index 1 with explicit sortIndex
materialization for existing items (avoids defined-first sort trap)
- i18n: quickMove* strings for zh/en/zh-TW/ja
ci: stop flaky WSL2 linker job from blocking main
The backend-windows-wsl2 job intermittently fails with LNK1327 /
mt.exe c1010070 when the Windows runner's mt.exe cannot parse a
manifest temp file on the WSL2 UNC TEMP path. The same flaky failure
occurs upstream and is unrelated to fork changes. Mark the job
continue-on-error so main stops going red on it; the result stays
visible in the Actions UI.
style: apply prettier to files touched by recent comet archives
Frontend CI format:check failed on 6 files modified by the app-name
and model-quick-switch archive commits plus the updater-disable commit.
Run prettier --write so format:check passes again.
chore: archive ccs-fork-app-name-cc-switch
chore: archive ccs-model-quick-switch-ui-polish
docs: rewrite README to document fork differences vs upstream
README 从上游完整功能介绍改为仅记录 fork 与上游的差别,完整功能
文档指回上游。涵盖 8 项 fork 魔改:构建标识/开发预览、预设精简、
Claude 表单调整、模型搜索移植、主页模型快速切换、侧边栏面板显隐、
关闭应用内更新、打包范围,并说明版本号与 rebase 同步流程。
feat: disable in-app updater, point users to GitHub releases
移除 tauri-plugin-updater 依赖与自动更新链路,改为统一引导用户到
GitHub Releases 手动下载新版。
- 前端:updater.ts 恒返回 up-to-date;UpdateContext 取消启动自检;
AboutSection「检查更新」直接打开 fork releases 页;DatabaseUpgrade
恢复界面去掉下载进度条,改为打开发布页。
- 后端:移除 updater 插件注册与 UpdaterExt 调用;
install_update_and_restart 降级为空壳,check_app_update_available
返回当前版本号;check_for_updates 指向 fork releases;capabilities
去掉 updater:default 权限。
- 打包:tauri.conf.json 删 plugins.updater、createUpdaterArtifacts=false;
Cargo.toml / package.json 删 updater 依赖,Cargo.lock / pnpm-lock
同步重生成。
- CI:release.yml 移除签名收集与 assemble-latest-json job,删除
.tar.gz/.sig 处理,release body 去掉 updater 说明。
版本号统一为 3.20.0-1(合法 semver 且 MSI 第 4 位为 1)。
Cargo.lock / pnpm-lock.yaml 已随依赖移除重生成,cargo check 通过。
ci: build only Windows x64 and macOS arm64, unsigned
feat: gate dev panel behind dev-only env flag
chore(comet): archive ccs-sidebar-panels-visibility change
fix: apply skills visibility guard to hermes branch toolbar button
feat: include prompts in sidebar panel visibility settings
fix: persist visibleSidebarPanels setting in backend AppSettings
style: use compact pill toggles for sidebar panel visibility
feat: add sidebar panel visibility switches to settings
feat: add sidebar panel visibility settings and wire into toolbar buttons
chore(comet): archive ccs-home-model-display change
fix(fork): read grokbuild api key from config TOML in quick switch dialog
feat(fork): align model badge with upstream extractModelBadge
feat(fork): show current model badge and quick-switch entry on provider cards
feat(fork): add model quick switch dialog
feat(fork): add provider model read/write utils with tests
chore(comet): archive ccs-model-search-port change
feat(i18n): add model search picker keys for all locales
feat(form): port SearchableModelPicker from upstream for searchable model selection
chore(comet): archive ccs-claude-form-layout change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-claude-form-layout/
- Sync claude-form-layout spec to main specs
- Annotate design doc as archived
- Add verification report
fix(i18n): update advanced options hint, add ja/zh-TW toggle-all, drop dead apiFormatHint key
fix(form): correct 1M toggle-all alignment direction (ml-1)
fix(form): align 1M toggle-all checkbox with row checkboxes
refactor(form): move api format select into EndpointField rightSlot
fix(form): widen api format select to fit option text
feat(form): add 1M toggle-all checkbox in model mapping header
feat(form): move api format select next to endpoint input
chore(comet): archive ccs-provider-presets-trim change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-provider-presets-trim/
- Sync provider-presets-visibility spec to main specs
- Annotate design doc as archived
- Add verification report
docs(fork): add presets-trim files to rebase whitelist
test(fork): mock IS_FORK_BUILD false in unit tests for upstream-form semantics
feat(fork): wire official preset filter into provider forms
feat(fork): add hidden field to preset interfaces
feat(fork): add official preset allowlist and filter util
chore(comet): archive ccs-fork-scaffolding change
- Move change artifacts to docs/openspec/changes/archive/2026-08-18-ccs-fork-scaffolding/
- Sync fork-build-identity spec to main specs
- Annotate design doc as archived
- Add verification report
feat(fork): add dev config with isolated identifier and config dir
docs(fork): complete rebase whitelist with all fork-touched files
fix(fork): guard setTitle with isTauri in test/non-tauri env
fix(fork): define __CCS_FORK_BUILD__ in vitest config
feat(fork): add DevPanel and settings footer entry
feat(fork): set macOS window title to CC Switch (Fork) on startup
feat(fork): inject __CCS_FORK_BUILD__ compile-time constant
feat(fork): bump version to 3.19.2-fork.1 and rename product to CC Switch (Fork)
docs(fork): add upstream rebase workflow and ignore tool artifacts
fix(proxy): accept the whole grok-4.x (x>=5) family in the reasoning-effort whitelist (#7369)
Replace the verbatim grok-4.5 / grok-4.6 entries in supports_reasoning_effort with a rule that parses the grok-4.x minor version and accepts x >= 5, mirroring the existing GPT-5+ rule. This covers grok-4.7 (released 2026-09-21), whose reasoning effort was previously dropped on the Claude -> Chat, Claude -> Responses and Codex Responses -> Chat conversion paths, and lets future releases pass without another whitelist edit. The grok-build-* family is retained for saved providers.
Co-authored-by: allenxu09 <171831965+allenxu09@users.noreply.github.com>
docs(release): add v3.20.4 release notes
chore(release): v3.20.4
feat(pricing): add Grok 4.7 and GLM-5.3-FlashX
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.
fix(i18n): remove outdated OpenCode Go referral offer
fix: add SudoCode.chat alternate API endpoint
feat(settings): add a GitHub star prompt to the About card
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
feat(presets): add Soshow aggregator presets
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.
fix(ci): stabilize frontend tests and verify renderer build
fix(mcode): refresh provider state after removal (#7578)
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
fix(lib): reset skip_taskbar before window.show() across entry points (#6348)
5处 window.show() 调用中 4处没有在 show() 前重置 Windows 的 skip_taskbar,导致以下场景出现窗口可见但不在任务栏、点最小化后窗口消失的 bug:
- single_instance handler (lib.rs:360-361):当原进程因 silent_startup / 关闭到托盘等处于 skip_taskbar(true) 状态时,从快捷方式/开始菜单唤起已运行实例触发。
- handle_deeplink_url 的 focus_main_window 分支 (lib.rs:256):通过 ccswitch:// URL 唤起时。
- DB 恢复流程 (lib.rs:556):数据库版本不匹配时显示恢复界面。
- RunEvent::Opened 处理器 (lib.rs:1803):系统接收到 deep-link URL 时。
修复后的行为与 tray.rs::handle_tray_menu_event::show_main 分支一致(那里已有相同的 set_skip_taskbar(false) 调用)。
经用户测试验证:autostart + silent_startup + 从开始菜单/快捷方式启动的场景下,最小化按钮恢复正常工作。
fix(proxy): omit missing tool descriptions in the Codex responses-to-chat converter (#7378)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): preserve max effort for GPT-5.6 and GPT-6 Astra (#7531)
fix(misc): ignore shell startup output when probing tool versions in WSL (#7348)
try_get_version_wsl runs "${SHELL:-sh}" -lic '<tool> --version' through
wsl.exe and takes the first x.y.z anywhere in stdout. A login+interactive
shell runs the distro's startup files first, so their output shares that
stdout. On Ubuntu, /etc/profile.d/update-motd.sh prints the MOTD once a day,
starting with "Welcome to Ubuntu 24.04.4 LTS", and the card then showed
24.04.4 as the Claude Code version. The once-a-day stamp makes it appear
after the first probe of the day and vanish on refresh.
The payload now prints a sentinel before running the tool, and parsing looks
only at what follows its last occurrence. The last occurrence handles the
-lic || -lc || -c fallback chain printing it several times; matching it as a
substring handles OSC sequences glued in front of it. If the sentinel is
absent (the shell failed before the payload ran), the output is used as
before. The stderr fallback for tools that print their version there is kept.
Fixes #7347
Co-authored-by: lyfuci <lyfuci11@gmail.com>
fix(claude): hide session URL attribution (#7053)
* fix(claude): hide session URL attribution
* docs(claude): document session URL attribution
fix(ui): reset provider scroll when switching apps (#5211)
fix(pi): fix provider logo (#6826)
fix(i18n): use the Pi form's own term for the zh-TW duplicate-key message (#7187)
Follow-up to the nit on #6768. In zh-TW the Pi form labels the field
供應商識別碼 and says 供應商識別碼為必填 when it is empty, but the
duplicate message I added said 供應商標識 — the term Hermes, OpenCode and
OpenClaw use for their own field. One form, two names for the same thing.
The other three locales already keep the label and both messages on one
term, so this makes zh-TW match the pattern rather than introduce one:
en Provider key / Provider key is required / This provider key already exists
ja プロバイダーキー / プロバイダーキーを入力してください / このプロバイダーキーは既に存在します
zh 供应商标识 / 供应商标识不能为空 / 该供应商标识已存在
zh-TW 供應商識別碼 / 供應商識別碼為必填 / 該供應商標識已存在 <- the odd one out
hermes.form.providerKeyDuplicate carries the same sentence and is left
alone: its own label is 供應商標識, so it is already consistent.
Verified: managementListLocales 7/7. Full unit suite 1085/1087, the same
two App.test.tsx integration cases failing before and after.
fix(proxy): strip stop param for GitHub Copilot to unblock auto mode classifier (#5404)
Claude Code's auto mode classifier sends `stop_sequences` on every
safety check. On the GitHub Copilot path the Anthropic -> OpenAI Chat
conversion mapped it to `stop`, and Copilot's Chat Completions route
for Claude models answers that request with HTTP 400, so every
classifier call failed while normal chat kept working.
- Remove the converted `stop` field only when the provider is GitHub
Copilot; other OpenAI-compatible upstreams keep it.
- Copilot detection in the transform now also covers providers that
are recognised only through `env.ANTHROPIC_BASE_URL`.
- Regression tests for the Copilot (meta and env URL) and generic
provider cases.
Fixes #5175
fix(codex): keep stored auth when the live snapshot has no credential (#7434)
Editing the current Codex provider seeds the form from the live ~/.codex
snapshot, and switching away backfills that snapshot into the DB row. When a
provider table declares its own credential source (an Authorization header,
env_key, auth/aws), no bearer token is injected into config.toml and the
shared auth.json is deleted on third-party switches, so the live snapshot is
permanently `{ auth: {}, config }` while auth.OPENAI_API_KEY in the DB is the
only copy of the key. Both paths treated that empty live auth as
authoritative and silently erased the stored key, which in turn made the
usage-script modal misread the provider as an official subscription and
reset the saved script.
- Treat a credential-less live auth (missing auth.json or the bare
auth_mode logout marker) as an absent field in both the edit dialog and
the switch-away backfill, keeping the stored auth template. Live still
wins whenever it carries material, and official providers keep the live
login as their source of truth.
- Gate the official exemption on is_codex_official_provider /
resolveCodexOfficialIdentity rather than the category alone: a legacy
fixed official card without a persisted category is still written as
official, so the fallback must not resurrect a logged-out login on
switch-back.
- Cover both layers with regression tests, including the category-less
official shapes.
Fixes #7433
Supersedes #7492
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): lift additional_tools input carriers on the Codex chat conversion (#7454)
Codex talks to third-party providers over its Responses Lite transport,
which ships the request's extra tools in an `additional_tools` input
carrier: `{"type":"additional_tools","role":"developer","tools":[...]}`,
a role but no `content`. The Responses->Chat conversion let that carrier
fall through the generic "item with a role is a message" arm and emitted
`{"role":"system","content":null}` at messages[1], which strict Chat
gateways reject with `messages[1]: missing field "content"` (or
`data did not match any variant of ...SystemMessageContent`). The tools
carried inside it were also dropped on the Chat and Anthropic conversions.
- Skip `additional_tools` items in the Chat message walk; a carrier
declares tools, it is not a message.
- Collect carrier tools through the existing CodexToolContext registry
(dedup, namespace flattening, custom-tool conversion), which also feeds
the Anthropic conversion and the response-side name round-trip.
- Requests without a carrier convert byte-for-byte as before.
Known boundary: `custom` children inside a namespace tool
(`functions.exec`) are still not lifted; that needs its own response-side
name round-trip and is left for a follow-up.
Diagnosed independently in #6158 by BigStrongSun (fix in #6159) and in
fixture.
Fixes #7451
Fixes #6158
Refs #6033
Refs #6776
Supersedes #6159
fix(proxy): normalize Codex detail:original images for Chat gateways (#7476)
Codex marks tool-returned images with detail:"original" whenever the model
catalog advertises supports_image_detail_original, a Responses-only value.
The Responses->Chat conversion copied it verbatim into image_url.detail, and
strict OpenAI-compatible Chat gateways (Ark/Doubao, SenseNova, GLM, vLLM, ...)
reject the whole request with 400. Because Codex replays the full rollout on
every turn, one poisoned view_image result made the session fail permanently,
including plain-text turns.
Two layers:
- tool_media::merge_top_level_detail downgrades original -> auto, so already
poisoned rollouts and Codex versions before 0.155.0 (which do not normalize
replayed history themselves) recover immediately. auto/low/high are untouched.
- codex_catalog_model_entry always writes supports_image_detail_original:false
on the ProxyChat profile, so Codex stops emitting original at the source once
the catalog is regenerated. NativeResponses/Anthropic profiles and the DeepSeek
official catalog are unaffected.
Top-level user images were never affected: responses_content_to_chat_content
drops the part-level detail entirely.
Fixes #6495
Fixes #7458
Refs #6569
Refs #5805
fix(omo): detect unified config on the WSL side when the OpenCode dir is in WSL (#7550)
When the OpenCode config directory points into WSL
(\\wsl.localhost\<distro>\home\<user>\.config\opencode), OMO >= 4.19.3 keeps
its unified config in the WSL-side home (~/.omo/omo.jsonc). Detection only
probed the Windows home, never found it, and silently fell back to the legacy
per-plugin file that OMO no longer reads, so provider switches looked
successful but had no effect.
- Add derive_wsl_home_dir to derive the WSL-side home from a WSL UNC path
(home/<user> or root); non-WSL and non-home paths return None.
- Probe a list of home candidates for the unified config, WSL-side home
first and the OS home as a fallback, shared by the read, write and delete
paths.
- Detection only: when no unified config exists on either side the legacy
fallback is unchanged.
Fixes #7363
Supersedes #7367
docs(partner): add a FluxA purchase link and tighten its sponsor blurb
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.
fix(claude-desktop): expose the 1M context variant for DeepSeek V4 routes
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.
feat(presets): add FluxA Token Plan partner presets
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.
fix(presets): correct AICodeWith Codex-family endpoint to /v1
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.
fix(ui): render monochrome preset icons in the foreground color
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.
docs(user-manual): point Chat-only Codex examples at plan endpoints
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.
fix(pricing): restore DeepSeek V4 Pro price and seed Qwen3.8 and Hy4 models
- 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).
fix(presets): move MiniMax CN and BaiLing to their current official endpoints
- 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.
feat(codex): refresh CN presets from the Responses API audit
- 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.
test(claude-desktop): run Linux path tests on unix hosts and cover Linux in provider sync tests
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.
fix(ui): stop expanding stale cached usage tiers on ineligible provider cards
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.
fix(skills): 支持 skillId 与目录名不一致的技能安装和更新 (#6381)
* fix(skills): resolve mismatched skillId directories
* fix(skills): preserve source path during updates
* fix(skills): refresh source URL after repository relocation
* test(skills): isolate relocation fixture from legacy home
feat(opencode): 支持从获取的模型列表中搜索并批量添加所选模型 (#7515)
* feat: implement fetched models selection and UI components
* fix(opencode): prevent model search from submitting provider form
Add Kimi Global preset variants (kimi.ai platform + api endpoints) (#7526)
Add a "Kimi Global" twin immediately after the domestic "Kimi" API preset and a "Kimi For Coding Global" twin after the domestic "Kimi For Coding" preset, in every preset file that ships them (claude, claude-desktop, codex, hermes, openclaw, opencode, pi).
"Kimi Global" points to the international open platform https://platform.kimi.ai?aff=cc-switch (apiKeyUrl mirrored as https://platform.kimi.ai/console/api-keys?aff=cc-switch) and swaps the endpoint host api.moonshot.cn for api.moonshot.ai with each file's path unchanged (/v1 or /anthropic).
"Kimi For Coding Global" points to the international site https://www.kimi.ai/code/?aff=cc-switch and swaps the endpoint host api.kimi.com for api.kimi.ai with the path unchanged (e.g. https://api.kimi.ai/coding/v1).
Domestic presets are unchanged; category, icons, apiFormat, model catalogs, endpointCandidates (host swapped), partnerPromotionKey and settings otherwise mirror the domestic blocks exactly.
primePartner stays only on the domestic presets so the new variants do not alter prime-partner ordering.
The Pi twins get distinct providerKey values ("cc-switch-kimi-global", "cc-switch-kimi-for-coding-global") because providerKey is the preset-level primary key and must not collide with the domestic presets.
Rebalance sponsor CTAs around Kimi Code plan with dual-region links (#7522)
Kimi Code plan purchases have resumed, so the README sponsor sections go back to leading with the coding plan instead of the API-only flow written during the purchase pause.
All four locales (EN/ZH/JA/DE) now present Kimi Code plan (中文站 | Global) as the primary CTA and the Kimi Open Platform API (中文站 | Global, with the first-top-up bonus) as the secondary one, mirroring the domestic|global split already used for API links.
The aff and track_id values are unchanged.
fix(prompts): refresh active prompts after external file edits (#7194)
Reload the enabled prompt from its live file when the Prompts list loads, and refresh the open panel when the window regains focus, so external edits to CLAUDE.md / AGENTS.md show up without a restart.
- Keep saved templates available when the live file cannot be read.
- Skip the backfill for empty or whitespace-only files, matching enable_prompt.
- Skip the backfill while a sync/restore holds the sync lock, so a list load cannot write a stale live file into a freshly restored database.
fix(skills): raise archive entry limit so large skill repos install (#7489)
Installing hugohe3/ppt-master from skills.sh failed with
ARCHIVE_TOO_MANY_ENTRIES: the GitHub archive we download is the whole
repository (13,248 entries), and the 10,000-entry cap added in #5811
treated it as a zip bomb.
- Raise MAX_ARCHIVE_ENTRIES to 30,000 for both the remote archive and
local ZIP paths, and align WebDAV's MAX_EXTRACT_ENTRIES so a skills.zip
containing such a skill still restores.
- Charge every file entry at least one 4 KiB block against the existing
512 MiB budget, mirroring directory accounting, so empty-file archives
are bounded by the byte budget rather than by the entry count alone.
- Add tests for a 13k-entry repo archive and for the per-file minimum
charge.
Fixes: https://github.com/farion1231/cc-switch/issues/7475
feat: add MiniMax Code harness support (#7383)
Add MiniMax Code (mcode) as a managed application: provider and model management through the native custom_provider configuration, MCP servers, Skills, global instructions (AGENTS.md), read-only local session history with resume, and usage import from the native runtime database. Native files shared by the TUI and desktop are written under the native lock with rollback when the database commit fails, unrelated native configuration is preserved, and the data directory honors MINIMAX_DATA_DIR and MAVIS_DATA_DIR. Deleting sessions stays in the native application because they are coupled to runtime state.
fix(codex): recover stale account bindings during takeover (#7395)
After deleting a managed ChatGPT account and logging in again, a Codex provider can still reference the old local account ID, which made route switching, takeover, startup restoration and rebinding fail permanently.
Distinguish a missing outgoing account from an existing account with no matching live token. A missing account only releases its ownership marker after the persisted account store is verified; the target write policy stays in force. A stale target now reports an account-selection instruction instead of a bare not-found error, including when takeover is already enabled.
Fixes #7392
feat(claude-desktop): support 3P configuration on Linux (#7331)
Add Claude Desktop 3P configuration support on Linux. Resolve the config root from an absolute XDG_CONFIG_HOME, falling back to ~/.config; when CC Switch itself runs as a Flatpak, use the host ~/.config instead of the sandbox-private XDG_CONFIG_HOME. Update the en/zh/ja user manuals with Linux paths and the Flatpak boundary.
Fixes #4855
fix(proxy): omit missing tool descriptions instead of serializing null (#7319)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): keep reasoning effort for grok-4.6 and pass xhigh through verbatim (#7318)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(models): support Zhipu OpenAI Responses model lists (#7330)
Zhipu's Responses endpoint (open.bigmodel.cn/api/v1) serves its model list in the Codex remote catalog shape (models[].slug) instead of the OpenAI-compatible data[].id shape, so the Codex form's model fetch came back empty. The parser now falls back to models[].slug when data is absent.
fix(misc): fetch npm dist-tags from the dedicated endpoint with the probe timeout (#7346)
The tool version probe requested the full npm packument for each package (metadata for every historical version, 14-25 MB uncompressed for codex / opencode / openclaw) only to read its dist-tags, and it was the only latest-version probe without LATEST_PROBE_TIMEOUT, so it fell back to the proxy client's 600 s total timeout and the cards sat on "loading" for minutes on slow links while the large downloads starved the Hermes GitHub probe into "unknown".
Switch fetch_npm_dist_tags to registry.npmjs.org/-/package/{package}/dist-tags, whose body is the dist-tags object itself (tens of bytes to a few KB), escape scoped names as %2f, and apply the same 15 s probe timeout as the GitHub and PyPI probes. No data is lost: the only consumer reads latest and the tool's prerelease tags.
Fixes #7339
feat(usage): display output tokens per second in request log table and detail panel (#3369)
Show tokens/s next to the output token count in the request log table and the request detail panel.
The generation window prefers an explicit duration when present, otherwise latency minus first-token time for streaming requests, and falls back to full latency for non-streaming requests. Rows without a positive token count or window show nothing.
Closes #5936
fix(presets): update APIKEY.FUN URLs to apikey.fan
docs(release): add v3.20.3 release notes
chore(release): v3.20.3
fix(pricing): move retired DeepSeek V4 family to the V4.1 Flash tier
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.
fix(presets): declare third-party Token Plan DeepSeek V4 rows text-only
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.
feat(presets): rename DouBaoSeed preset to localized Volcengine Doubao
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.
fix(codex): skip unchanged incomplete rollout tails
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.
fix(claude): align quick model selection with form order
fix(tray): show bound ChatGPT account quota for managed Codex cards
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
feat(claude): add "Disable Artifact Tool" quick toggle to provider editor
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.
feat(codex): refresh aggregator model presets and catalogs
feat(codex): switch Kimi presets to native Responses direct-connect
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.
fix(usage): show Claude Fable weekly limit from the usage API limits array
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.
fix(codex): mirror vision-capable DeepSeek catalog and un-gate the legacy v4-flash alias (#7286)
Refresh the bundled DeepSeek official Codex catalog to the vendor's current models.json (slug deepseek-flash, image input modality, supports_image_detail_original) and remove deepseek-v4-flash from the confirmed text-only registry: the legacy id is still accepted by DeepSeek and routed to the vision-capable V4.1 Flash, so both the proxy media sanitizer and the generated Codex catalog must fail open for it. deepseek-v4-pro stays text-only per the vendor catalog.
Fixes #7283
fix(proxy): clamp sub-floor max_tokens to the Responses API minimum (#7287)
The Responses API rejects max_output_tokens below 16, but Anthropic clients legitimately send tiny probe budgets (Claude Desktop's model-availability probe uses max_tokens=1). The Anthropic→Responses conversion copied the value verbatim, so strict upstreams returned 400 and Claude Desktop reported the mapped model as unavailable under local routing.
Clamp 1..=15 up to 16 during conversion; 16+, 0 and non-integer values keep their pass-through semantics. Normal session requests convert byte-for-byte as before, and the Codex OAuth path still strips max_output_tokens afterwards.
Fixes #7103
fix(codex): coalesce adjacent commentary with pending tool calls (#7280)
The Codex Responses -> Chat Completions converter emitted two consecutive
assistant messages when one Responses model turn contained a commentary
message directly followed by function_call items. Chat upstreams imitate
the text-only assistant message as a complete turn and return
finish_reason=stop before the expected tool call, so long agent tasks end
after a progress update.
Merge pending tool calls into the directly adjacent assistant message when
it carries no tool_calls yet, attach pending reasoning per segment so
parallel calls do not duplicate it, and keep the new-message path for every
other boundary shape (user/tool boundaries, prior tool-call batches, media
flushes). The reasoning placeholder backfill still applies to coalesced
calls without reasoning.
Fixes #6529
Supersedes #6530
Co-authored-by: BigStrongSun <BigStrongSun@users.noreply.github.com>
fix(proxy): skip empty reasoning_content placeholders in Anthropic SSE streaming (#7227)
Some OpenAI-compatible upstreams (GLM, Qwen, DeepSeek-V4-Pro via ModelScope/DashScope,
Bailian, JD Cloud, ...) keep a `reasoning_content: ""` placeholder in every content
chunk instead of omitting the field. The reasoning branch of the OpenAI Chat -> Anthropic
SSE converter only checked for presence, and it shares `current_non_tool_block_type`
with the content branch, so every content chunk closed the open text block, opened an
empty thinking block, then reopened a text block. N content chunks became 2N content
blocks, half of them empty thinking blocks, which Claude Code rendered as one line per
token with dozens of empty Thought blocks and bloated the session JSONL.
Filter empty reasoning before entering the branch, matching the `!content.is_empty()`
guard the content branch already had and the non-streaming `openai_to_anthropic`
converter. Two regression tests pin block index, type and order: the placeholder shape
must yield a single text block and no thinking_delta at all, and real reasoning must
still yield one thinking block followed by one text block.
Fixes #5028
Fixes #4404
fix(codex): detect growing rollouts with a persisted byte cursor (#7219)
Codex keeps the rollout append handle open for the whole session, so on Windows NTFS the file mtime does not advance while the file grows and the pure-mtime gate skipped growing rollouts forever. Persist the observed byte length in session_log_sync.last_byte_offset for Codex rows and skip a file only when both mtime and size are unchanged. The parser now splits records on newlines manually so an incomplete trailing record is retried on the next pass without advancing the line cursor.
Fixes #6060
fix(codex): proxy image edits and Images API follow-ups (#7177)
* fix(codex): proxy image edits and Images API follow-ups
Follow-ups to #7036 for Codex's built-in ImageGen over the local proxy:
- Register `/images/edits` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them through the shared Codex standalone passthrough.
ImageGen posts to `images/edits` whenever a call references existing
images, and the router returned an empty 404 for it.
- Add `CodexStandaloneEndpoint::ImagesEdits`. Both Images endpoints share
one full-URL suffix table, so a complete Responses / Compact / Chat
Completions / Images URL derives the sibling endpoint (and vice versa)
while opaque full URLs keep failing closed.
- Apply the standalone sibling rewrite when the base URL is pasted as a
complete endpoint with the full-URL switch off, mirroring
`codex_chat_base_is_full_endpoint`; standalone requests no longer become
`.../chat/completions/images/generations`.
- Parse Images API streaming usage: `image_generation.completed` carries
`usage` on the event top level, so scan for `usage.input_tokens` before
falling back to the Chat Completions stream parser.
Related #7036
* fix(codex): match standalone URL suffixes case-insensitively
Keep endpoint detection and sibling rewriting consistent while preserving the original URL prefix and query. Cover mixed-case Images and Alpha Search endpoints through URL and proxy regression cases.
---------
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): preserve per-app settings on shutdown and port allocation (#7210)
On normal exit, stop_with_restore_keep_state() called the legacy global
proxy-config writer to clear the long-retired live_takeover_active flag.
That writer reads the Claude row and runs an UPDATE without a WHERE clause,
so every shutdown copied Claude's max_retries and the three timeout fields
onto the Codex, Gemini and Grok Build rows — and the UPDATE never carried
live_takeover_active in the first place, so the step had no effect beyond
the clobber. The ephemeral listen-port path did the same on every proxy
start.
Drop the dead write-back (live-config restore, backup cleanup and health
reset are unchanged) and persist the resolved ephemeral port through the
global-config interface, which only touches the shared listen/logging
columns. Two regression tests seed distinct per-app settings for all four
apps and assert they survive both paths.
Fixes #7204
fix(provider): preserve child metadata during universal sync (#7212)
Syncing a universal provider regenerates its Claude/Codex/Gemini child
providers. The child's `settings_config` was merged with the existing row,
but `meta`, `created_at` and `sort_index` were taken from the generated
object, and the universal provider never carries any of them from the UI.
The UPDATE therefore wiped the child's usage script, common-config opt-out,
endpoint auto-select and other per-app settings to `{}` and reset the sort
index to NULL, which pushed the card to the bottom of the list and looked
like the provider had been deleted and re-created.
Keep the existing child's `meta`, `created_at` and `sort_index` when the
row already exists. Name, base URL, key, models, website and notes are
still driven by the universal provider, and first-time creation still
inherits the parent's metadata.
Fixes #7134
feat(presets): rebrand DashScope as 千问AI平台 and refresh Qwen models (#7183)
Rename the domestic Bailian presets to 千问AI平台 with a dedicated icon, refresh the Qwen family to the 3.8 generation across Claude Code, Claude Desktop, Codex, Hermes, OpenClaw, OpenCode and Pi, add the domestic Token Plan presets, restore the international QwenCloud Coding Plan presets, and align OpenClaw/OpenCode/Pi model metadata with the official client docs.
fix(codex): honor proxy URL when model_provider is omitted (#7263)
When a Codex provider card's TOML has no `model_provider`, Codex falls back to
its built-in `openai` provider, but takeover wrote the proxy address as a
top-level `base_url` that Codex never reads, so requests bypassed the local
proxy and went straight to api.openai.com.
Treat the missing selector as the built-in `openai` provider and write the
proxy address to `openai_base_url`; the existing legacy-reroute migration then
turns it into a `cc-switch` custom provider table carrying the `PROXY_MANAGED`
bearer, the same shape every other third-party takeover uses. Explicit
provider selections are unchanged.
Refs #6256, #7217
fix(provider): update MiniMax defaults to M3 and remove expired offers (#7255)
* fix(provider): refresh MiniMax M3 presets and remove expired promotions
* fix(provider): complete MiniMax M3 capability declarations
---------
Co-authored-by: saladday <1203511142@qq.com>
chore(provider): remove Atlas Cloud sponsor status, keep presets
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`
docs(release): add v3.20.2 release notes
chore(release): v3.20.2
feat(provider): add SoleAPI sponsor presets across all nine apps
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.
fix(codex): proxy image edits endpoint
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.
fix(provider): pin models URL for JieKou and Novita Claude presets
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.
feat(pricing): refresh seed for September 2026 vendor price changes
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.
fix(codex): stamp requires_openai_auth on takeover writes to match the live login state
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.
fix(updater): show the real reason when an update check fails (#6482)
The updater plugin rejects with a plain string, so `err instanceof Error`
never matched and the real failure reason was replaced by the generic
fallback message. Use the existing extractErrorMessage helper, and pass
the recovered message into the check-update failure toast so it actually
reaches the user instead of stopping at an unread context state.
Co-authored-by: Jason <farion1231@gmail.com>
docs: fix the locales path in the README directory trees (#6100)
All four READMEs showed locales/ as a direct child of src/, but the
translations live at src/i18n/locales/. Split it into two tree nodes so it
matches how the rest of the tree is drawn. Also fixes a CLaude -> Claude
typo in README_ZH.md.
Co-authored-by: Jason <farion1231@gmail.com>
fix(claude): exclude workflow journal.jsonl from session list
Claude Code's workflow feature writes subagents/workflows/wf_*/journal.jsonl inside the session directory. The Claude session scanner only excluded agent-* files, so each journal parsed as an empty ghost session titled "journal". Exclude journal.jsonl by file name and add regression tests.
Fixes #6042
fix(i18n): add the missing pi.form.providerKeyDuplicate and common.collapse keys
translatePiProviderMutationError returns pi.form.providerKeyDuplicate for the Pi backend's duplicate-key error, but the key existed in none of the four locale files, so the toast showed the raw key. common.collapse (aria-label of the thinking-map toggle) was missing the same way. Add both keys to zh/en/ja/zh-TW and lock them in the locale coverage test.
fix(proxy): stop mask_url panicking on multi-byte UTF-8 near the truncation boundary
mask_url's fallback branch (used when the proxy URL fails Url::parse) sliced the raw string at byte 20 and panicked when a multi-byte UTF-8 character straddled that offset, e.g. a CJK string pasted into the global proxy address. Snap the cut to the nearest char boundary at or before 20 and add a regression test.
fix(tests): isolate LOCALAPPDATA so tests stop writing the real Claude De…
- Rust Provider/UniversalProvider 新增 website_url_2(serde websiteUrl2,skip none)
- SQLite providers 表新增 website_url_2 列(schema v18→v19 幂等迁移 + create_tables 补列 + 旧 config.json 迁移)
- DAO SELECT/INSERT/UPDATE/replace 全链路带出新列
- 表单共享 BasicFormFields + 统一供应商表单第二输入框;zod 同第一链接规则校验与回填
- ProviderCard 主页双链接横排、两空格分隔、逐链接 openExternal 打开默认浏览器
- 搜索匹配、复制供应商、统一供应商生成 claude/codex/gemini 随行透传
- gemini_auth packycode 认证检测纳入 website_url_2;ClaudeDesktop/GrokBuild 预设切换清空第二链接;卡片同值链接去重
- 4 语言 i18n + 前端/前端单测覆盖
Comet change dual-website-links-homepage(19/19 验收通过,归档)
feat(connectivity-test): 拉取模型列表改为「搜索添加」选择器交互
- 新增共享组件 SearchableModelMultiPicker:搜索过滤 + 多选 + 已在列表标记 + 已选计数
- ConnectivityTestDialog 获取模型列表后不再全量追加合并去重,改为会话内缓存完整列表并自动弹出一次选择器;用户搜索挑选后添加进表格并自动勾选
- 重新拉取只刷新缓存,已添加进表格的行保留;拉取结果仍仅会话内有效,不写回供应商配置
- 四语言 i18n 新增 searchAdd / addToTest / alreadyInList / selectedCount
- 同步更新 ConnectivityTestDialog 测试并新增 SearchableModelMultiPicker 测试
feat(connectivity-test): 探测目标按供应商 API 格式对齐真实转发链路
- build_claude 按 get_claude_api_format 分派端点与请求体:openai_chat → /v1/chat/completions(复用 transform_claude_request_for_api_format,流式注入 stream_options.include_usage)、openai_responses → /v1/responses、gemini_native → /v1beta/models/{model}:{method}(resolve_gemini_native_url)、anthropic 保持 /v1/messages 并补 anthropic-version/beta 头
- build_codex 按 should_convert_codex_responses_to_chat/anthropic 分派:chat → /chat/completions(responses_to_chat_completions_with_reasoning)、anthropic → /v1/messages(responses_request_to_anthropic,含 max_output_tokens 注入与 [1m] 剥离 + context-1m beta 头)
- 鉴权改用 ClaudeAdapter/CodexAdapter 的 extract_auth + get_auth_headers(与真实出站请求同源:AUTH_TOKEN→Bearer、ANTHROPIC_API_KEY→x-api-key、gemini→x-goog-api-key、GeminiCli OAuth→Bearer、OpenRouter→Bearer、Codex 按 apiKeyField)
- 转换前剥离 [1m] 标记;meta.isFullUrl 或 base 已是完整端点时直接使用 base_url;自定义 body 浅合并移至转换后的最终协议体
- 新增 18 个目标构造断言单测;Comet 归档产物同步提交
chore: archive connectivity-test-optimizations
feat(connectivity-test): 单一模型表格 + 实时拉取模型 + 勾选即测试范围 + 新默认提示词
- 弹窗合并为单一模型表格:打开即全部模型行(初始待测试)、行勾选 + 表头全选(含半选态)、5 格统计卡,移除上方独立选择区
- 获取模型列表:凭据提取抽为共享模块 src/utils/providerCredentials.ts(ModelQuickSwitchDialog 同步改引用),复用 fetch_models_for_config 拉取 /v1/models,按 ID 追加合并去重、仅会话内有效
- 默认提示词改为「你好,你可以帮我做什么事情」:前后端常量同步,merge 差异判断基准跟随
- 勾选即测试范围:后端 connectivity_test_provider_models 新增 modelIds 参数(None/空白回退静态清单),前端逐模型独立 invoke 并行 + 行级流式更新
- 一轮测完自动勾选失败项,全部成功保持原勾选
fix(connectivity-test): UI 四项修复
- 弹窗抬至 alert 层(z-[60]):打开连通性测试时 header 切换栏正确变灰,
详情子弹窗同步抬层(同 z + 更晚挂载 portal 在上)
- 批量检测入口移至 header 图标组(状态提升到 App,ProviderList 经
props 读取探针结果),不再另起一行
- 批量检测可停止:runId 代次守卫(Tauri invoke 无法真正取消),再次
点击即清除 waiting/running 徽标、丢弃在途迟到写入、不再拉起排队项
- 弹窗打开自动勾选模型:已保存默认测试模型 → 供应商当前模型
(claude env.ANTHROPIC_MODEL / codex config.toml model,新增
currentProviderModelId 与后端同口径)→ 清单首个
前端全量 141 文件 / 1106 测试全绿;typecheck clean
chore: archive per-model-connectivity-test
fix(connectivity-test): shouldShowTestEntry 增判 providerType + 收尾清理
chore(connectivity-test): 勾选 Task 9 / OpenSpec 5.1-5.2 + verify 报告
chore(connectivity-test): 勾选 Task 8 / OpenSpec 4.1-4.3
refactor(connectivity-test): 移除旧 stream_check 可达性探测全链路(保留表结构)
- 删除 6 个文件:commands/stream_check.rs、services/stream_check.rs、
database/dao/stream_check.rs、hooks/useStreamCheck.ts、lib/api/connectivity-check.ts、
components/usage/ConnectivityCheckConfigPanel.tsx
- 移除 mod.rs 声明、lib.rs generate_handler! 注册、cleanup_old_stream_check_logs 调用
- 从 SYNC_SKIP_TABLES / SYNC_PRESERVE_TABLES 移除 stream_check_logs(表结构保留在 schema.rs)
- 更新 backup.rs 测试:移除 stream_check_logs fixture 与断言
- 更新 SettingsPage:移除 ConnectivityCheckConfigPanel AccordionItem 与 FlaskConical 导入
- i18n:删除 streamCheck.* 命名空间(zh/en/ja/zh-TW 四语言)
- 更新注释:移除 stream_check 旧引用(forwarder.rs、provider.rs、userAgentPresets.ts、
connectivity_test.rs)
- 修复 4 个 pre-existing clippy lint(connectivity_test: matches! 宏、unused_unit、
derive Default)
- 移除 ProviderList.test.tsx 中已失效的 useStreamCheck mock
chore(connectivity-test): 勾选 Task 7 / OpenSpec 2.4 3.1-3.3
feat(connectivity-test): 供应商列表集成(弹窗入口 + 批量探针徽标)
chore(connectivity-test): 勾选 Task 6 / OpenSpec 2.2-2.3
fix(connectivity-test): 模型列表解析与后端对齐(每条目首个匹配,提取共享 helper)
feat(connectivity-test): 连通性测试弹窗/详情/徽标组件与四语言文案
chore(connectivity-test): 勾选 Task 5 / OpenSpec 2.1
test(connectivity-test): 锁定 toBackendParams 字段映射(customHeaders/customBody 等)
feat(connectivity-test): 前端命令绑定与测试/探针状态 hooks
chore(connectivity-test): 勾选 Task 4 / OpenSpec 1.4
feat(connectivity-test): 测试参数持久化纯函数(缺省即默认值)
chore(connectivity-test): 勾选 Task 3 / OpenSpec 1.3
feat(connectivity-test): Tauri 命令与注册,官方/动态端点过滤,sanitize 隔离私有字段
chore(connectivity-test): 勾选 Task 2 / OpenSpec 1.2
feat(connectivity-test): 后端直连执行器(reqwest+SSE,总/空闲超时,度量与错误识别)
feat(connectivity-test): 后端直连执行器(reqwest+SSE,总/空闲超时,度量与错误识别)
chore(connectivity-test): 勾选 Task 1 / OpenSpec 1.1
fix(connectivity-test): claude 鉴权对齐 adapter 顶层 apiKey/api_key 兜底
feat(connectivity-test): 新增后端连通性测试服务类型/模型解析/协议构造
chore: archive model-quick-switch-picker-scroll
chore: model-quick-switch-picker-scroll 验证报告与状态元数据
fix: 模型快捷切换弹窗内模型列表滚轮无法滚动
模态 Dialog 的 RemoveScroll 会对 shards 外的 portaled PopoverContent
wheel 事件无条件 preventDefault。在 SearchableModelPicker 的
PopoverContent 捕获阶段阻断 wheel 传播,恢复原生滚动。
chore: bump fork version to 3.20.1-1 after upstream v3.20.1 rebase
fix(form): only auto-expand Claude advanced options on divergent model mapping
Uniform mapping produced by the quick-set button (all roles equal to the
fallback model) no longer counts as an advanced value, so the advanced
section stays collapsed on edit. A 1M marker is stripped before comparing
so marked roles are not misjudged as divergent. Truly customized mappings
still auto-expand the section.
feat: align fallback-model quick-set buttons with model input right edge
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
docs: update README with fork build notes and quick-move provider changes
chore: archive comet-native claude-model-quick-access
feat: surface Claude fallback model as quick-access section below endpoint
Move the default fallback model (ANTHROPIC_MODEL) block out of the
collapsed Advanced Options into a quick-access section directly below
the request endpoint: input + 1M checkbox + quick-set + fetch-models
buttons are all reachable without expanding.
- header toggle-all "declare 1M" now includes the fallback model
(Haiku still excluded); partial selection shows indeterminate state
- drop the duplicated quick-set/fetch buttons and the old fallback
block inside Advanced Options
- exclude claudeModel from hasAnyAdvancedValue so a fallback-only
value no longer auto-expands Advanced Options
- quick-set priority, env write semantics, and OAuth preset branches
unchanged; add 4 component tests covering the new layout
chore: archive comet-native provider-right-click-top-bottom
Enable native workflow in .comet/config.yaml and move the Comet managed
state block in .gitignore so the exclusion rules stay grouped with the
other fork tool artifacts.
feat: add provider right-click move-to-top/bottom and insert new providers at second position
- ProviderList: context menu with quick move-to-top/move-to-bottom, reusing
update_providers_sort_order and refreshing tray menu like drag sort
- useAddProviderMutation: default insert at index 1 with explicit sortIndex
materialization for existing items (avoids defined-first sort trap)
- i18n: quickMove* strings for zh/en/zh-TW/ja
ci: stop flaky WSL2 linker job from blocking main
The backend-windows-wsl2 job intermittently fails with LNK1327 /
mt.exe c1010070 when the Windows runner's mt.exe cannot parse a
manifest temp file on the WSL2 UNC TEMP path. The same flaky failure
occurs upstream and is unrelated to fork changes. Mark the job
continue-on-error so main stops going red on it; the result stays
visible in the Actions UI.
style: apply prettier to files touched by recent comet archives
Frontend CI format:check failed on 6 files modified by the app-name
and model-quick-switch archive commits plus the updater-disable commit.
Run prettier --write so format:check passes again.
chore: archive ccs-fork-app-name-cc-switch
chore: archive ccs-model-quick-switch-ui-polish
docs: rewrite README to document fork differences vs upstream
README 从上游完整功能介绍改为仅记录 fork 与上游的差别,完整功能
文档指回上游。涵盖 8 项 fork 魔改:构建标识/开发预览、预设精简、
Claude 表单调整、模型搜索移植、主页模型快速切换、侧边栏面板显隐、
关闭应用内更新、打包范围,并说明版本号与 rebase 同步流程。
feat: disable in-app updater, point users to GitHub releases
移除 tauri-plugin-updater 依赖与自动更新链路,改为统一引导用户到
GitHub Releases 手动下载新版。
- 前端:updater.ts 恒返回 up-to-date;UpdateContext 取消启动自检;
AboutSection「检查更新」直接打开 fork releases 页;DatabaseUpgrade
恢复界面去掉下载进度条,改为打开发布页。
- 后端:移除 updater 插件注册与 UpdaterExt 调用;
install_update_and_restart 降级为空壳,check_app_update_available
返回当前版本号;check_for_updates 指向 fork releases;capabilities
去掉 updater:default 权限。
- 打包:tauri.conf.json 删 plugins.updater、createUpdaterArtifacts=false;
Cargo.toml / package.json 删 updater 依赖,Cargo.lock / pnpm-lock
同步重生成。
- CI:release.yml 移除签名收集与 assemble-latest-json job,删除
.tar.gz/.sig 处理,release body 去掉 updater 说明。
版本号统一为 3.20.0-1(合法 semver 且 MSI 第 4 位为 1)。
Cargo.lock / pnpm-lock.yaml 已随依赖移除重生成,cargo check 通过。
ci: build only Windows x64 and macOS arm64, unsigned
feat: gate dev panel behind dev-only env flag
chore(comet): archive ccs-sidebar-panels-visibility change
fix: apply skills visibility guard to hermes branch toolbar button
feat: include prompts in sidebar panel visibility settings
fix: persist visibleSidebarPanels setting in backend AppSettings
style: use compact pill toggles for sidebar panel visibility
feat: add sidebar panel visibility switches to settings
feat: add sidebar panel visibility settings and wire into toolbar buttons
chore(comet): archive ccs-home-model-display change
fix(fork): read grokbuild api key from config TOML in quick switch dialog
feat(fork): align model badge with upstream extractModelBadge
feat(fork): show current model badge and quick-switch entry on provider cards
feat(fork): add model quick switch dialog
feat(fork): add provider model read/write utils with tests
chore(comet): archive ccs-model-search-port change
feat(i18n): add model search picker keys for all locales
feat(form): port SearchableModelPicker from upstream for searchable model selection
chore(comet): archive ccs-claude-form-layout change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-claude-form-layout/
- Sync claude-form-layout spec to main specs
- Annotate design doc as archived
- Add verification report
fix(i18n): update advanced options hint, add ja/zh-TW toggle-all, drop dead apiFormatHint key
fix(form): correct 1M toggle-all alignment direction (ml-1)
fix(form): align 1M toggle-all checkbox with row checkboxes
refactor(form): move api format select into EndpointField rightSlot
fix(form): widen api format select to fit option text
feat(form): add 1M toggle-all checkbox in model mapping header
feat(form): move api format select next to endpoint input
chore(comet): archive ccs-provider-presets-trim change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-provider-presets-trim/
- Sync provider-presets-visibility spec to main specs
- Annotate design doc as archived
- Add verification report
docs(fork): add presets-trim files to rebase whitelist
test(fork): mock IS_FORK_BUILD false in unit tests for upstream-form semantics
feat(fork): wire official preset filter into provider forms
feat(fork): add hidden field to preset interfaces
feat(fork): add official preset allowlist and filter util
chore(comet): archive ccs-fork-scaffolding change
- Move change artifacts to docs/openspec/changes/archive/2026-08-18-ccs-fork-scaffolding/
- Sync fork-build-identity spec to main specs
- Annotate design doc as archived
- Add verification report
feat(fork): add dev config with isolated identifier and config dir
docs(fork): complete rebase whitelist with all fork-touched files
fix(fork): guard setTitle with isTauri in test/non-tauri env
fix(fork): define __CCS_FORK_BUILD__ in vitest config
feat(fork): add DevPanel and settings footer entry
feat(fork): set macOS window title to CC Switch (Fork) on startup
feat(fork): inject __CCS_FORK_BUILD__ compile-time constant
feat(fork): bump version to 3.19.2-fork.1 and rename product to CC Switch (Fork)
docs(fork): add upstream rebase workflow and ignore tool artifacts
fix(proxy): accept the whole grok-4.x (x>=5) family in the reasoning-effort whitelist (#7369)
Replace the verbatim grok-4.5 / grok-4.6 entries in supports_reasoning_effort with a rule that parses the grok-4.x minor version and accepts x >= 5, mirroring the existing GPT-5+ rule. This covers grok-4.7 (released 2026-09-21), whose reasoning effort was previously dropped on the Claude -> Chat, Claude -> Responses and Codex Responses -> Chat conversion paths, and lets future releases pass without another whitelist edit. The grok-build-* family is retained for saved providers.
Co-authored-by: allenxu09 <171831965+allenxu09@users.noreply.github.com>
docs(release): add v3.20.4 release notes
chore(release): v3.20.4
feat(pricing): add Grok 4.7 and GLM-5.3-FlashX
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.
fix(i18n): remove outdated OpenCode Go referral offer
fix: add SudoCode.chat alternate API endpoint
feat(settings): add a GitHub star prompt to the About card
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
feat(presets): add Soshow aggregator presets
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.
fix(ci): stabilize frontend tests and verify renderer build
fix(mcode): refresh provider state after removal (#7578)
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
fix(lib): reset skip_taskbar before window.show() across entry points (#6348)
5处 window.show() 调用中 4处没有在 show() 前重置 Windows 的 skip_taskbar,导致以下场景出现窗口可见但不在任务栏、点最小化后窗口消失的 bug:
- single_instance handler (lib.rs:360-361):当原进程因 silent_startup / 关闭到托盘等处于 skip_taskbar(true) 状态时,从快捷方式/开始菜单唤起已运行实例触发。
- handle_deeplink_url 的 focus_main_window 分支 (lib.rs:256):通过 ccswitch:// URL 唤起时。
- DB 恢复流程 (lib.rs:556):数据库版本不匹配时显示恢复界面。
- RunEvent::Opened 处理器 (lib.rs:1803):系统接收到 deep-link URL 时。
修复后的行为与 tray.rs::handle_tray_menu_event::show_main 分支一致(那里已有相同的 set_skip_taskbar(false) 调用)。
经用户测试验证:autostart + silent_startup + 从开始菜单/快捷方式启动的场景下,最小化按钮恢复正常工作。
fix(proxy): omit missing tool descriptions in the Codex responses-to-chat converter (#7378)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): preserve max effort for GPT-5.6 and GPT-6 Astra (#7531)
fix(misc): ignore shell startup output when probing tool versions in WSL (#7348)
try_get_version_wsl runs "${SHELL:-sh}" -lic '<tool> --version' through
wsl.exe and takes the first x.y.z anywhere in stdout. A login+interactive
shell runs the distro's startup files first, so their output shares that
stdout. On Ubuntu, /etc/profile.d/update-motd.sh prints the MOTD once a day,
starting with "Welcome to Ubuntu 24.04.4 LTS", and the card then showed
24.04.4 as the Claude Code version. The once-a-day stamp makes it appear
after the first probe of the day and vanish on refresh.
The payload now prints a sentinel before running the tool, and parsing looks
only at what follows its last occurrence. The last occurrence handles the
-lic || -lc || -c fallback chain printing it several times; matching it as a
substring handles OSC sequences glued in front of it. If the sentinel is
absent (the shell failed before the payload ran), the output is used as
before. The stderr fallback for tools that print their version there is kept.
Fixes #7347
Co-authored-by: lyfuci <lyfuci11@gmail.com>
fix(claude): hide session URL attribution (#7053)
* fix(claude): hide session URL attribution
* docs(claude): document session URL attribution
fix(ui): reset provider scroll when switching apps (#5211)
fix(pi): fix provider logo (#6826)
fix(i18n): use the Pi form's own term for the zh-TW duplicate-key message (#7187)
Follow-up to the nit on #6768. In zh-TW the Pi form labels the field
供應商識別碼 and says 供應商識別碼為必填 when it is empty, but the
duplicate message I added said 供應商標識 — the term Hermes, OpenCode and
OpenClaw use for their own field. One form, two names for the same thing.
The other three locales already keep the label and both messages on one
term, so this makes zh-TW match the pattern rather than introduce one:
en Provider key / Provider key is required / This provider key already exists
ja プロバイダーキー / プロバイダーキーを入力してください / このプロバイダーキーは既に存在します
zh 供应商标识 / 供应商标识不能为空 / 该供应商标识已存在
zh-TW 供應商識別碼 / 供應商識別碼為必填 / 該供應商標識已存在 <- the odd one out
hermes.form.providerKeyDuplicate carries the same sentence and is left
alone: its own label is 供應商標識, so it is already consistent.
Verified: managementListLocales 7/7. Full unit suite 1085/1087, the same
two App.test.tsx integration cases failing before and after.
fix(proxy): strip stop param for GitHub Copilot to unblock auto mode classifier (#5404)
Claude Code's auto mode classifier sends `stop_sequences` on every
safety check. On the GitHub Copilot path the Anthropic -> OpenAI Chat
conversion mapped it to `stop`, and Copilot's Chat Completions route
for Claude models answers that request with HTTP 400, so every
classifier call failed while normal chat kept working.
- Remove the converted `stop` field only when the provider is GitHub
Copilot; other OpenAI-compatible upstreams keep it.
- Copilot detection in the transform now also covers providers that
are recognised only through `env.ANTHROPIC_BASE_URL`.
- Regression tests for the Copilot (meta and env URL) and generic
provider cases.
Fixes #5175
fix(codex): keep stored auth when the live snapshot has no credential (#7434)
Editing the current Codex provider seeds the form from the live ~/.codex
snapshot, and switching away backfills that snapshot into the DB row. When a
provider table declares its own credential source (an Authorization header,
env_key, auth/aws), no bearer token is injected into config.toml and the
shared auth.json is deleted on third-party switches, so the live snapshot is
permanently `{ auth: {}, config }` while auth.OPENAI_API_KEY in the DB is the
only copy of the key. Both paths treated that empty live auth as
authoritative and silently erased the stored key, which in turn made the
usage-script modal misread the provider as an official subscription and
reset the saved script.
- Treat a credential-less live auth (missing auth.json or the bare
auth_mode logout marker) as an absent field in both the edit dialog and
the switch-away backfill, keeping the stored auth template. Live still
wins whenever it carries material, and official providers keep the live
login as their source of truth.
- Gate the official exemption on is_codex_official_provider /
resolveCodexOfficialIdentity rather than the category alone: a legacy
fixed official card without a persisted category is still written as
official, so the fallback must not resurrect a logged-out login on
switch-back.
- Cover both layers with regression tests, including the category-less
official shapes.
Fixes #7433
Supersedes #7492
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): lift additional_tools input carriers on the Codex chat conversion (#7454)
Codex talks to third-party providers over its Responses Lite transport,
which ships the request's extra tools in an `additional_tools` input
carrier: `{"type":"additional_tools","role":"developer","tools":[...]}`,
a role but no `content`. The Responses->Chat conversion let that carrier
fall through the generic "item with a role is a message" arm and emitted
`{"role":"system","content":null}` at messages[1], which strict Chat
gateways reject with `messages[1]: missing field "content"` (or
`data did not match any variant of ...SystemMessageContent`). The tools
carried inside it were also dropped on the Chat and Anthropic conversions.
- Skip `additional_tools` items in the Chat message walk; a carrier
declares tools, it is not a message.
- Collect carrier tools through the existing CodexToolContext registry
(dedup, namespace flattening, custom-tool conversion), which also feeds
the Anthropic conversion and the response-side name round-trip.
- Requests without a carrier convert byte-for-byte as before.
Known boundary: `custom` children inside a namespace tool
(`functions.exec`) are still not lifted; that needs its own response-side
name round-trip and is left for a follow-up.
Diagnosed independently in #6158 by BigStrongSun (fix in #6159) and in
fixture.
Fixes #7451
Fixes #6158
Refs #6033
Refs #6776
Supersedes #6159
fix(proxy): normalize Codex detail:original images for Chat gateways (#7476)
Codex marks tool-returned images with detail:"original" whenever the model
catalog advertises supports_image_detail_original, a Responses-only value.
The Responses->Chat conversion copied it verbatim into image_url.detail, and
strict OpenAI-compatible Chat gateways (Ark/Doubao, SenseNova, GLM, vLLM, ...)
reject the whole request with 400. Because Codex replays the full rollout on
every turn, one poisoned view_image result made the session fail permanently,
including plain-text turns.
Two layers:
- tool_media::merge_top_level_detail downgrades original -> auto, so already
poisoned rollouts and Codex versions before 0.155.0 (which do not normalize
replayed history themselves) recover immediately. auto/low/high are untouched.
- codex_catalog_model_entry always writes supports_image_detail_original:false
on the ProxyChat profile, so Codex stops emitting original at the source once
the catalog is regenerated. NativeResponses/Anthropic profiles and the DeepSeek
official catalog are unaffected.
Top-level user images were never affected: responses_content_to_chat_content
drops the part-level detail entirely.
Fixes #6495
Fixes #7458
Refs #6569
Refs #5805
fix(omo): detect unified config on the WSL side when the OpenCode dir is in WSL (#7550)
When the OpenCode config directory points into WSL
(\\wsl.localhost\<distro>\home\<user>\.config\opencode), OMO >= 4.19.3 keeps
its unified config in the WSL-side home (~/.omo/omo.jsonc). Detection only
probed the Windows home, never found it, and silently fell back to the legacy
per-plugin file that OMO no longer reads, so provider switches looked
successful but had no effect.
- Add derive_wsl_home_dir to derive the WSL-side home from a WSL UNC path
(home/<user> or root); non-WSL and non-home paths return None.
- Probe a list of home candidates for the unified config, WSL-side home
first and the OS home as a fallback, shared by the read, write and delete
paths.
- Detection only: when no unified config exists on either side the legacy
fallback is unchanged.
Fixes #7363
Supersedes #7367
docs(partner): add a FluxA purchase link and tighten its sponsor blurb
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.
fix(claude-desktop): expose the 1M context variant for DeepSeek V4 routes
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.
feat(presets): add FluxA Token Plan partner presets
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.
fix(presets): correct AICodeWith Codex-family endpoint to /v1
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.
fix(ui): render monochrome preset icons in the foreground color
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.
docs(user-manual): point Chat-only Codex examples at plan endpoints
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.
fix(pricing): restore DeepSeek V4 Pro price and seed Qwen3.8 and Hy4 models
- 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).
fix(presets): move MiniMax CN and BaiLing to their current official endpoints
- 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.
feat(codex): refresh CN presets from the Responses API audit
- 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.
test(claude-desktop): run Linux path tests on unix hosts and cover Linux in provider sync tests
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.
fix(ui): stop expanding stale cached usage tiers on ineligible provider cards
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.
fix(skills): 支持 skillId 与目录名不一致的技能安装和更新 (#6381)
* fix(skills): resolve mismatched skillId directories
* fix(skills): preserve source path during updates
* fix(skills): refresh source URL after repository relocation
* test(skills): isolate relocation fixture from legacy home
feat(opencode): 支持从获取的模型列表中搜索并批量添加所选模型 (#7515)
* feat: implement fetched models selection and UI components
* fix(opencode): prevent model search from submitting provider form
Add Kimi Global preset variants (kimi.ai platform + api endpoints) (#7526)
Add a "Kimi Global" twin immediately after the domestic "Kimi" API preset and a "Kimi For Coding Global" twin after the domestic "Kimi For Coding" preset, in every preset file that ships them (claude, claude-desktop, codex, hermes, openclaw, opencode, pi).
"Kimi Global" points to the international open platform https://platform.kimi.ai?aff=cc-switch (apiKeyUrl mirrored as https://platform.kimi.ai/console/api-keys?aff=cc-switch) and swaps the endpoint host api.moonshot.cn for api.moonshot.ai with each file's path unchanged (/v1 or /anthropic).
"Kimi For Coding Global" points to the international site https://www.kimi.ai/code/?aff=cc-switch and swaps the endpoint host api.kimi.com for api.kimi.ai with the path unchanged (e.g. https://api.kimi.ai/coding/v1).
Domestic presets are unchanged; category, icons, apiFormat, model catalogs, endpointCandidates (host swapped), partnerPromotionKey and settings otherwise mirror the domestic blocks exactly.
primePartner stays only on the domestic presets so the new variants do not alter prime-partner ordering.
The Pi twins get distinct providerKey values ("cc-switch-kimi-global", "cc-switch-kimi-for-coding-global") because providerKey is the preset-level primary key and must not collide with the domestic presets.
Rebalance sponsor CTAs around Kimi Code plan with dual-region links (#7522)
Kimi Code plan purchases have resumed, so the README sponsor sections go back to leading with the coding plan instead of the API-only flow written during the purchase pause.
All four locales (EN/ZH/JA/DE) now present Kimi Code plan (中文站 | Global) as the primary CTA and the Kimi Open Platform API (中文站 | Global, with the first-top-up bonus) as the secondary one, mirroring the domestic|global split already used for API links.
The aff and track_id values are unchanged.
fix(prompts): refresh active prompts after external file edits (#7194)
Reload the enabled prompt from its live file when the Prompts list loads, and refresh the open panel when the window regains focus, so external edits to CLAUDE.md / AGENTS.md show up without a restart.
- Keep saved templates available when the live file cannot be read.
- Skip the backfill for empty or whitespace-only files, matching enable_prompt.
- Skip the backfill while a sync/restore holds the sync lock, so a list load cannot write a stale live file into a freshly restored database.
fix(skills): raise archive entry limit so large skill repos install (#7489)
Installing hugohe3/ppt-master from skills.sh failed with
ARCHIVE_TOO_MANY_ENTRIES: the GitHub archive we download is the whole
repository (13,248 entries), and the 10,000-entry cap added in #5811
treated it as a zip bomb.
- Raise MAX_ARCHIVE_ENTRIES to 30,000 for both the remote archive and
local ZIP paths, and align WebDAV's MAX_EXTRACT_ENTRIES so a skills.zip
containing such a skill still restores.
- Charge every file entry at least one 4 KiB block against the existing
512 MiB budget, mirroring directory accounting, so empty-file archives
are bounded by the byte budget rather than by the entry count alone.
- Add tests for a 13k-entry repo archive and for the per-file minimum
charge.
Fixes: https://github.com/farion1231/cc-switch/issues/7475
feat: add MiniMax Code harness support (#7383)
Add MiniMax Code (mcode) as a managed application: provider and model management through the native custom_provider configuration, MCP servers, Skills, global instructions (AGENTS.md), read-only local session history with resume, and usage import from the native runtime database. Native files shared by the TUI and desktop are written under the native lock with rollback when the database commit fails, unrelated native configuration is preserved, and the data directory honors MINIMAX_DATA_DIR and MAVIS_DATA_DIR. Deleting sessions stays in the native application because they are coupled to runtime state.
fix(codex): recover stale account bindings during takeover (#7395)
After deleting a managed ChatGPT account and logging in again, a Codex provider can still reference the old local account ID, which made route switching, takeover, startup restoration and rebinding fail permanently.
Distinguish a missing outgoing account from an existing account with no matching live token. A missing account only releases its ownership marker after the persisted account store is verified; the target write policy stays in force. A stale target now reports an account-selection instruction instead of a bare not-found error, including when takeover is already enabled.
Fixes #7392
feat(claude-desktop): support 3P configuration on Linux (#7331)
Add Claude Desktop 3P configuration support on Linux. Resolve the config root from an absolute XDG_CONFIG_HOME, falling back to ~/.config; when CC Switch itself runs as a Flatpak, use the host ~/.config instead of the sandbox-private XDG_CONFIG_HOME. Update the en/zh/ja user manuals with Linux paths and the Flatpak boundary.
Fixes #4855
fix(proxy): omit missing tool descriptions instead of serializing null (#7319)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): keep reasoning effort for grok-4.6 and pass xhigh through verbatim (#7318)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(models): support Zhipu OpenAI Responses model lists (#7330)
Zhipu's Responses endpoint (open.bigmodel.cn/api/v1) serves its model list in the Codex remote catalog shape (models[].slug) instead of the OpenAI-compatible data[].id shape, so the Codex form's model fetch came back empty. The parser now falls back to models[].slug when data is absent.
fix(misc): fetch npm dist-tags from the dedicated endpoint with the probe timeout (#7346)
The tool version probe requested the full npm packument for each package (metadata for every historical version, 14-25 MB uncompressed for codex / opencode / openclaw) only to read its dist-tags, and it was the only latest-version probe without LATEST_PROBE_TIMEOUT, so it fell back to the proxy client's 600 s total timeout and the cards sat on "loading" for minutes on slow links while the large downloads starved the Hermes GitHub probe into "unknown".
Switch fetch_npm_dist_tags to registry.npmjs.org/-/package/{package}/dist-tags, whose body is the dist-tags object itself (tens of bytes to a few KB), escape scoped names as %2f, and apply the same 15 s probe timeout as the GitHub and PyPI probes. No data is lost: the only consumer reads latest and the tool's prerelease tags.
Fixes #7339
feat(usage): display output tokens per second in request log table and detail panel (#3369)
Show tokens/s next to the output token count in the request log table and the request detail panel.
The generation window prefers an explicit duration when present, otherwise latency minus first-token time for streaming requests, and falls back to full latency for non-streaming requests. Rows without a positive token count or window show nothing.
Closes #5936
fix(presets): update APIKEY.FUN URLs to apikey.fan
docs(release): add v3.20.3 release notes
chore(release): v3.20.3
fix(pricing): move retired DeepSeek V4 family to the V4.1 Flash tier
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.
fix(presets): declare third-party Token Plan DeepSeek V4 rows text-only
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.
feat(presets): rename DouBaoSeed preset to localized Volcengine Doubao
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.
fix(codex): skip unchanged incomplete rollout tails
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.
fix(claude): align quick model selection with form order
fix(tray): show bound ChatGPT account quota for managed Codex cards
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
feat(claude): add "Disable Artifact Tool" quick toggle to provider editor
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.
feat(codex): refresh aggregator model presets and catalogs
feat(codex): switch Kimi presets to native Responses direct-connect
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.
fix(usage): show Claude Fable weekly limit from the usage API limits array
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.
fix(codex): mirror vision-capable DeepSeek catalog and un-gate the legacy v4-flash alias (#7286)
Refresh the bundled DeepSeek official Codex catalog to the vendor's current models.json (slug deepseek-flash, image input modality, supports_image_detail_original) and remove deepseek-v4-flash from the confirmed text-only registry: the legacy id is still accepted by DeepSeek and routed to the vision-capable V4.1 Flash, so both the proxy media sanitizer and the generated Codex catalog must fail open for it. deepseek-v4-pro stays text-only per the vendor catalog.
Fixes #7283
fix(proxy): clamp sub-floor max_tokens to the Responses API minimum (#7287)
The Responses API rejects max_output_tokens below 16, but Anthropic clients legitimately send tiny probe budgets (Claude Desktop's model-availability probe uses max_tokens=1). The Anthropic→Responses conversion copied the value verbatim, so strict upstreams returned 400 and Claude Desktop reported the mapped model as unavailable under local routing.
Clamp 1..=15 up to 16 during conversion; 16+, 0 and non-integer values keep their pass-through semantics. Normal session requests convert byte-for-byte as before, and the Codex OAuth path still strips max_output_tokens afterwards.
Fixes #7103
fix(codex): coalesce adjacent commentary with pending tool calls (#7280)
The Codex Responses -> Chat Completions converter emitted two consecutive
assistant messages when one Responses model turn contained a commentary
message directly followed by function_call items. Chat upstreams imitate
the text-only assistant message as a complete turn and return
finish_reason=stop before the expected tool call, so long agent tasks end
after a progress update.
Merge pending tool calls into the directly adjacent assistant message when
it carries no tool_calls yet, attach pending reasoning per segment so
parallel calls do not duplicate it, and keep the new-message path for every
other boundary shape (user/tool boundaries, prior tool-call batches, media
flushes). The reasoning placeholder backfill still applies to coalesced
calls without reasoning.
Fixes #6529
Supersedes #6530
Co-authored-by: BigStrongSun <BigStrongSun@users.noreply.github.com>
fix(proxy): skip empty reasoning_content placeholders in Anthropic SSE streaming (#7227)
Some OpenAI-compatible upstreams (GLM, Qwen, DeepSeek-V4-Pro via ModelScope/DashScope,
Bailian, JD Cloud, ...) keep a `reasoning_content: ""` placeholder in every content
chunk instead of omitting the field. The reasoning branch of the OpenAI Chat -> Anthropic
SSE converter only checked for presence, and it shares `current_non_tool_block_type`
with the content branch, so every content chunk closed the open text block, opened an
empty thinking block, then reopened a text block. N content chunks became 2N content
blocks, half of them empty thinking blocks, which Claude Code rendered as one line per
token with dozens of empty Thought blocks and bloated the session JSONL.
Filter empty reasoning before entering the branch, matching the `!content.is_empty()`
guard the content branch already had and the non-streaming `openai_to_anthropic`
converter. Two regression tests pin block index, type and order: the placeholder shape
must yield a single text block and no thinking_delta at all, and real reasoning must
still yield one thinking block followed by one text block.
Fixes #5028
Fixes #4404
fix(codex): detect growing rollouts with a persisted byte cursor (#7219)
Codex keeps the rollout append handle open for the whole session, so on Windows NTFS the file mtime does not advance while the file grows and the pure-mtime gate skipped growing rollouts forever. Persist the observed byte length in session_log_sync.last_byte_offset for Codex rows and skip a file only when both mtime and size are unchanged. The parser now splits records on newlines manually so an incomplete trailing record is retried on the next pass without advancing the line cursor.
Fixes #6060
fix(codex): proxy image edits and Images API follow-ups (#7177)
* fix(codex): proxy image edits and Images API follow-ups
Follow-ups to #7036 for Codex's built-in ImageGen over the local proxy:
- Register `/images/edits` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them through the shared Codex standalone passthrough.
ImageGen posts to `images/edits` whenever a call references existing
images, and the router returned an empty 404 for it.
- Add `CodexStandaloneEndpoint::ImagesEdits`. Both Images endpoints share
one full-URL suffix table, so a complete Responses / Compact / Chat
Completions / Images URL derives the sibling endpoint (and vice versa)
while opaque full URLs keep failing closed.
- Apply the standalone sibling rewrite when the base URL is pasted as a
complete endpoint with the full-URL switch off, mirroring
`codex_chat_base_is_full_endpoint`; standalone requests no longer become
`.../chat/completions/images/generations`.
- Parse Images API streaming usage: `image_generation.completed` carries
`usage` on the event top level, so scan for `usage.input_tokens` before
falling back to the Chat Completions stream parser.
Related #7036
* fix(codex): match standalone URL suffixes case-insensitively
Keep endpoint detection and sibling rewriting consistent while preserving the original URL prefix and query. Cover mixed-case Images and Alpha Search endpoints through URL and proxy regression cases.
---------
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): preserve per-app settings on shutdown and port allocation (#7210)
On normal exit, stop_with_restore_keep_state() called the legacy global
proxy-config writer to clear the long-retired live_takeover_active flag.
That writer reads the Claude row and runs an UPDATE without a WHERE clause,
so every shutdown copied Claude's max_retries and the three timeout fields
onto the Codex, Gemini and Grok Build rows — and the UPDATE never carried
live_takeover_active in the first place, so the step had no effect beyond
the clobber. The ephemeral listen-port path did the same on every proxy
start.
Drop the dead write-back (live-config restore, backup cleanup and health
reset are unchanged) and persist the resolved ephemeral port through the
global-config interface, which only touches the shared listen/logging
columns. Two regression tests seed distinct per-app settings for all four
apps and assert they survive both paths.
Fixes #7204
fix(provider): preserve child metadata during universal sync (#7212)
Syncing a universal provider regenerates its Claude/Codex/Gemini child
providers. The child's `settings_config` was merged with the existing row,
but `meta`, `created_at` and `sort_index` were taken from the generated
object, and the universal provider never carries any of them from the UI.
The UPDATE therefore wiped the child's usage script, common-config opt-out,
endpoint auto-select and other per-app settings to `{}` and reset the sort
index to NULL, which pushed the card to the bottom of the list and looked
like the provider had been deleted and re-created.
Keep the existing child's `meta`, `created_at` and `sort_index` when the
row already exists. Name, base URL, key, models, website and notes are
still driven by the universal provider, and first-time creation still
inherits the parent's metadata.
Fixes #7134
feat(presets): rebrand DashScope as 千问AI平台 and refresh Qwen models (#7183)
Rename the domestic Bailian presets to 千问AI平台 with a dedicated icon, refresh the Qwen family to the 3.8 generation across Claude Code, Claude Desktop, Codex, Hermes, OpenClaw, OpenCode and Pi, add the domestic Token Plan presets, restore the international QwenCloud Coding Plan presets, and align OpenClaw/OpenCode/Pi model metadata with the official client docs.
fix(codex): honor proxy URL when model_provider is omitted (#7263)
When a Codex provider card's TOML has no `model_provider`, Codex falls back to
its built-in `openai` provider, but takeover wrote the proxy address as a
top-level `base_url` that Codex never reads, so requests bypassed the local
proxy and went straight to api.openai.com.
Treat the missing selector as the built-in `openai` provider and write the
proxy address to `openai_base_url`; the existing legacy-reroute migration then
turns it into a `cc-switch` custom provider table carrying the `PROXY_MANAGED`
bearer, the same shape every other third-party takeover uses. Explicit
provider selections are unchanged.
Refs #6256, #7217
fix(provider): update MiniMax defaults to M3 and remove expired offers (#7255)
* fix(provider): refresh MiniMax M3 presets and remove expired promotions
* fix(provider): complete MiniMax M3 capability declarations
---------
Co-authored-by: saladday <1203511142@qq.com>
chore(provider): remove Atlas Cloud sponsor status, keep presets
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`
docs(release): add v3.20.2 release notes
chore(release): v3.20.2
feat(provider): add SoleAPI sponsor presets across all nine apps
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.
fix(codex): proxy image edits endpoint
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.
fix(provider): pin models URL for JieKou and Novita Claude presets
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.
feat(pricing): refresh seed for September 2026 vendor price changes
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 tes…
- tray.rs 新增 show_main_window/hide_main_window/toggle_main_window 助手:
显示语义与"打开主界面"一致,隐藏与"关闭到托盘"平台处理一致
(Windows set_skip_taskbar、macOS apply_tray_policy)
- lib.rs 托盘 show_menu_on_left_click 改为 false,左键 Click(Left, Up)
触发切换(过滤 Down/Up 双事件),右键仍弹菜单;Enter/Click 用量刷新保持
- show_main 菜单事件与 macOS Reopen 复用 show_main_window
docs(readme): 更新日志补充连通性测试、双官网链接与托盘左键切换
feat(model-quick-switch): 获取模型列表成功后自动展开选择器
SearchableModelPicker 增加可选 open/onOpenChange 受控展开(不传保持内部自管,
表单等既有用法不受影响);快捷切换弹窗拉取成功且列表非空时直接展开,关闭弹窗复位。
chore(native): archive fix-quick-switch-1m
chore(comet): 记录 fix-quick-switch-1m 验收通过与归档状态
fix(model-quick-switch): 补齐 Haiku 显示名同步,支持不选模型仅翻转 1M
- 显示名同步映射补上 ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME(沿用空/旧base/旧base[1M] 覆盖、自定义保留语义)
- 新增 setClaudeOneMInSettings:仅原地翻转 1M 标记,不改各角色模型 base 与显示名
- 弹窗打开时 1M 开关按供应商当前状态初始化,避免已开 1M 被静默剥离
- claude 未选模型且 1M 状态有变化时允许保存;无可应用变化时保存禁用
- 补单测与四语言 toast 文案
feat(provider-website-links): 供应商支持第二个官网链接,主页横排双链接
- Rust Provider/UniversalProvider 新增 website_url_2(serde websiteUrl2,skip none)
- SQLite providers 表新增 website_url_2 列(schema v18→v19 幂等迁移 + create_tables 补列 + 旧 config.json 迁移)
- DAO SELECT/INSERT/UPDATE/replace 全链路带出新列
- 表单共享 BasicFormFields + 统一供应商表单第二输入框;zod 同第一链接规则校验与回填
- ProviderCard 主页双链接横排、两空格分隔、逐链接 openExternal 打开默认浏览器
- 搜索匹配、复制供应商、统一供应商生成 claude/codex/gemini 随行透传
- gemini_auth packycode 认证检测纳入 website_url_2;ClaudeDesktop/GrokBuild 预设切换清空第二链接;卡片同值链接去重
- 4 语言 i18n + 前端/前端单测覆盖
Comet change dual-website-links-homepage(19/19 验收通过,归档)
feat(connectivity-test): 拉取模型列表改为「搜索添加」选择器交互
- 新增共享组件 SearchableModelMultiPicker:搜索过滤 + 多选 + 已在列表标记 + 已选计数
- ConnectivityTestDialog 获取模型列表后不再全量追加合并去重,改为会话内缓存完整列表并自动弹出一次选择器;用户搜索挑选后添加进表格并自动勾选
- 重新拉取只刷新缓存,已添加进表格的行保留;拉取结果仍仅会话内有效,不写回供应商配置
- 四语言 i18n 新增 searchAdd / addToTest / alreadyInList / selectedCount
- 同步更新 ConnectivityTestDialog 测试并新增 SearchableModelMultiPicker 测试
feat(connectivity-test): 探测目标按供应商 API 格式对齐真实转发链路
- build_claude 按 get_claude_api_format 分派端点与请求体:openai_chat → /v1/chat/completions(复用 transform_claude_request_for_api_format,流式注入 stream_options.include_usage)、openai_responses → /v1/responses、gemini_native → /v1beta/models/{model}:{method}(resolve_gemini_native_url)、anthropic 保持 /v1/messages 并补 anthropic-version/beta 头
- build_codex 按 should_convert_codex_responses_to_chat/anthropic 分派:chat → /chat/completions(responses_to_chat_completions_with_reasoning)、anthropic → /v1/messages(responses_request_to_anthropic,含 max_output_tokens 注入与 [1m] 剥离 + context-1m beta 头)
- 鉴权改用 ClaudeAdapter/CodexAdapter 的 extract_auth + get_auth_headers(与真实出站请求同源:AUTH_TOKEN→Bearer、ANTHROPIC_API_KEY→x-api-key、gemini→x-goog-api-key、GeminiCli OAuth→Bearer、OpenRouter→Bearer、Codex 按 apiKeyField)
- 转换前剥离 [1m] 标记;meta.isFullUrl 或 base 已是完整端点时直接使用 base_url;自定义 body 浅合并移至转换后的最终协议体
- 新增 18 个目标构造断言单测;Comet 归档产物同步提交
chore: archive connectivity-test-optimizations
feat(connectivity-test): 单一模型表格 + 实时拉取模型 + 勾选即测试范围 + 新默认提示词
- 弹窗合并为单一模型表格:打开即全部模型行(初始待测试)、行勾选 + 表头全选(含半选态)、5 格统计卡,移除上方独立选择区
- 获取模型列表:凭据提取抽为共享模块 src/utils/providerCredentials.ts(ModelQuickSwitchDialog 同步改引用),复用 fetch_models_for_config 拉取 /v1/models,按 ID 追加合并去重、仅会话内有效
- 默认提示词改为「你好,你可以帮我做什么事情」:前后端常量同步,merge 差异判断基准跟随
- 勾选即测试范围:后端 connectivity_test_provider_models 新增 modelIds 参数(None/空白回退静态清单),前端逐模型独立 invoke 并行 + 行级流式更新
- 一轮测完自动勾选失败项,全部成功保持原勾选
fix(connectivity-test): UI 四项修复
- 弹窗抬至 alert 层(z-[60]):打开连通性测试时 header 切换栏正确变灰,
详情子弹窗同步抬层(同 z + 更晚挂载 portal 在上)
- 批量检测入口移至 header 图标组(状态提升到 App,ProviderList 经
props 读取探针结果),不再另起一行
- 批量检测可停止:runId 代次守卫(Tauri invoke 无法真正取消),再次
点击即清除 waiting/running 徽标、丢弃在途迟到写入、不再拉起排队项
- 弹窗打开自动勾选模型:已保存默认测试模型 → 供应商当前模型
(claude env.ANTHROPIC_MODEL / codex config.toml model,新增
currentProviderModelId 与后端同口径)→ 清单首个
前端全量 141 文件 / 1106 测试全绿;typecheck clean
chore: archive per-model-connectivity-test
fix(connectivity-test): shouldShowTestEntry 增判 providerType + 收尾清理
chore(connectivity-test): 勾选 Task 9 / OpenSpec 5.1-5.2 + verify 报告
chore(connectivity-test): 勾选 Task 8 / OpenSpec 4.1-4.3
refactor(connectivity-test): 移除旧 stream_check 可达性探测全链路(保留表结构)
- 删除 6 个文件:commands/stream_check.rs、services/stream_check.rs、
database/dao/stream_check.rs、hooks/useStreamCheck.ts、lib/api/connectivity-check.ts、
components/usage/ConnectivityCheckConfigPanel.tsx
- 移除 mod.rs 声明、lib.rs generate_handler! 注册、cleanup_old_stream_check_logs 调用
- 从 SYNC_SKIP_TABLES / SYNC_PRESERVE_TABLES 移除 stream_check_logs(表结构保留在 schema.rs)
- 更新 backup.rs 测试:移除 stream_check_logs fixture 与断言
- 更新 SettingsPage:移除 ConnectivityCheckConfigPanel AccordionItem 与 FlaskConical 导入
- i18n:删除 streamCheck.* 命名空间(zh/en/ja/zh-TW 四语言)
- 更新注释:移除 stream_check 旧引用(forwarder.rs、provider.rs、userAgentPresets.ts、
connectivity_test.rs)
- 修复 4 个 pre-existing clippy lint(connectivity_test: matches! 宏、unused_unit、
derive Default)
- 移除 ProviderList.test.tsx 中已失效的 useStreamCheck mock
chore(connectivity-test): 勾选 Task 7 / OpenSpec 2.4 3.1-3.3
feat(connectivity-test): 供应商列表集成(弹窗入口 + 批量探针徽标)
chore(connectivity-test): 勾选 Task 6 / OpenSpec 2.2-2.3
fix(connectivity-test): 模型列表解析与后端对齐(每条目首个匹配,提取共享 helper)
feat(connectivity-test): 连通性测试弹窗/详情/徽标组件与四语言文案
chore(connectivity-test): 勾选 Task 5 / OpenSpec 2.1
test(connectivity-test): 锁定 toBackendParams 字段映射(customHeaders/customBody 等)
feat(connectivity-test): 前端命令绑定与测试/探针状态 hooks
chore(connectivity-test): 勾选 Task 4 / OpenSpec 1.4
feat(connectivity-test): 测试参数持久化纯函数(缺省即默认值)
chore(connectivity-test): 勾选 Task 3 / OpenSpec 1.3
feat(connectivity-test): Tauri 命令与注册,官方/动态端点过滤,sanitize 隔离私有字段
chore(connectivity-test): 勾选 Task 2 / OpenSpec 1.2
feat(connectivity-test): 后端直连执行器(reqwest+SSE,总/空闲超时,度量与错误识别)
feat(connectivity-test): 后端直连执行器(reqwest+SSE,总/空闲超时,度量与错误识别)
chore(connectivity-test): 勾选 Task 1 / OpenSpec 1.1
fix(connectivity-test): claude 鉴权对齐 adapter 顶层 apiKey/api_key 兜底
feat(connectivity-test): 新增后端连通性测试服务类型/模型解析/协议构造
chore: archive model-quick-switch-picker-scroll
chore: model-quick-switch-picker-scroll 验证报告与状态元数据
fix: 模型快捷切换弹窗内模型列表滚轮无法滚动
模态 Dialog 的 RemoveScroll 会对 shards 外的 portaled PopoverContent
wheel 事件无条件 preventDefault。在 SearchableModelPicker 的
PopoverContent 捕获阶段阻断 wheel 传播,恢复原生滚动。
chore: bump fork version to 3.20.1-1 after upstream v3.20.1 rebase
fix(form): only auto-expand Claude advanced options on divergent model mapping
Uniform mapping produced by the quick-set button (all roles equal to the
fallback model) no longer counts as an advanced value, so the advanced
section stays collapsed on edit. A 1M marker is stripped before comparing
so marked roles are not misjudged as divergent. Truly customized mappings
still auto-expand the section.
feat: align fallback-model quick-set buttons with model input right edge
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
docs: update README with fork build notes and quick-move provider changes
chore: archive comet-native claude-model-quick-access
feat: surface Claude fallback model as quick-access section below endpoint
Move the default fallback model (ANTHROPIC_MODEL) block out of the
collapsed Advanced Options into a quick-access section directly below
the request endpoint: input + 1M checkbox + quick-set + fetch-models
buttons are all reachable without expanding.
- header toggle-all "declare 1M" now includes the fallback model
(Haiku still excluded); partial selection shows indeterminate state
- drop the duplicated quick-set/fetch buttons and the old fallback
block inside Advanced Options
- exclude claudeModel from hasAnyAdvancedValue so a fallback-only
value no longer auto-expands Advanced Options
- quick-set priority, env write semantics, and OAuth preset branches
unchanged; add 4 component tests covering the new layout
chore: archive comet-native provider-right-click-top-bottom
Enable native workflow in .comet/config.yaml and move the Comet managed
state block in .gitignore so the exclusion rules stay grouped with the
other fork tool artifacts.
feat: add provider right-click move-to-top/bottom and insert new providers at second position
- ProviderList: context menu with quick move-to-top/move-to-bottom, reusing
update_providers_sort_order and refreshing tray menu like drag sort
- useAddProviderMutation: default insert at index 1 with explicit sortIndex
materialization for existing items (avoids defined-first sort trap)
- i18n: quickMove* strings for zh/en/zh-TW/ja
ci: stop flaky WSL2 linker job from blocking main
The backend-windows-wsl2 job intermittently fails with LNK1327 /
mt.exe c1010070 when the Windows runner's mt.exe cannot parse a
manifest temp file on the WSL2 UNC TEMP path. The same flaky failure
occurs upstream and is unrelated to fork changes. Mark the job
continue-on-error so main stops going red on it; the result stays
visible in the Actions UI.
style: apply prettier to files touched by recent comet archives
Frontend CI format:check failed on 6 files modified by the app-name
and model-quick-switch archive commits plus the updater-disable commit.
Run prettier --write so format:check passes again.
chore: archive ccs-fork-app-name-cc-switch
chore: archive ccs-model-quick-switch-ui-polish
docs: rewrite README to document fork differences vs upstream
README 从上游完整功能介绍改为仅记录 fork 与上游的差别,完整功能
文档指回上游。涵盖 8 项 fork 魔改:构建标识/开发预览、预设精简、
Claude 表单调整、模型搜索移植、主页模型快速切换、侧边栏面板显隐、
关闭应用内更新、打包范围,并说明版本号与 rebase 同步流程。
feat: disable in-app updater, point users to GitHub releases
移除 tauri-plugin-updater 依赖与自动更新链路,改为统一引导用户到
GitHub Releases 手动下载新版。
- 前端:updater.ts 恒返回 up-to-date;UpdateContext 取消启动自检;
AboutSection「检查更新」直接打开 fork releases 页;DatabaseUpgrade
恢复界面去掉下载进度条,改为打开发布页。
- 后端:移除 updater 插件注册与 UpdaterExt 调用;
install_update_and_restart 降级为空壳,check_app_update_available
返回当前版本号;check_for_updates 指向 fork releases;capabilities
去掉 updater:default 权限。
- 打包:tauri.conf.json 删 plugins.updater、createUpdaterArtifacts=false;
Cargo.toml / package.json 删 updater 依赖,Cargo.lock / pnpm-lock
同步重生成。
- CI:release.yml 移除签名收集与 assemble-latest-json job,删除
.tar.gz/.sig 处理,release body 去掉 updater 说明。
版本号统一为 3.20.0-1(合法 semver 且 MSI 第 4 位为 1)。
Cargo.lock / pnpm-lock.yaml 已随依赖移除重生成,cargo check 通过。
ci: build only Windows x64 and macOS arm64, unsigned
feat: gate dev panel behind dev-only env flag
chore(comet): archive ccs-sidebar-panels-visibility change
fix: apply skills visibility guard to hermes branch toolbar button
feat: include prompts in sidebar panel visibility settings
fix: persist visibleSidebarPanels setting in backend AppSettings
style: use compact pill toggles for sidebar panel visibility
feat: add sidebar panel visibility switches to settings
feat: add sidebar panel visibility settings and wire into toolbar buttons
chore(comet): archive ccs-home-model-display change
fix(fork): read grokbuild api key from config TOML in quick switch dialog
feat(fork): align model badge with upstream extractModelBadge
feat(fork): show current model badge and quick-switch entry on provider cards
feat(fork): add model quick switch dialog
feat(fork): add provider model read/write utils with tests
chore(comet): archive ccs-model-search-port change
feat(i18n): add model search picker keys for all locales
feat(form): port SearchableModelPicker from upstream for searchable model selection
chore(comet): archive ccs-claude-form-layout change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-claude-form-layout/
- Sync claude-form-layout spec to main specs
- Annotate design doc as archived
- Add verification report
fix(i18n): update advanced options hint, add ja/zh-TW toggle-all, drop dead apiFormatHint key
fix(form): correct 1M toggle-all alignment direction (ml-1)
fix(form): align 1M toggle-all checkbox with row checkboxes
refactor(form): move api format select into EndpointField rightSlot
fix(form): widen api format select to fit option text
feat(form): add 1M toggle-all checkbox in model mapping header
feat(form): move api format select next to endpoint input
chore(comet): archive ccs-provider-presets-trim change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-provider-presets-trim/
- Sync provider-presets-visibility spec to main specs
- Annotate design doc as archived
- Add verification report
docs(fork): add presets-trim files to rebase whitelist
test(fork): mock IS_FORK_BUILD false in unit tests for upstream-form semantics
feat(fork): wire official preset filter into provider forms
feat(fork): add hidden field to preset interfaces
feat(fork): add official preset allowlist and filter util
chore(comet): archive ccs-fork-scaffolding change
- Move change artifacts to docs/openspec/changes/archive/2026-08-18-ccs-fork-scaffolding/
- Sync fork-build-identity spec to main specs
- Annotate design doc as archived
- Add verification report
feat(fork): add dev config with isolated identifier and config dir
docs(fork): complete rebase whitelist with all fork-touched files
fix(fork): guard setTitle with isTauri in test/non-tauri env
fix(fork): define __CCS_FORK_BUILD__ in vitest config
feat(fork): add DevPanel and settings footer entry
feat(fork): set macOS window title to CC Switch (Fork) on startup
feat(fork): inject __CCS_FORK_BUILD__ compile-time constant
feat(fork): bump version to 3.19.2-fork.1 and rename product to CC Switch (Fork)
docs(fork): add upstream rebase workflow and ignore tool artifacts
fix(proxy): accept the whole grok-4.x (x>=5) family in the reasoning-effort whitelist (#7369)
Replace the verbatim grok-4.5 / grok-4.6 entries in supports_reasoning_effort with a rule that parses the grok-4.x minor version and accepts x >= 5, mirroring the existing GPT-5+ rule. This covers grok-4.7 (released 2026-09-21), whose reasoning effort was previously dropped on the Claude -> Chat, Claude -> Responses and Codex Responses -> Chat conversion paths, and lets future releases pass without another whitelist edit. The grok-build-* family is retained for saved providers.
Co-authored-by: allenxu09 <171831965+allenxu09@users.noreply.github.com>
docs(release): add v3.20.4 release notes
chore(release): v3.20.4
feat(pricing): add Grok 4.7 and GLM-5.3-FlashX
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.
fix(i18n): remove outdated OpenCode Go referral offer
fix: add SudoCode.chat alternate API endpoint
feat(settings): add a GitHub star prompt to the About card
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
feat(presets): add Soshow aggregator presets
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.
fix(ci): stabilize frontend tests and verify renderer build
fix(mcode): refresh provider state after removal (#7578)
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
fix(lib): reset skip_taskbar before window.show() across entry points (#6348)
5处 window.show() 调用中 4处没有在 show() 前重置 Windows 的 skip_taskbar,导致以下场景出现窗口可见但不在任务栏、点最小化后窗口消失的 bug:
- single_instance handler (lib.rs:360-361):当原进程因 silent_startup / 关闭到托盘等处于 skip_taskbar(true) 状态时,从快捷方式/开始菜单唤起已运行实例触发。
- handle_deeplink_url 的 focus_main_window 分支 (lib.rs:256):通过 ccswitch:// URL 唤起时。
- DB 恢复流程 (lib.rs:556):数据库版本不匹配时显示恢复界面。
- RunEvent::Opened 处理器 (lib.rs:1803):系统接收到 deep-link URL 时。
修复后的行为与 tray.rs::handle_tray_menu_event::show_main 分支一致(那里已有相同的 set_skip_taskbar(false) 调用)。
经用户测试验证:autostart + silent_startup + 从开始菜单/快捷方式启动的场景下,最小化按钮恢复正常工作。
fix(proxy): omit missing tool descriptions in the Codex responses-to-chat converter (#7378)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): preserve max effort for GPT-5.6 and GPT-6 Astra (#7531)
fix(misc): ignore shell startup output when probing tool versions in WSL (#7348)
try_get_version_wsl runs "${SHELL:-sh}" -lic '<tool> --version' through
wsl.exe and takes the first x.y.z anywhere in stdout. A login+interactive
shell runs the distro's startup files first, so their output shares that
stdout. On Ubuntu, /etc/profile.d/update-motd.sh prints the MOTD once a day,
starting with "Welcome to Ubuntu 24.04.4 LTS", and the card then showed
24.04.4 as the Claude Code version. The once-a-day stamp makes it appear
after the first probe of the day and vanish on refresh.
The payload now prints a sentinel before running the tool, and parsing looks
only at what follows its last occurrence. The last occurrence handles the
-lic || -lc || -c fallback chain printing it several times; matching it as a
substring handles OSC sequences glued in front of it. If the sentinel is
absent (the shell failed before the payload ran), the output is used as
before. The stderr fallback for tools that print their version there is kept.
Fixes #7347
Co-authored-by: lyfuci <lyfuci11@gmail.com>
fix(claude): hide session URL attribution (#7053)
* fix(claude): hide session URL attribution
* docs(claude): document session URL attribution
fix(ui): reset provider scroll when switching apps (#5211)
fix(pi): fix provider logo (#6826)
fix(i18n): use the Pi form's own term for the zh-TW duplicate-key message (#7187)
Follow-up to the nit on #6768. In zh-TW the Pi form labels the field
供應商識別碼 and says 供應商識別碼為必填 when it is empty, but the
duplicate message I added said 供應商標識 — the term Hermes, OpenCode and
OpenClaw use for their own field. One form, two names for the same thing.
The other three locales already keep the label and both messages on one
term, so this makes zh-TW match the pattern rather than introduce one:
en Provider key / Provider key is required / This provider key already exists
ja プロバイダーキー / プロバイダーキーを入力してください / このプロバイダーキーは既に存在します
zh 供应商标识 / 供应商标识不能为空 / 该供应商标识已存在
zh-TW 供應商識別碼 / 供應商識別碼為必填 / 該供應商標識已存在 <- the odd one out
hermes.form.providerKeyDuplicate carries the same sentence and is left
alone: its own label is 供應商標識, so it is already consistent.
Verified: managementListLocales 7/7. Full unit suite 1085/1087, the same
two App.test.tsx integration cases failing before and after.
fix(proxy): strip stop param for GitHub Copilot to unblock auto mode classifier (#5404)
Claude Code's auto mode classifier sends `stop_sequences` on every
safety check. On the GitHub Copilot path the Anthropic -> OpenAI Chat
conversion mapped it to `stop`, and Copilot's Chat Completions route
for Claude models answers that request with HTTP 400, so every
classifier call failed while normal chat kept working.
- Remove the converted `stop` field only when the provider is GitHub
Copilot; other OpenAI-compatible upstreams keep it.
- Copilot detection in the transform now also covers providers that
are recognised only through `env.ANTHROPIC_BASE_URL`.
- Regression tests for the Copilot (meta and env URL) and generic
provider cases.
Fixes #5175
fix(codex): keep stored auth when the live snapshot has no credential (#7434)
Editing the current Codex provider seeds the form from the live ~/.codex
snapshot, and switching away backfills that snapshot into the DB row. When a
provider table declares its own credential source (an Authorization header,
env_key, auth/aws), no bearer token is injected into config.toml and the
shared auth.json is deleted on third-party switches, so the live snapshot is
permanently `{ auth: {}, config }` while auth.OPENAI_API_KEY in the DB is the
only copy of the key. Both paths treated that empty live auth as
authoritative and silently erased the stored key, which in turn made the
usage-script modal misread the provider as an official subscription and
reset the saved script.
- Treat a credential-less live auth (missing auth.json or the bare
auth_mode logout marker) as an absent field in both the edit dialog and
the switch-away backfill, keeping the stored auth template. Live still
wins whenever it carries material, and official providers keep the live
login as their source of truth.
- Gate the official exemption on is_codex_official_provider /
resolveCodexOfficialIdentity rather than the category alone: a legacy
fixed official card without a persisted category is still written as
official, so the fallback must not resurrect a logged-out login on
switch-back.
- Cover both layers with regression tests, including the category-less
official shapes.
Fixes #7433
Supersedes #7492
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): lift additional_tools input carriers on the Codex chat conversion (#7454)
Codex talks to third-party providers over its Responses Lite transport,
which ships the request's extra tools in an `additional_tools` input
carrier: `{"type":"additional_tools","role":"developer","tools":[...]}`,
a role but no `content`. The Responses->Chat conversion let that carrier
fall through the generic "item with a role is a message" arm and emitted
`{"role":"system","content":null}` at messages[1], which strict Chat
gateways reject with `messages[1]: missing field "content"` (or
`data did not match any variant of ...SystemMessageContent`). The tools
carried inside it were also dropped on the Chat and Anthropic conversions.
- Skip `additional_tools` items in the Chat message walk; a carrier
declares tools, it is not a message.
- Collect carrier tools through the existing CodexToolContext registry
(dedup, namespace flattening, custom-tool conversion), which also feeds
the Anthropic conversion and the response-side name round-trip.
- Requests without a carrier convert byte-for-byte as before.
Known boundary: `custom` children inside a namespace tool
(`functions.exec`) are still not lifted; that needs its own response-side
name round-trip and is left for a follow-up.
Diagnosed independently in #6158 by BigStrongSun (fix in #6159) and in
fixture.
Fixes #7451
Fixes #6158
Refs #6033
Refs #6776
Supersedes #6159
fix(proxy): normalize Codex detail:original images for Chat gateways (#7476)
Codex marks tool-returned images with detail:"original" whenever the model
catalog advertises supports_image_detail_original, a Responses-only value.
The Responses->Chat conversion copied it verbatim into image_url.detail, and
strict OpenAI-compatible Chat gateways (Ark/Doubao, SenseNova, GLM, vLLM, ...)
reject the whole request with 400. Because Codex replays the full rollout on
every turn, one poisoned view_image result made the session fail permanently,
including plain-text turns.
Two layers:
- tool_media::merge_top_level_detail downgrades original -> auto, so already
poisoned rollouts and Codex versions before 0.155.0 (which do not normalize
replayed history themselves) recover immediately. auto/low/high are untouched.
- codex_catalog_model_entry always writes supports_image_detail_original:false
on the ProxyChat profile, so Codex stops emitting original at the source once
the catalog is regenerated. NativeResponses/Anthropic profiles and the DeepSeek
official catalog are unaffected.
Top-level user images were never affected: responses_content_to_chat_content
drops the part-level detail entirely.
Fixes #6495
Fixes #7458
Refs #6569
Refs #5805
fix(omo): detect unified config on the WSL side when the OpenCode dir is in WSL (#7550)
When the OpenCode config directory points into WSL
(\\wsl.localhost\<distro>\home\<user>\.config\opencode), OMO >= 4.19.3 keeps
its unified config in the WSL-side home (~/.omo/omo.jsonc). Detection only
probed the Windows home, never found it, and silently fell back to the legacy
per-plugin file that OMO no longer reads, so provider switches looked
successful but had no effect.
- Add derive_wsl_home_dir to derive the WSL-side home from a WSL UNC path
(home/<user> or root); non-WSL and non-home paths return None.
- Probe a list of home candidates for the unified config, WSL-side home
first and the OS home as a fallback, shared by the read, write and delete
paths.
- Detection only: when no unified config exists on either side the legacy
fallback is unchanged.
Fixes #7363
Supersedes #7367
docs(partner): add a FluxA purchase link and tighten its sponsor blurb
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.
fix(claude-desktop): expose the 1M context variant for DeepSeek V4 routes
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.
feat(presets): add FluxA Token Plan partner presets
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.
fix(presets): correct AICodeWith Codex-family endpoint to /v1
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.
fix(ui): render monochrome preset icons in the foreground color
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.
docs(user-manual): point Chat-only Codex examples at plan endpoints
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.
fix(pricing): restore DeepSeek V4 Pro price and seed Qwen3.8 and Hy4 models
- 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).
fix(presets): move MiniMax CN and BaiLing to their current official endpoints
- 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.
feat(codex): refresh CN presets from the Responses API audit
- 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.
test(claude-desktop): run Linux path tests on unix hosts and cover Linux in provider sync tests
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.
fix(ui): stop expanding stale cached usage tiers on ineligible provider cards
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.
fix(skills): 支持 skillId 与目录名不一致的技能安装和更新 (#6381)
* fix(skills): resolve mismatched skillId directories
* fix(skills): preserve source path during updates
* fix(skills): refresh source URL after repository relocation
* test(skills): isolate relocation fixture from legacy home
feat(opencode): 支持从获取的模型列表中搜索并批量添加所选模型 (#7515)
* feat: implement fetched models selection and UI components
* fix(opencode): prevent model search from submitting provider form
Add Kimi Global preset variants (kimi.ai platform + api endpoints) (#7526)
Add a "Kimi Global" twin immediately after the domestic "Kimi" API preset and a "Kimi For Coding Global" twin after the domestic "Kimi For Coding" preset, in every preset file that ships them (claude, claude-desktop, codex, hermes, openclaw, opencode, pi).
"Kimi Global" points to the international open platform https://platform.kimi.ai?aff=cc-switch (apiKeyUrl mirrored as https://platform.kimi.ai/console/api-keys?aff=cc-switch) and swaps the endpoint host api.moonshot.cn for api.moonshot.ai with each file's path unchanged (/v1 or /anthropic).
"Kimi For Coding Global" points to the international site https://www.kimi.ai/code/?aff=cc-switch and swaps the endpoint host api.kimi.com for api.kimi.ai with the path unchanged (e.g. https://api.kimi.ai/coding/v1).
Domestic presets are unchanged; category, icons, apiFormat, model catalogs, endpointCandidates (host swapped), partnerPromotionKey and settings otherwise mirror the domestic blocks exactly.
primePartner stays only on the domestic presets so the new variants do not alter prime-partner ordering.
The Pi twins get distinct providerKey values ("cc-switch-kimi-global", "cc-switch-kimi-for-coding-global") because providerKey is the preset-level primary key and must not collide with the domestic presets.
Rebalance sponsor CTAs around Kimi Code plan with dual-region links (#7522)
Kimi Code plan purchases have resumed, so the README sponsor sections go back to leading with the coding plan instead of the API-only flow written during the purchase pause.
All four locales (EN/ZH/JA/DE) now present Kimi Code plan (中文站 | Global) as the primary CTA and the Kimi Open Platform API (中文站 | Global, with the first-top-up bonus) as the secondary one, mirroring the domestic|global split already used for API links.
The aff and track_id values are unchanged.
fix(prompts): refresh active prompts after external file edits (#7194)
Reload the enabled prompt from its live file when the Prompts list loads, and refresh the open panel when the window regains focus, so external edits to CLAUDE.md / AGENTS.md show up without a restart.
- Keep saved templates available when the live file cannot be read.
- Skip the backfill for empty or whitespace-only files, matching enable_prompt.
- Skip the backfill while a sync/restore holds the sync lock, so a list load cannot write a stale live file into a freshly restored database.
fix(skills): raise archive entry limit so large skill repos install (#7489)
Installing hugohe3/ppt-master from skills.sh failed with
ARCHIVE_TOO_MANY_ENTRIES: the GitHub archive we download is the whole
repository (13,248 entries), and the 10,000-entry cap added in #5811
treated it as a zip bomb.
- Raise MAX_ARCHIVE_ENTRIES to 30,000 for both the remote archive and
local ZIP paths, and align WebDAV's MAX_EXTRACT_ENTRIES so a skills.zip
containing such a skill still restores.
- Charge every file entry at least one 4 KiB block against the existing
512 MiB budget, mirroring directory accounting, so empty-file archives
are bounded by the byte budget rather than by the entry count alone.
- Add tests for a 13k-entry repo archive and for the per-file minimum
charge.
Fixes: https://github.com/farion1231/cc-switch/issues/7475
feat: add MiniMax Code harness support (#7383)
Add MiniMax Code (mcode) as a managed application: provider and model management through the native custom_provider configuration, MCP servers, Skills, global instructions (AGENTS.md), read-only local session history with resume, and usage import from the native runtime database. Native files shared by the TUI and desktop are written under the native lock with rollback when the database commit fails, unrelated native configuration is preserved, and the data directory honors MINIMAX_DATA_DIR and MAVIS_DATA_DIR. Deleting sessions stays in the native application because they are coupled to runtime state.
fix(codex): recover stale account bindings during takeover (#7395)
After deleting a managed ChatGPT account and logging in again, a Codex provider can still reference the old local account ID, which made route switching, takeover, startup restoration and rebinding fail permanently.
Distinguish a missing outgoing account from an existing account with no matching live token. A missing account only releases its ownership marker after the persisted account store is verified; the target write policy stays in force. A stale target now reports an account-selection instruction instead of a bare not-found error, including when takeover is already enabled.
Fixes #7392
feat(claude-desktop): support 3P configuration on Linux (#7331)
Add Claude Desktop 3P configuration support on Linux. Resolve the config root from an absolute XDG_CONFIG_HOME, falling back to ~/.config; when CC Switch itself runs as a Flatpak, use the host ~/.config instead of the sandbox-private XDG_CONFIG_HOME. Update the en/zh/ja user manuals with Linux paths and the Flatpak boundary.
Fixes #4855
fix(proxy): omit missing tool descriptions instead of serializing null (#7319)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): keep reasoning effort for grok-4.6 and pass xhigh through verbatim (#7318)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(models): support Zhipu OpenAI Responses model lists (#7330)
Zhipu's Responses endpoint (open.bigmodel.cn/api/v1) serves its model list in the Codex remote catalog shape (models[].slug) instead of the OpenAI-compatible data[].id shape, so the Codex form's model fetch came back empty. The parser now falls back to models[].slug when data is absent.
fix(misc): fetch npm dist-tags from the dedicated endpoint with the probe timeout (#7346)
The tool version probe requested the full npm packument for each package (metadata for every historical version, 14-25 MB uncompressed for codex / opencode / openclaw) only to read its dist-tags, and it was the only latest-version probe without LATEST_PROBE_TIMEOUT, so it fell back to the proxy client's 600 s total timeout and the cards sat on "loading" for minutes on slow links while the large downloads starved the Hermes GitHub probe into "unknown".
Switch fetch_npm_dist_tags to registry.npmjs.org/-/package/{package}/dist-tags, whose body is the dist-tags object itself (tens of bytes to a few KB), escape scoped names as %2f, and apply the same 15 s probe timeout as the GitHub and PyPI probes. No data is lost: the only consumer reads latest and the tool's prerelease tags.
Fixes #7339
feat(usage): display output tokens per second in request log table and detail panel (#3369)
Show tokens/s next to the output token count in the request log table and the request detail panel.
The generation window prefers an explicit duration when present, otherwise latency minus first-token time for streaming requests, and falls back to full latency for non-streaming requests. Rows without a positive token count or window show nothing.
Closes #5936
fix(presets): update APIKEY.FUN URLs to apikey.fan
docs(release): add v3.20.3 release notes
chore(release): v3.20.3
fix(pricing): move retired DeepSeek V4 family to the V4.1 Flash tier
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.
fix(presets): declare third-party Token Plan DeepSeek V4 rows text-only
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.
feat(presets): rename DouBaoSeed preset to localized Volcengine Doubao
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.
fix(codex): skip unchanged incomplete rollout tails
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.
fix(claude): align quick model selection with form order
fix(tray): show bound ChatGPT account quota for managed Codex cards
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
feat(claude): add "Disable Artifact Tool" quick toggle to provider editor
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.
feat(codex): refresh aggregator model presets and catalogs
feat(codex): switch Kimi presets to native Responses direct-connect
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.
fix(usage): show Claude Fable weekly limit from the usage API limits array
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.
fix(codex): mirror vision-capable DeepSeek catalog and un-gate the legacy v4-flash alias (#7286)
Refresh the bundled DeepSeek official Codex catalog to the vendor's current models.json (slug deepseek-flash, image input modality, supports_image_detail_original) and remove deepseek-v4-flash from the confirmed text-only registry: the legacy id is still accepted by DeepSeek and routed to the vision-capable V4.1 Flash, so both the proxy media sanitizer and the generated Codex catalog must fail open for it. deepseek-v4-pro stays text-only per the vendor catalog.
Fixes #7283
fix(proxy): clamp sub-floor max_tokens to the Responses API minimum (#7287)
The Responses API rejects max_output_tokens below 16, but Anthropic clients legitimately send tiny probe budgets (Claude Desktop's model-availability probe uses max_tokens=1). The Anthropic→Responses conversion copied the value verbatim, so strict upstreams returned 400 and Claude Desktop reported the mapped model as unavailable under local routing.
Clamp 1..=15 up to 16 during conversion; 16+, 0 and non-integer values keep their pass-through semantics. Normal session requests convert byte-for-byte as before, and the Codex OAuth path still strips max_output_tokens afterwards.
Fixes #7103
fix(codex): coalesce adjacent commentary with pending tool calls (#7280)
The Codex Responses -> Chat Completions converter emitted two consecutive
assistant messages when one Responses model turn contained a commentary
message directly followed by function_call items. Chat upstreams imitate
the text-only assistant message as a complete turn and return
finish_reason=stop before the expected tool call, so long agent tasks end
after a progress update.
Merge pending tool calls into the directly adjacent assistant message when
it carries no tool_calls yet, attach pending reasoning per segment so
parallel calls do not duplicate it, and keep the new-message path for every
other boundary shape (user/tool boundaries, prior tool-call batches, media
flushes). The reasoning placeholder backfill still applies to coalesced
calls without reasoning.
Fixes #6529
Supersedes #6530
Co-authored-by: BigStrongSun <BigStrongSun@users.noreply.github.com>
fix(proxy): skip empty reasoning_content placeholders in Anthropic SSE streaming (#7227)
Some OpenAI-compatible upstreams (GLM, Qwen, DeepSeek-V4-Pro via ModelScope/DashScope,
Bailian, JD Cloud, ...) keep a `reasoning_content: ""` placeholder in every content
chunk instead of omitting the field. The reasoning branch of the OpenAI Chat -> Anthropic
SSE converter only checked for presence, and it shares `current_non_tool_block_type`
with the content branch, so every content chunk closed the open text block, opened an
empty thinking block, then reopened a text block. N content chunks became 2N content
blocks, half of them empty thinking blocks, which Claude Code rendered as one line per
token with dozens of empty Thought blocks and bloated the session JSONL.
Filter empty reasoning before entering the branch, matching the `!content.is_empty()`
guard the content branch already had and the non-streaming `openai_to_anthropic`
converter. Two regression tests pin block index, type and order: the placeholder shape
must yield a single text block and no thinking_delta at all, and real reasoning must
still yield one thinking block followed by one text block.
Fixes #5028
Fixes #4404
fix(codex): detect growing rollouts with a persisted byte cursor (#7219)
Codex keeps the rollout append handle open for the whole session, so on Windows NTFS the file mtime does not advance while the file grows and the pure-mtime gate skipped growing rollouts forever. Persist the observed byte length in session_log_sync.last_byte_offset for Codex rows and skip a file only when both mtime and size are unchanged. The parser now splits records on newlines manually so an incomplete trailing record is retried on the next pass without advancing the line cursor.
Fixes #6060
fix(codex): proxy image edits and Images API follow-ups (#7177)
* fix(codex): proxy image edits and Images API follow-ups
Follow-ups to #7036 for Codex's built-in ImageGen over the local proxy:
- Register `/images/edits` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them through the shared Codex standalone passthrough.
ImageGen posts to `images/edits` whenever a call references existing
images, and the router returned an empty 404 for it.
- Add `CodexStandaloneEndpoint::ImagesEdits`. Both Images endpoints share
one full-URL suffix table, so a complete Responses / Compact / Chat
Completions / Images URL derives the sibling endpoint (and vice versa)
while opaque full URLs keep failing closed.
- Apply the standalone sibling rewrite when the base URL is pasted as a
complete endpoint with the full-URL switch off, mirroring
`codex_chat_base_is_full_endpoint`; standalone requests no longer become
`.../chat/completions/images/generations`.
- Parse Images API streaming usage: `image_generation.completed` carries
`usage` on the event top level, so scan for `usage.input_tokens` before
falling back to the Chat Completions stream parser.
Related #7036
* fix(codex): match standalone URL suffixes case-insensitively
Keep endpoint detection and sibling rewriting consistent while preserving the original URL prefix and query. Cover mixed-case Images and Alpha Search endpoints through URL and proxy regression cases.
---------
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): preserve per-app settings on shutdown and port allocation (#7210)
On normal exit, stop_with_restore_keep_state() called the legacy global
proxy-config writer to clear the long-retired live_takeover_active flag.
That writer reads the Claude row and runs an UPDATE without a WHERE clause,
so every shutdown copied Claude's max_retries and the three timeout fields
onto the Codex, Gemini and Grok Build rows — and the UPDATE never carried
live_takeover_active in the first place, so the step had no effect beyond
the clobber. The ephemeral listen-port path did the same on every proxy
start.
Drop the dead write-back (live-config restore, backup cleanup and health
reset are unchanged) and persist the resolved ephemeral port through the
global-config interface, which only touches the shared listen/logging
columns. Two regression tests seed distinct per-app settings for all four
apps and assert they survive both paths.
Fixes #7204
fix(provider): preserve child metadata during universal sync (#7212)
Syncing a universal provider regenerates its Claude/Codex/Gemini child
providers. The child's `settings_config` was merged with the existing row,
but `meta`, `created_at` and `sort_index` were taken from the generated
object, and the universal provider never carries any of them from the UI.
The UPDATE therefore wiped the child's usage script, common-config opt-out,
endpoint auto-select and other per-app settings to `{}` and reset the sort
index to NULL, which pushed the card to the bottom of the list and looked
like the provider had been deleted and re-created.
Keep the existing child's `meta`, `created_at` and `sort_index` when the
row already exists. Name, base URL, key, models, website and notes are
still driven by the universal provider, and first-time creation still
inherits the parent's metadata.
Fixes #7134
feat(presets): rebrand DashScope as 千问AI平台 and refresh Qwen models (#7183)
Rename the domestic Bailian presets to 千问AI平台 with a dedicated icon, refresh the Qwen family to the 3.8 generation across Claude Code, Claude Desktop, Codex, Hermes, OpenClaw, OpenCode and Pi, add the domestic Token Plan presets, restore the international QwenCloud Coding Plan presets, and align OpenClaw/OpenCode/Pi model metadata with the official client docs.
fix(codex): honor proxy URL when model_provider is omitted (#7263)
When a Codex provider card's TOML has no `model_provider`, Codex falls back to
its built-in `openai` provider, but takeover wrote the proxy address as a
top-level `base_url` that Codex never reads, so requests bypassed the local
proxy and went straight to api.openai.com.
Treat the missing selector as the built-in `openai` provider and write the
proxy address to `openai_base_url`; the existing legacy-reroute migration then
turns it into a `cc-switch` custom provider table carrying the `PROXY_MANAGED`
bearer, the same shape every other third-party takeover uses. Explicit
provider selections are unchanged.
Refs #6256, #7217
fix(provider): update MiniMax defaults to M3 and remove expired offers (#7255)
* fix(provider): refresh MiniMax M3 presets and remove expired promotions
* fix(provider): complete MiniMax M3 capability declarations
---------
Co-authored-by: saladday <1203511142@qq.com>
chore(provider): remove Atlas Cloud sponsor status, keep presets
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`
docs(release): add v3.20.2 release notes
chore(release): v3.20.2
feat(provider): add SoleAPI sponsor presets across all nine apps
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.
fix(codex): proxy image edits endpoint
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.
fix(provider): pin models URL for JieKou and Novita Claude presets
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.…
重放 fork 提交到 upstream/main(f3b18df1,v3.20.2)后统一版本号。
Cargo.lock 同步移除 updater 依赖树(fork 已禁用应用内更新器)。
chore(native): archive tray-left-click-toggle-window
chore(comet): 记录 tray-left-click-toggle-window 验收通过与归档状态
feat(tray): 托盘图标左键单击切换主窗口显示/隐藏
- tray.rs 新增 show_main_window/hide_main_window/toggle_main_window 助手:
显示语义与"打开主界面"一致,隐藏与"关闭到托盘"平台处理一致
(Windows set_skip_taskbar、macOS apply_tray_policy)
- lib.rs 托盘 show_menu_on_left_click 改为 false,左键 Click(Left, Up)
触发切换(过滤 Down/Up 双事件),右键仍弹菜单;Enter/Click 用量刷新保持
- show_main 菜单事件与 macOS Reopen 复用 show_main_window
docs(readme): 更新日志补充连通性测试、双官网链接与托盘左键切换
feat(model-quick-switch): 获取模型列表成功后自动展开选择器
SearchableModelPicker 增加可选 open/onOpenChange 受控展开(不传保持内部自管,
表单等既有用法不受影响);快捷切换弹窗拉取成功且列表非空时直接展开,关闭弹窗复位。
chore(native): archive fix-quick-switch-1m
chore(comet): 记录 fix-quick-switch-1m 验收通过与归档状态
fix(model-quick-switch): 补齐 Haiku 显示名同步,支持不选模型仅翻转 1M
- 显示名同步映射补上 ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME(沿用空/旧base/旧base[1M] 覆盖、自定义保留语义)
- 新增 setClaudeOneMInSettings:仅原地翻转 1M 标记,不改各角色模型 base 与显示名
- 弹窗打开时 1M 开关按供应商当前状态初始化,避免已开 1M 被静默剥离
- claude 未选模型且 1M 状态有变化时允许保存;无可应用变化时保存禁用
- 补单测与四语言 toast 文案
feat(provider-website-links): 供应商支持第二个官网链接,主页横排双链接
- Rust Provider/UniversalProvider 新增 website_url_2(serde websiteUrl2,skip none)
- SQLite providers 表新增 website_url_2 列(schema v18→v19 幂等迁移 + create_tables 补列 + 旧 config.json 迁移)
- DAO SELECT/INSERT/UPDATE/replace 全链路带出新列
- 表单共享 BasicFormFields + 统一供应商表单第二输入框;zod 同第一链接规则校验与回填
- ProviderCard 主页双链接横排、两空格分隔、逐链接 openExternal 打开默认浏览器
- 搜索匹配、复制供应商、统一供应商生成 claude/codex/gemini 随行透传
- gemini_auth packycode 认证检测纳入 website_url_2;ClaudeDesktop/GrokBuild 预设切换清空第二链接;卡片同值链接去重
- 4 语言 i18n + 前端/前端单测覆盖
Comet change dual-website-links-homepage(19/19 验收通过,归档)
feat(connectivity-test): 拉取模型列表改为「搜索添加」选择器交互
- 新增共享组件 SearchableModelMultiPicker:搜索过滤 + 多选 + 已在列表标记 + 已选计数
- ConnectivityTestDialog 获取模型列表后不再全量追加合并去重,改为会话内缓存完整列表并自动弹出一次选择器;用户搜索挑选后添加进表格并自动勾选
- 重新拉取只刷新缓存,已添加进表格的行保留;拉取结果仍仅会话内有效,不写回供应商配置
- 四语言 i18n 新增 searchAdd / addToTest / alreadyInList / selectedCount
- 同步更新 ConnectivityTestDialog 测试并新增 SearchableModelMultiPicker 测试
feat(connectivity-test): 探测目标按供应商 API 格式对齐真实转发链路
- build_claude 按 get_claude_api_format 分派端点与请求体:openai_chat → /v1/chat/completions(复用 transform_claude_request_for_api_format,流式注入 stream_options.include_usage)、openai_responses → /v1/responses、gemini_native → /v1beta/models/{model}:{method}(resolve_gemini_native_url)、anthropic 保持 /v1/messages 并补 anthropic-version/beta 头
- build_codex 按 should_convert_codex_responses_to_chat/anthropic 分派:chat → /chat/completions(responses_to_chat_completions_with_reasoning)、anthropic → /v1/messages(responses_request_to_anthropic,含 max_output_tokens 注入与 [1m] 剥离 + context-1m beta 头)
- 鉴权改用 ClaudeAdapter/CodexAdapter 的 extract_auth + get_auth_headers(与真实出站请求同源:AUTH_TOKEN→Bearer、ANTHROPIC_API_KEY→x-api-key、gemini→x-goog-api-key、GeminiCli OAuth→Bearer、OpenRouter→Bearer、Codex 按 apiKeyField)
- 转换前剥离 [1m] 标记;meta.isFullUrl 或 base 已是完整端点时直接使用 base_url;自定义 body 浅合并移至转换后的最终协议体
- 新增 18 个目标构造断言单测;Comet 归档产物同步提交
chore: archive connectivity-test-optimizations
feat(connectivity-test): 单一模型表格 + 实时拉取模型 + 勾选即测试范围 + 新默认提示词
- 弹窗合并为单一模型表格:打开即全部模型行(初始待测试)、行勾选 + 表头全选(含半选态)、5 格统计卡,移除上方独立选择区
- 获取模型列表:凭据提取抽为共享模块 src/utils/providerCredentials.ts(ModelQuickSwitchDialog 同步改引用),复用 fetch_models_for_config 拉取 /v1/models,按 ID 追加合并去重、仅会话内有效
- 默认提示词改为「你好,你可以帮我做什么事情」:前后端常量同步,merge 差异判断基准跟随
- 勾选即测试范围:后端 connectivity_test_provider_models 新增 modelIds 参数(None/空白回退静态清单),前端逐模型独立 invoke 并行 + 行级流式更新
- 一轮测完自动勾选失败项,全部成功保持原勾选
fix(connectivity-test): UI 四项修复
- 弹窗抬至 alert 层(z-[60]):打开连通性测试时 header 切换栏正确变灰,
详情子弹窗同步抬层(同 z + 更晚挂载 portal 在上)
- 批量检测入口移至 header 图标组(状态提升到 App,ProviderList 经
props 读取探针结果),不再另起一行
- 批量检测可停止:runId 代次守卫(Tauri invoke 无法真正取消),再次
点击即清除 waiting/running 徽标、丢弃在途迟到写入、不再拉起排队项
- 弹窗打开自动勾选模型:已保存默认测试模型 → 供应商当前模型
(claude env.ANTHROPIC_MODEL / codex config.toml model,新增
currentProviderModelId 与后端同口径)→ 清单首个
前端全量 141 文件 / 1106 测试全绿;typecheck clean
chore: archive per-model-connectivity-test
fix(connectivity-test): shouldShowTestEntry 增判 providerType + 收尾清理
chore(connectivity-test): 勾选 Task 9 / OpenSpec 5.1-5.2 + verify 报告
chore(connectivity-test): 勾选 Task 8 / OpenSpec 4.1-4.3
refactor(connectivity-test): 移除旧 stream_check 可达性探测全链路(保留表结构)
- 删除 6 个文件:commands/stream_check.rs、services/stream_check.rs、
database/dao/stream_check.rs、hooks/useStreamCheck.ts、lib/api/connectivity-check.ts、
components/usage/ConnectivityCheckConfigPanel.tsx
- 移除 mod.rs 声明、lib.rs generate_handler! 注册、cleanup_old_stream_check_logs 调用
- 从 SYNC_SKIP_TABLES / SYNC_PRESERVE_TABLES 移除 stream_check_logs(表结构保留在 schema.rs)
- 更新 backup.rs 测试:移除 stream_check_logs fixture 与断言
- 更新 SettingsPage:移除 ConnectivityCheckConfigPanel AccordionItem 与 FlaskConical 导入
- i18n:删除 streamCheck.* 命名空间(zh/en/ja/zh-TW 四语言)
- 更新注释:移除 stream_check 旧引用(forwarder.rs、provider.rs、userAgentPresets.ts、
connectivity_test.rs)
- 修复 4 个 pre-existing clippy lint(connectivity_test: matches! 宏、unused_unit、
derive Default)
- 移除 ProviderList.test.tsx 中已失效的 useStreamCheck mock
chore(connectivity-test): 勾选 Task 7 / OpenSpec 2.4 3.1-3.3
feat(connectivity-test): 供应商列表集成(弹窗入口 + 批量探针徽标)
chore(connectivity-test): 勾选 Task 6 / OpenSpec 2.2-2.3
fix(connectivity-test): 模型列表解析与后端对齐(每条目首个匹配,提取共享 helper)
feat(connectivity-test): 连通性测试弹窗/详情/徽标组件与四语言文案
chore(connectivity-test): 勾选 Task 5 / OpenSpec 2.1
test(connectivity-test): 锁定 toBackendParams 字段映射(customHeaders/customBody 等)
feat(connectivity-test): 前端命令绑定与测试/探针状态 hooks
chore(connectivity-test): 勾选 Task 4 / OpenSpec 1.4
feat(connectivity-test): 测试参数持久化纯函数(缺省即默认值)
chore(connectivity-test): 勾选 Task 3 / OpenSpec 1.3
feat(connectivity-test): Tauri 命令与注册,官方/动态端点过滤,sanitize 隔离私有字段
chore(connectivity-test): 勾选 Task 2 / OpenSpec 1.2
feat(connectivity-test): 后端直连执行器(reqwest+SSE,总/空闲超时,度量与错误识别)
feat(connectivity-test): 后端直连执行器(reqwest+SSE,总/空闲超时,度量与错误识别)
chore(connectivity-test): 勾选 Task 1 / OpenSpec 1.1
fix(connectivity-test): claude 鉴权对齐 adapter 顶层 apiKey/api_key 兜底
feat(connectivity-test): 新增后端连通性测试服务类型/模型解析/协议构造
chore: archive model-quick-switch-picker-scroll
chore: model-quick-switch-picker-scroll 验证报告与状态元数据
fix: 模型快捷切换弹窗内模型列表滚轮无法滚动
模态 Dialog 的 RemoveScroll 会对 shards 外的 portaled PopoverContent
wheel 事件无条件 preventDefault。在 SearchableModelPicker 的
PopoverContent 捕获阶段阻断 wheel 传播,恢复原生滚动。
chore: bump fork version to 3.20.1-1 after upstream v3.20.1 rebase
fix(form): only auto-expand Claude advanced options on divergent model mapping
Uniform mapping produced by the quick-set button (all roles equal to the
fallback model) no longer counts as an advanced value, so the advanced
section stays collapsed on edit. A 1M marker is stripped before comparing
so marked roles are not misjudged as divergent. Truly customized mappings
still auto-expand the section.
feat: align fallback-model quick-set buttons with model input right edge
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
docs: update README with fork build notes and quick-move provider changes
chore: archive comet-native claude-model-quick-access
feat: surface Claude fallback model as quick-access section below endpoint
Move the default fallback model (ANTHROPIC_MODEL) block out of the
collapsed Advanced Options into a quick-access section directly below
the request endpoint: input + 1M checkbox + quick-set + fetch-models
buttons are all reachable without expanding.
- header toggle-all "declare 1M" now includes the fallback model
(Haiku still excluded); partial selection shows indeterminate state
- drop the duplicated quick-set/fetch buttons and the old fallback
block inside Advanced Options
- exclude claudeModel from hasAnyAdvancedValue so a fallback-only
value no longer auto-expands Advanced Options
- quick-set priority, env write semantics, and OAuth preset branches
unchanged; add 4 component tests covering the new layout
chore: archive comet-native provider-right-click-top-bottom
Enable native workflow in .comet/config.yaml and move the Comet managed
state block in .gitignore so the exclusion rules stay grouped with the
other fork tool artifacts.
feat: add provider right-click move-to-top/bottom and insert new providers at second position
- ProviderList: context menu with quick move-to-top/move-to-bottom, reusing
update_providers_sort_order and refreshing tray menu like drag sort
- useAddProviderMutation: default insert at index 1 with explicit sortIndex
materialization for existing items (avoids defined-first sort trap)
- i18n: quickMove* strings for zh/en/zh-TW/ja
ci: stop flaky WSL2 linker job from blocking main
The backend-windows-wsl2 job intermittently fails with LNK1327 /
mt.exe c1010070 when the Windows runner's mt.exe cannot parse a
manifest temp file on the WSL2 UNC TEMP path. The same flaky failure
occurs upstream and is unrelated to fork changes. Mark the job
continue-on-error so main stops going red on it; the result stays
visible in the Actions UI.
style: apply prettier to files touched by recent comet archives
Frontend CI format:check failed on 6 files modified by the app-name
and model-quick-switch archive commits plus the updater-disable commit.
Run prettier --write so format:check passes again.
chore: archive ccs-fork-app-name-cc-switch
chore: archive ccs-model-quick-switch-ui-polish
docs: rewrite README to document fork differences vs upstream
README 从上游完整功能介绍改为仅记录 fork 与上游的差别,完整功能
文档指回上游。涵盖 8 项 fork 魔改:构建标识/开发预览、预设精简、
Claude 表单调整、模型搜索移植、主页模型快速切换、侧边栏面板显隐、
关闭应用内更新、打包范围,并说明版本号与 rebase 同步流程。
feat: disable in-app updater, point users to GitHub releases
移除 tauri-plugin-updater 依赖与自动更新链路,改为统一引导用户到
GitHub Releases 手动下载新版。
- 前端:updater.ts 恒返回 up-to-date;UpdateContext 取消启动自检;
AboutSection「检查更新」直接打开 fork releases 页;DatabaseUpgrade
恢复界面去掉下载进度条,改为打开发布页。
- 后端:移除 updater 插件注册与 UpdaterExt 调用;
install_update_and_restart 降级为空壳,check_app_update_available
返回当前版本号;check_for_updates 指向 fork releases;capabilities
去掉 updater:default 权限。
- 打包:tauri.conf.json 删 plugins.updater、createUpdaterArtifacts=false;
Cargo.toml / package.json 删 updater 依赖,Cargo.lock / pnpm-lock
同步重生成。
- CI:release.yml 移除签名收集与 assemble-latest-json job,删除
.tar.gz/.sig 处理,release body 去掉 updater 说明。
版本号统一为 3.20.0-1(合法 semver 且 MSI 第 4 位为 1)。
Cargo.lock / pnpm-lock.yaml 已随依赖移除重生成,cargo check 通过。
ci: build only Windows x64 and macOS arm64, unsigned
feat: gate dev panel behind dev-only env flag
chore(comet): archive ccs-sidebar-panels-visibility change
fix: apply skills visibility guard to hermes branch toolbar button
feat: include prompts in sidebar panel visibility settings
fix: persist visibleSidebarPanels setting in backend AppSettings
style: use compact pill toggles for sidebar panel visibility
feat: add sidebar panel visibility switches to settings
feat: add sidebar panel visibility settings and wire into toolbar buttons
chore(comet): archive ccs-home-model-display change
fix(fork): read grokbuild api key from config TOML in quick switch dialog
feat(fork): align model badge with upstream extractModelBadge
feat(fork): show current model badge and quick-switch entry on provider cards
feat(fork): add model quick switch dialog
feat(fork): add provider model read/write utils with tests
chore(comet): archive ccs-model-search-port change
feat(i18n): add model search picker keys for all locales
feat(form): port SearchableModelPicker from upstream for searchable model selection
chore(comet): archive ccs-claude-form-layout change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-claude-form-layout/
- Sync claude-form-layout spec to main specs
- Annotate design doc as archived
- Add verification report
fix(i18n): update advanced options hint, add ja/zh-TW toggle-all, drop dead apiFormatHint key
fix(form): correct 1M toggle-all alignment direction (ml-1)
fix(form): align 1M toggle-all checkbox with row checkboxes
refactor(form): move api format select into EndpointField rightSlot
fix(form): widen api format select to fit option text
feat(form): add 1M toggle-all checkbox in model mapping header
feat(form): move api format select next to endpoint input
chore(comet): archive ccs-provider-presets-trim change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-provider-presets-trim/
- Sync provider-presets-visibility spec to main specs
- Annotate design doc as archived
- Add verification report
docs(fork): add presets-trim files to rebase whitelist
test(fork): mock IS_FORK_BUILD false in unit tests for upstream-form semantics
feat(fork): wire official preset filter into provider forms
feat(fork): add hidden field to preset interfaces
feat(fork): add official preset allowlist and filter util
chore(comet): archive ccs-fork-scaffolding change
- Move change artifacts to docs/openspec/changes/archive/2026-08-18-ccs-fork-scaffolding/
- Sync fork-build-identity spec to main specs
- Annotate design doc as archived
- Add verification report
feat(fork): add dev config with isolated identifier and config dir
docs(fork): complete rebase whitelist with all fork-touched files
fix(fork): guard setTitle with isTauri in test/non-tauri env
fix(fork): define __CCS_FORK_BUILD__ in vitest config
feat(fork): add DevPanel and settings footer entry
feat(fork): set macOS window title to CC Switch (Fork) on startup
feat(fork): inject __CCS_FORK_BUILD__ compile-time constant
feat(fork): bump version to 3.19.2-fork.1 and rename product to CC Switch (Fork)
docs(fork): add upstream rebase workflow and ignore tool artifacts
fix(proxy): accept the whole grok-4.x (x>=5) family in the reasoning-effort whitelist (#7369)
Replace the verbatim grok-4.5 / grok-4.6 entries in supports_reasoning_effort with a rule that parses the grok-4.x minor version and accepts x >= 5, mirroring the existing GPT-5+ rule. This covers grok-4.7 (released 2026-09-21), whose reasoning effort was previously dropped on the Claude -> Chat, Claude -> Responses and Codex Responses -> Chat conversion paths, and lets future releases pass without another whitelist edit. The grok-build-* family is retained for saved providers.
Co-authored-by: allenxu09 <171831965+allenxu09@users.noreply.github.com>
docs(release): add v3.20.4 release notes
chore(release): v3.20.4
feat(pricing): add Grok 4.7 and GLM-5.3-FlashX
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.
fix(i18n): remove outdated OpenCode Go referral offer
fix: add SudoCode.chat alternate API endpoint
feat(settings): add a GitHub star prompt to the About card
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
feat(presets): add Soshow aggregator presets
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.
fix(ci): stabilize frontend tests and verify renderer build
fix(mcode): refresh provider state after removal (#7578)
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
fix(lib): reset skip_taskbar before window.show() across entry points (#6348)
5处 window.show() 调用中 4处没有在 show() 前重置 Windows 的 skip_taskbar,导致以下场景出现窗口可见但不在任务栏、点最小化后窗口消失的 bug:
- single_instance handler (lib.rs:360-361):当原进程因 silent_startup / 关闭到托盘等处于 skip_taskbar(true) 状态时,从快捷方式/开始菜单唤起已运行实例触发。
- handle_deeplink_url 的 focus_main_window 分支 (lib.rs:256):通过 ccswitch:// URL 唤起时。
- DB 恢复流程 (lib.rs:556):数据库版本不匹配时显示恢复界面。
- RunEvent::Opened 处理器 (lib.rs:1803):系统接收到 deep-link URL 时。
修复后的行为与 tray.rs::handle_tray_menu_event::show_main 分支一致(那里已有相同的 set_skip_taskbar(false) 调用)。
经用户测试验证:autostart + silent_startup + 从开始菜单/快捷方式启动的场景下,最小化按钮恢复正常工作。
fix(proxy): omit missing tool descriptions in the Codex responses-to-chat converter (#7378)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): preserve max effort for GPT-5.6 and GPT-6 Astra (#7531)
fix(misc): ignore shell startup output when probing tool versions in WSL (#7348)
try_get_version_wsl runs "${SHELL:-sh}" -lic '<tool> --version' through
wsl.exe and takes the first x.y.z anywhere in stdout. A login+interactive
shell runs the distro's startup files first, so their output shares that
stdout. On Ubuntu, /etc/profile.d/update-motd.sh prints the MOTD once a day,
starting with "Welcome to Ubuntu 24.04.4 LTS", and the card then showed
24.04.4 as the Claude Code version. The once-a-day stamp makes it appear
after the first probe of the day and vanish on refresh.
The payload now prints a sentinel before running the tool, and parsing looks
only at what follows its last occurrence. The last occurrence handles the
-lic || -lc || -c fallback chain printing it several times; matching it as a
substring handles OSC sequences glued in front of it. If the sentinel is
absent (the shell failed before the payload ran), the output is used as
before. The stderr fallback for tools that print their version there is kept.
Fixes #7347
Co-authored-by: lyfuci <lyfuci11@gmail.com>
fix(claude): hide session URL attribution (#7053)
* fix(claude): hide session URL attribution
* docs(claude): document session URL attribution
fix(ui): reset provider scroll when switching apps (#5211)
fix(pi): fix provider logo (#6826)
fix(i18n): use the Pi form's own term for the zh-TW duplicate-key message (#7187)
Follow-up to the nit on #6768. In zh-TW the Pi form labels the field
供應商識別碼 and says 供應商識別碼為必填 when it is empty, but the
duplicate message I added said 供應商標識 — the term Hermes, OpenCode and
OpenClaw use for their own field. One form, two names for the same thing.
The other three locales already keep the label and both messages on one
term, so this makes zh-TW match the pattern rather than introduce one:
en Provider key / Provider key is required / This provider key already exists
ja プロバイダーキー / プロバイダーキーを入力してください / このプロバイダーキーは既に存在します
zh 供应商标识 / 供应商标识不能为空 / 该供应商标识已存在
zh-TW 供應商識別碼 / 供應商識別碼為必填 / 該供應商標識已存在 <- the odd one out
hermes.form.providerKeyDuplicate carries the same sentence and is left
alone: its own label is 供應商標識, so it is already consistent.
Verified: managementListLocales 7/7. Full unit suite 1085/1087, the same
two App.test.tsx integration cases failing before and after.
fix(proxy): strip stop param for GitHub Copilot to unblock auto mode classifier (#5404)
Claude Code's auto mode classifier sends `stop_sequences` on every
safety check. On the GitHub Copilot path the Anthropic -> OpenAI Chat
conversion mapped it to `stop`, and Copilot's Chat Completions route
for Claude models answers that request with HTTP 400, so every
classifier call failed while normal chat kept working.
- Remove the converted `stop` field only when the provider is GitHub
Copilot; other OpenAI-compatible upstreams keep it.
- Copilot detection in the transform now also covers providers that
are recognised only through `env.ANTHROPIC_BASE_URL`.
- Regression tests for the Copilot (meta and env URL) and generic
provider cases.
Fixes #5175
fix(codex): keep stored auth when the live snapshot has no credential (#7434)
Editing the current Codex provider seeds the form from the live ~/.codex
snapshot, and switching away backfills that snapshot into the DB row. When a
provider table declares its own credential source (an Authorization header,
env_key, auth/aws), no bearer token is injected into config.toml and the
shared auth.json is deleted on third-party switches, so the live snapshot is
permanently `{ auth: {}, config }` while auth.OPENAI_API_KEY in the DB is the
only copy of the key. Both paths treated that empty live auth as
authoritative and silently erased the stored key, which in turn made the
usage-script modal misread the provider as an official subscription and
reset the saved script.
- Treat a credential-less live auth (missing auth.json or the bare
auth_mode logout marker) as an absent field in both the edit dialog and
the switch-away backfill, keeping the stored auth template. Live still
wins whenever it carries material, and official providers keep the live
login as their source of truth.
- Gate the official exemption on is_codex_official_provider /
resolveCodexOfficialIdentity rather than the category alone: a legacy
fixed official card without a persisted category is still written as
official, so the fallback must not resurrect a logged-out login on
switch-back.
- Cover both layers with regression tests, including the category-less
official shapes.
Fixes #7433
Supersedes #7492
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): lift additional_tools input carriers on the Codex chat conversion (#7454)
Codex talks to third-party providers over its Responses Lite transport,
which ships the request's extra tools in an `additional_tools` input
carrier: `{"type":"additional_tools","role":"developer","tools":[...]}`,
a role but no `content`. The Responses->Chat conversion let that carrier
fall through the generic "item with a role is a message" arm and emitted
`{"role":"system","content":null}` at messages[1], which strict Chat
gateways reject with `messages[1]: missing field "content"` (or
`data did not match any variant of ...SystemMessageContent`). The tools
carried inside it were also dropped on the Chat and Anthropic conversions.
- Skip `additional_tools` items in the Chat message walk; a carrier
declares tools, it is not a message.
- Collect carrier tools through the existing CodexToolContext registry
(dedup, namespace flattening, custom-tool conversion), which also feeds
the Anthropic conversion and the response-side name round-trip.
- Requests without a carrier convert byte-for-byte as before.
Known boundary: `custom` children inside a namespace tool
(`functions.exec`) are still not lifted; that needs its own response-side
name round-trip and is left for a follow-up.
Diagnosed independently in #6158 by BigStrongSun (fix in #6159) and in
fixture.
Fixes #7451
Fixes #6158
Refs #6033
Refs #6776
Supersedes #6159
fix(proxy): normalize Codex detail:original images for Chat gateways (#7476)
Codex marks tool-returned images with detail:"original" whenever the model
catalog advertises supports_image_detail_original, a Responses-only value.
The Responses->Chat conversion copied it verbatim into image_url.detail, and
strict OpenAI-compatible Chat gateways (Ark/Doubao, SenseNova, GLM, vLLM, ...)
reject the whole request with 400. Because Codex replays the full rollout on
every turn, one poisoned view_image result made the session fail permanently,
including plain-text turns.
Two layers:
- tool_media::merge_top_level_detail downgrades original -> auto, so already
poisoned rollouts and Codex versions before 0.155.0 (which do not normalize
replayed history themselves) recover immediately. auto/low/high are untouched.
- codex_catalog_model_entry always writes supports_image_detail_original:false
on the ProxyChat profile, so Codex stops emitting original at the source once
the catalog is regenerated. NativeResponses/Anthropic profiles and the DeepSeek
official catalog are unaffected.
Top-level user images were never affected: responses_content_to_chat_content
drops the part-level detail entirely.
Fixes #6495
Fixes #7458
Refs #6569
Refs #5805
fix(omo): detect unified config on the WSL side when the OpenCode dir is in WSL (#7550)
When the OpenCode config directory points into WSL
(\\wsl.localhost\<distro>\home\<user>\.config\opencode), OMO >= 4.19.3 keeps
its unified config in the WSL-side home (~/.omo/omo.jsonc). Detection only
probed the Windows home, never found it, and silently fell back to the legacy
per-plugin file that OMO no longer reads, so provider switches looked
successful but had no effect.
- Add derive_wsl_home_dir to derive the WSL-side home from a WSL UNC path
(home/<user> or root); non-WSL and non-home paths return None.
- Probe a list of home candidates for the unified config, WSL-side home
first and the OS home as a fallback, shared by the read, write and delete
paths.
- Detection only: when no unified config exists on either side the legacy
fallback is unchanged.
Fixes #7363
Supersedes #7367
docs(partner): add a FluxA purchase link and tighten its sponsor blurb
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.
fix(claude-desktop): expose the 1M context variant for DeepSeek V4 routes
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.
feat(presets): add FluxA Token Plan partner presets
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.
fix(presets): correct AICodeWith Codex-family endpoint to /v1
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.
fix(ui): render monochrome preset icons in the foreground color
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.
docs(user-manual): point Chat-only Codex examples at plan endpoints
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.
fix(pricing): restore DeepSeek V4 Pro price and seed Qwen3.8 and Hy4 models
- 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).
fix(presets): move MiniMax CN and BaiLing to their current official endpoints
- 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.
feat(codex): refresh CN presets from the Responses API audit
- 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.
test(claude-desktop): run Linux path tests on unix hosts and cover Linux in provider sync tests
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.
fix(ui): stop expanding stale cached usage tiers on ineligible provider cards
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.
fix(skills): 支持 skillId 与目录名不一致的技能安装和更新 (#6381)
* fix(skills): resolve mismatched skillId directories
* fix(skills): preserve source path during updates
* fix(skills): refresh source URL after repository relocation
* test(skills): isolate relocation fixture from legacy home
feat(opencode): 支持从获取的模型列表中搜索并批量添加所选模型 (#7515)
* feat: implement fetched models selection and UI components
* fix(opencode): prevent model search from submitting provider form
Add Kimi Global preset variants (kimi.ai platform + api endpoints) (#7526)
Add a "Kimi Global" twin immediately after the domestic "Kimi" API preset and a "Kimi For Coding Global" twin after the domestic "Kimi For Coding" preset, in every preset file that ships them (claude, claude-desktop, codex, hermes, openclaw, opencode, pi).
"Kimi Global" points to the international open platform https://platform.kimi.ai?aff=cc-switch (apiKeyUrl mirrored as https://platform.kimi.ai/console/api-keys?aff=cc-switch) and swaps the endpoint host api.moonshot.cn for api.moonshot.ai with each file's path unchanged (/v1 or /anthropic).
"Kimi For Coding Global" points to the international site https://www.kimi.ai/code/?aff=cc-switch and swaps the endpoint host api.kimi.com for api.kimi.ai with the path unchanged (e.g. https://api.kimi.ai/coding/v1).
Domestic presets are unchanged; category, icons, apiFormat, model catalogs, endpointCandidates (host swapped), partnerPromotionKey and settings otherwise mirror the domestic blocks exactly.
primePartner stays only on the domestic presets so the new variants do not alter prime-partner ordering.
The Pi twins get distinct providerKey values ("cc-switch-kimi-global", "cc-switch-kimi-for-coding-global") because providerKey is the preset-level primary key and must not collide with the domestic presets.
Rebalance sponsor CTAs around Kimi Code plan with dual-region links (#7522)
Kimi Code plan purchases have resumed, so the README sponsor sections go back to leading with the coding plan instead of the API-only flow written during the purchase pause.
All four locales (EN/ZH/JA/DE) now present Kimi Code plan (中文站 | Global) as the primary CTA and the Kimi Open Platform API (中文站 | Global, with the first-top-up bonus) as the secondary one, mirroring the domestic|global split already used for API links.
The aff and track_id values are unchanged.
fix(prompts): refresh active prompts after external file edits (#7194)
Reload the enabled prompt from its live file when the Prompts list loads, and refresh the open panel when the window regains focus, so external edits to CLAUDE.md / AGENTS.md show up without a restart.
- Keep saved templates available when the live file cannot be read.
- Skip the backfill for empty or whitespace-only files, matching enable_prompt.
- Skip the backfill while a sync/restore holds the sync lock, so a list load cannot write a stale live file into a freshly restored database.
fix(skills): raise archive entry limit so large skill repos install (#7489)
Installing hugohe3/ppt-master from skills.sh failed with
ARCHIVE_TOO_MANY_ENTRIES: the GitHub archive we download is the whole
repository (13,248 entries), and the 10,000-entry cap added in #5811
treated it as a zip bomb.
- Raise MAX_ARCHIVE_ENTRIES to 30,000 for both the remote archive and
local ZIP paths, and align WebDAV's MAX_EXTRACT_ENTRIES so a skills.zip
containing such a skill still restores.
- Charge every file entry at least one 4 KiB block against the existing
512 MiB budget, mirroring directory accounting, so empty-file archives
are bounded by the byte budget rather than by the entry count alone.
- Add tests for a 13k-entry repo archive and for the per-file minimum
charge.
Fixes: https://github.com/farion1231/cc-switch/issues/7475
feat: add MiniMax Code harness support (#7383)
Add MiniMax Code (mcode) as a managed application: provider and model management through the native custom_provider configuration, MCP servers, Skills, global instructions (AGENTS.md), read-only local session history with resume, and usage import from the native runtime database. Native files shared by the TUI and desktop are written under the native lock with rollback when the database commit fails, unrelated native configuration is preserved, and the data directory honors MINIMAX_DATA_DIR and MAVIS_DATA_DIR. Deleting sessions stays in the native application because they are coupled to runtime state.
fix(codex): recover stale account bindings during takeover (#7395)
After deleting a managed ChatGPT account and logging in again, a Codex provider can still reference the old local account ID, which made route switching, takeover, startup restoration and rebinding fail permanently.
Distinguish a missing outgoing account from an existing account with no matching live token. A missing account only releases its ownership marker after the persisted account store is verified; the target write policy stays in force. A stale target now reports an account-selection instruction instead of a bare not-found error, including when takeover is already enabled.
Fixes #7392
feat(claude-desktop): support 3P configuration on Linux (#7331)
Add Claude Desktop 3P configuration support on Linux. Resolve the config root from an absolute XDG_CONFIG_HOME, falling back to ~/.config; when CC Switch itself runs as a Flatpak, use the host ~/.config instead of the sandbox-private XDG_CONFIG_HOME. Update the en/zh/ja user manuals with Linux paths and the Flatpak boundary.
Fixes #4855
fix(proxy): omit missing tool descriptions instead of serializing null (#7319)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): keep reasoning effort for grok-4.6 and pass xhigh through verbatim (#7318)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(models): support Zhipu OpenAI Responses model lists (#7330)
Zhipu's Responses endpoint (open.bigmodel.cn/api/v1) serves its model list in the Codex remote catalog shape (models[].slug) instead of the OpenAI-compatible data[].id shape, so the Codex form's model fetch came back empty. The parser now falls back to models[].slug when data is absent.
fix(misc): fetch npm dist-tags from the dedicated endpoint with the probe timeout (#7346)
The tool version probe requested the full npm packument for each package (metadata for every historical version, 14-25 MB uncompressed for codex / opencode / openclaw) only to read its dist-tags, and it was the only latest-version probe without LATEST_PROBE_TIMEOUT, so it fell back to the proxy client's 600 s total timeout and the cards sat on "loading" for minutes on slow links while the large downloads starved the Hermes GitHub probe into "unknown".
Switch fetch_npm_dist_tags to registry.npmjs.org/-/package/{package}/dist-tags, whose body is the dist-tags object itself (tens of bytes to a few KB), escape scoped names as %2f, and apply the same 15 s probe timeout as the GitHub and PyPI probes. No data is lost: the only consumer reads latest and the tool's prerelease tags.
Fixes #7339
feat(usage): display output tokens per second in request log table and detail panel (#3369)
Show tokens/s next to the output token count in the request log table and the request detail panel.
The generation window prefers an explicit duration when present, otherwise latency minus first-token time for streaming requests, and falls back to full latency for non-streaming requests. Rows without a positive token count or window show nothing.
Closes #5936
fix(presets): update APIKEY.FUN URLs to apikey.fan
docs(release): add v3.20.3 release notes
chore(release): v3.20.3
fix(pricing): move retired DeepSeek V4 family to the V4.1 Flash tier
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.
fix(presets): declare third-party Token Plan DeepSeek V4 rows text-only
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.
feat(presets): rename DouBaoSeed preset to localized Volcengine Doubao
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.
fix(codex): skip unchanged incomplete rollout tails
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.
fix(claude): align quick model selection with form order
fix(tray): show bound ChatGPT account quota for managed Codex cards
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
feat(claude): add "Disable Artifact Tool" quick toggle to provider editor
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.
feat(codex): refresh aggregator model presets and catalogs
feat(codex): switch Kimi presets to native Responses direct-connect
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.
fix(usage): show Claude Fable weekly limit from the usage API limits array
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.
fix(codex): mirror vision-capable DeepSeek catalog and un-gate the legacy v4-flash alias (#7286)
Refresh the bundled DeepSeek official Codex catalog to the vendor's current models.json (slug deepseek-flash, image input modality, supports_image_detail_original) and remove deepseek-v4-flash from the confirmed text-only registry: the legacy id is still accepted by DeepSeek and routed to the vision-capable V4.1 Flash, so both the proxy media sanitizer and the generated Codex catalog must fail open for it. deepseek-v4-pro stays text-only per the vendor catalog.
Fixes #7283
fix(proxy): clamp sub-floor max_tokens to the Responses API minimum (#7287)
The Responses API rejects max_output_tokens below 16, but Anthropic clients legitimately send tiny probe budgets (Claude Desktop's model-availability probe uses max_tokens=1). The Anthropic→Responses conversion copied the value verbatim, so strict upstreams returned 400 and Claude Desktop reported the mapped model as unavailable under local routing.
Clamp 1..=15 up to 16 during conversion; 16+, 0 and non-integer values keep their pass-through semantics. Normal session requests convert byte-for-byte as before, and the Codex OAuth path still strips max_output_tokens afterwards.
Fixes #7103
fix(codex): coalesce adjacent commentary with pending tool calls (#7280)
The Codex Responses -> Chat Completions converter emitted two consecutive
assistant messages when one Responses model turn contained a commentary
message directly followed by function_call items. Chat upstreams imitate
the text-only assistant message as a complete turn and return
finish_reason=stop before the expected tool call, so long agent tasks end
after a progress update.
Merge pending tool calls into the directly adjacent assistant message when
it carries no tool_calls yet, attach pending reasoning per segment so
parallel calls do not duplicate it, and keep the new-message path for every
other boundary shape (user/tool boundaries, prior tool-call batches, media
flushes). The reasoning placeholder backfill still applies to coalesced
calls without reasoning.
Fixes #6529
Supersedes #6530
Co-authored-by: BigStrongSun <BigStrongSun@users.noreply.github.com>
fix(proxy): skip empty reasoning_content placeholders in Anthropic SSE streaming (#7227)
Some OpenAI-compatible upstreams (GLM, Qwen, DeepSeek-V4-Pro via ModelScope/DashScope,
Bailian, JD Cloud, ...) keep a `reasoning_content: ""` placeholder in every content
chunk instead of omitting the field. The reasoning branch of the OpenAI Chat -> Anthropic
SSE converter only checked for presence, and it shares `current_non_tool_block_type`
with the content branch, so every content chunk closed the open text block, opened an
empty thinking block, then reopened a text block. N content chunks became 2N content
blocks, half of them empty thinking blocks, which Claude Code rendered as one line per
token with dozens of empty Thought blocks and bloated the session JSONL.
Filter empty reasoning before entering the branch, matching the `!content.is_empty()`
guard the content branch already had and the non-streaming `openai_to_anthropic`
converter. Two regression tests pin block index, type and order: the placeholder shape
must yield a single text block and no thinking_delta at all, and real reasoning must
still yield one thinking block followed by one text block.
Fixes #5028
Fixes #4404
fix(codex): detect growing rollouts with a persisted byte cursor (#7219)
Codex keeps the rollout append handle open for the whole session, so on Windows NTFS the file mtime does not advance while the file grows and the pure-mtime gate skipped growing rollouts forever. Persist the observed byte length in session_log_sync.last_byte_offset for Codex rows and skip a file only when both mtime and size are unchanged. The parser now splits records on newlines manually so an incomplete trailing record is retried on the next pass without advancing the line cursor.
Fixes #6060
fix(codex): proxy image edits and Images API follow-ups (#7177)
* fix(codex): proxy image edits and Images API follow-ups
Follow-ups to #7036 for Codex's built-in ImageGen over the local proxy:
- Register `/images/edits` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them through the shared Codex standalone passthrough.
ImageGen posts to `images/edits` whenever a call references existing
images, and the router returned an empty 404 for it.
- Add `CodexStandaloneEndpoint::ImagesEdits`. Both Images endpoints share
one full-URL suffix table, so a complete Responses / Compact / Chat
Completions / Images URL derives the sibling endpoint (and vice versa)
while opaque full URLs keep failing closed.
- Apply the standalone sibling rewrite when the base URL is pasted as a
complete endpoint with the full-URL switch off, mirroring
`codex_chat_base_is_full_endpoint`; standalone requests no longer become
`.../chat/completions/images/generations`.
- Parse Images API streaming usage: `image_generation.completed` carries
`usage` on the event top level, so scan for `usage.input_tokens` before
falling back to the Chat Completions stream parser.
Related #7036
* fix(codex): match standalone URL suffixes case-insensitively
Keep endpoint detection and sibling rewriting consistent while preserving the original URL prefix and query. Cover mixed-case Images and Alpha Search endpoints through URL and proxy regression cases.
---------
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): preserve per-app settings on shutdown and port allocation (#7210)
On normal exit, stop_with_restore_keep_state() called the legacy global
proxy-config writer to clear the long-retired live_takeover_active flag.
That writer reads the Claude row and runs an UPDATE without a WHERE clause,
so every shutdown copied Claude's max_retries and the three timeout fields
onto the Codex, Gemini and Grok Build rows — and the UPDATE never carried
live_takeover_active in the first place, so the step had no effect beyond
the clobber. The ephemeral listen-port path did the same on every proxy
start.
Drop the dead write-back (live-config restore, backup cleanup and health
reset are unchanged) and persist the resolved ephemeral port through the
global-config interface, which only touches the shared listen/logging
columns. Two regression tests seed distinct per-app settings for all four
apps and assert they survive both paths.
Fixes #7204
fix(provider): preserve child metadata during universal sync (#7212)
Syncing a universal provider regenerates its Claude/Codex/Gemini child
providers. The child's `settings_config` was merged with the existing row,
but `meta`, `created_at` and `sort_index` were taken from the generated
object, and the universal provider never carries any of them from the UI.
The UPDATE therefore wiped the child's usage script, common-config opt-out,
endpoint auto-select and other per-app settings to `{}` and reset the sort
index to NULL, which pushed the card to the bottom of the list and looked
like the provider had been deleted and re-created.
Keep the existing child's `meta`, `created_at` and `sort_index` when the
row already exists. Name, base URL, key, models, website and notes are
still driven by the universal provider, and first-time creation still
inherits the parent's metadata.
Fixes #7134
feat(presets): rebrand DashScope as 千问AI平台 and refresh Qwen models (#7183)
Rename the domestic Bailian presets to 千问AI平台 with a dedicated icon, refresh the Qwen family to the 3.8 generation across Claude Code, Claude Desktop, Codex, Hermes, OpenClaw, OpenCode and Pi, add the domestic Token Plan presets, restore the international QwenCloud Coding Plan presets, and align OpenClaw/OpenCode/Pi model metadata with the official client docs.
fix(codex): honor proxy URL when model_provider is omitted (#7263)
When a Codex provider card's TOML has no `model_provider`, Codex falls back to
its built-in `openai` provider, but takeover wrote the proxy address as a
top-level `base_url` that Codex never reads, so requests bypassed the local
proxy and went straight to api.openai.com.
Treat the missing selector as the built-in `openai` provider and write the
proxy address to `openai_base_url`; the existing legacy-reroute migration then
turns it into a `cc-switch` custom provider table carrying the `PROXY_MANAGED`
bearer, the same shape every other third-party takeover uses. Explicit
provider selections are unchanged.
Refs #6256, #7217
fix(provider): update MiniMax defaults to M3 and remove expired offers (#7255)
* fix(provider): refresh MiniMax M3 presets and remove expired promotions
* fix(provider): complete MiniMax M3 capability declarations
---------
Co-authored-by: saladday <1203511142@qq.com>
chore(provider): remove Atlas Cloud sponsor status, keep presets
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`
docs(release): add v3.20.2 release notes
chore(release): v3.20.2
feat(provider): add SoleAPI sponsor presets across all nine apps
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.
fix(codex): proxy image edits endpoint
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.
fix(pro…
修复 CI Frontend Checks 的 Check formatting 步骤失败
(App.tsx、ConnectivityTestDialog、ProviderCard、ProviderList、
provider schema 共 5 个文件)。
style: apply cargo fmt to connectivity-test era backend files
修复 CI 的 Check Rust formatting 步骤失败(connectivity_test、
backup、tests、provider 等文件格式不符合 rustfmt 规范)。
chore(native): archive sync-upstream
docs(fork): update rebase whitelist for connectivity-test era fork features
- 白名单补充连通性测试前后端、模型快捷切换、双官网链接测试、
CI 裁剪版、README/CHANGELOG、.comet/superpowers 产物等 fork 专属文件
- 新增共享文件 fork 专属语义表(托盘左键切换、禁用更新器、hidden 字段等)
- 修正 §3.1 rebase 方向说明:rebase 中 fork 侧是 --theirs 而非 --ours
(v3.20.2 同步实测确认,与 merge 语义相反)
chore: bump fork version to 3.20.2-1 after upstream v3.20.2 rebase
重放 fork 提交到 upstream/main(f3b18df1,v3.20.2)后统一版本号。
Cargo.lock 同步移除 updater 依赖树(fork 已禁用应用内更新器)。
chore(native): archive tray-left-click-toggle-window
chore(comet): 记录 tray-left-click-toggle-window 验收通过与归档状态
feat(tray): 托盘图标左键单击切换主窗口显示/隐藏
- tray.rs 新增 show_main_window/hide_main_window/toggle_main_window 助手:
显示语义与"打开主界面"一致,隐藏与"关闭到托盘"平台处理一致
(Windows set_skip_taskbar、macOS apply_tray_policy)
- lib.rs 托盘 show_menu_on_left_click 改为 false,左键 Click(Left, Up)
触发切换(过滤 Down/Up 双事件),右键仍弹菜单;Enter/Click 用量刷新保持
- show_main 菜单事件与 macOS Reopen 复用 show_main_window
docs(readme): 更新日志补充连通性测试、双官网链接与托盘左键切换
feat(model-quick-switch): 获取模型列表成功后自动展开选择器
SearchableModelPicker 增加可选 open/onOpenChange 受控展开(不传保持内部自管,
表单等既有用法不受影响);快捷切换弹窗拉取成功且列表非空时直接展开,关闭弹窗复位。
chore(native): archive fix-quick-switch-1m
chore(comet): 记录 fix-quick-switch-1m 验收通过与归档状态
fix(model-quick-switch): 补齐 Haiku 显示名同步,支持不选模型仅翻转 1M
- 显示名同步映射补上 ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME(沿用空/旧base/旧base[1M] 覆盖、自定义保留语义)
- 新增 setClaudeOneMInSettings:仅原地翻转 1M 标记,不改各角色模型 base 与显示名
- 弹窗打开时 1M 开关按供应商当前状态初始化,避免已开 1M 被静默剥离
- claude 未选模型且 1M 状态有变化时允许保存;无可应用变化时保存禁用
- 补单测与四语言 toast 文案
feat(provider-website-links): 供应商支持第二个官网链接,主页横排双链接
- Rust Provider/UniversalProvider 新增 website_url_2(serde websiteUrl2,skip none)
- SQLite providers 表新增 website_url_2 列(schema v18→v19 幂等迁移 + create_tables 补列 + 旧 config.json 迁移)
- DAO SELECT/INSERT/UPDATE/replace 全链路带出新列
- 表单共享 BasicFormFields + 统一供应商表单第二输入框;zod 同第一链接规则校验与回填
- ProviderCard 主页双链接横排、两空格分隔、逐链接 openExternal 打开默认浏览器
- 搜索匹配、复制供应商、统一供应商生成 claude/codex/gemini 随行透传
- gemini_auth packycode 认证检测纳入 website_url_2;ClaudeDesktop/GrokBuild 预设切换清空第二链接;卡片同值链接去重
- 4 语言 i18n + 前端/前端单测覆盖
Comet change dual-website-links-homepage(19/19 验收通过,归档)
feat(connectivity-test): 拉取模型列表改为「搜索添加」选择器交互
- 新增共享组件 SearchableModelMultiPicker:搜索过滤 + 多选 + 已在列表标记 + 已选计数
- ConnectivityTestDialog 获取模型列表后不再全量追加合并去重,改为会话内缓存完整列表并自动弹出一次选择器;用户搜索挑选后添加进表格并自动勾选
- 重新拉取只刷新缓存,已添加进表格的行保留;拉取结果仍仅会话内有效,不写回供应商配置
- 四语言 i18n 新增 searchAdd / addToTest / alreadyInList / selectedCount
- 同步更新 ConnectivityTestDialog 测试并新增 SearchableModelMultiPicker 测试
feat(connectivity-test): 探测目标按供应商 API 格式对齐真实转发链路
- build_claude 按 get_claude_api_format 分派端点与请求体:openai_chat → /v1/chat/completions(复用 transform_claude_request_for_api_format,流式注入 stream_options.include_usage)、openai_responses → /v1/responses、gemini_native → /v1beta/models/{model}:{method}(resolve_gemini_native_url)、anthropic 保持 /v1/messages 并补 anthropic-version/beta 头
- build_codex 按 should_convert_codex_responses_to_chat/anthropic 分派:chat → /chat/completions(responses_to_chat_completions_with_reasoning)、anthropic → /v1/messages(responses_request_to_anthropic,含 max_output_tokens 注入与 [1m] 剥离 + context-1m beta 头)
- 鉴权改用 ClaudeAdapter/CodexAdapter 的 extract_auth + get_auth_headers(与真实出站请求同源:AUTH_TOKEN→Bearer、ANTHROPIC_API_KEY→x-api-key、gemini→x-goog-api-key、GeminiCli OAuth→Bearer、OpenRouter→Bearer、Codex 按 apiKeyField)
- 转换前剥离 [1m] 标记;meta.isFullUrl 或 base 已是完整端点时直接使用 base_url;自定义 body 浅合并移至转换后的最终协议体
- 新增 18 个目标构造断言单测;Comet 归档产物同步提交
chore: archive connectivity-test-optimizations
feat(connectivity-test): 单一模型表格 + 实时拉取模型 + 勾选即测试范围 + 新默认提示词
- 弹窗合并为单一模型表格:打开即全部模型行(初始待测试)、行勾选 + 表头全选(含半选态)、5 格统计卡,移除上方独立选择区
- 获取模型列表:凭据提取抽为共享模块 src/utils/providerCredentials.ts(ModelQuickSwitchDialog 同步改引用),复用 fetch_models_for_config 拉取 /v1/models,按 ID 追加合并去重、仅会话内有效
- 默认提示词改为「你好,你可以帮我做什么事情」:前后端常量同步,merge 差异判断基准跟随
- 勾选即测试范围:后端 connectivity_test_provider_models 新增 modelIds 参数(None/空白回退静态清单),前端逐模型独立 invoke 并行 + 行级流式更新
- 一轮测完自动勾选失败项,全部成功保持原勾选
fix(connectivity-test): UI 四项修复
- 弹窗抬至 alert 层(z-[60]):打开连通性测试时 header 切换栏正确变灰,
详情子弹窗同步抬层(同 z + 更晚挂载 portal 在上)
- 批量检测入口移至 header 图标组(状态提升到 App,ProviderList 经
props 读取探针结果),不再另起一行
- 批量检测可停止:runId 代次守卫(Tauri invoke 无法真正取消),再次
点击即清除 waiting/running 徽标、丢弃在途迟到写入、不再拉起排队项
- 弹窗打开自动勾选模型:已保存默认测试模型 → 供应商当前模型
(claude env.ANTHROPIC_MODEL / codex config.toml model,新增
currentProviderModelId 与后端同口径)→ 清单首个
前端全量 141 文件 / 1106 测试全绿;typecheck clean
chore: archive per-model-connectivity-test
fix(connectivity-test): shouldShowTestEntry 增判 providerType + 收尾清理
chore(connectivity-test): 勾选 Task 9 / OpenSpec 5.1-5.2 + verify 报告
chore(connectivity-test): 勾选 Task 8 / OpenSpec 4.1-4.3
refactor(connectivity-test): 移除旧 stream_check 可达性探测全链路(保留表结构)
- 删除 6 个文件:commands/stream_check.rs、services/stream_check.rs、
database/dao/stream_check.rs、hooks/useStreamCheck.ts、lib/api/connectivity-check.ts、
components/usage/ConnectivityCheckConfigPanel.tsx
- 移除 mod.rs 声明、lib.rs generate_handler! 注册、cleanup_old_stream_check_logs 调用
- 从 SYNC_SKIP_TABLES / SYNC_PRESERVE_TABLES 移除 stream_check_logs(表结构保留在 schema.rs)
- 更新 backup.rs 测试:移除 stream_check_logs fixture 与断言
- 更新 SettingsPage:移除 ConnectivityCheckConfigPanel AccordionItem 与 FlaskConical 导入
- i18n:删除 streamCheck.* 命名空间(zh/en/ja/zh-TW 四语言)
- 更新注释:移除 stream_check 旧引用(forwarder.rs、provider.rs、userAgentPresets.ts、
connectivity_test.rs)
- 修复 4 个 pre-existing clippy lint(connectivity_test: matches! 宏、unused_unit、
derive Default)
- 移除 ProviderList.test.tsx 中已失效的 useStreamCheck mock
chore(connectivity-test): 勾选 Task 7 / OpenSpec 2.4 3.1-3.3
feat(connectivity-test): 供应商列表集成(弹窗入口 + 批量探针徽标)
chore(connectivity-test): 勾选 Task 6 / OpenSpec 2.2-2.3
fix(connectivity-test): 模型列表解析与后端对齐(每条目首个匹配,提取共享 helper)
feat(connectivity-test): 连通性测试弹窗/详情/徽标组件与四语言文案
chore(connectivity-test): 勾选 Task 5 / OpenSpec 2.1
test(connectivity-test): 锁定 toBackendParams 字段映射(customHeaders/customBody 等)
feat(connectivity-test): 前端命令绑定与测试/探针状态 hooks
chore(connectivity-test): 勾选 Task 4 / OpenSpec 1.4
feat(connectivity-test): 测试参数持久化纯函数(缺省即默认值)
chore(connectivity-test): 勾选 Task 3 / OpenSpec 1.3
feat(connectivity-test): Tauri 命令与注册,官方/动态端点过滤,sanitize 隔离私有字段
chore(connectivity-test): 勾选 Task 2 / OpenSpec 1.2
feat(connectivity-test): 后端直连执行器(reqwest+SSE,总/空闲超时,度量与错误识别)
feat(connectivity-test): 后端直连执行器(reqwest+SSE,总/空闲超时,度量与错误识别)
chore(connectivity-test): 勾选 Task 1 / OpenSpec 1.1
fix(connectivity-test): claude 鉴权对齐 adapter 顶层 apiKey/api_key 兜底
feat(connectivity-test): 新增后端连通性测试服务类型/模型解析/协议构造
chore: archive model-quick-switch-picker-scroll
chore: model-quick-switch-picker-scroll 验证报告与状态元数据
fix: 模型快捷切换弹窗内模型列表滚轮无法滚动
模态 Dialog 的 RemoveScroll 会对 shards 外的 portaled PopoverContent
wheel 事件无条件 preventDefault。在 SearchableModelPicker 的
PopoverContent 捕获阶段阻断 wheel 传播,恢复原生滚动。
chore: bump fork version to 3.20.1-1 after upstream v3.20.1 rebase
fix(form): only auto-expand Claude advanced options on divergent model mapping
Uniform mapping produced by the quick-set button (all roles equal to the
fallback model) no longer counts as an advanced value, so the advanced
section stays collapsed on edit. A 1M marker is stripped before comparing
so marked roles are not misjudged as divergent. Truly customized mappings
still auto-expand the section.
feat: align fallback-model quick-set buttons with model input right edge
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
docs: update README with fork build notes and quick-move provider changes
chore: archive comet-native claude-model-quick-access
feat: surface Claude fallback model as quick-access section below endpoint
Move the default fallback model (ANTHROPIC_MODEL) block out of the
collapsed Advanced Options into a quick-access section directly below
the request endpoint: input + 1M checkbox + quick-set + fetch-models
buttons are all reachable without expanding.
- header toggle-all "declare 1M" now includes the fallback model
(Haiku still excluded); partial selection shows indeterminate state
- drop the duplicated quick-set/fetch buttons and the old fallback
block inside Advanced Options
- exclude claudeModel from hasAnyAdvancedValue so a fallback-only
value no longer auto-expands Advanced Options
- quick-set priority, env write semantics, and OAuth preset branches
unchanged; add 4 component tests covering the new layout
chore: archive comet-native provider-right-click-top-bottom
Enable native workflow in .comet/config.yaml and move the Comet managed
state block in .gitignore so the exclusion rules stay grouped with the
other fork tool artifacts.
feat: add provider right-click move-to-top/bottom and insert new providers at second position
- ProviderList: context menu with quick move-to-top/move-to-bottom, reusing
update_providers_sort_order and refreshing tray menu like drag sort
- useAddProviderMutation: default insert at index 1 with explicit sortIndex
materialization for existing items (avoids defined-first sort trap)
- i18n: quickMove* strings for zh/en/zh-TW/ja
ci: stop flaky WSL2 linker job from blocking main
The backend-windows-wsl2 job intermittently fails with LNK1327 /
mt.exe c1010070 when the Windows runner's mt.exe cannot parse a
manifest temp file on the WSL2 UNC TEMP path. The same flaky failure
occurs upstream and is unrelated to fork changes. Mark the job
continue-on-error so main stops going red on it; the result stays
visible in the Actions UI.
style: apply prettier to files touched by recent comet archives
Frontend CI format:check failed on 6 files modified by the app-name
and model-quick-switch archive commits plus the updater-disable commit.
Run prettier --write so format:check passes again.
chore: archive ccs-fork-app-name-cc-switch
chore: archive ccs-model-quick-switch-ui-polish
docs: rewrite README to document fork differences vs upstream
README 从上游完整功能介绍改为仅记录 fork 与上游的差别,完整功能
文档指回上游。涵盖 8 项 fork 魔改:构建标识/开发预览、预设精简、
Claude 表单调整、模型搜索移植、主页模型快速切换、侧边栏面板显隐、
关闭应用内更新、打包范围,并说明版本号与 rebase 同步流程。
feat: disable in-app updater, point users to GitHub releases
移除 tauri-plugin-updater 依赖与自动更新链路,改为统一引导用户到
GitHub Releases 手动下载新版。
- 前端:updater.ts 恒返回 up-to-date;UpdateContext 取消启动自检;
AboutSection「检查更新」直接打开 fork releases 页;DatabaseUpgrade
恢复界面去掉下载进度条,改为打开发布页。
- 后端:移除 updater 插件注册与 UpdaterExt 调用;
install_update_and_restart 降级为空壳,check_app_update_available
返回当前版本号;check_for_updates 指向 fork releases;capabilities
去掉 updater:default 权限。
- 打包:tauri.conf.json 删 plugins.updater、createUpdaterArtifacts=false;
Cargo.toml / package.json 删 updater 依赖,Cargo.lock / pnpm-lock
同步重生成。
- CI:release.yml 移除签名收集与 assemble-latest-json job,删除
.tar.gz/.sig 处理,release body 去掉 updater 说明。
版本号统一为 3.20.0-1(合法 semver 且 MSI 第 4 位为 1)。
Cargo.lock / pnpm-lock.yaml 已随依赖移除重生成,cargo check 通过。
ci: build only Windows x64 and macOS arm64, unsigned
feat: gate dev panel behind dev-only env flag
chore(comet): archive ccs-sidebar-panels-visibility change
fix: apply skills visibility guard to hermes branch toolbar button
feat: include prompts in sidebar panel visibility settings
fix: persist visibleSidebarPanels setting in backend AppSettings
style: use compact pill toggles for sidebar panel visibility
feat: add sidebar panel visibility switches to settings
feat: add sidebar panel visibility settings and wire into toolbar buttons
chore(comet): archive ccs-home-model-display change
fix(fork): read grokbuild api key from config TOML in quick switch dialog
feat(fork): align model badge with upstream extractModelBadge
feat(fork): show current model badge and quick-switch entry on provider cards
feat(fork): add model quick switch dialog
feat(fork): add provider model read/write utils with tests
chore(comet): archive ccs-model-search-port change
feat(i18n): add model search picker keys for all locales
feat(form): port SearchableModelPicker from upstream for searchable model selection
chore(comet): archive ccs-claude-form-layout change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-claude-form-layout/
- Sync claude-form-layout spec to main specs
- Annotate design doc as archived
- Add verification report
fix(i18n): update advanced options hint, add ja/zh-TW toggle-all, drop dead apiFormatHint key
fix(form): correct 1M toggle-all alignment direction (ml-1)
fix(form): align 1M toggle-all checkbox with row checkboxes
refactor(form): move api format select into EndpointField rightSlot
fix(form): widen api format select to fit option text
feat(form): add 1M toggle-all checkbox in model mapping header
feat(form): move api format select next to endpoint input
chore(comet): archive ccs-provider-presets-trim change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-provider-presets-trim/
- Sync provider-presets-visibility spec to main specs
- Annotate design doc as archived
- Add verification report
docs(fork): add presets-trim files to rebase whitelist
test(fork): mock IS_FORK_BUILD false in unit tests for upstream-form semantics
feat(fork): wire official preset filter into provider forms
feat(fork): add hidden field to preset interfaces
feat(fork): add official preset allowlist and filter util
chore(comet): archive ccs-fork-scaffolding change
- Move change artifacts to docs/openspec/changes/archive/2026-08-18-ccs-fork-scaffolding/
- Sync fork-build-identity spec to main specs
- Annotate design doc as archived
- Add verification report
feat(fork): add dev config with isolated identifier and config dir
docs(fork): complete rebase whitelist with all fork-touched files
fix(fork): guard setTitle with isTauri in test/non-tauri env
fix(fork): define __CCS_FORK_BUILD__ in vitest config
feat(fork): add DevPanel and settings footer entry
feat(fork): set macOS window title to CC Switch (Fork) on startup
feat(fork): inject __CCS_FORK_BUILD__ compile-time constant
feat(fork): bump version to 3.19.2-fork.1 and rename product to CC Switch (Fork)
docs(fork): add upstream rebase workflow and ignore tool artifacts
fix(proxy): accept the whole grok-4.x (x>=5) family in the reasoning-effort whitelist (#7369)
Replace the verbatim grok-4.5 / grok-4.6 entries in supports_reasoning_effort with a rule that parses the grok-4.x minor version and accepts x >= 5, mirroring the existing GPT-5+ rule. This covers grok-4.7 (released 2026-09-21), whose reasoning effort was previously dropped on the Claude -> Chat, Claude -> Responses and Codex Responses -> Chat conversion paths, and lets future releases pass without another whitelist edit. The grok-build-* family is retained for saved providers.
Co-authored-by: allenxu09 <171831965+allenxu09@users.noreply.github.com>
docs(release): add v3.20.4 release notes
chore(release): v3.20.4
feat(pricing): add Grok 4.7 and GLM-5.3-FlashX
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.
fix(i18n): remove outdated OpenCode Go referral offer
fix: add SudoCode.chat alternate API endpoint
feat(settings): add a GitHub star prompt to the About card
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
feat(presets): add Soshow aggregator presets
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.
fix(ci): stabilize frontend tests and verify renderer build
fix(mcode): refresh provider state after removal (#7578)
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
fix(lib): reset skip_taskbar before window.show() across entry points (#6348)
5处 window.show() 调用中 4处没有在 show() 前重置 Windows 的 skip_taskbar,导致以下场景出现窗口可见但不在任务栏、点最小化后窗口消失的 bug:
- single_instance handler (lib.rs:360-361):当原进程因 silent_startup / 关闭到托盘等处于 skip_taskbar(true) 状态时,从快捷方式/开始菜单唤起已运行实例触发。
- handle_deeplink_url 的 focus_main_window 分支 (lib.rs:256):通过 ccswitch:// URL 唤起时。
- DB 恢复流程 (lib.rs:556):数据库版本不匹配时显示恢复界面。
- RunEvent::Opened 处理器 (lib.rs:1803):系统接收到 deep-link URL 时。
修复后的行为与 tray.rs::handle_tray_menu_event::show_main 分支一致(那里已有相同的 set_skip_taskbar(false) 调用)。
经用户测试验证:autostart + silent_startup + 从开始菜单/快捷方式启动的场景下,最小化按钮恢复正常工作。
fix(proxy): omit missing tool descriptions in the Codex responses-to-chat converter (#7378)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): preserve max effort for GPT-5.6 and GPT-6 Astra (#7531)
fix(misc): ignore shell startup output when probing tool versions in WSL (#7348)
try_get_version_wsl runs "${SHELL:-sh}" -lic '<tool> --version' through
wsl.exe and takes the first x.y.z anywhere in stdout. A login+interactive
shell runs the distro's startup files first, so their output shares that
stdout. On Ubuntu, /etc/profile.d/update-motd.sh prints the MOTD once a day,
starting with "Welcome to Ubuntu 24.04.4 LTS", and the card then showed
24.04.4 as the Claude Code version. The once-a-day stamp makes it appear
after the first probe of the day and vanish on refresh.
The payload now prints a sentinel before running the tool, and parsing looks
only at what follows its last occurrence. The last occurrence handles the
-lic || -lc || -c fallback chain printing it several times; matching it as a
substring handles OSC sequences glued in front of it. If the sentinel is
absent (the shell failed before the payload ran), the output is used as
before. The stderr fallback for tools that print their version there is kept.
Fixes #7347
Co-authored-by: lyfuci <lyfuci11@gmail.com>
fix(claude): hide session URL attribution (#7053)
* fix(claude): hide session URL attribution
* docs(claude): document session URL attribution
fix(ui): reset provider scroll when switching apps (#5211)
fix(pi): fix provider logo (#6826)
fix(i18n): use the Pi form's own term for the zh-TW duplicate-key message (#7187)
Follow-up to the nit on #6768. In zh-TW the Pi form labels the field
供應商識別碼 and says 供應商識別碼為必填 when it is empty, but the
duplicate message I added said 供應商標識 — the term Hermes, OpenCode and
OpenClaw use for their own field. One form, two names for the same thing.
The other three locales already keep the label and both messages on one
term, so this makes zh-TW match the pattern rather than introduce one:
en Provider key / Provider key is required / This provider key already exists
ja プロバイダーキー / プロバイダーキーを入力してください / このプロバイダーキーは既に存在します
zh 供应商标识 / 供应商标识不能为空 / 该供应商标识已存在
zh-TW 供應商識別碼 / 供應商識別碼為必填 / 該供應商標識已存在 <- the odd one out
hermes.form.providerKeyDuplicate carries the same sentence and is left
alone: its own label is 供應商標識, so it is already consistent.
Verified: managementListLocales 7/7. Full unit suite 1085/1087, the same
two App.test.tsx integration cases failing before and after.
fix(proxy): strip stop param for GitHub Copilot to unblock auto mode classifier (#5404)
Claude Code's auto mode classifier sends `stop_sequences` on every
safety check. On the GitHub Copilot path the Anthropic -> OpenAI Chat
conversion mapped it to `stop`, and Copilot's Chat Completions route
for Claude models answers that request with HTTP 400, so every
classifier call failed while normal chat kept working.
- Remove the converted `stop` field only when the provider is GitHub
Copilot; other OpenAI-compatible upstreams keep it.
- Copilot detection in the transform now also covers providers that
are recognised only through `env.ANTHROPIC_BASE_URL`.
- Regression tests for the Copilot (meta and env URL) and generic
provider cases.
Fixes #5175
fix(codex): keep stored auth when the live snapshot has no credential (#7434)
Editing the current Codex provider seeds the form from the live ~/.codex
snapshot, and switching away backfills that snapshot into the DB row. When a
provider table declares its own credential source (an Authorization header,
env_key, auth/aws), no bearer token is injected into config.toml and the
shared auth.json is deleted on third-party switches, so the live snapshot is
permanently `{ auth: {}, config }` while auth.OPENAI_API_KEY in the DB is the
only copy of the key. Both paths treated that empty live auth as
authoritative and silently erased the stored key, which in turn made the
usage-script modal misread the provider as an official subscription and
reset the saved script.
- Treat a credential-less live auth (missing auth.json or the bare
auth_mode logout marker) as an absent field in both the edit dialog and
the switch-away backfill, keeping the stored auth template. Live still
wins whenever it carries material, and official providers keep the live
login as their source of truth.
- Gate the official exemption on is_codex_official_provider /
resolveCodexOfficialIdentity rather than the category alone: a legacy
fixed official card without a persisted category is still written as
official, so the fallback must not resurrect a logged-out login on
switch-back.
- Cover both layers with regression tests, including the category-less
official shapes.
Fixes #7433
Supersedes #7492
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): lift additional_tools input carriers on the Codex chat conversion (#7454)
Codex talks to third-party providers over its Responses Lite transport,
which ships the request's extra tools in an `additional_tools` input
carrier: `{"type":"additional_tools","role":"developer","tools":[...]}`,
a role but no `content`. The Responses->Chat conversion let that carrier
fall through the generic "item with a role is a message" arm and emitted
`{"role":"system","content":null}` at messages[1], which strict Chat
gateways reject with `messages[1]: missing field "content"` (or
`data did not match any variant of ...SystemMessageContent`). The tools
carried inside it were also dropped on the Chat and Anthropic conversions.
- Skip `additional_tools` items in the Chat message walk; a carrier
declares tools, it is not a message.
- Collect carrier tools through the existing CodexToolContext registry
(dedup, namespace flattening, custom-tool conversion), which also feeds
the Anthropic conversion and the response-side name round-trip.
- Requests without a carrier convert byte-for-byte as before.
Known boundary: `custom` children inside a namespace tool
(`functions.exec`) are still not lifted; that needs its own response-side
name round-trip and is left for a follow-up.
Diagnosed independently in #6158 by BigStrongSun (fix in #6159) and in
fixture.
Fixes #7451
Fixes #6158
Refs #6033
Refs #6776
Supersedes #6159
fix(proxy): normalize Codex detail:original images for Chat gateways (#7476)
Codex marks tool-returned images with detail:"original" whenever the model
catalog advertises supports_image_detail_original, a Responses-only value.
The Responses->Chat conversion copied it verbatim into image_url.detail, and
strict OpenAI-compatible Chat gateways (Ark/Doubao, SenseNova, GLM, vLLM, ...)
reject the whole request with 400. Because Codex replays the full rollout on
every turn, one poisoned view_image result made the session fail permanently,
including plain-text turns.
Two layers:
- tool_media::merge_top_level_detail downgrades original -> auto, so already
poisoned rollouts and Codex versions before 0.155.0 (which do not normalize
replayed history themselves) recover immediately. auto/low/high are untouched.
- codex_catalog_model_entry always writes supports_image_detail_original:false
on the ProxyChat profile, so Codex stops emitting original at the source once
the catalog is regenerated. NativeResponses/Anthropic profiles and the DeepSeek
official catalog are unaffected.
Top-level user images were never affected: responses_content_to_chat_content
drops the part-level detail entirely.
Fixes #6495
Fixes #7458
Refs #6569
Refs #5805
fix(omo): detect unified config on the WSL side when the OpenCode dir is in WSL (#7550)
When the OpenCode config directory points into WSL
(\\wsl.localhost\<distro>\home\<user>\.config\opencode), OMO >= 4.19.3 keeps
its unified config in the WSL-side home (~/.omo/omo.jsonc). Detection only
probed the Windows home, never found it, and silently fell back to the legacy
per-plugin file that OMO no longer reads, so provider switches looked
successful but had no effect.
- Add derive_wsl_home_dir to derive the WSL-side home from a WSL UNC path
(home/<user> or root); non-WSL and non-home paths return None.
- Probe a list of home candidates for the unified config, WSL-side home
first and the OS home as a fallback, shared by the read, write and delete
paths.
- Detection only: when no unified config exists on either side the legacy
fallback is unchanged.
Fixes #7363
Supersedes #7367
docs(partner): add a FluxA purchase link and tighten its sponsor blurb
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.
fix(claude-desktop): expose the 1M context variant for DeepSeek V4 routes
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.
feat(presets): add FluxA Token Plan partner presets
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.
fix(presets): correct AICodeWith Codex-family endpoint to /v1
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.
fix(ui): render monochrome preset icons in the foreground color
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.
docs(user-manual): point Chat-only Codex examples at plan endpoints
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.
fix(pricing): restore DeepSeek V4 Pro price and seed Qwen3.8 and Hy4 models
- 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).
fix(presets): move MiniMax CN and BaiLing to their current official endpoints
- 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.
feat(codex): refresh CN presets from the Responses API audit
- 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.
test(claude-desktop): run Linux path tests on unix hosts and cover Linux in provider sync tests
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.
fix(ui): stop expanding stale cached usage tiers on ineligible provider cards
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.
fix(skills): 支持 skillId 与目录名不一致的技能安装和更新 (#6381)
* fix(skills): resolve mismatched skillId directories
* fix(skills): preserve source path during updates
* fix(skills): refresh source URL after repository relocation
* test(skills): isolate relocation fixture from legacy home
feat(opencode): 支持从获取的模型列表中搜索并批量添加所选模型 (#7515)
* feat: implement fetched models selection and UI components
* fix(opencode): prevent model search from submitting provider form
Add Kimi Global preset variants (kimi.ai platform + api endpoints) (#7526)
Add a "Kimi Global" twin immediately after the domestic "Kimi" API preset and a "Kimi For Coding Global" twin after the domestic "Kimi For Coding" preset, in every preset file that ships them (claude, claude-desktop, codex, hermes, openclaw, opencode, pi).
"Kimi Global" points to the international open platform https://platform.kimi.ai?aff=cc-switch (apiKeyUrl mirrored as https://platform.kimi.ai/console/api-keys?aff=cc-switch) and swaps the endpoint host api.moonshot.cn for api.moonshot.ai with each file's path unchanged (/v1 or /anthropic).
"Kimi For Coding Global" points to the international site https://www.kimi.ai/code/?aff=cc-switch and swaps the endpoint host api.kimi.com for api.kimi.ai with the path unchanged (e.g. https://api.kimi.ai/coding/v1).
Domestic presets are unchanged; category, icons, apiFormat, model catalogs, endpointCandidates (host swapped), partnerPromotionKey and settings otherwise mirror the domestic blocks exactly.
primePartner stays only on the domestic presets so the new variants do not alter prime-partner ordering.
The Pi twins get distinct providerKey values ("cc-switch-kimi-global", "cc-switch-kimi-for-coding-global") because providerKey is the preset-level primary key and must not collide with the domestic presets.
Rebalance sponsor CTAs around Kimi Code plan with dual-region links (#7522)
Kimi Code plan purchases have resumed, so the README sponsor sections go back to leading with the coding plan instead of the API-only flow written during the purchase pause.
All four locales (EN/ZH/JA/DE) now present Kimi Code plan (中文站 | Global) as the primary CTA and the Kimi Open Platform API (中文站 | Global, with the first-top-up bonus) as the secondary one, mirroring the domestic|global split already used for API links.
The aff and track_id values are unchanged.
fix(prompts): refresh active prompts after external file edits (#7194)
Reload the enabled prompt from its live file when the Prompts list loads, and refresh the open panel when the window regains focus, so external edits to CLAUDE.md / AGENTS.md show up without a restart.
- Keep saved templates available when the live file cannot be read.
- Skip the backfill for empty or whitespace-only files, matching enable_prompt.
- Skip the backfill while a sync/restore holds the sync lock, so a list load cannot write a stale live file into a freshly restored database.
fix(skills): raise archive entry limit so large skill repos install (#7489)
Installing hugohe3/ppt-master from skills.sh failed with
ARCHIVE_TOO_MANY_ENTRIES: the GitHub archive we download is the whole
repository (13,248 entries), and the 10,000-entry cap added in #5811
treated it as a zip bomb.
- Raise MAX_ARCHIVE_ENTRIES to 30,000 for both the remote archive and
local ZIP paths, and align WebDAV's MAX_EXTRACT_ENTRIES so a skills.zip
containing such a skill still restores.
- Charge every file entry at least one 4 KiB block against the existing
512 MiB budget, mirroring directory accounting, so empty-file archives
are bounded by the byte budget rather than by the entry count alone.
- Add tests for a 13k-entry repo archive and for the per-file minimum
charge.
Fixes: https://github.com/farion1231/cc-switch/issues/7475
feat: add MiniMax Code harness support (#7383)
Add MiniMax Code (mcode) as a managed application: provider and model management through the native custom_provider configuration, MCP servers, Skills, global instructions (AGENTS.md), read-only local session history with resume, and usage import from the native runtime database. Native files shared by the TUI and desktop are written under the native lock with rollback when the database commit fails, unrelated native configuration is preserved, and the data directory honors MINIMAX_DATA_DIR and MAVIS_DATA_DIR. Deleting sessions stays in the native application because they are coupled to runtime state.
fix(codex): recover stale account bindings during takeover (#7395)
After deleting a managed ChatGPT account and logging in again, a Codex provider can still reference the old local account ID, which made route switching, takeover, startup restoration and rebinding fail permanently.
Distinguish a missing outgoing account from an existing account with no matching live token. A missing account only releases its ownership marker after the persisted account store is verified; the target write policy stays in force. A stale target now reports an account-selection instruction instead of a bare not-found error, including when takeover is already enabled.
Fixes #7392
feat(claude-desktop): support 3P configuration on Linux (#7331)
Add Claude Desktop 3P configuration support on Linux. Resolve the config root from an absolute XDG_CONFIG_HOME, falling back to ~/.config; when CC Switch itself runs as a Flatpak, use the host ~/.config instead of the sandbox-private XDG_CONFIG_HOME. Update the en/zh/ja user manuals with Linux paths and the Flatpak boundary.
Fixes #4855
fix(proxy): omit missing tool descriptions instead of serializing null (#7319)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): keep reasoning effort for grok-4.6 and pass xhigh through verbatim (#7318)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(models): support Zhipu OpenAI Responses model lists (#7330)
Zhipu's Responses endpoint (open.bigmodel.cn/api/v1) serves its model list in the Codex remote catalog shape (models[].slug) instead of the OpenAI-compatible data[].id shape, so the Codex form's model fetch came back empty. The parser now falls back to models[].slug when data is absent.
fix(misc): fetch npm dist-tags from the dedicated endpoint with the probe timeout (#7346)
The tool version probe requested the full npm packument for each package (metadata for every historical version, 14-25 MB uncompressed for codex / opencode / openclaw) only to read its dist-tags, and it was the only latest-version probe without LATEST_PROBE_TIMEOUT, so it fell back to the proxy client's 600 s total timeout and the cards sat on "loading" for minutes on slow links while the large downloads starved the Hermes GitHub probe into "unknown".
Switch fetch_npm_dist_tags to registry.npmjs.org/-/package/{package}/dist-tags, whose body is the dist-tags object itself (tens of bytes to a few KB), escape scoped names as %2f, and apply the same 15 s probe timeout as the GitHub and PyPI probes. No data is lost: the only consumer reads latest and the tool's prerelease tags.
Fixes #7339
feat(usage): display output tokens per second in request log table and detail panel (#3369)
Show tokens/s next to the output token count in the request log table and the request detail panel.
The generation window prefers an explicit duration when present, otherwise latency minus first-token time for streaming requests, and falls back to full latency for non-streaming requests. Rows without a positive token count or window show nothing.
Closes #5936
fix(presets): update APIKEY.FUN URLs to apikey.fan
docs(release): add v3.20.3 release notes
chore(release): v3.20.3
fix(pricing): move retired DeepSeek V4 family to the V4.1 Flash tier
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.
fix(presets): declare third-party Token Plan DeepSeek V4 rows text-only
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.
feat(presets): rename DouBaoSeed preset to localized Volcengine Doubao
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.
fix(codex): skip unchanged incomplete rollout tails
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.
fix(claude): align quick model selection with form order
fix(tray): show bound ChatGPT account quota for managed Codex cards
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
feat(claude): add "Disable Artifact Tool" quick toggle to provider editor
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.
feat(codex): refresh aggregator model presets and catalogs
feat(codex): switch Kimi presets to native Responses direct-connect
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.
fix(usage): show Claude Fable weekly limit from the usage API limits array
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.
fix(codex): mirror vision-capable DeepSeek catalog and un-gate the legacy v4-flash alias (#7286)
Refresh the bundled DeepSeek official Codex catalog to the vendor's current models.json (slug deepseek-flash, image input modality, supports_image_detail_original) and remove deepseek-v4-flash from the confirmed text-only registry: the legacy id is still accepted by DeepSeek and routed to the vision-capable V4.1 Flash, so both the proxy media sanitizer and the generated Codex catalog must fail open for it. deepseek-v4-pro stays text-only per the vendor catalog.
Fixes #7283
fix(proxy): clamp sub-floor max_tokens to the Responses API minimum (#7287)
The Responses API rejects max_output_tokens below 16, but Anthropic clients legitimately send tiny probe budgets (Claude Desktop's model-availability probe uses max_tokens=1). The Anthropic→Responses conversion copied the value verbatim, so strict upstreams returned 400 and Claude Desktop reported the mapped model as unavailable under local routing.
Clamp 1..=15 up to 16 during conversion; 16+, 0 and non-integer values keep their pass-through semantics. Normal session requests convert byte-for-byte as before, and the Codex OAuth path still strips max_output_tokens afterwards.
Fixes #7103
fix(codex): coalesce adjacent commentary with pending tool calls (#7280)
The Codex Responses -> Chat Completions converter emitted two consecutive
assistant messages when one Responses model turn contained a commentary
message directly followed by function_call items. Chat upstreams imitate
the text-only assistant message as a complete turn and return
finish_reason=stop before the expected tool call, so long agent tasks end
after a progress update.
Merge pending tool calls into the directly adjacent assistant message when
it carries no tool_calls yet, attach pending reasoning per segment so
parallel calls do not duplicate it, and keep the new-message path for every
other boundary shape (user/tool boundaries, prior tool-call batches, media
flushes). The reasoning placeholder backfill still applies to coalesced
calls without reasoning.
Fixes #6529
Supersedes #6530
Co-authored-by: BigStrongSun <BigStrongSun@users.noreply.github.com>
fix(proxy): skip empty reasoning_content placeholders in Anthropic SSE streaming (#7227)
Some OpenAI-compatible upstreams (GLM, Qwen, DeepSeek-V4-Pro via ModelScope/DashScope,
Bailian, JD Cloud, ...) keep a `reasoning_content: ""` placeholder in every content
chunk instead of omitting the field. The reasoning branch of the OpenAI Chat -> Anthropic
SSE converter only checked for presence, and it shares `current_non_tool_block_type`
with the content branch, so every content chunk closed the open text block, opened an
empty thinking block, then reopened a text block. N content chunks became 2N content
blocks, half of them empty thinking blocks, which Claude Code rendered as one line per
token with dozens of empty Thought blocks and bloated the session JSONL.
Filter empty reasoning before entering the branch, matching the `!content.is_empty()`
guard the content branch already had and the non-streaming `openai_to_anthropic`
converter. Two regression tests pin block index, type and order: the placeholder shape
must yield a single text block and no thinking_delta at all, and real reasoning must
still yield one thinking block followed by one text block.
Fixes #5028
Fixes #4404
fix(codex): detect growing rollouts with a persisted byte cursor (#7219)
Codex keeps the rollout append handle open for the whole session, so on Windows NTFS the file mtime does not advance while the file grows and the pure-mtime gate skipped growing rollouts forever. Persist the observed byte length in session_log_sync.last_byte_offset for Codex rows and skip a file only when both mtime and size are unchanged. The parser now splits records on newlines manually so an incomplete trailing record is retried on the next pass without advancing the line cursor.
Fixes #6060
fix(codex): proxy image edits and Images API follow-ups (#7177)
* fix(codex): proxy image edits and Images API follow-ups
Follow-ups to #7036 for Codex's built-in ImageGen over the local proxy:
- Register `/images/edits` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them through the shared Codex standalone passthrough.
ImageGen posts to `images/edits` whenever a call references existing
images, and the router returned an empty 404 for it.
- Add `CodexStandaloneEndpoint::ImagesEdits`. Both Images endpoints share
one full-URL suffix table, so a complete Responses / Compact / Chat
Completions / Images URL derives the sibling endpoint (and vice versa)
while opaque full URLs keep failing closed.
- Apply the standalone sibling rewrite when the base URL is pasted as a
complete endpoint with the full-URL switch off, mirroring
`codex_chat_base_is_full_endpoint`; standalone requests no longer become
`.../chat/completions/images/generations`.
- Parse Images API streaming usage: `image_generation.completed` carries
`usage` on the event top level, so scan for `usage.input_tokens` before
falling back to the Chat Completions stream parser.
Related #7036
* fix(codex): match standalone URL suffixes case-insensitively
Keep endpoint detection and sibling rewriting consistent while preserving the original URL prefix and query. Cover mixed-case Images and Alpha Search endpoints through URL and proxy regression cases.
---------
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): preserve per-app settings on shutdown and port allocation (#7210)
On normal exit, stop_with_restore_keep_state() called the legacy global
proxy-config writer to clear the long-retired live_takeover_active flag.
That writer reads the Claude row and runs an UPDATE without a WHERE clause,
so every shutdown copied Claude's max_retries and the three timeout fields
onto the Codex, Gemini and Grok Build rows — and the UPDATE never carried
live_takeover_active in the first place, so the step had no effect beyond
the clobber. The ephemeral listen-port path did the same on every proxy
start.
Drop the dead write-back (live-config restore, backup cleanup and health
reset are unchanged) and persist the resolved ephemeral port through the
global-config interface, which only touches the shared listen/logging
columns. Two regression tests seed distinct per-app settings for all four
apps and assert they survive both paths.
Fixes #7204
fix(provider): preserve child metadata during universal sync (#7212)
Syncing a universal provider regenerates its Claude/Codex/Gemini child
providers. The child's `settings_config` was merged with the existing row,
but `meta`, `created_at` and `sort_index` were taken from the generated
object, and the universal provider never carries any of them from the UI.
The UPDATE therefore wiped the child's usage script, common-config opt-out,
endpoint auto-select and other per-app settings to `{}` and reset the sort
index to NULL, which pushed the card to the bottom of the list and looked
like the provider had been deleted and re-created.
Keep the existing child's `meta`, `created_at` and `sort_index` when the
row already exists. Name, base URL, key, models, website and notes are
still driven by the universal provider, and first-time creation still
inherits the parent's metadata.
Fixes #7134
feat(presets): rebrand DashScope as 千问AI平台 and refresh Qwen models (#7183)
Rename the domestic Bailian presets to 千问AI平台 with a dedicated icon, refresh the Qwen family to the 3.8 generation across Claude Code, Claude Desktop, Codex, Hermes, OpenClaw, OpenCode and Pi, add the domestic Token Plan presets, restore the international QwenCloud Coding Plan presets, and align OpenClaw/OpenCode/Pi model metadata with the official client docs.
fix(codex): honor proxy URL when model_provider is omitted (#7263)
When a Codex provider card's TOML has no `model_provider`, Codex falls back to
its built-in `openai` provider, but takeover wrote the proxy address as a
top-level `base_url` that Codex never reads, so requests bypassed the local
proxy and went straight to api.openai.com.
Treat the missing selector as the built-in `openai` provider and write the
proxy address to `openai_base_url`; the existing legacy-reroute migration then
turns it into a `cc-switch` custom provider table carrying the `PROXY_MANAGED`
bearer, the same shape every other third-party takeover uses. Explicit
provider selections are unchanged.
Refs #6256, #7217
fix(provider): update MiniMax defaults to M3 and remove expired offers (#7255)
* fix(provider): refresh MiniMax M3 presets and remove expired promotions
* fix(provider): complete MiniMax M3 capability declarations
---------
Co-authored-by: saladday <1203511142@qq.com>
chore(provider): remove Atlas Cloud sponsor status, keep presets
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`
docs(release): add v3.20.2 release notes
chore(release): v3.20.2
feat(provider): add SoleAPI sponsor presets across all nine apps
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 19…
…itHub
Rebase fork main onto upstream/main (v3.20.3, 06082e18, 33 new commits)
while keeping all fork modifications: connectivity test, model quick
switch, sidebar panel visibility, preset filtering, tray left-click
toggle, dual website links, disabled in-app updater, fork CI.
- Bump fork version to 3.20.3-1 (package.json, tauri.conf.json,
Cargo.toml, Cargo.lock).
- Merged upstream's v18->v19 schema migration with the fork's
website_url_2 column migration in the same migration arm.
- Added mcode entry to forkOfficialAllowlist (no official presets,
same treatment as hermes/openclaw/opencode/pi).
- Changed the top-left CC Switch brand link target from
https://ccswitch.io to https://github.com/farion1231/cc-switch.
- Updated HOW_TO_REBASE_UPSTREAM.md whitelist with the brand link
override.
style: apply prettier to files flagged by CI format check
修复 CI Frontend Checks 的 Check formatting 步骤失败
(App.tsx、ConnectivityTestDialog、ProviderCard、ProviderList、
provider schema 共 5 个文件)。
style: apply cargo fmt to connectivity-test era backend files
修复 CI 的 Check Rust formatting 步骤失败(connectivity_test、
backup、tests、provider 等文件格式不符合 rustfmt 规范)。
chore(native): archive sync-upstream
docs(fork): update rebase whitelist for connectivity-test era fork features
- 白名单补充连通性测试前后端、模型快捷切换、双官网链接测试、
CI 裁剪版、README/CHANGELOG、.comet/superpowers 产物等 fork 专属文件
- 新增共享文件 fork 专属语义表(托盘左键切换、禁用更新器、hidden 字段等)
- 修正 §3.1 rebase 方向说明:rebase 中 fork 侧是 --theirs 而非 --ours
(v3.20.2 同步实测确认,与 merge 语义相反)
chore: bump fork version to 3.20.2-1 after upstream v3.20.2 rebase
重放 fork 提交到 upstream/main(f3b18df1,v3.20.2)后统一版本号。
Cargo.lock 同步移除 updater 依赖树(fork 已禁用应用内更新器)。
chore(native): archive tray-left-click-toggle-window
chore(comet): 记录 tray-left-click-toggle-window 验收通过与归档状态
feat(tray): 托盘图标左键单击切换主窗口显示/隐藏
- tray.rs 新增 show_main_window/hide_main_window/toggle_main_window 助手:
显示语义与"打开主界面"一致,隐藏与"关闭到托盘"平台处理一致
(Windows set_skip_taskbar、macOS apply_tray_policy)
- lib.rs 托盘 show_menu_on_left_click 改为 false,左键 Click(Left, Up)
触发切换(过滤 Down/Up 双事件),右键仍弹菜单;Enter/Click 用量刷新保持
- show_main 菜单事件与 macOS Reopen 复用 show_main_window
docs(readme): 更新日志补充连通性测试、双官网链接与托盘左键切换
feat(model-quick-switch): 获取模型列表成功后自动展开选择器
SearchableModelPicker 增加可选 open/onOpenChange 受控展开(不传保持内部自管,
表单等既有用法不受影响);快捷切换弹窗拉取成功且列表非空时直接展开,关闭弹窗复位。
chore(native): archive fix-quick-switch-1m
chore(comet): 记录 fix-quick-switch-1m 验收通过与归档状态
fix(model-quick-switch): 补齐 Haiku 显示名同步,支持不选模型仅翻转 1M
- 显示名同步映射补上 ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME(沿用空/旧base/旧base[1M] 覆盖、自定义保留语义)
- 新增 setClaudeOneMInSettings:仅原地翻转 1M 标记,不改各角色模型 base 与显示名
- 弹窗打开时 1M 开关按供应商当前状态初始化,避免已开 1M 被静默剥离
- claude 未选模型且 1M 状态有变化时允许保存;无可应用变化时保存禁用
- 补单测与四语言 toast 文案
feat(provider-website-links): 供应商支持第二个官网链接,主页横排双链接
- Rust Provider/UniversalProvider 新增 website_url_2(serde websiteUrl2,skip none)
- SQLite providers 表新增 website_url_2 列(schema v18→v19 幂等迁移 + create_tables 补列 + 旧 config.json 迁移)
- DAO SELECT/INSERT/UPDATE/replace 全链路带出新列
- 表单共享 BasicFormFields + 统一供应商表单第二输入框;zod 同第一链接规则校验与回填
- ProviderCard 主页双链接横排、两空格分隔、逐链接 openExternal 打开默认浏览器
- 搜索匹配、复制供应商、统一供应商生成 claude/codex/gemini 随行透传
- gemini_auth packycode 认证检测纳入 website_url_2;ClaudeDesktop/GrokBuild 预设切换清空第二链接;卡片同值链接去重
- 4 语言 i18n + 前端/前端单测覆盖
Comet change dual-website-links-homepage(19/19 验收通过,归档)
feat(connectivity-test): 拉取模型列表改为「搜索添加」选择器交互
- 新增共享组件 SearchableModelMultiPicker:搜索过滤 + 多选 + 已在列表标记 + 已选计数
- ConnectivityTestDialog 获取模型列表后不再全量追加合并去重,改为会话内缓存完整列表并自动弹出一次选择器;用户搜索挑选后添加进表格并自动勾选
- 重新拉取只刷新缓存,已添加进表格的行保留;拉取结果仍仅会话内有效,不写回供应商配置
- 四语言 i18n 新增 searchAdd / addToTest / alreadyInList / selectedCount
- 同步更新 ConnectivityTestDialog 测试并新增 SearchableModelMultiPicker 测试
feat(connectivity-test): 探测目标按供应商 API 格式对齐真实转发链路
- build_claude 按 get_claude_api_format 分派端点与请求体:openai_chat → /v1/chat/completions(复用 transform_claude_request_for_api_format,流式注入 stream_options.include_usage)、openai_responses → /v1/responses、gemini_native → /v1beta/models/{model}:{method}(resolve_gemini_native_url)、anthropic 保持 /v1/messages 并补 anthropic-version/beta 头
- build_codex 按 should_convert_codex_responses_to_chat/anthropic 分派:chat → /chat/completions(responses_to_chat_completions_with_reasoning)、anthropic → /v1/messages(responses_request_to_anthropic,含 max_output_tokens 注入与 [1m] 剥离 + context-1m beta 头)
- 鉴权改用 ClaudeAdapter/CodexAdapter 的 extract_auth + get_auth_headers(与真实出站请求同源:AUTH_TOKEN→Bearer、ANTHROPIC_API_KEY→x-api-key、gemini→x-goog-api-key、GeminiCli OAuth→Bearer、OpenRouter→Bearer、Codex 按 apiKeyField)
- 转换前剥离 [1m] 标记;meta.isFullUrl 或 base 已是完整端点时直接使用 base_url;自定义 body 浅合并移至转换后的最终协议体
- 新增 18 个目标构造断言单测;Comet 归档产物同步提交
chore: archive connectivity-test-optimizations
feat(connectivity-test): 单一模型表格 + 实时拉取模型 + 勾选即测试范围 + 新默认提示词
- 弹窗合并为单一模型表格:打开即全部模型行(初始待测试)、行勾选 + 表头全选(含半选态)、5 格统计卡,移除上方独立选择区
- 获取模型列表:凭据提取抽为共享模块 src/utils/providerCredentials.ts(ModelQuickSwitchDialog 同步改引用),复用 fetch_models_for_config 拉取 /v1/models,按 ID 追加合并去重、仅会话内有效
- 默认提示词改为「你好,你可以帮我做什么事情」:前后端常量同步,merge 差异判断基准跟随
- 勾选即测试范围:后端 connectivity_test_provider_models 新增 modelIds 参数(None/空白回退静态清单),前端逐模型独立 invoke 并行 + 行级流式更新
- 一轮测完自动勾选失败项,全部成功保持原勾选
fix(connectivity-test): UI 四项修复
- 弹窗抬至 alert 层(z-[60]):打开连通性测试时 header 切换栏正确变灰,
详情子弹窗同步抬层(同 z + 更晚挂载 portal 在上)
- 批量检测入口移至 header 图标组(状态提升到 App,ProviderList 经
props 读取探针结果),不再另起一行
- 批量检测可停止:runId 代次守卫(Tauri invoke 无法真正取消),再次
点击即清除 waiting/running 徽标、丢弃在途迟到写入、不再拉起排队项
- 弹窗打开自动勾选模型:已保存默认测试模型 → 供应商当前模型
(claude env.ANTHROPIC_MODEL / codex config.toml model,新增
currentProviderModelId 与后端同口径)→ 清单首个
前端全量 141 文件 / 1106 测试全绿;typecheck clean
chore: archive per-model-connectivity-test
fix(connectivity-test): shouldShowTestEntry 增判 providerType + 收尾清理
chore(connectivity-test): 勾选 Task 9 / OpenSpec 5.1-5.2 + verify 报告
chore(connectivity-test): 勾选 Task 8 / OpenSpec 4.1-4.3
refactor(connectivity-test): 移除旧 stream_check 可达性探测全链路(保留表结构)
- 删除 6 个文件:commands/stream_check.rs、services/stream_check.rs、
database/dao/stream_check.rs、hooks/useStreamCheck.ts、lib/api/connectivity-check.ts、
components/usage/ConnectivityCheckConfigPanel.tsx
- 移除 mod.rs 声明、lib.rs generate_handler! 注册、cleanup_old_stream_check_logs 调用
- 从 SYNC_SKIP_TABLES / SYNC_PRESERVE_TABLES 移除 stream_check_logs(表结构保留在 schema.rs)
- 更新 backup.rs 测试:移除 stream_check_logs fixture 与断言
- 更新 SettingsPage:移除 ConnectivityCheckConfigPanel AccordionItem 与 FlaskConical 导入
- i18n:删除 streamCheck.* 命名空间(zh/en/ja/zh-TW 四语言)
- 更新注释:移除 stream_check 旧引用(forwarder.rs、provider.rs、userAgentPresets.ts、
connectivity_test.rs)
- 修复 4 个 pre-existing clippy lint(connectivity_test: matches! 宏、unused_unit、
derive Default)
- 移除 ProviderList.test.tsx 中已失效的 useStreamCheck mock
chore(connectivity-test): 勾选 Task 7 / OpenSpec 2.4 3.1-3.3
feat(connectivity-test): 供应商列表集成(弹窗入口 + 批量探针徽标)
chore(connectivity-test): 勾选 Task 6 / OpenSpec 2.2-2.3
fix(connectivity-test): 模型列表解析与后端对齐(每条目首个匹配,提取共享 helper)
feat(connectivity-test): 连通性测试弹窗/详情/徽标组件与四语言文案
chore(connectivity-test): 勾选 Task 5 / OpenSpec 2.1
test(connectivity-test): 锁定 toBackendParams 字段映射(customHeaders/customBody 等)
feat(connectivity-test): 前端命令绑定与测试/探针状态 hooks
chore(connectivity-test): 勾选 Task 4 / OpenSpec 1.4
feat(connectivity-test): 测试参数持久化纯函数(缺省即默认值)
chore(connectivity-test): 勾选 Task 3 / OpenSpec 1.3
feat(connectivity-test): Tauri 命令与注册,官方/动态端点过滤,sanitize 隔离私有字段
chore(connectivity-test): 勾选 Task 2 / OpenSpec 1.2
feat(connectivity-test): 后端直连执行器(reqwest+SSE,总/空闲超时,度量与错误识别)
feat(connectivity-test): 后端直连执行器(reqwest+SSE,总/空闲超时,度量与错误识别)
chore(connectivity-test): 勾选 Task 1 / OpenSpec 1.1
fix(connectivity-test): claude 鉴权对齐 adapter 顶层 apiKey/api_key 兜底
feat(connectivity-test): 新增后端连通性测试服务类型/模型解析/协议构造
chore: archive model-quick-switch-picker-scroll
chore: model-quick-switch-picker-scroll 验证报告与状态元数据
fix: 模型快捷切换弹窗内模型列表滚轮无法滚动
模态 Dialog 的 RemoveScroll 会对 shards 外的 portaled PopoverContent
wheel 事件无条件 preventDefault。在 SearchableModelPicker 的
PopoverContent 捕获阶段阻断 wheel 传播,恢复原生滚动。
chore: bump fork version to 3.20.1-1 after upstream v3.20.1 rebase
fix(form): only auto-expand Claude advanced options on divergent model mapping
Uniform mapping produced by the quick-set button (all roles equal to the
fallback model) no longer counts as an advanced value, so the advanced
section stays collapsed on edit. A 1M marker is stripped before comparing
so marked roles are not misjudged as divergent. Truly customized mappings
still auto-expand the section.
feat: align fallback-model quick-set buttons with model input right edge
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
docs: update README with fork build notes and quick-move provider changes
chore: archive comet-native claude-model-quick-access
feat: surface Claude fallback model as quick-access section below endpoint
Move the default fallback model (ANTHROPIC_MODEL) block out of the
collapsed Advanced Options into a quick-access section directly below
the request endpoint: input + 1M checkbox + quick-set + fetch-models
buttons are all reachable without expanding.
- header toggle-all "declare 1M" now includes the fallback model
(Haiku still excluded); partial selection shows indeterminate state
- drop the duplicated quick-set/fetch buttons and the old fallback
block inside Advanced Options
- exclude claudeModel from hasAnyAdvancedValue so a fallback-only
value no longer auto-expands Advanced Options
- quick-set priority, env write semantics, and OAuth preset branches
unchanged; add 4 component tests covering the new layout
chore: archive comet-native provider-right-click-top-bottom
Enable native workflow in .comet/config.yaml and move the Comet managed
state block in .gitignore so the exclusion rules stay grouped with the
other fork tool artifacts.
feat: add provider right-click move-to-top/bottom and insert new providers at second position
- ProviderList: context menu with quick move-to-top/move-to-bottom, reusing
update_providers_sort_order and refreshing tray menu like drag sort
- useAddProviderMutation: default insert at index 1 with explicit sortIndex
materialization for existing items (avoids defined-first sort trap)
- i18n: quickMove* strings for zh/en/zh-TW/ja
ci: stop flaky WSL2 linker job from blocking main
The backend-windows-wsl2 job intermittently fails with LNK1327 /
mt.exe c1010070 when the Windows runner's mt.exe cannot parse a
manifest temp file on the WSL2 UNC TEMP path. The same flaky failure
occurs upstream and is unrelated to fork changes. Mark the job
continue-on-error so main stops going red on it; the result stays
visible in the Actions UI.
style: apply prettier to files touched by recent comet archives
Frontend CI format:check failed on 6 files modified by the app-name
and model-quick-switch archive commits plus the updater-disable commit.
Run prettier --write so format:check passes again.
chore: archive ccs-fork-app-name-cc-switch
chore: archive ccs-model-quick-switch-ui-polish
docs: rewrite README to document fork differences vs upstream
README 从上游完整功能介绍改为仅记录 fork 与上游的差别,完整功能
文档指回上游。涵盖 8 项 fork 魔改:构建标识/开发预览、预设精简、
Claude 表单调整、模型搜索移植、主页模型快速切换、侧边栏面板显隐、
关闭应用内更新、打包范围,并说明版本号与 rebase 同步流程。
feat: disable in-app updater, point users to GitHub releases
移除 tauri-plugin-updater 依赖与自动更新链路,改为统一引导用户到
GitHub Releases 手动下载新版。
- 前端:updater.ts 恒返回 up-to-date;UpdateContext 取消启动自检;
AboutSection「检查更新」直接打开 fork releases 页;DatabaseUpgrade
恢复界面去掉下载进度条,改为打开发布页。
- 后端:移除 updater 插件注册与 UpdaterExt 调用;
install_update_and_restart 降级为空壳,check_app_update_available
返回当前版本号;check_for_updates 指向 fork releases;capabilities
去掉 updater:default 权限。
- 打包:tauri.conf.json 删 plugins.updater、createUpdaterArtifacts=false;
Cargo.toml / package.json 删 updater 依赖,Cargo.lock / pnpm-lock
同步重生成。
- CI:release.yml 移除签名收集与 assemble-latest-json job,删除
.tar.gz/.sig 处理,release body 去掉 updater 说明。
版本号统一为 3.20.0-1(合法 semver 且 MSI 第 4 位为 1)。
Cargo.lock / pnpm-lock.yaml 已随依赖移除重生成,cargo check 通过。
ci: build only Windows x64 and macOS arm64, unsigned
feat: gate dev panel behind dev-only env flag
chore(comet): archive ccs-sidebar-panels-visibility change
fix: apply skills visibility guard to hermes branch toolbar button
feat: include prompts in sidebar panel visibility settings
fix: persist visibleSidebarPanels setting in backend AppSettings
style: use compact pill toggles for sidebar panel visibility
feat: add sidebar panel visibility switches to settings
feat: add sidebar panel visibility settings and wire into toolbar buttons
chore(comet): archive ccs-home-model-display change
fix(fork): read grokbuild api key from config TOML in quick switch dialog
feat(fork): align model badge with upstream extractModelBadge
feat(fork): show current model badge and quick-switch entry on provider cards
feat(fork): add model quick switch dialog
feat(fork): add provider model read/write utils with tests
chore(comet): archive ccs-model-search-port change
feat(i18n): add model search picker keys for all locales
feat(form): port SearchableModelPicker from upstream for searchable model selection
chore(comet): archive ccs-claude-form-layout change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-claude-form-layout/
- Sync claude-form-layout spec to main specs
- Annotate design doc as archived
- Add verification report
fix(i18n): update advanced options hint, add ja/zh-TW toggle-all, drop dead apiFormatHint key
fix(form): correct 1M toggle-all alignment direction (ml-1)
fix(form): align 1M toggle-all checkbox with row checkboxes
refactor(form): move api format select into EndpointField rightSlot
fix(form): widen api format select to fit option text
feat(form): add 1M toggle-all checkbox in model mapping header
feat(form): move api format select next to endpoint input
chore(comet): archive ccs-provider-presets-trim change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-provider-presets-trim/
- Sync provider-presets-visibility spec to main specs
- Annotate design doc as archived
- Add verification report
docs(fork): add presets-trim files to rebase whitelist
test(fork): mock IS_FORK_BUILD false in unit tests for upstream-form semantics
feat(fork): wire official preset filter into provider forms
feat(fork): add hidden field to preset interfaces
feat(fork): add official preset allowlist and filter util
chore(comet): archive ccs-fork-scaffolding change
- Move change artifacts to docs/openspec/changes/archive/2026-08-18-ccs-fork-scaffolding/
- Sync fork-build-identity spec to main specs
- Annotate design doc as archived
- Add verification report
feat(fork): add dev config with isolated identifier and config dir
docs(fork): complete rebase whitelist with all fork-touched files
fix(fork): guard setTitle with isTauri in test/non-tauri env
fix(fork): define __CCS_FORK_BUILD__ in vitest config
feat(fork): add DevPanel and settings footer entry
feat(fork): set macOS window title to CC Switch (Fork) on startup
feat(fork): inject __CCS_FORK_BUILD__ compile-time constant
feat(fork): bump version to 3.19.2-fork.1 and rename product to CC Switch (Fork)
docs(fork): add upstream rebase workflow and ignore tool artifacts
fix(proxy): accept the whole grok-4.x (x>=5) family in the reasoning-effort whitelist (#7369)
Replace the verbatim grok-4.5 / grok-4.6 entries in supports_reasoning_effort with a rule that parses the grok-4.x minor version and accepts x >= 5, mirroring the existing GPT-5+ rule. This covers grok-4.7 (released 2026-09-21), whose reasoning effort was previously dropped on the Claude -> Chat, Claude -> Responses and Codex Responses -> Chat conversion paths, and lets future releases pass without another whitelist edit. The grok-build-* family is retained for saved providers.
Co-authored-by: allenxu09 <171831965+allenxu09@users.noreply.github.com>
docs(release): add v3.20.4 release notes
chore(release): v3.20.4
feat(pricing): add Grok 4.7 and GLM-5.3-FlashX
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.
fix(i18n): remove outdated OpenCode Go referral offer
fix: add SudoCode.chat alternate API endpoint
feat(settings): add a GitHub star prompt to the About card
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
feat(presets): add Soshow aggregator presets
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.
fix(ci): stabilize frontend tests and verify renderer build
fix(mcode): refresh provider state after removal (#7578)
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
fix(lib): reset skip_taskbar before window.show() across entry points (#6348)
5处 window.show() 调用中 4处没有在 show() 前重置 Windows 的 skip_taskbar,导致以下场景出现窗口可见但不在任务栏、点最小化后窗口消失的 bug:
- single_instance handler (lib.rs:360-361):当原进程因 silent_startup / 关闭到托盘等处于 skip_taskbar(true) 状态时,从快捷方式/开始菜单唤起已运行实例触发。
- handle_deeplink_url 的 focus_main_window 分支 (lib.rs:256):通过 ccswitch:// URL 唤起时。
- DB 恢复流程 (lib.rs:556):数据库版本不匹配时显示恢复界面。
- RunEvent::Opened 处理器 (lib.rs:1803):系统接收到 deep-link URL 时。
修复后的行为与 tray.rs::handle_tray_menu_event::show_main 分支一致(那里已有相同的 set_skip_taskbar(false) 调用)。
经用户测试验证:autostart + silent_startup + 从开始菜单/快捷方式启动的场景下,最小化按钮恢复正常工作。
fix(proxy): omit missing tool descriptions in the Codex responses-to-chat converter (#7378)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): preserve max effort for GPT-5.6 and GPT-6 Astra (#7531)
fix(misc): ignore shell startup output when probing tool versions in WSL (#7348)
try_get_version_wsl runs "${SHELL:-sh}" -lic '<tool> --version' through
wsl.exe and takes the first x.y.z anywhere in stdout. A login+interactive
shell runs the distro's startup files first, so their output shares that
stdout. On Ubuntu, /etc/profile.d/update-motd.sh prints the MOTD once a day,
starting with "Welcome to Ubuntu 24.04.4 LTS", and the card then showed
24.04.4 as the Claude Code version. The once-a-day stamp makes it appear
after the first probe of the day and vanish on refresh.
The payload now prints a sentinel before running the tool, and parsing looks
only at what follows its last occurrence. The last occurrence handles the
-lic || -lc || -c fallback chain printing it several times; matching it as a
substring handles OSC sequences glued in front of it. If the sentinel is
absent (the shell failed before the payload ran), the output is used as
before. The stderr fallback for tools that print their version there is kept.
Fixes #7347
Co-authored-by: lyfuci <lyfuci11@gmail.com>
fix(claude): hide session URL attribution (#7053)
* fix(claude): hide session URL attribution
* docs(claude): document session URL attribution
fix(ui): reset provider scroll when switching apps (#5211)
fix(pi): fix provider logo (#6826)
fix(i18n): use the Pi form's own term for the zh-TW duplicate-key message (#7187)
Follow-up to the nit on #6768. In zh-TW the Pi form labels the field
供應商識別碼 and says 供應商識別碼為必填 when it is empty, but the
duplicate message I added said 供應商標識 — the term Hermes, OpenCode and
OpenClaw use for their own field. One form, two names for the same thing.
The other three locales already keep the label and both messages on one
term, so this makes zh-TW match the pattern rather than introduce one:
en Provider key / Provider key is required / This provider key already exists
ja プロバイダーキー / プロバイダーキーを入力してください / このプロバイダーキーは既に存在します
zh 供应商标识 / 供应商标识不能为空 / 该供应商标识已存在
zh-TW 供應商識別碼 / 供應商識別碼為必填 / 該供應商標識已存在 <- the odd one out
hermes.form.providerKeyDuplicate carries the same sentence and is left
alone: its own label is 供應商標識, so it is already consistent.
Verified: managementListLocales 7/7. Full unit suite 1085/1087, the same
two App.test.tsx integration cases failing before and after.
fix(proxy): strip stop param for GitHub Copilot to unblock auto mode classifier (#5404)
Claude Code's auto mode classifier sends `stop_sequences` on every
safety check. On the GitHub Copilot path the Anthropic -> OpenAI Chat
conversion mapped it to `stop`, and Copilot's Chat Completions route
for Claude models answers that request with HTTP 400, so every
classifier call failed while normal chat kept working.
- Remove the converted `stop` field only when the provider is GitHub
Copilot; other OpenAI-compatible upstreams keep it.
- Copilot detection in the transform now also covers providers that
are recognised only through `env.ANTHROPIC_BASE_URL`.
- Regression tests for the Copilot (meta and env URL) and generic
provider cases.
Fixes #5175
fix(codex): keep stored auth when the live snapshot has no credential (#7434)
Editing the current Codex provider seeds the form from the live ~/.codex
snapshot, and switching away backfills that snapshot into the DB row. When a
provider table declares its own credential source (an Authorization header,
env_key, auth/aws), no bearer token is injected into config.toml and the
shared auth.json is deleted on third-party switches, so the live snapshot is
permanently `{ auth: {}, config }` while auth.OPENAI_API_KEY in the DB is the
only copy of the key. Both paths treated that empty live auth as
authoritative and silently erased the stored key, which in turn made the
usage-script modal misread the provider as an official subscription and
reset the saved script.
- Treat a credential-less live auth (missing auth.json or the bare
auth_mode logout marker) as an absent field in both the edit dialog and
the switch-away backfill, keeping the stored auth template. Live still
wins whenever it carries material, and official providers keep the live
login as their source of truth.
- Gate the official exemption on is_codex_official_provider /
resolveCodexOfficialIdentity rather than the category alone: a legacy
fixed official card without a persisted category is still written as
official, so the fallback must not resurrect a logged-out login on
switch-back.
- Cover both layers with regression tests, including the category-less
official shapes.
Fixes #7433
Supersedes #7492
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): lift additional_tools input carriers on the Codex chat conversion (#7454)
Codex talks to third-party providers over its Responses Lite transport,
which ships the request's extra tools in an `additional_tools` input
carrier: `{"type":"additional_tools","role":"developer","tools":[...]}`,
a role but no `content`. The Responses->Chat conversion let that carrier
fall through the generic "item with a role is a message" arm and emitted
`{"role":"system","content":null}` at messages[1], which strict Chat
gateways reject with `messages[1]: missing field "content"` (or
`data did not match any variant of ...SystemMessageContent`). The tools
carried inside it were also dropped on the Chat and Anthropic conversions.
- Skip `additional_tools` items in the Chat message walk; a carrier
declares tools, it is not a message.
- Collect carrier tools through the existing CodexToolContext registry
(dedup, namespace flattening, custom-tool conversion), which also feeds
the Anthropic conversion and the response-side name round-trip.
- Requests without a carrier convert byte-for-byte as before.
Known boundary: `custom` children inside a namespace tool
(`functions.exec`) are still not lifted; that needs its own response-side
name round-trip and is left for a follow-up.
Diagnosed independently in #6158 by BigStrongSun (fix in #6159) and in
fixture.
Fixes #7451
Fixes #6158
Refs #6033
Refs #6776
Supersedes #6159
fix(proxy): normalize Codex detail:original images for Chat gateways (#7476)
Codex marks tool-returned images with detail:"original" whenever the model
catalog advertises supports_image_detail_original, a Responses-only value.
The Responses->Chat conversion copied it verbatim into image_url.detail, and
strict OpenAI-compatible Chat gateways (Ark/Doubao, SenseNova, GLM, vLLM, ...)
reject the whole request with 400. Because Codex replays the full rollout on
every turn, one poisoned view_image result made the session fail permanently,
including plain-text turns.
Two layers:
- tool_media::merge_top_level_detail downgrades original -> auto, so already
poisoned rollouts and Codex versions before 0.155.0 (which do not normalize
replayed history themselves) recover immediately. auto/low/high are untouched.
- codex_catalog_model_entry always writes supports_image_detail_original:false
on the ProxyChat profile, so Codex stops emitting original at the source once
the catalog is regenerated. NativeResponses/Anthropic profiles and the DeepSeek
official catalog are unaffected.
Top-level user images were never affected: responses_content_to_chat_content
drops the part-level detail entirely.
Fixes #6495
Fixes #7458
Refs #6569
Refs #5805
fix(omo): detect unified config on the WSL side when the OpenCode dir is in WSL (#7550)
When the OpenCode config directory points into WSL
(\\wsl.localhost\<distro>\home\<user>\.config\opencode), OMO >= 4.19.3 keeps
its unified config in the WSL-side home (~/.omo/omo.jsonc). Detection only
probed the Windows home, never found it, and silently fell back to the legacy
per-plugin file that OMO no longer reads, so provider switches looked
successful but had no effect.
- Add derive_wsl_home_dir to derive the WSL-side home from a WSL UNC path
(home/<user> or root); non-WSL and non-home paths return None.
- Probe a list of home candidates for the unified config, WSL-side home
first and the OS home as a fallback, shared by the read, write and delete
paths.
- Detection only: when no unified config exists on either side the legacy
fallback is unchanged.
Fixes #7363
Supersedes #7367
docs(partner): add a FluxA purchase link and tighten its sponsor blurb
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.
fix(claude-desktop): expose the 1M context variant for DeepSeek V4 routes
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.
feat(presets): add FluxA Token Plan partner presets
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.
fix(presets): correct AICodeWith Codex-family endpoint to /v1
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.
fix(ui): render monochrome preset icons in the foreground color
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.
docs(user-manual): point Chat-only Codex examples at plan endpoints
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.
fix(pricing): restore DeepSeek V4 Pro price and seed Qwen3.8 and Hy4 models
- 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).
fix(presets): move MiniMax CN and BaiLing to their current official endpoints
- 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.
feat(codex): refresh CN presets from the Responses API audit
- 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.
test(claude-desktop): run Linux path tests on unix hosts and cover Linux in provider sync tests
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.
fix(ui): stop expanding stale cached usage tiers on ineligible provider cards
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.
fix(skills): 支持 skillId 与目录名不一致的技能安装和更新 (#6381)
* fix(skills): resolve mismatched skillId directories
* fix(skills): preserve source path during updates
* fix(skills): refresh source URL after repository relocation
* test(skills): isolate relocation fixture from legacy home
feat(opencode): 支持从获取的模型列表中搜索并批量添加所选模型 (#7515)
* feat: implement fetched models selection and UI components
* fix(opencode): prevent model search from submitting provider form
Add Kimi Global preset variants (kimi.ai platform + api endpoints) (#7526)
Add a "Kimi Global" twin immediately after the domestic "Kimi" API preset and a "Kimi For Coding Global" twin after the domestic "Kimi For Coding" preset, in every preset file that ships them (claude, claude-desktop, codex, hermes, openclaw, opencode, pi).
"Kimi Global" points to the international open platform https://platform.kimi.ai?aff=cc-switch (apiKeyUrl mirrored as https://platform.kimi.ai/console/api-keys?aff=cc-switch) and swaps the endpoint host api.moonshot.cn for api.moonshot.ai with each file's path unchanged (/v1 or /anthropic).
"Kimi For Coding Global" points to the international site https://www.kimi.ai/code/?aff=cc-switch and swaps the endpoint host api.kimi.com for api.kimi.ai with the path unchanged (e.g. https://api.kimi.ai/coding/v1).
Domestic presets are unchanged; category, icons, apiFormat, model catalogs, endpointCandidates (host swapped), partnerPromotionKey and settings otherwise mirror the domestic blocks exactly.
primePartner stays only on the domestic presets so the new variants do not alter prime-partner ordering.
The Pi twins get distinct providerKey values ("cc-switch-kimi-global", "cc-switch-kimi-for-coding-global") because providerKey is the preset-level primary key and must not collide with the domestic presets.
Rebalance sponsor CTAs around Kimi Code plan with dual-region links (#7522)
Kimi Code plan purchases have resumed, so the README sponsor sections go back to leading with the coding plan instead of the API-only flow written during the purchase pause.
All four locales (EN/ZH/JA/DE) now present Kimi Code plan (中文站 | Global) as the primary CTA and the Kimi Open Platform API (中文站 | Global, with the first-top-up bonus) as the secondary one, mirroring the domestic|global split already used for API links.
The aff and track_id values are unchanged.
fix(prompts): refresh active prompts after external file edits (#7194)
Reload the enabled prompt from its live file when the Prompts list loads, and refresh the open panel when the window regains focus, so external edits to CLAUDE.md / AGENTS.md show up without a restart.
- Keep saved templates available when the live file cannot be read.
- Skip the backfill for empty or whitespace-only files, matching enable_prompt.
- Skip the backfill while a sync/restore holds the sync lock, so a list load cannot write a stale live file into a freshly restored database.
fix(skills): raise archive entry limit so large skill repos install (#7489)
Installing hugohe3/ppt-master from skills.sh failed with
ARCHIVE_TOO_MANY_ENTRIES: the GitHub archive we download is the whole
repository (13,248 entries), and the 10,000-entry cap added in #5811
treated it as a zip bomb.
- Raise MAX_ARCHIVE_ENTRIES to 30,000 for both the remote archive and
local ZIP paths, and align WebDAV's MAX_EXTRACT_ENTRIES so a skills.zip
containing such a skill still restores.
- Charge every file entry at least one 4 KiB block against the existing
512 MiB budget, mirroring directory accounting, so empty-file archives
are bounded by the byte budget rather than by the entry count alone.
- Add tests for a 13k-entry repo archive and for the per-file minimum
charge.
Fixes: https://github.com/farion1231/cc-switch/issues/7475
feat: add MiniMax Code harness support (#7383)
Add MiniMax Code (mcode) as a managed application: provider and model management through the native custom_provider configuration, MCP servers, Skills, global instructions (AGENTS.md), read-only local session history with resume, and usage import from the native runtime database. Native files shared by the TUI and desktop are written under the native lock with rollback when the database commit fails, unrelated native configuration is preserved, and the data directory honors MINIMAX_DATA_DIR and MAVIS_DATA_DIR. Deleting sessions stays in the native application because they are coupled to runtime state.
fix(codex): recover stale account bindings during takeover (#7395)
After deleting a managed ChatGPT account and logging in again, a Codex provider can still reference the old local account ID, which made route switching, takeover, startup restoration and rebinding fail permanently.
Distinguish a missing outgoing account from an existing account with no matching live token. A missing account only releases its ownership marker after the persisted account store is verified; the target write policy stays in force. A stale target now reports an account-selection instruction instead of a bare not-found error, including when takeover is already enabled.
Fixes #7392
feat(claude-desktop): support 3P configuration on Linux (#7331)
Add Claude Desktop 3P configuration support on Linux. Resolve the config root from an absolute XDG_CONFIG_HOME, falling back to ~/.config; when CC Switch itself runs as a Flatpak, use the host ~/.config instead of the sandbox-private XDG_CONFIG_HOME. Update the en/zh/ja user manuals with Linux paths and the Flatpak boundary.
Fixes #4855
fix(proxy): omit missing tool descriptions instead of serializing null (#7319)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): keep reasoning effort for grok-4.6 and pass xhigh through verbatim (#7318)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(models): support Zhipu OpenAI Responses model lists (#7330)
Zhipu's Responses endpoint (open.bigmodel.cn/api/v1) serves its model list in the Codex remote catalog shape (models[].slug) instead of the OpenAI-compatible data[].id shape, so the Codex form's model fetch came back empty. The parser now falls back to models[].slug when data is absent.
fix(misc): fetch npm dist-tags from the dedicated endpoint with the probe timeout (#7346)
The tool version probe requested the full npm packument for each package (metadata for every historical version, 14-25 MB uncompressed for codex / opencode / openclaw) only to read its dist-tags, and it was the only latest-version probe without LATEST_PROBE_TIMEOUT, so it fell back to the proxy client's 600 s total timeout and the cards sat on "loading" for minutes on slow links while the large downloads starved the Hermes GitHub probe into "unknown".
Switch fetch_npm_dist_tags to registry.npmjs.org/-/package/{package}/dist-tags, whose body is the dist-tags object itself (tens of bytes to a few KB), escape scoped names as %2f, and apply the same 15 s probe timeout as the GitHub and PyPI probes. No data is lost: the only consumer reads latest and the tool's prerelease tags.
Fixes #7339
feat(usage): display output tokens per second in request log table and detail panel (#3369)
Show tokens/s next to the output token count in the request log table and the request detail panel.
The generation window prefers an explicit duration when present, otherwise latency minus first-token time for streaming requests, and falls back to full latency for non-streaming requests. Rows without a positive token count or window show nothing.
Closes #5936
fix(presets): update APIKEY.FUN URLs to apikey.fan
docs(release): add v3.20.3 release notes
chore(release): v3.20.3
fix(pricing): move retired DeepSeek V4 family to the V4.1 Flash tier
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.
fix(presets): declare third-party Token Plan DeepSeek V4 rows text-only
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.
feat(presets): rename DouBaoSeed preset to localized Volcengine Doubao
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.
fix(codex): skip unchanged incomplete rollout tails
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.
fix(claude): align quick model selection with form order
fix(tray): show bound ChatGPT account quota for managed Codex cards
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
feat(claude): add "Disable Artifact Tool" quick toggle to provider editor
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.
feat(codex): refresh aggregator model presets and catalogs
feat(codex): switch Kimi presets to native Responses direct-connect
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.
fix(usage): show Claude Fable weekly limit from the usage API limits array
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.
fix(codex): mirror vision-capable DeepSeek catalog and un-gate the legacy v4-flash alias (#7286)
Refresh the bundled DeepSeek official Codex catalog to the vendor's current models.json (slug deepseek-flash, image input modality, supports_image_detail_original) and remove deepseek-v4-flash from the confirmed text-only registry: the legacy id is still accepted by DeepSeek and routed to the vision-capable V4.1 Flash, so both the proxy media sanitizer and the generated Codex catalog must fail open for it. deepseek-v4-pro stays text-only per the vendor catalog.
Fixes #7283
fix(proxy): clamp sub-floor max_tokens to the Responses API minimum (#7287)
The Responses API rejects max_output_tokens below 16, but Anthropic clients legitimately send tiny probe budgets (Claude Desktop's model-availability probe uses max_tokens=1). The Anthropic→Responses conversion copied the value verbatim, so strict upstreams returned 400 and Claude Desktop reported the mapped model as unavailable under local routing.
Clamp 1..=15 up to 16 during conversion; 16+, 0 and non-integer values keep their pass-through semantics. Normal session requests convert byte-for-byte as before, and the Codex OAuth path still strips max_output_tokens afterwards.
Fixes #7103
fix(codex): coalesce adjacent commentary with pending tool calls (#7280)
The Codex Responses -> Chat Completions converter emitted two consecutive
assistant messages when one Responses model turn contained a commentary
message directly followed by function_call items. Chat upstreams imitate
the text-only assistant message as a complete turn and return
finish_reason=stop before the expected tool call, so long agent tasks end
after a progress update.
Merge pending tool calls into the directly adjacent assistant message when
it carries no tool_calls yet, attach pending reasoning per segment so
parallel calls do not duplicate it, and keep the new-message path for every
other boundary shape (user/tool boundaries, prior tool-call batches, media
flushes). The reasoning placeholder backfill still applies to coalesced
calls without reasoning.
Fixes #6529
Supersedes #6530
Co-authored-by: BigStrongSun <BigStrongSun@users.noreply.github.com>
fix(proxy): skip empty reasoning_content placeholders in Anthropic SSE streaming (#7227)
Some OpenAI-compatible upstreams (GLM, Qwen, DeepSeek-V4-Pro via ModelScope/DashScope,
Bailian, JD Cloud, ...) keep a `reasoning_content: ""` placeholder in every content
chunk instead of omitting the field. The reasoning branch of the OpenAI Chat -> Anthropic
SSE converter only checked for presence, and it shares `current_non_tool_block_type`
with the content branch, so every content chunk closed the open text block, opened an
empty thinking block, then reopened a text block. N content chunks became 2N content
blocks, half of them empty thinking blocks, which Claude Code rendered as one line per
token with dozens of empty Thought blocks and bloated the session JSONL.
Filter empty reasoning before entering the branch, matching the `!content.is_empty()`
guard the content branch already had and the non-streaming `openai_to_anthropic`
converter. Two regression tests pin block index, type and order: the placeholder shape
must yield a single text block and no thinking_delta at all, and real reasoning must
still yield one thinking block followed by one text block.
Fixes #5028
Fixes #4404
fix(codex): detect growing rollouts with a persisted byte cursor (#7219)
Codex keeps the rollout append handle open for the whole session, so on Windows NTFS the file mtime does not advance while the file grows and the pure-mtime gate skipped growing rollouts forever. Persist the observed byte length in session_log_sync.last_byte_offset for Codex rows and skip a file only when both mtime and size are unchanged. The parser now splits records on newlines manually so an incomplete trailing record is retried on the next pass without advancing the line cursor.
Fixes #6060
fix(codex): proxy image edits and Images API follow-ups (#7177)
* fix(codex): proxy image edits and Images API follow-ups
Follow-ups to #7036 for Codex's built-in ImageGen over the local proxy:
- Register `/images/edits` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them through the shared Codex standalone passthrough.
ImageGen posts to `images/edits` whenever a call references existing
images, and the router returned an empty 404 for it.
- Add `CodexStandaloneEndpoint::ImagesEdits`. Both Images endpoints share
one full-URL suffix table, so a complete Responses / Compact / Chat
Completions / Images URL derives the sibling endpoint (and vice versa)
while opaque full URLs keep failing closed.
- Apply the standalone sibling rewrite when the base URL is pasted as a
complete endpoint with the full-URL switch off, mirroring
`codex_chat_base_is_full_endpoint`; standalone requests no longer become
`.../chat/completions/images/generations`.
- Parse Images API streaming usage: `image_generation.completed` carries
`usage` on the event top level, so scan for `usage.input_tokens` before
falling back to the Chat Completions stream parser.
Related #7036
* fix(codex): match standalone URL suffixes case-insensitively
Keep endpoint detection and sibling rewriting consistent while preserving the original URL prefix and query. Cover mixed-case Images and Alpha Search endpoints through URL and proxy regression cases.
---------
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): preserve per-app settings on shutdown and port allocation (#7210)
On normal exit, stop_with_restore_keep_state() called the legacy global
proxy-config writer to clear the long-retired live_takeover_active flag.
That writer reads the Claude row and runs an UPDATE without a WHERE clause,
so every shutdown copied Claude's max_retries and the three timeout fields
onto the Codex, Gemini and Grok Build rows — and the UPDATE never carried
live_takeover_active in the first place, so the step had no effect beyond
the clobber. The ephemeral listen-port path did the same on every proxy
start.
Drop the dead write-back (live-config restore, backup cleanup and health
reset are unchanged) and persist the resolved ephemeral port through the
global-config interface, which only touches the shared listen/logging
columns. Two regression tests seed distinct per-app settings for all four
apps and assert they survive both paths.
Fixes #7204
fix(provider): preserve child metadata during universal sync (#7212)
Syncing a universal provider regenerates its Claude/Codex/Gemini child
providers. The child's `settings_config` was merged with the existing row,
but `meta`, `created_at` and `sort_index` were taken from the generated
object, and the universal provider never carries any of them from the UI.
The UPDATE therefore wiped the child's usage script, common-config opt-out,
endpoint auto-select and other per-app settings to `{}` and reset the sort
index to NULL, which pushed the card to the bottom of the list and looked
like the provider had been deleted and re-created.
Keep the existing child's `meta`, `created_at` and `sort_index` when the
row already exists. Name, base URL, key, models, website and notes are
still driven by the universal provider, and first-time creation still
inherits the parent's metadata.
Fixes #7134
feat(presets): rebrand DashScope as 千问AI平台 and refresh Qwen models (#7183)
Rename the domestic Bailian presets to 千问AI平台 with a dedicated icon, refresh the Qwen family to the 3.8 generation across Claude Code, Claude Desktop, Codex, Hermes, OpenClaw, OpenCode and Pi, add the domestic Token Plan presets, restore the international QwenCloud Coding Plan presets, and align OpenClaw/OpenCode/Pi model metadata with the official client docs.
fix(codex): honor proxy URL when model_provider is omitted (#7263)
When a Codex provider card's TOML has no `model_provider`, Codex falls back to
its built-in `openai` provider, but takeover wrote the proxy address as a
top-level `base_url` that Codex never reads, so requests bypassed the local
proxy and went straight to api.openai.com.
Treat the missing selector as the built-in `openai` provider and write the
proxy address to `openai_base_url`; the existing legacy-reroute migration then
turns it into a `cc-switch` custom provider table carrying the `PROXY_MANAGED`
bearer, the same shape every other third-party takeover uses. Explicit
provider selections are unchanged.
Refs #6256, #7217
fix(provider): update MiniMax defaults to M3 and remove expired offers (#7255)
* fix(provider): refresh MiniMax M3 presets and remove expired promotions
* fix(provider): complete MiniMax M3 capability declarations
---------
Co-authored-by: saladday <1203511142@qq.com>
chore(provider): remove Atlas Cloud sponsor status, keep presets
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`
docs(release): add v3.20.2 release notes
chore(release): v3.20.2
feat(provider): add SoleAPI sponsor presets across all nine apps
SoleAPI (soleapi.com) joins as a sponsor, placed directly above Micu in
the sponsor order (preset files and the four READ…
新建/编辑供应商表单里,可从其他已启用应用的已有供应商一次性带入
名称、备注、官网链接、API Key、请求地址、默认(兜底)模型名与上游
API 格式,其余配置由用户自行填写。10 个应用的配置形状各不相同,
映射层按目标应用分别落键;编辑模式只覆盖这三个键位,配置对象其余
部分(providerKey、模型表、Codex modelCatalog、Gemini 顶层 config、
env/options/auth 其他键含 Codex 登录态键)原样保留。
- src/utils/providerImport.ts:读侧按应用取可迁移字段;写侧拆成
buildImportedSettingsConfig(新建,按目标应用「自定义」模板重建)
与 patchImportedSettingsConfig(编辑,就地覆盖)两条路径
- src/components/providers/forms/ProviderImportEntry.tsx:来源应用
按钮行 + 供应商选择弹窗,预告「本次将写入」的字段,不渲染 API Key
- useProviderImportSources / useProviderImportApply:来源枚举与表单
写入;ProviderPresetSelector 增加 extraActions 插槽
- src/utils/piProviderConfig.ts:buildPiSettingsConfig 自
PiProviderForm 抽出,供映射层复用
- i18n 四语言新增 providerImport 文案
release 工作流改为直接以正式版发布(prerelease: false),不再先挂
预发布等人工提升;注意这同时移除了那道人工放行关卡,tag 一推发布
即对 /releases/latest、下载页和应用内更新器可见。sync-r2 不受影响,
其 released 触发器对直接以 stable 发布同样触发。
chore(native): archive cross-app-provider-import
chore(native): archive sync-upstream-3203
chore: sync fork with upstream v3.20.3 and rebrand top-left link to GitHub
Rebase fork main onto upstream/main (v3.20.3, 06082e18, 33 new commits)
while keeping all fork modifications: connectivity test, model quick
switch, sidebar panel visibility, preset filtering, tray left-click
toggle, dual website links, disabled in-app updater, fork CI.
- Bump fork version to 3.20.3-1 (package.json, tauri.conf.json,
Cargo.toml, Cargo.lock).
- Merged upstream's v18->v19 schema migration with the fork's
website_url_2 column migration in the same migration arm.
- Added mcode entry to forkOfficialAllowlist (no official presets,
same treatment as hermes/openclaw/opencode/pi).
- Changed the top-left CC Switch brand link target from
https://ccswitch.io to https://github.com/farion1231/cc-switch.
- Updated HOW_TO_REBASE_UPSTREAM.md whitelist with the brand link
override.
style: apply prettier to files flagged by CI format check
修复 CI Frontend Checks 的 Check formatting 步骤失败
(App.tsx、ConnectivityTestDialog、ProviderCard、ProviderList、
provider schema 共 5 个文件)。
style: apply cargo fmt to connectivity-test era backend files
修复 CI 的 Check Rust formatting 步骤失败(connectivity_test、
backup、tests、provider 等文件格式不符合 rustfmt 规范)。
chore(native): archive sync-upstream
docs(fork): update rebase whitelist for connectivity-test era fork features
- 白名单补充连通性测试前后端、模型快捷切换、双官网链接测试、
CI 裁剪版、README/CHANGELOG、.comet/superpowers 产物等 fork 专属文件
- 新增共享文件 fork 专属语义表(托盘左键切换、禁用更新器、hidden 字段等)
- 修正 §3.1 rebase 方向说明:rebase 中 fork 侧是 --theirs 而非 --ours
(v3.20.2 同步实测确认,与 merge 语义相反)
chore: bump fork version to 3.20.2-1 after upstream v3.20.2 rebase
重放 fork 提交到 upstream/main(f3b18df1,v3.20.2)后统一版本号。
Cargo.lock 同步移除 updater 依赖树(fork 已禁用应用内更新器)。
chore(native): archive tray-left-click-toggle-window
chore(comet): 记录 tray-left-click-toggle-window 验收通过与归档状态
feat(tray): 托盘图标左键单击切换主窗口显示/隐藏
- tray.rs 新增 show_main_window/hide_main_window/toggle_main_window 助手:
显示语义与"打开主界面"一致,隐藏与"关闭到托盘"平台处理一致
(Windows set_skip_taskbar、macOS apply_tray_policy)
- lib.rs 托盘 show_menu_on_left_click 改为 false,左键 Click(Left, Up)
触发切换(过滤 Down/Up 双事件),右键仍弹菜单;Enter/Click 用量刷新保持
- show_main 菜单事件与 macOS Reopen 复用 show_main_window
docs(readme): 更新日志补充连通性测试、双官网链接与托盘左键切换
feat(model-quick-switch): 获取模型列表成功后自动展开选择器
SearchableModelPicker 增加可选 open/onOpenChange 受控展开(不传保持内部自管,
表单等既有用法不受影响);快捷切换弹窗拉取成功且列表非空时直接展开,关闭弹窗复位。
chore(native): archive fix-quick-switch-1m
chore(comet): 记录 fix-quick-switch-1m 验收通过与归档状态
fix(model-quick-switch): 补齐 Haiku 显示名同步,支持不选模型仅翻转 1M
- 显示名同步映射补上 ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME(沿用空/旧base/旧base[1M] 覆盖、自定义保留语义)
- 新增 setClaudeOneMInSettings:仅原地翻转 1M 标记,不改各角色模型 base 与显示名
- 弹窗打开时 1M 开关按供应商当前状态初始化,避免已开 1M 被静默剥离
- claude 未选模型且 1M 状态有变化时允许保存;无可应用变化时保存禁用
- 补单测与四语言 toast 文案
feat(provider-website-links): 供应商支持第二个官网链接,主页横排双链接
- Rust Provider/UniversalProvider 新增 website_url_2(serde websiteUrl2,skip none)
- SQLite providers 表新增 website_url_2 列(schema v18→v19 幂等迁移 + create_tables 补列 + 旧 config.json 迁移)
- DAO SELECT/INSERT/UPDATE/replace 全链路带出新列
- 表单共享 BasicFormFields + 统一供应商表单第二输入框;zod 同第一链接规则校验与回填
- ProviderCard 主页双链接横排、两空格分隔、逐链接 openExternal 打开默认浏览器
- 搜索匹配、复制供应商、统一供应商生成 claude/codex/gemini 随行透传
- gemini_auth packycode 认证检测纳入 website_url_2;ClaudeDesktop/GrokBuild 预设切换清空第二链接;卡片同值链接去重
- 4 语言 i18n + 前端/前端单测覆盖
Comet change dual-website-links-homepage(19/19 验收通过,归档)
feat(connectivity-test): 拉取模型列表改为「搜索添加」选择器交互
- 新增共享组件 SearchableModelMultiPicker:搜索过滤 + 多选 + 已在列表标记 + 已选计数
- ConnectivityTestDialog 获取模型列表后不再全量追加合并去重,改为会话内缓存完整列表并自动弹出一次选择器;用户搜索挑选后添加进表格并自动勾选
- 重新拉取只刷新缓存,已添加进表格的行保留;拉取结果仍仅会话内有效,不写回供应商配置
- 四语言 i18n 新增 searchAdd / addToTest / alreadyInList / selectedCount
- 同步更新 ConnectivityTestDialog 测试并新增 SearchableModelMultiPicker 测试
feat(connectivity-test): 探测目标按供应商 API 格式对齐真实转发链路
- build_claude 按 get_claude_api_format 分派端点与请求体:openai_chat → /v1/chat/completions(复用 transform_claude_request_for_api_format,流式注入 stream_options.include_usage)、openai_responses → /v1/responses、gemini_native → /v1beta/models/{model}:{method}(resolve_gemini_native_url)、anthropic 保持 /v1/messages 并补 anthropic-version/beta 头
- build_codex 按 should_convert_codex_responses_to_chat/anthropic 分派:chat → /chat/completions(responses_to_chat_completions_with_reasoning)、anthropic → /v1/messages(responses_request_to_anthropic,含 max_output_tokens 注入与 [1m] 剥离 + context-1m beta 头)
- 鉴权改用 ClaudeAdapter/CodexAdapter 的 extract_auth + get_auth_headers(与真实出站请求同源:AUTH_TOKEN→Bearer、ANTHROPIC_API_KEY→x-api-key、gemini→x-goog-api-key、GeminiCli OAuth→Bearer、OpenRouter→Bearer、Codex 按 apiKeyField)
- 转换前剥离 [1m] 标记;meta.isFullUrl 或 base 已是完整端点时直接使用 base_url;自定义 body 浅合并移至转换后的最终协议体
- 新增 18 个目标构造断言单测;Comet 归档产物同步提交
chore: archive connectivity-test-optimizations
feat(connectivity-test): 单一模型表格 + 实时拉取模型 + 勾选即测试范围 + 新默认提示词
- 弹窗合并为单一模型表格:打开即全部模型行(初始待测试)、行勾选 + 表头全选(含半选态)、5 格统计卡,移除上方独立选择区
- 获取模型列表:凭据提取抽为共享模块 src/utils/providerCredentials.ts(ModelQuickSwitchDialog 同步改引用),复用 fetch_models_for_config 拉取 /v1/models,按 ID 追加合并去重、仅会话内有效
- 默认提示词改为「你好,你可以帮我做什么事情」:前后端常量同步,merge 差异判断基准跟随
- 勾选即测试范围:后端 connectivity_test_provider_models 新增 modelIds 参数(None/空白回退静态清单),前端逐模型独立 invoke 并行 + 行级流式更新
- 一轮测完自动勾选失败项,全部成功保持原勾选
fix(connectivity-test): UI 四项修复
- 弹窗抬至 alert 层(z-[60]):打开连通性测试时 header 切换栏正确变灰,
详情子弹窗同步抬层(同 z + 更晚挂载 portal 在上)
- 批量检测入口移至 header 图标组(状态提升到 App,ProviderList 经
props 读取探针结果),不再另起一行
- 批量检测可停止:runId 代次守卫(Tauri invoke 无法真正取消),再次
点击即清除 waiting/running 徽标、丢弃在途迟到写入、不再拉起排队项
- 弹窗打开自动勾选模型:已保存默认测试模型 → 供应商当前模型
(claude env.ANTHROPIC_MODEL / codex config.toml model,新增
currentProviderModelId 与后端同口径)→ 清单首个
前端全量 141 文件 / 1106 测试全绿;typecheck clean
chore: archive per-model-connectivity-test
fix(connectivity-test): shouldShowTestEntry 增判 providerType + 收尾清理
chore(connectivity-test): 勾选 Task 9 / OpenSpec 5.1-5.2 + verify 报告
chore(connectivity-test): 勾选 Task 8 / OpenSpec 4.1-4.3
refactor(connectivity-test): 移除旧 stream_check 可达性探测全链路(保留表结构)
- 删除 6 个文件:commands/stream_check.rs、services/stream_check.rs、
database/dao/stream_check.rs、hooks/useStreamCheck.ts、lib/api/connectivity-check.ts、
components/usage/ConnectivityCheckConfigPanel.tsx
- 移除 mod.rs 声明、lib.rs generate_handler! 注册、cleanup_old_stream_check_logs 调用
- 从 SYNC_SKIP_TABLES / SYNC_PRESERVE_TABLES 移除 stream_check_logs(表结构保留在 schema.rs)
- 更新 backup.rs 测试:移除 stream_check_logs fixture 与断言
- 更新 SettingsPage:移除 ConnectivityCheckConfigPanel AccordionItem 与 FlaskConical 导入
- i18n:删除 streamCheck.* 命名空间(zh/en/ja/zh-TW 四语言)
- 更新注释:移除 stream_check 旧引用(forwarder.rs、provider.rs、userAgentPresets.ts、
connectivity_test.rs)
- 修复 4 个 pre-existing clippy lint(connectivity_test: matches! 宏、unused_unit、
derive Default)
- 移除 ProviderList.test.tsx 中已失效的 useStreamCheck mock
chore(connectivity-test): 勾选 Task 7 / OpenSpec 2.4 3.1-3.3
feat(connectivity-test): 供应商列表集成(弹窗入口 + 批量探针徽标)
chore(connectivity-test): 勾选 Task 6 / OpenSpec 2.2-2.3
fix(connectivity-test): 模型列表解析与后端对齐(每条目首个匹配,提取共享 helper)
feat(connectivity-test): 连通性测试弹窗/详情/徽标组件与四语言文案
chore(connectivity-test): 勾选 Task 5 / OpenSpec 2.1
test(connectivity-test): 锁定 toBackendParams 字段映射(customHeaders/customBody 等)
feat(connectivity-test): 前端命令绑定与测试/探针状态 hooks
chore(connectivity-test): 勾选 Task 4 / OpenSpec 1.4
feat(connectivity-test): 测试参数持久化纯函数(缺省即默认值)
chore(connectivity-test): 勾选 Task 3 / OpenSpec 1.3
feat(connectivity-test): Tauri 命令与注册,官方/动态端点过滤,sanitize 隔离私有字段
chore(connectivity-test): 勾选 Task 2 / OpenSpec 1.2
feat(connectivity-test): 后端直连执行器(reqwest+SSE,总/空闲超时,度量与错误识别)
feat(connectivity-test): 后端直连执行器(reqwest+SSE,总/空闲超时,度量与错误识别)
chore(connectivity-test): 勾选 Task 1 / OpenSpec 1.1
fix(connectivity-test): claude 鉴权对齐 adapter 顶层 apiKey/api_key 兜底
feat(connectivity-test): 新增后端连通性测试服务类型/模型解析/协议构造
chore: archive model-quick-switch-picker-scroll
chore: model-quick-switch-picker-scroll 验证报告与状态元数据
fix: 模型快捷切换弹窗内模型列表滚轮无法滚动
模态 Dialog 的 RemoveScroll 会对 shards 外的 portaled PopoverContent
wheel 事件无条件 preventDefault。在 SearchableModelPicker 的
PopoverContent 捕获阶段阻断 wheel 传播,恢复原生滚动。
chore: bump fork version to 3.20.1-1 after upstream v3.20.1 rebase
fix(form): only auto-expand Claude advanced options on divergent model mapping
Uniform mapping produced by the quick-set button (all roles equal to the
fallback model) no longer counts as an advanced value, so the advanced
section stays collapsed on edit. A 1M marker is stripped before comparing
so marked roles are not misjudged as divergent. Truly customized mappings
still auto-expand the section.
feat: align fallback-model quick-set buttons with model input right edge
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
docs: update README with fork build notes and quick-move provider changes
chore: archive comet-native claude-model-quick-access
feat: surface Claude fallback model as quick-access section below endpoint
Move the default fallback model (ANTHROPIC_MODEL) block out of the
collapsed Advanced Options into a quick-access section directly below
the request endpoint: input + 1M checkbox + quick-set + fetch-models
buttons are all reachable without expanding.
- header toggle-all "declare 1M" now includes the fallback model
(Haiku still excluded); partial selection shows indeterminate state
- drop the duplicated quick-set/fetch buttons and the old fallback
block inside Advanced Options
- exclude claudeModel from hasAnyAdvancedValue so a fallback-only
value no longer auto-expands Advanced Options
- quick-set priority, env write semantics, and OAuth preset branches
unchanged; add 4 component tests covering the new layout
chore: archive comet-native provider-right-click-top-bottom
Enable native workflow in .comet/config.yaml and move the Comet managed
state block in .gitignore so the exclusion rules stay grouped with the
other fork tool artifacts.
feat: add provider right-click move-to-top/bottom and insert new providers at second position
- ProviderList: context menu with quick move-to-top/move-to-bottom, reusing
update_providers_sort_order and refreshing tray menu like drag sort
- useAddProviderMutation: default insert at index 1 with explicit sortIndex
materialization for existing items (avoids defined-first sort trap)
- i18n: quickMove* strings for zh/en/zh-TW/ja
ci: stop flaky WSL2 linker job from blocking main
The backend-windows-wsl2 job intermittently fails with LNK1327 /
mt.exe c1010070 when the Windows runner's mt.exe cannot parse a
manifest temp file on the WSL2 UNC TEMP path. The same flaky failure
occurs upstream and is unrelated to fork changes. Mark the job
continue-on-error so main stops going red on it; the result stays
visible in the Actions UI.
style: apply prettier to files touched by recent comet archives
Frontend CI format:check failed on 6 files modified by the app-name
and model-quick-switch archive commits plus the updater-disable commit.
Run prettier --write so format:check passes again.
chore: archive ccs-fork-app-name-cc-switch
chore: archive ccs-model-quick-switch-ui-polish
docs: rewrite README to document fork differences vs upstream
README 从上游完整功能介绍改为仅记录 fork 与上游的差别,完整功能
文档指回上游。涵盖 8 项 fork 魔改:构建标识/开发预览、预设精简、
Claude 表单调整、模型搜索移植、主页模型快速切换、侧边栏面板显隐、
关闭应用内更新、打包范围,并说明版本号与 rebase 同步流程。
feat: disable in-app updater, point users to GitHub releases
移除 tauri-plugin-updater 依赖与自动更新链路,改为统一引导用户到
GitHub Releases 手动下载新版。
- 前端:updater.ts 恒返回 up-to-date;UpdateContext 取消启动自检;
AboutSection「检查更新」直接打开 fork releases 页;DatabaseUpgrade
恢复界面去掉下载进度条,改为打开发布页。
- 后端:移除 updater 插件注册与 UpdaterExt 调用;
install_update_and_restart 降级为空壳,check_app_update_available
返回当前版本号;check_for_updates 指向 fork releases;capabilities
去掉 updater:default 权限。
- 打包:tauri.conf.json 删 plugins.updater、createUpdaterArtifacts=false;
Cargo.toml / package.json 删 updater 依赖,Cargo.lock / pnpm-lock
同步重生成。
- CI:release.yml 移除签名收集与 assemble-latest-json job,删除
.tar.gz/.sig 处理,release body 去掉 updater 说明。
版本号统一为 3.20.0-1(合法 semver 且 MSI 第 4 位为 1)。
Cargo.lock / pnpm-lock.yaml 已随依赖移除重生成,cargo check 通过。
ci: build only Windows x64 and macOS arm64, unsigned
feat: gate dev panel behind dev-only env flag
chore(comet): archive ccs-sidebar-panels-visibility change
fix: apply skills visibility guard to hermes branch toolbar button
feat: include prompts in sidebar panel visibility settings
fix: persist visibleSidebarPanels setting in backend AppSettings
style: use compact pill toggles for sidebar panel visibility
feat: add sidebar panel visibility switches to settings
feat: add sidebar panel visibility settings and wire into toolbar buttons
chore(comet): archive ccs-home-model-display change
fix(fork): read grokbuild api key from config TOML in quick switch dialog
feat(fork): align model badge with upstream extractModelBadge
feat(fork): show current model badge and quick-switch entry on provider cards
feat(fork): add model quick switch dialog
feat(fork): add provider model read/write utils with tests
chore(comet): archive ccs-model-search-port change
feat(i18n): add model search picker keys for all locales
feat(form): port SearchableModelPicker from upstream for searchable model selection
chore(comet): archive ccs-claude-form-layout change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-claude-form-layout/
- Sync claude-form-layout spec to main specs
- Annotate design doc as archived
- Add verification report
fix(i18n): update advanced options hint, add ja/zh-TW toggle-all, drop dead apiFormatHint key
fix(form): correct 1M toggle-all alignment direction (ml-1)
fix(form): align 1M toggle-all checkbox with row checkboxes
refactor(form): move api format select into EndpointField rightSlot
fix(form): widen api format select to fit option text
feat(form): add 1M toggle-all checkbox in model mapping header
feat(form): move api format select next to endpoint input
chore(comet): archive ccs-provider-presets-trim change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-provider-presets-trim/
- Sync provider-presets-visibility spec to main specs
- Annotate design doc as archived
- Add verification report
docs(fork): add presets-trim files to rebase whitelist
test(fork): mock IS_FORK_BUILD false in unit tests for upstream-form semantics
feat(fork): wire official preset filter into provider forms
feat(fork): add hidden field to preset interfaces
feat(fork): add official preset allowlist and filter util
chore(comet): archive ccs-fork-scaffolding change
- Move change artifacts to docs/openspec/changes/archive/2026-08-18-ccs-fork-scaffolding/
- Sync fork-build-identity spec to main specs
- Annotate design doc as archived
- Add verification report
feat(fork): add dev config with isolated identifier and config dir
docs(fork): complete rebase whitelist with all fork-touched files
fix(fork): guard setTitle with isTauri in test/non-tauri env
fix(fork): define __CCS_FORK_BUILD__ in vitest config
feat(fork): add DevPanel and settings footer entry
feat(fork): set macOS window title to CC Switch (Fork) on startup
feat(fork): inject __CCS_FORK_BUILD__ compile-time constant
feat(fork): bump version to 3.19.2-fork.1 and rename product to CC Switch (Fork)
docs(fork): add upstream rebase workflow and ignore tool artifacts
fix(proxy): accept the whole grok-4.x (x>=5) family in the reasoning-effort whitelist (#7369)
Replace the verbatim grok-4.5 / grok-4.6 entries in supports_reasoning_effort with a rule that parses the grok-4.x minor version and accepts x >= 5, mirroring the existing GPT-5+ rule. This covers grok-4.7 (released 2026-09-21), whose reasoning effort was previously dropped on the Claude -> Chat, Claude -> Responses and Codex Responses -> Chat conversion paths, and lets future releases pass without another whitelist edit. The grok-build-* family is retained for saved providers.
Co-authored-by: allenxu09 <171831965+allenxu09@users.noreply.github.com>
docs(release): add v3.20.4 release notes
chore(release): v3.20.4
feat(pricing): add Grok 4.7 and GLM-5.3-FlashX
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.
fix(i18n): remove outdated OpenCode Go referral offer
fix: add SudoCode.chat alternate API endpoint
feat(settings): add a GitHub star prompt to the About card
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
feat(presets): add Soshow aggregator presets
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.
fix(ci): stabilize frontend tests and verify renderer build
fix(mcode): refresh provider state after removal (#7578)
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
fix(lib): reset skip_taskbar before window.show() across entry points (#6348)
5处 window.show() 调用中 4处没有在 show() 前重置 Windows 的 skip_taskbar,导致以下场景出现窗口可见但不在任务栏、点最小化后窗口消失的 bug:
- single_instance handler (lib.rs:360-361):当原进程因 silent_startup / 关闭到托盘等处于 skip_taskbar(true) 状态时,从快捷方式/开始菜单唤起已运行实例触发。
- handle_deeplink_url 的 focus_main_window 分支 (lib.rs:256):通过 ccswitch:// URL 唤起时。
- DB 恢复流程 (lib.rs:556):数据库版本不匹配时显示恢复界面。
- RunEvent::Opened 处理器 (lib.rs:1803):系统接收到 deep-link URL 时。
修复后的行为与 tray.rs::handle_tray_menu_event::show_main 分支一致(那里已有相同的 set_skip_taskbar(false) 调用)。
经用户测试验证:autostart + silent_startup + 从开始菜单/快捷方式启动的场景下,最小化按钮恢复正常工作。
fix(proxy): omit missing tool descriptions in the Codex responses-to-chat converter (#7378)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): preserve max effort for GPT-5.6 and GPT-6 Astra (#7531)
fix(misc): ignore shell startup output when probing tool versions in WSL (#7348)
try_get_version_wsl runs "${SHELL:-sh}" -lic '<tool> --version' through
wsl.exe and takes the first x.y.z anywhere in stdout. A login+interactive
shell runs the distro's startup files first, so their output shares that
stdout. On Ubuntu, /etc/profile.d/update-motd.sh prints the MOTD once a day,
starting with "Welcome to Ubuntu 24.04.4 LTS", and the card then showed
24.04.4 as the Claude Code version. The once-a-day stamp makes it appear
after the first probe of the day and vanish on refresh.
The payload now prints a sentinel before running the tool, and parsing looks
only at what follows its last occurrence. The last occurrence handles the
-lic || -lc || -c fallback chain printing it several times; matching it as a
substring handles OSC sequences glued in front of it. If the sentinel is
absent (the shell failed before the payload ran), the output is used as
before. The stderr fallback for tools that print their version there is kept.
Fixes #7347
Co-authored-by: lyfuci <lyfuci11@gmail.com>
fix(claude): hide session URL attribution (#7053)
* fix(claude): hide session URL attribution
* docs(claude): document session URL attribution
fix(ui): reset provider scroll when switching apps (#5211)
fix(pi): fix provider logo (#6826)
fix(i18n): use the Pi form's own term for the zh-TW duplicate-key message (#7187)
Follow-up to the nit on #6768. In zh-TW the Pi form labels the field
供應商識別碼 and says 供應商識別碼為必填 when it is empty, but the
duplicate message I added said 供應商標識 — the term Hermes, OpenCode and
OpenClaw use for their own field. One form, two names for the same thing.
The other three locales already keep the label and both messages on one
term, so this makes zh-TW match the pattern rather than introduce one:
en Provider key / Provider key is required / This provider key already exists
ja プロバイダーキー / プロバイダーキーを入力してください / このプロバイダーキーは既に存在します
zh 供应商标识 / 供应商标识不能为空 / 该供应商标识已存在
zh-TW 供應商識別碼 / 供應商識別碼為必填 / 該供應商標識已存在 <- the odd one out
hermes.form.providerKeyDuplicate carries the same sentence and is left
alone: its own label is 供應商標識, so it is already consistent.
Verified: managementListLocales 7/7. Full unit suite 1085/1087, the same
two App.test.tsx integration cases failing before and after.
fix(proxy): strip stop param for GitHub Copilot to unblock auto mode classifier (#5404)
Claude Code's auto mode classifier sends `stop_sequences` on every
safety check. On the GitHub Copilot path the Anthropic -> OpenAI Chat
conversion mapped it to `stop`, and Copilot's Chat Completions route
for Claude models answers that request with HTTP 400, so every
classifier call failed while normal chat kept working.
- Remove the converted `stop` field only when the provider is GitHub
Copilot; other OpenAI-compatible upstreams keep it.
- Copilot detection in the transform now also covers providers that
are recognised only through `env.ANTHROPIC_BASE_URL`.
- Regression tests for the Copilot (meta and env URL) and generic
provider cases.
Fixes #5175
fix(codex): keep stored auth when the live snapshot has no credential (#7434)
Editing the current Codex provider seeds the form from the live ~/.codex
snapshot, and switching away backfills that snapshot into the DB row. When a
provider table declares its own credential source (an Authorization header,
env_key, auth/aws), no bearer token is injected into config.toml and the
shared auth.json is deleted on third-party switches, so the live snapshot is
permanently `{ auth: {}, config }` while auth.OPENAI_API_KEY in the DB is the
only copy of the key. Both paths treated that empty live auth as
authoritative and silently erased the stored key, which in turn made the
usage-script modal misread the provider as an official subscription and
reset the saved script.
- Treat a credential-less live auth (missing auth.json or the bare
auth_mode logout marker) as an absent field in both the edit dialog and
the switch-away backfill, keeping the stored auth template. Live still
wins whenever it carries material, and official providers keep the live
login as their source of truth.
- Gate the official exemption on is_codex_official_provider /
resolveCodexOfficialIdentity rather than the category alone: a legacy
fixed official card without a persisted category is still written as
official, so the fallback must not resurrect a logged-out login on
switch-back.
- Cover both layers with regression tests, including the category-less
official shapes.
Fixes #7433
Supersedes #7492
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): lift additional_tools input carriers on the Codex chat conversion (#7454)
Codex talks to third-party providers over its Responses Lite transport,
which ships the request's extra tools in an `additional_tools` input
carrier: `{"type":"additional_tools","role":"developer","tools":[...]}`,
a role but no `content`. The Responses->Chat conversion let that carrier
fall through the generic "item with a role is a message" arm and emitted
`{"role":"system","content":null}` at messages[1], which strict Chat
gateways reject with `messages[1]: missing field "content"` (or
`data did not match any variant of ...SystemMessageContent`). The tools
carried inside it were also dropped on the Chat and Anthropic conversions.
- Skip `additional_tools` items in the Chat message walk; a carrier
declares tools, it is not a message.
- Collect carrier tools through the existing CodexToolContext registry
(dedup, namespace flattening, custom-tool conversion), which also feeds
the Anthropic conversion and the response-side name round-trip.
- Requests without a carrier convert byte-for-byte as before.
Known boundary: `custom` children inside a namespace tool
(`functions.exec`) are still not lifted; that needs its own response-side
name round-trip and is left for a follow-up.
Diagnosed independently in #6158 by BigStrongSun (fix in #6159) and in
fixture.
Fixes #7451
Fixes #6158
Refs #6033
Refs #6776
Supersedes #6159
fix(proxy): normalize Codex detail:original images for Chat gateways (#7476)
Codex marks tool-returned images with detail:"original" whenever the model
catalog advertises supports_image_detail_original, a Responses-only value.
The Responses->Chat conversion copied it verbatim into image_url.detail, and
strict OpenAI-compatible Chat gateways (Ark/Doubao, SenseNova, GLM, vLLM, ...)
reject the whole request with 400. Because Codex replays the full rollout on
every turn, one poisoned view_image result made the session fail permanently,
including plain-text turns.
Two layers:
- tool_media::merge_top_level_detail downgrades original -> auto, so already
poisoned rollouts and Codex versions before 0.155.0 (which do not normalize
replayed history themselves) recover immediately. auto/low/high are untouched.
- codex_catalog_model_entry always writes supports_image_detail_original:false
on the ProxyChat profile, so Codex stops emitting original at the source once
the catalog is regenerated. NativeResponses/Anthropic profiles and the DeepSeek
official catalog are unaffected.
Top-level user images were never affected: responses_content_to_chat_content
drops the part-level detail entirely.
Fixes #6495
Fixes #7458
Refs #6569
Refs #5805
fix(omo): detect unified config on the WSL side when the OpenCode dir is in WSL (#7550)
When the OpenCode config directory points into WSL
(\\wsl.localhost\<distro>\home\<user>\.config\opencode), OMO >= 4.19.3 keeps
its unified config in the WSL-side home (~/.omo/omo.jsonc). Detection only
probed the Windows home, never found it, and silently fell back to the legacy
per-plugin file that OMO no longer reads, so provider switches looked
successful but had no effect.
- Add derive_wsl_home_dir to derive the WSL-side home from a WSL UNC path
(home/<user> or root); non-WSL and non-home paths return None.
- Probe a list of home candidates for the unified config, WSL-side home
first and the OS home as a fallback, shared by the read, write and delete
paths.
- Detection only: when no unified config exists on either side the legacy
fallback is unchanged.
Fixes #7363
Supersedes #7367
docs(partner): add a FluxA purchase link and tighten its sponsor blurb
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.
fix(claude-desktop): expose the 1M context variant for DeepSeek V4 routes
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.
feat(presets): add FluxA Token Plan partner presets
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.
fix(presets): correct AICodeWith Codex-family endpoint to /v1
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.
fix(ui): render monochrome preset icons in the foreground color
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.
docs(user-manual): point Chat-only Codex examples at plan endpoints
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.
fix(pricing): restore DeepSeek V4 Pro price and seed Qwen3.8 and Hy4 models
- 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).
fix(presets): move MiniMax CN and BaiLing to their current official endpoints
- 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.
feat(codex): refresh CN presets from the Responses API audit
- 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.
test(claude-desktop): run Linux path tests on unix hosts and cover Linux in provider sync tests
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.
fix(ui): stop expanding stale cached usage tiers on ineligible provider cards
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.
fix(skills): 支持 skillId 与目录名不一致的技能安装和更新 (#6381)
* fix(skills): resolve mismatched skillId directories
* fix(skills): preserve source path during updates
* fix(skills): refresh source URL after repository relocation
* test(skills): isolate relocation fixture from legacy home
feat(opencode): 支持从获取的模型列表中搜索并批量添加所选模型 (#7515)
* feat: implement fetched models selection and UI components
* fix(opencode): prevent model search from submitting provider form
Add Kimi Global preset variants (kimi.ai platform + api endpoints) (#7526)
Add a "Kimi Global" twin immediately after the domestic "Kimi" API preset and a "Kimi For Coding Global" twin after the domestic "Kimi For Coding" preset, in every preset file that ships them (claude, claude-desktop, codex, hermes, openclaw, opencode, pi).
"Kimi Global" points to the international open platform https://platform.kimi.ai?aff=cc-switch (apiKeyUrl mirrored as https://platform.kimi.ai/console/api-keys?aff=cc-switch) and swaps the endpoint host api.moonshot.cn for api.moonshot.ai with each file's path unchanged (/v1 or /anthropic).
"Kimi For Coding Global" points to the international site https://www.kimi.ai/code/?aff=cc-switch and swaps the endpoint host api.kimi.com for api.kimi.ai with the path unchanged (e.g. https://api.kimi.ai/coding/v1).
Domestic presets are unchanged; category, icons, apiFormat, model catalogs, endpointCandidates (host swapped), partnerPromotionKey and settings otherwise mirror the domestic blocks exactly.
primePartner stays only on the domestic presets so the new variants do not alter prime-partner ordering.
The Pi twins get distinct providerKey values ("cc-switch-kimi-global", "cc-switch-kimi-for-coding-global") because providerKey is the preset-level primary key and must not collide with the domestic presets.
Rebalance sponsor CTAs around Kimi Code plan with dual-region links (#7522)
Kimi Code plan purchases have resumed, so the README sponsor sections go back to leading with the coding plan instead of the API-only flow written during the purchase pause.
All four locales (EN/ZH/JA/DE) now present Kimi Code plan (中文站 | Global) as the primary CTA and the Kimi Open Platform API (中文站 | Global, with the first-top-up bonus) as the secondary one, mirroring the domestic|global split already used for API links.
The aff and track_id values are unchanged.
fix(prompts): refresh active prompts after external file edits (#7194)
Reload the enabled prompt from its live file when the Prompts list loads, and refresh the open panel when the window regains focus, so external edits to CLAUDE.md / AGENTS.md show up without a restart.
- Keep saved templates available when the live file cannot be read.
- Skip the backfill for empty or whitespace-only files, matching enable_prompt.
- Skip the backfill while a sync/restore holds the sync lock, so a list load cannot write a stale live file into a freshly restored database.
fix(skills): raise archive entry limit so large skill repos install (#7489)
Installing hugohe3/ppt-master from skills.sh failed with
ARCHIVE_TOO_MANY_ENTRIES: the GitHub archive we download is the whole
repository (13,248 entries), and the 10,000-entry cap added in #5811
treated it as a zip bomb.
- Raise MAX_ARCHIVE_ENTRIES to 30,000 for both the remote archive and
local ZIP paths, and align WebDAV's MAX_EXTRACT_ENTRIES so a skills.zip
containing such a skill still restores.
- Charge every file entry at least one 4 KiB block against the existing
512 MiB budget, mirroring directory accounting, so empty-file archives
are bounded by the byte budget rather than by the entry count alone.
- Add tests for a 13k-entry repo archive and for the per-file minimum
charge.
Fixes: https://github.com/farion1231/cc-switch/issues/7475
feat: add MiniMax Code harness support (#7383)
Add MiniMax Code (mcode) as a managed application: provider and model management through the native custom_provider configuration, MCP servers, Skills, global instructions (AGENTS.md), read-only local session history with resume, and usage import from the native runtime database. Native files shared by the TUI and desktop are written under the native lock with rollback when the database commit fails, unrelated native configuration is preserved, and the data directory honors MINIMAX_DATA_DIR and MAVIS_DATA_DIR. Deleting sessions stays in the native application because they are coupled to runtime state.
fix(codex): recover stale account bindings during takeover (#7395)
After deleting a managed ChatGPT account and logging in again, a Codex provider can still reference the old local account ID, which made route switching, takeover, startup restoration and rebinding fail permanently.
Distinguish a missing outgoing account from an existing account with no matching live token. A missing account only releases its ownership marker after the persisted account store is verified; the target write policy stays in force. A stale target now reports an account-selection instruction instead of a bare not-found error, including when takeover is already enabled.
Fixes #7392
feat(claude-desktop): support 3P configuration on Linux (#7331)
Add Claude Desktop 3P configuration support on Linux. Resolve the config root from an absolute XDG_CONFIG_HOME, falling back to ~/.config; when CC Switch itself runs as a Flatpak, use the host ~/.config instead of the sandbox-private XDG_CONFIG_HOME. Update the en/zh/ja user manuals with Linux paths and the Flatpak boundary.
Fixes #4855
fix(proxy): omit missing tool descriptions instead of serializing null (#7319)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): keep reasoning effort for grok-4.6 and pass xhigh through verbatim (#7318)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(models): support Zhipu OpenAI Responses model lists (#7330)
Zhipu's Responses endpoint (open.bigmodel.cn/api/v1) serves its model list in the Codex remote catalog shape (models[].slug) instead of the OpenAI-compatible data[].id shape, so the Codex form's model fetch came back empty. The parser now falls back to models[].slug when data is absent.
fix(misc): fetch npm dist-tags from the dedicated endpoint with the probe timeout (#7346)
The tool version probe requested the full npm packument for each package (metadata for every historical version, 14-25 MB uncompressed for codex / opencode / openclaw) only to read its dist-tags, and it was the only latest-version probe without LATEST_PROBE_TIMEOUT, so it fell back to the proxy client's 600 s total timeout and the cards sat on "loading" for minutes on slow links while the large downloads starved the Hermes GitHub probe into "unknown".
Switch fetch_npm_dist_tags to registry.npmjs.org/-/package/{package}/dist-tags, whose body is the dist-tags object itself (tens of bytes to a few KB), escape scoped names as %2f, and apply the same 15 s probe timeout as the GitHub and PyPI probes. No data is lost: the only consumer reads latest and the tool's prerelease tags.
Fixes #7339
feat(usage): display output tokens per second in request log table and detail panel (#3369)
Show tokens/s next to the output token count in the request log table and the request detail panel.
The generation window prefers an explicit duration when present, otherwise latency minus first-token time for streaming requests, and falls back to full latency for non-streaming requests. Rows without a positive token count or window show nothing.
Closes #5936
fix(presets): update APIKEY.FUN URLs to apikey.fan
docs(release): add v3.20.3 release notes
chore(release): v3.20.3
fix(pricing): move retired DeepSeek V4 family to the V4.1 Flash tier
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.
fix(presets): declare third-party Token Plan DeepSeek V4 rows text-only
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.
feat(presets): rename DouBaoSeed preset to localized Volcengine Doubao
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.
fix(codex): skip unchanged incomplete rollout tails
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.
fix(claude): align quick model selection with form order
fix(tray): show bound ChatGPT account quota for managed Codex cards
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
feat(claude): add "Disable Artifact Tool" quick toggle to provider editor
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.
feat(codex): refresh aggregator model presets and catalogs
feat(codex): switch Kimi presets to native Responses direct-connect
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.
fix(usage): show Claude Fable weekly limit from the usage API limits array
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.
fix(codex): mirror vision-capable DeepSeek catalog and un-gate the legacy v4-flash alias (#7286)
Refresh the bundled DeepSeek official Codex catalog to the vendor's current models.json (slug deepseek-flash, image input modality, supports_image_detail_original) and remove deepseek-v4-flash from the confirmed text-only registry: the legacy id is still accepted by DeepSeek and routed to the vision-capable V4.1 Flash, so both the proxy media sanitizer and the generated Codex catalog must fail open for it. deepseek-v4-pro stays text-only per the vendor catalog.
Fixes #7283
fix(proxy): clamp sub-floor max_tokens to the Responses API minimum (#7287)
The Responses API rejects max_output_tokens below 16, but Anthropic clients legitimately send tiny probe budgets (Claude Desktop's model-availability probe uses max_tokens=1). The Anthropic→Responses conversion copied the value verbatim, so strict upstreams returned 400 and Claude Desktop reported the mapped model as unavailable under local routing.
Clamp 1..=15 up to 16 during conversion; 16+, 0 and non-integer values keep their pass-through semantics. Normal session requests convert byte-for-byte as before, and the Codex OAuth path still strips max_output_tokens afterwards.
Fixes #7103
fix(codex): coalesce adjacent commentary with pending tool calls (#7280)
The Codex Responses -> Chat Completions converter emitted two consecutive
assistant messages when one Responses model turn contained a commentary
message directly followed by function_call items. Chat upstreams imitate
the text-only assistant message as a complete turn and return
finish_reason=stop before the expected tool call, so long agent tasks end
after a progress update.
Merge pending tool calls into the directly adjacent assistant message when
it carries no tool_calls yet, attach pending reasoning per segment so
parallel calls do not duplicate it, and keep the new-message path for every
other boundary shape (user/tool boundaries, prior tool-call batches, media
flushes). The reasoning placeholder backfill still applies to coalesced
calls without reasoning.
Fixes #6529
Supersedes #6530
Co-authored-by: BigStrongSun <BigStrongSun@users.noreply.github.com>
fix(proxy): skip empty reasoning_content placeholders in Anthropic SSE streaming (#7227)
Some OpenAI-compatible upstreams (GLM, Qwen, DeepSeek-V4-Pro via ModelScope/DashScope,
Bailian, JD Cloud, ...) keep a `reasoning_content: ""` placeholder in every content
chunk instead of omitting the field. The reasoning branch of the OpenAI Chat -> Anthropic
SSE converter only checked for presence, and it shares `current_non_tool_block_type`
with the content branch, so every content chunk closed the open text block, opened an
empty thinking block, then reopened a text block. N content chunks became 2N content
blocks, half of them empty thinking blocks, which Claude Code rendered as one line per
token with dozens of empty Thought blocks and bloated the session JSONL.
Filter empty reasoning before entering the branch, matching the `!content.is_empty()`
guard the content branch already had and the non-streaming `openai_to_anthropic`
converter. Two regression tests pin block index, type and order: the placeholder shape
must yield a single text block and no thinking_delta at all, and real reasoning must
still yield one thinking block followed by one text block.
Fixes #5028
Fixes #4404
fix(codex): detect growing rollouts with a persisted byte cursor (#7219)
Codex keeps the rollout append handle open for the whole session, so on Windows NTFS the file mtime does not advance while the file grows and the pure-mtime gate skipped growing rollouts forever. Persist the observed byte length in session_log_sync.last_byte_offset for Codex rows and skip a file only when both mtime and size are unchanged. The parser now splits records on newlines manually so an incomplete trailing record is retried on the next pass without advancing the line cursor.
Fixes #6060
fix(codex): proxy image edits and Images API follow-ups (#7177)
* fix(codex): proxy image edits and Images API follow-ups
Follow-ups to #7036 for Codex's built-in ImageGen over the local proxy:
- Register `/images/edits` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them through the shared Codex standalone passthrough.
ImageGen posts to `images/edits` whenever a call references existing
images, and the router returned an empty 404 for it.
- Add `CodexStandaloneEndpoint::ImagesEdits`. Both Images endpoints share
one full-URL suffix table, so a complete Responses / Compact / Chat
Completions / Images URL derives the sibling endpoint (and vice versa)
while opaque full URLs keep failing closed.
- Apply the standalone sibling rewrite when the base URL is pasted as a
complete endpoint with the full-URL switch off, mirroring
`codex_chat_base_is_full_endpoint`; standalone requests no longer become
`.../chat/completions/images/generations`.
- Parse Images API streaming usage: `image_generation.completed` carries
`usage` on the event top level, so scan for `usage.input_tokens` before
falling back to the Chat Completions stream parser.
Related #7036
* fix(codex): match standalone URL suffixes case-insensitively
Keep endpoint detection and sibling rewriting consistent while preserving the original URL prefix and query. Cover mixed-case Images and Alpha Search endpoints through URL and proxy regression cases.
---------
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): preserve per-app settings on shutdown and port allocation (#7210)
On normal exit, stop_with_restore_keep_state() called the legacy global
proxy-config writer to clear the long-retired live_takeover_active flag.
That writer reads the Claude row and runs an UPDATE without a WHERE clause,
so every shutdown copied Claude's max_retries and the three timeout fields
onto the Codex, Gemini and Grok Build rows — and the UPDATE never carried
live_takeover_active in the first place, so the step had no effect beyond
the clobber. The ephemeral listen-port path did the same on every proxy
start.
Drop the dead write-back (live-config restore, backup cleanup and health
reset are unchanged) and persist the resolved ephemeral port through the
global-config interface, which only touches the shared listen/logging
columns. Two regression tests seed distinct per-app settings for all four
apps and assert they survive both paths.
Fixes #7204
fix(provider): preserve child metadata during universal sync (#7212)
Syncing a universal provider regenerates its Claude/Codex/Gemini child
providers. The child's `settings_config` was merged with the existing row,
but `meta`, `created_at` and `sort_index` were taken from the generated
object, and the universal provider never carries any of them from the UI.
The UPDATE therefore wiped the child's usage script, common-config opt-out,
endpoint auto-select and other per-app settings to `{}` and reset the sort
index to NULL, which pushed the card to the bottom of the list and looked
like the provider had been deleted and re-created.
Keep the existing child's `meta`, `created_at` and `sort_index` when the
row already exists. Name, base URL, key, models, website and notes are
still driven by the universal provider, and first-time creation still
inherits the parent's metadata.
Fixes #7134
feat(presets): rebrand DashScope as 千问AI平台 and refresh Qwen models (#7183)
Rename the domestic Bailian presets to 千问AI平台 with a dedicated icon, refresh the Qwen family to the 3.8 generation across Claude Code, Claude Desktop, Codex, Hermes, OpenClaw, OpenCode and Pi, add the domestic Token Plan presets, restore the international QwenCloud Coding Plan presets, and align OpenClaw/OpenCode/Pi model metadata with the official client docs.
fix(codex): honor proxy URL when model_provider is omitted (#7263)
When a Codex provider card's TOML has no `model_provider`, Codex falls back to
it…
设置页可隐藏主页顶栏批量检测入口;关闭时停止进行中的探测。旧数据缺字段时默认显示。README 补上该开关,以及从其他应用导入供应商。
chore(native): archive sync-upstream-latest
chore(native): record sync-upstream-latest verification state
Commit the Runtime-managed comet-state.yaml and verification.md so the
working tree is clean after the v3.20.4 upstream sync.
chore: sync fork with upstream v3.20.4
Rebase fork main onto upstream/main (v3.20.4, 8e478b2b, 40 new commits)
while keeping all fork modifications: connectivity test, model quick
switch, sidebar panel visibility, preset filtering, tray left-click
toggle, dual website links, cross-app provider import, disabled in-app
updater, fork CI.
- Bump fork version to 3.20.4-1 (package.json, tauri.conf.json,
Cargo.toml, Cargo.lock).
- Keep vitest testTimeout at 30s so full provider-form jsdom tests stop
flaking past the 5s default.
- Resolved conflicts: version files kept on the fork side, dropped the
duplicate GitHub button in AboutSection (upstream now ships its own
star prompt), and restored .comet/config.yaml with native enabled.
- Updated HOW_TO_REBASE_UPSTREAM.md whitelist with the cross-app import
files, searchable model pickers, and the dev config.
feat(provider-import): 从其他已启用应用导入供应商配置
新建/编辑供应商表单里,可从其他已启用应用的已有供应商一次性带入
名称、备注、官网链接、API Key、请求地址、默认(兜底)模型名与上游
API 格式,其余配置由用户自行填写。10 个应用的配置形状各不相同,
映射层按目标应用分别落键;编辑模式只覆盖这三个键位,配置对象其余
部分(providerKey、模型表、Codex modelCatalog、Gemini 顶层 config、
env/options/auth 其他键含 Codex 登录态键)原样保留。
- src/utils/providerImport.ts:读侧按应用取可迁移字段;写侧拆成
buildImportedSettingsConfig(新建,按目标应用「自定义」模板重建)
与 patchImportedSettingsConfig(编辑,就地覆盖)两条路径
- src/components/providers/forms/ProviderImportEntry.tsx:来源应用
按钮行 + 供应商选择弹窗,预告「本次将写入」的字段,不渲染 API Key
- useProviderImportSources / useProviderImportApply:来源枚举与表单
写入;ProviderPresetSelector 增加 extraActions 插槽
- src/utils/piProviderConfig.ts:buildPiSettingsConfig 自
PiProviderForm 抽出,供映射层复用
- i18n 四语言新增 providerImport 文案
release 工作流改为直接以正式版发布(prerelease: false),不再先挂
预发布等人工提升;注意这同时移除了那道人工放行关卡,tag 一推发布
即对 /releases/latest、下载页和应用内更新器可见。sync-r2 不受影响,
其 released 触发器对直接以 stable 发布同样触发。
chore(native): archive cross-app-provider-import
chore(native): archive sync-upstream-3203
chore: sync fork with upstream v3.20.3 and rebrand top-left link to GitHub
Rebase fork main onto upstream/main (v3.20.3, 06082e18, 33 new commits)
while keeping all fork modifications: connectivity test, model quick
switch, sidebar panel visibility, preset filtering, tray left-click
toggle, dual website links, disabled in-app updater, fork CI.
- Bump fork version to 3.20.3-1 (package.json, tauri.conf.json,
Cargo.toml, Cargo.lock).
- Merged upstream's v18->v19 schema migration with the fork's
website_url_2 column migration in the same migration arm.
- Added mcode entry to forkOfficialAllowlist (no official presets,
same treatment as hermes/openclaw/opencode/pi).
- Changed the top-left CC Switch brand link target from
https://ccswitch.io to https://github.com/farion1231/cc-switch.
- Updated HOW_TO_REBASE_UPSTREAM.md whitelist with the brand link
override.
style: apply prettier to files flagged by CI format check
修复 CI Frontend Checks 的 Check formatting 步骤失败
(App.tsx、ConnectivityTestDialog、ProviderCard、ProviderList、
provider schema 共 5 个文件)。
style: apply cargo fmt to connectivity-test era backend files
修复 CI 的 Check Rust formatting 步骤失败(connectivity_test、
backup、tests、provider 等文件格式不符合 rustfmt 规范)。
chore(native): archive sync-upstream
docs(fork): update rebase whitelist for connectivity-test era fork features
- 白名单补充连通性测试前后端、模型快捷切换、双官网链接测试、
CI 裁剪版、README/CHANGELOG、.comet/superpowers 产物等 fork 专属文件
- 新增共享文件 fork 专属语义表(托盘左键切换、禁用更新器、hidden 字段等)
- 修正 §3.1 rebase 方向说明:rebase 中 fork 侧是 --theirs 而非 --ours
(v3.20.2 同步实测确认,与 merge 语义相反)
chore: bump fork version to 3.20.2-1 after upstream v3.20.2 rebase
重放 fork 提交到 upstream/main(f3b18df1,v3.20.2)后统一版本号。
Cargo.lock 同步移除 updater 依赖树(fork 已禁用应用内更新器)。
chore(native): archive tray-left-click-toggle-window
chore(comet): 记录 tray-left-click-toggle-window 验收通过与归档状态
feat(tray): 托盘图标左键单击切换主窗口显示/隐藏
- tray.rs 新增 show_main_window/hide_main_window/toggle_main_window 助手:
显示语义与"打开主界面"一致,隐藏与"关闭到托盘"平台处理一致
(Windows set_skip_taskbar、macOS apply_tray_policy)
- lib.rs 托盘 show_menu_on_left_click 改为 false,左键 Click(Left, Up)
触发切换(过滤 Down/Up 双事件),右键仍弹菜单;Enter/Click 用量刷新保持
- show_main 菜单事件与 macOS Reopen 复用 show_main_window
docs(readme): 更新日志补充连通性测试、双官网链接与托盘左键切换
feat(model-quick-switch): 获取模型列表成功后自动展开选择器
SearchableModelPicker 增加可选 open/onOpenChange 受控展开(不传保持内部自管,
表单等既有用法不受影响);快捷切换弹窗拉取成功且列表非空时直接展开,关闭弹窗复位。
chore(native): archive fix-quick-switch-1m
chore(comet): 记录 fix-quick-switch-1m 验收通过与归档状态
fix(model-quick-switch): 补齐 Haiku 显示名同步,支持不选模型仅翻转 1M
- 显示名同步映射补上 ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME(沿用空/旧base/旧base[1M] 覆盖、自定义保留语义)
- 新增 setClaudeOneMInSettings:仅原地翻转 1M 标记,不改各角色模型 base 与显示名
- 弹窗打开时 1M 开关按供应商当前状态初始化,避免已开 1M 被静默剥离
- claude 未选模型且 1M 状态有变化时允许保存;无可应用变化时保存禁用
- 补单测与四语言 toast 文案
feat(provider-website-links): 供应商支持第二个官网链接,主页横排双链接
- Rust Provider/UniversalProvider 新增 website_url_2(serde websiteUrl2,skip none)
- SQLite providers 表新增 website_url_2 列(schema v18→v19 幂等迁移 + create_tables 补列 + 旧 config.json 迁移)
- DAO SELECT/INSERT/UPDATE/replace 全链路带出新列
- 表单共享 BasicFormFields + 统一供应商表单第二输入框;zod 同第一链接规则校验与回填
- ProviderCard 主页双链接横排、两空格分隔、逐链接 openExternal 打开默认浏览器
- 搜索匹配、复制供应商、统一供应商生成 claude/codex/gemini 随行透传
- gemini_auth packycode 认证检测纳入 website_url_2;ClaudeDesktop/GrokBuild 预设切换清空第二链接;卡片同值链接去重
- 4 语言 i18n + 前端/前端单测覆盖
Comet change dual-website-links-homepage(19/19 验收通过,归档)
feat(connectivity-test): 拉取模型列表改为「搜索添加」选择器交互
- 新增共享组件 SearchableModelMultiPicker:搜索过滤 + 多选 + 已在列表标记 + 已选计数
- ConnectivityTestDialog 获取模型列表后不再全量追加合并去重,改为会话内缓存完整列表并自动弹出一次选择器;用户搜索挑选后添加进表格并自动勾选
- 重新拉取只刷新缓存,已添加进表格的行保留;拉取结果仍仅会话内有效,不写回供应商配置
- 四语言 i18n 新增 searchAdd / addToTest / alreadyInList / selectedCount
- 同步更新 ConnectivityTestDialog 测试并新增 SearchableModelMultiPicker 测试
feat(connectivity-test): 探测目标按供应商 API 格式对齐真实转发链路
- build_claude 按 get_claude_api_format 分派端点与请求体:openai_chat → /v1/chat/completions(复用 transform_claude_request_for_api_format,流式注入 stream_options.include_usage)、openai_responses → /v1/responses、gemini_native → /v1beta/models/{model}:{method}(resolve_gemini_native_url)、anthropic 保持 /v1/messages 并补 anthropic-version/beta 头
- build_codex 按 should_convert_codex_responses_to_chat/anthropic 分派:chat → /chat/completions(responses_to_chat_completions_with_reasoning)、anthropic → /v1/messages(responses_request_to_anthropic,含 max_output_tokens 注入与 [1m] 剥离 + context-1m beta 头)
- 鉴权改用 ClaudeAdapter/CodexAdapter 的 extract_auth + get_auth_headers(与真实出站请求同源:AUTH_TOKEN→Bearer、ANTHROPIC_API_KEY→x-api-key、gemini→x-goog-api-key、GeminiCli OAuth→Bearer、OpenRouter→Bearer、Codex 按 apiKeyField)
- 转换前剥离 [1m] 标记;meta.isFullUrl 或 base 已是完整端点时直接使用 base_url;自定义 body 浅合并移至转换后的最终协议体
- 新增 18 个目标构造断言单测;Comet 归档产物同步提交
chore: archive connectivity-test-optimizations
feat(connectivity-test): 单一模型表格 + 实时拉取模型 + 勾选即测试范围 + 新默认提示词
- 弹窗合并为单一模型表格:打开即全部模型行(初始待测试)、行勾选 + 表头全选(含半选态)、5 格统计卡,移除上方独立选择区
- 获取模型列表:凭据提取抽为共享模块 src/utils/providerCredentials.ts(ModelQuickSwitchDialog 同步改引用),复用 fetch_models_for_config 拉取 /v1/models,按 ID 追加合并去重、仅会话内有效
- 默认提示词改为「你好,你可以帮我做什么事情」:前后端常量同步,merge 差异判断基准跟随
- 勾选即测试范围:后端 connectivity_test_provider_models 新增 modelIds 参数(None/空白回退静态清单),前端逐模型独立 invoke 并行 + 行级流式更新
- 一轮测完自动勾选失败项,全部成功保持原勾选
fix(connectivity-test): UI 四项修复
- 弹窗抬至 alert 层(z-[60]):打开连通性测试时 header 切换栏正确变灰,
详情子弹窗同步抬层(同 z + 更晚挂载 portal 在上)
- 批量检测入口移至 header 图标组(状态提升到 App,ProviderList 经
props 读取探针结果),不再另起一行
- 批量检测可停止:runId 代次守卫(Tauri invoke 无法真正取消),再次
点击即清除 waiting/running 徽标、丢弃在途迟到写入、不再拉起排队项
- 弹窗打开自动勾选模型:已保存默认测试模型 → 供应商当前模型
(claude env.ANTHROPIC_MODEL / codex config.toml model,新增
currentProviderModelId 与后端同口径)→ 清单首个
前端全量 141 文件 / 1106 测试全绿;typecheck clean
chore: archive per-model-connectivity-test
fix(connectivity-test): shouldShowTestEntry 增判 providerType + 收尾清理
chore(connectivity-test): 勾选 Task 9 / OpenSpec 5.1-5.2 + verify 报告
chore(connectivity-test): 勾选 Task 8 / OpenSpec 4.1-4.3
refactor(connectivity-test): 移除旧 stream_check 可达性探测全链路(保留表结构)
- 删除 6 个文件:commands/stream_check.rs、services/stream_check.rs、
database/dao/stream_check.rs、hooks/useStreamCheck.ts、lib/api/connectivity-check.ts、
components/usage/ConnectivityCheckConfigPanel.tsx
- 移除 mod.rs 声明、lib.rs generate_handler! 注册、cleanup_old_stream_check_logs 调用
- 从 SYNC_SKIP_TABLES / SYNC_PRESERVE_TABLES 移除 stream_check_logs(表结构保留在 schema.rs)
- 更新 backup.rs 测试:移除 stream_check_logs fixture 与断言
- 更新 SettingsPage:移除 ConnectivityCheckConfigPanel AccordionItem 与 FlaskConical 导入
- i18n:删除 streamCheck.* 命名空间(zh/en/ja/zh-TW 四语言)
- 更新注释:移除 stream_check 旧引用(forwarder.rs、provider.rs、userAgentPresets.ts、
connectivity_test.rs)
- 修复 4 个 pre-existing clippy lint(connectivity_test: matches! 宏、unused_unit、
derive Default)
- 移除 ProviderList.test.tsx 中已失效的 useStreamCheck mock
chore(connectivity-test): 勾选 Task 7 / OpenSpec 2.4 3.1-3.3
feat(connectivity-test): 供应商列表集成(弹窗入口 + 批量探针徽标)
chore(connectivity-test): 勾选 Task 6 / OpenSpec 2.2-2.3
fix(connectivity-test): 模型列表解析与后端对齐(每条目首个匹配,提取共享 helper)
feat(connectivity-test): 连通性测试弹窗/详情/徽标组件与四语言文案
chore(connectivity-test): 勾选 Task 5 / OpenSpec 2.1
test(connectivity-test): 锁定 toBackendParams 字段映射(customHeaders/customBody 等)
feat(connectivity-test): 前端命令绑定与测试/探针状态 hooks
chore(connectivity-test): 勾选 Task 4 / OpenSpec 1.4
feat(connectivity-test): 测试参数持久化纯函数(缺省即默认值)
chore(connectivity-test): 勾选 Task 3 / OpenSpec 1.3
feat(connectivity-test): Tauri 命令与注册,官方/动态端点过滤,sanitize 隔离私有字段
chore(connectivity-test): 勾选 Task 2 / OpenSpec 1.2
feat(connectivity-test): 后端直连执行器(reqwest+SSE,总/空闲超时,度量与错误识别)
feat(connectivity-test): 后端直连执行器(reqwest+SSE,总/空闲超时,度量与错误识别)
chore(connectivity-test): 勾选 Task 1 / OpenSpec 1.1
fix(connectivity-test): claude 鉴权对齐 adapter 顶层 apiKey/api_key 兜底
feat(connectivity-test): 新增后端连通性测试服务类型/模型解析/协议构造
chore: archive model-quick-switch-picker-scroll
chore: model-quick-switch-picker-scroll 验证报告与状态元数据
fix: 模型快捷切换弹窗内模型列表滚轮无法滚动
模态 Dialog 的 RemoveScroll 会对 shards 外的 portaled PopoverContent
wheel 事件无条件 preventDefault。在 SearchableModelPicker 的
PopoverContent 捕获阶段阻断 wheel 传播,恢复原生滚动。
chore: bump fork version to 3.20.1-1 after upstream v3.20.1 rebase
fix(form): only auto-expand Claude advanced options on divergent model mapping
Uniform mapping produced by the quick-set button (all roles equal to the
fallback model) no longer counts as an advanced value, so the advanced
section stays collapsed on edit. A 1M marker is stripped before comparing
so marked roles are not misjudged as divergent. Truly customized mappings
still auto-expand the section.
feat: align fallback-model quick-set buttons with model input right edge
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
docs: update README with fork build notes and quick-move provider changes
chore: archive comet-native claude-model-quick-access
feat: surface Claude fallback model as quick-access section below endpoint
Move the default fallback model (ANTHROPIC_MODEL) block out of the
collapsed Advanced Options into a quick-access section directly below
the request endpoint: input + 1M checkbox + quick-set + fetch-models
buttons are all reachable without expanding.
- header toggle-all "declare 1M" now includes the fallback model
(Haiku still excluded); partial selection shows indeterminate state
- drop the duplicated quick-set/fetch buttons and the old fallback
block inside Advanced Options
- exclude claudeModel from hasAnyAdvancedValue so a fallback-only
value no longer auto-expands Advanced Options
- quick-set priority, env write semantics, and OAuth preset branches
unchanged; add 4 component tests covering the new layout
chore: archive comet-native provider-right-click-top-bottom
Enable native workflow in .comet/config.yaml and move the Comet managed
state block in .gitignore so the exclusion rules stay grouped with the
other fork tool artifacts.
feat: add provider right-click move-to-top/bottom and insert new providers at second position
- ProviderList: context menu with quick move-to-top/move-to-bottom, reusing
update_providers_sort_order and refreshing tray menu like drag sort
- useAddProviderMutation: default insert at index 1 with explicit sortIndex
materialization for existing items (avoids defined-first sort trap)
- i18n: quickMove* strings for zh/en/zh-TW/ja
ci: stop flaky WSL2 linker job from blocking main
The backend-windows-wsl2 job intermittently fails with LNK1327 /
mt.exe c1010070 when the Windows runner's mt.exe cannot parse a
manifest temp file on the WSL2 UNC TEMP path. The same flaky failure
occurs upstream and is unrelated to fork changes. Mark the job
continue-on-error so main stops going red on it; the result stays
visible in the Actions UI.
style: apply prettier to files touched by recent comet archives
Frontend CI format:check failed on 6 files modified by the app-name
and model-quick-switch archive commits plus the updater-disable commit.
Run prettier --write so format:check passes again.
chore: archive ccs-fork-app-name-cc-switch
chore: archive ccs-model-quick-switch-ui-polish
docs: rewrite README to document fork differences vs upstream
README 从上游完整功能介绍改为仅记录 fork 与上游的差别,完整功能
文档指回上游。涵盖 8 项 fork 魔改:构建标识/开发预览、预设精简、
Claude 表单调整、模型搜索移植、主页模型快速切换、侧边栏面板显隐、
关闭应用内更新、打包范围,并说明版本号与 rebase 同步流程。
feat: disable in-app updater, point users to GitHub releases
移除 tauri-plugin-updater 依赖与自动更新链路,改为统一引导用户到
GitHub Releases 手动下载新版。
- 前端:updater.ts 恒返回 up-to-date;UpdateContext 取消启动自检;
AboutSection「检查更新」直接打开 fork releases 页;DatabaseUpgrade
恢复界面去掉下载进度条,改为打开发布页。
- 后端:移除 updater 插件注册与 UpdaterExt 调用;
install_update_and_restart 降级为空壳,check_app_update_available
返回当前版本号;check_for_updates 指向 fork releases;capabilities
去掉 updater:default 权限。
- 打包:tauri.conf.json 删 plugins.updater、createUpdaterArtifacts=false;
Cargo.toml / package.json 删 updater 依赖,Cargo.lock / pnpm-lock
同步重生成。
- CI:release.yml 移除签名收集与 assemble-latest-json job,删除
.tar.gz/.sig 处理,release body 去掉 updater 说明。
版本号统一为 3.20.0-1(合法 semver 且 MSI 第 4 位为 1)。
Cargo.lock / pnpm-lock.yaml 已随依赖移除重生成,cargo check 通过。
ci: build only Windows x64 and macOS arm64, unsigned
feat: gate dev panel behind dev-only env flag
chore(comet): archive ccs-sidebar-panels-visibility change
fix: apply skills visibility guard to hermes branch toolbar button
feat: include prompts in sidebar panel visibility settings
fix: persist visibleSidebarPanels setting in backend AppSettings
style: use compact pill toggles for sidebar panel visibility
feat: add sidebar panel visibility switches to settings
feat: add sidebar panel visibility settings and wire into toolbar buttons
chore(comet): archive ccs-home-model-display change
fix(fork): read grokbuild api key from config TOML in quick switch dialog
feat(fork): align model badge with upstream extractModelBadge
feat(fork): show current model badge and quick-switch entry on provider cards
feat(fork): add model quick switch dialog
feat(fork): add provider model read/write utils with tests
chore(comet): archive ccs-model-search-port change
feat(i18n): add model search picker keys for all locales
feat(form): port SearchableModelPicker from upstream for searchable model selection
chore(comet): archive ccs-claude-form-layout change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-claude-form-layout/
- Sync claude-form-layout spec to main specs
- Annotate design doc as archived
- Add verification report
fix(i18n): update advanced options hint, add ja/zh-TW toggle-all, drop dead apiFormatHint key
fix(form): correct 1M toggle-all alignment direction (ml-1)
fix(form): align 1M toggle-all checkbox with row checkboxes
refactor(form): move api format select into EndpointField rightSlot
fix(form): widen api format select to fit option text
feat(form): add 1M toggle-all checkbox in model mapping header
feat(form): move api format select next to endpoint input
chore(comet): archive ccs-provider-presets-trim change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-provider-presets-trim/
- Sync provider-presets-visibility spec to main specs
- Annotate design doc as archived
- Add verification report
docs(fork): add presets-trim files to rebase whitelist
test(fork): mock IS_FORK_BUILD false in unit tests for upstream-form semantics
feat(fork): wire official preset filter into provider forms
feat(fork): add hidden field to preset interfaces
feat(fork): add official preset allowlist and filter util
chore(comet): archive ccs-fork-scaffolding change
- Move change artifacts to docs/openspec/changes/archive/2026-08-18-ccs-fork-scaffolding/
- Sync fork-build-identity spec to main specs
- Annotate design doc as archived
- Add verification report
feat(fork): add dev config with isolated identifier and config dir
docs(fork): complete rebase whitelist with all fork-touched files
fix(fork): guard setTitle with isTauri in test/non-tauri env
fix(fork): define __CCS_FORK_BUILD__ in vitest config
feat(fork): add DevPanel and settings footer entry
feat(fork): set macOS window title to CC Switch (Fork) on startup
feat(fork): inject __CCS_FORK_BUILD__ compile-time constant
feat(fork): bump version to 3.19.2-fork.1 and rename product to CC Switch (Fork)
docs(fork): add upstream rebase workflow and ignore tool artifacts
fix(proxy): accept the whole grok-4.x (x>=5) family in the reasoning-effort whitelist (#7369)
Replace the verbatim grok-4.5 / grok-4.6 entries in supports_reasoning_effort with a rule that parses the grok-4.x minor version and accepts x >= 5, mirroring the existing GPT-5+ rule. This covers grok-4.7 (released 2026-09-21), whose reasoning effort was previously dropped on the Claude -> Chat, Claude -> Responses and Codex Responses -> Chat conversion paths, and lets future releases pass without another whitelist edit. The grok-build-* family is retained for saved providers.
Co-authored-by: allenxu09 <171831965+allenxu09@users.noreply.github.com>
docs(release): add v3.20.4 release notes
chore(release): v3.20.4
feat(pricing): add Grok 4.7 and GLM-5.3-FlashX
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.
fix(i18n): remove outdated OpenCode Go referral offer
fix: add SudoCode.chat alternate API endpoint
feat(settings): add a GitHub star prompt to the About card
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
feat(presets): add Soshow aggregator presets
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.
fix(ci): stabilize frontend tests and verify renderer build
fix(mcode): refresh provider state after removal (#7578)
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
fix(lib): reset skip_taskbar before window.show() across entry points (#6348)
5处 window.show() 调用中 4处没有在 show() 前重置 Windows 的 skip_taskbar,导致以下场景出现窗口可见但不在任务栏、点最小化后窗口消失的 bug:
- single_instance handler (lib.rs:360-361):当原进程因 silent_startup / 关闭到托盘等处于 skip_taskbar(true) 状态时,从快捷方式/开始菜单唤起已运行实例触发。
- handle_deeplink_url 的 focus_main_window 分支 (lib.rs:256):通过 ccswitch:// URL 唤起时。
- DB 恢复流程 (lib.rs:556):数据库版本不匹配时显示恢复界面。
- RunEvent::Opened 处理器 (lib.rs:1803):系统接收到 deep-link URL 时。
修复后的行为与 tray.rs::handle_tray_menu_event::show_main 分支一致(那里已有相同的 set_skip_taskbar(false) 调用)。
经用户测试验证:autostart + silent_startup + 从开始菜单/快捷方式启动的场景下,最小化按钮恢复正常工作。
fix(proxy): omit missing tool descriptions in the Codex responses-to-chat converter (#7378)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): preserve max effort for GPT-5.6 and GPT-6 Astra (#7531)
fix(misc): ignore shell startup output when probing tool versions in WSL (#7348)
try_get_version_wsl runs "${SHELL:-sh}" -lic '<tool> --version' through
wsl.exe and takes the first x.y.z anywhere in stdout. A login+interactive
shell runs the distro's startup files first, so their output shares that
stdout. On Ubuntu, /etc/profile.d/update-motd.sh prints the MOTD once a day,
starting with "Welcome to Ubuntu 24.04.4 LTS", and the card then showed
24.04.4 as the Claude Code version. The once-a-day stamp makes it appear
after the first probe of the day and vanish on refresh.
The payload now prints a sentinel before running the tool, and parsing looks
only at what follows its last occurrence. The last occurrence handles the
-lic || -lc || -c fallback chain printing it several times; matching it as a
substring handles OSC sequences glued in front of it. If the sentinel is
absent (the shell failed before the payload ran), the output is used as
before. The stderr fallback for tools that print their version there is kept.
Fixes #7347
Co-authored-by: lyfuci <lyfuci11@gmail.com>
fix(claude): hide session URL attribution (#7053)
* fix(claude): hide session URL attribution
* docs(claude): document session URL attribution
fix(ui): reset provider scroll when switching apps (#5211)
fix(pi): fix provider logo (#6826)
fix(i18n): use the Pi form's own term for the zh-TW duplicate-key message (#7187)
Follow-up to the nit on #6768. In zh-TW the Pi form labels the field
供應商識別碼 and says 供應商識別碼為必填 when it is empty, but the
duplicate message I added said 供應商標識 — the term Hermes, OpenCode and
OpenClaw use for their own field. One form, two names for the same thing.
The other three locales already keep the label and both messages on one
term, so this makes zh-TW match the pattern rather than introduce one:
en Provider key / Provider key is required / This provider key already exists
ja プロバイダーキー / プロバイダーキーを入力してください / このプロバイダーキーは既に存在します
zh 供应商标识 / 供应商标识不能为空 / 该供应商标识已存在
zh-TW 供應商識別碼 / 供應商識別碼為必填 / 該供應商標識已存在 <- the odd one out
hermes.form.providerKeyDuplicate carries the same sentence and is left
alone: its own label is 供應商標識, so it is already consistent.
Verified: managementListLocales 7/7. Full unit suite 1085/1087, the same
two App.test.tsx integration cases failing before and after.
fix(proxy): strip stop param for GitHub Copilot to unblock auto mode classifier (#5404)
Claude Code's auto mode classifier sends `stop_sequences` on every
safety check. On the GitHub Copilot path the Anthropic -> OpenAI Chat
conversion mapped it to `stop`, and Copilot's Chat Completions route
for Claude models answers that request with HTTP 400, so every
classifier call failed while normal chat kept working.
- Remove the converted `stop` field only when the provider is GitHub
Copilot; other OpenAI-compatible upstreams keep it.
- Copilot detection in the transform now also covers providers that
are recognised only through `env.ANTHROPIC_BASE_URL`.
- Regression tests for the Copilot (meta and env URL) and generic
provider cases.
Fixes #5175
fix(codex): keep stored auth when the live snapshot has no credential (#7434)
Editing the current Codex provider seeds the form from the live ~/.codex
snapshot, and switching away backfills that snapshot into the DB row. When a
provider table declares its own credential source (an Authorization header,
env_key, auth/aws), no bearer token is injected into config.toml and the
shared auth.json is deleted on third-party switches, so the live snapshot is
permanently `{ auth: {}, config }` while auth.OPENAI_API_KEY in the DB is the
only copy of the key. Both paths treated that empty live auth as
authoritative and silently erased the stored key, which in turn made the
usage-script modal misread the provider as an official subscription and
reset the saved script.
- Treat a credential-less live auth (missing auth.json or the bare
auth_mode logout marker) as an absent field in both the edit dialog and
the switch-away backfill, keeping the stored auth template. Live still
wins whenever it carries material, and official providers keep the live
login as their source of truth.
- Gate the official exemption on is_codex_official_provider /
resolveCodexOfficialIdentity rather than the category alone: a legacy
fixed official card without a persisted category is still written as
official, so the fallback must not resurrect a logged-out login on
switch-back.
- Cover both layers with regression tests, including the category-less
official shapes.
Fixes #7433
Supersedes #7492
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): lift additional_tools input carriers on the Codex chat conversion (#7454)
Codex talks to third-party providers over its Responses Lite transport,
which ships the request's extra tools in an `additional_tools` input
carrier: `{"type":"additional_tools","role":"developer","tools":[...]}`,
a role but no `content`. The Responses->Chat conversion let that carrier
fall through the generic "item with a role is a message" arm and emitted
`{"role":"system","content":null}` at messages[1], which strict Chat
gateways reject with `messages[1]: missing field "content"` (or
`data did not match any variant of ...SystemMessageContent`). The tools
carried inside it were also dropped on the Chat and Anthropic conversions.
- Skip `additional_tools` items in the Chat message walk; a carrier
declares tools, it is not a message.
- Collect carrier tools through the existing CodexToolContext registry
(dedup, namespace flattening, custom-tool conversion), which also feeds
the Anthropic conversion and the response-side name round-trip.
- Requests without a carrier convert byte-for-byte as before.
Known boundary: `custom` children inside a namespace tool
(`functions.exec`) are still not lifted; that needs its own response-side
name round-trip and is left for a follow-up.
Diagnosed independently in #6158 by BigStrongSun (fix in #6159) and in
fixture.
Fixes #7451
Fixes #6158
Refs #6033
Refs #6776
Supersedes #6159
fix(proxy): normalize Codex detail:original images for Chat gateways (#7476)
Codex marks tool-returned images with detail:"original" whenever the model
catalog advertises supports_image_detail_original, a Responses-only value.
The Responses->Chat conversion copied it verbatim into image_url.detail, and
strict OpenAI-compatible Chat gateways (Ark/Doubao, SenseNova, GLM, vLLM, ...)
reject the whole request with 400. Because Codex replays the full rollout on
every turn, one poisoned view_image result made the session fail permanently,
including plain-text turns.
Two layers:
- tool_media::merge_top_level_detail downgrades original -> auto, so already
poisoned rollouts and Codex versions before 0.155.0 (which do not normalize
replayed history themselves) recover immediately. auto/low/high are untouched.
- codex_catalog_model_entry always writes supports_image_detail_original:false
on the ProxyChat profile, so Codex stops emitting original at the source once
the catalog is regenerated. NativeResponses/Anthropic profiles and the DeepSeek
official catalog are unaffected.
Top-level user images were never affected: responses_content_to_chat_content
drops the part-level detail entirely.
Fixes #6495
Fixes #7458
Refs #6569
Refs #5805
fix(omo): detect unified config on the WSL side when the OpenCode dir is in WSL (#7550)
When the OpenCode config directory points into WSL
(\\wsl.localhost\<distro>\home\<user>\.config\opencode), OMO >= 4.19.3 keeps
its unified config in the WSL-side home (~/.omo/omo.jsonc). Detection only
probed the Windows home, never found it, and silently fell back to the legacy
per-plugin file that OMO no longer reads, so provider switches looked
successful but had no effect.
- Add derive_wsl_home_dir to derive the WSL-side home from a WSL UNC path
(home/<user> or root); non-WSL and non-home paths return None.
- Probe a list of home candidates for the unified config, WSL-side home
first and the OS home as a fallback, shared by the read, write and delete
paths.
- Detection only: when no unified config exists on either side the legacy
fallback is unchanged.
Fixes #7363
Supersedes #7367
docs(partner): add a FluxA purchase link and tighten its sponsor blurb
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.
fix(claude-desktop): expose the 1M context variant for DeepSeek V4 routes
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.
feat(presets): add FluxA Token Plan partner presets
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.
fix(presets): correct AICodeWith Codex-family endpoint to /v1
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.
fix(ui): render monochrome preset icons in the foreground color
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.
docs(user-manual): point Chat-only Codex examples at plan endpoints
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.
fix(pricing): restore DeepSeek V4 Pro price and seed Qwen3.8 and Hy4 models
- 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).
fix(presets): move MiniMax CN and BaiLing to their current official endpoints
- 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.
feat(codex): refresh CN presets from the Responses API audit
- 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.
test(claude-desktop): run Linux path tests on unix hosts and cover Linux in provider sync tests
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.
fix(ui): stop expanding stale cached usage tiers on ineligible provider cards
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.
fix(skills): 支持 skillId 与目录名不一致的技能安装和更新 (#6381)
* fix(skills): resolve mismatched skillId directories
* fix(skills): preserve source path during updates
* fix(skills): refresh source URL after repository relocation
* test(skills): isolate relocation fixture from legacy home
feat(opencode): 支持从获取的模型列表中搜索并批量添加所选模型 (#7515)
* feat: implement fetched models selection and UI components
* fix(opencode): prevent model search from submitting provider form
Add Kimi Global preset variants (kimi.ai platform + api endpoints) (#7526)
Add a "Kimi Global" twin immediately after the domestic "Kimi" API preset and a "Kimi For Coding Global" twin after the domestic "Kimi For Coding" preset, in every preset file that ships them (claude, claude-desktop, codex, hermes, openclaw, opencode, pi).
"Kimi Global" points to the international open platform https://platform.kimi.ai?aff=cc-switch (apiKeyUrl mirrored as https://platform.kimi.ai/console/api-keys?aff=cc-switch) and swaps the endpoint host api.moonshot.cn for api.moonshot.ai with each file's path unchanged (/v1 or /anthropic).
"Kimi For Coding Global" points to the international site https://www.kimi.ai/code/?aff=cc-switch and swaps the endpoint host api.kimi.com for api.kimi.ai with the path unchanged (e.g. https://api.kimi.ai/coding/v1).
Domestic presets are unchanged; category, icons, apiFormat, model catalogs, endpointCandidates (host swapped), partnerPromotionKey and settings otherwise mirror the domestic blocks exactly.
primePartner stays only on the domestic presets so the new variants do not alter prime-partner ordering.
The Pi twins get distinct providerKey values ("cc-switch-kimi-global", "cc-switch-kimi-for-coding-global") because providerKey is the preset-level primary key and must not collide with the domestic presets.
Rebalance sponsor CTAs around Kimi Code plan with dual-region links (#7522)
Kimi Code plan purchases have resumed, so the README sponsor sections go back to leading with the coding plan instead of the API-only flow written during the purchase pause.
All four locales (EN/ZH/JA/DE) now present Kimi Code plan (中文站 | Global) as the primary CTA and the Kimi Open Platform API (中文站 | Global, with the first-top-up bonus) as the secondary one, mirroring the domestic|global split already used for API links.
The aff and track_id values are unchanged.
fix(prompts): refresh active prompts after external file edits (#7194)
Reload the enabled prompt from its live file when the Prompts list loads, and refresh the open panel when the window regains focus, so external edits to CLAUDE.md / AGENTS.md show up without a restart.
- Keep saved templates available when the live file cannot be read.
- Skip the backfill for empty or whitespace-only files, matching enable_prompt.
- Skip the backfill while a sync/restore holds the sync lock, so a list load cannot write a stale live file into a freshly restored database.
fix(skills): raise archive entry limit so large skill repos install (#7489)
Installing hugohe3/ppt-master from skills.sh failed with
ARCHIVE_TOO_MANY_ENTRIES: the GitHub archive we download is the whole
repository (13,248 entries), and the 10,000-entry cap added in #5811
treated it as a zip bomb.
- Raise MAX_ARCHIVE_ENTRIES to 30,000 for both the remote archive and
local ZIP paths, and align WebDAV's MAX_EXTRACT_ENTRIES so a skills.zip
containing such a skill still restores.
- Charge every file entry at least one 4 KiB block against the existing
512 MiB budget, mirroring directory accounting, so empty-file archives
are bounded by the byte budget rather than by the entry count alone.
- Add tests for a 13k-entry repo archive and for the per-file minimum
charge.
Fixes: https://github.com/farion1231/cc-switch/issues/7475
feat: add MiniMax Code harness support (#7383)
Add MiniMax Code (mcode) as a managed application: provider and model management through the native custom_provider configuration, MCP servers, Skills, global instructions (AGENTS.md), read-only local session history with resume, and usage import from the native runtime database. Native files shared by the TUI and desktop are written under the native lock with rollback when the database commit fails, unrelated native configuration is preserved, and the data directory honors MINIMAX_DATA_DIR and MAVIS_DATA_DIR. Deleting sessions stays in the native application because they are coupled to runtime state.
fix(codex): recover stale account bindings during takeover (#7395)
After deleting a managed ChatGPT account and logging in again, a Codex provider can still reference the old local account ID, which made route switching, takeover, startup restoration and rebinding fail permanently.
Distinguish a missing outgoing account from an existing account with no matching live token. A missing account only releases its ownership marker after the persisted account store is verified; the target write policy stays in force. A stale target now reports an account-selection instruction instead of a bare not-found error, including when takeover is already enabled.
Fixes #7392
feat(claude-desktop): support 3P configuration on Linux (#7331)
Add Claude Desktop 3P configuration support on Linux. Resolve the config root from an absolute XDG_CONFIG_HOME, falling back to ~/.config; when CC Switch itself runs as a Flatpak, use the host ~/.config instead of the sandbox-private XDG_CONFIG_HOME. Update the en/zh/ja user manuals with Linux paths and the Flatpak boundary.
Fixes #4855
fix(proxy): omit missing tool descriptions instead of serializing null (#7319)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): keep reasoning effort for grok-4.6 and pass xhigh through verbatim (#7318)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(models): support Zhipu OpenAI Responses model lists (#7330)
Zhipu's Responses endpoint (open.bigmodel.cn/api/v1) serves its model list in the Codex remote catalog shape (models[].slug) instead of the OpenAI-compatible data[].id shape, so the Codex form's model fetch came back empty. The parser now falls back to models[].slug when data is absent.
fix(misc): fetch npm dist-tags from the dedicated endpoint with the probe timeout (#7346)
The tool version probe requested the full npm packument for each package (metadata for every historical version, 14-25 MB uncompressed for codex / opencode / openclaw) only to read its dist-tags, and it was the only latest-version probe without LATEST_PROBE_TIMEOUT, so it fell back to the proxy client's 600 s total timeout and the cards sat on "loading" for minutes on slow links while the large downloads starved the Hermes GitHub probe into "unknown".
Switch fetch_npm_dist_tags to registry.npmjs.org/-/package/{package}/dist-tags, whose body is the dist-tags object itself (tens of bytes to a few KB), escape scoped names as %2f, and apply the same 15 s probe timeout as the GitHub and PyPI probes. No data is lost: the only consumer reads latest and the tool's prerelease tags.
Fixes #7339
feat(usage): display output tokens per second in request log table and detail panel (#3369)
Show tokens/s next to the output token count in the request log table and the request detail panel.
The generation window prefers an explicit duration when present, otherwise latency minus first-token time for streaming requests, and falls back to full latency for non-streaming requests. Rows without a positive token count or window show nothing.
Closes #5936
fix(presets): update APIKEY.FUN URLs to apikey.fan
docs(release): add v3.20.3 release notes
chore(release): v3.20.3
fix(pricing): move retired DeepSeek V4 family to the V4.1 Flash tier
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.
fix(presets): declare third-party Token Plan DeepSeek V4 rows text-only
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.
feat(presets): rename DouBaoSeed preset to localized Volcengine Doubao
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.
fix(codex): skip unchanged incomplete rollout tails
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.
fix(claude): align quick model selection with form order
fix(tray): show bound ChatGPT account quota for managed Codex cards
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
feat(claude): add "Disable Artifact Tool" quick toggle to provider editor
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.
feat(codex): refresh aggregator model presets and catalogs
feat(codex): switch Kimi presets to native Responses direct-connect
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.
fix(usage): show Claude Fable weekly limit from the usage API limits array
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.
fix(codex): mirror vision-capable DeepSeek catalog and un-gate the legacy v4-flash alias (#7286)
Refresh the bundled DeepSeek official Codex catalog to the vendor's current models.json (slug deepseek-flash, image input modality, supports_image_detail_original) and remove deepseek-v4-flash from the confirmed text-only registry: the legacy id is still accepted by DeepSeek and routed to the vision-capable V4.1 Flash, so both the proxy media sanitizer and the generated Codex catalog must fail open for it. deepseek-v4-pro stays text-only per the vendor catalog.
Fixes #7283
fix(proxy): clamp sub-floor max_tokens to the Responses API minimum (#7287)
The Responses API rejects max_output_tokens below 16, but Anthropic clients legitimately send tiny probe budgets (Claude Desktop's model-availability probe uses max_tokens=1). The Anthropic→Responses conversion copied the value verbatim, so strict upstreams returned 400 and Claude Desktop reported the mapped model as unavailable under local routing.
Clamp 1..=15 up to 16 during conversion; 16+, 0 and non-integer values keep their pass-through semantics. Normal session requests convert byte-for-byte as before, and the Codex OAuth path still strips max_output_tokens afterwards.
Fixes #7103
fix(codex): coalesce adjacent commentary with pending tool calls (#7280)
The Codex Responses -> Chat Completions converter emitted two consecutive
assistant messages when one Responses model turn contained a commentary
message directly followed by function_call items. Chat upstreams imitate
the text-only assistant message as a complete turn and return
finish_reason=stop before the expected tool call, so long agent tasks end
after a progress update.
Merge pending tool calls into the directly adjacent assistant message when
it carries no tool_calls yet, attach pending reasoning per segment so
parallel calls do not duplicate it, and keep the new-message path for every
other boundary shape (user/tool boundaries, prior tool-call batches, media
flushes). The reasoning placeholder backfill still applies to coalesced
calls without reasoning.
Fixes #6529
Supersedes #6530
Co-authored-by: BigStrongSun <BigStrongSun@users.noreply.github.com>
fix(proxy): skip empty reasoning_content placeholders in Anthropic SSE streaming (#7227)
Some OpenAI-compatible upstreams (GLM, Qwen, DeepSeek-V4-Pro via ModelScope/DashScope,
Bailian, JD Cloud, ...) keep a `reasoning_content: ""` placeholder in every content
chunk instead of omitting the field. The reasoning branch of the OpenAI Chat -> Anthropic
SSE converter only checked for presence, and it shares `current_non_tool_block_type`
with the content branch, so every content chunk closed the open text block, opened an
empty thinking block, then reopened a text block. N content chunks became 2N content
blocks, half of them empty thinking blocks, which Claude Code rendered as one line per
token with dozens of empty Thought blocks and bloated the session JSONL.
Filter empty reasoning before entering the branch, matching the `!content.is_empty()`
guard the content branch already had and the non-streaming `openai_to_anthropic`
converter. Two regression tests pin block index, type and order: the placeholder shape
must yield a single text block and no thinking_delta at all, and real reasoning must
still yield one thinking block followed by one text block.
Fixes #5028
Fixes #4404
fix(codex): detect growing rollouts with a persisted byte cursor (#7219)
Codex keeps the rollout append handle open for the whole session, so on Windows NTFS the file mtime does not advance while the file grows and the pure-mtime gate skipped growing rollouts forever. Persist the observed byte length in session_log_sync.last_byte_offset for Codex rows and skip a file only when both mtime and size are unchanged. The parser now splits records on newlines manually so an incomplete trailing record is retried on the next pass without advancing the line cursor.
Fixes #6060
fix(codex): proxy image edits and Images API follow-ups (#7177)
* fix(codex): proxy image edits and Images API follow-ups
Follow-ups to #7036 for Codex's built-in ImageGen over the local proxy:
- Register `/images/edits` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them through the shared Codex standalone passthrough.
ImageGen posts to `images/edits` whenever a call references existing
images, and the router returned an empty 404 for it.
- Add `CodexStandaloneEndpoint::ImagesEdits`. Both Images endpoints share
one full-URL suffix table, so a complete Responses / Compact / Chat
Completions / Images URL derives the sibling endpoint (and vice versa)
while opaque full URLs keep failing closed.
- Apply the standalone sibling rewrite when the base URL is pasted as a
complete endpoint with the full-URL switch off, mirroring
`codex_chat_base_is_full_endpoint`; standalone requests no longer become
`.../chat/completions/images/generations`.
- Parse Images API streaming usage: `image_generation.completed` carries
`usage` on the event top level, so scan for `usage.input_tokens` before
falling back to the Chat Completions stream parser.
Related #7036
* fix(codex): match standalone URL suffixes case-insensitively
Keep endpoint detection and sibling rewriting consistent while preserving the original URL prefix and query. Cover mixed-case Images and Alpha Search endpoints through URL and proxy regression cases.
---------
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): preserve per-app settings on shutdown and port allocation (#7210)
On normal exit, stop_with_restore_keep_state() called the legacy global
proxy-config writer to clear the long-retired live_takeover_active flag.
That writer reads the Claude row and runs an UPDATE without a WHERE clause,
so every shutdown copied Claude's max_retries and the three timeout fields
onto the Codex, Gemini and Grok Build rows — and the UPDATE never carried
live_takeover_active in the first place, so the step had no effect beyond
the clobber. The ephemeral listen-port path did the same on every proxy
start.
Drop the dead write-back (live-config restore, backup cleanup and health
reset are unchanged) and persist the resolved ephemeral port through the
global-config interface, which only touches the shared listen/logging
columns. Two regression tests seed distinct per-app settings for all four
apps and assert they survive both paths.
Fixes #7204
fix(provider): preserve child metadata during universal sync (#7212)
Syncing a universal provider regenerates its Claude/Codex/Gemini c…
feat: gate dev panel behind dev-only env flag
chore(comet): archive ccs-sidebar-panels-visibility change
fix: apply skills visibility guard to hermes branch toolbar button
feat: include prompts in sidebar panel visibility settings
fix: persist visibleSidebarPanels setting in backend AppSettings
style: use compact pill toggles for sidebar panel visibility
feat: add sidebar panel visibility switches to settings
feat: add sidebar panel visibility settings and wire into toolbar buttons
chore(comet): archive ccs-home-model-display change
fix(fork): read grokbuild api key from config TOML in quick switch dialog
feat(fork): align model badge with upstream extractModelBadge
feat(fork): show current model badge and quick-switch entry on provider cards
feat(fork): add model quick switch dialog
feat(fork): add provider model read/write utils with tests
chore(comet): archive ccs-model-search-port change
feat(i18n): add model search picker keys for all locales
feat(form): port SearchableModelPicker from upstream for searchable model selection
chore(comet): archive ccs-claude-form-layout change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-claude-form-layout/
- Sync claude-form-layout spec to main specs
- Annotate design doc as archived
- Add verification report
fix(i18n): update advanced options hint, add ja/zh-TW toggle-all, drop dead apiFormatHint key
fix(form): correct 1M toggle-all alignment direction (ml-1)
fix(form): align 1M toggle-all checkbox with row checkboxes
refactor(form): move api format select into EndpointField rightSlot
fix(form): widen api format select to fit option text
feat(form): add 1M toggle-all checkbox in model mapping header
feat(form): move api format select next to endpoint input
chore(comet): archive ccs-provider-presets-trim change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-provider-presets-trim/
- Sync provider-presets-visibility spec to main specs
- Annotate design doc as archived
- Add verification report
docs(fork): add presets-trim files to rebase whitelist
test(fork): mock IS_FORK_BUILD false in unit tests for upstream-form semantics
feat(fork): wire official preset filter into provider forms
feat(fork): add hidden field to preset interfaces
feat(fork): add official preset allowlist and filter util
chore(comet): archive ccs-fork-scaffolding change
- Move change artifacts to docs/openspec/changes/archive/2026-08-18-ccs-fork-scaffolding/
- Sync fork-build-identity spec to main specs
- Annotate design doc as archived
- Add verification report
feat(fork): add dev config with isolated identifier and config dir
docs(fork): complete rebase whitelist with all fork-touched files
fix(fork): guard setTitle with isTauri in test/non-tauri env
fix(fork): define __CCS_FORK_BUILD__ in vitest config
feat(fork): add DevPanel and settings footer entry
feat(fork): set macOS window title to CC Switch (Fork) on startup
feat(fork): inject __CCS_FORK_BUILD__ compile-time constant
feat(fork): bump version to 3.19.2-fork.1 and rename product to CC Switch (Fork)
docs(fork): add upstream rebase workflow and ignore tool artifacts
fix(proxy): accept the whole grok-4.x (x>=5) family in the reasoning-effort whitelist (#7369)
Replace the verbatim grok-4.5 / grok-4.6 entries in supports_reasoning_effort with a rule that parses the grok-4.x minor version and accepts x >= 5, mirroring the existing GPT-5+ rule. This covers grok-4.7 (released 2026-09-21), whose reasoning effort was previously dropped on the Claude -> Chat, Claude -> Responses and Codex Responses -> Chat conversion paths, and lets future releases pass without another whitelist edit. The grok-build-* family is retained for saved providers.
Co-authored-by: allenxu09 <171831965+allenxu09@users.noreply.github.com>
docs(release): add v3.20.4 release notes
chore(release): v3.20.4
feat(pricing): add Grok 4.7 and GLM-5.3-FlashX
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.
fix(i18n): remove outdated OpenCode Go referral offer
fix: add SudoCode.chat alternate API endpoint
feat(settings): add a GitHub star prompt to the About card
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
feat(presets): add Soshow aggregator presets
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.
fix(ci): stabilize frontend tests and verify renderer build
fix(mcode): refresh provider state after removal (#7578)
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
fix(lib): reset skip_taskbar before window.show() across entry points (#6348)
5处 window.show() 调用中 4处没有在 show() 前重置 Windows 的 skip_taskbar,导致以下场景出现窗口可见但不在任务栏、点最小化后窗口消失的 bug:
- single_instance handler (lib.rs:360-361):当原进程因 silent_startup / 关闭到托盘等处于 skip_taskbar(true) 状态时,从快捷方式/开始菜单唤起已运行实例触发。
- handle_deeplink_url 的 focus_main_window 分支 (lib.rs:256):通过 ccswitch:// URL 唤起时。
- DB 恢复流程 (lib.rs:556):数据库版本不匹配时显示恢复界面。
- RunEvent::Opened 处理器 (lib.rs:1803):系统接收到 deep-link URL 时。
修复后的行为与 tray.rs::handle_tray_menu_event::show_main 分支一致(那里已有相同的 set_skip_taskbar(false) 调用)。
经用户测试验证:autostart + silent_startup + 从开始菜单/快捷方式启动的场景下,最小化按钮恢复正常工作。
fix(proxy): omit missing tool descriptions in the Codex responses-to-chat converter (#7378)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): preserve max effort for GPT-5.6 and GPT-6 Astra (#7531)
fix(misc): ignore shell startup output when probing tool versions in WSL (#7348)
try_get_version_wsl runs "${SHELL:-sh}" -lic '<tool> --version' through
wsl.exe and takes the first x.y.z anywhere in stdout. A login+interactive
shell runs the distro's startup files first, so their output shares that
stdout. On Ubuntu, /etc/profile.d/update-motd.sh prints the MOTD once a day,
starting with "Welcome to Ubuntu 24.04.4 LTS", and the card then showed
24.04.4 as the Claude Code version. The once-a-day stamp makes it appear
after the first probe of the day and vanish on refresh.
The payload now prints a sentinel before running the tool, and parsing looks
only at what follows its last occurrence. The last occurrence handles the
-lic || -lc || -c fallback chain printing it several times; matching it as a
substring handles OSC sequences glued in front of it. If the sentinel is
absent (the shell failed before the payload ran), the output is used as
before. The stderr fallback for tools that print their version there is kept.
Fixes #7347
Co-authored-by: lyfuci <lyfuci11@gmail.com>
fix(claude): hide session URL attribution (#7053)
* fix(claude): hide session URL attribution
* docs(claude): document session URL attribution
fix(ui): reset provider scroll when switching apps (#5211)
fix(pi): fix provider logo (#6826)
fix(i18n): use the Pi form's own term for the zh-TW duplicate-key message (#7187)
Follow-up to the nit on #6768. In zh-TW the Pi form labels the field
供應商識別碼 and says 供應商識別碼為必填 when it is empty, but the
duplicate message I added said 供應商標識 — the term Hermes, OpenCode and
OpenClaw use for their own field. One form, two names for the same thing.
The other three locales already keep the label and both messages on one
term, so this makes zh-TW match the pattern rather than introduce one:
en Provider key / Provider key is required / This provider key already exists
ja プロバイダーキー / プロバイダーキーを入力してください / このプロバイダーキーは既に存在します
zh 供应商标识 / 供应商标识不能为空 / 该供应商标识已存在
zh-TW 供應商識別碼 / 供應商識別碼為必填 / 該供應商標識已存在 <- the odd one out
hermes.form.providerKeyDuplicate carries the same sentence and is left
alone: its own label is 供應商標識, so it is already consistent.
Verified: managementListLocales 7/7. Full unit suite 1085/1087, the same
two App.test.tsx integration cases failing before and after.
fix(proxy): strip stop param for GitHub Copilot to unblock auto mode classifier (#5404)
Claude Code's auto mode classifier sends `stop_sequences` on every
safety check. On the GitHub Copilot path the Anthropic -> OpenAI Chat
conversion mapped it to `stop`, and Copilot's Chat Completions route
for Claude models answers that request with HTTP 400, so every
classifier call failed while normal chat kept working.
- Remove the converted `stop` field only when the provider is GitHub
Copilot; other OpenAI-compatible upstreams keep it.
- Copilot detection in the transform now also covers providers that
are recognised only through `env.ANTHROPIC_BASE_URL`.
- Regression tests for the Copilot (meta and env URL) and generic
provider cases.
Fixes #5175
fix(codex): keep stored auth when the live snapshot has no credential (#7434)
Editing the current Codex provider seeds the form from the live ~/.codex
snapshot, and switching away backfills that snapshot into the DB row. When a
provider table declares its own credential source (an Authorization header,
env_key, auth/aws), no bearer token is injected into config.toml and the
shared auth.json is deleted on third-party switches, so the live snapshot is
permanently `{ auth: {}, config }` while auth.OPENAI_API_KEY in the DB is the
only copy of the key. Both paths treated that empty live auth as
authoritative and silently erased the stored key, which in turn made the
usage-script modal misread the provider as an official subscription and
reset the saved script.
- Treat a credential-less live auth (missing auth.json or the bare
auth_mode logout marker) as an absent field in both the edit dialog and
the switch-away backfill, keeping the stored auth template. Live still
wins whenever it carries material, and official providers keep the live
login as their source of truth.
- Gate the official exemption on is_codex_official_provider /
resolveCodexOfficialIdentity rather than the category alone: a legacy
fixed official card without a persisted category is still written as
official, so the fallback must not resurrect a logged-out login on
switch-back.
- Cover both layers with regression tests, including the category-less
official shapes.
Fixes #7433
Supersedes #7492
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): lift additional_tools input carriers on the Codex chat conversion (#7454)
Codex talks to third-party providers over its Responses Lite transport,
which ships the request's extra tools in an `additional_tools` input
carrier: `{"type":"additional_tools","role":"developer","tools":[...]}`,
a role but no `content`. The Responses->Chat conversion let that carrier
fall through the generic "item with a role is a message" arm and emitted
`{"role":"system","content":null}` at messages[1], which strict Chat
gateways reject with `messages[1]: missing field "content"` (or
`data did not match any variant of ...SystemMessageContent`). The tools
carried inside it were also dropped on the Chat and Anthropic conversions.
- Skip `additional_tools` items in the Chat message walk; a carrier
declares tools, it is not a message.
- Collect carrier tools through the existing CodexToolContext registry
(dedup, namespace flattening, custom-tool conversion), which also feeds
the Anthropic conversion and the response-side name round-trip.
- Requests without a carrier convert byte-for-byte as before.
Known boundary: `custom` children inside a namespace tool
(`functions.exec`) are still not lifted; that needs its own response-side
name round-trip and is left for a follow-up.
Diagnosed independently in #6158 by BigStrongSun (fix in #6159) and in
fixture.
Fixes #7451
Fixes #6158
Refs #6033
Refs #6776
Supersedes #6159
fix(proxy): normalize Codex detail:original images for Chat gateways (#7476)
Codex marks tool-returned images with detail:"original" whenever the model
catalog advertises supports_image_detail_original, a Responses-only value.
The Responses->Chat conversion copied it verbatim into image_url.detail, and
strict OpenAI-compatible Chat gateways (Ark/Doubao, SenseNova, GLM, vLLM, ...)
reject the whole request with 400. Because Codex replays the full rollout on
every turn, one poisoned view_image result made the session fail permanently,
including plain-text turns.
Two layers:
- tool_media::merge_top_level_detail downgrades original -> auto, so already
poisoned rollouts and Codex versions before 0.155.0 (which do not normalize
replayed history themselves) recover immediately. auto/low/high are untouched.
- codex_catalog_model_entry always writes supports_image_detail_original:false
on the ProxyChat profile, so Codex stops emitting original at the source once
the catalog is regenerated. NativeResponses/Anthropic profiles and the DeepSeek
official catalog are unaffected.
Top-level user images were never affected: responses_content_to_chat_content
drops the part-level detail entirely.
Fixes #6495
Fixes #7458
Refs #6569
Refs #5805
fix(omo): detect unified config on the WSL side when the OpenCode dir is in WSL (#7550)
When the OpenCode config directory points into WSL
(\\wsl.localhost\<distro>\home\<user>\.config\opencode), OMO >= 4.19.3 keeps
its unified config in the WSL-side home (~/.omo/omo.jsonc). Detection only
probed the Windows home, never found it, and silently fell back to the legacy
per-plugin file that OMO no longer reads, so provider switches looked
successful but had no effect.
- Add derive_wsl_home_dir to derive the WSL-side home from a WSL UNC path
(home/<user> or root); non-WSL and non-home paths return None.
- Probe a list of home candidates for the unified config, WSL-side home
first and the OS home as a fallback, shared by the read, write and delete
paths.
- Detection only: when no unified config exists on either side the legacy
fallback is unchanged.
Fixes #7363
Supersedes #7367
docs(partner): add a FluxA purchase link and tighten its sponsor blurb
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.
fix(claude-desktop): expose the 1M context variant for DeepSeek V4 routes
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.
feat(presets): add FluxA Token Plan partner presets
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.
fix(presets): correct AICodeWith Codex-family endpoint to /v1
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.
fix(ui): render monochrome preset icons in the foreground color
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.
docs(user-manual): point Chat-only Codex examples at plan endpoints
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.
fix(pricing): restore DeepSeek V4 Pro price and seed Qwen3.8 and Hy4 models
- 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).
fix(presets): move MiniMax CN and BaiLing to their current official endpoints
- 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.
feat(codex): refresh CN presets from the Responses API audit
- 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.
test(claude-desktop): run Linux path tests on unix hosts and cover Linux in provider sync tests
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.
fix(ui): stop expanding stale cached usage tiers on ineligible provider cards
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.
fix(skills): 支持 skillId 与目录名不一致的技能安装和更新 (#6381)
* fix(skills): resolve mismatched skillId directories
* fix(skills): preserve source path during updates
* fix(skills): refresh source URL after repository relocation
* test(skills): isolate relocation fixture from legacy home
feat(opencode): 支持从获取的模型列表中搜索并批量添加所选模型 (#7515)
* feat: implement fetched models selection and UI components
* fix(opencode): prevent model search from submitting provider form
Add Kimi Global preset variants (kimi.ai platform + api endpoints) (#7526)
Add a "Kimi Global" twin immediately after the domestic "Kimi" API preset and a "Kimi For Coding Global" twin after the domestic "Kimi For Coding" preset, in every preset file that ships them (claude, claude-desktop, codex, hermes, openclaw, opencode, pi).
"Kimi Global" points to the international open platform https://platform.kimi.ai?aff=cc-switch (apiKeyUrl mirrored as https://platform.kimi.ai/console/api-keys?aff=cc-switch) and swaps the endpoint host api.moonshot.cn for api.moonshot.ai with each file's path unchanged (/v1 or /anthropic).
"Kimi For Coding Global" points to the international site https://www.kimi.ai/code/?aff=cc-switch and swaps the endpoint host api.kimi.com for api.kimi.ai with the path unchanged (e.g. https://api.kimi.ai/coding/v1).
Domestic presets are unchanged; category, icons, apiFormat, model catalogs, endpointCandidates (host swapped), partnerPromotionKey and settings otherwise mirror the domestic blocks exactly.
primePartner stays only on the domestic presets so the new variants do not alter prime-partner ordering.
The Pi twins get distinct providerKey values ("cc-switch-kimi-global", "cc-switch-kimi-for-coding-global") because providerKey is the preset-level primary key and must not collide with the domestic presets.
Rebalance sponsor CTAs around Kimi Code plan with dual-region links (#7522)
Kimi Code plan purchases have resumed, so the README sponsor sections go back to leading with the coding plan instead of the API-only flow written during the purchase pause.
All four locales (EN/ZH/JA/DE) now present Kimi Code plan (中文站 | Global) as the primary CTA and the Kimi Open Platform API (中文站 | Global, with the first-top-up bonus) as the secondary one, mirroring the domestic|global split already used for API links.
The aff and track_id values are unchanged.
fix(prompts): refresh active prompts after external file edits (#7194)
Reload the enabled prompt from its live file when the Prompts list loads, and refresh the open panel when the window regains focus, so external edits to CLAUDE.md / AGENTS.md show up without a restart.
- Keep saved templates available when the live file cannot be read.
- Skip the backfill for empty or whitespace-only files, matching enable_prompt.
- Skip the backfill while a sync/restore holds the sync lock, so a list load cannot write a stale live file into a freshly restored database.
fix(skills): raise archive entry limit so large skill repos install (#7489)
Installing hugohe3/ppt-master from skills.sh failed with
ARCHIVE_TOO_MANY_ENTRIES: the GitHub archive we download is the whole
repository (13,248 entries), and the 10,000-entry cap added in #5811
treated it as a zip bomb.
- Raise MAX_ARCHIVE_ENTRIES to 30,000 for both the remote archive and
local ZIP paths, and align WebDAV's MAX_EXTRACT_ENTRIES so a skills.zip
containing such a skill still restores.
- Charge every file entry at least one 4 KiB block against the existing
512 MiB budget, mirroring directory accounting, so empty-file archives
are bounded by the byte budget rather than by the entry count alone.
- Add tests for a 13k-entry repo archive and for the per-file minimum
charge.
Fixes: https://github.com/farion1231/cc-switch/issues/7475
feat: add MiniMax Code harness support (#7383)
Add MiniMax Code (mcode) as a managed application: provider and model management through the native custom_provider configuration, MCP servers, Skills, global instructions (AGENTS.md), read-only local session history with resume, and usage import from the native runtime database. Native files shared by the TUI and desktop are written under the native lock with rollback when the database commit fails, unrelated native configuration is preserved, and the data directory honors MINIMAX_DATA_DIR and MAVIS_DATA_DIR. Deleting sessions stays in the native application because they are coupled to runtime state.
fix(codex): recover stale account bindings during takeover (#7395)
After deleting a managed ChatGPT account and logging in again, a Codex provider can still reference the old local account ID, which made route switching, takeover, startup restoration and rebinding fail permanently.
Distinguish a missing outgoing account from an existing account with no matching live token. A missing account only releases its ownership marker after the persisted account store is verified; the target write policy stays in force. A stale target now reports an account-selection instruction instead of a bare not-found error, including when takeover is already enabled.
Fixes #7392
feat(claude-desktop): support 3P configuration on Linux (#7331)
Add Claude Desktop 3P configuration support on Linux. Resolve the config root from an absolute XDG_CONFIG_HOME, falling back to ~/.config; when CC Switch itself runs as a Flatpak, use the host ~/.config instead of the sandbox-private XDG_CONFIG_HOME. Update the en/zh/ja user manuals with Linux paths and the Flatpak boundary.
Fixes #4855
fix(proxy): omit missing tool descriptions instead of serializing null (#7319)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): keep reasoning effort for grok-4.6 and pass xhigh through verbatim (#7318)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(models): support Zhipu OpenAI Responses model lists (#7330)
Zhipu's Responses endpoint (open.bigmodel.cn/api/v1) serves its model list in the Codex remote catalog shape (models[].slug) instead of the OpenAI-compatible data[].id shape, so the Codex form's model fetch came back empty. The parser now falls back to models[].slug when data is absent.
fix(misc): fetch npm dist-tags from the dedicated endpoint with the probe timeout (#7346)
The tool version probe requested the full npm packument for each package (metadata for every historical version, 14-25 MB uncompressed for codex / opencode / openclaw) only to read its dist-tags, and it was the only latest-version probe without LATEST_PROBE_TIMEOUT, so it fell back to the proxy client's 600 s total timeout and the cards sat on "loading" for minutes on slow links while the large downloads starved the Hermes GitHub probe into "unknown".
Switch fetch_npm_dist_tags to registry.npmjs.org/-/package/{package}/dist-tags, whose body is the dist-tags object itself (tens of bytes to a few KB), escape scoped names as %2f, and apply the same 15 s probe timeout as the GitHub and PyPI probes. No data is lost: the only consumer reads latest and the tool's prerelease tags.
Fixes #7339
feat(usage): display output tokens per second in request log table and detail panel (#3369)
Show tokens/s next to the output token count in the request log table and the request detail panel.
The generation window prefers an explicit duration when present, otherwise latency minus first-token time for streaming requests, and falls back to full latency for non-streaming requests. Rows without a positive token count or window show nothing.
Closes #5936
fix(presets): update APIKEY.FUN URLs to apikey.fan
docs(release): add v3.20.3 release notes
chore(release): v3.20.3
fix(pricing): move retired DeepSeek V4 family to the V4.1 Flash tier
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.
fix(presets): declare third-party Token Plan DeepSeek V4 rows text-only
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.
feat(presets): rename DouBaoSeed preset to localized Volcengine Doubao
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.
fix(codex): skip unchanged incomplete rollout tails
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.
fix(claude): align quick model selection with form order
fix(tray): show bound ChatGPT account quota for managed Codex cards
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
feat(claude): add "Disable Artifact Tool" quick toggle to provider editor
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.
feat(codex): refresh aggregator model presets and catalogs
feat(codex): switch Kimi presets to native Responses direct-connect
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.
fix(usage): show Claude Fable weekly limit from the usage API limits array
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.
fix(codex): mirror vision-capable DeepSeek catalog and un-gate the legacy v4-flash alias (#7286)
Refresh the bundled DeepSeek official Codex catalog to the vendor's current models.json (slug deepseek-flash, image input modality, supports_image_detail_original) and remove deepseek-v4-flash from the confirmed text-only registry: the legacy id is still accepted by DeepSeek and routed to the vision-capable V4.1 Flash, so both the proxy media sanitizer and the generated Codex catalog must fail open for it. deepseek-v4-pro stays text-only per the vendor catalog.
Fixes #7283
fix(proxy): clamp sub-floor max_tokens to the Responses API minimum (#7287)
The Responses API rejects max_output_tokens below 16, but Anthropic clients legitimately send tiny probe budgets (Claude Desktop's model-availability probe uses max_tokens=1). The Anthropic→Responses conversion copied the value verbatim, so strict upstreams returned 400 and Claude Desktop reported the mapped model as unavailable under local routing.
Clamp 1..=15 up to 16 during conversion; 16+, 0 and non-integer values keep their pass-through semantics. Normal session requests convert byte-for-byte as before, and the Codex OAuth path still strips max_output_tokens afterwards.
Fixes #7103
fix(codex): coalesce adjacent commentary with pending tool calls (#7280)
The Codex Responses -> Chat Completions converter emitted two consecutive
assistant messages when one Responses model turn contained a commentary
message directly followed by function_call items. Chat upstreams imitate
the text-only assistant message as a complete turn and return
finish_reason=stop before the expected tool call, so long agent tasks end
after a progress update.
Merge pending tool calls into the directly adjacent assistant message when
it carries no tool_calls yet, attach pending reasoning per segment so
parallel calls do not duplicate it, and keep the new-message path for every
other boundary shape (user/tool boundaries, prior tool-call batches, media
flushes). The reasoning placeholder backfill still applies to coalesced
calls without reasoning.
Fixes #6529
Supersedes #6530
Co-authored-by: BigStrongSun <BigStrongSun@users.noreply.github.com>
fix(proxy): skip empty reasoning_content placeholders in Anthropic SSE streaming (#7227)
Some OpenAI-compatible upstreams (GLM, Qwen, DeepSeek-V4-Pro via ModelScope/DashScope,
Bailian, JD Cloud, ...) keep a `reasoning_content: ""` placeholder in every content
chunk instead of omitting the field. The reasoning branch of the OpenAI Chat -> Anthropic
SSE converter only checked for presence, and it shares `current_non_tool_block_type`
with the content branch, so every content chunk closed the open text block, opened an
empty thinking block, then reopened a text block. N content chunks became 2N content
blocks, half of them empty thinking blocks, which Claude Code rendered as one line per
token with dozens of empty Thought blocks and bloated the session JSONL.
Filter empty reasoning before entering the branch, matching the `!content.is_empty()`
guard the content branch already had and the non-streaming `openai_to_anthropic`
converter. Two regression tests pin block index, type and order: the placeholder shape
must yield a single text block and no thinking_delta at all, and real reasoning must
still yield one thinking block followed by one text block.
Fixes #5028
Fixes #4404
fix(codex): detect growing rollouts with a persisted byte cursor (#7219)
Codex keeps the rollout append handle open for the whole session, so on Windows NTFS the file mtime does not advance while the file grows and the pure-mtime gate skipped growing rollouts forever. Persist the observed byte length in session_log_sync.last_byte_offset for Codex rows and skip a file only when both mtime and size are unchanged. The parser now splits records on newlines manually so an incomplete trailing record is retried on the next pass without advancing the line cursor.
Fixes #6060
fix(codex): proxy image edits and Images API follow-ups (#7177)
* fix(codex): proxy image edits and Images API follow-ups
Follow-ups to #7036 for Codex's built-in ImageGen over the local proxy:
- Register `/images/edits` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them through the shared Codex standalone passthrough.
ImageGen posts to `images/edits` whenever a call references existing
images, and the router returned an empty 404 for it.
- Add `CodexStandaloneEndpoint::ImagesEdits`. Both Images endpoints share
one full-URL suffix table, so a complete Responses / Compact / Chat
Completions / Images URL derives the sibling endpoint (and vice versa)
while opaque full URLs keep failing closed.
- Apply the standalone sibling rewrite when the base URL is pasted as a
complete endpoint with the full-URL switch off, mirroring
`codex_chat_base_is_full_endpoint`; standalone requests no longer become
`.../chat/completions/images/generations`.
- Parse Images API streaming usage: `image_generation.completed` carries
`usage` on the event top level, so scan for `usage.input_tokens` before
falling back to the Chat Completions stream parser.
Related #7036
* fix(codex): match standalone URL suffixes case-insensitively
Keep endpoint detection and sibling rewriting consistent while preserving the original URL prefix and query. Cover mixed-case Images and Alpha Search endpoints through URL and proxy regression cases.
---------
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): preserve per-app settings on shutdown and port allocation (#7210)
On normal exit, stop_with_restore_keep_state() called the legacy global
proxy-config writer to clear the long-retired live_takeover_active flag.
That writer reads the Claude row and runs an UPDATE without a WHERE clause,
so every shutdown copied Claude's max_retries and the three timeout fields
onto the Codex, Gemini and Grok Build rows — and the UPDATE never carried
live_takeover_active in the first place, so the step had no effect beyond
the clobber. The ephemeral listen-port path did the same on every proxy
start.
Drop the dead write-back (live-config restore, backup cleanup and health
reset are unchanged) and persist the resolved ephemeral port through the
global-config interface, which only touches the shared listen/logging
columns. Two regression tests seed distinct per-app settings for all four
apps and assert they survive both paths.
Fixes #7204
fix(provider): preserve child metadata during universal sync (#7212)
Syncing a universal provider regenerates its Claude/Codex/Gemini child
providers. The child's `settings_config` was merged with the existing row,
but `meta`, `created_at` and `sort_index` were taken from the generated
object, and the universal provider never carries any of them from the UI.
The UPDATE therefore wiped the child's usage script, common-config opt-out,
endpoint auto-select and other per-app settings to `{}` and reset the sort
index to NULL, which pushed the card to the bottom of the list and looked
like the provider had been deleted and re-created.
Keep the existing child's `meta`, `created_at` and `sort_index` when the
row already exists. Name, base URL, key, models, website and notes are
still driven by the universal provider, and first-time creation still
inherits the parent's metadata.
Fixes #7134
feat(presets): rebrand DashScope as 千问AI平台 and refresh Qwen models (#7183)
Rename the domestic Bailian presets to 千问AI平台 with a dedicated icon, refresh the Qwen family to the 3.8 generation across Claude Code, Claude Desktop, Codex, Hermes, OpenClaw, OpenCode and Pi, add the domestic Token Plan presets, restore the international QwenCloud Coding Plan presets, and align OpenClaw/OpenCode/Pi model metadata with the official client docs.
fix(codex): honor proxy URL when model_provider is omitted (#7263)
When a Codex provider card's TOML has no `model_provider`, Codex falls back to
its built-in `openai` provider, but takeover wrote the proxy address as a
top-level `base_url` that Codex never reads, so requests bypassed the local
proxy and went straight to api.openai.com.
Treat the missing selector as the built-in `openai` provider and write the
proxy address to `openai_base_url`; the existing legacy-reroute migration then
turns it into a `cc-switch` custom provider table carrying the `PROXY_MANAGED`
bearer, the same shape every other third-party takeover uses. Explicit
provider selections are unchanged.
Refs #6256, #7217
fix(provider): update MiniMax defaults to M3 and remove expired offers (#7255)
* fix(provider): refresh MiniMax M3 presets and remove expired promotions
* fix(provider): complete MiniMax M3 capability declarations
---------
Co-authored-by: saladday <1203511142@qq.com>
chore(provider): remove Atlas Cloud sponsor status, keep presets
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`
docs(release): add v3.20.2 release notes
chore(release): v3.20.2
feat(provider): add SoleAPI sponsor presets across all nine apps
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.
fix(codex): proxy image edits endpoint
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.
fix(provider): pin models URL for JieKou and Novita Claude presets
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.
feat(pricing): refresh seed for September 2026 vendor price changes
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.
fix(codex): stamp requires_openai_auth on takeover writes to match the live login state
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.
fix(updater): show the real reason when an update check fails (#6482)
The updater plugin rejects with a plain string, so `err instanceof Error`
never matched and the real failure reason was replaced by the generic
fallback message. Use the existing extractErrorMessage helper, and pass
the recovered message into the check-update failure toast so it actually
reaches the user instead of stopping at an unread context state.
Co-authored-by: Jason <farion1231@gmail.com>
docs: fix the locales path in the README directory trees (#6100)
All four READMEs showed locales/ as a direct child of src/, but the
translations live at src/i18n/locales/. Split it into two tree nodes so it
matches how the rest of the tree is drawn. Also fixes a CLaude -> Claude
typo in README_ZH.md.
Co-authored-by: Jason <farion1231@gmail.com>
fix(claude): exclude workflow journal.jsonl from session list
Claude Code's workflow feature writes subagents/workflows/wf_*/journal.jsonl inside the session directory. The Claude session scanner only excluded agent-* files, so each journal parsed as an empty ghost session titled "journal". Exclude journal.jsonl by file name and add regression tests.
Fixes #6042
fix(i18n): add the missing pi.form.providerKeyDuplicate and common.collapse keys
translatePiProviderMutationError returns pi.form.providerKeyDuplicate for the Pi backend's duplicate-key error, but the key existed in none of the four locale files, so the toast showed the raw key. common.collapse (aria-label of the thinking-map toggle) was missing the same way. Add both keys to zh/en/ja/zh-TW and lock them in the locale coverage test.
fix(proxy): stop mask_url panicking on multi-byte UTF-8 near the truncation boundary
mask_url's fallback branch (used when the proxy URL fails Url::parse) sliced the raw string at byte 20 and panicked when a multi-byte UTF-8 character straddled that offset, e.g. a CJK string pasted into the global proxy address. Snap the cut to the nearest char boundary at or before 20 and add a regression test.
fix(tests): isolate LOCALAPPDATA so tests stop writing the real Claude Desktop config
On Windows, Claude Desktop config paths resolve from LOCALAPPDATA, which the test home override in tests/support.rs did not redirect, so profile_roundtrip wrote the https://desktop.test fixture into the developer's real Claude Desktop config. Point LOCALAPPDATA at the isolated test home alongside HOME and USERPROFILE.
Fixes #6077
fix(usage): widen pricing model source select to fit localized labels
The pricing model source SelectTrigger in the usage cost settings table was w-28, which leaves about 70px for the label; the English and Japanese labels need about 107px and overflowed the trigger. Widen it to w-40 and drop the h-7 overrides so the trigger and the adjacent Input use the shared h-9 height.
feat(pi): add Tencent TokenHub and Token Plan presets (#7159)
Add eight Tencent presets for Pi, covering the TokenHub pay-as-you-go line
(domestic and international) and the six Token Plan subscription products —
Pi was the only managed app still missing them. Model IDs, ordering and
endpoints follow Tencent's own model lists verbatim, and the tests assert each
product's line-up in order so future drift is caught.
Declare thinking control alongside the capabilities rather than leaving it to
defaults: the 31 DeepSeek entries carry a Tencent-specific compat so selecting
"off" actually disables thinking instead of silently billing reasoning tokens,
the eight Kimi K2.7 Code entries no longer offer an "off" level the model
cannot honour, and kimi-k2.7-code-highspeed is marked image-capable so
tool-returned screenshots are no longer dropped.
Drop minimax-m2.5 from every app: it is gone from Tencent's model tables and
the platform is retiring it.
fix(codex): proxy image generations endpoint (#7036)
Codex's built-in ImageGen calls the legacy OpenAI Images API, but the local
proxy only registered Responses / Chat Completions / Compact / Alpha Search
routes, so `/v1/images/generations` returned an empty 404 under Codex routing.
- Register `/images/generations` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them as a Codex standalone passthrough shared with Alpha Search.
- Generalize the Alpha Search full-URL rewrite into
`rewrite_codex_standalone_full_url`: derive the sibling
`/images/generations` from full Responses / Compact / Chat Completions URLs,
keep an existing Images URL as-is, and fail closed on opaque full URLs.
- Record `usage.input_tokens` / `output_tokens` through the Codex usage parser.
Fixes #5429
Fixes #6745
feat(pricing): add Gemini 3.8 Flash pricing (#7164)
Seeds the built-in pricing row for Gemini 3.8 Flash at 0.75 / 3.75 input/output per million tokens, 0.075 cache read, no cache-write charge, plus a regression test locking all four fields.
Values verified against Google's own pricing page (introductory price through 2026-12-31; list price 1.50 / 7.50 / 0.15 from 2027-01-01) and models.dev.
feat(provider): extend PPIO presets for Pi (#6870)
Add a PPIO provider preset for Pi using the OpenAI-compatible endpoint,
and pin an explicit models URL on the Claude Code PPIO preset so model
discovery resolves against https://api.ppio.com/openai/v1/models instead
of the derived candidates, which all return 404.
Co-authored-by: Jason <farion1231@gmail.com>
feat(pricing): add GLM-5.3 Flash pricing (#7163)
fix(proxy): enable parallel tool calls for Codex OAuth (#7024)
Codex OAuth requests defaulted parallel_tool_calls to false when the incoming
Anthropic request carried no explicit value, forcing Claude Code into one tool
call per model turn. Default it to true (matching codex-rs), map Anthropic
tool_choice.disable_parallel_tool_use to the inverse Responses value so explicit
serial execution is preserved, and add regression coverage for both directions.
Carries #5722 forward on top of current main.
Fixes #5719
Co-authored-by: 太一 <taiyi@douyu.tv>
feat(pricing): add GPT-6 Astra pricing (#7162)
fix(codex): resolve input_modalities for unknown models in vendor catalog (#6750)
When a model slug is not in the bundled DeepSeek official catalog, the vendor path clones the flagship entry and previously inherited its text-only input_modalities. Resolve modalities for unmatched slugs through the same registry/fail-open logic the non-vendor path uses, so a vision variant such as deepseek-v4-flash-vision-exp is no longer declared text-only. Explicit user inputModalities still win; matched models keep the vendor's declaration verbatim.
Fixes #6725
fix(claude): expose Opus 5 and Sonnet 5 in takeover mode (#5882)
Bump the stable client-facing role aliases that takeover mode writes to
Claude Code: Opus claude-opus-4-8 -> claude-opus-5 and Sonnet
claude-sonnet-4-6 -> claude-sonnet-5, so the /model menu matches the
Claude Desktop default routes and preset defaults that already moved to
Opus 5 / Sonnet 5. Add "opus-5" to the adaptive-thinking classifier so
the Bedrock thinking optimizer, which runs on the client alias before
model mapping, keeps sending adaptive thinking instead of the removed
budget_tokens.
Routing is unaffected: the proxy maps client aliases by role keyword, and
the aliases are rewritten automatically the next time the proxy starts.
Fixes #5876
Co-authored-by: Jason <farion1231@gmail.com>
feat(pricing): add GLM-5.3 to built-in model pricing table (#6591)
Seed glm-5.3 at $1.4/$4.4 per MTok with $0.26 cache read, matching the
official Z.ai list price (identical to the GLM-5.2 tier):
https://docs.z.ai/guides/overview/pricing
New model id, so seed only; no repair entry and no SCHEMA_VERSION bump.
Existing installs pick the row up on next launch via INSERT OR IGNORE, and
the startup cost backfill re-prices historical glm-5.3 rows recorded at $0.
fix(codex): align OAuth client identity for GPT-6 (#7132)
Requests for gpt-6-astra through the Claude-to-Codex OAuth takeover route advertised Codex 0.144.1, below the model's 0.153.0 minimal_client_version, so ChatGPT's backend rejected them with HTTP 400 "requires a newer version of Codex". Bump the advertised client version to 0.153.4 and share the originator/version constants between generation and model discovery, which previously sent cc-switch's own package version and originator.
Two regression tests pin the generation headers and the model-list request to a version at or above the gpt-6-astra floor.
Refs #7129
fix(codex): disable supports_search_tool for DeepSeek catalog models (#6653)
DeepSeek's Responses API has no tool_search support, but the bundled
official catalog declared supports_search_tool=true for deepseek-v4-pro
and deepseek-v4-flash. Codex gates deferred/namespaced MCP tools on
this flag (search_tool_enabled = supports_search_tool &&
namespace_tools), so MCP tools were hidden behind a tool_search the
gateway cannot serve — no MCP tool could ever be called.
Set the flag to false for both models so Codex lists MCP tools inline.
Hosted web_search is unaffected (gated by provider capabilities, not
this flag) and DeepSeek's server-side web_search still works.
Fixes #6647
fix(codex): move $ref siblings into allOf for Moonshot/Kimi chat upstreams (#6863)
Moonshot's Chat Completions validator (api.moonshot.cn, api.moonshot.ai and the Kimi For Coding endpoint api.kimi.com) rejects a `$ref` that carries sibling keywords. Codex Desktop's built-in tool schemas carry exactly that shape, so every desktop turn routed through the local proxy to Kimi failed with HTTP 400.
When the resolved upstream host is Moonshot/Kimi, each `$ref` with siblings is moved into `allOf` after the Responses -> Chat conversion and the siblings stay in place. The traversal only enters schema-valued keywords and the rewrite is idempotent. Every other provider keeps byte-identical tool schemas, and the Codex -> Anthropic path is left alone because Moonshot's Anthropic-compatible endpoint accepts the original shape.
Fixes #6867
Co-authored-by: Jason <farion1231@gmail.com>
fix(codex): point Zhipu GLM presets at the official Responses endpoint (#6957)
Zhipu documents three separate base URLs per site (Anthropic /api/anthropic,
Chat /api/coding/paas/v4, Responses /api/v1) and warns that the wrong one
cannot use Coding Plan quota. Codex direct-connect speaks Responses, so the
presets must target /api/v1; the 400 in #6944 (`unknown variant custom`)
came from the Chat endpoint's strict legacy gateway.
- Switch the Zhipu GLM / Zhipu GLM en presets to apiFormat openai_responses
(NativeResponses catalog: shell_command edits, no freeform apply_patch)
…
移除 tauri-plugin-updater 依赖与自动更新链路,改为统一引导用户到
GitHub Releases 手动下载新版。
- 前端:updater.ts 恒返回 up-to-date;UpdateContext 取消启动自检;
AboutSection「检查更新」直接打开 fork releases 页;DatabaseUpgrade
恢复界面去掉下载进度条,改为打开发布页。
- 后端:移除 updater 插件注册与 UpdaterExt 调用;
install_update_and_restart 降级为空壳,check_app_update_available
返回当前版本号;check_for_updates 指向 fork releases;capabilities
去掉 updater:default 权限。
- 打包:tauri.conf.json 删 plugins.updater、createUpdaterArtifacts=false;
Cargo.toml / package.json 删 updater 依赖,Cargo.lock / pnpm-lock
同步重生成。
- CI:release.yml 移除签名收集与 assemble-latest-json job,删除
.tar.gz/.sig 处理,release body 去掉 updater 说明。
版本号统一为 3.20.0-1(合法 semver 且 MSI 第 4 位为 1)。
Cargo.lock / pnpm-lock.yaml 已随依赖移除重生成,cargo check 通过。
ci: build only Windows x64 and macOS arm64, unsigned
feat: gate dev panel behind dev-only env flag
chore(comet): archive ccs-sidebar-panels-visibility change
fix: apply skills visibility guard to hermes branch toolbar button
feat: include prompts in sidebar panel visibility settings
fix: persist visibleSidebarPanels setting in backend AppSettings
style: use compact pill toggles for sidebar panel visibility
feat: add sidebar panel visibility switches to settings
feat: add sidebar panel visibility settings and wire into toolbar buttons
chore(comet): archive ccs-home-model-display change
fix(fork): read grokbuild api key from config TOML in quick switch dialog
feat(fork): align model badge with upstream extractModelBadge
feat(fork): show current model badge and quick-switch entry on provider cards
feat(fork): add model quick switch dialog
feat(fork): add provider model read/write utils with tests
chore(comet): archive ccs-model-search-port change
feat(i18n): add model search picker keys for all locales
feat(form): port SearchableModelPicker from upstream for searchable model selection
chore(comet): archive ccs-claude-form-layout change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-claude-form-layout/
- Sync claude-form-layout spec to main specs
- Annotate design doc as archived
- Add verification report
fix(i18n): update advanced options hint, add ja/zh-TW toggle-all, drop dead apiFormatHint key
fix(form): correct 1M toggle-all alignment direction (ml-1)
fix(form): align 1M toggle-all checkbox with row checkboxes
refactor(form): move api format select into EndpointField rightSlot
fix(form): widen api format select to fit option text
feat(form): add 1M toggle-all checkbox in model mapping header
feat(form): move api format select next to endpoint input
chore(comet): archive ccs-provider-presets-trim change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-provider-presets-trim/
- Sync provider-presets-visibility spec to main specs
- Annotate design doc as archived
- Add verification report
docs(fork): add presets-trim files to rebase whitelist
test(fork): mock IS_FORK_BUILD false in unit tests for upstream-form semantics
feat(fork): wire official preset filter into provider forms
feat(fork): add hidden field to preset interfaces
feat(fork): add official preset allowlist and filter util
chore(comet): archive ccs-fork-scaffolding change
- Move change artifacts to docs/openspec/changes/archive/2026-08-18-ccs-fork-scaffolding/
- Sync fork-build-identity spec to main specs
- Annotate design doc as archived
- Add verification report
feat(fork): add dev config with isolated identifier and config dir
docs(fork): complete rebase whitelist with all fork-touched files
fix(fork): guard setTitle with isTauri in test/non-tauri env
fix(fork): define __CCS_FORK_BUILD__ in vitest config
feat(fork): add DevPanel and settings footer entry
feat(fork): set macOS window title to CC Switch (Fork) on startup
feat(fork): inject __CCS_FORK_BUILD__ compile-time constant
feat(fork): bump version to 3.19.2-fork.1 and rename product to CC Switch (Fork)
docs(fork): add upstream rebase workflow and ignore tool artifacts
fix(proxy): accept the whole grok-4.x (x>=5) family in the reasoning-effort whitelist (#7369)
Replace the verbatim grok-4.5 / grok-4.6 entries in supports_reasoning_effort with a rule that parses the grok-4.x minor version and accepts x >= 5, mirroring the existing GPT-5+ rule. This covers grok-4.7 (released 2026-09-21), whose reasoning effort was previously dropped on the Claude -> Chat, Claude -> Responses and Codex Responses -> Chat conversion paths, and lets future releases pass without another whitelist edit. The grok-build-* family is retained for saved providers.
Co-authored-by: allenxu09 <171831965+allenxu09@users.noreply.github.com>
docs(release): add v3.20.4 release notes
chore(release): v3.20.4
feat(pricing): add Grok 4.7 and GLM-5.3-FlashX
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.
fix(i18n): remove outdated OpenCode Go referral offer
fix: add SudoCode.chat alternate API endpoint
feat(settings): add a GitHub star prompt to the About card
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
feat(presets): add Soshow aggregator presets
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.
fix(ci): stabilize frontend tests and verify renderer build
fix(mcode): refresh provider state after removal (#7578)
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
fix(lib): reset skip_taskbar before window.show() across entry points (#6348)
5处 window.show() 调用中 4处没有在 show() 前重置 Windows 的 skip_taskbar,导致以下场景出现窗口可见但不在任务栏、点最小化后窗口消失的 bug:
- single_instance handler (lib.rs:360-361):当原进程因 silent_startup / 关闭到托盘等处于 skip_taskbar(true) 状态时,从快捷方式/开始菜单唤起已运行实例触发。
- handle_deeplink_url 的 focus_main_window 分支 (lib.rs:256):通过 ccswitch:// URL 唤起时。
- DB 恢复流程 (lib.rs:556):数据库版本不匹配时显示恢复界面。
- RunEvent::Opened 处理器 (lib.rs:1803):系统接收到 deep-link URL 时。
修复后的行为与 tray.rs::handle_tray_menu_event::show_main 分支一致(那里已有相同的 set_skip_taskbar(false) 调用)。
经用户测试验证:autostart + silent_startup + 从开始菜单/快捷方式启动的场景下,最小化按钮恢复正常工作。
fix(proxy): omit missing tool descriptions in the Codex responses-to-chat converter (#7378)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): preserve max effort for GPT-5.6 and GPT-6 Astra (#7531)
fix(misc): ignore shell startup output when probing tool versions in WSL (#7348)
try_get_version_wsl runs "${SHELL:-sh}" -lic '<tool> --version' through
wsl.exe and takes the first x.y.z anywhere in stdout. A login+interactive
shell runs the distro's startup files first, so their output shares that
stdout. On Ubuntu, /etc/profile.d/update-motd.sh prints the MOTD once a day,
starting with "Welcome to Ubuntu 24.04.4 LTS", and the card then showed
24.04.4 as the Claude Code version. The once-a-day stamp makes it appear
after the first probe of the day and vanish on refresh.
The payload now prints a sentinel before running the tool, and parsing looks
only at what follows its last occurrence. The last occurrence handles the
-lic || -lc || -c fallback chain printing it several times; matching it as a
substring handles OSC sequences glued in front of it. If the sentinel is
absent (the shell failed before the payload ran), the output is used as
before. The stderr fallback for tools that print their version there is kept.
Fixes #7347
Co-authored-by: lyfuci <lyfuci11@gmail.com>
fix(claude): hide session URL attribution (#7053)
* fix(claude): hide session URL attribution
* docs(claude): document session URL attribution
fix(ui): reset provider scroll when switching apps (#5211)
fix(pi): fix provider logo (#6826)
fix(i18n): use the Pi form's own term for the zh-TW duplicate-key message (#7187)
Follow-up to the nit on #6768. In zh-TW the Pi form labels the field
供應商識別碼 and says 供應商識別碼為必填 when it is empty, but the
duplicate message I added said 供應商標識 — the term Hermes, OpenCode and
OpenClaw use for their own field. One form, two names for the same thing.
The other three locales already keep the label and both messages on one
term, so this makes zh-TW match the pattern rather than introduce one:
en Provider key / Provider key is required / This provider key already exists
ja プロバイダーキー / プロバイダーキーを入力してください / このプロバイダーキーは既に存在します
zh 供应商标识 / 供应商标识不能为空 / 该供应商标识已存在
zh-TW 供應商識別碼 / 供應商識別碼為必填 / 該供應商標識已存在 <- the odd one out
hermes.form.providerKeyDuplicate carries the same sentence and is left
alone: its own label is 供應商標識, so it is already consistent.
Verified: managementListLocales 7/7. Full unit suite 1085/1087, the same
two App.test.tsx integration cases failing before and after.
fix(proxy): strip stop param for GitHub Copilot to unblock auto mode classifier (#5404)
Claude Code's auto mode classifier sends `stop_sequences` on every
safety check. On the GitHub Copilot path the Anthropic -> OpenAI Chat
conversion mapped it to `stop`, and Copilot's Chat Completions route
for Claude models answers that request with HTTP 400, so every
classifier call failed while normal chat kept working.
- Remove the converted `stop` field only when the provider is GitHub
Copilot; other OpenAI-compatible upstreams keep it.
- Copilot detection in the transform now also covers providers that
are recognised only through `env.ANTHROPIC_BASE_URL`.
- Regression tests for the Copilot (meta and env URL) and generic
provider cases.
Fixes #5175
fix(codex): keep stored auth when the live snapshot has no credential (#7434)
Editing the current Codex provider seeds the form from the live ~/.codex
snapshot, and switching away backfills that snapshot into the DB row. When a
provider table declares its own credential source (an Authorization header,
env_key, auth/aws), no bearer token is injected into config.toml and the
shared auth.json is deleted on third-party switches, so the live snapshot is
permanently `{ auth: {}, config }` while auth.OPENAI_API_KEY in the DB is the
only copy of the key. Both paths treated that empty live auth as
authoritative and silently erased the stored key, which in turn made the
usage-script modal misread the provider as an official subscription and
reset the saved script.
- Treat a credential-less live auth (missing auth.json or the bare
auth_mode logout marker) as an absent field in both the edit dialog and
the switch-away backfill, keeping the stored auth template. Live still
wins whenever it carries material, and official providers keep the live
login as their source of truth.
- Gate the official exemption on is_codex_official_provider /
resolveCodexOfficialIdentity rather than the category alone: a legacy
fixed official card without a persisted category is still written as
official, so the fallback must not resurrect a logged-out login on
switch-back.
- Cover both layers with regression tests, including the category-less
official shapes.
Fixes #7433
Supersedes #7492
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): lift additional_tools input carriers on the Codex chat conversion (#7454)
Codex talks to third-party providers over its Responses Lite transport,
which ships the request's extra tools in an `additional_tools` input
carrier: `{"type":"additional_tools","role":"developer","tools":[...]}`,
a role but no `content`. The Responses->Chat conversion let that carrier
fall through the generic "item with a role is a message" arm and emitted
`{"role":"system","content":null}` at messages[1], which strict Chat
gateways reject with `messages[1]: missing field "content"` (or
`data did not match any variant of ...SystemMessageContent`). The tools
carried inside it were also dropped on the Chat and Anthropic conversions.
- Skip `additional_tools` items in the Chat message walk; a carrier
declares tools, it is not a message.
- Collect carrier tools through the existing CodexToolContext registry
(dedup, namespace flattening, custom-tool conversion), which also feeds
the Anthropic conversion and the response-side name round-trip.
- Requests without a carrier convert byte-for-byte as before.
Known boundary: `custom` children inside a namespace tool
(`functions.exec`) are still not lifted; that needs its own response-side
name round-trip and is left for a follow-up.
Diagnosed independently in #6158 by BigStrongSun (fix in #6159) and in
fixture.
Fixes #7451
Fixes #6158
Refs #6033
Refs #6776
Supersedes #6159
fix(proxy): normalize Codex detail:original images for Chat gateways (#7476)
Codex marks tool-returned images with detail:"original" whenever the model
catalog advertises supports_image_detail_original, a Responses-only value.
The Responses->Chat conversion copied it verbatim into image_url.detail, and
strict OpenAI-compatible Chat gateways (Ark/Doubao, SenseNova, GLM, vLLM, ...)
reject the whole request with 400. Because Codex replays the full rollout on
every turn, one poisoned view_image result made the session fail permanently,
including plain-text turns.
Two layers:
- tool_media::merge_top_level_detail downgrades original -> auto, so already
poisoned rollouts and Codex versions before 0.155.0 (which do not normalize
replayed history themselves) recover immediately. auto/low/high are untouched.
- codex_catalog_model_entry always writes supports_image_detail_original:false
on the ProxyChat profile, so Codex stops emitting original at the source once
the catalog is regenerated. NativeResponses/Anthropic profiles and the DeepSeek
official catalog are unaffected.
Top-level user images were never affected: responses_content_to_chat_content
drops the part-level detail entirely.
Fixes #6495
Fixes #7458
Refs #6569
Refs #5805
fix(omo): detect unified config on the WSL side when the OpenCode dir is in WSL (#7550)
When the OpenCode config directory points into WSL
(\\wsl.localhost\<distro>\home\<user>\.config\opencode), OMO >= 4.19.3 keeps
its unified config in the WSL-side home (~/.omo/omo.jsonc). Detection only
probed the Windows home, never found it, and silently fell back to the legacy
per-plugin file that OMO no longer reads, so provider switches looked
successful but had no effect.
- Add derive_wsl_home_dir to derive the WSL-side home from a WSL UNC path
(home/<user> or root); non-WSL and non-home paths return None.
- Probe a list of home candidates for the unified config, WSL-side home
first and the OS home as a fallback, shared by the read, write and delete
paths.
- Detection only: when no unified config exists on either side the legacy
fallback is unchanged.
Fixes #7363
Supersedes #7367
docs(partner): add a FluxA purchase link and tighten its sponsor blurb
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.
fix(claude-desktop): expose the 1M context variant for DeepSeek V4 routes
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.
feat(presets): add FluxA Token Plan partner presets
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.
fix(presets): correct AICodeWith Codex-family endpoint to /v1
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.
fix(ui): render monochrome preset icons in the foreground color
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.
docs(user-manual): point Chat-only Codex examples at plan endpoints
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.
fix(pricing): restore DeepSeek V4 Pro price and seed Qwen3.8 and Hy4 models
- 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).
fix(presets): move MiniMax CN and BaiLing to their current official endpoints
- 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.
feat(codex): refresh CN presets from the Responses API audit
- 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.
test(claude-desktop): run Linux path tests on unix hosts and cover Linux in provider sync tests
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.
fix(ui): stop expanding stale cached usage tiers on ineligible provider cards
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.
fix(skills): 支持 skillId 与目录名不一致的技能安装和更新 (#6381)
* fix(skills): resolve mismatched skillId directories
* fix(skills): preserve source path during updates
* fix(skills): refresh source URL after repository relocation
* test(skills): isolate relocation fixture from legacy home
feat(opencode): 支持从获取的模型列表中搜索并批量添加所选模型 (#7515)
* feat: implement fetched models selection and UI components
* fix(opencode): prevent model search from submitting provider form
Add Kimi Global preset variants (kimi.ai platform + api endpoints) (#7526)
Add a "Kimi Global" twin immediately after the domestic "Kimi" API preset and a "Kimi For Coding Global" twin after the domestic "Kimi For Coding" preset, in every preset file that ships them (claude, claude-desktop, codex, hermes, openclaw, opencode, pi).
"Kimi Global" points to the international open platform https://platform.kimi.ai?aff=cc-switch (apiKeyUrl mirrored as https://platform.kimi.ai/console/api-keys?aff=cc-switch) and swaps the endpoint host api.moonshot.cn for api.moonshot.ai with each file's path unchanged (/v1 or /anthropic).
"Kimi For Coding Global" points to the international site https://www.kimi.ai/code/?aff=cc-switch and swaps the endpoint host api.kimi.com for api.kimi.ai with the path unchanged (e.g. https://api.kimi.ai/coding/v1).
Domestic presets are unchanged; category, icons, apiFormat, model catalogs, endpointCandidates (host swapped), partnerPromotionKey and settings otherwise mirror the domestic blocks exactly.
primePartner stays only on the domestic presets so the new variants do not alter prime-partner ordering.
The Pi twins get distinct providerKey values ("cc-switch-kimi-global", "cc-switch-kimi-for-coding-global") because providerKey is the preset-level primary key and must not collide with the domestic presets.
Rebalance sponsor CTAs around Kimi Code plan with dual-region links (#7522)
Kimi Code plan purchases have resumed, so the README sponsor sections go back to leading with the coding plan instead of the API-only flow written during the purchase pause.
All four locales (EN/ZH/JA/DE) now present Kimi Code plan (中文站 | Global) as the primary CTA and the Kimi Open Platform API (中文站 | Global, with the first-top-up bonus) as the secondary one, mirroring the domestic|global split already used for API links.
The aff and track_id values are unchanged.
fix(prompts): refresh active prompts after external file edits (#7194)
Reload the enabled prompt from its live file when the Prompts list loads, and refresh the open panel when the window regains focus, so external edits to CLAUDE.md / AGENTS.md show up without a restart.
- Keep saved templates available when the live file cannot be read.
- Skip the backfill for empty or whitespace-only files, matching enable_prompt.
- Skip the backfill while a sync/restore holds the sync lock, so a list load cannot write a stale live file into a freshly restored database.
fix(skills): raise archive entry limit so large skill repos install (#7489)
Installing hugohe3/ppt-master from skills.sh failed with
ARCHIVE_TOO_MANY_ENTRIES: the GitHub archive we download is the whole
repository (13,248 entries), and the 10,000-entry cap added in #5811
treated it as a zip bomb.
- Raise MAX_ARCHIVE_ENTRIES to 30,000 for both the remote archive and
local ZIP paths, and align WebDAV's MAX_EXTRACT_ENTRIES so a skills.zip
containing such a skill still restores.
- Charge every file entry at least one 4 KiB block against the existing
512 MiB budget, mirroring directory accounting, so empty-file archives
are bounded by the byte budget rather than by the entry count alone.
- Add tests for a 13k-entry repo archive and for the per-file minimum
charge.
Fixes: https://github.com/farion1231/cc-switch/issues/7475
feat: add MiniMax Code harness support (#7383)
Add MiniMax Code (mcode) as a managed application: provider and model management through the native custom_provider configuration, MCP servers, Skills, global instructions (AGENTS.md), read-only local session history with resume, and usage import from the native runtime database. Native files shared by the TUI and desktop are written under the native lock with rollback when the database commit fails, unrelated native configuration is preserved, and the data directory honors MINIMAX_DATA_DIR and MAVIS_DATA_DIR. Deleting sessions stays in the native application because they are coupled to runtime state.
fix(codex): recover stale account bindings during takeover (#7395)
After deleting a managed ChatGPT account and logging in again, a Codex provider can still reference the old local account ID, which made route switching, takeover, startup restoration and rebinding fail permanently.
Distinguish a missing outgoing account from an existing account with no matching live token. A missing account only releases its ownership marker after the persisted account store is verified; the target write policy stays in force. A stale target now reports an account-selection instruction instead of a bare not-found error, including when takeover is already enabled.
Fixes #7392
feat(claude-desktop): support 3P configuration on Linux (#7331)
Add Claude Desktop 3P configuration support on Linux. Resolve the config root from an absolute XDG_CONFIG_HOME, falling back to ~/.config; when CC Switch itself runs as a Flatpak, use the host ~/.config instead of the sandbox-private XDG_CONFIG_HOME. Update the en/zh/ja user manuals with Linux paths and the Flatpak boundary.
Fixes #4855
fix(proxy): omit missing tool descriptions instead of serializing null (#7319)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): keep reasoning effort for grok-4.6 and pass xhigh through verbatim (#7318)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(models): support Zhipu OpenAI Responses model lists (#7330)
Zhipu's Responses endpoint (open.bigmodel.cn/api/v1) serves its model list in the Codex remote catalog shape (models[].slug) instead of the OpenAI-compatible data[].id shape, so the Codex form's model fetch came back empty. The parser now falls back to models[].slug when data is absent.
fix(misc): fetch npm dist-tags from the dedicated endpoint with the probe timeout (#7346)
The tool version probe requested the full npm packument for each package (metadata for every historical version, 14-25 MB uncompressed for codex / opencode / openclaw) only to read its dist-tags, and it was the only latest-version probe without LATEST_PROBE_TIMEOUT, so it fell back to the proxy client's 600 s total timeout and the cards sat on "loading" for minutes on slow links while the large downloads starved the Hermes GitHub probe into "unknown".
Switch fetch_npm_dist_tags to registry.npmjs.org/-/package/{package}/dist-tags, whose body is the dist-tags object itself (tens of bytes to a few KB), escape scoped names as %2f, and apply the same 15 s probe timeout as the GitHub and PyPI probes. No data is lost: the only consumer reads latest and the tool's prerelease tags.
Fixes #7339
feat(usage): display output tokens per second in request log table and detail panel (#3369)
Show tokens/s next to the output token count in the request log table and the request detail panel.
The generation window prefers an explicit duration when present, otherwise latency minus first-token time for streaming requests, and falls back to full latency for non-streaming requests. Rows without a positive token count or window show nothing.
Closes #5936
fix(presets): update APIKEY.FUN URLs to apikey.fan
docs(release): add v3.20.3 release notes
chore(release): v3.20.3
fix(pricing): move retired DeepSeek V4 family to the V4.1 Flash tier
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.
fix(presets): declare third-party Token Plan DeepSeek V4 rows text-only
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.
feat(presets): rename DouBaoSeed preset to localized Volcengine Doubao
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.
fix(codex): skip unchanged incomplete rollout tails
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.
fix(claude): align quick model selection with form order
fix(tray): show bound ChatGPT account quota for managed Codex cards
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
feat(claude): add "Disable Artifact Tool" quick toggle to provider editor
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.
feat(codex): refresh aggregator model presets and catalogs
feat(codex): switch Kimi presets to native Responses direct-connect
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.
fix(usage): show Claude Fable weekly limit from the usage API limits array
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.
fix(codex): mirror vision-capable DeepSeek catalog and un-gate the legacy v4-flash alias (#7286)
Refresh the bundled DeepSeek official Codex catalog to the vendor's current models.json (slug deepseek-flash, image input modality, supports_image_detail_original) and remove deepseek-v4-flash from the confirmed text-only registry: the legacy id is still accepted by DeepSeek and routed to the vision-capable V4.1 Flash, so both the proxy media sanitizer and the generated Codex catalog must fail open for it. deepseek-v4-pro stays text-only per the vendor catalog.
Fixes #7283
fix(proxy): clamp sub-floor max_tokens to the Responses API minimum (#7287)
The Responses API rejects max_output_tokens below 16, but Anthropic clients legitimately send tiny probe budgets (Claude Desktop's model-availability probe uses max_tokens=1). The Anthropic→Responses conversion copied the value verbatim, so strict upstreams returned 400 and Claude Desktop reported the mapped model as unavailable under local routing.
Clamp 1..=15 up to 16 during conversion; 16+, 0 and non-integer values keep their pass-through semantics. Normal session requests convert byte-for-byte as before, and the Codex OAuth path still strips max_output_tokens afterwards.
Fixes #7103
fix(codex): coalesce adjacent commentary with pending tool calls (#7280)
The Codex Responses -> Chat Completions converter emitted two consecutive
assistant messages when one Responses model turn contained a commentary
message directly followed by function_call items. Chat upstreams imitate
the text-only assistant message as a complete turn and return
finish_reason=stop before the expected tool call, so long agent tasks end
after a progress update.
Merge pending tool calls into the directly adjacent assistant message when
it carries no tool_calls yet, attach pending reasoning per segment so
parallel calls do not duplicate it, and keep the new-message path for every
other boundary shape (user/tool boundaries, prior tool-call batches, media
flushes). The reasoning placeholder backfill still applies to coalesced
calls without reasoning.
Fixes #6529
Supersedes #6530
Co-authored-by: BigStrongSun <BigStrongSun@users.noreply.github.com>
fix(proxy): skip empty reasoning_content placeholders in Anthropic SSE streaming (#7227)
Some OpenAI-compatible upstreams (GLM, Qwen, DeepSeek-V4-Pro via ModelScope/DashScope,
Bailian, JD Cloud, ...) keep a `reasoning_content: ""` placeholder in every content
chunk instead of omitting the field. The reasoning branch of the OpenAI Chat -> Anthropic
SSE converter only checked for presence, and it shares `current_non_tool_block_type`
with the content branch, so every content chunk closed the open text block, opened an
empty thinking block, then reopened a text block. N content chunks became 2N content
blocks, half of them empty thinking blocks, which Claude Code rendered as one line per
token with dozens of empty Thought blocks and bloated the session JSONL.
Filter empty reasoning before entering the branch, matching the `!content.is_empty()`
guard the content branch already had and the non-streaming `openai_to_anthropic`
converter. Two regression tests pin block index, type and order: the placeholder shape
must yield a single text block and no thinking_delta at all, and real reasoning must
still yield one thinking block followed by one text block.
Fixes #5028
Fixes #4404
fix(codex): detect growing rollouts with a persisted byte cursor (#7219)
Codex keeps the rollout append handle open for the whole session, so on Windows NTFS the file mtime does not advance while the file grows and the pure-mtime gate skipped growing rollouts forever. Persist the observed byte length in session_log_sync.last_byte_offset for Codex rows and skip a file only when both mtime and size are unchanged. The parser now splits records on newlines manually so an incomplete trailing record is retried on the next pass without advancing the line cursor.
Fixes #6060
fix(codex): proxy image edits and Images API follow-ups (#7177)
* fix(codex): proxy image edits and Images API follow-ups
Follow-ups to #7036 for Codex's built-in ImageGen over the local proxy:
- Register `/images/edits` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them through the shared Codex standalone passthrough.
ImageGen posts to `images/edits` whenever a call references existing
images, and the router returned an empty 404 for it.
- Add `CodexStandaloneEndpoint::ImagesEdits`. Both Images endpoints share
one full-URL suffix table, so a complete Responses / Compact / Chat
Completions / Images URL derives the sibling endpoint (and vice versa)
while opaque full URLs keep failing closed.
- Apply the standalone sibling rewrite when the base URL is pasted as a
complete endpoint with the full-URL switch off, mirroring
`codex_chat_base_is_full_endpoint`; standalone requests no longer become
`.../chat/completions/images/generations`.
- Parse Images API streaming usage: `image_generation.completed` carries
`usage` on the event top level, so scan for `usage.input_tokens` before
falling back to the Chat Completions stream parser.
Related #7036
* fix(codex): match standalone URL suffixes case-insensitively
Keep endpoint detection and sibling rewriting consistent while preserving the original URL prefix and query. Cover mixed-case Images and Alpha Search endpoints through URL and proxy regression cases.
---------
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): preserve per-app settings on shutdown and port allocation (#7210)
On normal exit, stop_with_restore_keep_state() called the legacy global
proxy-config writer to clear the long-retired live_takeover_active flag.
That writer reads the Claude row and runs an UPDATE without a WHERE clause,
so every shutdown copied Claude's max_retries and the three timeout fields
onto the Codex, Gemini and Grok Build rows — and the UPDATE never carried
live_takeover_active in the first place, so the step had no effect beyond
the clobber. The ephemeral listen-port path did the same on every proxy
start.
Drop the dead write-back (live-config restore, backup cleanup and health
reset are unchanged) and persist the resolved ephemeral port through the
global-config interface, which only touches the shared listen/logging
columns. Two regression tests seed distinct per-app settings for all four
apps and assert they survive both paths.
Fixes #7204
fix(provider): preserve child metadata during universal sync (#7212)
Syncing a universal provider regenerates its Claude/Codex/Gemini child
providers. The child's `settings_config` was merged with the existing row,
but `meta`, `created_at` and `sort_index` were taken from the generated
object, and the universal provider never carries any of them from the UI.
The UPDATE therefore wiped the child's usage script, common-config opt-out,
endpoint auto-select and other per-app settings to `{}` and reset the sort
index to NULL, which pushed the card to the bottom of the list and looked
like the provider had been deleted and re-created.
Keep the existing child's `meta`, `created_at` and `sort_index` when the
row already exists. Name, base URL, key, models, website and notes are
still driven by the universal provider, and first-time creation still
inherits the parent's metadata.
Fixes #7134
feat(presets): rebrand DashScope as 千问AI平台 and refresh Qwen models (#7183)
Rename the domestic Bailian presets to 千问AI平台 with a dedicated icon, refresh the Qwen family to the 3.8 generation across Claude Code, Claude Desktop, Codex, Hermes, OpenClaw, OpenCode and Pi, add the domestic Token Plan presets, restore the international QwenCloud Coding Plan presets, and align OpenClaw/OpenCode/Pi model metadata with the official client docs.
fix(codex): honor proxy URL when model_provider is omitted (#7263)
When a Codex provider card's TOML has no `model_provider`, Codex falls back to
its built-in `openai` provider, but takeover wrote the proxy address as a
top-level `base_url` that Codex never reads, so requests bypassed the local
proxy and went straight to api.openai.com.
Treat the missing selector as the built-in `openai` provider and write the
proxy address to `openai_base_url`; the existing legacy-reroute migration then
turns it into a `cc-switch` custom provider table carrying the `PROXY_MANAGED`
bearer, the same shape every other third-party takeover uses. Explicit
provider selections are unchanged.
Refs #6256, #7217
fix(provider): update MiniMax defaults to M3 and remove expired offers (#7255)
* fix(provider): refresh MiniMax M3 presets and remove expired promotions
* fix(provider): complete MiniMax M3 capability declarations
---------
Co-authored-by: saladday <1203511142@qq.com>
chore(provider): remove Atlas Cloud sponsor status, keep presets
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`
docs(release): add v3.20.2 release notes
chore(release): v3.20.2
feat(provider): add SoleAPI sponsor presets across all nine apps
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.
fix(codex): proxy image edits endpoint
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.
fix(provider): pin models URL for JieKou and Novita Claude presets
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.
feat(pricing): refresh seed for September 2026 vendor price changes
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.
fix(codex): stamp requires_openai_auth on takeover writes to match the live login state
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.
fix(updater): show the real reason when an update check fails (#6482)
The updater plugin rejects with a plain string, so `err instanceof Error`
never matched and the real failure reason was replaced by the generic
fallback message. Use the existing extractErrorMessage helper, and pass
the recovered message into the check-update failure toast so it actually
reaches the user instead of stopping at an unread context state.
Co-authored-by: Jason <farion1231@gmail.com>
docs: fix the locales path in the README directory trees (#6100)
All four READMEs showed locales/ as a direct child of src/, but the
translations live at src/i18n/locales/. Split it into two tree nodes so it
matches how the rest of the tree is drawn. Also fixes a CLaude -> Claude
typo in README_ZH.md.
Co-authored-by: Jason <farion1231@gmail.com>
fix(claude): exclude workflow journal.jsonl from session list
Claude Code's workflow feature writes subagents/workflows/wf_*/journal.jsonl inside the session directory. The Claude session scanner only excluded agent-* files, so each journal parsed as an empty ghost session titled "journal". Exclude journal.jsonl by file name and add regression tests.
Fixes #6042
fix(i18n): add the missing pi.form.providerKeyDuplicate and common.collapse keys
translatePiProviderMutationError returns pi.form.providerKeyDuplicate for the Pi backend's duplicate-key error, but the key existed in none of the four locale files, so the toast showed the raw key. common.collapse (aria-label of the thinking-map toggle) was missing the same way. Add both keys to zh/en/ja/zh-TW and lock them in the locale coverage test.
fix(proxy): stop mask_url panicking on multi-byte UTF-8 near the truncation boundary
mask_url's fallback branch (used when the proxy URL fails Url::parse) sliced the raw string at byte 20 and panicked when a multi-byte UTF-8 character straddled that offset, e.g. a CJK string pasted into the global proxy address. Snap the cut to the nearest char boundary at or before 20 and add a regression test.
fix(tests): isolate LOCALAPPDATA so tests stop writing the real Claude Desktop config
On Windows, Claude Desktop config paths resolve from LOCALAPPDATA, which the test home override in tests/support.rs did not redirect, so profile_roundtrip wrote the https://desktop.test fixture into the developer's real Claude Desktop config. Point LOCALAPPDATA at the isolated test home alongside HOME and USERPROFILE.
Fixes #6077
fix(usage): widen pricing model source select to fit localized labels
The pricing model source SelectTrigger in the usage cost settings table was w-28, which leaves about 70px for the label; the English and Japanese labels need about 107px and overflowed the trigger. Widen it to w-40 and drop the h-7 overrides so the trigger and the adjacent Input use the shared h-9 height.
feat(pi): add Tencent TokenHub and Token Plan presets (#7159)
Add eight Tencent presets for Pi, covering the TokenHub pay-as-you-go line
(domestic and international) and the six Token Plan subscription products —
Pi was the only managed app still missing them. Model IDs, ordering and
endpoints follow Tencent's own model lists verbatim, and the tests assert each
product's line-up in order so future drift is caught.
Declare thinking control alongside the capabilities rather than leaving it to
defaults: the 31 DeepSeek entries carry a Tencent-specific compat so selecting
"off" actually disables thinking instead of silently billing reasoning tokens,
the eight Kimi K2.7 Code entries no longer offer an "off" level the model
cannot honour, and kimi-k2.7-code-highspeed is marked image-capable so
tool-returned screenshots are no longer dropped.
Drop minimax-m2.5 from every app: it is gone from Tencent's model tables and
the platform is retiring it.
fix(codex): proxy image generations endpoint (#7036)
Codex's built-in ImageGen calls the legacy OpenAI Images API, but the local
proxy only registered Responses / Chat Completions / Compact / Alpha Search
routes, so `/v1/images/generations` returned an empty 404 under Codex routing.
- Register `/images/generations` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them as a Codex standalone passthrough shared with Alpha Search.
- Generalize the Alpha Search full-URL rewrite into
`rewrite_codex_standalone_full_url`: derive the sibling
`/images/generations` from full Responses / Compact / Chat Completions URLs,
keep an existing Images URL as-is, and fail closed on opaque full URLs.
- Record `usage.input_tokens` / `output_tokens` through the Codex usage parser.
Fixes #5429
Fixes #6745
feat(pricing): add Gemini 3.8 Flash pricing (#7164)
Seeds the built-in pricing row for Gemini 3.8 Flash at 0.75 / 3.75 input/output per million tokens, 0.075 cache read, no cache-write charge, plus a regression test locking all four fields.
Values verified against Google's own pricing page (introductory price through 2026-12-31; list price 1.50 / 7.50 / 0.15 from 2027-01-01) and models.dev.
feat(provider): extend PPIO presets for Pi (#6870)
Add a PPIO provider preset for Pi using the OpenAI-compatible endpoint,
and pin an explicit models URL on the Claude Code PPIO preset so model
discovery resolves against https://api.ppio.com/openai/v1/models instead
of the derived candidates, which all return 404.
Co-authored-by: Jason <farion1231@gmail.com>
feat(pricing): add GLM-5.3 Flash pricing (#7163)
fix(proxy): enable parallel tool calls for Codex OAuth (#7024)
Codex OAuth requests defaulted parallel_tool_calls to false when the incoming
Anthropic request carried no explicit value, forcing Claude Code into one tool
call per model turn. Default it to true (matching codex-rs), map Anthropic
tool_choice.disable_parallel_tool_use to the inverse Responses value so explicit
serial execution is preserved, and add regression coverage for both directions.
Carries #5722 forward on top of current main.
Fixes #5719
Co-authored-by: 太一 <taiyi@douyu.tv>
feat(pricing): add GPT-6 Astra pricing (#7162)
fix(codex): resolve input_modalities for unknown models in vendor catalog (#6750)
When a model slug is not in the bundled DeepSeek official catalog, the vendor path clones the flagship entry and previously inherited its text-only input_modalities. Resolve modalities for unmatched slugs through the same registry/fail-open logic the non-vendor path uses, so a vision variant such as deepseek-v4-flash-vision-exp is no longer declared text-only. Explicit user inputModalities still win; matched models keep the vendor's declaration verbatim.
Fixes #6725
fix(claude): expose Opus 5 and Sonnet 5 in takeover mode (#5882)
Bump the stable client-facing role aliases that takeover mode writes to
Claude Code: Opus claude-opus-4-8 -> claude-opus-5 and Sonnet
claude-sonnet-4-6 -> claude-sonnet-5, so the /model menu matches the
Claude Desktop default routes and preset defaults that already moved to
Opus 5 / Sonnet 5. Add "opus-5" to the adaptive-thinking classifier so
the Bedrock thinking optimizer, which runs on the client alias before
model mapping, keeps sending adaptive thinking instead of the removed
budget_tokens.
Routing is unaffected: the proxy maps client aliases by role keyword, and
the aliases are rewritten automatically the next time the proxy starts.
Fixes #5876
Co-authored-by: Jason <farion1231@gmail.com>
feat(pricing): add GLM-5.3 to built-in model pricing table (#6591)
Seed glm-5.3 at $1.4/$4.4 per MTok with $0.26 cache read, matching the
official Z.ai list price (identical to the GLM-5.2 tier):
https://docs.z.ai/guides/overview/pricing
New model id, so seed only; no repair entry and no SCHEMA_VERSION bump.
Existing installs pick the row up on next launch via INSERT OR IGNORE, and
the startup cost backfill re-prices historical glm-5.3 rows recorded at $0.
fix(codex): align OAuth client identity for GPT-6 (#7132)
Requests for gpt-6-astra through the Claude-to-Codex OAuth takeover route advertised Codex 0.144.1, below the model's 0.153.0 minimal_client_version, so ChatGPT's backend rejected them with HTTP 400 "requires a newer version of Codex". Bump the advertised client version to 0.153.4 and share the originator/version constants between generation and model discovery, which previously sent cc-switch's own package version and originator.
Two regression tests pin the generation headers and the model-list request to a version at or above the gpt-6-astra floor.
Refs #7129
fix(codex): disable supports_search_tool for DeepSeek catalog models (#6653)
DeepSeek's Responses API has no tool_search support, but the bundled
official catalog declared supports_search_tool=true for deepseek-v4-pro
and deepseek-v4-flash. Codex gates deferred/namespaced MCP tools on
this flag (search_tool_enabled = supports_search_tool &&
namespace_tools), so MCP tools were hidden behind a tool_search the
gateway cannot serve — no MCP tool could ever be called.
Set the flag to false for both models so Codex lists MCP tools inline.
Hosted web_search is unaffected (gated by provider capabilities, not
this flag) and DeepSeek's server-side web_search still works.
Fixes #6647
fix(codex): move $ref siblings into allOf for Moonshot/Kimi chat upstreams (#6863)
Moonshot's Chat Completions validator (api.moonshot.cn, api.moonshot.ai and the Kimi For Coding endpoint api.kimi.com) rejects a `$ref` that carries sibling keywords. Codex Desktop's built-in tool schemas carry exactly that shape, so every desktop turn rou…
README 从上游完整功能介绍改为仅记录 fork 与上游的差别,完整功能
文档指回上游。涵盖 8 项 fork 魔改:构建标识/开发预览、预设精简、
Claude 表单调整、模型搜索移植、主页模型快速切换、侧边栏面板显隐、
关闭应用内更新、打包范围,并说明版本号与 rebase 同步流程。
feat: disable in-app updater, point users to GitHub releases
移除 tauri-plugin-updater 依赖与自动更新链路,改为统一引导用户到
GitHub Releases 手动下载新版。
- 前端:updater.ts 恒返回 up-to-date;UpdateContext 取消启动自检;
AboutSection「检查更新」直接打开 fork releases 页;DatabaseUpgrade
恢复界面去掉下载进度条,改为打开发布页。
- 后端:移除 updater 插件注册与 UpdaterExt 调用;
install_update_and_restart 降级为空壳,check_app_update_available
返回当前版本号;check_for_updates 指向 fork releases;capabilities
去掉 updater:default 权限。
- 打包:tauri.conf.json 删 plugins.updater、createUpdaterArtifacts=false;
Cargo.toml / package.json 删 updater 依赖,Cargo.lock / pnpm-lock
同步重生成。
- CI:release.yml 移除签名收集与 assemble-latest-json job,删除
.tar.gz/.sig 处理,release body 去掉 updater 说明。
版本号统一为 3.20.0-1(合法 semver 且 MSI 第 4 位为 1)。
Cargo.lock / pnpm-lock.yaml 已随依赖移除重生成,cargo check 通过。
ci: build only Windows x64 and macOS arm64, unsigned
feat: gate dev panel behind dev-only env flag
chore(comet): archive ccs-sidebar-panels-visibility change
fix: apply skills visibility guard to hermes branch toolbar button
feat: include prompts in sidebar panel visibility settings
fix: persist visibleSidebarPanels setting in backend AppSettings
style: use compact pill toggles for sidebar panel visibility
feat: add sidebar panel visibility switches to settings
feat: add sidebar panel visibility settings and wire into toolbar buttons
chore(comet): archive ccs-home-model-display change
fix(fork): read grokbuild api key from config TOML in quick switch dialog
feat(fork): align model badge with upstream extractModelBadge
feat(fork): show current model badge and quick-switch entry on provider cards
feat(fork): add model quick switch dialog
feat(fork): add provider model read/write utils with tests
chore(comet): archive ccs-model-search-port change
feat(i18n): add model search picker keys for all locales
feat(form): port SearchableModelPicker from upstream for searchable model selection
chore(comet): archive ccs-claude-form-layout change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-claude-form-layout/
- Sync claude-form-layout spec to main specs
- Annotate design doc as archived
- Add verification report
fix(i18n): update advanced options hint, add ja/zh-TW toggle-all, drop dead apiFormatHint key
fix(form): correct 1M toggle-all alignment direction (ml-1)
fix(form): align 1M toggle-all checkbox with row checkboxes
refactor(form): move api format select into EndpointField rightSlot
fix(form): widen api format select to fit option text
feat(form): add 1M toggle-all checkbox in model mapping header
feat(form): move api format select next to endpoint input
chore(comet): archive ccs-provider-presets-trim change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-provider-presets-trim/
- Sync provider-presets-visibility spec to main specs
- Annotate design doc as archived
- Add verification report
docs(fork): add presets-trim files to rebase whitelist
test(fork): mock IS_FORK_BUILD false in unit tests for upstream-form semantics
feat(fork): wire official preset filter into provider forms
feat(fork): add hidden field to preset interfaces
feat(fork): add official preset allowlist and filter util
chore(comet): archive ccs-fork-scaffolding change
- Move change artifacts to docs/openspec/changes/archive/2026-08-18-ccs-fork-scaffolding/
- Sync fork-build-identity spec to main specs
- Annotate design doc as archived
- Add verification report
feat(fork): add dev config with isolated identifier and config dir
docs(fork): complete rebase whitelist with all fork-touched files
fix(fork): guard setTitle with isTauri in test/non-tauri env
fix(fork): define __CCS_FORK_BUILD__ in vitest config
feat(fork): add DevPanel and settings footer entry
feat(fork): set macOS window title to CC Switch (Fork) on startup
feat(fork): inject __CCS_FORK_BUILD__ compile-time constant
feat(fork): bump version to 3.19.2-fork.1 and rename product to CC Switch (Fork)
docs(fork): add upstream rebase workflow and ignore tool artifacts
fix(proxy): accept the whole grok-4.x (x>=5) family in the reasoning-effort whitelist (#7369)
Replace the verbatim grok-4.5 / grok-4.6 entries in supports_reasoning_effort with a rule that parses the grok-4.x minor version and accepts x >= 5, mirroring the existing GPT-5+ rule. This covers grok-4.7 (released 2026-09-21), whose reasoning effort was previously dropped on the Claude -> Chat, Claude -> Responses and Codex Responses -> Chat conversion paths, and lets future releases pass without another whitelist edit. The grok-build-* family is retained for saved providers.
Co-authored-by: allenxu09 <171831965+allenxu09@users.noreply.github.com>
docs(release): add v3.20.4 release notes
chore(release): v3.20.4
feat(pricing): add Grok 4.7 and GLM-5.3-FlashX
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.
fix(i18n): remove outdated OpenCode Go referral offer
fix: add SudoCode.chat alternate API endpoint
feat(settings): add a GitHub star prompt to the About card
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
feat(presets): add Soshow aggregator presets
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.
fix(ci): stabilize frontend tests and verify renderer build
fix(mcode): refresh provider state after removal (#7578)
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
fix(lib): reset skip_taskbar before window.show() across entry points (#6348)
5处 window.show() 调用中 4处没有在 show() 前重置 Windows 的 skip_taskbar,导致以下场景出现窗口可见但不在任务栏、点最小化后窗口消失的 bug:
- single_instance handler (lib.rs:360-361):当原进程因 silent_startup / 关闭到托盘等处于 skip_taskbar(true) 状态时,从快捷方式/开始菜单唤起已运行实例触发。
- handle_deeplink_url 的 focus_main_window 分支 (lib.rs:256):通过 ccswitch:// URL 唤起时。
- DB 恢复流程 (lib.rs:556):数据库版本不匹配时显示恢复界面。
- RunEvent::Opened 处理器 (lib.rs:1803):系统接收到 deep-link URL 时。
修复后的行为与 tray.rs::handle_tray_menu_event::show_main 分支一致(那里已有相同的 set_skip_taskbar(false) 调用)。
经用户测试验证:autostart + silent_startup + 从开始菜单/快捷方式启动的场景下,最小化按钮恢复正常工作。
fix(proxy): omit missing tool descriptions in the Codex responses-to-chat converter (#7378)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): preserve max effort for GPT-5.6 and GPT-6 Astra (#7531)
fix(misc): ignore shell startup output when probing tool versions in WSL (#7348)
try_get_version_wsl runs "${SHELL:-sh}" -lic '<tool> --version' through
wsl.exe and takes the first x.y.z anywhere in stdout. A login+interactive
shell runs the distro's startup files first, so their output shares that
stdout. On Ubuntu, /etc/profile.d/update-motd.sh prints the MOTD once a day,
starting with "Welcome to Ubuntu 24.04.4 LTS", and the card then showed
24.04.4 as the Claude Code version. The once-a-day stamp makes it appear
after the first probe of the day and vanish on refresh.
The payload now prints a sentinel before running the tool, and parsing looks
only at what follows its last occurrence. The last occurrence handles the
-lic || -lc || -c fallback chain printing it several times; matching it as a
substring handles OSC sequences glued in front of it. If the sentinel is
absent (the shell failed before the payload ran), the output is used as
before. The stderr fallback for tools that print their version there is kept.
Fixes #7347
Co-authored-by: lyfuci <lyfuci11@gmail.com>
fix(claude): hide session URL attribution (#7053)
* fix(claude): hide session URL attribution
* docs(claude): document session URL attribution
fix(ui): reset provider scroll when switching apps (#5211)
fix(pi): fix provider logo (#6826)
fix(i18n): use the Pi form's own term for the zh-TW duplicate-key message (#7187)
Follow-up to the nit on #6768. In zh-TW the Pi form labels the field
供應商識別碼 and says 供應商識別碼為必填 when it is empty, but the
duplicate message I added said 供應商標識 — the term Hermes, OpenCode and
OpenClaw use for their own field. One form, two names for the same thing.
The other three locales already keep the label and both messages on one
term, so this makes zh-TW match the pattern rather than introduce one:
en Provider key / Provider key is required / This provider key already exists
ja プロバイダーキー / プロバイダーキーを入力してください / このプロバイダーキーは既に存在します
zh 供应商标识 / 供应商标识不能为空 / 该供应商标识已存在
zh-TW 供應商識別碼 / 供應商識別碼為必填 / 該供應商標識已存在 <- the odd one out
hermes.form.providerKeyDuplicate carries the same sentence and is left
alone: its own label is 供應商標識, so it is already consistent.
Verified: managementListLocales 7/7. Full unit suite 1085/1087, the same
two App.test.tsx integration cases failing before and after.
fix(proxy): strip stop param for GitHub Copilot to unblock auto mode classifier (#5404)
Claude Code's auto mode classifier sends `stop_sequences` on every
safety check. On the GitHub Copilot path the Anthropic -> OpenAI Chat
conversion mapped it to `stop`, and Copilot's Chat Completions route
for Claude models answers that request with HTTP 400, so every
classifier call failed while normal chat kept working.
- Remove the converted `stop` field only when the provider is GitHub
Copilot; other OpenAI-compatible upstreams keep it.
- Copilot detection in the transform now also covers providers that
are recognised only through `env.ANTHROPIC_BASE_URL`.
- Regression tests for the Copilot (meta and env URL) and generic
provider cases.
Fixes #5175
fix(codex): keep stored auth when the live snapshot has no credential (#7434)
Editing the current Codex provider seeds the form from the live ~/.codex
snapshot, and switching away backfills that snapshot into the DB row. When a
provider table declares its own credential source (an Authorization header,
env_key, auth/aws), no bearer token is injected into config.toml and the
shared auth.json is deleted on third-party switches, so the live snapshot is
permanently `{ auth: {}, config }` while auth.OPENAI_API_KEY in the DB is the
only copy of the key. Both paths treated that empty live auth as
authoritative and silently erased the stored key, which in turn made the
usage-script modal misread the provider as an official subscription and
reset the saved script.
- Treat a credential-less live auth (missing auth.json or the bare
auth_mode logout marker) as an absent field in both the edit dialog and
the switch-away backfill, keeping the stored auth template. Live still
wins whenever it carries material, and official providers keep the live
login as their source of truth.
- Gate the official exemption on is_codex_official_provider /
resolveCodexOfficialIdentity rather than the category alone: a legacy
fixed official card without a persisted category is still written as
official, so the fallback must not resurrect a logged-out login on
switch-back.
- Cover both layers with regression tests, including the category-less
official shapes.
Fixes #7433
Supersedes #7492
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): lift additional_tools input carriers on the Codex chat conversion (#7454)
Codex talks to third-party providers over its Responses Lite transport,
which ships the request's extra tools in an `additional_tools` input
carrier: `{"type":"additional_tools","role":"developer","tools":[...]}`,
a role but no `content`. The Responses->Chat conversion let that carrier
fall through the generic "item with a role is a message" arm and emitted
`{"role":"system","content":null}` at messages[1], which strict Chat
gateways reject with `messages[1]: missing field "content"` (or
`data did not match any variant of ...SystemMessageContent`). The tools
carried inside it were also dropped on the Chat and Anthropic conversions.
- Skip `additional_tools` items in the Chat message walk; a carrier
declares tools, it is not a message.
- Collect carrier tools through the existing CodexToolContext registry
(dedup, namespace flattening, custom-tool conversion), which also feeds
the Anthropic conversion and the response-side name round-trip.
- Requests without a carrier convert byte-for-byte as before.
Known boundary: `custom` children inside a namespace tool
(`functions.exec`) are still not lifted; that needs its own response-side
name round-trip and is left for a follow-up.
Diagnosed independently in #6158 by BigStrongSun (fix in #6159) and in
fixture.
Fixes #7451
Fixes #6158
Refs #6033
Refs #6776
Supersedes #6159
fix(proxy): normalize Codex detail:original images for Chat gateways (#7476)
Codex marks tool-returned images with detail:"original" whenever the model
catalog advertises supports_image_detail_original, a Responses-only value.
The Responses->Chat conversion copied it verbatim into image_url.detail, and
strict OpenAI-compatible Chat gateways (Ark/Doubao, SenseNova, GLM, vLLM, ...)
reject the whole request with 400. Because Codex replays the full rollout on
every turn, one poisoned view_image result made the session fail permanently,
including plain-text turns.
Two layers:
- tool_media::merge_top_level_detail downgrades original -> auto, so already
poisoned rollouts and Codex versions before 0.155.0 (which do not normalize
replayed history themselves) recover immediately. auto/low/high are untouched.
- codex_catalog_model_entry always writes supports_image_detail_original:false
on the ProxyChat profile, so Codex stops emitting original at the source once
the catalog is regenerated. NativeResponses/Anthropic profiles and the DeepSeek
official catalog are unaffected.
Top-level user images were never affected: responses_content_to_chat_content
drops the part-level detail entirely.
Fixes #6495
Fixes #7458
Refs #6569
Refs #5805
fix(omo): detect unified config on the WSL side when the OpenCode dir is in WSL (#7550)
When the OpenCode config directory points into WSL
(\\wsl.localhost\<distro>\home\<user>\.config\opencode), OMO >= 4.19.3 keeps
its unified config in the WSL-side home (~/.omo/omo.jsonc). Detection only
probed the Windows home, never found it, and silently fell back to the legacy
per-plugin file that OMO no longer reads, so provider switches looked
successful but had no effect.
- Add derive_wsl_home_dir to derive the WSL-side home from a WSL UNC path
(home/<user> or root); non-WSL and non-home paths return None.
- Probe a list of home candidates for the unified config, WSL-side home
first and the OS home as a fallback, shared by the read, write and delete
paths.
- Detection only: when no unified config exists on either side the legacy
fallback is unchanged.
Fixes #7363
Supersedes #7367
docs(partner): add a FluxA purchase link and tighten its sponsor blurb
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.
fix(claude-desktop): expose the 1M context variant for DeepSeek V4 routes
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.
feat(presets): add FluxA Token Plan partner presets
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.
fix(presets): correct AICodeWith Codex-family endpoint to /v1
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.
fix(ui): render monochrome preset icons in the foreground color
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.
docs(user-manual): point Chat-only Codex examples at plan endpoints
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.
fix(pricing): restore DeepSeek V4 Pro price and seed Qwen3.8 and Hy4 models
- 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).
fix(presets): move MiniMax CN and BaiLing to their current official endpoints
- 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.
feat(codex): refresh CN presets from the Responses API audit
- 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.
test(claude-desktop): run Linux path tests on unix hosts and cover Linux in provider sync tests
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.
fix(ui): stop expanding stale cached usage tiers on ineligible provider cards
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.
fix(skills): 支持 skillId 与目录名不一致的技能安装和更新 (#6381)
* fix(skills): resolve mismatched skillId directories
* fix(skills): preserve source path during updates
* fix(skills): refresh source URL after repository relocation
* test(skills): isolate relocation fixture from legacy home
feat(opencode): 支持从获取的模型列表中搜索并批量添加所选模型 (#7515)
* feat: implement fetched models selection and UI components
* fix(opencode): prevent model search from submitting provider form
Add Kimi Global preset variants (kimi.ai platform + api endpoints) (#7526)
Add a "Kimi Global" twin immediately after the domestic "Kimi" API preset and a "Kimi For Coding Global" twin after the domestic "Kimi For Coding" preset, in every preset file that ships them (claude, claude-desktop, codex, hermes, openclaw, opencode, pi).
"Kimi Global" points to the international open platform https://platform.kimi.ai?aff=cc-switch (apiKeyUrl mirrored as https://platform.kimi.ai/console/api-keys?aff=cc-switch) and swaps the endpoint host api.moonshot.cn for api.moonshot.ai with each file's path unchanged (/v1 or /anthropic).
"Kimi For Coding Global" points to the international site https://www.kimi.ai/code/?aff=cc-switch and swaps the endpoint host api.kimi.com for api.kimi.ai with the path unchanged (e.g. https://api.kimi.ai/coding/v1).
Domestic presets are unchanged; category, icons, apiFormat, model catalogs, endpointCandidates (host swapped), partnerPromotionKey and settings otherwise mirror the domestic blocks exactly.
primePartner stays only on the domestic presets so the new variants do not alter prime-partner ordering.
The Pi twins get distinct providerKey values ("cc-switch-kimi-global", "cc-switch-kimi-for-coding-global") because providerKey is the preset-level primary key and must not collide with the domestic presets.
Rebalance sponsor CTAs around Kimi Code plan with dual-region links (#7522)
Kimi Code plan purchases have resumed, so the README sponsor sections go back to leading with the coding plan instead of the API-only flow written during the purchase pause.
All four locales (EN/ZH/JA/DE) now present Kimi Code plan (中文站 | Global) as the primary CTA and the Kimi Open Platform API (中文站 | Global, with the first-top-up bonus) as the secondary one, mirroring the domestic|global split already used for API links.
The aff and track_id values are unchanged.
fix(prompts): refresh active prompts after external file edits (#7194)
Reload the enabled prompt from its live file when the Prompts list loads, and refresh the open panel when the window regains focus, so external edits to CLAUDE.md / AGENTS.md show up without a restart.
- Keep saved templates available when the live file cannot be read.
- Skip the backfill for empty or whitespace-only files, matching enable_prompt.
- Skip the backfill while a sync/restore holds the sync lock, so a list load cannot write a stale live file into a freshly restored database.
fix(skills): raise archive entry limit so large skill repos install (#7489)
Installing hugohe3/ppt-master from skills.sh failed with
ARCHIVE_TOO_MANY_ENTRIES: the GitHub archive we download is the whole
repository (13,248 entries), and the 10,000-entry cap added in #5811
treated it as a zip bomb.
- Raise MAX_ARCHIVE_ENTRIES to 30,000 for both the remote archive and
local ZIP paths, and align WebDAV's MAX_EXTRACT_ENTRIES so a skills.zip
containing such a skill still restores.
- Charge every file entry at least one 4 KiB block against the existing
512 MiB budget, mirroring directory accounting, so empty-file archives
are bounded by the byte budget rather than by the entry count alone.
- Add tests for a 13k-entry repo archive and for the per-file minimum
charge.
Fixes: https://github.com/farion1231/cc-switch/issues/7475
feat: add MiniMax Code harness support (#7383)
Add MiniMax Code (mcode) as a managed application: provider and model management through the native custom_provider configuration, MCP servers, Skills, global instructions (AGENTS.md), read-only local session history with resume, and usage import from the native runtime database. Native files shared by the TUI and desktop are written under the native lock with rollback when the database commit fails, unrelated native configuration is preserved, and the data directory honors MINIMAX_DATA_DIR and MAVIS_DATA_DIR. Deleting sessions stays in the native application because they are coupled to runtime state.
fix(codex): recover stale account bindings during takeover (#7395)
After deleting a managed ChatGPT account and logging in again, a Codex provider can still reference the old local account ID, which made route switching, takeover, startup restoration and rebinding fail permanently.
Distinguish a missing outgoing account from an existing account with no matching live token. A missing account only releases its ownership marker after the persisted account store is verified; the target write policy stays in force. A stale target now reports an account-selection instruction instead of a bare not-found error, including when takeover is already enabled.
Fixes #7392
feat(claude-desktop): support 3P configuration on Linux (#7331)
Add Claude Desktop 3P configuration support on Linux. Resolve the config root from an absolute XDG_CONFIG_HOME, falling back to ~/.config; when CC Switch itself runs as a Flatpak, use the host ~/.config instead of the sandbox-private XDG_CONFIG_HOME. Update the en/zh/ja user manuals with Linux paths and the Flatpak boundary.
Fixes #4855
fix(proxy): omit missing tool descriptions instead of serializing null (#7319)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): keep reasoning effort for grok-4.6 and pass xhigh through verbatim (#7318)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(models): support Zhipu OpenAI Responses model lists (#7330)
Zhipu's Responses endpoint (open.bigmodel.cn/api/v1) serves its model list in the Codex remote catalog shape (models[].slug) instead of the OpenAI-compatible data[].id shape, so the Codex form's model fetch came back empty. The parser now falls back to models[].slug when data is absent.
fix(misc): fetch npm dist-tags from the dedicated endpoint with the probe timeout (#7346)
The tool version probe requested the full npm packument for each package (metadata for every historical version, 14-25 MB uncompressed for codex / opencode / openclaw) only to read its dist-tags, and it was the only latest-version probe without LATEST_PROBE_TIMEOUT, so it fell back to the proxy client's 600 s total timeout and the cards sat on "loading" for minutes on slow links while the large downloads starved the Hermes GitHub probe into "unknown".
Switch fetch_npm_dist_tags to registry.npmjs.org/-/package/{package}/dist-tags, whose body is the dist-tags object itself (tens of bytes to a few KB), escape scoped names as %2f, and apply the same 15 s probe timeout as the GitHub and PyPI probes. No data is lost: the only consumer reads latest and the tool's prerelease tags.
Fixes #7339
feat(usage): display output tokens per second in request log table and detail panel (#3369)
Show tokens/s next to the output token count in the request log table and the request detail panel.
The generation window prefers an explicit duration when present, otherwise latency minus first-token time for streaming requests, and falls back to full latency for non-streaming requests. Rows without a positive token count or window show nothing.
Closes #5936
fix(presets): update APIKEY.FUN URLs to apikey.fan
docs(release): add v3.20.3 release notes
chore(release): v3.20.3
fix(pricing): move retired DeepSeek V4 family to the V4.1 Flash tier
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.
fix(presets): declare third-party Token Plan DeepSeek V4 rows text-only
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.
feat(presets): rename DouBaoSeed preset to localized Volcengine Doubao
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.
fix(codex): skip unchanged incomplete rollout tails
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.
fix(claude): align quick model selection with form order
fix(tray): show bound ChatGPT account quota for managed Codex cards
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
feat(claude): add "Disable Artifact Tool" quick toggle to provider editor
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.
feat(codex): refresh aggregator model presets and catalogs
feat(codex): switch Kimi presets to native Responses direct-connect
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.
fix(usage): show Claude Fable weekly limit from the usage API limits array
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.
fix(codex): mirror vision-capable DeepSeek catalog and un-gate the legacy v4-flash alias (#7286)
Refresh the bundled DeepSeek official Codex catalog to the vendor's current models.json (slug deepseek-flash, image input modality, supports_image_detail_original) and remove deepseek-v4-flash from the confirmed text-only registry: the legacy id is still accepted by DeepSeek and routed to the vision-capable V4.1 Flash, so both the proxy media sanitizer and the generated Codex catalog must fail open for it. deepseek-v4-pro stays text-only per the vendor catalog.
Fixes #7283
fix(proxy): clamp sub-floor max_tokens to the Responses API minimum (#7287)
The Responses API rejects max_output_tokens below 16, but Anthropic clients legitimately send tiny probe budgets (Claude Desktop's model-availability probe uses max_tokens=1). The Anthropic→Responses conversion copied the value verbatim, so strict upstreams returned 400 and Claude Desktop reported the mapped model as unavailable under local routing.
Clamp 1..=15 up to 16 during conversion; 16+, 0 and non-integer values keep their pass-through semantics. Normal session requests convert byte-for-byte as before, and the Codex OAuth path still strips max_output_tokens afterwards.
Fixes #7103
fix(codex): coalesce adjacent commentary with pending tool calls (#7280)
The Codex Responses -> Chat Completions converter emitted two consecutive
assistant messages when one Responses model turn contained a commentary
message directly followed by function_call items. Chat upstreams imitate
the text-only assistant message as a complete turn and return
finish_reason=stop before the expected tool call, so long agent tasks end
after a progress update.
Merge pending tool calls into the directly adjacent assistant message when
it carries no tool_calls yet, attach pending reasoning per segment so
parallel calls do not duplicate it, and keep the new-message path for every
other boundary shape (user/tool boundaries, prior tool-call batches, media
flushes). The reasoning placeholder backfill still applies to coalesced
calls without reasoning.
Fixes #6529
Supersedes #6530
Co-authored-by: BigStrongSun <BigStrongSun@users.noreply.github.com>
fix(proxy): skip empty reasoning_content placeholders in Anthropic SSE streaming (#7227)
Some OpenAI-compatible upstreams (GLM, Qwen, DeepSeek-V4-Pro via ModelScope/DashScope,
Bailian, JD Cloud, ...) keep a `reasoning_content: ""` placeholder in every content
chunk instead of omitting the field. The reasoning branch of the OpenAI Chat -> Anthropic
SSE converter only checked for presence, and it shares `current_non_tool_block_type`
with the content branch, so every content chunk closed the open text block, opened an
empty thinking block, then reopened a text block. N content chunks became 2N content
blocks, half of them empty thinking blocks, which Claude Code rendered as one line per
token with dozens of empty Thought blocks and bloated the session JSONL.
Filter empty reasoning before entering the branch, matching the `!content.is_empty()`
guard the content branch already had and the non-streaming `openai_to_anthropic`
converter. Two regression tests pin block index, type and order: the placeholder shape
must yield a single text block and no thinking_delta at all, and real reasoning must
still yield one thinking block followed by one text block.
Fixes #5028
Fixes #4404
fix(codex): detect growing rollouts with a persisted byte cursor (#7219)
Codex keeps the rollout append handle open for the whole session, so on Windows NTFS the file mtime does not advance while the file grows and the pure-mtime gate skipped growing rollouts forever. Persist the observed byte length in session_log_sync.last_byte_offset for Codex rows and skip a file only when both mtime and size are unchanged. The parser now splits records on newlines manually so an incomplete trailing record is retried on the next pass without advancing the line cursor.
Fixes #6060
fix(codex): proxy image edits and Images API follow-ups (#7177)
* fix(codex): proxy image edits and Images API follow-ups
Follow-ups to #7036 for Codex's built-in ImageGen over the local proxy:
- Register `/images/edits` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them through the shared Codex standalone passthrough.
ImageGen posts to `images/edits` whenever a call references existing
images, and the router returned an empty 404 for it.
- Add `CodexStandaloneEndpoint::ImagesEdits`. Both Images endpoints share
one full-URL suffix table, so a complete Responses / Compact / Chat
Completions / Images URL derives the sibling endpoint (and vice versa)
while opaque full URLs keep failing closed.
- Apply the standalone sibling rewrite when the base URL is pasted as a
complete endpoint with the full-URL switch off, mirroring
`codex_chat_base_is_full_endpoint`; standalone requests no longer become
`.../chat/completions/images/generations`.
- Parse Images API streaming usage: `image_generation.completed` carries
`usage` on the event top level, so scan for `usage.input_tokens` before
falling back to the Chat Completions stream parser.
Related #7036
* fix(codex): match standalone URL suffixes case-insensitively
Keep endpoint detection and sibling rewriting consistent while preserving the original URL prefix and query. Cover mixed-case Images and Alpha Search endpoints through URL and proxy regression cases.
---------
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): preserve per-app settings on shutdown and port allocation (#7210)
On normal exit, stop_with_restore_keep_state() called the legacy global
proxy-config writer to clear the long-retired live_takeover_active flag.
That writer reads the Claude row and runs an UPDATE without a WHERE clause,
so every shutdown copied Claude's max_retries and the three timeout fields
onto the Codex, Gemini and Grok Build rows — and the UPDATE never carried
live_takeover_active in the first place, so the step had no effect beyond
the clobber. The ephemeral listen-port path did the same on every proxy
start.
Drop the dead write-back (live-config restore, backup cleanup and health
reset are unchanged) and persist the resolved ephemeral port through the
global-config interface, which only touches the shared listen/logging
columns. Two regression tests seed distinct per-app settings for all four
apps and assert they survive both paths.
Fixes #7204
fix(provider): preserve child metadata during universal sync (#7212)
Syncing a universal provider regenerates its Claude/Codex/Gemini child
providers. The child's `settings_config` was merged with the existing row,
but `meta`, `created_at` and `sort_index` were taken from the generated
object, and the universal provider never carries any of them from the UI.
The UPDATE therefore wiped the child's usage script, common-config opt-out,
endpoint auto-select and other per-app settings to `{}` and reset the sort
index to NULL, which pushed the card to the bottom of the list and looked
like the provider had been deleted and re-created.
Keep the existing child's `meta`, `created_at` and `sort_index` when the
row already exists. Name, base URL, key, models, website and notes are
still driven by the universal provider, and first-time creation still
inherits the parent's metadata.
Fixes #7134
feat(presets): rebrand DashScope as 千问AI平台 and refresh Qwen models (#7183)
Rename the domestic Bailian presets to 千问AI平台 with a dedicated icon, refresh the Qwen family to the 3.8 generation across Claude Code, Claude Desktop, Codex, Hermes, OpenClaw, OpenCode and Pi, add the domestic Token Plan presets, restore the international QwenCloud Coding Plan presets, and align OpenClaw/OpenCode/Pi model metadata with the official client docs.
fix(codex): honor proxy URL when model_provider is omitted (#7263)
When a Codex provider card's TOML has no `model_provider`, Codex falls back to
its built-in `openai` provider, but takeover wrote the proxy address as a
top-level `base_url` that Codex never reads, so requests bypassed the local
proxy and went straight to api.openai.com.
Treat the missing selector as the built-in `openai` provider and write the
proxy address to `openai_base_url`; the existing legacy-reroute migration then
turns it into a `cc-switch` custom provider table carrying the `PROXY_MANAGED`
bearer, the same shape every other third-party takeover uses. Explicit
provider selections are unchanged.
Refs #6256, #7217
fix(provider): update MiniMax defaults to M3 and remove expired offers (#7255)
* fix(provider): refresh MiniMax M3 presets and remove expired promotions
* fix(provider): complete MiniMax M3 capability declarations
---------
Co-authored-by: saladday <1203511142@qq.com>
chore(provider): remove Atlas Cloud sponsor status, keep presets
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`
docs(release): add v3.20.2 release notes
chore(release): v3.20.2
feat(provider): add SoleAPI sponsor presets across all nine apps
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.
fix(codex): proxy image edits endpoint
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.
fix(provider): pin models URL for JieKou and Novita Claude presets
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.
feat(pricing): refresh seed for September 2026 vendor price changes
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.
fix(codex): stamp requires_openai_auth on takeover writes to match the live login state
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.
fix(updater): show the real reason when an update check fails (#6482)
The updater plugin rejects with a plain string, so `err instanceof Error`
never matched and the real failure reason was replaced by the generic
fallback message. Use the existing extractErrorMessage helper, and pass
the recovered message into the check-update failure toast so it actually
reaches the user instead of stopping at an unread context state.
Co-authored-by: Jason <farion1231@gmail.com>
docs: fix the locales path in the README directory trees (#6100)
All four READMEs showed locales/ as a direct child of src/, but the
translations live at src/i18n/locales/. Split it into two tree nodes so it
matches how the rest of the tree is drawn. Also fixes a CLaude -> Claude
typo in README_ZH.md.
Co-authored-by: Jason <farion1231@gmail.com>
fix(claude): exclude workflow journal.jsonl from session list
Claude Code's workflow feature writes subagents/workflows/wf_*/journal.jsonl inside the session directory. The Claude session scanner only excluded agent-* files, so each journal parsed as an empty ghost session titled "journal". Exclude journal.jsonl by file name and add regression tests.
Fixes #6042
fix(i18n): add the missing pi.form.providerKeyDuplicate and common.collapse keys
translatePiProviderMutationError returns pi.form.providerKeyDuplicate for the Pi backend's duplicate-key error, but the key existed in none of the four locale files, so the toast showed the raw key. common.collapse (aria-label of the thinking-map toggle) was missing the same way. Add both keys to zh/en/ja/zh-TW and lock them in the locale coverage test.
fix(proxy): stop mask_url panicking on multi-byte UTF-8 near the truncation boundary
mask_url's fallback branch (used when the proxy URL fails Url::parse) sliced the raw string at byte 20 and panicked when a multi-byte UTF-8 character straddled that offset, e.g. a CJK string pasted into the global proxy address. Snap the cut to the nearest char boundary at or before 20 and add a regression test.
fix(tests): isolate LOCALAPPDATA so tests stop writing the real Claude Desktop config
On Windows, Claude Desktop config paths resolve from LOCALAPPDATA, which the test home override in tests/support.rs did not redirect, so profile_roundtrip wrote the https://desktop.test fixture into the developer's real Claude Desktop config. Point LOCALAPPDATA at the isolated test home alongside HOME and USERPROFILE.
Fixes #6077
fix(usage): widen pricing model source select to fit localized labels
The pricing model source SelectTrigger in the usage cost settings table was w-28, which leaves about 70px for the label; the English and Japanese labels need about 107px and overflowed the trigger. Widen it to w-40 and drop the h-7 overrides so the trigger and the adjacent Input use the shared h-9 height.
feat(pi): add Tencent TokenHub and Token Plan presets (#7159)
Add eight Tencent presets for Pi, covering the TokenHub pay-as-you-go line
(domestic and international) and the six Token Plan subscription products —
Pi was the only managed app still missing them. Model IDs, ordering and
endpoints follow Tencent's own model lists verbatim, and the tests assert each
product's line-up in order so future drift is caught.
Declare thinking control alongside the capabilities rather than leaving it to
defaults: the 31 DeepSeek entries carry a Tencent-specific compat so selecting
"off" actually disables thinking instead of silently billing reasoning tokens,
the eight Kimi K2.7 Code entries no longer offer an "off" level the model
cannot honour, and kimi-k2.7-code-highspeed is marked image-capable so
tool-returned screenshots are no longer dropped.
Drop minimax-m2.5 from every app: it is gone from Tencent's model tables and
the platform is retiring it.
fix(codex): proxy image generations endpoint (#7036)
Codex's built-in ImageGen calls the legacy OpenAI Images API, but the local
proxy only registered Responses / Chat Completions / Compact / Alpha Search
routes, so `/v1/images/generations` returned an empty 404 under Codex routing.
- Register `/images/generations` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them as a Codex standalone passthrough shared with Alpha Search.
- Generalize the Alpha Search full-URL rewrite into
`rewrite_codex_standalone_full_url`: derive the sibling
`/images/generations` from full Responses / Compact / Chat Completions URLs,
keep an existing Images URL as-is, and fail closed on opaque full URLs.
- Record `usage.input_tokens` / `output_tokens` through the Codex usage parser.
Fixes #5429
Fixes #6745
feat(pricing): add Gemini 3.8 Flash pricing (#7164)
Seeds the built-in pricing row for Gemini 3.8 Flash at 0.75 / 3.75 input/output per million tokens, 0.075 cache read, no cache-write charge, plus a regression test locking all four fields.
Values verified against Google's own pricing page (introductory price through 2026-12-31; list price 1.50 / 7.50 / 0.15 from 2027-01-01) and models.dev.
feat(provider): extend PPIO presets for Pi (#6870)
Add a PPIO provider preset for Pi using the OpenAI-compatible endpoint,
and pin an explicit models URL on the Claude Code PPIO preset so model
discovery resolves against https://api.ppio.com/openai/v1/models instead
of the derived candidates, which all return 404.
Co-authored-by: Jason <farion1231@gmail.com>
feat(pricing): add GLM-5.3 Flash pricing (#7163)
fix(proxy): enable parallel tool calls for Codex OAuth (#7024)
Codex OAuth requests defaulted parallel_tool_calls to false when the incoming
Anthropic request carried no explicit value, forcing Claude Code into one tool
call per model turn. Default it to true (matching codex-rs), map Anthropic
tool_choice.disable_parallel_tool_use to the inverse Responses value so explicit
serial execution is preserved, and add regression coverage for both directions.
Carries #5722 forward on top of current main.
Fixes #5719
Co-authored-by: 太一 <taiyi@douyu.tv>
feat(pricing): add GPT-6 Astra pricing (#7162)
fix(codex): resolve input_modalities for unknown models in vendor catalog (#6750)
When a model slug is not in the bundled DeepSeek official catalog, the vendor path clones the flagship entry and previously inherited its text-only input_modalities. Resolve modalities for unmatched slugs through the same registry/fail-open logic the non-vendor path uses, so a vision variant such as deepseek-v4-flash-vision-exp is no longer declared text-only. Explicit user inputModalities still win; matched models keep the vendor's declaration verbatim.
Fixes #6725
fix(claude): expose Opus 5 and Sonnet 5 in takeover mode (#5882)
Bump the stable client-facing role aliases that takeover mode writes to
Claude Code: Opus claude-opus-4-8 -> claude-opus-5 and Sonnet
claude-sonnet-4-6 -> claude-sonnet-5, so the /model menu matches the
Claude Desktop default routes and preset defaults that already moved to
Opus 5 / Sonnet 5. Add "opus-5" to the adaptive-thinking classifier so
the Bedrock thinking optimizer, which runs on the client alias before
model mapping, keeps sending adaptive thinking instead of the removed
budget_tokens.
Routing is unaffected: the proxy maps client aliases by role keyword, and
the aliases are rewritten automatically the next time the proxy starts.
Fixes #5876
Co-authored-by: Jason <farion1231@gmail.com>
feat(pricing): add GLM-5.3 to built-in model pricing table (#6591)
Seed glm-5.3 at $1.4/$4.4 per MTok with $0.26 cache read, matching the
official Z.ai list price (identical to the GLM-5.2 tier):
https://docs.z.ai/guides/overview/pricing
New model id, so seed only; no repair entry and no SCHEMA_VERSION bump.
Existing installs pick the row up on next launch via INSERT OR IGNORE, and
the startup cost backfill re-prices historical glm-5.3 rows recorded at $0.
fix(codex): align OAuth client identity for GPT-6 (#7132)
Requests for gpt-6-astra through the Claude-to-Codex OAuth takeover route advertised Codex 0.144.1, below the model's 0.153.0 minimal_client_version, so ChatGPT's backend rejected them with HTTP 400 "requires a newer version of Codex". Bump the advertised client version to 0.153.4 and share the originator/version constants between generation and model discovery, which previously sent cc-switch's own package version and originator.
Two regression tests pin the generation headers and the model-list request to a version at or above the gpt-6-astra floor.
Refs #7129
fix(codex): disable supports_search_tool for DeepSeek catalog models (#6653)
DeepSeek's Responses API has no tool_search support, but the bundled
official catalog declared supports_search_tool=true for deepseek-v4-pro
and deepseek-v4-flash. Codex gates deferred/namespaced MCP tools on
this flag (search_tool_enabled = supports_search_tool &&
namespace_tools), so MCP tools were hidden behind a tool_search the
gateway cannot serve — no MCP tool could ever be called.
Set the flag to false for both models so Codex lists MCP tools inline.
Hosted web_search is unaffected (gated by provider capabilit…
Frontend CI format:check failed on 6 files modified by the app-name
and model-quick-switch archive commits plus the updater-disable commit.
Run prettier --write so format:check passes again.
chore: archive ccs-fork-app-name-cc-switch
chore: archive ccs-model-quick-switch-ui-polish
docs: rewrite README to document fork differences vs upstream
README 从上游完整功能介绍改为仅记录 fork 与上游的差别,完整功能
文档指回上游。涵盖 8 项 fork 魔改:构建标识/开发预览、预设精简、
Claude 表单调整、模型搜索移植、主页模型快速切换、侧边栏面板显隐、
关闭应用内更新、打包范围,并说明版本号与 rebase 同步流程。
feat: disable in-app updater, point users to GitHub releases
移除 tauri-plugin-updater 依赖与自动更新链路,改为统一引导用户到
GitHub Releases 手动下载新版。
- 前端:updater.ts 恒返回 up-to-date;UpdateContext 取消启动自检;
AboutSection「检查更新」直接打开 fork releases 页;DatabaseUpgrade
恢复界面去掉下载进度条,改为打开发布页。
- 后端:移除 updater 插件注册与 UpdaterExt 调用;
install_update_and_restart 降级为空壳,check_app_update_available
返回当前版本号;check_for_updates 指向 fork releases;capabilities
去掉 updater:default 权限。
- 打包:tauri.conf.json 删 plugins.updater、createUpdaterArtifacts=false;
Cargo.toml / package.json 删 updater 依赖,Cargo.lock / pnpm-lock
同步重生成。
- CI:release.yml 移除签名收集与 assemble-latest-json job,删除
.tar.gz/.sig 处理,release body 去掉 updater 说明。
版本号统一为 3.20.0-1(合法 semver 且 MSI 第 4 位为 1)。
Cargo.lock / pnpm-lock.yaml 已随依赖移除重生成,cargo check 通过。
ci: build only Windows x64 and macOS arm64, unsigned
feat: gate dev panel behind dev-only env flag
chore(comet): archive ccs-sidebar-panels-visibility change
fix: apply skills visibility guard to hermes branch toolbar button
feat: include prompts in sidebar panel visibility settings
fix: persist visibleSidebarPanels setting in backend AppSettings
style: use compact pill toggles for sidebar panel visibility
feat: add sidebar panel visibility switches to settings
feat: add sidebar panel visibility settings and wire into toolbar buttons
chore(comet): archive ccs-home-model-display change
fix(fork): read grokbuild api key from config TOML in quick switch dialog
feat(fork): align model badge with upstream extractModelBadge
feat(fork): show current model badge and quick-switch entry on provider cards
feat(fork): add model quick switch dialog
feat(fork): add provider model read/write utils with tests
chore(comet): archive ccs-model-search-port change
feat(i18n): add model search picker keys for all locales
feat(form): port SearchableModelPicker from upstream for searchable model selection
chore(comet): archive ccs-claude-form-layout change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-claude-form-layout/
- Sync claude-form-layout spec to main specs
- Annotate design doc as archived
- Add verification report
fix(i18n): update advanced options hint, add ja/zh-TW toggle-all, drop dead apiFormatHint key
fix(form): correct 1M toggle-all alignment direction (ml-1)
fix(form): align 1M toggle-all checkbox with row checkboxes
refactor(form): move api format select into EndpointField rightSlot
fix(form): widen api format select to fit option text
feat(form): add 1M toggle-all checkbox in model mapping header
feat(form): move api format select next to endpoint input
chore(comet): archive ccs-provider-presets-trim change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-provider-presets-trim/
- Sync provider-presets-visibility spec to main specs
- Annotate design doc as archived
- Add verification report
docs(fork): add presets-trim files to rebase whitelist
test(fork): mock IS_FORK_BUILD false in unit tests for upstream-form semantics
feat(fork): wire official preset filter into provider forms
feat(fork): add hidden field to preset interfaces
feat(fork): add official preset allowlist and filter util
chore(comet): archive ccs-fork-scaffolding change
- Move change artifacts to docs/openspec/changes/archive/2026-08-18-ccs-fork-scaffolding/
- Sync fork-build-identity spec to main specs
- Annotate design doc as archived
- Add verification report
feat(fork): add dev config with isolated identifier and config dir
docs(fork): complete rebase whitelist with all fork-touched files
fix(fork): guard setTitle with isTauri in test/non-tauri env
fix(fork): define __CCS_FORK_BUILD__ in vitest config
feat(fork): add DevPanel and settings footer entry
feat(fork): set macOS window title to CC Switch (Fork) on startup
feat(fork): inject __CCS_FORK_BUILD__ compile-time constant
feat(fork): bump version to 3.19.2-fork.1 and rename product to CC Switch (Fork)
docs(fork): add upstream rebase workflow and ignore tool artifacts
fix(proxy): accept the whole grok-4.x (x>=5) family in the reasoning-effort whitelist (#7369)
Replace the verbatim grok-4.5 / grok-4.6 entries in supports_reasoning_effort with a rule that parses the grok-4.x minor version and accepts x >= 5, mirroring the existing GPT-5+ rule. This covers grok-4.7 (released 2026-09-21), whose reasoning effort was previously dropped on the Claude -> Chat, Claude -> Responses and Codex Responses -> Chat conversion paths, and lets future releases pass without another whitelist edit. The grok-build-* family is retained for saved providers.
Co-authored-by: allenxu09 <171831965+allenxu09@users.noreply.github.com>
docs(release): add v3.20.4 release notes
chore(release): v3.20.4
feat(pricing): add Grok 4.7 and GLM-5.3-FlashX
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.
fix(i18n): remove outdated OpenCode Go referral offer
fix: add SudoCode.chat alternate API endpoint
feat(settings): add a GitHub star prompt to the About card
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
feat(presets): add Soshow aggregator presets
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.
fix(ci): stabilize frontend tests and verify renderer build
fix(mcode): refresh provider state after removal (#7578)
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
fix(lib): reset skip_taskbar before window.show() across entry points (#6348)
5处 window.show() 调用中 4处没有在 show() 前重置 Windows 的 skip_taskbar,导致以下场景出现窗口可见但不在任务栏、点最小化后窗口消失的 bug:
- single_instance handler (lib.rs:360-361):当原进程因 silent_startup / 关闭到托盘等处于 skip_taskbar(true) 状态时,从快捷方式/开始菜单唤起已运行实例触发。
- handle_deeplink_url 的 focus_main_window 分支 (lib.rs:256):通过 ccswitch:// URL 唤起时。
- DB 恢复流程 (lib.rs:556):数据库版本不匹配时显示恢复界面。
- RunEvent::Opened 处理器 (lib.rs:1803):系统接收到 deep-link URL 时。
修复后的行为与 tray.rs::handle_tray_menu_event::show_main 分支一致(那里已有相同的 set_skip_taskbar(false) 调用)。
经用户测试验证:autostart + silent_startup + 从开始菜单/快捷方式启动的场景下,最小化按钮恢复正常工作。
fix(proxy): omit missing tool descriptions in the Codex responses-to-chat converter (#7378)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): preserve max effort for GPT-5.6 and GPT-6 Astra (#7531)
fix(misc): ignore shell startup output when probing tool versions in WSL (#7348)
try_get_version_wsl runs "${SHELL:-sh}" -lic '<tool> --version' through
wsl.exe and takes the first x.y.z anywhere in stdout. A login+interactive
shell runs the distro's startup files first, so their output shares that
stdout. On Ubuntu, /etc/profile.d/update-motd.sh prints the MOTD once a day,
starting with "Welcome to Ubuntu 24.04.4 LTS", and the card then showed
24.04.4 as the Claude Code version. The once-a-day stamp makes it appear
after the first probe of the day and vanish on refresh.
The payload now prints a sentinel before running the tool, and parsing looks
only at what follows its last occurrence. The last occurrence handles the
-lic || -lc || -c fallback chain printing it several times; matching it as a
substring handles OSC sequences glued in front of it. If the sentinel is
absent (the shell failed before the payload ran), the output is used as
before. The stderr fallback for tools that print their version there is kept.
Fixes #7347
Co-authored-by: lyfuci <lyfuci11@gmail.com>
fix(claude): hide session URL attribution (#7053)
* fix(claude): hide session URL attribution
* docs(claude): document session URL attribution
fix(ui): reset provider scroll when switching apps (#5211)
fix(pi): fix provider logo (#6826)
fix(i18n): use the Pi form's own term for the zh-TW duplicate-key message (#7187)
Follow-up to the nit on #6768. In zh-TW the Pi form labels the field
供應商識別碼 and says 供應商識別碼為必填 when it is empty, but the
duplicate message I added said 供應商標識 — the term Hermes, OpenCode and
OpenClaw use for their own field. One form, two names for the same thing.
The other three locales already keep the label and both messages on one
term, so this makes zh-TW match the pattern rather than introduce one:
en Provider key / Provider key is required / This provider key already exists
ja プロバイダーキー / プロバイダーキーを入力してください / このプロバイダーキーは既に存在します
zh 供应商标识 / 供应商标识不能为空 / 该供应商标识已存在
zh-TW 供應商識別碼 / 供應商識別碼為必填 / 該供應商標識已存在 <- the odd one out
hermes.form.providerKeyDuplicate carries the same sentence and is left
alone: its own label is 供應商標識, so it is already consistent.
Verified: managementListLocales 7/7. Full unit suite 1085/1087, the same
two App.test.tsx integration cases failing before and after.
fix(proxy): strip stop param for GitHub Copilot to unblock auto mode classifier (#5404)
Claude Code's auto mode classifier sends `stop_sequences` on every
safety check. On the GitHub Copilot path the Anthropic -> OpenAI Chat
conversion mapped it to `stop`, and Copilot's Chat Completions route
for Claude models answers that request with HTTP 400, so every
classifier call failed while normal chat kept working.
- Remove the converted `stop` field only when the provider is GitHub
Copilot; other OpenAI-compatible upstreams keep it.
- Copilot detection in the transform now also covers providers that
are recognised only through `env.ANTHROPIC_BASE_URL`.
- Regression tests for the Copilot (meta and env URL) and generic
provider cases.
Fixes #5175
fix(codex): keep stored auth when the live snapshot has no credential (#7434)
Editing the current Codex provider seeds the form from the live ~/.codex
snapshot, and switching away backfills that snapshot into the DB row. When a
provider table declares its own credential source (an Authorization header,
env_key, auth/aws), no bearer token is injected into config.toml and the
shared auth.json is deleted on third-party switches, so the live snapshot is
permanently `{ auth: {}, config }` while auth.OPENAI_API_KEY in the DB is the
only copy of the key. Both paths treated that empty live auth as
authoritative and silently erased the stored key, which in turn made the
usage-script modal misread the provider as an official subscription and
reset the saved script.
- Treat a credential-less live auth (missing auth.json or the bare
auth_mode logout marker) as an absent field in both the edit dialog and
the switch-away backfill, keeping the stored auth template. Live still
wins whenever it carries material, and official providers keep the live
login as their source of truth.
- Gate the official exemption on is_codex_official_provider /
resolveCodexOfficialIdentity rather than the category alone: a legacy
fixed official card without a persisted category is still written as
official, so the fallback must not resurrect a logged-out login on
switch-back.
- Cover both layers with regression tests, including the category-less
official shapes.
Fixes #7433
Supersedes #7492
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): lift additional_tools input carriers on the Codex chat conversion (#7454)
Codex talks to third-party providers over its Responses Lite transport,
which ships the request's extra tools in an `additional_tools` input
carrier: `{"type":"additional_tools","role":"developer","tools":[...]}`,
a role but no `content`. The Responses->Chat conversion let that carrier
fall through the generic "item with a role is a message" arm and emitted
`{"role":"system","content":null}` at messages[1], which strict Chat
gateways reject with `messages[1]: missing field "content"` (or
`data did not match any variant of ...SystemMessageContent`). The tools
carried inside it were also dropped on the Chat and Anthropic conversions.
- Skip `additional_tools` items in the Chat message walk; a carrier
declares tools, it is not a message.
- Collect carrier tools through the existing CodexToolContext registry
(dedup, namespace flattening, custom-tool conversion), which also feeds
the Anthropic conversion and the response-side name round-trip.
- Requests without a carrier convert byte-for-byte as before.
Known boundary: `custom` children inside a namespace tool
(`functions.exec`) are still not lifted; that needs its own response-side
name round-trip and is left for a follow-up.
Diagnosed independently in #6158 by BigStrongSun (fix in #6159) and in
fixture.
Fixes #7451
Fixes #6158
Refs #6033
Refs #6776
Supersedes #6159
fix(proxy): normalize Codex detail:original images for Chat gateways (#7476)
Codex marks tool-returned images with detail:"original" whenever the model
catalog advertises supports_image_detail_original, a Responses-only value.
The Responses->Chat conversion copied it verbatim into image_url.detail, and
strict OpenAI-compatible Chat gateways (Ark/Doubao, SenseNova, GLM, vLLM, ...)
reject the whole request with 400. Because Codex replays the full rollout on
every turn, one poisoned view_image result made the session fail permanently,
including plain-text turns.
Two layers:
- tool_media::merge_top_level_detail downgrades original -> auto, so already
poisoned rollouts and Codex versions before 0.155.0 (which do not normalize
replayed history themselves) recover immediately. auto/low/high are untouched.
- codex_catalog_model_entry always writes supports_image_detail_original:false
on the ProxyChat profile, so Codex stops emitting original at the source once
the catalog is regenerated. NativeResponses/Anthropic profiles and the DeepSeek
official catalog are unaffected.
Top-level user images were never affected: responses_content_to_chat_content
drops the part-level detail entirely.
Fixes #6495
Fixes #7458
Refs #6569
Refs #5805
fix(omo): detect unified config on the WSL side when the OpenCode dir is in WSL (#7550)
When the OpenCode config directory points into WSL
(\\wsl.localhost\<distro>\home\<user>\.config\opencode), OMO >= 4.19.3 keeps
its unified config in the WSL-side home (~/.omo/omo.jsonc). Detection only
probed the Windows home, never found it, and silently fell back to the legacy
per-plugin file that OMO no longer reads, so provider switches looked
successful but had no effect.
- Add derive_wsl_home_dir to derive the WSL-side home from a WSL UNC path
(home/<user> or root); non-WSL and non-home paths return None.
- Probe a list of home candidates for the unified config, WSL-side home
first and the OS home as a fallback, shared by the read, write and delete
paths.
- Detection only: when no unified config exists on either side the legacy
fallback is unchanged.
Fixes #7363
Supersedes #7367
docs(partner): add a FluxA purchase link and tighten its sponsor blurb
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.
fix(claude-desktop): expose the 1M context variant for DeepSeek V4 routes
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.
feat(presets): add FluxA Token Plan partner presets
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.
fix(presets): correct AICodeWith Codex-family endpoint to /v1
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.
fix(ui): render monochrome preset icons in the foreground color
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.
docs(user-manual): point Chat-only Codex examples at plan endpoints
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.
fix(pricing): restore DeepSeek V4 Pro price and seed Qwen3.8 and Hy4 models
- 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).
fix(presets): move MiniMax CN and BaiLing to their current official endpoints
- 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.
feat(codex): refresh CN presets from the Responses API audit
- 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.
test(claude-desktop): run Linux path tests on unix hosts and cover Linux in provider sync tests
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.
fix(ui): stop expanding stale cached usage tiers on ineligible provider cards
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.
fix(skills): 支持 skillId 与目录名不一致的技能安装和更新 (#6381)
* fix(skills): resolve mismatched skillId directories
* fix(skills): preserve source path during updates
* fix(skills): refresh source URL after repository relocation
* test(skills): isolate relocation fixture from legacy home
feat(opencode): 支持从获取的模型列表中搜索并批量添加所选模型 (#7515)
* feat: implement fetched models selection and UI components
* fix(opencode): prevent model search from submitting provider form
Add Kimi Global preset variants (kimi.ai platform + api endpoints) (#7526)
Add a "Kimi Global" twin immediately after the domestic "Kimi" API preset and a "Kimi For Coding Global" twin after the domestic "Kimi For Coding" preset, in every preset file that ships them (claude, claude-desktop, codex, hermes, openclaw, opencode, pi).
"Kimi Global" points to the international open platform https://platform.kimi.ai?aff=cc-switch (apiKeyUrl mirrored as https://platform.kimi.ai/console/api-keys?aff=cc-switch) and swaps the endpoint host api.moonshot.cn for api.moonshot.ai with each file's path unchanged (/v1 or /anthropic).
"Kimi For Coding Global" points to the international site https://www.kimi.ai/code/?aff=cc-switch and swaps the endpoint host api.kimi.com for api.kimi.ai with the path unchanged (e.g. https://api.kimi.ai/coding/v1).
Domestic presets are unchanged; category, icons, apiFormat, model catalogs, endpointCandidates (host swapped), partnerPromotionKey and settings otherwise mirror the domestic blocks exactly.
primePartner stays only on the domestic presets so the new variants do not alter prime-partner ordering.
The Pi twins get distinct providerKey values ("cc-switch-kimi-global", "cc-switch-kimi-for-coding-global") because providerKey is the preset-level primary key and must not collide with the domestic presets.
Rebalance sponsor CTAs around Kimi Code plan with dual-region links (#7522)
Kimi Code plan purchases have resumed, so the README sponsor sections go back to leading with the coding plan instead of the API-only flow written during the purchase pause.
All four locales (EN/ZH/JA/DE) now present Kimi Code plan (中文站 | Global) as the primary CTA and the Kimi Open Platform API (中文站 | Global, with the first-top-up bonus) as the secondary one, mirroring the domestic|global split already used for API links.
The aff and track_id values are unchanged.
fix(prompts): refresh active prompts after external file edits (#7194)
Reload the enabled prompt from its live file when the Prompts list loads, and refresh the open panel when the window regains focus, so external edits to CLAUDE.md / AGENTS.md show up without a restart.
- Keep saved templates available when the live file cannot be read.
- Skip the backfill for empty or whitespace-only files, matching enable_prompt.
- Skip the backfill while a sync/restore holds the sync lock, so a list load cannot write a stale live file into a freshly restored database.
fix(skills): raise archive entry limit so large skill repos install (#7489)
Installing hugohe3/ppt-master from skills.sh failed with
ARCHIVE_TOO_MANY_ENTRIES: the GitHub archive we download is the whole
repository (13,248 entries), and the 10,000-entry cap added in #5811
treated it as a zip bomb.
- Raise MAX_ARCHIVE_ENTRIES to 30,000 for both the remote archive and
local ZIP paths, and align WebDAV's MAX_EXTRACT_ENTRIES so a skills.zip
containing such a skill still restores.
- Charge every file entry at least one 4 KiB block against the existing
512 MiB budget, mirroring directory accounting, so empty-file archives
are bounded by the byte budget rather than by the entry count alone.
- Add tests for a 13k-entry repo archive and for the per-file minimum
charge.
Fixes: https://github.com/farion1231/cc-switch/issues/7475
feat: add MiniMax Code harness support (#7383)
Add MiniMax Code (mcode) as a managed application: provider and model management through the native custom_provider configuration, MCP servers, Skills, global instructions (AGENTS.md), read-only local session history with resume, and usage import from the native runtime database. Native files shared by the TUI and desktop are written under the native lock with rollback when the database commit fails, unrelated native configuration is preserved, and the data directory honors MINIMAX_DATA_DIR and MAVIS_DATA_DIR. Deleting sessions stays in the native application because they are coupled to runtime state.
fix(codex): recover stale account bindings during takeover (#7395)
After deleting a managed ChatGPT account and logging in again, a Codex provider can still reference the old local account ID, which made route switching, takeover, startup restoration and rebinding fail permanently.
Distinguish a missing outgoing account from an existing account with no matching live token. A missing account only releases its ownership marker after the persisted account store is verified; the target write policy stays in force. A stale target now reports an account-selection instruction instead of a bare not-found error, including when takeover is already enabled.
Fixes #7392
feat(claude-desktop): support 3P configuration on Linux (#7331)
Add Claude Desktop 3P configuration support on Linux. Resolve the config root from an absolute XDG_CONFIG_HOME, falling back to ~/.config; when CC Switch itself runs as a Flatpak, use the host ~/.config instead of the sandbox-private XDG_CONFIG_HOME. Update the en/zh/ja user manuals with Linux paths and the Flatpak boundary.
Fixes #4855
fix(proxy): omit missing tool descriptions instead of serializing null (#7319)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): keep reasoning effort for grok-4.6 and pass xhigh through verbatim (#7318)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(models): support Zhipu OpenAI Responses model lists (#7330)
Zhipu's Responses endpoint (open.bigmodel.cn/api/v1) serves its model list in the Codex remote catalog shape (models[].slug) instead of the OpenAI-compatible data[].id shape, so the Codex form's model fetch came back empty. The parser now falls back to models[].slug when data is absent.
fix(misc): fetch npm dist-tags from the dedicated endpoint with the probe timeout (#7346)
The tool version probe requested the full npm packument for each package (metadata for every historical version, 14-25 MB uncompressed for codex / opencode / openclaw) only to read its dist-tags, and it was the only latest-version probe without LATEST_PROBE_TIMEOUT, so it fell back to the proxy client's 600 s total timeout and the cards sat on "loading" for minutes on slow links while the large downloads starved the Hermes GitHub probe into "unknown".
Switch fetch_npm_dist_tags to registry.npmjs.org/-/package/{package}/dist-tags, whose body is the dist-tags object itself (tens of bytes to a few KB), escape scoped names as %2f, and apply the same 15 s probe timeout as the GitHub and PyPI probes. No data is lost: the only consumer reads latest and the tool's prerelease tags.
Fixes #7339
feat(usage): display output tokens per second in request log table and detail panel (#3369)
Show tokens/s next to the output token count in the request log table and the request detail panel.
The generation window prefers an explicit duration when present, otherwise latency minus first-token time for streaming requests, and falls back to full latency for non-streaming requests. Rows without a positive token count or window show nothing.
Closes #5936
fix(presets): update APIKEY.FUN URLs to apikey.fan
docs(release): add v3.20.3 release notes
chore(release): v3.20.3
fix(pricing): move retired DeepSeek V4 family to the V4.1 Flash tier
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.
fix(presets): declare third-party Token Plan DeepSeek V4 rows text-only
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.
feat(presets): rename DouBaoSeed preset to localized Volcengine Doubao
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.
fix(codex): skip unchanged incomplete rollout tails
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.
fix(claude): align quick model selection with form order
fix(tray): show bound ChatGPT account quota for managed Codex cards
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
feat(claude): add "Disable Artifact Tool" quick toggle to provider editor
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.
feat(codex): refresh aggregator model presets and catalogs
feat(codex): switch Kimi presets to native Responses direct-connect
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.
fix(usage): show Claude Fable weekly limit from the usage API limits array
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.
fix(codex): mirror vision-capable DeepSeek catalog and un-gate the legacy v4-flash alias (#7286)
Refresh the bundled DeepSeek official Codex catalog to the vendor's current models.json (slug deepseek-flash, image input modality, supports_image_detail_original) and remove deepseek-v4-flash from the confirmed text-only registry: the legacy id is still accepted by DeepSeek and routed to the vision-capable V4.1 Flash, so both the proxy media sanitizer and the generated Codex catalog must fail open for it. deepseek-v4-pro stays text-only per the vendor catalog.
Fixes #7283
fix(proxy): clamp sub-floor max_tokens to the Responses API minimum (#7287)
The Responses API rejects max_output_tokens below 16, but Anthropic clients legitimately send tiny probe budgets (Claude Desktop's model-availability probe uses max_tokens=1). The Anthropic→Responses conversion copied the value verbatim, so strict upstreams returned 400 and Claude Desktop reported the mapped model as unavailable under local routing.
Clamp 1..=15 up to 16 during conversion; 16+, 0 and non-integer values keep their pass-through semantics. Normal session requests convert byte-for-byte as before, and the Codex OAuth path still strips max_output_tokens afterwards.
Fixes #7103
fix(codex): coalesce adjacent commentary with pending tool calls (#7280)
The Codex Responses -> Chat Completions converter emitted two consecutive
assistant messages when one Responses model turn contained a commentary
message directly followed by function_call items. Chat upstreams imitate
the text-only assistant message as a complete turn and return
finish_reason=stop before the expected tool call, so long agent tasks end
after a progress update.
Merge pending tool calls into the directly adjacent assistant message when
it carries no tool_calls yet, attach pending reasoning per segment so
parallel calls do not duplicate it, and keep the new-message path for every
other boundary shape (user/tool boundaries, prior tool-call batches, media
flushes). The reasoning placeholder backfill still applies to coalesced
calls without reasoning.
Fixes #6529
Supersedes #6530
Co-authored-by: BigStrongSun <BigStrongSun@users.noreply.github.com>
fix(proxy): skip empty reasoning_content placeholders in Anthropic SSE streaming (#7227)
Some OpenAI-compatible upstreams (GLM, Qwen, DeepSeek-V4-Pro via ModelScope/DashScope,
Bailian, JD Cloud, ...) keep a `reasoning_content: ""` placeholder in every content
chunk instead of omitting the field. The reasoning branch of the OpenAI Chat -> Anthropic
SSE converter only checked for presence, and it shares `current_non_tool_block_type`
with the content branch, so every content chunk closed the open text block, opened an
empty thinking block, then reopened a text block. N content chunks became 2N content
blocks, half of them empty thinking blocks, which Claude Code rendered as one line per
token with dozens of empty Thought blocks and bloated the session JSONL.
Filter empty reasoning before entering the branch, matching the `!content.is_empty()`
guard the content branch already had and the non-streaming `openai_to_anthropic`
converter. Two regression tests pin block index, type and order: the placeholder shape
must yield a single text block and no thinking_delta at all, and real reasoning must
still yield one thinking block followed by one text block.
Fixes #5028
Fixes #4404
fix(codex): detect growing rollouts with a persisted byte cursor (#7219)
Codex keeps the rollout append handle open for the whole session, so on Windows NTFS the file mtime does not advance while the file grows and the pure-mtime gate skipped growing rollouts forever. Persist the observed byte length in session_log_sync.last_byte_offset for Codex rows and skip a file only when both mtime and size are unchanged. The parser now splits records on newlines manually so an incomplete trailing record is retried on the next pass without advancing the line cursor.
Fixes #6060
fix(codex): proxy image edits and Images API follow-ups (#7177)
* fix(codex): proxy image edits and Images API follow-ups
Follow-ups to #7036 for Codex's built-in ImageGen over the local proxy:
- Register `/images/edits` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them through the shared Codex standalone passthrough.
ImageGen posts to `images/edits` whenever a call references existing
images, and the router returned an empty 404 for it.
- Add `CodexStandaloneEndpoint::ImagesEdits`. Both Images endpoints share
one full-URL suffix table, so a complete Responses / Compact / Chat
Completions / Images URL derives the sibling endpoint (and vice versa)
while opaque full URLs keep failing closed.
- Apply the standalone sibling rewrite when the base URL is pasted as a
complete endpoint with the full-URL switch off, mirroring
`codex_chat_base_is_full_endpoint`; standalone requests no longer become
`.../chat/completions/images/generations`.
- Parse Images API streaming usage: `image_generation.completed` carries
`usage` on the event top level, so scan for `usage.input_tokens` before
falling back to the Chat Completions stream parser.
Related #7036
* fix(codex): match standalone URL suffixes case-insensitively
Keep endpoint detection and sibling rewriting consistent while preserving the original URL prefix and query. Cover mixed-case Images and Alpha Search endpoints through URL and proxy regression cases.
---------
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): preserve per-app settings on shutdown and port allocation (#7210)
On normal exit, stop_with_restore_keep_state() called the legacy global
proxy-config writer to clear the long-retired live_takeover_active flag.
That writer reads the Claude row and runs an UPDATE without a WHERE clause,
so every shutdown copied Claude's max_retries and the three timeout fields
onto the Codex, Gemini and Grok Build rows — and the UPDATE never carried
live_takeover_active in the first place, so the step had no effect beyond
the clobber. The ephemeral listen-port path did the same on every proxy
start.
Drop the dead write-back (live-config restore, backup cleanup and health
reset are unchanged) and persist the resolved ephemeral port through the
global-config interface, which only touches the shared listen/logging
columns. Two regression tests seed distinct per-app settings for all four
apps and assert they survive both paths.
Fixes #7204
fix(provider): preserve child metadata during universal sync (#7212)
Syncing a universal provider regenerates its Claude/Codex/Gemini child
providers. The child's `settings_config` was merged with the existing row,
but `meta`, `created_at` and `sort_index` were taken from the generated
object, and the universal provider never carries any of them from the UI.
The UPDATE therefore wiped the child's usage script, common-config opt-out,
endpoint auto-select and other per-app settings to `{}` and reset the sort
index to NULL, which pushed the card to the bottom of the list and looked
like the provider had been deleted and re-created.
Keep the existing child's `meta`, `created_at` and `sort_index` when the
row already exists. Name, base URL, key, models, website and notes are
still driven by the universal provider, and first-time creation still
inherits the parent's metadata.
Fixes #7134
feat(presets): rebrand DashScope as 千问AI平台 and refresh Qwen models (#7183)
Rename the domestic Bailian presets to 千问AI平台 with a dedicated icon, refresh the Qwen family to the 3.8 generation across Claude Code, Claude Desktop, Codex, Hermes, OpenClaw, OpenCode and Pi, add the domestic Token Plan presets, restore the international QwenCloud Coding Plan presets, and align OpenClaw/OpenCode/Pi model metadata with the official client docs.
fix(codex): honor proxy URL when model_provider is omitted (#7263)
When a Codex provider card's TOML has no `model_provider`, Codex falls back to
its built-in `openai` provider, but takeover wrote the proxy address as a
top-level `base_url` that Codex never reads, so requests bypassed the local
proxy and went straight to api.openai.com.
Treat the missing selector as the built-in `openai` provider and write the
proxy address to `openai_base_url`; the existing legacy-reroute migration then
turns it into a `cc-switch` custom provider table carrying the `PROXY_MANAGED`
bearer, the same shape every other third-party takeover uses. Explicit
provider selections are unchanged.
Refs #6256, #7217
fix(provider): update MiniMax defaults to M3 and remove expired offers (#7255)
* fix(provider): refresh MiniMax M3 presets and remove expired promotions
* fix(provider): complete MiniMax M3 capability declarations
---------
Co-authored-by: saladday <1203511142@qq.com>
chore(provider): remove Atlas Cloud sponsor status, keep presets
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`
docs(release): add v3.20.2 release notes
chore(release): v3.20.2
feat(provider): add SoleAPI sponsor presets across all nine apps
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.
fix(codex): proxy image edits endpoint
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.
fix(provider): pin models URL for JieKou and Novita Claude presets
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.
feat(pricing): refresh seed for September 2026 vendor price changes
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.
fix(codex): stamp requires_openai_auth on takeover writes to match the live login state
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.
fix(updater): show the real reason when an update check fails (#6482)
The updater plugin rejects with a plain string, so `err instanceof Error`
never matched and the real failure reason was replaced by the generic
fallback message. Use the existing extractErrorMessage helper, and pass
the recovered message into the check-update failure toast so it actually
reaches the user instead of stopping at an unread context state.
Co-authored-by: Jason <farion1231@gmail.com>
docs: fix the locales path in the README directory trees (#6100)
All four READMEs showed locales/ as a direct child of src/, but the
translations live at src/i18n/locales/. Split it into two tree nodes so it
matches how the rest of the tree is drawn. Also fixes a CLaude -> Claude
typo in README_ZH.md.
Co-authored-by: Jason <farion1231@gmail.com>
fix(claude): exclude workflow journal.jsonl from session list
Claude Code's workflow feature writes subagents/workflows/wf_*/journal.jsonl inside the session directory. The Claude session scanner only excluded agent-* files, so each journal parsed as an empty ghost session titled "journal". Exclude journal.jsonl by file name and add regression tests.
Fixes #6042
fix(i18n): add the missing pi.form.providerKeyDuplicate and common.collapse keys
translatePiProviderMutationError returns pi.form.providerKeyDuplicate for the Pi backend's duplicate-key error, but the key existed in none of the four locale files, so the toast showed the raw key. common.collapse (aria-label of the thinking-map toggle) was missing the same way. Add both keys to zh/en/ja/zh-TW and lock them in the locale coverage test.
fix(proxy): stop mask_url panicking on multi-byte UTF-8 near the truncation boundary
mask_url's fallback branch (used when the proxy URL fails Url::parse) sliced the raw string at byte 20 and panicked when a multi-byte UTF-8 character straddled that offset, e.g. a CJK string pasted into the global proxy address. Snap the cut to the nearest char boundary at or before 20 and add a regression test.
fix(tests): isolate LOCALAPPDATA so tests stop writing the real Claude Desktop config
On Windows, Claude Desktop config paths resolve from LOCALAPPDATA, which the test home override in tests/support.rs did not redirect, so profile_roundtrip wrote the https://desktop.test fixture into the developer's real Claude Desktop config. Point LOCALAPPDATA at the isolated test home alongside HOME and USERPROFILE.
Fixes #6077
fix(usage): widen pricing model source select to fit localized labels
The pricing model source SelectTrigger in the usage cost settings table was w-28, which leaves about 70px for the label; the English and Japanese labels need about 107px and overflowed the trigger. Widen it to w-40 and drop the h-7 overrides so the trigger and the adjacent Input use the shared h-9 height.
feat(pi): add Tencent TokenHub and Token Plan presets (#7159)
Add eight Tencent presets for Pi, covering the TokenHub pay-as-you-go line
(domestic and international) and the six Token Plan subscription products —
Pi was the only managed app still missing them. Model IDs, ordering and
endpoints follow Tencent's own model lists verbatim, and the tests assert each
product's line-up in order so future drift is caught.
Declare thinking control alongside the capabilities rather than leaving it to
defaults: the 31 DeepSeek entries carry a Tencent-specific compat so selecting
"off" actually disables thinking instead of silently billing reasoning tokens,
the eight Kimi K2.7 Code entries no longer offer an "off" level the model
cannot honour, and kimi-k2.7-code-highspeed is marked image-capable so
tool-returned screenshots are no longer dropped.
Drop minimax-m2.5 from every app: it is gone from Tencent's model tables and
the platform is retiring it.
fix(codex): proxy image generations endpoint (#7036)
Codex's built-in ImageGen calls the legacy OpenAI Images API, but the local
proxy only registered Responses / Chat Completions / Compact / Alpha Search
routes, so `/v1/images/generations` returned an empty 404 under Codex routing.
- Register `/images/generations` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them as a Codex standalone passthrough shared with Alpha Search.
- Generalize the Alpha Search full-URL rewrite into
`rewrite_codex_standalone_full_url`: derive the sibling
`/images/generations` from full Responses / Compact / Chat Completions URLs,
keep an existing Images URL as-is, and fail closed on opaque full URLs.
- Record `usage.input_tokens` / `output_tokens` through the Codex usage parser.
Fixes #5429
Fixes #6745
feat(pricing): add Gemini 3.8 Flash pricing (#7164)
Seeds the built-in pricing row for Gemini 3.8 Flash at 0.75 / 3.75 input/output per million tokens, 0.075 cache read, no cache-write charge, plus a regression test locking all four fields.
Values verified against Google's own pricing page (introductory price through 2026-12-31; list price 1.50 / 7.50 / 0.15 from 2027-01-01) and models.dev.
feat(provider): extend PPIO presets for Pi (#6870)
Add a PPIO provider preset for Pi using the OpenAI-compatible endpoint,
and pin an explicit models URL on the Claude Code PPIO preset so model
discovery resolves against https://api.ppio.com/openai/v1/models instead
of the derived candidates, which all return 404.
Co-authored-by: Jason <farion1231@gmail.com>
feat(pricing): add GLM-5.3 Flash pricing (#7163)
fix(proxy): enable parallel tool calls for Codex OAuth (#7024)
Codex OAuth requests defaulted parallel_tool_calls to false when the incoming
Anthropic request carried no explicit value, forcing Claude Code into one tool
call per model turn. Default it to true (matching codex-rs), map Anthropic
tool_choice.disable_parallel_tool_use to the inverse Responses value so explicit
serial execution is preserved, and add regression coverage for both directions.
Carries #5722 forward on top of current main.
Fixes #5719
Co-authored-by: 太一 <taiyi@douyu.tv>
feat(pricing): add GPT-6 Astra pricing (#7162)
fix(codex): resolve input_modalities for unknown models in vendor catalog (#6750)
When a model slug is not in the bundled DeepSeek official catalog, the vendor path clones the flagship entry and previously inherited its text-only input_modalities. Resolve modalities for unmatched slugs through the same registry/fail-open logic the non-vendor path uses, so a vision variant such as deepseek-v4-flash-vision-exp is no longer declared text-only. Explicit user inputModalities still win; matched models keep the vendor's declaration verbatim.
Fixes #6725
fix(claude): expose Opus 5 and Sonnet 5 in takeover mode (#5882)
Bump the stable client-facing role aliases that takeover mode writes to
Claude Code: Opus claude-opus-4-8 -> claude-opus-5 and Sonnet
claude-sonnet-4-6 -> claude-sonnet-5, so the /model menu matches the
Claude Desktop default routes and preset defaults that already moved to
Opus 5 / Sonnet 5. Add "opus-5" to the adaptive-thinking classifier so
the Bedrock thinking optimizer, which runs on the client alias before
model mapping, keeps sending adaptive thinking instead of the removed
budget_tokens.
Routing is unaffected: the proxy maps client aliases by role keyword, and
the aliases are rewritten automatically the next time the proxy starts.
Fixes #5876
Co-authored-by: Jason <farion1231@gmail.com>
feat(pricing): add GLM-5.3 to built-in model pricing table (#6591)
Seed glm-5.3 at $1.4/$4.4 per MTok with $0.26 cache read, matching the
official Z.ai list price (identical to the GLM-5.2 tier):
https://docs.z.ai/guides/overview/pricing
New model id, so seed only; no repair entry and no SCHEMA_VERSION bump.
Existing installs pick the row up on next launch via INSERT OR IGNORE, and
the startup cost backfill re-prices historical glm-5.3 rows recorded at $0.
fix(codex): align OAuth client identity for GPT-6 (#7132)
Requests for gpt-6-astra through the Claude-to-Codex OAuth takeover route advertised Codex 0.144.1, below the model's 0.153.0 minimal_client_version, so ChatGPT's backend rejected them with HTTP 400 "requires a newer version of Codex". Bump the advertised client version to 0.153.4 and share the originator/version constants between generation and model discovery, which previously sent cc-switch's own package version and originator.
Two regression tests pin the generation headers and the model-list request to a version at or above the gpt-6-astra floor.
Refs #7129
fix(codex): disable supports_search_tool for DeepSeek catalog models (#6653)
DeepSeek's Responses API has no tool_search support, but the bundled
official catalog declared supports_search_tool=true for deepseek-v4-pro
and deepseek-v4-flash. Codex gates…
…iders at second position
- ProviderList: context menu with quick move-to-top/move-to-bottom, reusing
update_providers_sort_order and refreshing tray menu like drag sort
- useAddProviderMutation: default insert at index 1 with explicit sortIndex
materialization for existing items (avoids defined-first sort trap)
- i18n: quickMove* strings for zh/en/zh-TW/ja
ci: stop flaky WSL2 linker job from blocking main
The backend-windows-wsl2 job intermittently fails with LNK1327 /
mt.exe c1010070 when the Windows runner's mt.exe cannot parse a
manifest temp file on the WSL2 UNC TEMP path. The same flaky failure
occurs upstream and is unrelated to fork changes. Mark the job
continue-on-error so main stops going red on it; the result stays
visible in the Actions UI.
style: apply prettier to files touched by recent comet archives
Frontend CI format:check failed on 6 files modified by the app-name
and model-quick-switch archive commits plus the updater-disable commit.
Run prettier --write so format:check passes again.
chore: archive ccs-fork-app-name-cc-switch
chore: archive ccs-model-quick-switch-ui-polish
docs: rewrite README to document fork differences vs upstream
README 从上游完整功能介绍改为仅记录 fork 与上游的差别,完整功能
文档指回上游。涵盖 8 项 fork 魔改:构建标识/开发预览、预设精简、
Claude 表单调整、模型搜索移植、主页模型快速切换、侧边栏面板显隐、
关闭应用内更新、打包范围,并说明版本号与 rebase 同步流程。
feat: disable in-app updater, point users to GitHub releases
移除 tauri-plugin-updater 依赖与自动更新链路,改为统一引导用户到
GitHub Releases 手动下载新版。
- 前端:updater.ts 恒返回 up-to-date;UpdateContext 取消启动自检;
AboutSection「检查更新」直接打开 fork releases 页;DatabaseUpgrade
恢复界面去掉下载进度条,改为打开发布页。
- 后端:移除 updater 插件注册与 UpdaterExt 调用;
install_update_and_restart 降级为空壳,check_app_update_available
返回当前版本号;check_for_updates 指向 fork releases;capabilities
去掉 updater:default 权限。
- 打包:tauri.conf.json 删 plugins.updater、createUpdaterArtifacts=false;
Cargo.toml / package.json 删 updater 依赖,Cargo.lock / pnpm-lock
同步重生成。
- CI:release.yml 移除签名收集与 assemble-latest-json job,删除
.tar.gz/.sig 处理,release body 去掉 updater 说明。
版本号统一为 3.20.0-1(合法 semver 且 MSI 第 4 位为 1)。
Cargo.lock / pnpm-lock.yaml 已随依赖移除重生成,cargo check 通过。
ci: build only Windows x64 and macOS arm64, unsigned
feat: gate dev panel behind dev-only env flag
chore(comet): archive ccs-sidebar-panels-visibility change
fix: apply skills visibility guard to hermes branch toolbar button
feat: include prompts in sidebar panel visibility settings
fix: persist visibleSidebarPanels setting in backend AppSettings
style: use compact pill toggles for sidebar panel visibility
feat: add sidebar panel visibility switches to settings
feat: add sidebar panel visibility settings and wire into toolbar buttons
chore(comet): archive ccs-home-model-display change
fix(fork): read grokbuild api key from config TOML in quick switch dialog
feat(fork): align model badge with upstream extractModelBadge
feat(fork): show current model badge and quick-switch entry on provider cards
feat(fork): add model quick switch dialog
feat(fork): add provider model read/write utils with tests
chore(comet): archive ccs-model-search-port change
feat(i18n): add model search picker keys for all locales
feat(form): port SearchableModelPicker from upstream for searchable model selection
chore(comet): archive ccs-claude-form-layout change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-claude-form-layout/
- Sync claude-form-layout spec to main specs
- Annotate design doc as archived
- Add verification report
fix(i18n): update advanced options hint, add ja/zh-TW toggle-all, drop dead apiFormatHint key
fix(form): correct 1M toggle-all alignment direction (ml-1)
fix(form): align 1M toggle-all checkbox with row checkboxes
refactor(form): move api format select into EndpointField rightSlot
fix(form): widen api format select to fit option text
feat(form): add 1M toggle-all checkbox in model mapping header
feat(form): move api format select next to endpoint input
chore(comet): archive ccs-provider-presets-trim change
- Move change artifacts to docs/openspec/changes/archive/2026-08-19-ccs-provider-presets-trim/
- Sync provider-presets-visibility spec to main specs
- Annotate design doc as archived
- Add verification report
docs(fork): add presets-trim files to rebase whitelist
test(fork): mock IS_FORK_BUILD false in unit tests for upstream-form semantics
feat(fork): wire official preset filter into provider forms
feat(fork): add hidden field to preset interfaces
feat(fork): add official preset allowlist and filter util
chore(comet): archive ccs-fork-scaffolding change
- Move change artifacts to docs/openspec/changes/archive/2026-08-18-ccs-fork-scaffolding/
- Sync fork-build-identity spec to main specs
- Annotate design doc as archived
- Add verification report
feat(fork): add dev config with isolated identifier and config dir
docs(fork): complete rebase whitelist with all fork-touched files
fix(fork): guard setTitle with isTauri in test/non-tauri env
fix(fork): define __CCS_FORK_BUILD__ in vitest config
feat(fork): add DevPanel and settings footer entry
feat(fork): set macOS window title to CC Switch (Fork) on startup
feat(fork): inject __CCS_FORK_BUILD__ compile-time constant
feat(fork): bump version to 3.19.2-fork.1 and rename product to CC Switch (Fork)
docs(fork): add upstream rebase workflow and ignore tool artifacts
fix(proxy): accept the whole grok-4.x (x>=5) family in the reasoning-effort whitelist (#7369)
Replace the verbatim grok-4.5 / grok-4.6 entries in supports_reasoning_effort with a rule that parses the grok-4.x minor version and accepts x >= 5, mirroring the existing GPT-5+ rule. This covers grok-4.7 (released 2026-09-21), whose reasoning effort was previously dropped on the Claude -> Chat, Claude -> Responses and Codex Responses -> Chat conversion paths, and lets future releases pass without another whitelist edit. The grok-build-* family is retained for saved providers.
Co-authored-by: allenxu09 <171831965+allenxu09@users.noreply.github.com>
docs(release): add v3.20.4 release notes
chore(release): v3.20.4
feat(pricing): add Grok 4.7 and GLM-5.3-FlashX
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.
fix(i18n): remove outdated OpenCode Go referral offer
fix: add SudoCode.chat alternate API endpoint
feat(settings): add a GitHub star prompt to the About card
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
feat(presets): add Soshow aggregator presets
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.
fix(ci): stabilize frontend tests and verify renderer build
fix(mcode): refresh provider state after removal (#7578)
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
fix(lib): reset skip_taskbar before window.show() across entry points (#6348)
5处 window.show() 调用中 4处没有在 show() 前重置 Windows 的 skip_taskbar,导致以下场景出现窗口可见但不在任务栏、点最小化后窗口消失的 bug:
- single_instance handler (lib.rs:360-361):当原进程因 silent_startup / 关闭到托盘等处于 skip_taskbar(true) 状态时,从快捷方式/开始菜单唤起已运行实例触发。
- handle_deeplink_url 的 focus_main_window 分支 (lib.rs:256):通过 ccswitch:// URL 唤起时。
- DB 恢复流程 (lib.rs:556):数据库版本不匹配时显示恢复界面。
- RunEvent::Opened 处理器 (lib.rs:1803):系统接收到 deep-link URL 时。
修复后的行为与 tray.rs::handle_tray_menu_event::show_main 分支一致(那里已有相同的 set_skip_taskbar(false) 调用)。
经用户测试验证:autostart + silent_startup + 从开始菜单/快捷方式启动的场景下,最小化按钮恢复正常工作。
fix(proxy): omit missing tool descriptions in the Codex responses-to-chat converter (#7378)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): preserve max effort for GPT-5.6 and GPT-6 Astra (#7531)
fix(misc): ignore shell startup output when probing tool versions in WSL (#7348)
try_get_version_wsl runs "${SHELL:-sh}" -lic '<tool> --version' through
wsl.exe and takes the first x.y.z anywhere in stdout. A login+interactive
shell runs the distro's startup files first, so their output shares that
stdout. On Ubuntu, /etc/profile.d/update-motd.sh prints the MOTD once a day,
starting with "Welcome to Ubuntu 24.04.4 LTS", and the card then showed
24.04.4 as the Claude Code version. The once-a-day stamp makes it appear
after the first probe of the day and vanish on refresh.
The payload now prints a sentinel before running the tool, and parsing looks
only at what follows its last occurrence. The last occurrence handles the
-lic || -lc || -c fallback chain printing it several times; matching it as a
substring handles OSC sequences glued in front of it. If the sentinel is
absent (the shell failed before the payload ran), the output is used as
before. The stderr fallback for tools that print their version there is kept.
Fixes #7347
Co-authored-by: lyfuci <lyfuci11@gmail.com>
fix(claude): hide session URL attribution (#7053)
* fix(claude): hide session URL attribution
* docs(claude): document session URL attribution
fix(ui): reset provider scroll when switching apps (#5211)
fix(pi): fix provider logo (#6826)
fix(i18n): use the Pi form's own term for the zh-TW duplicate-key message (#7187)
Follow-up to the nit on #6768. In zh-TW the Pi form labels the field
供應商識別碼 and says 供應商識別碼為必填 when it is empty, but the
duplicate message I added said 供應商標識 — the term Hermes, OpenCode and
OpenClaw use for their own field. One form, two names for the same thing.
The other three locales already keep the label and both messages on one
term, so this makes zh-TW match the pattern rather than introduce one:
en Provider key / Provider key is required / This provider key already exists
ja プロバイダーキー / プロバイダーキーを入力してください / このプロバイダーキーは既に存在します
zh 供应商标识 / 供应商标识不能为空 / 该供应商标识已存在
zh-TW 供應商識別碼 / 供應商識別碼為必填 / 該供應商標識已存在 <- the odd one out
hermes.form.providerKeyDuplicate carries the same sentence and is left
alone: its own label is 供應商標識, so it is already consistent.
Verified: managementListLocales 7/7. Full unit suite 1085/1087, the same
two App.test.tsx integration cases failing before and after.
fix(proxy): strip stop param for GitHub Copilot to unblock auto mode classifier (#5404)
Claude Code's auto mode classifier sends `stop_sequences` on every
safety check. On the GitHub Copilot path the Anthropic -> OpenAI Chat
conversion mapped it to `stop`, and Copilot's Chat Completions route
for Claude models answers that request with HTTP 400, so every
classifier call failed while normal chat kept working.
- Remove the converted `stop` field only when the provider is GitHub
Copilot; other OpenAI-compatible upstreams keep it.
- Copilot detection in the transform now also covers providers that
are recognised only through `env.ANTHROPIC_BASE_URL`.
- Regression tests for the Copilot (meta and env URL) and generic
provider cases.
Fixes #5175
fix(codex): keep stored auth when the live snapshot has no credential (#7434)
Editing the current Codex provider seeds the form from the live ~/.codex
snapshot, and switching away backfills that snapshot into the DB row. When a
provider table declares its own credential source (an Authorization header,
env_key, auth/aws), no bearer token is injected into config.toml and the
shared auth.json is deleted on third-party switches, so the live snapshot is
permanently `{ auth: {}, config }` while auth.OPENAI_API_KEY in the DB is the
only copy of the key. Both paths treated that empty live auth as
authoritative and silently erased the stored key, which in turn made the
usage-script modal misread the provider as an official subscription and
reset the saved script.
- Treat a credential-less live auth (missing auth.json or the bare
auth_mode logout marker) as an absent field in both the edit dialog and
the switch-away backfill, keeping the stored auth template. Live still
wins whenever it carries material, and official providers keep the live
login as their source of truth.
- Gate the official exemption on is_codex_official_provider /
resolveCodexOfficialIdentity rather than the category alone: a legacy
fixed official card without a persisted category is still written as
official, so the fallback must not resurrect a logged-out login on
switch-back.
- Cover both layers with regression tests, including the category-less
official shapes.
Fixes #7433
Supersedes #7492
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): lift additional_tools input carriers on the Codex chat conversion (#7454)
Codex talks to third-party providers over its Responses Lite transport,
which ships the request's extra tools in an `additional_tools` input
carrier: `{"type":"additional_tools","role":"developer","tools":[...]}`,
a role but no `content`. The Responses->Chat conversion let that carrier
fall through the generic "item with a role is a message" arm and emitted
`{"role":"system","content":null}` at messages[1], which strict Chat
gateways reject with `messages[1]: missing field "content"` (or
`data did not match any variant of ...SystemMessageContent`). The tools
carried inside it were also dropped on the Chat and Anthropic conversions.
- Skip `additional_tools` items in the Chat message walk; a carrier
declares tools, it is not a message.
- Collect carrier tools through the existing CodexToolContext registry
(dedup, namespace flattening, custom-tool conversion), which also feeds
the Anthropic conversion and the response-side name round-trip.
- Requests without a carrier convert byte-for-byte as before.
Known boundary: `custom` children inside a namespace tool
(`functions.exec`) are still not lifted; that needs its own response-side
name round-trip and is left for a follow-up.
Diagnosed independently in #6158 by BigStrongSun (fix in #6159) and in
fixture.
Fixes #7451
Fixes #6158
Refs #6033
Refs #6776
Supersedes #6159
fix(proxy): normalize Codex detail:original images for Chat gateways (#7476)
Codex marks tool-returned images with detail:"original" whenever the model
catalog advertises supports_image_detail_original, a Responses-only value.
The Responses->Chat conversion copied it verbatim into image_url.detail, and
strict OpenAI-compatible Chat gateways (Ark/Doubao, SenseNova, GLM, vLLM, ...)
reject the whole request with 400. Because Codex replays the full rollout on
every turn, one poisoned view_image result made the session fail permanently,
including plain-text turns.
Two layers:
- tool_media::merge_top_level_detail downgrades original -> auto, so already
poisoned rollouts and Codex versions before 0.155.0 (which do not normalize
replayed history themselves) recover immediately. auto/low/high are untouched.
- codex_catalog_model_entry always writes supports_image_detail_original:false
on the ProxyChat profile, so Codex stops emitting original at the source once
the catalog is regenerated. NativeResponses/Anthropic profiles and the DeepSeek
official catalog are unaffected.
Top-level user images were never affected: responses_content_to_chat_content
drops the part-level detail entirely.
Fixes #6495
Fixes #7458
Refs #6569
Refs #5805
fix(omo): detect unified config on the WSL side when the OpenCode dir is in WSL (#7550)
When the OpenCode config directory points into WSL
(\\wsl.localhost\<distro>\home\<user>\.config\opencode), OMO >= 4.19.3 keeps
its unified config in the WSL-side home (~/.omo/omo.jsonc). Detection only
probed the Windows home, never found it, and silently fell back to the legacy
per-plugin file that OMO no longer reads, so provider switches looked
successful but had no effect.
- Add derive_wsl_home_dir to derive the WSL-side home from a WSL UNC path
(home/<user> or root); non-WSL and non-home paths return None.
- Probe a list of home candidates for the unified config, WSL-side home
first and the OS home as a fallback, shared by the read, write and delete
paths.
- Detection only: when no unified config exists on either side the legacy
fallback is unchanged.
Fixes #7363
Supersedes #7367
docs(partner): add a FluxA purchase link and tighten its sponsor blurb
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.
fix(claude-desktop): expose the 1M context variant for DeepSeek V4 routes
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.
feat(presets): add FluxA Token Plan partner presets
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.
fix(presets): correct AICodeWith Codex-family endpoint to /v1
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.
fix(ui): render monochrome preset icons in the foreground color
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.
docs(user-manual): point Chat-only Codex examples at plan endpoints
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.
fix(pricing): restore DeepSeek V4 Pro price and seed Qwen3.8 and Hy4 models
- 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).
fix(presets): move MiniMax CN and BaiLing to their current official endpoints
- 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.
feat(codex): refresh CN presets from the Responses API audit
- 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.
test(claude-desktop): run Linux path tests on unix hosts and cover Linux in provider sync tests
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.
fix(ui): stop expanding stale cached usage tiers on ineligible provider cards
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.
fix(skills): 支持 skillId 与目录名不一致的技能安装和更新 (#6381)
* fix(skills): resolve mismatched skillId directories
* fix(skills): preserve source path during updates
* fix(skills): refresh source URL after repository relocation
* test(skills): isolate relocation fixture from legacy home
feat(opencode): 支持从获取的模型列表中搜索并批量添加所选模型 (#7515)
* feat: implement fetched models selection and UI components
* fix(opencode): prevent model search from submitting provider form
Add Kimi Global preset variants (kimi.ai platform + api endpoints) (#7526)
Add a "Kimi Global" twin immediately after the domestic "Kimi" API preset and a "Kimi For Coding Global" twin after the domestic "Kimi For Coding" preset, in every preset file that ships them (claude, claude-desktop, codex, hermes, openclaw, opencode, pi).
"Kimi Global" points to the international open platform https://platform.kimi.ai?aff=cc-switch (apiKeyUrl mirrored as https://platform.kimi.ai/console/api-keys?aff=cc-switch) and swaps the endpoint host api.moonshot.cn for api.moonshot.ai with each file's path unchanged (/v1 or /anthropic).
"Kimi For Coding Global" points to the international site https://www.kimi.ai/code/?aff=cc-switch and swaps the endpoint host api.kimi.com for api.kimi.ai with the path unchanged (e.g. https://api.kimi.ai/coding/v1).
Domestic presets are unchanged; category, icons, apiFormat, model catalogs, endpointCandidates (host swapped), partnerPromotionKey and settings otherwise mirror the domestic blocks exactly.
primePartner stays only on the domestic presets so the new variants do not alter prime-partner ordering.
The Pi twins get distinct providerKey values ("cc-switch-kimi-global", "cc-switch-kimi-for-coding-global") because providerKey is the preset-level primary key and must not collide with the domestic presets.
Rebalance sponsor CTAs around Kimi Code plan with dual-region links (#7522)
Kimi Code plan purchases have resumed, so the README sponsor sections go back to leading with the coding plan instead of the API-only flow written during the purchase pause.
All four locales (EN/ZH/JA/DE) now present Kimi Code plan (中文站 | Global) as the primary CTA and the Kimi Open Platform API (中文站 | Global, with the first-top-up bonus) as the secondary one, mirroring the domestic|global split already used for API links.
The aff and track_id values are unchanged.
fix(prompts): refresh active prompts after external file edits (#7194)
Reload the enabled prompt from its live file when the Prompts list loads, and refresh the open panel when the window regains focus, so external edits to CLAUDE.md / AGENTS.md show up without a restart.
- Keep saved templates available when the live file cannot be read.
- Skip the backfill for empty or whitespace-only files, matching enable_prompt.
- Skip the backfill while a sync/restore holds the sync lock, so a list load cannot write a stale live file into a freshly restored database.
fix(skills): raise archive entry limit so large skill repos install (#7489)
Installing hugohe3/ppt-master from skills.sh failed with
ARCHIVE_TOO_MANY_ENTRIES: the GitHub archive we download is the whole
repository (13,248 entries), and the 10,000-entry cap added in #5811
treated it as a zip bomb.
- Raise MAX_ARCHIVE_ENTRIES to 30,000 for both the remote archive and
local ZIP paths, and align WebDAV's MAX_EXTRACT_ENTRIES so a skills.zip
containing such a skill still restores.
- Charge every file entry at least one 4 KiB block against the existing
512 MiB budget, mirroring directory accounting, so empty-file archives
are bounded by the byte budget rather than by the entry count alone.
- Add tests for a 13k-entry repo archive and for the per-file minimum
charge.
Fixes: https://github.com/farion1231/cc-switch/issues/7475
feat: add MiniMax Code harness support (#7383)
Add MiniMax Code (mcode) as a managed application: provider and model management through the native custom_provider configuration, MCP servers, Skills, global instructions (AGENTS.md), read-only local session history with resume, and usage import from the native runtime database. Native files shared by the TUI and desktop are written under the native lock with rollback when the database commit fails, unrelated native configuration is preserved, and the data directory honors MINIMAX_DATA_DIR and MAVIS_DATA_DIR. Deleting sessions stays in the native application because they are coupled to runtime state.
fix(codex): recover stale account bindings during takeover (#7395)
After deleting a managed ChatGPT account and logging in again, a Codex provider can still reference the old local account ID, which made route switching, takeover, startup restoration and rebinding fail permanently.
Distinguish a missing outgoing account from an existing account with no matching live token. A missing account only releases its ownership marker after the persisted account store is verified; the target write policy stays in force. A stale target now reports an account-selection instruction instead of a bare not-found error, including when takeover is already enabled.
Fixes #7392
feat(claude-desktop): support 3P configuration on Linux (#7331)
Add Claude Desktop 3P configuration support on Linux. Resolve the config root from an absolute XDG_CONFIG_HOME, falling back to ~/.config; when CC Switch itself runs as a Flatpak, use the host ~/.config instead of the sandbox-private XDG_CONFIG_HOME. Update the en/zh/ja user manuals with Linux paths and the Flatpak boundary.
Fixes #4855
fix(proxy): omit missing tool descriptions instead of serializing null (#7319)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(proxy): keep reasoning effort for grok-4.6 and pass xhigh through verbatim (#7318)
Co-authored-by: SailingLoong <309827397+SailingLoong@users.noreply.github.com>
fix(models): support Zhipu OpenAI Responses model lists (#7330)
Zhipu's Responses endpoint (open.bigmodel.cn/api/v1) serves its model list in the Codex remote catalog shape (models[].slug) instead of the OpenAI-compatible data[].id shape, so the Codex form's model fetch came back empty. The parser now falls back to models[].slug when data is absent.
fix(misc): fetch npm dist-tags from the dedicated endpoint with the probe timeout (#7346)
The tool version probe requested the full npm packument for each package (metadata for every historical version, 14-25 MB uncompressed for codex / opencode / openclaw) only to read its dist-tags, and it was the only latest-version probe without LATEST_PROBE_TIMEOUT, so it fell back to the proxy client's 600 s total timeout and the cards sat on "loading" for minutes on slow links while the large downloads starved the Hermes GitHub probe into "unknown".
Switch fetch_npm_dist_tags to registry.npmjs.org/-/package/{package}/dist-tags, whose body is the dist-tags object itself (tens of bytes to a few KB), escape scoped names as %2f, and apply the same 15 s probe timeout as the GitHub and PyPI probes. No data is lost: the only consumer reads latest and the tool's prerelease tags.
Fixes #7339
feat(usage): display output tokens per second in request log table and detail panel (#3369)
Show tokens/s next to the output token count in the request log table and the request detail panel.
The generation window prefers an explicit duration when present, otherwise latency minus first-token time for streaming requests, and falls back to full latency for non-streaming requests. Rows without a positive token count or window show nothing.
Closes #5936
fix(presets): update APIKEY.FUN URLs to apikey.fan
docs(release): add v3.20.3 release notes
chore(release): v3.20.3
fix(pricing): move retired DeepSeek V4 family to the V4.1 Flash tier
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.
fix(presets): declare third-party Token Plan DeepSeek V4 rows text-only
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.
feat(presets): rename DouBaoSeed preset to localized Volcengine Doubao
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.
fix(codex): skip unchanged incomplete rollout tails
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.
fix(claude): align quick model selection with form order
fix(tray): show bound ChatGPT account quota for managed Codex cards
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
feat(claude): add "Disable Artifact Tool" quick toggle to provider editor
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.
feat(codex): refresh aggregator model presets and catalogs
feat(codex): switch Kimi presets to native Responses direct-connect
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.
fix(usage): show Claude Fable weekly limit from the usage API limits array
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.
fix(codex): mirror vision-capable DeepSeek catalog and un-gate the legacy v4-flash alias (#7286)
Refresh the bundled DeepSeek official Codex catalog to the vendor's current models.json (slug deepseek-flash, image input modality, supports_image_detail_original) and remove deepseek-v4-flash from the confirmed text-only registry: the legacy id is still accepted by DeepSeek and routed to the vision-capable V4.1 Flash, so both the proxy media sanitizer and the generated Codex catalog must fail open for it. deepseek-v4-pro stays text-only per the vendor catalog.
Fixes #7283
fix(proxy): clamp sub-floor max_tokens to the Responses API minimum (#7287)
The Responses API rejects max_output_tokens below 16, but Anthropic clients legitimately send tiny probe budgets (Claude Desktop's model-availability probe uses max_tokens=1). The Anthropic→Responses conversion copied the value verbatim, so strict upstreams returned 400 and Claude Desktop reported the mapped model as unavailable under local routing.
Clamp 1..=15 up to 16 during conversion; 16+, 0 and non-integer values keep their pass-through semantics. Normal session requests convert byte-for-byte as before, and the Codex OAuth path still strips max_output_tokens afterwards.
Fixes #7103
fix(codex): coalesce adjacent commentary with pending tool calls (#7280)
The Codex Responses -> Chat Completions converter emitted two consecutive
assistant messages when one Responses model turn contained a commentary
message directly followed by function_call items. Chat upstreams imitate
the text-only assistant message as a complete turn and return
finish_reason=stop before the expected tool call, so long agent tasks end
after a progress update.
Merge pending tool calls into the directly adjacent assistant message when
it carries no tool_calls yet, attach pending reasoning per segment so
parallel calls do not duplicate it, and keep the new-message path for every
other boundary shape (user/tool boundaries, prior tool-call batches, media
flushes). The reasoning placeholder backfill still applies to coalesced
calls without reasoning.
Fixes #6529
Supersedes #6530
Co-authored-by: BigStrongSun <BigStrongSun@users.noreply.github.com>
fix(proxy): skip empty reasoning_content placeholders in Anthropic SSE streaming (#7227)
Some OpenAI-compatible upstreams (GLM, Qwen, DeepSeek-V4-Pro via ModelScope/DashScope,
Bailian, JD Cloud, ...) keep a `reasoning_content: ""` placeholder in every content
chunk instead of omitting the field. The reasoning branch of the OpenAI Chat -> Anthropic
SSE converter only checked for presence, and it shares `current_non_tool_block_type`
with the content branch, so every content chunk closed the open text block, opened an
empty thinking block, then reopened a text block. N content chunks became 2N content
blocks, half of them empty thinking blocks, which Claude Code rendered as one line per
token with dozens of empty Thought blocks and bloated the session JSONL.
Filter empty reasoning before entering the branch, matching the `!content.is_empty()`
guard the content branch already had and the non-streaming `openai_to_anthropic`
converter. Two regression tests pin block index, type and order: the placeholder shape
must yield a single text block and no thinking_delta at all, and real reasoning must
still yield one thinking block followed by one text block.
Fixes #5028
Fixes #4404
fix(codex): detect growing rollouts with a persisted byte cursor (#7219)
Codex keeps the rollout append handle open for the whole session, so on Windows NTFS the file mtime does not advance while the file grows and the pure-mtime gate skipped growing rollouts forever. Persist the observed byte length in session_log_sync.last_byte_offset for Codex rows and skip a file only when both mtime and size are unchanged. The parser now splits records on newlines manually so an incomplete trailing record is retried on the next pass without advancing the line cursor.
Fixes #6060
fix(codex): proxy image edits and Images API follow-ups (#7177)
* fix(codex): proxy image edits and Images API follow-ups
Follow-ups to #7036 for Codex's built-in ImageGen over the local proxy:
- Register `/images/edits` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them through the shared Codex standalone passthrough.
ImageGen posts to `images/edits` whenever a call references existing
images, and the router returned an empty 404 for it.
- Add `CodexStandaloneEndpoint::ImagesEdits`. Both Images endpoints share
one full-URL suffix table, so a complete Responses / Compact / Chat
Completions / Images URL derives the sibling endpoint (and vice versa)
while opaque full URLs keep failing closed.
- Apply the standalone sibling rewrite when the base URL is pasted as a
complete endpoint with the full-URL switch off, mirroring
`codex_chat_base_is_full_endpoint`; standalone requests no longer become
`.../chat/completions/images/generations`.
- Parse Images API streaming usage: `image_generation.completed` carries
`usage` on the event top level, so scan for `usage.input_tokens` before
falling back to the Chat Completions stream parser.
Related #7036
* fix(codex): match standalone URL suffixes case-insensitively
Keep endpoint detection and sibling rewriting consistent while preserving the original URL prefix and query. Cover mixed-case Images and Alpha Search endpoints through URL and proxy regression cases.
---------
Co-authored-by: thisTom <19346741+thisTom@users.noreply.github.com>
Co-authored-by: Jason <farion1231@gmail.com>
fix(proxy): preserve per-app settings on shutdown and port allocation (#7210)
On normal exit, stop_with_restore_keep_state() called the legacy global
proxy-config writer to clear the long-retired live_takeover_active flag.
That writer reads the Claude row and runs an UPDATE without a WHERE clause,
so every shutdown copied Claude's max_retries and the three timeout fields
onto the Codex, Gemini and Grok Build rows — and the UPDATE never carried
live_takeover_active in the first place, so the step had no effect beyond
the clobber. The ephemeral listen-port path did the same on every proxy
start.
Drop the dead write-back (live-config restore, backup cleanup and health
reset are unchanged) and persist the resolved ephemeral port through the
global-config interface, which only touches the shared listen/logging
columns. Two regression tests seed distinct per-app settings for all four
apps and assert they survive both paths.
Fixes #7204
fix(provider): preserve child metadata during universal sync (#7212)
Syncing a universal provider regenerates its Claude/Codex/Gemini child
providers. The child's `settings_config` was merged with the existing row,
but `meta`, `created_at` and `sort_index` were taken from the generated
object, and the universal provider never carries any of them from the UI.
The UPDATE therefore wiped the child's usage script, common-config opt-out,
endpoint auto-select and other per-app settings to `{}` and reset the sort
index to NULL, which pushed the card to the bottom of the list and looked
like the provider had been deleted and re-created.
Keep the existing child's `meta`, `created_at` and `sort_index` when the
row already exists. Name, base URL, key, models, website and notes are
still driven by the universal provider, and first-time creation still
inherits the parent's metadata.
Fixes #7134
feat(presets): rebrand DashScope as 千问AI平台 and refresh Qwen models (#7183)
Rename the domestic Bailian presets to 千问AI平台 with a dedicated icon, refresh the Qwen family to the 3.8 generation across Claude Code, Claude Desktop, Codex, Hermes, OpenClaw, OpenCode and Pi, add the domestic Token Plan presets, restore the international QwenCloud Coding Plan presets, and align OpenClaw/OpenCode/Pi model metadata with the official client docs.
fix(codex): honor proxy URL when model_provider is omitted (#7263)
When a Codex provider card's TOML has no `model_provider`, Codex falls back to
its built-in `openai` provider, but takeover wrote the proxy address as a
top-level `base_url` that Codex never reads, so requests bypassed the local
proxy and went straight to api.openai.com.
Treat the missing selector as the built-in `openai` provider and write the
proxy address to `openai_base_url`; the existing legacy-reroute migration then
turns it into a `cc-switch` custom provider table carrying the `PROXY_MANAGED`
bearer, the same shape every other third-party takeover uses. Explicit
provider selections are unchanged.
Refs #6256, #7217
fix(provider): update MiniMax defaults to M3 and remove expired offers (#7255)
* fix(provider): refresh MiniMax M3 presets and remove expired promotions
* fix(provider): complete MiniMax M3 capability declarations
---------
Co-authored-by: saladday <1203511142@qq.com>
chore(provider): remove Atlas Cloud sponsor status, keep presets
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`
docs(release): add v3.20.2 release notes
chore(release): v3.20.2
feat(provider): add SoleAPI sponsor presets across all nine apps
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.
fix(codex): proxy image edits endpoint
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.
fix(provider): pin models URL for JieKou and Novita Claude presets
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.
feat(pricing): refresh seed for September 2026 vendor price changes
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.
fix(codex): stamp requires_openai_auth on takeover writes to match the live login state
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.
fix(updater): show the real reason when an update check fails (#6482)
The updater plugin rejects with a plain string, so `err instanceof Error`
never matched and the real failure reason was replaced by the generic
fallback message. Use the existing extractErrorMessage helper, and pass
the recovered message into the check-update failure toast so it actually
reaches the user instead of stopping at an unread context state.
Co-authored-by: Jason <farion1231@gmail.com>
docs: fix the locales path in the README directory trees (#6100)
All four READMEs showed locales/ as a direct child of src/, but the
translations live at src/i18n/locales/. Split it into two tree nodes so it
matches how the rest of the tree is drawn. Also fixes a CLaude -> Claude
typo in README_ZH.md.
Co-authored-by: Jason <farion1231@gmail.com>
fix(claude): exclude workflow journal.jsonl from session list
Claude Code's workflow feature writes subagents/workflows/wf_*/journal.jsonl inside the session directory. The Claude session scanner only excluded agent-* files, so each journal parsed as an empty ghost session titled "journal". Exclude journal.jsonl by file name and add regression tests.
Fixes #6042
fix(i18n): add the missing pi.form.providerKeyDuplicate and common.collapse keys
translatePiProviderMutationError returns pi.form.providerKeyDuplicate for the Pi backend's duplicate-key error, but the key existed in none of the four locale files, so the toast showed the raw key. common.collapse (aria-label of the thinking-map toggle) was missing the same way. Add both keys to zh/en/ja/zh-TW and lock them in the locale coverage test.
fix(proxy): stop mask_url panicking on multi-byte UTF-8 near the truncation boundary
mask_url's fallback branch (used when the proxy URL fails Url::parse) sliced the raw string at byte 20 and panicked when a multi-byte UTF-8 character straddled that offset, e.g. a CJK string pasted into the global proxy address. Snap the cut to the nearest char boundary at or before 20 and add a regression test.
fix(tests): isolate LOCALAPPDATA so tests stop writing the real Claude Desktop config
On Windows, Claude Desktop config paths resolve from LOCALAPPDATA, which the test home override in tests/support.rs did not redirect, so profile_roundtrip wrote the https://desktop.test fixture into the developer's real Claude Desktop config. Point LOCALAPPDATA at the isolated test home alongside HOME and USERPROFILE.
Fixes #6077
fix(usage): widen pricing model source select to fit localized labels
The pricing model source SelectTrigger in the usage cost settings table was w-28, which leaves about 70px for the label; the English and Japanese labels need about 107px and overflowed the trigger. Widen it to w-40 and drop the h-7 overrides so the trigger and the adjacent Input use the shared h-9 height.
feat(pi): add Tencent TokenHub and Token Plan presets (#7159)
Add eight Tencent presets for Pi, covering the TokenHub pay-as-you-go line
(domestic and international) and the six Token Plan subscription products —
Pi was the only managed app still missing them. Model IDs, ordering and
endpoints follow Tencent's own model lists verbatim, and the tests assert each
product's line-up in order so future drift is caught.
Declare thinking control alongside the capabilities rather than leaving it to
defaults: the 31 DeepSeek entries carry a Tencent-specific compat so selecting
"off" actually disables thinking instead of silently billing reasoning tokens,
the eight Kimi K2.7 Code entries no longer offer an "off" level the model
cannot honour, and kimi-k2.7-code-highspeed is marked image-capable so
tool-returned screenshots are no longer dropped.
Drop minimax-m2.5 from every app: it is gone from Tencent's model tables and
the platform is retiring it.
fix(codex): proxy image generations endpoint (#7036)
Codex's built-in ImageGen calls the legacy OpenAI Images API, but the local
proxy only registered Responses / Chat Completions / Compact / Alpha Search
routes, so `/v1/images/generations` returned an empty 404 under Codex routing.
- Register `/images/generations` aliases (bare, `/v1`, `/v1/v1`, `/codex/v1`)
and forward them as a Codex standalone passthrough shared with Alpha Search.
- Generalize the Alpha Search full-URL rewrite into
`rewrite_codex_standalone_full_url`: derive the sibling
`/images/generations` from full Responses / Compact / Chat Completions URLs,
keep an existing Images URL as-is, and fail closed on opaque full URLs.
- Record `usage.input_tokens` / `output_tokens` through the Codex usage parser.
Fixes #5429
Fixes #6745
feat(pricing): add Gemini 3.8 Flash pricing (#7164)
Seeds the built-in pricing row for Gemini 3.8 Flash at 0.75 / 3.75 input/output per million tokens, 0.075 cache read, no cache-write charge, plus a regression test locking all four fields.
Values verified against Google's own pricing page (introductory price through 2026-12-31; list price 1.50 / 7.50 / 0.15 from 2027-01-01) and models.dev.
feat(provider): extend PPIO presets for Pi (#6870)
Add a PPIO provider preset for Pi using the OpenAI-compatible endpoint,
and pin an explicit models URL on the Claude Code PPIO preset so model
discovery resolves against https://api.ppio.com/openai/v1/models instead
of the derived candidates, which all return 404.
Co-authored-by: Jason <farion1231@gmail.com>
feat(pricing): add GLM-5.3 Flash pricing (#7163)
fix(proxy): enable parallel tool calls for Codex OAuth (#7024)
Codex OAuth requests defaulted parallel_tool_calls to false when the incoming
Anthropic request carried no explicit value, forcing Claude Code into one tool
call per model turn. Default it to true (matching codex-rs), map Anthropic
tool_choice.disable_parallel_tool_use to the inverse Responses value so explicit
serial execution is preserved, and add regression coverage for both directions.
Carries #5722 forward on top of current main.
Fixes #5719
Co-authored-by: 太一 <taiyi@douyu.tv>
feat(pricing): add GPT-6 Astra pricing (#7162)
fix(codex): resolve input_modalities for unknown models in vendor catalog (#6750)
When a model slug is not in the bundled DeepSeek official catalog, the vendor path clones the flagship entry and previously inherited its text-only input_modalities. Resolve modalities for unmatched slugs through the same registry/fail-open logic the non-vendor path uses, so a vision variant such as deepseek-v4-flash-vision-exp is no longer declared text-only. Explicit user inputModalities still win; matched models keep the vendor's declaration verbatim.
Fixes #6725
fix(claude): expose Opus 5 and Sonnet 5 in takeover mode (#5882)
Bump the stable client-facing role aliases that takeover mode writes to
Claude Code: Opus claude-opus-4-8 -> claude-opus-5 and Sonnet
claude-sonnet-4-6 -> claude-sonnet-5, so the /model menu matches the
Claude Desktop default routes and preset defaults that already moved to
Opus 5 / Sonnet 5. Add "opus-5" to the adaptive-thinking classifier so
the Bedrock thinking optimizer, which runs on the client alias before
model mapping, keeps sending adaptive thinking instead of the removed
budget_tokens.
Routing is unaffected: the proxy maps client aliases by role keyword, and
the aliases are rewritten automatically the next time the proxy starts.
Fixes #5876
Co-authored-by: Jason <farion1231@gmail.com>
feat(pricing): add GLM-5.3 to built-in model pricing table (#6591)
Seed glm-5.3 at $1.4/$4.4 per MTok with $0.26 cache read, matching the
official Z.ai list price (identical to the GLM-5.2 tier):
https://docs.z.ai/guides/overview/pricing
New model id, so seed only; no repair entry and no SCHEMA_VERSION bump.
Existing installs pick the row up on next launch via INSERT OR IGNORE, and
the startup cost backfill re-prices historical glm-5.3 rows recorded at $0.
fix(codex): align OAuth client identity for…
Summary / 概述
修复 Codex 本地路由模式下内置 ImageGen 调用 legacy OpenAI Images API 时返回 404,或在
is_full_url = trueprovider 下被误转发到 Responses / Chat Completions URL 的问题。Codex 的图片生成会请求
/v1/images/generations。此前本地 proxy 只注册了 Chat Completions、Responses、Responses Compact 和 Alpha Search 等 Codex 端点,Images generations 请求会在路由层直接 404,无法转发到当前选中的 Codex provider。同时,如果当前 Codex provider 配置为 full URL(例如完整的
/v1/responses、/responses/compact或/v1/chat/completions地址),普通 full-URL 分支会复用配置 URL,导致 ImageGen payload 被发送到聊天/Responses 端点,而不是 sibling/v1/images/generations。本 PR 将 Codex standalone endpoint 的 full URL 改写收敛到同一 helper:仅对可明确推导 sibling endpoint 的 URL 进行改写,已指向/images/generations的 full URL 会保留原路径并合并 query,无法安全推导的 opaque full URL 会 fail closed,避免把图片请求误发到无关端点。请求仍走当前 Codex provider 的鉴权、模型映射、重试和响应透传;返回中的
usage.input_tokens/usage.output_tokens也会通过 Codex usage parser 正确记录。/v1/images/generations/v1/images/generations/images/generations、/v1/v1/images/generations、/codex/v1/images/generations不可用/v1/responses、/responses/compact或/v1/chat/completionsprovider 会把 ImageGen payload 继续发送到原聊天/Responses URL/v1/images/generations;已有 Images full URL 保持路径并合并 queryinput_tokens/output_tokens无法通过 Chat Completions parser 正确记录Related Issue / 关联 Issue
Fixes #5429
Fixes #6745
Screenshots / 截图
imagegen技能时,/v1/images/generations在 proxy 路由层返回404 Not Found,无法生成图片。Testing / 测试
cargo test --manifest-path src-tauri/Cargo.toml proxy::server::tests::codex_images_generation_aliases_forward_and_record_usage -- --exact/v1/responses改写到/v1/images/generationscargo test --manifest-path src-tauri/Cargo.toml proxy::forwarder::tests::images_generations_rewrites_known_full_codex_urls -- --exactcargo test --manifest-path src-tauri/Cargo.toml proxy::forwarder::tests:: -- --nocapturecargo fmt --check --manifest-path src-tauri/Cargo.tomlcargo clippy --manifest-path src-tauri/Cargo.toml -- -D warningscargo test --manifest-path src-tauri/Cargo.toml -- --skip services::provider::tests::update_current_claude_desktop_provider_syncs_profile_when_proxy_takeover_is_activecargo test --manifest-path src-tauri/Cargo.tomlai-manage占用127.0.0.1:15721,导致一个固定默认端口的既有测试update_current_claude_desktop_provider_syncs_profile_when_proxy_takeover_is_active绑定失败;该失败与本 PR 修改路径无关sol+qwen生图模型完成 ImageGen 调用,图片可正常生成并展示git diff --checkpnpm typecheck/pnpm format:checkChecklist / 检查清单
pnpm typecheckpasses / 通过 TypeScript 类型检查(N/A:未修改前端或 TypeScript 文件)pnpm format:checkpasses / 通过代码格式检查(N/A:未修改前端或 TypeScript 文件)cargo clippypasses (if Rust code changed) / 通过 Clippy 检查(如修改了 Rust 代码)