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