use serde::{Deserialize, Serialize};
use crate::{Action, PlanItem, TurnResult, Usage};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
#[non_exhaustive]
pub enum AgentEvent {
SessionStarted {
session_id: String,
},
TurnStarted,
TurnCompleted {
#[serde(default)]
usage: Option<Usage>,
#[serde(default)]
result: Option<TurnResult>,
},
TurnFailed {
message: String,
},
Message {
id: String,
text: String,
},
TextDelta {
id: String,
delta: String,
},
Reasoning {
id: String,
text: String,
},
ActionStarted {
id: String,
action: Action,
},
ActionUpdated {
id: String,
action: Action,
},
ActionCompleted {
id: String,
action: Action,
},
PlanUpdated {
items: Vec<PlanItem>,
},
ToolCall {
id: String,
name: String,
arguments: String,
},
Error {
message: String,
},
Raw {
backend: String,
data: serde_json::Value,
},
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ActionStatus;
#[test]
fn session_started_serde() {
let event = AgentEvent::SessionStarted {
session_id: "abc-123".into(),
};
let json = serde_json::to_string(&event).unwrap();
assert!(json.contains("\"type\":\"session_started\""));
let parsed: AgentEvent = serde_json::from_str(&json).unwrap();
match parsed {
AgentEvent::SessionStarted { session_id } => assert_eq!(session_id, "abc-123"),
_ => panic!("wrong variant"),
}
}
#[test]
fn turn_completed_with_usage() {
let event = AgentEvent::TurnCompleted {
usage: Some(Usage {
input_tokens: Some(100),
output_tokens: Some(50),
..Default::default()
}),
result: None,
};
let json = serde_json::to_string(&event).unwrap();
let parsed: AgentEvent = serde_json::from_str(&json).unwrap();
match parsed {
AgentEvent::TurnCompleted { usage, .. } => {
let u = usage.unwrap();
assert_eq!(u.input_tokens, Some(100));
assert_eq!(u.output_tokens, Some(50));
}
_ => panic!("wrong variant"),
}
}
#[test]
fn action_started_serde() {
let event = AgentEvent::ActionStarted {
id: "item-1".into(),
action: Action::Command {
command: "echo hi".into(),
output: String::new(),
exit_code: None,
status: ActionStatus::InProgress,
},
};
let json = serde_json::to_string(&event).unwrap();
assert!(json.contains("\"type\":\"action_started\""));
let parsed: AgentEvent = serde_json::from_str(&json).unwrap();
match parsed {
AgentEvent::ActionStarted { id, .. } => assert_eq!(id, "item-1"),
_ => panic!("wrong variant"),
}
}
#[test]
fn raw_event_serde() {
let event = AgentEvent::Raw {
backend: "codex".into(),
data: serde_json::json!({"custom": true}),
};
let json = serde_json::to_string(&event).unwrap();
let parsed: AgentEvent = serde_json::from_str(&json).unwrap();
match parsed {
AgentEvent::Raw { backend, data } => {
assert_eq!(backend, "codex");
assert_eq!(data["custom"], true);
}
_ => panic!("wrong variant"),
}
}
}