agent: an agent's "restart it" advice reaches Windows, where a model picked in magpie looked like it had done nothing (#898)

Running() answers whether a process whose command line matches a pattern is
alive, by asking pgrep -f. Windows has no pgrep, so it answered no for every
pattern — and every one of its 25 call sites is the advice an agent's own
lists need after magpie changed what that agent reads at start ("Codex builds
its model list at start-up — restart the Codex app", "New dsh sessions start
on this; one already open keeps its model until you pick another in it"), each
inside a Notice closure, so nothing else rides on the answer. On Windows that
advice was never given: the Agents page wrote the file, said nothing, and a
model picked in magpie looked like it had done nothing at all until the agent
was restarted by hand.

claudeRunning and Pencil's own check already answer yes where a machine can't
be asked (#353, and Pencil's detect), which is why only the other 23 call
sites were silenced; Running now says the same for the same reason, so the one
path every agent's Notice goes through stops disagreeing with the two that
worked around it. No pattern is no question, and still answers no: nothing
asks it, but a caller that passed none isn't told a process is there.

Asked the machine for real would mean a second pattern set: the pgrep-style
patterns don't describe Windows at all — `(^|/)codex( |$)` does not match
C:\…\OpenAI\Codex\bin\…\codex.exe, and `(^|/)dsh( |$)` does not match
"C:\…\DeepSeek Harness.exe" "…\dsh\…". Cost was not the reason either way:
Notice runs only after a pick or a connect/disconnect (gui/api.go:688, :857;
main.go:410, :440), never from GET /api/state and never per render.

Windows only: the pgrep branch is untouched, so the answer elsewhere is what
it was. Nothing else changes — every other caller (connectinfo.go's Stale,
which sizes how many copies are out of date) uses the lowercase running var
and its own GOOS check, not this.

TestDshRunningOnWindows and TestDshAndCodexAdviceOnWindows fail without the
change (the first: "a pattern that may match was answered no"; the second:
"dsh says nothing about an open session") and pass with it, run under the
Windows job's own selector: that job compiles the whole suite but executes
only `go test ./internal/agent -run '^Test(Dsh|KeepDsh)'`, so both are named
TestDsh… to be among the tests a real Windows actually runs, rather than
skipping on Linux and macOS and never being selected on Windows. go vet
./internal/agent passes and go build passes for linux, windows and darwin
(darwin with -tags nogui). go test -tags nogui ./... has the same failures it
has on 1534382 untouched: the seven TestAside* and
TestCodexStaleOnlyForMagpiesChange and TestSuffixModesReachAgentFiles in
internal/agent, the skills tests in internal/library ("A required privilege
is not held by the client" — symlinks need Developer Mode),
TestPurposeAssetMatchesUsage (the generated catalog is stale) and
TestGatewayFixQuitsOnlyAMagpie in internal/gui,
TestSweepBridgeProjectsTakesOnlyTheBridgesFolders in internal/gateway,
TestBunThroughBridge in internal/netproxy, and the internal/plugin and
internal/provider timeouts — each reproduced on 1534382.

Co-authored-by: wizzy-yang <wizzy-yang@users.noreply.github.com>
This commit is contained in:
wizzy-yangandwizzy-yang authored and GitHub committed 2026-10-05 19:59:34 +08:00
1 parent d7a8ddc84d
commit bf9bd9c935
2 files changed
+80 -2

No files matched your search

+8 -2
View File
@@ -194,10 +194,16 @@ type Agent struct {
}
// Running reports whether a process whose command line matches any pattern
// (an extended regexp, as for pgrep -f) is alive. Unknown on Windows.
// (an extended regexp, as for pgrep -f) is alive. Windows can't be asked
// what runs, so anything may be: every caller is the advice an agent's own
// lists need after magpie changed what it reads at start ("restart Codex",
// "open a new dsh session"), and a Windows that answered no here dropped
// that advice silently — a model picked in magpie looked like it had done
// nothing at all. claudeRunning and Pencil's own check already say they
// can't be told, and say yes for the same reason.
func Running(patterns ...string) bool {
if runtime.GOOS == "windows" {
return false
return len(patterns) > 0
}
for _, pat := range patterns {
if err := proc.Command("pgrep", "-f", pat).Run(); err == nil {
+72
View File
@@ -0,0 +1,72 @@
package agent
import (
"os"
"path/filepath"
"runtime"
"strings"
"testing"
)
// These are named TestDsh… on purpose: the Windows job in
// .github/workflows/test.yml compiles the whole suite but runs only
// `-run '^Test(Dsh|KeepDsh)'` on a real Windows, so a Windows-only check
// under any other name would be compiled on every platform and executed on
// none — it skips on Linux and macOS (GOOS), and Windows never selects it.
// Running reports whether a process whose command line matches a pattern is
// alive, by asking pgrep -f. Windows has no pgrep, so it answered no for
// every pattern — and every one of its 25 call sites is the advice an
// agent's own lists need after magpie changed what that agent reads at
// start ("Codex builds its model list at start-up — restart the Codex app",
// "New dsh sessions start on this; one already open keeps its model until
// you pick another in it"), each inside a Notice closure. So on Windows the
// Agents page wrote the file, said nothing, and a model picked in magpie
// looked like it had done nothing at all.
func TestDshRunningOnWindows(t *testing.T) {
if runtime.GOOS != "windows" {
t.Skip("the Windows answer is only given there")
}
if !Running(`(^|/)codex( |$)`) {
t.Fatal("a pattern that may match was answered no: Windows can't tell")
}
if !Running(`(^|/)dsh( |$)`, `(^|/)codex( |$)`) {
t.Fatal("the second of two patterns was answered no")
}
// no pattern is no question: a caller that asked nothing isn't told a
// process is there
if Running() {
t.Fatal("no pattern was answered yes")
}
}
// The two rows whose "an open one keeps its model" advice Windows dropped:
// asked there, a pick says what an open session keeps, so it is not left
// looking like it did nothing. claudeRunning and Pencil's own check answer
// yes where a machine can't be asked, which is why only the other 23 call
// sites were silenced.
func TestDshAndCodexAdviceOnWindows(t *testing.T) {
if runtime.GOOS != "windows" {
t.Skip("the Windows answer is only given there")
}
home, _, _ := dshRouteHome(t)
a := dsh(home)
if err := a.Field("model").Set("magpie/deepseek/pro"); err != nil {
t.Fatal(err)
}
if n := a.Notice(); !strings.Contains(n, "open") {
t.Fatalf("dsh says nothing about an open session: %q", n)
}
dir := filepath.Join(home, ".codex")
if err := os.MkdirAll(dir, 0o755); err != nil {
t.Fatal(err)
}
cx := codex(home)
if err := cx.Field("model").Set("magpie/deepseek/pro"); err != nil {
t.Fatal(err)
}
if n := cx.Notice(); !strings.Contains(n, "restart") {
t.Fatalf("Codex says nothing about restarting: %q", n)
}
}