Commit Graph

  • 07e246e515 gui: a key typed into the routing group filter, a provider's keys filter or the plugins search goes in once, through an IME too (#1055: 1, 2 → 1122) yetoneandKuaaMU 2026-10-07 17:57:16 +08:00
  • 01aaa8e925 plugins: the built-in market lists OpenCode Zen Free (@magpie-community/opencode-zen-free-auth), OpenCode Zen's free models, with the bundled opencode icon yetone 2026-10-07 17:14:55 +08:00
  • d40fee800d library: Pi's mcp.json is backed up to ~/.config/magpie/backups before magpie moves it whole to mcp-adapter.json, as it already was before a merge (#1097, heliar-k) yetone 2026-10-07 16:49:30 +08:00
  • c83c0d5fef gateway: sharing keeps the host MAGPIE_ADDR names (127.0.0.1 behind Tailscale Serve, one interface's address) instead of listening on every interface, and still asks what reaches it from elsewhere for a gateway key (#1112) yetone 2026-10-07 16:47:41 +08:00
  • 6b6113aa7a gateway keys: a key can be created with a value of the user's own, one their clients already send, so moving from another gateway (CLIProxyAPI's api-keys) doesn't mean re-keying every client (love1sbug on X) yetone 2026-10-07 14:34:36 +08:00
  • 290c60a31e docs: Plugins says a GitHub repository tagged magpie-plugin is listed under Discover as unofficial (en, zh, ja) yetone 2026-10-07 10:13:24 +08:00
  • f6f960e42c plugins: a GitHub repository tagged magpie-plugin shows on the Plugins page under Discover, as an unofficial plugin nobody at magpie has reviewed yetone 2026-10-07 10:13:20 +08:00
  • f1821f4869 gui: Settings' Web pages help says a page from another site is refused, not that it only gets no CORS headers yetone 2026-10-07 07:32:22 +08:00
  • e8a2b50703 gateway: a Claude subscription run started on a long conversation is told its past tool calls as mcp__magpie__<Name>, the names it can call, so the model no longer calls a bare Bash and gives up on "Bash is disabled for this session" (#958, Cyb3rN8) yetone 2026-10-07 07:29:10 +08:00
  • 3e38bd76dc gateway tests: TestRestingKeyReadAgainInAnOrderedGroup lets the key be back already once the relay has answered with its limit raised (red on ubuntu CI at fb9d1a4c, run 37540375720: "resting: ko first, its rest <nil>") — since ee2588c2 a reading of the key's windows still out as it refused for quota is followed by another at once. When that second reading reaches the relay after the test raises the limit, it lifts the rest before the test plans again, which is right; the test asked for the rest to still stand there. It now accepts ko first and not resting when a reading saw the raised limit, and still wants kx first and ko resting otherwise. With the first reading held 100 ms and 200 ms between raising the limit and planning (a loaded CI runner), the old test fails as CI did and the new one passes. With ee2588c2's re-read taken out and the second reading held 300 ms, the new test still fails 2 of 2 ("still rests by window"). go test -race -count=40 -cpu 1,2,4 -run 'TestRestingKey|TestKey|Lone|Allowance' ./internal/gateway and go vet pass. yetone 2026-10-07 07:17:22 +08:00
  • d688a8b43c providers, gui: a Copilot account on the individual_max plan reads "Max", not "Individual_max", and individual_edu reads "Education" yetone 2026-10-07 07:05:13 +08:00
  • 1ef6c92670 tests: a fake that a test runs under a fixed wait is a file macOS has already let run, so the first-run check no longer kills it on a loaded Mac (WSL omp probe, Reasonix CLI, Grok install) yetone 2026-10-07 07:05:02 +08:00
  • fb9d1a4cc6 provider, plugins: a sign-in reads as done only once its account is listed again, and the command line's plugin sign-in brings the account back and takes its lapsed mark off (#1061) forever 2026-10-06 15:24:24 -07:00
  • 395c4bc51e tests: a test that sets time.Local does it through testenv.Zone, which first waits out the stand-ins' first runs — their os.Stat reads time.Local, and TestDsh -race alone raced it every run yetone 2026-10-07 06:17:52 +08:00
  • 2c2137beb7 sessions: a DeepSeek Harness session can be deleted — its folder, its subagents' and their cached listing rows go to magpie's trash, and Restore puts them back (lc on Discord: deepseek harness无法删除会话) yetone 2026-10-07 05:59:11 +08:00
  • 11ec99de71 providers: a provider whose id isn't one magpie would make (b.ai, put in providers.json by hand) saves, and its editor's ID renames it, where every Save said "provider id must be lowercase letters, digits and dashes" yetone 2026-10-07 05:58:17 +08:00
  • 40ece3a0d1 gui: a provider's Save keeps the tag, website and key page set from the CLI or TUI, and on a preset's provider its Balance and Models URLs, which the editor doesn't show yetone 2026-10-07 04:37:48 +08:00
  • 54b4e36dcf providers, gui: a custom provider saved as OpenAI Responses opens as OpenAI Responses, not OpenAI compatible (01huadalang on Discord: 我选 response 保存了然后再打开一会就变成 openai 兼容了) yetone 2026-10-07 04:34:56 +08:00
  • e13f151302 gateway tests: TestConcurrentRedactedCountTokens' children run the stand-in CLIs their parent already ran, and stop at the parent's deadline rather than a fixed 20s yetone 2026-10-07 06:09:58 +08:00
  • ee7ab461c5 gateway tests: a Claude Code run a test keeps for the next turn ends with the test, so -count no longer gathers 11 fake Claude Code processes a round (#1060) forever 2026-10-06 14:59:51 -07:00
  • a24458ca4b providers: a Copilot Business seat that has used up its premium requests reads as used up, not unlimited (#1063, n1majne3: Github copilot Bussiness额度用尽时显示为无限) yetone 2026-10-07 05:52:09 +08:00
  • d2aa2e1f4e gui: Settings' "Codex's own pick" chips draw their OpenAI mark at the chip's 14px, not the row's 18–22px (duanckham on X) yetone 2026-10-07 05:43:49 +08:00
  • 6f65ffb5ff agent: CodeBuddy Code, Cline, Gemini CLI, AtomCode, Goose and Reasonix installed in a WSL distro are found and wired there (lgtm on Discord: magpie 现在无法检测到安装在wsl中的codebuddy呀?) yetone 2026-10-07 05:37:23 +08:00
  • 51626258d1 gui tests: text-size zooms the page out only after the 100% post and the click's frame, as the webview does (scrolled from 120 to 61, max 78, since bd33e575's font rows made Settings run past the window's foot) yetone 2026-10-07 03:03:07 +08:00
  • 61838f7c94 gui tests: codex-auto-reset clicks its account group's Show more, not the panel's Arrange, which 490d2ea3 made a .pq-more too (strict-mode violation on .pq-more since the panel's Allowances tab got Arrange at its foot) yetone 2026-10-07 02:33:30 +08:00
  • 134d388ba0 gui: a call's JSON tree keeps its keyboard focus styles in Safari before 15.4 — :focus-visible goes in rules of its own (old-webkit failed on 5b9b9c9b's .jt-fold and .jt-copy selector lists, which those Safaris drop whole, hover included) yetone 2026-10-07 02:32:53 +08:00
  • 479865bcfd gui: a Providers row with a vendor's outage badge is laid out by a class, not :has(), which Safari 15.0 doesn't parse (old-webkit failed on 2a990175's .provider .name:has(> .badge.upstream) since #971) yetone 2026-10-07 02:30:21 +08:00
  • d9b2cdc593 plugin tests: TestHostJSCancellation's ready-wait cancels 1,000 bootstrap waits rather than 30,000, so it finishes inside its 10 s under a full suite's load yetone 2026-10-07 04:37:28 +08:00
  • d5468283bb gateway: a web page that isn't listed in Settings, on this computer or the gateway's own address is refused 403 on every route yetone 2026-10-07 04:10:35 +08:00
  • 7837f90d00 gateway: a listed web page's call with a gateway key is answered by the running gateway, not refused 401 yetone 2026-10-07 03:48:11 +08:00
  • 82a36a35c7 click-scroll test: Live and a day, and the room kept for a click, are checked where the accounts sit beside the requests (1440px) — red on main since 5d871723 (#860) yetone 2026-10-07 04:30:23 +08:00
  • 780b3fa3da docs: the FAQ says how to let an agent reach magpie through Surge, Clash or mihomo: rules that send 127.0.0.0/8 and ::1 direct (amor90s, baksili_ on X: Codex got 502 from the proxy and magpie saw nothing) yetone 2026-10-07 04:28:25 +08:00
  • 90960edcb7 gateway: the Routing view keeps a day's routes when a request that began before midnight is done after the day was gzipped, and lists the day once (waitingsnow) (#1057) forever 2026-10-06 13:21:29 -07:00
  • 443f1cbc2d gateway: a held stream's first keepalive reads only magpie's notes until the vendor's reply has its status, so it never races the try writing the reply's headers yetone 2026-10-07 03:58:27 +08:00
  • b234507842 gateway, settings, gui: web pages the user lists in Settings › Network may call the gateway from a browser, each request with a gateway key (SkyAerope, #1051) yetone 2026-10-07 03:33:58 +08:00
  • 76d82528e6 agents, gui: every agent's model list has Order, not only Codex's (cjyrainbow, #1052) yetone 2026-10-07 03:15:24 +08:00
  • 35485f2d93 providers: Cursor's mover asks for opencode-cursor-auth 0.1.10, which far from Cursor counts a tool step whose usage event shows late in the conversation's next steps instead of guessing it uncached (#1053, plugins#33) yetone 2026-10-07 03:24:32 +08:00
  • 0bf9d77d3f agent tests: TestDriftBypassed's request reaching the gateway is kept to its run, so -count=2 passes yetone 2026-10-07 03:03:06 +08:00
  • 06420a7121 tests: a fake program is a file macOS has already let run (testenv.Program), so a fake's first start no longer eats a test's wait under load yetone 2026-10-07 03:00:44 +08:00
  • 0c08b1d5bb agent: Claude Code's tier and subagent models the user set for their own endpoint come back with it when magpie steps out (#1050) yetone 2026-10-07 02:25:42 +08:00
  • c44698bdaa agent: a model given to one of Claude Code's tiers or its subagents stays theirs when the main model moves onto it and on again (#1050) yetone 2026-10-07 02:17:26 +08:00
  • 99f07249e8 agent: Claude Code's tiers and subagents following a main model moved onto the model magpie gave them aren't "changed outside magpie" (#1050, cjyrainbow: Claude Code · WSL Ubuntu 已在 magpie 之外换掉 claude/claude-sonnet-5-5[1m]) yetone 2026-10-07 02:10:54 +08:00
  • 89b3a20f5d gui tests: a test run leaves no gui.test folder in the real ~/Library/WebKit or ~/Library/Caches (waitingsnow) (#1047) forever 2026-10-06 11:23:19 -07:00
  • c06a550b73 provider tests: the hold tests pass on any -count, each starting from a config folder of its own instead of the files the last round left yetone 2026-10-07 02:21:39 +08:00
  • 108852b4e1 provider tests: the CLI sign-in tests run their fake login as /bin/sh -c, not a script written for the test, so macOS's first-run check of a new executable no longer eats linkWait (TestCursorSignInJoinsAWrappedLink failed once in a full go test ./... with "login gave no link to open") — macOS assesses a newly written executable before its first exec: ~0.3s alone (a fresh #!/bin/sh script runs in 0.25–0.35s, the same file again in 0.00s), and seconds while other new executables start, as a go test ./... starts its test binaries. runCLISignIn's linkWait counts from Start, so the "never whole, and nothing more" case, which sets linkWait to 3s, got no line at all before the wait was over. Before: with a loop starting 8 fresh scripts at a time beside it, the test failed 2 of 3 at cursor_signin_test.go:70 with "login gave no link to open", 39s a run. After, under the same load: 5 of 5 pass, 3.9s a run; -race -count=50 -cpu 1,2,4 of TestCursorSignIn|TestCLISignIn pass; -shuffle=on -count=5 of the package beside go test ./internal/gateway and that loop never fails them. TestCLISignInSaysWhyNoLink starts its fake the same way. No fake outlives its test: stop() kills the shell, which has exec'd its sleep. yetone 2026-10-07 02:17:12 +08:00
  • e961836fb0 usage, gateway, gui, provider, cli: the tests pass when a run crosses midnight, and a usage answer reads the clock once (#1046) forever 2026-10-06 11:14:29 -07:00
  • e277f5c5ef usage: four tests pass when run alone and in any order — one saves no account copy mid-test, three price with the catalogue they write (#1045) forever 2026-10-06 11:09:24 -07:00
  • 8a7f600cb0 provider: the Requests page keeps its cache when magpie first saves a signed-in account (#1049) forever 2026-10-06 11:09:17 -07:00
  • 9db5753ba3 providers: a provider's model list reads the settings once, not three times per model (lml on Discord, Windows: 每次至少都得等五六秒…供应商或者额度列表还在加载) — each model's row asked for its image answer twice (ApplyImage, ImageOverride) and its reply limit once (ReplyLimit), and each ask parsed settings.json again, even inside a GET's hold, since filememo keeps its bytes, not the parse. providerInfo now reads the settings once and uses provider.ImageOverrideIn and Provider.ReplyLimitIn. This removes redundant work found while measuring lml's report; it does not explain the whole 5-6s, which needs their setup (see below). yetone 2026-10-07 01:52:56 +08:00
  • 54c3cb06fc agent: the default thinking effort set on one of magpie's models in WorkBuddy stays through a sync, as it does in CodeBuddy Code, ZCode, Kimi Code and MiniMax Code (悠悠哥 on Discord: 供应商能设置模型默认的思考强度吗,我的workbuddy每次手动设置了思考强度,还是会被更新覆盖掉) yetone 2026-10-07 01:43:55 +08:00
  • 05683637d5 plugins: the Bun the plugin host runs on is kept as magpie-bun, so a proxy app's PROCESS-NAME rule can route the requests plugins (Cursor and the other moved subscriptions) make (#1048) yetone 2026-10-07 01:37:23 +08:00
  • de2c84ace7 gateway: a Responses relay that turns away reasoning with nothing sealed in it, naming only the input, is asked once more without it, and isn't sent it again for that model (#1044: Codex's /compact through Anyrouter failed with 400 invalid_request_error param=input on old conversations) yetone 2026-10-07 01:34:57 +08:00
  • 615eba382b providers: MiMo's mover asks for opencode-mimo-auth 0.1.8, whose card shows a Token Plan bought at the open platform rather than Free (yetone: 我明明已经订阅了啊为啥说我是 free 账号啊) yetone 2026-10-07 00:57:27 +08:00
  • 71f0541342 gateway: Antigravity's Claude gets a tool turn's images after every tool result, each labelled with the call it came from (@sinswing on X: an image between two functionResponses came back from Google as "tool_use ids were found without tool_result blocks immediately after") yetone 2026-10-07 00:44:32 +08:00
  • 698da36f31 gateway, providers, gui: a model on this computer or the local network can be set to get requests unmasked by Settings' redaction (lc on Discord: 本地模型可以不脱敏) yetone 2026-10-07 00:31:11 +08:00
  • 1caea63804 tui tests: the package runs in UTC from TestMain, so no test swaps time.Local under the gateway's loops yetone 2026-10-07 00:32:42 +08:00
  • 5d8717239d routing: accounts sit beside the requests only where each request still keeps to one line (#860, huoranxuanyuan: 宽窗口下变成两栏,请求行折成三行) yetone 2026-10-07 00:25:28 +08:00
  • fdcdb5af21 usage: the requests table fits a narrow window with the reporter's rows, Speed and Status in sight (#860, huoranxuanyuan: 窗口较窄的提示出现了,速度和状态列仍被挤出去) yetone 2026-10-07 00:25:28 +08:00
  • 2fb538c4d7 panel: the tray panel's usage has All beside Today, 7 days and 30 days, as the window's Usage does (#860, huoranxuanyuan: 托盘里用量只有今天/7天/30天,没有全部) yetone 2026-10-07 00:25:20 +08:00
  • 8ef53f2ec8 gui: an Antigravity card keeps its order when a refresh leaves out a pool's 5 hours (#860, huoranxuanyuan: 刷新额度有时会让5h和七天的顺序颠倒) yetone 2026-10-07 00:04:28 +08:00
  • 92469ee9f9 library, gui: a check for updates lists the skills a repository added beside the library's skills from it, to Add or Ignore (Raven on Discord) yetone 2026-10-07 00:22:43 +08:00
  • da1fd6cb0c gateway: a Gemini functionResponse's own parts reach the upstream — the image agy's view_file read (#1038, handong66: agy via magpie can't see images, the model makes up what they show) yetone 2026-10-07 00:14:48 +08:00
  • 18d973ba7b docs: LESSONS.md gets the rest of 10-06's review — merging before writing what was run (9 of 10 merges), the reviewed head (#974), re-deriving what a change builds on (e0bf3e61, 2a990175) and another app's semantics from its code (#966/#996) are seen a second day; two new: no blocking send under a lock the reader needs (4fb8e887, c8690ca0), and a test's fake processes end with it (38a0f470) yetone 2026-10-07 00:23:19 +08:00
  • 1dceda18a1 agent: taking magpie out of a running OpenHanako 1.0 puts its agent on one of OpenHanako's own models, as provider/id (#1040: "model: OpenHanako: 400 Bad Request /api/agents/xiaojing/config: models.chat requires provider/id") yetone 2026-10-07 00:17:35 +08:00
  • 117b6c7fb4 usage tests: TestReasoningReplyTimedByItsAnswer's timeline rows are made now, as LedgerSeries buckets today by the clock yetone 2026-10-07 00:16:14 +08:00
  • 764bbfdad5 sessions, gui, tui, cli: the Sessions list is as long as the count beside it, and drawn in pages (#1019) wizzy-yangandwizzy-yang 2026-10-07 00:16:13 +08:00
  • d7b1d9df41 gui: with Settings' Bar icon on, Omarchy's bar shows one magpie, not the widget and the tray icon side by side (Alex on Discord) yetone 2026-10-07 00:15:59 +08:00
  • a7e7fe2b0f library: switching a group of skills on or off no longer reads every copy every agent has (#1031, mintonight: 开关一整个技能组时很卡,要10多秒才能完成) yetone 2026-10-07 00:13:56 +08:00
  • 14af0f100b provider, plugins: a plugin's browser sign-in finishes from the address the browser ended on, for magpie on a server or in Docker (Chicring) yetone 2026-10-07 00:12:58 +08:00
  • 9d84443086 test: a profile with no catalog to write back from is still said (#1037) wizzy-yang 2026-10-07 00:02:41 +08:00
  • b1cba65477 davsync tests: the slow transfer runs on a line with small buffers, and the PUT's tail is the server's to hold (TestSlowTransferIsNotCutShort red on ubuntu in PR #1030's run 37466034290: "the server sent and took nothing for 300ms (PUT)") yetone 2026-10-06 22:29:43 +08:00
  • 60f87223c3 gui tests: skill-how clicks a By agent choice once its sheet has finished growing in (skill-how.test.cjs red on WebKit about 2 runs in 5, Codex's way posted "" where "link" was asked) yetone 2026-10-06 22:21:46 +08:00
  • e2f1c6bacf agent: OpenHanako 1.0's encrypted provider-catalog.json is never written over; magpie's address and models are kept in its plugin file (owner: "~/.hanako/provider-catalog.json: top level is not a JSON object") yetone 2026-10-06 23:06:47 +08:00
  • 7bdb0b34e8 ui-preview: the sandbox gets made-up data before it is recorded, so a change that only shows with data can be seen (owner: pr-preview 的时候有时候没有数据压根儿测不了,能否帮忙造一些假数据之后再测啊) yetone 2026-10-06 22:49:33 +08:00
  • dfc66030e8 provider: keep saved credentials when reading or backing them up fails (#1034) Zhizhen He 2026-10-06 22:48:34 +08:00
  • 5b9b9c9b05 gui: a call's JSON body in the Gateway page's recent calls folds, is coloured and copies a key or a value (owner: 这里应该支持 json 对象折叠以及代码高亮以及方便复制某个key/value) yetone 2026-10-06 22:41:28 +08:00
  • dcbae2ef98 library, gui: the MCP servers page has the skills page's Select, Turn all on, Turn all off and Remove all, under one "In the library" heading (#1027, emo172) yetone 2026-10-06 21:37:05 +08:00
  • 51d2cf88ea plugin host: a plugin's node:http2 connection goes through magpie's proxy like its fetch does (#1035, cataglyphis: Cursor opus-5-5 high ends after ~148s with "Cursor: 502 Bad Gateway" cursor_error) — the Cursor plugin runs agent.v1.AgentService/Run over node:http2, which Bun connects directly and ignores the proxy that host.js gives every fetch; the built-in sent the same request through s.client, so a user who needs magpie's proxy (setting, env or the system's) to reach Cursor had their sign-in, models and usage proxied but every Run went out directly. host.js now wraps http2.connect: for a URL proxyFor gives a proxy to (provider/account via, global, SOCKS bridged), it opens an HTTP CONNECT tunnel (Proxy-Authorization from the URL) and runs TLS with ALPN h2 over it; "direct", a caller's own createConnection and unproxied URLs keep Bun's connect; a tunnel that fails ends the session with "proxyconnect tcp: …". TestPluginHTTP2TakesTheProxy (an HTTP/2 vendor at an unresolvable host reached only through a CONNECT proxy; direct and a dead proxy fail with the reason) fails without the change ("h2: getaddrinfo ENOTFOUND h2vendor.invalid"; it carried 0) and passes -race -count=10. With the owner's Cursor account in a sandbox HOME and a logging proxy set, Claude Code's captured request on cursor/claude-opus-5-5 high: before, the proxy saw api2.cursor.sh only; after, it also sees agentn.global.api5.cursor.sh and the reply streams. The reporter's 502 wasn't reproduced here (direct runs answer in 4–20s). go test -race ./internal/plugin, the gateway/provider Plugin|Proxy|Cursor tests, gofmt and vet pass. yetone 2026-10-06 22:16:09 +08:00
  • a0cc4562e7 agent: a dsh patch list that lost its route to dsh's own save is written back quietly (#1029) wizzy-yangandwizzy-yang 2026-10-06 22:09:44 +08:00
  • 33e4f165aa docs: how magpie picks an account — the steps a request's accounts go through, what Make first counts under each routing, Smart's and Weekly pace's real rules, rests after running out, gateway vs. on its own, and how often an allowance is read (#1032, Cyb3rN8) yetone 2026-10-06 21:52:41 +08:00
  • 38a0f4709c provider tests: the fake claude auth login ends once its test's folder is gone, rather than polling for ever — a TestClaudeSignInByPaste run that ended without signing in left its fake looping every 50ms for 2 days (pid seen 10-06, ppid 1). Its loop waits for the callback's file or the pasted code, both in a TempDir the test removed, so neither ever came. Checked: the loop body exits 3 after its folder is removed; TestClaudeSignIn*, TestClaudeSavedAccountDir pass -race -count=5. yetone 2026-10-06 21:46:23 +08:00
  • 6e53ee1f1d library, gui: skills installed from GitHub are above and this computer's below, and one author's repositories are one group (#1031, mintonight: 同一个作者的skill没有自动分组,原本在local的一些技能自动跑到上面来了 … local和online上下分开,给local中的skill分组后也只显示在下方的local里面,恢复上方online的自动分组功能) yetone 2026-10-06 21:29:59 +08:00
  • ee2588c268 provider: a key that says it is out of its windows while they are being read has them read again once that reading is back (TestRestingKeyReadAgainInAnOrderedGroup red on macOS CI, runs 37467016755 and on main since 555f5706) — since 555f5706 the first planning of a lone key reads its windows behind the request. When that reading was still out as the key refused for quota, StaleKeyAllowance only zeroed the time it was read, and the reading, asked before the refusal, came back, set it to now and was trusted for a minute: the rest out of the key's windows wasn't lifted by the reading planned after it, though its limit had been raised. StaleKeyAllowance now marks a reading that is out (stale), and once it is back the key's windows are read again at once; forgetting every key's windows clears the mark. TestKeyAllowanceStaleMidRead (the first reading held until after StaleKeyAllowance, then answered; the second answered with the week half used) fails without the change (asked once) and passes -race -count=20 with the other TestKeyAllowance tests. The gateway test fails every time with its first reading delayed 300 ms ("still rests by window …, its windows read 2 times", as CI said) and passes with the change. go test -race ./internal/provider (8 of 9 runs pass; the first run's failure wasn't kept and didn't come back), go test -race -count=10 -run 'TestResting|Key|Lone|Allowance' ./internal/gateway, go vet, gofmt -l pass. yetone 2026-10-06 21:29:55 +08:00
  • 29f4909262 fonts: list upright styles before slanted faces at the same weight (#1030) tsuixl 2026-10-06 21:15:19 +08:00
  • c8690ca0d2 gateway: a Claude subscription run knows the tools ToolSearch loaded before its agent is handed the results (TestToolSearchLoadKeepsTheRun failed on CI now and then; wizzy-yang's #1026 retried the assertion) — on the continue path run.continueWith answered the agent's MCP call before run.tools recorded the request's tools, so a turn that finished quickly could be shelved (shelve copies run.tools) or asked offers() without the tool ToolSearch had just loaded, and the next request started a new run; on a new run, run.tools was written without run.mu while the agent was already running. The tools are now recorded under run.mu before continueWith, and a new run's tools and search are set under one lock. A 50ms sleep after continueWith fails TestToolSearchLoadKeepsTheRun 5/5 on the old code with CI's message and passes with this; go test -race -count=100 -cpu 1,2,8 -run TestToolSearch and the gateway's subscription tests pass; gofmt, vet pass. yetone 2026-10-06 21:11:25 +08:00
  • a8d798e2cb gateway, provider tests: the tests stay off the network (testenv.Offline in their TestMains) — after 555f5706 (#1016) routing reads a plan key's quota windows, and a test whose provider has a vendor's real base URL and a made-up key would ask the vendor in the background. Routing's own read (keyAllowance, allowances) was already stubbed in routing_test.go's init, and a survey with a dial guard found no plan-key dial, but it found others: every plugin host's first start fetched models.dev's catalog (65 dials in the gateway's plugin tests), and provider tests asked api.github.com (TestSubscriptionUsageEachAccount, TestSubscriptionUsageServesStale), chatgpt.com (TestCopilotQuotaWithoutEditorsSignIn), server.codeium.com (TestDevinSignedInOnlyInMagpie) and decide.test (TestModelTest) with made-up accounts; each passed whether the vendor answered or not. internal/testenv's Offline wraps http.DefaultTransport's DialContext (so http.DefaultClient and every transport cloned from it later): localhost, *.localhost, loopback and unspecified addresses are dialled, anything else is refused before it is looked up, said once per address on stderr ("testenv: a test asked models.dev:443, refused: tests stay off the network"). internal/gateway's TestMain and internal/provider's (kiro_main_test.go, in RunIn's setup) call it before the tests run. TestLocal (the address table) and TestOffline (a loopback httptest server answers through DefaultClient; models.dev is refused through it and through a Clone made after the call) pass -race -count=3. go test -race -tags nogui -timeout 30m ./internal/testenv ./internal/provider ./internal/gateway passes under a temp HOME (gateway 516 s). The gateway and provider test binaries were then run under sandbox-exec with every non-loopback network-outbound denied, with the kernel's Sandbox reports streamed: both PASS, Offline refused only the six addresses above, and the kernel reported no denial, so no transport outside DefaultTransport reaches the network either. go vet, gofmt -l, linux and windows (nogui) builds pass. Tests only. yetone 2026-10-06 21:08:49 +08:00
  • f311a61859 gui tests: usage-speed.test.cjs's comment gives the numbers the fixture makes (75.0 tok/s for glm-fast against the hour's 69.7, not 80.8 against 75.2) — comment only yetone 2026-10-06 20:55:01 +08:00
  • 70e7e36675 gui tests: a press is made only on a target wholly in sight, so Playwright's click doesn't scroll it the last fraction of a pixel itself (request-archive-usage.test.cjs: "a click moved the page: 860 to 861" in Chromium once the Usage page's archive switch shows, which #1017 makes it do) — reader.inView took a target cut off by up to a pixel at the view's foot as in sight; Fetch from archive spanned 539.80–562.59 in a 562px view (the switch makes .led-head 4px taller, so the 20px wheel steps land the rows elsewhere), and Playwright's click scrollIntoViewIfNeeded'd it 1px before dispatching, before the app's capture-phase click listener took the positions hold() keeps, so the app saw nothing to put back. A raw page.mouse.click at the same point moves nothing; no page code scrolls or focuses there, and overflow-anchor: none changes nothing. inView now wants the target wholly in the view (or, taller than it, its top in sight), and the press test's web ±1 allowance ("settles a row's first opening up to a pixel off") is gone: a click moves the page by nothing. With #1017's diff applied, request-archive-usage.test.cjs failed in Chromium (both app modes) and passed in WebKit; with this it passes 5/5 in both, and 3/3 with the allowance removed. The 32 suites using reader.cjs, request-archive*, usage-*, click-scroll and ledger-* pass in Chromium and WebKit (275/275). No app change. yetone 2026-10-06 20:55:01 +08:00
  • aad799eae6 library: an RTK release winget or Homebrew doesn't have yet says "v0.51.0 is out · waiting for winget" with no Upgrade, until it does (#1025, Fermin214) yetone 2026-10-06 20:56:48 +08:00
  • dd027bc433 sessions: a Claude Code workflow's agents are read as their session's calls (ksinverse on X: 发现Claude的用量和会话不太准,codex就非常好。是不是sub-agents没有被统计进去?默认开ultracode) — Claude Code writes a workflow's (ultracode) agents a folder deeper than other subagents, <session>/subagents/workflows/<run>/agent-*.jsonl beside the run's journal.jsonl, and ccFiles read only <session>/subagents/*.jsonl, so with ultracode on every workflow agent's calls and tokens were missing from Usage, the Requests page and the session's totals. ccFiles now reads each run's agent-*.jsonl too (the journal isn't a transcript and stays out), and sessionOfPath gives them their session, so they count as the session's like any subagent's; Claude Desktop's and Qoder's readers, which share ccFiles, follow. TestCallsWorkflowAgents (a session, a workflow agent's file and a journal with a line that reads as a call) fails without the change (1 call, the agent's missing) and passes. On this machine's real ~/.claude (read only), 34 workflow-agent calls of one session were unread and are now that session's. go test ./internal/sessions, -race -count=10 on TestCalls*/Subagent*/Trace*, go vet (darwin, windows), gofmt -l, linux and windows (nogui) builds pass. Not tried against the reporter's own transcripts. docs/reference.md says it. yetone 2026-10-06 20:54:36 +08:00
  • a334fe091c gateway: a request a proxy or tunnel on this computer forwards to loopback is answered as one from another machine, and needs the gateway key (#1022, znjhahaha: asked for a Cloudflare Tunnel in magpie, with the gateway key checked for requests through it) — local() was the request's address alone, so cloudflared, ngrok, Tailscale serve or funnel, frp's HTTP proxy, or a Caddy or nginx here, all reaching 127.0.0.1:3425 for clients anywhere, were taken for this computer's agents: whoever had the tunnel's address used the user's subscriptions with any token, read /v1/magpie/quotas (the accounts and their balances) and the remote cards, and their usage was counted as local, all without sharing on. reference.md said a proxy here had to sign its clients in itself; a quick tunnel signs in nobody. A request on loopback carrying a forwarding header (Forwarded, X-Forwarded-For, X-Real-IP, Cf-Connecting-IP, True-Client-IP, Tailscale-User-Login), which no agent sends, is now proxied, and local() is false for it, so every check built on local() takes it as from the network: lanGuard refuses it while magpie isn't shared, saying it came through a proxy or tunnel and that Settings → Share on local network and a gateway key let it in; shared, it needs an enabled gateway key and is then sharedWith, as a LAN client is, for the model APIs, quotas, remote cards, concurrency, session route and MCP sign-ins; the Omarchy theme and the Codex title link stay this machine's only, and its usage is no longer marked local. A gateway MAGPIE_ADDR opens to the network stays open to a tunnel as to anyone; one MAGPIE_ADDR keeps on loopback is closed to it. MAGPIE_TRUST_PROXY=1 says a proxy here signs its clients in itself and keeps what it forwards local, as before (listed in agentenv_test's notAnAgent). A tunnel forwarding bare TCP (frp tcp, ssh -R, socat) adds no header and can't be told from an agent: reference.md says to point one at the LAN address with sharing on. Running cloudflared from magpie with a pasted Tunnel Token is not done here: magpie would have to download or run a program it doesn't ship; reference.md says how to serve magpie through a Cloudflare Tunnel today. TestProxiedLoopbackNeedsTheKey (from 127.0.0.1 with cloudflared's, ngrok's, nginx's, RFC 7239's, Akamai's and Tailscale serve's headers: refused unshared, 401 shared without a key, with magpie's token or a wrong key, let in with the key under its identity, a disabled key refused; an agent on loopback let in with any token; MAGPIE_TRUST_PROXY; MAGPIE_ADDR on loopback and open) and TestProxiedLoopbackRoutes (quotas, quotas history, models, Omarchy and /mcp through a tunnel, unshared and shared, with and without the key) fail on main (200 for every tunnel unshared) and pass -race -count=10 with TestLANGuard, TestQuotasOverLAN, TestManagedLANGuard and TestExplicitOpenGatewayAcceptsAllRemoteTokens; go test -race -tags nogui ./internal/gateway (579 s, under -timeout 30m) and go test -tags nogui ./internal/... under a temp HOME pass; go vet (darwin, windows), gofmt -l, linux and windows (nogui) builds pass. No GUI change. Not tried with a real cloudflared (not installed here); its Cf-Connecting-IP and X-Forwarded-For are what Cloudflare documents it sends. yetone 2026-10-06 20:29:45 +08:00
  • 555f570660 gateway, provider: the Routing page says what a lone key or account has left, and a key bought as a plan (Kimi Code, GLM Coding Plan, MiniMax, OpenCode Go, Command Code) is weighed by its plan's windows (#1016, gphysign: Kimi Code (China) with one key on, Smart routing; the card said only "Kimi Code (China) 只启用了一个 Key,无需选择。" while /v1/magpie/quotas had its 5 hours 49 used, 51 left and its 7 days 45 used) — two gaps. weighRouted returned before reading anyone's allowance when there was one candidate, so a lone key or a lone subscription account was never known in the trace, and the Routing page, which shows a bar and "{n} used · renews in {d}" for a known row, had nothing to show; every single-account subscription said "what's left not known yet" the same way. And KeyAllowance read only a sub2api key's limits, so a key whose plan the Usage page reads (planQuotaSourceOf: Kimi Code, GLM Coding Plan and Z.ai, MiniMax Coding Plan, OpenCode Go, Command Code) was never known to routing, with one key or several. weighRouted now reads what each candidate has left before returning a lone one (one alone is still weighed against nobody). readsKeyWindows takes a plan key too; keyWindows reads it through planKeyWindows (planWindows, with Zhipu's team quota behind a key that has none of its own, as PlanQuotas does), and PlanQuotas' own read of a card is noted for routing at once (noteKeyAllowance), so the Usage page and routing never ask twice; the card kept on disk seeds it after a restart (keyCardTag keys a plan card as plan:, a sub2api one as balance:). A key at such a vendor that turns out to be pay-as-you-go, with no windows, is asked again every 30 minutes (keyNoWindowsAge), not every minute it is used. So, beyond the issue: several plan keys on one provider are now weighed by their windows under Smart, usage and pace, and a plan key the vendor refuses as out of quota rests until its window renews and comes back once read short of full, as a sub2api key does. affine no longer says a lone key or account all but used up "moved" its conversation: there is nobody to move to (at > 0). The GUI needed no change. TestLoneOneTellsWhatItHasLeft (a lone Kimi key known at 49% with both renews, the week first; a lone Codex account known; a plain DeepSeek key not), TestLoneSpentKeyKeepsItsConversation (fails with the at > 0 guard taken out), TestPlanKeyAllowance (the windows read behind the request, asked once; DeepSeek and Moonshot keys never asked), TestPlanKeyAllowanceFromItsCard (PlanQuotas' read known at once without a second ask; after a restart with the vendor down, the card on disk stands in and a failed read keeps it) and TestPayAsYouGoKeyAskedSeldom (a GLM pay-as-you-go key asked once, not two minutes on, again 31 minutes on) all fail without the change and pass -race -count=20 (provider) / -count=10 (gateway). routing-key-windows.test.cjs gains a lone Kimi Code key at 1100px and 440px (the one-key story, the row's bar at 49%, its provider row "49% used · renews in …" fitting, no sideways scroll) and passes in full on Chromium and WebKit in en, zh, ja and de; routing-quota-left and panel-routing pass. go test -tags nogui ./internal/..., go test -race on ./internal/provider and ./internal/gateway in full, go vet, gofmt -l, darwin, linux and windows (nogui) builds pass. Not tried with a real Kimi Code key. StepFun's Step Plan is read through a sign-in, not the key, so it isn't one of these. docs/subsystems/gateway-routing.md, providers-accounts.md and docs/reference.md say it. yetone 2026-10-06 20:25:01 +08:00
  • 3bcf71b003 gui: the speed chart's marks that leave their column get a leader line (#1018) wizzy-yangandwizzy-yang 2026-10-06 20:23:11 +08:00
  • 0fb669175b agent: fx is found by its folder, and omp isn't found by Pi's (an fx on PATH is often antonmedv/fx, the JSON viewer; PI_CODING_AGENT_DIR pointed omp at Pi's folder, so a machine with only Pi showed omp as installed) (#1021) XYenon 2026-10-06 20:14:37 +08:00
  • 655cdd0abd provider: the hook a landed usage refresh tells is held atomically and told before the refresh is done (#1023) — OnSubscriptionUsage was a plain package variable that the background refresh SubscriptionUsage starts read after closing its pending channel, unsynchronised, while the tray sets it as it starts (watchTrayUsage) and a test swapped it: go test -race ./internal/provider failed in TestSubscriptionUsageServesStale when TestSubscriptionUsageReadingWhileStale's refresh, waited for by its pending, was still reading the hook after it (subscription_usage.go:185 against subscription_usage_test.go:84). OnSubscriptionUsage is now a setter over an atomic.Pointer, so setting it while a refresh is under way is safe in the app too, and the refresh tells it before closing pending, so whoever waits for a refresh has it finished, hook and all, and may change what it reads. The tray's hook only signals a buffered channel and never waits, so telling it first holds no one; the test's hook no longer blocks either. With -race -count=20 the two tests failed on main with the DATA RACE and pass now; go test -race -tags nogui ./internal/provider passes in full; go vet, gofmt -l, darwin, linux and windows (nogui) and windows builds pass. yetone 2026-10-06 19:59:29 +08:00
  • fde0c38c56 fix(gateway): a refusal that arrives once the reply began rests nobody (#1015) TryWorld 2026-10-06 19:54:53 +08:00
  • e6dd5d20bb usage, gateway, gui: a reply that reasoned is timed by its answer, from its first text, not by all its output from its first token (tony on Discord #general: Usage › Requests read "gpt-6.1-sol 163 token/秒 · 8,036 个请求 · 首字 14 秒" and asked whether subagents were added up) — speed was every output token over the time after the first content (text, reasoning or a tool call). Output counts the reasoning, and a model whose reasoning is hidden (OpenAI's encrypted reasoning, a summary sent when it is done, Claude's omitted thinking) writes it before the stream shows anything, inside the TTFT; so a 2,000-token think and a 200-token answer over the 1.5 s after the first token read 2,200 in 1.5 s, 1,467 tok/s. Concurrency was not the cause: the aggregate is Σ tokens over Σ per-request windows, so subagents running at once never add up. In the owner's own usage.jsonl codex/gpt-6-luna turns read up to 600 tok/s (out 496, reasoning 224, 11,055 ms, ttft 10,228) and its aggregate 133; it is 80 now. usage.DecodeOf is the one rule: a reply with reasoning counts out − reasoning over ms − firstText, one that reasoned and wrote only tool calls tells no speed, one without reasoning is timed from its TTFT as before; DecodeWindow's burst cut (#731) still applies. Record.Decode feeds summarize, the ledger's Totals and its timeline Parts; a route keeps its reply's reasoning (Route.Reasoning, from gateway.go and codex_backend.go), and routing.js's decodeOf/speedOf times its rows, the story and a session's average by it (an older route takes its served usage's reasoning); app.js's ledDecode does the Usage page's rows, whose tooltip says the reasoning left out, and the Output speed help says the rule, in en, zh, ja and de. History is read the same way, as it keeps the reasoning and the first text. Routing never ranks by speed. TestReasoningReplyTimedByItsAnswer (the 2200/2000 turn, a summary 10 ms before the text, a concurrent subagent, tool calls only, reasoning over output, no reasoning, not streamed: 1,200 tokens in 11 s through summarize, addRow, LedgerSeries and Decode) fails without the change and passes -race -count=10; TestUsageKeepsTimeToFirstToken now has its paced replies report reasoning and checks the route keeps it. reasoning-speed.test.cjs (Routing rows 133, 100, none and 100, the story never 1,467, the session 105; the Usage rows, tooltip and KPI the same; no sideways scroll) passes on Chromium and WebKit in en, zh, ja and de, and fails against the old assets (1467 tok/s); routing-request-metrics, routing-burst-speed, usage-speed, usage-ledger-detail, i18n-keys, gui-ja, gui-de, ledger-hscroll and click-scroll pass on both. go test -race -tags nogui ./internal/... under a temp HOME passes but internal/proc's TestUserPath ("the login shell's PATH never came"), which failed under the full run's load, passes 3/3 alone and is not touched here; go vet, gofmt -l, linux and windows (nogui) builds pass. Not tried with tony's data. docs/reference.md says how output speed is counted. yetone 2026-10-06 19:47:53 +08:00
  • 76321e3b42 gateway: a lone provider's Claude or GPT reasoning streams as it comes, not held for a refusal no one else could answer (iTianbao on X: 请加一个开关,让思考实时透传; through a relay Claude Code showed the thinking only all at once with the text, Codex none until then) — holdWriter held Claude's, GPT's and Gemini's reasoning (refusesAfterThinking, up to holdThinking) on every candidate, so a refusal after it could go to the next one (#248), but a refusal on the last candidate is never asked again (the !last checks in serve), so there it only delayed the thinking. On the last candidate Claude's and GPT's reasoning is now shown at once (thinkingShown), and a refusal after it reaches the agent as the vendor's own stop; Gemini's stays held there, for a reply that only reasoned to be asked again (#667, TestGeminiEmptyRepliesAskedAgain). A group with members after it still holds as before. Before: on a slow fake relay (4 thoughts 300ms apart, then text) every relay shape (Messages, Chat with reasoning_content and reasoning, Responses) for claude-sonnet-4-5 and gpt-5.1 gave Claude Code's thinking_delta and Codex's reasoning_summary_text.delta all at ~1.2–1.5s with the text; after, 0/300/600/900ms. TestThinkingStreamsWithNoOneElse (Messages, Chat for Claude and GPT, Responses; each asked from /v1/messages and /v1/responses) fails without the change in every case and passes; TestHeldThinkingKeepsTheAgentAlive's nobody-left case now expects the thinking and Claude's refusal rather than a held error. -race -count=5 on TestThinking*, TestRefusal*, TestHeld*, TestGeminiEmpty*; go test -tags nogui ./internal/gateway, go vet, gofmt -l, linux and windows (nogui) builds pass. Not tried with the reporter's relay: whether it sends GPT's reasoning at all (OpenAI's Chat API doesn't) is unknown, and no switch was added. docs/subsystems/gateway-routing.md says it. yetone 2026-10-06 19:29:50 +08:00
  • bd33e575f1 gui: let users choose installed interface and code fonts (#1007) tsuixl 2026-10-06 19:32:56 +08:00
  • 490d2ea362 gui: the tray panel's Allowances tab hides subscriptions and moves them, from an Arrange at its foot (H20 on Discord: "菜单栏的详情页能不能选择某些不显示,有点额度并不想看到或者可以调整顺序,目前的还不行") — the panel's tab already followed the Usage page's order (usageOrder, byUsageOrder), but it could be set only by dragging a card's logo on the window's Usage page, which nothing in the panel said, and there was no way to leave a subscription out of the panel at all. The tab's foot now has Arrange, and "{n} hidden" beside it when some are: Arrange lists a row a subscription (balances too) in the shared order, its logo the handle (a drag, or Alt+arrows; the grip by it on hover only, as on the Usage page) and a Hide/Show pill, under a note that hiding is the panel's only and the order is the Usage page's too; Done draws the cards again. What's hidden is kept in magpie's settings (settings.PanelUsageHidden, panelUsageHidden), not the browser: routing, caps, the Usage page and the menu bar's cells still have it, and a menu bar cell for a hidden subscription still opens its card (panelPeek) until the panel is put away or the tab left. POST /api/usage/arrange takes order and panelHidden and changes only the one it is sent (both were whole-order before; an order alone still works), and the Settings save keeps panelUsageHidden as it does usageOrder. Saves draw at once and are put back with the error if they fail; no click moves the page (the view's scroll is kept across the redraw); no native select, no left-border stripe. Which allowances the menu bar shows beside its icon stays Settings' pick (trayUsages). New t() strings have zh, ja and de. TestUsageArrangePanelHidden (each field set and cleared on its own, a Settings save between) passes -race -count=20, TestSettingsSaveKeepsWhatItDoesNotSend's fixture names the field. panel-arrange.test.cjs (440px: the hidden card left out and counted, Arrange keeping the reader's scroll, the rows and note, the hover-only grip, no stripe, no sideways overflow, Show/Hide posting panelHidden without a scroll and a failed save put back, Alt+arrow and a drag posting the order, Done's cards in it, a menu bar cell opening a hidden card until the panel hides, the Usage page keeping the hidden card) passes on Chromium and WebKit in en, zh, ja and de; click-scroll, gui-ja, gui-de, i18n-keys, panel-fold, panel-usage, quota-*, routing-quota-left, tray-cell-click, tray-inuse, tray-logos, tray-usages, usage-accounts(-fold), usage-arrange, usage-fold, usage-quota-head, usage-card-refresh, usage-stale-answer, usage-remote-cards, api-key-usage, claude-usage-asked and usage-checkin pass on both. go test -tags nogui ./internal/... under a temp HOME, go vet, gofmt -l, linux (nogui) and windows builds pass. Not tried in the real tray panel on a Mac. docs/reference.md and docs/subsystems/gui-shell.md say it. yetone 2026-10-06 19:18:07 +08:00