use chrono::{DateTime, Utc};
use serde::Serialize;
use serde_json::Value;
use std::path::PathBuf;
use std::str::FromStr;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum Role {
User,
Assistant,
Tool,
}
impl Role {
pub fn label(&self) -> &'static str {
match self {
Role::User => "user",
Role::Assistant => "assistant",
Role::Tool => "tool",
}
}
}
impl FromStr for Role {
type Err = &'static str;
fn from_str(value: &str) -> Result<Self, Self::Err> {
match value {
"user" => Ok(Role::User),
"assistant" => Ok(Role::Assistant),
"tool" => Ok(Role::Tool),
_ => Err("role must be user, assistant, or tool"),
}
}
}
#[derive(Debug, Serialize)]
pub struct Message {
pub role: Role,
pub text: String,
pub ts: Option<DateTime<Utc>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tool: Option<ToolEvent>,
}
#[derive(Debug, Clone, Serialize)]
#[serde(tag = "kind", content = "value", rename_all = "snake_case")]
pub enum ToolEvent {
Part(ToolPart),
Summary(ToolSummary),
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum ToolPart {
Call(ToolCall),
Result(ToolResult),
}
#[derive(Debug, Clone, Serialize)]
pub struct ToolCall {
pub id: Option<String>,
pub name: String,
pub args: Value,
}
#[derive(Debug, Clone, Serialize)]
pub struct ToolResult {
pub call_id: Option<String>,
pub text: String,
pub is_error: bool,
}
#[derive(Debug, Clone, Serialize)]
pub struct ToolSummary {
pub name: String,
pub args: Value,
pub output: Option<String>,
pub is_error: Option<bool>,
pub lines: Option<usize>,
}
impl Message {
pub fn tool_call(ts: Option<DateTime<Utc>>, call: ToolCall) -> Self {
Self {
role: Role::Tool,
text: String::new(),
ts,
tool: Some(ToolEvent::Part(ToolPart::Call(call))),
}
}
pub fn tool_result(ts: Option<DateTime<Utc>>, result: ToolResult) -> Self {
Self {
role: Role::Tool,
text: String::new(),
ts,
tool: Some(ToolEvent::Part(ToolPart::Result(result))),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum EditKind {
Edit,
Write,
MultiEdit,
NotebookEdit,
}
impl EditKind {
pub fn as_str(&self) -> &'static str {
match self {
EditKind::Edit => "edit",
EditKind::Write => "write",
EditKind::MultiEdit => "multiedit",
EditKind::NotebookEdit => "notebookedit",
}
}
}
#[derive(Debug, Serialize)]
pub struct EditEvent {
pub path: String,
pub kind: EditKind,
pub snippet: String,
pub ts: Option<DateTime<Utc>>,
}
#[derive(Debug, Serialize)]
pub struct Session {
pub id: String,
pub tool: &'static str,
pub path: PathBuf,
pub project: String,
pub started: Option<DateTime<Utc>>,
pub ended: Option<DateTime<Utc>>,
pub title: String,
pub subagent: bool,
pub messages: Vec<Message>,
pub touched: Vec<String>,
pub edits: Vec<EditEvent>,
}
pub struct StoreReport {
pub tool: &'static str,
pub root: PathBuf,
pub files: usize,
pub bytes: u64,
pub oldest: Option<DateTime<Utc>>,
pub newest: Option<DateTime<Utc>>,
}