main
376
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
8714abd45d |
agent: TestUpdateWithBunBridgesSOCKS reads only the proxy variables it sets, so a machine with NO_PROXY or NODE_USE_ENV_PROXY set no longer fails it
The test collected every variable whose name ends in _PROXY from updateEnv's environment and required each to be a bridged http://127.0.0.1 URL for bun. updateEnv passes the rest of the environment through, so a NO_PROXY of the machine running the test ("bun is given localhost,127.0.0.1") or Node's NODE_USE_ENV_PROXY=1 ("bun is given 1") made it red, though neither names a proxy. It now reads the six proxy variables it sets (the ones netproxy.EnvForBun bridges, in either case). Test-only; updateEnv is unchanged. What was run: on the old main the test is red with NO_PROXY=localhost,127.0.0.1 and, separately, with NODE_USE_ENV_PROXY=1 in the environment, green with neither. With this change it is green with both set. Broken by hand (updateEnv's netproxy.EnvForBun call removed) it fails with "bun is given socks5://127.0.0.1:4401". gofmt -l prints nothing; go vet -tags nogui ./... passes for darwin, linux and windows; go test -tags nogui -count=1 ./... under a temp HOME passes. Not run on the Windows box. Reported and fixed by ggbdpq (#1472). Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> |
||
|
|
9e665491ec |
context: Tuned for you — each agent's settings worked out from the user's own calls, for the fewest tokens, put in on a click and undone on another
The Context tab now replays the period's calls of each agent against the
settings that decide what they cost, and shows, per agent and per model,
the value that would have used the fewest tokens for how this user works:
Claude Code's auto-compact window per model (modelSettings.<model>.
autoCompactWindow), its main and subagent prompt cache lifetimes
(promptCacheTtl, subagentPromptCacheTtl: 5m or 1h), and Codex's
model_auto_compact_token_limit. Each row says where the value comes from
now (the agent's default, the user's own setting, one magpie set, or a
variable that comes first and locks it), why (how fast conversations grow,
what a compaction leaves, how long the user pauses), the cost curve, and
the saving. Nothing is written until "Use …" is clicked; what was there is
stashed and Undo puts it back, unless the user changed it since. A window
the user set past the model's own is shown capped, as Claude Code caps it
(min(setting, model window)), so no saving is claimed that wouldn't happen.
Advice under a 3% saving is "already the leanest".
On the owner's 7 days (88K Claude Code calls, 956 Codex): opus keeps 200K
(their 500K is capped to it), fable 5.1 200K → 160K saves 5% (2.56M
tokens), both cache lifetimes and Codex's 272K are already the leanest.
Run: go vet ./... and go test ./... pass (internal/tune, internal/agent
TestTune*: user-set values kept, env locks, undo after a user edit, TOML
key order). context-tune.test.cjs passes in Chromium and WebKit, en/zh/
zh-TW/ja/de, at 360 and 1100px (22); context-window, context-scroll-end
pass. Full GUI suite: library-servers-bulk and market-have failed once
under load and pass alone; market-have fails on origin/main
|
||
|
|
282c9feb23 |
agent: a packaged Claude Desktop gets magpie's gateway in the real %LOCALAPPDATA%\Claude-3p too, which 2.31226 reads, not only the package's (bg5eau on X)
bg5eau on X: Claude Desktop 2.31226.1, installed as an MSIX package on
Windows, never reached magpie. magpie wrote deploymentMode and its
configLibrary profile only into the package's
LocalCache\Local\Claude-3p, and that build reads the real
%LOCALAPPDATA%\Claude-3p. Copying configLibrary there and setting
deploymentMode 3p fixed it for them.
What the build does: 2.31226.1's asar (index.chunk-Cyt2WTko.js) resolves
the 3p userData with Ou() = path.join(LOCALAPPDATA, "Claude-3p"). 2.7032.0
has the same code (GY()). It reads deploymentMode from
Ou()/claude_desktop_config.json and the gateway from Ou()/configLibrary.
The path didn't change; the package stopped redirecting it into
LocalCache. An install's folders don't say which build it is, so:
- desktopdir.Sets returns every set of folders: Find's choice first, then
the other one for an MSIX install. Everywhere lists every set's folders.
- Claude Desktop's switch writes and removes deploymentMode, the profile
and _meta.json in each set. The package's set keeps its own stash keys
(".package"). A stash that an older magpie wrote for the package's
folders moves onto those keys, so turning it off still restores them.
- The check runs on each set, names the folder when there are two, and
checks Claude-3p's deploymentMode as well.
- The siblings: the library's MCP servers go into every Claude-3p file
that exists, and Cowork skills and Desktop sessions are read from every
set.
Run:
- go test ./internal/{desktopdir,agent,library,sessions,usage}: green.
- The new tests (TestClaudeDesktopMSIX, TestClaudeDesktopMSIXStashBefore,
TestClaudeDesktopMSIXBoth3p) and TestCallsCoworkMSIX are red against
the old code. StashBefore also goes red with desktopAdoptStash disabled.
- gofmt; go vet for darwin, linux and windows.
- On the Windows box, Windows test binaries for the four packages:
- desktopdir and library pass.
- agent and sessions fail exactly the set that main's binaries fail
there (#1388, and newly filed #1468).
- Every Desktop test passes.
|
||
|
|
74c48ac060 |
agent: the managed daemon's subcommand helpers are no stale Codex copy (#1461)
Codex keeps a long-lived helper next to the managed app-server (codex
app-server daemon pid-update-loop). It matches the codex startsWith
pattern, carries no --managed-daemon, sits under no app bundle or
Application Support folder, so codexCopyKind called it "cli": a helper
that started before magpie's last change to what Codex reads counted as
"A Codex in a terminal" forever, a daemon restart didn't end it, and the
only way to clear the row was killing a process the user can't name.
codexCopyKind now answers "helper" for an app-server daemon <sub>
command line, and StaleCopies skips it: it reads no list and there is no
Codex to quit. The daemon itself (--managed-daemon) is told apart
before, and a terminal Codex still counts.
Red/green: TestCodexDaemonHelperIsNoStaleCopy runs the helper alone from
before magpie's change — FAIL on the old code (one stale "cli" copy),
passes now; a terminal codex from before still counts 1.
Generated-by: GLM-5.3-Flash (ZCode)
Reviewed and landed by the maintainer (cherry-pick of b40a1dd0 from
ggbdpq:fix/codex-daemon-helper-1461, lychjck's report). Run on main at
|
||
|
|
793af76242 |
gui: Codex's subagents setting, unset, says the lead picks the model and only falls back to the main model, not just "same as model" (willz on Discord)
willz set Codex's "subagents" to 同主模型 (same as model), and a subagent
still ran on another model. Codex 0.162 lets the spawn_agent call's own
`model` argument win over `[agents] default_subagent_model`
(core/src/agent/child_config.rs:
requested_model.or(agent_default_subagent_model)). The default only
applies to a spawn that names no model, and the spawn_agent tool lists
up to 5 models the lead may name. So "same as model" was false whenever
the lead named a model.
This was reproduced with the real codex-cli 0.162.0 in a sandbox
HOME/CODEX_HOME, against a fake Responses server on port 3683 that logs
each request's model and x-openai-subagent header. The lead ran on
gpt-6.1-sol in every case:
a. lead names astra, default unset -> subagent on gpt-6-astra
b. lead names none, default unset -> subagent on gpt-6.1-sol
c. lead names astra, default sol -> subagent on gpt-6-astra
d. lead names none, default astra -> subagent on gpt-6-astra
magpie already holds every subagent to one model with the "subagent
model" setting (the gateway's codexSubagentPick,
|
||
|
|
9bb2a467e4 |
agent: Snow CLI is an agent magpie sets up: a magpie profile in ~/.snow, made the active one, that Snow App takes in with "Sync Snow CLI API config" (#1457)
Snow CLI (npm snow-ai, MIT, MayDay-wpf/snow-cli) keeps one profile per file under ~/.snow/profiles (or $SNOW_CONFIG_DIR), names the active one in active-profile.json, and copies it over config.json at every start, headless `snow --ask` included. Each profile is the whole snowcfg: base URL, key, request method and models. Snow App (MayDay-wpf/snow-app) reads ~/.snow/profiles/*.json when "Sync Snow CLI API config" is pressed. magpie writes profiles/magpie.json from the profile the user was on (their settings kept, Snow's OAuth sign-in left out): baseUrlMode base at the gateway's /v1, requestMethod chat, the agent's key, the model as advancedModel and basicModel, and the catalog's window and output (200000 and 64000 when not known, the output cut to the window). It makes that profile active and copies it to config.json. The profile the user was on, whether there was an active-profile.json at all, and a magpie.json of their own are stashed; Disconnect, `magpie snow default`, or picking a model of one of the user's profiles puts those back. A Snow from before profiles (config.json alone) gets the default.json Snow itself would make, so its next start doesn't take magpie's config for the user's. Check reports a base URL, key or request method moved off magpie's; Sync rewrites the profile on a port move. A WSL distro's ~/.snow is found and written the same way. Registered: agents.go, the WSL kinds, SNOW_CONFIG_DIR in agentenv, the npm update spec (snow-ai), the icon (Snow App's own, trimmed to 96px), README, docs/reference.md, docs/subsystems/agent-wiring.md, the site's agent list and start.html (en, zh, ja). Run: - go test -tags nogui ./... in a scratch HOME: 44 packages ok. New: TestSnow, TestSnowOwnProfile, TestSnowNoProfiles, TestSnowUsersMagpieProfile, TestSnowCheckSync, TestAgentSnowDefault; snow added to TestOwnProviderNamedAsMagpies, the WSL more-agents tests and TestMaxTokensWithinContextWindow. All fail before this commit (no agent "snow"). - go vet -tags nogui for darwin, linux and windows; gofmt -l clean. - Real Snow CLI 0.9.2 in a sandbox HOME against a sandbox magpie (`magpie serve` on 127.0.0.1:3571, a fake OpenAI upstream on 3561): `magpie snow fake/pro` made the magpie profile active, `snow --ask` answered through the gateway (POST /v1/chat/completions, model pro, logged as agent snow), and `magpie snow default` left profiles, active-profile.json and config.json byte-identical to before. - GUI: agent-layout, agent-connect, agent-models-picker, agent-field-names and models-add-and-icons in Chromium and WebKit; a Snow CLI row checked at 520-960px in en, zh, zh-TW, ja and de in both engines. No new GUI strings. |
||
|
|
43cb6fe34e |
agent: Claude Code is told the window of Claude Desktop's models while Desktop is on magpie, so its Code tab no longer shows GPT 272K and Grok 256K as 200K (#1458)
Claude Desktop's Code tab runs Claude Code on ~/.claude/settings.json
and hands it the id Desktop lists a model by (mythos-magpie-<n>,
anthropic/magpie-<n>), with nothing of the model's window. Claude Code
2.1.293 (the one Desktop 2.31226.1 ships) gives an id it doesn't know
200000 unless the id has [1m] or CLAUDE_CODE_MAX_CONTEXT_TOKENS is set:
its catalog rows come from Anthropic's served catalog alone, and gateway
discovery keeps only id, name and description. Desktop sets that env
only for Cowork (yukon-silver-config contextWindowByModel), never for a
Code session, and reads the gateway's max_input_tokens only to add a
1M entry. magpie wrote the env from Claude Code's own main model alone,
and not at all on a [1m] or Claude main model, so GPT-6-Astra and Grok
4.7 ran as 200K. GLM 5.3 Flash is 1M, listed as [1m], so it ran at 1M
and showed magpie's auto-compact window, 300K.
While Claude Desktop is on magpie, the window written is now the least
of Claude Code's own model's and of the models Desktop is shown, leaving
out one listed by its 1M id (1M whatever the env says) and one named as a
Claude model Claude Code knows. Claude Code takes one value for every
model it doesn't know, so it is the least: never more than any of them
takes (an overflow is still turned into "prompt is too long" by the
gateway). A value the user set is still left alone. Sync writes it too,
so a model added or hidden for Desktop moves it, and turning
DesktopLongest on or off tells the agents again (catalog.Touched).
gateway.DesktopListed1M is the one rule desktopModels lists [1m] by, so
bb3b3f14's 1M label is unchanged.
Standalone Claude Code has the same limit: a magpie model picked in
/model runs on the one value; that is not changed beyond the Desktop
case.
Run:
- TestClaudeDesktopContextWindow, TestClaudeDesktopContextWindow1M fail
on the old claude.go/desktop.go ("" want 256000; "" want 1000000), and
each exclusion broken by hand turns them red.
- Real Claude Code 2.1.293 (from Desktop 2.31226.1's download), sandbox
HOME/CLAUDE_CONFIG_DIR, against a fake gateway on 3611, with the
settings.json magpie's agent code wrote (GPT 272K, Grok 256K, GLM 1M
under DesktopLongest, main on GLM [1m]): /context shows 256k for
GPT's and Grok's mythos-magpie ids (200k before), 272k for GLM's
[1m] id (the compact window).
- gofmt -l; go vet, GOOS=linux and GOOS=windows go vet -tags nogui on
internal/agent, internal/gateway, internal/gui; go test -tags nogui
./... under a temp HOME.
Not run: the Claude Desktop GUI itself.
|
||
|
|
e611fc65d6 |
codex: magpie restarts the codex app-server a change left on the old model list when no codex session is on it, and the Agents page offers Restart when one is (luci on Discord)
luci (Codex CLI 0.162, magpie 0.1.1150): after Codex was wired back to
magpie, codex in a new terminal listed Codex's own models although
config.toml was magpie's.
Cause, reproduced with real codex 0.162.0 in a sandbox CODEX_HOME:
codex sessions attach to a background app-server (the managed daemon)
that is per CODEX_HOME, not per terminal (socket under
$CODEX_HOME/app-server-control). It is detached, has no idle exit and
builds its model list once, at start. A daemon started while Codex was
off magpie keeps the official list for every later session, in this
terminal or a new one, until it restarts. magpie writes only PATH to
shell rc files and doesn't unwire Codex on quit, so nothing
per-terminal is involved. luci's "a new terminal fixed it" isn't
explained by the daemon. Codex's hourly updater restarting it is a
guess, not proven.
What changed:
- provider.KeepCodexDaemonCurrent(ctx, home, changed) compares the
daemon's start time with when magpie last changed what Codex reads at
start. The start time is the mtime of a pid file in that home that
names the running daemon's pid, so another home's daemon (ChatGPT.app,
other sandboxes) is never acted on.
- With no codex session attached, it runs
`codex app-server daemon restart` with CODEX_HOME set to that home.
- Otherwise it reports Behind and how many sessions are attached.
- isCodexClient leaves out apps' and editors' own codex, exec-server,
mcp-server and the daemon's own processes. It reads the whole ps line,
since ps leaves a path's spaces unquoted ("Codex Computer
Use.app/...").
- agent: Codex's Notice after a change runs this check first. It says
either that magpie restarted the daemon, or that the daemon is behind
and how to restart it (Agents page or the command).
- The GUI backend and `magpie serve` re-check every 15s. A session that
was open at the change is usually closed later, and that is when the
daemon can be restarted.
- Agents page: the stale "daemon" copy line has a Restart button that
POSTs /api/codex/daemon/restart (timeout now 2 min, as a running turn
is let finish). It reuses translated strings. On Windows, where
processes aren't listed on each look, the row shows the last check.
- Restarts are serialized by one mutex.
- WSL's Codex is not handled automatically. Its row keeps the advice
text, which names the restart command.
What was run:
- Real codex 0.162.0 in a sandbox CODEX_HOME, with a fake gateway, on
luci's sequence: the daemon started on the official config, then
magpie's config and catalog were written.
- With a TUI open, /model listed GPT-6.x. The check said Behind with
a session attached and did not restart.
- With the TUI quit, the check restarted the daemon. A new codex in
the same terminal listed Magpie Alpha and Beta.
- Earlier in the sandbox: a daemon restart under an idle attached 0.162
TUI reconnects, and /model then shows magpie's models. A running turn
is waited for up to 60s.
- New tests:
- TestKeepCodexDaemonCurrent(Fails), TestIsCodexClient (with the
space-in-path case from the real process list) and
TestCodexNoticeKeepsTheDaemonCurrent. They don't build or fail on
the old code. The space case failed before the ps-line fix.
- The GUI test "the daemon's line restarts it" times out on the old
app.js.
- Results:
- agent-stale-copies.test.cjs: 60/60 in Chromium and WebKit, in en,
zh, zh-TW, ja and de, at widths 980 and 440.
- go test (temp HOME) for ./internal/agent, ./internal/provider,
./internal/gui and the root package: ok.
- gofmt -l: clean.
- GOOS=linux and GOOS=windows go vet -tags nogui: ok.
- The Windows path is checked only by vet and not tried on a Windows
box.
|
||
|
|
d9bf7c1ccf |
zcode: the API format the user picks for magpie's provider in ZCode's settings stays theirs; a sync no longer puts Anthropic Messages back on every start (杰多夫 on Discord)
What changed: zcodeRules wrote api.type "anthropic-messages" into provider_config.json, and zcodeProviderJSON wrote kind "anthropic" into config.json, on every sync (magpie's start, any catalog change). A user who switched the provider to OpenAI Chat Completions or Responses in ZCode had it switched back the next time magpie started. zcodeFormat now reads the format already there and keeps it, in both files: provider_config.json's api.type (ZCode 3.14 on) or config.json's kind (older ZCode), the one written last where they differ, mapped as ZCode maps them (anthropic-messages/anthropic, openai-chat-completions/openai-compatible, openai-responses/openai). A new provider, or a format ZCode doesn't have, is Anthropic Messages as before. Why it's safe: magpie's gateway answers all three at the same base address. Checked with ZCode 0.16.9's own agent CLI (glm/zcode.cjs -p, run from a copy under a sandbox HOME, never ~/.zcode) against a sandbox gateway with the DeepSeek provider: anthropic-messages asks /v1/messages, openai-chat-completions /chat/completions, openai-responses /responses, and a tool-using turn (Read a.txt) answered 200 on each, to deepseek-flash. What was run: - TestZCodeKeepsUserFormat (new): a format picked in provider_config.json, for each of the three, survives two syncs and is carried to config.json's kind; one picked in config.json is carried to provider_config.json; an unknown one isn't kept. On the old zcode.go it fails: "picked openai-chat-completions: kind "anthropic", api "anthropic-messages"". - Sandbox, real binaries: provider_config.json set to openai-responses, then `magpie zcode magpie`: the old build wrote anthropic-messages back, this one kept openai-responses (config.json kind openai). Then openai-chat-completions, resynced, and the real ZCode CLI ran a turn on the file magpie wrote, served over /chat/completions. - gofmt -l; GOOS=linux and GOOS=windows go vet -tags nogui ./internal/agent; go test -tags nogui ./internal/agent under a temp HOME: ok. The "bcww: Field required" 400 on Anthropic Messages is not part of this commit: ZCode's real requests (captured from its CLI, plain and with web search) to deepseek/deepseek-flash through magpie returned 200, and DeepSeek answers schema errors in serde's words, not "Field required". |
||
|
|
c65d9fb473 |
codex: magpie-models.json keeps the keys the user added by hand to a model, and says supports_reasoning_summaries itself, so Codex before 0.145 sends the effort and shows thinking (heng91, #1450)
What was wrong: every write of ~/.codex/magpie-models.json rendered the catalog from magpie's models alone, so a key the user had put into an entry was gone after the next model switch, catalog sync (Refresh, a provider added, a gateway start) or a switch to Codex's own default, which also deleted the file outright. The key heng91 added, supports_reasoning_summaries, is one Codex before 0.145 needs: its client.rs build_reasoning sends no `reasoning` (no effort, no summary, so no thinking shown) for a model whose entry doesn't say true, until openai/codex#32206 (first in 0.145). 0.144 also refuses to load a catalog an entry of which lacks the key at all. What changed: - codexcat: every entry says supports_reasoning_summaries, true for a model with effort levels, false otherwise. Later Codex ignore it. - codexcat.Keep: before a write, the keys of an entry on disk magpie doesn't write are carried over to the entry of the same slug, and a true of supports_reasoning_summaries stays over magpie's false. A key magpie writes keeps magpie's value; a model magpie no longer serves drops out; an unreadable file keeps nothing. - agent/codex.go: the write when magpie becomes the provider and the catalog sync go through Keep; the three places that removed the file remove it only when nothing of the user's is in it. Run: - New tests TestCodexCatalogKeepsWhatTheUserAdded, TestCodexCatalogRemovedWhenNothingAdded (agent, from the file magpie writes, edited by hand) and TestCodexCatalogReasoningSummaries, TestKeepUserKeys (codexcat). Each fails on origin/main's code. - Real Codex in a sandbox CODEX_HOME against a capture server: 0.144.0 fails to load the old catalog ("missing field supports_reasoning_summaries"), loads the new one and sends reasoning {effort: high, summary: auto}; with the user's keys kept it sends summary "detailed". 0.162.0 loads both and sends the same. - gofmt -l clean; go vet, GOOS=linux and GOOS=windows go vet of ./internal/agent ./internal/codexcat; linux and windows builds; go test -tags nogui ./... under a temp HOME, all ok. |
||
|
|
1134c7a8af |
codex: the restart advice after a model or catalog change names the app-server codex sessions share ("codex app-server daemon restart"), on Windows and WSL too (TJHHHH, luci on Discord)
TJHHHH and luci (magpie 0.1.1137, codex-cli 0.162.0): the Codex app
listed magpie's models while the codex CLI showed only the official
list, on and off. Codex 0.162's TUI attaches to a background
`codex app-server --managed-daemon` (feature daemon_auto_start, stable
and on by default on every OS; skipped on Windows only when elevated or
detached launch is restricted, on WSL only when CODEX_HOME is on drvfs).
That daemon builds its model list once, when it starts, and restarts
itself only when its binary or version changes. So closing every codex
session and opening a new one keeps the list from before magpie wrote
model_catalog_json, while the desktop app's own app-server, restarted
with the app, has magpie's.
Reproduced with the real codex-cli 0.162.0 in a sandbox CODEX_HOME: a
daemon started with no catalog kept listing Codex's 8 models in model/list
after config.toml switched to a magpie catalog and every client closed;
`codex app-server daemon restart` made it list the catalog's models.
Nothing since v0.1.1137 changed this.
magpie's advice only said "restart the Codex app (and open codex
sessions)", which doesn't reach the daemon. On macOS and Linux the
Agents row already flags a stale daemon copy (StaleCopies), but that
returns nil on Windows and WSL, where the reporters may be. The Notice,
and the WSL codex kind's restart text, now name the shared app-server
and provider.CodexDaemonRestart. magpie still doesn't restart it itself:
that ends every session attached to it (as
|
||
|
|
5aca7d0924 |
codex: effort left at Default stays Default and says the level Codex takes then ("default (medium)"), and isn't written over with medium when a model is picked or the catalog synced (lgtm on Discord)
lgtm set Codex · WSL Ubuntu's 推理强度 to 默认 and it came back as 中.
Two things did that, on every platform (the WSL row is the same codexIn
code as the local one):
- The effort field's Get answered an unset model_reasoning_effort on a
magpie model with codexcat.DefaultEffort (medium), so right after the
pick the slider and the row read 中, not 默认.
- settle() counted an unset effort as one the model doesn't take and
wrote DefaultEffort into config.toml: on every Sync for a magpie
model, on Join, and on a model pick, Codex's own models included. So
Default was pinned as medium, and on Codex's own models whose default
is low (gpt-6.1-sol, gpt-6-astra, gpt-5.6-sol in 0.162's models.json)
magpie overrode Codex's own default.
With model_reasoning_effort unset, Codex takes the catalog entry's
default_reasoning_level (core/src/client.rs build_reasoning). Checked
for real with codex-cli 0.162.0 in a sandbox CODEX_HOME: a
model_catalog_json entry with default_reasoning_level "low" and no
model_reasoning_effort sent {"reasoning":{"effort":"low"}} to a stand-in
Responses endpoint.
Now settle replaces only a set effort the model doesn't take, Get
returns the unset value, and the effort control's options start with
Default carrying Takes: the level Codex then takes, from the entry it
reads (codexcat.TakesEffort: Codex's own models_cache entry for its
models and a ChatGPT account's codex/ ones, else the one Entries writes).
The GUI shows it as "default (medium)" / 默认(中) / 預設(中) /
デフォルト(中) / Standard (mittel). A stopped WSL distro's options get the
same Default (codexEfforts).
Tests: TestCodexDefaultEffortStaysDefault fails on the old settle
("Default written over: model_reasoning_effort = \"high\"", "Default
shows as \"high\"") and passes now. TestCodexCustomProviderModelHasEfforts
now expects Default first with Takes "high" and an unset value read as
"". codex-default-effort.test.cjs (window at 560 and 360px, tray panel;
en, zh, ja, de; Chromium and WebKit) fails on the old effortName and
passes now.
Run: gofmt -l (clean); go vet -tags nogui on darwin, GOOS=linux and
GOOS=windows for ./internal/agent ./internal/codexcat ./internal/gui
./internal/tui and the main package; go test -tags nogui of those and
./internal/catalog under a scratch HOME (all ok); the GUI effort, gui-ja,
gui-de, gui-zh-tw and i18n-keys tests (all pass), and the whole GUI suite
in both engines: 4711 pass, 8 fail, all in library-servers-bulk
(chromium, ".lib-everyon moved from 290 to 288") and purpose-filter
(webkit), which fail the same way run alone on origin/main
|
||
|
|
7f62dbf3ed |
gateway, agents, gui: a model's reply limit is published within the window beside it, on /v1/models, Cursor's and Muse's lists, the Gateway page and magpie model output (#1438, yhong91)
#1438 (yhong91): GET /v1/models listed grok/grok-4.7 with context_window 256000 (the Grok backend's own) and max_output_tokens 500000 (models.dev's x-ai entry, which catalog.Decorate fills in when the backend gives no output). A client that reads both sizes its replies past what the window holds. The agents' files already kept output within the window (maxTokens, #338/#347), but the gateway's lists, the GUI and the CLI did not. What changed: - provider.OutputWithin(window, output) and Entry.PublishedOutput(): the output is kept within the window; an unknown window or output (0) is left as it is. - Every place that publishes the pair uses it: modelObject (/v1/models), cursorLocalModel (Cursor Private Inference), Muse Code's list, the Gateway page's models and groups (providerInfo, providersState), the agents' maxTokens, and `magpie model output` (which also says "500k, kept within its 200k window" when it lowers one). - Entry.Output stays the model's own limit. The request-path cap (outputLimit/withMaxOutput) and the user's Max output setting are unchanged, so no request is lowered further than before. This is why the clamp is not in replyLimit/entryFor as the issue suggests, and why the commenter's patch (modelObject only) was not taken as is: it was right but missed Cursor, Muse, the GUI and the CLI. - docs/subsystems/gateway-routing.md: the rule and its routes. Tests (each fails without the change): - gateway TestModelListsKeepOutputWithinTheWindow: models.dev 500000 over a backend 256000 window, plus output within the window, unknown window and unknown output, on /v1/models, Cursor's capabilities and Muse's limit; a group request with max_tokens 300000 still reaches upstream as 300000. - provider TestOutputWithin, gui TestProviderModelOutputWithinTheWindow, TestModelOutputCmdWithinTheWindow. Breaking it by hand: OutputWithin returning output unclamped turns all of them red, plus agent TestMaxTokensWithinContextWindow; gateway sites back on e.Output turn the gateway test red on all three lists; moving the clamp into Entry.Output turns the fixture and the group-request guard red. Real data: an offline dump of the owner's catalog (copies of ~/.config/magpie and its caches, 203 entries) has 3 models with output over their window, grok/grok-4.5, 4.6 and 4.7 (256000 vs 500000), now published as 256000; 0 are not Grok. The live gateway on 3425 was not running, so /v1/models itself was not read, and no real Grok request was made. Run: gofmt -l; go vet -tags nogui ./... on darwin, linux and windows; go test -tags nogui ./... -count=1: all green except TestLoopEndsWorkBuddysReply/plugin/anthropic ("let go only after 3207 lines", bound 2000) at load average ~35 with other suites running; it passes 5 of 5 alone and does not touch the output limit or request path. |
||
|
|
189586612e |
agents, gateway: Codex's subagents go on a model set in its row, a ChatGPT account's (willz on Discord)
Codex's lead names each subagent's model itself in spawn_agent, and
Codex's [agents] config can't overrule it. The Agents page's Codex row
now has a "subagent model" square (the app's model picker, Default
first). Its options are only models a ChatGPT account in magpie serves,
because a subagent's task is sealed for ChatGPT accounts.
- settings.CodexSubagentModel holds it. It is an agent Field
(subagent_model), so the TUI/CLI, profiles, davsync's "agents" part
and WSL twins carry it too. The setter refuses another provider's
model. It keeps a ChatGPT account's model that isn't served now, so a
backup restored on another computer still applies (ApplyFields stops
at the first failed Set). POST /api/settings keeps it.
- The gateway (codexSubagentPick) moves a collab_spawn request onto it.
codexBackend does this for Codex's own bare models; serve() does it
for any other model. When no ChatGPT account on in magpie serves it,
or a held gateway key may not use it, the request stays on the model
asked for. Route.Subagent records asked/to/kept. The lead's turns,
titles, guardian and the rest are never moved. Off (the default)
changes nothing.
- Routing page: kindWhy says the subagent was put on the model set, or
stayed on its own and why. "only account on for" names the model it
went on. Strings added in zh, zh-TW, ja and de.
- docs/subsystems: gateway-routing.md and agent-wiring.md describe it.
Run (darwin, rebased on origin/main):
- gofmt -l internal/: clean.
- go vet -tags nogui on gateway, provider, agent, gui and settings:
darwin, GOOS=linux and GOOS=windows.
- go build ./...
- HOME=<temp> go test -tags nogui ./...: all ok.
TestSettingsSaveKeepsWhatItDoesNotSend flagged the new field; it is
now set there.
- GUI tests, Chromium + WebKit:
- codex-subagent-model.test.cjs: 20/20 (en, zh, zh-TW, ja, de at
1100 and 560 px).
- Also green: routing-kind, subagent-effort, codex-memories,
codex-memories-settings, i18n-keys, gui-ja, gui-de, gui-zh-tw and
click-scroll.
Failing without the change (codexSubagentPick returning nil):
- TestCodexSubagentPutOnTheModelSet
- TestCodexSubagentOnAnotherProviderPutOnTheModelSet
- TestCodexSubagentModelNotServedKeepsTheAsked
- TestCodexSubagentModelHeldKeyKeepsTheAsked
TestCodexSubagentModelLeavesOtherRequests guards the other side: the
lead's turns, titles and guardian stay untouched.
Not tried with a real Codex CLI and ChatGPT account. A subagent spawn
needs a signed-in Codex home, and this session may not touch the live
~/.codex. The fake ChatGPT backend tests cover both routes.
|
||
|
|
7953c7ef9b |
agent: a config folder holding only skills/ no longer counts as the agent installed, since skill installers make one for every agent they know (qtwaiter on X)
qtwaiter's Agents page listed Goose, Crush, Command Code, fx, Devin, Hermes and Droid, none installed. Skill installers (npx skills and the like) write <folder>/skills for each agent they support, and a config folder alone made an agent detected. agentDir now takes a folder for the agent's own only when it holds something besides skills/ (.DS_Store aside); the default rule, Goose's and fx's detect use it. Run: go test ./internal/agent ./internal/library ./internal/gui ./internal/profile ./internal/backup ./internal/tui; TestSkillsOnlyFolderIsNotInstalled fails on the old code for all seven. gofmt, go vet darwin/linux/windows clean. |
||
|
|
603dc0c44f |
dsh: Disconnect keeps the model picked in dsh 0.2, which saves it into magpie's agent-default-model entry (#1392)
What dsh 0.2.0-rc.2 changed (read in @deepseek-ai/dsh-settings and
dsh-agent-default-model from npm): ~/.dsh/settings.yaml is gone. On its
first boot dsh renames it to settings.yaml.imported and merges its sections
into the booted profile's patch list (profiles/<name>/cordis.patch.yml);
nothing reads the .imported file afterwards. A /model pick is no longer
kept in settings.yaml either: agentDefaultModel.saveSelection goes through
ConfigEditor.edit, which replaces the config of the profile's last
agent-default-model entry. That entry is magpie's, so the user's pick sits
under magpie's "# magpie" mark. Profile paths, the home layer, .env and
.credentials.yaml are unchanged.
What broke: Disconnect took that entry for magpie's own, dropped it and put
back what magpie stashed from before it connected, so the model picked in
dsh (or imported from settings.yaml) was lost. Seen with real dsh
0.2.0-rc.2: after the import, Disconnect left `[]` and dsh started on its
shipped deepseek-flash instead of the imported deepseek-v4-flash.
Fix: dshKeepPick, run per profile before Disconnect takes magpie's wiring
out, hands a marked agent-default-model that is off magpie's route back to
the user (mark removed, old stash discarded). An entry on magpie's route,
or one on the pre-0.1.5 wiring, is still magpie's. dshStart still reads a
settings.yaml not yet imported, which dsh will put over the booted profile.
Run: TestDshPickSavedIntoMagpiesEntryOutlivesDisconnect (fixture from the
patch real dsh 0.2.0-rc.2 wrote) fails on the old code ("the pick made in
dsh was lost", the stashed deepseek-v4-pro came back) and passes. Real dsh
0.2.0-rc.2 under a temp HOME/DSH_HOME, fake gateway on 127.0.0.1:3561:
`dsh headless "say hi"` sent POST /v1/chat/completions, Bearer magpie,
model deepseek/pro to it, before and after a settings.yaml import; after
Disconnect `dsh headless --dump-config` keeps deepseek-v4-flash;
reconnecting sends deepseek/flash to the fake gateway. go test -tags nogui
./... (temp HOME, pinned caches) passes, -race -count=5 -run TestDsh
passes, gofmt -l clean, go vet and GOOS=linux/windows go vet -tags nogui
./internal/agent pass.
|
||
|
|
dec951229c |
agent: Grok Build installed by xAI's installer shows its newest version and offers grok update, and its version is read again after each update (xiaoxiaofeixz on Discord: Grok at C:\Users\<user>\.grok\bin\grok.exe)
Grok Build had no cliSpec and no Bin (grok is also Homebrew's regex tool), so the Agents page showed nothing about its CLI: no version, no newest, no update. xAI's installers (read from https://x.ai/cli/install.ps1 and install.sh): - install.ps1 copies grok.exe (and agent.exe) into %USERPROFILE%\.grok\bin and puts that on the user PATH; no junction, no versioned folder. grok update copies the new grok.exe over it (the running one renamed to grok.exe.old), so its size and time change. - install.sh links ~/.grok/bin/grok to ../downloads/grok-<platform>; grok update downloads ../downloads/grok-<version>-<platform> and relinks, a new real path per version. - The newest version of a channel is the body of x.ai/cli/<channel> (stable, alpha, enterprise; GCS fallback), the channel kept under [cli] in ~/.grok/config.toml. cliSpec gains `at` (where the vendor installer puts the CLI under home, read there instead of PATH for an agent with no Bin) and `latest` (the newest its own updater installs, for a CLI not on npm). grok's spec: at .grok/bin/grok(.exe), self = grok update for a real path under .grok/bin or .grok/downloads, latest = grokLatest (channel pointer, 6h memo per channel). TestGrokInstalled builds the platform's installer layout under a temp HOME/USERPROFILE with a fake grok on PATH, a served channel pointer and fake version/update runs: PATH's grok isn't taken; 1.0.46 shows 1.0.50 and "grok update" (grok.exe update on Windows); the update reads 1.0.50; grok's own update outside magpie (auto_update) is read again; the alpha channel's newest is alpha's. It fails without the grok spec (macOS and the Windows box: "before: {Version: ...} false") and passes with it. Real use: macOS, a sandbox HOME with the real grok 1.0.46 in install.sh's layout: CLI() said 1.0.46 / 1.0.50 / grok update, UpdateCLI ran the real grok update and read 1.0.50 (~/.grok untouched). Windows box (10.0.0.132, sandboxed USERPROFILE/APPDATA/LOCALAPPDATA): the real grok-1.0.46 exe at home\.grok\bin\grok.exe, same result through a real grok update; the real profile's .grok, LocalAppData\grok and user PATH unchanged. There TestGrokInstalled, TestCodexInstallPs1Junctions and TestHowInstalledWindowsShim pass. Siblings: Claude's install.ps1 lands in ~/.local/bin/claude.exe (already matched); Cursor's and Qoder's Windows installers make no junctions or links, and those agents have no cliSpec (not in scope); opencode has no install.ps1 (404); Factory's refused the fetch (403), droid has no cliSpec either. go test -tags nogui ./internal/agent (temp HOME), gofmt -l, go vet, and GOOS=linux / GOOS=windows go vet -tags nogui ./internal/agent pass. |
||
|
|
3691c2047d |
plugin, agent: bun is given a SOCKS proxy through the bridge, so a plugin installs with only a SOCKS system proxy (#1409)
With only a SOCKS system proxy on macOS (127.0.0.1:4401), every plugin
install failed: netproxy.Env put socks5://… in bun add's *_PROXY, and Bun
takes only http:// and https:// proxies, so it stopped with
"UnsupportedProxyProtocol downloading package manifest". The plugin
host's own fetches already went through netproxy.Bridge (hostEnv), but
bunCommand, which runs bun add, update and remove, did not.
- netproxy.EnvForBun rewrites a SOCKS5 HTTPS_PROXY/HTTP_PROXY/ALL_PROXY
(any case) to the loopback HTTP bridge in front of it; others as they are.
- bunCommand uses it, so add, update, remove and the host's run all get it.
- Sibling: an agent CLI installed with bun is updated by `bun add -g`
with the same env (updateEnv in cliupdate.go); npm is left as it is.
- provider-plugins.md says so.
Run:
- TestInstallThroughSOCKSProxy (real bun 1.4.2, fake registry, SOCKS
proxy only): on the old bunCommand it fails with the reporter's exact
"error: UnsupportedProxyProtocol downloading package manifest"; passes
with the fix, through the SOCKS proxy.
- TestEnvForBunBridgesSystemSOCKS (system SOCKS proxy via Env) and
TestUpdateWithBunBridgesSOCKS fail with EnvForBun / the bun branch
broken ("bun is given …socks5://…"), pass with it.
- Real use: a throwaway test with a real SOCKS5 server and only
*_PROXY=socks5://… installed @magpie-community/opencode-cursor-auth
0.2.2 from registry.npmjs.org through it; on the old code the same
failed as UnsupportedProxyProtocol. Scratch HOME, not committed.
- gofmt -l clean; go vet ./...; GOOS=linux go vet -tags nogui ./...;
GOOS=windows go vet ./...; go build; GOOS=linux go build -tags nogui;
GOOS=windows go build; go test -tags nogui ./... (scratch HOME) all ok.
|
||
|
|
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. |
||
|
|
1bc0ed404a |
minimax-code: magpie's models have one slash in the whole ref, so SubAgents take them (#1387, worksyang)
MiniMax Code 3.1.0 checks a SubAgent's model against /^[^/\s]+\/[^/\s]+$/ (uQn in @minimax-ai/code's chunk-2XPMNZVR.js), and magpie wrote its models under custom_provider.magpie by catalog id, so custom_provider:magpie/bb-codex/gpt-6.1-sol was refused (AGENT_CONFIG_INVALID). - The writer keys each model by its catalog id with "~" for each slash (bb-codex~gpt-6.1-sol, provider.FlatID), when exactly one served id spells that way (provider.Flattened); else it keeps the slashed id. - The gateway takes such an id back to that one provider's model or group in validateModel and unprefixed (unflat / provider.Unflat): only an id with "~", no slash, that resolves as nothing else; two ids spelled alike are neither. Never a bare-name first hit. Key limits, middleware and usage see the catalog id, so a key held to bb-codex/* is refused other~gpt-6.1-sol on every route, count_tokens too. - A config an older magpie wrote moves on the next sync: the user's keys on a model go to its new key, defaultModel, defaultLightModel and memory.captureModel/consolidationModel follow, and the slashed key stays with enabled: false, so a session begun on it still resumes (checked with mcode exec --continue). Get still reads magpie/<catalog id>, and Disconnect still puts back the stashed default and variant. Other agents: no installed agent bundle here (cline, command-code, qwen-code, deepseek, pi, omp, codebuff, openclaw) has this regex; native agents were not checked. Verified: - go test -tags nogui ./internal/agent ./internal/gateway ./internal/provider (temp HOME), gofmt -l, go vet darwin/linux/windows. - New TestMiniMaxCodeOneSlash uses the reporter's regex and ids; TestFlatModelIDsReachTheirProvider has two providers serving gpt-6.1-sol. Breaking unflat, mcodeKey, the key carry-over, the legacy enabled:false and the ref rewrite each turned the tests red. - Real run: built magpie serve in a temp HOME on 47812, two fake upstreams; `magpie mcode magpie/other/gpt-6.1-sol` wrote other~gpt-6.1-sol; real mcode 0.6.5 exec reached other's upstream with gpt-6.1-sol (usage: other/gpt-6.1-sol); its agent loader accepted an agent.md with custom_provider:magpie/other~gpt-6.1-sol and refused the two-slash one; `magpie mcode default` put minimax/MiniMax-M2.7 back. |
||
|
|
e66fa16593 |
codex: magpie's actor header goes into a [model_providers.magpie.http_headers] table the config already has, instead of failing every model switch (wztlink1013 on Discord)
wztlink1013 (v0.1.1134, Windows, Codex 0.162.0) could not switch Codex's
model on the Agents page: "config.toml: edited TOML is invalid: toml: key
http_headers should be a table, not a value".
Cause: putProvider rewrites [model_providers.magpie] whole with
http_headers = { "x-openai-actor-authorization" = "magpie" }. A config
that holds the headers as their own [model_providers.magpie.http_headers]
table keeps that table (a separate block the rewrite leaves), so the file
defined http_headers twice and writeTOML refused it. Reproduced in a temp
dir with exactly that message, in LF and CRLF files alike, so line
endings are not the cause. With only the subtable and no parent table the
same edit failed as "key http_headers is already defined". Dotted keys
inside the table (http_headers."x-org" = …) did not fail but were
dropped, the user's other headers with them.
Fix: edit.Inline, an inline table SetTOMLTable merges into the spelling
the file has: in [name.key] each entry is set with SetTOMLKey's in-place
edit (other headers and comments kept); among dotted keys their lines are
kept as written and each entry set there; otherwise written inline,
byte for byte as before. putProvider passes the header as an Inline.
SetTOMLKey's body moved into setTOMLKeyLines so both share it.
Siblings checked: no other agent writes an inline table with
SetTOMLTable (kimi's providers.magpie and Codex's thread tables are
scalars only); the MCP writer (putCodex) already removes a server's
subtables before writing it. Not changed: a magpie provider spelled by
dotted keys outside its own table ([model_providers] magpie.x = …, or at
the top) still fails as "table magpie already exists"; that is not this
report's shape.
Run:
- TestSetTOMLTableInlineMergesUsersSpelling (internal/edit): own table,
own table without the header, own table only, dotted keys (with and
without the header), inline, absent; each LF and CRLF, checked by
decoding the result. Red with the subtable branch disabled (the
reported error) and with the dotted branch disabled (user's x-org lost).
- TestCodexMagpieHeadersAsOwnTable (internal/agent): the reporter's shape
through codex().Fields[0].Set, LF and CRLF. Red with putProvider back
on edit.Raw: "key http_headers should be a table, not a value".
- gofmt -l (clean); go vet, GOOS=linux go vet -tags nogui, GOOS=windows
go vet (with and without nogui) on internal/edit and internal/agent;
go build for darwin, windows and linux (nogui).
- go test -tags nogui ./internal/edit ./internal/agent ./internal/library
./internal/provider under a temp HOME with pinned caches: all ok.
- codex-cli 0.161.0 (CODEX_HOME in a temp dir, codex mcp list) loads the
merged config. Not tried on the Windows box or Codex 0.162.0.
|
||
|
|
98236edd77 |
agent: Codex installed by install.ps1 on Windows shows its newest version and offers codex update, and its version is read again after each update (xiaoxiaofeixz on Discord: 不会自动检测最新的版本)
Codex's install.ps1 puts %LOCALAPPDATA%\Programs\OpenAI\Codex\bin on PATH. That folder is a junction to ~\.codex\packages\standalone\current\bin, and current is a junction to releases\<version>-<target>, retargeted by each update. filepath.EvalSymlinks leaves junctions alone since Go 1.23, so magpie saw the binary only as ...\Programs\OpenAI\Codex\bin\codex.exe: - howInstalled didn't find .codex/packages/standalone in its path, so the Agents page showed the version with no newest version and no update. - installedVersion's stamp was that unchanged path plus size and mtime. npm's archive, which install.ps1's legacy layout copies from, gives every version's codex.exe the same 1985 mtime. So a new release was asked again only if its size happened to differ, else after the 24h memo. proc.RealPath is now the path Windows opens (GetFinalPathNameByHandle), junctions followed, and filepath.EvalSymlinks elsewhere. cliupdate's howInstalled and installedVersion and proc.Version use it. TestCodexInstallPs1Junctions (Windows) builds install.ps1's layout with mklink /J, two releases whose codex.exe have the same bytes and mtime. It checks that codex is recognized as self-updated (codex update) and that retargeting current makes the version asked again. On the Windows box (10.0.0.132, sandboxed USERPROFILE/APPDATA/LOCALAPPDATA) it fails on the old code (howInstalled nil; "0.161.0" after the update) and passes on the new. The rest of the agent package fails on Windows with the same 17 tests before and after (TestFolderVarsAreListed, TestAside*, TestVSCodium, ...), which CI never runs there. `codex update` on Windows is in codex.exe 0.162.0 (win32-x64): it runs install.ps1. The layout was read from https://chatgpt.com/codex/install.ps1. Not tried with a real install.ps1 install (it rewrites the user's PATH in the registry). go test -tags nogui ./internal/agent ./internal/proc ./internal/provider ./internal/library (macOS), gofmt, and go vet for GOOS=linux and windows pass. |
||
|
|
8e0e7d3c40 |
dsh: the desktop app's profile made while magpie runs gets magpie's route within a round, not at magpie's next start (star on Discord)
star on Discord reported DeepSeek Harness Desktop with the red
"已不经过 magpie" line. One way there that a desktop user meets: the
CLI's web profile wired, then the desktop app opened for the first time
while magpie runs. dsh makes profiles/desktop/cordis.patch.yml as its
empty template ([]), dshCheck says the desktop profile has none of
magpie's models (Drift "unwired"), and only dshSync — the catalog sync,
at magpie's start or a catalog change — filled new profiles, so it stayed
so until Reapply or a restart. The reporter's exact detail is cut off in
the screenshot, so this is a likely cause, not a confirmed one.
dshWiredOnce, the 30s serving round, now runs dshFillNewProfiles over a
profile that is dsh's empty template, beside the wired ones: magpie's
route and start go in as the catalog sync would put them. A profile with
entries of its own and no route is the user's and is still left alone.
Run: TestDshDesktopProfileMadeWhileServing fails on the old code ("the
round left the desktop profile bare"), passes with the fix, and checks a
user's own unrouted profile is not written. go test -tags nogui
./internal/agent ./internal/gui under a temp HOME with pinned caches: ok;
go test -race -run TestDsh -count=3: ok. gofmt clean; go vet darwin,
GOOS=linux -tags nogui, GOOS=windows; go build darwin, linux nogui,
windows: ok. Not tried with the real desktop app live.
|
||
|
|
ee3f83b59d |
agents: DeepSeek Harness Desktop installed is no missing dsh CLI — its version shows instead of 未找到 CLI (star on Discord)
star on Discord: "DeepSeek Harness 就是现在已经有桌面版了,但是他还是非要找CLI". With DeepSeek Harness Desktop in use and no dsh on PATH, the Agents page said 未找到 CLI and Install another agent offered npm's dsh: CLIMissing (#843) took ~/.dsh without a CLI for an uninstall. The desktop app (DeepSeek Harness.app, com.deepseek.dsh, 0.2.0-rc.2 from download.deepseek.com) keeps the same ~/.dsh — its own profile, profiles/desktop/cordis.patch.yml, and .credentials.yaml — and needs no CLI; its terminal command (Manage dsh Command…) is optional. - desktopapp.go: dsh's app is known by its bundle id on macOS and by %LOCALAPPDATA%\Programs\DeepSeek Harness\DeepSeek Harness.exe on Windows (electron-builder per-user NSIS, productName DeepSeek Harness, getWindowsInstallationDirName); HasApp says either is installed. - install.go: CLIMissing is false with the agent's desktop app installed, so the GUI row, Install another agent and /api all follow; the app's version shows through the existing CLI().App path. - dsh.go: the restart notice also sees DeepSeek Harness.app running. The red "已不经过 magpie" line in the report is a separate dshCheck finding, not caused by the missing CLI: a desktop-only ~/.dsh shaped from the app's real new-profile template wires cleanly (Check "", no Drift; TestDshDesktopOnlyWired). The reporter's exact detail is cut off in the screenshot and is not reproduced. Run: TestDshDesktopAppIsNoMissingCLI fails on the old code ("with the desktop app: missing true, offered true"; with the Windows path removed, "with the Windows app: missing true, offered true"), passes with the fix. go test -tags nogui ./internal/agent ./internal/gui under a temp HOME with pinned caches: ok. gofmt -l clean; go vet darwin, GOOS=linux -tags nogui, GOOS=windows on both packages; go build darwin, GOOS=linux -tags nogui, GOOS=windows: ok. Not tried: the real desktop app running live (not installed here; its userData isn't redirected by a sandbox HOME), a real Windows install. |
||
|
|
4dc7222fdc |
kimi: each of magpie's models in Kimi Code has its name (display_name) and, where magpie knows it, its most output (max_output_size); a value the user set in the table stays (Sorghum on Discord)
What: kimiModelTables writes display_name (the name magpie shows for the model, left out when it is just the id) in every [models."magpie/…"] table, and for the new Kimi Code max_output_size (the catalog's output, capped at the window by maxTokens; left out when unknown). kimi-cli gets display_name only: it has no max_output_size. What magpie last wrote in each table is kept in the stash (kimi:<path>:wrote); a value there that isn't it is the user's and stays, while magpie's own follows the catalog and goes when the catalog no longer knows it. The record goes when magpie steps out. Why the keys are safe: Kimi Code (MoonshotAI/kimi-code) has had both maxOutputSize and displayName on its model alias since 0.2.0, its model record passthrough on 2.x (agent-core-v2 kosongConfig/configSection.ts); max_output_size becomes the request's max_completion_tokens for the kimi type. kimi-cli (MoonshotAI/kimi-cli) added display_name to LLMModel in 1.37.0 (0bc7928, #1948); its config never sets pydantic extra, so older ones ignore the key. Run: TestKimiNamesAndOutput (new; both config dirs, with hand-set values) fails three ways with the change broken: user values not kept (display_name "Big One · Relay", want "My Big"), magpie's stale value kept (max_output_size 32000, want 64000), neither key written (display_name ""). go test -tags nogui ./internal/agent/... (temp HOME, caches pinned) ok; gofmt -l clean; go vet + GOOS=linux/windows go vet ./internal/agent ok; go build, GOOS=linux go build -tags nogui, GOOS=windows go build ok. Real use in a temp HOME: Kimi Code 2.1.1 (npm) `kimi doctor config` OK on the written config, and `kimi -p` against a local fake server answered, its request carrying max_completion_tokens 32000; kimi-cli 1.52.0's own load_config read display_name and ignored an extra max_output_size. |
||
|
|
9fbdd584ed |
codex: a thread started on a provider config.toml no longer has opens again, through magpie (#1372, obstrux: "ChatGPT 无法加载 config.toml … Model provider custom not found")
The Codex app resumes a thread on the provider it was started on, kept in
Codex's state database (app-server thread_processor.rs:
merge_persisted_resume_metadata), whatever config.toml's model_provider is
now, and loads no config for it when config.toml has no table of that id
(core config: "Model provider `{id}` not found"). CC Switch puts every
third-party thread on "custom" and rewrites config.toml whole when it
switches, so a switch to its official provider leaves those threads with no
table, and the app can't open them. magpie never removes [model_providers.custom],
but nothing put it back either.
While magpie is wired into Codex (takeTables: set, Join and Sync), each
provider a thread in state_*.sqlite names that config.toml has none of
(table, inline or dotted) gets a table pointed at magpie's gateway, as
CC Switch's own tables already are. Codex's built-in providers (openai,
ollama, lmstudio, amazon-bedrock[-runtime], ollama-chat) and magpie's own
are left out, and a provider the user has stays as it is. The table stays
when Codex steps off magpie, as magpie's own does, for the threads that
name it. Not wired, nothing is written. sessions.CodexThreadProviders reads
the distinct model_provider of the threads table, read-only.
Run:
- Real Codex 0.161 in a sandbox HOME: `codex exec` recorded a thread under
a [model_providers.custom] pointed at a fake Responses server, the table
was removed, and the app-server's thread/resume answered "failed to load
configuration: Model provider `custom` not found" (the reporter's error).
With this build's `magpie codex fakerelay/m1` (sandbox HOME, gateway on
127.0.0.1:3546), [model_providers.custom] was written to the gateway, the
same thread/resume succeeded, and a turn on it completed through magpie to
the provider (its request reached the fake). The thread kept "custom".
- TestCodexWritesTablesThreadsName (signed in and not) fails without the
call ("custom not written through magpie"), and its inline case fails
without the parsed check ("edited TOML is invalid: key inl should be a
table"); TestCodexThreadTablesOnlyWired.
- gofmt -l, go vet, GOOS=linux/windows go vet -tags nogui on agent and
sessions, GOOS=linux go build -tags nogui, GOOS=windows go build, and
go test -tags nogui ./... under a temp HOME. One full run failed
TestSetAnswersWhileDevinAndProvidersHang (internal/gui, set took 1.6s
against its 1.5s wall-clock bound; Claude only, Codex not wired, so this
code isn't on its path); it passes alone 5/5 (0.19-0.42s) and in a rerun
of gui, agent and sessions together.
|
||
|
|
58ba737811 |
remote magpie: a remote magpie's model prices are this magpie's list prices for its models, so both Usage pages count a call at one cost (Sorghum on Discord: 希望本地 magpie 可以自动集成 remote magpie 手动设置的模型价格)
What changed
- The local magpie asks a remote magpie's /v1/models with a new header,
X-Magpie-Prices (provider.PricesHeader, beside the Drawers and
Videomakers headers in listHeaders).
- The remote answers each model's "magpie_price": what its own Usage page
counts the model at (provider.EntryPriceIn: the price its user set by
hand, else its provider's list price at its price rate; a routing group
only when every member costs the same). EntryPriceIn is agent/route.go's
entryPrice moved into provider unchanged, so the agents' cost and the
remote's price are one rule.
- It tells prices only to a request from its own computer or one let in
with a gateway key (local || sharedWith), and only when asked: a machine
let in without a key (MAGPIE_ADDR open, not shared) and an older magpie
or an agent that sends no header get nothing new.
- The local keeps the price with the fetched list (catalog.Model.Price; read
raw, so an odd value or a negative part drops the price and keeps the
model and the list) and uses it as that provider's list price
(ListPrice -> remotePrice). A price the local user set (provider/model,
provider/*, */model) still comes first, and the local provider's own
price rate still applies on top.
- Old remote <-> new local: no field, models priced as before.
Old local <-> new remote: no header, nothing new sent (and an unknown
field would be ignored by encoding/json anyway).
- The model editor's price tooltip and `magpie model price` say the greyed
price is what the other magpie counts the model at (remoteList; new
string in zh, zh-TW, ja, de).
- Side effect: smallModel/cheapest picks over a remote's models (image
description, web search) now see these prices, where every remote model
was unpriced before.
- docs/subsystems/providers-accounts.md: a row for it.
What was run
- New TestRemoteMagpiePrices (real gateway handler behind lanGuard as the
remote, a local remote-magpie provider fetching it): the remote's hand-set
price is the local's price; a real chat call through it costs $9 on both
the local's and the remote's usage records; the local's own price wins
and survives a refetch; an old remote's list leaves the model unpriced.
New TestRemoteMagpiePricesTold: priced for loopback asking, not without
the header, not for a keyless LAN caller with MAGPIE_ADDR open, priced
with a gateway key. New TestLiveMagpiePrice in catalog.
- Broken by hand, each red: no remotePrice in ListPrice (FAIL: priced
false); remote price over the user's (FAIL: remote's price won); the
local/sharedWith gate removed (FAIL: keyless machine priced); the header
gate removed (FAIL: told to a list that didn't ask); sane() always true
(FAIL: a/negative priced). A string-typed magpie_price first failed the
whole list ("not a model list"), hence the raw read.
- gofmt -l clean; go vet ./..., GOOS=linux go vet -tags nogui ./...,
GOOS=windows go vet ./...: clean; go build, GOOS=linux -tags nogui and
GOOS=windows builds OK; go test -tags nogui ./... green under a temp HOME
with pinned caches.
- GUI: model-price-edit (new remote-title check), model-price-tiers,
provider-price-rate, gui-ja, gui-de, gui-zh-tw, i18n-keys: 18/18 in
Chromium and WebKit; the title check goes red with the remoteList branch
removed.
- Not tried between two real magpie binaries on two computers, nor in the
real window.
|
||
|
|
ae24bcfcfb |
agent, gui: Codex's row on the Agents page shows its desktop app's version beside its CLI's ("CLI 0.130.0", "App 26.930.31730"), and the app's alone where no CLI is on PATH (#1334, laacmilan)
The app's version is read from /Applications or ~/Applications
ChatGPT.app (or Codex.app) Contents/Info.plist, taken only when its bundle
id is com.openai.codex, so the ChatGPT chat app (com.openai.chat) isn't
taken for it. macOS only: the Windows Store package isn't read yet. The
app chip's tooltip says it reads the same settings as the CLI, so
connecting Codex connects both. At narrow widths the chip ellipsizes, and
below 250px it gives way so the name keeps its room.
Verified: TestCodexAppVersion fails without the CLI() change ("the app
alone, no CLI"); agent-app-version.test.cjs fails 12/16 without the app.js
change and passes 16/16 with it, Chromium and WebKit, en/zh/ja/de, at 980,
560 and 440px. agent-cli-missing, agent-connect, agent-field-names,
agent-layout, agent-update-all, cli-update, click-scroll, gui-ja, gui-de,
i18n-keys pass in both engines. internal/agent passes in Linux Docker
(non-root); full ./... on macOS; vet darwin/linux/windows. Read
26.930.51102 from the real /Applications/ChatGPT.app on this Mac. Not
tried on the reporter's machine.
|
||
|
|
172f45a5e6 |
agent: dsh's check stays quiet when its key store holds a caller key of this gateway's (#1332) (#1339)
The drift check asked only whether refs.MAGPIE_GATEWAY_KEY equals the key magpie writes into .env, so a caller key the gateway itself issued — which dsh's store prefers and the gateway authenticates — was named as "another MAGPIE_GATEWAY_KEY" and the user was told to remove it there, while delete → disconnect → re-apply wrote the very same state back. The check now asks the gateway's own Authenticate first: a key it takes means dsh is going through magpie, and only a key it would refuse (a foreign one, a switched-off one) is drift. The .env side needs nothing: the serving round rewrites .env to magpie's key, so a stray value there heals itself. TestDshStoreHoldingAGatewayCallerKey (a key from access.Update add-key in the store's refs) failed without the change with the "holds another MAGPIE_GATEWAY_KEY" drift; with it the check is quiet, and a key the gateway doesn't take still reports. Verified: go test -tags nogui ./internal/agent -count=1; the three dsh store tests under -race -count=10; gofmt -l clean; go vet -tags nogui ./internal/agent; go build and GOOS=linux/GOOS=windows go build -tags nogui. Generated-by: GLM-5.3-Flash (ZCode) |
||
|
|
0feffaa7f9 |
agent: TestFolderVarsAreListed knows MAGPIE_PARTNERS is magpie's own partner-list source, not an agent's folder (red on main since 10578ce6)
Run: go test -tags nogui ./internal/agent under a temp HOME: FAIL before
("MAGPIE_PARTNERS, read by ../provider/partners.go, is not in
agentenv.Vars"), ok after.
Co-Authored-By: Claude <noreply@anthropic.com>
|
||
|
|
1d1c9e07d2 |
agent: without dsh, the dsh round every 30 s doesn't build magpie's whole catalog for no patch list (H20 on Discord: magpie idles near 500 MB)
dshWiredOnce built magpieModels("dsh") before looking for a profile, so every magpie, dsh or not, built the catalog every 30 s and threw it away: 93% of what a magpie at rest allocated (pprof heap?gc=1 delta over 120 s, a linux nogui `magpie web` in Docker on a copy of a real config with 13.5k Codex and Claude Code sessions mounted read-only). That garbage kept the heap at its GC goal, about twice what it keeps. It now returns before the catalog when there is no profile; with one, nothing changes.
Same measurement, this commit and the warm-up one together: idle allocation 73 MB per 2 min -> 6.8 MB (3.8 MB of it the profile read itself); RSS at rest after the first minutes ~250 MB -> ~203 MB, the heap no longer climbing 125 -> 192 MB between GCs. TestDshRoundWithoutDshBuildsNoCatalog fails without the change (475 allocations a round) and passes with it. go test -tags nogui ./internal/agent (Docker golang:1.26.8, non-root, temp HOME), go vet -tags nogui ./internal/agent for linux, windows and darwin, and gofmt -l pass. Not measured on macOS: a fresh build there hangs at exec behind syspolicyd today.
|
||
|
|
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 |
||
|
|
7611c36e6f |
agent: wiring Alma also makes the model magpie draws with Alma's image generation model, when Alma has it on Auto (Sorghum on Discord)
Alma has one: Settings › Image Generation, settings.imageGen.model,
"<providerId>:<model>" like chat.defaultModel, "" for Auto (Alma's
src/services/settingsApiService.ts defaults). Its `alma image gen` and
in-chat image requests send an openai-type provider's model to
{baseURL}/images/generations, which the gateway draws with.
Choosing one of magpie's models for Alma now also sets imageGen.model to
magpie's provider and the model magpie draws with (gateway.Drawer: the
Settings' Image generation model, else AutoDrawer's), only when it is
Auto: a model the user picked, theirs or magpie's, is left. Sync leaves
an Auto alone (the user may have gone back to it) and only replaces one of
magpie's that magpie no longer offers. That model is listed on magpie's
provider with capabilityOverrides.imageOutput, as Alma's picker lists only
a provider's models that make images and shows another as "unavailable".
Going back to Alma's own model puts an image model of magpie's back to
Auto with the provider. Nothing changes when magpie draws with nothing.
Verified: TestAlmaImageGen against the fake Alma (settings kept whole,
aspectRatio untouched): Auto filled with p1:img/gpt-image-1 and the model
listed with imageOutput; the user's own pick and a magpie chat model kept
by Apply and Sync; Auto kept by Sync; a stale magpie drawer replaced; Auto
again on going back. It fails without almaSetImage ("image generation:
\"\", want \"p1:img/gpt-image-1\""). TestAlma checks nothing is written
when magpie draws with nothing. Go tests ran on Linux (golang:1.26 in
Docker). Not tried with the real Alma: Alma's settings page reading the
override as an image model, and an image drawn through it, are unverified.
|
||
|
|
cf10394527 |
agent: alma-server on Linux is found and wired as Alma, so its requests show as Alma, not "ai-sdk" (Lutra.x on Discord)
alma-server (Alma without a desktop, Linux x64/arm64) keeps its data in
$ALMA_DATA_DIR, else $XDG_DATA_HOME/alma, else ~/.local/share/alma
(Alma's electron/headless/electron-shim.ts getDefaultUserDataPath), and
answers on the same localhost:23001. magpie looked only in the desktop's
folder (os.UserConfigDir()/alma), so on such a machine Alma's Sync never
ran, its magpie provider kept the old shared key, and the gateway read its
requests by their User-Agent alone, the AI SDK's
("ai-sdk/openai/4.0.15 ai-sdk/provider-utils/5.0.10 runtime/node.js/22",
@ai-sdk/openai 4.0.15 and provider-utils 5.0.10 as alma-server bundles
them; Alma sends no header of its own). almaDir now also looks in
alma-server's folders on systems other than macOS and Windows, the
desktop's first; ALMA_DATA_DIR is in agentenv's Vars.
Verified: TestAlmaServerKeyed (a Linux home with only
~/.local/share/alma and a fake Alma whose magpie provider has the old
key): Sync re-keys it to magpie-alma; XDG_DATA_HOME and ALMA_DATA_DIR are
followed, the desktop's folder wins, and macOS/Windows look only in the
desktop's. It fails without the change ("Alma's folder:
.../.config/alma, want alma-server's .../.local/share/alma").
TestAgentOf has alma-server's real User-Agent bytes: Alma with magpie's
Alma key, "ai-sdk" without. Go tests ran on Linux (golang:1.26 in Docker;
this Mac's syspolicyd held every new test binary at launch).
Not tried with a real alma-server, and an alma-server on another
computer, reaching magpie with a gateway key, still reads as the AI SDK:
its User-Agent says nothing of Alma.
|
||
|
|
169c23c18e | stats: the day's event says which agents, providers and models magpie is used with, by magpie's own ids (a provider of the user's own, and its models, only "custom"), with how many of each and of routing groups: one "magpie uses" event per id in the same PostHog batch, so unique users break down by agent, provider and model. Settings → Privacy → Share the agents, providers and models I use turns it off (on by default; gone while Count me as a user is off); no names, URLs, accounts, keys or usage. go test ./internal/stats (payload, off switch, labels), the Privacy switch in Chromium and WebKit (en, zh), go test -tags nogui ./... in a temp HOME, go vet, GOOS=linux/windows builds pass; read against the real config: 9 agents, 24 providers, 25 groups in 0.75 s | ||
|
|
78d90571cf |
agent: each named omp profile is an agent of its own, on Windows and in each WSL distro (#1233)
* agent: each named omp profile is an agent of its own, on Windows and in each WSL distro (#1187) The Agents tab's omp row only ever wrote the one folder ompDir picks, so a profile started with omp --profile kept the model it had. ~/.omp/profiles/* is now listed per location: omp#<name> on Windows, omp#<name>@wsl:<distro> in a distro, each with the omp row's model, role and thinking controls writing that profile's own config.yml and models.yml. The profile the default row already points at (OMP_PROFILE) is not listed twice, and a stopped distro is not started to list its profiles: the names come from the probe and stay in wsl.json. * gui: an omp profile's id is encoded in the Agents page's requests (#1187) omp#<name> went into /api/agents/connect and the rest raw, so a browser read #work as the fragment and connected, disconnected or previewed the default omp row instead. api() now encodes each path segment, which is what agent-models and agents/cli already did for the id alone. * agent, gui: an omp profile name is one omp takes, and only its # is encoded (#1187) api() encoded every character of a path, so a query's ? = & became part of the path and Usage, quotas and Sessions 404ed. Only # is encoded now, which is what omp#<name> needs; a query stays a query. ompProfileOK also refuses the Windows device names omp refuses on every platform (aux, nul, com1, lpt9.x, con.work), and work. is checked through that rule rather than by making the folder, which Windows would turn into work. |
||
|
|
d2241a5798 | agent: Copilot in JetBrains IDEs takes magpie's models as a Bring Your Own Key custom endpoint provider, written into the Copilot language server's byok.json beside the user's own (KamToHung on Discord) | ||
|
|
d7bdb0765f |
agent: with magpie Codex's provider, Codex's own models on the Agents page say what picking them does: one magpie serves on a ChatGPT account is picked as magpie serves it and Codex stays on magpie; any other reads "direct, not via magpie" and asks before taking Codex off magpie (#1269, shenghsi)
Picking Codex's own gpt-5.5 tagged "via magpie" while magpie was Codex's provider took Codex off magpie at once, with no question: the tag came from routed(), but set() unroutes for a model of Codex's own unless it is joined beside a ChatGPT sign-in or on magpie API. Verified: TestCodexProviderOwnModelSaysItsPath fails without the change (gpt-a reads Via:true with no ChatGPT account; Value gpt-a, not codex/gpt-a, with one), passes with it. Reproduced and checked in a sandbox HOME with Playwright against `magpie web`, on macOS and on Linux (WSL2 Ubuntu 24.04, codex-cli 0.161.0): before, picking gpt-5.5 left Codex "Not connected"; after, it reads direct and opens "Take Codex off magpie?". go test -tags nogui ./... green; go vet for darwin, linux, windows on internal/agent. |
||
|
|
e5e87d980a |
支持 Ante:把 magpie 作为 provider 写进 ~/.ante/catalog.json (#1210)
* agent: Ante takes magpie as a provider in ~/.ante/catalog.json Ante (antigma.ai, AntigmaLabs/ante) is asked for in #1179. Its catalog is ~/.ante/catalog.json ($ANTE_HOME moves the folder), merged on top of the providers it ships: a providers map keyed by the id --provider takes, each with base_url, wire_style (AnthropicMessage, OpenAiCompatible, OpenAiResponse or Gemini), an optional auth, and preferred_models, the ids its model picker shows (docs.antigma.ai, reference/catalog-reference). magpie adds the entry "magpie" at the gateway's /v1 on OpenAiCompatible, which is the wire style Ante speaks Chat Completions with and what the gateway takes every model on, and lists magpie's models in its preferred_models. The user's own providers and the models section are left as they are, and magpie's entry goes again when magpie steps out; a file magpie made goes with it. No auth block is written. The gateway listens on loopback and lets any token in from this machine (gateway.Token's own comment, lan.go's identifyCaller: "Loopback takes any token"), and Ante counts a provider with no auth as authenticated. A bearer env_key would mean putting the gateway's key into Ante's environment, which the user would have to do themselves -- the same decision Empryo's entry makes, which sends no key of the user's either. A gateway shared on the local network is another matter, and the same one every agent magpie wires has. Two things are deliberately left off a model's entry. A routing group is not told what it reasons at: which member answers, and what that member takes, is the group's own to decide per turn, and magpieModels sets no APIs for a group for the same reason. And support_vision is never written: Ante takes a model as seeing images unless told otherwise, and what magpie knows is only as good as a provider's own list -- a relay's models are often not described at all, so writing false would have Ante drop the reader's images before the gateway, which describes them to a model that can't see them itself (provider.Described). No other agent magpie wires declares it either. Ante ships macOS and Linux builds alone and suggests WSL on Windows, so the Windows machine's Ante is usually the one in a distro: wslKinds gains it beside Empryo, which also means the gateway address Ante is pointed at is the one that distro reaches magpie on (place.base), and a distro not known to be in mirrored networking gets the notice it already gets for the others. TestAnte covers a user's own providers and models section kept as they are, magpie's entry added and taken away, Sync repointing it, and a file magpie made going again. TestAnteKeepsTheUsersFile covers a catalog magpie can't parse costing magpie's provider and not the user's. TestAnteGroupSaysNoCapab ilities covers a group declaring neither effort nor support_vision. TestAnteEfforts covers the ladder: Ante's has no "none" or "minimal", its two lowest, so both are its "min", and the rungs come out in Ante's order whatever order magpie lists them in. Tried with a real Ante 0.2.9 in WSL Ubuntu: magpie wrote the catalog, and `ante catalog` then read 16 providers with magpie's among them, 39 models, and Ante filled in support_vision: true for each itself. Ante then ran a real turn through the gateway — `ante --provider magpie --model group/auto-deepseek-v4-1-flash -p …` answered, and magpie's own usage log has the request (ep /v1/chat/completions, req the group's id, served by one of its members, out 10) — and so did a second model on another provider. The gateway was reached over the Windows host's address from the distro (172.17.128.1), this machine's own being on 127.0.0.1 alone, which is what notMirrored tells the user about. go vet, gofmt -l and go build (linux nogui, windows) pass. go test -tags nogui ./internal/agent: 12 failures here, the same 12 on unmodified main (the Windows ones AGENTS.md lists). ./internal/gui and ./internal/provider: TestGatewayFixQuitsOnlyAMagpie, TestPurposeAssetMatchesUsage, TestUpdateThatWontStart and TestLoginUsageClaudeAskedMeanwhile, all four on unmodified main here too. docs/reference.md's adapter table gains Ante. * agent: Ante keeps the user's own keys, reads ANTE_HOME, sends no effort it wasn't told to, and starts Ante on magpie (#1210) Seven points from the review, each with a test that fails without it. 1. ANTE_HOME was in the file's own comment and in docs/reference.md but never read: the path was fixed to $HOME/.ante, so a user who set it had magpie write a catalog Ante does not look at. anteDir reads it through place.getenv, the way MiniMax Code reads MINIMAX_DATA_DIR, so a distro's own ANTE_HOME - which magpie can't read - still means ~/.ante there. "ANTE_HOME" joins agentenv.Vars so a sandbox clears it. 2. providers.magpie was written whole, so every key of the user's that magpie doesn't write - the auth block a gateway shared on the LAN or reached from WSL needs to send a key, http_headers, extra_body, stream_idle_timeout_secs, thinking_display - went at the next sync, and an unchanged catalog was rewritten every round. It goes through theirsKept and syncJSON now, as #1103 did for the others: the user's keys in magpie's entry stay, preferred_models is magpie's list alone, and a file that says the same as it does is left as it is. 3. The two files were written in separate steps, so a change that failed halfway left Ante's catalog without magpie's entry and its settings still naming magpie. Each field's Set and its Sync are wrapped in atomic(), as empryo, minimax and kimi do. 4. The comment said Ante's "min" was the rung it sends no effort at, and folded both magpie's "none" and its "minimal" into it. Ante's reference says what min is: "OpenAI-compatible providers send the selected level as the same-named reasoning_effort (min is sent as minimal)" - so min is a sent level, and magpie's "none" has no rung of Ante's to fold into. Ante's "min" is magpie's "minimal"; a model magpie knows only "none" of comes out as the empty list, which is Ante's own way of saying it takes no effort setting at all. 5. The default was the highest rung the model takes, so every model magpie listed ran at Ante's slowest and dearest level. No effort is written: "Omit for models with no reasoning knob (Ante then sends no effort parameter and hides the effort slider)". 6. Having magpie in the catalog never meant Ante used it. What a session starts on is the provider and the model of ~/.ante/settings.json; with neither set Ante "uses the first authenticated provider in catalog order", and Ante's own presets come before magpie's entry - so while the user has any of those signed in, that is the one picked and magpie is not. (Measured: nothing named in settings, two keyless providers ordered aaa_other then magpie, and the request went to aaa_other's port; settings provider=magpie and it went to magpie's.) magpie sets that pair now and saves what it found there, so Disconnect hands it back; a provider the user picks in Ante's own /providers while magpie is wired is theirs and stands, and Check says so instead of keeping quiet. Ante's settings name the model apart from the provider, so magpie writes the catalog id it put in preferred_models, un-prefixed, and reads a bare id as magpie's only beside magpie's provider. Tests lean on the package's own testenv, not a real ~/.ante. TestAnteSyncKeepsTheUsersKeys is modelled on ad3b7fe5's TestAirProviderWritesKeepCustomFields and goes both ways - sync and pick - and wants an unchanged file to keep its mtime. Tried with a real Ante 0.2.9 in WSL Ubuntu on this machine (Ante ships macOS and Linux builds alone; there is no Windows Ante here). The strongest of these is the whole of it: magpie's own Go code - ante(home).Connect(), then the model field - wrote a catalog.json and settings.json into a temp folder, that folder was handed to the real Ante as ANTE_HOME, and Ante ran a turn through it with no --provider, no --model and nothing typed by hand. - `ante doctor` read `home ok /tmp/ante-e2e` and `16 providers (1 authed), 108 models` from ANTE_HOME, and magpie's entry came back with the base_url it was given: ANTE_HOME really moves the folder, and a base_url already ending in /v1 is not doubled (the request went to /v1/chat/completions). - What a session starts on is settings.json's provider, measured against two keyless providers each answering on its own port, so the logged port names the one Ante asked. Catalog ordered aaa_other then magpie, nothing named in settings: Ante asked 44331, aaa_other - the first authenticated in catalog order, exactly the review's "很可能选不到 magpie". settings provider=magpie: it asked 44220, magpie. settings provider=aaa_other: 44331 again, so a pick the user makes in Ante's own /providers overrules what magpie wrote, which is why stepping out leaves it rather than handing back the one magpie found. And with magpie the only authenticated provider, auto-detection does reach it - so the catalog entry is not useless, it is simply not enough while the user has any other provider signed in, which is every real install. - What Ante sends for effort, from the same files: no reasoning_effort at all for a model with no effort written (item 5, as its reference says omitting it sends none); `--effort min` on a model sent `reasoning_effort: "minimal"` - Ante's min is magpie's minimal, exactly item 4; and a model Ante was told `"supported_efforts": []` for sent no reasoning_effort even when asked `--effort low`, which is what a none-only model now gets instead of a level that refuses it. - The user's own keys inside magpie's entry are ones Ante reads, not decoration: `ante catalog` hands back the entry's auth, http_headers, extra_body, stream_idle_timeout_secs and thinking_display unchanged, and a bearer env_key there makes `ante doctor` count the provider as the one authed. That is item 2. - The no-auth decision holds on Ante's side, isolated by doctor's counts: Ante's own presets alone read `15 providers (0 authed)`; a magpie with no auth block reads `(1 authed)`; the same magpie given a bearer env_key that isn't set drops back to `(0 authed)`; with the key set, `(1 authed)`. So an entry with no auth is authenticated and one that asks for a key the user doesn't have is not - writing an env_key, which the first version's comment considered, would have made magpie the one provider Ante refuses to auto-detect. - Ante fills `support_vision: true` in for itself, as the PR's own note said, and a routing group declared with no capabilities at all still worked. Not covered by that run: the stand-in server answered inside the distro, so this proves Ante's config handling end to end, not a live magpie gateway relay (the first commit's message records a real turn through one). Every probe used ANTE_HOME into a temp folder; this machine's real ~/.ante gained no catalog.json or settings.json. What each fix failed without it, one test each: taking ANTE_HOME out -> TestAnteHome "$ANTE_HOME ignored"; theirsKept out -> "the user's auth lost or changed"; syncJSON out -> "an unchanged catalog was rewritten"; atomic out -> "settings.json kept half of a change that failed"; the old min mapping -> "anteEfforts([minimal high]) = [high], want [min high]"; a written default -> "deepseek/pro: effort written"; settings never pointed at magpie -> TestAnte; a model never written -> TestAnteModelField; Check left quiet -> "moved off magpie: \"\""; handing magpie back over a pick the user made while wired -> TestAnteKeepsWhatTheUserPickedWhileWired; never restoring the model -> TestAnteConnectDisconnect "not put back"; Connect picking the model field instead of magpie as provider -> "Connect left the user's model beside magpie's provider"; ANTE_HOME kept out of agentenv.Vars -> TestFolderVarsAreListed. Taking preferred_models out of the whole list is the one mutation no test turns red on: mergeTheirs cannot merge a JSON array (sync.go returns want for it), so the file behaves the same either way and the argument is there for intent and for the other keys, not for behaviour. go vet ./... and go build ./... pass, as do GOOS=linux go build -tags nogui ./... and GOOS=windows go build ./..., and gofmt is clean on LF content (this clone has core.autocrlf and no .gitattributes, so gofmt -l reports every file). go test -tags nogui ./internal/agent fails twelve tests, and an untouched worktree of origin/main run the same command on this machine fails the same twelve, test for test; ./internal/agentenv, ./internal/appdir and ./internal/edit pass, and every Ante test passes at -count=5. The whole ./... was run on both: the failing sets are alike, origin/main having one more, TestLoginUsageClaudeAskedMeanwhile, already filed as #1228. None of those twelve is this change's, and none is fixed here. * docs: agent-wiring records that Ante keeps the user's keys in magpie's entry and starts on settings.json's provider (#1210) The two rules the review asked for, written where the repo keeps them - the same lines |
||
|
|
1b29cb81a2 |
agent, wslrun, sessions, gui: Settings → General has Detect agents in WSL; off, magpie asks wsl.exe nothing and shows no WSL agents or sessions (#1264)
On Windows magpie lists the running distros and probes each one for agents, their sessions and Claude Code. Etsuya233 asked for a way to turn that off. settings.NoWSLAgents (per machine, kept by KeepOwn) now gates every auto-discovery path: agent.wslAgents/wslHomes/WSLRunning, sessions' wslFiles/wslDirs (via the new sessions.WSLOff hook), and wslrun.Find, which the Claude subscription uses to run Claude Code in WSL. The toggle is shown on Windows only and is on by default. Verified: - red without the change: TestWSLDetectionOff (1 WSL agent with detection off; with only sessions reverted, a distro's session is listed), TestFindWithDetectionOff (Find returned Ubuntu after asking wsl.exe). - go test -tags nogui ./... in a sandbox HOME: every package ok except TestPluginLoginTakesTheLapseOff, which timed out at 63.7s under the full run and passes alone (-count=3) and with its package (-count=1); it doesn't reach WSL. - go vet -tags nogui on agent, wslrun, sessions, settings and gui for darwin, linux and windows. - GUI: wsl-agents-setting (en/zh/zh-TW/ja/de, 900 and 440px) 30/30; gui-ja, gui-de, gui-zh-tw, i18n-keys, keep-awake, lightweight, settings-sections, settings-groups 44/44 in Chromium and 44/44 in WebKit. - Windows box (10.0.0.132), nogui magpie.exe in a sandbox profile with a logging wsl.exe first on PATH: on, `magpie agents` listed Claude Code · WSL magpie-desktop after 3 wsl.exe calls; off, `agents`, `ls` and `sessions` made no wsl.exe call and wrote no wsl.json. Not tried: the toggle in the real Windows GUI window, and Claude Code in WSL through the Claude subscription with the setting off (unit-tested only). |
||
|
|
811681bc72 |
agent: on a Claude model that is no Fable, Claude Code's fable tier is left unset instead of following the main model, so Opus is no longer taken for Fable (Zhenzhen on Discord)
`magpie claude claude/claude-opus-5-5` wrote ANTHROPIC_DEFAULT_FABLE_MODEL= claude/claude-opus-5-5[1m]. Claude Code 2.1.293 takes a model id equal to that variable for a Fable model (Sye/Vle in its bundle), so on a Claude Pro sign-in without Fable it asked for usage credits and fell back to Haiku. follow() now gives the fable tier no model on a Claude model that isn't a Fable; writeTiers leaves such a tier out of the env. On a Fable main model, and on another vendor's model (Claude Code knows nothing of it), the fable tier follows as before. Claude Code's own /model fable still runs on the main model at the gateway (claudeStandInAt, asserted in the test). Kept for the user: a fable pick of their own (even the main model) stays; a fable effort they set keeps the tier on the main model at that effort; a non-magpie value they wrote while routed stays as written; their own pre-magpie value is still stashed and given back on `model default`. Follow takes out a fable tier an older magpie wrote on a Claude model. Verified: TestClaudeFableTierOnClaude fails on the old claude.go with "on Opus: fable tier written as \"anth/claude-opus-5-5\", want it left out"; breaking the Follow migration, the user-value keep and the effort fallback each fails it too. go test -tags nogui ./... green under a temp HOME with pinned caches; go vet + GOOS=linux/windows go vet of internal/agent; go build, GOOS=linux -tags nogui and GOOS=windows builds. Not tried with a real Claude Pro account. |
||
|
|
5d0c1dcad7 |
agent, library, sessions: a Claude Desktop installed as an MSIX package on Windows is found, wired, given MCP servers and skills, and its Cowork calls read, in the package's folders (Kilig on Discord, Windows 10 LTSC 21H2)
Desktop from its setup.exe there runs packaged, so Windows keeps what it
writes under %APPDATA% and %LOCALAPPDATA% in
%LOCALAPPDATA%\Packages\Claude_<publisher>\LocalCache\{Roaming,Local}.
Nothing was in %APPDATA%\Claude or %LOCALAPPDATA%\Claude*, so magpie didn't
recognise Desktop at all. The packaged app reads its own copy before the
real folder's, so writing the usual folders wouldn't reach it either.
New internal/desktopdir is the one resolver for Desktop's folders: Data
(its own userData: %APPDATA%\Claude, the package's LocalCache\Roaming\Claude),
Mode (the Claude folder deploymentMode goes into, %LOCALAPPDATA%\Claude as
before) and ThreeP (Claude-3p). The package is found by globbing
Packages\Claude_* (the publisher id isn't assumed) and used unless Desktop
runs in the usual folders: it has run there (Chromium's "Local State"; an
%APPDATA%\Claude holding only another tool's claude_desktop_config.json
doesn't count) and not later in the package's. Every place that built
these paths goes through it:
- agent: desktopDirs (its 3p switch's writes, DesktopConfig3p, Claude
Code's desktop check in claude.go) and detection, which also looks at
Data (replacing the separate %APPDATA%\Claude check). windowsDesktopDir
moved into desktopdir unchanged.
- library: apps() (Desktop's MCP file, was os.UserConfigDir) and
desktopSkillRoots (Cowork's skills-plugin).
- sessions: desktopDataDirs, a copy of agent's resolver, now desktopdir's
All(); it also adds %APPDATA%\Claude on a usual Windows install, where
Desktop keeps 1p Cowork sessions (only %LOCALAPPDATA%'s were read). usage's
request_page and session_provider read it through sessions.DesktopDataDirs.
Tests: TestFindMSIX/TestFindWindowsClassic/TestFindOthers (desktopdir),
TestClaudeDesktopMSIX (agent: detected, switch writes LocalCache\Local\Claude
and Claude-3p, nothing in the usual folders), TestClaudeDesktopMSIXLibrary
(MCP file and skills-plugin in LocalCache\Roaming\Claude),
TestCallsCoworkMSIX (sessions). With the call sites put back to origin/main
the last three fail: "an MSIX Claude Desktop isn't detected", "no Claude
Desktop target: <nil>", "dirs: [.../Claude .../Claude-3p]".
Run: go test -tags nogui ./... under a temp HOME with pinned caches (all
ok); GOOS=darwin|linux|windows go vet -tags nogui on desktopdir, agent,
library, sessions, usage; GOOS=windows go build, GOOS=linux go build -tags
nogui. On the Windows 11 box (Squirrel install, no MSIX package): the
windows test binaries pass every Desktop/MSIX/Cowork test; the full agent
and sessions binaries fail the same 16 and 76 tests (run outside their
testdata) as origin/main's; a read-only probe of desktopdir.Here() gives
the usual %APPDATA%\Claude, %LOCALAPPDATA%\Claude and Claude-3p, Package "".
Not tried against a real MSIX Desktop: none on the box, and Claude-3p's
place in the package (LocalCache\Local, as Local\Claude is in Kilig's
listing) is inferred, not seen.
|
||
|
|
2d9db1b60d |
edit: malformed JSON configs stay untouched when agents connect (#1242)
Validate the complete comment-stripped document before object and array edits, including previews. Regression tests fail on the old implementation because edits and VS Code Connect succeed and alter malformed files. Targeted edit and VS Code tests pass in an isolated HOME. |
||
|
|
ad3b7fe5c2 | agents: keep Air provider fields on sync and model picks (#1239) | ||
|
|
6d7b574b5a | agents: a Codex another app ships and runs as its app-server is named by that app, so the row says to reopen Agents Anywhere, not a terminal (miaopasi on Discord: the copy left was Agents Anywhere's connector's codex app-server) | ||
|
|
2f74a963d7 |
agents: a Codex left on the old list is named, with how to reopen it — the Codex app (⌘Q: closing its window keeps it running on macOS), an editor's Codex, the CLI's background app-server, a terminal's codex (miaopasi on Discord: "已接入 · 重开 Codex 后生效" stayed after restarting Codex)
Agent.Stale counted every process matching (^|/)codex( |$) that started before magpie's last change, and the row only said "reopen Codex". Several of those don't end when Codex is "reopened": ChatGPT.app's main process registers window-all-closed so a packaged app on darwin doesn't quit (read from /Applications/ChatGPT.app/Contents/Resources/app.asar), so closing the window leaves its app-server (CodexCLI.app/.../codex app-server) running; an editor extension's codex app-server, the CLI's --managed-daemon (ppid 1) and a codex in another terminal run on through a reopened app. One app also counted twice: its app-server and its `codex exec-server --remote` both match (this Mac's ps). Agent.StaleCopies gives each copy with its kind (app / ide / daemon / cli, from the command line) and start, one app's processes as one copy; Stale is its length. /api/state carries staleCopies, and the opened row under "N running Codex still have the old list" says, per copy, what it is, when it started and how to end it: ⌘Q for the app, the editor's window reload, `codex app-server daemon restart` for the daemon, quit for a terminal's. Strings in zh, zh-TW, ja, de. TestCodexStaleCopiesSayWhichIsLeft (an app with its exec-server, VS Code's Codex, this home's daemon, an old and a new terminal codex: app, ide, daemon, cli; Stale 4) fails on the old logic (5 copies, no kinds). agent-stale-copies.test.cjs (Chromium and WebKit, en/zh/zh-TW/ja/de, 980 and 440px) fails on the old app.js (no such lines). Run: go vet -tags nogui (darwin, GOOS=linux, GOOS=windows) ./internal/agent ./internal/gui; go test -tags nogui ./internal/agent ./internal/gui (temp HOME); node --test internal/gui/tests/agent-*.test.cjs gui-ja gui-de gui-zh-tw i18n-keys codex-model-order: 202 pass, both engines. Not tried with the reporter's Codex: which copy is left on their Mac isn't known. |
||
|
|
eeee883f11 |
agent, wslrun: an agent in a WSL distro under consomme networking is pointed at 127.0.0.1, not the distro's default route (#1230: Codex in WSL 3.0.2 consomme written with 198.18.0.2)
Consomme (WSL 2.9's name for virtioproxy) relays the distro's 127.0.0.1 to Windows' own while localhostForwarding is on (microsoft/WSL ConsommeTests::LoopbackGuestToHost); its default route is Windows' next hop, a TUN proxy's 198.18.0.2 for the reporter, never Windows. wslrun.HostLoopback names the modes where 127.0.0.1 reaches Windows (mirrored, consomme, virtioproxy); every agent magpie writes into a distro (d.base), Claude Code's stand-in, and wslrun's proxy rewrite follow it. An unknown mode, or a WSL without wslinfo whose .wslconfig says Consomme, is not taken for one. An agent an older magpie left on the route is offered 127.0.0.1 (wslMovedFrom); localhostForwarding=false under consomme gets a notice. |
||
|
|
ca387c3ce3 |
agent: a Claude Code tier's _MODEL_NAME, _DESCRIPTION and _SUPPORTED_CAPABILITIES go with its model: the user's own, written for the model before magpie, are kept and given back when magpie steps out, and one written for a model of magpie's stays while the tier is on it (#1227: every tier on opencode-go/deepseek-flash, shown in /model as grok-4.5)
Claude Code 2.1.293 shows ANTHROPIC_DEFAULT_<TIER>_MODEL_NAME and _DESCRIPTION as the tier's label and description in /model, and takes _SUPPORTED_CAPABILITIES as what the tier's model can do. magpie never touched them, so the user's own, written for their relay's model, stayed on magpie's. A Claude Code an older magpie routed is tidied on the next catalog sync, its names kept to give back. |
||
|
|
ac142bb1d2 |
agent, gateway: Claude Code through magpie can keep its claude.ai sign-in (#1218)
* gateway: Claude Code signed in to claude.ai is told a provider's 401 as a 502, not as its own sign-in refused
Claude Code signed in to claude.ai, its base URL magpie's and no key set,
sends its own sign-in as its key (Authorization: Bearer sk-ant-oat…),
which magpie never passes on. A 401 it reads as that sign-in refused: it
renews the sign-in before each of its retries (ten for one request) and
then asks to /login; a 403 saying "OAuth token has been revoked" the
same. Whatever magpie answers such a request with is never about that
sign-in: a provider refused magpie's credential, or one of magpie's own
Claude accounts lapsed (the bridge's 401 or revoked 403). keepsSignIn,
on /v1/messages, /messages and /v1/messages/count_tokens, tells such a
request's 401 as a 502, and a 403 too when its body says the token was
revoked; the body is the provider's message as it was. Recent calls and
the usage log keep the provider's status. Any other 403, and a request
with magpie's key or an API key, is answered as before; no
x-should-retry is added, so Claude Code retries a 502 as it does a 5xx.
Verified: TestClaudeSignInNotToldItsSignInFailed (401 on /v1/messages,
/messages and count_tokens; revoked 403; another 403; magpie's key; an
API key; a reply and a stream untouched; Recent calls' status) fails
without the route change ("401: 401 … want 502", "revoked: 403 … want
502", "401 counting: 401 … want 502"); TestSignInWriterReleasesA403. go
test -tags nogui ./internal/gateway, go vet and gofmt are clean.
* gateway: a provider never gets the oauth beta of Claude Code's claude.ai sign-in
Claude Code signed in to claude.ai asks oauth-2025-04-20 in
anthropic-beta with its sign-in. magpie relayed the agent's betas to
every Anthropic-protocol provider, so that one went too, naming a
credential that never leaves magpie: a provider got a request claiming
an OAuth sign-in it was not sent, beside magpie's key for it. askedBetas
leaves the agent's oauth- betas out of what forwardOnce asks, so a
provider gets the request as Claude Code with magpie's key sends it. A
beta the user set on the provider (header.anthropic-beta, fitUserBetas)
still goes, oauth- or not.
Verified: TestSignInBetaStaysWithMagpie (the oauth beta among others,
alone, and the user's own on a provider) fails without the forwardOnce
change (sent ["claude-code-20250219,oauth-2025-04-20,…"], and
["oauth-2025-04-20"] where none was wanted); TestUserBetas* and
TestClaudeCodeHeadersReachAnthropicRelay pass. go test -tags nogui
./internal/gateway, go vet and gofmt are clean.
* agent, gui: Claude Code through magpie can keep its claude.ai sign-in
Claude Code's new sign-in field is magpie's key (the default) or claude.ai.
claude.ai writes ANTHROPIC_AUTH_TOKEN empty, which Claude Code reads as no
key: it stays signed in to claude.ai (/usage plan limits, connectors, voice,
/teleport) and sends that sign-in to magpie, which never passes it on. Every
later write (a model, a tier, the subagents, a /model pick followed, Reapply,
a port move, Disconnect and Connect) keeps it so, and a key put there by hand
is reported by Check rather than taken for magpie's. A token emptied or taken
out by hand in key mode is reported with the way to keep it so.
It is offered only where the gateway takes any key (loopback, a WSL distro
mirrored), not from a distro under NAT while magpie is shared on the network.
The GUI draws no square for a Claude Code with no other sign-in to take.
Verified: go test -tags nogui ./internal/agent (new claude_login_test.go,
each test fails with its change reverted) and -run 'SignIn|Claude|Beta'
./internal/gateway; go vet; GOOS=linux/windows builds; claude-signin.test.cjs
(fails without the app.js change) plus i18n-keys, agent-field-names,
agent-layout, claude-direct, claude-effort, codex-signin-always,
subagent-effort and menu-scroll, Chromium and WebKit, en/zh/ja/de, 1100 and
440px. Real Claude Code 2.1.292 with the token empty sent Bearer sk-ant-oat
and the oauth beta to the base URL, took a 502 as a server error, and stayed
signed in to claude.ai.
* agent: Claude Code given a magpie model at an older gateway address keeps the endpoint it had before magpie
When the gateway moved while magpie wasn't running to follow it, a magpie
model picked next found Claude Code at the older address and stashed what
was there as the user's own: the dead gateway URL, magpie's key or the
empty one of a claude.ai sign-in kept, and magpie's own tier models. That
overwrote the endpoint and token stashed when magpie was first wired in, so
switching off put the dead address back, and with the claude.ai sign-in
kept "model default" left it and five magpie tiers too.
What's there at an older address is now taken for magpie's (keyOurs: its
key, or an empty token with the claude.ai sign-in kept), and the stash made
when magpie was wired in stays. The same test replaces "model default"'s
guess from the model's name, which could take out a user's own endpoint
with an empty token.
Verified: TestClaudeAtAnOlderGatewayAddress (both sign-ins, Disconnect and
model default; fails on the previous commit, the key-mode case on main
too); go test -tags nogui ./internal/agent; -race -count=5 on the Claude
and WSL Claude tests; go vet; GOOS=windows build.
* gateway: Claude Code signed in to claude.ai is told a provider's refusal in magpie's words, never mentioning x-api-key
Claude Code 2.1.293 tells any error whose message mentions x-api-key as
"Not logged in · Please run /login", whatever its status, so the 502 alone
still signed it out (yetone on #1218). A 401, or a 403 saying the OAuth
token was revoked, now reaches it as an api_error "<provider> refused
magpie's credential (HTTP 401): invalid API key", and a stream's error
after its 200 says "API key" too. Recent calls keep the provider's status
and message.
Checked with Claude Code 2.1.292 signed in to claude.ai, through the
gateway to a provider answering 401 invalid x-api-key: it shows "API
Error: 502 Fake refused magpie's credential (HTTP 401): invalid API key.
This is a server-side issue" and stays signed in.
* agent: an empty token at an endpoint of the user's own stays theirs, though Claude Code once kept its claude.ai sign-in through magpie
keyOurs took any empty ANTHROPIC_AUTH_TOKEN for magpie's once the sign-in
was kept, so after Disconnect a user's own https://corp.example with an
empty token lost both to the model default (yetone on #1218). The empty
token is magpie's only beside the address magpie last wrote, stashed as
claude.wired_at, which Rewire carries to the gateway's new port.
* gateway: no error Claude Code signed in to claude.ai is told mentions x-api-key, whatever its status
Claude Code reads any error naming x-api-key as its own sign-in refused,
so a provider's 403 "invalid x-api-key" or 400 "x-api-key header is
required" still told it "Not logged in · Please run /login" (yetone on
#1218, Claude Code 2.1.293). Every error such a request is told is held
until its body and says "API key" there, at the status it had; a 401 and
a revoked 403 stay the 502 in magpie's words.
|
||
|
|
d5cf133b47 |
agent tests: a fake omp a Windows PATH lookup answers to, and the distro probe is skipped where a Windows shell calls the temp folder a drive (#1180)
TestOmpDetectedWithPiDir plants a #! script under the bare name omp, which is the one command a Unix PATH answers to. Windows answers to a name with a PATHEXT extension, so Detected() found nothing there and the check failed with "omp's command is not". The fake is now planted as omp.cmd on Windows; the test never runs it, it only asks PATH to find it. TestWSLProbeScriptSaysWhere runs the distro probe under sh, which on a Windows box is Git Bash's own. A Windows shell mounts the folder it is given (/tmp, and / and /bin) as drives of its own, so the probe reports them as win: lines, and a program on a drive is not the distro's -- which is right for the product and not what this check is about. It is skipped there, as it already was with no sh. Both were the last of the internal/agent checks a Windows host runs red; the rest are covered by #1069 and #1080, which take the platform facts and the shared %APPDATA% sandbox. CI never runs either: the Windows job filters ./internal/agent to ^Test(Dsh|KeepDsh) and ^TestVSCodeDetected$. |