Choose persistence based on what you need to recover: a conversation, or unfinished execution.
Three kinds of state
| State | Storage | Recovery |
|---|---|---|
| Ordinary Agent | Process memory | Lives as long as the instance |
| Agent snapshot | Explicit files in a session directory | Restore configuration, messages, usage, and supported extension state |
| AgentRuntime | SQLite, PostgreSQL, or Temporal | Resume persisted execution under replay and approval policies |
Repeated calls to the same Agent keep its conversation. That alone does not make a session durable.
Save a conversation snapshot
from harnessx import Agent
async def save_and_continue():
async with Agent() as agent:
await agent.run("Remember that this project analyzes invoices.")
session_id = await agent.save_session(storage_dir=".agent_sessions")
async with await Agent.load_session(
session_id, storage_dir=".agent_sessions"
) as restored:
result = await restored.run("What does this project analyze?")
return result
Snapshots contain serializable state, not executable Python handlers or live MCP connections. Rebind custom tools and provide compatible extensions when loading them. Supply the same extension names; explicitly supplied configuration must match the snapshot. Snapshots written by harnessx 0.3 load in 0.4 (AgentConfig.from_dict() reads both configuration shapes); state written by 0.4 does not load in 0.3.
When you need crash recovery
Use AgentRuntime when you need to recover an unfinished run or preserve an approval wait across process restarts. This is covered under Advanced topics → Durable agent runs.
That guide explains AgentRuntime(agent, backend=backend), session IDs and run IDs, explicit resume, RunHandle, RunResult, event cursors, approvals, and resource ownership. The PostgreSQL walkthrough provides a runnable crash/restart demonstration.
Saving a conversation snapshot and persisting execution are separate operations. Loading a snapshot does not resume an in-flight tool call.
Browser chat state is separate
The current harness-web demo keeps chat IDs, run IDs, messages, and live MCP connections in server memory. Skills and generated files live on disk. Browser refresh preserves a live server's conversations; server restart does not restore them. The demo has not yet adopted durable web session storage.
Ownership
A runtime closes the agent it executes. It closes a backend only if it created that backend. Explicitly close injected backends, providers, and other caller-owned resources. Agent instances support one active run at a time.