Session File Format
Sessions are stored as JSONL (JSON Lines) files. Each line is a JSON object with atype field. Session entries form a tree structure via id/parentId fields, enabling in-place branching without creating new files.
File Location
<path> is the working directory with / replaced by -.
Deleting Sessions
Sessions can be removed by deleting their.jsonl files under ~/.atomic/agent/sessions/ (legacy ~/.pi/agent/sessions/ may exist from older installs).
Atomic also supports deleting sessions interactively from /resume (select a session and press CTRL+D, then confirm). When available, Atomic uses the trash CLI to avoid permanent deletion.
Session Version
Sessions have a version field in the header:- Version 1: Linear entry sequence (legacy, auto-migrated on load)
- Version 2: Tree structure with
id/parentIdlinking - Version 3: Renamed
hookMessagerole tocustom(extensions unification)
Source Files
Source on GitHub (atomic):packages/coding-agent/src/core/session-manager.ts- Session entry types and SessionManagerpackages/coding-agent/src/core/messages.ts- Extended message types (BashExecutionMessage, CustomMessage, etc.)
@earendil-works/pi-ai and @earendil-works/pi-agent-core), not by separate packages/ai or packages/agent directories in this monorepo. For TypeScript definitions in your project, inspect node_modules/@bastani/atomic/dist/, node_modules/@earendil-works/pi-ai/dist/, and node_modules/@earendil-works/pi-agent-core/dist/.
Message Types
Session entries containAgentMessage objects. Understanding these types is essential for parsing sessions and writing extensions.
Content Blocks
Messages contain arrays of typed content blocks:Base Message Types (from @earendil-works/pi-ai)
Extended Message Types (from Atomic coding-agent)
AgentMessage Union
Entry Base
All entries (exceptSessionHeader) extend SessionEntryBase:
Entry Types
SessionHeader
First line of the file. Metadata only, not part of the tree (noid/parentId).
/fork, /clone, or newSession({ parentSession })):
SessionMessageEntry
A message in the conversation. Themessage field contains an AgentMessage.
ModelChangeEntry
Emitted when the user switches models mid-session.ThinkingLevelChangeEntry
Emitted when the user changes the thinking/reasoning level.CompactionEntry
Created when context is compacted. Stores a summary of earlier messages.details: Implementation-specific data (e.g.,{ readFiles: string[], modifiedFiles: string[] }for default, or custom data for extensions)fromHook:trueif generated by an extension,false/undefinedif Atomic-generated (legacy field name)
BranchSummaryEntry
Created when switching branches via/tree with an LLM generated summary of the left branch up to the common ancestor. Captures context from the abandoned path.
details: File tracking data ({ readFiles: string[], modifiedFiles: string[] }) for default, or custom data for extensionsfromHook:trueif generated by an extension,false/undefinedif Atomic-generated (legacy field name)
CustomEntry
Extension state persistence. Does NOT participate in LLM context.customType to identify your extension’s entries on reload.
CustomMessageEntry
Extension-injected messages that DO participate in LLM context.content: String or(TextContent | ImageContent)[](same as UserMessage)display:true= show in TUI with distinct styling,false= hiddendetails: Optional extension-specific metadata (not sent to LLM)
LabelEntry
User-defined bookmark/marker on an entry.label to undefined to clear a label.
SessionInfoEntry
Session metadata (e.g., user-defined display name). Set via/name command or pi.setSessionName() in extensions.
/resume) instead of the first message when set.
Tree Structure
Entries form a tree:- First entry has
parentId: null - Each subsequent entry points to its parent via
parentId - Branching creates new children from an earlier entry
- The “leaf” is the current position in the tree
Context Building
buildSessionContext() walks from the current leaf to the root, producing the message list for the LLM:
- Collects all entries on the path
- Extracts current model and thinking level settings
- If a
CompactionEntryis on the path:- Emits the summary first
- Then messages from
firstKeptEntryIdto compaction - Then messages after compaction
- Converts
BranchSummaryEntryandCustomMessageEntryto appropriate message formats
Parsing Example
SessionManager API
Key methods for working with sessions programmatically.Static Creation Methods
SessionManager.create(cwd, sessionDir?)- New sessionSessionManager.open(path, sessionDir?)- Open existing session fileSessionManager.continueRecent(cwd, sessionDir?)- Continue most recent or create newSessionManager.inMemory(cwd?)- No file persistenceSessionManager.forkFrom(sourcePath, targetCwd, sessionDir?)- Fork session from another project
Static Listing Methods
SessionManager.list(cwd, sessionDir?, onProgress?)- List sessions for a directorySessionManager.listAll(onProgress?)- List all sessions across all projects
Instance Methods - Session Management
newSession(options?)- Start a new session (options:{ parentSession?: string })setSessionFile(path)- Switch to a different session filecreateBranchedSession(leafId)- Extract branch to new session file
Instance Methods - Appending (all return entry ID)
appendMessage(message)- Add messageappendThinkingLevelChange(level)- Record thinking changeappendModelChange(provider, modelId)- Record model changeappendCompaction(summary, firstKeptEntryId, tokensBefore, details?, fromHook?)- Add compactionappendCustomEntry(customType, data?)- Extension state (not in context)appendSessionInfo(name)- Set session display nameappendCustomMessageEntry(customType, content, display, details?)- Extension message (in context)appendLabelChange(targetId, label)- Set/clear label
Instance Methods - Tree Navigation
getLeafId()- Current positiongetLeafEntry()- Get current leaf entrygetEntry(id)- Get entry by IDgetBranch(fromId?)- Walk from entry to rootgetTree()- Get full tree structuregetChildren(parentId)- Get direct childrengetLabel(id)- Get label for entrybranch(entryId)- Move leaf to earlier entryresetLeaf()- Reset leaf to null (before any entries)branchWithSummary(entryId, summary, details?, fromHook?)- Branch with context summary
Instance Methods - Context & Info
buildSessionContext()- Get messages, thinkingLevel, and model for LLMgetEntries()- All entries (excluding header)getHeader()- Session header metadatagetSessionName()- Get display name from latest session_info entrygetCwd()- Working directorygetSessionDir()- Session storage directorygetSessionId()- Session UUIDgetSessionFile()- Session file path (undefined for in-memory)isPersisted()- Whether session is saved to disk