Hermes Agent Sessions: Listing, Searching, Resuming, and Continuing
How Hermes persists sessions: what `hermes sessions list --source` shows, when to use --resume vs --continue, and how to grep old sessions via the SQLite store.

Hermes remembers what you talked about. The question is whether you can find it again. Sessions are not a black box — they sit in a SQLite database on disk, indexed by FTS5, scoped per profile, and addressable from the CLI by ID, by name, or by full-text query. Once you know where they live and which flag reaches them, three months of investigations stop disappearing into the dark.
This guide walks the four things you actually do with sessions: list them, search them, resume one by ID, and continue the most recent one by label. It closes with a verification checklist you can run on any host to confirm the flags match the version installed there.
Where sessions live
Every chat, cron run, Telegram message, and subagent call produces a row in a SQLite database. The default store is /opt/data/state.db; per-profile stores live at /opt/data/profiles/<profile>/state.db (e.g. /opt/data/profiles/senior-dev/state.db). Profiles are isolated — a session created as profile default is not visible to profile senior-dev, and --continue will not cross that boundary.
The sessions table is wide. Confirmed against a live Hermes v0.18.2 install, the columns that matter day-to-day are:
| Field | What it tells you |
|---|---|
id | The session ID — what you pass to --resume. Format varies by source: cron jobs use cron_<jobid>_<ts>, CLI chats use <ts>_<rand>, subagent runs use the parent format. |
source | Where the session came from: cli, tui, telegram, cron, subagent, unknown. Use this to filter. |
title | Human-readable label. Auto-set from the first user message; editable with hermes sessions rename. |
model | The model that ran the session (e.g. M3, claude-sonnet-4-5). |
started_at / ended_at | Unix epoch seconds. ended_at is null while a session is live. |
message_count | Cheap proxy for “how much happened here.” |
session_key | Stable per-conversation-thread key, useful for grouping follow-ups. |
user_id | The owning user (Telegram id, local user, etc.). |
archived | Soft-hide flag set by hermes sessions archive. |
cwd | The working directory the agent was in. |
There is no formal project or tag column. The closest things are source (channels), title (free-text, renameable), cwd (directory grouping), and session_key (conversation-thread grouping). If you want a project axis, you fake it by giving related sessions a shared prefix in title.
Sessions are cheap. Default retention is “everything since install” — there is no automatic expiry. hermes sessions stats on a working host reports tens of thousands of rows and a multi-gigabyte DB without complaint.
hermes sessions list
The canonical inventory. Two useful flags:
hermes sessions list --limit 20 # most recent 20, all sources
hermes sessions list --source telegram --limit 5 # last 5 telegram sessions
hermes sessions list --source cli --limit 50 # your own CLI work
Real output looks like this (anonymised session IDs):
Title Preview Last Active ID
──────────────────────────────────────────────────────────────────────────────────────────────────────────────
Daily cloudflared tunnel restart [IMPORTANT: You are running as a sched 4m ago cron_360e63a4ffc3_20260714_060048
Cloudflared Watchdog · Jul 14 [IMPORTANT: You are running as a sched 5m ago cron_f0d9b90e151e_20260714_014837
a2a-relay-watchdog · Jul 14 15 [IMPORTANT: You are running as a sched 16m ago cron_cdecc9e63bae_20260714_153208
hermes-agent-setup-v2 follow-up Walk me through the v2 setup wizard 2h ago 20260714_120412_a1b2c3
Four columns you will use constantly: Title, Preview, Last Active, ID. The preview is a snippet of the first user message — useful for skimming, useless for keyword search (use the DB directly for that).
The ID column is the only thing you need from this view. Copy it, paste it into --resume, move on.
Resume vs continue
Hermes exposes two distinct ways to re-enter a session, and they are not interchangeable. Both flags live on the top-level hermes command, so they work with any subcommand.
--resume SESSION joins an existing session by ID. Full prior context loads: every previous user message, every assistant reply, every tool call and its result, the original system prompt, and any in-session handoff state. You use this when context matters and you have the ID.
--continue [SESSION_NAME] is the soft, name-based entry. Without an argument it continues whatever session is currently active for your shell — useful for “actually, one more thing” follow-ups. With a name argument it continues the most recent session whose title matches that name, falling back to the most recent session of the matching source. Use this when you are iterating on the same active thread and do not want to copy IDs around.
Default (no flag) starts a fresh session. Use this whenever the topic changes — old context is a tax you pay on every turn.
Decision rule:
- Resume when you have a session ID, the prior context is load-bearing, and you may be resuming hours or days later.
- Continue when you are still in the same working session and want to add a turn without copying IDs.
- New chat when the topic changes, even by a little. Starting a fresh session is cheaper than dragging an irrelevant five-thousand-token history into a new question.
Searching by keyword
hermes sessions list does not search inside session content — its preview is just the first user message. For real keyword lookup, go straight to the SQLite store. Two approaches:
Pattern match on the title — fast, no setup:
sqlite3 /opt/data/state.db \
"SELECT id, title, started_at FROM sessions
WHERE title LIKE '%cloudflare%'
ORDER BY started_at DESC LIMIT 10;"
Full-text search on message content — Hermes ships with FTS5 indexes on messages and on a trigram fallback. If your sqlite3 build has FTS5 (most do), this works:
sqlite3 /opt/data/state.db \
"SELECT session_id, snippet(messages_fts, 0, '[', ']', '...', 8) AS hit
FROM messages_fts
WHERE messages_fts MATCH 'cloudflare OR tunnel'
ORDER BY rank LIMIT 10;"
If sqlite3 is not installed on the host, use Python — every Hermes host has it:
python3 -c "
import sqlite3
con = sqlite3.connect('/opt/data/state.db')
for row in con.execute(
\"SELECT session_id, snippet(messages_fts, 0, '[', ']', '...', 8) \"
\"FROM messages_fts WHERE messages_fts MATCH ? ORDER BY rank LIMIT 10\",
('cloudflare',)
):
print(row)
"
Filter by source to scope one channel at a time:
hermes sessions list --source cli --limit 50 # scope to your own CLI work
sqlite3 /opt/data/state.db \
"SELECT id, title FROM sessions WHERE source='cli' AND title LIKE '%deploy%';"
Once the search returns the ID you want, pass it to --resume.
Passing session IDs between calls
The session ID is the pivot. It appears in hermes sessions list, in the FTS5 search results, and in the logs of any session that handed off to another. To re-enter a specific session:
hermes chat --resume 20260714_120412_a1b2c3 -q "Continue from where we left off."
The session ID is also exposed through --pass-session-id, which is how one agent hands the current session reference to a child process — useful for subagent chains that need rolling context.
Long-running investigations earn a named session. Give it a stable title with hermes sessions rename <id> "Project X — week 3", then resume by that title when you come back tomorrow.
What not to do
- Don’t pass stale
--resumeIDs across a profile switch. Sessions are per-profile. A session created as profilesenior-devis invisible todefault, and--continuewill not bridge them. - Don’t conflate “no
--continue” with “no current session.” If you started a chat in TUI mode, closed it, then ran a CLI command without--continue, you started a new session, not resumed the old one. - Don’t rely on session
titlefor exact-match lookups. Titles are free-text and renameable. Use the session ID for anything programmatic; use title only for human skimming. - Don’t expect automatic expiry. Sessions persist until you
pruneorarchivethem. On a long-running host, runhermes sessions statsoccasionally and clean up if the DB is growing without bound.
Verification checklist
Run these on any Hermes host to confirm the flags match the version installed there.
| # | Command | What it proves |
|---|---|---|
| 1 | hermes --help shows --resume SESSION and --continue [SESSION_NAME] | Both flags exist at the top level. |
| 2 | hermes sessions --help lists list, browse, rename, export, delete, prune, archive, stats, optimize, repair | Subcommands are intact. |
| 3 | hermes sessions list --help shows --source SOURCE and --limit LIMIT | List filtering works. |
| 4 | hermes sessions stats | DB is reachable; row counts look sane. |
| 5 | sqlite3 /opt/data/state.db "SELECT id, title FROM sessions ORDER BY started_at DESC LIMIT 3;" | You can query the store directly. |
| 6 | hermes chat --resume <id> -q "test" (replace <id>) | Resume rejoins the named session. |
| 7 | hermes sessions list --source <your-source> | Source scoping filters as expected. |
Sources
- Hermes Agent sessions docs
- ABS companion: Hermes Agent Memory vs Skills
- ABS companion: Hermes Agent Setup v2
Last verified: 2026-07-14 against Hermes v0.18.2 (2026.7.7.2) and /opt/data/sessions.db.


Submit a take
Have a different read on this? Drop a comment below — your email isn't published, and I read every one. Nothing leaves the site until I approve it.