hermes-agent/apps/desktop
emozilla 9f61698369 feat(desktop,cli,docker): serve the desktop renderer at /app on the gateway
The web bridge (previous commit) made the renderer boot in a browser; this
makes an instance actually serve it, end to end, plus the Phase 1 UI gating.

Web bridge + renderer:
- petOverlay/terminal/updates/uninstall/themes become OPTIONAL bridge
  members, absent on web rather than stubbed inert — every caller already
  ?.-guards, so features hide through existing branches (typecheck-verified)
- Gateway settings nav entry absent on web (connection IS the serving
  origin); About hides the Updates section behind a capability check on
  window.hermesDesktop?.updates (version + release notes stay)
- notify requests Notification permission at point of first use
- getVersion reports the renderer package version via a vite define

Serving (web_server.py):
- APP_DIST/mount_app: /app/assets static mount, /app 307-canonicalized to
  /app/ (relative asset URLs), index served with the same bootstrap-global
  injection contract as the dashboard SPA (token in loopback mode, cookie +
  ws-ticket in gated mode); same traversal guard, same auth-gate coverage
- Mounts ONLY when HERMES_APP_DIST is explicitly set: no path fallback, so
  a regular desktop build leaving a dist at apps/desktop/dist can't silently
  enable /app. Env set but dist missing logs a WARNING (a Docker image with
  a broken frontend build should not surface as a mystery 404)

Build + image:
- apps/desktop build:web script: renderer-only build (typecheck + vite),
  no Electron packaging steps
- Dockerfile: desktop manifest joins the cached npm-install layer
  (ELECTRON_SKIP_BINARY_DOWNLOAD=1 keeps the ~100MB binary out), build:web
  appended to the frontend-build layer, HERMES_APP_DIST set at runtime
- .dockerignore: apps/desktop source enters the build context; node_modules/
  dist/build/release re-excluded after the negation (last-match-wins would
  otherwise re-include them — a stale local dist would clobber the
  image-built one via COPY . ., and a Windows node_modules would clobber
  the image's Linux natives)

Verified: build:web dist served at /app/ from a real gateway (token injected
server-side, host:'web', gateway WS connected, zero console errors); mount
contract curl-verified in both directions (no env var = /app falls through
to the dashboard SPA even with a dist present on disk). Image build itself
pending a docker-capable machine.

No-op for every install that doesn't set HERMES_APP_DIST; zero behavior
change for Electron users.
2026-07-11 01:14:35 -04:00
..
assets fix(desktop): pad app icon to Apple grid so dock size matches peers (#42946) 2026-06-09 11:48:26 -05:00
electron Merge branch 'main' into feat/desktop-web-bridge 2026-07-10 22:02:52 -04:00
pr-assets chore(desktop): drop PR screenshot assets from tree 2026-07-07 13:04:32 -07:00
public fix(desktop): pad app icon to Apple grid so dock size matches peers (#42946) 2026-06-09 11:48:26 -05:00
scripts fix(desktop): stop using tsx to boot Electron main in dev 2026-07-08 22:40:51 -05:00
src feat(desktop,cli,docker): serve the desktop renderer at /app on the gateway 2026-07-11 01:14:35 -04:00
.prettierrc Add Hermes desktop app (#20059) 2026-05-31 17:46:56 -05:00
components.json feat(desktop): add shared project UI primitives 2026-06-25 16:40:27 -05:00
DESIGN.md docs(desktop): add DESIGN.md design-system guide + close two consistency gaps (#40823) 2026-06-06 22:13:17 +00:00
eslint.config.mjs feat(desktop): ts-ify everything 2026-07-08 16:24:16 -07:00
index.html feat(desktop): composer status stack, live subagent windows, editable prompts (#44630) 2026-06-12 08:30:06 -05:00
package.json feat(desktop,cli,docker): serve the desktop renderer at /app on the gateway 2026-07-11 01:14:35 -04:00
preview-demo.html Add Hermes desktop app (#20059) 2026-05-31 17:46:56 -05:00
README.md feat(dev): add isolated sandbox script for local dev 2026-07-10 18:51:49 -04:00
tsconfig.electron.json feat(desktop): ts-ify everything 2026-07-08 16:24:16 -07:00
tsconfig.json feat(desktop): ts-ify everything 2026-07-08 16:24:16 -07:00
vite.config.ts feat(desktop,cli,docker): serve the desktop renderer at /app on the gateway 2026-07-11 01:14:35 -04:00

Hermes Desktop ☤

Download Documentation Discord License: MIT

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 agentStreaming responses, live tool activity, structured tool summaries, and the same conversation history as every other Hermes surface.
Side-by-side previewsRender web pages, files, and tool outputs in a right-hand pane while you keep chatting.
File browserExplore and preview the working directory without leaving the app.
VoiceTalk to Hermes and hear it back.
Settings & onboardingManage providers, models, tools, and credentials from a real UI. First-run setup gets you to your first message in seconds.
Stays currentBuilt-in updates pull the latest agent and rebuild the app in place.

Install

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:

# throwaway HERMES_HOME, separate Electron userData, distinct app name to avoid the single-instance lock
../scripts/dev-sandbox.sh npm run dev
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 the Electron shell and a native React chat surface. On first launch it can install 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. Backend resolution first honours HERMES_DESKTOP_HERMES_ROOT, then a completed managed install, then a probed hermes on PATH (unless HERMES_DESKTOP_IGNORE_EXISTING=1 is set), and finally an explicit HERMES_DESKTOP_HERMES command override for packagers/troubleshooting. The renderer (React, in src/) talks to a headless backend the app launches for you — a hermes serve process that serves the tui_gateway JSON-RPC/WebSocket API — through the framework-agnostic client in apps/shared (the same client the web dashboard consumes), and reuses the agent runtime rather than embedding hermes --tui. The app is self-contained: it runs its own hermes serve backend and never opens or requires the web dashboard UI. (For backward compatibility, a runtime that predates the serve command automatically falls back to a headless dashboard --no-open — see electron/backend-command.ts — so mid-upgrade installs never break.) The install, backend-resolution, and self-update logic all live in electron/main.ts.

Verification

Run before opening a PR (lint may surface pre-existing warnings but must exit cleanly):

npm run fix
npm run typecheck
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 the HERMES_HOME env var if you've relocated it.


Community


License

MIT — see LICENSE.

Built by Nous Research.