# Sessions & snapshots

Retain a conversation, save snapshots, and choose when to add durable execution.

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.

## What persists

Three memory layers sit under those states, all optional.

`ConversationMemory` is created for every agent. It holds the message list. As the context limit approaches, the harness summarizes the oldest turns with the agent's own model and fires `HookEvent.CONTEXT_CONDENSED`; see [Long-run reliability](../loop-reliability/#condensing-a-history-that-has-outgrown-its-window). A snapshot stores it; nothing else does.

~~~python
agent.memory.get_messages()        # current message list
agent.memory.set_messages(saved)   # restore
~~~

`AgentMemory` writes titled, tagged notes as markdown files on disk. `LongTermMemory` stores categorized facts with ids. Both outlive the process on their own. `register_memory_tools` gives the model the built-in `save_memory` and `recall_memories` tools over the stores you pass, and your own tools can share the same stores.

~~~python
from harnessx import AgentMemory, LongTermMemory, PermissionLevel
from harnessx.builtin import register_memory_tools

notes = AgentMemory(storage_dir=".agent_memory/agent")
facts = LongTermMemory(storage_dir=".agent_memory/long_term")

register_memory_tools(agent.tools, long_term=facts, agent_memory=notes)

@agent.tools.register(permission=PermissionLevel.ALLOW)
async def save_note(title: str, content: str, tags: str = "") -> str:
    """Save a titled note.

    Args:
        title: Short title for the memory.
        content: The information to remember.
        tags: Comma-separated tags.
    """
    return notes.save(title, content, tags=[t.strip() for t in tags.split(",") if t.strip()])

fact_id = facts.save("User prefers dark mode", category="preference")
facts.update(fact_id, "User prefers light mode")
facts.search(query="dark mode")
~~~

Tell the agent in the system prompt which tools exist and when to use them. `AgentMemory.search(query=...)` returns matching notes. `examples/04-context/custom_memory_tools.py` combines the built-in tools with two of its own over the same stores.

## Save a conversation snapshot

~~~python
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.

Try it with no API key: `uv run python examples/06-durability/session_snapshots.py`.

Before summarizing, the harness first clears old tool output, and
`agent.memory.prune_tool_results()` runs that step on demand. Snapshots also
carry the estimated cost and the model limits learned from provider refusals,
scoped to each provider and model.

## 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](../durable-agent-runs/)**. The [PostgreSQL walkthrough](../backends/#crash-and-resume) 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.

## 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.
