Files
magpie/internal/agent
yetone ffa55fc277 copilot: Auto — Copilot's own pick, the only choice a Student plan has — is listed for every Copilot account and works through magpie, as Copilot's clients do it (#256; Discord, CakeJskSUMM: Copilot Student can only use Auto) — Copilot's /models doesn't list Auto; its clients add it themselves (Copilot CLI 1.0.79: {id:"auto",name:"Auto"}, AUTO_MODEL_ID) and ask POST {api}/models/session with {"auto_mode":{"model_hints":["auto"]}} for a session_token, the selected_model and expires_at, each request then naming that model with the token in Copilot-Session-Token. magpie listed no Auto, failed an account whose list left nothing to pick by hand ("Copilot lists no chat model for this account"), and sent "auto" as a model. Now copilot_auto.go keeps the session per GitHub account, asked again two minutes before it expires; the gateway resolves Auto before choosing the API, so a picked model served only on /responses goes there; a body still naming "auto" (the model test) is rewritten; the endpoints of every model Copilot lists, hidden ones too, are kept for the picked one. A reply to "auto" from another model isn't counted as swapped (Cursor's auto too); the Copilot CLI's picker, which offers auto itself, leaves out the listed one. Tested with fakes in a sandbox HOME: provider/copilot_auto_test.go (a Student-like account whose models are all disabled lists [auto], the session body sent as above, requests carry the picked gpt-5-mini and the token, one session reused, renewed near expiry, an object-shaped selected_model, a refusal reported) and gateway/copilot_auto_test.go (a chat request for copilot/auto goes to /responses as gpt-5-mini with the token, two turns one session; fails without the gateway change); served_test gains the auto cases. go vet ./internal/..., go build -tags nogui (and GOOS=linux, GOOS=windows) and go test -tags nogui ./... pass. Not tried with a real Student account: what its /models returns, whether the plain session request suffices (Auto v2's /models/session/intent is not done), and the body is from VS Code's Copilot Chat, not captured traffic.
2026-09-30 10:17:15 +08:00
..
toml: Codex's config.toml with a multi-line array (notify = [ … ]) is edited right, and an edit that fails leaves every file as it was (#253 kerwinliu00001: "edited TOML is invalid: line 2, column 1: incomplete number", config.toml left with model_provider gone and [model_providers.magpie] added but no model) — the top-level key edits (GetTOMLTop, SetTOMLTop, DelTOMLTop) went line by line with regexes: a new key went after the last line that looked like key = …, which for notify = [ was inside the array; key = value lines inside a multi-line string were read as keys and a line starting with [ as a table; replacing or deleting a multi-line value touched only its first line. They now locate the top-level keys with the TOML parser, as the table edits already did, each with its value's whole range however many lines it takes (parseTOMLFile, the unnamed root table beside the tables); SetTOMLTop finds all its keys in the file as read and writes once. A file the parser refuses is still read line by line, so a broken config keeps showing its model, and writes to it are refused untouched. And a pick is several writes (Codex: drop model_provider, add magpie's provider table, set openai_base_url and model), each checked, so one failing left the earlier ones on disk: edit.Atomically(fn, paths…) puts each file back to its bytes and mode, or removes one it created, when fn fails, and every field set and Sync of Codex (config.toml and magpie-models.json, so Codex in WSL too), Kimi and Grok goes through it, as do Codex's MCP server writes (putCodex, delCodex). Tested in temp dirs and a temp HOME: the reporter's file read, set and deleted directly and through Codex's model field (signed in, with model_provider and model above the array, with magpie as the provider), the array's text kept and the TOML valid, and back to a Codex model; nested arrays, trailing commas, comments in arrays, [x] and a = b in strings, an inline table in an array; """ and ''' strings holding model = … and [table] lines not read as keys, new keys after their closing quotes, one deleted whole; a forced failure of Codex's last write leaving config.toml byte-identical; TestAtomically. They fail on main (the Codex ones with the reporter's error). go vet ./internal/..., go build -tags nogui (and GOOS=linux, GOOS=windows) and go test -tags nogui ./... pass. Not tried against a real ~/.codex, in WSL or in the GUI; multi-line inline tables (TOML 1.1) are refused by the parser and so left untouched with an error.
2026-09-30 09:31:59 +08:00
copilot: Auto — Copilot's own pick, the only choice a Student plan has — is listed for every Copilot account and works through magpie, as Copilot's clients do it (#256; Discord, CakeJskSUMM: Copilot Student can only use Auto) — Copilot's /models doesn't list Auto; its clients add it themselves (Copilot CLI 1.0.79: {id:"auto",name:"Auto"}, AUTO_MODEL_ID) and ask POST {api}/models/session with {"auto_mode":{"model_hints":["auto"]}} for a session_token, the selected_model and expires_at, each request then naming that model with the token in Copilot-Session-Token. magpie listed no Auto, failed an account whose list left nothing to pick by hand ("Copilot lists no chat model for this account"), and sent "auto" as a model. Now copilot_auto.go keeps the session per GitHub account, asked again two minutes before it expires; the gateway resolves Auto before choosing the API, so a picked model served only on /responses goes there; a body still naming "auto" (the model test) is rewritten; the endpoints of every model Copilot lists, hidden ones too, are kept for the picked one. A reply to "auto" from another model isn't counted as swapped (Cursor's auto too); the Copilot CLI's picker, which offers auto itself, leaves out the listed one. Tested with fakes in a sandbox HOME: provider/copilot_auto_test.go (a Student-like account whose models are all disabled lists [auto], the session body sent as above, requests carry the picked gpt-5-mini and the token, one session reused, renewed near expiry, an object-shaped selected_model, a refusal reported) and gateway/copilot_auto_test.go (a chat request for copilot/auto goes to /responses as gpt-5-mini with the token, two turns one session; fails without the gateway change); served_test gains the auto cases. go vet ./internal/..., go build -tags nogui (and GOOS=linux, GOOS=windows) and go test -tags nogui ./... pass. Not tried with a real Student account: what its /models returns, whether the plain session request suffices (Auto v2's /models/session/intent is not done), and the body is from VS Code's Copilot Chat, not captured traffic.
2026-09-30 10:17:15 +08:00
agents: Antigravity CLI (agy) takes magpie's models (Discord Str: asked to use agy through magpie) — agy is on the Agents page like the others. Picking one of magpie's models writes ~/.gemini/antigravity-cli/settings.json: modelProvider "gemini", an entry per magpie model under customModelsConfig.customModels (magpie/<ref> → {apiProvider: API_PROVIDER_GOOGLE_GEMINI, modelName: <ref>}) beside the user's own, and model. Every other key is kept, and "as installed" gives back the user's model and modelProvider and takes magpie's entries out (the file byte-identical to before). agy reads its endpoint and key only from GEMINI_API_KEY / GOOGLE_GEMINI_BASE_URL, and in agy 1.2.13 a custom model chosen by settings or its picker fails ("model is empty"): only --model resolves it. So the agent has a Launch command, GEMINI_API_KEY=magpie GOOGLE_GEMINI_BASE_URL=<gateway> agy --model '<model>' (a $env: form on Windows), which magpie agy prints as its notice and the row's new square copies in one click (tooltip shows it; no scroll; zh strings). Check says unwired when the entry is gone or modelProvider isn't gemini. Tested with TestAgy (set/switch/drift/restore, a user's custom model kept, a file with nothing of the user's left as it was) and agy-launch.test.cjs in Chromium and WebKit, en and zh (fails without the app.js change); end to end in a sandbox HOME with agy 1.2.13 against a sandbox gateway on 38472 and a fake OpenAI upstream: magpie agy magpie/fakeup/echo-1 wrote the settings, agy (interactive and --print) answered from the upstream through the gateway, magpie agy default restored the file. Not tested: the Windows command, a real Gemini upstream; agy's requests show in usage as google-genai-sdk (its UA), and its title requests for gemini-3.1-flash-lite-preview get a 404. go build (nogui, linux, windows) and go test -tags nogui ./... pass; the GUI suite passes but for click-scroll's WebKit case, failing already
2026-09-29 19:41:29 +08:00
agents: Antigravity CLI (agy) takes magpie's models (Discord Str: asked to use agy through magpie) — agy is on the Agents page like the others. Picking one of magpie's models writes ~/.gemini/antigravity-cli/settings.json: modelProvider "gemini", an entry per magpie model under customModelsConfig.customModels (magpie/<ref> → {apiProvider: API_PROVIDER_GOOGLE_GEMINI, modelName: <ref>}) beside the user's own, and model. Every other key is kept, and "as installed" gives back the user's model and modelProvider and takes magpie's entries out (the file byte-identical to before). agy reads its endpoint and key only from GEMINI_API_KEY / GOOGLE_GEMINI_BASE_URL, and in agy 1.2.13 a custom model chosen by settings or its picker fails ("model is empty"): only --model resolves it. So the agent has a Launch command, GEMINI_API_KEY=magpie GOOGLE_GEMINI_BASE_URL=<gateway> agy --model '<model>' (a $env: form on Windows), which magpie agy prints as its notice and the row's new square copies in one click (tooltip shows it; no scroll; zh strings). Check says unwired when the entry is gone or modelProvider isn't gemini. Tested with TestAgy (set/switch/drift/restore, a user's custom model kept, a file with nothing of the user's left as it was) and agy-launch.test.cjs in Chromium and WebKit, en and zh (fails without the app.js change); end to end in a sandbox HOME with agy 1.2.13 against a sandbox gateway on 38472 and a fake OpenAI upstream: magpie agy magpie/fakeup/echo-1 wrote the settings, agy (interactive and --print) answered from the upstream through the gateway, magpie agy default restored the file. Not tested: the Windows command, a real Gemini upstream; agy's requests show in usage as google-genai-sdk (its UA), and its title requests for gemini-3.1-flash-lite-preview get a 404. go build (nogui, linux, windows) and go test -tags nogui ./... pass; the GUI suite passes but for click-scroll's WebKit case, failing already
2026-09-29 19:41:29 +08:00
agents: Antigravity CLI (agy) takes magpie's models (Discord Str: asked to use agy through magpie) — agy is on the Agents page like the others. Picking one of magpie's models writes ~/.gemini/antigravity-cli/settings.json: modelProvider "gemini", an entry per magpie model under customModelsConfig.customModels (magpie/<ref> → {apiProvider: API_PROVIDER_GOOGLE_GEMINI, modelName: <ref>}) beside the user's own, and model. Every other key is kept, and "as installed" gives back the user's model and modelProvider and takes magpie's entries out (the file byte-identical to before). agy reads its endpoint and key only from GEMINI_API_KEY / GOOGLE_GEMINI_BASE_URL, and in agy 1.2.13 a custom model chosen by settings or its picker fails ("model is empty"): only --model resolves it. So the agent has a Launch command, GEMINI_API_KEY=magpie GOOGLE_GEMINI_BASE_URL=<gateway> agy --model '<model>' (a $env: form on Windows), which magpie agy prints as its notice and the row's new square copies in one click (tooltip shows it; no scroll; zh strings). Check says unwired when the entry is gone or modelProvider isn't gemini. Tested with TestAgy (set/switch/drift/restore, a user's custom model kept, a file with nothing of the user's left as it was) and agy-launch.test.cjs in Chromium and WebKit, en and zh (fails without the app.js change); end to end in a sandbox HOME with agy 1.2.13 against a sandbox gateway on 38472 and a fake OpenAI upstream: magpie agy magpie/fakeup/echo-1 wrote the settings, agy (interactive and --print) answered from the upstream through the gateway, magpie agy default restored the file. Not tested: the Windows command, a real Gemini upstream; agy's requests show in usage as google-genai-sdk (its UA), and its title requests for gemini-3.1-flash-lite-preview get a 404. go build (nogui, linux, windows) and go test -tags nogui ./... pass; the GUI suite passes but for click-scroll's WebKit case, failing already
2026-09-29 19:41:29 +08:00
wsl: Claude Code installed in a WSL distro is found and wired as its own agent, Claude Code · WSL <distro> (#254 Xws-s: magpie finds Codex and Pi in WSL but not Claude Code) — the WSL registry gains Claude Code: the probe looks for ~/.claude and command -v claude, and claude@wsl:<distro> edits the distro's ~/.claude/settings.json through \\wsl.localhost, with the gateway as the distro reaches it (127.0.0.1 when mirrored, Windows' address under NAT). claude(home) is now claudeIn(here(home)) like Codex and Pi: the base URL written, the routed test and Check use the place's gateway; what Claude Code had before magpie (model, base URL, token) is kept under claude@wsl:<distro> so it never mixes with Windows'; Check reads the distro's own /etc/claude-code/managed-settings.json. A stopped distro shows Claude Code's and magpie's models without being opened. Claude Code's model stand-in (a Claude name magpie doesn't serve) also looks at the routed distros seen running, never starting one. Sign-in, sessions, prompt history and Last used stay Windows-only, as for the other WSL agents. Native Claude Code is as before. Tested in wsl_claude_test.go with fake distros in a sandbox HOME: probe and discovery (folder only, program only, beside Codex and Pi), a pick under NAT and mirrored writing the distro's address and otherwise the same settings.json, Check and managed settings, restoring on the way back, a stopped distro never opened, the stand-in. go vet ./internal/..., go build -tags nogui (and GOOS=linux, GOOS=windows) and go test -tags nogui ./... pass. Not tried on real Windows/WSL.
2026-09-30 10:02:51 +08:00
agent: Claude Desktop runs on magpie in its third-party mode (#156: Claude Desktop 不会解锁 Claude Code 面板 — CC Switch 可以,咱这个支持吗?) — signed in with a Free account, Desktop's Code tab is only "Get Claude Code"; in its 3p deployment mode it needs no Anthropic sign-in and Code and Cowork run on the gateway. A Claude Desktop agent whose provider field switches it: on writes deploymentMode "3p" into Claude/ and Claude-3p/claude_desktop_config.json, a magpie profile in Claude-3p/configLibrary/<id>.json (inferenceProvider gateway, inferenceGatewayBaseUrl the gateway's address, inferenceGatewayApiKey magpie-claude-desktop, inferenceGatewayAuthScheme bearer; disableDeploymentModeChooser and coworkEgressAllowedHosts ["*"] only where unset) and its entry in _meta.json, applied — the keys and files CC Switch writes (claude_desktop_config.rs). No inferenceModels: Desktop lists /v1/models, which the gateway already gives it by ids it keeps. Off puts each deploymentMode back as it was (gone where there was none), the profile applied before applied again, magpie's profile and entry removed, and files and folders magpie made gone if nothing else went in; other keys and profiles are never touched. Folders as CC Switch finds them: ~/Library/Application Support, %LOCALAPPDATA% (a Claude… folder there if not Claude itself), $XDG_CONFIG_HOME. Desktop reads them at start-up, which the picker and notice say. The Library keeps giving Desktop's MCP servers to %APPDATA%'s file on Windows, listed once. Tested in a sandbox home: fresh, with the user's settings and 3p profiles (off restores every byte), on twice, on → off → on, drift, odd files refused, each system's folders; and against Claude Desktop 2.9939.2 on Windows 11 (winget): on, it started in 3P mode (provider gateway, apiHost the gateway) with the Code tab and no sign-in, and asked the gateway's /v1/models; off, it went back to claude.ai
2026-09-28 22:37:26 +08:00
agent: Claude Desktop runs on magpie in its third-party mode (#156: Claude Desktop 不会解锁 Claude Code 面板 — CC Switch 可以,咱这个支持吗?) — signed in with a Free account, Desktop's Code tab is only "Get Claude Code"; in its 3p deployment mode it needs no Anthropic sign-in and Code and Cowork run on the gateway. A Claude Desktop agent whose provider field switches it: on writes deploymentMode "3p" into Claude/ and Claude-3p/claude_desktop_config.json, a magpie profile in Claude-3p/configLibrary/<id>.json (inferenceProvider gateway, inferenceGatewayBaseUrl the gateway's address, inferenceGatewayApiKey magpie-claude-desktop, inferenceGatewayAuthScheme bearer; disableDeploymentModeChooser and coworkEgressAllowedHosts ["*"] only where unset) and its entry in _meta.json, applied — the keys and files CC Switch writes (claude_desktop_config.rs). No inferenceModels: Desktop lists /v1/models, which the gateway already gives it by ids it keeps. Off puts each deploymentMode back as it was (gone where there was none), the profile applied before applied again, magpie's profile and entry removed, and files and folders magpie made gone if nothing else went in; other keys and profiles are never touched. Folders as CC Switch finds them: ~/Library/Application Support, %LOCALAPPDATA% (a Claude… folder there if not Claude itself), $XDG_CONFIG_HOME. Desktop reads them at start-up, which the picker and notice say. The Library keeps giving Desktop's MCP servers to %APPDATA%'s file on Windows, listed once. Tested in a sandbox home: fresh, with the user's settings and 3p profiles (off restores every byte), on twice, on → off → on, drift, odd files refused, each system's folders; and against Claude Desktop 2.9939.2 on Windows 11 (winget): on, it started in 3P mode (provider gateway, apiHost the gateway) with the Code tab and no sign-in, and asked the gateway's /v1/models; off, it went back to claude.ai
2026-09-28 22:37:26 +08:00
toml: Codex's config.toml with a multi-line array (notify = [ … ]) is edited right, and an edit that fails leaves every file as it was (#253 kerwinliu00001: "edited TOML is invalid: line 2, column 1: incomplete number", config.toml left with model_provider gone and [model_providers.magpie] added but no model) — the top-level key edits (GetTOMLTop, SetTOMLTop, DelTOMLTop) went line by line with regexes: a new key went after the last line that looked like key = …, which for notify = [ was inside the array; key = value lines inside a multi-line string were read as keys and a line starting with [ as a table; replacing or deleting a multi-line value touched only its first line. They now locate the top-level keys with the TOML parser, as the table edits already did, each with its value's whole range however many lines it takes (parseTOMLFile, the unnamed root table beside the tables); SetTOMLTop finds all its keys in the file as read and writes once. A file the parser refuses is still read line by line, so a broken config keeps showing its model, and writes to it are refused untouched. And a pick is several writes (Codex: drop model_provider, add magpie's provider table, set openai_base_url and model), each checked, so one failing left the earlier ones on disk: edit.Atomically(fn, paths…) puts each file back to its bytes and mode, or removes one it created, when fn fails, and every field set and Sync of Codex (config.toml and magpie-models.json, so Codex in WSL too), Kimi and Grok goes through it, as do Codex's MCP server writes (putCodex, delCodex). Tested in temp dirs and a temp HOME: the reporter's file read, set and deleted directly and through Codex's model field (signed in, with model_provider and model above the array, with magpie as the provider), the array's text kept and the TOML valid, and back to a Codex model; nested arrays, trailing commas, comments in arrays, [x] and a = b in strings, an inline table in an array; """ and ''' strings holding model = … and [table] lines not read as keys, new keys after their closing quotes, one deleted whole; a forced failure of Codex's last write leaving config.toml byte-identical; TestAtomically. They fail on main (the Codex ones with the reporter's error). go vet ./internal/..., go build -tags nogui (and GOOS=linux, GOOS=windows) and go test -tags nogui ./... pass. Not tried against a real ~/.codex, in WSL or in the GUI; multi-line inline tables (TOML 1.1) are refused by the parser and so left untouched with an error.
2026-09-30 09:31:59 +08:00
agent: Claude Desktop runs on magpie in its third-party mode (#156: Claude Desktop 不会解锁 Claude Code 面板 — CC Switch 可以,咱这个支持吗?) — signed in with a Free account, Desktop's Code tab is only "Get Claude Code"; in its 3p deployment mode it needs no Anthropic sign-in and Code and Cowork run on the gateway. A Claude Desktop agent whose provider field switches it: on writes deploymentMode "3p" into Claude/ and Claude-3p/claude_desktop_config.json, a magpie profile in Claude-3p/configLibrary/<id>.json (inferenceProvider gateway, inferenceGatewayBaseUrl the gateway's address, inferenceGatewayApiKey magpie-claude-desktop, inferenceGatewayAuthScheme bearer; disableDeploymentModeChooser and coworkEgressAllowedHosts ["*"] only where unset) and its entry in _meta.json, applied — the keys and files CC Switch writes (claude_desktop_config.rs). No inferenceModels: Desktop lists /v1/models, which the gateway already gives it by ids it keeps. Off puts each deploymentMode back as it was (gone where there was none), the profile applied before applied again, magpie's profile and entry removed, and files and folders magpie made gone if nothing else went in; other keys and profiles are never touched. Folders as CC Switch finds them: ~/Library/Application Support, %LOCALAPPDATA% (a Claude… folder there if not Claude itself), $XDG_CONFIG_HOME. Desktop reads them at start-up, which the picker and notice say. The Library keeps giving Desktop's MCP servers to %APPDATA%'s file on Windows, listed once. Tested in a sandbox home: fresh, with the user's settings and 3p profiles (off restores every byte), on twice, on → off → on, drift, odd files refused, each system's folders; and against Claude Desktop 2.9939.2 on Windows 11 (winget): on, it started in 3P mode (provider gateway, apiHost the gateway) with the Code tab and no sign-in, and asked the gateway's /v1/models; off, it went back to claude.ai
2026-09-28 22:37:26 +08:00
toml: Codex's config.toml with a multi-line array (notify = [ … ]) is edited right, and an edit that fails leaves every file as it was (#253 kerwinliu00001: "edited TOML is invalid: line 2, column 1: incomplete number", config.toml left with model_provider gone and [model_providers.magpie] added but no model) — the top-level key edits (GetTOMLTop, SetTOMLTop, DelTOMLTop) went line by line with regexes: a new key went after the last line that looked like key = …, which for notify = [ was inside the array; key = value lines inside a multi-line string were read as keys and a line starting with [ as a table; replacing or deleting a multi-line value touched only its first line. They now locate the top-level keys with the TOML parser, as the table edits already did, each with its value's whole range however many lines it takes (parseTOMLFile, the unnamed root table beside the tables); SetTOMLTop finds all its keys in the file as read and writes once. A file the parser refuses is still read line by line, so a broken config keeps showing its model, and writes to it are refused untouched. And a pick is several writes (Codex: drop model_provider, add magpie's provider table, set openai_base_url and model), each checked, so one failing left the earlier ones on disk: edit.Atomically(fn, paths…) puts each file back to its bytes and mode, or removes one it created, when fn fails, and every field set and Sync of Codex (config.toml and magpie-models.json, so Codex in WSL too), Kimi and Grok goes through it, as do Codex's MCP server writes (putCodex, delCodex). Tested in temp dirs and a temp HOME: the reporter's file read, set and deleted directly and through Codex's model field (signed in, with model_provider and model above the array, with magpie as the provider), the array's text kept and the TOML valid, and back to a Codex model; nested arrays, trailing commas, comments in arrays, [x] and a = b in strings, an inline table in an array; """ and ''' strings holding model = … and [table] lines not read as keys, new keys after their closing quotes, one deleted whole; a forced failure of Codex's last write leaving config.toml byte-identical; TestAtomically. They fail on main (the Codex ones with the reporter's error). go vet ./internal/..., go build -tags nogui (and GOOS=linux, GOOS=windows) and go test -tags nogui ./... pass. Not tried against a real ~/.codex, in WSL or in the GUI; multi-line inline tables (TOML 1.1) are refused by the parser and so left untouched with an error.
2026-09-30 09:31:59 +08:00
agents: a model picked for Claude Code (or any agent) shows on its row at once, in the window and the tray panel, and /api/set answers in ~0.1s instead of seconds (Discord: switching Claude Code's model took 5–10s to show, magpie 0.1.410+) — /api/set writes the config and answers with the whole state, and the frontend drew the pick only from that answer. Every state asked the devin CLI (devin models list, ~1.5–3s a run) again several times whenever Devin was installed: its model's family, its options, its effort's options and Drift's re-read, and a CLI that gave no list (signed out, offline) was asked anew on every state, since only a list was cached. With a 4s CLI one set took 28s (measured, before). Now DevinFamilies keeps a failure for a minute, serves a list past its 5 minutes while it is read again in the background, and only a caller with nothing known waits — never one marked DevinNoWait, which Devin's field reads and options are (its Set still waits, so what is written is unchanged). Goose's detection read a binary's whole build info on every state (~150ms); the answer is kept while the file's size and time are the same. In the page, commit() draws the picked model and its flash before /api/set is sent, then draws the answer (a later pick's answer winning over an earlier one's); a refused pick puts the old model back and gives the reason. What is written to the agent's config is as before. Measured in a sandbox HOME with magpie web: /api/state 2.8–3.1s and /api/set 2.9s before, 0.1–0.5s after (state() ~110ms steady in-process); a provider whose endpoint never answers made no difference either way. Tested with internal/gui/set_fast_unix_test.go (devin CLI that sleeps 4s and a provider that never answers: three sets and the states after them each under 1.5s, ~5ms, settings.json as before; fails before the fix at 28s a set) and internal/gui/tests/model-pick.test.cjs (a set held 2.5s: the pick shown within 600ms in the window scrolled and in the tray panel's opened row and line, the page not moved, the answer keeping it, a refusal restoring the old model, en and zh, Chromium and WebKit; fails on main). click-scroll, panel-fold, menu-scroll, agy-launch, cli-update and old-webkit pass. go build -tags nogui (and GOOS=linux, GOOS=windows), go vet and go test -tags nogui ./... pass (TestClaudeAccountNamedAsItsLogin failed under load and passes alone). Not tried in the real Wails window or tray, nor with a real signed-in Devin CLI.
2026-09-30 03:21:24 +08:00
agents: Factory's Droid CLI is an agent magpie sets up (#242 tasselx) — droid keeps its settings in ~/.factory/settings.json ($FACTORY_HOME_OVERRIDE/.factory when set), its BYOK models the customModels array ({model, displayName, baseUrl, apiKey, provider "anthropic" | "openai" (Responses) | "generic-chat-completion-api", maxOutputTokens, noImageSupport …}), picked as "custom:<id>". Picking a catalog model through magpie appends one entry per catalog model after the user's own (so their ids never move), each with an explicit id "custom:magpie/<provider>/<model>", the catalog's label as displayName, the gateway and token magpie, its context (maxContextLimit) and output when known, noImageSupport always written (droid takes a Chat Completions model as blind otherwise), and the API its provider speaks natively so the gateway relays it as it is: Responses → "openai" at /v1, Messages → "anthropic" at the gateway root (Anthropic's SDK adds /v1), anything else "generic-chat-completion-api" at /v1; and sets sessionDefaultSettings.model. The model shows in magpie as magpie/<ref>; the user's sessionDefaultSettings.model is stashed and put back on reset (a top-level "model", the docs' spelling, is left alone and is what droid falls back to), magpie's entries go and an emptied customModels or sessionDefaultSettings with them; everything else in the file, the user's entries included, is kept byte for byte. The picker offers the user's own custom models by the id droid gives them (its "id", else the displayName with whitespace as dashes and a count of the same name before it), including the legacy ~/.factory/config.json custom_models, which magpie never writes. Sync rewrites magpie's entries as providers change; Check reports a missing entry or a changed baseUrl/apiKey; its requests are Droid's in usage (User-Agent factory-cli/<version>). Icon is Factory's own favicon glyph (factory.ai/favicon.svg); lobehub has none. From the docs: docs.factory.ai/cli/byok/overview (customModels fields, the three providers, config.json legacy snake_case merged under settings.json), /cli/configuration/settings (model, sessionDefaultSettings.*), /droid-exec/overview (custom:<Display-Name>-<index>). Verified by reading the shipped droid 0.229.0 bundle (@factory/cli-linux-x64, unpacked, never installed or run): the zod schema of an entry (also id, index, maxContextLimit, enableThinking, reasoningEffort, bedrock-converse), ids as custom:${displayName.trim().replace(/\s+/g,"-")}-${n} with n counting equal names, an entry's own id winning, the lookup by id, top-level model/reasoningEffort/specModeModel moved into sessionDefaultSettings only when unset there, getModel reading sessionDefaultSettings.model, noImageSupport defaulting by provider, FACTORY_HOME_OVERRIDE, and BYOK requests carrying factory-cli/<version>. Tested in a sandbox HOME (internal/agent/droid_test.go): set/sync/reset on a settings.json as droid leaves it with a Groq BYOK model (its entry byte-identical, the file restored exactly), each protocol's provider and baseUrl, a moved baseUrl caught by Check and healed by Sync, picking the user's own model while on magpie's, no file at all, a top-level model, FACTORY_HOME_OVERRIDE and legacy config.json ids (custom:Local-Qwen-0/-1) offered and never written. go build -tags nogui (and GOOS=linux, GOOS=windows) and go test -tags nogui ./... pass (TestClaudeSignedOut and TestClaudeAccountNamedAsItsLogin flaked under load and pass alone). Not tried with a real droid: whether droid's /model lists the entries, that it reaches the gateway on each protocol, and how it sends thinking for a custom model (magpie sets no reasoningEffort/enableThinking) are unverified.
2026-09-30 02:35:59 +08:00
agents: Factory's Droid CLI is an agent magpie sets up (#242 tasselx) — droid keeps its settings in ~/.factory/settings.json ($FACTORY_HOME_OVERRIDE/.factory when set), its BYOK models the customModels array ({model, displayName, baseUrl, apiKey, provider "anthropic" | "openai" (Responses) | "generic-chat-completion-api", maxOutputTokens, noImageSupport …}), picked as "custom:<id>". Picking a catalog model through magpie appends one entry per catalog model after the user's own (so their ids never move), each with an explicit id "custom:magpie/<provider>/<model>", the catalog's label as displayName, the gateway and token magpie, its context (maxContextLimit) and output when known, noImageSupport always written (droid takes a Chat Completions model as blind otherwise), and the API its provider speaks natively so the gateway relays it as it is: Responses → "openai" at /v1, Messages → "anthropic" at the gateway root (Anthropic's SDK adds /v1), anything else "generic-chat-completion-api" at /v1; and sets sessionDefaultSettings.model. The model shows in magpie as magpie/<ref>; the user's sessionDefaultSettings.model is stashed and put back on reset (a top-level "model", the docs' spelling, is left alone and is what droid falls back to), magpie's entries go and an emptied customModels or sessionDefaultSettings with them; everything else in the file, the user's entries included, is kept byte for byte. The picker offers the user's own custom models by the id droid gives them (its "id", else the displayName with whitespace as dashes and a count of the same name before it), including the legacy ~/.factory/config.json custom_models, which magpie never writes. Sync rewrites magpie's entries as providers change; Check reports a missing entry or a changed baseUrl/apiKey; its requests are Droid's in usage (User-Agent factory-cli/<version>). Icon is Factory's own favicon glyph (factory.ai/favicon.svg); lobehub has none. From the docs: docs.factory.ai/cli/byok/overview (customModels fields, the three providers, config.json legacy snake_case merged under settings.json), /cli/configuration/settings (model, sessionDefaultSettings.*), /droid-exec/overview (custom:<Display-Name>-<index>). Verified by reading the shipped droid 0.229.0 bundle (@factory/cli-linux-x64, unpacked, never installed or run): the zod schema of an entry (also id, index, maxContextLimit, enableThinking, reasoningEffort, bedrock-converse), ids as custom:${displayName.trim().replace(/\s+/g,"-")}-${n} with n counting equal names, an entry's own id winning, the lookup by id, top-level model/reasoningEffort/specModeModel moved into sessionDefaultSettings only when unset there, getModel reading sessionDefaultSettings.model, noImageSupport defaulting by provider, FACTORY_HOME_OVERRIDE, and BYOK requests carrying factory-cli/<version>. Tested in a sandbox HOME (internal/agent/droid_test.go): set/sync/reset on a settings.json as droid leaves it with a Groq BYOK model (its entry byte-identical, the file restored exactly), each protocol's provider and baseUrl, a moved baseUrl caught by Check and healed by Sync, picking the user's own model while on magpie's, no file at all, a top-level model, FACTORY_HOME_OVERRIDE and legacy config.json ids (custom:Local-Qwen-0/-1) offered and never written. go build -tags nogui (and GOOS=linux, GOOS=windows) and go test -tags nogui ./... pass (TestClaudeSignedOut and TestClaudeAccountNamedAsItsLogin flaked under load and pass alone). Not tried with a real droid: whether droid's /model lists the entries, that it reaches the gateway on each protocol, and how it sends thinking for a custom model (magpie sets no reasoningEffort/enableThinking) are unverified.
2026-09-30 02:35:59 +08:00
mimo: a Xiaomi MiMo account (the one MiMo's app signs in to) is signed in inside magpie and its models served through the gateway to any agent, and MiMo Code is an agent magpie configures (#249 thedavidweng) — magpie had neither. The subscription (id mimo-app, shown as Xiaomi MiMo) signs in with Xiaomi's long-poll login (account.xiaomi.com/longPolling/loginUrl, sid mimosgp) and keeps the MiMo server's cookie session (serviceToken, userId) in logins.json at 0600; the passToken is replayed to renew the session after a day or on a 401. Requests go to POST <base>/route/chat/completions on mimo-server-{sgp,ru,in}.xiaomimimo.com/api with X-Mimo-Source mimocode-cli-free and X-Client-Version; mimo-auto is sent as mimo-pro; the models are mimo-pro and mimo-flash. Usage from /user/usage and /user/xiaomi/subscription/self. Several accounts, switched, on/off and forgotten as the other subscriptions. The add list has a Xiaomi MiMo tile (Free · Starter · Plus · Pro · Ultra) that says Xiaomi may treat this as third-party use before anything is posted; en and zh. magpie accounts takes mimo. MiMo Code (agent mimocode, OpenCode-like) is configured in its config dir (MIMOCODE_HOME/config when set), reading mimocode.jsonc, mimocode.json or config.json, whichever is there. Tested with internal/provider/mimo_test.go against fake Xiaomi SSO and MiMo servers in a temp HOME, internal/agent tests for MiMo Code's config, and internal/gui/tests/mimo-sub.test.cjs, Chromium and WebKit, en and zh; they fail without the change. factory-sub, click-scroll, old-webkit, zed-sub and add-sheet pass; go vet, go build -tags nogui (and GOOS=linux, GOOS=windows) and go test -tags nogui ./... pass. Not tried with a real Xiaomi account: the long-poll's success payload, the SSO cookie chain, whether /route accepts magpie's requests, the UTC+8 times, and whether MiMo Code picks up the config; not tried in the real Wails window.
2026-09-30 07:43:58 +08:00
toml: Codex's config.toml with a multi-line array (notify = [ … ]) is edited right, and an edit that fails leaves every file as it was (#253 kerwinliu00001: "edited TOML is invalid: line 2, column 1: incomplete number", config.toml left with model_provider gone and [model_providers.magpie] added but no model) — the top-level key edits (GetTOMLTop, SetTOMLTop, DelTOMLTop) went line by line with regexes: a new key went after the last line that looked like key = …, which for notify = [ was inside the array; key = value lines inside a multi-line string were read as keys and a line starting with [ as a table; replacing or deleting a multi-line value touched only its first line. They now locate the top-level keys with the TOML parser, as the table edits already did, each with its value's whole range however many lines it takes (parseTOMLFile, the unnamed root table beside the tables); SetTOMLTop finds all its keys in the file as read and writes once. A file the parser refuses is still read line by line, so a broken config keeps showing its model, and writes to it are refused untouched. And a pick is several writes (Codex: drop model_provider, add magpie's provider table, set openai_base_url and model), each checked, so one failing left the earlier ones on disk: edit.Atomically(fn, paths…) puts each file back to its bytes and mode, or removes one it created, when fn fails, and every field set and Sync of Codex (config.toml and magpie-models.json, so Codex in WSL too), Kimi and Grok goes through it, as do Codex's MCP server writes (putCodex, delCodex). Tested in temp dirs and a temp HOME: the reporter's file read, set and deleted directly and through Codex's model field (signed in, with model_provider and model above the array, with magpie as the provider), the array's text kept and the TOML valid, and back to a Codex model; nested arrays, trailing commas, comments in arrays, [x] and a = b in strings, an inline table in an array; """ and ''' strings holding model = … and [table] lines not read as keys, new keys after their closing quotes, one deleted whole; a forced failure of Codex's last write leaving config.toml byte-identical; TestAtomically. They fail on main (the Codex ones with the reporter's error). go vet ./internal/..., go build -tags nogui (and GOOS=linux, GOOS=windows) and go test -tags nogui ./... pass. Not tried against a real ~/.codex, in WSL or in the GUI; multi-line inline tables (TOML 1.1) are refused by the parser and so left untouched with an error.
2026-09-30 09:31:59 +08:00
toml: Codex's config.toml with a multi-line array (notify = [ … ]) is edited right, and an edit that fails leaves every file as it was (#253 kerwinliu00001: "edited TOML is invalid: line 2, column 1: incomplete number", config.toml left with model_provider gone and [model_providers.magpie] added but no model) — the top-level key edits (GetTOMLTop, SetTOMLTop, DelTOMLTop) went line by line with regexes: a new key went after the last line that looked like key = …, which for notify = [ was inside the array; key = value lines inside a multi-line string were read as keys and a line starting with [ as a table; replacing or deleting a multi-line value touched only its first line. They now locate the top-level keys with the TOML parser, as the table edits already did, each with its value's whole range however many lines it takes (parseTOMLFile, the unnamed root table beside the tables); SetTOMLTop finds all its keys in the file as read and writes once. A file the parser refuses is still read line by line, so a broken config keeps showing its model, and writes to it are refused untouched. And a pick is several writes (Codex: drop model_provider, add magpie's provider table, set openai_base_url and model), each checked, so one failing left the earlier ones on disk: edit.Atomically(fn, paths…) puts each file back to its bytes and mode, or removes one it created, when fn fails, and every field set and Sync of Codex (config.toml and magpie-models.json, so Codex in WSL too), Kimi and Grok goes through it, as do Codex's MCP server writes (putCodex, delCodex). Tested in temp dirs and a temp HOME: the reporter's file read, set and deleted directly and through Codex's model field (signed in, with model_provider and model above the array, with magpie as the provider), the array's text kept and the TOML valid, and back to a Codex model; nested arrays, trailing commas, comments in arrays, [x] and a = b in strings, an inline table in an array; """ and ''' strings holding model = … and [table] lines not read as keys, new keys after their closing quotes, one deleted whole; a forced failure of Codex's last write leaving config.toml byte-identical; TestAtomically. They fail on main (the Codex ones with the reporter's error). go vet ./internal/..., go build -tags nogui (and GOOS=linux, GOOS=windows) and go test -tags nogui ./... pass. Not tried against a real ~/.codex, in WSL or in the GUI; multi-line inline tables (TOML 1.1) are refused by the parser and so left untouched with an error.
2026-09-30 09:31:59 +08:00
provider: Antigravity's models at several levels are one model with those levels (#150: 统一 Antigravity 模型变体与客户端推理档位的映射) — Antigravity lists gemini-3.7-flash-low, -medium and -high as three models, "Gemini 3.7 Flash (Low)" …, so a client picked a level twice: once in the model, once as its reasoning effort, with no telling which won. Now ids that are one id with an effort word after it (-low, -medium, -high, -extra-low, -minimal, -xhigh) are one model, gemini-3.7-flash "Gemini 3.7 Flash", declaring only the levels there are (low,medium,high; Gemini 3.1 Pro low,high), so Codex, Pi and the rest get reasoning levels for it. The level a variant is at is what Antigravity's name says where each name says a different one (gemini-3.5-flash-extra-low is "(Low)", gemini-3.5-flash-low "(Medium)"), else its id's word; a model only named like another (gemini-3-flash-agent, "Gemini 3.5 Flash (High)"; gemini-pro-agent) or with no sibling (gpt-oss-120b-medium) stays as listed. A request asks for the variant the effort picks, an effort it lacks at the nearest level (xhigh is high; medium on low/high is high); with none asked, the id without a level if Antigravity has one, else high — what Gemini 3 does unasked — else the highest. A variant's request carries no thinkingLevel, so the id and the level can't disagree. An old variant id still works: as named with no effort, and moved to the effort asked for. Saved picks and agents set to a variant (antigravity/gemini-3.7-flash-high) are moved to the family, at that level where the agent's own is unset, as Cursor's are. For Gemini 3 models without variants, medium now goes as medium to Flash (which takes minimal/low/medium/high) and still as high to Pro (low/high). Tested against the list an account's fetchAvailableModels gave (sandbox cache)
2026-09-28 20:10:00 +08:00
models: a model's own name given by the user keeps its provider after it too (#203 uclort: 如果在供应商模型页自定义模型名称,在 Codex 中就不会显示供应商了) — Entry.Label() showed "name · provider" only for a model's own (vendor's or models.dev's) name; a name the user gave it (provider's model page → Names & levels) dropped the provider entirely, so a renamed model in Codex's picker (and Pi's, ZCode's, OMP's, magpie's own gateway list — everything magpieModels/Label feeds) could no longer be told apart by vendor at a glance. Label() now appends " · provider" (or "routing group") to a custom name too, the same as it does for the vendor's own, and only leaves it off when the name already contains the provider's (case-insensitively), so a name like "Sol on A" isn't doubled to "Sol on A · A". No settings toggle added: the fix makes the provider suffix unconditional (what the issue actually asked for), so a preference to hide/show it would only reintroduce the gap; there's also no existing "model list" preferences surface to hang it on cheaply — Names & levels only holds the name and reasoning-level pickers. Tested with TestModelName (internal/provider/modelprefs_test.go, sandbox HOME): a custom name now reads "My Sol · A" in Label() and in CodexListed(), and "Sol on A" (already containing the provider) is left alone; and TestModelNameAndLevelsReachAgentFiles (internal/agent/modelprefs_test.go, sandbox HOME) checks the generated Codex model_catalog_json and Pi models.json entries carry "GLM · Relay" for a custom name and don't double it to "GLM via Relay · Relay" when the name already says Relay. go build -tags nogui (and GOOS=linux, GOOS=windows), go test -tags nogui -count=1 ./... pass except TestUserPath and TestClaudeAccountNamedAsItsLogin, which are load-flaky (pass alone, unrelated to this change) same as the pre-existing rtk.go gofmt / TestConfiguredModelsAdvertiseImageInput flakiness
2026-09-29 18:56:32 +08:00
pi: a model's thinkingLevelMap in models.json names every one of Pi's levels, the model's own mapped to its effort and the rest null, so Pi's /thinking offers only what the vendor takes (#243) — magpie wrote only xhigh and max (glm-5.3-flash at low/high/max got {"max":"max"}), and Pi offers every level a map leaves out but xhigh and max, so minimal and medium were offered and sent efforts the vendor turned away. Now glm-5.3-flash gets {"off":null,"minimal":null,"low":"low","medium":null,"high":"high","xhigh":null,"max":"max"}. Pi's off (read from pi-ai 0.99's getSupportedThinkingLevels and its openai-completions, openai-responses and anthropic-messages streams) maps to none for a model that takes none, so off really turns reasoning off; a model without none has off null, since Pi's off asks a Responses model for effort none and leaves a Chat model on the vendor's default; on Anthropic's Messages API off is left out, as before, since there Pi's off sends thinking disabled, which Claude takes, and Claude keeps its xhigh and max. A model whose levels magpie doesn't know still gets no map and reasoning false. Tested with TestPiThinkingLevels (full maps with and without none, Anthropic keeping off, none for unknown) and TestOpenCodeNoResellerLevelsForSwitchModel, which now checks the full map written for a relay's low/high/max model and no map for an unknown one (fails on main with map[max:max]). go vet ./internal/agent, go build -tags nogui (and GOOS=linux), GOOS=windows go build and go test -tags nogui ./... pass (TestUserPath, TestClaudeSignedOut, TestClaudeAccountNamedAsItsLogin, TestClaudeIdentityServedWhileAsked, TestCodexVersion and TestAddSecondOfPreset failed in the full run under load and pass alone). Not tried in a real Pi session.
2026-09-30 02:48:10 +08:00
pi: a model's thinkingLevelMap in models.json names every one of Pi's levels, the model's own mapped to its effort and the rest null, so Pi's /thinking offers only what the vendor takes (#243) — magpie wrote only xhigh and max (glm-5.3-flash at low/high/max got {"max":"max"}), and Pi offers every level a map leaves out but xhigh and max, so minimal and medium were offered and sent efforts the vendor turned away. Now glm-5.3-flash gets {"off":null,"minimal":null,"low":"low","medium":null,"high":"high","xhigh":null,"max":"max"}. Pi's off (read from pi-ai 0.99's getSupportedThinkingLevels and its openai-completions, openai-responses and anthropic-messages streams) maps to none for a model that takes none, so off really turns reasoning off; a model without none has off null, since Pi's off asks a Responses model for effort none and leaves a Chat model on the vendor's default; on Anthropic's Messages API off is left out, as before, since there Pi's off sends thinking disabled, which Claude takes, and Claude keeps its xhigh and max. A model whose levels magpie doesn't know still gets no map and reasoning false. Tested with TestPiThinkingLevels (full maps with and without none, Anthropic keeping off, none for unknown) and TestOpenCodeNoResellerLevelsForSwitchModel, which now checks the full map written for a relay's low/high/max model and no map for an unknown one (fails on main with map[max:max]). go vet ./internal/agent, go build -tags nogui (and GOOS=linux), GOOS=windows go build and go test -tags nogui ./... pass (TestUserPath, TestClaudeSignedOut, TestClaudeAccountNamedAsItsLogin, TestClaudeIdentityServedWhileAsked, TestCodexVersion and TestAddSecondOfPreset failed in the full run under load and pass alone). Not tried in a real Pi session.
2026-09-30 02:48:10 +08:00
providers: a provider of the user's saved on a subscription's id is moved off it, so the subscription is listed once signed in (LingYi_Stu on X: workbuddy 国内版本无法使用呢 — "凌一 已登录,但 magpie 无法列出它,请反馈此问题", magpie 0.1.384, WorkBuddy 5.6.2 signed in, the international build listed with 3 accounts beside it) — before WorkBuddy's plan was a subscription (v0.1.261), a provider added as "WorkBuddy" was saved with the id workbuddy, and All() skips an account whose id a saved provider has: the sign-in said done, logins.json kept the account, and the Providers page listed only the user's own provider, the card still saying Free · Pro. Now start-up (agent.MoveOffAccountIDs, beside RenameLegacy) moves each provider with a base URL saved on a subscription's id to the next free one (workbuddy-2) the way a rename does: routing groups, fallbacks and the agents on its models follow; the old id is not kept as one it Was, being the subscription's. Rename allows it for such a provider alone, a subscription still keeps its id, and Find takes an id before a name, so workbuddy is the subscription and not a provider called WorkBuddy. Tested with a new TestWorkBuddyUnderUsersOwnID (the sign-in done and unlisted, then both listed after the move, a fallback moved, the subscription refused a rename), and end to end in a sandbox HOME with a WorkBuddy auth file and providers.json: main lists the custom WorkBuddy alone; with the change magpie providers lists workbuddy-2 (its fallback moved) and WorkBuddy signed in as 凌一. go test -tags nogui ./... (TestUserPath failed once under load, passes on its own), go build for linux and windows
2026-09-29 18:17:14 +08:00
agents: a provider switched off or removed, or its last account signed out, moves the agents on its models to others (#200: 希望关闭某个llm provider的时候,更改agent选择的model到可用的model … 关闭这个provider之后,第一次使用会报错,因为copilot/ 开头的模型不存在) — switching Copilot off took copilot/… out of the catalog and the agents' model lists, but an agent picked on one kept it in its config and its next request was refused as switched off. Now every field on one of magpie's models (the model, Claude Code's opus/sonnet/haiku/fable tiers, any other) whose model the change stopped serving is moved: to the same model from a provider still on, the same id first, else one spelt another way (claude-sonnet-4.5 and claude-sonnet-4-5-20250929), else back to the agent's own default (a Claude Code tier: following the main model). It is done around switching off, removing, a key removed or off, and a sign-in signed out or off, in the window, the tray panel, the TUI and the CLI; the page's status line, the TUI and the CLI say which agent went to what. A session started before the switch still asks for the old model: the gateway now takes a switched-off provider's model as unserved, so Claude Code's stand-in (its model as set now) answers it rather than a refusal. Tested in a sandbox HOME: Claude Code on the main model and three tiers plus Hermes, switched off and removed (same id, other spelling, default, a tier following), switched on again moving nothing; /api/provider/off returns the moves; the gateway serves a switched-off model through the stand-in (fails without the change) and still refuses it to an agent left on it
2026-09-29 17:56:43 +08:00
agents: a provider switched off or removed, or its last account signed out, moves the agents on its models to others (#200: 希望关闭某个llm provider的时候,更改agent选择的model到可用的model … 关闭这个provider之后,第一次使用会报错,因为copilot/ 开头的模型不存在) — switching Copilot off took copilot/… out of the catalog and the agents' model lists, but an agent picked on one kept it in its config and its next request was refused as switched off. Now every field on one of magpie's models (the model, Claude Code's opus/sonnet/haiku/fable tiers, any other) whose model the change stopped serving is moved: to the same model from a provider still on, the same id first, else one spelt another way (claude-sonnet-4.5 and claude-sonnet-4-5-20250929), else back to the agent's own default (a Claude Code tier: following the main model). It is done around switching off, removing, a key removed or off, and a sign-in signed out or off, in the window, the tray panel, the TUI and the CLI; the page's status line, the TUI and the CLI say which agent went to what. A session started before the switch still asks for the old model: the gateway now takes a switched-off provider's model as unserved, so Claude Code's stand-in (its model as set now) answers it rather than a refusal. Tested in a sandbox HOME: Claude Code on the main model and three tiers plus Hermes, switched off and removed (same id, other spelling, default, a tier following), switched on again moving nothing; /api/provider/off returns the moves; the gateway serves a switched-off model through the stand-in (fails without the change) and still refuses it to an agent left on it
2026-09-29 17:56:43 +08:00
mimo: a Xiaomi MiMo account (the one MiMo's app signs in to) is signed in inside magpie and its models served through the gateway to any agent, and MiMo Code is an agent magpie configures (#249 thedavidweng) — magpie had neither. The subscription (id mimo-app, shown as Xiaomi MiMo) signs in with Xiaomi's long-poll login (account.xiaomi.com/longPolling/loginUrl, sid mimosgp) and keeps the MiMo server's cookie session (serviceToken, userId) in logins.json at 0600; the passToken is replayed to renew the session after a day or on a 401. Requests go to POST <base>/route/chat/completions on mimo-server-{sgp,ru,in}.xiaomimimo.com/api with X-Mimo-Source mimocode-cli-free and X-Client-Version; mimo-auto is sent as mimo-pro; the models are mimo-pro and mimo-flash. Usage from /user/usage and /user/xiaomi/subscription/self. Several accounts, switched, on/off and forgotten as the other subscriptions. The add list has a Xiaomi MiMo tile (Free · Starter · Plus · Pro · Ultra) that says Xiaomi may treat this as third-party use before anything is posted; en and zh. magpie accounts takes mimo. MiMo Code (agent mimocode, OpenCode-like) is configured in its config dir (MIMOCODE_HOME/config when set), reading mimocode.jsonc, mimocode.json or config.json, whichever is there. Tested with internal/provider/mimo_test.go against fake Xiaomi SSO and MiMo servers in a temp HOME, internal/agent tests for MiMo Code's config, and internal/gui/tests/mimo-sub.test.cjs, Chromium and WebKit, en and zh; they fail without the change. factory-sub, click-scroll, old-webkit, zed-sub and add-sheet pass; go vet, go build -tags nogui (and GOOS=linux, GOOS=windows) and go test -tags nogui ./... pass. Not tried with a real Xiaomi account: the long-poll's success payload, the SSO cookie chain, whether /route accepts magpie's requests, the UTC+8 times, and whether MiMo Code picks up the config; not tried in the real Wails window.
2026-09-30 07:43:58 +08:00
agents: each agent's CLI shows its version and updates in one click (#202 JoeyMa-zh: Agents 页面增加"更新 CLI"功能 — 检查各 agent CLI 的版本并一键更新) — the Agents page now puts each CLI's installed version after its name (read with <cli> --version, 10s timeout, no stdin) and, when a newer one is out and magpie can tell how it was installed, a small "Update to x.y.z" pill. How it was installed comes from the resolved binary: the vendor's own installer first (Claude Code's ~/.local/share/claude/versions → claude update, Codex standalone → codex update, ~/.opencode/bin → opencode upgrade), then a global node_modules (bun, pnpm, else npm, run as npm install -g --prefix <prefix> pkg@latest with that prefix's npm, and only where the prefix is writable), then a Homebrew formula or cask allowlisted per agent (brew upgrade [--cask]); anything else shows the version alone, with no button. The latest comes from the npm registry or brew info --json=v2 (no auto-update), cached 6h (15min after a failure); installed versions are cached by the binary's path, size and mtime. GET /api/agents/cli waits at most 3s and says pending, and the page asks again; POST /api/agents/cli/{id} runs the update (one at a time per agent, 10min cap), reads the version again and says so if it did not change, or gives the command's last lines when it fails. The pill goes busy in place and the page never scrolls; not in the tray panel. zh strings added. Tested with a new internal/agent/cliupdate_test.go (version order and parsing; npm/bun/pnpm/brew formula and cask/native installs, a read-only prefix, a local node_modules and unknown binaries in a fake PATH; an update against a fake registry and updater) and a new internal/gui/tests/cli-update.test.cjs in Chromium and WebKit (versions, pills only where magpie can update, row heights, an update with the view scrolled, a failing one, zh). No real update was run. go build (nogui, GOOS=windows, GOOS=linux), go test -tags nogui ./... and the GUI tests pass
2026-09-29 17:56:43 +08:00
agents: each agent's CLI shows its version and updates in one click (#202 JoeyMa-zh: Agents 页面增加"更新 CLI"功能 — 检查各 agent CLI 的版本并一键更新) — the Agents page now puts each CLI's installed version after its name (read with <cli> --version, 10s timeout, no stdin) and, when a newer one is out and magpie can tell how it was installed, a small "Update to x.y.z" pill. How it was installed comes from the resolved binary: the vendor's own installer first (Claude Code's ~/.local/share/claude/versions → claude update, Codex standalone → codex update, ~/.opencode/bin → opencode upgrade), then a global node_modules (bun, pnpm, else npm, run as npm install -g --prefix <prefix> pkg@latest with that prefix's npm, and only where the prefix is writable), then a Homebrew formula or cask allowlisted per agent (brew upgrade [--cask]); anything else shows the version alone, with no button. The latest comes from the npm registry or brew info --json=v2 (no auto-update), cached 6h (15min after a failure); installed versions are cached by the binary's path, size and mtime. GET /api/agents/cli waits at most 3s and says pending, and the page asks again; POST /api/agents/cli/{id} runs the update (one at a time per agent, 10min cap), reads the version again and says so if it did not change, or gives the command's last lines when it fails. The pill goes busy in place and the page never scrolls; not in the tray panel. zh strings added. Tested with a new internal/agent/cliupdate_test.go (version order and parsing; npm/bun/pnpm/brew formula and cask/native installs, a read-only prefix, a local node_modules and unknown binaries in a fake PATH; an update against a fake registry and updater) and a new internal/gui/tests/cli-update.test.cjs in Chromium and WebKit (versions, pills only where magpie can update, row heights, an update with the view scrolled, a failing one, zh). No real update was run. go build (nogui, GOOS=windows, GOOS=linux), go test -tags nogui ./... and the GUI tests pass
2026-09-29 17:56:43 +08:00
wsl: Claude Code installed in a WSL distro is found and wired as its own agent, Claude Code · WSL <distro> (#254 Xws-s: magpie finds Codex and Pi in WSL but not Claude Code) — the WSL registry gains Claude Code: the probe looks for ~/.claude and command -v claude, and claude@wsl:<distro> edits the distro's ~/.claude/settings.json through \\wsl.localhost, with the gateway as the distro reaches it (127.0.0.1 when mirrored, Windows' address under NAT). claude(home) is now claudeIn(here(home)) like Codex and Pi: the base URL written, the routed test and Check use the place's gateway; what Claude Code had before magpie (model, base URL, token) is kept under claude@wsl:<distro> so it never mixes with Windows'; Check reads the distro's own /etc/claude-code/managed-settings.json. A stopped distro shows Claude Code's and magpie's models without being opened. Claude Code's model stand-in (a Claude name magpie doesn't serve) also looks at the routed distros seen running, never starting one. Sign-in, sessions, prompt history and Last used stay Windows-only, as for the other WSL agents. Native Claude Code is as before. Tested in wsl_claude_test.go with fake distros in a sandbox HOME: probe and discovery (folder only, program only, beside Codex and Pi), a pick under NAT and mirrored writing the distro's address and otherwise the same settings.json, Check and managed settings, restoring on the way back, a stopped distro never opened, the stand-in. go vet ./internal/..., go build -tags nogui (and GOOS=linux, GOOS=windows) and go test -tags nogui ./... pass. Not tried on real Windows/WSL.
2026-09-30 10:02:51 +08:00
wsl: Pi installed in a WSL distro is found and pointed at magpie there, as Codex in WSL is (#251 X4fun) — magpie looked in each running distro only for Codex (~/.codex, command -v codex), so Pi's ~/.pi/agent there was never listed. The agents magpie looks for in a distro are now a small registry (wslKinds: id, name, the folder under $HOME, the program, the agent at a place, the restart advice, a stopped distro's options), from which the probe script is built and the distro's agents listed; Codex and Pi are in it. Pi · WSL <distro> (pi@wsl:<distro>) reads and writes ~/.pi/agent/settings.json and models.json through \\wsl.localhost\<distro>, with the same models.json block as Pi on this machine (thinkingLevelMap, reasoning, compat, contextWindow, maxTokens) but its baseUrl, and the Anthropic models' /messages baseUrl, at the gateway as the distro reaches it: 127.0.0.1 when mirrored, Windows' address under NAT, with the same NAT notice as Codex; its config check and refresh keep that address. A stopped distro shows what magpie last saw (wsl.json keeps Pi's fields as pi.model and pi.effort, Codex's bare keys unchanged so existing files still read), reads nothing, and a pick starts it first. Pi on this machine is unchanged (pi(home) is piIn(here(home))). Tested with a new internal/agent/wsl_pi_test.go against fake distros in temp dirs: the probe script and its dir:.pi / bin:pi lines; a distro with Pi only by folder, only by program, with both agents and with neither (ids, order, name, icon, current model and thinking level, the two agents' values kept apart); a pick under NAT and mirrored writing settings.json and models.json equal to Pi's native block but for the address, the check passing and a refresh keeping the distro's address, the real ~/.pi never made; a stopped distro listed from wsl.json with nothing opened, a pick starting then writing. All four fail on main; the Codex WSL tests pass unchanged. go vet ./internal/... (and GOOS=windows for internal/agent), go build -tags nogui (and GOOS=linux, GOOS=windows) and go test -tags nogui ./... pass (TestUserPath and three Claude status tests timed out under load and pass alone). Not tried on real Windows/WSL nor with a real Pi in a distro.
2026-09-30 09:29:28 +08:00
wsl: Claude Code installed in a WSL distro is found and wired as its own agent, Claude Code · WSL <distro> (#254 Xws-s: magpie finds Codex and Pi in WSL but not Claude Code) — the WSL registry gains Claude Code: the probe looks for ~/.claude and command -v claude, and claude@wsl:<distro> edits the distro's ~/.claude/settings.json through \\wsl.localhost, with the gateway as the distro reaches it (127.0.0.1 when mirrored, Windows' address under NAT). claude(home) is now claudeIn(here(home)) like Codex and Pi: the base URL written, the routed test and Check use the place's gateway; what Claude Code had before magpie (model, base URL, token) is kept under claude@wsl:<distro> so it never mixes with Windows'; Check reads the distro's own /etc/claude-code/managed-settings.json. A stopped distro shows Claude Code's and magpie's models without being opened. Claude Code's model stand-in (a Claude name magpie doesn't serve) also looks at the routed distros seen running, never starting one. Sign-in, sessions, prompt history and Last used stay Windows-only, as for the other WSL agents. Native Claude Code is as before. Tested in wsl_claude_test.go with fake distros in a sandbox HOME: probe and discovery (folder only, program only, beside Codex and Pi), a pick under NAT and mirrored writing the distro's address and otherwise the same settings.json, Check and managed settings, restoring on the way back, a stopped distro never opened, the stand-in. go vet ./internal/..., go build -tags nogui (and GOOS=linux, GOOS=windows) and go test -tags nogui ./... pass. Not tried on real Windows/WSL.
2026-09-30 10:02:51 +08:00