1use crate::app::conversation::Message;
2use crate::app::domain::action::{ApprovalDecision, ApprovalMemory, McpServerState};
3use crate::app::domain::types::{
4 CompactionRecord, MessageId, OpId, RequestId, SessionId, ToolCallId,
5};
6use crate::config::model::ModelId;
7use crate::session::state::SessionConfig;
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10use steer_tools::ToolCall;
11use steer_tools::result::ToolResult;
12
13pub use crate::app::domain::state::OperationKind;
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
16pub enum SessionEvent {
17 SessionCreated {
20 config: Box<SessionConfig>,
21 metadata: HashMap<String, String>,
22 #[serde(default, skip_serializing_if = "Option::is_none")]
24 parent_session_id: Option<SessionId>,
25 },
26
27 SessionConfigUpdated {
28 config: Box<SessionConfig>,
29 primary_agent_id: String,
30 },
31
32 AssistantMessageAdded {
34 message: Message,
35 model: ModelId,
36 },
37
38 UserMessageAdded {
40 message: Message,
41 },
42
43 ToolMessageAdded {
45 message: Message,
46 },
47
48 MessageUpdated {
49 message: Message,
50 },
51
52 ToolCallStarted {
53 id: ToolCallId,
54 name: String,
55 parameters: serde_json::Value,
56 model: ModelId,
57 },
58
59 ToolCallCompleted {
60 id: ToolCallId,
61 name: String,
62 result: ToolResult,
63 model: ModelId,
64 },
65
66 ToolCallFailed {
67 id: ToolCallId,
68 name: String,
69 error: String,
70 model: ModelId,
71 },
72
73 ApprovalRequested {
74 request_id: RequestId,
75 tool_call: ToolCall,
76 },
77
78 ApprovalDecided {
79 request_id: RequestId,
80 decision: ApprovalDecision,
81 remember: Option<ApprovalMemory>,
82 },
83
84 OperationStarted {
85 op_id: OpId,
86 kind: OperationKind,
87 },
88
89 OperationCompleted {
90 op_id: OpId,
91 },
92
93 OperationCancelled {
94 op_id: OpId,
95 info: CancellationInfo,
96 },
97
98 CompactResult {
99 result: CompactResult,
100 },
101
102 ConversationCompacted {
103 record: CompactionRecord,
104 },
105
106 WorkspaceChanged,
107
108 QueueUpdated {
109 queue: Vec<QueuedWorkItemSnapshot>,
110 },
111
112 Error {
113 message: String,
114 },
115
116 McpServerStateChanged {
117 server_name: String,
118 state: McpServerState,
119 },
120}
121
122#[derive(Debug, Clone, PartialEq)]
123pub enum CompactResult {
124 Success(String),
125 Cancelled,
126 InsufficientMessages,
127}
128
129impl Serialize for CompactResult {
130 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
131 where
132 S: serde::Serializer,
133 {
134 use serde::ser::SerializeStruct;
135
136 match self {
137 CompactResult::Success(summary) => {
138 let mut state = serializer.serialize_struct("CompactResult", 2)?;
139 state.serialize_field("result_type", "success")?;
140 state.serialize_field("summary", summary)?;
141 state.end()
142 }
143 CompactResult::Cancelled => {
144 let mut state = serializer.serialize_struct("CompactResult", 1)?;
145 state.serialize_field("result_type", "cancelled")?;
146 state.end()
147 }
148 CompactResult::InsufficientMessages => {
149 let mut state = serializer.serialize_struct("CompactResult", 1)?;
150 state.serialize_field("result_type", "insufficient_messages")?;
151 state.end()
152 }
153 }
154 }
155}
156
157impl<'de> Deserialize<'de> for CompactResult {
158 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
159 where
160 D: serde::Deserializer<'de>,
161 {
162 #[derive(Deserialize)]
163 struct CompactResultPayload {
164 result_type: String,
165 #[serde(default)]
166 summary: Option<String>,
167 #[serde(default)]
168 success: Option<String>,
169 }
170
171 let payload = CompactResultPayload::deserialize(deserializer)?;
172 match payload.result_type.as_str() {
173 "success" => {
174 let summary = payload
175 .summary
176 .or(payload.success)
177 .ok_or_else(|| serde::de::Error::missing_field("summary"))?;
178 Ok(CompactResult::Success(summary))
179 }
180 "cancelled" => Ok(CompactResult::Cancelled),
181 "insufficient_messages" => Ok(CompactResult::InsufficientMessages),
182 other => Err(serde::de::Error::unknown_variant(
183 other,
184 &["success", "cancelled", "insufficient_messages"],
185 )),
186 }
187 }
188}
189
190#[derive(Debug, Clone, Serialize, Deserialize)]
191pub struct CancellationInfo {
192 pub pending_tool_calls: usize,
193 #[serde(default)]
194 pub popped_queued_item: Option<QueuedWorkItemSnapshot>,
195}
196
197#[derive(Debug, Clone, Serialize, Deserialize)]
198pub struct QueuedWorkItemSnapshot {
199 pub kind: Option<QueuedWorkKind>,
200 pub content: String,
201 pub queued_at: u64,
202 pub model: Option<ModelId>,
203 pub op_id: OpId,
204 pub message_id: MessageId,
205}
206
207#[derive(Debug, Clone, Serialize, Deserialize)]
208pub enum QueuedWorkKind {
209 UserMessage,
210 DirectBash,
211}
212
213impl SessionEvent {
214 pub fn is_error(&self) -> bool {
215 matches!(
216 self,
217 SessionEvent::Error { .. } | SessionEvent::ToolCallFailed { .. }
218 )
219 }
220
221 pub fn operation_id(&self) -> Option<OpId> {
222 match self {
223 SessionEvent::OperationStarted { op_id, .. }
224 | SessionEvent::OperationCompleted { op_id }
225 | SessionEvent::OperationCancelled { op_id, .. } => Some(*op_id),
226 _ => None,
227 }
228 }
229
230 pub fn tool_call_id(&self) -> Option<&ToolCallId> {
231 match self {
232 SessionEvent::ToolCallStarted { id, .. }
233 | SessionEvent::ToolCallCompleted { id, .. }
234 | SessionEvent::ToolCallFailed { id, .. } => Some(id),
235 _ => None,
236 }
237 }
238}