use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
use super::{Extra, Field, tagged_enum};
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum System {
Init(SystemInit),
CompactBoundary(CompactBoundary),
TaskStarted(TaskStarted),
TaskProgress(TaskProgress),
TaskUpdated(TaskUpdated),
TaskNotification(TaskNotification),
Status(SystemStatus),
BackgroundTasksChanged(BackgroundTasksChanged),
Other {
subtype: String,
raw: Value,
},
}
tagged_enum!(System, key = "subtype", skip = ["type"], other = subtype, {
"init" => Init,
"compact_boundary" => CompactBoundary,
"task_started" => TaskStarted,
"task_progress" => TaskProgress,
"task_updated" => TaskUpdated,
"task_notification" => TaskNotification,
"status" => Status,
"background_tasks_changed" => BackgroundTasksChanged,
});
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct SystemInit {
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub session_id: Field<String>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub uuid: Field<String>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub model: Field<String>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub cwd: Field<String>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub tools: Field<Vec<String>>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub mcp_servers: Field<Vec<McpServerStatus>>,
#[serde(
default,
rename = "permissionMode",
skip_serializing_if = "Field::is_absent"
)]
pub permission_mode: Field<String>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub claude_code_version: Field<String>,
#[serde(
default,
rename = "apiKeySource",
skip_serializing_if = "Field::is_absent"
)]
pub api_key_source: Field<String>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub slash_commands: Field<Vec<String>>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub agents: Field<Vec<String>>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub skills: Field<Vec<String>>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub terminal_slash_commands: Field<Vec<String>>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub output_style: Field<String>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub capabilities: Field<Vec<String>>,
#[serde(flatten)]
pub extra: Extra,
}
impl SystemInit {
pub fn mcp_status(&self, name: &str) -> Option<&str> {
self.mcp_servers
.get()?
.iter()
.find(|s| s.name == name)
.map(|s| s.status.as_str())
}
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct McpServerStatus {
pub name: String,
pub status: String,
#[serde(flatten)]
pub extra: Extra,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct CompactBoundary {
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub compact_metadata: Field<CompactMetadata>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub session_id: Field<String>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub uuid: Field<String>,
#[serde(flatten)]
pub extra: Extra,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct CompactMetadata {
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub trigger: Field<String>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub pre_tokens: Field<u64>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub post_tokens: Field<u64>,
#[serde(flatten)]
pub extra: Extra,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct TaskUsage {
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub total_tokens: Field<u64>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub tool_uses: Field<u64>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub duration_ms: Field<u64>,
#[serde(flatten)]
pub extra: Extra,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct TaskStarted {
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub task_id: Field<String>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub tool_use_id: Field<String>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub description: Field<String>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub task_type: Field<String>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub owned_by_subagent: Field<bool>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub session_id: Field<String>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub uuid: Field<String>,
#[serde(flatten)]
pub extra: Extra,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct TaskProgress {
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub task_id: Field<String>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub tool_use_id: Field<String>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub description: Field<String>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub last_tool_name: Field<String>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub usage: Field<TaskUsage>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub owned_by_subagent: Field<bool>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub session_id: Field<String>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub uuid: Field<String>,
#[serde(flatten)]
pub extra: Extra,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct TaskUpdated {
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub task_id: Field<String>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub patch: Field<Map<String, Value>>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub status: Field<String>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub owned_by_subagent: Field<bool>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub session_id: Field<String>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub uuid: Field<String>,
#[serde(flatten)]
pub extra: Extra,
}
impl TaskUpdated {
pub fn any_status(&self) -> Option<&str> {
self.status.as_deref().or_else(|| {
self.patch
.get()
.and_then(|p| p.get("status"))
.and_then(Value::as_str)
})
}
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct TaskNotification {
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub task_id: Field<String>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub tool_use_id: Field<String>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub status: Field<String>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub output_file: Field<String>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub summary: Field<String>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub usage: Field<TaskUsage>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub owned_by_subagent: Field<bool>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub session_id: Field<String>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub uuid: Field<String>,
#[serde(flatten)]
pub extra: Extra,
}
macro_rules! subagent_owned {
($($t:ident),*) => {$(
impl $t {
pub fn subagent_owned(&self) -> bool {
self.owned_by_subagent.get() == Some(&true)
}
}
)*};
}
subagent_owned!(TaskStarted, TaskProgress, TaskUpdated, TaskNotification);
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct SystemStatus {
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub status: Field<String>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub session_id: Field<String>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub uuid: Field<String>,
#[serde(flatten)]
pub extra: Extra,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct BackgroundTasksChanged {
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub tasks: Field<Vec<Value>>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub session_id: Field<String>,
#[serde(default, skip_serializing_if = "Field::is_absent")]
pub uuid: Field<String>,
#[serde(flatten)]
pub extra: Extra,
}
impl BackgroundTasksChanged {
pub fn task_objects(&self) -> impl Iterator<Item = &Map<String, Value>> {
self.tasks
.get()
.into_iter()
.flatten()
.filter_map(Value::as_object)
}
}