Durable runs, and forking a run into a new one, are part of Agentor’s 0.1.0 engine — the current stable release. Plain
pip install agentor gets you both.Save a run
Pass a store when you build the agent.FileStore keeps one file per run in the directory you name:
agent.py
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
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.
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:Fork a run
resume() continues a run in place, and deliberately refuses to re-run one that already finished. fork() takes a different approach: it copies a run’s whole history — every step, including the model’s reasoning where the provider returned it — into a new run id, then continues from there. The original run is never touched, whether it is still in progress, finished, or crashed.
That makes fork the way to branch a conversation: try a different instruction from the same starting point without losing the original, or pick up a completed run and keep going, which resume() will not do for you.
fork.py
await agent.afork(...) from async code.
As with
resume(), the agent doing the forking does not have to be the one that made the original run — but if the original run has no system message, the forking agent’s instructions fill in on the fork.Fork without new input
Leave out the second argument andfork() just copies the run under a new id:
- If the parent was still unfinished — interrupted mid-run — the fork picks up where it left off and finishes, exactly like
resume()would. - If the parent had already finished, the fork is an identical copy of it. No model call is made.
Choosing the new run’s id
Passfork_id to name the new run yourself instead of getting a generated one:
fork() raises ValueError if that id is already taken.
max_turns and tracing behave the same as on run() — they apply only to the fork’s continuation, never to the run being copied.
What gets written
The store records the same events the agent emits while it runs, one JSON object per line:runs/cabc9046ac5040298aa362c0d6d2c5c5.jsonl
generation records the exact messages that were sent to the model — reasoning included, when the provider returns any — 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.
Forking adds one more line to the copy: a fork event naming the run it came from. It is provenance only — replaying the log or checking whether it is complete works exactly as it does for a run that was never forked.
You can read the same events back off any finished result:
Choose a store
- FileStore
- MemoryStore
- Your own
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
Two workers must not resume the same unfinished run
Two workers must not resume the same unfinished run
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.
Forking a run still in progress copies whatever has been saved so far
Forking a run still in progress copies whatever has been saved so far
Like resume, fork takes no lock. Fork a run that is actively being written to and you get a copy of whatever prefix of its log had reached the store at that instant — not necessarily its very latest step.
Pausing for human approval is not built in
Pausing for human approval is not built in
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.
Saving is best-effort during a live run
Saving is best-effort during a live run
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.
Nothing prunes old runs
Nothing prunes old runs
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.
