Skip to main content

kimi_wire/protocol/
request.rs

1use serde::{Deserialize, Serialize, Serializer};
2
3use super::content::{DisplayBlock, ToolReturnValue};
4use super::event::HookAction;
5
6/// A request from the agent to the client, sent via the `request` method.
7///
8/// The client must respond before the agent can continue execution.
9///
10/// Wire type names are `PascalCase` (e.g. `ApprovalRequest`).
11///
12/// Serialization follows the official wire envelope format:
13/// `{"type": "ApprovalRequest", "payload": {"id": ...}}`.
14#[derive(Debug, Clone, PartialEq)]
15#[non_exhaustive]
16pub enum Request {
17    /// Request for user approval before executing a tool.
18    ApprovalRequest(ApprovalRequest),
19    /// Request to execute a tool.
20    ToolCallRequest(ToolCallRequest),
21    /// Interactive question for the user.
22    QuestionRequest(QuestionRequest),
23    /// Hook trigger notification.
24    HookRequest(HookRequest),
25}
26
27// ---------------------------------------------------------------------------
28// FlatRequest – internal mirror used for (de)serialization
29// ---------------------------------------------------------------------------
30
31#[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// ---------------------------------------------------------------------------
64// RequestEnvelope – {type, payload} wire format
65// ---------------------------------------------------------------------------
66
67#[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/// Approval request sent by the agent.
109#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
110pub struct ApprovalRequest {
111    /// Request id.
112    pub id: String,
113    /// Id of the tool call requiring approval.
114    pub tool_call_id: String,
115    /// Tool sender name.
116    pub sender: String,
117    /// Action description.
118    pub action: String,
119    /// Detailed description.
120    pub description: String,
121    /// Display blocks for the user.
122    #[serde(skip_serializing_if = "Option::is_none", default)]
123    pub display: Option<Vec<DisplayBlock>>,
124    /// Source of the request.
125    #[serde(skip_serializing_if = "Option::is_none")]
126    pub source_kind: Option<SourceKind>,
127    /// Source id.
128    #[serde(skip_serializing_if = "Option::is_none")]
129    pub source_id: Option<String>,
130    /// Agent id.
131    #[serde(skip_serializing_if = "Option::is_none")]
132    pub agent_id: Option<String>,
133    /// Subagent type.
134    #[serde(skip_serializing_if = "Option::is_none")]
135    pub subagent_type: Option<String>,
136    /// Source description.
137    #[serde(skip_serializing_if = "Option::is_none")]
138    pub source_description: Option<String>,
139}
140
141/// Source of an approval request.
142#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
143#[serde(rename_all = "snake_case")]
144#[non_exhaustive]
145pub enum SourceKind {
146    /// Request originated from a foreground turn.
147    ForegroundTurn,
148    /// Request originated from a background agent.
149    BackgroundAgent,
150}
151
152/// Tool call request sent by the agent.
153#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
154pub struct ToolCallRequest {
155    /// Request id.
156    pub id: String,
157    /// Tool name.
158    pub name: String,
159    /// JSON-encoded arguments.
160    #[serde(skip_serializing_if = "Option::is_none")]
161    pub arguments: Option<String>,
162}
163
164/// Question request sent by the agent.
165#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
166pub struct QuestionRequest {
167    /// Request id.
168    pub id: String,
169    /// Id of the tool call that triggered the question.
170    pub tool_call_id: String,
171    /// Questions to ask the user.
172    pub questions: Vec<QuestionItem>,
173}
174
175/// A single question item.
176#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
177pub struct QuestionItem {
178    /// Question text.
179    pub question: String,
180    /// Short label, max 12 characters.
181    #[serde(skip_serializing_if = "Option::is_none")]
182    pub header: Option<String>,
183    /// Available options.
184    pub options: Vec<QuestionOption>,
185    /// Whether multiple options can be selected.
186    #[serde(skip_serializing_if = "Option::is_none")]
187    pub multi_select: Option<bool>,
188}
189
190/// An option for a question.
191#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
192pub struct QuestionOption {
193    /// Option label.
194    pub label: String,
195    /// Option description.
196    #[serde(skip_serializing_if = "Option::is_none")]
197    pub description: Option<String>,
198}
199
200/// Hook request sent by the agent.
201#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
202pub struct HookRequest {
203    /// Request id.
204    pub id: String,
205    /// Subscription id that matched.
206    pub subscription_id: String,
207    /// Event name.
208    pub event: String,
209    /// Event target.
210    pub target: String,
211    /// Event input data.
212    pub input_data: serde_json::Value,
213}
214
215// ============================================================================
216// Response types (what the client sends back)
217// ============================================================================
218
219/// Response to an [`ApprovalRequest`].
220#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
221pub struct ApprovalResponse {
222    /// Id of the approval request.
223    pub request_id: String,
224    /// Approval decision.
225    pub response: ApprovalResponseKind,
226    /// Optional feedback text.
227    #[serde(skip_serializing_if = "Option::is_none")]
228    pub feedback: Option<String>,
229}
230
231pub use crate::protocol::event::ApprovalResponseKind;
232
233/// Response to a [`ToolCallRequest`].
234#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
235pub struct ToolCallResponse {
236    /// Id of the tool call.
237    pub tool_call_id: String,
238    /// Tool return value.
239    pub return_value: ToolReturnValue,
240}
241
242/// Response to a [`QuestionRequest`].
243#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
244pub struct QuestionResponse {
245    /// Id of the question request.
246    pub request_id: String,
247    /// Mapping of question text to selected option label(s).
248    /// For multi-select, values are comma-separated.
249    pub answers: std::collections::HashMap<String, String>,
250}
251
252/// Response to a [`HookRequest`].
253#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
254pub struct HookResponse {
255    /// Id of the hook request.
256    pub request_id: String,
257    /// Action taken.
258    pub action: HookAction,
259    /// Reason for the action.
260    pub reason: String,
261}