1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
//! Salvor runtime: the IO edge where core (replay), store, llm, and tools
//! meet. Durable, resumable agent runs with hard budgets, in two tiers.
//!
//! # The two tiers
//!
//! - **The substrate: [`RunCtx`].** The library-first tier. A team that
//! owns its control flow writes an ordinary async function against
//! `RunCtx` (recorded model calls, recorded tool dispatch, `now`,
//! `random`, suspension) and gets durability, crash-exact replay, and the
//! suspension protocol with no framework in the way. A runtime that sits
//! under your loop is infrastructure; this type is that runtime.
//! - **The batteries: [`Agent`] + [`Runtime`].** [`Agent::builder`]
//! configures model, prompt, tools, budgets, pricing;
//! [`Runtime::start`] / [`Runtime::recover`] / [`Runtime::resume`] drive
//! the single built-in loop. The loop is implemented entirely against the
//! public `RunCtx` surface and doubles as its reference example; it holds
//! no private hooks, by design and by test.
//!
//! # Where this crate sits
//!
//! The agent loop cannot live inside `salvor-core`: `salvor-store` and
//! `salvor-tools` both depend on
//! `salvor-core`, and the loop depends on both of them (plus `salvor-llm`),
//! so a loop in core would be a dependency cycle. This crate is the
//! resolution: core keeps the pure event model and replay engine with **no
//! new dependencies**, and `salvor-runtime` sits above all four crates as
//! the one place allowed to do IO. For the same reason this is the one
//! crate that may be tokio-bound: it is the IO edge, while core and store
//! stay executor-agnostic.
//!
//! # Constraints this crate honors
//!
//! The replay engine fixes the ground rules the runtime builds on:
//!
//! - **Budget checks are computed from replayed data** (recorded usage,
//! recorded `now` observations), so a crossing re-fires identically at
//! the same position on replay ([`Budgets`]).
//! - **Suspension is `suspend` + `await_resume`**, and budget crossings
//! park through the same protocol ([`RunCtx::suspend`],
//! [`RunCtx::await_resume`], [`RunCtx::budget_exceeded`]).
//! - **Divergence detection is always on**: every replayed step is compared
//! structurally against the log, and a mismatch is a typed error, never a
//! silent drift.
//! - **Random values are raw recorded bits** ([`RunCtx::random`] returns
//! `u64`); richer values derive from the bits deterministically, which is
//! how idempotency keys reproduce on replay.
//! - **Sequence numbers are 0-based and contiguous**, with completions
//! correlated to their intent's position; the cursor enforces it and this
//! crate persists exactly what the cursor emits, in order.
//!
//! # Recorded wire shapes this crate defines
//!
//! On top of the core event vocabulary, three shapes are contracts here:
//! the suspension sentinel and the structured tool-error object recorded in
//! tool-call completions (module docs of [`wire`], exported constants
//! [`SUSPEND_SENTINEL_KEY`] / [`ERROR_SENTINEL_KEY`]), and the
//! budget-extension resume input (module docs of [`budgets`]). Error
//! compaction, what a failed tool call puts into the model's context, is
//! specified and exported in [`compact`].
//!
//! [`wire`]: crate::wire
//! [`budgets`]: crate::Budgets
//! [`compact`]: crate::compact_error_message
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use RuntimeError;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use validate_against_schema;
pub use ;