Skip to content

fix(desktop/windows): free all venv holders on Update hand-off - #65935

Closed
maxwell7891 wants to merge 3 commits into
NousResearch:mainfrom
maxwell7891:fix/windows-desktop-update-venv-holders-v2
Closed

fix(desktop/windows): free all venv holders on Update hand-off#65935
maxwell7891 wants to merge 3 commits into
NousResearch:mainfrom
maxwell7891:fix/windows-desktop-update-venv-holders-v2

Conversation

@maxwell7891

Copy link
Copy Markdown

Summary

  • Desktop Update hand-off now discovers and force-kills all processes holding the install venv open (gateways, slash workers, trampoline pythons), not only backends owned by the current Electron window. Wait extended to 45s.
  • Tauri hermes-setup --update straggler cleanup also reaps venv python(w).exe holders, not only hermes.exe.
  • hermes update --gateway force-stops remaining venv holders after gateway pause instead of immediately exiting 2.
  • Venv health probe treats a missing base interpreter (pyvenv.cfg home) as unhealthy so repair recreates the runtime instead of leaving a bricked desktop pointing at a dead system Python path.

Root cause

On Windows, Telegram/multi-profile gateways run as venv\Scripts\python.exe and keep hermes.exe / .pyd files mandatory-locked. Clicking Update stopped only the desktop-owned backends, so the shim stayed locked and the UI aborted: "another process is holding the Hermes install open". Partial updates could leave a broken runtime tied to C:\Users\...\Python311\python.exe.

Test plan

  • vitest electron/windows-venv-holders.test.ts (6 tests)
  • Manual unit probe: missing pyvenv.cfg home → unhealthy + recreate path
  • Click desktop Update with at least one gateway running; window should hand off without the 15s abort
  • Updater completes; desktop relaunches; backend ready; profiles (incl. Sophie) work
  • No manual hermes-maj.ps1 / repair path required

@tonydwb tonydwb left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Summary

Verdict: Comment

Looks Good

  • Clean, well-scoped fix: Windows Update hand-off now kills all venv holders (pythonw.exe processes) in addition to hermes.exe, preventing the "install locked" abort after 15s
  • The new windows-venv-holders.ts module with selectVenvHolders/forceKillVenvHolders/listVenvHolders is cleanly separated
  • Deadline extended from 15s to 45s to accommodate gateways on loaded Windows
  • New windows-venv-holders.test.ts with good coverage of isVenvHolderProcess, normalizeWinPath, selectVenvHolders
  • Rust side (update.rs) adds force_kill_other_hermes_for(Some(install_root)) scoped kill to avoid collateral damage

Notes

  • console.print in the diff is in test/discovery code, not production — acceptable
  • No security concerns; PowerShell taskkill is appropriately scoped to the install root

Reviewed by Hermes Agent

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/desktop Electron desktop app (apps/desktop/*) comp/cli CLI entry point, hermes_cli/, setup wizard platform/windows Native Windows-specific behavior or breakage sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades labels Jul 16, 2026
@maxwell7891

Copy link
Copy Markdown
Author

Runtime / functional follow-up (local Windows install)

What this PR is for (one-liner)

Make the Desktop Update button succeed on Windows even when gateways / multi-profile backends / trampoline Python311\python.exe processes still hold the venv open — without hermes-maj.ps1 or manual repair.

Functional checks run on the PR branch (4d9994065)

Check Result
vitest electron/windows-venv-holders.test.ts 6/6 pass
Electron main bundle contains hand-off sweep (force-killing N external venv holder(s), 45s timeout) yes
Packaged app.asar re-injected with that main yes (hasFix / has45)
hermes_cli base-home probe (pyvenv.cfg → missing base → unhealthy) present
Live lock probe at test time shim unlocked, 0 external holders (machine quiet / no gateways holding venv right then)
Code review (tonydwb) Comment / Looks Good — no blocking notes

Not yet closed on this run

  • Full GUI path: click Update with at least one live gateway + dashboard, confirm hand-off log line + auto relaunch + backend ready + profiles back.
  • That needs a Desktop restart onto the patched asar, then a real button click (will briefly pause Telegram gateways by design).

Note

A later hermes update on this machine had returned main to upstream and overwrote an earlier local asar patch; re-checked out this PR branch and re-validated units + package contents before commenting.

Happy to push a follow-up with E2E log excerpts once the loaded-gateway click path is exercised.

@maxwell7891

Copy link
Copy Markdown
Author

Follow-up: explicit interrupt warning + live-agent preflight

Pushed b2219fdf9 on this branch.

Behaviour

  1. Idle update card always shows a short note that mid-run agents will be interrupted while the install is freed.
  2. Before Windows hand-off, Desktop scans:
    • active_sessions.json under HERMES_HOME + each profile
    • venv process holders (gateways, slash workers, dashboard backends)
  3. If anything is live → stage busyConfirm with details:
    • what will be interrupted (mid-flight tools / Telegram)
    • what is kept (disk state, wiki, config, session history)
    • gateways pause then restart after update
    • actions: Update anyway (force: true) / Not now
  4. Applying copy also mentions agents/gateways pause for a few minutes.

Tests

  • update-busy-preflight.test.ts + windows-venv-holders.test.ts10/10 pass

This answers the operational question: the force-kill is required for a clean Windows update, but the user is told before it happens when an agent is still running.

@maxwell7891
maxwell7891 force-pushed the fix/windows-desktop-update-venv-holders-v2 branch 4 times, most recently from 6e3960c to 33daf57 Compare July 17, 2026 01:43

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for targeting a real Windows updater failure: current main only kills Desktop-owned backend PIDs in apps/desktop/electron/main.ts:2341-2401, then aborts after the 15-second lock wait.

Problems

  • hermes_cli/main.py:9198 reports a missing pyvenv.cfg base interpreter as unhealthy, but current repair logic recreates only when venv/Scripts/python.exe is absent (hermes_cli/main.py:10074-10086). This new case will not recreate the broken venv as claimed.
  • apps/bootstrap-installer/src-tauri/src/update.rs:514 now calls the scoped cleanup, while :610-641 limits it to the venv. A stale packaged Desktop executable can still hold the separately probed app.asar (:541-560).
  • apps/desktop/electron/main.ts:2382 force-kills external gateways/workers without the busyConfirm preflight described in the July 17 follow-up; the final PR diff contains no corresponding UI/preflight code or test.

Suggested changes

  • Make the base-interpreter failure select the venv-recreate path and cover it with a Python regression test.
  • Scope process cleanup to the install root while including both venv interpreters and packaged Desktop executables; test both same-install and other-install processes.
  • Restore the explicit busy-session confirmation before terminating external holders.

Automated hermes-sweeper review.

Comment thread hermes_cli/main.py
home_path = Path(home_val)
base_py = home_path / "python.exe"
if not home_path.is_dir() or not base_py.exists():
return (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This returns an unhealthy result, but the existing repair path recreates only when venv/Scripts/python.exe is absent. Here that shim still exists, so dependency installation is attempted against the same pyvenv.cfg whose base interpreter is gone. Carry this reason to the repair branch (or remove/recreate the venv here) and add a regression test for the selected recreation command.

@@ -511,7 +511,7 @@ pub(crate) async fn wait_for_install_locks_free(install_root: &Path, app: &AppHa
format_locked_paths(&locked)
),
);
force_kill_other_hermes();
force_kill_other_hermes_for(Some(install_root));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Passing Some(install_root) disables the legacy taskkill /IM hermes.exe, but the scoped selector below only matches <install>/venv. The lock probe also includes apps/desktop/release/.../resources/app.asar; a stale packaged Desktop Hermes.exe can retain that lock and is no longer killed. Scope the selector to the install root while covering the packaged executable too.

`[${tag}] force-killing ${external.length} external venv holder(s): ` +
external.map(h => `${h.pid}/${h.name}`).join(', ')
)
forceKillVenvHolders(external, { forceKillProcessTree })

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This immediately terminates gateways and active workers outside the current Desktop window. The follow-up comment describes a busyConfirm preflight for that destructive hand-off, but the final PR head has no corresponding UI/preflight change. Please restore that confirmation before this call.

@teknium1 teknium1 added sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 18, 2026
The Update button aborted after 15s whenever a gateway (Telegram profiles,
slash workers) held venv\Scripts\hermes.exe / .pyd files open, because
releaseBackendLock only tree-killed backends owned by the current desktop
instance.

- Discover and taskkill external venv holders before hand-off (45s wait)
- Tauri updater force-kill also reaps venv python(w), not only hermes.exe
- hermes update --gateway force-stops remaining holders after gateway pause
- Recreate/repair venv when pyvenv.cfg base home python is missing
@maxwell7891
maxwell7891 force-pushed the fix/windows-desktop-update-venv-holders-v2 branch from 33daf57 to 4dbe370 Compare July 19, 2026 15:33
@maxwell7891

Copy link
Copy Markdown
Author

Review follow-up pushed

Rebased onto current main and addressed the July 18 sweeper findings in 4dbe37005:

  • Broken pyvenv.cfg base interpreter now selects an actual uv venv --clear venv recreation path, with present/missing-base regression tests.
  • Scoped bootstrap cleanup now includes both same-install venv interpreters and packaged Hermes.exe holders (including app.asar owners), while excluding other install roots; path classification test added.
  • Added Windows busy preflight before hand-off: scans live active_sessions.json leases across profiles plus install holder processes, returns a dedicated busyConfirm state, and exposes Update anyway (force: true) / Not now. The idle update card also warns that running agents and gateways are interrupted briefly.

Local verification:

  • Desktop typecheck: pass
  • Targeted ESLint: pass
  • UI update-store tests: 26/26 pass
  • Electron venv-holder tests: 6/6 pass
  • New Python recreation tests: 2/2 pass; remaining applicable Windows health tests: 18/18 pass
  • Desktop production build: pass
  • git diff --check + Python compile: pass

Rust/Cargo was unavailable on the Windows host, so the added Rust unit test is left to CI.

@OutThisLife

Copy link
Copy Markdown
Collaborator

Closing in favor of #74436, which fixes the cause this works around.

Your diff treats the symptom correctly — a venv holder blocks the updater — but the holder in these reports is the dashboard's own detached hermes update, spawned unregistered while the desktop updater was already running. The in-progress marker existed but nothing enforced it as a lock, so two updaters could mutate one checkout at once. #74436 makes that marker a real cross-process lock claimed by every update entrypoint, so a second updater is refused instead of racing.

The venv-holder detection you were working around is untouched and still correct; it should just rarely fire now.

Thanks for digging into this — the logs and repro in here were genuinely useful in tracing the orchestration bug.

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

Labels

area/install-update Installer, updater, packaging, wheels, doctor comp/cli CLI entry point, hermes_cli/, setup wizard comp/desktop Electron desktop app (apps/desktop/*) P2 Medium — degraded but workaround exists platform/windows Native Windows-specific behavior or breakage sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants