π Pi, Explained
Home / Chapter 5

Time travel — sessions, trees, and second chances

The 30-second version: every Pi conversation is saved automatically as a tree of entries in a plain JSONL file. Going back never deletes anything — it just moves a pointer. Which means you can try approach A, rewind, try approach B, and keep both histories forever.


Conversations deserve better than a straight line

Most chat tools treat history as a list: newest at the bottom, delete to undo. That's fine for small talk. It's wrong for engineering work, because engineering is full of:

  • "Let me try that again" — the agent's answer was off; you want to rewind one turn and re-ask
  • "What if we'd gone left?" — you implemented approach A, but approach B keeps nagging at you
  • "I want that back" — three days later you need the context from Tuesday's session

A linear history can only answer the first of those by destroying data. Pi's answer: don't store conversations as lists. Store them as trees.

How a session tree grows

Every session lives in a JSONL file under ~/.pi/agent/sessions/ (organized by project directory). One line per entry, and every entry knows exactly one thing about the tree: its parent.

e1  model change (switched to Claude)
 └─ e2  you: "why does salt verification fail in auth.ts?"
     ├─ e3  assistant: reads auth.ts
     │   └─ e4  tool result: file contents
     │       └─ e5  assistant: "line 23 — salt isn't encoded"
     │
     └─ e6  you: "first show me the hash function"      ← a second branch!
         └─ e7  assistant: greps for the hash…

Notice e6: its parent is e2 — the same parent as e3. That's all a branch is: two entries claiming the same parent. Nothing else. No special branch objects, no bookkeeping structures.

And here's the design decision that makes the whole thing work: entries only point backwards. A node knows its parent; parents don't keep lists of children. Why? Because then adding a node never means editing an old one. The file only ever grows — append-only, forever. (If parents tracked children, every branch would require rewriting an old line, and the whole guarantee collapses.)

Rewinding is just moving a pointer

When you rewind in Pi — say you're unhappy with the "line 23" analysis and want to re-ask from e2 — nothing is deleted. Internally, the whole operation is essentially:

current position ← e2

The old branch (e3 → e4 → e5) stays in the file, intact, forever. You can return to it tomorrow. This is the same trick that makes git powerful: history is immutable, and "undo" is just choosing a different place to stand.

The everyday commands:

You want to… Use
Continue your last session pi -c
Browse old sessions pi -r (or /resume inside)
See the current session's tree /tree
Fork a new session from an earlier message /fork (or pi --fork)
Duplicate the active branch into a fresh file /clone
Export the session as HTML /export

/tree is the fun one: an interactive view of your session's entire branching structure, where you can jump to any node — and keep going from there.

But wait — what about what the model remembers?

Sharp question. The file keeps every branch, but the model only ever sees one path: the chain from your current position back to the root. Jump to another branch and, from the model's perspective, the old timeline evaporates.

Pi softens this with branch summarization: when you navigate the tree, it can summarize the timeline you're leaving behind and inject that summary into the new branch. So the model knows "we tried PostgreSQL triggers earlier and abandoned them for performance reasons" without carrying the full transcript of that dead end.

It's the same structured-summary machinery as context compaction — same template, same file tracking — just pointed at a different problem. Compaction fights a long history; branch summarization fights an abandoned one.

Plain text is a feature

A quiet virtue of this whole design: sessions are just JSONL files. You can grep them, diff them, back them up with any tool you like, or read them in an editor at 2 a.m. when something seems off. There's no database to administer and no export ritual — the archive is the data.

(Pi will even use your system trash when you delete a session, if you have a trash CLI installed. Small kindness, very Pi.)

The takeaway

Pi's session model is one idea, consistently applied: history is append-only, navigation is a pointer, and every second chance is kept. Once you've worked in an agent where "rewind" is free and permanent, going back to linear chat history feels like driving without a reverse gear.


Sources & further reading: Sessions · Session format