A transaction framework for AI agents. Wraps every side-effecting action an agent takes (booking, charging, sending, writing) in a saga step with exactly-once execution, automatic rollback, and a full audit ledger.
AI agents can now plan and execute multi-step, real-world actions — book a flight, charge a card, send an email, update a record. But when a step fails, or a human reviewer rejects a plan halfway through, or the agent retries after a timeout, most teams have no standard way to:
- Undo what the agent already did (cancel the hotel it booked before the flight failed).
- Guarantee exactly-once execution when the agent retries a flaky step (so it doesn't double-charge a card).
- Produce an audit trail that's actually defensible to an auditor — not scattered application logs.
Enterprise research backs this up directly: 41% of enterprises report at least one production rollback of an AI agent in the last 12 months due to reliability issues, and agents without automated evaluation had a 47% rollback rate. The industry-recognized fix is a set of "reusable platform services" — identity, evaluation, observability, human approval, and rollback — that most teams are currently building ad hoc, per-project, if at all.
agent-saga is a small, focused implementation of the rollback + idempotency + audit piece of that stack, built on the distributed-systems Saga pattern that's long been standard for microservice transactions — applied here to agent tool calls instead.
Each step in a saga is defined as:
interface SagaStep<TContext> {
name: string;
execute(ctx: TContext): Promise<unknown>; // do the real thing
compensate(ctx: TContext, priorResult): Promise<void>; // undo it
idempotencyKey(ctx: TContext): string; // dedupe key for retries
isApprovalGate?: boolean; // pause for a human
}The SagaOrchestrator runs steps in order. If any step fails, or a human rejects an approval gate, it automatically runs compensate() for every step that already succeeded — in reverse order — and records the entire sequence of events to a Ledger.
If a step's idempotencyKey() has already been recorded (e.g. the agent is retrying after a timeout), the orchestrator skips re-execution and reuses the cached result — so a flaky retry never double-books a flight or double-charges a card.
npm install
# Happy path: books flight + hotel, gets approved, sends confirmation
npm run demo:approved
# Rejection path: books flight + hotel, human rejects, watch it roll both back
npm run demo:rejected
# Idempotency path: runs the same saga twice, second run skips already-done steps
npm run demo:retry
# Run the automated test suite
npm testRejecting the approval gate produces this ledger — notice the hotel is compensated before the flight, undoing the dependency chain in reverse:
[STEP_COMPLETED] book-flight
[STEP_COMPLETED] book-hotel
[APPROVAL_REJECTED] human-approval
[COMPENSATING] book-hotel
[COMPENSATED] book-hotel
[COMPENSATING] book-flight
[COMPENSATED] book-flight
[SAGA_ROLLED_BACK] _saga_
See ARCHITECTURE.md for the full component breakdown, sequence diagrams, and production-scaling notes (Kafka-backed ledger, Redis-backed idempotency store).
The demo uses hardcoded steps to keep the project dependency-free, but the same SagaStep interface is designed to wrap real LangGraph tool calls. See docs/langgraph-integration.md for a worked example of bridging a LangGraph agent's tool-calling loop into this orchestrator.
This is a working proof-of-concept, not a production-hardened library. It's built to demonstrate the pattern end-to-end (execution, rollback, idempotency, audit) with a real, runnable, tested demo — not to be a drop-in production dependency yet. See docs/production-notes.md for what a production hardening pass would need to add.
MIT