Overview
Inspired by the Pi coding agent (GitHub), this proposes adding tree-structured session history with branching and navigation to Hermes Agent. Pi's most innovative architectural choice is storing sessions as append-only trees rather than linear message lists, allowing users to branch conversations, explore multiple approaches, and navigate back to any previous point without losing history.
Currently, Hermes stores sessions as linear message sequences in SQLite, with session chaining via parent_session_id when context compression fires. This proposal would add in-session branching -- the ability to "go back" to an earlier point and explore a different direction, while preserving all branches.
This is independently valuable from Pi's other features and addresses a real pain point: when an agent goes down a wrong path, users currently must either undo step-by-step or start fresh, losing all context.
Research Findings
How Pi's Tree Sessions Work
Pi stores sessions as JSONL files with an append-only tree structure. Each entry has {id, parentId, timestamp, type} forming a DAG/tree:
- SessionHeader (
type: "session") -- file header with version, cwd
- SessionMessageEntry (
type: "message") -- user/assistant/tool messages
- CompactionEntry -- summarization marker with summary text
- BranchSummaryEntry -- summary of abandoned branch when navigating away
- CustomEntry -- extension state persistence (not sent to LLM)
- LabelEntry -- user bookmarks on entries
The "leaf pointer" (leafId) tracks the current position. Appending creates a child of the current leaf. Branching moves the leaf to an earlier entry without modifying history -- the next message simply becomes a new child of that earlier node.
buildSessionContext() walks from the current leaf to root, building the message array for the LLM. This means only the active branch is in context, but all branches are preserved.
Key commands:
/tree -- visual tree navigator, filter by message type or bookmarks
/fork -- extract a branch into a separate session file
- Labels -- user-defined bookmarks for navigation waypoints
The append-only design means compaction is non-destructive: a CompactionEntry marks where summarization occurred, and buildSessionContext() respects these boundaries.
Key Design Decisions
- Append-only storage -- No mutations, only appends. This simplifies concurrency, prevents data loss, and makes the tree structure natural (parent pointers, not mutation).
- Branch summaries -- When the user navigates away from a branch, Pi generates a summary of the abandoned branch. This preserves context awareness without keeping the full branch in the active context.
- Leaf pointer model -- Simple yet effective: the current position is just a pointer to a node. "Go back" just moves the pointer. "Branch" creates a new child.
- Labels as bookmarks -- Instead of complex checkpoint/snapshot systems, simple labels let users mark important points for later navigation.
Current State in Hermes Agent
Hermes currently uses linear session storage in SQLite (hermes_state.py):
sessions table: id, source, user_id, model, parent_session_id, timestamps, counters
messages table: session_id, role, content, tool metadata, timestamps
- Session chaining: when compression fires, old session ends and new one starts with
parent_session_id pointing back (linked list, not tree)
- FTS5 full-text search across messages
- Dual-write to JSONL during transition period
What's missing:
- No in-session branching -- history is strictly linear
- No way to "go back" to an earlier point and try a different approach
- When the agent goes down a wrong path, options are
/undo (step by step) or /new (lose everything)
- No visual tree navigation
- No bookmarks/labels on conversation points
Relevant existing files:
hermes_state.py -- SessionDB class, SQLite schema
gateway/session.py -- SessionStore, SessionEntry
run_agent.py -- AIAgent, _compress_context(), conversation loop
agent/context_compressor.py -- ContextCompressor class
Implementation Plan
Skill vs. Tool Classification
This is a core codebase change -- neither a skill nor a tool. It requires modifications to session storage, the conversation loop, and gateway commands. The changes touch fundamental data structures (how messages are stored and retrieved) that can't be expressed as instructions to the agent.
What We'd Need
- Schema migration adding tree metadata to messages (parent_id, branch_id, leaf tracking)
- Modified message retrieval that walks the active branch path
- New slash commands:
/branch, /tree, /label, /fork
- Branch-aware context compression
- Updated gateway integrations (messaging platforms need simplified branch interaction)
Phased Rollout
Phase 1: Foundation -- Branching Data Model
- Add
parent_msg_id column to messages table (nullable, NULL = linear/root)
- Add
branch_metadata table: session_id, active_leaf_id, labels
- Modify
get_messages_as_conversation() to walk parent chain from active leaf
- Add
/branch <message_number> command -- set active leaf to earlier message
- Add
/branches command -- list branch points in current session
- Backward compatible: existing linear sessions work unchanged (parent_msg_id = NULL means linear order)
Phase 2: Navigation & Visualization
- Add
/tree command -- ASCII tree visualization of session structure
- Add
/label <name> command -- bookmark current position
- Add
/goto <label> command -- jump to labeled position
- Branch summary generation when switching branches (summarize abandoned path)
- Integration with session search -- search across all branches
Phase 3: Platform Integration & Polish
- Simplified branch UX for messaging platforms (Telegram/Discord -- inline keyboard for branch selection)
/fork command -- extract a branch into a new session
- Branch-aware context compression (compress per-branch, not globally)
- Branch metadata in session export/import
- Consider: branch diff view (compare two branches)
Pros & Cons
Pros
- Recovery from wrong paths -- Users can branch back without losing work, solving a real frustration
- Exploratory workflows -- Try multiple approaches to the same problem, compare results
- Non-destructive history -- All conversation history preserved, nothing lost
- Backward compatible -- Phase 1 can be designed so existing linear sessions work unchanged
- Unique differentiator -- Few agents have this; Pi proved it works well in practice
Cons / Risks
- Schema complexity -- Tree traversal in SQLite adds query complexity vs. simple ordered messages
- UX challenge on messaging platforms -- Tree navigation is natural in CLI but harder in Telegram/Discord
- Context window interaction -- Branch-aware compression adds complexity to an already nuanced system
- Migration burden -- Existing sessions would need migration or dual-path handling
- Performance -- Tree traversal with large session histories could be slower than linear reads
Open Questions
- Should branch navigation be available on all platforms, or CLI-only initially?
- Should abandoned branches be auto-summarized (Pi's approach) or left as-is?
- How should branching interact with the existing
parent_session_id compression chain?
- Should there be a limit on branch depth/count to prevent unbounded growth?
- Should
/undo be reimplemented as "branch back one step" for consistency?
References
Overview
Inspired by the Pi coding agent (GitHub), this proposes adding tree-structured session history with branching and navigation to Hermes Agent. Pi's most innovative architectural choice is storing sessions as append-only trees rather than linear message lists, allowing users to branch conversations, explore multiple approaches, and navigate back to any previous point without losing history.
Currently, Hermes stores sessions as linear message sequences in SQLite, with session chaining via
parent_session_idwhen context compression fires. This proposal would add in-session branching -- the ability to "go back" to an earlier point and explore a different direction, while preserving all branches.This is independently valuable from Pi's other features and addresses a real pain point: when an agent goes down a wrong path, users currently must either undo step-by-step or start fresh, losing all context.
Research Findings
How Pi's Tree Sessions Work
Pi stores sessions as JSONL files with an append-only tree structure. Each entry has
{id, parentId, timestamp, type}forming a DAG/tree:type: "session") -- file header with version, cwdtype: "message") -- user/assistant/tool messagesThe "leaf pointer" (
leafId) tracks the current position. Appending creates a child of the current leaf. Branching moves the leaf to an earlier entry without modifying history -- the next message simply becomes a new child of that earlier node.buildSessionContext()walks from the current leaf to root, building the message array for the LLM. This means only the active branch is in context, but all branches are preserved.Key commands:
/tree-- visual tree navigator, filter by message type or bookmarks/fork-- extract a branch into a separate session fileThe append-only design means compaction is non-destructive: a
CompactionEntrymarks where summarization occurred, andbuildSessionContext()respects these boundaries.Key Design Decisions
Current State in Hermes Agent
Hermes currently uses linear session storage in SQLite (
hermes_state.py):sessionstable: id, source, user_id, model, parent_session_id, timestamps, countersmessagestable: session_id, role, content, tool metadata, timestampsparent_session_idpointing back (linked list, not tree)What's missing:
/undo(step by step) or/new(lose everything)Relevant existing files:
hermes_state.py--SessionDBclass, SQLite schemagateway/session.py--SessionStore,SessionEntryrun_agent.py--AIAgent,_compress_context(), conversation loopagent/context_compressor.py--ContextCompressorclassImplementation Plan
Skill vs. Tool Classification
This is a core codebase change -- neither a skill nor a tool. It requires modifications to session storage, the conversation loop, and gateway commands. The changes touch fundamental data structures (how messages are stored and retrieved) that can't be expressed as instructions to the agent.
What We'd Need
/branch,/tree,/label,/forkPhased Rollout
Phase 1: Foundation -- Branching Data Model
parent_msg_idcolumn to messages table (nullable, NULL = linear/root)branch_metadatatable: session_id, active_leaf_id, labelsget_messages_as_conversation()to walk parent chain from active leaf/branch <message_number>command -- set active leaf to earlier message/branchescommand -- list branch points in current sessionPhase 2: Navigation & Visualization
/treecommand -- ASCII tree visualization of session structure/label <name>command -- bookmark current position/goto <label>command -- jump to labeled positionPhase 3: Platform Integration & Polish
/forkcommand -- extract a branch into a new sessionPros & Cons
Pros
Cons / Risks
Open Questions
parent_session_idcompression chain?/undobe reimplemented as "branch back one step" for consistency?References
session.ts,session-context.ts