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"));
}
}