How to Restore Conversation State in Earendil PI Sessions
Earendil PI persists every interaction to a JSONL session file beside your project and rehydrates the full conversation state—including pending messages and editor buffers—when you use the --resume flag, the /resume slash-command, or the SDK's switchSession() method.
The Earendil PI CLI (located in the earendil-works/pi repository) stores your entire coding conversation in local *.jsonl session files, enabling you to suspend work and resume days later without losing context. Understanding how to restore conversation state in Earendil PI sessions allows you to maintain continuity across terminal sessions, external editor invocations, and system interruptions.
Where Conversation State Is Stored
PI stores every interaction in a session file (*.jsonl) that lives beside your project's source tree. When you suspend a session—by exiting the TUI, spawning an external editor, or explicitly pausing—the CLI records the current editor buffer and pending messages to this append-only JSONL format. This design guarantees deterministic replay because the exact entries are re-executed during restoration.
The Restoration Workflow
The resume operation follows a five-step pipeline that tightly couples the CLI argument parser, session manager, and interactive mode components.
1. Session Selection via CLI Arguments
When you invoke PI with the --resume flag, packages/coding-agent/src/cli/args.ts parses the option at line 81 (result.resume = true) and triggers the session picker UI.
pi --resume
The picker interface defined in packages/coding-agent/src/cli/session-picker.ts displays available JSONL files and awaits your selection.
2. File Loading and Entry Parsing
Once you select a file, the SessionManager class instantiated in packages/coding-agent/src/core/session-manager.ts calls loadEntriesFromFile(this.sessionFile) at line 732. This method deserializes each JSONL entry and reconstructs the in-memory conversation model, including system prompts, user messages, and any assistant partial replies.
3. Message Restoration and Buffer Replay
After the new AgentSession is created, AgentSessionRuntime orchestrates the switch. In packages/coding-agent/src/core/agent-session-runtime.ts at line 128, the runtime emits emitBeforeSwitch("resume", …) and teardownCurrent("resume", …) to signal the transition.
The actual message replay occurs in packages/coding-agent/src/modes/interactive/interactive-mode.ts. The method restoreQueuedMessagesToEditor() defined at line 3692 re-populates the editor buffer with your previously typed text. This method is invoked following the session switch at line 3428, ensuring theUI displays your exact state prior to suspension.
4. Event Emission for Extensions
For extensions that need to synchronize with session lifecycle changes, the runtime emits two distinct events. At lines 132–138 in agent-session-runtime.ts, PI fires session_before_switch with reason: "resume" followed by session_start with the same reason. Listeners receive an event object containing targetSessionFile and can optionally cancel the resume by returning { cancelled: true }.
5. Editor State Recovery
The restoration routine copies options.currentText (if supplied) or the editor's saved snapshot, then returns the count of restored messages. If you had an in-flight completion when the session paused, that message is re-queued so the UI can display it again.
Resuming from the Command Line
Using the --resume Flag
The simplest method to restore conversation state is launching PI with the resume flag:
pi --resume
This triggers the file picker, loads the selected JSONL, and automatically restores the conversation context.
Using the /resume Slash Command
While inside the interactive TUI, type:
/resume
This slash-command defined in packages/coding-agent/src/core/slash-commands.ts invokes the same runtime switch as the CLI flag. The command calls the internal resume handler which executes AgentSessionRuntime.switchSession() with your last active session file.
Programmatic Session Restoration
You can resume sessions programmatically using the PI SDK. This mirrors the internal AgentSessionRuntime.switchSession() logic and emits the same lifecycle events.
import { Pi } from "@earendil/pi";
const pi = await Pi.create();
await pi.sessionRuntime.switchSession("/path/to/old-session.jsonl");
The SDK call emits session_before_switch with reason: "resume" and restores all queued messages through the same restoreQueuedMessagesToEditor() pathway used by the CLI.
Hooking into Resume Lifecycle Events
Extensions can monitor or intercept resume operations:
pi.on("session_before_switch", async (event) => {
if (event.reason === "resume") {
console.log("About to resume:", event.targetSessionFile);
// Validate file or preload resources here
}
});
Returning { cancelled: true } from this handler prevents the resume operation, allowing extensions to implement custom validation logic.
Summary
- Session files are stored as
*.jsonlbeside your project tree, preserving every message and editor state. - Resume via CLI using
pi --resumetriggers the session picker inpackages/coding-agent/src/cli/session-picker.ts. - Resume via TUI by typing
/resume, which invokes the same runtime switch defined inpackages/coding-agent/src/core/slash-commands.ts. - Message restoration happens through
InteractiveMode.restoreQueuedMessagesToEditor()at line 3692 of the interactive mode source, repopulating buffers with pending text. - Extension hooks receive
session_before_switchandsession_startevents withreason: "resume", enabling synchronization with external tools.
Frequently Asked Questions
What file format does Earendil PI use to store session history?
PI uses JSONL (JSON Lines) format with a *.jsonl extension. Each line represents a discrete interaction event, allowing the system to append new messages efficiently and replay the conversation deterministically by reading the file sequentially.
How do I resume a session after closing the terminal?
Run pi --resume from the same project directory. The CLI will present a picker interface (implemented in packages/coding-agent/src/cli/session-picker.ts) showing available session files. Select your previous session, and the SessionManager in packages/coding-agent/src/core/session-manager.ts will rehydrate the conversation at line 732 via loadEntriesFromFile().
Can extensions intercept or cancel a session restoration?
Yes. Extensions can listen for the session_before_switch event emitted by AgentSessionRuntime at lines 132–138. If your handler returns an object with { cancelled: true }, PI aborts the resume operation. This allows validation logic to prevent restoration of corrupt or incompatible session files.
Does resuming restore my partially typed message in the editor?
Yes. The restoreQueuedMessagesToEditor() method in packages/coding-agent/src/modes/interactive/interactive-mode.ts (line 3692) specifically handles editor buffer restoration. It recovers options.currentText or the saved editor snapshot, ensuring your draft message appears exactly as you left it, even if you had unsent text or an in-flight completion request.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →