use crate::gate::{Approval, CheckRecord};
use crate::identity::ActorId;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum Event {
Message {
text: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
raw: Option<serde_json::Value>,
},
ToolUse {
name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
raw: Option<serde_json::Value>,
},
Error {
message: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
raw: Option<serde_json::Value>,
},
Finished {
exit_code: Option<i32>,
files_touched: Vec<String>,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Outcome {
Approved,
Rejected,
Failed,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct TokenUsage {
pub adapter: String,
pub role: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub input: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub output: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub total: Option<u64>,
#[serde(default)]
pub approximate: bool,
}
impl TokenUsage {
pub fn counted(&self) -> u64 {
self.total
.unwrap_or_else(|| self.input.unwrap_or(0) + self.output.unwrap_or(0))
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RunRecord {
pub run_id: String,
pub task_id: String,
#[serde(default)]
pub prompt: String,
pub author: ActorId,
pub adapter: String,
#[serde(default)]
pub repository: String,
pub started_at: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub finished_at: Option<String>,
#[serde(default)]
pub checks: Vec<CheckRecord>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub approval: Option<Approval>,
#[serde(default)]
pub usage: Vec<TokenUsage>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub outcome: Option<Outcome>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn events_round_trip_through_jsonl() {
let event = Event::Finished {
exit_code: Some(0),
files_touched: vec!["src/lib.rs".to_string()],
};
let line = serde_json::to_string(&event).expect("serializes");
let back: Event = serde_json::from_str(&line).expect("deserializes");
match back {
Event::Finished { exit_code, .. } => assert_eq!(exit_code, Some(0)),
other => panic!("wrong variant: {other:?}"),
}
}
}