Skip to main content
Long jobs get interrupted. A machine reboots, a deploy rolls, a container is evicted — and your agent was halfway through, having already paid for the tokens and called half its tools. Give the agent a store — somewhere to write down what it did — and each step is saved as it happens. A different process, knowing nothing but the run’s id, can pick the job up and finish it.
Durable runs are new in Agentor 0.1.0, which ships as a prerelease. Install it with pip install --pre agentor.

Save a run

Pass a store when you build the agent. FileStore keeps one file per run in the directory you name:
agent.py
Every run now gets an id, and runs/<run_id>.jsonl holds the whole story. Keep the id somewhere you can find it again — a database row, a queue message, a log line.
Without store=, result.run_id is None and nothing is written. Runs work exactly as before; they are just not recoverable.

Resume after a crash

resume() takes a run id and continues from the last thing that was saved:
resume.py
The agent object has to be built the same way — same tools, same instructions — but it does not have to be the same process. This is the whole point: the process that started the run can be gone. Two things make resume() safe to call whenever you are unsure:

Finished runs are returned, not re-run

If the run already completed, you get its result back without another model call and without any tool running twice.

Half-finished tool calls are rewound

If the process died between asking for tools and recording every result, the agent goes back to the request that produced them and decides again.
Use await agent.aresume(run_id) from async code.

A worked example

Start a run and kill the process partway through — after the weather tool answered but before the agent wrote its reply:
Four lines made it to disk. Now a separate interpreter, given only that id:
The tool did not run a second time. The saved result was enough.

What gets written

The store records the same events the agent emits while it runs, one JSON object per line:
runs/cabc9046ac5040298aa362c0d6d2c5c5.jsonl
Each generation records the exact messages that were sent to the model, so rebuilding the conversation is a replay rather than a guess. FileStore flushes and fsyncs after every line, because a crash is the case it exists for. You can read the same events back off any finished result:

Choose a store

One append-only file per run, on local disk. The right default for a worker, a cron job, or anything with a volume attached.

Streamed runs are saved too

stream_chat() persists a run whenever the agent has a store, so a stream that a client abandons is still recoverable:

Limits to know about

The bundled stores are single-process and take no lock. If two workers both see a run as incomplete, both will continue it, and any tool with a side effect runs twice. Coordinate outside Agentor — a queue with visibility timeouts, a row lock, a lease — if more than one worker can recover the same run.
There is no way to stop a run, ask a person to approve a tool call, and continue. The event log makes it possible to build, but Agentor does not ship it yet.
If a write to the store fails, the error is logged and the run carries on rather than dying. You get your answer; you may not be able to resume that run.
FileStore writes files and never deletes them. Rotate or expire the directory yourself.

Next steps

Tracing

The same events power Celesto traces. Watch a run instead of reading its log.

Structured output

Get a typed object back from a run instead of a block of text.
Last modified on July 29, 2026