graph TD
A[Start from active leaf] --> B[Walk up to root]
B --> C[Collect entries in reverse]
C --> D[Reverse to chronological order]
D --> E[Apply compaction]
E --> F[Apply tool result truncation]
F --> G[Build content blocks]
G --> H[Send to model]
24 Session File Format
24.1 File Location
Sessions are stored as JSONL (JSON Lines) files:
~/.pi/agent/sessions/<encoded-cwd>/<session-id>.jsonl
The working directory path is encoded by replacing / with -. For example:
/Users/wuzhiguo/projects/my-app
→ Users-wuzhiguo-projects-my-app
Session files can also have custom names when set via --name or /name.
24.2 Session Versions
The current session format version is 1. Each JSONL file starts with a header entry:
{"type":"session_header","version":1,"createdAt":"2026-07-31T12:00:00Z","cwd":"/Users/wuzhiguo/projects/my-app"}24.3 Message Types
Each line in the JSONL file is a JSON object representing a session entry. All entries share common fields:
| Field | Type | Description |
|---|---|---|
id |
string |
Unique entry ID (UUID or sequential) |
parentId |
string\|null |
Parent entry ID (for tree structure) |
type |
string |
Entry type |
timestamp |
string |
ISO 8601 timestamp |
modelId |
string\|null |
Model used (for model-generated entries) |
24.3.1 Common Entry Types
| Type | Description |
|---|---|
session_header |
File header with metadata |
user_message |
User input |
assistant_message |
Model response |
tool_call |
Tool invocation by the model |
tool_result |
Result of a tool call |
compaction |
Summarized context |
branch_summary |
Summary of an abandoned branch |
thinking |
Model thinking/reasoning content |
24.4 Content Blocks
Messages contain an array of content blocks:
{
"type": "assistant_message",
"id": "entry-003",
"parentId": "entry-002",
"timestamp": "2026-07-31T12:00:02Z",
"modelId": "claude-sonnet-4-20250514",
"content": [
{
"type": "text",
"text": "I'll create a new file for you."
}
],
"usage": {
"inputTokens": 1500,
"outputTokens": 45,
"cacheReadTokens": 200,
"cacheWriteTokens": 0
},
"cost": 0.0023,
"durationMs": 1200
}24.4.1 Content Block Types
24.4.1.1 Text Block
{
"type": "text",
"text": "The content text"
}24.4.1.2 Image Block
{
"type": "image",
"source": {
"type": "base64",
"mediaType": "image/png",
"data": "iVBORw0KGgo..."
}
}24.4.1.3 Tool Use Block
{
"type": "tool_use",
"id": "tc_001",
"name": "bash",
"input": { "command": "ls -la" }
}24.4.1.4 Tool Result Block
{
"type": "tool_result",
"toolUseId": "tc_001",
"content": [
{ "type": "text", "text": "total 48\n..." }
],
"isError": false
}24.4.1.5 Thinking Block
{
"type": "thinking",
"text": "The user wants to list files. I should use ls.",
"signature": "..."
}24.5 Entry Types (Detailed)
24.5.1 session_header
{
"type": "session_header",
"version": 1,
"createdAt": "2026-07-31T12:00:00Z",
"cwd": "/Users/wuzhiguo/projects/my-app",
"provider": "anthropic",
"model": "claude-sonnet-4-20250514",
"thinkingLevel": "medium"
}24.5.2 user_message
{
"type": "user_message",
"id": "entry-001",
"parentId": null,
"timestamp": "2026-07-31T12:00:01Z",
"content": [
{ "type": "text", "text": "Create a REST API endpoint for users" }
]
}24.5.3 assistant_message
{
"type": "assistant_message",
"id": "entry-002",
"parentId": "entry-001",
"timestamp": "2026-07-31T12:00:03Z",
"modelId": "claude-sonnet-4-20250514",
"content": [
{ "type": "text", "text": "I'll create the endpoint." },
{ "type": "tool_use", "id": "tc_001", "name": "write", "input": { "path": "src/routes/users.ts", "content": "..." } }
],
"usage": {
"inputTokens": 1500,
"outputTokens": 120,
"cacheReadTokens": 200,
"cacheWriteTokens": 0
},
"cost": 0.005,
"durationMs": 2300,
"thinkingLevel": "medium"
}24.5.4 tool_call
{
"type": "tool_call",
"id": "entry-003",
"parentId": "entry-002",
"timestamp": "2026-07-31T12:00:03Z",
"toolName": "write",
"toolCallId": "tc_001",
"input": {
"path": "src/routes/users.ts",
"content": "import express from 'express';\n..."
}
}24.5.5 tool_result
{
"type": "tool_result",
"id": "entry-004",
"parentId": "entry-003",
"timestamp": "2026-07-31T12:00:04Z",
"toolCallId": "tc_001",
"toolName": "write",
"output": {
"content": [
{ "type": "text", "text": "File written successfully: src/routes/users.ts (45 lines)" }
]
},
"details": {
"bytesWritten": 1200,
"lines": 45
},
"isError": false,
"durationMs": 15
}24.5.6 compaction
{
"type": "compaction",
"id": "entry-010",
"parentId": "entry-009",
"timestamp": "2026-07-31T12:30:00Z",
"modelId": "claude-sonnet-4-20250514",
"summary": "The user asked to create a REST API for users. We created src/routes/users.ts with endpoints for GET, POST, PUT, DELETE. We also created a User model in src/models/User.ts. Tests were written in src/routes/users.test.ts and all passing. The current task is adding authentication middleware.",
"summarizedEntryIds": ["entry-001", "entry-002", "entry-003", "entry-004", "entry-005", "entry-006", "entry-007", "entry-008"],
"filesMentioned": [
"src/routes/users.ts",
"src/models/User.ts",
"src/routes/users.test.ts"
],
"tokenCount": 350
}24.5.7 branch_summary
{
"type": "branch_summary",
"id": "entry-015",
"parentId": "entry-005",
"timestamp": "2026-07-31T13:00:00Z",
"modelId": "claude-sonnet-4-20250514",
"summary": "Explored using GraphQL instead of REST. Decided against it due to project constraints.",
"summarizedEntryIds": ["entry-006", "entry-007"],
"branchRootId": "entry-005"
}24.5.8 thinking
{
"type": "thinking",
"id": "entry-002a",
"parentId": "entry-001",
"timestamp": "2026-07-31T12:00:02Z",
"modelId": "claude-sonnet-4-20250514",
"content": [
{ "type": "thinking", "text": "The user wants a REST API. I should check the existing project structure first.", "signature": "..." }
],
"thinkingLevel": "medium"
}24.6 Tree Structure
The session tree is defined by id and parentId fields. Each entry (except the root) has exactly one parent. Multiple entries can share the same parent, creating branches.
entry-001 (user_message, parentId: null)
├── entry-002 (assistant_message, parentId: entry-001)
│ ├── entry-003 (tool_call, parentId: entry-002)
│ │ └── entry-004 (tool_result, parentId: entry-003)
│ └── entry-005 (user_message, parentId: entry-002)
│ ├── entry-006 (assistant_message, parentId: entry-005) [ACTIVE]
│ └── entry-008 (assistant_message, parentId: entry-005) [ABANDONED]
│ └── entry-009 (branch_summary, parentId: entry-008)
└── entry-010 (compaction, parentId: entry-001)
24.6.1 Building the Tree
To reconstruct the tree from a JSONL file:
- Parse each line as a JSON object
- Build a map of entries by ID
- For each entry, find its parent via
parentId - Group entries by parent to form children arrays
- The root entry has
parentId: null
24.7 Context Building
When Pi prepares context for a model API call, it follows this process:
24.7.1 Steps
- Walk the active path — from the current position up to the root
- Collect entries — gather all entries on the active path
- Handle compaction — if a CompactionEntry is encountered, use its summary and stop walking
- Truncate tool results — truncate large tool results based on settings
- Build messages — convert entries to model API messages
- Prepend system prompt — add the system prompt and context files
24.8 SessionManager API
The SessionManager class manages session files:
interface SessionManager {
// Create or load a session
createSession(cwd: string, options?: SessionOptions): Promise<Session>;
loadSession(path: string): Promise<Session>;
// List sessions
listSessions(cwd: string): SessionSummary[];
// Session operations
appendEntry(session: Session, entry: Entry): Promise<void>;
getEntries(session: Session): Entry[];
getTree(session: Session): TreeNode;
// Forking
forkSession(session: Session, fromEntryId: string): Promise<Session>;
cloneSession(session: Session): Promise<Session>;
// Export
exportSession(session: Session, format: "html" | "jsonl"): Promise<string>;
importSession(path: string): Promise<Session>;
}24.8.1 Using SessionManager in Extensions
pi.on("session_start", async (_event, ctx) => {
const session = ctx.sessionManager.getCurrentSession();
// Read entries
const entries = ctx.sessionManager.getEntries(session);
console.log(`Loaded ${entries.length} entries`);
// Access the tree
const tree = ctx.sessionManager.getTree(session);
console.log(`Tree has ${tree.children.length} top-level branches`);
});Session files are plain JSONL. You can inspect them with standard tools: jq, grep, head, tail. This makes session data accessible outside Pi.