With PostgresDb, AsyncPostgresDb, SqliteDb or AsyncSqliteDb as its db, Agno's ClaudeAgent hands Claude Code a transcript store. Each line Claude Code appends to its local transcript also becomes a row in the new agno_transcripts table, and the Claude session id sits in session_data["claude_sdk_session_id"]. A replica that never ran the session passes that id to Claude Code as resume, and Claude Code loads the conversation from the rows.
Claude Code keeps every conversation as a transcript file on the disk of the machine that ran it. Earlier releases kept the session id only in process memory, so after a restart, or when a turn reached another AgentOS worker, Claude quietly began a new conversation while the AgentOS UI still showed the full history.

Give the agent a supported database and the same id on every replica:
from agno.agents.claude import ClaudeAgent
from agno.db.postgres import PostgresDb
agent = ClaudeAgent(
id="support-agent", # also the transcript project_key, keep it stable across replicas
db=PostgresDb(db_url="postgresql+psycopg://ai:ai@localhost:5532/ai"),
)
agent.run("When is the deploy window?", session_id="agno-session-1")Transcripts are keyed by project_key, which defaults to the agent id. Set your own stable, tenant-specific key when several agents share one database. The store keeps append order across processes and skips entries whose uuid it already holds.
What the table holds
We ran two replicas as separate Python processes against one SQLite file, each with its own working directory. A stand-in replaced Claude Code and the model: like the real SDK, it resumes a session only from a transcript it can find, and here the only place to find one is the store Agno attaches. On the previous release, replica B started a new Claude session (resume=None) and nothing was stored. The output below is real, from this release.
[replica-a] session_data.claude_sdk_session_id=a33d4c0a-0022-4734-883c-0cc0b5eaab62
[replica-b] SDK asked to resume=a33d4c0a-0022-4734-883c-0cc0b5eaab62 prompt='When is the deploy window?'
[replica-b] status=COMPLETED content='Earlier you told me: Remember this: the deploy window is Tuesday 14:00'
agno_transcripts
project_key | session_id | subpath | position | type | agno_session_id
support-agent | a33d4c0a-0022-4734-883c-0cc0b5eaab62 | | 1 | user | agno-session-1
support-agent | a33d4c0a-0022-4734-883c-0cc0b5eaab62 | | 2 | assistant | agno-session-1
support-agent | a33d4c0a-0022-4734-883c-0cc0b5eaab62 | | 3 | user | agno-session-1
support-agent | a33d4c0a-0022-4734-883c-0cc0b5eaab62 | | 4 | assistant | agno-session-1Each row also carries framework (claude-agent-sdk), the line's entry_uuid, the raw JSON entry and created_at. The stand-in sent a project key derived from each replica's working directory, and the store wrote support-agent for both, which is why replica B finds replica A's lines.
When the session cannot resume
If Claude Code reports the stored session as missing before it sends any message, Agno logs a warning, forgets the id and starts a new Claude session with the Agno history written into the prompt. Deleting the rows and running a third replica showed it:
WARNING Claude SDK: could not resume session a33d4c0a-0022-4734-883c-0cc0b5eaab62 for session agno-session-1: Command failed with exit code 1. Starting a new one.
[replica-c] SDK asked to resume=None prompt='Previous conversation (for context, do not repeat it):\nuser: Remember this: the deploy window is Tuesday 14:00\nassistant: OK\nuser: When is the deploy window?\nassistant: Earlier you told me: Remember this: the deploy window is Tuesday 14:00\n\nCurrent message:\nWhen is the deploy window?'
[replica-c] session_data.claude_sdk_session_id=f97dfb7c-aee3-4fce-9b8a-8bf4976e0f39Errors that arrive after Claude has started streaming still fail the run, so no output is duplicated, and an API error on resume keeps the stored id. If mirroring a line keeps failing, the run still completes and records a transcript_persistence_failed warning in RunOutput.metadata["warnings"].
Agno skips the store when the database has no transcript support, when you pass your own session_store, or when enable_file_checkpointing is on, and logs which case applies. Claude and Codex runs without streaming now also keep their tool calls and results in the stored run history. The release's own tests pass against this setup, with 19 store tests on SQLite and async SQLite, including the Claude SDK's session store conformance suite, and 17 ClaudeAgent persistence, resume and fallback tests against their fake SDK.
See the cookbook, and learn more about Claude native transcripts in the documentation.
Frequently asked questions
With PostgresDb, AsyncPostgresDb, SqliteDb or AsyncSqliteDb as the agent's db, every transcript line goes into the agno_transcripts table, one row per line in append order. The Claude session id sits in the Agno session's session_data under claude_sdk_session_id.
Point every replica at the same supported database and give the agent the same id everywhere, or set project_key yourself. Transcripts are keyed by project_key, which defaults to the agent id. Use a stable, tenant-specific key when several agents share one database.
If Claude Code reports the session as missing before it sends any message, Agno logs a warning, drops the stored id and starts a new Claude session with the earlier Agno conversation written into the prompt. Other errors, such as an overloaded API, keep the stored id and fail the run.
PostgresDb, AsyncPostgresDb, SqliteDb and AsyncSqliteDb. With any other database Agno logs a warning and Claude Code keeps its transcript on the local disk, so only that machine can resume it natively.


