Files
Teknium 2a7308b7c4 fix(update): quarantine hermes.exe vs concurrent Windows instance (#26670) (#26677)
* fix(update): detect concurrent hermes.exe on Windows; retry + restart-defer quarantine

Closes #26670.

When 'hermes update' runs on Windows with another hermes.exe alive (most
commonly the Hermes Desktop Electron app's spawned backend) _quarantine_running_hermes_exe()
fails to rename the venv shim with [WinError 32]. uv pip install -e .
then exits 2, the git-pull fast path is silently abandoned, and the ZIP
fallback runs (and fails the same way) before eventually succeeding.

This change implements three of the five proposed fixes from the issue:

1. Concurrent-instance detection (preferred fix). _detect_concurrent_hermes_instances()
   uses psutil to enumerate processes whose .exe is one of our venv shims
   (hermes.exe / hermes-gateway.exe), excluding the caller's PID. When any
   match exists, cmd_update prints an actionable message naming the
   blocking PIDs and exits 2 BEFORE any destructive work. New --force flag
   bypasses the gate.

2. Retry + restart-deferred fallback. _quarantine_running_hermes_exe()
   now retries the rename up to 4 times with 100/250/500/1000 ms backoff
   (covers the transient AV-scanner-handle case). If all retries fail,
   it schedules the replacement via MoveFileExW with the OS deferred-rename
   flag so the new shim can land at the original path and the update
   completes; the old image is fully unloaded after the user's next
   system restart.

3. Actionable warning text. The old 'Could not quarantine: [WinError 32]'
   warning is replaced with one that names the likely culprits (Hermes
   Desktop, REPLs, gateway, AV) and points to the new --force flag.

Tests:
- 13 new tests in tests/hermes_cli/test_update_concurrent_quarantine.py
  covering: psutil-based enumeration, self-pid exclusion, case-insensitive
  matching of .EXE, no-psutil graceful degradation, off-Windows no-op,
  helpful warning formatting, retry-then-succeed, restart-deferred fallback,
  cmd_update abort + exit code 2, and --force bypass.
- New autouse fixture in tests/hermes_cli/conftest.py defaults
  _detect_concurrent_hermes_instances to [] so the rest of the suite
  isn't tripped by the developer's own running hermes.exe. Opt-out marker
  'real_concurrent_gate' registered in pyproject.toml.
- Updating docs page (website/docs/getting-started/updating.md) gains a
  short section explaining the new Windows error and remediation.

* chore: refresh uv.lock to match pyproject.toml exact pins

aiohttp 3.13.4 -> 3.13.3 (matches pyproject pin: aiohttp==3.13.3)
anthropic 0.87.0 -> 0.86.0 (matches pyproject pin: anthropic==0.86.0)
hermes-agent 0.13.0 -> 0.14.0 (matches pyproject version)

CI's uv lock --check was failing on the merged state because main
drifted: pyproject.toml uses exact == pins for those two deps and the
hermes-agent version was bumped to 0.14.0 but the lockfile still had
0.13.0.
2026-05-19 11:10:51 -07:00

8.9 KiB
Raw Permalink Blame History

sidebar_position, title, description
sidebar_position title description
3 Updating & Uninstalling How to update Hermes Agent to the latest version or uninstall it

Updating & Uninstalling

Updating

Git installs

Update to the latest version with a single command:

hermes update

This pulls the latest code from main, updates dependencies, and prompts you to configure any new options that were added since your last update.

pip installs

PyPI releases track tagged versions (major and minor releases), not every commit on main. Check for updates and upgrade with:

hermes update --check    # see if a newer release is on PyPI
hermes update            # runs pip install --upgrade hermes-agent

Or manually:

pip install --upgrade hermes-agent    # or: uv pip install --upgrade hermes-agent

:::tip hermes update automatically detects new configuration options and prompts you to add them. If you skipped that prompt, you can manually run hermes config check to see missing options, then hermes config migrate to interactively add them. :::

What happens during an update (git installs)

When you run hermes update, the following steps occur:

  1. Pairing-data snapshot — a lightweight pre-update state snapshot is saved (covers ~/.hermes/pairing/, Feishu comment rules, and other state files that get modified at runtime). Recoverable via the snapshot restore flow described under Snapshots and rollback, or by extracting the most recent quick-snapshot zip Hermes wrote next to your ~/.hermes/ directory.
  2. Git pull — pulls the latest code from the main branch and updates submodules
  3. Dependency install — runs uv pip install -e ".[all]" to pick up new or changed dependencies
  4. Config migration — detects new config options added since your version and prompts you to set them
  5. Gateway auto-restart — running gateways are refreshed after the update completes so the new code takes effect immediately. Service-managed gateways (systemd on Linux, launchd on macOS) are restarted through the service manager. Manual gateways are relaunched automatically when Hermes can map the running PID back to a profile.

Preview-only: hermes update --check

Want to know if an update is available before pulling? Run hermes update --check — for git installs it fetches and compares commits against origin/main; for pip installs it queries PyPI for the latest release. No files are modified, no gateway is restarted. Useful in scripts and cron jobs that gate on "is there an update".

Full pre-update backup: --backup

For high-value profiles (production gateways, shared team installs) you can opt into a full pre-pull backup of HERMES_HOME (config, auth, sessions, skills, pairing):

hermes update --backup

Or make it the default for every run:

# ~/.hermes/config.yaml
updates:
  pre_update_backup: true

--backup was the always-on behavior in earlier builds, but it was adding minutes to every update on large homes, so it's now opt-in. The lightweight pairing-data snapshot above still runs unconditionally.

Windows: another hermes.exe is running

On Windows, hermes update will refuse to run if it detects another hermes.exe process holding the venv's entry-point executable open — most commonly the Hermes Desktop app's spawned backend, an open hermes REPL in another terminal, or a running gateway:

$ hermes update
✗ Another hermes.exe is running:
    PID 12345  hermes.exe

  Updating now would fail to overwrite ...\venv\Scripts\hermes.exe because
  Windows blocks REPLACE on a running executable.

  Close Hermes Desktop, exit any open `hermes` REPLs, and
  stop the gateway (`hermes gateway stop`) before retrying.
  Override with `hermes update --force` if you've already
  confirmed those processes will not write to the venv.

Close the listed processes and re-run. If you're sure the concurrent process won't interfere (rare — usually only useful when an antivirus shim is mis-attributed), pass --force to skip the check. In that case the updater will still retry the .exe rename with exponential backoff and, on stubborn locks, schedule the replacement for next reboot via MoveFileEx(MOVEFILE_DELAY_UNTIL_REBOOT) so the update can complete.

Expected output looks like:

$ hermes update
Updating Hermes Agent...
📥 Pulling latest code...
Already up to date.  (or: Updating abc1234..def5678)
📦 Updating dependencies...
✅ Dependencies updated
🔍 Checking for new config options...
✅ Config is up to date  (or: Found 2 new options — running migration...)
🔄 Restarting gateways...
✅ Gateway restarted
✅ Hermes Agent updated successfully!

Recommended Post-Update Validation

hermes update handles the main update path, but a quick validation confirms everything landed cleanly:

  1. git status --short — if the tree is unexpectedly dirty, inspect before continuing
  2. hermes doctor — checks config, dependencies, and service health
  3. hermes --version — confirm the version bumped as expected
  4. If you use the gateway: hermes gateway status
  5. If doctor reports npm audit issues: run npm audit fix in the flagged directory

:::warning Dirty working tree after update If git status --short shows unexpected changes after hermes update, stop and inspect them before continuing. This usually means local modifications were reapplied on top of the updated code, or a dependency step refreshed lockfiles. :::

If your terminal disconnects mid-update

hermes update protects itself against accidental terminal loss:

  • The update ignores SIGHUP, so closing your SSH session or terminal window no longer kills it mid-install. pip and git child processes inherit this protection, so the Python environment cannot be left half-installed by a dropped connection.
  • All output is mirrored to ~/.hermes/logs/update.log while the update runs. If your terminal disappears, reconnect and inspect the log to see whether the update finished and whether the gateway restart succeeded:
tail -f ~/.hermes/logs/update.log
  • Ctrl-C (SIGINT) and system shutdown (SIGTERM) are still honored — those are deliberate cancellations, not accidents.

You no longer need to wrap hermes update in screen or tmux to survive a terminal drop.

Checking your current version

hermes version

Compare against the latest release at the GitHub releases page.

Updating from Messaging Platforms

You can also update directly from Telegram, Discord, Slack, WhatsApp, or Teams by sending:

/update

This pulls the latest code, updates dependencies, and restarts running gateways. The bot will briefly go offline during the restart (typically 515 seconds) and then resume.

Manual Update

If you installed manually (not via the quick installer):

cd /path/to/hermes-agent
export VIRTUAL_ENV="$(pwd)/venv"

# Pull latest code
git pull origin main

# Reinstall (picks up new dependencies)
uv pip install -e ".[all]"

# Check for new config options
hermes config check
hermes config migrate   # Interactively add any missing options

Rollback instructions

If an update introduces a problem, you can roll back to a previous version:

cd /path/to/hermes-agent

# List recent versions
git log --oneline -10

# Roll back to a specific commit
git checkout <commit-hash>
git submodule update --init --recursive
uv pip install -e ".[all]"

# Restart the gateway if running
hermes gateway restart

To roll back to a specific release tag:

git checkout v0.6.0
git submodule update --init --recursive
uv pip install -e ".[all]"

:::warning Rolling back may cause config incompatibilities if new options were added. Run hermes config check after rolling back and remove any unrecognized options from config.yaml if you encounter errors. :::

Note for Nix users

If you installed via Nix flake, updates are managed through the Nix package manager:

# Update the flake input
nix flake update hermes-agent

# Or rebuild with the latest
nix profile upgrade hermes-agent

Nix installations are immutable — rollback is handled by Nix's generation system:

nix profile rollback

See Nix Setup for more details.


Uninstalling

Git installs

hermes uninstall

The uninstaller gives you the option to keep your configuration files (~/.hermes/) for a future reinstall.

pip installs

pip uninstall hermes-agent
rm -rf ~/.hermes            # Optional — keep if you plan to reinstall

Manual Uninstall

rm -f ~/.local/bin/hermes
rm -rf /path/to/hermes-agent
rm -rf ~/.hermes            # Optional — keep if you plan to reinstall

:::info If you installed the gateway as a system service, stop and disable it first:

hermes gateway stop
# Linux: systemctl --user disable hermes-gateway
# macOS: launchctl remove ai.hermes.gateway

:::