1use serde::{Deserialize, Serialize, Serializer};
2
3use super::content::{DisplayBlock, ToolReturnValue};
4use super::event::HookAction;
5
6#[derive(Debug, Clone, PartialEq)]
15pub enum Request {
16 ApprovalRequest(ApprovalRequest),
18 ToolCallRequest(ToolCallRequest),
20 QuestionRequest(QuestionRequest),
22 HookRequest(HookRequest),
24}
25
26#[derive(Serialize, Deserialize)]
31#[serde(tag = "type")]
32#[allow(clippy::enum_variant_names)]
33pub(crate) enum FlatRequest {
34 ApprovalRequest(ApprovalRequest),
35 ToolCallRequest(ToolCallRequest),
36 QuestionRequest(QuestionRequest),
37 HookRequest(HookRequest),
38}
39
40impl From<Request> for FlatRequest {
41 fn from(req: Request) -> Self {
42 match req {
43 Request::ApprovalRequest(inner) => FlatRequest::ApprovalRequest(inner),
44 Request::ToolCallRequest(inner) => FlatRequest::ToolCallRequest(inner),
45 Request::QuestionRequest(inner) => FlatRequest::QuestionRequest(inner),
46 Request::HookRequest(inner) => FlatRequest::HookRequest(inner),
47 }
48 }
49}
50
51impl From<FlatRequest> for Request {
52 fn from(req: FlatRequest) -> Self {
53 match req {
54 FlatRequest::ApprovalRequest(inner) => Request::ApprovalRequest(inner),
55 FlatRequest::ToolCallRequest(inner) => Request::ToolCallRequest(inner),
56 FlatRequest::QuestionRequest(inner) => Request::QuestionRequest(inner),
57 FlatRequest::HookRequest(inner) => Request::HookRequest(inner),
58 }
59 }
60}
61
62#[derive(Serialize, Deserialize)]
67struct RequestEnvelope {
68 #[serde(rename = "type")]
69 type_name: String,
70 payload: serde_json::Value,
71}
72
73impl Serialize for Request {
74 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
75 let flat = FlatRequest::from(self.clone());
76 let mut value = serde_json::to_value(&flat).map_err(serde::ser::Error::custom)?;
77 let obj = value
78 .as_object_mut()
79 .ok_or_else(|| serde::ser::Error::custom("expected object"))?;
80 let type_name = obj
81 .remove("type")
82 .and_then(|v| v.as_str().map(String::from))
83 .ok_or_else(|| serde::ser::Error::custom("missing type"))?;
84 RequestEnvelope { type_name, payload: value }.serialize(serializer)
85 }
86}
87
88impl<'de> Deserialize<'de> for Request {
89 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
90 let envelope = RequestEnvelope::deserialize(deserializer)?;
91 let mut value = envelope.payload;
92 if let Some(obj) = value.as_object_mut() {
93 obj.insert("type".to_string(), serde_json::Value::String(envelope.type_name));
94 }
95 let flat: FlatRequest = serde_json::from_value(value).map_err(serde::de::Error::custom)?;
96 Ok(Request::from(flat))
97 }
98}
99
100#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
102pub struct ApprovalRequest {
103 pub id: String,
105 pub tool_call_id: String,
107 pub sender: String,
109 pub action: String,
111 pub description: String,
113 #[serde(skip_serializing_if = "Option::is_none", default)]
115 pub display: Option<Vec<DisplayBlock>>,
116 #[serde(skip_serializing_if = "Option::is_none")]
118 pub source_kind: Option<SourceKind>,
119 #[serde(skip_serializing_if = "Option::is_none")]
121 pub source_id: Option<String>,
122 #[serde(skip_serializing_if = "Option::is_none")]
124 pub agent_id: Option<String>,
125 #[serde(skip_serializing_if = "Option::is_none")]
127 pub subagent_type: Option<String>,
128 #[serde(skip_serializing_if = "Option::is_none")]
130 pub source_description: Option<String>,
131}
132
133#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
135#[serde(rename_all = "snake_case")]
136pub enum SourceKind {
137 ForegroundTurn,
139 BackgroundAgent,
141}
142
143#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
145pub struct ToolCallRequest {
146 pub id: String,
148 pub name: String,
150 #[serde(skip_serializing_if = "Option::is_none")]
152 pub arguments: Option<String>,
153}
154
155#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
157pub struct QuestionRequest {
158 pub id: String,
160 pub tool_call_id: String,
162 pub questions: Vec<QuestionItem>,
164}
165
166#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
168pub struct QuestionItem {
169 pub question: String,
171 #[serde(skip_serializing_if = "Option::is_none")]
173 pub header: Option<String>,
174 pub options: Vec<QuestionOption>,
176 #[serde(skip_serializing_if = "Option::is_none")]
178 pub multi_select: Option<bool>,
179}
180
181#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
183pub struct QuestionOption {
184 pub label: String,
186 #[serde(skip_serializing_if = "Option::is_none")]
188 pub description: Option<String>,
189}
190
191#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
193pub struct HookRequest {
194 pub id: String,
196 pub subscription_id: String,
198 pub event: String,
200 pub target: String,
202 pub input_data: serde_json::Value,
204}
205
206#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
212pub struct ApprovalResponse {
213 pub request_id: String,
215 pub response: ApprovalResponseKind,
217 #[serde(skip_serializing_if = "Option::is_none")]
219 pub feedback: Option<String>,
220}
221
222pub use crate::protocol::event::ApprovalResponseKind;
223
224#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
226pub struct ToolCallResponse {
227 pub tool_call_id: String,
229 pub return_value: ToolReturnValue,
231}
232
233#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
235pub struct QuestionResponse {
236 pub request_id: String,
238 pub answers: std::collections::HashMap<String, String>,
241}
242
243#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
245pub struct HookResponse {
246 pub request_id: String,
248 pub action: HookAction,
250 pub reason: String,
252}