Files
library: a remote MCP server signed in once in magpie is used by every agent without signing in again (#615) — a streamable-HTTP server behind OAuth (Neon, Notion, Supabase) had each agent run its own sign-in. The server's editor now has Sign in: magpie finds the authorization server from the server's 401 (RFC 9728 protected-resource metadata, RFC 8414/OIDC metadata, else the 2025-03 /authorize, /token, /register at its origin), registers itself (RFC 7591, a 127.0.0.1 redirect), opens the browser with PKCE S256 and the resource, and keeps the tokens in mcp-signins.json beside its other account secrets (0600, never the keychain). While signed in, the agents are given the gateway's /mcp/<name> for that server, without the Authorization the library has for it; the gateway passes POST (JSON or event stream), the GET stream and DELETE on with the Mcp-Session-Id and magpie's token, renews it 2 minutes before it runs out and once on a 401, one renewal per server at a time, and a sign-in the server won't renew is answered 502 "sign in to it again" (never a 401 the agent would act on). Signing out, a new URL or an SSE server gives the agents the server's own address as before; a rename keeps the sign-in and removing the server drops it. /mcp/ is local-only or LAN-shared with the key, and refuses a request with an Origin or a Host not naming this computer, so a web page can't borrow the sign-in through the browser (form posts, DNS rebinding), and isn't counted as busy or key-limited. WSL agents are given their distro's gateway address. The row says Signed in or Sign-in ran out; zh strings added. Tests: TestSignIn, TestSignInWithoutMetadata, TestSignInPage, TestNothingToSignIn, TestTokenRenewsBeforeItRunsOut (8 calls, 1 renewal), TestRenewRefused, TestRenewRefusedForGood, TestMCPProxy, TestMCPProxyStaysHere, TestMCPProxyNotForWebPages (fails without the Origin/Host check), TestSignedInServerGoesThroughMagpie against a local fake OAuth+MCP server; mcp-signin.test.cjs (webkit, en and zh). Not tried against a real Neon/Notion/Supabase sign-in. go vet ./internal/..., go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui -count=1 ./... under a temp HOME pass.
agents: Empryo (#612) — picking a magpie model in Empryo adds a "magpie" provider at the gateway to ~/.empryo/config.json's providers (baseURL + modelsAPI, no envVar) and sets defaultModel to magpie/<catalog id>; the user's defaultModel is stashed and put back on leaving, magpie's provider goes again, and a file magpie made is removed when empty. The user's own providers and keys are left alone. The gateway now answers GET /v1 (and /v1/) with its info, as Empryo lists a keyless provider only when its baseURL answers. Also on WSL. Built from Empryo's docs and its public v2 code, not run against the Empryo binary. TestEmpryo, TestBaseURLAnswers (GET /v1 was 404 before).
…
…
gui: Claude Code's model picker shows each model once (#496) — the rows magpie serves on the very Claude account Claude Code is signed in to fold into one, and models.dev's alias and dated id of one model are one row
agent: Codex's subagents can start at an effort of their own, and Claude Code's subagents can have a model of their own (#468, #469) — Codex reads [agents] default_subagent_reasoning_effort, the effort a spawned subagent starts at when the spawn names none (openai/codex core/src/agent/child_config.rs, config_toml.rs; in codex 0.159.2), and magpie never wrote it. Codex gets a subagent effort field: a square beside its subagents' one that opens the effort slider, unset said to mean the session's effort, or the subagent model's own default. Codex refuses a spawn at a level the subagents' model lacks, so only one it has is taken, and one a new subagent or main model lacks is dropped. Claude Code takes the Agent call's model, then the agent's frontmatter, then CLAUDE_CODE_SUBAGENT_MODEL, then the session's (2.1.287); magpie wrote that env only by itself, the main model while every tier followed it. It gets a subagents field, offered once routed through magpie, whose model outlives a new main model and a tier of its own; unset, the env is written as before. Claude Code has no setting for its subagents' effort beyond each agent's own frontmatter, and omp's task role already takes a thinking level, so neither changes. TestCodexSubagentEffort, TestClaudeSubagentModel and subagent-effort.test.cjs fail without the change. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass; subagent-effort, omp-roles, claude-effort, panel-effort, agent-layout, model-pick, click-scroll, old-webkit pass.
claude: each of Claude Code's tiers (opus, sonnet, haiku, fable) and its subagents can run at a reasoning effort of their own through magpie, picked from the tiers' square under the tier's model (#536: 希望 Claude Code 的 opus/sonnet/haiku/subagent 各自可设置推理强度) — until now one effort was set for Claude Code (modelSettings / effortLevel / CLAUDE_CODE_EFFORT_LEVEL) and every tier ran at what Claude Code asked, so a haiku tier used for small side calls ran at the main model's high. Claude Code takes a tier only as a model id (ANTHROPIC_DEFAULT_<TIER>_MODEL, CLAUDE_CODE_SUBAGENT_MODEL) and has no per-tier effort setting, so the effort travels in that id, the way a routing group's member is fixed at one (#189): a tier at an effort is "<model>:<level>", before any [1m] mark ("v/big:high[1m]"), and the gateway (askedAt) takes the level off before resolving, then asks the model for it at the model's nearest level (withFixedEffort, fitLevel) whatever Claude Code asked, the trace's Try.Fixed saying so; a group member's own effort still wins, and a model whose own id ends in ":<level>" (MemberEffort's isModel) is taken whole. Token counting strips it too.
fix(agent/codex): recognize CC Switch official OpenAI mirror for grouping and failover (#504) (#579)
agent: Codex's subagents can start at an effort of their own, and Claude Code's subagents can have a model of their own (#468, #469) — Codex reads [agents] default_subagent_reasoning_effort, the effort a spawned subagent starts at when the spawn names none (openai/codex core/src/agent/child_config.rs, config_toml.rs; in codex 0.159.2), and magpie never wrote it. Codex gets a subagent effort field: a square beside its subagents' one that opens the effort slider, unset said to mean the session's effort, or the subagent model's own default. Codex refuses a spawn at a level the subagents' model lacks, so only one it has is taken, and one a new subagent or main model lacks is dropped. Claude Code takes the Agent call's model, then the agent's frontmatter, then CLAUDE_CODE_SUBAGENT_MODEL, then the session's (2.1.287); magpie wrote that env only by itself, the main model while every tier followed it. It gets a subagents field, offered once routed through magpie, whose model outlives a new main model and a tier of its own; unset, the env is written as before. Claude Code has no setting for its subagents' effort beyond each agent's own frontmatter, and omp's task role already takes a thinking level, so neither changes. TestCodexSubagentEffort, TestClaudeSubagentModel and subagent-effort.test.cjs fail without the change. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass; subagent-effort, omp-roles, claude-effort, panel-effort, agent-layout, model-pick, click-scroll, old-webkit pass.
codex: when the ChatGPT account runs out of its allowance after a magpie model was picked, magpie becomes Codex's provider, and goes back beside the sign-in when the allowance is back (#540: once the Codex 5-hour/weekly quota is used up, a third-party model through magpie sends nothing in new or old sessions; Dazzle-sys: the session was on the magpie model all along, the quota went elsewhere) — the Codex desktop app refuses to send for a ChatGPT account that is out of its allowance before any request is made, whichever model the turn is on, so a magpie model beside the sign-in (openai_base_url, ChatGPT mode) is blocked with it; Codex's open-source core and TUI only report a usage limit after a response, the block is the app's, keyed on the account.
bd5bf422 made magpie the provider (model_provider = "magpie", model_catalog_json; the app-server's getAuthStatus then answers requires_openai_auth false, the app's API state) only when a model was picked while the account was already out, so an allowance running out later left Codex as it was. KeepOnAnAccountWithRoom now also looks, every 5 minutes, whether the active Codex account is out (CodexUsedUp, now read through LoginUsage's cache, fetched at most once a minute, as the agents' sync asks it on every catalog sync) and tells the agents' sync when that changes (noteCodexUsedUp → catalog.Touched); Codex's Sync then sets the magpie model again, which wires it as the provider while the account is out, marked codex.out in the stash, and puts it back beside the sign-in once the allowance is back, only where it was moved for that (a provider asked for with sign-in "api" stays). Threads started on Codex's own provider and reopened may still be held by the app; the Codex app may need a restart to take the new config. agent's TestMain stubs codexUsedUp so no test asks OpenAI. TestCodexRunsOutAfterPick (ChatGPT mode, then out: provider; out again: stays; back: beside the sign-in; api stays) fails without the change; TestNoteCodexUsedUp checks only a change is told. No request was made to OpenAI. go vet ./internal/..., go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui -count=1 ./... under a temp HOME pass.
fix(agent/codex): recognize CC Switch official OpenAI mirror for grouping and failover (#504) (#579)
wsl: OpenCode, Kimi Code, omp and the other CLIs magpie sets up through a file in the home are set up in WSL distros too (#515: on Windows only Codex, Pi, OmO and Claude Code were found in a distro) — fourteen agents join wslKinds, each found by its folder under the distro's $HOME or its command on the distro's own PATH, and listed as <name> · WSL <distro> (opencode@wsl:Ubuntu…): OpenCode (~/.config/opencode, opencode), MiMo Code (~/.config/mimocode, mimo), Kimi Code (~/.kimi-code, kimi; the old kimi-cli's ~/.kimi where that is all there is, found by the command), omp (~/.omp/agent, omp), Crush (~/.config/crush/crush.json, Linux's place, not Windows' LOCALAPPDATA), Hermes Agent (~/.hermes), Grok Build (~/.grok, by its folder alone, as on this machine), Droid (~/.factory), fx (~/.fx, by its folder alone: fx is also the JSON viewer), Command Code (~/.commandcode), MiniMax Code's mcode (~/.minimax), Muse Code (~/.config/muse), Qoder and Qoder CN (~/.qoder, ~/.qoder-cn). Each is its host constructor at a place (opencodeIn, kimiIn, ompIn, crushIn, hermesIn, grokIn, droidIn, fxIn, commandCodeIn, miniMaxIn, museIn, qoderIn/qoderCNIn; the host's opencode(home, cfg) and the rest wrap them), so it writes the same keys and schema as on this machine, but the gateway as the distro reaches it (place.gw/v1: 127.0.0.1 when mirrored, Windows' address under NAT) in its provider, its Check and its Sync, and its folder at the default, as the distro's variables (OPENCODE_CONFIG_DIR, KIMI_CODE_HOME, HERMES_HOME, FACTORY_HOME_OVERRIDE, MINIMAX_DATA_DIR, QODER_CONFIG_DIR…) aren't read (place.getenv is "" there); OpenChamber, a Windows app, isn't looked at for a distro's OpenCode, and omp there, whose version isn't known, gets xhigh rather than a max an older omp refuses. Their notices are the restart advice of their own, as for Codex. A stopped distro's agent is built without looking at its files (place.cold: exists/isDir are false, so the defaults are taken), its options read none (wslOwnAsleep, kimiOwnOptions/ompOwnOptions/miniMaxOwnOptions without a file), and a pick, once the distro is started, builds the agent again warm, so it writes the file the agent reads — an opencode.jsonc already there, ~/.kimi for kimi-cli — not the default. Left out: Gemini CLI and Antigravity (a key lent through .env, a launch command), dsh (profiles and .env), Devin (no gateway; its model list from the devin command on this machine), Goose (no magpie routing; its name is pressly's goose too), Cursor and Copilot (no magpie routing), and the apps (Cline, Hanako, WorkBuddy, ZCode, Claude Desktop, OpenChamber, Alma, Cindy); and the library's skills, MCP and instructions for the new WSL agents, which wslTargetOf still gives only Claude Code, Codex, Pi and OmO. Nothing ran on Windows or in a real WSL. wsl_more_test.go, on fake distro roots under temp dirs with the host's variables pointed elsewhere: TestWSLProbeFindsMoreAgents (each folder and command in the probe, none for grok and fx, kimi-cli by its command), TestWSLMoreAgentsDiscovered (all fourteen listed, no command, UA or aliases), TestWSLMoreAgentsPick (each agent, mirrored and NAT: its file in the distro names the distro's gateway, no 127.0.0.1 under NAT, Check passes, Sync keeps it, the notice; this machine's home and the folders its variables name untouched), TestWSLMoreAgentsStopped (nothing asked nor opened, the last value shown, options, a pick starts the distro and writes there) fail without the change; TestWSLStoppedPickFindsItsFiles fails without the warm rebuild (the pick wrote a second opencode.json beside the .jsonc). go vet ./internal/..., go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build and vet ./internal/..., GOOS=windows go test -c ./internal/agent/ ./internal/library/, and go test -tags nogui -count=1 ./... under a temp HOME pass (TestFXInState failed once on its TempDir cleanup race, passes on the rerun; untouched).
…
wsl: OpenCode, Kimi Code, omp and the other CLIs magpie sets up through a file in the home are set up in WSL distros too (#515: on Windows only Codex, Pi, OmO and Claude Code were found in a distro) — fourteen agents join wslKinds, each found by its folder under the distro's $HOME or its command on the distro's own PATH, and listed as <name> · WSL <distro> (opencode@wsl:Ubuntu…): OpenCode (~/.config/opencode, opencode), MiMo Code (~/.config/mimocode, mimo), Kimi Code (~/.kimi-code, kimi; the old kimi-cli's ~/.kimi where that is all there is, found by the command), omp (~/.omp/agent, omp), Crush (~/.config/crush/crush.json, Linux's place, not Windows' LOCALAPPDATA), Hermes Agent (~/.hermes), Grok Build (~/.grok, by its folder alone, as on this machine), Droid (~/.factory), fx (~/.fx, by its folder alone: fx is also the JSON viewer), Command Code (~/.commandcode), MiniMax Code's mcode (~/.minimax), Muse Code (~/.config/muse), Qoder and Qoder CN (~/.qoder, ~/.qoder-cn). Each is its host constructor at a place (opencodeIn, kimiIn, ompIn, crushIn, hermesIn, grokIn, droidIn, fxIn, commandCodeIn, miniMaxIn, museIn, qoderIn/qoderCNIn; the host's opencode(home, cfg) and the rest wrap them), so it writes the same keys and schema as on this machine, but the gateway as the distro reaches it (place.gw/v1: 127.0.0.1 when mirrored, Windows' address under NAT) in its provider, its Check and its Sync, and its folder at the default, as the distro's variables (OPENCODE_CONFIG_DIR, KIMI_CODE_HOME, HERMES_HOME, FACTORY_HOME_OVERRIDE, MINIMAX_DATA_DIR, QODER_CONFIG_DIR…) aren't read (place.getenv is "" there); OpenChamber, a Windows app, isn't looked at for a distro's OpenCode, and omp there, whose version isn't known, gets xhigh rather than a max an older omp refuses. Their notices are the restart advice of their own, as for Codex. A stopped distro's agent is built without looking at its files (place.cold: exists/isDir are false, so the defaults are taken), its options read none (wslOwnAsleep, kimiOwnOptions/ompOwnOptions/miniMaxOwnOptions without a file), and a pick, once the distro is started, builds the agent again warm, so it writes the file the agent reads — an opencode.jsonc already there, ~/.kimi for kimi-cli — not the default. Left out: Gemini CLI and Antigravity (a key lent through .env, a launch command), dsh (profiles and .env), Devin (no gateway; its model list from the devin command on this machine), Goose (no magpie routing; its name is pressly's goose too), Cursor and Copilot (no magpie routing), and the apps (Cline, Hanako, WorkBuddy, ZCode, Claude Desktop, OpenChamber, Alma, Cindy); and the library's skills, MCP and instructions for the new WSL agents, which wslTargetOf still gives only Claude Code, Codex, Pi and OmO. Nothing ran on Windows or in a real WSL. wsl_more_test.go, on fake distro roots under temp dirs with the host's variables pointed elsewhere: TestWSLProbeFindsMoreAgents (each folder and command in the probe, none for grok and fx, kimi-cli by its command), TestWSLMoreAgentsDiscovered (all fourteen listed, no command, UA or aliases), TestWSLMoreAgentsPick (each agent, mirrored and NAT: its file in the distro names the distro's gateway, no 127.0.0.1 under NAT, Check passes, Sync keeps it, the notice; this machine's home and the folders its variables name untouched), TestWSLMoreAgentsStopped (nothing asked nor opened, the last value shown, options, a pick starts the distro and writes there) fail without the change; TestWSLStoppedPickFindsItsFiles fails without the warm rebuild (the pick wrote a second opencode.json beside the .jsonc). go vet ./internal/..., go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build and vet ./internal/..., GOOS=windows go test -c ./internal/agent/ ./internal/library/, and go test -tags nogui -count=1 ./... under a temp HOME pass (TestFXInState failed once on its TempDir cleanup race, passes on the rerun; untouched).
agents: dsh's patch list written in flow style is edited like a block one, and a file magpie can't edit is named as itself (#445) — a profile's cordis.patch.yml whose top-level array was written as [ { id: some-plugin, disabled: false } ] (valid YAML, and what dsh's own writers keep the template's [] as once they add a row) failed every model switch with "config.yaml is not a list of entries magpie can edit": the patch-list reader took only '- ' lines, and the error named config.yaml whatever the file; since web's list goes first, one such profile stopped all of them. New edit.BlockList reads a file whose one document is a flow sequence as the same list in block style (nested mappings and lists in block style, scalars quoted and tagged as they were, the lines before it and the comments after it kept); dsh's model/effort wiring and its MCP servers (internal/library) read and write such a file through it, and its errors name the real path. dshProfiles puts web first by partition instead of a sort whose less wasn't a strict weak order. TestDshFlowPatchList (agent, library) fails without the change; TestBlockList covers one-line, folded/nested/!!js, empty, CRLF and what isn't a flow list. Sandbox HOME: magpie dsh deepseek-v4-pro on main fails with the config.yaml error, with the change both profiles get the model and web keeps some-plugin. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass.
dsh: a model through magpie that lists no thinking levels offers none, rather than dsh's own four it then turned away (Dazzle-sys, #473: on LongCat-2.5-Preview, every step of DeepSeek Harness's thinking slider failed with "takes an effort of for this model, not \"low\"") — the Agents page's effort field fell back to dsh's own off, low, high and max when a gateway model's catalog entry listed no levels, while setting one checked the model's own levels, which were none, so the slider offered four stops and each was refused with an empty list. Such a model has no levels in dsh either (its route gives it no reasoningEfforts), so none is offered now and the Agents page shows no thinking control for it, as it does for Claude Code on Haiku 4.5; an effort asked for anyway (magpie dsh effort low) says the model has no thinking levels in magpie, never an empty list. dsh's own models and gateway models with levels are unchanged; no other agent's effort field offers levels its setter refuses. TestDshEffortModelWithoutLevels fails without the change. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass; panel-effort, claude-effort, click-scroll, old-webkit pass.
agents: Empryo (#612) — picking a magpie model in Empryo adds a "magpie" provider at the gateway to ~/.empryo/config.json's providers (baseURL + modelsAPI, no envVar) and sets defaultModel to magpie/<catalog id>; the user's defaultModel is stashed and put back on leaving, magpie's provider goes again, and a file magpie made is removed when empty. The user's own providers and keys are left alone. The gateway now answers GET /v1 (and /v1/) with its info, as Empryo lists a keyless provider only when its baseURL answers. Also on WSL. Built from Empryo's docs and its public v2 code, not run against the Empryo binary. TestEmpryo, TestBaseURLAnswers (GET /v1 was 404 before).
agents: Empryo (#612) — picking a magpie model in Empryo adds a "magpie" provider at the gateway to ~/.empryo/config.json's providers (baseURL + modelsAPI, no envVar) and sets defaultModel to magpie/<catalog id>; the user's defaultModel is stashed and put back on leaving, magpie's provider goes again, and a file magpie made is removed when empty. The user's own providers and keys are left alone. The gateway now answers GET /v1 (and /v1/) with its info, as Empryo lists a keyless provider only when its baseURL answers. Also on WSL. Built from Empryo's docs and its public v2 code, not run against the Empryo binary. TestEmpryo, TestBaseURLAnswers (GET /v1 was 404 before).
wsl: OpenCode, Kimi Code, omp and the other CLIs magpie sets up through a file in the home are set up in WSL distros too (#515: on Windows only Codex, Pi, OmO and Claude Code were found in a distro) — fourteen agents join wslKinds, each found by its folder under the distro's $HOME or its command on the distro's own PATH, and listed as <name> · WSL <distro> (opencode@wsl:Ubuntu…): OpenCode (~/.config/opencode, opencode), MiMo Code (~/.config/mimocode, mimo), Kimi Code (~/.kimi-code, kimi; the old kimi-cli's ~/.kimi where that is all there is, found by the command), omp (~/.omp/agent, omp), Crush (~/.config/crush/crush.json, Linux's place, not Windows' LOCALAPPDATA), Hermes Agent (~/.hermes), Grok Build (~/.grok, by its folder alone, as on this machine), Droid (~/.factory), fx (~/.fx, by its folder alone: fx is also the JSON viewer), Command Code (~/.commandcode), MiniMax Code's mcode (~/.minimax), Muse Code (~/.config/muse), Qoder and Qoder CN (~/.qoder, ~/.qoder-cn). Each is its host constructor at a place (opencodeIn, kimiIn, ompIn, crushIn, hermesIn, grokIn, droidIn, fxIn, commandCodeIn, miniMaxIn, museIn, qoderIn/qoderCNIn; the host's opencode(home, cfg) and the rest wrap them), so it writes the same keys and schema as on this machine, but the gateway as the distro reaches it (place.gw/v1: 127.0.0.1 when mirrored, Windows' address under NAT) in its provider, its Check and its Sync, and its folder at the default, as the distro's variables (OPENCODE_CONFIG_DIR, KIMI_CODE_HOME, HERMES_HOME, FACTORY_HOME_OVERRIDE, MINIMAX_DATA_DIR, QODER_CONFIG_DIR…) aren't read (place.getenv is "" there); OpenChamber, a Windows app, isn't looked at for a distro's OpenCode, and omp there, whose version isn't known, gets xhigh rather than a max an older omp refuses. Their notices are the restart advice of their own, as for Codex. A stopped distro's agent is built without looking at its files (place.cold: exists/isDir are false, so the defaults are taken), its options read none (wslOwnAsleep, kimiOwnOptions/ompOwnOptions/miniMaxOwnOptions without a file), and a pick, once the distro is started, builds the agent again warm, so it writes the file the agent reads — an opencode.jsonc already there, ~/.kimi for kimi-cli — not the default. Left out: Gemini CLI and Antigravity (a key lent through .env, a launch command), dsh (profiles and .env), Devin (no gateway; its model list from the devin command on this machine), Goose (no magpie routing; its name is pressly's goose too), Cursor and Copilot (no magpie routing), and the apps (Cline, Hanako, WorkBuddy, ZCode, Claude Desktop, OpenChamber, Alma, Cindy); and the library's skills, MCP and instructions for the new WSL agents, which wslTargetOf still gives only Claude Code, Codex, Pi and OmO. Nothing ran on Windows or in a real WSL. wsl_more_test.go, on fake distro roots under temp dirs with the host's variables pointed elsewhere: TestWSLProbeFindsMoreAgents (each folder and command in the probe, none for grok and fx, kimi-cli by its command), TestWSLMoreAgentsDiscovered (all fourteen listed, no command, UA or aliases), TestWSLMoreAgentsPick (each agent, mirrored and NAT: its file in the distro names the distro's gateway, no 127.0.0.1 under NAT, Check passes, Sync keeps it, the notice; this machine's home and the folders its variables name untouched), TestWSLMoreAgentsStopped (nothing asked nor opened, the last value shown, options, a pick starts the distro and writes there) fail without the change; TestWSLStoppedPickFindsItsFiles fails without the warm rebuild (the pick wrote a second opencode.json beside the .jsonc). go vet ./internal/..., go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build and vet ./internal/..., GOOS=windows go test -c ./internal/agent/ ./internal/library/, and go test -tags nogui -count=1 ./... under a temp HOME pass (TestFXInState failed once on its TempDir cleanup race, passes on the rerun; untouched).
agents: Grok Build's model picker folds magpie's rows for the account Grok Build is signed in to (Fate on Discord: grokbuild 在登录态下会加载重复的模型) — signed in, the picker listed Grok Build's own models and then the same models again as the Grok (SuperGrok) subscription via magpie. They are marked Same, as Claude Code's are (#496), so the picker folds them into one row a click opens. viaMagpie also counts as the agent's own a plugin's account for the agent's vendor (Grok moved onto its plugin) when it is the user the agent is signed in to (provider.AgentUser: Grok Build's auth.json). TestGrokFoldsItsOwnAccount fails without it (magpie/grok/grok-4.7 unfolded); the plugin path is not covered by a test.
agents: Grok Build's model picker folds magpie's rows for the account Grok Build is signed in to (Fate on Discord: grokbuild 在登录态下会加载重复的模型) — signed in, the picker listed Grok Build's own models and then the same models again as the Grok (SuperGrok) subscription via magpie. They are marked Same, as Claude Code's are (#496), so the picker folds them into one row a click opens. viaMagpie also counts as the agent's own a plugin's account for the agent's vendor (Grok moved onto its plugin) when it is the user the agent is signed in to (provider.AgentUser: Grok Build's auth.json). TestGrokFoldsItsOwnAccount fails without it (magpie/grok/grok-4.7 unfolded); the plugin path is not covered by a test.
agents: Droid, Qoder, OpenHanako, WorkBuddy, OpenCode and ZCode no longer get an output limit above the model's context window (#347)
wsl: OpenCode, Kimi Code, omp and the other CLIs magpie sets up through a file in the home are set up in WSL distros too (#515: on Windows only Codex, Pi, OmO and Claude Code were found in a distro) — fourteen agents join wslKinds, each found by its folder under the distro's $HOME or its command on the distro's own PATH, and listed as <name> · WSL <distro> (opencode@wsl:Ubuntu…): OpenCode (~/.config/opencode, opencode), MiMo Code (~/.config/mimocode, mimo), Kimi Code (~/.kimi-code, kimi; the old kimi-cli's ~/.kimi where that is all there is, found by the command), omp (~/.omp/agent, omp), Crush (~/.config/crush/crush.json, Linux's place, not Windows' LOCALAPPDATA), Hermes Agent (~/.hermes), Grok Build (~/.grok, by its folder alone, as on this machine), Droid (~/.factory), fx (~/.fx, by its folder alone: fx is also the JSON viewer), Command Code (~/.commandcode), MiniMax Code's mcode (~/.minimax), Muse Code (~/.config/muse), Qoder and Qoder CN (~/.qoder, ~/.qoder-cn). Each is its host constructor at a place (opencodeIn, kimiIn, ompIn, crushIn, hermesIn, grokIn, droidIn, fxIn, commandCodeIn, miniMaxIn, museIn, qoderIn/qoderCNIn; the host's opencode(home, cfg) and the rest wrap them), so it writes the same keys and schema as on this machine, but the gateway as the distro reaches it (place.gw/v1: 127.0.0.1 when mirrored, Windows' address under NAT) in its provider, its Check and its Sync, and its folder at the default, as the distro's variables (OPENCODE_CONFIG_DIR, KIMI_CODE_HOME, HERMES_HOME, FACTORY_HOME_OVERRIDE, MINIMAX_DATA_DIR, QODER_CONFIG_DIR…) aren't read (place.getenv is "" there); OpenChamber, a Windows app, isn't looked at for a distro's OpenCode, and omp there, whose version isn't known, gets xhigh rather than a max an older omp refuses. Their notices are the restart advice of their own, as for Codex. A stopped distro's agent is built without looking at its files (place.cold: exists/isDir are false, so the defaults are taken), its options read none (wslOwnAsleep, kimiOwnOptions/ompOwnOptions/miniMaxOwnOptions without a file), and a pick, once the distro is started, builds the agent again warm, so it writes the file the agent reads — an opencode.jsonc already there, ~/.kimi for kimi-cli — not the default. Left out: Gemini CLI and Antigravity (a key lent through .env, a launch command), dsh (profiles and .env), Devin (no gateway; its model list from the devin command on this machine), Goose (no magpie routing; its name is pressly's goose too), Cursor and Copilot (no magpie routing), and the apps (Cline, Hanako, WorkBuddy, ZCode, Claude Desktop, OpenChamber, Alma, Cindy); and the library's skills, MCP and instructions for the new WSL agents, which wslTargetOf still gives only Claude Code, Codex, Pi and OmO. Nothing ran on Windows or in a real WSL. wsl_more_test.go, on fake distro roots under temp dirs with the host's variables pointed elsewhere: TestWSLProbeFindsMoreAgents (each folder and command in the probe, none for grok and fx, kimi-cli by its command), TestWSLMoreAgentsDiscovered (all fourteen listed, no command, UA or aliases), TestWSLMoreAgentsPick (each agent, mirrored and NAT: its file in the distro names the distro's gateway, no 127.0.0.1 under NAT, Check passes, Sync keeps it, the notice; this machine's home and the folders its variables name untouched), TestWSLMoreAgentsStopped (nothing asked nor opened, the last value shown, options, a pick starts the distro and writes there) fail without the change; TestWSLStoppedPickFindsItsFiles fails without the warm rebuild (the pick wrote a second opencode.json beside the .jsonc). go vet ./internal/..., go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build and vet ./internal/..., GOOS=windows go test -c ./internal/agent/ ./internal/library/, and go test -tags nogui -count=1 ./... under a temp HOME pass (TestFXInState failed once on its TempDir cleanup race, passes on the rerun; untouched).
…
wsl: OpenCode, Kimi Code, omp and the other CLIs magpie sets up through a file in the home are set up in WSL distros too (#515: on Windows only Codex, Pi, OmO and Claude Code were found in a distro) — fourteen agents join wslKinds, each found by its folder under the distro's $HOME or its command on the distro's own PATH, and listed as <name> · WSL <distro> (opencode@wsl:Ubuntu…): OpenCode (~/.config/opencode, opencode), MiMo Code (~/.config/mimocode, mimo), Kimi Code (~/.kimi-code, kimi; the old kimi-cli's ~/.kimi where that is all there is, found by the command), omp (~/.omp/agent, omp), Crush (~/.config/crush/crush.json, Linux's place, not Windows' LOCALAPPDATA), Hermes Agent (~/.hermes), Grok Build (~/.grok, by its folder alone, as on this machine), Droid (~/.factory), fx (~/.fx, by its folder alone: fx is also the JSON viewer), Command Code (~/.commandcode), MiniMax Code's mcode (~/.minimax), Muse Code (~/.config/muse), Qoder and Qoder CN (~/.qoder, ~/.qoder-cn). Each is its host constructor at a place (opencodeIn, kimiIn, ompIn, crushIn, hermesIn, grokIn, droidIn, fxIn, commandCodeIn, miniMaxIn, museIn, qoderIn/qoderCNIn; the host's opencode(home, cfg) and the rest wrap them), so it writes the same keys and schema as on this machine, but the gateway as the distro reaches it (place.gw/v1: 127.0.0.1 when mirrored, Windows' address under NAT) in its provider, its Check and its Sync, and its folder at the default, as the distro's variables (OPENCODE_CONFIG_DIR, KIMI_CODE_HOME, HERMES_HOME, FACTORY_HOME_OVERRIDE, MINIMAX_DATA_DIR, QODER_CONFIG_DIR…) aren't read (place.getenv is "" there); OpenChamber, a Windows app, isn't looked at for a distro's OpenCode, and omp there, whose version isn't known, gets xhigh rather than a max an older omp refuses. Their notices are the restart advice of their own, as for Codex. A stopped distro's agent is built without looking at its files (place.cold: exists/isDir are false, so the defaults are taken), its options read none (wslOwnAsleep, kimiOwnOptions/ompOwnOptions/miniMaxOwnOptions without a file), and a pick, once the distro is started, builds the agent again warm, so it writes the file the agent reads — an opencode.jsonc already there, ~/.kimi for kimi-cli — not the default. Left out: Gemini CLI and Antigravity (a key lent through .env, a launch command), dsh (profiles and .env), Devin (no gateway; its model list from the devin command on this machine), Goose (no magpie routing; its name is pressly's goose too), Cursor and Copilot (no magpie routing), and the apps (Cline, Hanako, WorkBuddy, ZCode, Claude Desktop, OpenChamber, Alma, Cindy); and the library's skills, MCP and instructions for the new WSL agents, which wslTargetOf still gives only Claude Code, Codex, Pi and OmO. Nothing ran on Windows or in a real WSL. wsl_more_test.go, on fake distro roots under temp dirs with the host's variables pointed elsewhere: TestWSLProbeFindsMoreAgents (each folder and command in the probe, none for grok and fx, kimi-cli by its command), TestWSLMoreAgentsDiscovered (all fourteen listed, no command, UA or aliases), TestWSLMoreAgentsPick (each agent, mirrored and NAT: its file in the distro names the distro's gateway, no 127.0.0.1 under NAT, Check passes, Sync keeps it, the notice; this machine's home and the folders its variables name untouched), TestWSLMoreAgentsStopped (nothing asked nor opened, the last value shown, options, a pick starts the distro and writes there) fail without the change; TestWSLStoppedPickFindsItsFiles fails without the warm rebuild (the pick wrote a second opencode.json beside the .jsonc). go vet ./internal/..., go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build and vet ./internal/..., GOOS=windows go test -c ./internal/agent/ ./internal/library/, and go test -tags nogui -count=1 ./... under a temp HOME pass (TestFXInState failed once on its TempDir cleanup race, passes on the rerun; untouched).
groups: a routing group whose member's reasoning levels nothing knows offers its other members' levels, and Pi's thinking field offers and shows what Pi runs the model at (Moody-Sin, #597: 路由组设了推理强度最高是否正确应用?) — Pi on group/deepseek (腾讯DeepSeek) at magpie's 最高 listed off alone in its /thinking, and sent no reasoning_effort: Pi clamps defaultThinkingLevel max to the off it offers, so the group's members were asked at the vendor's default, not max.
wsl: OpenCode, Kimi Code, omp and the other CLIs magpie sets up through a file in the home are set up in WSL distros too (#515: on Windows only Codex, Pi, OmO and Claude Code were found in a distro) — fourteen agents join wslKinds, each found by its folder under the distro's $HOME or its command on the distro's own PATH, and listed as <name> · WSL <distro> (opencode@wsl:Ubuntu…): OpenCode (~/.config/opencode, opencode), MiMo Code (~/.config/mimocode, mimo), Kimi Code (~/.kimi-code, kimi; the old kimi-cli's ~/.kimi where that is all there is, found by the command), omp (~/.omp/agent, omp), Crush (~/.config/crush/crush.json, Linux's place, not Windows' LOCALAPPDATA), Hermes Agent (~/.hermes), Grok Build (~/.grok, by its folder alone, as on this machine), Droid (~/.factory), fx (~/.fx, by its folder alone: fx is also the JSON viewer), Command Code (~/.commandcode), MiniMax Code's mcode (~/.minimax), Muse Code (~/.config/muse), Qoder and Qoder CN (~/.qoder, ~/.qoder-cn). Each is its host constructor at a place (opencodeIn, kimiIn, ompIn, crushIn, hermesIn, grokIn, droidIn, fxIn, commandCodeIn, miniMaxIn, museIn, qoderIn/qoderCNIn; the host's opencode(home, cfg) and the rest wrap them), so it writes the same keys and schema as on this machine, but the gateway as the distro reaches it (place.gw/v1: 127.0.0.1 when mirrored, Windows' address under NAT) in its provider, its Check and its Sync, and its folder at the default, as the distro's variables (OPENCODE_CONFIG_DIR, KIMI_CODE_HOME, HERMES_HOME, FACTORY_HOME_OVERRIDE, MINIMAX_DATA_DIR, QODER_CONFIG_DIR…) aren't read (place.getenv is "" there); OpenChamber, a Windows app, isn't looked at for a distro's OpenCode, and omp there, whose version isn't known, gets xhigh rather than a max an older omp refuses. Their notices are the restart advice of their own, as for Codex. A stopped distro's agent is built without looking at its files (place.cold: exists/isDir are false, so the defaults are taken), its options read none (wslOwnAsleep, kimiOwnOptions/ompOwnOptions/miniMaxOwnOptions without a file), and a pick, once the distro is started, builds the agent again warm, so it writes the file the agent reads — an opencode.jsonc already there, ~/.kimi for kimi-cli — not the default. Left out: Gemini CLI and Antigravity (a key lent through .env, a launch command), dsh (profiles and .env), Devin (no gateway; its model list from the devin command on this machine), Goose (no magpie routing; its name is pressly's goose too), Cursor and Copilot (no magpie routing), and the apps (Cline, Hanako, WorkBuddy, ZCode, Claude Desktop, OpenChamber, Alma, Cindy); and the library's skills, MCP and instructions for the new WSL agents, which wslTargetOf still gives only Claude Code, Codex, Pi and OmO. Nothing ran on Windows or in a real WSL. wsl_more_test.go, on fake distro roots under temp dirs with the host's variables pointed elsewhere: TestWSLProbeFindsMoreAgents (each folder and command in the probe, none for grok and fx, kimi-cli by its command), TestWSLMoreAgentsDiscovered (all fourteen listed, no command, UA or aliases), TestWSLMoreAgentsPick (each agent, mirrored and NAT: its file in the distro names the distro's gateway, no 127.0.0.1 under NAT, Check passes, Sync keeps it, the notice; this machine's home and the folders its variables name untouched), TestWSLMoreAgentsStopped (nothing asked nor opened, the last value shown, options, a pick starts the distro and writes there) fail without the change; TestWSLStoppedPickFindsItsFiles fails without the warm rebuild (the pick wrote a second opencode.json beside the .jsonc). go vet ./internal/..., go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build and vet ./internal/..., GOOS=windows go test -c ./internal/agent/ ./internal/library/, and go test -tags nogui -count=1 ./... under a temp HOME pass (TestFXInState failed once on its TempDir cleanup race, passes on the rerun; untouched).
wsl: OpenCode, Kimi Code, omp and the other CLIs magpie sets up through a file in the home are set up in WSL distros too (#515: on Windows only Codex, Pi, OmO and Claude Code were found in a distro) — fourteen agents join wslKinds, each found by its folder under the distro's $HOME or its command on the distro's own PATH, and listed as <name> · WSL <distro> (opencode@wsl:Ubuntu…): OpenCode (~/.config/opencode, opencode), MiMo Code (~/.config/mimocode, mimo), Kimi Code (~/.kimi-code, kimi; the old kimi-cli's ~/.kimi where that is all there is, found by the command), omp (~/.omp/agent, omp), Crush (~/.config/crush/crush.json, Linux's place, not Windows' LOCALAPPDATA), Hermes Agent (~/.hermes), Grok Build (~/.grok, by its folder alone, as on this machine), Droid (~/.factory), fx (~/.fx, by its folder alone: fx is also the JSON viewer), Command Code (~/.commandcode), MiniMax Code's mcode (~/.minimax), Muse Code (~/.config/muse), Qoder and Qoder CN (~/.qoder, ~/.qoder-cn). Each is its host constructor at a place (opencodeIn, kimiIn, ompIn, crushIn, hermesIn, grokIn, droidIn, fxIn, commandCodeIn, miniMaxIn, museIn, qoderIn/qoderCNIn; the host's opencode(home, cfg) and the rest wrap them), so it writes the same keys and schema as on this machine, but the gateway as the distro reaches it (place.gw/v1: 127.0.0.1 when mirrored, Windows' address under NAT) in its provider, its Check and its Sync, and its folder at the default, as the distro's variables (OPENCODE_CONFIG_DIR, KIMI_CODE_HOME, HERMES_HOME, FACTORY_HOME_OVERRIDE, MINIMAX_DATA_DIR, QODER_CONFIG_DIR…) aren't read (place.getenv is "" there); OpenChamber, a Windows app, isn't looked at for a distro's OpenCode, and omp there, whose version isn't known, gets xhigh rather than a max an older omp refuses. Their notices are the restart advice of their own, as for Codex. A stopped distro's agent is built without looking at its files (place.cold: exists/isDir are false, so the defaults are taken), its options read none (wslOwnAsleep, kimiOwnOptions/ompOwnOptions/miniMaxOwnOptions without a file), and a pick, once the distro is started, builds the agent again warm, so it writes the file the agent reads — an opencode.jsonc already there, ~/.kimi for kimi-cli — not the default. Left out: Gemini CLI and Antigravity (a key lent through .env, a launch command), dsh (profiles and .env), Devin (no gateway; its model list from the devin command on this machine), Goose (no magpie routing; its name is pressly's goose too), Cursor and Copilot (no magpie routing), and the apps (Cline, Hanako, WorkBuddy, ZCode, Claude Desktop, OpenChamber, Alma, Cindy); and the library's skills, MCP and instructions for the new WSL agents, which wslTargetOf still gives only Claude Code, Codex, Pi and OmO. Nothing ran on Windows or in a real WSL. wsl_more_test.go, on fake distro roots under temp dirs with the host's variables pointed elsewhere: TestWSLProbeFindsMoreAgents (each folder and command in the probe, none for grok and fx, kimi-cli by its command), TestWSLMoreAgentsDiscovered (all fourteen listed, no command, UA or aliases), TestWSLMoreAgentsPick (each agent, mirrored and NAT: its file in the distro names the distro's gateway, no 127.0.0.1 under NAT, Check passes, Sync keeps it, the notice; this machine's home and the folders its variables name untouched), TestWSLMoreAgentsStopped (nothing asked nor opened, the last value shown, options, a pick starts the distro and writes there) fail without the change; TestWSLStoppedPickFindsItsFiles fails without the warm rebuild (the pick wrote a second opencode.json beside the .jsonc). go vet ./internal/..., go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build and vet ./internal/..., GOOS=windows go test -c ./internal/agent/ ./internal/library/, and go test -tags nogui -count=1 ./... under a temp HOME pass (TestFXInState failed once on its TempDir cleanup race, passes on the rerun; untouched).
agents: a command WSL finds on a Windows drive is not the distro's (#406) — WSL appends Windows' PATH, so command -v pi in Ubuntu found /mnt/c/Users/<u>/AppData/Roaming/npm/pi, the Windows npm shim, and magpie listed "Pi · WSL Ubuntu-24.04" and kept bin:pi in wsl.json. The probe now says where each command is and where /proc/mounts puts Windows' drives (/mnt/<letter>/ when it can't), and a command on one is not taken as installed in the distro, for Codex, Pi, OmO and Claude Code alike; one inside the distro, or its config directory, still is. A distro's findings carry the probe that made them, and a command found by an older probe is forgotten on load, so a stale bin:pi goes without the distro running. TestWSLWindowsCommandIsNotTheDistros, TestWSLOldProbeBinsForgotten and TestWSLRunningStaleWindowsPiReprobed fail without the change; TestWSLProbeScriptSaysWhere runs the script under sh. On a real WSL2 distro the probe said win:/mnt/c and win:/mnt/d. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass.
…
wsl: OpenCode, Kimi Code, omp and the other CLIs magpie sets up through a file in the home are set up in WSL distros too (#515: on Windows only Codex, Pi, OmO and Claude Code were found in a distro) — fourteen agents join wslKinds, each found by its folder under the distro's $HOME or its command on the distro's own PATH, and listed as <name> · WSL <distro> (opencode@wsl:Ubuntu…): OpenCode (~/.config/opencode, opencode), MiMo Code (~/.config/mimocode, mimo), Kimi Code (~/.kimi-code, kimi; the old kimi-cli's ~/.kimi where that is all there is, found by the command), omp (~/.omp/agent, omp), Crush (~/.config/crush/crush.json, Linux's place, not Windows' LOCALAPPDATA), Hermes Agent (~/.hermes), Grok Build (~/.grok, by its folder alone, as on this machine), Droid (~/.factory), fx (~/.fx, by its folder alone: fx is also the JSON viewer), Command Code (~/.commandcode), MiniMax Code's mcode (~/.minimax), Muse Code (~/.config/muse), Qoder and Qoder CN (~/.qoder, ~/.qoder-cn). Each is its host constructor at a place (opencodeIn, kimiIn, ompIn, crushIn, hermesIn, grokIn, droidIn, fxIn, commandCodeIn, miniMaxIn, museIn, qoderIn/qoderCNIn; the host's opencode(home, cfg) and the rest wrap them), so it writes the same keys and schema as on this machine, but the gateway as the distro reaches it (place.gw/v1: 127.0.0.1 when mirrored, Windows' address under NAT) in its provider, its Check and its Sync, and its folder at the default, as the distro's variables (OPENCODE_CONFIG_DIR, KIMI_CODE_HOME, HERMES_HOME, FACTORY_HOME_OVERRIDE, MINIMAX_DATA_DIR, QODER_CONFIG_DIR…) aren't read (place.getenv is "" there); OpenChamber, a Windows app, isn't looked at for a distro's OpenCode, and omp there, whose version isn't known, gets xhigh rather than a max an older omp refuses. Their notices are the restart advice of their own, as for Codex. A stopped distro's agent is built without looking at its files (place.cold: exists/isDir are false, so the defaults are taken), its options read none (wslOwnAsleep, kimiOwnOptions/ompOwnOptions/miniMaxOwnOptions without a file), and a pick, once the distro is started, builds the agent again warm, so it writes the file the agent reads — an opencode.jsonc already there, ~/.kimi for kimi-cli — not the default. Left out: Gemini CLI and Antigravity (a key lent through .env, a launch command), dsh (profiles and .env), Devin (no gateway; its model list from the devin command on this machine), Goose (no magpie routing; its name is pressly's goose too), Cursor and Copilot (no magpie routing), and the apps (Cline, Hanako, WorkBuddy, ZCode, Claude Desktop, OpenChamber, Alma, Cindy); and the library's skills, MCP and instructions for the new WSL agents, which wslTargetOf still gives only Claude Code, Codex, Pi and OmO. Nothing ran on Windows or in a real WSL. wsl_more_test.go, on fake distro roots under temp dirs with the host's variables pointed elsewhere: TestWSLProbeFindsMoreAgents (each folder and command in the probe, none for grok and fx, kimi-cli by its command), TestWSLMoreAgentsDiscovered (all fourteen listed, no command, UA or aliases), TestWSLMoreAgentsPick (each agent, mirrored and NAT: its file in the distro names the distro's gateway, no 127.0.0.1 under NAT, Check passes, Sync keeps it, the notice; this machine's home and the folders its variables name untouched), TestWSLMoreAgentsStopped (nothing asked nor opened, the last value shown, options, a pick starts the distro and writes there) fail without the change; TestWSLStoppedPickFindsItsFiles fails without the warm rebuild (the pick wrote a second opencode.json beside the .jsonc). go vet ./internal/..., go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build and vet ./internal/..., GOOS=windows go test -c ./internal/agent/ ./internal/library/, and go test -tags nogui -count=1 ./... under a temp HOME pass (TestFXInState failed once on its TempDir cleanup race, passes on the rerun; untouched).
groups: a routing group whose member's reasoning levels nothing knows offers its other members' levels, and Pi's thinking field offers and shows what Pi runs the model at (Moody-Sin, #597: 路由组设了推理强度最高是否正确应用?) — Pi on group/deepseek (腾讯DeepSeek) at magpie's 最高 listed off alone in its /thinking, and sent no reasoning_effort: Pi clamps defaultThinkingLevel max to the off it offers, so the group's members were asked at the vendor's default, not max.
groups: a routing group whose member's reasoning levels nothing knows offers its other members' levels, and Pi's thinking field offers and shows what Pi runs the model at (Moody-Sin, #597: 路由组设了推理强度最高是否正确应用?) — Pi on group/deepseek (腾讯DeepSeek) at magpie's 最高 listed off alone in its /thinking, and sent no reasoning_effort: Pi clamps defaultThinkingLevel max to the off it offers, so the group's members were asked at the vendor's default, not max.
wsl: OpenCode, Kimi Code, omp and the other CLIs magpie sets up through a file in the home are set up in WSL distros too (#515: on Windows only Codex, Pi, OmO and Claude Code were found in a distro) — fourteen agents join wslKinds, each found by its folder under the distro's $HOME or its command on the distro's own PATH, and listed as <name> · WSL <distro> (opencode@wsl:Ubuntu…): OpenCode (~/.config/opencode, opencode), MiMo Code (~/.config/mimocode, mimo), Kimi Code (~/.kimi-code, kimi; the old kimi-cli's ~/.kimi where that is all there is, found by the command), omp (~/.omp/agent, omp), Crush (~/.config/crush/crush.json, Linux's place, not Windows' LOCALAPPDATA), Hermes Agent (~/.hermes), Grok Build (~/.grok, by its folder alone, as on this machine), Droid (~/.factory), fx (~/.fx, by its folder alone: fx is also the JSON viewer), Command Code (~/.commandcode), MiniMax Code's mcode (~/.minimax), Muse Code (~/.config/muse), Qoder and Qoder CN (~/.qoder, ~/.qoder-cn). Each is its host constructor at a place (opencodeIn, kimiIn, ompIn, crushIn, hermesIn, grokIn, droidIn, fxIn, commandCodeIn, miniMaxIn, museIn, qoderIn/qoderCNIn; the host's opencode(home, cfg) and the rest wrap them), so it writes the same keys and schema as on this machine, but the gateway as the distro reaches it (place.gw/v1: 127.0.0.1 when mirrored, Windows' address under NAT) in its provider, its Check and its Sync, and its folder at the default, as the distro's variables (OPENCODE_CONFIG_DIR, KIMI_CODE_HOME, HERMES_HOME, FACTORY_HOME_OVERRIDE, MINIMAX_DATA_DIR, QODER_CONFIG_DIR…) aren't read (place.getenv is "" there); OpenChamber, a Windows app, isn't looked at for a distro's OpenCode, and omp there, whose version isn't known, gets xhigh rather than a max an older omp refuses. Their notices are the restart advice of their own, as for Codex. A stopped distro's agent is built without looking at its files (place.cold: exists/isDir are false, so the defaults are taken), its options read none (wslOwnAsleep, kimiOwnOptions/ompOwnOptions/miniMaxOwnOptions without a file), and a pick, once the distro is started, builds the agent again warm, so it writes the file the agent reads — an opencode.jsonc already there, ~/.kimi for kimi-cli — not the default. Left out: Gemini CLI and Antigravity (a key lent through .env, a launch command), dsh (profiles and .env), Devin (no gateway; its model list from the devin command on this machine), Goose (no magpie routing; its name is pressly's goose too), Cursor and Copilot (no magpie routing), and the apps (Cline, Hanako, WorkBuddy, ZCode, Claude Desktop, OpenChamber, Alma, Cindy); and the library's skills, MCP and instructions for the new WSL agents, which wslTargetOf still gives only Claude Code, Codex, Pi and OmO. Nothing ran on Windows or in a real WSL. wsl_more_test.go, on fake distro roots under temp dirs with the host's variables pointed elsewhere: TestWSLProbeFindsMoreAgents (each folder and command in the probe, none for grok and fx, kimi-cli by its command), TestWSLMoreAgentsDiscovered (all fourteen listed, no command, UA or aliases), TestWSLMoreAgentsPick (each agent, mirrored and NAT: its file in the distro names the distro's gateway, no 127.0.0.1 under NAT, Check passes, Sync keeps it, the notice; this machine's home and the folders its variables name untouched), TestWSLMoreAgentsStopped (nothing asked nor opened, the last value shown, options, a pick starts the distro and writes there) fail without the change; TestWSLStoppedPickFindsItsFiles fails without the warm rebuild (the pick wrote a second opencode.json beside the .jsonc). go vet ./internal/..., go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build and vet ./internal/..., GOOS=windows go test -c ./internal/agent/ ./internal/library/, and go test -tags nogui -count=1 ./... under a temp HOME pass (TestFXInState failed once on its TempDir cleanup race, passes on the rerun; untouched).
agents: omp's roles pick their own models (subagents, smol, slow), keep their thinking level, and get back what they had before magpie (#325)
providers: a model a vendor names its own way is merged with the same model from other providers — by its id as Volcengine Ark spells it, or by the model the user says it is the same as in Names & levels (kyzhouxu, #583: 火山引擎获取到的默认 ds 4.1 flash 模型的名称跟其他供应商的名称不一样,没有自动合并到同一个模型分组) — the groups magpie finds merged a model by its id lowercased, its vendor prefix and an eight-digit date taken off and a version's dot made a dash, so Ark's deepseek-v4-1-flash-260910 (a six-digit YYMMDD snapshot) was a group of its own beside ali-tp's deepseek-v4.1-flash. sameModel now also takes off Ark's YYMMDD (a year from 23 to 39, a month and a day that are one), Vertex's "@YYYYMMDD", reads Fireworks' "p" between digits (deepseek-v4p1) as the dot and "_" as "-"; -flash, -thinking, ":batch", a four-digit release (qwen's -2507, kimi-k2-0905) and a number that is no date (-128000, -261301) stay apart. For an id no rule matches (an Ark endpoint id, a relay's own name), each model in a provider's Names & levels now has a "Same as" (等同于) box, its placeholder the name it is merged by now: the model it is the same as, as any vendor spells it, staged in draft.modelPrefs and made with the provider's Save like the other prefs (Cancel drops it, Restore default empties it, nothing sent or redrawn or scrolled before the Save). It is kept in settings' ModelSameAs by "<provider>/<model>" (renamed with the provider like every Model* map), set by provider.SetModelSame (its own id however spelt keeps nothing; a model the provider doesn't serve, or a name of no model, is refused) and taken by autoGroups, AutoStandIn and the agents' reseat (AutoGroupOf); the agents are told once. TestSameModelVendorSpellings (Ark, Fireworks, Vertex, underscores, and pairs that must stay apart) and TestModelSameAsMergesFoundGroup (c/ep-sol-x joins auto-sol once said, GroupFor("sol") finds it, back out when cleared, refusals) are new; TestProviderSaveModelPrefs covers a Save's same; provider-same-as.test.cjs (WebKit, en and zh: placeholder, staging, unsaved, Restore default, Cancel, one Save carrying exactly the changes, no scroll, Chinese for each string) fails without the change and passes with it; provider-levels-save and provider-detect pass. go vet ./internal/..., go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui -count=1 ./... under a temp HOME pass.
agents: Grok Build's model picker folds magpie's rows for the account Grok Build is signed in to (Fate on Discord: grokbuild 在登录态下会加载重复的模型) — signed in, the picker listed Grok Build's own models and then the same models again as the Grok (SuperGrok) subscription via magpie. They are marked Same, as Claude Code's are (#496), so the picker folds them into one row a click opens. viaMagpie also counts as the agent's own a plugin's account for the agent's vendor (Grok moved onto its plugin) when it is the user the agent is signed in to (provider.AgentUser: Grok Build's auth.json). TestGrokFoldsItsOwnAccount fails without it (magpie/grok/grok-4.7 unfolded); the plugin path is not covered by a test.
gateway: every full id of a Claude family Claude Code names goes to its tier's model, served by a provider of the user's or not (KevinXC on Discord, v0.1.665, T3 Code 0.0.45 over Claude Code 2.1.287: claude-haiku-4-5, claude-opus-5, claude-fable-5 and claude-sonnet-4-6 went to the haiku/opus/fable/sonnet tiers, but claude-fable-5-1, claude-sonnet-5 and claude-sonnet-5-5 went to the Claude account, 429 "requires usage credits" / "weekly limit") — the stand-in (
4cbd96a3) was only asked for a model magpie shows no entry for, so a full id a provider of the user's serves bare (the Claude account lists claude-sonnet-5, claude-fable-5-1, …; a found auto-<model> group has it too) went there, while one none listed went to the tier: which tier a model reached depended on what the user's providers happen to list, not on the model. T3 Code names every model by its full id rather than Claude Code's alias, so it hit both. Now an id of the opus, sonnet, haiku or fable family (claudeFamily: claude-<family>-<any version>[-date][1m], and claude-3-5-sonnet-… alike, so a release magpie has never heard of is one too) from Claude Code routed through magpie goes to the model its tier is set to before groups or providers are looked at, count_tokens counting it there too. Kept as asked: a provider's "anth/claude-…", another agent's request, Claude Code not routed through magpie (StandIn says nothing), a routing group of the user's own named by the id (one magpie found, auto-…, was never picked for it), and the main model when Claude Code's ANTHROPIC_MODEL is that very id. A tier at an effort of its own (#536, "fake/sol:high[1m]") now reaches its model at that level through a stand-in too, where it was looked up whole and refused. TestClaudeTierStandInByFamily (claude-haiku-4-5, -20251001, claude-haiku-5, claude-opus-5, -5-5, -5-5[1m], claude-opus-6, claude-fable-5, -5-1, -5-1[1m], claude-sonnet-4-6, -5, -5-5, -5-5-20261001, claude-3-5-sonnet-20241022 through /v1/messages, beside a provider serving seven of them bare) fails without the change for the nine served ones; TestClaudeStandIn gains the families and the main model. go vet ./internal/..., go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui -count=1 ./... under a temp HOME pass (TestDevinSignedInOnlyInMagpie, a known flake, aside).
…
models: Provider in model names has a third way, a name the user gave used just as they wrote it (#92 marsxxl: v0.1.591 的 Codex 下拉里自定义名称又带上了 · WorkBuddy / · Claude Code) —
559ce021 (#203) had a custom name keep " · <provider>" after it like any other, so "Opus 5.5" read "Opus 5.5 · Claude Code" in every list Labels feeds (Codex's model_catalog_json and the gateway's Codex /models, Pi's, OpenCode's, ZCode's, OMP's, Muse's), while #92 had been promised the name as written. Both are kept: the setting is now Off / Not on names I set (自定义名称不带供应商) / On. On stays the default and keeps #203's behaviour, so nobody's list changes unasked; own leaves a name the user gave as it is and a vendor's name with its provider's; off is #335's plain names. Stored as plainOwnNames beside plainNames, so a settings.json of before reads as it did (plainNames: true is off). POST /api/settings/plain-names takes mode on|own|off (a body of on alone still works), magpie model suffix on|own|off, a Settings save keeps it, and /v1/models' magpie_label now follows the setting through Labels as the agents' lists do. The row's hint is one line that fits (en, zh), and its zh name says 供应商 as the rest of the app. Prompts and headers are unchanged. TestSuffixMode, TestSuffixModesReachAgentFiles (Codex model_catalog_json and Pi models.json), TestModelsListSuffixModes (/v1/models magpie_label) and model-suffix-own.test.cjs (Chromium and WebKit, en and zh) fail without the change; TestModelName and TestModelNameAndLevelsReachAgentFiles keep #203's assertions. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass (TestConfiguredModelsAdvertiseImageInput load-flaky, passes alone); plain-names, click-scroll, old-webkit pass.
…
agents: Droid, Qoder, OpenHanako, WorkBuddy, OpenCode and ZCode no longer get an output limit above the model's context window (#347)
gui: Claude Code's folded picker (#496) heads Claude Code once, leaves the keys to the filter after a click, shows what a query finds and keeps a starred dated id; a WSL distro's Claude Code folds nothing (#518)
library: agents in a running WSL distro take skills, MCP servers and instructions (#331) — the Library knew only this machine's agent ids, so codex@wsl:Ubuntu and the like were never offered, and the id's colon was refused as an agent's name and can't be in a Windows file name. Claude Code, Codex, Pi and OmO in a distro now get them at their default places under the distro's $HOME through \\wsl.localhost, only while it runs (opening a stopped one's files starts it; its state is kept and asking for it says to start it). Skills go in as copies, made beside and renamed into place and made again when the library's changes, since a Linux agent can't follow a link to a Windows folder (project skills share the same copy code); an MCP server that runs a Windows program (C:\…, *.exe/.cmd/.bat/.ps1) is refused for them with a message; Pi there gets no MCP servers, where its version decides the file. The id's colon is a dot in file and folder names, and setup export/import turns it back. TestWSLTargets, TestWSLSkillsCopied, TestWSLServers and TestWSLInstructions (a fake distro) fail without the change. go vet (and GOOS=windows for agent, library), go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass. Not run against a real distro.
wsl: OpenCode, Kimi Code, omp and the other CLIs magpie sets up through a file in the home are set up in WSL distros too (#515: on Windows only Codex, Pi, OmO and Claude Code were found in a distro) — fourteen agents join wslKinds, each found by its folder under the distro's $HOME or its command on the distro's own PATH, and listed as <name> · WSL <distro> (opencode@wsl:Ubuntu…): OpenCode (~/.config/opencode, opencode), MiMo Code (~/.config/mimocode, mimo), Kimi Code (~/.kimi-code, kimi; the old kimi-cli's ~/.kimi where that is all there is, found by the command), omp (~/.omp/agent, omp), Crush (~/.config/crush/crush.json, Linux's place, not Windows' LOCALAPPDATA), Hermes Agent (~/.hermes), Grok Build (~/.grok, by its folder alone, as on this machine), Droid (~/.factory), fx (~/.fx, by its folder alone: fx is also the JSON viewer), Command Code (~/.commandcode), MiniMax Code's mcode (~/.minimax), Muse Code (~/.config/muse), Qoder and Qoder CN (~/.qoder, ~/.qoder-cn). Each is its host constructor at a place (opencodeIn, kimiIn, ompIn, crushIn, hermesIn, grokIn, droidIn, fxIn, commandCodeIn, miniMaxIn, museIn, qoderIn/qoderCNIn; the host's opencode(home, cfg) and the rest wrap them), so it writes the same keys and schema as on this machine, but the gateway as the distro reaches it (place.gw/v1: 127.0.0.1 when mirrored, Windows' address under NAT) in its provider, its Check and its Sync, and its folder at the default, as the distro's variables (OPENCODE_CONFIG_DIR, KIMI_CODE_HOME, HERMES_HOME, FACTORY_HOME_OVERRIDE, MINIMAX_DATA_DIR, QODER_CONFIG_DIR…) aren't read (place.getenv is "" there); OpenChamber, a Windows app, isn't looked at for a distro's OpenCode, and omp there, whose version isn't known, gets xhigh rather than a max an older omp refuses. Their notices are the restart advice of their own, as for Codex. A stopped distro's agent is built without looking at its files (place.cold: exists/isDir are false, so the defaults are taken), its options read none (wslOwnAsleep, kimiOwnOptions/ompOwnOptions/miniMaxOwnOptions without a file), and a pick, once the distro is started, builds the agent again warm, so it writes the file the agent reads — an opencode.jsonc already there, ~/.kimi for kimi-cli — not the default. Left out: Gemini CLI and Antigravity (a key lent through .env, a launch command), dsh (profiles and .env), Devin (no gateway; its model list from the devin command on this machine), Goose (no magpie routing; its name is pressly's goose too), Cursor and Copilot (no magpie routing), and the apps (Cline, Hanako, WorkBuddy, ZCode, Claude Desktop, OpenChamber, Alma, Cindy); and the library's skills, MCP and instructions for the new WSL agents, which wslTargetOf still gives only Claude Code, Codex, Pi and OmO. Nothing ran on Windows or in a real WSL. wsl_more_test.go, on fake distro roots under temp dirs with the host's variables pointed elsewhere: TestWSLProbeFindsMoreAgents (each folder and command in the probe, none for grok and fx, kimi-cli by its command), TestWSLMoreAgentsDiscovered (all fourteen listed, no command, UA or aliases), TestWSLMoreAgentsPick (each agent, mirrored and NAT: its file in the distro names the distro's gateway, no 127.0.0.1 under NAT, Check passes, Sync keeps it, the notice; this machine's home and the folders its variables name untouched), TestWSLMoreAgentsStopped (nothing asked nor opened, the last value shown, options, a pick starts the distro and writes there) fail without the change; TestWSLStoppedPickFindsItsFiles fails without the warm rebuild (the pick wrote a second opencode.json beside the .jsonc). go vet ./internal/..., go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build and vet ./internal/..., GOOS=windows go test -c ./internal/agent/ ./internal/library/, and go test -tags nogui -count=1 ./... under a temp HOME pass (TestFXInState failed once on its TempDir cleanup race, passes on the rerun; untouched).
agents: whether WSL is in mirrored networking is asked of WSL itself (wslinfo --networking-mode), and a UTF-16 .wslconfig is read (lgtm on Discord: told "WSL Ubuntu isn't in mirrored networking … pointed at Windows (http://192.168.16.1:3425)", while their WSL has always been in Mirrored) — magpie decided it by %UserProfile%\.wslconfig alone, read as UTF-8: a .wslconfig Notepad saved as "Unicode" (UTF-16, which WSL reads) said nothing, a "[wsl2] # comment" header ended the section, and mirrored set anywhere magpie didn't read was missed — so a really mirrored distro was pointed at its default route, which under mirrored is the LAN router, not Windows. The probe each running distro already gets now also prints wslinfo --networking-mode (WSL 2.0 on), kept as the distro's Net ("mirrored", "nat", …) and deciding Mirrored; .wslconfig decides only when there is no wslinfo, and is read as UTF-8, UTF-16LE (BOM or not) or UTF-16BE. A distro seen stopped is probed again once it runs, so WSL restarted in another mode is seen without restarting magpie. The notice says what it knows: "WSL Ubuntu is in nat networking, not mirrored, …", or, without wslinfo, that magpie couldn't ask and .wslconfig doesn't set it — never "isn't in mirrored" on .wslconfig's word alone. TestWSLConfigUTF16 (UTF-8, its BOM, UTF-16LE with and without BOM, UTF-16BE; fails without the change for the UTF-16 three), TestParseProbeNet, TestWSLNetworkingFromWslinfo (wslinfo mirrored with no .wslconfig is 127.0.0.1; wslinfo nat beats a .wslconfig saying mirrored; the fallback either way and its notice) and TestWSLReprobedAfterStop are new. The probe run through the real wsl.exe on a Windows 11 box (WSL 2.6.1, a NAT distro) read net:nat. go vet ./internal/..., go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui -count=1 ./... under a temp HOME pass.
agents: a command WSL finds on a Windows drive is not the distro's (#406) — WSL appends Windows' PATH, so command -v pi in Ubuntu found /mnt/c/Users/<u>/AppData/Roaming/npm/pi, the Windows npm shim, and magpie listed "Pi · WSL Ubuntu-24.04" and kept bin:pi in wsl.json. The probe now says where each command is and where /proc/mounts puts Windows' drives (/mnt/<letter>/ when it can't), and a command on one is not taken as installed in the distro, for Codex, Pi, OmO and Claude Code alike; one inside the distro, or its config directory, still is. A distro's findings carry the probe that made them, and a command found by an older probe is forgotten on load, so a stale bin:pi goes without the distro running. TestWSLWindowsCommandIsNotTheDistros, TestWSLOldProbeBinsForgotten and TestWSLRunningStaleWindowsPiReprobed fail without the change; TestWSLProbeScriptSaysWhere runs the script under sh. On a real WSL2 distro the probe said win:/mnt/c and win:/mnt/d. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass.
agents: a command WSL finds on a Windows drive is not the distro's (#406) — WSL appends Windows' PATH, so command -v pi in Ubuntu found /mnt/c/Users/<u>/AppData/Roaming/npm/pi, the Windows npm shim, and magpie listed "Pi · WSL Ubuntu-24.04" and kept bin:pi in wsl.json. The probe now says where each command is and where /proc/mounts puts Windows' drives (/mnt/<letter>/ when it can't), and a command on one is not taken as installed in the distro, for Codex, Pi, OmO and Claude Code alike; one inside the distro, or its config directory, still is. A distro's findings carry the probe that made them, and a command found by an older probe is forgotten on load, so a stale bin:pi goes without the distro running. TestWSLWindowsCommandIsNotTheDistros, TestWSLOldProbeBinsForgotten and TestWSLRunningStaleWindowsPiReprobed fail without the change; TestWSLProbeScriptSaysWhere runs the script under sh. On a real WSL2 distro the probe said win:/mnt/c and win:/mnt/d. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass.
library: a remote MCP server signed in once in magpie is used by every agent without signing in again (#615) — a streamable-HTTP server behind OAuth (Neon, Notion, Supabase) had each agent run its own sign-in. The server's editor now has Sign in: magpie finds the authorization server from the server's 401 (RFC 9728 protected-resource metadata, RFC 8414/OIDC metadata, else the 2025-03 /authorize, /token, /register at its origin), registers itself (RFC 7591, a 127.0.0.1 redirect), opens the browser with PKCE S256 and the resource, and keeps the tokens in mcp-signins.json beside its other account secrets (0600, never the keychain). While signed in, the agents are given the gateway's /mcp/<name> for that server, without the Authorization the library has for it; the gateway passes POST (JSON or event stream), the GET stream and DELETE on with the Mcp-Session-Id and magpie's token, renews it 2 minutes before it runs out and once on a 401, one renewal per server at a time, and a sign-in the server won't renew is answered 502 "sign in to it again" (never a 401 the agent would act on). Signing out, a new URL or an SSE server gives the agents the server's own address as before; a rename keeps the sign-in and removing the server drops it. /mcp/ is local-only or LAN-shared with the key, and refuses a request with an Origin or a Host not naming this computer, so a web page can't borrow the sign-in through the browser (form posts, DNS rebinding), and isn't counted as busy or key-limited. WSL agents are given their distro's gateway address. The row says Signed in or Sign-in ran out; zh strings added. Tests: TestSignIn, TestSignInWithoutMetadata, TestSignInPage, TestNothingToSignIn, TestTokenRenewsBeforeItRunsOut (8 calls, 1 renewal), TestRenewRefused, TestRenewRefusedForGood, TestMCPProxy, TestMCPProxyStaysHere, TestMCPProxyNotForWebPages (fails without the Origin/Host check), TestSignedInServerGoesThroughMagpie against a local fake OAuth+MCP server; mcp-signin.test.cjs (webkit, en and zh). Not tried against a real Neon/Notion/Supabase sign-in. go vet ./internal/..., go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui -count=1 ./... under a temp HOME pass.
agents: Droid, Qoder, OpenHanako, WorkBuddy, OpenCode and ZCode no longer get an output limit above the model's context window (#347)