mirror of
https://github.com/multica-ai/multica.git
synced 2026-06-17 11:48:42 +02:00
* fix(daemon): use CREATE_NEW_CONSOLE to stop grandchild console popups on Windows (#1521) CREATE_NO_WINDOW strips the console entirely. When the agent CLI then spawns a console-subsystem grandchild (bash, cmd, netstat, findstr, timeout) without itself passing CREATE_NO_WINDOW, Windows allocates a brand-new visible console window per invocation — trading one popup per agent run for N popups per tool call. Switch to CREATE_NEW_CONSOLE + HideWindow=true so the agent gets a hidden console that grandchildren inherit. Stdio pipes still work via STARTF_USESTDHANDLES; no changes needed at the 17 hideAgentWindow call sites. Add a Windows-only regression test asserting CREATE_NEW_CONSOLE is set and CREATE_NO_WINDOW is not, per the #1474 Windows-test follow-up. Root-cause diagnosis by @matrenitski (verified against the shipped multica.exe and the Claude Code CLI it spawns) in issue #1521. * test(agent): use CREATE_NEW_CONSOLE-compatible flag in preservation test CREATE_NEW_PROCESS_GROUP is silently ignored by Windows when combined with CREATE_NEW_CONSOLE, so asserting it 'survives' was only bitwise-true, not semantically meaningful. Switch the example to CREATE_UNICODE_ENVIRONMENT (documented compatible) and also assert a non-flag field (NoInheritHandles) survives to exercise full struct preservation.
66 lines
2.4 KiB
Go
66 lines
2.4 KiB
Go
//go:build windows
|
|
|
|
package agent
|
|
|
|
import (
|
|
"os/exec"
|
|
"syscall"
|
|
"testing"
|
|
)
|
|
|
|
// TestHideAgentWindowSetsCreateNewConsole guards against a regression where
|
|
// hideAgentWindow reverts to CREATE_NO_WINDOW. CREATE_NO_WINDOW strips the
|
|
// console entirely, which forces Windows to allocate a new visible console
|
|
// per grandchild that doesn't itself pass CREATE_NO_WINDOW — the popup
|
|
// storm reported in #1521.
|
|
func TestHideAgentWindowSetsCreateNewConsole(t *testing.T) {
|
|
cmd := exec.Command("cmd.exe", "/c", "echo", "hi")
|
|
hideAgentWindow(cmd)
|
|
|
|
if cmd.SysProcAttr == nil {
|
|
t.Fatal("SysProcAttr should be initialized")
|
|
}
|
|
if !cmd.SysProcAttr.HideWindow {
|
|
t.Error("HideWindow should be true")
|
|
}
|
|
if cmd.SysProcAttr.CreationFlags&createNewConsole == 0 {
|
|
t.Errorf("CreationFlags should include CREATE_NEW_CONSOLE (0x%x), got 0x%x",
|
|
createNewConsole, cmd.SysProcAttr.CreationFlags)
|
|
}
|
|
const createNoWindow = 0x08000000
|
|
if cmd.SysProcAttr.CreationFlags&createNoWindow != 0 {
|
|
t.Errorf("CreationFlags must NOT include CREATE_NO_WINDOW (0x%x), got 0x%x — "+
|
|
"see #1521 for why this causes grandchild popups",
|
|
createNoWindow, cmd.SysProcAttr.CreationFlags)
|
|
}
|
|
}
|
|
|
|
// TestHideAgentWindowPreservesExistingSysProcAttr ensures hideAgentWindow
|
|
// does not overwrite fields set by callers — a regression caught in PR #1474
|
|
// where the whole SysProcAttr struct was replaced. We verify both a
|
|
// non-CreationFlags field and a pre-existing CreationFlags bit survive.
|
|
//
|
|
// CREATE_UNICODE_ENVIRONMENT (0x00000400) is chosen because it is documented
|
|
// as compatible with CREATE_NEW_CONSOLE (unlike CREATE_NEW_PROCESS_GROUP,
|
|
// which Windows silently ignores when combined with CREATE_NEW_CONSOLE), so
|
|
// a surviving bit here is semantically meaningful, not just bitwise intact.
|
|
func TestHideAgentWindowPreservesExistingSysProcAttr(t *testing.T) {
|
|
const createUnicodeEnvironment = 0x00000400
|
|
cmd := exec.Command("cmd.exe", "/c", "echo", "hi")
|
|
cmd.SysProcAttr = &syscall.SysProcAttr{
|
|
CreationFlags: createUnicodeEnvironment,
|
|
NoInheritHandles: true,
|
|
}
|
|
hideAgentWindow(cmd)
|
|
|
|
if !cmd.SysProcAttr.NoInheritHandles {
|
|
t.Error("NoInheritHandles set by caller should be preserved")
|
|
}
|
|
if cmd.SysProcAttr.CreationFlags&createUnicodeEnvironment == 0 {
|
|
t.Error("existing CreationFlags bits (CREATE_UNICODE_ENVIRONMENT) should be preserved")
|
|
}
|
|
if cmd.SysProcAttr.CreationFlags&createNewConsole == 0 {
|
|
t.Error("CREATE_NEW_CONSOLE should be OR'd into existing flags")
|
|
}
|
|
}
|