Hermes yesterday, October 18, 2026: idle worktrees are archived, then deleted
What landed on Hermes Agent main on October 18, 2026: idle worktrees with unsaved work are archived then removed, Docker sandboxes stop leaving root and setuid files, Desktop stops piling up sign-in windows, TLS survives an install rotation and 11 new catalog plugins.

Table of contents
- 1. Idle worktrees with unsaved work are archived, then removed
- 2. Docker sandboxes stop leaving root files and setuid binaries on your host
- 3. Desktop stops piling up sign-in windows overnight
- 4. TLS keeps working after the install environment rotates
- 5. Chat messages keep their order, and Desktop queues behind a busy bot
- 6. An unattended gateway no longer freezes on a hook prompt
- 7. Per-chat settings and history survive restarts
- 8. Desktop: /usage before the first message, and a usable New Bot dialog
- 9. Plugins can post live progress and hear about compactions and deletes
- 10. 11 new plugins in the catalog: voice and devices lead
- Meanwhile, in the engine room
- Watch the recap
Heads-up if you keep work in Hermes worktrees. An idle worktree with uncommitted or unpushed work used to sit on disk forever; on main the sweep now archives it to a git ref and then deletes it, and gitignored files such as .env copies go with it. Also: Docker sandboxes stop leaving root-owned and setuid files on your host, and Desktop stops opening a pile of sign-in windows overnight. 339 commits landed on main; here are the 10 that matter.
1. Idle worktrees with unsaved work are archived, then removed
Before: the worktree pruner never removed a dirty or unpushed tree at any age, and its 6-hour sweep only knew the Hermes checkout and cron workdirs, so trees piled up (658 trees and about 1 TB on one machine). After: on by default (worktrees.archive_dirty: true), a tree under a repo’s .worktrees folder that sits idle past its tier (hermes-* and subagent-* 24 h, named trees 72 h) and holds uncommitted or unpushed work is first saved as a commit at refs/hermes/archive/<tree> (HEAD, staged, unstaged and untracked files, exact bytes) and then removed. hermes worktree restore <name> brings it back, hermes worktree list shows archives and hermes worktree prune --dry-run prints the plan without touching anything. Every repo Hermes has made a worktree in is now swept, a subagent’s worktree with edits is archived when it finishes (its branch kept for the parent), and a dirty hermes -w exit asks Keep, Archive & remove or Discard (Keep is the default). Heads-up: gitignored files (.env copies, caches, build output) are not archived and are deleted with the tree, and archive refs older than 30 days are deleted (archive_retention_days). Left alone: trees with a running process or a live Hermes lock inside, recently touched ones, Kanban trees, trees with a submodule or a nested repo, and trees holding an untracked file over 50 MB. worktrees.archive_dirty: false brings back the old never-delete behavior. Not covered: closing a Desktop session never asks.
Pull request: Worktree archive.
2. Docker sandboxes stop leaving root files and setuid binaries on your host
Before: on Linux the Docker backend runs as the image’s user (root by default), so what it wrote into its bind-mounted /root and /workspace dirs stayed root-owned on the host and could not be deleted without sudo, and a file there carrying setuid/setgid bits or file capabilities kept them on the host, where running it as your user gave root’s rights. Since the one-gateway change, hermes chat sessions on Docker also got no /workspace mount (container_persistent: false) or all shared one container bound to the first session’s folder. After: at teardown a locked-down throwaway container hands root-owned files back to your user; setuid/setgid bits and file capabilities are stripped at every session boundary, persistent sandboxes included (on detach, before reattach and in the orphan sweep); each gateway session mounts its own folder, persistent Docker keeps one container per profile and folder, and idle per-folder containers are stopped instead of left running. Not covered: rootless Docker, userns-remap, Docker Desktop on macOS and Windows, and Podman (the pass is off or untested there); the default persistent container is never removed, so its file handback waits until it is; a setuid file planted while a sandbox is attached and run on the host before the next boundary; legitimate setuid files under /root or /workspace lose their bits inside the container too; reattach takes about 0.4 s longer.
Pull requests: Files handed back, setuid and caps stripped, Per-session workspaces.
3. Desktop stops piling up sign-in windows overnight
Before: after a gateway session lapsed (sleep, a gateway restart, an identity provider outage), every background request that got a 401 opened its own sign-in window, and when automatic sign-in could not finish, each one showed itself after 2.5 s, so people woke up to a screen covered in Hermes and Nous Portal sign-in windows. After: unattended sign-in runs hidden with a 12 s deadline and, if it fails, Desktop shows its own not signed in state with a Sign in button; a window appears only after you click Sign in, Use gateway or pick a cloud agent. There is one login flow per cookie jar: background callers join it and back off exponentially after a failure, and a second click focuses the open window instead of opening another one (which used to break the first with state_mismatch). Not covered: a real Nous Portal round trip and a real macOS lid close and wake were not tested.
Pull request: Desktop sign-in windows.
4. TLS keeps working after the install environment rotates
Before: when the package manager replaced the install environment under a long-running Hermes process (a launchd gateway, for example), certifi kept pointing at the deleted CA bundle, so every model call failed with No such file or directory, Nous credential refreshes failed too, and only a restart helped. After: Hermes re-resolves the CA bundle once per rotation and logs one warning, and the Nous auth exchange, Weixin, WeCom and the Actual relay go through the same repair; the browser and Keenable plugins’ requests calls get a live bundle (on Windows, the system store written once to $HERMES_HOME/cache/platform-ca-bundle.pem), and REQUESTS_CA_BUNDLE or CURL_CA_BUNDLE overrides keep working. Nothing changes on a healthy install. Not covered: other direct requests callers (models.dev lookups, image and video generation plugins, keyless MCP and others) still fail after a rotation until a restart.
Pull request: TLS after a rotation.
5. Chat messages keep their order, and Desktop queues behind a busy bot
Before: since the one-gateway change, a photo or video followed quickly by text could be handled out of order, a message claimed just before /new ran on the fresh conversation, slash-command turns (/queue, /plan, skills) jumped ahead of waiting messages, and on serial platforms such as Signal and IRC a burst sent during a turn held up the reader. Typing into a Desktop chat while its gateway owner was mid-turn (a Bot Chat delivery) showed Turn failed, and peer DM, peer run, POST /v1/runs and bot or cron deliveries to a Bot Chat first created over the API failed with not_found. After: media and the text after it keep arrival order; /new drops the claimed turn; slash commands queue behind waiting messages; a burst on a serial platform merges into one follow-up; a Desktop prompt to a busy owner is queued and runs once after the turn, and when it cannot be queued (rewind, attachments) the card says why and that you can send it when the turn ends; the old API-created Bot Chat is moved aside as Bot Chat (API <id>) with its history, and deliveries land in a new local one. Not covered: per the PRs, these still need a live retest on real Telegram, Signal and Desktop setups.
Pull requests: Messaging and Bot Chat, Desktop queue and client recovery.
6. An unattended gateway no longer freezes on a hook prompt
Before: a shell hook that was not yet approved made the gateway ask Allow this hook to run? on a console nobody watched (a hidden console still counts as a terminal), which froze it until the liveness watchdog killed it with exit 75; this could hit at start, when a secondary profile loaded or after a config set. Approving a hook later did nothing until a restart or a local session opened. After: the gateway, cron, mcp serve, ACP and the TUI backend never prompt; an unapproved hook is skipped, and the warning names the allowlist file of the profile that needs it (for example profiles/p2/shell-hooks-allowlist.json); only foreground hermes chat and hermes rl may ask. A running gateway registers a hook within about 30 s of its consent being recorded and drops it about 30 s after hermes hooks revoke, on single-profile and multiplexed gateways, without rebuilding adapters. A watchdog kill now also writes its thread stacks to logs/gateway-shutdown-watchdog.log.
Pull requests: No prompt when unattended, Consent without restart.
7. Per-chat settings and history survive restarts
Before: since the one-gateway change, a /reasoning pick on a Bot Chat that follows its profile did not stick; the classic CLI’s /model --once changed the shared session; deleting a parent session dropped launch secrets a /branch child still used; a managed worker’s first Always approval on a fresh profile never reached config.yaml; resuming a compressed conversation showed only its last segment; and after a Desktop restart, opening a bot group by name created a fork of the room, All profiles switched itself off, and the all-profiles and Archived lists dropped or repeated sessions across pages. After: /reasoning is stored per session and survives profile following and restarts (/reasoning reset returns to the profile’s level); --once applies to that one turn only; branch secrets stay while a branch uses them; Always is saved; resume returns the whole history with archived rows marked; Desktop reopens the existing group (a failed lookup offers Retry, never a new room), keeps All profiles on and lists each session once. Also: the gateway now refuses /voice tts on local sessions by name instead of synthesizing audio nobody hears.
Pull requests: Per-session settings, History across compression, Desktop lists and groups.
8. Desktop: /usage before the first message, and a usable New Bot dialog
Before: Desktop and TUI /usage on a fresh session only said No active agent – send a message first. In Bot Mode’s New Bot dialog, opening Advanced, Capabilities before typing a name left a spinner forever and Create Bot then failed; the embedded Capabilities frame was a fixed 440 px. Main model settings squeezed Provider and Model into one row. After: /usage shows the provider’s account limits and Nous credits right away, read from the session’s own profile (a second profile’s key included); the Capabilities tab loads whenever the name arrives, and its frame grows to min(85vh, 960px) in New Bot and Edit Profile; Main model settings get separate labeled Provider and Model rows with Apply below them, and choosing alone still saves nothing.
Pull requests: /usage limits, New Bot Capabilities, Main model settings.
9. Plugins can post live progress and hear about compactions and deletes
New for plugin authors: ctx.post_message(text) posts one message into the conversation the plugin is working for and returns a handle whose edit() updates it in place on any channel (a fresh message where the platform cannot edit, a Desktop toast or TUI notice on local sessions, a line at turn end in the classic CLI); with no bound conversation it returns ok=False and sends nothing, and it is not available under plugins.isolation: host. A post_compaction hook fires once for every compaction that actually lands, on every path, so memory providers can re-inject what was summarized away; on_session_delete fires after any delete or deliberate archive from any surface, with a reason field. Plugin slash commands now receive session_id, session_key, platform and source when they declare them (one-argument handlers keep working), and the skills tool’s lookup helpers (find_all_skills, locate_skill and five more) are public. Both hooks are observers: return values are ignored and delivery is process-local.
Pull requests: post_message, post_compaction, on_session_delete, Slash command context, Skills helpers.
10. 11 new plugins in the catalog: voice and devices lead
11 new community entries, and talking to Hermes outside the chat window was the day’s theme: live WhatsApp voice calls on a linked personal number (through an unofficial protocol and a sidecar that streams call audio to Gemini Live) and small ESP32 devices with a screen, mic and speaker. Several disclose that they open a network port, run local subprocesses on the host or send data to a hosted service (tsubasa’s API currently serves Canada only): read the disclosure before installing. Catalog entries do not wait for a release: they go live once the published catalog refreshes.
- Voice and devices: hermes-whatsapp-voice, gadget
- Desktop: completion-sound, offpeak-bekci, hermes-lang-it-full
- Agent logs: edit-ledger, interim-ledger
- Tools and connectors: hermes-seo, scalekit, hermes-jetstream
- Models: tsubasa
Meanwhile, in the engine room
Dozens of fixes landed yesterday that most users will never see, and most of them went to the shared gateway: queues, sessions and profiles catching up with the one-gateway change.
- With proactive tool-result pruning on (
proactive_prune_tokens), every prune rewrote the whole live transcript intostate.db, leaving tens of thousands of superseded copies on one install; it now updates only the rows it changed, in place (pull request) - Scratch cleanup reclaims idle clones whose pack files are hardlinked to a live repo (one host had 100 GB held that way), and logs a root-owned leftover once, with the command that removes it, instead of re-walking it every hour (pull request, pull request)
- On Windows, a normal Desktop or CLI facing a gateway started as administrator gets a named refusal with the fix (restart it without elevation) instead of a generic authorization error or, on some hosts, a silent attach across the UAC boundary (pull request)
hermes updateon Linux with a system-scope dashboard unit and no root prints one exactsudo systemctl restartcommand instead of a polkit error and advice to restart the gateway (pull request)
If you noticed none of this, it worked.
All on main, ships in the next release. Latest stable: v0.22.2.
Watch the recap
The 56-second motion recap, made with Remotion, is on X.
Was this configuration guide helpful?
If you have questions, encountered issues during setup, or want to discuss networking and security, connect or reach out on LinkedIn.
Connect on LinkedIn