Skip to main content

ReplayCursor

Struct ReplayCursor 

Source
pub struct ReplayCursor { /* private fields */ }
Expand description

The pure replay/live cursor over one run’s event log.

Construct it with the recorded log (possibly empty, for a fresh run) and drive it with one request per orchestration step. Each request returns an Outcome: Outcome::Replayed while history covers the step, Outcome::Live from the first uncovered step onward. See the module docs for the full protocol and how RunCtx wraps it.

Implementations§

Source§

impl ReplayCursor

Source

pub fn new(log: Vec<EventEnvelope>) -> Result<ReplayCursor, ReplayError>

Builds a cursor over a run’s recorded log.

An empty log is a fresh run: every request will be live. A non-empty log must be well formed: it starts with a run head (Event::RunStarted for an agent run or Event::GraphRunStarted for a graph run) at position 0, all envelopes share one run id, positions are contiguous, and nothing follows a terminal event.

§Errors

Returns ReplayError::MalformedLog when the log violates any of those shape rules.

Source

pub fn run_id(&self) -> Option<RunId>

The run id the log carries, or None for a fresh (empty) log.

Source

pub fn is_replaying(&self) -> bool

Whether recorded history remains to be consumed.

true means the next request will be answered from the log (or fail as a divergence); false means the cursor has handed off to live mode.

Source

pub fn is_finished(&self) -> bool

Whether a terminal event (Event::RunCompleted or Event::RunFailed) has been consumed or emitted.

Source

pub fn next_seq(&self) -> SequenceNumber

The position the next consumed or emitted event occupies.

Source

pub fn begin( &mut self, agent_def_hash: &str, labels: Option<BTreeMap<String, String>>, ) -> Result<Outcome<Value, BeginPermit<'_>>, ReplayError>

Requests the start of the run.

Replayed: verifies agent_def_hash against the recorded Event::RunStarted (a changed agent definition must not silently resume an old run) and returns the recorded input. Live: returns a BeginPermit the caller redeems with the run’s input.

labels is the optional correlation tags to stamp on a genuinely fresh Event::RunStarted; see the field’s docs. It plays no part in replay: a recorded RunStarted is matched on agent_def_hash alone, and whatever labels the caller passes here is ignored on that path, exactly as ReplayCursor::model_call’s request_body is ignored when replaying. Bounds on labels are not checked here (see the field’s docs for why); a caller enforces them before calling.

§Errors

ReplayError::Divergence on a hash mismatch, a different recorded event, or a request after the run already ended.

Source

pub fn begin_graph( &mut self, graph_hash: &str, labels: Option<BTreeMap<String, String>>, forked_from: Option<ForkOrigin>, ) -> Result<Outcome<Value, GraphBeginPermit<'_>>, ReplayError>

Requests the start of a graph run.

The graph-run counterpart of begin: the log opens with Event::GraphRunStarted rather than Event::RunStarted, because a graph run coordinates many agent hashes and has none at its head. Replayed: verifies graph_hash against the recorded head (a changed graph document must not silently resume an old run) and returns the recorded input. Live: returns a GraphBeginPermit the caller redeems with the run’s input.

labels and forked_from land on a genuinely fresh Event::GraphRunStarted; they play no part in replay, exactly as begin’s labels do. Bounds on labels are not checked here; a caller enforces them before calling.

§Errors

ReplayError::Divergence on a hash mismatch, a different recorded event, or a request after the run already ended.

Source

pub fn node_entered( &mut self, node: &str, ) -> Result<Outcome<(), Emitted>, ReplayError>

Requests entry into a graph node.

Replayed: matches the recorded Event::NodeEntered for node. Live: returns the event to persist. A graph node’s own events (an agent loop’s model calls, a tool call) are recorded between this and the matching node_exited.

§Errors

ReplayError::Divergence on a node-id mismatch, a different recorded event, or a request after the run already ended.

Source

pub fn node_exited( &mut self, node: &str, ) -> Result<Outcome<(), Emitted>, ReplayError>

Requests exit from a graph node, having produced its output.

Replayed: matches the recorded Event::NodeExited for node. Live: returns the event to persist. The counterpart of node_entered.

§Errors

ReplayError::Divergence on a node-id mismatch, a different recorded event, or a request after the run already ended.

Source

pub fn node_skipped( &mut self, node: &str, reason: &str, ) -> Result<Outcome<(), Emitted>, ReplayError>

Records (or replays) that a graph node was skipped: reached on the walk but deliberately not run (a branch routed past it). No node_entered precedes a skip; the skip is the sole marker for the node.

Replayed: matches the recorded Event::NodeSkipped for node (reason included, so a skip that recorded one reason live cannot replay under another). Live: returns the event to persist. The reason a caller passes must be a pure function of the document and recorded values, like every other emitted payload, so it reproduces on replay.

§Errors

ReplayError::Divergence on a node-id or reason mismatch, a different recorded event, or a request after the run already ended.

Source

pub fn branch_taken( &mut self, node: &str, case: &str, ) -> Result<Outcome<(), Emitted>, ReplayError>

Records (or replays) that a branch node routed: the named case fired. This is the sole recorded authority for which way a branch went; the executed path is read from these events, never inferred from skips.

Recorded between the branch’s node_entered and node_exited. Replayed: matches the recorded Event::BranchTaken for node and case (a branch that fired one case live cannot replay having fired another). Live: returns the event to persist. The chosen case must be a deterministic function of recorded values (a pure expression over the routed value, or a decision recomputed from a replayed model reply), so replay reproduces the same route.

§Errors

ReplayError::Divergence on a node-id or case mismatch, a different recorded event, or a request after the run already ended.

Source

pub fn map_fanned_out( &mut self, node: &str, items: &Value, ) -> Result<Outcome<(), Emitted>, ReplayError>

Records (or replays) that a map node fanned out over a resolved item list.

Recorded between the map node’s node_entered and its per-iteration markers. Replayed: matches the recorded Event::MapFannedOut for node and items (the resolved list is part of the recorded fact, so a fan-out that recorded one list live cannot replay under another). Live: returns the event to persist. The items a caller passes must be a deterministic function of recorded values (the over reference resolved against the recorded routed value) so replay reproduces the identical fan-out, which is what makes the derived per-iteration child ids reproducible.

§Errors

ReplayError::Divergence on a node-id or item-list mismatch, a different recorded event, or a request after the run already ended.

Source

pub fn map_iteration_started( &mut self, node: &str, index: u64, child_run: &str, ) -> Result<Outcome<(), Emitted>, ReplayError>

Records (or replays) that one iteration of a map fan-out started, as a child run with the derived id child_run.

Replayed: matches the recorded Event::MapIterationStarted on its STRUCTURAL position — node and index — and the RECORDED child_run wins, exactly as the recorded input wins in begin. The passed child_run is compared only loosely (it is derived data), for a deliberate reason: the id is sha256: over the parent run id, the node, and the index, and a FORK replays the origin’s prefix under a NEW run id, so a caller re-deriving the id there produces a different value than the origin recorded. Trusting the recorded id keeps a fork’s inherited map markers replayable while node/index still pin the structural position. Live: returns the event to persist with the caller’s derived child_run.

§Errors

ReplayError::Divergence on a node-id or index mismatch, a different recorded event, or a request after the run already ended.

Source

pub fn map_iteration_joined( &mut self, node: &str, index: u64, ) -> Result<Outcome<(), Emitted>, ReplayError>

Records (or replays) that one iteration of a map fan-out joined back into the map node’s output.

Joins are recorded in index order, never completion order, so the concurrency of the fan-out never influences the parent log’s byte sequence. Replayed: matches the recorded Event::MapIterationJoined for node and index. Live: returns the event to persist.

§Errors

ReplayError::Divergence on a node-id or index mismatch, a different recorded event, or a request after the run already ended.

Source

pub fn now( &mut self, ) -> Result<Outcome<OffsetDateTime, NowPermit<'_>>, ReplayError>

Requests the current time.

Replayed: returns the recorded instant, bit for bit. Live: returns a NowPermit; the caller reads a real clock and redeems the permit with the reading.

§Errors

ReplayError::Divergence when the log records a different kind of event here, or the run already ended.

Source

pub fn random(&mut self) -> Result<Outcome<u64, RandomPermit<'_>>, ReplayError>

Requests random bits.

Replayed: returns the recorded bits, exactly. Live: returns a RandomPermit; the caller draws from a real random source and redeems the permit with the draw.

§Errors

ReplayError::Divergence when the log records a different kind of event here, or the run already ended.

Source

pub fn model_call( &mut self, request_hash: &str, request_body: Option<Value>, ) -> Result<Outcome<ModelReply, ModelCallPermit<'_>>, ReplayError>

Requests a model call identified by its request hash.

Replayed: matches the recorded intent/completion pair and returns the recorded ModelReply. A recorded intent with no completion (the process died mid-call) hands back a live ModelCallPermit with no intent event: re-issuing an unanswered model request is safe, and the fresh completion correlates to the recorded intent. Live: the permit carries the intent event to persist before calling the provider.

request_body is the optional full request to record on the intent, off by default (see the field on Event::ModelCallRequested). It is stored only on a genuinely fresh live intent and is ignored everywhere else: correlation keys on request_hash alone, so passing a body never changes which recorded call matches or what replays.

§Errors

ReplayError::Divergence on a hash mismatch or a different recorded event; ReplayError::MalformedLog when the event after the intent is not its completion.

Source

pub fn tool_call( &mut self, tool: &str, input: &Value, effect: Effect, idempotency_key: Option<&str>, ) -> Result<Outcome<Value, ToolCallPermit<'_>>, ReplayError>

Requests a tool call.

Replayed: matches the recorded intent/completion pair (tool, input, effect, and idempotency key must all be equal) and returns the recorded output. A recorded intent with no completion is the effect-table case:

Live: the permit carries the intent event, which the caller must persist before executing the tool. That write-ahead ordering is what makes a crash between intent and completion detectable.

§Errors

ReplayError::Divergence on any payload mismatch or a different recorded event; ReplayError::NeedsReconciliation for a dangling write intent; ReplayError::MalformedLog when the event after the intent is not its completion.

Source

pub fn suspend( &mut self, reason: &str, input_schema: &Value, ) -> Result<Outcome<(), Emitted>, ReplayError>

Requests suspension of the run.

Replayed: matches the recorded Event::Suspended. Live: returns the event to persist. Either way, follow with ReplayCursor::await_resume to obtain the resume input (or learn the run is still parked).

§Errors

ReplayError::Divergence on a payload mismatch, a different recorded event, or a request after the run already ended.

Source

pub fn await_resume( &mut self, ) -> Result<Outcome<Value, Parked<'_>>, ReplayError>

Requests the input a parked run was resumed with.

Replayed: returns the recorded Event::Resumed input. Live: the resume has not happened yet; the Parked value can be redeemed with the input once a human provides it, or simply dropped, in which case the run stays parked and the process may exit.

§Errors

ReplayError::Divergence when the log records a different kind of event here, or the run already ended.

Source

pub fn budget_exceeded( &mut self, budget: Budget, observed: f64, ) -> Result<Outcome<(), Emitted>, ReplayError>

Reports a budget crossing, recorded so replay parks at the same point.

This is a runtime request, not an orchestration one: the budget gate between events calls it when a declared limit is crossed. Checks must be computed from replayed data (recorded token usage, recorded timestamps), so a crossing that fired live is recomputed identically on replay and matched here. Follow with ReplayCursor::await_resume, exactly like a suspension.

§Errors

ReplayError::Divergence on a payload mismatch, a different recorded event, or a request after the run already ended.

Source

pub fn complete_run( &mut self, output: &Value, ) -> Result<Outcome<(), Emitted>, ReplayError>

Requests successful completion of the run.

Replayed: verifies the produced output equals the recorded one (a deterministic orchestration over replayed values must reproduce its own final answer). Live: returns the terminal event to persist. After either, every further request is a divergence.

§Errors

ReplayError::Divergence on an output mismatch, a different recorded event, or a request after the run already ended.

Source

pub fn fail_run( &mut self, error: &str, ) -> Result<Outcome<(), Emitted>, ReplayError>

Requests failure of the run.

The failing counterpart of ReplayCursor::complete_run, with the same replay/live/terminal behavior.

§Errors

ReplayError::Divergence on an error-message mismatch, a different recorded event, or a request after the run already ended.

Trait Implementations§

Source§

impl Debug for ReplayCursor

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.