fix(windows): hide console flashes in GUI-reachable exec paths and provider transports (#56747)

Six spawn sites reachable from the desktop GUI / TUI gateway lacked CREATE_NO_WINDOW, so a windowless parent (pythonw/Electron) flashed a conhost per spawn: cli.exec RPC, quick-commands exec dispatch, and shell.exec RPC in tui_gateway/server.py; the CLI REPL quick-commands exec in cli.py; and the per-session provider transports in agent/copilot_acp_client.py and agent/transports/codex_app_server.py (Popen, hide-only so PIPE stdio stays intact).

All use hermes_cli._subprocess_compat.windows_hide_flags() (no-op on POSIX), matching the pattern already used at three other sites in tui_gateway/server.py. Deliberately hide-only — no detach flags, no Electron changes (per the #54220 revert history).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Basil Al Shukaili 2026-07-02 11:21:14 +04:00 committed by Teknium
parent 1c58fe4362
commit 714d7cc1a4
4 changed files with 25 additions and 1 deletions

View file

@ -503,6 +503,10 @@ class CopilotACPClient:
def _run_prompt(self, prompt_text: str, *, timeout_seconds: float) -> tuple[str, str]:
try:
# Hide the console the CLI child would otherwise flash on Windows
# (#56747). Hide-only — stdio pipes stay intact for the ACP wire.
from hermes_cli._subprocess_compat import windows_hide_flags
proc = subprocess.Popen(
[self._acp_command] + self._acp_args,
stdin=subprocess.PIPE,
@ -512,6 +516,7 @@ class CopilotACPClient:
bufsize=1,
cwd=self._acp_cwd,
env=_build_subprocess_env(),
creationflags=windows_hide_flags(),
)
except FileNotFoundError as exc:
raise RuntimeError(

View file

@ -127,6 +127,10 @@ class CodexAppServerClient:
# Codex emits tracing to stderr; default WARN keeps it quiet for users.
spawn_env.setdefault("RUST_LOG", "warn")
# Hide the console the codex child would otherwise flash on Windows
# (#56747). Hide-only — stdio pipes stay intact for the app-server wire.
from hermes_cli._subprocess_compat import windows_hide_flags
self._proc = subprocess.Popen(
cmd,
stdin=subprocess.PIPE,
@ -134,6 +138,7 @@ class CodexAppServerClient:
stderr=subprocess.PIPE,
bufsize=0,
env=spawn_env,
creationflags=windows_hide_flags(),
)
self._next_id = 1
self._pending: dict[int, _Pending] = {}

5
cli.py
View file

@ -9481,9 +9481,12 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin, CLIBillingMixin):
# has all API keys in os.environ.
from tools.environments.local import _sanitize_subprocess_env
sanitized_env = _sanitize_subprocess_env(os.environ.copy())
from hermes_cli._subprocess_compat import windows_hide_flags
result = subprocess.run(
exec_cmd, shell=True, capture_output=True,
text=True, timeout=30, env=sanitized_env
text=True, timeout=30, env=sanitized_env,
# No console flash on Windows (#56747).
creationflags=windows_hide_flags(),
)
output = result.stdout.strip() or result.stderr.strip()
if output:

View file

@ -13841,6 +13841,10 @@ def _(rid, params: dict) -> dict:
if hint:
return _ok(rid, {"blocked": True, "hint": hint, "code": -1, "output": ""})
try:
# CREATE_NO_WINDOW on Windows — under the desktop GUI's windowless
# parent, this spawn otherwise flashes a console (#56747).
from hermes_cli._subprocess_compat import windows_hide_flags
r = subprocess.run(
[sys.executable, "-m", "hermes_cli.main", *argv],
capture_output=True,
@ -13851,6 +13855,7 @@ def _(rid, params: dict) -> dict:
# needs provider credentials. Tier-1 secrets still stripped (#29157).
env=hermes_subprocess_env(inherit_credentials=True),
stdin=subprocess.DEVNULL,
creationflags=windows_hide_flags(),
)
parts = [r.stdout or "", r.stderr or ""]
out = "\n".join(p for p in parts if p).strip() or "(no output)"
@ -13910,6 +13915,8 @@ def _(rid, params: dict) -> dict:
# has all API keys in os.environ.
from tools.environments.local import _sanitize_subprocess_env
sanitized_env = _sanitize_subprocess_env(os.environ.copy())
from hermes_cli._subprocess_compat import windows_hide_flags
r = subprocess.run(
qc.get("command", ""),
shell=True,
@ -13918,6 +13925,7 @@ def _(rid, params: dict) -> dict:
timeout=30,
stdin=subprocess.DEVNULL,
env=sanitized_env,
creationflags=windows_hide_flags(),
)
output = (
(r.stdout or "")
@ -16984,9 +16992,12 @@ def _(rid, params: dict) -> dict:
except ImportError:
return _err(rid, 5001, "shell.exec unavailable: approval safety module not importable")
try:
from hermes_cli._subprocess_compat import windows_hide_flags
r = subprocess.run(
cmd, shell=True, capture_output=True, text=True, timeout=30, cwd=os.getcwd(),
stdin=subprocess.DEVNULL,
creationflags=windows_hide_flags(),
)
return _ok(
rid,