* fix(desktop): cross-profile session history in app-global remote mode #39894 made remote-profile sessions first-class for PER-PROFILE remote overrides. But the common setup — Settings → Gateway → "All profiles" → Remote — writes app-GLOBAL remote mode (connection.json top-level mode:'remote', empty profiles map), which the intercept didn't recognize. Switching to a non-launch profile then 404'd every session read, so no history showed for it. In global remote mode a SINGLE backend serves every profile via ?profile= (it reads each profile's state.db off the remote host's own disk — verified: one dashboard returns /api/profiles and /api/profiles/sessions?profile=all across all profiles). The fix: when no per-profile override matches but global remote mode is active, route per-session reads/mutations to that one backend and KEEP the ?profile= param so it opens the right state.db (instead of bailing to the local path and dropping the profile scope). - new globalRemoteActive() — true for connection.json mode:'remote' or the HERMES_DESKTOP_REMOTE_URL env override. - per-session branch: per-profile override → route sans profile (own db); global mode → route to the single backend WITH ?profile= preserved. - unified list is unchanged in global mode: it already passes through to the one backend, which aggregates all profiles natively. Verified live against a one-dashboard / multi-profile remote (Austin's topology): cross-profile transcript reads load (was 404), rename/delete route to the right profile, unified list spans both profiles. Known limitation (architectural, not fixed here): LIVE chat as a non-launch profile still needs a per-profile dashboard on the remote — the dashboard binds HERMES_HOME once at process start, so one global backend can't run an agent turn as another profile. Session history/read/mutate now work regardless. * fix(gateway): resume + chat any profile over one global-remote dashboard The REST half of this branch made cross-profile session history visible in app-global remote mode, but resume + chat still went over the WebSocket gateway, which was hard-bound to the dashboard's launch profile. Resuming a non-launch profile's session 404'd ("session not found") and sending spawned a new session — because session.resume/prompt.submit had no profile concept and the live agent + state.db were process-global to the launch profile's HERMES_HOME. Make the WS gateway per-session profile-aware so ONE dashboard can serve every local profile on its host (the app-global remote topology): - session.resume accepts an optional `profile`. _profile_home() resolves that profile's home on this host; resume opens THAT profile's state.db, binds its HERMES_HOME (ContextVar override) while building the agent so config/skills/ model resolve to it, and passes the profile db to the agent so turns persist to the right state.db. The owning profile_home is stored on the session. - prompt.submit re-binds the stored profile_home for the turn thread (mid-turn home reads — memory, skills — resolve to the resumed profile), reset in finally. - _make_agent gains an optional session_db param (defaults to _get_db()). - _load_cfg honors the home override (falls back to _hermes_home) so a resumed profile loads its own config; cache keyed on resolved path. - desktop: session.resume now sends the owning profile. Omitted/launch profile → unchanged (single-profile and per-profile-remote setups are byte-for-byte the same path). Verified live against a one-dashboard / multi-profile remote: resuming a non-launch profile's session loads its history, runs a real turn against THAT profile's home/env, and persists to its state.db. tests/tui_gateway/test_protocol.py: _make_agent mocks updated for the new param. |
||
|---|---|---|
| .. | ||
| assets | ||
| electron | ||
| public | ||
| scripts | ||
| src | ||
| .prettierrc | ||
| components.json | ||
| eslint.config.mjs | ||
| index.html | ||
| package.json | ||
| preview-demo.html | ||
| README.md | ||
| tsconfig.json | ||
| vite.config.ts | ||
Hermes Desktop ☤
The native desktop app for Hermes Agent — the self-improving AI agent from Nous Research. Same agent, same skills, same memory as the CLI and gateway, in a polished native window — chat with streaming tool output, side-by-side previews, a file browser, voice, and settings, no terminal required. Available for macOS, Windows, and Linux.
| Chat with the full agent | Streaming responses, live tool activity, structured tool summaries, and the same conversation history as every other Hermes surface. |
| Side-by-side previews | Render web pages, files, and tool outputs in a right-hand pane while you keep chatting. |
| File browser | Explore and preview the working directory without leaving the app. |
| Voice | Talk to Hermes and hear it back. |
| Settings & onboarding | Manage providers, models, tools, and credentials from a real UI. First-run setup gets you to your first message in seconds. |
| Stays current | Built-in updates pull the latest agent and rebuild the app in place. |
Install
Install with Hermes (recommended)
Already have the Hermes CLI? Just run:
hermes desktop
It builds and launches the GUI against your existing install — same config, keys, sessions, and skills. On first launch Hermes walks you through picking a provider and model; nothing else to configure.
Prebuilt installers
Prebuilt installers are built and distributed via the Hermes Desktop website..
Updating
The app checks for updates in the background and offers a one-click update when one is ready. You can also update any time from the CLI:
hermes update
Requirements
The installer handles everything for you (Python 3.11+, a portable Git, ripgrep).
Development
Want to hack on the app itself? Install workspace deps from the repo root once, then run the dev server from this directory:
npm install # from repo root — links apps/desktop, web, apps/shared
cd apps/desktop
npm run dev # Vite renderer + Electron, which boots the Python backend
Point the app at a specific source checkout, or sandbox it away from your real config:
HERMES_DESKTOP_HERMES_ROOT=/path/to/clone npm run dev
HERMES_HOME=/tmp/throwaway npm run dev
npm run dev:fake-boot # exercise the startup overlay with deterministic delays
Building installers
npm run dist:mac # DMG + zip
npm run dist:win # NSIS + MSI
npm run dist:linux # AppImage + deb + rpm
npm run pack # unpacked app under release/ (no installer)
Installers are built and uploaded to GitHub Releases manually. macOS/Windows signing & notarization happen automatically when the relevant credentials are present in the environment (CSC_LINK / CSC_KEY_PASSWORD / APPLE_* for macOS, WIN_CSC_* for Windows).
How it works
The packaged app ships only the Electron shell. On first launch it installs the Hermes Agent runtime into HERMES_HOME (~/.hermes, or %LOCALAPPDATA%\hermes on Windows) — the same layout a CLI install uses, so the two are interchangeable. The renderer (React, in src/) talks to a hermes dashboard backend over the standard gateway APIs and reuses the embedded TUI rather than reimplementing chat. The install, backend-resolution, and self-update logic all live in electron/main.cjs.
Verification
Run before opening a PR (lint may surface pre-existing warnings but must exit cleanly):
npm run fix
npm run type-check
npm run lint
npm run test:desktop:all
Troubleshooting
Boot logs land in HERMES_HOME/logs/desktop.log (includes backend output and recent Python tracebacks) — check it first if the app reports a boot failure.
macOS / Linux:
# Force a clean first-launch setup
rm "$HOME/.hermes/hermes-agent/.hermes-bootstrap-complete"
# Rebuild a broken Python venv
rm -rf "$HOME/.hermes/hermes-agent/venv"
# Reset a stuck macOS microphone prompt (macOS only)
tccutil reset Microphone com.nousresearch.hermes
Windows (PowerShell):
# Force a clean first-launch setup
Remove-Item "$env:LOCALAPPDATA\hermes\hermes-agent\.hermes-bootstrap-complete"
# Rebuild a broken Python venv
Remove-Item -Recurse -Force "$env:LOCALAPPDATA\hermes\hermes-agent\venv"
The default Hermes home on Windows is
%LOCALAPPDATA%\hermes. Set theHERMES_HOMEenv var if you've relocated it.
Community
- 💬 Discord
- 📖 Documentation
- 🐛 Issues
License
MIT — see LICENSE.
Built by Nous Research.