From 8bf09455dc498581fe6dea21402ee2a9238a2212 Mon Sep 17 00:00:00 2001 From: Grogger Date: Sat, 16 May 2026 12:06:09 -0400 Subject: [PATCH] fix(windows): suppress console window flash on subprocess spawns Add creationflags=CREATE_NO_WINDOW to every Windows Popen call across the terminal, process registry, code execution, and kanban worker subsystems. Prevents visible CMD windows from flashing on the user's desktop during agent operation. Also adds the _IS_WINDOWS module constant to kanban_db.py where it was missing, for consistency with the other patched files. 5 Popen sites across 4 files: - tools/environments/local.py (terminal foreground spawn) - tools/process_registry.py (background process spawn) - tools/code_execution_tool.py (sandbox + interpreter probe) - hermes_cli/kanban_db.py (kanban worker spawn) --- hermes_cli/kanban_db.py | 2 ++ tools/code_execution_tool.py | 2 ++ tools/environments/local.py | 1 + tools/process_registry.py | 1 + 4 files changed, 6 insertions(+) diff --git a/hermes_cli/kanban_db.py b/hermes_cli/kanban_db.py index 0db694ff5b..9d5ddad6ed 100644 --- a/hermes_cli/kanban_db.py +++ b/hermes_cli/kanban_db.py @@ -93,6 +93,7 @@ from toolsets import get_toolset_names VALID_STATUSES = {"triage", "todo", "ready", "running", "blocked", "done", "archived"} VALID_WORKSPACE_KINDS = {"scratch", "worktree", "dir"} KNOWN_TOOLSET_NAMES = frozenset(name.casefold() for name in get_toolset_names()) +_IS_WINDOWS = sys.platform == "win32" # A running task's claim is valid for 15 minutes; after that the next # dispatcher tick reclaims it. Workers that outlive this window should call @@ -4024,6 +4025,7 @@ def _default_spawn( stderr=subprocess.STDOUT, env=env, start_new_session=True, + creationflags=subprocess.CREATE_NO_WINDOW if _IS_WINDOWS else 0, ) except FileNotFoundError: log_f.close() diff --git a/tools/code_execution_tool.py b/tools/code_execution_tool.py index 3822ce539f..bdbc4bfbe1 100644 --- a/tools/code_execution_tool.py +++ b/tools/code_execution_tool.py @@ -1238,6 +1238,7 @@ def execute_code( stderr=subprocess.PIPE, stdin=subprocess.DEVNULL, preexec_fn=None if _IS_WINDOWS else os.setsid, + creationflags=subprocess.CREATE_NO_WINDOW if _IS_WINDOWS else 0, ) # --- Poll loop: watch for exit, timeout, and interrupt --- @@ -1568,6 +1569,7 @@ def _is_usable_python(python_path: str) -> bool: "import sys; sys.exit(0 if sys.version_info >= (3, 8) else 1)"], timeout=5, capture_output=True, + creationflags=subprocess.CREATE_NO_WINDOW if _IS_WINDOWS else 0, ) return result.returncode == 0 except (OSError, subprocess.TimeoutExpired, subprocess.SubprocessError): diff --git a/tools/environments/local.py b/tools/environments/local.py index 3b9d65449f..177e5efab1 100644 --- a/tools/environments/local.py +++ b/tools/environments/local.py @@ -513,6 +513,7 @@ class LocalEnvironment(BaseEnvironment): stderr=subprocess.STDOUT, stdin=subprocess.PIPE if stdin_data is not None else subprocess.DEVNULL, preexec_fn=None if _IS_WINDOWS else os.setsid, + creationflags=subprocess.CREATE_NO_WINDOW if _IS_WINDOWS else 0, cwd=_popen_cwd, ) if not _IS_WINDOWS: diff --git a/tools/process_registry.py b/tools/process_registry.py index 184939adf7..8429a71e08 100644 --- a/tools/process_registry.py +++ b/tools/process_registry.py @@ -557,6 +557,7 @@ class ProcessRegistry: stderr=subprocess.STDOUT, stdin=subprocess.PIPE, preexec_fn=None if _IS_WINDOWS else os.setsid, + creationflags=subprocess.CREATE_NO_WINDOW if _IS_WINDOWS else 0, ) session.process = proc