Use HierarchicalSessionStore automatically when fork/snapshot operations are needed:
from praisonaiagents import Agentfrom praisonaiagents.session import get_hierarchical_session_store# Agent with hierarchical session supportagent = Agent( name="Assistant", memory={"session_id": "project-alpha"},)response = agent.start("Let's plan the migration")# Later - fork the conversation to explore alternativesstore = get_hierarchical_session_store()fork_id = store.fork_session("project-alpha")
2
Direct Store Usage
For advanced control, use the store directly:
from praisonaiagents.session import HierarchicalSessionStore# Create a hierarchical session storestore = HierarchicalSessionStore(session_dir="./sessions")# Create a sessionsession_id = store.create_session(title="My Project")# Add messagesstore.add_message(session_id, "user", "Hello!")store.add_message(session_id, "assistant", "Hi there!")# Create a snapshotsnapshot_id = store.create_snapshot(session_id, label="Checkpoint 1")# Continue working...store.add_message(session_id, "user", "Do something risky")# Revert to snapshot if neededstore.revert_to_snapshot(session_id, snapshot_id)
HierarchicalSessionStore is safe under concurrent multi-process and multi-instance use, with the same guarantees as DefaultSessionStore plus extended-field preservation:
File locking — fcntl.flock() on Unix, msvcrt.locking() on Windows
Reload under lock — every mutator (add_message, update_session_metadata, clear_session, set_agent_info, set_gateway_info) reloads the session from disk inside the FileLock before mutating, so two processes sharing the same session directory cannot drop each other’s messages.
Extended-field preservation — parent_id, children_ids, snapshots, and forked_from_message_id survive across update_session_metadata and clear_session calls (fixed in PR #1745). Earlier releases could silently wipe these fields on the next save after a metadata update.
This matters most for the PraisonAI UI host, where the UI calls add_message from the request thread while the agent’s auto_save writes assistant turns from a worker thread, and _persist_session_stats() runs update_session_metadata after every turn. PR #1745 closes the last remaining race in that path.
store = HierarchicalSessionStore()# Create main sessionmain_id = store.create_session(title="Main Branch")store.add_message(main_id, "user", "Start project")store.add_message(main_id, "assistant", "Project initialized")# Fork from message index 1fork_id = store.fork_session( main_id, from_message_index=1, title="Experimental Branch")# Fork has messages up to index 1# Can now diverge independentlystore.add_message(fork_id, "user", "Try experimental approach")
Take snapshots before potentially destructive changes:
# Before making experimental changessnap_id = store.create_snapshot(session_id, label="Before refactor")# ... make changes ...# Revert if neededstore.revert_to_snapshot(session_id, snap_id)
Clean up old sessions
Regularly remove unused sessions to save disk space:
# List all sessionssessions = store.list_sessions()for session in sessions: if should_delete(session): store.delete_session(session.session_id)
Use forks for experimentation
Fork sessions to explore different conversation paths:
# Main conversation pathmain_id = store.create_session(title="Main Discussion")store.add_message(main_id, "user", "Let's solve this problem")# Experimental pathexperiment_id = store.fork_session(main_id, title="Experimental Solution")store.add_message(experiment_id, "user", "What if we try a different approach?")