Skip to main content

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}