agent_client_protocol_schema/v2/
proxy.rs1use std::sync::Arc;
7
8use derive_more::From;
9use serde::{Deserialize, Serialize};
10use serde_json::value::RawValue;
11use serde_with::{DefaultOnError, serde_as, skip_serializing_none};
12
13use super::{InitializeRequest, InitializeResponse, Meta};
14use crate::IntoOption;
15
16#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
30#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
31#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "proxy", "x-method" = PROXY_INITIALIZE_METHOD_NAME)))]
32#[serde(transparent)]
33#[non_exhaustive]
34pub struct InitializeProxyRequest {
35 pub initialize: InitializeRequest,
37}
38
39impl InitializeProxyRequest {
40 #[must_use]
42 pub fn new(initialize: InitializeRequest) -> Self {
43 Self { initialize }
44 }
45}
46
47impl From<InitializeRequest> for InitializeProxyRequest {
48 fn from(initialize: InitializeRequest) -> Self {
49 Self::new(initialize)
50 }
51}
52
53#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
62#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
63#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "proxy", "x-method" = PROXY_INITIALIZE_METHOD_NAME)))]
64#[serde(transparent)]
65#[non_exhaustive]
66pub struct InitializeProxyResponse {
67 pub initialize: InitializeResponse,
69}
70
71impl InitializeProxyResponse {
72 #[must_use]
74 pub fn new(initialize: InitializeResponse) -> Self {
75 Self { initialize }
76 }
77}
78
79impl From<InitializeResponse> for InitializeProxyResponse {
80 fn from(initialize: InitializeResponse) -> Self {
81 Self::new(initialize)
82 }
83}
84
85#[serde_as]
102#[skip_serializing_none]
103#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
104#[derive(Debug, Clone, Serialize, Deserialize)]
105#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "proxy", "x-method" = PROXY_SUCCESSOR_METHOD_NAME)))]
106#[serde(rename_all = "camelCase")]
107#[non_exhaustive]
108pub struct ProxySuccessorRequest {
109 pub method: Arc<str>,
111 #[cfg_attr(feature = "schemars", schemars(with = "Option<serde_json::Value>"))]
116 #[serde(default)]
117 pub params: Option<Arc<RawValue>>,
118 #[serde_as(deserialize_as = "DefaultOnError")]
127 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
128 #[serde(default)]
129 #[serde(rename = "_meta")]
130 pub meta: Option<Meta>,
131}
132
133impl ProxySuccessorRequest {
134 #[must_use]
136 pub fn new(method: impl Into<Arc<str>>) -> Self {
137 Self {
138 method: method.into(),
139 params: None,
140 meta: None,
141 }
142 }
143
144 #[must_use]
146 pub fn params(mut self, params: impl IntoOption<Arc<RawValue>>) -> Self {
147 self.params = params.into_option();
148 self
149 }
150
151 #[must_use]
157 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
158 self.meta = meta.into_option();
159 self
160 }
161}
162
163#[serde_as]
175#[skip_serializing_none]
176#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
177#[derive(Debug, Clone, Serialize, Deserialize)]
178#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "proxy", "x-method" = PROXY_SUCCESSOR_METHOD_NAME)))]
179#[serde(rename_all = "camelCase")]
180#[non_exhaustive]
181pub struct ProxySuccessorNotification {
182 pub method: Arc<str>,
184 #[cfg_attr(feature = "schemars", schemars(with = "Option<serde_json::Value>"))]
189 #[serde(default)]
190 pub params: Option<Arc<RawValue>>,
191 #[serde_as(deserialize_as = "DefaultOnError")]
200 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
201 #[serde(default)]
202 #[serde(rename = "_meta")]
203 pub meta: Option<Meta>,
204}
205
206impl ProxySuccessorNotification {
207 #[must_use]
209 pub fn new(method: impl Into<Arc<str>>) -> Self {
210 Self {
211 method: method.into(),
212 params: None,
213 meta: None,
214 }
215 }
216
217 #[must_use]
219 pub fn params(mut self, params: impl IntoOption<Arc<RawValue>>) -> Self {
220 self.params = params.into_option();
221 self
222 }
223
224 #[must_use]
230 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
231 self.meta = meta.into_option();
232 self
233 }
234}
235
236#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
242#[derive(Debug, Clone, Serialize, Deserialize, From)]
243#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "proxy", "x-method" = PROXY_SUCCESSOR_METHOD_NAME)))]
244#[serde(transparent)]
245#[non_exhaustive]
246pub struct ProxySuccessorResponse(
247 #[cfg_attr(feature = "schemars", schemars(with = "serde_json::Value"))] pub Arc<RawValue>,
248);
249
250impl ProxySuccessorResponse {
251 #[must_use]
253 pub fn new(result: Arc<RawValue>) -> Self {
254 Self(result)
255 }
256}
257
258#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
266#[non_exhaustive]
267pub struct ProxyMethodNames {
268 pub proxy_initialize: &'static str,
270 pub proxy_successor: &'static str,
272}
273
274pub const PROXY_METHOD_NAMES: ProxyMethodNames = ProxyMethodNames {
276 proxy_initialize: PROXY_INITIALIZE_METHOD_NAME,
277 proxy_successor: PROXY_SUCCESSOR_METHOD_NAME,
278};
279
280pub(crate) const PROXY_INITIALIZE_METHOD_NAME: &str = "proxy/initialize";
282pub(crate) const PROXY_SUCCESSOR_METHOD_NAME: &str = "proxy/successor";
284
285#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
294#[derive(Clone, Debug, Serialize, Deserialize)]
295#[serde(untagged)]
296#[cfg_attr(feature = "schemars", schemars(inline))]
297#[non_exhaustive]
298pub enum ProxyRequest {
299 InitializeProxyRequest(Box<InitializeProxyRequest>),
304 ProxySuccessorRequest(Box<ProxySuccessorRequest>),
310}
311
312impl ProxyRequest {
313 #[must_use]
315 pub fn method(&self) -> &str {
316 match self {
317 Self::InitializeProxyRequest(_) => PROXY_METHOD_NAMES.proxy_initialize,
318 Self::ProxySuccessorRequest(_) => PROXY_METHOD_NAMES.proxy_successor,
319 }
320 }
321}
322
323#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
329#[derive(Clone, Debug, Serialize, Deserialize)]
330#[serde(untagged)]
331#[cfg_attr(feature = "schemars", schemars(inline))]
332#[non_exhaustive]
333pub enum ProxyResponse {
334 InitializeProxyResponse(Box<InitializeProxyResponse>),
336 ProxySuccessorResponse(Box<ProxySuccessorResponse>),
338}
339
340#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
346#[derive(Clone, Debug, Serialize, Deserialize)]
347#[serde(untagged)]
348#[cfg_attr(feature = "schemars", schemars(inline))]
349#[non_exhaustive]
350pub enum ProxyNotification {
351 ProxySuccessorNotification(Box<ProxySuccessorNotification>),
356}
357
358impl ProxyNotification {
359 #[must_use]
361 pub fn method(&self) -> &str {
362 match self {
363 Self::ProxySuccessorNotification(_) => PROXY_METHOD_NAMES.proxy_successor,
364 }
365 }
366}
367
368#[cfg(test)]
369mod tests {
370 use serde_json::json;
371
372 use super::*;
373 use crate::ProtocolVersion;
374
375 fn raw(value: &serde_json::Value) -> Arc<RawValue> {
376 RawValue::from_string(value.to_string()).unwrap().into()
377 }
378
379 #[test]
380 fn test_initialize_proxy_request_matches_initialize_params() {
381 let request = InitializeProxyRequest::new(InitializeRequest::new(
382 ProtocolVersion::V2,
383 crate::v2::Implementation::new("conductor", "1.0.0"),
384 ));
385 let value = serde_json::to_value(&request).unwrap();
386 assert_eq!(value, serde_json::to_value(&request.initialize).unwrap());
387 assert_eq!(
388 ProxyRequest::InitializeProxyRequest(Box::new(request)).method(),
389 "proxy/initialize"
390 );
391 let response = InitializeProxyResponse::new(InitializeResponse::new(
392 ProtocolVersion::V2,
393 crate::v2::Implementation::new("proxy", "1.0.0"),
394 ));
395 assert_eq!(
396 serde_json::to_value(&response).unwrap(),
397 serde_json::to_value(&response.initialize).unwrap()
398 );
399 let parsed: InitializeProxyRequest = serde_json::from_value(value).unwrap();
400 assert_eq!(parsed.initialize.protocol_version, ProtocolVersion::V2);
401 }
402
403 #[test]
404 fn test_proxy_successor_round_trip() {
405 let request = ProxySuccessorRequest::new("session/prompt")
406 .params(raw(
407 &json!({"sessionId": "sess_abc123", "_meta": {"inner": true}}),
408 ))
409 .meta(json!({"outer": true}).as_object().cloned());
410 let value = serde_json::to_value(&request).unwrap();
411 assert_eq!(
412 value,
413 json!({
414 "method": "session/prompt",
415 "params": {"sessionId": "sess_abc123", "_meta": {"inner": true}},
416 "_meta": {"outer": true}
417 })
418 );
419 assert_eq!(
420 ProxyRequest::ProxySuccessorRequest(Box::new(request)).method(),
421 "proxy/successor"
422 );
423
424 for params in [
425 json!({"method": "session/cancel"}),
426 json!({"method": "session/cancel", "params": null}),
427 ] {
428 let notification: ProxySuccessorNotification = serde_json::from_value(params).unwrap();
429 assert_eq!(&*notification.method, "session/cancel");
430 assert!(notification.params.is_none());
431 }
432 assert!(
433 serde_json::from_value::<ProxySuccessorNotification>(json!({"params": {}})).is_err()
434 );
435
436 let response = ProxySuccessorResponse::new(raw(&json!({"stopReason": "end_turn"})));
437 assert_eq!(
438 serde_json::to_value(&response).unwrap(),
439 json!({"stopReason": "end_turn"})
440 );
441 }
442}