Hermes Agent v0.21.2
Overview
v0.21.2 — The state.db Patch Release. Released September 11, 2026. 947 non-merge commits · 1,869 files changed (+182,504 / −15,564) · 312 merged PRs · 140 contributors since v0.21.1.
v0.21.0 rewrote how session storage handles its connections, and part of that rewrite left state.db fragile on a subset of installs: a second writer could cancel the first writer’s locks, a perfectly healthy database could be judged corrupt, and a single bad row was enough to take down sessions list. v0.21.2 closes that whole class of failure. Six PRs account for 44 closed issues, working layer by layer through lock ownership, WAL recovery, read paths and diagnostics, so the database either works or fails loudly and specifically — never silently, and never by dragging the conversation down with it.
Around that campaign, the four days since v0.21.1 also landed a batch of multi-profile isolation hardening, the end of the desktop backend startup storm, a credential vault the agent can use without ever seeing a secret, a reviewed SHA-pinned plugin index with a unified Plugins page in Desktop, and a Nous free tier with a guided first run. This page covers the window in that order.
Highlights
1. The state.db reliability campaign: six PRs, 44 issues closed
Every failure mode below was reproduced, fixed, and left guarded so the same shape cannot come back quietly.
- No more second writers. The profile gateway used to write managed-room state into the root state.db every 5 seconds; the dashboard opened a writable handle at startup; the cron lifecycle guard did a bare
open()against a live database, which is enough to cancel the gateway’s POSIX locks; anddoctor --fixwould checkpoint while a live holder still existed. Today managed rooms live inshared-state.db, the dashboard opens read-only first, the guard goes through the managed connection registry, anddoctor --fixrefuses any checkpoint it cannot prove safe (#108076). - Healthy WAL databases no longer wedge. A set of shapes where a working database had been judged dead are gone: an OpenZFS
(deleted)dentry,close()racingappend_message, the read pool handing out connections in unacknowledged journal mode, a transientdisk I/O erroron WSL2 failingget_sessionon the very first attempt, and “state.db locked” still being broadcast after the locks were released (#108082). - A corrupt search index no longer kills the turn. Errors that only affected the full-text index used to be treated as whole-file corruption and fail closed. They are now classified as
fts_index: search degrades, the index is rebuilt later, and the transcript is untouched. The same PR makesdoctordistinguish structural corruption from index corruption honestly, adds an FTS write probe that catches the stale-index shape where every check passes but every write fails, stops.recoverfrom failing to start because of orphan FTS5 shadow tables, recovers databases whose header was zeroed out, and returns a 503 from dashboard analytics polling instead of 520,000 tracebacks a day (#108130). - One bad row no longer takes down
sessions list, export or insights. A TEXT timestamp or a1e30epoch used to crash the entire list; malformed marker JSON crashedjson_extract; more than 999 ids crashed bulk delete and prune. Every reader now goes throughcoerce_epoch()— a bad row renders as?with a WARNING that names the session — plusjson_validguards, chunked IN lists and batched export hydration (#108086). - Sessions no longer bind to, or read, another profile’s database. Desktop backend startup could pin the wrong profile’s
state.dbunder a HERMES_HOME override race;session_searchwith a bare ID silently scanned every profile and returned other people’s conversations; recovery guidance pointed at the wrong file; and deleting a profile left handles open (WinError 32) (#108074). - Opening state.db no longer takes a write lock when there is nothing to write. A one-off
hermesprocess opening behind a busy gateway used to stall for 4–20 seconds and then report “database is locked”. It now takes 0.01 seconds (#108067).
If your state.db was already damaged by 0.21.0 or 0.21.1, start here:
hermes doctor
hermes sessions recover --inspect-only
2. Multi-profile isolation hardened
v0.21.2 turns the boundary between profiles from a convention into something enforced: a secondary profile’s bots no longer inherit the default profile’s allow-list (#107616); adapters no longer send credentials to the default profile’s host (#107617); stdio MCP servers no longer receive the default profile’s vault secrets (#107630); MEDIA: delivery can no longer carry another profile’s .env / auth.json / state.db (#107609); Feishu drive callbacks and /p/<profile>/ webhook replies stay inside the routed profile (#107620, #107626); and a secondary profile no longer receives a sibling profile’s Nous bearer (#107611).
Put together, the rule is simple: if a process belongs to one profile, it can neither reach another profile’s files and secrets nor make requests under another profile’s identity.
3. The desktop backend startup storm is over
Bot Mode used to spawn or dial a backend for every profile at startup and again on every roster tick; hovering the Bots roster spawned a backend per row; and switching profiles could spawn duplicate primaries. Backend creation and reuse now follow an explicit lifecycle, roster rendering has no side effects, and switching profiles hands you the same primary instead of a new copy (#108069, #108107, #108118, #108134, #107969, #108112). What you notice as a user is that a desktop cold start no longer sounds like a fan takeoff and a busy gateway at the same time.
4. A credential vault the agent cannot see into
The agent can now log in, pay and fill in addresses from 1Password, Bitwarden or the local Hermes vault without ever seeing the secret; two-factor codes come from stored authenticator secrets, or are asked for in your own UI (#106480, #107585). Private git plugins install with saved credentials too (#106981). Put another way, secrets travel from the vault straight to the target site or repository: the agent orchestrates, sees “done” or “failed”, and never sees plaintext.
5. A plugin catalog, and one unified Plugins page
Plugins now have a reviewed index with SHA pinning, complete with CLI, admission CI, docs and a dashboard (#69446). Desktop gains a single Plugins page that manages agent and desktop plugins in one place — install, browse the catalog and pin to a commit without leaving it (#107212, #107314, #107321). Radio ships as an optional SDK plugin (#107072).
6. A Nous free tier and a guided first run
Free inference and connectors work out of the box, with a one-command login (#105258, #105260), and /login works right in chat (#105261); connector tools (Gmail, Linear, Notion and more) are discoverable through tool_search (#106842); and with HERMES_GUEST_ONBOARDING=1 set, the first run becomes a guided flow that gets accounts, connectors and models configured before the first conversation (#107697, #107958, #107985, #108211).
export HERMES_GUEST_ONBOARDING=1
Improvements
v0.21.2 is a patch release: the changes below do not make a headline, but they belong to the same job of making state.db stop lying.
- Rebuild and publish order: a newly created state.db no longer publishes its FTS tables before it takes the rebuild lock (#106311).
- WAL generations and exit: handles that lost a WAL generation no longer checkpoint stale frames on exit (#106315, #106840), and a database that cannot be verified is left untouched during WAL init (#106568).
- Quarantine instead of destructive open: a database whose header was overwritten is quarantined together with its WAL instead of being opened destructively (#106587), and quarantined handles refuse VACUUM and FTS optimize (#106343, #106349).
Fixes
- Gateway and platforms: a bare
display:key in config.yaml no longer crashes every turn (#106305); queued-lane final messages rejected by the platform are recorded and re-delivered (#106316); a stuck WebSocket send no longer blocks every event behind it (#106581); the first turn no longer waits on Python toolchain probing (#106556); withbots_require_mentionenabled, Telegram bots must be @mentioned, which breaks bot-to-bot ping-pong (#106534); Matrix renders LaTeX (#106515); Signal renders Markdown tables (#106538); WhatsApp replies keep their quote on view-once messages (#106541); media-only replies report SUCCESS on every platform (#106557). - Providers and routing:
/modeland auxiliary auto-routing can no longer bill a provider you did not select (#107366); Hermes never silently falls through to a provider with no credentials (#107281); Bedrock Claude/Converse/Mantle models survive/model, fallback and recovery (#107621, #107658); Bedrock Guardrails actually apply (#107815); a Codex patch-budget image 400 is retried with a downscaled image (#106525); an unauthorized primary provider plus fallback no longer oscillates (#106549); Azure Foundry rejected replay reasoning is classified and trimmed (#106718); an MCP OAuth refresh no longer wipes the refresh token (#106185); the Anthropic client sends exactly one credential (#107978); DeepSeek V4.1 Flash joins the Nous Portal and OpenRouter pickers (#107489); OpenAI and FAL support GPT Image 2.5 (#105988); Opus 5 and Fable 5.1 join the native Anthropic picker (#106636). - Agent loop and compaction: a blocked periodic callback no longer stalls lease refresh (#106308); an in-turn
/steeris stored as its own user row (#106317, #106344); local-inference memory-limit rejections back off instead of compacting history (#106307); a context overflow after partial streaming ends the turn (#106567); length continuations stop when the previous text has already filled the window (#106571); compaction no longer times out silently on aux retries (#106866);model_thresholdskeys can be provider-qualified (#108061); switching between Desktop and TUI no longer rebuilds the system prompt, which preserves the prompt cache (#105844); the CLI keeps anapi_contentsidecar so even early persistence preserves the cache (#105842). - CLI, TUI and Desktop:
hermes -z --resumeresumes the session (#106313); extended keys bring Shift+letter and Cmd+Shift+Z to terminals (#90674, #105493);browser_exectimeouts kill the whole process tree (#106589); the update check polls the GitHub API once a day instead of running a git fetch every 30 minutes (#107648);hermes updatestates the real reason and no longer wedges on fetch (#108053); on Desktop, the UI language survives an update restart (#106476), expired OAuth grants get a one-click re-login (#106965), HUD mode shows the conversation again and always hands the window back (#107491, #107423), the backend exits when its Desktop parent exits (#107977), Windows updates stop reporting false failures (#106175, #107183), WSLg renders on Windows GPUs (#106528), and Telegram QR quick setup is ported over from the dashboard (#107242). - Cron and Kanban: an off-the-hour “run now” no longer cancels the next scheduled run (#106306); a killed manual run no longer blocks the next 5-minute slot (#106733); a job changed from one-off to recurring keeps firing (#106532); jobs without a pinned model run the model snapshotted at creation (#106499);
--clone-allno longer copies cron jobs (#106478);kanban promoterejects an incomplete parent task (#106550);kanban_request_reviewrejects an unknown reviewer profile (#106547). - Tools and memory: a stdio MCP server dying mid-call no longer replays the tool call (#106546); a skills-only background review can no longer delete memory entries (#106310); mem0 memories no longer drop long turns (#106542);
tool_searchreturns nothing rather than five unrelated tools that share one word (#106676); remote NOPASSWD sudo no longer prompts for approval (#107939); RSS and Reddit reads are no longer enabled by default (#105873). - Housekeeping: config.yaml backups are consolidated into one bounded
backups/config/directory (#106388);hermes backupkeeps only the newest 3 zips (#106455);hermes setup --resetbacks up the real config (#106453);debug shareretention on the dpaste fallback path drops to 1 day (#106531).
Upgrade
Existing installs update in place:
hermes update
Fresh installs:
curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash
Managed deployments should be updated through their own deployment tooling using the new tag.
If state.db was already damaged by 0.21.0 or 0.21.1: run hermes doctor first — it now separates structural corruption from index corruption correctly — and when a rebuild is not enough it points at hermes sessions recover --inspect-only (profile-scoped).
The full diff is at v2026.9.7…v2026.9.11.
← Hermes Agent Changelog