# How pi-coding-agent Handles Session Management for Resuming, Forking, and Continuing Tasks

> Discover how pi-coding-agent's SessionManager uses a JSON-L tree for robust session management, enabling effortless task resumption, forking, and continuation.

- Repository: [Mario Zechner/pi-mono](https://github.com/badlogic/pi-mono)
- Tags: how-to-guide
- Published: 2026-02-15

---

**pi-coding-agent uses an append-only JSON-L tree managed by `SessionManager` to persist every interaction, enabling automatic resumption of recent sessions, explicit forking across projects, and branching within conversations.**

The `pi-coding-agent` package in the [badlogic/pi-mono](https://github.com/badlogic/pi-mono) repository implements a robust session management system that treats conversation history as a versioned, navigable tree. By storing each message as an immutable entry in a JSON-L file, the system guarantees data integrity while supporting complex workflows like resuming interrupted tasks, forking conversations into new projects, and branching to explore alternative solutions.

## The Append-Only Session Tree Architecture

At the core of pi-coding-agent session management is the **`SessionManager`** class defined in [`packages/coding-agent/src/core/session-manager.ts`](https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/src/core/session-manager.ts). This manager treats each session as an append-only log where every entry is a JSON object written to a `.jsonl` file.

The session file begins with a header entry containing:
- A **UUID** for the session
- **Timestamp** and **working directory** (`cwd`)
- Optional **`parentSession`** field when forking

As interactions occur, the manager appends message entries (`user`, `assistant`, `system`) and metadata entries (`branch_summary`, `compaction_summary`) without ever modifying previous lines. This immutability ensures that historical states remain recoverable at any time.

## Creating and Persisting Sessions

### Starting a New Session

You can initialize a session in two modes: **in-memory** for ephemeral conversations or **file-backed** for persistence.

To create a transient session that never touches disk:

```typescript
import { SessionManager } from "@pi/coding-agent";

const mem = SessionManager.inMemory();
mem.appendMessage({ role: "user", content: "Hello" });
mem.appendMessage({ role: "assistant", content: "Hi!" });

```

To create a persisted session with a new file on disk:

```typescript
const session = SessionManager.create(process.cwd());
// Creates: ~/.pi/agent/sessions/<cwd>/<timestamp>_<uuid>.jsonl

```

The default session directory is computed by helpers in [`packages/coding-agent/src/config.ts`](https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/src/config.ts) using `getDefaultAgentDir` and `getSessionsDir`.

### The Session File Format

Each session maintains an in-memory index (`byId` map and `leafId`) that mirrors the file structure. When loading, `SessionManager` uses `loadEntriesFromFile` to parse each line, `migrateToCurrentVersion` to upgrade legacy formats, and `_buildIndex` to reconstruct the navigable tree. This index enables O(1) lookups for any message ID and constant-time traversal from leaf to root.

## Resuming Sessions

### Automatic Resumption of Recent Sessions

The `SessionManager.continueRecent(cwd, sessionDir?)` static method implements the "resume" workflow. It scans the default *sessions* folder for the most recently modified `.jsonl` file, loads it, and rebuilds the in-memory index.

When you run the `pi` CLI without explicit flags, it automatically invokes this method to pick up where you left off:

```typescript
const cwd = process.cwd();
const recent = SessionManager.continueRecent(cwd);
// Loads: ~/.pi/agent/sessions/<cwd>/latest.jsonl
recent.appendMessage({ role: "user", content: "What's the status?" });

```

New messages are appended to the same logical conversation, preserving the full history.

### Explicit Session Selection

For advanced workflows, the CLI supports the `--session` flag parsed in [`packages/coding-agent/src/cli/args.ts`](https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/src/cli/args.ts). This triggers `sessionManager.setSessionFile(path)`, which swaps the manager to an arbitrary file:

```typescript
const mgr = new SessionManager();
mgr.setSessionFile("/path/to/specific_session.jsonl");
// If file is empty: creates fresh session header
// If file exists: loads entries, runs migrations, rebuilds tree

```

This mechanism enables users to maintain multiple parallel conversations within the same project or to open a fork of another project's session.

## Forking and Branching Workflows

### Forking Sessions Across Projects

The `SessionManager.forkFrom(sourcePath, targetCwd, sessionDir?)` static method enables **cross-project session inheritance**. It copies the entire history from a source session file into a new file located under a different working directory:

```typescript
const sourceFile = "/home/alice/.pi/agent/sessions/oldproj/2024-10-01_abc123.jsonl";
const targetCwd = "/home/alice/workspaces/newproj";

const forked = SessionManager.forkFrom(sourceFile, targetCwd);
// Creates new file in newproj with parentSession: sourceFile

```

The new session header records `parentSession: sourcePath`, creating a provenance chain. This allows users to migrate context from one codebase to another without losing conversational history.

### Branching Within a Session

Inside a single session, users can explore alternative solutions by **branching** to an earlier message. The `branch(id)` method moves the internal `leafId` pointer to a specific entry ID, effectively rewinding the conversation:

```typescript
const mgr = SessionManager.continueRecent(cwd);
const earlyId = "a1b2c3d4"; // ID of a previous user message
mgr.branch(earlyId);
mgr.appendMessage({ role: "user", content: "Let's try a different approach." });

```

To document why a branch was taken, use `branchWithSummary()`:

```typescript
mgr.branchWithSummary(earlyId, "Switched to alternative plan", { reason: "timeout" });

```

This inserts a `branch_summary` entry into the log, providing metadata for future context building.

### Exporting Branched Sessions

The `createBranchedSession(leafId)` method generates a compact session file containing only the path from the specified leaf back to the root:

```typescript
const mgr = SessionManager.continueRecent(cwd);
const leaf = mgr.getLeafId()!;
const newFile = mgr.createBranchedSession(leaf);
// Returns path to new compact session file

```

This is useful for sharing specific conversation branches or archiving successful solution paths without the full tree.

## Building Context for the LLM

When preparing context for the language model, the static helper `buildSessionContext()` walks the tree from the current `leafId` to the root, handling special entries:

- **Compaction entries**: Summarize pruned branches to save tokens
- **Branch summaries**: Provide context for why alternative paths were taken
- **Labels**: Attach metadata to specific messages

The function returns a linearized array of `AgentMessage` objects that represent the current conversation path, regardless of how many branches exist in the underlying tree. Special entry types are defined in [`packages/coding-agent/src/core/messages.ts`](https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/src/core/messages.ts), including `createBranchSummaryMessage` and `createCompactionSummaryMessage`.

## Summary

- **pi-coding-agent session management** relies on an append-only JSON-L tree stored in [`packages/coding-agent/src/core/session-manager.ts`](https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/src/core/session-manager.ts).
- **Session creation** uses `SessionManager.create()` or `inMemory()` for ephemeral chats, writing headers with UUID, timestamp, and optional `parentSession`.
- **Resuming** happens automatically via `continueRecent()` or explicitly via `setSessionFile()`, rebuilding the in-memory index (`byId`, `leafId`) from disk.
- **Forking** copies entire histories across projects using `forkFrom()`, recording provenance in the new session header.
- **Branching** rewinds the conversation pointer with `branch()` or `branchWithSummary()`, enabling exploration of alternative solutions without losing history.
- **Context building** linearizes the current branch for the LLM using `buildSessionContext()`, handling compaction and branch summaries defined in [`messages.ts`](https://github.com/badlogic/pi-mono/blob/main/messages.ts).

## Frequently Asked Questions

### How does pi-coding-agent automatically resume my last conversation?

When you run the CLI without specifying a session file, `SessionManager.continueRecent(cwd)` scans the default sessions directory (computed by `getSessionsDir` in [`packages/coding-agent/src/config.ts`](https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/src/config.ts)), identifies the most recently modified `.jsonl` file, and rebuilds the in-memory tree index. New messages are then appended to this existing file, creating a seamless continuation of your previous work.

### Can I fork a conversation from one project to another?

Yes, the `SessionManager.forkFrom(sourcePath, targetCwd)` static method copies the entire conversation history from a source session file into a new session file located under a different working directory. The new session header records the original file path in its `parentSession` field, creating a provenance chain that allows you to migrate context across projects while preserving the full interaction history.

### What is the difference between branching and forking in pi-coding-agent?

**Branching** occurs within a single session file using `branch(id)` or `branchWithSummary()`, which moves the internal `leafId` pointer to an earlier message ID. This allows you to explore alternative solutions from a specific point in the conversation without affecting the original timeline. **Forking**, conversely, creates an entirely new session file in a different directory, copying the complete history from a source session. Forking is used for cross-project continuation, while branching is used for intra-conversation exploration.

### How does the system handle old session file formats?

When loading a session via `setSessionFile()` or `continueRecent()`, the `SessionManager` invokes `migrateToCurrentVersion()` on each entry after parsing with `loadEntriesFromFile()`. This migration system upgrades legacy formats to the current schema before `_buildIndex()` reconstructs the in-memory maps (`byId`, `leafId`). This ensures backward compatibility while maintaining the integrity of the append-only log structure.