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?”
~/.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 toLOST.
Behaviour on gateway restart
The gateway wires this recovery automatically — you don’t callrecover_orphans() yourself. On boot, after resuming interrupted turns, it reconciles the ledger and notifies each lost run’s origin.
_recover_orphaned_runs()runs on boot, right after_resume_interrupted_turns().recover_orphans()terminalises every still-active run toLOST, preserving itschannelandthread_id.- Each
LOSTrun’s origin receives a durable restart notice, enqueued into the SQLitescheduled_outboxunder a stablerun-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. - It’s a no-op when core lacks the ledger or
ledger.dbdoesn’t exist yet — gateways that never used the ledger are unaffected, and no empty DB is ever created.
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 stdlibsqlite3.
- No new dependencies — stdlib
sqlite3only. - 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 runpraisonai 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
The gateway calls recover_orphans() for you at boot
The gateway calls recover_orphans() for you at boot
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.Always set channel and thread_id
Always set channel and thread_id
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.Update status at each transition
Update status at each transition
Call
upsert() as the run moves queued → running → waiting → succeeded/failed. The more current the status, the fewer false LOST reconciliations after a restart.Swap the store for heavier backends
Swap the store for heavier backends
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.Related
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.

