1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3use serde_json::Value;
4use ts_rs::TS;
5
6use crate::events::{RunEvent, RunEventPayload, ToolStatus};
7use crate::types::AgentStatus;
8
9use super::approval::{ApprovalDecision, ApprovalRequestParams};
10use super::turn::{AppTurn, TurnCompletedParams, TurnStatus};
11use super::ServerNotification;
12
13#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, TS)]
14#[serde(rename_all = "camelCase")]
15pub struct ItemStartedParams {
16 pub thread_id: String,
17 pub turn_id: String,
18 pub item: AppItem,
19}
20
21#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]
22#[serde(rename_all = "camelCase")]
23pub struct AgentMessageDeltaParams {
24 pub thread_id: String,
25 pub turn_id: String,
26 pub item_id: String,
27 pub delta: String,
28}
29
30#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, TS)]
31#[serde(rename_all = "camelCase")]
32pub struct ToolCallDeltaParams {
33 pub thread_id: String,
34 pub turn_id: String,
35 pub item_id: String,
36 pub delta: Value,
37}
38
39#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, TS)]
40#[serde(rename_all = "camelCase")]
41pub struct ItemCompletedParams {
42 pub thread_id: String,
43 pub turn_id: String,
44 pub item: AppItem,
45}
46
47#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, TS)]
48#[serde(rename_all = "camelCase")]
49pub struct AppItem {
50 pub id: String,
51 pub run_event_id: String,
52 #[serde(rename = "type")]
53 pub kind: AppItemKind,
54 pub status: AppItemStatus,
55 pub created_at_ms: u128,
56 #[serde(default, skip_serializing_if = "Option::is_none")]
57 pub completed_at_ms: Option<u128>,
58 #[serde(default, skip_serializing_if = "Option::is_none")]
59 pub content: Option<Value>,
60}
61
62#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]
63#[serde(rename_all = "camelCase")]
64pub enum AppItemKind {
65 UserMessage,
66 AgentMessage,
67 ToolCall,
68 ApprovalRequest,
69 ApprovalResolved,
70 MemoryCompact,
71 SubRun,
72 Handoff,
73 RunStatus,
74}
75
76#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]
77#[serde(rename_all = "camelCase")]
78pub enum AppItemStatus {
79 Queued,
80 InProgress,
81 Completed,
82 Failed,
83 Interrupted,
84}
85
86pub fn map_run_event_to_notifications(
87 thread_id: &str,
88 turn_id: &str,
89 event: &RunEvent,
90) -> Vec<ServerNotification> {
91 match event.payload() {
92 RunEventPayload::AssistantDelta { delta } => {
93 vec![ServerNotification::AgentMessageDelta(
94 AgentMessageDeltaParams {
95 thread_id: thread_id.to_string(),
96 turn_id: turn_id.to_string(),
97 item_id: event.event_id().as_str().to_string(),
98 delta: delta.clone(),
99 },
100 )]
101 }
102 RunEventPayload::ToolCallStarted {
103 tool_call_id,
104 tool_name,
105 arguments,
106 } => vec![ServerNotification::ItemStarted(ItemStartedParams {
107 thread_id: thread_id.to_string(),
108 turn_id: turn_id.to_string(),
109 item: AppItem {
110 id: event.event_id().as_str().to_string(),
111 run_event_id: event.event_id().as_str().to_string(),
112 kind: AppItemKind::ToolCall,
113 status: AppItemStatus::InProgress,
114 created_at_ms: event.created_at_ms(),
115 completed_at_ms: None,
116 content: Some(serde_json::json!({
117 "toolCallId": tool_call_id,
118 "toolName": tool_name,
119 "arguments": arguments,
120 })),
121 },
122 })],
123 RunEventPayload::ToolCallCompleted {
124 tool_call_id,
125 tool_name,
126 status,
127 } => vec![ServerNotification::ItemCompleted(ItemCompletedParams {
128 thread_id: thread_id.to_string(),
129 turn_id: turn_id.to_string(),
130 item: AppItem {
131 id: event.event_id().as_str().to_string(),
132 run_event_id: event.event_id().as_str().to_string(),
133 kind: AppItemKind::ToolCall,
134 status: tool_status_to_item_status(*status),
135 created_at_ms: event.created_at_ms(),
136 completed_at_ms: Some(event.created_at_ms()),
137 content: Some(serde_json::json!({
138 "toolCallId": tool_call_id,
139 "toolName": tool_name,
140 "status": status,
141 })),
142 },
143 })],
144 RunEventPayload::ApprovalRequested {
145 request_id,
146 tool_name,
147 preview,
148 ..
149 } => vec![ServerNotification::ApprovalRequested(
150 ApprovalRequestParams {
151 thread_id: thread_id.to_string(),
152 turn_id: turn_id.to_string(),
153 request_id: request_id.clone(),
154 tool_name: tool_name.clone(),
155 preview: preview.clone(),
156 choices: vec![ApprovalDecision::Allow, ApprovalDecision::Deny],
157 },
158 )],
159 RunEventPayload::RunCompleted { status } => {
160 vec![ServerNotification::TurnCompleted(TurnCompletedParams {
161 turn: completed_turn(
162 thread_id,
163 turn_id,
164 event,
165 agent_status_to_turn_status(status),
166 ),
167 })]
168 }
169 RunEventPayload::RunFailed { .. } => {
170 vec![ServerNotification::TurnCompleted(TurnCompletedParams {
171 turn: completed_turn(thread_id, turn_id, event, TurnStatus::Failed),
172 })]
173 }
174 RunEventPayload::RunCancelled { .. } => {
175 vec![ServerNotification::TurnCompleted(TurnCompletedParams {
176 turn: completed_turn(thread_id, turn_id, event, TurnStatus::Interrupted),
177 })]
178 }
179 _ => Vec::new(),
180 }
181}
182
183fn completed_turn(thread_id: &str, turn_id: &str, event: &RunEvent, status: TurnStatus) -> AppTurn {
184 AppTurn {
185 id: turn_id.to_string(),
186 thread_id: thread_id.to_string(),
187 run_id: event.run_id().to_string(),
188 status,
189 input: Vec::new(),
190 started_at_ms: None,
191 completed_at_ms: Some(event.created_at_ms()),
192 token_usage: None,
193 }
194}
195
196fn tool_status_to_item_status(status: ToolStatus) -> AppItemStatus {
197 match status {
198 ToolStatus::Started => AppItemStatus::InProgress,
199 ToolStatus::Success => AppItemStatus::Completed,
200 ToolStatus::Error => AppItemStatus::Failed,
201 ToolStatus::WaitResponse => AppItemStatus::InProgress,
202 }
203}
204
205fn agent_status_to_turn_status(status: &AgentStatus) -> TurnStatus {
206 match status {
207 AgentStatus::Completed => TurnStatus::Completed,
208 AgentStatus::Failed => TurnStatus::Failed,
209 AgentStatus::Pending => TurnStatus::Queued,
210 AgentStatus::Running => TurnStatus::Running,
211 AgentStatus::WaitUser => TurnStatus::Running,
212 AgentStatus::MaxCycles => TurnStatus::Failed,
213 }
214}