# magpie reference The full manual: every agent magpie wires, every provider option, the gateway, routing, plugins, the CLI, Docker, sync and the files magpie keeps. For the overview, see the [README](../README.md). One place to pick every agent's model: Codex on DeepSeek, Claude Code on Kimi, Gemini CLI on GLM, from the menu bar. [usemagpie.ai](https://usemagpie.ai) [![Discord](https://img.shields.io/badge/Discord-join%20the%20community-5865F2?logo=discord&logoColor=white)](https://discord.gg/vGSnD3ZKQF) `magpie` is a single screen that lists each AI agent on your machine and the model it is set to. Click a value, pick a model. That is the whole app. It lives in the menu bar: click the icon and a panel drops down; the same screen also opens as a normal window (`magpie`, or *Open magpie* in the tray menu), and there is a terminal version (`magpie tui`) and a plain CLI. The terminal version serves the gateway while it is open when no other magpie (the app, `magpie web`, `magpie serve`) does, and says so on its status line; agents connected to magpie lose it when it quits. ``` ◉ magpie ▸ Claude Code claude-fable-5-1[1m] ~/.claude/settings.json Codex gpt-6-astra effort medium Gemini CLI gemini-3.1-pro OpenCode anthropic/claude-sonnet-5 small anthropic/claude-haiku-4-5 MiMo Code anthropic/claude-sonnet-5 Pi openrouter/z-ai/glm-5.2:batch Goose anthropic/claude-sonnet-5 Cursor auto Copilot CLI claude-fable-5 ↑↓ agent · ←→ field · ↵ change · s save profile · p profiles · q quit ``` - **One binary.** About 30 MB with the desktop app (it uses the system webview through [Wails](https://wails.io), nothing bundled; a 15 MB download on a Mac), and under 30 MB for the terminal-only build. macOS, Linux and Windows. - **Edits config files surgically.** Only the one key you change is touched; comments, ordering and indentation in your `settings.json`, `config.toml`, `opencode.jsonc` or `config.yaml` survive intact. Writes are atomic. - **One endpoint for every agent.** magpie runs a local gateway that speaks OpenAI chat completions, OpenAI Responses and the Anthropic Messages API, and forwards to whichever vendor serves the model. Codex, Claude Code, OpenCode and the rest all point at `http://127.0.0.1:3425/v1` and pick from one catalog; the translation between APIs happens in magpie, streaming and tool calls included. - **Your subscriptions, shared.** Sign in to Claude Code, Codex (ChatGPT) or Copilot and that login shows up as a provider: every other agent can use its models through the gateway, with nothing copied and no key to paste. - **Providers with one field.** Pick a preset (Anthropic, OpenAI, Gemini, DeepSeek, Kimi, GLM, MiniMax, StepFun, Qwen, Baidu Qianfan, Tencent Cloud Token Plan, Huawei Cloud MaaS, Volcengine Ark, Mistral, Groq, xAI, OpenRouter, Together, Fireworks, SiliconFlow, NVIDIA NIM, ModelScope, AiHubMix, PipeLLM, 302.AI, Ollama, LM Studio…), paste a key, done. Custom vendors need a name and a base URL. magpie never reads keys from your shell environment. - **Real model lists, nothing compiled in.** With a key in hand magpie asks the vendor which models it serves and offers exactly those; the [models.dev](https://models.dev) catalog fills in names, reasoning efforts and the list for vendors that have none (a model the vendor's own entry doesn't list is named as the other providers serving it name it, so glm-5-turbo reads GLM-5-Turbo under Zhipu as under ZCode; an Azure deployment keeps its own name), and refreshes itself in the background once it goes stale. Choose which models each provider exposes, or expose them all — a model released this morning is in the picker on the next refresh. - **Each agent's own model list.** Under an agent's name on the Agents page, "Showing 5 / 32 models" opens its list: click a model to take it out of that agent's picker (Codex's `/model` included, its ChatGPT models too) or put it back; other agents still use it, and a new model is shown. Its "Only models I pick" switch turns the list around: the models ticked when it is switched on stay, and a model added later, of any provider, stays out of that agent's picker and its config until it is ticked (`magpie visible --only-picked`, back with `--show-new`). A model asked for by name still works either way. - **Profiles.** Snapshot every agent's settings under a name and switch all of them back in one move. - **Real logos, no framework.** Plain HTML over the system webview; brand icons from [lobehub/icons](https://github.com/lobehub/lobe-icons). ## Agents | Agent | File | Fields | | ------------ | --------------------------------- | --------------- | | Claude Code | `~/.claude/settings.json` | provider, model, opus/sonnet/haiku/fable, sign-in (through magpie) | | Claude Desktop | `Claude/` + `Claude-3p/configLibrary/` in `~/Library/Application Support` (`%LOCALAPPDATA%` on Windows, `~/.config` on Linux). A Desktop installed as an MSIX package on Windows keeps them in its package instead (`%LOCALAPPDATA%\Packages\Claude_\LocalCache\Local`, or `AnthropicPBC.Claude_` for the Store's package, its own data in `LocalCache\Roaming\Claude`). Desktop 2.31226 and later, packaged, reads the real `%LOCALAPPDATA%\Claude-3p` instead, so for a packaged Desktop magpie writes both ([`desktopdir`](../internal/desktopdir/desktopdir.go)) | provider (its third-party gateway mode: Code and Cowork on magpie, no Anthropic sign-in; restart Desktop) | | Codex | `~/.codex/config.toml` | provider, model, effort | | Gemini CLI | `~/.gemini/settings.json`, `~/.gemini/.env` | auth, model | | OpenCode | `~/.config/opencode/opencode.json(c)` (`$OPENCODE_CONFIG_DIR`) | model, small | | OpenChamber | `~/.config/openchamber/preferences.json` (`$OPENCHAMBER_DATA_DIR`; magpie's provider in OpenCode's config) | model, small (its own defaults, over OpenCode's) | | MiMo Code | `~/.config/mimocode/mimocode.json(c)` | model, small | | Pi | `~/.pi/agent/settings.json` | model | | Aside | `~/.aside/u/0/settings.json` (+ `models.json`; the first account, the only one magpie wires) | model, effort, fast, standard, deep, visual, image | | OmO (omo-ai) | `~/.omo/agent/settings.json` (+ `models.json`; `$OMO_CODING_AGENT_DIR`, `$SENPI_CODING_AGENT_DIR`) | model | | Goose | `~/.config/goose/config.yaml` | model | | Cursor CLI | `~/.cursor/cli-config.json` | model | | Zed | `~/.config/zed/settings.json` (`$XDG_CONFIG_HOME` on Linux, `%APPDATA%\Zed` on Windows) | model (a `magpie` OpenAI-compatible provider; its catalog in Zed's picker) | | VS Code (Chat) | `~/Library/Application Support/Code/User/settings.json` + `chatLanguageModels.json` (`~/.config/Code/User` on Linux, `%APPDATA%\Code\User` on Windows) | model (`chat.defaultModel`; a `magpie` Custom Endpoint group, its catalog in Chat's model picker; VS Code 1.122+, no Copilot sign-in or key needed). Each profile's own pair under `User/profiles//` (listed in `globalStorage/storage.json`) gets the same, unless the profile uses the default's | | VS Code Insiders (Chat) | the same files under `Code - Insiders/User` in place of `Code/User` | as VS Code, a row of its own; its models send the token `magpie-vscode-insiders`, since its chat's User-Agent is VS Code's | | VSCodium (Chat) | the same files under `VSCodium/User` in place of `Code/User` | as VS Code, a row of its own; its models send the token `magpie-vscodium`, so Usage counts them as VSCodium; its Chat features must be turned on (`chat.disableAIFeatures=false`, and `defaultChatAgent` plus `trustedExtensionAuthAccess` for GitHub.copilot-chat in its product.json). With the Marketplace's GitHub Copilot Chat 0.48.1, which has no Custom Endpoint provider, the group is written for its OpenAI Compatible provider (`customoai`, the token sent as `x-api-key`), which lists models only while Copilot Chat is signed in to GitHub with a personal Copilot plan | | Copilot (JetBrains) | `byok.json` in `~/.config/github-copilot` (`$XDG_CONFIG_HOME/github-copilot` when set, `%USERPROFILE%\AppData\Local\github-copilot` on Windows), the Copilot language server's own file, which Xcode's and Eclipse's Copilot read too | provider (a `magpie` custom endpoint provider beside the user's: every magpie model at the gateway's `/v1/chat/completions`, sent with `magpie-copilot-jetbrains`; a model hidden in Manage Models stays hidden through a sync; off removes magpie's three keys alone). Found by the `github-copilot-intellij` plugin in a JetBrains IDE or Android Studio. Listed after the IDE restarts, and only while Copilot is signed in to GitHub on a plan with Bring Your Own Key (Free, Pro, Pro+; Business and Enterprise when the organisation allows it); the model is picked in Copilot Chat | | JetBrains Air | `acp.json` in `~/Library/Application Support/JetBrains/Air` (`~/.config/JetBrains/Air` on Linux, `%APPDATA%\JetBrains\Air` on Windows) + `magpie-opencode.json` beside it | model (a `Magpie` ACP agent: OpenCode's `opencode acp` on magpie's provider alone, its models and routing groups in Air's model menu; needs OpenCode installed) | | Copilot CLI | `~/.copilot/settings.json` | model | | Crush | `~/.config/crush/crush.json` | large, small | | DeepSeek Harness (dsh) | `~/.dsh/profiles/*/cordis.patch.yml` (`$DSH_HOME`; a custom provider, Magpie, its key `MAGPIE_GATEWAY_KEY` in `~/.dsh/.env` and dsh's own key store `~/.dsh/.credentials.yaml`, which the desktop app reads; on one of magpie's models its `web-search-deepseek` row also goes to the gateway, which searches with the model or Settings › Web search, unless dsh has a `DEEPSEEK_API_KEY` or a row of your own), or `~/.dsh/config.yaml` before dsh 0.1.5 | model, effort | | Reasonix Studio (2.x, or a native Go `reasonix` 2.x / 1.39.x CLI) | `~/.reasonix/config.toml` + `.env` (`%APPDATA%/reasonix` on Windows; `$REASONIX_HOME`) | model (Executor), planner (Plan), effort (a dedicated `magpie` provider, shared by Studio and the native CLI) | | Command Code | `~/.commandcode/settings.json` (+ `providers.json`) | model | | fx | `~/.fx/settings.json` | model (a keyless `magpie` provider) | | omp (oh-my-pi) | `~/.omp/agent/config.yml` (+ `models.yml`) | model | | Devin | `~/.config/devin/config.json` (`%APPDATA%\devin\config.json` on Windows) | model | | Hermes Agent | `~/.hermes/config.yaml` (`$HERMES_HOME`) | model | | Mister Morph | `~/.morph/config.yaml` (`$MISTER_MORPH_CONFIG`) | model, effort (`llm` on the gateway as `openai_response_compatible`, the Responses API; what it had comes back when you switch away) | | Kimi Code | `~/.kimi/config.toml` (`$KIMI_SHARE_DIR`) | model (a `magpie` provider; magpie's models in Kimi's /model) | | Qwen Code | `~/.qwen/settings.json` (`$QWEN_HOME`) | model (magpie's models as `modelProviders.openai` entries on a `MAGPIE_QWEN_API_KEY` env var; settings.model; `security.auth.selectedType` openai while wired) | | Muse Code | `~/.config/muse/settings.json` (`$XDG_CONFIG_HOME`) | model (endpoint_transport to the gateway, auth none; magpie's models in Muse's list) | | Empryo | `~/.empryo/config.json` | defaultModel (a `magpie` provider at the gateway in `providers`) | | Ante | `~/.ante/catalog.json` and `settings.json` (`$ANTE_HOME`) | provider, model (a `magpie` provider on OpenAiCompatible, with no auth of magpie's: the gateway lets this machine in with any token and Ante counts a provider with none as authenticated; magpie's models in Ante's model picker, and Ante started on magpie, which is what its settings' `provider` decides) | | MiniMax Code (mcode) | `~/.minimax/config.yaml` (`$MINIMAX_DATA_DIR`) | model (a `magpie` custom provider; magpie's models in its /model) | | Droid (Factory) | `~/.factory/settings.json` (`$FACTORY_HOME_OVERRIDE`) | model (magpie's models as BYOK `customModels`, in Droid's /model) | | Cline (CLI) | `~/.cline/data/settings/providers.json` (`$CLINE_DIR`) | model, effort (magpie takes its openai-compatible provider) | | Qoder (CLI) | `~/.qoder/settings.json` (`$QODER_CONFIG_DIR`) | model, effort (a `magpie` custom provider; needs a Qoder plan with BYOK) | | Qoder CN (CLI) | `~/.qoder-cn/settings.json` (`$QODERCN_CONFIG_DIR`) | model, effort (as Qoder; its own accounts, a Qoder CN plan with BYOK) | | Grok Build | `~/.grok/config.toml` (`$GROK_HOME`) | model, effort | | ZCode | `~/.zcode/v2/config.json` | provider (magpie's models in ZCode's picker) | | WorkBuddy | `~/.workbuddy/models.json` (`$WORKBUDDY_CONFIG_DIR`) | provider (magpie's models in WorkBuddy's picker) | | CodeBuddy Code | `~/.codebuddy/models.json` (`$CODEBUDDY_CONFIG_DIR`), `settings.json` | model (magpie's models in its list; the session model) | | T3 Code | `~/.t3/userdata/settings.json` (`$T3CODE_HOME/userdata`) | provider (a `magpie` provider instance on Claude Code, magpie's models as its custom models) | | OpenHanako | `~/.hanako/provider-catalog.json` + `agents//config.yaml` (`$HANA_HOME`; its local API while it runs) | model (the primary agent's; magpie's models as a provider) | | AtomCode | `~/.atomcode/config.toml` (`$ATOMCODE_HOME`) | model, effort (a `magpie` provider account, one model table per catalog model as its own sign-in writes) | | Snow CLI | `~/.snow/profiles/magpie.json` (`$SNOW_CONFIG_DIR`), made the active profile and copied to `config.json` | model (the user's other profiles' models too; Snow App takes the profile in with "Sync Snow CLI API config") | | Alma | Alma's local API (`localhost:23001`, while Alma runs; alma-server's data in `$ALMA_DATA_DIR`, `$XDG_DATA_HOME/alma` or `~/.local/share/alma` on Linux) | model (Alma's default; magpie's models as a provider), and Image Generation's model when it is Auto: the one magpie draws with | Provider-scoped agents (OpenCode, MiMo Code, Pi, OmO, Aside, Goose, Crush, omp, Hermes Agent) take `provider/model`. Only agents that are installed or configured are shown. ### Notes on some agents #### Reasonix Studio Reasonix Studio is detected from its desktop installation or a native Go `reasonix` 2.x or 1.39.x CLI; historical npm wrappers and a shared config alone do not count. Tested with [Studio 2.24.0](https://github.com/esengine/DeepSeek-Reasonix/releases/tag/studio-v2.24.0). Pick the Executor model in the app, or use `magpie reasonix magpie/deepseek/deepseek-chat` (`magpie reasonix magpie/group/code` for a routing group). Select Plan independently with `magpie reasonix planner magpie/deepseek/deepseek-chat`, or use `magpie reasonix planner off` to disable the separate planner. `magpie reasonix effort high` sets an advertised Executor reasoning level. `magpie reasonix default` restores the previous Executor selection; `magpie reasonix planner default` restores Plan. Magpie's provider and private `.env` key are removed when neither role needs them. Restart Studio for new sessions; project/session overrides still take precedence. Other providers and credentials are preserved. A non-managed provider named `magpie` is a conflict, reported without overwriting it. Studio and the native CLI share these files, so updating only Studio does not isolate their settings. The released-client stream/tool contract test is run with `MAGPIE_TEST_REASONIX_CLI=/path/to/reasonix go test ./internal/agent -run '^TestReasonixStudioCLIIntegration$' -count=1`. It uses isolated settings and a local test upstream, with no vendor credentials. #### VSCodium Chat VSCodium's Chat features are disabled by default. To use the Chat model picker with magpie, set `"chat.disableAIFeatures": false` in VSCodium's settings and add the `defaultChatAgent` and `trustedExtensionAuthAccess` entries required by [VSCodium's Copilot guide](https://github.com/VSCodium/vscodium/blob/master/docs/ext-github-copilot.md) to VSCodium's `product.json`. The guide also explains how to install a compatible GitHub Copilot Chat extension, since the Open VSX registry does not normally provide Microsoft's extension. Restart VSCodium, or run **Developer: Reload Window**, after changing these files. Magpie's models then appear under the `magpie` custom endpoint group. #### Zed-compatible Agent paths Magpie can use a Zed-compatible fork or installation whose executable or configuration directory is different from the upstream defaults. Set these variables before starting Magpie: ```sh export MAGPIE_ZED_BIN=/Applications/ZedG.app/Contents/MacOS/zedg export MAGPIE_ZED_CONFIG_DIR="$HOME/.config/zed" export MAGPIE_ZED_PROCESS_NAMES=zedg,ZedG magpie serve ``` `MAGPIE_ZED_BIN` controls installation detection, `MAGPIE_ZED_CONFIG_DIR` selects the directory containing `settings.json`, and `MAGPIE_ZED_PROCESS_NAMES` supplies comma-separated process names used for restart notices. The existing Zed configuration adapter is reused, so this is intended for forks that keep Zed's `settings.json` and Agent model schema, such as [ZedG](https://github.com/x6nux/zed-globalization). These variables affect the Magpie process in which they are set; put them in the service environment when running Magpie under systemd or Docker. They configure a Zed-compatible Agent on the same machine as Magpie; they do not discover or modify an Agent running on another host. On macOS, if the configured binary does not resolve to a `.app` bundle, Magpie falls back to the standard Zed application locations when authorizing the gateway credential. ## Providers and the gateway Every model an agent can pick is spelled `provider/model` and served by magpie's gateway, so agents never hold vendor keys or vendor URLs. Add a provider, and its models appear in every agent's picker: ```sh magpie presets # the vendors magpie knows, grouped: vendors, relays, local magpie provider add deepseek sk-… # a preset needs only the key magpie provider add ollama # local servers need none magpie provider add "My Relay" url=https://relay.example.com/v1 key=sk-… models=gpt-5.5,claude-sonnet-5 magpie providers # host, key, exposed models, who uses what magpie provider deepseek # one provider in detail magpie provider models deepseek # re-fetch the vendor's list (add ids to choose which to expose) magpie provider models deepseek +deepseek-v4 -deepseek-chat # expose one more, take one out; the rest stay magpie provider models deepseek a b c # the whole list of models to expose, replacing it (all: the default) magpie provider refresh deepseek # re-fetch it, and drop picks it no longer has (the TUI: m) magpie provider test deepseek # one tiny request per API, with latency magpie provider key deepseek sk-… # replace the key magpie provider rm deepseek magpie models # the catalog agents see magpie claude deepseek/deepseek-chat # use it ``` Custom providers take `url=` (an OpenAI-compatible base), `anthropic=` (an Anthropic-compatible base), or both, plus `responses=` when the vendor has a separate Responses endpoint, `catalog=` to borrow a models.dev list, and `models=` to name the models to expose. Anything a preset does not know can be overridden the same way. A model whose vendor answers that it is deprecated or retired (OpenCode Zen's 410 "Model exo-free has been deprecated.") leaves that provider's models in `magpie models`, `/v1/models` and every agent's list, even while the vendor's own list still names it. It comes back once a request for it as `provider/model` is answered, or after a week. `magpie provider set header.=` sends a header of your own on every request to a key+URL provider (an empty value removes it; signed-in accounts ignore them). It replaces a header of the same name magpie would send, except `anthropic-beta`, which is a list: your betas are added after the ones the agent asked for that request (Claude Code's, its 1M context's `context-1m-2025-08-07` for a `[1m]` model, fast mode's), each once. A beta the provider turns away (`Unexpected value(s) … for the anthropic-beta header`) is dropped from the retry and from then on, yours as well as the agent's; any other of yours is always sent. The beta of the agent's own sign-in (`oauth-2025-04-20`, which Claude Code signed in to claude.ai asks) never goes, as the sign-in never does; one you set yourself does. `magpie usage` also lists **upstream provider keys** to help check upstream bills. Each request records the fingerprint and saved name of the key that actually served it, including image calls and account/key failover. The CSV adds `provider_key_id` and `provider_key_name`. JSON uses `providerKeyId` and `providerKeyName`, distinct from gateway caller keys. System One calls use the same attribution. No raw credential is stored in usage records. Rotating the provider's first key does not move old usage to its replacement; deleted keys keep their historical identity. Older records appear as **key not recorded**, never inferred from today's configured key. These are upstream credentials, not keys clients use to call Magpie. The CSV's `cache_write_tokens` is still every cache write; the last two columns, `cache_write_5m_tokens` and `cache_write_1h_tokens`, split it by how long Anthropic keeps it (a record from before the split is all 5-minute). The app shows the split on a request's Cache and the usage tiles' tooltips. **Output speed** (tok/s on the Usage and Routing pages, `magpie usage`) is output tokens over the time after the first token, summed per request: the tokens of every timed request over the sum of their windows, so concurrent subagents never add up. A reply that reasoned counts only its answer, from its first text (`usage.DecodeOf`): hidden reasoning is written before the stream shows anything, so counting it in a window that starts after it read gpt-6.1-sol at hundreds of tok/s. A reply that reasoned and then wrote only tool calls, one not streamed, and a burst (under 100 ms, or over 10,000 tok/s) tell no speed. So does a reply its vendor held back and let go in a burst at its end, however long the burst took to come: one whose content came in under a quarter of its window (`flow_ms`, from a tenth of its bytes to nine tenths, scaled to the whole), or that reasoned and whose answer would have come over 20 times faster than its reasoning came before its first text (its text came with the burst). The time a burst takes to come is not the time it took to write, and that time isn't seen. History is read the same way, as it keeps the reasoning, the first text and the flow; a record from before the flow was kept is told by its reasoning's pace alone. It lists **accounts** too: each Codex, Claude or other subscription account's tokens and cost, by the account that actually answered — the one that took over after a failover, the one `X-Magpie-Account` pinned. The account is named as the Routing view names it (its email or login, never a token); the CSV adds `provider_account` (JSON `providerAccount`), `magpie usage --csv --account ` keeps one account's calls, and the app's Usage page has an Accounts list and an Account filter on Requests. An older record names the account its call went out as when its host says so (`chatgpt.com as dee@example.com`); otherwise it appears as **account not recorded**, never inferred from today's sign-in. OTLP export never carries the account. One magpie can serve several computers (an office one, a personal one): share it on the network (Settings → Share on local network), and on each other computer add it as a **Remote magpie** — in the app's Add sheet, on the TUI's Providers page (`a`, then its address and key; `w` changes the address, `m` fetches its list again), or `magpie provider add remote-magpie sk-magpie-… url=http://192.168.1.20:3425 id=office`. Each computer's own magpie still wires its agents, while the providers, routing groups (`office/group/…`) and usage are the shared one's. A request goes on in the API the agent spoke — Anthropic Messages, Responses, Chat Completions, token counting — and a model the shared magpie's provider serves on another API only is turned into that API once, never on both computers. Its list is the models the shared magpie's agents are shown, each named with its provider there (`Claude Sonnet 5 · Relay A · office`) — the ids stay ids (`office/relay-a/claude-sonnet-5`), only these labels carry the names. Its image and video models are discovered automatically and served through its image generation/edit and video creation/status/content APIs. System One is also configured automatically at its `/v1/systemone`: refresh the remote provider's model list to discover its decision models, including Jev, Clef and models with custom names. These appear as decision models rather than chat models. A bare provider id such as `office` asks the first listed decision model; it does not pick another model to satisfy a key's limits. If the key permits only another model, use its full `office/provider/model` id to avoid a 403. Embeddings and rerank requests use the remote's corresponding APIs, and its exposed retrieval models are kept when fetching its list. These remain in agent model lists as on the remote; being listed does not mean an embedding or rerank model can hold a conversation. The remote must serve the requested API and model, and allow the supplied key to use them. Responses uses HTTP, including SSE, rather than WebSocket. An older remote without decision-model discovery still serves its existing APIs; update it and refresh the list to expose System One models here. Its quotas show too: the Usage page, the menu bar and `magpie quota` list the shared magpie's cards named with it (`Codex · office`, id `office/codex`), as that magpie last read them — only the computer holding the sign-ins asks the vendors, and it answers from what it has kept (`GET /v1/magpie/quotas/cards`). A card's refresh here has it read that card once more (`POST /v1/magpie/quotas/refresh`, at most once in 30 seconds a card); until it has read anything, the remote's one card says so. Their history is shown with its own. A Codex reset or a check-in is pressed on that computer, not here. Codex's native image tool first asks the provider that served its conversation turn for the requested image model. If that provider does not list the model, or the turn cannot be identified, it uses Settings → Images → Image generation (including Automatic). Off disables this fallback, not a matching model on the turn's provider. An explicit `provider/model` keeps its destination; an upstream image error does not retry through the setting. Magpie Image MCP is not required. WorkBuddy (China)'s daily check-in (签到) is pressed from the app's Usage card (*Check in now*), `magpie accounts checkin`, or `c` on the TUI's Usage page, which checks in every account signed in on this computer at once; its WorkBuddy lines say how today's went. A shared magpie's accounts are checked in on that magpie, from its own app, TUI or CLI. A plugin whose `auth` hook has `checkin` checks its own accounts in the same way: once a day while its switch under **Settings → Usage → Plugins** is on, with the result on each account's Usage card. A check-in the vendor wants a captcha for is shown as such (check in in the vendor's app); magpie never solves captchas. See [provider-plugins.md](subsystems/provider-plugins.md#a-plugins-daily-check-in). The gateway issues **gateway keys** for clients, separate from a provider's upstream API keys. Turn on **Settings → Share on local network**, then open **Gateway → Gateway keys → Add gateway key**. This block appears only while sharing is on. Create a named key for each client and copy it from its row. To move from another gateway without touching its clients, enter the key they already send in **Your own key (optional)** (8–256 characters, no spaces, not starting with `sk-magpie-`, unlike any other key); left empty, magpie makes one. The list shows only its last four characters, and it is counted, limited and restricted as any other key, from this computer too. Rename, disable, rotate or remove keys independently; rotation and removal ask for confirmation. Rotation keeps the name, enabled state and usage history; other keys are unchanged. While sharing is on, **Gateway → Connect** offers loopback and shared addresses, plus enabled gateway keys, for all connection examples. With sharing off, Connect keeps the original **API key** field and local `magpie` token, without a gateway-key picker. Its arbitrary local option is **This computer**, distinct from the named **Magpie** key. For a headless gateway, use the CLI before exposing the port: ```sh magpie gateway-key add "Remote laptop" # prints the new credential once magpie gateway-key add Family --key - # keeps a key clients already send, read from stdin magpie gateway-key list # ids, names, enabled state and masked keys magpie gateway-key rotate # prints the replacement; identity stays the same magpie gateway-key remove # revokes remote access magpie gateway-key limit week --tokens 2m --cost 5 # its own limit magpie gateway-key limit # limit, used, left and reset magpie gateway-key limit off # no limit magpie gateway-key models openai/gpt-5 anthropic/* # only these models magpie gateway-key models all # every model magpie gateway-key accounts codex/me@example.com # only these accounts magpie gateway-key accounts all # every account ``` Each gateway key can have its own **limit**: a token total, an estimated cost in US$, or both, per day, week or month (calendar windows in local time: from midnight, from Monday, from the 1st). Set it with **Limit** on the key's row (saved with Save) or `magpie gateway-key limit`; the row shows what the key has used, what is left and when it resets. Tokens counted are a call's uncached input, output and cache writes, plus cache reads when **Count cache reads too** is on. Cost is an estimate at the Usage page's prices; a call with no known price adds none. A call counts in the window it started in. Once a key is spent, its requests are refused before any provider is asked, with a 429 in the API's own error shape that names the key, the limit and the reset time, plus `Retry-After`; other keys are unaffected. The counts are read from the usage log, so they survive a restart. A request in flight holds a reservation (its body's size in tokens plus the key's mean output per call), so requests sent at once overshoot by about one call; a streamed reply is settled when it ends with the usage its vendor reported. A key can read its own status with `GET /v1/magpie/limit`. Requests from this computer that send no gateway key are not limited; a gateway key used from this computer is. A gateway key can also be held to some **models** (#882): pick them with the **All models** badge on the key's row, or run `magpie gateway-key models openai/gpt-5 anthropic/*` (`all` takes the restriction off). A pattern is `/` or `/*`, matched against the provider that serves the call, so a bare model name is resolved first, or a routing group, `group/` (`group/*` for every group). A key that names a group may use the group with every member in it, though not those members asked for by name. Such a key sees only its models in `/v1/models`, `/v1/codex/models` and the Anthropic and Gemini lists, a routing group it doesn't name only when it may use every member, and is refused any other model with a 403 in the API's error shape before a provider is asked; a fallback it may not use is skipped. A key with no models listed may use every model. A gateway key can also be held to some **accounts** (#905): the accounts and keys its requests may use, picked in the same menu right after their provider's models — an account by who is signed in, a key by its fingerprint — or with `magpie gateway-key accounts codex/me@example.com openai/` (`all` takes the restriction off). The list holds a key to some accounts **of the providers it names**: a provider it names no account of, the key uses as it always did. The list keeps an account by its stable id, kept through renames, shown by who is signed in; one gone later — a key rotated, an account signed out, an account of an agent that keeps no logins renamed — is kept as it is, matching nothing, so the key is held closer, never wider. A routing group the key names is its members' through the accounts it may use only: naming a group is not naming its accounts, and magpie's own calls for the key — a web search, a picture described, a Codex title — are of the models the user picked, but their spend lands on an account, so the accounts hold them too. When every account or key behind a model is one the key may not use, the request is refused with a 403 in the API's own error shape naming the model and the accounts it may use, before a provider is asked — after a usage cap's 429 and before an account barred for the model's — and `/v1/models` lists the key only the models an account or key it may use serves, the Routing view's left-out list marking what the key held out. A key with no accounts listed may use every account, as keys always did. While LAN sharing is enabled, or `MAGPIE_ADDR` puts the gateway on the network (a host other than loopback or `localhost`: `0.0.0.0` in the Docker image, a server's address), remote requests require an enabled gateway key sent as Bearer, `x-api-key`, `x-goog-api-key` or `?key=`; one without gets 401. A request is remote when its peer isn't loopback, which includes Docker's port publishing (the peer is the bridge, not the host's loopback) and a WSL distro under NAT. Loopback remains permissive: any token works, including a stale or disabled gateway key. Only a valid, enabled key is attributed to its named identity. Neither shared nor on the network, a remote request is refused (403). A `MAGPIE_ADDR` pinned to one interface's address asks a key even of this computer's agents calling that address; `0.0.0.0` with agents on `127.0.0.1` doesn't. Sharing listens on every interface, unless `MAGPIE_ADDR` names a host of its own: `MAGPIE_ADDR=127.0.0.1:3425` behind Tailscale Serve, or one interface's address, stays where it is while shared, and what reaches it from elsewhere still needs an enabled gateway key (#1112). **Codex on another computer** (#1281) can name the shared magpie its model provider, with a gateway key, and read magpie's models in Codex's own catalog shape from `GET /v1/codex/models`. `/v1/models` is OpenAI's list, which Codex can't read, and `/backend-api/codex/models` is the ChatGPT backend's, for a Codex signed in to ChatGPT on the gateway's own computer; a gateway key gets a 401 there. ```toml model_provider = "magpie" model = "provider/model" [model_providers.magpie] name = "magpie" base_url = "http://192.168.1.20:3425/v1" model_catalog_url = "http://192.168.1.20:3425/v1/codex/models" env_key = "MAGPIE_API_KEY" # the gateway key wire_api = "responses" supports_websockets = false ``` The list is the models the shared magpie shows Codex (its Agents page picks), each as the `model_catalog_json` magpie writes for a Codex on its own computer describes it, and only the models the key may use. Codex 0.161 reads it by default; 0.160 needs `[features] api_key_model_discovery = true`. Codex reads at most 1 MiB of a catalog, and every entry carries Codex's prompt, so past about 125 models the list keeps the first that fit and the answer's `X-Magpie-Left-Out` header says how many it left out: pick fewer for Codex, or hold the key to fewer. A request that reaches loopback through a proxy or tunnel on this computer (Cloudflare Tunnel's `cloudflared`, ngrok, Tailscale serve or funnel, frp's HTTP proxy, Caddy, nginx) is someone else's, and is answered as one from another machine (#1022): it carries a forwarding header (`Forwarded`, `X-Forwarded-For`, `X-Real-IP`, `Cf-Connecting-IP`, `True-Client-IP`, `Tailscale-User-Login`), which no agent sends. Without sharing it is refused; with sharing, or a `MAGPIE_ADDR` on the network, it needs an enabled gateway key, as from the network. This covers every route: the model APIs, quotas, MCP sign-ins. To serve magpie through a Cloudflare Tunnel, turn on **Settings → Share on local network**, add a gateway key for each client, point the tunnel's service at `http://127.0.0.1:3425`, and give clients the tunnel's address with that key. A tunnel that forwards bare TCP (frp's `tcp`, `ssh -R`, socat) adds no header and can't be told from an agent here: point it at this computer's LAN address with sharing on, not at loopback. A proxy here that signs its clients in itself can be trusted with `MAGPIE_TRUST_PROXY=1`, which keeps what it forwards local, as before. **Usage → Overview → Gateway keys** groups calls by the client's key, never the provider's credential. **Usage → Requests** offers the same filter; CSV includes `caller_key_id` and `caller_key_name`. Deleted keys keep their history. The usual local `magpie` token and older records stay unattributed. Caller attribution includes chat, images, video creation and System One. Provider attribution stays independent; CSV puts `provider_key_*` before `caller_key_*`. An existing LAN key becomes **Magpie** without changing the credential. `lanKey` remains in settings for older Magpie versions. Disabling or removing the default key replaces that mirror with a random non-empty revoked value; rotation does not re-enable it. The key store records migration completion, even if `lanKeyId` cannot be saved, so reads do not keep retrying that write. A migration write failure is logged without preventing gateway startup, CLI key management, or the Settings and key-list pages from opening. If settings are read-only, changing the default key fails without changing it: make `settings.json` writable and retry so older versions cannot keep accepting its old credential. Independent named keys remain manageable. Gateway credentials stay in `~/.config/magpie/caller-keys.json` (XDG-aware, mode `0600`), never in usage records or list responses. Baidu Qianfan's [Token Plans](https://cloud.baidu.com/doc/qianfan/s/Dmrabu8b6) are available as `baidu-qianfan`: a personal (个人版) and an enterprise (企业版) plan and pay as you go, each with its own Chat Completions, Responses and Anthropic Messages endpoints, and a key that works only on its own plan. Add it with `magpie provider add baidu-qianfan ` — the id it carried its first day, `qianfan-token-plan`, is taken too. The plans serve no model list, so the preset carries their documented models; pay as you go serves its own at `/v2/models`. ### Plugins A subscription magpie doesn't sign in to itself can come from an [OpenCode](https://opencode.ai) provider plugin or a [pi](https://github.com/earendil-works/pi) package: the npm packages OpenCode users install to sign in to a plan (their `auth` hook), and the pi packages that register a provider (`pi.registerProvider`), work in magpie as they do there. magpie runs them on [Bun](https://bun.sh), downloaded the first time a plugin needs it, and the plugin signs in, lists the models and makes each request; magpie serves them to agents like any provider's. ```sh magpie plugin add opencode-gemini-auth # an npm package, or a path to a plugin of your own magpie plugin add pi-antigravity # a pi package, the same way magpie plugin # the plugins, what each signs in to, and whether you are magpie plugin login google-plugin # its sign-in: the method, its questions, the browser or a key magpie plugin logout google-plugin magpie plugin off opencode-gemini-auth # on brings it back; rm removes it; update updates them all ``` A provider id magpie already has (google, openai, anthropic) is `-plugin`. In the app, Settings → Plugins adds and removes them, and the providers they sign in to are in Add provider → From plugins. Sometimes two installed plugins sign in to the same provider id, for example a third-party plugin and your own copy of it. One of them serves that id: the one you picked, or else the one added last. Both plugins stay listed, and each row in Plugins › Installed says which one serves the provider. The other row has **Use for **, which switches the provider to it. `magpie plugin` prints the same thing, and ```sh magpie plugin use ~/dev/my-acme-auth acme # this plugin serves acme from now on ``` makes the pick. Each plugin can still be switched off or removed on its own. A package is pi's when its `package.json` has a `pi` manifest, the `pi-package` keyword, or depends on `@earendil-works/pi-coding-agent`. magpie installs pi beside it and loads it with pi's own loader; the providers it registers are what magpie uses, and its commands, tools and renderers are left alone. Its sign-in runs as in pi: an OAuth login's questions and pages, and the dialogs and pickers it shows (`ctx.ui`, its terminal components too), are asked in magpie's sign-in; an API key works as well. pi's own files (its `auth.json` and what a package keeps) are in `pi/` under magpie's config directory, not your `~/.pi`, unless `PI_CODING_AGENT_DIR` says otherwise. Requests reach the package as pi sends them, and its replies keep their thinking and tool calls. #### For plugin authors A plugin is an OpenCode plugin or a pi package; nothing magpie-specific is needed. For an OpenCode plugin magpie reads a few more fields, which OpenCode ignores: - **The provider's icon**: `icon` on the `auth` hook, or `"magpie": { "icon": "…" }` in the plugin's `package.json` (for every provider it signs in to that names none). An `https://` URL of a picture on a public host, which magpie fetches once and keeps, or a `data:image/…` URI; PNG, JPEG, GIF, WebP, ICO or SVG, at most 1 MB. Anything else is ignored, and the icon the plugin market lists for the plugin is shown instead. The `package.json` icon is also the card's picture under Discover → Unofficial, on GitHub for a repository tagged `magpie-plugin`. - **An API key's field**: a `type: "api"` method's `label` titles the key's field, as OpenCode's dialog does (one that only says "API key" reads " API key"), and its `placeholder` is the hint inside the field (and after the question in `magpie plugin login`). The method's `prompts` are asked first, as in OpenCode, and reach `authorize(inputs)`. ```js export const LemonPlugin = async () => ({ auth: { provider: "lemon", icon: "https://lemon.example/icon.png", // or "data:image/svg+xml;base64,…" methods: [ { type: "api", label: "Lemon API key (lemon.example/keys)", placeholder: "sk-lemon-…" }, ], }, }) ``` In TypeScript, `icon` and `placeholder` aren't in OpenCode's types: build the hook as a variable (or cast it), or put the icon in `package.json`. #### Gateway middleware A plugin can also be gateway middleware: `onRequest(body, ctx)` sees each request an agent sends before it is routed, `onEvent(event, ctx)` each event of a streamed reply and `onResponse(body, ctx)` a whole one, all in the agent's own API. A hook returns the changed object, or nothing to leave it; `onEvent` returns `null` to drop an event, and `ctx.reject(status, message)` turns a request away. Declare it with `"magpie": { "middleware": "./mw.js" }` in `package.json`, or add a single `*.middleware.js` file: ```js // alias.middleware.js — magpie plugin add ./alias.middleware.js export const events = ["message_start"] export function onRequest(body, ctx) { ctx.state.asked = body.model if (body.model === "fast") body.model = "deepseek/deepseek-chat" return body } export function onEvent(ev, ctx) { ev.message.model = ctx.state.asked // the agent sees the name it asked for return ev } ``` Middleware runs inside the gateway in [moejs](https://github.com/Calcium-Ion/moejs), a JavaScript engine written in Go (about a microsecond a streamed event), not on Bun: no timers, `fetch` or Node APIs, and imports only of files beside it. A hook that throws or takes too long (250 ms a request, 50 ms an event) leaves what it was given as it was. Settings → Plugins shows each middleware's calls, time and failures. The [plugin guide](https://usemagpie.ai/docs/plugins#middleware) has the rest. `ctx.options` is the middleware's entry's `options` in `plugins.json`: set them with its **Options** button in Plugins › Installed, or ```sh magpie plugin options model-map '{"mapping": {"fast": "deepseek/deepseek-chat"}}' magpie plugin options model-map off # clears them ``` The name can be a package's short name (`model-map` for `@magpie-community/middleware-model-map`). A package's `"magpie": { "options": {…} }` is the example the editor starts from. Ready-made middleware, under Plugins › Discover › Gateway middleware (`@magpie-community/middleware-`), mostly New API's channel settings with the same JSON: `param-override` (`param_override`), `model-map` (`model_mapping`), `system-prompt`, `word-guard` (sensitive words) and `think-tags` (strip `…`, or `thinking_to_content`). A middleware's entry in the community `registry.json` has `"kind": "middleware"` and no `providers`. ### What a model costs A call is counted at its **effective price**: what you set for that provider and model if you did, otherwise what the provider's own catalogue lists, and otherwise what models.dev lists for the model's maker. Out of the box that last one is the whole story, and it is the wrong number for any provider that does not charge list price — a relay reselling at a discount or a multiplier is counted at whatever the model's maker charges. Say what a provider actually charges, and the usage ledger and the session totals use that: ```sh magpie model price relay-a/gpt-5.5 # what it is counted at, and where that came from magpie model price relay-a/gpt-5.5 0.12,0.60,0.01,0.15 # input,output,cache read,cache write magpie model price relay-a/gpt-5.5 --reset # take your price off this model magpie model prices # every model you priced ``` The four numbers are USD per million tokens. All four are asked for, because a price missing one would understate the rest of every call; `0` is a model served at no cost, which is a price, not the absence of one. Decimals take a point or, in the app's boxes, a comma (`0,25`). A **Kimi Code** membership's models are counted at the Kimi API model each one is, not at the $0 models.dev lists them at for the plan: `k3` and `k3-256k` at `kimi-k3`, `kimi-for-coding-highspeed` at `kimi-k2.7-code-highspeed`. `kimi-for-coding` is K2.8 Preview, which the API doesn't sell, so it has no price until you give it one (`magpie model price kimi-code/kimi-for-coding …`). A **fifth number** is a 1-hour cache write's price. Anthropic bills a cache write kept for 5 minutes at 1.25× input and one kept for an hour at 2× input, and its usage says which were which (`cache_creation.ephemeral_5m_input_tokens` / `ephemeral_1h_input_tokens`, from the API and from Claude Code's transcripts). The fourth number is the 5-minute price; the fifth, when not given, is 2× input for a Claude model — Anthropic's rule. Any other model has no 1-hour price unless you give one: a 1-hour write it reports is counted at the 5-minute price, and neither `magpie model price` nor the app's boxes show a 1-hour price for it. A call recorded before magpie kept the split counts all its writes at the 5-minute price, as it did. A **long-context price** is what the whole request costs once its prompt is over a size, as OpenAI bills gpt-6-astra, gpt-6.1-sol and gpt-6-luna over 272K input (2× input and cache, 1.5× output): ```sh magpie model price openai/gpt-6-astra 10,50,1,12.5 --tier 272k 20,75,2,25 ``` The size counts the prompt the way OpenAI does: uncached input + cache reads + cache writes, and the tier applies when that is **over** the size (272,000 is not, 272,001 is). Then every part of the call — input, output, cache — is at the tier's price, not only the tokens past the size. `--tier` may be given again for another size. The built-in prices carry a tier only where models.dev lists one (`cost.tiers` of type `context`), as it does for those three. Session and day totals sum many calls, so they are counted at the base price; the ledger and each request are counted at the tier they reached. In the app, a model's price row has a quiet **Long-context price** row under it, shown with the list's tier greyed in, or behind a link where there is none. The order a price is looked for in is: **the price for this model → the price for `/*`, which covers every model of that provider → what the provider's own catalogue lists → what models.dev lists for the maker.** `--reset` removes the first, and says so when a `/*` price is still in force; reset that one by name to take it away too. A price is **one provider's tariff for one model**, not the model's own: the same model through two providers is two prices, and each keeps its own. Nothing an agent can see changes. The model list, the agents' own settings and the pickers that choose a model for a background task — an image, a web search, a description — all still work from the catalogue; only the cost reports read the effective price. Two things worth knowing. The ledger and the session totals re-price when they are read, so adding or changing a price restates earlier figures: they are estimates at the effective price, not settled charges. And a price is per provider and model. Records now identify upstream API keys, but a provider charging different tariffs per key still cannot be costed exactly from a single provider-wide price. ### What a model takes A provider that serves a model models.dev does not list, or lists at the wrong size, has a window and a reply limit magpie cannot know. Say what they are: ```sh magpie model context relay-a/gpt-5.5 262144 # the window a request may hold, or 1m magpie model output relay-a/gpt-5.5 131072 # the most a reply may hold, or 128k magpie model context "relay-a/*" 200000 # every model of that provider magpie model output relay-a/gpt-5.5 --reset # take your limit off this model ``` The `*` is quoted because zsh treats a name it cannot expand as a command that failed, rather than passing the name on as bash does. Both are looked for in this order: **this model → this provider's `*` → the provider's own list → models.dev.** `--reset` removes only the value this model has of its own. A provider you keep unlisted, or switch off, takes them like any other: the numbers are kept, and are what its models take once it is serving again. A window is a number agents are shown **and a routing input**: at 95% of the window a request held on a routing-group member moves to one that takes more, so overstating a window makes that move happen too late. A reply limit is advertised in `/models` and is what a group advertises the smallest of; the gateway does not itself cap a reply by it. Saving a window or a reply limit writes that number into the model lists magpie keeps in the agents' own files — Pi's `contextWindow` and `maxTokens`, OpenCode's `limit`, and Crush's, droid's, Cline's, Qoder's and Zcode's — which an agent reads at start-up. A session already running therefore keeps the window it began with, while the gateway's own `/models` and every request from then on are right at once. ### The name a vendor knows a model by A relay often serves a model under an id of its own — a prefix it namespaces with, a dated name, a `-latest` that is not what models.dev calls it. Say which name to ask for: ```sh magpie model wire relay-b/model-2 # what the vendor is asked for magpie model wire relay-b/model-2 vendor-c/model-2-preview # ask for it by this magpie model wire 'relay-b/*' 'vendor-c/*' # every model, * being the model magpie model wire relay-b/model-2 --reset # ask for it by its own name again magpie model wires # every name your vendors are asked for models by ``` Quote the arguments with a `*` in them: zsh reads a bare `*` as a glob and answers `no matches found`. In the name, `*` stands for the model itself, so `'relay-b/*' 'vendor-c/*'` sends `vendor-c/model-3` for `model-3` and `vendor-c/model-2` for `model-2` — one name for a relay that namespaces its models, each still asked for by its own. A name with no `*` in it sends every model of that key under that one name, which is the right answer only for a relay that does serve them all alike. The model's own key wins over the provider's, and `--reset` takes away only the one it is given, so resetting `relay-b/model-2` while `'relay-b/*'` is set leaves the provider's name in force — the CLI says which of the two is in force after every change, naming the models a name for the whole provider leaves to their own, and `--reset` over a name that was never given says so rather than ticking a removal that took nothing away. Only the request that goes out carries that name, in the model field of a chat, Responses or Anthropic Messages request and of an Anthropic token count; image requests are left with the name magpie knows the model by. Gemini CLI and Antigravity sign-ins go on Code Assist, and there the model an effort picks is a variant of the model magpie knows — `gemini-3.7-flash` at `high` is sent as `gemini-3.7-flash-high` — so a `*` in the name is that variant: `'antigravity/*' 'vendor-c/*'` asks for `vendor-c/gemini-3.7-flash-high` and `vendor-c/gemini-3.7-flash-low` each by its own, as it does everywhere else. A name with no `*` in it, or one given for `antigravity/gemini-3.7-flash` itself, is that one name at every level. Everything else keeps the name magpie knows the model by: the catalog agents pick from, the routing groups' membership, `GET /models`, and what a call is recorded and priced as. What the vendor's own reply said answered is kept beside that, in the ledger's `served_model`, which is what it is for, and it is compared with the name the vendor was asked for — so a model answered under the relay's own id is not read as a swap. That is against the names in force when the ledger is read, since a record keeps what the vendor answered and not the name the request went out under: naming a model after the call re-judges that call, which is then left reading as a swap. What a provider *supports* — whether it takes a temperature, which reasoning levels fit — is still asked about the model magpie knows, so a rename upstream does not change how magpie behaves towards the model. An upstream name is **one provider's**, not the model's: another provider serving the same id is asked for it under its own name, or this one. The model it is given for has to be one that provider serves — `/*` is the way to say one for every model — because a name for a model magpie would never ask the provider for is not a name of its own: the model is asked for by the name magpie knows it by, and nothing anywhere would say the name given for it is not the one in force. A relay that serves a model under an id of its own *and* lists it does get the name; what is refused is a model the provider's list does not have at all. The model test uses the name too, so a relay that only knows its own ids does not report a working model as broken. An image model is the exception that follows the rule above: it is tested by the name magpie knows it by, on the images API and on the chat it falls back to, because that is how a drawing is asked for. ### Routing groups A routing group is several models, from one provider or many, that an agent picks as one: `group/`. The gateway routes each request over every member's keys and accounts together. A model two of your providers serve under the same name becomes a group on its own; the Routing view in the app and `magpie group` make any other: ```sh magpie groups # yours, then those magpie found magpie group add "Opus anywhere" models=claude/claude-opus-5-5,copilot/claude-opus-5.5 routing=order stays=session magpie group opus-anywhere # one group, its models in order magpie group set opus-anywhere models+=openrouter/anthropic/claude-opus-5.5 routing=usage magpie group set opus-anywhere models-=copilot/claude-opus-5.5 magpie group copy opus-anywhere "Opus test" # the same models, routing and rules, to change on its own magpie group rm opus-anywhere # one magpie found is hidden; magpie group restore brings it back magpie claude group/opus-anywhere # use it ``` `routing=` is `smart` (the default: of the subscriptions with quota to spare, the one whose allowance renews soonest first), `order` (the first model until it can't answer, then the next), `rotate` (each turn to the next member), `usage` (least used first) or `pace` (weekly pace: the account with the most of its week left per hour until it renews first, so less of a week is lost at its reset). A key has no allowance to weigh, so it goes by its order or by what magpie sent it lately — except a sub2api key its owner gave a 5-hour, day or 7-day limit: with the provider's Balance URL set to the relay's `/v1/usage`, its card shows those windows and routing weighs it by them as it does a subscription, resting it till the window renews once the relay says `api key 7天限额已用完` — or till its windows are next read short of full, its limit raised or its usage reset. A key bought as a plan — Kimi Code, GLM Coding Plan or Z.ai, MiniMax Coding Plan, OpenCode Go, Command Code — is weighed by its plan's windows the same way. With one key or account, the Routing page still shows what it has left. `stays=` is how long a conversation stays with the key or account that answered it: `auto` (the default, while the vendor's cache of it is worth keeping), `session`, `turn` or `off`. `models=` replaces the whole list, in order; a bare model id works when only one provider serves it. The Routing page's Requests list defaults to the time-ordered By request view. Choose By session to group calls by the agent's session ID; the page remembers your choice across reloads. Codex title helpers with an explicit parent or fork source join their originating chat, retaining their title badge and contributing to its cost. Titles without ancestry and ordinary forked chats stay separate. Codex chat names come from its local name index and follow renames. Other agents' sessions (Claude Code and the rest the Sessions page reads) take the name the Sessions page shows for them, read from their own files on this computer. Unknown or remote-only names fall back to the ID; the full ID remains in the heading tooltip. Expand a session to see each request. Each request and session shows its estimated cost at the effective model prices, including cache reads and writes. Session totals cover the listed requests only (the live trace or the selected day's retained history), and a `+` marks a partial estimate. Calls without a session ID are listed separately; old history without token tiers, or a model without a known price, shows `—`. Opening the Providers page automatically checks Alma, CC Switch, Claude Code and Codex for local provider configurations that can be imported. With no providers yet, a hint shows how many were found and which apps they came from; otherwise a small **Import local configurations** link sits beside **Add provider**. Both open the existing picker with only the offered configurations selected; nothing is added until you confirm the selection. **Ignore** remembers the offered configurations on this device across window restarts; new or changed configurations can be offered again. **Add provider → Import…** still includes ignored configurations. Discovery runs in the background, at most once a minute when opening or refreshing the page, and refreshes after an import. Configurations already in magpie, entries that cannot be imported, providers turned off in their source app, and ID collisions that cannot join as another key are excluded from the hint. The latter two remain available through manual import. Counts refer to configurations in source apps, which may include the same provider in more than one app. Only app names and opaque configuration fingerprints reach the automatic hint; ignored fingerprints are kept in local browser storage. The app's Import from other apps dialog can copy providers from Claude Code's `settings.json` (`CLAUDE_CONFIG_DIR` when set) and Codex's `config.toml` (`CODEX_HOME` when set) into magpie. Codex imports custom `[model_providers.*]` entries with an inline `experimental_bearer_token`, including fixed headers for custom providers in `[model_providers.*.http_headers]` and models from `[profiles.*]` or `model_catalog_json`. Review the entries before importing; subsequent changes to agent settings are not automatically copied to magpie. Entries that point back to magpie or only name an `env_key` are skipped. ### How magpie picks an account When a subscription has several accounts on (Claude Code's or Codex's saved accounts, say), or a routing group has several members, the gateway orders them for each request in these steps. The Routing page shows the result for every request: who went first, and why. 1. **Who can answer.** The account the agent is signed in to, unless it is paused, and the other accounts that are ticked. An account set not to serve the model, or held at its usage cap, is never tried. An account whose plan doesn't list the model (a Free one behind a Plus) is tried only when no other lists it. A Claude account that has to be signed in again stays in the list, but is passed over when its turn comes, without resting. 2. **The routing.** What is left is put in order by the routing the subscription or group uses (below). 3. **Rate limited ones last**, only with *Sink* on: an account a 429 rate limited while it still had quota goes behind every one not rate limited since. 4. **The provider's fallback models** come after its own accounts. 5. **Resting ones last.** An account resting after a failure goes to the back, behind every fallback. It is never dropped: when it is the only one, it is tried anyway. 6. **The conversation's account first.** Once an account has answered a conversation, it is moved to the front for the conversation's next requests, as *Stays* says. In auto mode that is within a turn, and across turns while at least 1,024 tokens of the last request were read from the vendor's cache and it answered in the last 5 minutes. This never brings back an account that is resting, nor one that has used 98% of a window (100% In order). So routing decides who goes first only for a conversation nobody has answered yet. An account that has used 90% goes behind the others for new conversations, but a conversation already on it stays there until 98%. **The routings.** Smart, Least used first and Weekly pace go by the share used of the fullest window that counts the model, the 5-hour window included. Smart and Weekly pace put an account behind the others once a window is at 90% or more, and behind those once one is at 98% or more, each of these groups ordered by the share used. - **Smart** (the default): of the accounts under 90%, the one whose allowance renews soonest goes first, because what it has left is lost at the reset. The biggest window decides: the week, then the 5 hours only when the weeks renew in the same hour. Reset times are compared to the hour, by the clock: 13:55 and 14:05 are different hours, 14:05 and 14:55 the same. An account with no week (Claude Enterprise) goes by its 5 hours. A Claude account magpie knows nothing of yet goes first once, to learn what it has left from the answer; any other unknown account goes after those known. - **In order**: the accounts in their order. The first takes every request until it is used up (100%) or fails. - **In turn**: each turn of a conversation goes to the next account. A new conversation goes to the account the fewest other conversations of the last 30 minutes are on. - **Least used first**: the lowest share used first; then the account magpie sent the fewest tokens lately (half of them stop counting after an hour). An account not known counts as unused. - **Weekly pace**: the account with the highest pace first. Pace is the share of the week left divided by the hours until the week renews (at least 1): 60% left with 20 hours to go is 3 per hour. With two weekly windows that count the model (Opus's own and the general one), the lower pace counts. An account with no week goes by its 5 hours. Accounts whose pace is within a tenth of the highest in their band count as alike, and among them the one magpie sent the fewest tokens lately goes first. A Claude account not known goes first once, as in Smart; another unknown account counts as a fresh week. **Make first** sets the accounts' order. How much that order counts depends on the routing: | Routing | What the order decides | | --- | --- | | In order | Everything: the first takes every request it can. | | In turn | Where the rotation goes next. | | Smart | Only between accounts whose windows all renew in the same hours, or whose shares used are equal. | | Least used first | Only between accounts with the same share used and the same tokens sent lately. | | Weekly pace | Only between accounts in the same pace band that magpie sent the same tokens lately, in practice none since magpie started. | For Claude Code and Codex, *Make first* also signs the agent in to that account, because the account the agent is signed in to is first. With *Keep … signed in to* on, *Make first* changes only the gateway's order, and the agent stays signed in where it is kept. **When an account runs out.** A 429 that says the allowance is used up rests the account until it is back, and the same request goes to the next candidate at once, as long as none of the reply has been sent. How long it rests comes from the first of these that is known: the reset time in the refusal (Claude Code's `usage limit reached|