You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The Desktop sidebar reads other profiles through read-only SessionDB connections. Those connections intentionally cannot migrate a stale schema, but the listing queries unconditionally selected and ordered by newer columns such as last_activity_at and profile_name. The resulting per-profile query error was swallowed by the aggregation layer, leaving that profile's history absent until another code path opened the database writable and migrated it.
This change keeps read-only access non-mutating. It discovers the columns that actually exist, falls back to started_at when activity metadata is unavailable, and limits compact projections to live columns. Writable connections retain their existing migration behavior.
🐛 Bug fix (non-breaking change that fixes an issue)
✨ New feature (non-breaking change that adds functionality)
🔒 Security fix
📝 Documentation update
✅ Tests (adding or improving test coverage)
♻️ Refactor (no behavior change)
🎯 New skill (bundled or hub)
Changes Made
hermes_state.py: cache the live sessions schema for read-only connections and route listing queries through schema-aware recency handling.
hermes_state_common.py: allow recency SQL helpers to fall back when last_activity_at is unavailable.
hermes_state_portability.py: build schema-aware list and compact-projection queries.
tests/test_hermes_state.py: add a regression test covering rich, sidebar, gateway, search, and Telegram-style listing paths against a stale schema, while asserting the database is not migrated.
How to Test
Create a sessions database, then remove last_activity_at and profile_name to simulate a dormant profile created by an older Hermes version.
Open it through SessionDB(read_only=True) and exercise the supported session-listing paths.
Verify the stored session is returned and the schema remains unchanged.
Run scripts/run_tests.sh tests/test_hermes_state.py -q.
My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
I searched for existing PRs to make sure this isn't a duplicate
My PR contains only changes related to this fix/feature (no unrelated commits)
I've run pytest tests/ -q and all tests pass
I've added tests for my changes (required for bug fixes, strongly encouraged for features)
I've tested on my platform: Linux container, Python 3.11
The focused state suite passes: 179 passed. The full locked-dependency suite has six reproducible environment-sensitive failures outside the changed session-state code (macOS launcher harness, Termux/container detection, Camofox port assumptions, GNU sort probing, and Vercel doctor environment detection), so the full-suite checkbox is intentionally left unchecked.
Documentation & Housekeeping
I've updated relevant documentation (README, docs/, docstrings) — or N/A
I've updated cli-config.yaml.example if I added/changed config keys — or N/A
I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — or N/A
I've considered cross-platform impact (Windows, macOS) per the compatibility guide — or N/A
I've updated tool descriptions/schemas if I changed tool behavior — or N/A
The summary says the recency fallback is updated_at, but the sessions table has no such column — _sql_session_last_active falls back to started_at.
The sessions schema declares started_at REAL NOT NULL and no updated_at; the helper's docstring states "then fall back to started_at". As a wording that matches the code: "falls back to started_at when activity metadata is unavailable".
Checked against de15545 — the tip of fix/79029-stale-profile-session-schema when this was written — and 1be70d6, main at the same moment.
Read-only profile databases with older sessions schemas now remain usable: session listing, gateway listing, search, and Telegram listing avoid selecting columns that are absent until that profile is opened writable and migrated.
Security evidence:
trust boundary: read-only cross-profile state is queried without granting migration or write authority.
source/sink/invariant: live sessions columns are captured once and constrain both projected columns and recency SQL expressions.
current-main reproduction: an unmigrated database failed on all four affected list/search surfaces with missing-column errors.
PR-head or patch-replay validation: the exact head passed the legacy-schema regression and the complete Hermes state test file.
positive/negative cases: migrated and unmigrated schemas preserve the same session and message results without mutating the read-only database.
residual bypass search: all changed recency and compact-column call sites were checked for missing-column references.
reviewer validation: the state, portability, and common SQL helpers were reviewed; no cross-profile data or write-authority bypass was identified.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
area/profilesMulti-profile isolation, HERMES_HOME scopingarea/sessionsSession lifecycle, resume, persistence, historycomp/desktopElectron desktop app (apps/desktop/*)comp/gatewayGateway runner, session dispatch, deliveryP2Medium — degraded but workaround existssweeper:risk-compatibilitySweeper risk: may break existing users, config, migrations, defaults, or upgradessweeper:risk-session-stateSweeper risk: may lose/corrupt/mis-associate session or context statetype/bugSomething isn't working
5 participants
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Fixes cross-profile session listing for dormant profiles whose databases predate newer
sessionscolumns.The Desktop sidebar reads other profiles through read-only
SessionDBconnections. Those connections intentionally cannot migrate a stale schema, but the listing queries unconditionally selected and ordered by newer columns such aslast_activity_atandprofile_name. The resulting per-profile query error was swallowed by the aggregation layer, leaving that profile's history absent until another code path opened the database writable and migrated it.This change keeps read-only access non-mutating. It discovers the columns that actually exist, falls back to
started_atwhen activity metadata is unavailable, and limits compact projections to live columns. Writable connections retain their existing migration behavior.Related Issue
Fixes #79029
Type of Change
Changes Made
hermes_state.py: cache the livesessionsschema for read-only connections and route listing queries through schema-aware recency handling.hermes_state_common.py: allow recency SQL helpers to fall back whenlast_activity_atis unavailable.hermes_state_portability.py: build schema-aware list and compact-projection queries.tests/test_hermes_state.py: add a regression test covering rich, sidebar, gateway, search, and Telegram-style listing paths against a stale schema, while asserting the database is not migrated.How to Test
last_activity_atandprofile_nameto simulate a dormant profile created by an older Hermes version.SessionDB(read_only=True)and exercise the supported session-listing paths.scripts/run_tests.sh tests/test_hermes_state.py -q.Checklist
Code
fix(scope):,feat(scope):, etc.)pytest tests/ -qand all tests passThe focused state suite passes: 179 passed. The full locked-dependency suite has six reproducible environment-sensitive failures outside the changed session-state code (macOS launcher harness, Termux/container detection, Camofox port assumptions, GNU
sortprobing, and Vercel doctor environment detection), so the full-suite checkbox is intentionally left unchecked.Documentation & Housekeeping
docs/, docstrings) — or N/Acli-config.yaml.exampleif I added/changed config keys — or N/ACONTRIBUTING.mdorAGENTS.mdif I changed architecture or workflows — or N/AScreenshots / Logs