| --- |
| sidebar_position: 7 |
| title: "Sessions" |
| description: "Session persistence, resume, search, management, and per-platform session tracking" |
| --- |
| |
| # Sessions |
|
|
| Hermes Agent automatically saves every conversation as a session. Sessions enable conversation resume, cross-session search, and full conversation history management. |
|
|
| ## How Sessions Work |
|
|
| Every conversation β whether from the CLI, Telegram, Discord, Slack, WhatsApp, Signal, Matrix, or any other messaging platform β is stored as a session with full message history. Sessions are tracked in two complementary systems: |
|
|
| 1. **SQLite database** (`~/.hermes/state.db`) β structured session metadata with FTS5 full-text search |
| 2. **JSONL transcripts** (`~/.hermes/sessions/`) β raw conversation transcripts including tool calls (gateway) |
|
|
| The SQLite database stores: |
| - Session ID, source platform, user ID |
| - **Session title** (unique, human-readable name) |
| - Model name and configuration |
| - System prompt snapshot |
| - Full message history (role, content, tool calls, tool results) |
| - Token counts (input/output) |
| - Timestamps (started_at, ended_at) |
| - Parent session ID (for compression-triggered session splitting) |
|
|
| ### Session Sources |
|
|
| Each session is tagged with its source platform: |
|
|
| | Source | Description | |
| |--------|-------------| |
| | `cli` | Interactive CLI (`hermes` or `hermes chat`) | |
| | `telegram` | Telegram messenger | |
| | `discord` | Discord server/DM | |
| | `slack` | Slack workspace | |
| | `whatsapp` | WhatsApp messenger | |
| | `signal` | Signal messenger | |
| | `matrix` | Matrix rooms and DMs | |
| | `mattermost` | Mattermost channels | |
| | `email` | Email (IMAP/SMTP) | |
| | `sms` | SMS via Twilio | |
| | `dingtalk` | DingTalk messenger | |
| | `feishu` | Feishu/Lark messenger | |
| | `wecom` | WeCom (WeChat Work) | |
| | `weixin` | Weixin (personal WeChat) | |
| | `bluebubbles` | Apple iMessage via BlueBubbles macOS server | |
| | `qqbot` | QQ Bot (Tencent QQ) via Official API v2 | |
| | `homeassistant` | Home Assistant conversation | |
| | `webhook` | Incoming webhooks | |
| | `api-server` | API server requests | |
| | `acp` | ACP editor integration | |
| | `cron` | Scheduled cron jobs | |
| | `batch` | Batch processing runs | |
|
|
| ## CLI Session Resume |
|
|
| Resume previous conversations from the CLI using `--continue` or `--resume`: |
|
|
| ### Continue Last Session |
|
|
| ```bash |
| # Resume the most recent CLI session |
| hermes --continue |
| hermes -c |
| |
| # Or with the chat subcommand |
| hermes chat --continue |
| hermes chat -c |
| ``` |
|
|
| This looks up the most recent `cli` session from the SQLite database and loads its full conversation history. |
|
|
| ### Resume by Name |
|
|
| If you've given a session a title (see [Session Naming](#session-naming) below), you can resume it by name: |
|
|
| ```bash |
| # Resume a named session |
| hermes -c "my project" |
| |
| # If there are lineage variants (my project, my project #2, my project #3), |
| # this automatically resumes the most recent one |
| hermes -c "my project" # β resumes "my project #3" |
| ``` |
|
|
| ### Resume Specific Session |
|
|
| ```bash |
| # Resume a specific session by ID |
| hermes --resume 20250305_091523_a1b2c3d4 |
| hermes -r 20250305_091523_a1b2c3d4 |
| |
| # Resume by title |
| hermes --resume "refactoring auth" |
| |
| # Or with the chat subcommand |
| hermes chat --resume 20250305_091523_a1b2c3d4 |
| ``` |
|
|
| Session IDs are shown when you exit a CLI session, and can be found with `hermes sessions list`. |
|
|
| ### Conversation Recap on Resume |
|
|
| When you resume a session, Hermes displays a compact recap of the previous conversation in a styled panel before the input prompt: |
|
|
| <img className="docs-terminal-figure" src="/img/docs/session-recap.svg" alt="Stylized preview of the Previous Conversation recap panel shown when resuming a Hermes session." /> |
| <p className="docs-figure-caption">Resume mode shows a compact recap panel with recent user and assistant turns before returning you to the live prompt.</p> |
|
|
| The recap: |
| - Shows **user messages** (gold `β`) and **assistant responses** (green `β`) |
| - **Truncates** long messages (300 chars for user, 200 chars / 3 lines for assistant) |
| - **Collapses tool calls** to a count with tool names (e.g., `[3 tool calls: terminal, web_search]`) |
| - **Hides** system messages, tool results, and internal reasoning |
| - **Caps** at the last 10 exchanges with a "... N earlier messages ..." indicator |
| - Uses **dim styling** to distinguish from the active conversation |
|
|
| To disable the recap and keep the minimal one-liner behavior, set in `~/.hermes/config.yaml`: |
|
|
| ```yaml |
| display: |
| resume_display: minimal # default: full |
| ``` |
|
|
| :::tip |
| Session IDs follow the format `YYYYMMDD_HHMMSS_<8-char-hex>`, e.g. `20250305_091523_a1b2c3d4`. You can resume by ID or by title β both work with `-c` and `-r`. |
| ::: |
|
|
| ## Session Naming |
|
|
| Give sessions human-readable titles so you can find and resume them easily. |
|
|
| ### Auto-Generated Titles |
|
|
| Hermes automatically generates a short descriptive title (3β7 words) for each session after the first exchange. This runs in a background thread using a fast auxiliary model, so it adds no latency. You'll see auto-generated titles when browsing sessions with `hermes sessions list` or `hermes sessions browse`. |
|
|
| Auto-titling only fires once per session and is skipped if you've already set a title manually. |
|
|
| ### Setting a Title Manually |
|
|
| Use the `/title` slash command inside any chat session (CLI or gateway): |
|
|
| ``` |
| /title my research project |
| ``` |
|
|
| The title is applied immediately. If the session hasn't been created in the database yet (e.g., you run `/title` before sending your first message), it's queued and applied once the session starts. |
|
|
| You can also rename existing sessions from the command line: |
|
|
| ```bash |
| hermes sessions rename 20250305_091523_a1b2c3d4 "refactoring auth module" |
| ``` |
|
|
| ### Title Rules |
|
|
| - **Unique** β no two sessions can share the same title |
| - **Max 100 characters** β keeps listing output clean |
| - **Sanitized** β control characters, zero-width chars, and RTL overrides are stripped automatically |
| - **Normal Unicode is fine** β emoji, CJK, accented characters all work |
|
|
| ### Auto-Lineage on Compression |
|
|
| When a session's context is compressed (manually via `/compress` or automatically), Hermes creates a new continuation session. If the original had a title, the new session automatically gets a numbered title: |
|
|
| ``` |
| "my project" β "my project #2" β "my project #3" |
| ``` |
|
|
| When you resume by name (`hermes -c "my project"`), it automatically picks the most recent session in the lineage. |
|
|
| ### /title in Messaging Platforms |
|
|
| The `/title` command works in all gateway platforms (Telegram, Discord, Slack, WhatsApp): |
|
|
| - `/title My Research` β set the session title |
| - `/title` β show the current title |
|
|
| ## Session Management Commands |
|
|
| Hermes provides a full set of session management commands via `hermes sessions`: |
|
|
| ### List Sessions |
|
|
| ```bash |
| # List recent sessions (default: last 20) |
| hermes sessions list |
| |
| # Filter by platform |
| hermes sessions list --source telegram |
| |
| # Show more sessions |
| hermes sessions list --limit 50 |
| ``` |
|
|
| When sessions have titles, the output shows titles, previews, and relative timestamps: |
|
|
| ``` |
| Title Preview Last Active ID |
| ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
| refactoring auth Help me refactor the auth module please 2h ago 20250305_091523_a |
| my project #3 Can you check the test failures? yesterday 20250304_143022_e |
| β What's the weather in Las Vegas? 3d ago 20250303_101500_f |
| ``` |
|
|
| When no sessions have titles, a simpler format is used: |
|
|
| ``` |
| Preview Last Active Src ID |
| ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
| Help me refactor the auth module please 2h ago cli 20250305_091523_a |
| What's the weather in Las Vegas? 3d ago tele 20250303_101500_f |
| ``` |
|
|
| ### Export Sessions |
|
|
| ```bash |
| # Export all sessions to a JSONL file |
| hermes sessions export backup.jsonl |
| |
| # Export sessions from a specific platform |
| hermes sessions export telegram-history.jsonl --source telegram |
| |
| # Export a single session |
| hermes sessions export session.jsonl --session-id 20250305_091523_a1b2c3d4 |
| ``` |
|
|
| Exported files contain one JSON object per line with full session metadata and all messages. |
|
|
| ### Delete a Session |
|
|
| ```bash |
| # Delete a specific session (with confirmation) |
| hermes sessions delete 20250305_091523_a1b2c3d4 |
| |
| # Delete without confirmation |
| hermes sessions delete 20250305_091523_a1b2c3d4 --yes |
| ``` |
|
|
| ### Rename a Session |
|
|
| ```bash |
| # Set or change a session's title |
| hermes sessions rename 20250305_091523_a1b2c3d4 "debugging auth flow" |
| |
| # Multi-word titles don't need quotes in the CLI |
| hermes sessions rename 20250305_091523_a1b2c3d4 debugging auth flow |
| ``` |
|
|
| If the title is already in use by another session, an error is shown. |
|
|
| ### Prune Old Sessions |
|
|
| ```bash |
| # Delete ended sessions older than 90 days (default) |
| hermes sessions prune |
| |
| # Custom age threshold |
| hermes sessions prune --older-than 30 |
| |
| # Only prune sessions from a specific platform |
| hermes sessions prune --source telegram --older-than 60 |
| |
| # Skip confirmation |
| hermes sessions prune --older-than 30 --yes |
| ``` |
|
|
| :::info |
| Pruning only deletes **ended** sessions (sessions that have been explicitly ended or auto-reset). Active sessions are never pruned. |
| ::: |
|
|
| ### Session Statistics |
|
|
| ```bash |
| hermes sessions stats |
| ``` |
|
|
| Output: |
|
|
| ``` |
| Total sessions: 142 |
| Total messages: 3847 |
| cli: 89 sessions |
| telegram: 38 sessions |
| discord: 15 sessions |
| Database size: 12.4 MB |
| ``` |
|
|
| For deeper analytics β token usage, cost estimates, tool breakdown, and activity patterns β use [`hermes insights`](/docs/reference/cli-commands#hermes-insights). |
|
|
| ## Session Search Tool |
|
|
| The agent has a built-in `session_search` tool that performs full-text search across all past conversations using SQLite's FTS5 engine. |
|
|
| ### How It Works |
|
|
| 1. FTS5 searches matching messages ranked by relevance |
| 2. Groups results by session, takes the top N unique sessions (default 3) |
| 3. Loads each session's conversation, truncates to ~100K chars centered on matches |
| 4. Sends to a fast summarization model for focused summaries |
| 5. Returns per-session summaries with metadata and surrounding context |
|
|
| ### FTS5 Query Syntax |
|
|
| The search supports standard FTS5 query syntax: |
|
|
| - Simple keywords: `docker deployment` |
| - Phrases: `"exact phrase"` |
| - Boolean: `docker OR kubernetes`, `python NOT java` |
| - Prefix: `deploy*` |
|
|
| ### When It's Used |
|
|
| The agent is prompted to use session search automatically: |
|
|
| > *"When the user references something from a past conversation or you suspect relevant prior context exists, use session_search to recall it before asking them to repeat themselves."* |
|
|
| ## Per-Platform Session Tracking |
|
|
| ### Gateway Sessions |
|
|
| On messaging platforms, sessions are keyed by a deterministic session key built from the message source: |
|
|
| | Chat Type | Default Key Format | Behavior | |
| |-----------|--------------------|----------| |
| | Telegram DM | `agent:main:telegram:dm:<chat_id>` | One session per DM chat | |
| | Discord DM | `agent:main:discord:dm:<chat_id>` | One session per DM chat | |
| | WhatsApp DM | `agent:main:whatsapp:dm:<chat_id>` | One session per DM chat | |
| | Group chat | `agent:main:<platform>:group:<chat_id>:<user_id>` | Per-user inside the group when the platform exposes a user ID | |
| | Group thread/topic | `agent:main:<platform>:group:<chat_id>:<thread_id>` | Shared session for all thread participants (default). Per-user with `thread_sessions_per_user: true`. | |
| | Channel | `agent:main:<platform>:channel:<chat_id>:<user_id>` | Per-user inside the channel when the platform exposes a user ID | |
|
|
| When Hermes cannot get a participant identifier for a shared chat, it falls back to one shared session for that room. |
|
|
| ### Shared vs Isolated Group Sessions |
|
|
| By default, Hermes uses `group_sessions_per_user: true` in `config.yaml`. That means: |
|
|
| - Alice and Bob can both talk to Hermes in the same Discord channel without sharing transcript history |
| - one user's long tool-heavy task does not pollute another user's context window |
| - interrupt handling also stays per-user because the running-agent key matches the isolated session key |
|
|
| If you want one shared "room brain" instead, set: |
|
|
| ```yaml |
| group_sessions_per_user: false |
| ``` |
|
|
| That reverts groups/channels to a single shared session per room, which preserves shared conversational context but also shares token costs, interrupt state, and context growth. |
|
|
| ### Session Reset Policies |
|
|
| Gateway sessions are automatically reset based on configurable policies: |
|
|
| - **idle** β reset after N minutes of inactivity |
| - **daily** β reset at a specific hour each day |
| - **both** β reset on whichever comes first (idle or daily) |
| - **none** β never auto-reset |
|
|
| Before a session is auto-reset, the agent is given a turn to save any important memories or skills from the conversation. |
|
|
| Sessions with **active background processes** are never auto-reset, regardless of policy. |
|
|
| ## Storage Locations |
|
|
| | What | Path | Description | |
| |------|------|-------------| |
| | SQLite database | `~/.hermes/state.db` | All session metadata + messages with FTS5 | |
| | Gateway transcripts | `~/.hermes/sessions/` | JSONL transcripts per session + sessions.json index | |
| | Gateway index | `~/.hermes/sessions/sessions.json` | Maps session keys to active session IDs | |
|
|
| The SQLite database uses WAL mode for concurrent readers and a single writer, which suits the gateway's multi-platform architecture well. |
|
|
| ### Database Schema |
|
|
| Key tables in `state.db`: |
|
|
| - **sessions** β session metadata (id, source, user_id, model, title, timestamps, token counts). Titles have a unique index (NULL titles allowed, only non-NULL must be unique). |
| - **messages** β full message history (role, content, tool_calls, tool_name, token_count) |
| - **messages_fts** β FTS5 virtual table for full-text search across message content |
| |
| ## Session Expiry and Cleanup |
| |
| ### Automatic Cleanup |
| |
| - Gateway sessions auto-reset based on the configured reset policy |
| - Before reset, the agent saves memories and skills from the expiring session |
| - Opt-in auto-pruning: when `sessions.auto_prune` is `true`, ended sessions older than `sessions.retention_days` (default 90) are pruned at CLI/gateway startup |
| - After a prune that actually removed rows, `state.db` is `VACUUM`ed to reclaim disk space (SQLite does not shrink the file on plain DELETE) |
| - Pruning runs at most once per `sessions.min_interval_hours` (default 24); the last-run timestamp is tracked inside `state.db` itself so it's shared across every Hermes process in the same `HERMES_HOME` |
| |
| Default is **off** β session history is valuable for `session_search` recall, and silently deleting it could surprise users. Enable in `~/.hermes/config.yaml`: |
|
|
| ```yaml |
| sessions: |
| auto_prune: true # opt in β default is false |
| retention_days: 90 # keep ended sessions this many days |
| vacuum_after_prune: true # reclaim disk space after a pruning sweep |
| min_interval_hours: 24 # don't re-run the sweep more often than this |
| ``` |
|
|
| Active sessions are never auto-pruned, regardless of age. |
|
|
| ### Manual Cleanup |
|
|
| ```bash |
| # Prune sessions older than 90 days |
| hermes sessions prune |
| |
| # Delete a specific session |
| hermes sessions delete <session_id> |
| |
| # Export before pruning (backup) |
| hermes sessions export backup.jsonl |
| hermes sessions prune --older-than 30 --yes |
| ``` |
|
|
| :::tip |
| The database grows slowly (typical: 10-15 MB for hundreds of sessions) and session history powers `session_search` recall across past conversations, so auto-prune ships disabled. Enable it if you're running a heavy gateway/cron workload where `state.db` is meaningfully affecting performance (observed failure mode: 384 MB state.db with ~1000 sessions slowing down FTS5 inserts and `/resume` listing). Use `hermes sessions prune` for one-off cleanup without turning on the automatic sweep. |
| ::: |
|
|