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
//! Owns an immutable protocol-data snapshot, not runnable activation or trusted HEAD.
use super::{
AgentState, Definition, GraphError, InspectedClosure, Limits, MaterializedBlocks, Memory,
};
use crate::{boundary::Boundary, episode::Episode, format::DaslCid, input::ActivationInput};
/// Complete bounded typed checkpoint data after all graph consistency checks.
/// Restoring data does NOT authorize Definition/code, facts/receipts/admissions or dispatch.
/// Application argument validation, units/lifetime/caller execution policy and fresh
/// prepared inputs remain runtime/host duties. No Activation or transcript is reconstructed.
#[derive(Debug, Eq, PartialEq)]
pub struct RestoredAgent {
closure: InspectedClosure,
}
impl RestoredAgent {
/// Restore exact materialized bytes with caller/Definition caps, never hydrate/fallback.
/// Includes genesis and post-genesis data; no partial successful snapshot on any failure.
///
/// ```
/// use raiv::graph::{Genesis, MaterializedBlocks, RestoredAgent};
/// use raiv::definition::{Definition, DefinitionSpec, MemoryProfile};
/// use raiv::format::{DaslCid, DecodeLimits};
/// use raiv::limits::Limits;
/// struct Store(Genesis);
/// impl MaterializedBlocks for Store {
/// fn get(&self, cid: DaslCid) -> Option<&[u8]> {
/// self.0.blocks().find(|(key, _)| *key == cid).map(|(_, bytes)| bytes)
/// }
/// }
/// let limits = Limits::default();
/// let definition = Definition::encode(&DefinitionSpec {
/// execution_domain: [1; 32], policy: "fixture", mandate: "fixture",
/// memory_profile: MemoryProfile::None, tools: &[], inference_profile: &[0xa0],
/// limits,
/// }, &DecodeLimits::default())?;
/// let store = Store(Genesis::build([2; 32], definition, None, &limits)?);
/// let snapshot = RestoredAgent::restore(store.0.root(), &store, &limits)?;
/// assert_eq!(snapshot.state().revision(), 0);
/// assert!(snapshot.current_episode().is_none());
/// # Ok::<(), Box<dyn core::error::Error>>(())
/// ```
pub fn restore(
root: DaslCid,
blocks: &impl MaterializedBlocks,
limits: &Limits,
) -> Result<Self, GraphError> {
Self::from_inspected(InspectedClosure::inspect(root, blocks, limits)?)
}
/// Move a previously inspected closure after validating all semantic data checks.
/// No new store reads, re-encoding, or another owned payload set.
pub fn from_inspected(closure: InspectedClosure) -> Result<Self, GraphError> {
closure.check_tool_bindings()?;
Ok(Self { closure })
}
pub fn root(&self) -> DaslCid {
self.closure.root()
}
pub fn state(&self) -> &AgentState {
self.closure.state()
}
pub fn definition(&self) -> &Definition {
self.closure.definition()
}
pub fn memory(&self) -> Option<&Memory> {
self.state()
.memory_link()
.map(|cid| self.closure.find_memory(cid).expect("restored root Memory"))
}
pub fn current_episode(&self) -> Option<&Episode> {
self.state()
.current_episode_link()
.map(|cid| self.closure.episode(cid))
}
/// Completed Episodes in declared chronological order, borrowed from the retained closure.
pub fn history(&self) -> impl Iterator<Item = &Episode> {
self.state().history().map(|cid| self.closure.episode(cid))
}
/// Typed lookup in this exact required closure only; no missing-block hydration.
pub fn boundary(&self, cid: DaslCid) -> Option<&Boundary> {
self.closure.find_boundary(cid)
}
pub fn input(&self, cid: DaslCid) -> Option<&ActivationInput> {
self.closure.find_input(cid)
}
pub fn memory_block(&self, cid: DaslCid) -> Option<&Memory> {
self.closure.find_memory(cid)
}
pub fn block_count(&self) -> u64 {
self.closure.block_count()
}
pub fn encoded_bytes(&self) -> u64 {
self.closure.encoded_bytes()
}
/// Exact unique bytes; discovery order is not CAR export order.
pub fn blocks(&self) -> impl Iterator<Item = (DaslCid, &[u8])> {
self.closure.blocks()
}
}