Skip to content

state.db FTS damage no longer kills turns; doctor/repair/recover handle real incident shapes (#97794, #88587, #100227, #103840, #106667, #102240, #98050, #103647, #96591, #105887; salvage #97843 #88604 #56824 #91413 #102808 #103657 #106890 #103321) - #108130

Merged
teknium1 merged 14 commits into
mainfrom
fix/statedb-repair
Sep 11, 2026

Conversation

@teknium1

Copy link
Copy Markdown
Collaborator

A damaged FTS5 index no longer kills turns or gets mislabelled as whole-file corruption, and the recovery lane (hermes doctor, sessions repair, sessions recover, optimize-storage) now handles the shapes real incident databases actually arrive in: orphan shadow tables after .recover, a torn page-1 header, phantom NOT NULL rows, a damaged rowid edge, a CJK index family, and a segment-collision write failure that the health probe used to miss.

Changes (13 commits, contributor authorship kept)

Fail-open for FTS, fail-closed for structure

Detection

Recovery lane

Live repro (real corruption fixtures, no mocked detectors)

Item Before (origin/main) After
#97794 FTS-scoped error escaping append DatabaseError code=267, classify → corrupt, turn fail-closes with .recover advice cause=fts_index, canonical messages intact, turn continues
#88587 torn sessions root page doctor: "FTS write corruption — run doctor --fix"; --fix writes .malformed-backup-* copy of the damage, fails doctor: "structural corruption (canonical tables damaged, not the FTS index)" → sessions recover --inspect-only; no backup written; DEADBEEF FTS stomp still → rebuild_fts
#105887 HERMES_HOME=<root>/profiles/research, active_profile=other 1. Run hermes doctor --fix hermes -p research doctor --fix / ... sessions recover --source <root>/profiles/research/state.db --inspect-only
#106890 rebuild_fts() on quarantined handle 3 param cases DID NOT RAISE raises like vacuum()
#100227 stale messages_fts_trigram_idx row at next segid integrity_check=ok, _db_opens_cleanly→None, check-only "opens cleanly", doctor healthy, real append → IntegrityError constraint failed _db_opens_cleanly→'fts5 write probe failed: constraint failed'; check-only ✗ rc 1; doctor ⚠; repair_state_db_schema heals, appends work
#96591 bytes 100..4096=0xFF, 2 polls 2×HTTP 500, 2 full tracebacks (164 log lines) 2×HTTP 503 state_db_corrupt, 1 WARNING, 0 tracebacks (12 lines)
#103840 vtable row removed, shadows kept OperationalError: fts5: error creating shadow table messages_fts_data: table already exists on open opens, fts_enabled=True, MATCH 5/5, trigram family rowids unchanged
#106667 random 100-byte header, sqlite3 3.53.1 page-level .recover salvage failed: file is not a database (26) Recovered 60 sessions and 300 messages, source md5 unchanged
#102240 phantom sessions row, NULL started_at IntegrityError: NOT NULL constraint failed: sessions.started_at, abort status=partial copied=3 destination_rejected_rows=1 verified=True
#98050 leftmost sessions leaf cell-count \xff\xff, 400 rows low=-9223372036854775808 copied=0 range_queries=10000 status=failed low=1 high=400 copied=391 range_queries=40 status=partial
#103647 gcc-built fts5_cjk.so, optimize-storage no such table: messages_fts_cjk_data in _demote_legacy_fts_to_trash._stage optimize ok, cjk searchable, 0 fts_v22_trash_messages_fts_cjk*

Root cause

Every corruption-shaped error was bucketed as whole-file damage, and the recovery lane assumed an openable file with a consistent schema — so FTS-only damage killed turns while doctor misnamed it, and the tools meant to salvage real incident databases refused the exact files those incidents produce.

Verified already fixed on main (no change here)

Design calls made in this PR (flagging for review)

Fixes #97794, #88587, #105887, #100227, #96591, #103840, #106667, #102240, #98050, #103647
Refs #106393, #96009, #92401, #56815, #106212, #86062

Infographic

state.db repair

@github-actions

github-actions Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

૮ >ﻌ< ა ci review

ran on 58767f6 — test(web): count only the dashboard's own warning in the cor

⚠️ Warnings

OSV vulnerability scan · View job

80 known vulnerabilities found in pinned dependencies.

How to fix:

Review the findings in the Security tab. Update the affected dependencies if a patched version is available.


debug info

CI timings

CI timings · View report · View job

Wall time 7m3s vs 6m (+17.5%). 8 job(s) slower, 4 faster, 2 unchanged.

  • OS-specific tests / Windows-only tests: +57.0s
  • Python lints / Windows footguns (blocking): +16.0s
  • OS-specific tests / macOS-only tests: +7.0s
  • Check contributors / check-attribution: +6.0s
  • Python tests / Run tests: +5.0s

@alt-glitch alt-glitch added type/bug Something isn't working P1 High — major feature broken, no workaround comp/cli CLI entry point, hermes_cli/, setup wizard comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/gateway Gateway runner, session dispatch, delivery area/sessions Session lifecycle, resume, persistence, history sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state labels Sep 11, 2026
liuhao1024 and others added 14 commits September 11, 2026 06:26
`sqlite3 state.db .recover` re-emits the FTS5 shadow tables (messages_fts_data,
_idx, _docsize, _config, _content) as ordinary tables but cannot re-emit the
CREATE VIRTUAL TABLE row. The next SessionDB open ran the FTS DDL in
_ensure_fts_schema and died with "fts5: error creating shadow table
messages_fts_data: table 'messages_fts_data' already exists", so a recovered
database was unusable until someone hand-dropped the shadows.

_init_fts now runs _drop_orphan_fts_shadow_tables before any FTS DDL. It is
per-family and exact-name scoped: a family's shadows are dropped only when its
own vtable row is absent from sqlite_master (type='table' AND sql LIKE
'CREATE VIRTUAL TABLE%'), so a healthy messages_fts_trigram survives a
base-family repair untouched. A repaired base/trigram family is then treated
like a missing-trigger repair and rebuilt from the canonical messages table
under the cross-process rebuild admission; the shadows are derived index
state, nothing is lost.

Live repro: real `sqlite3 x.db .recover | sqlite3 y.db` on sqlite 3.50.4
keeps the vtable rows (the shell emits CREATE VIRTUAL TABLE), so the
deterministic fixture removes the vtable row via writable_schema leaving the
shadows behind: BEFORE OperationalError on open; AFTER opens, fts_enabled,
MATCH returns every message, trigram sqlite_master rowids unchanged.

Salvaged from PR #56824 (intent applied onto the current hermes_state_fts /
hermes_state_schema siblings). The ownership-safety point (never touch a live
family's shadows) was raised by @ggoldani in #103868 / #103840.

Refs #103840
Refs #56815
Co-authored-by: ggoldani <ggoldani@users.noreply.github.com>
…l salvage

`hermes sessions recover --allow-partial` walks damaged tables by rowid range
and falls back to an exact-rowid lookup for the last cell of a broken range.
A phantom row produced by page damage (a `sessions` row with a NULL
`started_at`, an `async_delegations` row with a NULL `state`) reads fine from
the source but violates the destination's NOT NULL constraint, and the
resulting sqlite3.IntegrityError escaped the exact-lookup boundary and aborted
the whole recovery — losing every healthy row behind it.

Catch IntegrityError at that boundary, count it under
`destination_rejected_rows` and record the rowid as a skipped singleton
("destination constraint rejected row: ...") so the run completes, verifies,
and the report shows exactly which rows were dropped.

Live repro (real fixture: source schema's NOT NULL relaxed via
writable_schema, phantom `sessions` row inserted): BEFORE
sqlite3.IntegrityError "NOT NULL constraint failed: sessions.started_at" at
session_recovery.py recover_exact_rowid; AFTER status=partial copied=3
destination_rejected_rows=1, verified=True. Reporter @i8ei confirmed the same
patch recovers the field database (22 sessions / 2,248 messages) in #102240.

Salvaged from PR #91413 (rebased onto the _RowidRangeSalvage refactor; test
reduced to one invariant).

Refs #102240
Reported-by: i8ei
…INT64 domain

When the leftmost (or rightmost) leaf of a table b-tree is damaged, the edge
probe `SELECT rowid ... ORDER BY rowid ASC LIMIT 1` walks the table tree and
raises, and _salvage_rowid_bounds fell back to INT64_MIN. The gallop from the
surviving edge cannot cap that side either (every probe crosses the damaged
leaf), so bisection burned the entire 10,000-query budget moving the bound
inward by a few thousand rowids out of 9.2e18 and the table was lost — a
4-row gateway_routing table in #98050, sessions + session_model_usage in
#100313.

`SELECT min(rowid), max(rowid)` is answered by the planner from any covering
index (every Hermes table has at least the PRIMARY KEY autoindex) without
touching the damaged leaf, which is exactly what the reporter verified by
hand. Ask it for the missing edge(s) first; only when it fails too does the
domain fallback + gallop run as before. Reported under `aggregate_edges` so
recovery.json still shows how the bound was obtained.

Live repro (real fixture: leftmost `sessions` leaf cell count overwritten,
400 rows): BEFORE bounds low=-9223372036854775808 copied=0 range_queries=10000
query_limit_reached=True status=failed; AFTER low=1 high=400 copied=391
range_queries=40 status=partial (only the damaged leaf's rows are lost).

Refs #98050
Refs #100313
Reported-by: Ace-Kelly
Corroborated-by: Proff506
…ename

The demote enumeration (name LIKE 'messages_fts_%') sweeps the
messages_fts_cjk vtable and its shadow tables into the fts_v22_trash_*
renames. Renaming the cjk vtable cascades to its shadow tables and breaks
the vtable constructor chain, so 'hermes sessions optimize-storage'
aborts with 'vtable constructor failed: messages_fts_cjk' on every DB
that carries both a legacy inline FTS layout and an established cjk
index (#103647). The cjk family is an independent v23+ index, not part
of the demoted legacy layout: skip it in the enumeration.
Review feedback on #103657: the sibling clauses in the same statement
declare ESCAPE, and the trash enumeration two blocks up escapes its
underscores via replace. Use the same escaped ESCAPE form so every
underscore in the statement is a literal match instead of a
single-character wildcard. Behavior on the fixed schema is unchanged
(same enumeration split); this is consistency plus defense against
future lookalike table names.

Also reword the exclusion comment to the verified failure mechanism:
fts5 xRename renames the whole shadow family in one step, so sweeping
the cjk vtable aborts the loop on the next shadow entry and drags the
_config table (read by the vtable constructor) into the trash family.
…nd_found lane

A SIGKILL mid-write (OOM killer, #106667) can leave state.db with a garbage
page-1 header. SQLite refuses the file outright ("file is not a database",
SQLITE_NOTADB) and the sqlite3 shell's .recover opens the file like any other
client, so the lost_and_found lane failed with the same rc=26 twice although
every data page after the header survived. The #106587 quarantine now
preserves such a file as state.db.notadb-<ts>-<pid>.bak; this makes that
preserved file recoverable with `hermes sessions recover --source <bak>
--allow-partial`.

When both .recover attempts fail with "not a database", zero the 100-byte
header of the lane's private snapshot copy and rerun them. .recover trips
only on the magic check and infers page size and layout from the pages
themselves, so a zeroed header is enough; a spliced donor header (the PR's
original mechanism) instead advertises a database size / freelist that
contradicts the file and yields "database disk image is malformed" on a
direct open — verified live on a 139-page fixture, which also showed the
zeroed header recovers 60/60 sessions and 300/300 messages whether the
damage covers 100 bytes or the whole first page. The user's file is never
written; the report carries `sqlite3_cli.header_zeroed` and a warning about
the WAL boundary.

Live repro (sqlite3 shell 3.53.1 on PATH, header overwritten with random
bytes): BEFORE "page-level .recover salvage failed: ... file is not a
database (26)"; AFTER "Recovered 60 sessions and 300 messages", source md5
unchanged.

Salvaged from PR #102808 (intent; trimmed from 302 to ~40 source LOC by
dropping the donor-header/page-size sweep and the redundant preopen probe —
the shell's own refusal is the detector). Independent review on the PR by
@strzhao.

Refs #106667
Refs #106587
Reported-by: TaoMasterCoder
Cross-referenced-by: kshitijk4poor
… too

optimize_fts() and vacuum() already refuse to run against a quarantined
handle (_db_corrupt / _db_replaced / _db_wal_generation_lost): both would
rewrite index/file pages in place, turning contained, diagnosable
corruption into an amplified one. rebuild_fts() never got the same guard,
despite being the more destructive of the two ("discards and recreates the
index data entirely", per its own docstring, vs. optimize_fts's segment
merge).

It's also independently reachable outside _execute_write's own quarantine
check: gateway/session_transcript.py's _rebuild_fts_once() calls
db.rebuild_fts() directly from the FTS-corruption transcript-retry path,
with no quarantine check of its own (only a WAL split-brain / foreign-holder
check, a different concern). A quarantined handle hitting that retry path
would run a full FTS rebuild — and commit it — on a corrupt, replaced, or
split-WAL-generation file.

Add the same self._raise_if_db_corrupt()/self._raise_if_db_replaced() pair
optimize_fts() already has, at the top of rebuild_fts(), before it enters
the cross-process rebuild admission.
…file damage

classify_persistence_error bucketed every _DB_CORRUPTION_MARKERS hit as "corrupt",
so an error SQLite itself scoped to the FTS5 index layer (SQLITE_CORRUPT_VTAB, or an
`fts5: corrupt structure record for table "messages_fts"` report) that escaped the
write path — the detach in _enter_fts_fail_open refused (generation/lock check),
or a read/search path with no fail-open at all — reached the turn boundary and the
gateway startup notice as structural corruption: the turn ended with `.recover` /
restore-backup advice on a file whose canonical tables were provably healthy.

One provenance rule, hermes_state_errors.is_fts_scoped_corruption_error, now feeds
both the write-repair gate (SessionDB._is_fts_write_corruption_error delegates to it,
so the gateway transcript retry inherits it) and the classifier: a known result code
outranks prose (only SQLITE_CORRUPT_VTAB is FTS-scoped; bare SQLITE_CORRUPT/NOTADB
and any contradictory code fail closed), and without a code the text must both carry
a corruption marker and name a messages_fts* object. The new "fts_index" cause
renders index-scoped guidance (doctor --fix / restart, do not run recovery) in the
turn explainer and the home-channel notice. The structural fail-close is untouched:
bare malformed / not-a-database still quarantine and still classify "corrupt".

Salvaged from PR #97843 (SulthanZahran1), trimmed: the quick_check-backed
"corrupt_unconfirmed" tier is dropped — on a live handle that just observed an
unscoped SQLITE_CORRUPT, PRAGMA quick_check on a damaged shadow b-tree raises rather
than reports on 3.53.1, so the probe could never downgrade the exact shape it was
built for, and a verdict that softens quarantine guidance on prose alone weakens the
fail-close. #97841 (Finn763) reached the same fts_index cause via text markers
only; its LIKE-degradation intent already lives in _search_messages_impl (_fts_stale).

Fixes #97794
Co-authored-by: finn763 <165816600+finn763@users.noreply.github.com>
…t to sessions recover

`hermes doctor` reported every write-health-probe failure as "state.db FTS write
corruption" and `--fix` ran the FTS repair ladder — rebuild, REINDEX, sqlite_master
surgery + VACUUM — on the damaged file. When the damage is structural (canonical
tables/indexes), none of those rungs can fix it, each one writes to the torn file in
place, and the operator is then told to "restore from the backup copy beside
state.db": a `.malformed-backup` that is a snapshot of the same corrupt image.
`hermes sessions recover`, the tool that actually rebuilds canonical rows into a
fresh file, was never mentioned (#88587; the 1.7 GB field incident lost days to it).

Discriminate before mutating. hermes_state_repair.integrity_damage_is_structural
maps `PRAGMA integrity_check` output onto the file: a `Tree N` id resolved through
sqlite_master.rootpage, an index named in `row N missing from index X`, or a
`Freelist:` line is structural unless the object is a Hermes-owned messages_fts*
table/shadow (full-matched, so a user lookalike such as archive_fts_data is never
swept into the rebuildable set). state_db_has_structural_damage runs it read-only on
a fresh connection; an integrity_check that RAISES under the walk (torn root page)
is structural too — no FTS-only fixture does that while sessions/messages read
cleanly. doctor's state check consults it first: structural damage becomes a
manual issue naming `hermes [-p <profile>] sessions recover --source <this db>
--inspect-only` (profile pinned, #105887) and explicitly warning off the
.malformed-backup; nothing is mutated and no backup is written. FTS-only damage
keeps the existing in-place repair path.

Verified against real fixtures: a torn `sessions` root page (before: "FTS write
corruption", --fix wrote a 1:1 malformed-backup and failed; after: structural,
recover guidance, no writes) and the 16-byte DEADBEEF messages_fts_data stomp
(still repaired in place via rebuild_fts).

Salvaged from PR #88604 (liuhao1024) onto the split doctor_state.py; the
classifier lives beside the repair ladder in hermes_state_repair so the ladder
itself can consult it next.

Fixes #88587
The recovery commands rendered on structural corruption — the turn explainer's
`session_persistence_failed`/corrupt body, the gateway's home-channel state.db
warning, and hermes_state_repair._persistent_repair_exhausted_error — already
interpolate the active profile's state.db path, but every `hermes ...` verb in them
was bare. A bare `hermes` follows the sticky `active_profile` file, so an operator
running the pasted `hermes doctor --fix` (or `hermes sessions recover` with a
relative source) from a named-profile incident could inspect or repair a different
profile's database (#105887).

hermes_constants.profile_cli_selector() renders `-p <name> ` for a named profile
home (default home and custom roots outside the profile tree render nothing: the
default is what a bare `hermes` already means, and a custom root is only reachable
via HERMES_HOME). Every command in the three guidance sites now carries it, and
the new `fts_index` guidance inherits the same interpolation.

Live check with HERMES_HOME=<root>/profiles/research and active_profile=other:
before `1. Run \`hermes doctor --fix\`` (targets "other"); after
`1. Run \`hermes -p research doctor --fix\`` and `hermes -p research sessions
recover --source <root>/profiles/research/state.db --inspect-only`.

Refs #105887
Reported-by: Cuttingwater
…IntegrityError

`_db_opens_cleanly` drove one probe row through the messages_fts* triggers
and rolled back. FTS5 only buffers that row in an in-memory segment until
commit, so the probe never wrote to `<fts>_idx`/`_data` and could not hit a
stale `messages_fts_trigram_idx` row waiting at the next segid — the class
where PRAGMA integrity_check, the FTS5 integrity-check command and MATCH all
report clean while every committed append fails with
`IntegrityError: constraint failed`. The probe also caught only
OperationalError; IntegrityError is a DatabaseError sibling, so even a
colliding probe would have escaped and been reported as healthy.

Now the probe issues `INSERT INTO <fts>(<fts>) VALUES('flush')` for every
FTS family inside the rolled-back transaction (capability / not-built errors
stay benign), catches sqlite3.DatabaseError, and always rolls back in a
finally. `hermes doctor` and `hermes sessions repair --check-only` surface
the corruption and `repair_state_db_schema` heals it via the FTS rebuild
strategy (verified with a real stale-segid fixture).

Refs #100227
Reported-by: #100227
…ealthy store

`hermes sessions repair --check-only` printed the corruption reason and
exited 0, so scripts and the console wrapper gating on the status read a
broken state.db as healthy. Return 1 from the CLI handler (main.py already
sys.exits a truthy return) and from the console handler, where
`_capture_output` turns the status into a ConsoleCommandError carrying the
printed reason.

Salvaged from PR #103321 (the check-only reporting part only; the probe
rewrite and connection-tracking changes were not taken). Refs #63386.
… of a traceback per poll

The dashboard polls /api/analytics/usage and /api/analytics/models every
few seconds. When state.db is malformed the read raised straight through
the handler, so uvicorn logged a full traceback at ERROR on every poll —
one fleet host wrote ~520K identical journal entries in 24 h.

Wrap both analytics handlers in `corrupt_store_as_status`: a corrupt-image
sqlite3.DatabaseError (is_malformed_db_error) becomes a 503 with an
explicit `state_db_corrupt` payload pointing at `hermes doctor`, and the
warning is gated per store path via `{path: monotonic}` (>=300 s), then
debug. Busy/locked and every other error propagate unchanged, and the file
is never renamed or quarantined from the dashboard — repair stays with
`hermes doctor` / `hermes sessions repair`. `_session_db_path_for_profile`
is split out of `_open_session_db_for_profile` so the router can name the
store without opening it.

Refs #96591
Reported-by: #96591
…e poll test

hermes_state emits a once-per-process SQLite-version advisory on CI's linked
3.50.4, which caplog captured as a second WARNING. Scope the assertion to the
hermes_cli.web_server logger the router actually writes to.
gabrielcosi pushed a commit to gabrielcosi/home-ops that referenced this pull request Sep 12, 2026
…9.7 ➔ v2026.9.11) (#754)

This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [ghcr.io/gabrielcosi/hermes-agent](https://github.com/NousResearch/hermes-agent) | patch | `v2026.9.7` → `v2026.9.11` |

---

### Release Notes

<details>
<summary>NousResearch/hermes-agent (ghcr.io/gabrielcosi/hermes-agent)</summary>

### [`v2026.9.11`](https://github.com/NousResearch/hermes-agent/releases/tag/v2026.9.11): Hermes Agent v0.21.2 (v2026.9.11)

[Compare Source](NousResearch/hermes-agent@v2026.9.7...v2026.9.11)

##### Hermes Agent v0.21.2 (v2026.9.11) — The state.db Patch Release

**Release Date:** September 11, 2026

> Patch release. v0.21.0 shipped a large rewrite of the session store's connection handling, and for some installs it made `state.db` fragile: second writers cancelling each other's locks, healthy databases reported as corrupt, one bad row killing `sessions list`. This release closes that class and rolls up everything else that landed on `main` in the four days since v0.21.1.

##### About this release

Measured at commit `04dd80a977f40b05e5b2054111747af07a61886a`, the window since v0.21.1 contains **947 non-merge commits** across **1,869 changed files** (+182,504 / −15,564) and **312 merged PRs**. **140 contributors** appear in commits, co-author trailers, or salvage credits.

##### ✨ Highlights

##### state.db reliability campaign (six PRs, 44 issues closed)

If your `state.db` broke after 0.21.0, this is the release for you. Six PRs fix the root causes rather than the symptoms:

- **No more second writers.** Profile gateways wrote hosted-room state into the *root* `state.db` every 5 seconds; the dashboard opened a writable handle on startup; cron's lifecycle guard did a raw `open()` on a live database (which cancels the gateway's POSIX locks — the classic "how to corrupt SQLite" recipe); `doctor --fix` would checkpoint under a live holder. All four are gone: hosted rooms live in `shared-state.db`, the dashboard opens read-only first, the guard goes through the tracked connection registry, and `doctor --fix` refuses a checkpoint it can't prove is safe. ([#&#8203;108076](NousResearch/hermes-agent#108076) — salvage [#&#8203;103489](NousResearch/hermes-agent#103489) [@&#8203;RikETS](https://github.com/RikETS), [#&#8203;102682](NousResearch/hermes-agent#102682) [@&#8203;JoaoMarcos44](https://github.com/JoaoMarcos44), [#&#8203;108012](NousResearch/hermes-agent#108012) [@&#8203;Halldrix](https://github.com/Halldrix), [#&#8203;105428](NousResearch/hermes-agent#105428) [@&#8203;TaoMasterCoder](https://github.com/TaoMasterCoder))
- **Healthy WAL databases stop wedging.** OpenZFS `(deleted)` dentries and a `close()` racing an `append_message` both produced a sticky `DeletedWalGenerationError` on a perfectly good store; the read pool was handed out under an unconfirmed journal mode; a transient `disk I/O error` on WSL2 killed `get_session` on the first attempt; and a "state.db locked" banner was broadcast after the lock had already cleared. ([#&#8203;108082](NousResearch/hermes-agent#108082) — salvage [#&#8203;107411](NousResearch/hermes-agent#107411) [@&#8203;chelsealong](https://github.com/chelsealong), [#&#8203;105578](NousResearch/hermes-agent#105578) [@&#8203;ca-shrimp](https://github.com/ca-shrimp), [#&#8203;105711](NousResearch/hermes-agent#105711) [@&#8203;gaoanze888](https://github.com/gaoanze888), [#&#8203;106958](NousResearch/hermes-agent#106958) [@&#8203;nikkoxgonzales](https://github.com/nikkoxgonzales); co-authored [@&#8203;QDung210](https://github.com/QDung210), [@&#8203;fangliquanflq](https://github.com/fangliquanflq), [@&#8203;Sahilvishnaliya](https://github.com/Sahilvishnaliya))
- **FTS damage no longer kills your turn.** An error scoped to the full-text-search index was classified as whole-file corruption and fail-closed the conversation. It's now `fts_index`: search degrades, the index rebuilds later, the transcript store is untouched. Same PR: doctor names structural damage honestly instead of "FTS write corruption", the FTS write probe catches the stale-index shape that passed every check while every write failed, `.recover` output no longer fails startup on orphan FTS5 shadow tables, header-zeroed databases recover instead of being refused, and the dashboard analytics poller returns a 503 instead of 520K tracebacks a day. ([#&#8203;108130](NousResearch/hermes-agent#108130) — salvage [#&#8203;97843](NousResearch/hermes-agent#97843) [@&#8203;SulthanZahran1](https://github.com/SulthanZahran1) + [#&#8203;97841](NousResearch/hermes-agent#97841) [@&#8203;Finn763](https://github.com/Finn763), [#&#8203;88604](NousResearch/hermes-agent#88604) [#&#8203;56824](NousResearch/hermes-agent#56824) [#&#8203;103657](NousResearch/hermes-agent#103657) [@&#8203;liuhao1024](https://github.com/liuhao1024), [#&#8203;106890](NousResearch/hermes-agent#106890) [@&#8203;nftpoetrist](https://github.com/nftpoetrist), [#&#8203;103321](NousResearch/hermes-agent#103321) [@&#8203;jangomango76](https://github.com/jangomango76), [#&#8203;91413](NousResearch/hermes-agent#91413) [@&#8203;leegunwoo98](https://github.com/leegunwoo98), [#&#8203;102808](NousResearch/hermes-agent#102808) [@&#8203;TaoMasterCoder](https://github.com/TaoMasterCoder))
- **One corrupt row no longer kills `sessions list`, export, or insights.** A TEXT timestamp or a `1e30` epoch used to crash the whole listing; malformed marker JSON crashed `json_extract`; more than 999 ids crashed bulk delete/prune. One `coerce_epoch()` helper on every reader (bad rows render `?` with a WARNING naming the session), a `json_valid` guard, IN-list chunking, and batched export hydration. ([#&#8203;108086](NousResearch/hermes-agent#108086) — salvage [#&#8203;106071](NousResearch/hermes-agent#106071) [@&#8203;Xipong](https://github.com/Xipong), [#&#8203;101726](NousResearch/hermes-agent#101726) [@&#8203;efe-arv](https://github.com/efe-arv), [#&#8203;94701](NousResearch/hermes-agent#94701) [@&#8203;liuhao1024](https://github.com/liuhao1024), [#&#8203;102679](NousResearch/hermes-agent#102679) [@&#8203;mssteuer](https://github.com/mssteuer), [#&#8203;100658](NousResearch/hermes-agent#100658) [@&#8203;Mi55ed](https://github.com/Mi55ed))
- **Sessions never bind to or read another profile's database.** The Desktop launch backend could pin itself to the wrong profile's `state.db` under a HERMES\_HOME override race; `session_search` by bare ID silently scanned every profile and returned someone else's transcript; recovery guidance pointed at the wrong file; profile delete kept a handle open (WinError 32). ([#&#8203;108074](NousResearch/hermes-agent#108074) — salvage [#&#8203;102534](NousResearch/hermes-agent#102534) [@&#8203;HexLab98](https://github.com/HexLab98), [#&#8203;106975](NousResearch/hermes-agent#106975) [@&#8203;Sora-bluesky](https://github.com/Sora-bluesky))
- **Opening state.db no longer takes the write lock when nothing needs writing.** A one-shot `hermes` process opening the store behind a busy gateway stalled 4–20 s and then failed with "database is locked". Now 0.01 s. ([#&#8203;108067](NousResearch/hermes-agent#108067) — salvage [#&#8203;106751](NousResearch/hermes-agent#106751) [@&#8203;kshitijk4poor](https://github.com/kshitijk4poor), [#&#8203;101881](NousResearch/hermes-agent#101881) [@&#8203;jonpol01](https://github.com/jonpol01))

Also in the window from the same subsystem: a fresh `state.db` no longer publishes FTS tables before owning the rebuild lock ([#&#8203;106311](NousResearch/hermes-agent#106311)), a handle that lost its WAL generation no longer checkpoints stale frames at shutdown ([#&#8203;106315](NousResearch/hermes-agent#106315), [#&#8203;106840](NousResearch/hermes-agent#106840)), a clobbered first page is quarantined with its WAL instead of opened destructively ([#&#8203;106587](NousResearch/hermes-agent#106587)), WAL setup leaves an unverifiable database untouched ([#&#8203;106568](NousResearch/hermes-agent#106568)), and quarantined handles refuse VACUUM/FTS optimize ([#&#8203;106343](NousResearch/hermes-agent#106343), [#&#8203;106349](NousResearch/hermes-agent#106349)). Most of these salvaged community diagnoses by [@&#8203;kshitijk4poor](https://github.com/kshitijk4poor).

##### Multi-profile isolation hardening

A cluster of fixes for installs running several profiles under one gateway (multiplex): secondary-profile bots no longer inherit the default profile's allow-lists ([#&#8203;107616](NousResearch/hermes-agent#107616)), adapters no longer send credentials to the default profile's host ([#&#8203;107617](NousResearch/hermes-agent#107617)), stdio MCP servers no longer receive the default profile's vault secrets ([#&#8203;107630](NousResearch/hermes-agent#107630)), `MEDIA:` delivery can no longer attach another profile's `.env` / `auth.json` / `state.db` ([#&#8203;107609](NousResearch/hermes-agent#107609)), Feishu drive callbacks and `/p/<profile>/` webhook replies stay on the routed profile ([#&#8203;107620](NousResearch/hermes-agent#107620), [#&#8203;107626](NousResearch/hermes-agent#107626)), and secondary profiles no longer get a sibling's Nous bearer from per-process memos ([#&#8203;107611](NousResearch/hermes-agent#107611)).

##### Desktop backend spawn storms are over

Bot Mode used to spawn or dial one backend per profile on launch and on every roster tick, hovering the Bots roster spawned a backend per row, and profile switches could spawn a duplicate primary. ([#&#8203;108069](NousResearch/hermes-agent#108069), [#&#8203;108107](NousResearch/hermes-agent#108107), [#&#8203;108118](NousResearch/hermes-agent#108118), [#&#8203;108134](NousResearch/hermes-agent#108134), [#&#8203;107969](NousResearch/hermes-agent#107969), [#&#8203;108112](NousResearch/hermes-agent#108112) — salvage [#&#8203;102512](NousResearch/hermes-agent#102512), [#&#8203;103634](NousResearch/hermes-agent#103634), [#&#8203;103399](NousResearch/hermes-agent#103399), [#&#8203;107997](NousResearch/hermes-agent#107997) and others by [@&#8203;kshitijk4poor](https://github.com/kshitijk4poor))

##### Password-blind credential vault

The agent can now sign in, pay, and fill addresses from 1Password, Bitwarden, or the local Hermes vault without ever seeing a secret; two-factor codes come from a saved authenticator key or are asked for in the user's UI ([#&#8203;106480](NousResearch/hermes-agent#106480), [#&#8203;107585](NousResearch/hermes-agent#107585)). Private git plugins install with the user's stored credentials ([#&#8203;106981](NousResearch/hermes-agent#106981)).

##### Plugin catalog and one Plugins page

A curated, SHA-pinned plugin index with CLI, admission CI, docs and dashboard ([#&#8203;69446](NousResearch/hermes-agent#69446)); Desktop gets one Plugins page owning agent + desktop plugins, install, catalog and per-commit pinning ([#&#8203;107212](NousResearch/hermes-agent#107212), [#&#8203;107314](NousResearch/hermes-agent#107314), [#&#8203;107321](NousResearch/hermes-agent#107321)); Radio ships as an opt-in SDK plugin ([#&#8203;107072](NousResearch/hermes-agent#107072)).

##### Nous free tier and guided first launch

Free inference and connectors out of the box with one command to sign in ([#&#8203;105258](NousResearch/hermes-agent#105258), [#&#8203;105260](NousResearch/hermes-agent#105260)), `/login` from a chat ([#&#8203;105261](NousResearch/hermes-agent#105261)), connector tools (Gmail, Linear, Notion, ...) searchable through `tool_search` ([#&#8203;106842](NousResearch/hermes-agent#106842)), and a guided first launch behind `HERMES_GUEST_ONBOARDING=1` ([#&#8203;107697](NousResearch/hermes-agent#107697), [#&#8203;107958](NousResearch/hermes-agent#107958), [#&#8203;107985](NousResearch/hermes-agent#107985), [#&#8203;108211](NousResearch/hermes-agent#108211)).

##### 🐛 Notable Bug Fixes

**Gateway & platforms**

- A bare `display:` key in config.yaml no longer crashes every gateway turn ([#&#8203;106305](NousResearch/hermes-agent#106305)); a queued-lane final refused by the platform is recorded and redelivered ([#&#8203;106316](NousResearch/hermes-agent#106316)); a stalled WebSocket send no longer blocks every later event ([#&#8203;106581](NousResearch/hermes-agent#106581)); the first turn no longer waits on the Python toolchain probe ([#&#8203;106556](NousResearch/hermes-agent#106556)).
- Telegram bots must @&#8203;mention when `bots_require_mention` is on, breaking bot-to-bot loops ([#&#8203;106534](NousResearch/hermes-agent#106534)); Matrix renders LaTeX ([#&#8203;106515](NousResearch/hermes-agent#106515)); Signal renders markdown tables ([#&#8203;106538](NousResearch/hermes-agent#106538)); WhatsApp replies to view-once messages keep their quote ([#&#8203;106541](NousResearch/hermes-agent#106541)); media-only replies report SUCCESS everywhere ([#&#8203;106557](NousResearch/hermes-agent#106557)).

**Providers & routing**

- `/model` and auxiliary auto never bill a provider you didn't select ([#&#8203;107366](NousResearch/hermes-agent#107366)); never auto-switch to a provider you have no credentials for ([#&#8203;107281](NousResearch/hermes-agent#107281)); Bedrock Claude/Converse/Mantle models survive `/model`, fallback and restore ([#&#8203;107621](NousResearch/hermes-agent#107621), [#&#8203;107658](NousResearch/hermes-agent#107658)); Bedrock Guardrails enforced ([#&#8203;107815](NousResearch/hermes-agent#107815)).
- Codex: patch-budget image 400 shrinks and retries ([#&#8203;106525](NousResearch/hermes-agent#106525)); unentitled primary + fallback no longer oscillate ([#&#8203;106549](NousResearch/hermes-agent#106549)); Azure Foundry replayed-reasoning rejection classified and pruned ([#&#8203;106718](NousResearch/hermes-agent#106718) [@&#8203;erosika](https://github.com/erosika)). MCP OAuth refresh no longer erases the refresh token ([#&#8203;106185](NousResearch/hermes-agent#106185)). Anthropic clients send exactly one credential ([#&#8203;107978](NousResearch/hermes-agent#107978)).
- DeepSeek V4.1 Flash on Nous Portal and OpenRouter pickers ([#&#8203;107489](NousResearch/hermes-agent#107489)); GPT Image 2.5 via OpenAI and FAL ([#&#8203;105988](NousResearch/hermes-agent#105988)); Opus 5 / Fable 5.1 on the native Anthropic picker ([#&#8203;106636](NousResearch/hermes-agent#106636) [@&#8203;xxxigm](https://github.com/xxxigm)).

**Agent loop & compression**

- One blocked periodic callback no longer stalls lease refresh ([#&#8203;106308](NousResearch/hermes-agent#106308)); a mid-turn `/steer` is persisted as its own user row ([#&#8203;106317](NousResearch/hermes-agent#106317), [#&#8203;106344](NousResearch/hermes-agent#106344)); local-inference memory-ceiling rejections back off instead of compressing history ([#&#8203;106307](NousResearch/hermes-agent#106307)); context-overflow after partial streaming ends the turn ([#&#8203;106567](NousResearch/hermes-agent#106567)); length continuation stops when the prompt filled the window ([#&#8203;106571](NousResearch/hermes-agent#106571)); compression no longer times out silently on aux retries ([#&#8203;106866](NousResearch/hermes-agent#106866)); `model_thresholds` keys can be provider-scoped ([#&#8203;108061](NousResearch/hermes-agent#108061)).
- Surface switch (Desktop↔TUI) no longer rebuilds the system prompt and busts the prompt cache ([#&#8203;105844](NousResearch/hermes-agent#105844)); CLI keeps the `api_content` sidecar so the cache survives an early persist ([#&#8203;105842](NousResearch/hermes-agent#105842)).

**CLI, TUI & Desktop**

- `hermes -z --resume` continues the session ([#&#8203;106313](NousResearch/hermes-agent#106313)); Shift+letter and Cmd+Shift+Z work on extended-key terminals ([#&#8203;90674](NousResearch/hermes-agent#90674) [@&#8203;francip](https://github.com/francip), [#&#8203;105493](NousResearch/hermes-agent#105493)); `browser_exec` timeout kills the whole process tree ([#&#8203;106589](NousResearch/hermes-agent#106589)); update checks poll the GitHub API once a day instead of git-fetching every 30 min ([#&#8203;107648](NousResearch/hermes-agent#107648)); `hermes update` names the real cause and can't hang on a stalled fetch ([#&#8203;108053](NousResearch/hermes-agent#108053)).
- Desktop: UI language survives the update relaunch ([#&#8203;106476](NousResearch/hermes-agent#106476)), expired OAuth grants get a one-click re-sign-in ([#&#8203;106965](NousResearch/hermes-agent#106965)), HUD mode shows the transcript again and always gives the window back ([#&#8203;107491](NousResearch/hermes-agent#107491), [#&#8203;107423](NousResearch/hermes-agent#107423)), the backend exits when its Desktop parent dies ([#&#8203;107977](NousResearch/hermes-agent#107977)), Windows updates stop reporting false failures ([#&#8203;106175](NousResearch/hermes-agent#106175), [#&#8203;107183](NousResearch/hermes-agent#107183)), WSLg renders on the Windows GPU ([#&#8203;106528](NousResearch/hermes-agent#106528)), Telegram quick setup with QR ported from the dashboard ([#&#8203;107242](NousResearch/hermes-agent#107242)), and \~60 more Desktop fixes largely from [@&#8203;OutThisLife](https://github.com/OutThisLife) and [@&#8203;kshitijk4poor](https://github.com/kshitijk4poor).

**Cron & Kanban**

- An off-tick "run now" no longer cancels the next scheduled run ([#&#8203;106306](NousResearch/hermes-agent#106306)); a killed manual run no longer blocks the next one for 5 minutes ([#&#8203;106733](NousResearch/hermes-agent#106733)); a one-shot changed to recurring keeps firing ([#&#8203;106532](NousResearch/hermes-agent#106532)); unpinned jobs run on their creation-snapshot model ([#&#8203;106499](NousResearch/hermes-agent#106499)); `--clone-all` no longer copies cron jobs ([#&#8203;106478](NousResearch/hermes-agent#106478)); `kanban promote` refuses undone parents ([#&#8203;106550](NousResearch/hermes-agent#106550)); `kanban_request_review` rejects unknown reviewer profiles ([#&#8203;106547](NousResearch/hermes-agent#106547)).

**Tools & memory**

- A stdio MCP server dying mid-call no longer replays the tool call ([#&#8203;106546](NousResearch/hermes-agent#106546)); a skills-only background review can no longer delete memory entries ([#&#8203;106310](NousResearch/hermes-agent#106310)); mem0 memory no longer drops long turns ([#&#8203;106542](NousResearch/hermes-agent#106542)); `tool_search` returns nothing rather than five tools sharing one word ([#&#8203;106676](NousResearch/hermes-agent#106676)); remote NOPASSWD sudo no longer prompts ([#&#8203;107939](NousResearch/hermes-agent#107939)); RSS and Reddit reading no longer activate by default ([#&#8203;105873](NousResearch/hermes-agent#105873)).

**Housekeeping**

- `config.yaml` backups live in one bounded `backups/config/` dir ([#&#8203;106388](NousResearch/hermes-agent#106388)); `hermes backup` keeps the newest 3 zips ([#&#8203;106455](NousResearch/hermes-agent#106455)); `hermes setup --reset` backs up the real config ([#&#8203;106453](NousResearch/hermes-agent#106453)); `debug share` retention shrunk to 1 day on the dpaste fallback ([#&#8203;106531](NousResearch/hermes-agent#106531)).

##### 👥 Contributors

Thank you to the **140 contributors** whose commits, co-author trailers, and salvaged PRs landed in this window.

**state.db campaign — salvaged PR authors:** [@&#8203;RikETS](https://github.com/RikETS), [@&#8203;JoaoMarcos44](https://github.com/JoaoMarcos44), [@&#8203;Halldrix](https://github.com/Halldrix), [@&#8203;TaoMasterCoder](https://github.com/TaoMasterCoder), [@&#8203;chelsealong](https://github.com/chelsealong), [@&#8203;ca-shrimp](https://github.com/ca-shrimp), [@&#8203;gaoanze888](https://github.com/gaoanze888), [@&#8203;nikkoxgonzales](https://github.com/nikkoxgonzales), [@&#8203;QDung210](https://github.com/QDung210), [@&#8203;fangliquanflq](https://github.com/fangliquanflq), [@&#8203;Sahilvishnaliya](https://github.com/Sahilvishnaliya), [@&#8203;kshitijk4poor](https://github.com/kshitijk4poor), [@&#8203;jonpol01](https://github.com/jonpol01), [@&#8203;HexLab98](https://github.com/HexLab98), [@&#8203;Sora-bluesky](https://github.com/Sora-bluesky), [@&#8203;Xipong](https://github.com/Xipong), [@&#8203;efe-arv](https://github.com/efe-arv), [@&#8203;liuhao1024](https://github.com/liuhao1024), [@&#8203;mssteuer](https://github.com/mssteuer), [@&#8203;Mi55ed](https://github.com/Mi55ed), [@&#8203;SulthanZahran1](https://github.com/SulthanZahran1), [@&#8203;Finn763](https://github.com/Finn763), [@&#8203;nftpoetrist](https://github.com/nftpoetrist), [@&#8203;jangomango76](https://github.com/jangomango76), [@&#8203;leegunwoo98](https://github.com/leegunwoo98), [@&#8203;ggoldani](https://github.com/ggoldani).

**state.db campaign — issue reporters** (the forensics in these threads were often better than the fixes): [@&#8203;thedigitalcarpenterdad](https://github.com/thedigitalcarpenterdad), [@&#8203;Rroven](https://github.com/Rroven), [@&#8203;aoeman84](https://github.com/aoeman84), [@&#8203;StephanRosin](https://github.com/StephanRosin), [@&#8203;rubensandrade-sketch](https://github.com/rubensandrade-sketch), [@&#8203;wanliqin](https://github.com/wanliqin), [@&#8203;chenzheshushi-commits](https://github.com/chenzheshushi-commits), [@&#8203;CarlosReyesPena](https://github.com/CarlosReyesPena), [@&#8203;revazone](https://github.com/revazone), [@&#8203;reservassai-art](https://github.com/reservassai-art), [@&#8203;Cuttingwater](https://github.com/Cuttingwater), [@&#8203;soroush5](https://github.com/soroush5), [@&#8203;e-shizz](https://github.com/e-shizz), [@&#8203;shobhit-87labs](https://github.com/shobhit-87labs), [@&#8203;shivanathd](https://github.com/shivanathd), [@&#8203;hoelzl](https://github.com/hoelzl), [@&#8203;i8ei](https://github.com/i8ei), [@&#8203;Ace-Kelly](https://github.com/Ace-Kelly), [@&#8203;YinsenWANG](https://github.com/YinsenWANG), [@&#8203;zbabiarz](https://github.com/zbabiarz), [@&#8203;Sravanjangam](https://github.com/Sravanjangam), [@&#8203;0gl20shk0sbt36](https://github.com/0gl20shk0sbt36), [@&#8203;RChina](https://github.com/RChina), [@&#8203;bronder](https://github.com/bronder), [@&#8203;ccwssy](https://github.com/ccwssy), [@&#8203;bottenbenny](https://github.com/bottenbenny), and [@&#8203;Hitman117890](https://github.com/Hitman117890) whose Discord report kicked the campaign off.

**Everyone in the window (alphabetical):** [@&#8203;0genlab](https://github.com/0genlab), [@&#8203;0xalydev](https://github.com/0xalydev), [@&#8203;100yenadmin](https://github.com/100yenadmin), [@&#8203;1052326311](https://github.com/1052326311), [@&#8203;686f6c61](https://github.com/686f6c61), [@&#8203;69k4xmdfm2-blip](https://github.com/69k4xmdfm2-blip), [@&#8203;abundantbeing](https://github.com/abundantbeing), [@&#8203;Adolanium](https://github.com/Adolanium), [@&#8203;Ahmett101](https://github.com/Ahmett101), [@&#8203;albert748](https://github.com/albert748), [@&#8203;AlexxRussell](https://github.com/AlexxRussell), [@&#8203;alt-glitch](https://github.com/alt-glitch), [@&#8203;auroracapital](https://github.com/auroracapital), [@&#8203;austinpickett](https://github.com/austinpickett), [@&#8203;babatorik](https://github.com/babatorik), [@&#8203;Bartok9](https://github.com/Bartok9), [@&#8203;benbarclay](https://github.com/benbarclay), [@&#8203;bennybuoy](https://github.com/bennybuoy), [@&#8203;brian717](https://github.com/brian717), [@&#8203;briandevans](https://github.com/briandevans), [@&#8203;buihongduc132](https://github.com/buihongduc132), [@&#8203;ca-shrimp](https://github.com/ca-shrimp), [@&#8203;cervantesh](https://github.com/cervantesh), [@&#8203;Cesar-Azeredo](https://github.com/Cesar-Azeredo), [@&#8203;ChanPark03](https://github.com/ChanPark03), [@&#8203;chelsealong](https://github.com/chelsealong), [@&#8203;ckomma](https://github.com/ckomma), [@&#8203;ClintonEmok](https://github.com/ClintonEmok), [@&#8203;crazyief](https://github.com/crazyief), [@&#8203;ctaylor86](https://github.com/ctaylor86), [@&#8203;dalzio](https://github.com/dalzio), [@&#8203;DavidMetcalfe](https://github.com/DavidMetcalfe), [@&#8203;Drexuxux](https://github.com/Drexuxux), [@&#8203;edosulai](https://github.com/edosulai), [@&#8203;efe-arv](https://github.com/efe-arv), [@&#8203;emozilla](https://github.com/emozilla), [@&#8203;ericmaddox](https://github.com/ericmaddox), [@&#8203;erosika](https://github.com/erosika), [@&#8203;ethernet8023](https://github.com/ethernet8023), [@&#8203;everm1nd](https://github.com/everm1nd), [@&#8203;FalconOrtiz](https://github.com/FalconOrtiz), [@&#8203;fangliquanflq](https://github.com/fangliquanflq), [@&#8203;Finn763](https://github.com/Finn763), [@&#8203;FirmamentalSpring](https://github.com/FirmamentalSpring), [@&#8203;francip](https://github.com/francip), [@&#8203;g3org3yo](https://github.com/g3org3yo), [@&#8203;gaoanze888](https://github.com/gaoanze888), [@&#8203;ggoldani](https://github.com/ggoldani), [@&#8203;Halldrix](https://github.com/Halldrix), [@&#8203;haydster7](https://github.com/haydster7), [@&#8203;hbizi](https://github.com/hbizi), [@&#8203;helix4u](https://github.com/helix4u), [@&#8203;HexLab98](https://github.com/HexLab98), [@&#8203;huklaa](https://github.com/huklaa), [@&#8203;IAvecilla](https://github.com/IAvecilla), [@&#8203;infinitycrew39](https://github.com/infinitycrew39), [@&#8203;jahfaliabdulrahman-dev](https://github.com/jahfaliabdulrahman-dev), [@&#8203;jangomango76](https://github.com/jangomango76), [@&#8203;JoaoMarcos44](https://github.com/JoaoMarcos44), [@&#8203;jonpol01](https://github.com/jonpol01), [@&#8203;jwilson411](https://github.com/jwilson411), [@&#8203;KeyArgo](https://github.com/KeyArgo), [@&#8203;kokhlo](https://github.com/kokhlo), [@&#8203;KoNit-K](https://github.com/KoNit-K), [@&#8203;kshitijk4poor](https://github.com/kshitijk4poor), [@&#8203;kyssta-exe](https://github.com/kyssta-exe), [@&#8203;leegunwoo98](https://github.com/leegunwoo98), [@&#8203;lesterlxt](https://github.com/lesterlxt), [@&#8203;liuhao1024](https://github.com/liuhao1024), [@&#8203;Mabolla](https://github.com/Mabolla), [@&#8203;manuelschipper](https://github.com/manuelschipper), [@&#8203;MaxFreedomPollard](https://github.com/MaxFreedomPollard), [@&#8203;mearls0501](https://github.com/mearls0501), [@&#8203;mengyuyuan](https://github.com/mengyuyuan), [@&#8203;Mi55ed](https://github.com/Mi55ed), [@&#8203;MiseHinoha](https://github.com/MiseHinoha), [@&#8203;mjshorty](https://github.com/mjshorty), [@&#8203;mkrb84](https://github.com/mkrb84), [@&#8203;moisesvalero](https://github.com/moisesvalero), [@&#8203;moken627-hub](https://github.com/moken627-hub), [@&#8203;mssteuer](https://github.com/mssteuer), [@&#8203;nateEc](https://github.com/nateEc), [@&#8203;nftpoetrist](https://github.com/nftpoetrist), [@&#8203;nickseelert](https://github.com/nickseelert), [@&#8203;nikkoxgonzales](https://github.com/nikkoxgonzales), [@&#8203;notwitcheer](https://github.com/notwitcheer), [@&#8203;onuraycicek](https://github.com/onuraycicek), [@&#8203;outdog-hwh](https://github.com/outdog-hwh), [@&#8203;OutThisLife](https://github.com/OutThisLife), [@&#8203;philmossman](https://github.com/philmossman), [@&#8203;phuongvm](https://github.com/phuongvm), [@&#8203;pierrenode](https://github.com/pierrenode), [@&#8203;portavales](https://github.com/portavales), [@&#8203;PRATHAMESH75](https://github.com/PRATHAMESH75), [@&#8203;QDung210](https://github.com/QDung210), [@&#8203;rewbs](https://github.com/rewbs), [@&#8203;RikETS](https://github.com/RikETS), [@&#8203;romanovzky](https://github.com/romanovzky), [@&#8203;ryantuc](https://github.com/ryantuc), [@&#8203;Sahilvishnaliya](https://github.com/Sahilvishnaliya), [@&#8203;salch-cred](https://github.com/salch-cred), [@&#8203;sgarrand](https://github.com/sgarrand), [@&#8203;shannonsands](https://github.com/shannonsands), [@&#8203;simpolism](https://github.com/simpolism), [@&#8203;Solitud1nem](https://github.com/Solitud1nem), [@&#8203;somewheresy](https://github.com/somewheresy), [@&#8203;Sora-bluesky](https://github.com/Sora-bluesky), [@&#8203;sprmn24](https://github.com/sprmn24), [@&#8203;squevo](https://github.com/squevo), [@&#8203;StellarisW](https://github.com/StellarisW), [@&#8203;Stoltemberg](https://github.com/Stoltemberg), [@&#8203;SulthanZahran1](https://github.com/SulthanZahran1), [@&#8203;Svector-anu](https://github.com/Svector-anu), [@&#8203;szicely](https://github.com/szicely), [@&#8203;TaoMasterCoder](https://github.com/TaoMasterCoder), [@&#8203;teknium1](https://github.com/teknium1), [@&#8203;ten82e](https://github.com/ten82e), [@&#8203;thedavidweng](https://github.com/thedavidweng), [@&#8203;tkaufmann](https://github.com/tkaufmann), [@&#8203;Totoro-qaq](https://github.com/Totoro-qaq), [@&#8203;Tranquil-Flow](https://github.com/Tranquil-Flow), [@&#8203;tuancookiez-hub](https://github.com/tuancookiez-hub), [@&#8203;TurgutKural](https://github.com/TurgutKural), [@&#8203;ugoenyioha](https://github.com/ugoenyioha), [@&#8203;unsupportedpastels](https://github.com/unsupportedpastels), [@&#8203;victor-kyriazakos](https://github.com/victor-kyriazakos), [@&#8203;webtecnica](https://github.com/webtecnica), [@&#8203;wliu-dev](https://github.com/wliu-dev), [@&#8203;wukangcheng1994](https://github.com/wukangcheng1994), [@&#8203;Xipong](https://github.com/Xipong), [@&#8203;Xixiartemis](https://github.com/Xixiartemis), [@&#8203;xkam7ar](https://github.com/xkam7ar), [@&#8203;xxxigm](https://github.com/xxxigm), [@&#8203;yavarb](https://github.com/yavarb), [@&#8203;yoniebans](https://github.com/yoniebans), [@&#8203;Youssef](https://github.com/Youssef), [@&#8203;yoyodine-industries](https://github.com/yoyodine-industries), [@&#8203;yuanchenglu](https://github.com/yuanchenglu), [@&#8203;YuhGuan](https://github.com/YuhGuan), [@&#8203;Zeus-Deus](https://github.com/Zeus-Deus).

Also: Youssef.

##### Updating

- Existing install: `hermes update`
- Fresh install: `curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash`
- Managed deployments should update through their deployment tooling using the new tag.
- If your `state.db` was already damaged by 0.21.0/0.21.1: run `hermes doctor` first; it now names structural vs index damage correctly and points at `hermes sessions recover --inspect-only` (profile-pinned) when a rebuild isn't enough.

**Full Changelog:** [v2026.9.7...v2026.9.11](NousResearch/hermes-agent@v2026.9.7...v2026.9.11)

</details>

---

### Configuration

📅 **Schedule**: (in timezone Europe/Berlin)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about these updates again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate CLI](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC42NS4wIiwidXBkYXRlZEluVmVyIjoiNDQuNjUuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsicmVub3ZhdGUvY29udGFpbmVyIiwidHlwZS9wYXRjaCJdfQ==-->

Reviewed-on: https://git.xcd.dev/gabrielcosi/home-ops/pulls/754
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/sessions Session lifecycle, resume, persistence, history comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/cli CLI entry point, hermes_cli/, setup wizard comp/gateway Gateway runner, session dispatch, delivery P1 High — major feature broken, no workaround sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/bug Something isn't working

Projects

None yet

7 participants