Skip to content

Feature: Tree-Structured Sessions with Branching & Navigation (inspired by Pi) #357

@teknium1

Description

@teknium1

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

  1. Append-only storage -- No mutations, only appends. This simplifies concurrency, prevents data loss, and makes the tree structure natural (parent pointers, not mutation).
  2. 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.
  3. 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.
  4. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions