Release history of mimo2codex, newest first.
Category tags
- [new] / [feat]: new features
- [fix]: bug fixes
- [opt] / [refactor]: optimization / refactor
- [doc]: documentation
- [test]: tests
-
[opt] MiMo v2 models retired → transparently aliased to v2.5 (no config edits needed) — MiMo took its v2 generation offline on 2026-06-30 (
mimo-v2-pro,mimo-v2-omni,mimo-v2-flash); requests using the old names now 400 upstream. mimo2codex now aliases the retired names to their official replacements so an existingconfig.tomlkeeps working and still hits the right model:mimo-v2-pro→mimo-v2.5-pro,mimo-v2-omni/mimo-v2-flash→mimo-v2.5. Implemented viaProviderModel.aliases+ an alias-awareresolveModel(mirrors DeepSeek); the alias is a clean resolve — the upstream gets the live id and there's no fallback rewrite/notice. The three retired entries are removed from the builtin catalog, so the model list, generated config.toml snippets, and the admin Models page now advertise only the live v2.5 models (mimo-v2.5-pro,mimo-v2.5-pro-ultraspeed,mimo-v2.5); their stale DB rows are auto-pruned byseedBuiltinson next boot. Behavior change for oldmimo-v2-flashusers: its replacementmimo-v2.5defaults thinking ON (flash defaulted OFF) and supports vision. The mimoskill helpers (ocr.py/mimo_chat.py, which call MiMo directly with no alias layer) now coerce retired names to their v2.5 replacement too. -
[fix] Thinking-mode sampling:
top_pis now stripped as well (wastemperatureonly) — per MiMo's docs the v2.5 family ignores customtemperatureandtop_pin thinking mode (upstream forcestemperature:1.0/top_p:0.95).normalizeMimoBodypreviously dropped onlytemperature; it now drops both for the v2.5 reasoning models (mimo-v2.5-pro,mimo-v2.5,mimo-v2.5-pro-ultraspeed), so the request matches the eventual upstream behavior. Normalization is also now keyed off the upstream model id (post-alias) at both the Responses and Chat call sites — previously the Chat path keyed off the client literal (out.model), which would mis-apply the rules to an aliased legacy name.
-
[opt] Admin dashboard no longer crawls (or stalls the proxy) on a large
data.db(issue #76): a heavy user'sdata.dbgrew to 22 GB and the Overview / Logs pages took 10+ minutes to load. Root cause: every dashboard load ran 5-6 live aggregations (aggregateStats/aggregateTokensTimeserieswithstrftime+ 3-wayGROUP BY/aggregateLatencypulling everyduration_msinto JS /aggregateProviderHealth/aggregateMappingswith no time filter) directly over the multi-GBchat_logstable — and since better-sqlite3 is synchronous, one slow scan blocked the entire HTTP loop, stalling even/v1/responses. Fix: a new hourly rollup tablechat_stats_hourlykeyed by(hour, provider, client_model, upstream_model)is incremented inside eachinsertLog's transaction ("write the log → bump the stat"); all dashboard aggregates now read that tiny table (rows = hours × model-tuples, independent of how bigchat_logsgrows). Latency percentiles come from an 8-bucket histogram (exact average from sum/count); the error-code breakdown still readschat_logsbut via a new partial indexWHERE status_code >= 400. Pre-existing rows are folded in by a background, newest-first backfill (chunked + non-blocking — recent ranges become accurate within minutes). Plus anti-avalanche tweaks: the Overview's auto-refresh drops from 5s → 20s/60s with an in-flight guard, and the Logs page no longer re-queries the DB size on every page turn. New filesrc/db/stats.ts; schema migration v6. Disk note: this makes the dashboard fast but doesn't shrink an already-huge file — set a retention (days) and/or switch body capture to errors-only in the Logs page's 存储设置, then VACUUM (needs free space ≈ db size). -
[fix] MiMo web search is now OFF by default — no more "webSearchEnabled is false" 400 loops on accounts without the plugin — when a request reached MiMo carrying a
web_searchtool but the account hadn't activated the (separately-billed) Web Search Plugin, the upstream 400'dweb search tool found in the request body, but webSearchEnabled is false; since 400 isn't retryable the error bubbled to Codex, which kept resending the same request per itsrequest_max_retries→ an endless fail loop. Root cause: mimo2codex forwardedweb_searchto any non-token-plan (sk-) account (mimo.tsenableWebSearch: !isTokenPlan), wrongly assuming pay-as-you-go accounts have the plugin — they don't, it's opt-in/billed for everyone. web_search forwarding is now opt-in and OFF by default for all accounts: mimo2codex stripsweb_searchbefore forwarding unless you explicitly enable it. Enable it (only if your account has the plugin activated) via the admin Codex Enable → Thinking & Override → Web search toggle, or--web-search/MIMO2CODEX_WEB_SEARCH=1. Token-plan (tp-) accounts never get web search regardless. New settingmimo.webSearchEnabled, newGET/PUT /admin/api/web-search-state, threaded throughPreprocessCtx.webSearchEnabled(mirrors thethinking.disabledtoggle end-to-end).
-
[new] "Keep ChatGPT login" apply mode + a Codex-mobile guide — stay signed into your real ChatGPT account and run the model through mimo2codex at the same time (the prerequisite for OpenAI's official "Codex mobile / remote", where your phone drives this computer's Codex). Until now, enabling a mimo2codex provider overwrote
~/.codex/auth.jsonwith a placeholder key — logging you out of "Sign in with ChatGPT" — so the official login and the proxy were mutually exclusive. The new path (default when a real login is detected) leaves auth.json byte-for-byte untouched and only rewritesconfig.toml(model_provider→ proxy,requires_openai_auth = true); Codex then attaches your real ChatGPT OAuth token as the bearer to the local proxy, which ignores it and forwards upstream. The Codex-Enable confirm dialog explains this and a checkbox still lets you opt into the old overwrite; a config-only "Keep ChatGPT login" snippet tab and a collapsible "Control this Codex from your phone" guide card were added, plusdoc/codex-mobile-remote.{md,zh.md}. Built as isolated new files (src/codex/preserveLogin.ts,src/setup/preserveLoginSnippet.ts,web/src/pages/codex/MobileRemoteCard.tsx) so the existing apply path is untouched. Caveat: whether OpenAI's remote mode actually runs the model through your local proxy (vs. forcing its own models) is undocumented and needs real-world testing — the guide says so. -
[opt] The client's requested model now takes precedence over a runtime override — previously a webui runtime override (Pass 0 in
selectProvider) won unconditionally, hijacking even a model Codex explicitly configured (e.g. Codex sendsmimo-v2.5-probut the override forceddeepseek). Routing priority is now: (1) the client model, when a registered+keyed provider recognizes it; (2) the runtime override, when the model id isn't in any provider catalog; (3) the default provider as a bare fallback. So an explicitly-configured Codex model is honored, while the override remains the smart fallback for unrecognized model ids (instead of silently rewriting to the default). Implementation:selectProviderinsrc/server.tsmoves the override block from Pass 0 to after the catalog passes; the admin "Runtime override only" copy was updated to match. No change when no override is set. -
[fix] Desktop Mac Apple-Silicon (arm64)
/admin/silent 404 (0.5.26 regression; mac-intel / win audited too): visiting/admin/returned{"error":{"type":"invalid_request_error","code":"not_found","message":"no route for GET /admin/","status":404}}, while 0.5.24 worked. Root-cause chain confirmed line-by-line: at startupopenDb()fails to load the better-sqlite3 native module (wrong arch / wrong ABI / unsigned-and-AMFI-rejected on Apple Silicon),cli.tsgracefully degrades toadminEnabled=false(issue #30), and/admin/then falls through to the generic 404 — the failure was swallowed silently and the message was misleading. Three-layer fix: (1) runtime: silent 404 → clear diagnostic — when admin is force-disabled by a DB load failure,/admin/now returns 503admin_db_unavailablecarrying the real better-sqlite3 error + remediation (reinstall / arch match / macOSxattr -cr), and keeps/admin/api/healthanswering200 {adminEnabled:false, reason:"db_unavailable", message}for CI / the desktop shell to probe; an intentional--no-admin/MIMO2CODEX_NO_ADMINstill gets the historic 404. (2) macOS ad-hoc re-sign: a newafterPackhook (scripts/after-pack-sign.cjs, mac-only, non-fatal) ad-hoc-signs the.nodefiles underContents/Resources/sidecarand the app bundle — the extraResourcesbetter_sqlite3.nodecan stay unsigned when there's no signing identity, and Apple Silicon's AMFI rejects it at dlopen (Intel / Windows don't enforce this, matching "M-chip broken, intel/win fine"). (3) post-package gate: a newscripts/postpack-healthcheck.mjslaunches the packaged sidecar via the bundled Electron and asserts/admin/api/health → adminEnabled:true, catching the wrong-arch / wrong-ABI / unsigned-.node class before release (wired intopack-desktop-localstep 7 and CIbuild-desktop.yml; runs on native targets, self-skips cross targets). -
[fix] Desktop: a blank Base URL left the upstream host empty (startup banner
upstream:blank, requests hostless): the desktop Settings writesMIMO_BASE_URL=(empty) when the Base URL field is left blank — this is intentional (clearing a field clears its line, which is how you disable a provider by clearing its API key). But config resolution used??, and an empty string isn't nullish, so it "won" the fallback chain — the key-prefix host inference (tp-→token-plan-cn.xiaomimimo.com/sk-→api.xiaomimimo.com) never ran andupstreambecame"". Empty / whitespace-only base URLs (from env or CLI) are now treated as "unset", restoring the fallback (key-based inference → default host). Existing.envfiles with a stray emptyMIMO_BASE_URLare auto-fixed on upgrade — no re-save needed. -
[opt] Stricter build-time verification, no more silent gaps:
build-sidecar.mjs's cross-build (arm64 runner → x64 package) used to silently skip the ABI smoke test; it now emits a loud warning noting ABI/signing must be validated by the post-package gate on a native machine. The native smoke test no longer justrequires — it opensnew Database(':memory:')and printsprocess.versions.modules(ABI) +process.archto aid future electron-drift debugging. Newscripts/verify-release-native.mjsinspects any desktop artifact (.app / win-unpacked / sidecar dir /.node): parses the Mach-O/PE/ELF header to report CPU arch, compares it against theSIDECAR_INFO.jsonbuild target, and prints the next-step commands to verify ABI + signing on the target machine. -
[opt] Desktop build arch cleanup: Windows ships x64 only, macOS ships Intel + Apple Silicon, and no arch is built twice (answering "check the Windows platforms too"): (1) win-arm64 is dropped entirely — better-sqlite3 v12.x has no reliable win32-arm64 prebuild, so a win-arm64 package bundles a wrong-arch native module and 404s on
/admin/anyway (a stale local win-arm64 artifact was found to contain an x64better_sqlite3.node). It's now blocked at the config level in bothelectron-builder.yml(win.targethas no arm64) andpack-desktop-local.mjs(VALID_TARGETSdropswin-arm64); Windows-on-ARM uses the x64 build under emulation. (2) The mac/win targets no longer pinarch: [x64, arm64]— arch is driven by the--x64/--arm64CLI flag (passed by both CI and the local packer). The pinned list made every mac job build BOTH arches (the CLI flag didn't override an in-target arch list), producing a redundant cross-built x64 in the arm64 job and duplicate release zips. macOS still ships two separate installers (Intel x64 + Apple Silicon arm64), but each job now builds only its own arch.
- [new] Built-in support for
MiMo-V2.5-Pro-UltraSpeed(issue #70): Xiaomi's new 1T-param flagship (500-1000 tok/s "experience" mode) is now a recognized built-in model. Before this, a request withmimo-v2.5-pro-ultraspeedwas silently rewritten tomimo-v2.5-pro(so you actually ran Pro, not UltraSpeed) and the model couldn't be picked from the admin UI. Now it routes verbatim, appears in the model catalog / Codex Enable page, andprint-configlists it. Declared text-only with reasoning + tool calling, 1M context, 131072 max output. Web search is off per the official spec — if you've enabled Codex web search on a pay-as-you-go account, UltraSpeed may 400 on the forwardedweb_searchtool; disable it for this model. Access: UltraSpeed is application-only (limited daily approval — apply at https://platform.xiaomimimo.com/ultraspeed) and is served only on the pay-as-you-go API host (sk-keys); token-plan / subscription (tp-) accounts can't use it. The admin Codex-Enable page tags it Restricted (hover shows the note), and selecting UltraSpeed with atp-key is rejected up front with a clear message (model_requires_payg) instead of a confusing upstream model-not-found.
-
[new] Database housekeeping — one-click cleanup + VACUUM, size visibility, auto-maintenance (issue #67):
data.dbcould balloon (a user hit 6 GB) because the default keeps full request/response bodies forever and deletes never reclaim disk (SQLite leaves freed pages in the file untilVACUUM). This adds: (1) "Clear old logs" / "Clear all logs" on the Logs page that auto-run VACUUM right after deleting (so the file actually shrinks, not just frees internal pages), plus a live database-size readout on the Logs page; (2) size-cap auto-maintenance — setlogging.maxDbSizeMband the 6-hour maintenance pass trims the oldest logs and runs a throttled VACUUM (≤ once/day); (3) db-friendlier defaults for fresh installs only — 30-day retention +errors-onlybody capture, while existing installs are migrated to explicitoff/fullso an upgrade never deletes anyone's logs or changes capture without consent. New endpoints:GET /admin/api/db/size,POST /admin/api/db/vacuum(with a free-disk pre-check), andDELETE /admin/api/logs?all=1|keepDays=<n>. -
[fix] Desktop app crashed on Intel (x86_64) macOS — admin UI 404 (issue #69): the macOS x64 package shipped an arm64
better-sqlite3native module, so on Intel Macs the sidecar failed to load it (incompatible architecture), the admin DB was unavailable, the admin routes were never registered, and every/admin/request 404'd. Root cause: the sidecar build passednpm_config_target_arch/_platform, but prebuild-install (better-sqlite3's binary fetcher) only honorsnpm_config_arch/npm_config_platform— so the cross-arch build (arm64 CI runner → x64 package) silently fell back to the runner's arch and fetched the wrong prebuild. Only macOS x64 was affected (Windows x64 / macOS arm64 are same-arch builds where the fallback happened to be correct). Two fixes: (1)build-sidecar.mjsnow setsnpm_config_arch/npm_config_platform(keepingtarget_*as a node-gyp source-build fallback); (2) a new static arch check (scripts/detectNativeArch.mjs, parses the Mach-O/PE/ELF header) runs on every build — including cross-arch, where the executable smoke test was skipped — so a wrong-arch module now fails CI instead of shipping to users. -
[fix]
install.sh/install.ps1cloned a non-existent repo (issue #66 follow-up): the bootstrap scripts' defaultMIMO2CODEX_REPOwas still the template placeholderyour-org/mimo2codex, socurl … | bash/irm … | iexfailed right at the clone step. Now points at the real repo. These git-clone bootstrap scripts are not the documented install path (that'snpm install -g mimo2codexor Docker), so this is unlikely to be the root of #66 — but it's a real bug regardless.
-
[new] Automatic context compaction (issue #65 follow-up): long Codex sessions resend their whole history every turn, and once it nears the model's context cap the upstream either 400s or prefills so slowly the stream drops. mimo2codex now estimates the input size and, when it crosses a token trigger that scales with the model's context window (
contextWindow × threshold, threshold default 0.8 — e.g. ~800k for a 1M-window model, ~205k for a 256k one), summarizes the older middle of the conversation into one compact note via the same model, keeping the leading system messages and the most recent turns verbatim. The split always lands on a cleanuserboundary so tool_call/tool_result pairs are never orphaned, image base64 is never fed to the summarizer, and a stable prefix is cached so it isn't re-summarized every turn. Best-effort: if the summary call fails the original history is left intact. Default on; toggle/tune viaMIMO2CODEX_AUTO_COMPACT(0=off),MIMO2CODEX_AUTO_COMPACT_THRESHOLD, or an absoluteMIMO2CODEX_AUTO_COMPACT_AT_TOKENS(for upstreams whose advertised window overstates their real cap) — also exposed as admin settingscodex.autoCompactEnabled/codex.autoCompactThreshold/codex.autoCompactAtTokens. Runs while the keepalive is active so the summary round-trip doesn't reintroduce a silent socket. -
[fix] Request-body cap is configurable and no longer disconnects on oversized image uploads (issue #65): the body limit was a hard-coded 16MB and overflow
destroy()d the socket mid-upload — which Codex saw as "error sending request for url" rather than a clean error. The cap is now 64MB default and configurable (MIMO2CODEX_MAX_REQUEST_BODY_MB), and overflow drains the rest of the body before returning a proper 413 the client actually receives. -
[fix] "stream disconnected before completion" on large contexts / image uploads (issue #65): the proxy used to
awaitthe upstream's first byte before sending Codex anything, and Node'sfetch(undici) caps the wait at a 300s default. A long prefill (big conversation, or a base64 image inflating the request) could blow past that window while Codex stared at a silent socket and tripped its own idle timeout. Three coordinated fixes: (1) a global undici dispatcher now applies a configurable upstream timeout (default 10 min,0= off) whether or not a proxy is set —MIMO2CODEX_UPSTREAM_HEADERS_TIMEOUT_MS/MIMO2CODEX_UPSTREAM_BODY_TIMEOUT_MS; (2) header/body timeouts no longer trigger the retry storm — they fail fast with a clear 504 instead of re-sending a multi-MB body up to 6×; (3) both streaming paths now flush SSE headers + start the keepalive before awaiting the upstream, so Codex keeps receiving: keepalivecomments during a long prefill. Trade-off: once the 200 SSE stream is committed, a terminal upstream error (e.g. context-overflow 400) is delivered as an SSEerrorevent rather than a JSON 4xx. The startup banner now shows the active timeouts, and image-bearing streaming requests are logged with their approximate size.
-
[new] Windows: isolated Codex CLI launcher (PR #64, thanks @Kaiyuan GONG): a new
scripts/codex-mimo-isolated.ps1lets you run Codex CLI against MiMo via mimo2codex without touching the~/.codexused by Codex Desktop. It uses a separateCODEX_HOME=%USERPROFILE%\.codex-mimo, writes a minimalauth.json+config.tomlthere on first run, auto-starts the proxy if:8788isn't already listening, prints the local API/admin URLs, then forwards all remaining args tocodex. API keys are not hardcoded — configure them viamimo2codex init. Seedoc/codex-cli-isolated-windows.zh.mdfor the walkthrough. -
[fix] Saving a generic provider with a duplicate shortcut no longer bricks the admin UI (
/admin/404) (issue #63):providers.shortcutisUNIQUE, but the save path only de-duped providerid, notshortcut. A generic whose shortcut collided with a built-in (mimo/ds) or with another generic would save fine, then crash the next startup's DB seed (UNIQUE constraint failed: providers.shortcut), which the cli.ts fallback then turned into a disabled admin — so every/admin/request 404'd. Two-layer fix: (1)writeSpecsToFilenow rejects a colliding shortcut at save time with a clear message (seeded with the built-in shortcuts); (2) DB seeding de-dupes by shortcut (dedupeProvidersByShortcut) — a duplicate is skipped with a warning instead of crashing the whole seed, so anyone who already saved a dirtyproviders.jsongets their admin back on the next start. -
[fix]
enhanceErrorPreset: "kimi"is no longer silently dropped from generic providers:kimiis a validProviderPresetId(src/providers/presets.ts) but the providers.json parser only acceptedsensenova/minimax, so a Kimi error-diagnostic preset never persisted. It's now accepted alongside the others.
- [new] Multimodal fallback — auto-switch to a vision model when a request carries images (PR #58, thanks @Grub): when a request contains images but the active model can't see them (e.g.
mimo-v2.5-pro), the proxy rewrites the upstream model to a vision-capable one (defaultmimo-v2.5) so the image is processed instead of silently dropped — applied on both the Responses and Chat paths. Scoped to MiMo — other providers are never affected: vision capability is a MiMo provider feature (provider.supportsVision), so only MiMo triggers the fallback; DeepSeek / generic requests are left untouched. Even on MiMo, the switch is skipped when the fallback model can't be resolved. Toggle + target model live in the admin UI → Codex Integration → "Multimodal fallback" card; disabled by default — enable it when your workflow mixes vision and non-vision models.
-
[fix] Sustained 429 rate limits no longer break the session (follow-up to v0.5.20's retry): v0.5.20 added proxy-side 429/5xx retry, but the default budget (3 retries, ~3.5s) only outlasted sub-second blips. Real per-minute quota limits (
429 Too many requests / limitation, often without aRetry-Afterheader) still exhausted it, so the raw 429 was forwarded to Codex, which then burned its own retries and surfaced "exceeded retry limit, last status: 429" again. The default retry budget is now larger: 6 retries with exponential backoff capped at 12s (~28s total), so a multi-second quota limit clears before we give up. Still abortable, still honorsRetry-Afterwhen present, and still tunable viaMIMO2CODEX_UPSTREAM_MAX_RETRIES(now up to 12) /MIMO2CODEX_UPSTREAM_RETRY_BASE_MS. Trade-off: while rate-limited, a single request now waits up to ~28s before failing instead of ~3.5s. -
[new] Log storage controls for long-running deployments: what problem this solves — every request/response used to be logged in full and kept forever, so on always-on installs (Docker, shared/team setups)
data.dbgrows without bound: it eats disk, slows backups and the Logs page, and keeps full conversation text around far longer than you may want for privacy. Two knobs now cap that.MIMO2CODEX_LOG_BODY_MODE=full|errors-only|off(also in the Logs page → "Storage settings") keeps full debugging detail, stores bodies for failed requests only (enough to triage, far smaller), or disables body capture entirely.MIMO2CODEX_LOG_RETENTION_DAYS=<n>(same place) auto-deletes rows older thanndays — on startup and every 6h while running;0disables pruning. Typical use: a small VPS / team proxy setserrors-only+30so the DB stays bounded instead of ballooning over months. Settings live in the DB (no restart) and the env/CLI value wins when set.
-
[fix] Transient upstream 429 / 5xx no longer break the session ("exceeded retry limit, last status: 429"): the proxy used to forward a rate-limit straight back to Codex, which then burned its own
request_max_retriesand gave up — leaving the user to manually hit "continue". mimo2codex now absorbs transient failures itself:postUpstreamretries429and500/502/503/504(and network connect failures) with exponential backoff + jitter, honoring the upstream'sRetry-Afterheader (capped at 10s so Codex doesn't time out). Retries are abortable — a Codex cancel during backoff stops immediately. Non-retryable errors (400/401/403 …) still fail fast. Tunable viaMIMO2CODEX_UPSTREAM_MAX_RETRIES(default 3) andMIMO2CODEX_UPSTREAM_RETRY_BASE_MS(default 500). -
[fix] "写入文件并启用" no longer wipes your other config.toml settings: applying a model used to overwrite the whole
~/.codex/config.tomlwith justmodel+model_provider+[model_providers.<key>], silently dropping everything else the user had —[projects]trust levels,[mcp_servers],[windows] sandbox,model_reasoning_effort,[notice.model_migrations], comments. Switching models now does a surgical merge (src/codex/tomlMerge.ts): only the four keys we manage (model,model_provider,model_context_window,model_max_output_tokens) and our own[model_providers.<key>]table are rewritten; every other byte is preserved. A fresh install (no existing config.toml) still gets the rich first-run snippet. Backups are still taken before every write, so any prior config remains fully restorable. -
[new] Session Manager — browse all Codex sessions across providers and migrate them (new left-nav tab). Codex Desktop stores each session in
~/.codex/state_<N>.sqlite(threadstable) tagged with onemodel_provider, and filters its session list by it — which is why switching providers in mimo2codex made "ds and mimo sessions invisible to each other". The new tab reads that DB (read-only) and shows every session grouped by provider → project (cwd) → session, regardless of which provider is currently active. Display niceties: the Windows extended-length\\?\prefix is stripped from project paths (so the same project doesn't split into two groups), session timestamps render correctly (Codex stores seconds), long titles are middle-ellipsized to one line, and only the first provider group is expanded by default. Since a session can't be shared across providers (the row holds exactly one), it can be migrated: "Migrate to…" rewrites the session'smodel_provider(in the DB and the rollout file'ssession_meta) so Codex lists it under the chosen provider after a restart. Batch migrate: tick the checkboxes (selection spans every project/provider table) and "Migrate selected" moves them all to one provider in one go. Safety: mimo2codex snapshots the whole state DB (+-wal/-shm) and the rollout into~/.codex/.m2c-backups/sessions/<ts>/before touching anything, and refuses to migrate while Codex Desktop holds the DB lock (a409 codex_running) so an open app can't corrupt its sessions. Local mode only.⚠️ This edits Codex's private, version-stamped state — if a future Codex changes the schema, the tab degrades to "unavailable" rather than breaking. -
[new] Preview a session's chat transcript + export to Markdown: each session row has a "Preview" button that opens a drawer rendering the conversation Codex-style — user/assistant messages, reasoning, and tool calls (shell commands,
apply_patch…) with their output in code blocks. Tool/shell calls are collapsed by default (header shows the tool + first command line) so the text conversation stands out — click to expand. The rollout JSONL is parsed server-side (src/codex/transcript.ts); injected developer/permission blocks are dropped and environment/instruction context is collapsed so the real conversation stands out. An "Export Markdown" button downloads the whole transcript as a.mdfile. Read-only, local mode only. -
[new] Live "当前状态" Codex indicator in the header: the current state is something users check constantly, so it now sits in the top bar alongside the other status items, instead of taking up a card on the Codex page. It's a compact ticker labeled "当前状态" that cycles every 3s through each state row — codex dir, auth.json owner, config.toml provider/model, runtime override — and turns blue when a runtime override is active. Clicking it opens the full state (codex dir + editor, auth.json, config.toml, override, export/import) — a popover on wide screens, a modal when narrow. Auto-refreshes every 30s.
-
[opt] Codex page slimmed to direct model switching: the "current state" card moved to the header (above) and the redundant quick-switch bar was dropped, so the Codex Integration page is now just the title + the model-switch table ("可启用模型" / "写入文件并启用"). The two "工作原理" mode explanations and the intro were already folded into the collapsible "先决条件" panel (default-collapsed).
-
[new] Restart Codex right after applying a config: a config switch only takes effect once Codex reloads, so applying one ("写入文件并启用") now pops a "Restart Codex to apply the change?" dialog (Restart now / Not now). It force-closes the running Codex Desktop app and relaunches it (and just launches it if it wasn't running), so you don't have to hunt for the app to close it yourself. Windows: targets only the Desktop app's own
Codex.exeprocesses (matched by executable path, so the VS Code extension'scodexengine is left alone) and relaunches via the Store AppUserModelID. macOS: best-effortpkill+open -a Codex. Local mode only; unsupported platforms tell you to restart manually. -
[new] Desktop: offer to open Codex on launch: when you start the mimo2codex desktop app and Codex Desktop isn't already running, a dialog asks whether to open it ("打开 Codex" / "暂不") and launches it for you if you confirm. Detection targets only the real Codex Desktop processes (by executable path); launch goes through the Store AppUserModelID on Windows /
open -a Codexon macOS. Skipped when Codex is already running, on first-run setup, and on autostart-at-boot launches (so it never nags during boot). Detection + launch reuse the same primitives as the in-app restart. -
[opt] Desktop: double-click the tray icon to open the admin console: previously double-clicking the system-tray icon did nothing (only right-click opened the menu). It now opens the admin console in-app directly — one gesture instead of right-click → menu. Menu access stays on right-click. (The quit confirmation already lists "Quit" left of "Cancel" with Cancel as the safe default, so that was already as requested.)
-
[opt] Config backups moved into a dedicated
~/.codex/.m2c-backups/folder: the per-switchauth.json.bak.*/config.toml.bak.*snapshots used to pile up directly in~/.codex/, cluttering the directory listing the Codex app and CLI also read. They now live in a hidden.m2c-backups/subfolder (still inside the codex dir, so restore works unchanged). Existing legacy sibling backups are migrated automatically on first read. -
[opt] Model-rewrite log: now silent by default + a quick toggle in the header (builds on PR #49, thanks @oxsean). When Codex sends a model id that differs from the provider's catalog (e.g.
gpt-5.4→mimo-v2.5-pro), the proxy logged a "model fallback applied" INFO line on every request. That log is now suppressed by default, with a quick switch under the admin header's "更多" menu ("静默模型改写日志") to flip it at runtime — no restart. Resolution order is env > admin setting > silent:MIMO2CODEX_SILENT_REWRITE=1/true(or0/false) still wins and, when set, disables the UI toggle.
-
[fix] Long-conversation 400 "unexpected end of data: line 1 column 46 (char 45)": once an upstream stream finished mid tool-call (length limit, network cut, client cancel, thinking-budget exhaustion …), Codex persisted the truncated
tool_call.argumentsas part of the session history. From that point on, every subsequent request in the same session carried the malformed JSON-as-string field, and strict upstreams (MiMo / DeepSeek / SenseNova …) rejected the request body with a JSON parser error pointing at the truncation point — the session looked permanently broken until the user started a new chat. mimo2codex now sanitizes tool-call arguments at three layers: (1) on the way back to Codex during streaming (streamToSse.finalizeToolCalls) — invalid JSON is salvaged to"{}"with a clear WARN that names the cause (length truncation vs. other), so the bad value never reaches Codex's history in the first place; (2) the same defense on the non-streaming path (respToResponses); (3) on the way out to the upstream (reqToChat'sfunction_callbranch) — historicalargumentsthat failed validation are likewise rewritten to"{}", which immediately revives sessions poisoned by older proxy versions. The matchingtoolmessage stays paired with the assistant turn (theremoveOrphanToolMessages/ensureToolCallsHaveOutputsinvariant is preserved). For the rare case the upstream still returns this shape,contextOverflow.detectMalformedJsonFieldrewrites the raw 400 into a bilingual recovery hint ("upgrade or start a new codex session") instead of dumping the cryptic upstream error at the user. -
[opt] Desktop Settings supports multiple provider keys at once + custom base URLs (PR #43 — A1, thanks @starlsd93-sudo). The original Settings window only let you configure one provider's API key at a time, so users running multiple providers (MiMo + DeepSeek) had to switch the provider dropdown and re-save once per key. The new layout shows API key + optional base URL fields for every provider (MiMo, DeepSeek, Generic) on a single page, with
GENERIC_DEFAULT_MODELexposed as well. Setup completes if at least one provider has a key; the base URL placeholders show the built-in defaults so users only fill what they want to override (MiMo TP subscription host, DeepSeek tenant, etc.). The MiMo Base URL field includes an inline hint that explains the proxy auto-routes based on the key'ssk-*/tp-*prefix, so new users don't paste the wrong host and get 401. How to open: tray icon → Settings…, or top menu bar 「文件 → 设置… (Ctrl+, / Cmd+,)」. What it looks like: -
[new] Desktop first-run can import config from an existing CLI install (PR #43 — A3, thanks @starlsd93-sudo). If
~/.mimo2codex/.envexists from a priornpm install -g mimo2codexinstall, the desktop Settings window shows a one-click "Import all into desktop" button that copies the API keys / base URLs / proxy vars over to the desktop's per-platform AppData. Existing desktop values are never overwritten — any key already set in the desktop is reported as "skipped". After import, fields are pre-filled in the multi-provider form for review before Save & Restart. Detection honors~/.mimo2codex-pointer.json, so users who previously migrated their CLI data directory via the admin UI get the active .env imported, not a stale default-location leftover. -
[new] Localized desktop application menu + top-bar "设置" entry (PR #43 — A2, thanks @starlsd93-sudo). The Electron default Windows / Linux menu bar was English-only ("File / Edit / View / Window / Help") with no Settings entry — users had to right-click the tray icon, which is unintuitive. v0.5.6 ships a unified Chinese menu on all three platforms (文件 / 编辑 / 视图 / 窗口 / 帮助). "文件 → 设置… (Ctrl+, / Cmd+,)" opens the Electron Settings window directly. As a redundant in-context entry, the admin web UI's header also has a "桌面端设置" button (only rendered inside the desktop shell, gated on
/admin/api/desktop/sentinel); it travels through a file-based signal channel (<dataDir>/.desktop-signal.jsonwritten by the sidecar, watched by Electron main) since the admin BrowserWindow has no preload bridge. -
[new] Windows app icon refresh (PR #43 — B, thanks @starlsd93-sudo). The contributor supplied a higher-resolution AI-rendered icon set; the orange variant is now wired up as
package/win/icon.ico. The full set (orange + purple, four sizes each) is preserved underpackage/brand/contributed-by-starlsd93/with provenance notes. macOS.icnsis untouched in this release — the contribution didn't include.icns/tray-Template*.png, so a future maintainer pass will refresh those.
- [new] Windows / macOS desktop app goes GA (no longer beta): after the beta-testing window that started with v0.4.8, the desktop app is now stable. Runs mimo2codex in the background; tray / menu-bar icon management; one-click admin UI; auto-update wired up. The CLI install (
npm install -g mimo2codex) is unchanged and can coexist. Downloads: https://mimodoc.chengj.online/download. - [fix]
tool_searchbuiltin now supported (issue #41): Codex Desktop's deferred-tool-discovery tool was previously dropped as an unknown type, blocking deferred tool discovery and triggering cascading orphan warnings. It's now translated to a regular function tool — works normally. - [fix] Connector plugins no longer fail with "unsupported call" (issue #39): GitHub / Canva / HeyGen / Dropbox / Gmail / Google Drive connectors require OpenAI's backend-hosted MCP runtime, which a third-party proxy can't substitute for. mimo2codex now tells the upstream model — the model suggests
shell+ a CLI alternative (e.g.ghfor GitHub) instead of failing. - [fix] Capability checks (vision, etc.) now follow the upstream model: when a runtime override / alias maps the client's
mimo-v2.5-proto upstreammimo-v2.5(which supports vision), images were still being stripped at the proxy because the check used the client literal. Fixed — capability decisions now follow the real upstream model id, so switching models at runtime takes effect immediately without restart.
- [fix] Codex Desktop namespace tools reporting
unsupported call(PR #34, issue #33, thanks @meesii): Codex Desktop's namespace-wrapped tools (e.g.spawn_agentundermulti_agent_v1) failed withunsupported callwhen routed through mimo2codex — the client uses thenamespacefield on eachfunction_calloutput item to dispatch to the correct local handler, and the proxy was dropping it during translation. The fix builds atoolName → namespaceNamemap from the request'stoolsarray and re-attachesnamespaceon both non-streaming (respToResponses) and streaming (streamToSse) outputs. Requests without namespace tools (MiMo / DeepSeek / plain Codex CLI) stay byte-identical.
- [new] Desktop preview (beta) — Windows tray / macOS menu-bar app: optional Electron companion that runs mimo2codex in the background — no terminal window required. First launch shows a small settings window to pick a provider + paste an API key; after that the tray / menu-bar icon opens the embedded admin UI (either in a window or in your default browser). The sidecar lifecycle (start / stop / restart on settings change) is fully managed; menu Quit stops it cleanly. Includes an opt-in "Start on system boot" toggle. The CLI install (
npm install -g mimo2codex) is unchanged and can coexist on the same machine — the desktop build ships as a separatev*-desktopartifact. This is a beta — installer, launch, sidecar, and auto-update flows still need real-world miles, so please report friction. Downloads + install guide: https://mimodoc.chengj.online/download. - [fix] CodeX Desktop string-input misidentified as probe (PR #31, thanks @85339098-afk): the OpenAI Responses API allows
inputto be either a string or an array of items; the probe-shape detector inhandleResponsesonly matched the array form, so requests like{model, input: "write hello world"}(CodeX Desktop's natural shape) were short-circuited to a synthetic 200 with emptyoutput: []— looked like the model said nothing, with no error signal. The check now also recognizes non-empty stringinput. Logic extracted into an exportedisResponsesProbe()helper with a focused unit-test suite (test/server.probe.test.ts) so this rule can't silently regress.
- [fix] DeepSeek V4 400
Invalid assistant message: content or tool_calls must be set(issue #29): when an assistant turn was assembled from a reasoning item + function_call without any visible text part (Codex Chrome plugin pattern), the wire shape became{role:"assistant", content: null, tool_calls:[…], reasoning_content:"…"}. DeepSeek's strict validator treats explicitnullas "neither field present" and rejects. The OpenAI Chat Completions spec sayscontentis optional whentool_callsis set, so we now OMIT the field instead of setting it to null. Reasoning-only fallback turns (rare: no text, no tools) getcontent: ""to satisfy the spec. - [fix] Windows / pnpm-global / Node 22 startup crash (issue #30):
mimo2codexno longer exits when the admin sqlite database can't be opened at startup. Typical cause: pnpm's global install layout didn't fetch a prebuiltbetter-sqlite3binary for the user's Node ABI (node-v127-win32-x64), sonew Database()throwsCould not locate the bindings file. The proxy now logs a clear, multi-line warning (with the underlying error and a Windows/pnpm-specific hint) and starts with admin DISABLED. Core Codex ↔ Chat-Completions translation never needed the DB and now works out-of-the-box on the install setups that hit this binding gap.
- [new] Desktop shell (Windows tray / macOS menu bar): optional companion app that runs mimo2codex in the background — no terminal window required. First-launch settings window for picking a provider + API key, embedded admin UI from the tray, sidecar lifecycle is fully managed (start / stop / restart on settings change), graceful quit, and an opt-in "Start on system boot" toggle. The CLI flow (
npm install -g mimo2codex) is completely unaffected; the desktop build is shipped as a separatev*-desktoprelease on GitHub. Downloads + install guide: https://mimodoc.chengj.online/download. - [opt] Desktop Mac builds ship as
.zip(was.dmg): multiple GitHub-runner hdiutil versions (macos-14 + macos-15) and dmg formats (UDZO + ULFO) consistently produced technically-valid-but-unmountable.dmgimages on consumer Macs ("此电脑不能读取你连接的磁盘" / "error 3840")..zipis boring and works everywhere — Finder unzips on double-click, dragmimo2codex.appto/Applications. The download page detects the format automatically; if a future signed.dmgis added we can re-enable that target. SHA256 verification +xattr -crfor quarantine clearing are unchanged. - [new] Proxy support: mimo2codex's outbound calls honor
HTTP_PROXY/HTTPS_PROXY/NO_PROXYenv vars — same behavior ascurl/git. Declare them indocker-compose.yml'senvironment:for Docker, orexportfrom your shell /.envfor local runs. The startup banner gains aproxy:line that echoes the active proxy so env-detection is verifiable at a glance.MIMO2CODEX_NO_PROXY_FROM_ENV=1opts out (for users whose shell keepsHTTPS_PROXYset forcurl/gitbut don't want mimo2codex to follow). - [opt] Upstream connect-failure logs carry the underlying cause's
codeandmessage(e.g.ECONNREFUSED/ENOTFOUND/ETIMEDOUT); the same detail flows into the 502UpstreamError.message, making proxy-port typos, DNS failures, and timeouts distinguishable at a glance. - [doc] Proxy FAQ §1 rewritten to spell out "system proxy ≠ process proxy" — Clash / Surge's "system proxy" toggle doesn't auto-export env vars. New 🩺 self-check callout turns the banner's
proxy:line into a one-glance diagnostic. §5 gains anECONNREFUSED <proxy-host>:<proxy-port>row (including the Docker127.0.0.1gotcha).
- [new] AI documentation assistant on the official docs site (mimodoc.chengj.online): click the bottom-right robot float — drop any common configuration question (first-time setup, why-502, generic-provider wiring, etc.) and the assistant runs a tool-calling agent loop over the project's
doc/*.mdcorpus, returning a streamed markdown answer. The reasoning trace is shown in a collapsible "thinking" panel above the answer (auto-collapses once the answer starts). MiMo V2.5 multimodal is wired in — paste / drag / click the paperclip to upload a config screenshot and the AI looks at it before answering. Chat history lives in localStorage per anonymous browser id; clear-conversation button in the drawer header.
- [new] Migrate the data directory from the admin UI: top-right ⚙️ Settings → Local data directory → Migrate. Pick a target path, preview file count + total size, then a live progress bar copies SQLite +
.env+providers.json. The server enters maintenance mode (503) while copying; the original directory is preserved so users can verify the new location before deleting. Auto-rollback on failure (partially-written destination is wiped + the old location is reopened). A persistent banner reminds the user to restart so the new directory takes effect; the resolver priority becomes CLI > env > pointer file (~/.mimo2codex-pointer.json) > default~/.mimo2codex/. - [doc] Official docs site goes live at mimodoc.chengj.online: single home for docs and tutorials. The admin footer now points at it directly with a tooltip nudge for stuck users.
- [fix] Hide server-only Codex entries in local mode: the "Export to local" / "Import from local" buttons and the
Historytab on the Codex 接入 page only make sense in Docker auth deployments (authMode=on), where operators ship renderedauth.json+config.tomlbundles between machines. Local single-user runs already write those files directly to~/.codex/, so the buttons were noise. Now gated onauthMode === "on".
- [new] Docker auth deployment goes GA: after v0.2.17 served as the preview, the Docker auth mode is now a stable feature — user registration / login, per-user m2c proxy API keys, BYOK (bring-your-own upstream key), Gitee / GitHub OAuth, downloadable Codex client config bundles. Put mimo2codex behind Docker / an internal network / a small private circle without leaking the upstream key. Local single-user runs (
authModedefaults tooff) are unaffected. Full guide: doc/auth-deployment.md — covers Docker compose, first-run bootstrap, OAuth setup, and troubleshooting. - [fix] Tool list dedup defense (issue #20): newer Codex CLI / Desktop / DeX builds emit duplicate tool names (typical shape: a top-level
_fetchfunction plus anamespace-wrapped_fetchthat flattens to a second copy), causing MiMo to 400 with"tools contains duplicate names: _fetch". reqToChat now dedupes byfunction.name/ builtintypekeep-first after the merge step; duplicates are logged atWARNso users can spot the client-side bug. - [new] Mixed-mode thinking history defense: when conversation history contains assistant messages without
reasoning_content(typical scenario: user toggled the thinking switch mid-session), automatically backfill those messages with the placeholder"(this turn ran without thinking mode)". Thinking stays ON — avoids upstream MiMo / DeepSeek 400"reasoning_content must be passed back". Logs a paired INFO line. - [opt] Quieter console log:
WARN client model rewritten on the way upstream→INFO model fallback applied — client sent unknown model id, request continues with provider default. Demoted to INFO + rephrased; it was always a graceful fallback (request succeeds), not an error. - [doc] New bilingual Proxy / Network FAQ: mac & win proxy setup, error-code lookup (502 / ECONNREFUSED / DNS / TLS-MITM, etc.), origin of the
gpt-5.4placeholder, mixed-mode thinking history explainer. - [doc] New bilingual Tag Log: migrated out of the README's
<details>changelog block; sorted newest-first with[new]/[fix]/[opt]/[doc]categorization across all 44 historical tags.
- [new] Docker auth mode (preview): users can register, log in, and generate their own m2c (mimo2codex proxy) API key. For Docker / intranet / small private deployments, replace
OPENAI_API_KEY'smimo2codex-localplaceholder with the generated m2c key — protects the upstream key from being abused. Single-user local runs (authModedefaults tooff) are unaffected.
⚠️ v0.2.17 is a preview release — the first cut of the Docker auth deployment. v0.3.0 is the GA. For production use, please run v0.3.0+. See Auth & deployment.
- [opt] Admin UI tightening: denser layout, dropped redundant displays, reduced visual noise.
Includes betas
v0.2.15-beta.0/1/2(SenseNova model adaptation + thinking fine-tuning + Kimi adaptation).
- [new] Thinking mode admin UI: the "Codex Enable" page gains a global Thinking card.
- Thinking ON/OFF: persists into the settings DB; no more
--disable-thinkingrestart. Takes effect immediately on the next request. OFF makes every provider skip thinking (thinking:{type:"disabled"}for mimo / deepseek,reasoning_effort:"none"for sensenova / other generic). - Force high reasoning effort: when Codex didn't pass
reasoning.effort, mimo2codex fills inreasoning_effort:"high". Disabled by default with a visible side-effect warning (billing can spike). CLI--disable-thinkingstill wins.
- Thinking ON/OFF: persists into the settings DB; no more
- [new] Kimi (Moonshot) preset: typing
https://api.moonshot.cn/v1(ormoonshot.ai) as baseUrl is auto-recognized and appliesdropReasoningEffort: true, so Kimi (which usesthinking:{enabled/disabled}instead ofreasoning_effort) doesn't 400 on the unknown field. Models:kimi-k2.6/kimi-k2.5/kimi-k2-thinking/kimi-k2-thinking-turbo/moonshot-v1-{8k,32k,128k}. See doc/kimi.md. - [new] Docker deployment: new
Dockerfile(multi-stage alpine, ~70MB),.dockerignore, GitHub Actions workflow that auto-builds multi-archlinux/amd64 / linux/arm64images and pushes to ghcr.io/7as0nch/mimo2codex; bundleddocker-compose.ymlfor one-command launch with the data dir bind-mounted to local./.mimo2codex/(sqlite + providers.json + admin UI config persist across container rebuilds); env supports both.envmount and-e/environment:injection. macOS / Windows / Linux. Based on #15 (thanks @hufang360). - [new] SenseNova model adaptation (from betas).
- [fix] Added inline comments to
.env.exampleso first-time users don't miss what each field means.
- [new] Version-update check (queries the upstream npm registry for newer releases). Iterated through four patches to refine network tolerance, caching, and message phrasing.
- [new] Universal
.envconfig:mimo2codex initthen fill in keys — same config across platforms.
MiniMax / strict OpenAI-compatible upstream support patchset (PR #12).
- [fix]
reqToChat: no longer sendsstrict: nullupstream (MiMo's Pydantic schema rejects null and 400s with"Input should be a valid boolean"). Fixes issue #11. - [fix]
minimax-compat: one-click preset no longer stripsstream_options/parallel_tool_callsby default. - [feat]
minimax-compat: inline<think>...</think>on the response side is split intoreasoning_content. - [feat] Admin webui providers form: new "Strict OpenAI compat" switch group (covers minimaxCompat etc.).
- [feat] Generic provider gains the MiniMax-compat patch (issue #7).
- [new] Full admin webui rewrite on Ant Design 5: dark/light themes, EN/中文 i18n, viewport-locked sider + footer, smoothed Token-usage curves.
- [new]
.env.example+ Bash / PowerShell one-liner key-loader scripts (.envis gitignored). - [new] Per-model ⚡Probe button on "Codex Enable": fires a minimal ping to validate key / baseUrl / model id end-to-end.
- [new] Token-usage chart folds in cache-hit bars (green = hits, gray ghost = prompt totals) plus a window-wide hit-rate summary.
- [new] Customizable Codex dir via settings or the
CODEX_HOMEenv var.
Includes betas
v0.2.6-beta.1/2/3: MiMo models'contextWindow128K → 1M (matching DeepSeek; fixes Codex 256K-config 400); webui refactor PR #1~#6 (antd 5 base, Setup/Models/CodexEnable theming, Logs table, Dashboard cache-hit overlay, viewport lockdown, etc.).
- [new] "Codex Enable" page (replaces cc-switch): admin webui writes
~/.codex/auth.json+config.tomlin one click. - [new] Runtime override: swap upstream models without restarting Codex.
- [new] Permanent backup retention + half-broken pair recovery + manual deletion: originals are auto-backed-up, and the first backup capturing your real external auth.json is permanently preserved — switch models 100 times and you can still restore the original Codex config.
- [fix]
removeOrphanToolMessages: drops orphan tool messages on DeepSeek V4 session desync, preventing 400"Messages with role 'tool' must be a response to..."(PR #10 / issue #8). - See doc/codex-enable.md.
Includes beta
v0.2.5-beta.1.
- [feat] MiMo / DeepSeek docs aligned.
- [fix] DeepSeek
tool_calls400 fix. - [feat] Friendly context-overflow error: surfaces a readable
/compacthint instead of a raw 400. - [feat] Beta release workflow (
npm run release:beta).
- [test] Added two-stage priority regression tests for
selectProvider. - [doc] Generic-provider routing-priority docs updated to match.
- [fix] Fixed MiMo
reasoning_contentround-trip per Xiaomi's official guidance.
- [fix] GitHub Actions workflow fix.
- [new] Added
mimoskill— Python helpers for image generation, OCR, etc. (stdlib only, no pip).
- [new] Early
mimoskilliteration (v0.1.17 ~ v0.1.19): image gen / OCR / pet generation, polished step by step. - [new] v0.1.16: support for additional models with
wireApi="responses"direct passthrough (in addition to the default mimo / deepseek chat-translation path).
- [fix] Registered
mimo-v2.5vision model in the builtin catalog so it no longer silently falls back tomimo-v2.5-pro(which would drop images).
Early-project iteration (v0.1.1 was the first public release on 2026-05-09). No detailed changelog kept for this phase; main work:
- mimo / deepseek dual-provider scaffolding.
- Responses API ↔ Chat Completions bidirectional translation core (
reqToChat/respToResponses/streamToSse). - First-cut admin webui (Tokens / Logs / Settings pages).
- SQLite persistence (chat logs, model catalog, runtime settings).
- CLI:
mimo2codex init/update/print-config/print-cc-switch.
Browse the full commit stream with git log v0.1.1..v0.1.14 --oneline.
Defined in package.json:
npm run release:patch # x.y.Z+1
npm run release:minor # x.Y+1.0
npm run release:major # X+1.0.0
npm run release:beta # pre-releaseFull runbook: PUBLISHING.md (repo root).
