Your Pi session is forty minutes into a refactor when the function times out. The process dies, the conversation is gone, and the half-finished edits go with it. That is the ordinary experience of moving a coding agent off a laptop and into the cloud, and it is the exact gap Vercel's new durable-harness package was built to close.
durable-harness is an Apache-2.0 npm package from the vercel-labs team. It does not replace Pi or rewrite it. It wraps Pi's own session in a durable log, so a dead function can hand the work to a fresh one without replaying finished tool calls or billing you twice for tokens you already paid for.
Here is how it works and how to wire it into a Pi project this afternoon.
What the Package Actually Does
Every harness session becomes a workflow run on a World, which is the Workflow SDK's bundle of storage, a queue and a stream behind one interface. The run's event log is the source of truth: the conversation, the turn in flight, and every model and tool call.
Before a call executes, the package records an intent. When the call finishes, it records the outcome. Each turn sits behind a message on Vercel Queues, so if the function dies, the queue passes the turn to a new function, which reads the log and continues from the last known point. Locally that sounds academic. In production it is the difference between a lost afternoon and a resumed task.
Recovery then depends on a detail most coverage skips: not every tool call is safe to repeat. durable-harness makes you declare that up front, per tool.
- Completed calls. Model and tool results are read back from the log, so finished tokens are never paid for twice.
- replay: 'safe'. Lookups and read-only commands are allowed to run again after a crash.
- replay: 'never'. Anything with a side effect, like a refund or a deploy, never runs twice. The model is told the call may already have happened.
Session files are mirrored into the log as well. That covers Pi's workspace, Claude Code's transcript, Codex's rollout and OpenCode's event store, which is why the same wrapper can serve five different harnesses without touching their internals.
Wiring It Into a Pi Project
The Pi adapter is the recommended path. durable-harness/pi-durable opens Pi's own durable Harness on a World rather than reimplementing the agent loop, so tool behavior stays what you already know. Four steps do the work.
- Install the packages alongside the harness:
npm install durable-harness @earendil-works/pi-durable @earendil-works/chord @earendil-works/pi-ai just-bash. - Open a harness in
lib/agent.tsusingresumeWith((storage) => Harness.open(storage, { models, registry, env: virtualEnv() }, ctx)). Register CodingTools on the registry, because agents are tool-less by default and Pi's coding tools live on a virtual workspace. - Serve it from
app/api/chat/route.tswithchatRoute({ open, agent, authorize }), which exports both GET and POST for you and leaves auth to your own function. - Wrap
next.config.tswithwithDurableHarness(). It discovers module-scope agents automatically. For a custom factory, list the module explicitly with{ agents: ['lib/agent.ts'] }.
The question every reviewer asks next is where model-chosen commands run. Never on the function's own disk. There are three workspace options, and picking the wrong one is the usual cause of a confusing failure.
virtual() keeps a bash-compatible workspace inside the session log, with no network, sized for roughly 500 files. virtual({ durable: false }) keeps scratch files in memory and clears them every turn, which is fine for throwaway inspection and wrong for a refactor. sandbox() gives each session its own Vercel Sandbox with a real /workspace on a Drive, which is what heavier builds need.
Before You Ship It
Start local, then move the storage layer. The agent code does not change between backends, which is the cleanest part of the design.
- Local. The Workflow SDK's local World, files on your machine, no configuration at all.
- Vercel. Managed storage, queuing and scaling, with every session traced in Agent Runs.
- Self-hosted. Postgres World 5.0.4 or newer on Node 22 and up, switched with two variables: WORKFLOW_TARGET_WORLD and WORKFLOW_POSTGRES_URL.
Two constraints deserve a second read. Claude Code and Codex run only inside the session's own Vercel Sandbox, even when you self-host, so the wrapper does not free them from Vercel infrastructure. Pi and OpenCode, by contrast, stay in your function, which is where the savings actually land for most teams.
The version number is the other caveat. npm lists 0.0.1, with a beta line behind it and a first publish in mid-October, and the README says plainly that the API may change between releases. There are no published latency or throughput benchmarks, and every recovery claim is currently self-reported by Vercel. Treat it as production-shaped tooling rather than production-proven tooling.
That is still no reason to wait. If Pi already runs in a function, the failure is not hypothetical, it is scheduled: long turns eventually die. Recording an intent before each call costs a handful of config lines today and saves an entire rerun tomorrow.
Start with the replay annotations. They are the part that decides whether recovery is safe or merely fast, and they are the part no crash test can fix after the fact.
Comments