Skip to main content
A mirror replicates each persisted turn to a remote backend on a background thread, so a session can follow the user across machines — local disk stays the source of truth.

Quick Start

1

Plug a mirror into DefaultSessionStore

The local file at ~/.praisonai/sessions/user-42.json and the mirror both receive the turn.
2

Continue on another machine

A configured mirror’s load(session_id) returns the full transcript, so a fresh install can pick up the conversation.

How It Works

The store writes local disk first and returns; the record is then handed to a bounded queue drained by a daemon thread that calls mirror.append. The failure modes never touch the local write:

Configuration Options

SessionMirrorProtocol methods

The runtime_checkable protocol lives at praisonaiagents.session.SessionMirrorProtocol.

DefaultSessionStore(mirror=…)

Writing a Mirror

A mirror is three tiny methods — the LoggingMirror from Quick Start is the whole story.
Concrete wrapper-side adapters (postgres/supabase/turso/sqlite) land in praisonai.persistence.conversation.* alongside store-unification (#3645). Until then, implement the protocol directly against your backend.

Local-First Guarantees

The mirror is fed after the message is persisted to the session file, off the lock. A mirror outage never reaches the caller as a failure.
Records are enqueued onto a bounded queue.Queue (DEFAULT_MIRROR_QUEUE_SIZE = 1000) drained by a daemon thread (praisonai-session-mirror).
On Queue.Full, records are dropped with a warning pointing operators at the (future) session sync reconciler; the local file is unaffected.
_SessionMirrorWriter._flush_one retries DEFAULT_MIRROR_MAX_RETRIES = 3 times with 50ms → 1s exponential backoff before logging and moving on.
The writer is only instantiated when mirror is not None — no queue, no thread, no import cost.
After close_mirror(), further enqueues drop-to-log rather than leaving records stranded with no consumer.

Testing & Reconciliation

Use flush_mirror(timeout=…) to make mirroring deterministic in a test and close_mirror() to drain in a teardown.
The CLI-facing session sync reconciler is not yet shipped (blocked on #3645). Until then, flush_mirror is the deterministic seam for reconciliation and tests.

Backward Compatibility

mirror=None is the default — existing DefaultSessionStore(...) call sites are byte-for-byte unchanged, with no daemon thread spawned and no dependency added.

Best Practices

Records carry stable ids, so re-appending the same record is last-writer-wins. Don’t add merge logic — rely on the id.
The writer retries with backoff and drops-to-log on permanent failure. Let it — raising in append just burns a retry.
append runs on the single writer thread. Keep it a fast network/DB write so the queue drains and doesn’t back up.
close_mirror() drains in-flight records before stopping the daemon writer. Skipping it can drop the last queued turns.

Session Store

The default JSON store the mirror wires into

Cross-Session Recall

Search past sessions — hydrate non-local ids from a mirror

Sessions & Remote Agents

Stateful conversations across restarts

Gateway Session Persistence

Persist gateway sessions across restarts