Repository navigation
fix(i18n): add the missing pi.form.providerKeyDuplicate and common.collapse keys - #6768
Conversation
|
@codex review |
|
Codex Review: Didn't find any major issues. Already looking forward to the next diff. 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". |
…llapse
`translatePiProviderMutationError` returns `t("pi.form.providerKeyDuplicate")`
for the duplicate-key error the Pi backend raises, and that key existed in none
of the four locales. `fallbackLng: "en"` cannot help, because `en` lacked it too,
so the toast rendered the raw key:
Failed to add provider: pi.form.providerKeyDuplicate
`common.collapse`, the aria-label of the thinking-map toggle in
`PiProviderForm`, was missing the same way.
It is a one-key omission rather than a missing namespace: the sibling branch one
line above (`pi.provider.writeConflict`) resolves in all four locales, and the
same duplicate-key string already exists for OpenCode, OpenClaw and Hermes. The
wording follows Hermes', which is the closest match.
The existing `errorUtils` test mocks `t` as the identity function and asserts the
return *is* the string `"pi.form.providerKeyDuplicate"`, which is why this stayed
green while users saw the key. Both paths are added to the `requiredPaths` list
in `managementListLocales`, which resolves against the real locale files.
da4998e to
bd00b4c
Compare
farion1231
left a comment
There was a problem hiding this comment.
Thanks for the fix. Confirmed end to end: loading the four locale files from main into a real i18next instance with the app's config, the duplicate-key toast rendered as 添加供应商失败:pi.form.providerKeyDuplicate (i18next returns the missing key verbatim as a non-empty string, so the || detail fallback never kicks in); with this branch it becomes …该供应商标识已存在 / This provider key already exists, and the toggle's aria-label resolves to 收起 / Collapse. I also scanned every literal t("…") key under src/ against the four JSON files — these two were the only missing ones. The requiredPaths guard is real: reverting the locale files while keeping the guard turns 7 cases red.
One optional nit: in zh-TW the Pi form labels the field 供應商識別碼, while the new message follows Hermes' 供應商標識. Either is fine — just flagging in case you want them to match.
Merging. Thanks!
|
Thanks for the thorough verification — and you were right about the nit. I checked the other three locales and zh-TW was the only one where the form's label and its messages used different terms:
So it was zh-TW out of step rather than a choice worth keeping. #7187 changes that one string to 該供應商識別碼已存在. |
…sage (#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.
…sage (farion1231#7187) Follow-up to the nit on farion1231#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.
…sage (farion1231#7187) Follow-up to the nit on farion1231#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. (cherry picked from commit de970c1)
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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> fix(proxy): keep reasoning effort for grok-4.6 and pass xhigh through verbatim (#7318) Co-authored-by: SailingLoong <[email protected]> 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 <[email protected]> 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 <[email protected]> Co-authored-by: Jason <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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: 太一 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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
The defect
translatePiProviderMutationErrorreturns a translation key for the duplicate-key error the Pi backend raises (src/utils/errorUtils.ts:55-57):That key exists in none of the four locale files.
fallbackLng: "en"cannot rescue it, becauseenlacks it too, somutations.ts:144renders the raw key into the toast:Resolved against the real JSON, before this change:
common.collapse— the aria-label of the thinking-map toggle atPiProviderForm.tsx:1747— was missing in all four the same way, so it rides along.The backend really does emit the matching string:
src-tauri/src/services/provider/pi.rs:51and:58.Why it is a one-key omission, not a missing namespace
The branch one line above resolves everywhere, and the same duplicate-key message already exists for three other providers:
The wording follows Hermes', which is the closest match — "This provider key already exists" / 该供应商标识已存在 / 該供應商標識已存在 / このプロバイダーキーは既に存在します.
Why the existing test did not catch it
tests/utils/errorUtils.test.ts:22-32mockstas the identity function and asserts the return is the string"pi.form.providerKeyDuplicate". It passes whether or not the key resolves, which is exactly how this survived.So the guard belongs where keys are resolved against the real files: both paths are added to
requiredPathsintests/config/managementListLocales.test.ts, which already walks that list across all four locales.Verification
Reverting only the locale files, with the new guard in place, fails all four locale cases with
expected [ Array(2) ] to deeply equal []— so the guard pins the keys rather than passing incidentally.The diff is additive: 4 insertions / 2 deletions per locale file, the two deletions being the trailing-comma lines the insertions follow. No reformatting.