Hermes State Internals
How to inspect and safely modify Hermes' on-disk SQLite state stores — when the GUI/sidebar shows something confusing ("my sessions disappeared", "which project owns this session", "where does X live") and the answer is in the databases, not a config flag.
This is the state-layer companion to inspecting-hermes-desktop-dom (live
DOM/CDP) and debugging-hermes-tui-commands (TUI code). Use those for runtime
UI; use this for the persisted data underneath.
Where the stores live
Resolve the real home from $HERMES_HOME (never hardcode ~/.hermes; on Windows
it's %LOCALAPPDATA%\hermes, e.g. C:\Users\<user>\AppData\Local\hermes).
| File | Holds |
|---|---|
state.db |
Canonical session store: sessions, messages (+ FTS), model usage, routing |
projects.db |
Desktop Projects (named workspaces): projects table |
kanban.db |
Kanban boards/tasks |
memories/ |
Memory + user-profile stores (separate dir, not in state.db) |
Hard rules before ANY write
- Back up first:
cp state.db state.db.pre-<change>-$(date +%Y%m%d_%H%M%S).bak - Inspect schema before assuming columns —
PRAGMA table_info(<table>). Schemas drift across versions; never trust a remembered column list. - The desktop app caches the DB — after a direct edit it may not reflect until the sidebar refreshes or the app restarts. Tell the user this.
- Prefer
hermesCLI / tools over hand-editing when a supported path exists. Direct SQL is the fallback when no CLI covers it.
The session→project mapping (the big one)
The desktop sidebar decides which Project a session belongs to by the session's
cwd column (falling back to git_repo_root): a session is filed under the
explicit project whose folder is the longest path-prefix of its cwd. Sessions
with cwd = NULL (or a cwd under no project folder) fall into the synthetic
"Home" bucket (__no_project__) and look "missing" from a project view —
they are NOT deleted, just unfiled.
Matching logic lives in apps/desktop/src/app/chat/sidebar/projects/workspace-groups.ts
(frontend overlay) and tui_gateway/project_tree.py (authoritative backend tree).
Windows path comparison is case- and separator-insensitive.
Practical consequence: creating a Project (project_create) does NOT
retroactively claim old sessions — they keep their old/NULL cwd. To move existing
sessions into a project, update their cwd to a path under the project folder.
See references/state-db-recipes.md for the exact schemas and the tested
session-migration SQL.
Pitfalls
sessionshas nolast_activecolumn — derive it viaMAX(messages.timestamp)per session.messagesusestimestamp, notcreated_at.- Don't conflate the desktop Files pane (right sidebar, shows the active project's folder tree) with the session list (left sidebar). "I see two files on the right" = Files pane; "my chats are gone" = session list / project filtering.
project_listreturning emptyprojects: []while the sidebar shows a project = you're querying a different profile/store than the desktop app, or the app hasn't synced. Check you're reading the sameprojects.db.