Commit Graph
819 Commits
Author SHA1 Message Date
yetone cd7b7a042f sessions: omp's local sessions are in Usage › Sessions (#303), and the GLM Coding Plans take Codex's Responses as they are (#306) — omp (oh-my-pi) keeps Pi's session jsonl (v3) under ~/.omp/agent/sessions (and profiles' and $XDG_DATA_HOME/omp's) with a title line, title changes, model_usage for calls outside the chat, provider/model changes and subagent/advisor transcripts beside the file; they are read through Pi's parser, subagents counted in their session, a fork's copied entries in the original only, resumed by omp --resume <id>. The zhipu and zai Coding Plan regions get their Responses base (open.bigmodel.cn/api/v1, api.z.ai/api/v1, per their Codex docs), so Codex's requests go there as they are instead of rebuilt for Chat; Chat and Claude Code routes are unchanged. TestOmp, TestDirsMore and TestGLMCodingPlanResponses fail without the change. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass. 2026-09-30 18:07:32 +08:00
yetone 9ec237d4e6 gui: a Plugins tab, a market to find and install provider plugins in (在产品里要有插件市场功能,整个插件市场要美观易用), and a way to it from the providers list (供应商列表里有个精致优雅的按钮) — Discover lists the magpie community's plugins, then OpenCode's community's by weekly downloads, each card with its logo, publisher, summary, downloads, version and one button that takes it all the way: Install (Bun fetched first if need be), Sign in, Signed in; a package not on npm yet says Coming soon. Typing searches the list and, after a pause, npm, with the field keeping its focus. A card opens the plugin's page: version, downloads, licence, updated, what it signs in to, npm/source/homepage links, and its README (no images, HTML or scripts; only http(s) links, opened outside). Installed lists each plugin with its providers' sign-ins, a newer version to update to, Update all, switch off and remove; Settings' Plugins section moves here. The list comes from registry.json in magpie-community/plugins, kept for 6 hours, falling back to the copy fetched last and then the one built in (MAGPIE_PLUGIN_MARKET overrides, "off" never asks). Under Add provider a "More subscriptions in Plugins" pill opens it; the add sheet ends in a Plugins tile, and a search that finds nothing offers to look for a plugin. The header, seven tabs now, draws them in at the narrowest window. plugin-market.test.cjs (Chromium, WebKit, en/zh) and market_test.go fail without the change; add-button, plugin-signin, usage-ledger, text-size, old-webkit, click-scroll pass. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass. 2026-09-30 17:57:46 +08:00
yetone 7e454bba9b agents: Claude Code on a magpie model is told the model's context window (#311: its auto-compact settings stay as they were for another model) — Claude Code takes any model it doesn't know by name (all but Claude's own) for 200K, and CLAUDE_CODE_AUTO_COMPACT_WINDOW is only ever the smaller of its value and that, so a 128K model compacted too late and a 400K one far too early; CLAUDE_AUTOCOMPACT_PCT_OVERRIDE is a share of the window and holds for any model (read in Claude Code 2.1.285). magpie now writes CLAUDE_CODE_MAX_CONTEXT_TOKENS, the window Claude Code takes those names for, from the model's known context (a group's smallest), with the model: another model's replaces it, one magpie doesn't know the window of, a switch back to a Claude model or a reset removes it; with a [1m] main model, the smallest of the tiers not marked. A value the user set, or changed magpie's to, is left as it is (magpie keeps the one it wrote). TestClaudeContextWindow fails without the change. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass. 2026-09-30 17:44:44 +08:00
yetone 91f10f929f gui: the tray menu is in the page's language (#301: 界面设为中文,托盘菜单仍是英文) — its lines were English in Go, which never read the language setting. Open, Version, Restart to Update (to v) and Quit now follow Settings › Language: en or zh as set, with System the system's (macOS AppleLanguages, Windows' preferred UI language, LANG elsewhere), Chinese for any zh; changing it in Settings relabels the menu at once. TestTrayMenuLabels and TestSettingsLangRelabelsTray fail without the change. go vet, go build (darwin GUI), go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass. 2026-09-30 17:44:44 +08:00
yetone 6168bd1a18 workbuddy: its "Illegal API invocation from an unapproved channel" says what to do (QuentinHsu on Discord, deepseek-v4-flash on WorkBuddy from Codex) — WorkBuddy, both builds, refuses a chat whose system prompt is Codex's or Claude Code's (#182), whatever the headers, and magpie does not rewrite that prompt. The refusal now carries "WorkBuddy refuses chats from Codex and Claude Code (their system prompt); use it from Hermes, OpenCode or Pi, or add another provider to this group", to the agent and on the Routing page (apart from the vendor's words, in the page's language); a group still passes the request on to its next member. workbuddy_refused_test (provider, gateway) and routing-wb-refused.test.cjs (Chromium, WebKit, en/zh) fail without the change. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass; click-scroll, old-webkit and the routing tests pass. 2026-09-30 17:41:16 +08:00
yetone 79a7ba8864 gateway: Codex's own headers go on to a keyed provider, as Claude Code's do (#179) (lx_78654 on Discord: "This account only allows Codex official clients") — magpie sent Codex's requests as magpie/<version> with none of Codex's headers, so a provider that serves only Codex's clients (by User-Agent or originator, a version and an x-codex- header) refused them. A request from Codex (User-Agent or originator starting codex: codex_cli_rs, codex_exec, codex_vscode, Codex Desktop) to a key+URL provider over Responses or Chat, relayed or rebuilt, now carries its User-Agent, originator, session-id, thread-id and the x-codex- headers codexHeader passes; the provider's own key is the one sent, never magpie's, nor Codex's attestation; other agents go as magpie/<version>, and a provider's own User-Agent is still set when it signs. Headers as codex-cli 0.159.2 sent them to a fake upstream in a sandbox HOME. TestCodexHeadersReachKeyedRelay fails without the change. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass. 2026-09-30 17:37:30 +08:00
yetone 947521194f qoder: a model Qoder's listing marks free is shown free (lml on Discord asked which Qoder models cost no credits) — a model with is_free (isFree) true or a price_factor (priceFactor) of 0, as Qoder's own client reads its listing, gets catalog.Model.Free, so the model picker's free badge shows on it; a saved account's models, re-read from its stored listing, carry it too. TestParseModelsFree and TestQoderSavedModelsFree fail without the change. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass (TestClaudeSignedOut, a load flake, passes alone). 2026-09-30 17:29:55 +08:00
yetone 782380a7f5 gui: a Claude account saved in magpie can be removed while Claude Code is signed out (𝕏 user on Discord: a banned account logged out, and its line in Providers had nothing to remove it with) — saved accounts are listed only beside the one the agent is signed in to, so with none signed in the line saying they are saved was all there was. It now names them and has Remove, which asks first and forgets magpie's own copies only (login/forget, one each): the agent's files, keychain and the account itself are left as they are. TestSavedButSignedOutCanBeRemoved and saved-signedout-remove.test.cjs (Chromium, WebKit, en/zh) fail without the change. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass (TestClaudeSignedOut, a load flake, passes alone); old-webkit, own-signin-remove pass. 2026-09-30 17:29:54 +08:00
yetone 51e7b51713 gui: an agent's model list closes whatever the clicks while it loads (有时候这个关不掉), and its rows sit indented under their group (左侧有缩进才对) — the list is fetched before its box is up, and a second click on the line then (Codex's 266 models are slow to put together) was taken as a fresh open: each fetch that came back drew a box, and the one magpie no longer held stayed on the screen with nothing to close it. Now a click on the line while it loads takes it back, as it closes the box, a click elsewhere too, and another open or a close supersedes the fetch; one box at most. A model row starts in its group icon's column, past the twisty, not flush with the group's header. New agent-models-loading.test.cjs (Chromium, WebKit: a slow list clicked twice, three times, clicked away from; row icon under the group icon) fails without the change; agent-models, agent-model-groups, click-scroll, old-webkit, model-test-one pass. go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test ./internal/gui pass. 2026-09-30 17:27:56 +08:00
yetone 4fc980347c agents: Codex's OpenAI models are its own, not magpie's routing groups read back (group/semantic, group/test … listed under OPENAI in Codex's model picker) — Codex keeps the list it was last handed in models_cache.json, and handed through the gateway it has magpie's models in it too; magpie read the file back as Codex's own models, so its groups (one since removed among them) were listed under OpenAI, and stayed after Codex was routed elsewhere. An entry magpie added (a magpie id — provider/model, group/<id>, codex/<slug> — in a list whose ETag magpie tagged, or described "… via magpie") is left out of Codex's own models and of the entries the gateway falls back to. TestCodexCacheLeavesMagpieOut fails without the change. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass. 2026-09-30 17:19:39 +08:00
yetone 865ad6b03f gui: one magpie in Omarchy's tray, not two (songlairui on X: 为什么我的 omarchy 上会有两只喜鹊,一起来,一起退) — Wails' tray takes the name org.kde.StatusNotifierItem-<pid>-1 and registers with the watcher by its path, which Quickshell's watcher files under the connection's own name (:1.x); something on their system also registered the well-known name, and Quickshell tells items apart by how they were named, not by who owns them, so the one icon was two items (another app of theirs was doubled the same way). magpie now gives the name up once the tray has taken it: a registration by that name is refused, and one made already is dropped with it. In the Omarchy VM, registering the name before the change showed :1.328/StatusNotifierItem and org.kde.StatusNotifierItem-123846-1/StatusNotifierItem; after it, one item, the icon still in the bar. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build pass; built with -tags gtk3 on the VM. 2026-09-30 17:10:09 +08:00
yetone 8931a4b3ac davsync: sync can keep its file in an S3-compatible bucket as well as a WebDAV folder (#296) — AWS S3, Cloudflare R2, Backblaze B2, MinIO, Garage or a NAS. Settings › Sync and backup picks WebDAV or S3: endpoint (empty is AWS), bucket, prefix, region (empty is us-east-1, auto for R2), access key ID and secret, path-style; CLI magpie s3 on s3://bucket/prefix access-key-id=… endpoint=… region=… path-style. The bucket holds the same passphrase-sealed file at <prefix>/magpie/magpie.magpie-backup, signed with SigV4 (no SDK). It is read with its ETag and written with If-Match (If-None-Match: * for the first), so two computers syncing at once don't overwrite each other; a server that refuses conditional writes (501, or 400 naming the header) is remembered and gets a HEAD-then-PUT instead. An IP, localhost or a dotted bucket on https goes path-style on its own. Errors say what to do: wrong key/secret, clock skew, bucket in another region (names it), no such bucket, 403 on the first read on AWS without s3:ListBucket. The secret is kept as the WebDAV password is, sent only to the endpoint and key it was typed for, and may not be the passphrase. sigv4_test (AWS's published vectors), s3_test (fake server: conditions, conflict, fallback, regions, errors), s3_cli_test and s3-sync.test.cjs (Chromium, WebKit, en/zh) fail without the change; tested against MinIO. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass (TestGroupAddSetRemove, a TempDir-cleanup flake under load, passes alone); click-scroll, old-webkit pass. 2026-09-30 17:07:47 +08:00
yetone e31384b1f9 gateway: Codex on magpie as its provider has its approvals auto-reviewed again (shihao H on X: 自动审批未执行 … 404: magpie knows no model "codex-auto-review") — Codex asks for its guardian review by OpenAI's reviewer model, codex-auto-review, whatever the provider ("independent of current login"), and magpie, knowing no such model, answered 404, which Codex reports as the review service failing. A model Codex names that magpie does not serve now goes to the model Codex is set to (config.toml model), when magpie is its provider, as Codex itself reviews with the turn's model when its catalog lacks the reviewer; the Routing view shows it as the Approval check. Signed in, Codex's own names go on to OpenAI as before. TestCodexStandIn fails without the change; TestCodexAutoReviewStandIn has the Codex request go to the stand-in. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass. 2026-09-30 16:55:50 +08:00
yetone 771506cf05 gui: a model chip's right-click tests that model alone (yonghe on Discord: 供应商编辑里只想测一个模型,「测试模型」会测全部) — the chip's menu, the one an agent row's right-click opens (now openRowMenu, shared), has Test this model, which posts provider/test with that model alone; its dot and title show the answer as Test models shows them, other models' results are kept, and the footer names the model and how it answered. The right-click neither picks the chip nor moves the page; Esc closes the menu, not the editor, and puts focus back without a scroll. The chip's title and Test models' say so; no menu where Test models isn't (signed-in accounts, classifiers). model-test-one.test.cjs (Chromium, WebKit, en/zh) fails without the change. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass; click-scroll, old-webkit, provider-proxy, nokey-editor, model-pick GUI tests pass. 2026-09-30 16:48:43 +08:00
yetone 5e3c7b8049 plugin: a plugin's provider has its models on a magpie that never fetched models.dev — the host took the models OpenCode would from models.dev's cache, so on a fresh install (magpie plugin add before the app or gateway ever ran) opencode-gemini-auth and opencode-copilot-auth listed 0 models, and plugin-providers.json kept that. Starting the host now fetches the catalog first when there is none (30 s at most). Tried in a sandbox HOME with the real packages: google-plugin 38 models, github-copilot 34; with a Copilot sign-in copied into the sandbox, magpie provider test github-copilot answered on Responses (gpt-5.4) through the plugin's fetch. go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test ./internal/plugin pass. 2026-09-30 16:48:31 +08:00
yetone e1fd75de6a agents: a routing group in an agent's model list shows its providers' icons stacked, as it does in the pickers and on the Routing page (这些路由组里的模型图标应该是它对应的 providers 的图标堆叠) — the list gave a group the logo of the maker its name suggested (GPT-6 Sol: OpenAI's alone, whatever it routes over) and the fan to the rest, though the icons were already in the response. Now 2+ providers stack (17px discs, the first on top, +n past 3), a lone provider sits in one disc like them, and a group with none keeps the fan; every group row has one 56px slot, the icons at its left, so the names start in a column. The server no longer sends a group a maker's logo. New agent-model-groups.test.cjs (Chromium, WebKit: groups of 4, 2, 1 and 0 providers, names in one column clear of the widest stack, icons at the row's left, a model keeps its maker's logo) fails without the change; agent-models passes; click-scroll's WebKit case flakes on main as well. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass. 2026-09-30 16:47:57 +08:00
yetone a31f97b30d plugin: OpenCode's provider plugins sign in to subscriptions in magpie — an npm package OpenCode users install for a plan (its auth hook: loader, methods, oauth authorize/callback, api keys, chat.headers) runs in a Bun host magpie downloads the first time a plugin needs it, speaking JSON-RPC over stdio; its sign-ins are kept in plugin-auth.json (0600). A provider a plugin signs in to is a provider like the others: its models the plugin lists, each spoken to on the API its AI SDK package speaks (anthropic → Messages, openai/azure → Responses, google → Gemini, Copilot's GPT-5+ → Responses, else chat completions), its requests carried by the plugin's fetch to the base URL its loader gives; an id magpie already has is <id>-plugin. CLI: magpie plugin [list|add|rm|update|on|off|login|logout]. App: Settings → Plugins adds, updates, switches off and removes them and says why one didn't load; Add provider → From plugins signs in with the plugin's methods, its questions (picks and checked text), the browser and a pasted code or a key, followed as the built-in sign-ins are; removing the account signs out. The built-in subscriptions stay. TestFakePlugin, TestPluginProvider (chat, Anthropic and Gemini models through the gateway) and TestPluginSignIn (a wrong code fails, the right one signs in, removing signs out, a key) run a fake plugin on Bun; plugin-signin.test.cjs (Chromium, WebKit, en/zh) covers the add sheet and Settings. signin-callback, add-sheet, zed-sub, account-proxy, own-signin-remove, routing-signin, click-scroll GUI tests pass; go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass (TestUserPath, TestClaudeSignedOut, TestClaudeAccountNamedAsItsLogin, TestClaudeIdentityServedWhileAsked, known flakes under load, pass alone). Not yet tried with a real plugin and account. 2026-09-30 16:44:32 +08:00
yetone e0e93c2ec5 gateway: a Codex conversation compacted on GPT goes on after switching to Grok (waroy on Discord: Grok (SuperGrok): Could not decrypt the provided encrypted_content) — when Codex compacts on OpenAI's own models, the earlier turns become a compaction item sealed by OpenAI (encrypted_content), which magpie kept for magpie models. Grok can't read it, any more than another vendor's reasoning. The retry after such a refusal (2476cd5) left out only the sealed reasoning and asked once, so Grok refused the compaction too and Codex got xAI's error. It now asks again without the compaction when the refusal comes back (or there was no reasoning to leave out), and remembers the compaction with the reasoning as refused for that conversation and account, so later turns leave both out up front while Grok's own reasoning goes along; each step remembers only what it took out. The summary of the turns before the compaction is lost to Grok; what was said since stays. TestForeignCompactionToGrok (Codex path, a SuperGrok-like upstream refusing in the stream) fails without the change with the reporter's error. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass (TestClaudeSignedOut, TestClaudeAccountNamedAsItsLogin, TestClaudeIdentityServedWhileAsked, known flakes under load, pass alone). Not tested against xAI itself. 2026-09-30 16:40:41 +08:00
bcb9db9356 fix(gui): 用量文字与提示显示最多一位小数 (#287)
* feat(gui): 订阅用量百分比保留两位小数

Co-authored-by: Codex <codex@openai.com>

* feat(gui): 支持配置订阅用量小数位

Co-authored-by: Codex <codex@openai.com>

* fix(gui): 默认用量百分比保持整数显示

Co-authored-by: Codex <codex@openai.com>

* fix(gui): 整数用量保留原有圆环尺寸

Co-authored-by: Codex <codex@openai.com>

* fix(gui): 按原布局显示用量小数

菜单栏与圆环维持整数;仅在用量文字和提示中为非整数比例显示一位小数。移除小数位配置及相关布局调整。

Co-authored-by: Codex <codex@openai.com>

---------

Co-authored-by: louis.ning <louis.ning@nio.com>
Co-authored-by: Codex <codex@openai.com>
2026-09-30 16:37:23 +08:00
yetone 7c0b5441a8 routing: a group can name the reasoning levels agents are offered, so one member with few doesn't take the rest from the others (#295) — a group offered only the levels every member has, so deepseek-flash (low/high/max) beside models with medium and xhigh left the group without them. Group.Levels (CLI magpie group set <id> levels=low,medium,high,xhigh, levels= clears; TUI l; GUI group editor's Levels: its models' shared / named) is offered instead in /v1/models, Codex's catalog, Pi and the rest; only levels magpie knows are taken, stored lowest first; a group in a group that names its own offers them for its models; unset is as before. A member without the level asked is sent its nearest, as it already was. The GUI's group save also keeps a family set from the CLI. TestGroupLevelsOverride, TestGroupLevelsReachAgents, TestGroupLevelsNamed (passthrough and translated: medium→high, xhigh→max on the low/high/max member, as asked on the one with all), TestGroupSetLevels and routing-levels.test.cjs (Chromium, WebKit, en/zh) fail without the change. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass (TestUserPath, a known flake under load, passes alone); click-scroll, old-webkit, routing-effort(-row), routing-levels, settings-groups GUI tests pass. 2026-09-30 16:26:37 +08:00
yetone 7f007660d6 gui: magpie web shows the logo and the name in the header again (magpie web 竟然没有显示 magpie logo 和名字) — the rule that hides them in the Windows window, whose title bar already has them, matched every window that is neither mac nor linux, and in magpie web the page is given neither class, so the brand was hidden in the browser on every system. The rule now leaves out .web too. brand.test.cjs (Chromium, WebKit: web on Windows, macOS, Linux shows logo and name beside the tabs; the Windows window hides them; the Mac window shows them) fails without the change; old-webkit, omarchy, mask-emails, click-scroll pass. go vet, go build -tags nogui (GOOS=linux), GOOS=windows go build pass. 2026-09-30 16:25:13 +08:00
yetone 4a64ee78c0 gui: the header's refresh button checks for a newer magpie too, and its tooltip says so (inaction on Discord: the Update pill beside it read as the refresh's own "upgrade", and a refresh didn't look for one) — it refreshed only the model lists, so a new version was noticed only by the six-hourly check or Settings → Version. A click now also asks POST /api/update/check, without holding up the model refresh, and draws the Update pill from its answer. Tooltip: "Refresh model lists (models.dev and every vendor) and check for a newer magpie" / "刷新模型列表(models.dev 与各供应商),并检查 magpie 更新". sync-update.test.cjs (Chromium, WebKit, en/zh: one click, one check, the pill shows) fails without the change; update-check, click-scroll, old-webkit, rail-tip, menu-scroll pass. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass (TestUserPath, TestClaudeSignedOut, known flakes under load, pass alone). 2026-09-30 16:16:18 +08:00
yetone ee8a371cf4 gui, update: saving the Settings page keeps the models hidden from each agent, and update checks go through magpie's proxy and say so (#294: 设置代理之后只有模型代理生效了,代理没有用于检查 magpie 是否有更新) — POST /api/settings carried over the models' names, levels and images but not HiddenModels, which the page never sends, so changing the theme, the language or anything else there showed every model hidden one by one on the Agents page again; it is carried over with the others. The update feed and the release came through http.DefaultTransport, proxied only because main installs netproxy first; the update client now takes netproxy.Func on a transport of its own (Settings' Proxy, else *_PROXY, else the system's). A check that failed at start-up, before a proxy was set, stayed failed for six hours: saving a new proxy now checks again through it. And the Settings row read "requests to vendors go through {proxy}", which says update checks don't: it now names both. TestSettingsSaveKeepsWhatItDoesNotSend (every field prefsKeep doesn't send survives a save of the theme alone; fails on main with HiddenModels emptied), TestUpdateGoesThroughTheProxy (feed and asset at hosts only a local proxy answers; fails without the transport), TestSettingsProxyRechecksAFailedUpdate (fails without the re-check). account-proxy, provider-proxy and click-scroll GUI tests pass; go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass (TestUserPath, TestClaudeSignedOut, TestClaudeAccountNamedAsItsLogin, known flakes under load, pass alone). 2026-09-30 16:03:52 +08:00
yetone a69ae948dc library: Claude Desktop's MCP servers go into Claude-3p/claude_desktop_config.json as well, where its 3p mode reads them (#297: Claude Desktop 使用 magpie 三方网关(3p)后,library 写入的 MCP 在 Desktop 里看不到) — in 3p mode, which magpie's own provider switch turns on, Desktop's whole userData is Claude-3p (~/Library/Application Support/Claude-3p, %LOCALAPPDATA%\Claude-3p on Windows, ~/.config/Claude-3p), its MCP servers included, so the servers the library wrote into Claude/'s file never showed in Settings → Developer. Where the Claude-3p folder is, its file is now written with Claude/'s, in one edit with each file's own keys kept, read for servers to bring in, and cleared on removal even when Claude/'s no longer has the entry — both, not whichever mode is on, so switching the provider leaves nothing stale and Desktop finds them in either mode; no Claude-3p folder is made for a Desktop never in 3p, and one only ever in 3p (no Claude folder) gets its servers there alone. A server given before reaches Claude-3p on the next sync. Tested with new desktop3p_test.go in a sandbox HOME (both files written and cleared, the user's servers and keys kept, sync of an older server, 3p only); each fails without the change. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass. Not tried against a real Desktop in 3p. 2026-09-30 15:56:52 +08:00
yetone 3810dc3f8c zed: every tool result sent to Zed says is_error, false as well as true, so a turn after a tool call is answered again (yetone/magpie-releases#9, aurevae: "Zed: failed to parse Anthropic request: missing field is_error" on Pi through /v1/chat/completions and on /v1/messages) — magpie's Anthropic request leaves is_error out when it is false, as Anthropic's API allows, but Zed's cloud refuses a tool result without it, so with a Zed account every request after the model's first tool call failed with a 400. The reporter's own table shows the same request going through with is_error true. Zed's Anthropic request now has is_error on each tool_result; the tool inputs' numbers are kept as they were. TestZedToolResultSaysIsError checks a successful and a failed tool result, and a 20-digit number in a tool's input; it fails without the change. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass (TestDevinSignedInOnlyInMagpie, a known flake under load, passes alone). 2026-09-30 15:53:29 +08:00
yetone de07d8de24 ci: a UI preview goes by the diff, not the PR's description — on #287 the description (out of date) promised a 用量小数位 setting the code doesn't have, and the preview repeated it as the change, captioned a screenshot "默认 0 位" of a setting that wasn't there. The planner is told the description may be wrong, to plan and summarise from the diff, and what the sandbox lacks (subscription quotas above all); the preview now says, apart from the summary (改动(按代码)), where the description and the code disagree (描述与代码不符) and what the sandbox can't show (沙盒里看不到). Tested locally with run.sh on #287's code: it names the missing setting, says the sandbox has only a DeepSeek balance and no percentage windows, and shows the two places the change would be. 2026-09-30 15:45:23 +08:00
Eachann 3dc5577d43 pi: changing the thinking level writes only that level, and only when it changed — with the model on magpie it rebuilt providers.magpie, so a contextWindow (or any other field) edited by hand was replaced. Default still clears defaultThinkingLevel and leaves the model and the provider where they are. Picking a model, and the catalog sync, still refresh the provider. OmO shares this path. (#285)
Tested with TestPiThinkingLeavesTheProvider: a hand-edited contextWindow and another provider stay byte for byte through an unchanged level, a changed level and Default; the level itself changes or clears, and Default again does not rewrite the file. gofmt, go vet -tags nogui ./internal/agent/ and go test -tags nogui ./internal/agent/ pass.
2026-09-30 15:41:17 +08:00
yetone bce583dc6e codexcat: each model magpie lists for Codex says it takes parallel tool calls, so Codex 0.147 loads the catalog again (#298) — it failed on every start with "failed to parse model_catalog_json … missing field supports_parallel_tool_calls", leaving Codex unusable with magpie. Codex 0.147 requires the field; later Codex (0.159) default it and ask for parallel calls whatever it says, so true is what they already do. Reproduced in a sandbox HOME with codex-cli 0.147.0 and magpie's catalog; with the field, 0.147.0 and 0.159.2 both load it and send the request (parallel_tool_calls true, as before on 0.159). TestCodexCatalogParallelToolCalls checks every entry; it fails without the field. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass. 2026-09-30 15:39:37 +08:00
yetone 795a4d7130 ci: the UI preview checks out a fork's PR — actions/checkout now refuses fork code under pull_request_target unless told allow-unsafe-pr-checkout, and every fork PR failed there (#287, run 36682482508); #289 came from a branch here, so it passed. The record job is the one meant to run the PR's code: read-only token, no credentials kept. 2026-09-30 15:34:30 +08:00
yetone d0d0fcef76 providers: DimAgent's subscription is taken out of magpie (a DimAgent team member on X: dim doesn't allow its subscription used through a proxy, and users doing so get banned or lose their discount) — #226 signed in to DimAgent as its desktop client does and served its models through the gateway. The sign-in, the account provider and its dimagent/<model> models, the quota card, magpie accounts … dimagent and the icon are gone, with internal/dimagent. An account signed in before is left out when logins.json is read and dropped from the file at its next write, so its tokens aren't kept. The pasted-callback field and route stay, generic, with no sign-in using them. Tested with TestDimAgentLoginDropped (fails without the filter); in a sandbox HOME with a DimAgent login left in logins.json, magpie accounts --json lists nothing and magpie accounts add dimagent is refused. signin-callback (fixture renamed), old-webkit and click-scroll pass; go vet, go build (GUI, -tags nogui, GOOS=linux, GOOS=windows) and go test -tags nogui ./... pass (TestUserPath and the Claude/Devin sign-in tests flaked under the full run and pass alone). 2026-09-30 15:33:40 +08:00
yetone c46370ad9a gateway: a Codex compaction for a magpie model asks for its summary as Codex asks for its own — streamed, with tool_choice and parallel_tool_calls — so an upstream that checks a Codex request's shape no longer refuses it with "invalid codex request" (#292, byhooi). Codex compacts remotely (a compaction_trigger in /responses) whenever it is on the OpenAI provider, which is how it reaches magpie, and it has no setting to turn that off (remote_compaction_v2 is gone). magpie answers the trigger by asking the model for a summary, but it sent that with stream false and no tool_choice or parallel_tool_calls. The tools are still left out, so the reply is text; compactReply reads the summary from a stream as well as from a whole response, and a failed stream is a 502 with the upstream's message rather than an empty summary. Tested with TestCodexCompactsThroughCodexRelay (a fake upstream that refuses a request not streamed or without tool_choice, parallel_tool_calls, prompt_cache_key or include; fails on main with "invalid codex request") and TestCodexCompactsMagpieModel. go vet, go build -tags nogui (darwin, GOOS=linux, GOOS=windows) and go test -tags nogui ./... pass. 2026-09-30 15:29:15 +08:00
yetone 4dc3d93343 kimi: the new Kimi Code (2.x) is wired in ~/.kimi-code, where it reads its settings — a model picked in magpie never reached it, nor did magpie's models reach its /model (#290, kyzhouxu: Gemini 3.8 flash picked, Kimi Code still starts on the old one). magpie wrote only ~/.kimi/config.toml, the old kimi-cli's; the new CLI copies that over once when it first starts and never reads it again. KimiDir picks $KIMI_CODE_HOME, ~/.kimi-code if there, $KIMI_SHARE_DIR, ~/.kimi if there, else ~/.kimi-code. The new CLI's model tables also say tool_use (kimi-cli refuses the word, so it isn't written there), and its skills go to ~/.agents/skills, which it reads (not ~/.config/agents/skills). Tested with TestKimiCode (fails on main: path .kimi/config.toml, want .kimi-code/config.toml) and TestKimi; in a sandbox HOME the new CLI's kimi provider list shows magpie and the new default model, kimi doctor finds the config valid, and kimi -p went through a sandbox gateway to a fake upstream asked for gemini-3.8-flash with its 26 tools. go vet, go build -tags nogui (darwin, GOOS=linux, GOOS=windows) and go test -tags nogui ./... pass (flaky tests under load pass alone). 2026-09-30 15:29:14 +08:00
yetone 972355cfbe gui: a provider with no key opens its editor again, and a key's plan and balance show the last reading while the vendor is failing — willz on Discord: a local Ollama added without a key only toggled its row when clicked, with "undefined is not an object (evaluating 'copied?.key.set')". drawEditor set copied to false when the provider wasn't a copy, and false?.key is undefined; it is null now. chalice on Discord: a Command Code key's card read "Usage unavailable" — Command Code answers billing/credits 503 at times, and a key's cards, unlike a subscription's, kept no last reading: the balance showed the error and the plan card went. Both now keep the last good reading (quotas.json, under a sha256 digest of the key, never the key) and show it with its time while the failure passes; a key never read still has no plan card. Tested with nokey-editor.test.cjs (an Ollama preset and a custom one without a key open in Chromium and WebKit, English and Chinese; fails without the change) and TestKeyQuotasKeepLast (a fake Command Code going 503, readings reloaded from disk; fails without the change). click-scroll passes; go vet, go build -tags nogui (darwin, GOOS=linux, GOOS=windows) and go test -tags nogui ./... pass (two Claude account tests flaky under load pass alone). 2026-09-30 15:22:28 +08:00
yetone 190248d204 ci: the UI preview records without waiting for approval (the user: 去掉批准吧) — the ui-preview environment keeps the DeepSeek key but no longer names a reviewer; the comments and the "recording" note stop mentioning one. 2026-09-30 15:07:06 +08:00
yetone c9d9652380 library: an agent whose folder is a file isn't written, and skills reach 13 more agents with one link in ~/.agents/skills for those that share it (neiko on Discord: with DeepSeek Harness not installed the Library kept failing on "open ~/.dsh/AGENTS.md: not a directory" and ~/.dsh/config.yaml; inaction: the skills list is short of the Agents page, Kimi Code has none). An agent counted as there when its folder path existed or its command was on PATH, never asking whether the path was a folder — a file at ~/.dsh, or another dsh, made DeepSeek Harness look installed. Now an agent whose folder is a file (or under one) isn't detected (agent.Taken; Goose's, Cindy's and Claude Desktop's own checks want a folder too), a Library target leaves out each place a file stands in the way of, one with none left isn't listed, and an agent given no MCP server with none of magpie's isn't read. Skills: Kimi Code, Goose and Cindy get ~/.agents/skills (Kimi's ~/.config/agents/skills when there, as Kimi reads it first; never ~/.kimi/skills, which would hide Claude Code's from it); Command Code, Devin, Hermes, Droid, Cline, Qoder, Qoder CN, Grok, WorkBuddy, Hanako and fx their own skills folder; Kimi, Goose and Grok a project's .agents/skills. The shared folder is filled first, and an agent that reads ~/.agents/skills besides its own (Codex, Gemini CLI, OpenCode, Crush, DeepSeek Harness and those above) gets no second link to a skill already there. Tested with new taken_test.go (neiko's two errors without the change) and agent_skills_test.go (skills folders, written once, a shared skill imported for Kimi), in sandbox HOMEs; library-items and shared-skills 24/24 in Chromium and WebKit; go vet, go build (-tags nogui, GOOS=linux, GOOS=windows), go test -tags nogui ./... pass. Not tried with real Kimi Code, Goose or Cindy. 2026-09-30 14:40:41 +08:00
yetone e79343b1bb ci: the UI preview's planner is shown how to reach what the PR changes — the pages are outlined closed, so on #289 (a hover colour inside an agent's model list) it once said nothing visible changed and once opened the drag handle's menu instead. It now also gets the PR's own front-end code around the diff: where the changed classes and functions are built and, two hops up, what calls them (here modelsEntry's .ag-models button opening openAgentModels), and is told that something missing from the outlines is to be opened, not taken as absent. A close-up no longer fails on clip (declared inside the try it was read after), and a DeepSeek reply that is cut short or not JSON is asked for again instead of ending the repair. Tested locally with run.sh on #289's source: 2 screenshots of the list, the button red on hover, and the video, no errors. 2026-09-30 14:40:04 +08:00
yetone 4828c66aa7 gui: the model picker's rail names an icon beside it, on the right, level with it — not in the browser's tooltip under the pointer, which put Devin's name over ZCode's Z below it, as if that were ZCode's (Elan on X: 切换的时候zcode的logo是一个动画bug吗). The name comes after a moment, moves at once from one icon to the next, and goes on a click, on leaving the rail, on its scroll and when the picker closes; a rail icon focused from the keyboard is named too. Tested with rail-tip.test.cjs in Chromium and WebKit, English light and Chinese dark: the name to the right of its icon, level with it, over no rail icon, no title left on the button, the next icon named at once, gone on click, leave and Esc; it fails on the old code. click-scroll, old-webkit, menu-scroll and model-pick pass; go vet, go build -tags nogui (darwin, GOOS=linux, GOOS=windows), go test -tags nogui ./internal/gui/... pass. 2026-09-30 14:31:04 +08:00
yetone 3a193423b6 ci: a UI preview's close-up keeps what's around the thing it shows (at least 560×340) and no screenshot is stretched past its own size in the PR — the first real run (#289) cropped a hover to the button alone, 100×68, then showed it 760 wide. Checked on #289 with workflow_dispatch. 2026-09-30 14:27:19 +08:00
yetone 1721fac530 devin: an account signed in only in magpie works without the Devin CLI signed in, and is listed once (蓝猫 on Discord: every request from Claude Code failed until devin auth login, then the same account showed twice) — the model list that turns a family and effort into the variant Devin takes (claude-sonnet-5-5 + high → claude-sonnet-5-5-high) was asked only of the CLI's own sign-in, so with that signed out or its key refused the bare family id went to Devin and was refused. It is now asked of the signed-in accounts in their own homes, the one in use first, the CLI's own last. And the CLI's own sign-in isn't listed beside a working magpie account of the same user (an old logins.json entry renamed to that user when the CLI signed in again). Tested with a new TestDevinSignedInOnlyInMagpie in a sandbox HOME with a fake devin CLI and exchange server, following 蓝猫's steps: it fails without either change (variant stays claude-sonnet-5-5; dev@example.com listed twice). go vet, go build (-tags nogui, GOOS=linux, GOOS=windows), go test -tags nogui ./... pass (TestUserPath and TestClaudeAccountNamedAsItsLogin flake under load, pass alone). Not tried against the real Devin API. 2026-09-30 14:24:53 +08:00
yetone 59aec42beb agents: an agent's model list shows each model's maker's logo (这里能否显示模型的图标?) — the list had a logo only on a provider's heading, so a relay's or a routing group's rows were names alone. Now each row has its maker's: known by the model's family (gpt → OpenAI, claude, gemini, grok, qwen, glm, mimo/xiaomi, step, …; "vendor/model" ids too), a routing group by the name it was given or its id, a model of a vendor's own provider by the provider's logo; a routing group whose maker isn't known shows the routing mark, anything else keeps the slot empty so the names line up; a model turned off has its logo dimmed. Checked in a sandbox HOME with a copy of the real providers (keys blanked): 路由组 shows OpenAI for GPT-6 Sol/Luna/Astra, Claude, MiMo, Grok, Qwen and the routing mark for semantic/test; AiHubMix's rows each their maker's; light and dark 2026-09-30 14:23:48 +08:00
yetone 7396b11998 ci: a pull request that changes the UI shows it — the real app built from the PR, run with a real DeepSeek key, screenshots and, when the change spans pages or needs clicking, an mp4 recording with the pointer, its trail and every click drawn in, in a fixed block at the foot of the PR's description (the user: 如果某个PR涉及了UI/UX改动,那么就把这个PR中的Magpie真实的运行起来…录屏中一定要画出鼠标指针的位置,以及鼠标的轨迹,以及鼠标的点击; 一定要贴在原始的PR Description的下面的固定区域).
.github/workflows/ui-preview.yml, on pull_request_target or by hand (-f pr=N): detect (internal/gui/assets/, the GUI's non-test Go, or the ui-preview label) puts a "recording" note in the block; record, behind the ui-preview environment's approval since PRs come from forks, builds the PR's magpie (nogui), runs `magpie web` in a fresh home with DeepSeek added and a few requests sent through the gateway, and has DeepSeek plan from the diff and the pages' real outlines what to click and capture (record.mjs, Chromium at 2x, cursor.js drawing the mouse); publish, which never runs the PR's code, puts the files on the ui-previews branch served by Pages and rewrites only the block between <!-- magpie-ui-preview:begin/end --> — the video's poster links to its player page, since GitHub embeds no video it didn't host. Captions say what is being done, never what should come of it. Anything on screen matching the key or the web link's key throws the whole recording away.

Tested locally with run.sh on 033a151's diff in a sandbox home: 3 screenshots and a 16 s mp4 of the Codex model list's 全部隐藏/全部显示, pointer, trail and clicks visible; actionlint passes. Not yet run on GitHub.
2026-09-30 14:22:13 +08:00
yetone 49e5c99c73 alma: a provider whose models Alma lists as the models themselves ({"id","name","enabled","capabilityOverrides",…}) is read as their ids, so it no longer leaves all of Alma's providers unread — Alma showed as not set with "json: cannot unmarshal object into Go struct field almaProvider.models of type string" (the user, a custom provider "Relay Test" with gpt-5.5 at medium/xhigh). Alma keeps a provider's models as ids or as objects and reads either as its id (server.ts updateProviderModels, database-sqlite, references/providers); magpie read only ids. The ids are still what magpie writes. Tested with TestAlmaModelObjects (a provider with object models beside the user's: magpie's model is applied and read back, magpie's own listed as objects syncs nothing, no drift; fails on main with that error) and by decoding the running Alma's /api/providers. go vet, go test -tags nogui ./..., go build -tags nogui (darwin, GOOS=linux, GOOS=windows) pass. 2026-09-30 14:21:35 +08:00
Anx 575a8f5fe3 tests: TestDriftUnwiredEveryAgent leaves WSL agents alone — it walks every agent and picks a magpie model to see the wiring come back, and on a Windows machine with a real distro running, the WSL agents in All() are the user's own: the pick wrote fake/m1 into their real ~/.codex/config.toml, ~/.claude/settings.json and ~/.pi/agent/settings.json through \wsl.localhost. Their files live in the distro, beyond the sandbox home, so the test skips them (a.WSL set). Verified on such a machine: the full ./internal/agent suite leaves the three files' hashes untouched. (#283) 2026-09-30 14:11:30 +08:00
yetone 85fa42c172 devin: a tool's description goes in the instructions and the tool with a pointer to it, so WorkBuddy's requests are answered (silenx on X: devin 的到 workbuddy 下使用一直出现 502,不知哪里设置问题) — Devin answers "an internal error occurred" to some agents' tools as they describe themselves, whatever the model: WorkBuddy's Read ("Reads a file from the local filesystem. You can access any file directly by using this tool.", also with case or spacing changed, or with words before it) and its TaskOutput ("Takes a task_id parameter identifying the task"), which magpie passed on as a 502 on every request. The same words in a message go through, so the descriptions are now at the head of the first message after the instructions, in <tool_descriptions>, and each tool is sent with its name, its schema and a pointer to its own. Reproduced in a sandbox HOME on a Devin Teams sign-in with WorkBuddy's own request, captured from its bundled CLI: 502 on SWE-2, Claude Opus 5.5 and SWE-1.7 Lightning, 200 without tools; now 200, and WorkBuddy's CLI on all three read a file and wrote another through its Read and Write tools; Claude Code on SWE-2 did the same. TestBuildDevin checks the descriptions in the instructions, each tool's pointer, and none of either when no tool is to be called. go vet, go build -tags nogui (darwin, GOOS=linux, GOOS=windows) and go test -tags nogui ./... pass. 2026-09-30 14:09:57 +08:00
yetone 033a15112d gui: an agent's model list hides every model at once — "Hide all" at its foot beside "Show all" (which was "Show all again"), so picking a few means hiding all and ticking those back, not a group at a time (the user: 能有一个便捷按钮来整体的全部隐藏?现在只能按照组全部隐藏). The model the agent is set to stays; "Shown" keeps what was just hidden till the view changes; each button is disabled when it has nothing to do. Tested with agent-models.test.cjs in Chromium and WebKit, English and Chinese: Hide all sends the 30 models but the one in use, the line under the name reads 1 / 31, then Show all sends none. click-scroll and old-webkit pass; go vet, GOOS=linux build, go test -tags nogui ./internal/gui/... pass. 2026-09-30 13:33:51 +08:00
yetone c6ca0ec3e5 gui: the tray panel's link to one request opens the Routing page on it again — ShowMain escaped the whole view it was given (831df85, v0.1.458, for magpie gui <tab>), so "routing&req=42" reached the window as one view named that, which is none: the window came up on no tab, the request not picked. Found reviewing #239, whose "Open Usage" link hit the same. The view mainView builds keeps its req as its own parameter (mainURL); only a tab named on the command line — magpie gui settings, a restart to update — is escaped (argView). Tested with a new TestMainURL (routing&req=42 comes through as view=routing, req=42; a command-line tab is escaped); it fails with the old escaping. panel-routing.test.cjs passes; go vet, go build (GUI, -tags nogui, GOOS=linux, GOOS=windows), go test -tags nogui ./... pass. 2026-09-30 13:09:27 +08:00
yetone 5b35b139c3 zcode: requests to zcode.z.ai name the machine with X-Device-Mid, so a Z.ai account with only ZCode's Start Plan signs in again (#282 volcanicll: /api/v1/zcode-plan/billing/balance now refuses a request without it, 400 code 3001 "parameter error", and sign-in read that as the Start Plan having ended) — ZCode's apiClient sends it on every request; the server asks only that it is there. magpie makes one v4 UUID for this machine, kept as zcode-device-mid beside providers.json (never ZCode's deviceMid, never ~/.zcode), and sends it on zcode.z.ai calls and Start Plan model requests; Z.ai's and BigModel's business APIs don't get it. A Start Plan balance that can't be read is now said as such in the sign-in error, with the server's code ("parameter error (400, code 3001)"), instead of "has ended or was never started", which stays for an account whose Start Plan really has. Tested with a fake zcode.z.ai refusing a request without the header like the real one (TestZCodeSignInStartPlan, TestZCodeStartPlanOwnAccount, TestZCodeStartPlanRouting and a new TestZCodeSignInStartPlanDeviceMid fail without it): the id is a v4 UUID, kept across a restart, sent to zcode.z.ai only; with it stripped sign-in says the server's reason; an ended Start Plan still says ended. go vet, go build (GUI, -tags nogui, GOOS=linux, GOOS=windows), go test -tags nogui ./... pass. Not tried against the real zcode.z.ai. 2026-09-30 12:51:59 +08:00
yetone 3df75514b9 agents: each agent's own model list — under an agent's name on the Agents page, "Showing 5 / 32 models" (or "All 41 models") opens a list to take models out of that agent's picker one by one and put them back (@ChongkaiX on X: 在 magpie 里选哪些模型出现在 Codex 的模型列表里) — until now an agent's list could only be narrowed by whole providers or families (Visible). settings.HiddenModels holds, by agent, the catalog entries taken out, after Visible: a deny list, so a new model is shown. provider.CatalogFor drops them, so the gateway's /models, the agents' files and pickers and Codex's catalog all follow in one place, and catalog.Touched rewrites the agents' files; Codex's own ChatGPT models are dropped from the backend's list too (CodexNativeHidden), and the list's ETag and models_cache tag carry them (CodexListTag) so Codex asks again. The model an agent is set to is marked 在用 and never taken out, on the server as well. The list groups routing groups first, then providers with their logos and on/total, a long group folded; search, 全部/已显示, a group's 全部显示/全部隐藏 on hover, 恢复全部显示; a click on a row applies at once, Esc or a click elsewhere closes it, and nothing scrolls. A renamed provider's hidden models follow it. Tested with agent-models.test.cjs in Chromium and WebKit, English and Chinese; TestHiddenModels (provider), TestCodexModelListHidesOnAgentsPage (gateway) and TestAgentModelsAPI (gui) in sandbox HOMEs. click-scroll, panel-fold, old-webkit, model-pick, menu-scroll, add-button pass; GOOS=linux go vet, linux and windows builds, go test -tags nogui ./... pass. Not tried in the real Wails window or against a real Codex. 2026-09-30 12:44:59 +08:00
yetone 98d1674a52 library: Antigravity is a Library target — rules, MCP servers and skills reach agy and the Antigravity app alike (#277) — both read their user-wide customizations from ~/.gemini/config: GEMINI.md there, mcp_config.json (a remote server written as agy's own mcp add writes it, its serverUrl, "type": "sse" only for an SSE one so magpie reads it back as one; a url agy read in place of serverUrl is read too; the user's own servers and anything they added to an entry, disabled included, are kept) and skills; a project's skills go in the shared .agents/skills. The rules card says Antigravity also reads Gemini CLI's ~/.gemini/GEMINI.md, so text given to both is read twice. RTK is left out: rtk 0.50 has no user-wide hook for Antigravity. Tested with antigravity_test.go in a sandbox home (target paths and the antigravity-cli alias; stdio, http and sse servers written and read back, user keys and servers kept); library-items.test.cjs 18/18; go vet, go build (GUI, -tags nogui, GOOS=linux, GOOS=windows), go test -tags nogui ./... pass. 2026-09-30 12:42:56 +08:00
yetone 831df8579e update: a restart to update from the open window (Windows, Linux) opens the window again, on the tab it was asked from, instead of the tray icon alone (#275 Aimer779: 设置页更新重启后主窗口消失,只剩托盘) — RelaunchBinary always started magpie tray, autostart's argument, so the new version came up as a tray icon however the update was asked for; the Mac reopens its .app and was fine. The window's Update pill and Settings' "Restart to update" send the tab they're on, the new process starts as magpie gui <tab>; the tray menu's Restart to Update brings the window back if it was open and keeps to the tray if not; autostart stays tray. magpie gui <tab> / magpie app <tab> open the window on that tab, and hand it to a magpie already running. The MAGPIE_REPLACES handover is unchanged: the new process waits for the old one before its single-instance lock. Tested with TestRelaunchArgs and TestRelaunchBinaryArgs (a script in magpie's place is started with gui settings <pid> / tray <pid>); go vet, go build (GUI and -tags nogui, GOOS=linux, GOOS=windows), go test -tags nogui ./... pass. Not tried: a real restart to update on Windows or Linux. 2026-09-30 12:33:36 +08:00