Skip to content

fix(windows): suppress console window flashes in gateway - #46754

Closed
chrisribe wants to merge 2 commits into
NousResearch:mainfrom
chrisribe:fix/windows-gateway-no-console-flash
Closed

fix(windows): suppress console window flashes in gateway#46754
chrisribe wants to merge 2 commits into
NousResearch:mainfrom
chrisribe:fix/windows-gateway-no-console-flash

Conversation

@chrisribe

@chrisribe chrisribe commented Jun 15, 2026

Copy link
Copy Markdown

Problem

When the Hermes gateway runs under pythonw.exe (windowless Python, as used by the Windows service/task scheduler), every subprocess.Popen call 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 hermes is 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 to subprocess.Popen across the codebase. When a process has no console (like pythonw.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.Popen calls. 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 propagate CREATE_NO_WINDOW to its children.

Cron scripts on Windows

no_agent=True cron jobs that use .sh wrappers to call PowerShell (pwsh) will flash because:

  1. Hermes spawns bash with CREATE_NO_WINDOW
  2. Bash spawns pwsh natively — no CREATE_NO_WINDOW

Workaround: Use .py wrappers instead of .sh — Python subprocess.run with the bootstrap patch applies CREATE_NO_WINDOW to grandchildren.

Fix

Primary 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 in one place: our code, third-party libs, and asyncio subprocesses.

Targeted fixes: Also add CREATE_NO_WINDOW to the three highest-frequency callers:

  • agent/copilot_acp_client.py — spawns copilot CLI on every message turn
  • agent/shell_hooks.py — runs shell hook subprocesses
  • plugins/platforms/photon/adapter.py — spawns the Node.js Discord sidecar

Testing

Verified on Windows 10 with:

  • Gateway running as Scheduled Task under pythonw.exe connected to Discord — no console flashes
  • CLI session with no attached console — no flashes from terminal(), search_files(), patch() tool calls
  • Cron no_agent=True jobs using .py wrappers — silent execution, no flashes
  • Cron jobs using .sh to pwsh chain — confirmed grandchild flash (documented as limitation above)

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
@alt-glitch alt-glitch added type/bug Something isn't working comp/gateway Gateway runner, session dispatch, delivery comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P2 Medium — degraded but workaround exists labels Jun 15, 2026
@chrisribe

Copy link
Copy Markdown
Author

Additional finding: grandchild process flash limitation

During real-world testing with no_agent=True cron jobs on Windows, discovered that the bootstrap monkey-patch has a scope boundary:

Works: Python subprocess.Popen -> child process (bash, pwsh, git, rg, etc.)
Does NOT work: Python -> bash -> pwsh/git (grandchild). Bash spawns its children natively, bypassing the Python-level Popen patch entirely.

Impact: Cron scripts using .sh wrappers that call pwsh -File script.ps1 will flash a console window for the pwsh grandchild process.

Workaround (documented in PR description): Use .py wrappers instead of .sh. Python subprocess.run goes through the patched Popen, so pwsh gets CREATE_NO_WINDOW.

The targeted fixes in copilot_acp_client.py, shell_hooks.py, and photon/adapter.py remain valuable because they set CREATE_NO_WINDOW explicitly at the call site -- they do not rely on the monkey-patch and protect against the (unlikely) case where bootstrap import is skipped.

Note: the cron scheduler (cron/scheduler.py) already has windows_hide_flags() on its subprocess.run call -- the direct script invocation is covered. The issue is specifically bash->pwsh grandchild chains.

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.
@teknium1

Copy link
Copy Markdown
Contributor

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants