agent_effects/lib.rs
1//! Reliable side-effect execution for AI agents and autonomous applications.
2//!
3//! `agent-effects` puts a durable execution boundary around operations that
4//! change the outside world: charging a card, sending an email, provisioning a
5//! server. It records intent before acting, distinguishes "failed" from "don't
6//! know", and resolves unknown outcomes by verification or idempotent retry
7//! instead of guessing.
8//!
9//! It does not provide exactly-once side effects. Nothing can, without
10//! cooperation from the target system. It provides controlled execution and
11//! deduplication, and refuses to retry when a retry could duplicate an effect.
12//!
13//! ```
14//! use agent_effects::{EffectFailure, EffectKind, EffectOutcome, Runtime};
15//! use agent_effects_memory::MemoryStore;
16//!
17//! # #[tokio::main(flavor = "current_thread")]
18//! # async fn main() -> Result<(), agent_effects::RuntimeError> {
19//! let runtime = Runtime::new(MemoryStore::new());
20//!
21//! let outcome = runtime
22//! .effect("ticket.create", "incident-42")
23//! .kind(EffectKind::IrreversibleWrite)
24//! .input(&"disk full on db-1")
25//! .run(|ctx| async move {
26//! // Call the remote system here, sending ctx.idempotency_key().
27//! Ok::<_, EffectFailure>(format!("TICKET-1 (attempt {})", ctx.attempt()))
28//! })
29//! .await?;
30//!
31//! match outcome {
32//! EffectOutcome::Committed(ticket) => println!("created {ticket}"),
33//! EffectOutcome::Unknown { id } | EffectOutcome::NeedsIntervention { id } => {
34//! println!("effect {id} may have happened; not retrying blindly")
35//! }
36//! other => println!("{other:?}"),
37//! }
38//! # Ok(())
39//! # }
40//! ```
41//!
42//! Beyond running effects with retries, timeouts, preconditions and
43//! verification, the runtime finishes registered [handlers](handler)
44//! without a caller after a crash ([`recovery`]), undoes effects
45//! ([`compensation`]), waits for [`approval`], applies a risk [`policy`],
46//! keeps secrets out of storage ([`redaction`]), reports metrics to
47//! [observers](observer), and prunes old records ([`retention`]).
48//!
49//! Stores: `agent-effects-memory` (tests), `agent-effects-sqlite`,
50//! `agent-effects-postgres`. The persisted vocabulary (identity, kinds, the
51//! state machine) and the [`EffectStore`] contract come from
52//! [`agent-effects-store`](store) and are re-exported here.
53
54pub mod approval;
55pub mod clock;
56pub mod compensation;
57pub mod effect;
58pub mod error;
59pub mod fault;
60pub mod handler;
61pub mod observer;
62pub mod policy;
63pub mod recovery;
64pub mod redaction;
65pub mod retention;
66pub mod retry;
67pub mod runtime;
68#[cfg(feature = "testkit")]
69pub mod testkit;
70pub mod verification;
71
72mod fingerprint;
73
74pub use agent_effects_store as store;
75pub use agent_effects_store::{failure, id, kind, state};
76
77pub use agent_effects_store::{
78 Disposition, EffectId, EffectKey, EffectKind, EffectName, EffectRecord, EffectStatus,
79 EffectStore, ErrorRecord, FailureClass, IdempotencyKey, IdentityError, InvalidTransition,
80 Lease, LogicalKey, StoreError, Transition, WorkerId,
81};
82pub use approval::{ApprovalDecision, ApprovalProvider, ApprovalRequest, CliApproval};
83pub use clock::{Clock, ManualClock, SystemClock, TokioClock};
84pub use compensation::{CompensationBuilder, CompensationContext, CompensationOutcome};
85pub use effect::{EffectBuilder, EffectContext, EffectFailure, EffectOutcome, Precondition};
86pub use error::RuntimeError;
87pub use handler::{
88 CompensableEffect, CompensationSubmission, EffectHandler, Handler, Submission, VerifiableEffect,
89};
90pub use observer::{EffectObserver, Observation};
91pub use policy::{Capabilities, PolicyBuilder, Requirements, RiskLevel, RiskPolicy, UnknownPlan};
92pub use recovery::{RecoveryReport, Resolution};
93pub use redaction::{RedactKeys, Redactor, Secret};
94pub use retention::{PruneReport, RetentionPolicy};
95pub use retry::RetryPolicy;
96pub use runtime::{Runtime, RuntimeBuilder};
97pub use verification::{NotFoundReading, Verification, VerificationMode};