Files
magpie/internal/agentenv
易良 ae39bc8ec8 agents: Qwen Code (qwen) as a managed agent (#1311)
* agents: Qwen Code (qwen) as a managed agent (#1279)

Qwen Code (QwenLM/qwen-code, a Gemini CLI fork) keeps custom model
providers in ~/.qwen/settings.json modelProviders.<protocol> entries
({baseUrl, envKey, id, name}, envKey naming a variable in the same
file's env object) and its session model in settings.model
{name, baseUrl}.

magpie writes one modelProviders.openai entry per catalog model —
wire id the gateway takes, name magpie/<provider>/<model>, every one
on env.MAGPIE_API_KEY holding the gateway's key, which is how an entry
of magpie's is told from the user's own — and points settings.model at
a pick. Stash, Check/drift, Sync, port-move Rewire and disconnect work
as for the other JSON-config agents; on disconnect the user's entries,
model and key are restored, the file byte-identical to before.

Verified: go build, GOOS=linux nogui + GOOS=windows builds, go vet
./...; go test -tags nogui ./internal/agent (TestQwen* pass;
TestDriftUnwiredEveryAgent/qwen and TestPortMovesEveryAgent pass after
binding drift identity to the entry, not the address), go test -race
-count=3 -run TestQwen, go test . (TestAgentQwenDefault: set then
'default' restores the settings byte-for-byte). TestQwen fails with
'unknown agent' when the registration is reverted. Real use: a copy of
a live 341-line settings.json in a sandbox HOME — wire, then
'qwen default' left it byte-identical. Not run: GUI suite (no
gui/assets change); TestReasonixCLI* and TestPluginHTTP2TakesTheProxy
fail on main too (fixture/network), unchanged by this.

* agents: qwen honors QWEN_HOME, sets auth type, owns a qwen-specific key name, carries context windows (#1279 review)

- $QWEN_HOME stands for ~/.qwen, resolved as the other agents'
  (via place.getenv, appdir.Getenv; a distro's isn't read), and is in
  agentenv.Vars so the sandbox clears it
- wiring sets security.auth.selectedType openai so qwen skips the
  /auth prompt; the user's own (including a pre-existing "openai",
  told from magpie's by a stash marker) is restored on disengage
- envKey is MAGPIE_QWEN_API_KEY: a shell-exported or ~/.qwen/.env
  value wins over the settings' env, so the magpie-generic name is
  not used
- entries carry generationConfig.contextWindowSize from the catalog
- a settings.json without modelProviders reads as no models of anyone,
  instead of gjson's 'cannot use null as iterable'

Verified: TestQwen* incl. TestQwenAuthOpenAIOfTheUsersOwn and
TestQwenHomeOverride, TestAgentQwenDefault (CLI), -race -count=3;
unwired-drift and port-move generic tests pass. Real end-to-end:
dev gateway on 127.0.0.1:3999 in a sandbox HOME, real qwen binary in
a sandbox QWEN_HOME ran a prompt through the gateway (PONG), and
wire → default left the settings byte-identical. Reasonix fixture
tests fail on main too (environment).

* agents: qwen's requests are attributed by their QwenCode/<version> User-Agent
2026-10-08 20:33:11 +08:00
..
test: one list of the agent folders a sandbox clears, so no sandbox misses an agent (yetone, on #522: A shared list of env vars for the other sandboxes would be a welcome follow-up) — every sandbox kept its own list of the variables that point at an agent's folder, and each had drifted: the gateway's cleared 5 of the 32 magpie reads, the provider package's 2, the tui's 10, the library's 20. A sandbox clearing 2 meets whatever Grok, Kimi, Hermes, MiniMax, Qoder or ZCode the developer has installed, which is how #522 came about — Qoder's two were in the library's list and in none of the four that failed. internal/agentenv is now the one list, and the 21 sandboxes that fake an environment with no agent in it clear it, each keeping beside it the variables that are not an agent's: APPDATA and LOCALAPPDATA, which a sandbox sets to a folder of its own rather than clearing, VISUAL and EDITOR. A name in the list is one magpie reads, and TestVarsAreRead walks the sources to say so, which is how two names a sandbox had been clearing turned out to be read by nothing at all — GEMINI_CLI_HOME and OPENCODE_CONFIG in internal/backup's, while OPENCODE_CONFIG_DIR, the one internal/agent reads (agents.go:545), was not in it. Where a sandbox set an agent's folder to a fixture of its own, the clearing now comes first, since a list of 32 wipes what a list of 5 left standing. Not converted: the omp tests' four variables, which are the ones omp's own lookup consults (a Pi fork) rather than every agent's, and the warm-up's Claude Code traffic flags, which are not folders — CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC stays out of the list for that reason too, its one test setting it on purpose. On Windows 11 the whole suite answers as it did before the change: 31 packages pass, and the same 15 tests in internal/library, internal/gateway and internal/agent fail for what they failed for before (symlinks this machine has no privilege for, and TestSuffixModesReachAgentFiles). go vet ./..., go build -tags nogui (windows, GOOS=linux, GOOS=darwin), and go vet -tags nogui for linux and darwin over every package touched pass. (#543)
2026-10-02 17:22:16 +08:00