tinyagents 2.1.0

A recursive language-model (RLM) harness for Rust.
Documentation
# graph::compiled

The superstep executor for the durable graph — `CompiledGraph<State, Update>`.

This is the engine that makes the recursive runtime durable: it drives a
compiled graph in checkpointed supersteps, and because a node handler may
recurse into another compiled graph (a subgraph) or a sub-agent, every level
of that recursion is observed through the same step/boundary/checkpoint
discipline — child runs roll their state, events, and interrupts up through
the parent's reducer and checkpointer.

## Superstep loop

Each step:

1. Take the active node set.
2. Run each active node against the committed state snapshot.
3. Collect updates / commands / interrupts.
4. Apply the reducer at the step boundary.
5. Persist a checkpoint at the boundary (when a checkpointer is configured).
   Under `DurabilityMode::Async` non-terminal boundary writes run on spawned
   background tasks; a failed write fails the run at the next durability
   boundary, and all in-flight writes are drained at the terminal / interrupt /
   failure boundary so the run result reflects persistence failures (see
   `docs/modules/graph/checkpointing.md`).
6. Select the next active set.

The loop stops when the active set empties, every branch reaches `END`, an
interrupt pauses the run, or the recursion limit is hit (a deterministic
`TinyAgentsError::RecursionLimit`).

## Sequential vs. parallel steps

By default execution is sequential within a step. When the graph is compiled
with `GraphBuilder::with_parallel`, a step with more than one active node runs
every branch concurrently via `futures::future::join_all`, but the data flow
is identical to the sequential case: each branch reads the same committed
snapshot (its own clone), and results fold into the reducer in deterministic
active-set order at the step boundary — the merged state is reproducible
regardless of which branch finishes first.

Concurrency and interrupt semantics:

- All active branches in a parallel step start before any is awaited, and all
  are driven to completion (`join_all`) before the step boundary runs.
- Branch results are folded in active-set index order — the reducer is the
  fan-in/join, with lower-index branches' updates applied first.
- The **lowest-index** branch that errors or interrupts is the step's terminal
  outcome. Updates produced by lower-index successful branches are still
  applied/persisted; an error persists a resumable failure boundary (below)
  and aborts; an interrupt persists a checkpoint whose pending nodes are that
  branch and every later active node.
- Because branches run on cloned snapshots and never share mutable state,
  concurrency is data-race free — the reducer alone resolves conflicting
  writes, deterministically, by index.

## Network resilience and resumable failures

Two opt-in mechanisms make a run durable under transient failure and
restartable after a hard one:

- **Node retry** (`CompiledGraph::with_node_retry`) — a node whose handler
  fails with a retryable error (`harness::retry::is_retryable` — the model/tool
  transient class) is re-run from its start up to the policy's attempt cap,
  emitting `GraphEvent::NodeRetryScheduled` and sleeping the opt-in backoff
  between attempts. A single network blip is absorbed without touching the
  run.
- **Resumable failure** — when a handler fails beyond the retry budget (or the
  error is non-retryable), the executor does not discard the step. On a
  checkpointed thread it folds the branches that already completed into
  committed state and persists a failure-boundary checkpoint whose
  `next_nodes` schedule the failed node (and the not-yet-run tail) for a later
  `CompiledGraph::resume` / `CompiledGraph::retry`, with the error and failed
  node stamped into checkpoint metadata. The run reports `Failed` (carrying
  that checkpoint id) and returns the error. A caller can restart it as-is, or
  edit state with `CompiledGraph::update_state` before resuming to continue on
  operator feedback. Without a checkpointer the run aborts immediately, as
  before.

## Public surface

### Construction / configuration (builders, taken by value)

`with_checkpointer`, `with_event_sink`, `with_durability`, `with_node_retry`,
`with_namespace`, `with_recursion_policy`, `with_recursion_frames`,
`with_recursion_node`, `with_event_journal`, `with_status_store`.

Accessors: `graph_id()`, `name()`, `namespace()`.

### Running

- `run(state)` — fresh, un-checkpointed run (or checkpointed with an
  auto-generated thread if a checkpointer is set).
- `run_with_inputs(..)` / `run_with_thread(thread_id, state)` /
  `run_with_thread_inputs(..)` — thread-scoped variants for checkpointed runs
  and multi-entry-point graphs.
- `resume(..)` / `resume_from(..)` — continue a checkpointed thread from its
  last (or a specified) checkpoint, including past an interrupt or a resumable
  failure boundary.
- `retry(thread_id)` — re-run the failed node(s) recorded in the last
  failure-boundary checkpoint.

### State inspection / time travel

- `get_state(..)` / `get_state_history(..)` — read the current or historical
  `StateSnapshot<State>` for a thread.
- `update_state(..)` / `bulk_update_state(..)` — operator-driven state edits
  between runs (e.g. before a `resume`/`retry`).
- `fork_state(..)` — branch a new thread from an existing checkpoint.

### Types

- `CompiledGraph<State, Update>` — the executor itself.
- `GraphExecution<State>` — the result of a run: final/paused state, status,
  interrupts, run tree.
- `GraphInput` — seeds a run at a specific node with a payload
  (`GraphInput::start` / `::new` / `::node`).
- `StateSnapshot<State>` — a point-in-time view (`values`, `tasks`,
  `next_nodes`, `config`, `metadata`, `parent_config`, `pending_interrupts`)
  returned by state-inspection calls, projected from a `CheckpointTuple` via
  `Checkpoint::to_metadata` so it always matches what
  `Checkpointer::list` reports.
- `ResumeTarget` — selects which checkpoint/branch a resume targets.

## Files

| File | Role |
| --- | --- |
| `types.rs` | `CompiledGraph`, `GraphExecution`, `GraphInput`, `StateSnapshot`, `ResumeTarget`. |
| `mod.rs` | Module wiring: shared helpers (`Activation`, checkpoint-id/snapshot projection, barrier persistence) and the builder/configuration `impl` (`with_*`, `emit`). |
| `executor.rs` | The superstep loop and run/resume entry points (`run`, `resume`, `retry`, `execute`, `execute_run`, node retry, resumable-failure persistence). |
| `state_api.rs` | State inspection / time travel (`get_state`, `get_state_history`, `update_state`, `bulk_update_state`, `fork_state`). |
| `routing.rs` | Resolving a completed step's active set into the next superstep's activations (`route`, `route_completed`, interrupt-durability preconditions). |
| `test.rs` | Unit tests (sequential/parallel steps, retry, resumable failure, resume/fork, state history). |

## Operational constraints

- Node retry and resumable failure both require a checkpointer to have
  observable durability; without one, retries still happen in-process but a
  hard failure aborts the run instead of leaving a resumable boundary.
- Parallel-step determinism depends on branches never mutating shared state
  outside their own snapshot clone — a node handler that reaches around the
  snapshot (e.g. into shared interior-mutable state) breaks the "index order
  resolves conflicts" guarantee.
- `id` generation (`next_checkpoint_id`) is collision-free across process
  restarts by delegating to `harness::ids::new_checkpoint_id`, not a
  process-local counter — this matters for resumed threads restarted in a new
  process.