Appearance
Checkpoints
Checkpoints are thin ReAct-loop bookmarks stored under an agent’s session. They answer: where did this agent last successfully complete a loop boundary (after_model or after_tools)?
They do not store conversation messages (Session does), orchestrator position (Graph/Swarm session does), or human-in-the-loop pauses (Interrupts do).
Default model is Amazon Bedrock
Agent() with no model= uses Amazon Bedrock (Claude Sonnet 4.6 in us-west-2). Configure AWS credentials (aws configure or AWS_* env vars), or pass an explicit model — see Installation and Model Providers.
Session vs Checkpoint
| Feature | Persists | Resume unit |
|---|---|---|
| Session | Messages + agent state | Conversation continuity |
| Checkpoint | Loop boundary bookmark | Mid-tool-cycle continue |
| Graph/Swarm session | Orchestrator cursor + node results | Which node runs next |
| Interrupt | HITL pause state | Human response path |
Rules to remember:
- Checkpoint requires a
session_manager— otherwise Agent construction raisesValueError. - There is no public
resume()API — rebuild the Agent with the samesession_id+agent_idand invoke again. - Pass
CheckpointConfigonAgentConfig— do not pass aCheckpointinstance. - Side-effecting tools may retry after a crash — keep them idempotent.
Quickstart
python
from elsai import Agent, tool
from elsai.agent import AgentConfig
from elsai.checkpoint import CheckpointConfig
from elsai.session import FileSessionManager
SESSION_ID = "my-job" # persist with your chat/job id — do not rotate every turn
AGENT_ID = "worker"
@tool
def get_weather(city: str) -> str:
"""Return weather for a city."""
return f"Sunny, 72F in {city}"
agent = Agent(
tools=[get_weather],
config=AgentConfig(
agent_id=AGENT_ID,
session_manager=FileSessionManager(
session_id=SESSION_ID,
storage_dir="./sessions",
),
checkpoint=CheckpointConfig(position="both"),
),
)
result = agent("What's the weather in Paris? Use get_weather.")
print(result.resumed_from_checkpoint, result.resume_position)
# After a crash / process restart: rebuild with the same ids
agent = Agent(
tools=[get_weather],
config=AgentConfig(
agent_id=AGENT_ID,
session_manager=FileSessionManager(
session_id=SESSION_ID,
storage_dir="./sessions",
),
checkpoint=CheckpointConfig(position="both"),
),
)
result = agent("continue")
print(result.resumed_from_checkpoint, result.resume_position)How it works
Crash after after_model (tools not finished):
Invariant: at each boundary, messages are written first; the checkpoint bookmark is written last.
Configure save boundaries
python
from elsai.checkpoint import CheckpointConfig
CheckpointConfig(position="after_tools") # default
CheckpointConfig(position="after_model")
CheckpointConfig(position="both") # recommended for tool-heavy agentsposition | When it saves | Typical use |
|---|---|---|
"after_tools" (default) | After tool results are durable | Continue after tools complete |
"after_model" | After model tool_use is durable (tools may still be running) | Recover mid-tool crashes |
"both" | Both boundaries | Production tool agents |
The on-disk bookmark always has a single position ("after_model" or "after_tools"). "both" exists only on config.
Storage layout
Checkpoint state is one JSON file per agent (checkpoint.json), stored next to that agent’s messages. It is a thin bookmark — it does not embed conversation messages or tool payloads (those stay under messages/).
Under FileSessionManager / S3SessionManager:
text
session_<session_id>/
session.json
agents/
agent_<agent_id>/
agent.json
messages/
message_0.json
...
checkpoint.json # single overwrite fileExample checkpoint.json (active mid-tool bookmark):
json
{
"position": "after_model",
"cycle_index": 0,
"schema_version": "1.0",
"created_at": "2026-08-18T06:00:00Z",
"message_count": 2,
"agent_id": "worker",
"execution_status": "active"
}| Field | Meaning |
|---|---|
position | Boundary just completed: "after_model" or "after_tools" |
cycle_index | 0-based ReAct cycle at save |
schema_version | Must match the SDK ("1.0") |
created_at | ISO-8601 UTC timestamp |
message_count | len(messages) at save — used by the resume gate |
agent_id | Agent that wrote the bookmark |
execution_status | "active" while work may be unfinished; "completed" after a clean end_turn |
| Behaviour | Detail |
|---|---|
| Overwrite | Each save replaces the entire checkpoint.json |
| Missing file | No bookmark — normal turn |
| Corrupt / bad schema | Raises CheckpointException |
Clean end_turn | Same file overwritten with execution_status="completed" (not deleted) |
Resume semantics
Auto-resume runs only when the loaded bookmark is active and passes consistency checks (message count and pending-tool shape match).
| Bookmark | What happens on next invoke |
|---|---|
after_model (active, allowed) | New user text is queued; pending tools run first; then the queued text is applied |
after_tools (active, allowed) | New user text appends normally; the loop continues |
completed or gate deny | Messages-only continue (safe fallback; warning logged on deny) |
AgentResult fields reflect action, not mere load:
python
result = agent("continue")
print(result.resumed_from_checkpoint) # True only if this invoke acted on a bookmark
print(result.resume_position) # "after_model" | "after_tools" | NoneCrash recovery recipe
Rebuild the Agent with the same session_id and agent_id:
python
from elsai import Agent, tool
from elsai.agent import AgentConfig
from elsai.checkpoint import CheckpointConfig
from elsai.session import FileSessionManager
SESSION_ID = "order-42"
AGENT_ID = "fulfillment"
@tool
def process_order(order_id: str) -> str:
"""Process an order by id."""
return f"Processed {order_id}"
agent = Agent(
tools=[process_order],
config=AgentConfig(
agent_id=AGENT_ID,
session_manager=FileSessionManager(
session_id=SESSION_ID,
storage_dir="./sessions",
),
checkpoint=CheckpointConfig(position="both"),
),
)
result = agent("Process this order")
# After process death: same ids → auto-resume if bookmark is active
agent = Agent(
tools=[process_order],
config=AgentConfig(
agent_id=AGENT_ID,
session_manager=FileSessionManager(
session_id=SESSION_ID,
storage_dir="./sessions",
),
checkpoint=CheckpointConfig(position="both"),
),
)
result = agent("continue")Do not rotate session_id every turn. Persist it with your chat or job id.
With Graph and Swarm
Orchestrator session and node checkpoint are complementary:
- Graph/Swarm
session_manager→ which node runs next (multi_agent.json) - Node
session_manager+CheckpointConfig→ mid-loop tool resume (checkpoint.json)
Use one durable job session_id and a unique agent_id per node:
python
from elsai import Agent, tool
from elsai.agent import AgentConfig
from elsai.checkpoint import CheckpointConfig
from elsai.multiagent import GraphBuilder
from elsai.session import FileSessionManager
JOB_ID = "job-9"
STORAGE = "./sessions"
@tool
def get_weather(city: str) -> str:
"""Return weather for a city."""
return f"Sunny, 72F in {city}"
researcher = Agent(
tools=[get_weather],
system_prompt="Use get_weather when asked about weather. Keep answers short.",
config=AgentConfig(
agent_id="researcher",
session_manager=FileSessionManager(session_id=JOB_ID, storage_dir=STORAGE),
checkpoint=CheckpointConfig(position="both"),
),
)
writer = Agent(
system_prompt="Turn the upstream research note into one short sentence.",
config=AgentConfig(
agent_id="writer",
session_manager=FileSessionManager(session_id=JOB_ID, storage_dir=STORAGE),
checkpoint=CheckpointConfig(position="both"),
),
)
builder = GraphBuilder()
builder.add_node(researcher, "researcher")
builder.add_node(writer, "writer")
builder.add_edge("researcher", "writer")
builder.set_entry_point("researcher")
# Orchestrator cursor (which node next) — keep this in addition to node CP
builder.set_session_manager(FileSessionManager(session_id=JOB_ID, storage_dir=STORAGE))
graph = builder.build()
result = graph("What's the weather in Paris? Use get_weather, then summarize.")The same dual-layer pattern applies to Swarm: pass session_manager= on the Swarm and configure each node Agent with session + checkpoint.
Tool retries
Checkpoint recovery can retry a tool that was in flight at process death. Keep side-effecting tools idempotent or provide your own exactly-once protocol.
Interactions with other features
| Feature | How Checkpoint fits |
|---|---|
| Tools | Primary value of checkpoints; text-only turns may leave no bookmark or only a finalized one |
| Streaming | Same semantics inside stream_async; the final event’s AgentResult carries resume flags |
| Structured output | Internal SO tool cycles can leave an active bookmark briefly; next invoke may auto-resume or finalize |
| Hooks | Outer invoke hooks still fire on resume invokes |
| Conversation managers | Rebuild with the same CM config; bookmark message_count is against stored session messages |
| Interrupts | Orthogonal HITL pause — do not confuse with checkpoint auto-resume; both can coexist |
| Agent as tool | Per-agent session + CP; agents passed as tools need as_tool(preserve_context=True) (not mode="spawn") |
Errors and troubleshooting
| Situation | Behaviour |
|---|---|
checkpoint without session_manager | ValueError at Agent init |
Wrong type for checkpoint= (e.g. a Checkpoint instance) | TypeError |
| Checkpoint write fails mid-loop | Exception propagates (fail loud) |
| Corrupt file / schema mismatch | CheckpointException |
| Consistency gate deny | Warning log; messages-only continue |
Finalize after end_turn fails | Warning log; turn still succeeds |
Best practices
- Use stable
session_id/agent_idvalues tied to your chat or job. - Prefer
CheckpointConfig(position="both")for tool-heavy agents. - Keep tools idempotent when crash recovery matters.
- Use
FileSessionManagerorS3SessionManager(repository backends that implement checkpoint I/O). - Check
result.resumed_from_checkpointwhen you need to know if a half-cycle was recovered.
Avoid:
- Expecting a public
resume()method - Rotating
session_idevery turn - Relying on Graph/Swarm session alone to recover mid-tool loops
- Importing from
elsai.experimental.checkpoint
Related
- Sessions — conversation persistence
- Agent Loop — ReAct cycle and save points
- Interrupts — human-in-the-loop pauses
- Graph / Swarm — dual-layer recovery
- Checkpoint API — types and fields