kaynine-core 0.1.0

Core agent loop, messages, events, policies, and provider abstractions for Kaynine
Documentation
use crate::error::RunFailureReason;
use crate::ids::{BranchId, RunId, SessionId, ToolCallId};
use crate::message::{FinishReason, Usage};
use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct EventEnvelope<E> {
    pub session_id: SessionId,
    pub branch_id: BranchId,
    pub run_id: Option<RunId>,
    pub revision: u64,
    pub run_seq: Option<u64>,
    pub payload: E,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(tag = "kind")]
pub enum RealtimeEvent {
    RunStarted,
    TurnStarted {
        turn: u32,
    },
    TextDelta {
        block: u32,
        text: String,
    },
    ReasoningDelta {
        block: u32,
        text: String,
    },
    ToolCallStarted {
        block: u32,
        id: ToolCallId,
        name: String,
    },
    UsageUpdated {
        usage: Usage,
    },
    ToolProgress {
        call_id: ToolCallId,
        message: String,
    },
    TurnCompleted {
        finish_reason: FinishReason,
    },
    RunCompleted,
    RunFailed {
        reason: RunFailureReason,
    },
    RunCancelled,
    ApprovalPending {
        call_id: ToolCallId,
        deadline_unix: i64,
    },
    ApprovalResolved {
        call_id: ToolCallId,
        approved: bool,
    },
    SteerInjected {
        count: u32,
    },
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn envelope_roundtrips() {
        let env = EventEnvelope {
            session_id: SessionId::from("s1"),
            branch_id: BranchId::from("b1"),
            run_id: Some(RunId::from("r1")),
            revision: 7,
            run_seq: Some(3),
            payload: RealtimeEvent::TextDelta {
                block: 0,
                text: "".into(),
            },
        };
        let json = serde_json::to_string(&env).unwrap();
        let back: EventEnvelope<RealtimeEvent> = serde_json::from_str(&json).unwrap();
        assert_eq!(back, env);
    }

    #[test]
    fn run_seq_is_optional_for_non_run_events() {
        let env = EventEnvelope {
            session_id: SessionId::from("s1"),
            branch_id: BranchId::from("b1"),
            run_id: None,
            revision: 1,
            run_seq: None,
            payload: RealtimeEvent::RunCompleted,
        };
        let json = serde_json::to_string(&env).unwrap();
        assert!(json.contains("\"run_seq\":null"));
    }
}