fix(windows): suppress console window flashes in gateway - #46754
fix(windows): suppress console window flashes in gateway#46754chrisribe wants to merge 2 commits into
Conversation
When the Hermes gateway runs under pythonw.exe (no console), every subprocess.Popen call for a console-subsystem .exe (copilot.exe, git, rg, ffprobe, etc.) briefly flashes a black console window. Root cause: CREATE_NO_WINDOW (0x08000000) was not being passed to subprocess.Popen across the codebase. Fix: monkey-patch subprocess.Popen.__init__ in hermes_bootstrap.py (the very first import in every Hermes entry point) to unconditionally inject CREATE_NO_WINDOW on win32. This covers all callers — our code, third-party libs, asyncio subprocesses — without hunting individual call sites. Also add targeted fixes to the three highest-frequency callers (copilot_acp_client, shell_hooks, photon adapter) which each spawn subprocesses on every message turn. Fixes console flash on message send and during response generation when running the Discord gateway on Windows. Co-authored-by: DaCRibe
Additional finding: grandchild process flash limitationDuring real-world testing with Works: Python Impact: Cron scripts using Workaround (documented in PR description): Use The targeted fixes in Note: the cron scheduler ( |
On Windows, cron no_agent=True Python scripts run as fresh subprocesses that don't load hermes_bootstrap. Any subprocess.Popen call inside the script (e.g. spawning pwsh) therefore lacks CREATE_NO_WINDOW and flashes a console window. Wrap the Python invocation on win32 so hermes_bootstrap is imported before the script runs, ensuring the Popen monkey-patch is active. Non-Windows is unchanged.
|
Closing as superseded by the consolidated Windows console-flash work tracked in #54220. The relevant pieces from this PR/cluster have now landed through the targeted follow-up PRs #54236, #53892, and #54417, or are recorded in the umbrella tracker for any remaining native-Windows verification. Keeping this separate PR open would duplicate the tracker and the merged follow-up work. Thanks for digging into this — the reports and PRs in this cluster helped identify the remaining spawn legs. |
Problem
When the Hermes gateway runs under
pythonw.exe(windowless Python, as used by the Windows service/task scheduler), everysubprocess.Popencall for a console-subsystem executable (copilot.exe, git.exe, rg.exe, ffprobe.exe, etc.) briefly flashes a black console window. This happens on every message send and during response generation, making the gateway unusable in a Windows desktop environment.The same issue affects CLI sessions — when
hermesis launched from certain environments (e.g. VS Code integrated terminal, Windows Terminal with specific configurations), the parent process may have no attached console (GetConsoleWindow() == 0), triggering the same flash behavior for every tool call.Root Cause
CREATE_NO_WINDOW(0x08000000) was not being passed tosubprocess.Popenacross the codebase. When a process has no console (likepythonw.exe), Windows briefly allocates a new console window for each console-subsystem child process before it exits.Important limitation: The monkey-patch only covers direct Python
subprocess.Popencalls. When a shell (bash/git-bash) is spawned as a child process, its own native child spawning (e.g. bash to pwsh, bash to git) bypasses the Python patch entirely. Grandchild processes flash because bash does not inherit or propagateCREATE_NO_WINDOWto its children.Cron scripts on Windows
no_agent=Truecron jobs that use.shwrappers to call PowerShell (pwsh) will flash because:CREATE_NO_WINDOWpwshnatively — noCREATE_NO_WINDOWWorkaround: Use
.pywrappers instead of.sh— Pythonsubprocess.runwith the bootstrap patch appliesCREATE_NO_WINDOWto grandchildren.Fix
Primary fix: Monkey-patch
subprocess.Popen.__init__inhermes_bootstrap.py— the very first import in every Hermes entry point — to unconditionally injectCREATE_NO_WINDOWonwin32. This covers all callers in one place: our code, third-party libs, andasynciosubprocesses.Targeted fixes: Also add
CREATE_NO_WINDOWto the three highest-frequency callers:agent/copilot_acp_client.py— spawns copilot CLI on every message turnagent/shell_hooks.py— runs shell hook subprocessesplugins/platforms/photon/adapter.py— spawns the Node.js Discord sidecarTesting
Verified on Windows 10 with:
pythonw.execonnected to Discord — no console flashesno_agent=Truejobs using.pywrappers — silent execution, no flashes.shtopwshchain — confirmed grandchild flash (documented as limitation above)