Files
magpie/docs/subsystems
yetone 0b5b10da2a agent, gateway, gui, tui: a Codex that goes through magpie only to fail over to the user's other ChatGPT accounts says so on the Agents row and keeps to its own models (#1385, shenghsi)
With several ChatGPT accounts on, failover writes openai_base_url into
~/.codex/config.toml so a turn the signed-in account can't pay for goes on
to the next account. With Codex's switch off, the row said only "not
connected", but every request still went through the gateway, and the
gateway's /backend-api/codex/models answer added all of magpie's models
(62 for the reporter).

- agent: Agent.FailingOver means not wired, while the stash's codex.failover
  (or a CC Switch mirror's codex.mirror_failover) base URL points at magpie.
  FailoverSaid is the line the CLI list and the TUI show. CodexOwnOnly is
  true for this machine's Codex when it is failing over and no WSL Codex is
  connected, since a WSL Codex shares the gateway path. Sync ages a
  models_cache.json that isn't tagged with the own-only list.
- gateway: codexModels leaves out provider.CodexListed when
  gateway.CodexOwnOnly (set by main to agent.CodexOwnOnly) says so. The
  ETag and X-Models-Etag carry provider.CodexOwnListTag. Neither tag
  contains the other, so Codex fetches the list again when it is connected
  or disconnected.
- gui: /api/state carries failover. The row says "Not connected · goes
  through magpie only to fail over to your other ChatGPT accounts". Its
  title says what turns it off. A "ChatGPT accounts" button opens
  Providers › Codex. Strings are in zh, zh-TW, ja and de. openProvider no
  longer draws the Providers list before it has loaded. It was only safe
  once the tab had been opened, and this is its first caller from another
  page.
- docs: agent-wiring and gateway-routing describe the failover-only Codex
  and list the new tests.

A user's hand-written openai_base_url (#856) isn't failover's, so it stays a
plain not-connected row.

Verified:
- TestCodexFailoverOnlyKeepsOwnModels (agent) builds the reporter's state in
  a temp HOME: Codex signed in, two more ChatGPT logins on, Sync, so the
  stash has codex.failover=1 and no joined/beside. It checks that the row is
  not wired and is FailingOver, that FailoverSaid names Providers › Codex,
  and that the real gateway handler's /backend-api/codex/models lists only
  the backend's gpt-5.5. It also checks that the cached full list is aged,
  and that after Join magpie's fake/m1 is back.
- TestCodexModelListOwnOnlyForFailover (gateway) checks the list and both
  ETags with the hook on and off.
- agent-failover.test.cjs checks the row text, title and button in en, zh,
  zh-TW, ja and de, at 980px and 420px, in Chromium and WebKit (20 pass).
  It also checks that the button opens Codex's editor, that the page
  doesn't scroll sideways, and that no click moves it.
- Broken by hand, each test goes red:
  - codexModels ignoring the hook fails both Go tests.
  - failingOver returning false fails the agent test.
  - dropping the Sync cache case fails the agent test.
  - removing the GUI branch fails the GUI test.
  - drawing the list before it has loaded (the old openProvider) fails the
    GUI test.
- go test -tags nogui ./internal/agent ./internal/gateway ./internal/provider
  ./internal/gui ./internal/tui ./internal/codexcat . in a temp HOME: all ok.
- node --test agent-*.test.cjs (254 pass) and i18n-keys, gui-ja, gui-de,
  click-scroll: all pass.
- gofmt -l is clean.
- go vet -tags nogui on darwin, linux and windows: clean.
- go build, and GOOS=windows go build: ok.
2026-10-09 14:10:13 +08:00
..
plugin, gui, docs: Plugins says which plugins are providers and which are middleware, lists the community's ready-made middleware, and edits a middleware's options in its row (the owner) — the Plugins page still read as if every plugin were a subscription, and nothing showed what a middleware does or let you set its options without editing plugins.json. Every Discover card and Installed row now carries a Provider or Middleware chip (both for a plugin that is both), also for one that is off (isMiddleware/middlewareOnly from the server, read from its files); Discover splits into Subscriptions and Gateway middleware ("runs in magpie's gateway on what every agent sends and gets back, whichever provider serves it"), the latter from market entries with "kind": "middleware" and no providers or icon (the built-in market.json lists the five new @magpie-community/middleware-* packages: param-override, model-map, system-prompt, word-guard, think-tags, mostly New API's channel settings with the same JSON). A middleware row has Options: a JSON editor in the row, starting from the package's magpie.options example when none are set, which validates it is an object, saves (POST /api/plugins/options) for the next request, clears, and cancels with Esc. magpie plugin options takes a short name (model-map for @magpie-community/middleware-model-map; plugin.ShortName, also in SetOptions). en/zh/ja/de. Docs: usemagpie.ai/docs/plugins (en/zh/ja) gains Ready-made middleware and Options and the registry's kind; reference.md, both READMEs and docs/subsystems/gateway-middleware.md likewise. Tested with a real DeepSeek account and real Claude Code through magpie web --gateway in a sandbox HOME, with all five installed and options set by short name: a mapped model named back, max_tokens capped, a prompt appended for claude only, word-guard's 400 and param-override's 403 in the agent's shape, <think> stripped from Chat and Responses streams and a whole Anthropic reply, reasoning_content moved into the text; /api/plugins showed the kinds and the example. TestSetOptionsShortName is new; TestBuiltinMarket checks a middleware entry has no providers or icon and no other kind. plugin-middleware.test.cjs (Chromium and WebKit, en and zh: the chips, Discover's two sections, the options editor with its errors, no select, no scroll, no left border) fails without the change; the other plugin tests and click-scroll pass in WebKit (plugin-market's section name updated; plugin-move fails only under parallel load and passes alone). go vet, go build (darwin, GOOS=linux nogui, GOOS=windows) and go test -tags nogui ./... under a temp HOME pass.
2026-10-05 20:50:09 +08:00

Subsystem design references

These pages describe current responsibilities, runtime paths, state, and contracts. Start with the subsystem relevant to your task, then follow its source links to verify the implementation you change.

Subsystem Reference
Built-in subscriptions moving to plugins Provider and plugin ownership
Plugins that run on the gateway's requests and replies Gateway middleware
Claude subscription turns across Claude Code runs Claude subscription bridge
How the gateway picks a provider, key or account, and falls back Gateway routing and fallback
Connecting an agent's config to the gateway, drift, disconnect, WSL twins Agent wiring
Providers, keys, subscription accounts, refresh and allowance Providers and accounts
Instructions, MCP servers, skills and RTK written into agents Library
Tray, panel, window, magpie web and the page's JSON API GUI app shell

A subsystem can span several packages or repositories. Its reference describes the behavior those parts provide together; it does not need to list every function.

Maintain a reference

Update the owning reference in the same PR when a change alters a documented responsibility, interface, state transition, runtime path, compatibility rule, or failure behavior. An internal refactor that preserves these contracts does not require a prose change.

Keep each fact in one reference and link to it from agent instructions or other pages. Link to source files and named symbols rather than copying implementation details that change frequently. Explain field meanings and constraints that signatures alone do not express.

For a new reference, include the responsibilities and boundaries, source of truth, main runtime paths, state transitions, important constraints, and existing verification commands. Include failure and compatibility behavior where relevant. Document current behavior separately from proposed changes; the proposal belongs in its issue or PR.

Describe a PR's semantic changes

For each affected subsystem, explain the observable change with its trigger and before/after behavior. Include changes to interfaces, state, compatibility, and failure behavior when relevant. Link the affected reference, implementation, and verification evidence. State when a refactor preserves the documented behavior.

For example, a change to plugin adoption should explain which migration state and account condition trigger it, which implementation handles requests before and after adoption, and which test exercises that transition.

This is an author-provided explanation that a reviewer verifies against the diff and surrounding code. It is not an automatically proven compatibility verdict.

Review a change

  1. Identify the affected subsystem and the actual runtime path, including plugin or built-in ownership.
  2. Compare the claimed before/after behavior with the base and head implementation. Trace relevant callers and state transitions beyond the changed lines.
  3. Check that changed contracts appear in the owning reference and that source links still resolve.
  4. Check that the cited tests or runtime evidence exercise the claimed behavior. Report checks that were not run.

A documentation update alone does not prove the implementation. Existing tests and runtime evidence remain necessary for behavior changes.