fix(gateway): eliminate console window on uv-venv Windows gateway launches - #49615
fix(gateway): eliminate console window on uv-venv Windows gateway launches#49615hqy2435662352 wants to merge 1 commit into
Conversation
|
Duplicate of #41028 — same root cause (uv venv |
|
Understood! I'll keep this PR open. Once #41028 is merged into main, I will immediately rebase this branch onto main, strip out the duplicate UV detection bits, and pivot this PR into a dedicated follow-up that brings the |
Background
----------
On Windows hosts with uv-managed venvs, `hermes gateway install`
(Scheduled Task execution) and manual double-click on the generated
`.cmd` wrapper both open a visible console window containing the running
gateway process. This contradicts the documented design intent of
`_build_gateway_cmd_script`, which already uses `pythonw.exe`.
Root cause: uv's venv launcher. Standard venvs ship
`venv\Scripts\pythonw.exe` as a true GUI-subsystem executable. uv
managed venvs ship it as a launcher that reads `pyvenv.cfg` and
respawns the base `python.exe` — a console-subsystem executable that
opens a visible Windows Terminal tab.
`_resolve_detached_python` already handles this correctly for direct
`Popen` spawns. `_build_gateway_cmd_script` (the Scheduled Task
`.cmd` wrapper generator) did not — it unconditionally used the venv
launcher path.
Change
------
`hermes_cli/gateway_windows.py::_build_gateway_cmd_script`:
1. Detect uv venvs and switch to the base `pythonw.exe`. Read
`pyvenv.cfg`; when `uv` is present, resolve the base interpreter
from the `home` key. Mirrors `_resolve_detached_python`.
2. Set `PYTHONPATH=project_root;site-packages`. Switching to the
base interpreter (outside the venv) breaks `-m hermes_cli.main`
because the `.cmd` wrapper `cd`s into the profile home, not the
repo checkout. PYTHONPATH restores module resolution.
3. Switch the wrapper invocation to `start "" /B` and redirect
stdout/stderr to `gateway-stdio.log`. `start /B` lets the wrapper
exit immediately instead of lingering as long as the gateway
runs. Without an inherited hidden console, stdout writes from
the detached process would trigger Windows to allocate a visible
one — so the redirect matches `_spawn_detached`'s approach.
Generated `.cmd` wrapper (after fix):
@echo off
cd /d C:\...\profiles\thinktank
set "HERMES_HOME=..."
set "PYTHONIOENCODING=utf-8"
set "HERMES_GATEWAY_DETACHED=1"
set "VIRTUAL_ENV=..."
set "PYTHONPATH=project_root;site-packages;%PYTHONPATH%"
if not exist "...\logs" mkdir "...\logs"
start "" /B ...\pythonw.exe -m hermes_cli.main gateway run >> "...\gateway-stdio.log" 2>&1
exit /b 0
Tests
-----
`tests/hermes_cli/test_gateway_windows.py::test_gateway_cmd_script_uses_start_b_without_replace_churn`
updated to assert the new `start "" /B` line, the `>> log 2>&1`
redirect, and the `mkdir` log dir guard. Existing assertions for
`pythonw.exe`, `gateway run`, no `--replace`, and `exit /b 0` retained.
Risk
----
- Non-uv venvs: uv detection gated on `"uv" in cfg and home`;
fallback to original `_derive_venv_pythonw` path. Zero change.
- Linux / macOS: only runs on `sys.platform == "win32"`. Zero
regression.
- No new env vars, config changes, or dependencies.
Out of scope
------------
- `tools/environments/local.py::_run_bash` and
`agent/shell_hooks.py::_spawn` are separate call paths (terminal
tool child processes, shell hook subprocesses) and are not
affected by this fix. Console suppression for those paths, if
needed, should land in dedicated follow-ups targeting the
specific user-visible symptom.
279c715 to
ba5b722
Compare
|
Closing this — and recommending #41028 be closed too. The reasoning is cleaner than what I wrote on 2026-06-24, so I am rewriting the comment in place rather than appending. Two changes from
Net effect on #49615: both halves of this PR were either absorbed by #45610 (uv handling) or run counter to Recommendation for #41028: it has the same shape — uv launcher + Salvage credit: if anything from #49615 ends up being reused, the original author is this PR's author — Not opening a follow-up myself. If a future use case actually triggers a visible console from double-clicking the wrapper, that would be worth a fresh PR with the real reproduction, not a continuation of this one's reasoning. |
Problem
On Windows hosts with uv-managed venvs,
hermes gateway install(followed by Scheduled Task execution) and manual double-click on the generated.cmdwrapper both open a visible console window with the gateway process running inside it. This contradicts the documented design intent of_build_gateway_cmd_script, which explicitly usespythonw.exeso that no gateway console window appears.The root cause is uv's venv launcher. Standard venvs ship
venv\Scripts\pythonw.exeas a true GUI-subsystem executable (no console). uv-managed venvs ship it as a launcher that readspyvenv.cfgand respawns the basepython.exe— a console-subsystem executable that opens a visible Windows Terminal tab._resolve_detached_pythonalready handles this correctly for directPopenspawns._build_gateway_cmd_script(which generates the Scheduled Task.cmdwrapper) did not — it unconditionally used the venv launcher path.Fix
Three changes in
_build_gateway_cmd_script:Detect uv venvs and use base
pythonw.exe: readpyvenv.cfg; whenuvis present, resolve the basepythonw.exefrom thehomekey. Same logic as_resolve_detached_python.Set
PYTHONPATH: switching to the base interpreter (outside the venv) breaks-m hermes_cli.mainbecause the.cmdwrappercds into the profile home, not the repo checkout.PYTHONPATH=project_root;site-packagesrestores module resolution.start "" /B+ stdout redirect:start /Blets the.cmdwrapper exit immediately instead of lingering as long as the gateway runs. Without an inherited hidden console, stdout writes from the detached gateway process would trigger Windows to allocate a visible one — so stdout/stderr are redirected togateway-stdio.log(matching_spawn_detached's approach).Generated
.cmdwrapper (after fix)Risk Assessment
"uv" in cfg and home; fallback to original_derive_venv_pythonwpath. Zero change.sys.platform == "win32". Zero regression.Verification
Windows 10, uv-managed Python 3.11, three profiles (thinktank, default, invest):
.cmd: no windowhermes gateway statusreports correct PIDs