Hermes Agent v0.20.3
Overview
v0.20.3 — The Resilience Patch. Released August 16, 2026. ~125 merged PRs · ~250 commits · ~461 files changed (+42,613 / −1,641) since v0.20.2.
One day after the Connections Patch, this release answers a quieter question: what happens when your automation quietly stops working and nothing tells you? The cron scheduler now survives file-descriptor exhaustion, reconciles stale claims against the executions ledger, re-arms wedged recurring jobs, and catches up missed external-provider fires — while last_fire_error makes every miss visible in the CLI, the dashboard, and the agent’s own tool output. Around it: the MCP 2.x SDK migration with 2026-07-28 stateless protocol support, CommandCode as a first-class provider, Copilot-CLI-inspired /worktree and safe /rollback, subprocess Python runtime ownership hardening, and session handoff data-loss fixes.
This is a patch release, so the official notes are brief and the full curated documentation ships with v0.21.0. This page covers the most valuable parts of the window.
Highlights
1. The cron scheduler heals itself
Three silent-stall failure modes that used to leave jobs dead for hours — while everything “looked healthy” — are now self-healing:
- EMFILE (file-descriptor exhaustion):
tick()used to swallow everyOSErroras “another instance holds the lock”, so an fd-exhaustion on the lock file was recorded as a successful tick and no job ever ran again. Lock-acquisition errno is now classified: only genuine contention skips silently, real failures propagate as a failed tick, and the ticker attempts best-effort fd reclamation (gc.collect()+ raising the soft nofile limit) with exponential backoff capped at 15 minutes (#88335, based on #87796 by @webtecnica). - Stale in-flight claims: a leaked claim younger than the age window (2×interval, 30 min) used to wedge a recurring job until an operator intervened. The sweep now reconciles against the durable executions ledger: if the run’s terminal row proves it ended, the claim is force-released — with a guard so a fresh claim is never confused with the previous run’s completed outcome (#88343, based on #87259).
- Wedged persisted-error state: a recurring job with
last_status=errorandnext_run_atparked in the future was invisible to every sweep and survived gateway restarts. It now re-arms automatically on the next tick — and the re-arm respects schedule legality, so a weekday-only cron expression never fires on a Saturday (#88339, based on #87261).
2. Missed fires are visible — and they catch up
The 2026-08-14 live incident (4 consecutive nightly misses, zero evidence outside a log line) produced two durable fixes. First, when the hosted fire path can’t reach the gateway, the job gets a last_fire_error stamp surfaced in cronjob list, hermes cron list (red ⚠ Missed scheduled fire: line), and the dashboard; a successful run clears it, so the stamp always describes current health (#88555). Second, external cron providers (Chronos / hosted managed cron) now get misfire catch-up: when a fire never arrives and retries exhaust, the gateway detects the overdue job and runs it locally after a grace window — cron.misfire_grace_minutes (default 10, ≤0 disables). The outage costs minutes instead of a silently lost day (#88563).
3. MCP: SDK 2.x + the 2026-07-28 stateless protocol
Hermes migrated to the MCP 2.x SDK (#88180) and now speaks the 2026-07-28 stateless protocol end to end (#88299). Servers with no initialize handshake connect out of the box via a single _negotiate_session() choke point on all four transport call sites (stdio, SSE, new HTTP, legacy HTTP). Per-server protocol key: auto (default) tries the legacy handshake first and falls back to server/discover when the server rejects it — zero extra round-trips for the existing fleet; stateless probes discover-first; legacy disables the fallback. SEP-2549 ttlMs/cacheScope hints from tools/list are captured into the schema cache with TTL expiry, and OAuth registration follows the new spec (native application_type, RFC 9207 iss validation).
4. New providers: CommandCode (and Muse Spark on main)
CommandCode (commandcode.ai) is now a first-class provider — commandcode (OpenAI chat completions) and commandcode-anthropic (Anthropic Messages, Bearer auth) profiles behind one COMMANDCODE_API_KEY, covering the GOAT/Pro/Max/Provider plans (~30+ open and closed models, live discovery from the public /provider/v1/models endpoint) (#88308, salvaging #32909). On main (post-tag), the Meta Model API (Muse Spark) joins as a built-in plugin: --provider meta-ai works out of the box with MODEL_API_KEY (aliases META_API_KEY/META_MODEL_API_KEY, META_BASE_URL override) and a muse-spark-1.2 / muse-spark-1.2-contributor catalog (#88565).
5. /worktree and safe /rollback — agent edits you can undo
Two Copilot-CLI-inspired commands land in this window. /worktree new [name] creates an isolated git worktree mid-session (.worktrees/ inside the repo, branch based on the freshly-fetched remote tip, worktree_sync honored) and retargets the session’s terminal and file tools into it — no restart; /worktree shows the active tree and /worktree list lists them, with the same keep-if-unpushed exit cleanup as hermes -w. /rollback now defaults to safe restore: a per-project ledger of agent-authored writes (sha256 of every landed write_file/patch) lets it revert only what the agent changed, delete agent-created files, and preserve your hand-edits; --all/--force restores everything, and skipped files are reported with a hint.
6. Subprocess Python runtime ownership
execute_code and friends now own their Python subprocess environment: PYTHONHOME/PYTHONPATH are composed from the managed runtime, not inherited contaminated from the parent shell, closing a class of “works in my terminal, breaks in the agent” surprises and hardening the boundary for untrusted code.
7. Session handoff data-loss fixes
Two user-reported data-loss bugs are fixed (#88244). A CLI→gateway race after /handoff telegram could finalize the session row the gateway was actively writing, making the entire handoff leg vanish from history and session_search; the CLI now tracks handed-off session ids and skips the cleanup finalize. Separately, a corrupted state.db that blocked all messages is now surfaced to the user: the gateway broadcasts recovery guidance (including hermes doctor --fix and sqlite3 .recover) to home channels instead of burying the failure in logs.
Improvements
- Plugin install/update security scanning (Claude-Cowork-inspired): installs are scanned for suspicious content before activation.
- UTF-16 text files are read by transcoding to UTF-8 (port from MoonshotAI/kimi-code#2647); Gemini 3 tool-call IDs are preserved across adapter rewrites (port from earendil-works/pi#7494).
- Git worktree self-heal: a failed/timed-out
hermes -wworktree add cleans up after itself (partial dir, LOCKED admin entry, orphan branch), and the startup maintenance pass repacks when packs sprawl — the August incident’s 39 packs (638 MB) became 2 (287 MB) and worktree creation went from a 30s timeout to 0.5s (#88306). - Cua Driver 0.20 runtime contracts: computer use verifies and auto-repairs an installed driver that fails the runtime contract at update and runtime (#87646 family).
- Desktop: DiffusionCanvas rendering is bounded (pause policy, 15fps budget, instance cap), the idle pixel-egg animation sleeps, Bot Mode group chats render Markdown, and remote-gateway desktops no longer show a phantom local default (#88564, #88406, #88553, #88554).
- Delegate: a subagent’s uncommitted work is kept when git inspection fails, and parents are told when a worktree was preserved un-inspected.
Fixes
/handoffCLI→gateway race losing the handoff leg (#88234); state.db corruption now surfaced with recovery guidance (#88235).- Matrix: blank bare media filenames from
m.audio/m.file/m.videobodies; Telegram prefers IPv4 API IPs and logs the first-choice stick as info. - Cron:
run_claimis cleared for one-shot jobs on dispatch failure; drift remediation is directed to user-owned pins. - Compression: an aborted rotation no longer grows the parent it could not publish.
- Desktop: the pre-start settle hold can’t latch the composer shut; a restored/edit arm keeps the composer usable.
Upgrade
hermes update
After upgrading, run hermes doctor to verify the install and restart the gateway (hermes gateway) so platform changes take effect. If you use an external cron provider, check hermes cron list once to confirm last_fire_error shows nothing pending.