harnessengineering agents agents/casestudies casestudies piagent
Core Idea
Pi Durable is a durable runtime for long-running AI agents.
It makes model calls, tool calls, subagents, compaction, and custom workflows into recoverable tasks with persisted state. If the process crashes, the agent can restart from stored checkpoints instead of losing all progress.
Key ideas:
- Durable tasks — work survives restarts
- Ownership — parent tasks control child tasks/subagents
- Replay safety — interrupted tools are only retried when safe
- Durable documents — structured app state is persisted
- Atomic commits — transcript, task state, and app state stay consistent
- Long-running conversations — context can be compacted without deleting history
- Multi-client support — several clients can observe and steer the same agent
Pi Durable | Earendil
pi/packages/durable | GitHub
What is a Harness? | Earendil
Pi-Durable-Technical-Manual.pdf
Introduction
AI agents can lose execution progress when their process crashes or restarts. Saving chat history does not preserve running tasks, unfinished tool calls, subagents, queued work, or retry state.
Pi Durable adds durable execution to the Pi agent runtime. Model calls, tool calls, compaction, subagents, and custom workflows are stored as recoverable tasks. After a restart, the runtime can inspect unfinished work and continue it.
Pi Durable is distributed-systems-style Durable Execution specialized for AI agent harnesses.
|Distributed durable execution|Pi Durable|
|---|---|
|Workflow|Conversation / task graph|
|Workflow step|Generation task / tool task|
|Durable state|Entries, documents, task checkpoints|
|Activity|Tool execution|
|Worker crash|Agent process crash|
|Resume execution|resume()unfinished tasks|
|Child workflow|Owned conversation / subagent|
|Retry policy|Replay-safe tool semantics|
The problem
A basic agent loop might look like this: Model -> Tool -> Model -> Answer
If the process crashes during the tool call, the application has to work out:
- Did the tool start?
- Did it finish?
- Is it safe to run again?
- Was its output stored?
- Which step should resume?
- Were child agents still running?
- Were new user messages waiting?
A transcript database cannot answer all of these questions. The runtime also needs persistent execution state.
What original Pi Keeps
The Pi coding agent already persists its sessions. A session is a JSONL file under ~/.pi/agent/sessions/. Each line is one
JSON object with a type; the first is a session header, and the rest are entries that form a tree through id and parentId coding-
agent.
That design keeps the conversation. It does not keep the turn. If the process dies while a tool runs, the file ends at the assistant
message that called it. No line says the tool started or what it printed. A half-streamed response is not in the file, and state an
extension held only in memory is gone. The next process can read the conversation back, but it cannot tell whether the tool’s
effect happened.

Pi Durable Answer
Pi Durable is essentially an agent harness built around durable, shared storage. Its core idea is to make an agent recoverable after crashes without losing track of what happened.
It does this in three main ways:
- Everything is persisted before it is shown. Conversations, model outputs, tool calls, partial streamed responses, and tool progress are written to storage first. Progress is committed frequently, so after a crash only the work since the last successful commit may be lost.
- Model and tool work runs as checkpointed tasks. Each call is treated as a durable state machine. Before a tool actually executes, Pi Durable records the intent, final arguments, and replay policy. After restarting, it can tell which operations had already begun and decide whether they are safe to resume or rerun.
- Application state is stored atomically with the conversation. Structured app data, such as a todo list, lives in the same storage and commit as transcript changes. This prevents app state and conversation history from getting out of sync.
The crash example demonstrates the benefit: another process can reopen the same SQLite database, recover an interrupted tool task, preserve the output already produced, and avoid automatically rerunning a tool that was not marked safe to replay.
Architecture

A Harness owns a storage backend and runs one or more conversations. Conversations contain transcript entries, durable documents, submissions, and tasks.
The scheduler reads task state from storage and resumes unfinished work when the process starts again.
Durable tasks
A durable task in Pi Durable is a unit of work whose progress is stored so it can survive a process crash and continue later. Every major action in the harness—model calls, tool calls, compaction, and custom workflows—is represented as a task.
A typical agent turn looks like this:

Before moving from one step to the next, the task stores a checkpoint. If the process dies, Pi Durable opens the same storage, finds the unfinished task, reads its checkpoint, and continues from there.
Tool recovery
Tool calls are durable tasks too. Some tools can safely run again after interruption:
read_file
search
list_directory
fetch_status
These can be marked replay-safe. Other tools may create external side effects:
deploy
send_email
charge_card
delete_resource
Pi Durable should not blindly repeat these after a crash.

Replay safety only covers the agent runtime’s decision to retry. External systems still need their own idempotency strategy for operations such as payments or deployments.
For example:
await bank.charge(card, `payment-${task.id}`)The task ID can become an idempotency key so the payment provider can reject duplicate charges.
Tasks and ownership
Tasks can own other tasks and conversations.
flowchart TD
C[Conversation]
G[Generation Task]
T[Tool Task]
CC[Child Task]
SC[Subagent Conversation]
SG[Subagent Generation]
C --> G
G --> T
T --> CC
T --> SC
SC --> SGOwnership matters because it controls lifecycle and cancellation. If a parent task is aborted, Pi Durable knows which child tasks and conversations belong to it and can clean them up consistently.
Task = durable work.
Ownership = relationship that controls child work, cancellation, and cleanup.
Subagents
The parent task owns the subagent, so Pi Durable can track its lifecycle, cancel it with the parent, and recover it after a restart.

Foreground and background work
Foreground work keeps a conversation busy. Background work can continue while the conversation accepts more input.
This supports jobs such as research, monitoring, reminders, report generation, and long-running subagents.
flowchart TD
C[Conversation]
C --> F[Foreground Task]
C --> B[Background Task]
F --> R1[Current response]
B --> R2[Long-running work]Durable application state
Conversation history is only one part of an agent application. The application may also need structured state such as:
- plans
- todos
- approvals
- deployment state
- issue status
- sandbox information
- workflow progress
Pi Durable stores this state in durable documents.
Transcript changes, document updates, and task changes can be written through the same commit.

This avoids partial application state such as a transcript saying a deployment completed while the deployment document still says pending.
Conversation forks
A conversation can fork from an earlier point.
For example, a Slack thread can branch from a channel conversation:
gitGraph
commit id: "Message A"
commit id: "Message B"
commit id: "Agent C"
branch thread
checkout thread
commit id: "Thread D"
commit id: "Thread E"
checkout main
commit id: "Channel F"The child conversation sees the parent history up to the fork point and can continue independently.
Each conversation can also have its own model, thinking level, tools, extensions, instructions, and working directory.
Long conversations
Pi Durable separates stored history from the context sent to the model.
A long conversation may contain thousands of entries:
1 2 3 4 5 ... 9500 9501 9502 ... 10000
The model may instead receive:
[summary of 1–9500]
9501
9502
...
10000
Compaction creates the summary as another durable task.
flowchart LR
H[Full Durable History] --> C[Compaction Task]
C --> S[Summary of older context]
S --> M[Model Context]
R[Recent Messages] --> MOlder entries remain in storage. Compaction only changes the working context used for future model calls.
Multiple clients
Several clients can observe the same conversation.
flowchart TB
C[Pi Durable Conversation]
V[Committed View]
C --> V
V --> W[Web]
V --> S[Slack]
V --> M[Mobile]
V --> A[Admin UI]Clients can load the current committed state instead of rebuilding the conversation from temporary streaming events.
The state can include transcript entries, running tools, queued submissions, agent configuration, usage, and current generation state.
Clients can also send steering input while the agent is working.
Storage
Pi Durable needs a storage backend to persist the things required for recovery.

It can use backends such as the memory, SQLite, JSONL or a custom storage implementation.
The important point is that storage contains more than chat history. It also stores checkpoints and task state, so after a crash Pi Durable can determine what work was unfinished and resume it.
Deployment
A Pi Durable deployment normally has one process owning one storage backend.

Why it matters for harness engineering
A short-lived agent can work with: Model, Prompt, Tools, Agent Loop
A long-running agent also needs execution state:
Tasks
Checkpoints
Ownership
Retry rules
Background work
Application state
Context management
Recovery
Client synchronization
Pi Durable puts these concerns inside the runtime instead of leaving every application to build its own recovery layer.
A useful distinction is:
- Persistence: “What has already been stored?”
- Durable execution: “What was running, what finished, and what should run next?”
Pi Durable handles the second problem.