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) => Self::ApprovalRequest(inner),
45 Request::ToolCallRequest(inner) => Self::ToolCallRequest(inner),
46 Request::QuestionRequest(inner) => Self::QuestionRequest(inner),
47 Request::HookRequest(inner) => Self::HookRequest(inner),
48 }
49 }
50}
51
52impl From<FlatRequest> for Request {
53 fn from(req: FlatRequest) -> Self {
54 match req {
55 FlatRequest::ApprovalRequest(inner) => Self::ApprovalRequest(inner),
56 FlatRequest::ToolCallRequest(inner) => Self::ToolCallRequest(inner),
57 FlatRequest::QuestionRequest(inner) => Self::QuestionRequest(inner),
58 FlatRequest::HookRequest(inner) => Self::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 {
86 type_name,
87 payload: value,
88 }
89 .serialize(serializer)
90 }
91}
92
93impl<'de> Deserialize<'de> for Request {
94 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
95 let envelope = RequestEnvelope::deserialize(deserializer)?;
96 let mut value = envelope.payload;
97 if let Some(obj) = value.as_object_mut() {
98 obj.insert(
99 "type".to_string(),
100 serde_json::Value::String(envelope.type_name),
101 );
102 }
103 let flat: FlatRequest = serde_json::from_value(value).map_err(serde::de::Error::custom)?;
104 Ok(Self::from(flat))
105 }
106}
107
108#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
110pub struct ApprovalRequest {
111 pub id: String,
113 pub tool_call_id: String,
115 pub sender: String,
117 pub action: String,
119 pub description: String,
121 #[serde(skip_serializing_if = "Option::is_none", default)]
123 pub display: Option<Vec<DisplayBlock>>,
124 #[serde(skip_serializing_if = "Option::is_none")]
126 pub source_kind: Option<SourceKind>,
127 #[serde(skip_serializing_if = "Option::is_none")]
129 pub source_id: Option<String>,
130 #[serde(skip_serializing_if = "Option::is_none")]
132 pub agent_id: Option<String>,
133 #[serde(skip_serializing_if = "Option::is_none")]
135 pub subagent_type: Option<String>,
136 #[serde(skip_serializing_if = "Option::is_none")]
138 pub source_description: Option<String>,
139}
140
141#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
143#[serde(rename_all = "snake_case")]
144#[non_exhaustive]
145pub enum SourceKind {
146 ForegroundTurn,
148 BackgroundAgent,
150}
151
152#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
154pub struct ToolCallRequest {
155 pub id: String,
157 pub name: String,
159 #[serde(skip_serializing_if = "Option::is_none")]
161 pub arguments: Option<String>,
162}
163
164#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
166pub struct QuestionRequest {
167 pub id: String,
169 pub tool_call_id: String,
171 pub questions: Vec<QuestionItem>,
173}
174
175#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
177pub struct QuestionItem {
178 pub question: String,
180 #[serde(skip_serializing_if = "Option::is_none")]
182 pub header: Option<String>,
183 pub options: Vec<QuestionOption>,
185 #[serde(skip_serializing_if = "Option::is_none")]
187 pub multi_select: Option<bool>,
188}
189
190#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
192pub struct QuestionOption {
193 pub label: String,
195 #[serde(skip_serializing_if = "Option::is_none")]
197 pub description: Option<String>,
198}
199
200#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
202pub struct HookRequest {
203 pub id: String,
205 pub subscription_id: String,
207 pub event: String,
209 pub target: String,
211 pub input_data: serde_json::Value,
213}
214
215#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
221pub struct ApprovalResponse {
222 pub request_id: String,
224 pub response: ApprovalResponseKind,
226 #[serde(skip_serializing_if = "Option::is_none")]
228 pub feedback: Option<String>,
229}
230
231pub use crate::protocol::event::ApprovalResponseKind;
232
233#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
235pub struct ToolCallResponse {
236 pub tool_call_id: String,
238 pub return_value: ToolReturnValue,
240}
241
242#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
244pub struct QuestionResponse {
245 pub request_id: String,
247 pub answers: std::collections::HashMap<String, String>,
250}
251
252#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
254pub struct HookResponse {
255 pub request_id: String,
257 pub action: HookAction,
259 pub reason: String,
261}