Skip to content
Changelog

Fork a ClaudeAgent run from a checkpoint

October 12, 20262 min read

Agno's ClaudeAgent.continue_run and acontinue_run mirror Agent.continue_run and also run with stream=True. While a run executes, Agno records where each prompt, tool call, tool result and final reply sits in the Claude SDK transcript, and marks the end of each tool step as a checkpoint.

Say Claude ran your tests, saw one failure and went straight to editing a file. You'd like it to stop after the test output and explain the failure first. Pick the checkpoint after pytest and continue from there.

from agno.agents.claude import ClaudeAgent
from agno.db.sqlite import SqliteDb
 
agent = ClaudeAgent(id="fixer", db=SqliteDb(db_file="tmp/claude.db"))
run = agent.run("Run the tests and fix the failing one", session_id="s")
 
branch = agent.continue_run(
    run_id=run.run_id,
    session_id="s",
    continue_from=3,
    input="Show me the failing test before you edit anything",
)

Agno forks the stored SDK transcript at message 3 and resumes the fork, so the model sees the test result and nothing after it. The session's SDK pointer moves to the fork, which means the next turn in that session builds on the branch.

Two columns of run messages. The source run on the left holds six messages: the user asks Claude to run the tests and fix the failing one, the assistant calls Bash with pytest -q, the tool returns 1 failed, 11 passed and is marked Checkpoint 1, the assistant calls Edit on app/math.py, the tool reports the file was updated and is marked Checkpoint 2, and the assistant replies that it fixed the off-by-one in add(). The sibling run on the right keeps messages 1 to 3, forks the transcript at Checkpoint 1, then adds a new user message asking to see the failing test before any edit and the assistant answers that test_add expects add(2, 2) == 4. Notes underneath say the checkpoints endpoint lists message_index 3, 5 and 6, and the sibling records the source run as forked_from_run_id with forked_from_message_index 3. Real output from Agno, with scripted Claude SDK messages standing in for the model.

AgentOS lists the checkpoints at GET /agents/{id}/runs/{run_id}/checkpoints?session_id={session_id}, including the boundaries after tool steps in a ClaudeAgent run. We ran the release with the test suite's scripted Claude SDK in place of a model, listed the checkpoints over HTTP, then forked from the first one in Python.

# real output, scripted Claude SDK messages in place of the model
GET /agents/fixer/runs/{run_id}/checkpoints?session_id=s   200
checkpoint 1  message_index=3  reason=checkpoint  tool       "1 failed, 11 passed"
checkpoint 2  message_index=5  reason=checkpoint  tool       "The file app/math.py has been updated."
checkpoint 3  message_index=6  reason=end         assistant  "Fixed the off-by-one in add()."
 
continue_run(continue_from=3, input=...)
fork_session_via_store  session=sdk-1  up_to=u-result-1
branch.run_id == source.run_id: False
branch.forked_from_run_id == source.run_id: True
branch.forked_from_message_index = 3
branch.content = "test_add expects add(2, 2) == 4."
source run messages = 6, content = "Fixed the off-by-one in add()."

A finished run is never rewritten. Continuing a completed run always writes a sibling run with forked_from_run_id and forked_from_message_index, even with fork=False. A run that never completed, such as one that errored partway, continues in place unless you pass fork=True, and a cancelled run can't be continued.

The other boundaries work the same way. continue_from="last_user" with no input replays the turn. Agno forks just before the prompt, or opens a fresh SDK session for the session's first turn, and sends the original prompt again. A mid-turn continue with no input sends "Continue from where you left off." Parallel tool calls share one checkpoint at the end of their batch, and tool calls inside a subagent are never checkpoints. Files the agent changed after the checkpoint stay changed on disk.

Continuing needs a run recorded with transcript storage, so the agent's db must be PostgresDb, AsyncPostgresDb, SqliteDb or AsyncSqliteDb. regenerate, replace_original, additional_instructions and human-in-the-loop tools are refused, with AgentOS answering 400.

See the cookbook, and learn more about replaying a Claude run in the documentation.

Frequently asked questions

List the run's checkpoints with GET /agents/{id}/runs/{run_id}/checkpoints?session_id={session_id}, then call agent.continue_run(run_id=..., session_id=..., continue_from=<message_index>, input="..."). Agno forks the stored Claude SDK transcript at that tool result and resumes the fork with your input.

A completed run stays untouched. Its continuation is saved as a new run in the same session with forked_from_run_id and forked_from_message_index set, whatever you pass for fork.

Call continue_run with continue_from="last_user" and no input. Agno forks the transcript just before the prompt, or opens a fresh SDK session when it was the first turn, and sends the original prompt again.

Runs must be recorded with transcript storage, which PostgresDb, AsyncPostgresDb, SqliteDb and AsyncSqliteDb provide. Runs stored without it raise an error explaining that only runs recorded with transcript storage can be continued.

Shipped around the same time