oris_kernel/kernel/event.rs
1//! Event type and EventStore for the Oris kernel.
2//!
3//! Events are the source of truth. All state is derived by reducing events.
4//! Constraints: append is atomic (all or nothing); every event has a seq; scan returns ordered by seq.
5
6use serde::{Deserialize, Serialize};
7use serde_json::Value;
8
9use crate::kernel::identity::{RunId, Seq};
10
11/// A single event in the kernel event log.
12///
13/// Covers: state updates, action lifecycle, interrupt/resume, completion.
14/// Aligns with existing trace (StepCompleted → StateUpdated + optional Action*; InterruptReached → Interrupted; ResumeReceived → Resumed).
15#[derive(Clone, Debug, Serialize, Deserialize)]
16pub enum Event {
17 /// State was updated by the reducer (e.g. after a node step).
18 StateUpdated {
19 /// Optional step/node identifier.
20 step_id: Option<String>,
21 /// Serialized state or state delta (schema depends on State type).
22 payload: Value,
23 },
24 /// An external action was requested (tool, LLM, sleep, wait signal).
25 ActionRequested {
26 /// Unique id for this action instance (for matching with result).
27 action_id: String,
28 /// Kind and input (e.g. CallTool { tool, input }).
29 payload: Value,
30 },
31 /// The action completed successfully; output is stored for replay.
32 ActionSucceeded { action_id: String, output: Value },
33 /// The action failed; error is stored for audit and retry policy.
34 ActionFailed { action_id: String, error: String },
35 /// Execution was interrupted (e.g. human-in-the-loop).
36 Interrupted { value: Value },
37 /// Execution was resumed with a value after an interrupt.
38 Resumed { value: Value },
39 /// The run completed.
40 Completed,
41}
42
43/// An event with its assigned sequence number (store may assign seq on append).
44#[derive(Clone, Debug, Serialize, Deserialize)]
45pub struct SequencedEvent {
46 pub seq: Seq,
47 pub event: Event,
48}
49
50/// Event store: append-only log per run, source of truth.
51///
52/// **Constraints (must hold in all implementations and tests):**
53/// - `append`: either all events in the batch succeed or none (atomicity).
54/// - Each event has a seq (assigned by store or caller).
55/// - `scan(run_id, from)` returns events in **ascending seq order**.
56pub trait EventStore: Send + Sync {
57 /// Appends events for the given run. Returns the seq of the last written event (or an error).
58 /// Implementations must assign seqs if not present and guarantee atomicity.
59 fn append(&self, run_id: &RunId, events: &[Event]) -> Result<Seq, KernelError>;
60
61 /// Scans events for the run starting at `from` (inclusive), in ascending seq order.
62 fn scan(&self, run_id: &RunId, from: Seq) -> Result<Vec<SequencedEvent>, KernelError>;
63
64 /// Returns the highest seq for the run (0 if no events).
65 fn head(&self, run_id: &RunId) -> Result<Seq, KernelError>;
66}
67
68/// Kernel-level error type.
69#[derive(Debug, thiserror::Error)]
70pub enum KernelError {
71 #[error("EventStore error: {0}")]
72 EventStore(String),
73 #[error("SnapshotStore error: {0}")]
74 SnapshotStore(String),
75 #[error("Reducer error: {0}")]
76 Reducer(String),
77 #[error("Policy error: {0}")]
78 Policy(String),
79 #[error("Driver error: {0}")]
80 Driver(String),
81 /// Executor returned a structured action error (for policy retry decisions).
82 #[error("Executor: {0}")]
83 Executor(crate::kernel::action::ActionError),
84}