Skip to main content
Give a background agent a stable ID that survives a restart — if PraisonAI crashes mid-run, the ledger reconciles it to LOST so you can wake the user and re-route.
Complements — does not replace — the Run-State Journal. The ledger tracks run status (queued/running/done/failed/lost); the journal tracks the per-event cursor (model decision, tool call, tool result, iteration index) so a crashed run can resume without re-executing tools or re-billing LLM calls. Use the ledger to answer “is this run alive?”; use the journal to answer “where in the loop did it die?”
No config, no new dependencies — SQLite lives at ~/.praisonai/runs/ledger.db.

Quick Start

1

Get the default ledger

2

Track a run

3

Reconcile on startup

recover_orphans() returns the list of records it marked LOST. It preserves each run’s channel and thread_id so the gateway can wake the same user back.

How It Works

A run is recorded as it starts, updated as it progresses, and finalised with a terminal status. If a process dies while a run is still active, the next boot reconciles it to LOST.

Behaviour on gateway restart

The gateway wires this recovery automatically — you don’t call recover_orphans() yourself. On boot, after resuming interrupted turns, it reconciles the ledger and notifies each lost run’s origin.
  1. _recover_orphaned_runs() runs on boot, right after _resume_interrupted_turns().
  2. recover_orphans() terminalises every still-active run to LOST, preserving its channel and thread_id.
  3. Each LOST run’s origin receives a durable restart notice, enqueued into the SQLite scheduled_outbox under a stable run-recovery:<run_id> key — exactly-once even across a crash loop. A boot-time miss (transport not ready yet) leaves a pending row that drains on the next boot.
  4. It’s a no-op when core lacks the ledger or ledger.db doesn’t exist yet — gateways that never used the ledger are unaffected, and no empty DB is ever created.
The user-facing message the origin receives:
This is the built-in gateway behaviour. The manual patterns below still work if you build your own runtime on the ledger, but a standard praisonai gateway start already does this for you. See also Gateway Restart Continuation › Run ledger recovery.

Run statuses

RunStatus partitions every run into active (recoverable) or terminal (done). Check the partition with the is_active / is_terminal helpers:
RunStatus is a str enum, so RunStatus.RUNNING == "running".

Configuration Options

RunRecord

A durable record of a single run. channel and thread_id capture the origin route so the gateway can wake the user back. to_dict() / from_dict() roundtrip a record to a JSON/SQLite-friendly dict and back.

RunLedgerProtocol

The pluggable store contract — swap in a heavier backend by implementing these four methods.

SQLiteRunLedger

The zero-dependency default, backed by stdlib sqlite3.
  • No new dependencies — stdlib sqlite3 only.
  • Thread-safe — a re-entrant lock guards a shared WAL connection.
  • recover_orphans() preserves the origin route and is idempotent (a second call returns []).
  • close() releases the connection; the file persists across restarts.

Common Patterns

Mark a run terminal on success

Manual integration (custom hosts)

When you run praisonai gateway start, boot recovery is automatic (see Automatic on gateway boot) — you do not need this pattern. It remains the way to wire recovery into a custom, non-gateway process.

List recent runs regardless of status


Best Practices

A standard praisonai gateway start runs reconciliation automatically on boot, before accepting new work, and notifies each lost run’s origin durably. Only call recover_orphans() yourself when you build a custom runtime on the ledger directly.
These fields are the only way the gateway can wake the right user back. Set them when the run starts so a later LOST reconciliation can reach the origin thread.
Call upsert() as the run moves queued → running → waiting → succeeded/failed. The more current the status, the fewer false LOST reconciliations after a restart.
SQLiteRunLedger is the default, but any object implementing RunLedgerProtocol (Postgres, Redis, a hosted queue) drops in unchanged — the gateway only depends on the protocol.

What The User Sees

1

User asks for a long task

A user messages a Telegram bot: “Research the top 10 databases and write a comparison.” The agent starts, and the run is recorded as RUNNING with channel="telegram" and the user’s thread_id.
2

PraisonAI restarts mid-run

The process is killed or crashes before the run finishes. In-memory state is gone, but the ledger row on disk survives.
3

The gateway wakes the user back

On boot, the gateway automatically calls recover_orphans(), marks the run LOST, and posts a durable notice back to the same Telegram thread: “A background run was interrupted by a restart… Please resend your request.” No user code required.

Background Tasks

Run agent work in the background and collect results later.

Background Subagents

Spawn subagents that return a job ID immediately — the general-purpose ledger backs their durable state.

Restart Continuation

The boot recovery that runs ledger reconciliation alongside interrupted-turn resumption.