Open source · Apache-2.0
The transactional rollback engine for autonomous AI agents.
Agents mutate real systems — databases, CRMs, payments — and they do it non-deterministically. When one hallucinates mid-run, SagaOps unwinds every side effect it caused, in reverse order — or refuses to start the one thing it could never undo.
pip install agent-saga
“Undo” is not one thing. It's three.
REVERSIBLE · COMPENSABLE · IRREVERSIBLE
Run it yourself
The same workflow, with the switch off and on.
Three steps: charge $499.00, provision the account, send the welcome mail. The mail server refuses the connection. Flip the switch and watch what each run leaves behind.
What you actually change
One wrapper, one inverse per call.
# nothing unwinds. an exception just propagates. charge = stripe.charge(customer, 49900) account = db.insert_account(customer, email) mail.send_welcome(email) # ← raises
async with saga_scope() as saga: charge = await saga.execute( tool="stripe.charge", semantics=COMPENSABLE, forward=lambda: stripe.charge(customer, 49900), # the inverse, derived from the RESULT: compensate=lambda r: Compensation( fn=refund, handler="stripe.refund", kwargs={"charge_id": r["id"]})) account = await saga.execute(...) # same shape await saga.execute(...) # ← raises here # charge refunded, account deleted, LIFO, on the way out
The only new idea is compensate: a factory that receives the
forward call's result, because you cannot refund a charge_id you have not seen yet.
Why it exists
A rollback layer built for agents, not for backups.
Two things people reach for don't fit. SagaOps sits exactly where they fall short.
Backups restore a machine. They don't unwind a workflow.
Snapshotting a whole database can't help when an agent charged a card, emailed a customer, and updated three SaaS systems in ninety seconds. SagaOps is a developer-first library — a decorator and a proxy, not an ops platform — that undoes the actions, in order.
You can't hold BEGIN…COMMIT open while a model thinks.
A transaction locks rows and pins a connection for the seconds an LLM takes to decide — and it can't span Stripe, Salesforce and Postgres at once anyway. SagaOps records forward calls and their runtime-derived inverses, then compensates LIFO across every system the agent touched.
Wrap a boundary · watch it unwind
One saga_scope. Every side effect accounted for.
from agent_saga import saga_scope from agent_saga.connectors import stripe, salesforce async with saga_scope() as saga: await salesforce.patch_object(saga, object_type="Lead", object_id="00Q…", patch={"Status": "Won"}, credential_ref="sf") await stripe.charge(saga, customer_id="cus_…", amount=42000, credential_ref="stripe_prod") # the agent hallucinates a field and raises → # charge refunded, Lead reverted, LIFO, on the way out. # had a step been IRREVERSIBLE, the pre-flight # gate would have refused to start it at all.
Representative view of the built-in time-travel debugger, rendered from the WAL.
The engine
Four parts. The gate is the one a risk committee buys.
Every effect is classified before it runs.
REVERSIBLE restores exactly. COMPENSABLE emits an inverse that leaves an honest trace (a refund, not a rewind). IRREVERSIBLE can't be undone — so it's never quietly attempted.
Refuse the uncompensable, up front.
The gate evaluates a tool call — including its arguments — before the side effect. Cross an irreversible boundary or a value threshold and it pauses for a human. That refusal is the contract, not the cleanup.
Intent is durable before the effect.
A tiered WAL fsyncs the plan for money-path steps and rides a lock-free fast path for the rest. Optional BYOK at-rest encryption; a configurable backpressure policy that never silently drops a record.
A crash mid-saga isn't an orphan.
If the process dies after a charge but before the rollback, an independent daemon reads the WAL, runs the compensations by name, and fails closed to a human queue for anything it can't resolve with certainty — no double refunds.
The wedge
The agent chose the action. So the inverse is derived at runtime.
Durable-workflow engines make a developer declare each compensating step at authoring time. But an LLM picks the tool, and its arguments, while it runs — the refund can't be known until the charge returns its id. SagaOps derives the compensating action from the forward call's actual result. That's the part a statically declared workflow can't express.
Batteries, not lock-in
Wraps the tools your agents already call.
Connectors compensable
Stripe (idempotent refunds), Postgres (full CRUD, compound keys, concurrency-guarded restore), Salesforce (field-scoped revert).
Framework adapters
LangGraph, CrewAI, and the OpenAI Agents SDK — wrap a tool, keep its name and schema, and it records on the active saga.
Snapshot capture
In-process reversible state and durable, crash-recoverable file snapshots — with a conservative store GC that never reaps what a rollback still needs.
Time-travel debugger
A zero-dependency WAL viewer: every saga, every step, latency, and what could not be undone — secrets scrubbed on read.
Credential-safe by design
Compensations carry credential references, never values. An authoring-time guard refuses a secret before it can reach the log.
Zero-dependency core
pip install and go. No broker, no Redis, no service to stand up. Extras are opt-in, per connector.
Deploy agents you can actually roll back
Start in one line.
pip install agent-saga