DeepSeek Chat defines cache hits through the documented fields
prompt_cache_hit_tokens / prompt_cache_miss_tokens. CC Switch previously
only read the standard/OpenAI-compatible cache fields, so responses that
omit prompt_tokens_details.cached_tokens lost cache-hit accounting. This
adds the DeepSeek-documented field as a last-resort fallback in both the
usage parser (proxy_request_logs / usage page) and the Codex
chat-to-Responses usage synthesis (shared by streaming and non-streaming
paths).
The official endpoint currently also mirrors the value into the
undocumented prompt_tokens_details.cached_tokens (verified 2026-08-05
against api.deepseek.com), which is why existing accounting was
unaffected there. The fallback aligns with the documented contract,
defends against the undocumented mirror disappearing, and covers relays
that only pass through the documented fields. Behavior is unchanged
whenever the upstream sends any standard cache field — the DeepSeek
field sits last in the or_else chain.
Refs #6073
Token Plan (personal) replaced Coding Plan for new purchases on
2026-07-13; existing Coding Plan presets stay untouched since active
subscriptions remain valid until expiry.
- claude/claude-desktop: /anthropic/tokenplan/personal, all roles on
deepseek-v4-pro per the official Claude Code integration guide
- codex: /v2/tokenplan/personal via openai_chat local routing with a
six-model catalog; first Qianfan preset with supportsEffort enabled
(no alias indirection anymore, and the default model is on the
official reasoning_effort support list) using effortValueMode
"deepseek", which matches Qianfan's own low/medium->high,
xhigh->max compatibility mapping verbatim; deepseek models omit
defaultReasoningLevel so the fallback (max) matches the platform's
automatic max for agent requests
- opencode/hermes: full six-model lineup on the OpenAI-compatible
endpoint per the official integration guides
- openclaw: model entry mirrors the official OpenClaw guide verbatim
(context 98304 is the vendor's OpenClaw-specific figure, not the
platform model list's 1M)
ernie-5.1 is excluded (official retirement on 8/20). New test locks
endpoints, lineups and OpenClaw figures across five apps; codex is
covered by the existing chat preset and reasoning level tests.
2026-08-15 audit, Kimi group. Both Kimi presets declared
supportsEffort:false, yet Moonshot's two official Codex integration
guides - which use CC Switch as their worked example - explicitly
instruct users to enable both "支持思考模式" and "支持推理强度/思考
等级". The missing piece was the top-level reasoning_effort string
(low/high/max), the only reasoning control the K3 family supports.
Open Platform preset (api.moonshot.cn/v1):
- supportsEffort:true + effortParam reasoning_effort; effortValueMode
stays unset (passthrough - the deepseek map would crush low to high)
- kimi-k3: low/high/max. No explicit default: the template default
medium is outside the subset so the backend falls back to the highest
tier, max, which is exactly the vendor default
- kimi-k2.7-code: single-tier ["high"] (always-thinking, effort
documented as unsupported - single tier avoids the template's fake
differentiated tiers, LongCat precedent)
Kimi For Coding preset (api.kimi.com/coding/v1):
- same declaration change; catalog extended with the full official
model table: kimi-for-coding-highspeed (262144), k3 (1048576),
k3-256k (262144)
- k3/k3-256k: low/high/max with explicit defaultReasoningLevel high -
first use of an explicit default: the vendor default (high) differs
from the fallback (max), and max would silently burn more tokens
- no none tier anywhere: k2.7/k3 cannot disable thinking, and on the
Kimi Code gateway disabling reroutes requests to K2.6 (different
model, different billing). Gateway effort whitelist rejects unknown
values with 400 (minimal is not in it); the declared subsets keep the
picker inside the whitelist
Known doc conflict noted inline: the parameter reference says K3 should
not receive the thinking param while the integration guide enables
thinking mode; production shows no failures, so we follow the guide -
first thing to check if users report Kimi 400s.
Test tuple gains an optional expected-default column.
2026-08-15 vendor audit, official-switch group:
- Baidu Qianfan Coding Plan: the v2 Chat API documents top-level
thinking:{type:enabled/disabled} (same shape as Zhipu) across the
plan's six main models, and Baidu's own OpenCode integration guide
sends it on the /v2/coding endpoint. Declaration added (with the
supportsThinking:true flag that gates the whole injection path) plus
none/high levels. Known alias caveat noted inline: if the console
resolves qianfan-code-latest to a non-thinking model the field is
ignored, not rejected (platform documents unsupported params as
ignore-not-error).
- BytePlus (ModelArk intl): the "not verified for the intl site"
comment is obsolete - BytePlus' own Codex guide configures this exact
coding/v3 endpoint with wire_api="responses". Switched apiFormat to
openai_responses (aligned with the domestic Volcengine Plan presets,
one less conversion layer) and pre-filled low/medium/high per the
official Codex doc, with the glm-5-2 tier-collapse caveat noted.
Test moves BytePlus from the Chat-route list to the native Responses
list; reasoning-level expectations extended for both presets.
2026-08-15 vendor audit, aggregator group. All three presets declared
the Zhipu-style top-level thinking:{type} object, which is a model
vendor dialect their platforms never documented:
- ModelScope: platform switch is the top-level enable_thinking bool
(official model-page extra_body samples + Bailian first-party GLM
docs). thinkingParam fixed; levels stay unset until field passthrough
to the upstream is verified.
- Novita: same fix - the Create-chat-completion parameter enum has no
thinking object; enable_thinking is documented platform-wide. GLM-5.1
catalog row gains none/high (two-hop inference, noted inline).
- Nvidia NIM: fake switch withdrawn. The per-model official OpenAPI
(additionalProperties:false) has no top-level thinking field; the
real control chat_template_kwargs:{thinking:bool} is outside our
thinkingParam value range, so the declaration becomes an explicit
no-injection block (supportsThinking:false, thinkingParam:none).
All three keep their codexChatReasoning block on purpose: deleting it
would fall through to the backend name/model inference, which matches
"glm"/"kimi" in the model id and would resurrect the wrong dialect.
A new modelscope branch in infer_aggregator_platform_config covers
hand-created ModelScope providers the same way the siliconflow branch
does (platform identity beats model-vendor hints), with a unit test.
The StepFun inference block predated step-3.7-flash and only granted
reasoning_effort to models containing "2603". Per the official
reasoning guide and model pages (both .com and .ai sites, 2026-08-15
audit), step-3.7-flash supports low/medium/high (default medium) while
step-3.5-flash-2603 supports low/high; the suffix-less step-3.5-flash
exposes no effort control.
- supports_effort now also matches step-3.7-flash
- effort_value_mode is split per model: 2603 keeps the low_high
collapsing map; 3.7-flash uses passthrough - reusing low_high would
collapse medium into high, creating tiers with no wire difference
- the fix stays in the inference table rather than a preset-level
codexChatReasoning declaration, which is provider-wide and would lose
the per-model gating (suffix-less step-3.5-flash must not receive
reasoning_effort - its behavior is undocumented)
- StepFun preset catalogs pre-fill the vendor-documented tiers
(3.7-flash low/medium/high, 2603 low/high, suffix-less unset); no
none tier anywhere - StepFun documents no way to disable thinking
- new unit test locks the three per-model inference outcomes
2026-08-15 vendor audit (official catalogs, cross-checked live):
- SiliconFlow: MiniMax-M2.7 was never listed on either site - users got
400 "Model does not exist" out of the box. The two sites carry
different MiniMax lineups and must not mirror each other:
- .cn -> Pro/MiniMaxAI/MiniMax-M2.5 (catalog contextLen 196608);
reasoning levels left unset (no official evidence M2.5 thinking can
be disabled via enable_thinking)
- .com -> MiniMaxAI/MiniMax-M3 (official 1M window = 1048576);
reasoningLevels none/high (platform enable_thinking switch is real
and M3 thinking is documented as disableable)
- ModelScope: bare ZhipuAI/GLM-5.1 id is not in the free API-Inference
service list (only the BYOK :DashScope variant exists) - replaced
with ZhipuAI/GLM-5.2 which is on the free community route. Levels
left unset until field passthrough to the upstream is verified.
Applied across all seven apps carrying these presets (claude,
claude-desktop, codex, hermes, openclaw, opencode, pi), plus the
hardcoded model id in the SiliconFlow platform-inference Rust test and
the context-window test assertions. Bare MiniMax-M2.7 ids (MiniMax's
own official endpoint) are a different platform and intentionally
untouched. Pi catalog key moved to the existing zai/glm-5.2 entry.
Known follow-up: openclaw SiliconFlow cost figures still reflect the
old M2.7 pricing and need a separate re-audit.
Second round of the #6228 preset pre-fill, reversing the round-1
"deliberately unfilled" list per product ruling: an "unset" reasoning
levels field in the provider form is more misleading than a redundant
or snapshot-style declaration.
- DeepSeek direct (flash + pro): low/high/max, copied from the official
catalog mirror. Per-row values override the mirror, so the preset must
be kept in sync if DeepSeek's catalog changes (noted inline).
- MiniMax x2 and Xiaomi MiMo x2 (both models): none/high, equal to the
template default - zero behavior change, declared only so the form
shows the real tiers.
- Zhipu GLM x2 (Chat route): none/high two-state. Also fixes a real
gap: the gpt5_5 template default tiers (low/medium/high/xhigh) had no
none, so users could not disable thinking from Codex's picker even
though GLM's thinking:{type:"disabled"} switch is real; the other
template tiers were fake differentiation (supportsEffort: false).
Still unfilled: Bailian qwen3-coder-plus (no per-model evidence) and
OpenCode Go (multi-vendor Chat gateway without a codexChatReasoning
declaration - the thinking toggle is inert there, so a none tier would
be a fake switch).
Test file extended to 20 assertions covering the new fills and the
updated unfilled list.
Follow-up to #6228 per-model reasoning levels: pass reasoningLevels /
defaultReasoningLevel through the modelCatalog() preset helper and
pre-fill the enums documented by each vendor for native Responses
presets, so known models ship with their real effort tiers out of the
box while remaining user-editable per provider.
Filled (official-doc evidence cited inline):
- Volcengine Agent/Coding Plan ark-code-latest: low/medium/high
- DouBaoSeed doubao-seed-2-1-pro-260628: minimal/low/medium/high
- Tencent Hunyuan hy3 & hy3-preview: low/high
- Longcat LongCat-2.0: high (single tier)
- xAI Grok (API & OAuth) grok-4.5: low/medium/high
Deliberately left unfilled (comments guard against "completing" them):
DeepSeek direct (official catalog mirror already declares low/high/max),
MiniMax & Xiaomi MiMo (official catalog equals template default,
zero-effect fill), Bailian qwen3-coder-plus (no per-model evidence).
defaultReasoningLevel is never declared: the backend fallback lands on
high for every filled entry. Chat-route presets are out of scope since
effort only toggles thinking there (supportsEffort: false).
Adds tests/config/codexReasoningLevelPresets.test.ts locking the
expected enums, the unfilled list, and a canonical-effort whitelist
over all presets (unknown values are silently dropped downstream).
* Add managed ChatGPT account binding for Codex
* Consolidate managed account entry points
* Clarify unmanaged Codex login option
* fix(provider-form): show Codex FAST mode toggle in account-select mode
The FAST mode switch was gated on mode === "manage", but the Claude and
Claude Desktop Codex OAuth forms render CodexOAuthSection in select mode
while still passing onFastModeChange. The toggle was therefore hidden:
new providers couldn't enable FAST mode and existing ones couldn't turn
it off, even though the backend still honors meta.codexFastMode. Gate the
toggle on onFastModeChange presence instead, restoring the pre-refactor
behavior without affecting the manage panel or native Codex form (which
do not pass the handler).
* fix(i18n): localize Codex managed-account none option label
ProviderForm passed a hardcoded Chinese string as codexOauthNoneOptionLabel,
so en/ja/zh-TW users saw Chinese text in the Codex account selector's
"none" option. Add codexOauth.noneOptionLabel to all four locales and
resolve it via t().
* feat(codex): write managed ChatGPT id_token into live auth + reauth prompt
Bind the selected ChatGPT account's id_token into the Codex official
managed auth.json so it matches a native browser login for the fields
that drive behavior (auth_mode, account/plan/email via id_token claims).
- Persist id_token on CodexAccountData; capture at login and on refresh
(empty string treated as missing). Add get_valid_token_and_id_token_for_account.
- codex_managed_oauth_live_auth now writes tokens.id_token when available.
- Keep the managed-vs-native safety marker intact: extract_codex_managed_oauth_auth
tolerates id_token but still rejects native logins (which carry refresh_token /
top-level last_refresh), so cc-switch never clears a real browser login.
refresh_token and last_refresh are intentionally NOT written for this reason.
- Surface reauth_required (account has no stored id_token) through
GitHubAccount -> ManagedAuthAccount -> TS. Legacy accounts get a styled
amber prompt + one-click re-login (device flow) in CodexOAuthSection,
flagged in both the selector dropdown and the select-mode hint.
- i18n: reauth strings added for en / ja / zh / zh-TW.
Verified: pnpm typecheck + build:renderer green; cross-model review of Rust
correctness, the managed-vs-native safety invariant, and the id_token lifecycle.
* style(codex): satisfy prettier format check for reauth UI
* fix(i18n): add missing managed-account selector keys
The managed OAuth account selector referenced codexOauth.chatgptAccount,
codexOauth.manageAccounts, copilot.githubAccount, copilot.manageAccounts and
providerForm.providerKeyStatusLoading with Chinese defaultValue fallbacks, so
the en/ja/zh-TW UIs rendered Chinese text. Add the translations across all four
locales.
Addresses review feedback on #3879.
* fix(providers): don't clear managed OAuth binding on status-query failure
useManagedAuth now exposes isStatusSuccess. The account-clear effect in the
Codex/Copilot selector sections only unbinds when the status query has
*successfully* loaded and the bound account is genuinely gone. Previously a
failed or still-pending query yielded an empty accounts array, which silently
cleared the selection; the subsequent save then dropped providerType/authBinding
and turned a managed provider into a broken unbound config.
Addresses review feedback on #3879.
* fix(codex-oauth): make managed accounts safe & usable with the bare CLI
Addresses review feedback on #3879. These backend changes are deeply
intertwined across shared files, so they are committed together.
Refreshable managed auth (bare `codex` support):
- Write a full, native-shaped auth.json bundle (tokens.refresh_token + top-level
last_refresh) instead of an access-only token, so the Codex CLI can self-refresh
and the managed account keeps working past ~1h without a proxy.
- last_refresh reflects the access token's real obtained-at time (not write time),
so the CLI doesn't treat a cached token as freshly refreshed.
- Read back the CLI-rotated refresh_token from ~/.codex/auth.json before writing
(account_id-matched, chatgpt-mode guarded) so re-switching doesn't clobber the
CLI's valid login with a stale refresh_token.
Never persist managed tokens at rest:
- Backfill of a managed provider always replaces live auth with the stored
placeholder (even on marker mismatch), so native tokens can't leak into the
provider's DB config.
- Sanitize managed auth out of Live backups on the initial/strict snapshot paths
and in update_live_backup_from_provider_inner before serialization.
Concurrency / blocking:
- Drop the redundant outer Arc<RwLock<CodexOAuthManager>> (all methods are &self)
so token refresh no longer holds a coarse lock across the network; give OAuth
token/device requests a 30s timeout instead of the shared 600s client default.
- Add a persistence lock and linearize the access-token cache under a consistent
accounts -> access_tokens order (existence-checked reads/writes; remove/clear
clear the cache atomically) to prevent stale or resurrected cache entries.
Switch ordering:
- Preflight the managed token before committing the current provider on
switch/add/update, so a failed token fetch can't leave DB/UI pointing at a
provider whose live config was never written.
Cleanup:
- Remove the now-dead content-fingerprint marker machinery; managed writes are
now shape-identical to a native login, so ownership is judged by account_id.
Known, documented limitations (B-scheme, narrow & recoverable): a remove+re-login
of the same account within an in-flight refresh window (ABA), a login authorized
after logout, and switch-away cleanup being unable to distinguish our write from a
user's native login of the same account.
* fix(codex-oauth): keep managed token in restore backup; fix failing tests
CI caught three test failures from the previous commit. Root cause: the Live
backup (proxy_live_backup) is local restore state that is replayed to
~/.codex/auth.json when proxy takeover ends, so it must contain the real auth to
restore a working login. Stripping it (the earlier "don't leak into backup"
change) broke restore and over-stripped a user's native login.
- Revert the backup auth stripping: remove sanitize_codex_backup_auth from the
initial/strict snapshot paths and the forced auth={} in
update_live_backup_from_provider_inner. The managed token belongs in the
restore backup (the refresh_token is already persisted by CodexOAuthManager, so
this is not a new exposure).
- Restore the ownership marker (account_id + access_token fingerprint) so backup
cleanup can tell our managed write from a user's native `codex login` of the
same account. extract now also tolerates the full-bundle shape (refresh_token +
last_refresh) so it fingerprints ①'s refreshable writes. clear_codex_auth_in_backup
and clear_codex_live_auth_for_managed_account use the marker again; the
account_id-only helper is dropped.
- Fix the adopt unit test: adopt now invalidates the cached access token, so the
test asserts the stored refresh_token/id_token were updated and the cache was
cleared instead of reading it back through get_valid_token_bundle (which would
trigger a network refresh).
Token stays out of the exported provider settings_config (backfill strip) — only
the local restore backup keeps it, matching pre-existing behavior.
* fix(codex): harden managed OAuth credential lifecycle
* fix: preserve Codex restore auth state
* feat(codex): support multiple official accounts
* feat(codex): refine official account login selection
Keep native Codex login separate from managed ChatGPT accounts, migrate provider identity safely when switching modes, and cover the account-selection states with regression tests.
* style(codex): satisfy clippy for login guard
---------
Co-authored-by: Jason <farion1231@gmail.com>
Co-authored-by: YUZHEthefool <2804776511@qq.com>
* feat(codex): support per-model reasoning levels in model catalog
The generated Codex model catalog hardcodes supported_reasoning_levels to
none/high from the native template, so users of gateways that accept more
effort levels (e.g. LiteLLM serving deepseek-v4-flash with
low/medium/high/xhigh/max) cannot select anything above high in the Codex
UI.
Add optional per-model reasoningLevels / defaultReasoningLevel fields to the
model mapping. The backend applies them on top of the native template and the
official vendor catalog (unknown efforts are dropped, and the default falls
back to the template/vendor default when still in the list, otherwise the
highest declared level). The model mapping rows now expose a reasoning-level
multi-select plus a default-level picker.
Fixes#6181
* fix(codex): validate reasoning default against canonical supported set
Address bot review feedback:
- Validate every default candidate (explicit value, template default,
fallback) against the canonical filtered efforts so a typo'd level can
never become default_reasoning_level while missing from
supported_reasoning_levels.
- The fallback now picks the highest supported level in canonical order
instead of the last declared entry, and supported_reasoning_levels
always emits canonical order regardless of declaration order.
- Drop the misleading "Auto (highest selected)" placeholder: the backend
prefers the template default while it stays supported, so the label is
now just "Auto" in all four locales.
* fix(proxy): map ultra reasoning effort instead of dropping it in codex transforms
The catalog UI now offers all eight canonical levels, but the proxy
transform layer predates ultra and silently dropped it:
- Chat transform: passthrough mode now forwards ultra verbatim (the
per-model reasoningLevels declaration vouches for upstream support,
same as the existing max/xhigh handling); deepseek and openrouter
modes clamp it to their highest legal tier (max / xhigh) instead of
dropping the effort parameter entirely.
- Anthropic transform: ultra now maps to the max thinking budget;
previously it fell through to None, so picking the deepest level
disabled extended thinking altogether.
* fix(ui): keep codex reasoning levels in canonical order and allow resetting default to Auto
- The reasoning-levels editor now stores selections in canonical
ascending-depth order instead of click order, matching the order the
generated catalog and the Codex picker use.
- The default-level select gains an explicit Auto item (Radix Select
forbids empty item values, so a sentinel maps back to undefined);
previously a chosen default could not be reverted without unchecking
and rechecking the level.
- Tests: cover duplicate level declarations in the canonical
normalization test.
* fix(ui): raise nested default-level menu above the reasoning popover
The enclosing reasoning-levels popover uses z-[1000] while SelectContent
defaults to z-[100], so the default-level menu rendered behind the panel
when it flipped upward near the viewport bottom. Raise it to z-[1100],
matching the existing nested-layer convention (usage panels use z-[120]
above z-[110] dialogs).
---------
Co-authored-by: Kaihang An <ztmdsbt@gmail.com>
Co-authored-by: Jason <farion1231@gmail.com>
- Gateway: apicdn.xycai.us primary, apicdn.xyc.ai fallback
- Claude auth uses ANTHROPIC_API_KEY per official docs
- Claude Desktop connects direct with native Anthropic format
- In-app partner promotion only (no README entry), four locales
- Register xycai icon (PNG via iconUrls) with metadata
The former 火山Agentplan preset actually pointed at Coding Plan
endpoints (/api/coding[/v3]). Split it into two presets across all six
app preset files:
- 火山 Agent Plan: /api/plan (Anthropic) and /api/plan/v3 (OpenAI),
with its own activity/agentplan invite link
- 火山 Coding Plan: keeps /api/coding[/v3] and the original
activity/codingplan invite link
Both plans' OpenAI endpoints are covered by the official Codex docs
(wire_api=responses), so the Coding Plan Codex preset also uses native
Responses. Widen the Volcengine usage detection to
volces.com/api/(plan|coding) in both the frontend pattern and the
backend detect_provider mirror; the quota query already auto-probes
both plans (GetAFPUsage then GetCodingPlanUsage). Split the partner
promo copy into volcengine_agentplan / volcengine_codingplan i18n keys
in all four locales, and pin both presets in the codex native
Responses preset test.
FennoAI changed its CC Switch benefit: the previous ¥9.9/month Coding
Plan worth $150 in credits is replaced by a $1.99 Trial Plan worth
$50 in credits, valid for 7 days. Update the in-app promo line in all
four locales and the partner sections of the four READMEs. The 20%
referral reward and the registration link stay unchanged.
Switch RunAPI website, registration and request URLs to runapi.host
across all seven app presets and the four README partner sections.
The previous runapi.co endpoints are retained as endpointCandidates
fallbacks where the preset type supports the field (claude,
claude-desktop, codex, grok-build).
The star was legacy display logic gated on meta.isPartner, which the
generic provider form almost never persisted. The standalone Grok Build
form unconditionally saves meta.isPartner, resurrecting the dead code
path. Partner stars belong only in the preset selector, so drop the
card-level badge and the now-unused officialPartner i18n key from all
four locales.
The official catalog mirror template was extracted from DeepSeek's docs
page with HTML unescaping applied before tag stripping, so literal
angle-bracket text inside the JSON (e.g. "<this good thing>" and the
spaced-path markdown link example) was swallowed as if it were HTML
tags — 8 spots across base_instructions and model_messages for both
models, 86 bytes missing per harness copy. Restore them; the template
is now structurally identical to the current official models.json.
Also refresh the preset comment: deepseek-v4-pro's Codex integration
is live as of 2026-08 (its catalog entry differs from flash only by
priority), so the "switching early errors upstream" warning is gone.
Findings from a models.dev audit of the seeded pricing table:
- Correct grok-4.5 cached input 0.50 -> 0.30 (seed + repair with an
old-value guard). The current xAI list table, models.dev and the billing
observed for grok-4.5-build all state 0.30; 0.50 is grok-4.6's rate.
- Seed grok-4.6 (2 / 6 / 0.50, released 2026-08-12). Both 4.5 and 4.6 double
their rates above a 200K prompt, but the table has no tier column, so the
base tier is stored, consistent with other tiered vendors.
- Seed a deepseek-v4-flash-0731 alias at DeepSeek's official price.
strip_model_date_suffix only strips ISO, 8-digit YYYYMMDD and 6-digit
YYMMDD suffixes, and the prefix fallback only matches longer rows, so this
id resolved to no pricing row and was silently billed at zero.
The GrokBuild backfill test expectations follow the corrected cache-read
price; its cache-inclusive input assertion (450 fresh tokens) is unchanged.
Moonshot/Kimi asked to exit the reasoning-vendor workaround: their
endpoints no longer require thinking replay on tool-call turns, and the
injected placeholders disrupt the model's chain of thought.
- Remove moonshot/kimi from REASONING_VENDOR_HINTS; Kimi now gets clean
passthrough on the anthropic path (no placeholder thinking blocks, no
signature stripping) and no reasoning_content on the openai_chat path.
- DeepSeek/MiMo keep the behavior (documented server-side requirement).
- Flip the two Kimi gating tests into negative regressions and update
stale comments/test model names that still referenced Kimi.
The restore backup is a snapshot taken when takeover starts and is never
refreshed from live afterwards. If the user logged into official ChatGPT
while takeover was active, exit/crash-recovery restored the stale
third-party API-key snapshot verbatim over auth.json, wiping the login on
every restart (and the auto re-takeover on startup looped the clobber).
Restore now arbitrates before writing: whenever live auth.json holds real
login credentials (codex_auth_has_credential_login_material, and not the
proxy placeholder), live wins — the backup's API key is demoted into
config.toml as experimental_bearer_token and the backup auth slot is
dropped, so auth.json is left untouched. Live credentials are always newer
than the snapshot because only Codex itself (login / token self-refresh)
advances them, so even an official-shape backup must not roll live tokens
back. Metadata residue (last_refresh / tokens.account_id) counts neither
as a live login to protect nor as backup material to restore. Both restore
entry points — the with-fallback path (exit / crash recovery / takeover
disable) and the simple path (takeover rebuild and failure rollback) —
share the same protection.
Fixes#6277
Zhipu renamed the quota entry type field from TOKENS_LIMIT to CREDIT_LIMIT on the mainland endpoint, causing all quota tiers to be skipped and the usage panel to show empty. Accept both values (case-insensitive) so old plans and the international endpoint keep working.
Fixes#6153
* refactor(hermes): align provider form hierarchy
* fix(hermes): match model detail label typography
* fix(hermes): mute context length label
* fix(hermes): align model detail field width
* fix(hermes): expose model detail state
The model mapping dropdown (used by Claude Desktop, Codex, and OpenCode
provider forms) lists every fetched model grouped by vendor with no way
to filter. Providers with hundreds of models make the right model hard
to find, as requested in #6275.
Convert ModelDropdown from a plain DropdownMenu to a Popover + Command
combobox (the existing OmoFormFields pattern), adding a fuzzy search
input powered by cmdk:
- non-matching groups auto-hide; an empty state shows when nothing matches
- vendor name is indexed as a keyword, so models can be matched by vendor
as well as by model id
- the { models, onSelect } API is unchanged, so all 5 call sites work as-is
Consolidate two near-identical inline copies of the same grouped
dropdown (ModelInputWithFetch and the Copilot branch in ClaudeFormFields)
onto ModelDropdown so every model picker gains the search and the
duplication is removed.
Add searchModelPlaceholder / searchModelEmpty / searchModelAriaLabel i18n
keys for en, zh, zh-TW, ja.
check_updates trusted the DB-cached content_hash before ever looking at
disk, so a skill whose SSOT directory is gone (e.g. after restoring a DB
backup on another machine) showed "no update" and the missing files were
permanently masked; the only recovery was uninstall + reinstall.
Extract the local-hash decision into local_hash_for_update_check with
the order inverted: validate the untrusted directory name, check that
the SSOT directory actually exists, and only then trust the cached
hash (computing and backfilling it when absent). A missing directory
now yields None, which never equals the remote hash, so the skill
surfaces as "update available" and the existing update_skill flow
rebuilds it — no new download logic.
Invalid directory values keep the pre-fix behavior (fall back to the
cached hash) instead of being reported as updatable, because
update_skill would hard-fail on the same validation gate.
Covered by four unit tests on the extracted helper.
The single backslash in Software\{{manufacturer}}\{{product_name}} was
consumed by Handlebars as a mustache escape, so the MSI wrote a literal
key named Software{{manufacturer}}{{product_name}} directly under HKCU.
Double the backslashes to match every other registry key in the template.
Fixes#6283
Upstream has had grok-4.5 since 2026-08, so 'no Grok models upstream' is no longer accurate.
Exclusion is now a deliberate product decision; enrollment requires separate review.
/v1/messages only accepts x-api-key: switch env from ANTHROPIC_AUTH_TOKEN to ANTHROPIC_API_KEY and add top-level apiKeyField.
Drop apiFormat so Claude Code connects directly (providerNeedsRouting no longer triggers) instead of through the proxy.
Desktop keeps mode: proxy (it cannot rename models itself) but switches to anthropic passthrough.
Add a changes-detection job (dorny/paths-filter, SHA-pinned) and gate
the frontend job, the 3-OS backend matrix, and the WSL2 contract gate
on whether the PR actually touches their inputs. About two thirds of
merged PRs only touch one side, and docs-only PRs now skip CI entirely.
Pushes to main still run everything unconditionally so the caches that
PRs restore from stay warm. Path groups mirror .github/labeler.yml,
plus rust-toolchain.toml which pins the compiler used by the backend
jobs.