1use serde::{Deserialize, Serialize, Serializer};
2
3use super::content::{DisplayBlock, ToolReturnValue};
4use super::event::HookAction;
5
6#[derive(Debug, Clone, PartialEq)]
15#[non_exhaustive]
16pub enum Request {
17 ApprovalRequest(ApprovalRequest),
19 ToolCallRequest(ToolCallRequest),
21 QuestionRequest(QuestionRequest),
23 HookRequest(HookRequest),
25}
26
27#[derive(Serialize, Deserialize)]
32#[serde(tag = "type")]
33#[allow(clippy::enum_variant_names)]
34pub(crate) enum FlatRequest {
35 ApprovalRequest(ApprovalRequest),
36 ToolCallRequest(ToolCallRequest),
37 QuestionRequest(QuestionRequest),
38 HookRequest(HookRequest),
39}
40
41impl From<Request> for FlatRequest {
42 fn from(req: Request) -> Self {
43 match req {
44 Request::ApprovalRequest(inner) => FlatRequest::ApprovalRequest(inner),
45 Request::ToolCallRequest(inner) => FlatRequest::ToolCallRequest(inner),
46 Request::QuestionRequest(inner) => FlatRequest::QuestionRequest(inner),
47 Request::HookRequest(inner) => FlatRequest::HookRequest(inner),
48 }
49 }
50}
51
52impl From<FlatRequest> for Request {
53 fn from(req: FlatRequest) -> Self {
54 match req {
55 FlatRequest::ApprovalRequest(inner) => Request::ApprovalRequest(inner),
56 FlatRequest::ToolCallRequest(inner) => Request::ToolCallRequest(inner),
57 FlatRequest::QuestionRequest(inner) => Request::QuestionRequest(inner),
58 FlatRequest::HookRequest(inner) => Request::HookRequest(inner),
59 }
60 }
61}
62
63#[derive(Serialize, Deserialize)]
68struct RequestEnvelope {
69 #[serde(rename = "type")]
70 type_name: String,
71 payload: serde_json::Value,
72}
73
74impl Serialize for Request {
75 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
76 let flat = FlatRequest::from(self.clone());
77 let mut value = serde_json::to_value(&flat).map_err(serde::ser::Error::custom)?;
78 let obj = value
79 .as_object_mut()
80 .ok_or_else(|| serde::ser::Error::custom("expected object"))?;
81 let type_name = obj
82 .remove("type")
83 .and_then(|v| v.as_str().map(String::from))
84 .ok_or_else(|| serde::ser::Error::custom("missing type"))?;
85 RequestEnvelope { type_name, payload: value }.serialize(serializer)
86 }
87}
88
89impl<'de> Deserialize<'de> for Request {
90 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
91 let envelope = RequestEnvelope::deserialize(deserializer)?;
92 let mut value = envelope.payload;
93 if let Some(obj) = value.as_object_mut() {
94 obj.insert("type".to_string(), serde_json::Value::String(envelope.type_name));
95 }
96 let flat: FlatRequest = serde_json::from_value(value).map_err(serde::de::Error::custom)?;
97 Ok(Request::from(flat))
98 }
99}
100
101#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
103pub struct ApprovalRequest {
104 pub id: String,
106 pub tool_call_id: String,
108 pub sender: String,
110 pub action: String,
112 pub description: String,
114 #[serde(skip_serializing_if = "Option::is_none", default)]
116 pub display: Option<Vec<DisplayBlock>>,
117 #[serde(skip_serializing_if = "Option::is_none")]
119 pub source_kind: Option<SourceKind>,
120 #[serde(skip_serializing_if = "Option::is_none")]
122 pub source_id: Option<String>,
123 #[serde(skip_serializing_if = "Option::is_none")]
125 pub agent_id: Option<String>,
126 #[serde(skip_serializing_if = "Option::is_none")]
128 pub subagent_type: Option<String>,
129 #[serde(skip_serializing_if = "Option::is_none")]
131 pub source_description: Option<String>,
132}
133
134#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
136#[serde(rename_all = "snake_case")]
137#[non_exhaustive]
138pub enum SourceKind {
139 ForegroundTurn,
141 BackgroundAgent,
143}
144
145#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
147pub struct ToolCallRequest {
148 pub id: String,
150 pub name: String,
152 #[serde(skip_serializing_if = "Option::is_none")]
154 pub arguments: Option<String>,
155}
156
157#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
159pub struct QuestionRequest {
160 pub id: String,
162 pub tool_call_id: String,
164 pub questions: Vec<QuestionItem>,
166}
167
168#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
170pub struct QuestionItem {
171 pub question: String,
173 #[serde(skip_serializing_if = "Option::is_none")]
175 pub header: Option<String>,
176 pub options: Vec<QuestionOption>,
178 #[serde(skip_serializing_if = "Option::is_none")]
180 pub multi_select: Option<bool>,
181}
182
183#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
185pub struct QuestionOption {
186 pub label: String,
188 #[serde(skip_serializing_if = "Option::is_none")]
190 pub description: Option<String>,
191}
192
193#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
195pub struct HookRequest {
196 pub id: String,
198 pub subscription_id: String,
200 pub event: String,
202 pub target: String,
204 pub input_data: serde_json::Value,
206}
207
208#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
214pub struct ApprovalResponse {
215 pub request_id: String,
217 pub response: ApprovalResponseKind,
219 #[serde(skip_serializing_if = "Option::is_none")]
221 pub feedback: Option<String>,
222}
223
224pub use crate::protocol::event::ApprovalResponseKind;
225
226#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
228pub struct ToolCallResponse {
229 pub tool_call_id: String,
231 pub return_value: ToolReturnValue,
233}
234
235#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
237pub struct QuestionResponse {
238 pub request_id: String,
240 pub answers: std::collections::HashMap<String, String>,
243}
244
245#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
247pub struct HookResponse {
248 pub request_id: String,
250 pub action: HookAction,
252 pub reason: String,
254}