1use std::{borrow::Cow, collections::BTreeMap, sync::Arc};
7
8#[cfg(feature = "unstable_llm_providers")]
9use std::collections::HashMap;
10
11use derive_more::{Display, From};
12#[cfg(feature = "schemars")]
13use schemars::Schema;
14use serde::{Deserialize, Serialize};
15use serde_with::{DefaultOnError, VecSkipError, serde_as, skip_serializing_none};
16
17use super::{
18 AbsolutePath, AvailableCommand, ClientCapabilities, ContentBlock, ExtNotification, ExtRequest,
19 ExtResponse, MessageId, Meta, SessionId,
20};
21use crate::{IntoOption, ProtocolVersion};
22
23#[cfg(feature = "unstable_mcp_over_acp")]
24use super::mcp::{MCP_MESSAGE_METHOD_NAME, MessageMcpNotification};
25
26#[cfg(feature = "unstable_nes")]
27use super::{
28 AcceptNesNotification, CloseNesRequest, CloseNesResponse, DidChangeDocumentNotification,
29 DidCloseDocumentNotification, DidFocusDocumentNotification, DidOpenDocumentNotification,
30 DidSaveDocumentNotification, NesCapabilities, PositionEncodingKind, RejectNesNotification,
31 StartNesRequest, StartNesResponse, SuggestNesRequest, SuggestNesResponse,
32};
33
34#[cfg(feature = "unstable_nes")]
35use super::{
36 DOCUMENT_DID_CHANGE_METHOD_NAME, DOCUMENT_DID_CLOSE_METHOD_NAME,
37 DOCUMENT_DID_FOCUS_METHOD_NAME, DOCUMENT_DID_OPEN_METHOD_NAME, DOCUMENT_DID_SAVE_METHOD_NAME,
38 NES_ACCEPT_METHOD_NAME, NES_CLOSE_METHOD_NAME, NES_REJECT_METHOD_NAME, NES_START_METHOD_NAME,
39 NES_SUGGEST_METHOD_NAME,
40};
41
42#[serde_as]
50#[skip_serializing_none]
51#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
52#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
53#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = INITIALIZE_METHOD_NAME)))]
54#[serde(rename_all = "camelCase")]
55#[non_exhaustive]
56pub struct InitializeRequest {
57 pub protocol_version: ProtocolVersion,
59 pub info: Implementation,
61 #[serde_as(deserialize_as = "DefaultOnError")]
63 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
64 #[serde(default)]
65 pub capabilities: ClientCapabilities,
66 #[serde_as(deserialize_as = "DefaultOnError")]
72 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
73 #[serde(default)]
74 #[serde(rename = "_meta")]
75 pub meta: Option<Meta>,
76}
77
78impl InitializeRequest {
79 #[must_use]
81 pub fn new(protocol_version: ProtocolVersion, info: Implementation) -> Self {
82 Self {
83 protocol_version,
84 capabilities: ClientCapabilities::default(),
85 info,
86 meta: None,
87 }
88 }
89
90 #[must_use]
92 pub fn capabilities(mut self, capabilities: ClientCapabilities) -> Self {
93 self.capabilities = capabilities;
94 self
95 }
96
97 #[must_use]
103 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
104 self.meta = meta.into_option();
105 self
106 }
107}
108
109#[serde_as]
115#[skip_serializing_none]
116#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
117#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
118#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = INITIALIZE_METHOD_NAME)))]
119#[serde(rename_all = "camelCase")]
120#[non_exhaustive]
121pub struct InitializeResponse {
122 pub protocol_version: ProtocolVersion,
127 pub info: Implementation,
129 #[serde_as(deserialize_as = "DefaultOnError")]
131 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
132 #[serde(default)]
133 pub capabilities: AgentCapabilities,
134 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
140 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
141 #[serde(default, skip_serializing_if = "Vec::is_empty")]
142 pub auth_methods: Vec<AuthMethod>,
143 #[serde_as(deserialize_as = "DefaultOnError")]
149 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
150 #[serde(default)]
151 #[serde(rename = "_meta")]
152 pub meta: Option<Meta>,
153}
154
155impl InitializeResponse {
156 #[must_use]
158 pub fn new(protocol_version: ProtocolVersion, info: Implementation) -> Self {
159 Self {
160 protocol_version,
161 capabilities: AgentCapabilities::default(),
162 auth_methods: vec![],
163 info,
164 meta: None,
165 }
166 }
167
168 #[must_use]
170 pub fn capabilities(mut self, capabilities: AgentCapabilities) -> Self {
171 self.capabilities = capabilities;
172 self
173 }
174
175 #[must_use]
180 pub fn auth_methods(mut self, auth_methods: Vec<AuthMethod>) -> Self {
181 self.auth_methods = auth_methods;
182 self
183 }
184
185 #[must_use]
191 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
192 self.meta = meta.into_option();
193 self
194 }
195}
196
197#[serde_as]
201#[skip_serializing_none]
202#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
203#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
204#[serde(rename_all = "camelCase")]
205#[non_exhaustive]
206pub struct Implementation {
207 pub name: String,
210 #[serde_as(deserialize_as = "DefaultOnError")]
215 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
216 #[serde(default)]
217 pub title: Option<String>,
218 pub version: String,
221 #[serde_as(deserialize_as = "DefaultOnError")]
227 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
228 #[serde(default)]
229 #[serde(rename = "_meta")]
230 pub meta: Option<Meta>,
231}
232
233impl Implementation {
234 #[must_use]
236 pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
237 Self {
238 name: name.into(),
239 title: None,
240 version: version.into(),
241 meta: None,
242 }
243 }
244
245 #[must_use]
250 pub fn title(mut self, title: impl IntoOption<String>) -> Self {
251 self.title = title.into_option();
252 self
253 }
254
255 #[must_use]
261 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
262 self.meta = meta.into_option();
263 self
264 }
265}
266
267#[serde_as]
277#[skip_serializing_none]
278#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
279#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
280#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGIN_METHOD_NAME)))]
281#[serde(rename_all = "camelCase")]
282#[non_exhaustive]
283pub struct LoginAuthRequest {
284 pub method_id: AuthMethodId,
287 #[serde_as(deserialize_as = "DefaultOnError")]
293 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
294 #[serde(default)]
295 #[serde(rename = "_meta")]
296 pub meta: Option<Meta>,
297}
298
299impl LoginAuthRequest {
300 #[must_use]
302 pub fn new(method_id: impl Into<AuthMethodId>) -> Self {
303 Self {
304 method_id: method_id.into(),
305 meta: None,
306 }
307 }
308
309 #[must_use]
315 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
316 self.meta = meta.into_option();
317 self
318 }
319}
320
321crate::serde_util::default_on_null! {
322 #[serde_as]
324 #[skip_serializing_none]
325 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
326 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
327 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGIN_METHOD_NAME)))]
328 #[serde(rename_all = "camelCase")]
329 #[non_exhaustive]
330 pub struct LoginAuthResponse {
331 #[serde_as(deserialize_as = "DefaultOnError")]
337 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
338 #[serde(default)]
339 #[serde(rename = "_meta")]
340 pub meta: Option<Meta>,
341 }
342}
343
344impl LoginAuthResponse {
345 #[must_use]
347 pub fn new() -> Self {
348 Self::default()
349 }
350
351 #[must_use]
357 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
358 self.meta = meta.into_option();
359 self
360 }
361}
362
363crate::serde_util::default_on_null! {
366 #[serde_as]
374 #[skip_serializing_none]
375 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
376 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
377 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGOUT_METHOD_NAME)))]
378 #[serde(rename_all = "camelCase")]
379 #[non_exhaustive]
380 pub struct LogoutAuthRequest {
381 #[serde_as(deserialize_as = "DefaultOnError")]
387 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
388 #[serde(default)]
389 #[serde(rename = "_meta")]
390 pub meta: Option<Meta>,
391 }
392}
393
394impl LogoutAuthRequest {
395 #[must_use]
397 pub fn new() -> Self {
398 Self::default()
399 }
400
401 #[must_use]
407 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
408 self.meta = meta.into_option();
409 self
410 }
411}
412
413crate::serde_util::default_on_null! {
414 #[serde_as]
416 #[skip_serializing_none]
417 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
418 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
419 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGOUT_METHOD_NAME)))]
420 #[serde(rename_all = "camelCase")]
421 #[non_exhaustive]
422 pub struct LogoutAuthResponse {
423 #[serde_as(deserialize_as = "DefaultOnError")]
429 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
430 #[serde(default)]
431 #[serde(rename = "_meta")]
432 pub meta: Option<Meta>,
433 }
434}
435
436impl LogoutAuthResponse {
437 #[must_use]
439 pub fn new() -> Self {
440 Self::default()
441 }
442
443 #[must_use]
449 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
450 self.meta = meta.into_option();
451 self
452 }
453}
454
455#[serde_as]
461#[skip_serializing_none]
462#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
463#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
464#[serde(rename_all = "camelCase")]
465#[non_exhaustive]
466pub struct AgentAuthCapabilities {
467 #[serde_as(deserialize_as = "DefaultOnError")]
473 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
474 #[serde(default)]
475 #[serde(rename = "_meta")]
476 pub meta: Option<Meta>,
477}
478
479impl AgentAuthCapabilities {
480 #[must_use]
482 pub fn new() -> Self {
483 Self::default()
484 }
485
486 #[must_use]
492 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
493 self.meta = meta.into_option();
494 self
495 }
496}
497
498#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
500#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
501#[serde(transparent)]
502#[from(forward)]
503#[non_exhaustive]
504pub struct AuthMethodId(pub Arc<str>);
505
506impl AuthMethodId {
507 #[must_use]
509 pub fn new(id: impl Into<Self>) -> Self {
510 id.into()
511 }
512}
513
514#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
518#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
519#[serde(tag = "type", rename_all = "snake_case")]
520#[non_exhaustive]
521pub enum AuthMethod {
522 Terminal(AuthMethodTerminal),
525 Agent(AuthMethodAgent),
529 #[serde(untagged)]
539 Other(OtherAuthMethod),
540}
541
542impl AuthMethod {
543 #[must_use]
545 pub fn method_id(&self) -> &AuthMethodId {
546 match self {
547 Self::Agent(a) => &a.method_id,
548 Self::Other(a) => &a.method_id,
549 Self::Terminal(t) => &t.method_id,
550 }
551 }
552
553 #[must_use]
555 pub fn name(&self) -> &str {
556 match self {
557 Self::Agent(a) => &a.name,
558 Self::Other(a) => &a.name,
559 Self::Terminal(t) => &t.name,
560 }
561 }
562
563 #[must_use]
565 pub fn description(&self) -> Option<&str> {
566 match self {
567 Self::Agent(a) => a.description.as_deref(),
568 Self::Other(a) => a.description.as_deref(),
569 Self::Terminal(t) => t.description.as_deref(),
570 }
571 }
572
573 #[must_use]
579 pub fn meta(&self) -> Option<&Meta> {
580 match self {
581 Self::Agent(a) => a.meta.as_ref(),
582 Self::Other(a) => a.meta.as_ref(),
583 Self::Terminal(t) => t.meta.as_ref(),
584 }
585 }
586}
587
588#[serde_as]
590#[skip_serializing_none]
591#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
592#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
593#[cfg_attr(feature = "schemars", schemars(inline))]
594#[cfg_attr(feature = "schemars", schemars(transform = other_auth_method_schema))]
595#[serde(rename_all = "camelCase")]
596#[non_exhaustive]
597pub struct OtherAuthMethod {
598 #[serde(rename = "type")]
604 pub type_: String,
605 pub method_id: AuthMethodId,
607 pub name: String,
609 #[serde_as(deserialize_as = "DefaultOnError")]
611 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
612 #[serde(default)]
613 pub description: Option<String>,
614 #[serde_as(deserialize_as = "DefaultOnError")]
620 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
621 #[serde(default)]
622 #[serde(rename = "_meta")]
623 pub meta: Option<Meta>,
624 #[serde(flatten)]
626 pub fields: BTreeMap<String, serde_json::Value>,
627}
628
629impl OtherAuthMethod {
630 #[must_use]
632 pub fn new(
633 type_: impl Into<String>,
634 method_id: impl Into<AuthMethodId>,
635 name: impl Into<String>,
636 mut fields: BTreeMap<String, serde_json::Value>,
637 ) -> Self {
638 fields.remove("type");
639 fields.remove("methodId");
640 fields.remove("name");
641 fields.remove("description");
642 fields.remove("_meta");
643 Self {
644 type_: type_.into(),
645 method_id: method_id.into(),
646 name: name.into(),
647 description: None,
648 meta: None,
649 fields,
650 }
651 }
652
653 #[must_use]
655 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
656 self.description = description.into_option();
657 self
658 }
659
660 #[must_use]
666 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
667 self.meta = meta.into_option();
668 self
669 }
670}
671
672impl<'de> Deserialize<'de> for OtherAuthMethod {
673 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
674 where
675 D: serde::Deserializer<'de>,
676 {
677 #[derive(Deserialize)]
678 #[serde(rename_all = "camelCase")]
679 struct RawOtherAuthMethod {
680 #[serde(rename = "type")]
681 type_: String,
682 method_id: AuthMethodId,
683 name: String,
684 description: Option<String>,
685 #[serde(rename = "_meta")]
686 meta: Option<Meta>,
687 #[serde(flatten)]
688 fields: BTreeMap<String, serde_json::Value>,
689 }
690
691 let raw = RawOtherAuthMethod::deserialize(deserializer)?;
692 if is_known_auth_method_type(&raw.type_) {
693 return Err(serde::de::Error::custom(format!(
694 "known authentication method `{}` did not match its schema",
695 raw.type_
696 )));
697 }
698
699 Ok(Self {
700 type_: raw.type_,
701 method_id: raw.method_id,
702 name: raw.name,
703 description: raw.description,
704 meta: raw.meta,
705 fields: raw.fields,
706 })
707 }
708}
709
710fn is_known_auth_method_type(type_: &str) -> bool {
711 matches!(type_, "agent" | "terminal")
712}
713
714#[cfg(feature = "schemars")]
715fn other_auth_method_schema(schema: &mut Schema) {
716 super::schema_util::reject_known_string_discriminators(schema, "type", &["agent", "terminal"]);
717}
718
719#[serde_as]
723#[skip_serializing_none]
724#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
725#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
726#[serde(rename_all = "camelCase")]
727#[non_exhaustive]
728pub struct AuthMethodAgent {
729 pub method_id: AuthMethodId,
731 pub name: String,
733 #[serde_as(deserialize_as = "DefaultOnError")]
735 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
736 #[serde(default)]
737 pub description: Option<String>,
738 #[serde_as(deserialize_as = "DefaultOnError")]
744 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
745 #[serde(default)]
746 #[serde(rename = "_meta")]
747 pub meta: Option<Meta>,
748}
749
750impl AuthMethodAgent {
751 #[must_use]
753 pub fn new(method_id: impl Into<AuthMethodId>, name: impl Into<String>) -> Self {
754 Self {
755 method_id: method_id.into(),
756 name: name.into(),
757 description: None,
758 meta: None,
759 }
760 }
761
762 #[must_use]
764 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
765 self.description = description.into_option();
766 self
767 }
768
769 #[must_use]
775 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
776 self.meta = meta.into_option();
777 self
778 }
779}
780
781#[serde_as]
789#[skip_serializing_none]
790#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
791#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
792#[serde(rename_all = "camelCase")]
793#[non_exhaustive]
794pub struct AuthMethodTerminal {
795 pub method_id: AuthMethodId,
797 pub name: String,
799 #[serde_as(deserialize_as = "DefaultOnError")]
801 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
802 #[serde(default)]
803 pub description: Option<String>,
804 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
806 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
807 #[serde(default, skip_serializing_if = "Vec::is_empty")]
808 pub args: Vec<String>,
809 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
813 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
814 #[serde(default, skip_serializing_if = "Vec::is_empty")]
815 pub env: Vec<EnvVariable>,
816 #[serde_as(deserialize_as = "DefaultOnError")]
822 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
823 #[serde(default)]
824 #[serde(rename = "_meta")]
825 pub meta: Option<Meta>,
826}
827
828impl AuthMethodTerminal {
829 #[must_use]
831 pub fn new(method_id: impl Into<AuthMethodId>, name: impl Into<String>) -> Self {
832 Self {
833 method_id: method_id.into(),
834 name: name.into(),
835 description: None,
836 args: Vec::new(),
837 env: Vec::new(),
838 meta: None,
839 }
840 }
841
842 #[must_use]
844 pub fn args(mut self, args: Vec<String>) -> Self {
845 self.args = args;
846 self
847 }
848
849 #[must_use]
853 pub fn env(mut self, env: Vec<EnvVariable>) -> Self {
854 self.env = env;
855 self
856 }
857
858 #[must_use]
860 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
861 self.description = description.into_option();
862 self
863 }
864
865 #[must_use]
871 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
872 self.meta = meta.into_option();
873 self
874 }
875}
876
877#[serde_as]
883#[skip_serializing_none]
884#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
885#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
886#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_NEW_METHOD_NAME)))]
887#[serde(rename_all = "camelCase")]
888#[non_exhaustive]
889pub struct NewSessionRequest {
890 pub cwd: AbsolutePath,
892 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
898 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
899 #[serde(default, skip_serializing_if = "Vec::is_empty")]
900 pub additional_directories: Vec<AbsolutePath>,
901 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
903 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
904 #[serde(default, skip_serializing_if = "Vec::is_empty")]
905 pub mcp_servers: Vec<McpServer>,
906 #[serde_as(deserialize_as = "DefaultOnError")]
912 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
913 #[serde(default)]
914 #[serde(rename = "_meta")]
915 pub meta: Option<Meta>,
916}
917
918impl NewSessionRequest {
919 #[must_use]
921 pub fn new(cwd: impl Into<AbsolutePath>) -> Self {
922 Self {
923 cwd: cwd.into(),
924 additional_directories: vec![],
925 mcp_servers: vec![],
926 meta: None,
927 }
928 }
929
930 #[must_use]
932 pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
933 where
934 I: IntoIterator<Item = P>,
935 P: Into<AbsolutePath>,
936 {
937 self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
938 self
939 }
940
941 #[must_use]
943 pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
944 self.mcp_servers = mcp_servers;
945 self
946 }
947
948 #[must_use]
954 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
955 self.meta = meta.into_option();
956 self
957 }
958}
959
960#[serde_as]
964#[skip_serializing_none]
965#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
966#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
967#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_NEW_METHOD_NAME)))]
968#[serde(rename_all = "camelCase")]
969#[non_exhaustive]
970pub struct NewSessionResponse {
971 pub session_id: SessionId,
975 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
977 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
978 #[serde(default, skip_serializing_if = "Vec::is_empty")]
979 pub config_options: Vec<SessionConfigOption>,
980 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
986 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
987 #[serde(default, skip_serializing_if = "Vec::is_empty")]
988 pub available_commands: Vec<AvailableCommand>,
989 #[serde_as(deserialize_as = "DefaultOnError")]
995 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
996 #[serde(default)]
997 #[serde(rename = "_meta")]
998 pub meta: Option<Meta>,
999}
1000
1001impl NewSessionResponse {
1002 #[must_use]
1004 pub fn new(session_id: impl Into<SessionId>) -> Self {
1005 Self {
1006 session_id: session_id.into(),
1007 config_options: Vec::new(),
1008 available_commands: Vec::new(),
1009 meta: None,
1010 }
1011 }
1012
1013 #[must_use]
1015 pub fn config_options(mut self, config_options: Vec<SessionConfigOption>) -> Self {
1016 self.config_options = config_options;
1017 self
1018 }
1019
1020 #[must_use]
1022 pub fn available_commands(mut self, available_commands: Vec<AvailableCommand>) -> Self {
1023 self.available_commands = available_commands;
1024 self
1025 }
1026
1027 #[must_use]
1033 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1034 self.meta = meta.into_option();
1035 self
1036 }
1037}
1038
1039#[cfg(feature = "unstable_session_fork")]
1052#[serde_as]
1053#[skip_serializing_none]
1054#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1055#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1056#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_FORK_METHOD_NAME)))]
1057#[serde(rename_all = "camelCase")]
1058#[non_exhaustive]
1059pub struct ForkSessionRequest {
1060 pub session_id: SessionId,
1062 pub cwd: AbsolutePath,
1064 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1070 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1071 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1072 pub additional_directories: Vec<AbsolutePath>,
1073 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1075 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1076 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1077 pub mcp_servers: Vec<McpServer>,
1078 #[serde_as(deserialize_as = "DefaultOnError")]
1084 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1085 #[serde(default)]
1086 #[serde(rename = "_meta")]
1087 pub meta: Option<Meta>,
1088}
1089
1090#[cfg(feature = "unstable_session_fork")]
1091impl ForkSessionRequest {
1092 #[must_use]
1094 pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<AbsolutePath>) -> Self {
1095 Self {
1096 session_id: session_id.into(),
1097 cwd: cwd.into(),
1098 additional_directories: vec![],
1099 mcp_servers: vec![],
1100 meta: None,
1101 }
1102 }
1103
1104 #[must_use]
1106 pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
1107 where
1108 I: IntoIterator<Item = P>,
1109 P: Into<AbsolutePath>,
1110 {
1111 self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
1112 self
1113 }
1114
1115 #[must_use]
1117 pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1118 self.mcp_servers = mcp_servers;
1119 self
1120 }
1121
1122 #[must_use]
1128 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1129 self.meta = meta.into_option();
1130 self
1131 }
1132}
1133
1134#[cfg(feature = "unstable_session_fork")]
1140#[serde_as]
1141#[skip_serializing_none]
1142#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1143#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1144#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_FORK_METHOD_NAME)))]
1145#[serde(rename_all = "camelCase")]
1146#[non_exhaustive]
1147pub struct ForkSessionResponse {
1148 pub session_id: SessionId,
1150 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1152 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1153 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1154 pub config_options: Vec<SessionConfigOption>,
1155 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1161 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1162 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1163 pub available_commands: Vec<AvailableCommand>,
1164 #[serde_as(deserialize_as = "DefaultOnError")]
1170 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1171 #[serde(default)]
1172 #[serde(rename = "_meta")]
1173 pub meta: Option<Meta>,
1174}
1175
1176#[cfg(feature = "unstable_session_fork")]
1177impl ForkSessionResponse {
1178 #[must_use]
1180 pub fn new(session_id: impl Into<SessionId>) -> Self {
1181 Self {
1182 session_id: session_id.into(),
1183 config_options: Vec::new(),
1184 available_commands: Vec::new(),
1185 meta: None,
1186 }
1187 }
1188
1189 #[must_use]
1191 pub fn config_options(mut self, config_options: Vec<SessionConfigOption>) -> Self {
1192 self.config_options = config_options;
1193 self
1194 }
1195
1196 #[must_use]
1198 pub fn available_commands(mut self, available_commands: Vec<AvailableCommand>) -> Self {
1199 self.available_commands = available_commands;
1200 self
1201 }
1202
1203 #[must_use]
1209 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1210 self.meta = meta.into_option();
1211 self
1212 }
1213}
1214
1215#[serde_as]
1222#[skip_serializing_none]
1223#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1224#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1225#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_RESUME_METHOD_NAME)))]
1226#[serde(rename_all = "camelCase")]
1227#[non_exhaustive]
1228pub struct ResumeSessionRequest {
1229 pub session_id: SessionId,
1231 pub cwd: AbsolutePath,
1233 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1240 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1241 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1242 pub additional_directories: Vec<AbsolutePath>,
1243 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1245 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1246 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1247 pub mcp_servers: Vec<McpServer>,
1248 #[serde_as(deserialize_as = "DefaultOnError")]
1256 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1257 #[serde(default)]
1258 pub replay_from: Option<ReplayFrom>,
1259 #[serde_as(deserialize_as = "DefaultOnError")]
1265 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1266 #[serde(default)]
1267 #[serde(rename = "_meta")]
1268 pub meta: Option<Meta>,
1269}
1270
1271impl ResumeSessionRequest {
1272 #[must_use]
1274 pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<AbsolutePath>) -> Self {
1275 Self {
1276 session_id: session_id.into(),
1277 cwd: cwd.into(),
1278 additional_directories: vec![],
1279 mcp_servers: vec![],
1280 replay_from: None,
1281 meta: None,
1282 }
1283 }
1284
1285 #[must_use]
1287 pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
1288 where
1289 I: IntoIterator<Item = P>,
1290 P: Into<AbsolutePath>,
1291 {
1292 self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
1293 self
1294 }
1295
1296 #[must_use]
1298 pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1299 self.mcp_servers = mcp_servers;
1300 self
1301 }
1302
1303 #[must_use]
1311 pub fn replay_from(mut self, replay_from: impl IntoOption<ReplayFrom>) -> Self {
1312 self.replay_from = replay_from.into_option();
1313 self
1314 }
1315
1316 #[must_use]
1322 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1323 self.meta = meta.into_option();
1324 self
1325 }
1326}
1327
1328#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1332#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1333#[serde(tag = "type", rename_all = "snake_case")]
1334#[non_exhaustive]
1335pub enum ReplayFrom {
1336 Start(ReplayFromStart),
1338 #[serde(untagged)]
1348 Other(OtherReplayFrom),
1349}
1350
1351impl From<ReplayFromStart> for ReplayFrom {
1352 fn from(replay_from: ReplayFromStart) -> Self {
1353 Self::Start(replay_from)
1354 }
1355}
1356
1357#[serde_as]
1359#[skip_serializing_none]
1360#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1361#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1362#[serde(rename_all = "camelCase")]
1363#[non_exhaustive]
1364pub struct ReplayFromStart {
1365 #[serde_as(deserialize_as = "DefaultOnError")]
1371 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1372 #[serde(default)]
1373 #[serde(rename = "_meta")]
1374 pub meta: Option<Meta>,
1375}
1376
1377impl ReplayFromStart {
1378 #[must_use]
1380 pub fn new() -> Self {
1381 Self::default()
1382 }
1383
1384 #[must_use]
1390 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1391 self.meta = meta.into_option();
1392 self
1393 }
1394}
1395
1396#[serde_as]
1398#[skip_serializing_none]
1399#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1400#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
1401#[cfg_attr(feature = "schemars", schemars(inline))]
1402#[cfg_attr(feature = "schemars", schemars(transform = other_replay_from_schema))]
1403#[serde(rename_all = "camelCase")]
1404#[non_exhaustive]
1405pub struct OtherReplayFrom {
1406 #[serde(rename = "type")]
1412 pub type_: String,
1413 #[serde_as(deserialize_as = "DefaultOnError")]
1419 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1420 #[serde(default)]
1421 #[serde(rename = "_meta")]
1422 pub meta: Option<Meta>,
1423 #[serde(flatten)]
1425 pub fields: BTreeMap<String, serde_json::Value>,
1426}
1427
1428impl OtherReplayFrom {
1429 #[must_use]
1431 pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
1432 fields.remove("type");
1433 fields.remove("_meta");
1434 Self {
1435 type_: type_.into(),
1436 meta: None,
1437 fields,
1438 }
1439 }
1440
1441 #[must_use]
1447 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1448 self.meta = meta.into_option();
1449 self
1450 }
1451}
1452
1453impl<'de> Deserialize<'de> for OtherReplayFrom {
1454 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1455 where
1456 D: serde::Deserializer<'de>,
1457 {
1458 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
1459 let type_ = fields
1460 .remove("type")
1461 .ok_or_else(|| serde::de::Error::missing_field("type"))?;
1462 let serde_json::Value::String(type_) = type_ else {
1463 return Err(serde::de::Error::custom("`type` must be a string"));
1464 };
1465
1466 if is_known_replay_from_type(&type_) {
1467 return Err(serde::de::Error::custom(format!(
1468 "known replay cursor `{type_}` did not match its schema"
1469 )));
1470 }
1471
1472 let meta = fields
1473 .remove("_meta")
1474 .and_then(|value| serde_json::from_value(value).ok());
1475
1476 Ok(Self {
1477 type_,
1478 meta,
1479 fields,
1480 })
1481 }
1482}
1483
1484fn is_known_replay_from_type(type_: &str) -> bool {
1485 matches!(type_, "start")
1486}
1487
1488#[cfg(feature = "schemars")]
1489fn other_replay_from_schema(schema: &mut Schema) {
1490 super::schema_util::reject_known_string_discriminators(schema, "type", &["start"]);
1491}
1492
1493crate::serde_util::default_on_null! {
1494 #[serde_as]
1496 #[skip_serializing_none]
1497 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1498 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
1499 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_RESUME_METHOD_NAME)))]
1500 #[serde(rename_all = "camelCase")]
1501 #[non_exhaustive]
1502 pub struct ResumeSessionResponse {
1503 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1505 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1506 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1507 pub config_options: Vec<SessionConfigOption>,
1508 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1514 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1515 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1516 pub available_commands: Vec<AvailableCommand>,
1517 #[serde_as(deserialize_as = "DefaultOnError")]
1523 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1524 #[serde(default)]
1525 #[serde(rename = "_meta")]
1526 pub meta: Option<Meta>,
1527 }
1528}
1529
1530impl ResumeSessionResponse {
1531 #[must_use]
1533 pub fn new() -> Self {
1534 Self::default()
1535 }
1536
1537 #[must_use]
1539 pub fn config_options(mut self, config_options: Vec<SessionConfigOption>) -> Self {
1540 self.config_options = config_options;
1541 self
1542 }
1543
1544 #[must_use]
1546 pub fn available_commands(mut self, available_commands: Vec<AvailableCommand>) -> Self {
1547 self.available_commands = available_commands;
1548 self
1549 }
1550
1551 #[must_use]
1557 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1558 self.meta = meta.into_option();
1559 self
1560 }
1561}
1562
1563#[serde_as]
1571#[skip_serializing_none]
1572#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1573#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1574#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_CLOSE_METHOD_NAME)))]
1575#[serde(rename_all = "camelCase")]
1576#[non_exhaustive]
1577pub struct CloseSessionRequest {
1578 pub session_id: SessionId,
1580 #[serde_as(deserialize_as = "DefaultOnError")]
1586 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1587 #[serde(default)]
1588 #[serde(rename = "_meta")]
1589 pub meta: Option<Meta>,
1590}
1591
1592impl CloseSessionRequest {
1593 #[must_use]
1595 pub fn new(session_id: impl Into<SessionId>) -> Self {
1596 Self {
1597 session_id: session_id.into(),
1598 meta: None,
1599 }
1600 }
1601
1602 #[must_use]
1608 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1609 self.meta = meta.into_option();
1610 self
1611 }
1612}
1613
1614crate::serde_util::default_on_null! {
1615 #[serde_as]
1617 #[skip_serializing_none]
1618 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1619 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
1620 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_CLOSE_METHOD_NAME)))]
1621 #[serde(rename_all = "camelCase")]
1622 #[non_exhaustive]
1623 pub struct CloseSessionResponse {
1624 #[serde_as(deserialize_as = "DefaultOnError")]
1630 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1631 #[serde(default)]
1632 #[serde(rename = "_meta")]
1633 pub meta: Option<Meta>,
1634 }
1635}
1636
1637impl CloseSessionResponse {
1638 #[must_use]
1640 pub fn new() -> Self {
1641 Self::default()
1642 }
1643
1644 #[must_use]
1650 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1651 self.meta = meta.into_option();
1652 self
1653 }
1654}
1655
1656#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1660#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
1661#[serde(transparent)]
1662#[from(Arc<str>, String, &str, &mut str, Box<str>, Cow<'_, str>)]
1663#[non_exhaustive]
1664pub struct SessionListCursor(pub Arc<str>);
1665
1666impl SessionListCursor {
1667 #[must_use]
1669 pub fn new(cursor: impl Into<Self>) -> Self {
1670 cursor.into()
1671 }
1672}
1673
1674impl AsRef<str> for SessionListCursor {
1675 fn as_ref(&self) -> &str {
1676 &self.0
1677 }
1678}
1679
1680impl From<&String> for SessionListCursor {
1681 fn from(cursor: &String) -> Self {
1682 Self(cursor.as_str().into())
1683 }
1684}
1685
1686macro_rules! impl_session_list_cursor_option_conversion {
1687 ($source:ty) => {
1688 impl IntoOption<SessionListCursor> for $source {
1689 fn into_option(self) -> Option<SessionListCursor> {
1690 Some(self.into())
1691 }
1692 }
1693 };
1694}
1695
1696impl_session_list_cursor_option_conversion!(Arc<str>);
1697impl_session_list_cursor_option_conversion!(String);
1698impl_session_list_cursor_option_conversion!(&str);
1699impl_session_list_cursor_option_conversion!(&mut str);
1700impl_session_list_cursor_option_conversion!(&String);
1701impl_session_list_cursor_option_conversion!(Box<str>);
1702impl_session_list_cursor_option_conversion!(Cow<'_, str>);
1703
1704crate::serde_util::default_on_null! {
1705 #[serde_as]
1707 #[skip_serializing_none]
1708 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1709 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
1710 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_LIST_METHOD_NAME)))]
1711 #[serde(rename_all = "camelCase")]
1712 #[non_exhaustive]
1713 pub struct ListSessionsRequest {
1714 #[serde(default)]
1716 pub cwd: Option<AbsolutePath>,
1717 #[serde(default)]
1719 pub cursor: Option<SessionListCursor>,
1720 #[serde_as(deserialize_as = "DefaultOnError")]
1726 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1727 #[serde(default)]
1728 #[serde(rename = "_meta")]
1729 pub meta: Option<Meta>,
1730 }
1731}
1732
1733impl ListSessionsRequest {
1734 #[must_use]
1736 pub fn new() -> Self {
1737 Self::default()
1738 }
1739
1740 #[must_use]
1742 pub fn cwd(mut self, cwd: impl IntoOption<AbsolutePath>) -> Self {
1743 self.cwd = cwd.into_option();
1744 self
1745 }
1746
1747 #[must_use]
1749 pub fn cursor(mut self, cursor: impl IntoOption<SessionListCursor>) -> Self {
1750 self.cursor = cursor.into_option();
1751 self
1752 }
1753
1754 #[must_use]
1760 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1761 self.meta = meta.into_option();
1762 self
1763 }
1764}
1765
1766#[serde_as]
1768#[skip_serializing_none]
1769#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1770#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1771#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_LIST_METHOD_NAME)))]
1772#[serde(rename_all = "camelCase")]
1773#[non_exhaustive]
1774pub struct ListSessionsResponse {
1775 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1777 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1778 pub sessions: Vec<SessionInfo>,
1779 #[serde_as(deserialize_as = "DefaultOnError")]
1782 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1783 #[serde(default)]
1784 pub next_cursor: Option<SessionListCursor>,
1785 #[serde_as(deserialize_as = "DefaultOnError")]
1791 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1792 #[serde(default)]
1793 #[serde(rename = "_meta")]
1794 pub meta: Option<Meta>,
1795}
1796
1797impl ListSessionsResponse {
1798 #[must_use]
1800 pub fn new(sessions: Vec<SessionInfo>) -> Self {
1801 Self {
1802 sessions,
1803 next_cursor: None,
1804 meta: None,
1805 }
1806 }
1807
1808 #[must_use]
1810 pub fn next_cursor(mut self, next_cursor: impl IntoOption<SessionListCursor>) -> Self {
1811 self.next_cursor = next_cursor.into_option();
1812 self
1813 }
1814
1815 #[must_use]
1821 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1822 self.meta = meta.into_option();
1823 self
1824 }
1825}
1826
1827#[serde_as]
1833#[skip_serializing_none]
1834#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1835#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1836#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_DELETE_METHOD_NAME)))]
1837#[serde(rename_all = "camelCase")]
1838#[non_exhaustive]
1839pub struct DeleteSessionRequest {
1840 pub session_id: SessionId,
1842 #[serde_as(deserialize_as = "DefaultOnError")]
1848 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1849 #[serde(default)]
1850 #[serde(rename = "_meta")]
1851 pub meta: Option<Meta>,
1852}
1853
1854impl DeleteSessionRequest {
1855 #[must_use]
1857 pub fn new(session_id: impl Into<SessionId>) -> Self {
1858 Self {
1859 session_id: session_id.into(),
1860 meta: None,
1861 }
1862 }
1863
1864 #[must_use]
1870 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1871 self.meta = meta.into_option();
1872 self
1873 }
1874}
1875
1876crate::serde_util::default_on_null! {
1877 #[serde_as]
1879 #[skip_serializing_none]
1880 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1881 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
1882 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_DELETE_METHOD_NAME)))]
1883 #[serde(rename_all = "camelCase")]
1884 #[non_exhaustive]
1885 pub struct DeleteSessionResponse {
1886 #[serde_as(deserialize_as = "DefaultOnError")]
1892 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1893 #[serde(default)]
1894 #[serde(rename = "_meta")]
1895 pub meta: Option<Meta>,
1896 }
1897}
1898
1899impl DeleteSessionResponse {
1900 #[must_use]
1902 pub fn new() -> Self {
1903 Self::default()
1904 }
1905
1906 #[must_use]
1912 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1913 self.meta = meta.into_option();
1914 self
1915 }
1916}
1917
1918#[serde_as]
1920#[skip_serializing_none]
1921#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1922#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1923#[serde(rename_all = "camelCase")]
1924#[non_exhaustive]
1925pub struct SessionInfo {
1926 pub session_id: SessionId,
1928 pub cwd: AbsolutePath,
1930 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1936 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1937 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1938 pub additional_directories: Vec<AbsolutePath>,
1939
1940 #[serde_as(deserialize_as = "DefaultOnError")]
1942 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1943 #[serde(default)]
1944 pub title: Option<String>,
1945 #[serde_as(deserialize_as = "DefaultOnError")]
1947 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "format" = "date-time")))]
1948 #[serde(default)]
1949 pub updated_at: Option<String>,
1950 #[serde_as(deserialize_as = "DefaultOnError")]
1956 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1957 #[serde(default)]
1958 #[serde(rename = "_meta")]
1959 pub meta: Option<Meta>,
1960}
1961
1962impl SessionInfo {
1963 #[must_use]
1965 pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<AbsolutePath>) -> Self {
1966 Self {
1967 session_id: session_id.into(),
1968 cwd: cwd.into(),
1969 additional_directories: vec![],
1970 title: None,
1971 updated_at: None,
1972 meta: None,
1973 }
1974 }
1975
1976 #[must_use]
1978 pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
1979 where
1980 I: IntoIterator<Item = P>,
1981 P: Into<AbsolutePath>,
1982 {
1983 self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
1984 self
1985 }
1986
1987 #[must_use]
1989 pub fn title(mut self, title: impl IntoOption<String>) -> Self {
1990 self.title = title.into_option();
1991 self
1992 }
1993
1994 #[must_use]
1996 pub fn updated_at(mut self, updated_at: impl IntoOption<String>) -> Self {
1997 self.updated_at = updated_at.into_option();
1998 self
1999 }
2000
2001 #[must_use]
2007 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2008 self.meta = meta.into_option();
2009 self
2010 }
2011}
2012
2013#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2017#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, From, Display)]
2018#[serde(transparent)]
2019#[from(forward)]
2020#[non_exhaustive]
2021pub struct SessionConfigId(pub Arc<str>);
2022
2023impl SessionConfigId {
2024 #[must_use]
2026 pub fn new(id: impl Into<Self>) -> Self {
2027 id.into()
2028 }
2029}
2030
2031#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2033#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, From, Display)]
2034#[serde(transparent)]
2035#[from(forward)]
2036#[non_exhaustive]
2037pub struct SessionConfigValueId(pub Arc<str>);
2038
2039impl SessionConfigValueId {
2040 #[must_use]
2042 pub fn new(id: impl Into<Self>) -> Self {
2043 id.into()
2044 }
2045}
2046
2047#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2049#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, From, Display)]
2050#[serde(transparent)]
2051#[from(forward)]
2052#[non_exhaustive]
2053pub struct SessionConfigGroupId(pub Arc<str>);
2054
2055impl SessionConfigGroupId {
2056 #[must_use]
2058 pub fn new(id: impl Into<Self>) -> Self {
2059 id.into()
2060 }
2061}
2062
2063#[serde_as]
2065#[skip_serializing_none]
2066#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2067#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2068#[serde(rename_all = "camelCase")]
2069#[non_exhaustive]
2070pub struct SessionConfigSelectOption {
2071 pub value: SessionConfigValueId,
2073 pub name: String,
2075 #[serde_as(deserialize_as = "DefaultOnError")]
2077 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2078 #[serde(default)]
2079 pub description: Option<String>,
2080 #[serde_as(deserialize_as = "DefaultOnError")]
2086 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2087 #[serde(default)]
2088 #[serde(rename = "_meta")]
2089 pub meta: Option<Meta>,
2090}
2091
2092impl SessionConfigSelectOption {
2093 #[must_use]
2095 pub fn new(value: impl Into<SessionConfigValueId>, name: impl Into<String>) -> Self {
2096 Self {
2097 value: value.into(),
2098 name: name.into(),
2099 description: None,
2100 meta: None,
2101 }
2102 }
2103
2104 #[must_use]
2106 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
2107 self.description = description.into_option();
2108 self
2109 }
2110
2111 #[must_use]
2117 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2118 self.meta = meta.into_option();
2119 self
2120 }
2121}
2122
2123#[serde_as]
2125#[skip_serializing_none]
2126#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2127#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2128#[serde(rename_all = "camelCase")]
2129#[non_exhaustive]
2130pub struct SessionConfigSelectGroup {
2131 pub group_id: SessionConfigGroupId,
2133 pub name: String,
2135 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
2137 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
2138 pub options: Vec<SessionConfigSelectOption>,
2139 #[serde_as(deserialize_as = "DefaultOnError")]
2145 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2146 #[serde(default)]
2147 #[serde(rename = "_meta")]
2148 pub meta: Option<Meta>,
2149}
2150
2151impl SessionConfigSelectGroup {
2152 #[must_use]
2154 pub fn new(
2155 group_id: impl Into<SessionConfigGroupId>,
2156 name: impl Into<String>,
2157 options: Vec<SessionConfigSelectOption>,
2158 ) -> Self {
2159 Self {
2160 group_id: group_id.into(),
2161 name: name.into(),
2162 options,
2163 meta: None,
2164 }
2165 }
2166
2167 #[must_use]
2173 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2174 self.meta = meta.into_option();
2175 self
2176 }
2177}
2178
2179#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2181#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2182#[serde(untagged)]
2183#[non_exhaustive]
2184pub enum SessionConfigSelectOptions {
2185 Ungrouped(Vec<SessionConfigSelectOption>),
2187 Grouped(Vec<SessionConfigSelectGroup>),
2189}
2190
2191impl From<Vec<SessionConfigSelectOption>> for SessionConfigSelectOptions {
2192 fn from(options: Vec<SessionConfigSelectOption>) -> Self {
2193 SessionConfigSelectOptions::Ungrouped(options)
2194 }
2195}
2196
2197impl From<Vec<SessionConfigSelectGroup>> for SessionConfigSelectOptions {
2198 fn from(groups: Vec<SessionConfigSelectGroup>) -> Self {
2199 SessionConfigSelectOptions::Grouped(groups)
2200 }
2201}
2202
2203#[skip_serializing_none]
2205#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2206#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2207#[serde(rename_all = "camelCase")]
2208#[non_exhaustive]
2209pub struct SessionConfigSelect {
2210 pub current_value: SessionConfigValueId,
2212 pub options: SessionConfigSelectOptions,
2214}
2215
2216impl SessionConfigSelect {
2217 #[must_use]
2219 pub fn new(
2220 current_value: impl Into<SessionConfigValueId>,
2221 options: impl Into<SessionConfigSelectOptions>,
2222 ) -> Self {
2223 Self {
2224 current_value: current_value.into(),
2225 options: options.into(),
2226 }
2227 }
2228}
2229
2230#[skip_serializing_none]
2232#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2233#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2234#[serde(rename_all = "camelCase")]
2235#[non_exhaustive]
2236pub struct SessionConfigBoolean {
2237 pub current_value: bool,
2239}
2240
2241impl SessionConfigBoolean {
2242 #[must_use]
2244 pub fn new(current_value: bool) -> Self {
2245 Self { current_value }
2246 }
2247}
2248
2249#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2259#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2260#[serde(rename_all = "snake_case")]
2261#[non_exhaustive]
2262pub enum SessionConfigOptionCategory {
2263 Mode,
2265 Model,
2267 ModelConfig,
2269 ThoughtLevel,
2271 #[serde(untagged)]
2277 Other(String),
2278}
2279
2280#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2282#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2283#[serde(tag = "type", rename_all = "snake_case")]
2284#[non_exhaustive]
2285pub enum SessionConfigKind {
2286 Select(SessionConfigSelect),
2288 Boolean(SessionConfigBoolean),
2290 #[serde(untagged)]
2300 Other(OtherSessionConfigKind),
2301}
2302
2303#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2305#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
2306#[cfg_attr(feature = "schemars", schemars(inline))]
2307#[cfg_attr(feature = "schemars", schemars(transform = other_session_config_kind_schema))]
2308#[serde(rename_all = "camelCase")]
2309#[non_exhaustive]
2310pub struct OtherSessionConfigKind {
2311 #[serde(rename = "type")]
2317 pub type_: String,
2318 #[serde(flatten)]
2320 pub fields: BTreeMap<String, serde_json::Value>,
2321}
2322
2323impl OtherSessionConfigKind {
2324 #[must_use]
2326 pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
2327 fields.remove("type");
2328 fields.remove("_meta");
2329 Self {
2330 type_: type_.into(),
2331 fields,
2332 }
2333 }
2334}
2335
2336impl<'de> Deserialize<'de> for OtherSessionConfigKind {
2337 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2338 where
2339 D: serde::Deserializer<'de>,
2340 {
2341 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
2342 let type_ = fields
2343 .remove("type")
2344 .ok_or_else(|| serde::de::Error::missing_field("type"))?;
2345 let serde_json::Value::String(type_) = type_ else {
2346 return Err(serde::de::Error::custom("`type` must be a string"));
2347 };
2348
2349 if is_known_session_config_kind_type(&type_) {
2350 return Err(serde::de::Error::custom(format!(
2351 "known session configuration option `{type_}` did not match its schema"
2352 )));
2353 }
2354
2355 Ok(Self { type_, fields })
2356 }
2357}
2358
2359fn is_known_session_config_kind_type(type_: &str) -> bool {
2360 matches!(type_, "select" | "boolean")
2361}
2362
2363#[cfg(feature = "schemars")]
2364fn other_session_config_kind_schema(schema: &mut Schema) {
2365 super::schema_util::reject_known_string_discriminators(schema, "type", &["select", "boolean"]);
2366}
2367
2368#[serde_as]
2370#[skip_serializing_none]
2371#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2372#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2373#[serde(rename_all = "camelCase")]
2374#[non_exhaustive]
2375pub struct SessionConfigOption {
2376 pub config_id: SessionConfigId,
2378 pub name: String,
2380 #[serde_as(deserialize_as = "DefaultOnError")]
2382 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2383 #[serde(default)]
2384 pub description: Option<String>,
2385 #[serde_as(deserialize_as = "DefaultOnError")]
2387 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2388 #[serde(default)]
2389 pub category: Option<SessionConfigOptionCategory>,
2390 #[serde(flatten)]
2392 pub kind: SessionConfigKind,
2393 #[serde_as(deserialize_as = "DefaultOnError")]
2399 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2400 #[serde(default)]
2401 #[serde(rename = "_meta")]
2402 pub meta: Option<Meta>,
2403}
2404
2405impl SessionConfigOption {
2406 #[must_use]
2408 pub fn new(
2409 config_id: impl Into<SessionConfigId>,
2410 name: impl Into<String>,
2411 kind: SessionConfigKind,
2412 ) -> Self {
2413 Self {
2414 config_id: config_id.into(),
2415 name: name.into(),
2416 description: None,
2417 category: None,
2418 kind,
2419 meta: None,
2420 }
2421 }
2422
2423 #[must_use]
2425 pub fn select(
2426 config_id: impl Into<SessionConfigId>,
2427 name: impl Into<String>,
2428 current_value: impl Into<SessionConfigValueId>,
2429 options: impl Into<SessionConfigSelectOptions>,
2430 ) -> Self {
2431 Self::new(
2432 config_id,
2433 name,
2434 SessionConfigKind::Select(SessionConfigSelect::new(current_value, options)),
2435 )
2436 }
2437
2438 #[must_use]
2440 pub fn boolean(
2441 config_id: impl Into<SessionConfigId>,
2442 name: impl Into<String>,
2443 current_value: bool,
2444 ) -> Self {
2445 Self::new(
2446 config_id,
2447 name,
2448 SessionConfigKind::Boolean(SessionConfigBoolean::new(current_value)),
2449 )
2450 }
2451
2452 #[must_use]
2454 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
2455 self.description = description.into_option();
2456 self
2457 }
2458
2459 #[must_use]
2461 pub fn category(mut self, category: impl IntoOption<SessionConfigOptionCategory>) -> Self {
2462 self.category = category.into_option();
2463 self
2464 }
2465
2466 #[must_use]
2472 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2473 self.meta = meta.into_option();
2474 self
2475 }
2476}
2477
2478#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2487#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
2488#[serde(tag = "type", rename_all = "snake_case")]
2489#[non_exhaustive]
2490pub enum SessionConfigOptionValue {
2491 Id {
2493 value: SessionConfigValueId,
2495 },
2496 Boolean {
2498 value: bool,
2500 },
2501 #[serde(untagged)]
2507 Other(OtherSessionConfigOptionValue),
2508}
2509
2510#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2512#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
2513#[cfg_attr(feature = "schemars", schemars(inline))]
2514#[cfg_attr(feature = "schemars", schemars(transform = other_session_config_option_value_schema))]
2515#[serde(rename_all = "camelCase")]
2516#[non_exhaustive]
2517pub struct OtherSessionConfigOptionValue {
2518 #[serde(rename = "type")]
2524 pub type_: String,
2525 pub value: serde_json::Value,
2527 #[serde(flatten)]
2529 pub fields: BTreeMap<String, serde_json::Value>,
2530}
2531
2532impl OtherSessionConfigOptionValue {
2533 #[must_use]
2535 pub fn new(
2536 type_: impl Into<String>,
2537 value: serde_json::Value,
2538 mut fields: BTreeMap<String, serde_json::Value>,
2539 ) -> Self {
2540 fields.remove("type");
2541 fields.remove("value");
2542 fields.remove("_meta");
2543 Self {
2544 type_: type_.into(),
2545 value,
2546 fields,
2547 }
2548 }
2549}
2550
2551impl<'de> Deserialize<'de> for OtherSessionConfigOptionValue {
2552 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2553 where
2554 D: serde::Deserializer<'de>,
2555 {
2556 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
2557 let type_ = fields
2558 .remove("type")
2559 .ok_or_else(|| serde::de::Error::missing_field("type"))?;
2560 let serde_json::Value::String(type_) = type_ else {
2561 return Err(serde::de::Error::custom("`type` must be a string"));
2562 };
2563
2564 if is_known_session_config_option_value_type(&type_) {
2565 return Err(serde::de::Error::custom(format!(
2566 "known session configuration option value `{type_}` did not match its schema"
2567 )));
2568 }
2569
2570 let value = fields
2571 .remove("value")
2572 .ok_or_else(|| serde::de::Error::missing_field("value"))?;
2573
2574 Ok(Self {
2575 type_,
2576 value,
2577 fields,
2578 })
2579 }
2580}
2581
2582impl<'de> Deserialize<'de> for SessionConfigOptionValue {
2583 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2584 where
2585 D: serde::Deserializer<'de>,
2586 {
2587 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
2588 let type_ = fields.remove("type");
2589 let value = fields
2590 .remove("value")
2591 .ok_or_else(|| serde::de::Error::missing_field("value"))?;
2592
2593 let type_ = type_.ok_or_else(|| serde::de::Error::missing_field("type"))?;
2594
2595 let serde_json::Value::String(type_) = type_ else {
2596 return Err(serde::de::Error::custom("`type` must be a string"));
2597 };
2598
2599 match type_.as_str() {
2600 "id" => {
2601 let value = serde_json::from_value(value).map_err(|error| {
2602 serde::de::Error::custom(format!(
2603 "`value` must be a string for `type: id`: {error}"
2604 ))
2605 })?;
2606 Ok(Self::Id { value })
2607 }
2608 "boolean" => {
2609 let value = serde_json::from_value(value).map_err(|error| {
2610 serde::de::Error::custom(format!(
2611 "`value` must be a boolean for `type: boolean`: {error}"
2612 ))
2613 })?;
2614 Ok(Self::Boolean { value })
2615 }
2616 _ => Ok(Self::Other(OtherSessionConfigOptionValue {
2617 type_,
2618 value,
2619 fields,
2620 })),
2621 }
2622 }
2623}
2624
2625fn is_known_session_config_option_value_type(type_: &str) -> bool {
2626 matches!(type_, "id" | "boolean")
2627}
2628
2629#[cfg(feature = "schemars")]
2630fn other_session_config_option_value_schema(schema: &mut Schema) {
2631 super::schema_util::reject_known_string_discriminators(schema, "type", &["id", "boolean"]);
2632}
2633
2634impl SessionConfigOptionValue {
2635 #[must_use]
2637 pub fn id(id: impl Into<SessionConfigValueId>) -> Self {
2638 Self::Id { value: id.into() }
2639 }
2640
2641 #[must_use]
2643 pub fn boolean(val: bool) -> Self {
2644 Self::Boolean { value: val }
2645 }
2646
2647 #[must_use]
2650 pub fn as_id(&self) -> Option<&SessionConfigValueId> {
2651 match self {
2652 Self::Id { value } => Some(value),
2653 _ => None,
2654 }
2655 }
2656
2657 #[must_use]
2659 pub fn as_bool(&self) -> Option<bool> {
2660 match self {
2661 Self::Boolean { value } => Some(*value),
2662 _ => None,
2663 }
2664 }
2665}
2666
2667impl From<SessionConfigValueId> for SessionConfigOptionValue {
2668 fn from(value: SessionConfigValueId) -> Self {
2669 Self::Id { value }
2670 }
2671}
2672
2673impl From<bool> for SessionConfigOptionValue {
2674 fn from(value: bool) -> Self {
2675 Self::Boolean { value }
2676 }
2677}
2678
2679impl From<&str> for SessionConfigOptionValue {
2680 fn from(value: &str) -> Self {
2681 Self::Id {
2682 value: SessionConfigValueId::new(value),
2683 }
2684 }
2685}
2686
2687#[serde_as]
2689#[skip_serializing_none]
2690#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2691#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2692#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_CONFIG_OPTION_METHOD_NAME)))]
2693#[serde(rename_all = "camelCase")]
2694#[non_exhaustive]
2695pub struct SetSessionConfigOptionRequest {
2696 pub session_id: SessionId,
2698 pub config_id: SessionConfigId,
2700 #[serde(flatten)]
2704 pub value: SessionConfigOptionValue,
2705 #[serde_as(deserialize_as = "DefaultOnError")]
2711 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2712 #[serde(default)]
2713 #[serde(rename = "_meta")]
2714 pub meta: Option<Meta>,
2715}
2716
2717impl SetSessionConfigOptionRequest {
2718 #[must_use]
2720 pub fn new(
2721 session_id: impl Into<SessionId>,
2722 config_id: impl Into<SessionConfigId>,
2723 value: impl Into<SessionConfigOptionValue>,
2724 ) -> Self {
2725 Self {
2726 session_id: session_id.into(),
2727 config_id: config_id.into(),
2728 value: value.into(),
2729 meta: None,
2730 }
2731 }
2732
2733 #[must_use]
2739 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2740 self.meta = meta.into_option();
2741 self
2742 }
2743}
2744
2745#[serde_as]
2747#[skip_serializing_none]
2748#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2749#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2750#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_CONFIG_OPTION_METHOD_NAME)))]
2751#[serde(rename_all = "camelCase")]
2752#[non_exhaustive]
2753pub struct SetSessionConfigOptionResponse {
2754 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
2756 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
2757 pub config_options: Vec<SessionConfigOption>,
2758 #[serde_as(deserialize_as = "DefaultOnError")]
2764 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2765 #[serde(default)]
2766 #[serde(rename = "_meta")]
2767 pub meta: Option<Meta>,
2768}
2769
2770impl SetSessionConfigOptionResponse {
2771 #[must_use]
2773 pub fn new(config_options: Vec<SessionConfigOption>) -> Self {
2774 Self {
2775 config_options,
2776 meta: None,
2777 }
2778 }
2779
2780 #[must_use]
2786 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2787 self.meta = meta.into_option();
2788 self
2789 }
2790}
2791
2792#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2801#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2802#[serde(tag = "type", rename_all = "snake_case")]
2803#[non_exhaustive]
2804pub enum McpServer {
2805 Http(McpServerHttp),
2809 #[cfg(feature = "unstable_mcp_over_acp")]
2818 Acp(McpServerAcp),
2819 Stdio(McpServerStdio),
2823 #[serde(untagged)]
2833 Other(OtherMcpServer),
2834}
2835
2836#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2838#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
2839#[cfg_attr(feature = "schemars", schemars(inline))]
2840#[cfg_attr(feature = "schemars", schemars(transform = other_mcp_server_schema))]
2841#[serde(rename_all = "camelCase")]
2842#[non_exhaustive]
2843pub struct OtherMcpServer {
2844 #[serde(rename = "type")]
2850 pub type_: String,
2851 #[serde(flatten)]
2853 pub fields: BTreeMap<String, serde_json::Value>,
2854}
2855
2856impl OtherMcpServer {
2857 #[must_use]
2859 pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
2860 fields.remove("type");
2861 Self {
2862 type_: type_.into(),
2863 fields,
2864 }
2865 }
2866}
2867
2868impl<'de> Deserialize<'de> for OtherMcpServer {
2869 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2870 where
2871 D: serde::Deserializer<'de>,
2872 {
2873 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
2874 let type_ = fields
2875 .remove("type")
2876 .ok_or_else(|| serde::de::Error::missing_field("type"))?;
2877 let serde_json::Value::String(type_) = type_ else {
2878 return Err(serde::de::Error::custom("`type` must be a string"));
2879 };
2880
2881 if is_known_mcp_server_type(&type_) {
2882 return Err(serde::de::Error::custom(format!(
2883 "known MCP server transport `{type_}` did not match its schema"
2884 )));
2885 }
2886
2887 Ok(Self { type_, fields })
2888 }
2889}
2890
2891fn is_known_mcp_server_type(type_: &str) -> bool {
2892 match type_ {
2893 "http" | "stdio" => true,
2894 #[cfg(feature = "unstable_mcp_over_acp")]
2895 "acp" => true,
2896 _ => false,
2897 }
2898}
2899
2900#[cfg(feature = "schemars")]
2901fn other_mcp_server_schema(schema: &mut Schema) {
2902 super::schema_util::reject_known_string_discriminators(
2903 schema,
2904 "type",
2905 &[
2906 "http",
2907 "stdio",
2908 #[cfg(feature = "unstable_mcp_over_acp")]
2909 "acp",
2910 ],
2911 );
2912}
2913
2914#[serde_as]
2916#[skip_serializing_none]
2917#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2918#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2919#[serde(rename_all = "camelCase")]
2920#[non_exhaustive]
2921pub struct McpServerHttp {
2922 pub name: String,
2924 #[cfg_attr(feature = "schemars", schemars(url))]
2926 pub url: String,
2927 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2929 pub headers: Vec<HttpHeader>,
2930 #[serde_as(deserialize_as = "DefaultOnError")]
2936 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2937 #[serde(default)]
2938 #[serde(rename = "_meta")]
2939 pub meta: Option<Meta>,
2940}
2941
2942impl McpServerHttp {
2943 #[must_use]
2945 pub fn new(name: impl Into<String>, url: impl Into<String>) -> Self {
2946 Self {
2947 name: name.into(),
2948 url: url.into(),
2949 headers: Vec::new(),
2950 meta: None,
2951 }
2952 }
2953
2954 #[must_use]
2956 pub fn headers(mut self, headers: Vec<HttpHeader>) -> Self {
2957 self.headers = headers;
2958 self
2959 }
2960
2961 #[must_use]
2967 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2968 self.meta = meta.into_option();
2969 self
2970 }
2971}
2972
2973#[cfg(feature = "unstable_mcp_over_acp")]
2982#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2983#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
2984#[serde(transparent)]
2985#[from(forward)]
2986#[non_exhaustive]
2987pub struct McpServerAcpId(pub Arc<str>);
2988
2989#[cfg(feature = "unstable_mcp_over_acp")]
2990impl McpServerAcpId {
2991 #[must_use]
2993 pub fn new(id: impl Into<Self>) -> Self {
2994 id.into()
2995 }
2996}
2997
2998#[serde_as]
3007#[skip_serializing_none]
3008#[cfg(feature = "unstable_mcp_over_acp")]
3009#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3010#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3011#[serde(rename_all = "camelCase")]
3012#[non_exhaustive]
3013pub struct McpServerAcp {
3014 pub name: String,
3016 pub server_id: McpServerAcpId,
3021 #[serde_as(deserialize_as = "DefaultOnError")]
3027 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3028 #[serde(default)]
3029 #[serde(rename = "_meta")]
3030 pub meta: Option<Meta>,
3031}
3032
3033#[cfg(feature = "unstable_mcp_over_acp")]
3034impl McpServerAcp {
3035 #[must_use]
3037 pub fn new(name: impl Into<String>, server_id: impl Into<McpServerAcpId>) -> Self {
3038 Self {
3039 name: name.into(),
3040 server_id: server_id.into(),
3041 meta: None,
3042 }
3043 }
3044
3045 #[must_use]
3051 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3052 self.meta = meta.into_option();
3053 self
3054 }
3055}
3056
3057#[serde_as]
3059#[skip_serializing_none]
3060#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3061#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3062#[serde(rename_all = "camelCase")]
3063#[non_exhaustive]
3064pub struct McpServerStdio {
3065 pub name: String,
3067 pub command: AbsolutePath,
3069 #[serde(default, skip_serializing_if = "Vec::is_empty")]
3071 pub args: Vec<String>,
3072 #[serde(default, skip_serializing_if = "Vec::is_empty")]
3074 pub env: Vec<EnvVariable>,
3075 #[serde_as(deserialize_as = "DefaultOnError")]
3081 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3082 #[serde(default)]
3083 #[serde(rename = "_meta")]
3084 pub meta: Option<Meta>,
3085}
3086
3087impl McpServerStdio {
3088 #[must_use]
3090 pub fn new(name: impl Into<String>, command: impl Into<AbsolutePath>) -> Self {
3091 Self {
3092 name: name.into(),
3093 command: command.into(),
3094 args: Vec::new(),
3095 env: Vec::new(),
3096 meta: None,
3097 }
3098 }
3099
3100 #[must_use]
3102 pub fn args(mut self, args: Vec<String>) -> Self {
3103 self.args = args;
3104 self
3105 }
3106
3107 #[must_use]
3109 pub fn env(mut self, env: Vec<EnvVariable>) -> Self {
3110 self.env = env;
3111 self
3112 }
3113
3114 #[must_use]
3120 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3121 self.meta = meta.into_option();
3122 self
3123 }
3124}
3125
3126#[serde_as]
3128#[skip_serializing_none]
3129#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3130#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3131#[serde(rename_all = "camelCase")]
3132#[non_exhaustive]
3133pub struct EnvVariable {
3134 pub name: String,
3136 pub value: String,
3138 #[serde_as(deserialize_as = "DefaultOnError")]
3144 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3145 #[serde(default)]
3146 #[serde(rename = "_meta")]
3147 pub meta: Option<Meta>,
3148}
3149
3150impl EnvVariable {
3151 #[must_use]
3153 pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
3154 Self {
3155 name: name.into(),
3156 value: value.into(),
3157 meta: None,
3158 }
3159 }
3160
3161 #[must_use]
3167 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3168 self.meta = meta.into_option();
3169 self
3170 }
3171}
3172
3173#[serde_as]
3175#[skip_serializing_none]
3176#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3177#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3178#[serde(rename_all = "camelCase")]
3179#[non_exhaustive]
3180pub struct HttpHeader {
3181 pub name: String,
3183 pub value: String,
3185 #[serde_as(deserialize_as = "DefaultOnError")]
3191 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3192 #[serde(default)]
3193 #[serde(rename = "_meta")]
3194 pub meta: Option<Meta>,
3195}
3196
3197impl HttpHeader {
3198 #[must_use]
3200 pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
3201 Self {
3202 name: name.into(),
3203 value: value.into(),
3204 meta: None,
3205 }
3206 }
3207
3208 #[must_use]
3214 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3215 self.meta = meta.into_option();
3216 self
3217 }
3218}
3219
3220#[serde_as]
3228#[skip_serializing_none]
3229#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3230#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
3231#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_PROMPT_METHOD_NAME)))]
3232#[serde(rename_all = "camelCase")]
3233#[non_exhaustive]
3234pub struct PromptRequest {
3235 pub session_id: SessionId,
3237 pub prompt: Vec<ContentBlock>,
3251 #[serde_as(deserialize_as = "DefaultOnError")]
3257 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3258 #[serde(default)]
3259 #[serde(rename = "_meta")]
3260 pub meta: Option<Meta>,
3261}
3262
3263impl PromptRequest {
3264 #[must_use]
3266 pub fn new(session_id: impl Into<SessionId>, prompt: Vec<ContentBlock>) -> Self {
3267 Self {
3268 session_id: session_id.into(),
3269 prompt,
3270 meta: None,
3271 }
3272 }
3273
3274 #[must_use]
3280 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3281 self.meta = meta.into_option();
3282 self
3283 }
3284}
3285
3286#[serde_as]
3294#[skip_serializing_none]
3295#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3296#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3297#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_PROMPT_METHOD_NAME)))]
3298#[serde(rename_all = "camelCase")]
3299#[non_exhaustive]
3300pub struct PromptResponse {
3301 pub message_id: MessageId,
3309 #[serde_as(deserialize_as = "DefaultOnError")]
3315 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3316 #[serde(default)]
3317 #[serde(rename = "_meta")]
3318 pub meta: Option<Meta>,
3319}
3320
3321impl PromptResponse {
3322 #[must_use]
3324 pub fn new(message_id: impl Into<MessageId>) -> Self {
3325 Self {
3326 message_id: message_id.into(),
3327 meta: None,
3328 }
3329 }
3330
3331 #[must_use]
3337 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3338 self.meta = meta.into_option();
3339 self
3340 }
3341}
3342
3343#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3347#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
3348#[serde(rename_all = "snake_case")]
3349#[non_exhaustive]
3350pub enum StopReason {
3351 EndTurn,
3353 MaxTokens,
3355 MaxTurnRequests,
3358 Refusal,
3362 Cancelled,
3368 #[serde(untagged)]
3374 Other(String),
3375}
3376
3377#[cfg(feature = "unstable_end_turn_token_usage")]
3383#[serde_as]
3384#[skip_serializing_none]
3385#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3386#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3387#[serde(rename_all = "camelCase")]
3388#[non_exhaustive]
3389pub struct Usage {
3390 pub total_tokens: u64,
3392 pub input_tokens: u64,
3394 pub output_tokens: u64,
3396 #[serde_as(deserialize_as = "DefaultOnError")]
3398 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3399 #[serde(default)]
3400 pub thought_tokens: Option<u64>,
3401 #[serde_as(deserialize_as = "DefaultOnError")]
3403 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3404 #[serde(default)]
3405 pub cached_read_tokens: Option<u64>,
3406 #[serde_as(deserialize_as = "DefaultOnError")]
3408 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3409 #[serde(default)]
3410 pub cached_write_tokens: Option<u64>,
3411 #[serde_as(deserialize_as = "DefaultOnError")]
3417 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3418 #[serde(default)]
3419 #[serde(rename = "_meta")]
3420 pub meta: Option<Meta>,
3421}
3422
3423#[cfg(feature = "unstable_end_turn_token_usage")]
3424impl Usage {
3425 #[must_use]
3427 pub fn new(total_tokens: u64, input_tokens: u64, output_tokens: u64) -> Self {
3428 Self {
3429 total_tokens,
3430 input_tokens,
3431 output_tokens,
3432 thought_tokens: None,
3433 cached_read_tokens: None,
3434 cached_write_tokens: None,
3435 meta: None,
3436 }
3437 }
3438
3439 #[must_use]
3441 pub fn thought_tokens(mut self, thought_tokens: impl IntoOption<u64>) -> Self {
3442 self.thought_tokens = thought_tokens.into_option();
3443 self
3444 }
3445
3446 #[must_use]
3448 pub fn cached_read_tokens(mut self, cached_read_tokens: impl IntoOption<u64>) -> Self {
3449 self.cached_read_tokens = cached_read_tokens.into_option();
3450 self
3451 }
3452
3453 #[must_use]
3455 pub fn cached_write_tokens(mut self, cached_write_tokens: impl IntoOption<u64>) -> Self {
3456 self.cached_write_tokens = cached_write_tokens.into_option();
3457 self
3458 }
3459
3460 #[must_use]
3466 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3467 self.meta = meta.into_option();
3468 self
3469 }
3470}
3471
3472#[cfg(feature = "unstable_llm_providers")]
3485#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3486#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3487#[serde(rename_all = "snake_case")]
3488#[non_exhaustive]
3489#[expect(clippy::doc_markdown)]
3490pub enum LlmProtocol {
3491 Anthropic,
3493 #[serde(rename = "openai")]
3495 OpenAi,
3496 Azure,
3498 Vertex,
3500 Bedrock,
3502 #[serde(untagged)]
3508 Other(String),
3509}
3510
3511#[cfg(feature = "unstable_llm_providers")]
3517#[serde_as]
3518#[skip_serializing_none]
3519#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3520#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3521#[serde(rename_all = "camelCase")]
3522#[non_exhaustive]
3523pub struct ProviderCurrentConfig {
3524 pub api_type: LlmProtocol,
3526 #[cfg_attr(feature = "schemars", schemars(url))]
3528 pub base_url: String,
3529 #[serde_as(deserialize_as = "DefaultOnError")]
3535 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3536 #[serde(default)]
3537 #[serde(rename = "_meta")]
3538 pub meta: Option<Meta>,
3539}
3540
3541#[cfg(feature = "unstable_llm_providers")]
3542impl ProviderCurrentConfig {
3543 #[must_use]
3545 pub fn new(api_type: LlmProtocol, base_url: impl Into<String>) -> Self {
3546 Self {
3547 api_type,
3548 base_url: base_url.into(),
3549 meta: None,
3550 }
3551 }
3552
3553 #[must_use]
3559 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3560 self.meta = meta.into_option();
3561 self
3562 }
3563}
3564
3565#[cfg(feature = "unstable_llm_providers")]
3571#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3572#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
3573#[serde(transparent)]
3574#[from(forward)]
3575#[non_exhaustive]
3576pub struct ProviderId(pub Arc<str>);
3577
3578#[cfg(feature = "unstable_llm_providers")]
3579impl ProviderId {
3580 #[must_use]
3582 pub fn new(id: impl Into<Self>) -> Self {
3583 id.into()
3584 }
3585}
3586
3587#[cfg(feature = "unstable_llm_providers")]
3593#[serde_as]
3594#[skip_serializing_none]
3595#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3596#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3597#[serde(rename_all = "camelCase")]
3598#[non_exhaustive]
3599pub struct ProviderInfo {
3600 pub provider_id: ProviderId,
3602 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
3604 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
3605 pub supported: Vec<LlmProtocol>,
3606 pub required: bool,
3609 #[serde(default)]
3612 pub current: Option<ProviderCurrentConfig>,
3613 #[serde_as(deserialize_as = "DefaultOnError")]
3619 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3620 #[serde(default)]
3621 #[serde(rename = "_meta")]
3622 pub meta: Option<Meta>,
3623}
3624
3625#[cfg(feature = "unstable_llm_providers")]
3626impl ProviderInfo {
3627 #[must_use]
3629 pub fn new(
3630 provider_id: impl Into<ProviderId>,
3631 supported: Vec<LlmProtocol>,
3632 required: bool,
3633 current: impl IntoOption<ProviderCurrentConfig>,
3634 ) -> Self {
3635 Self {
3636 provider_id: provider_id.into(),
3637 supported,
3638 required,
3639 current: current.into_option(),
3640 meta: None,
3641 }
3642 }
3643
3644 #[must_use]
3650 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3651 self.meta = meta.into_option();
3652 self
3653 }
3654}
3655
3656#[cfg(feature = "unstable_llm_providers")]
3657crate::serde_util::default_on_null! {
3658 #[serde_as]
3664 #[skip_serializing_none]
3665 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3666 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
3667 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_LIST_METHOD_NAME)))]
3668 #[serde(rename_all = "camelCase")]
3669 #[non_exhaustive]
3670 pub struct ListProvidersRequest {
3671 #[serde_as(deserialize_as = "DefaultOnError")]
3677 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3678 #[serde(default)]
3679 #[serde(rename = "_meta")]
3680 pub meta: Option<Meta>,
3681 }
3682}
3683
3684#[cfg(feature = "unstable_llm_providers")]
3685impl ListProvidersRequest {
3686 #[must_use]
3688 pub fn new() -> Self {
3689 Self::default()
3690 }
3691
3692 #[must_use]
3698 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3699 self.meta = meta.into_option();
3700 self
3701 }
3702}
3703
3704#[cfg(feature = "unstable_llm_providers")]
3710#[serde_as]
3711#[skip_serializing_none]
3712#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3713#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3714#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_LIST_METHOD_NAME)))]
3715#[serde(rename_all = "camelCase")]
3716#[non_exhaustive]
3717pub struct ListProvidersResponse {
3718 pub providers: Vec<ProviderInfo>,
3720 #[serde_as(deserialize_as = "DefaultOnError")]
3726 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3727 #[serde(default)]
3728 #[serde(rename = "_meta")]
3729 pub meta: Option<Meta>,
3730}
3731
3732#[cfg(feature = "unstable_llm_providers")]
3733impl ListProvidersResponse {
3734 #[must_use]
3736 pub fn new(providers: Vec<ProviderInfo>) -> Self {
3737 Self {
3738 providers,
3739 meta: None,
3740 }
3741 }
3742
3743 #[must_use]
3749 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3750 self.meta = meta.into_option();
3751 self
3752 }
3753}
3754
3755#[cfg(feature = "unstable_llm_providers")]
3763#[serde_as]
3764#[skip_serializing_none]
3765#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3766#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
3767#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_SET_METHOD_NAME)))]
3768#[serde(rename_all = "camelCase")]
3769#[non_exhaustive]
3770pub struct SetProviderRequest {
3771 pub provider_id: ProviderId,
3773 pub api_type: LlmProtocol,
3775 #[cfg_attr(feature = "schemars", schemars(url))]
3777 pub base_url: String,
3778 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
3781 pub headers: HashMap<String, String>,
3782 #[serde_as(deserialize_as = "DefaultOnError")]
3788 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3789 #[serde(default)]
3790 #[serde(rename = "_meta")]
3791 pub meta: Option<Meta>,
3792}
3793
3794#[cfg(feature = "unstable_llm_providers")]
3795impl SetProviderRequest {
3796 #[must_use]
3798 pub fn new(
3799 provider_id: impl Into<ProviderId>,
3800 api_type: LlmProtocol,
3801 base_url: impl Into<String>,
3802 ) -> Self {
3803 Self {
3804 provider_id: provider_id.into(),
3805 api_type,
3806 base_url: base_url.into(),
3807 headers: HashMap::new(),
3808 meta: None,
3809 }
3810 }
3811
3812 #[must_use]
3815 pub fn headers(mut self, headers: HashMap<String, String>) -> Self {
3816 self.headers = headers;
3817 self
3818 }
3819
3820 #[must_use]
3826 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3827 self.meta = meta.into_option();
3828 self
3829 }
3830}
3831
3832#[cfg(feature = "unstable_llm_providers")]
3833crate::serde_util::default_on_null! {
3834 #[serde_as]
3840 #[skip_serializing_none]
3841 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3842 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
3843 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_SET_METHOD_NAME)))]
3844 #[serde(rename_all = "camelCase")]
3845 #[non_exhaustive]
3846 pub struct SetProviderResponse {
3847 #[serde_as(deserialize_as = "DefaultOnError")]
3853 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3854 #[serde(default)]
3855 #[serde(rename = "_meta")]
3856 pub meta: Option<Meta>,
3857 }
3858}
3859
3860#[cfg(feature = "unstable_llm_providers")]
3861impl SetProviderResponse {
3862 #[must_use]
3864 pub fn new() -> Self {
3865 Self::default()
3866 }
3867
3868 #[must_use]
3874 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3875 self.meta = meta.into_option();
3876 self
3877 }
3878}
3879
3880#[cfg(feature = "unstable_llm_providers")]
3886#[serde_as]
3887#[skip_serializing_none]
3888#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3889#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3890#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_DISABLE_METHOD_NAME)))]
3891#[serde(rename_all = "camelCase")]
3892#[non_exhaustive]
3893pub struct DisableProviderRequest {
3894 pub provider_id: ProviderId,
3896 #[serde_as(deserialize_as = "DefaultOnError")]
3902 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3903 #[serde(default)]
3904 #[serde(rename = "_meta")]
3905 pub meta: Option<Meta>,
3906}
3907
3908#[cfg(feature = "unstable_llm_providers")]
3909impl DisableProviderRequest {
3910 #[must_use]
3912 pub fn new(provider_id: impl Into<ProviderId>) -> Self {
3913 Self {
3914 provider_id: provider_id.into(),
3915 meta: None,
3916 }
3917 }
3918
3919 #[must_use]
3925 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3926 self.meta = meta.into_option();
3927 self
3928 }
3929}
3930
3931#[cfg(feature = "unstable_llm_providers")]
3932crate::serde_util::default_on_null! {
3933 #[serde_as]
3939 #[skip_serializing_none]
3940 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3941 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
3942 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_DISABLE_METHOD_NAME)))]
3943 #[serde(rename_all = "camelCase")]
3944 #[non_exhaustive]
3945 pub struct DisableProviderResponse {
3946 #[serde_as(deserialize_as = "DefaultOnError")]
3952 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3953 #[serde(default)]
3954 #[serde(rename = "_meta")]
3955 pub meta: Option<Meta>,
3956 }
3957}
3958
3959#[cfg(feature = "unstable_llm_providers")]
3960impl DisableProviderResponse {
3961 #[must_use]
3963 pub fn new() -> Self {
3964 Self::default()
3965 }
3966
3967 #[must_use]
3973 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3974 self.meta = meta.into_option();
3975 self
3976 }
3977}
3978
3979#[serde_as]
3988#[skip_serializing_none]
3989#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3990#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3991#[serde(rename_all = "camelCase")]
3992#[non_exhaustive]
3993pub struct AgentCapabilities {
3994 #[serde_as(deserialize_as = "DefaultOnError")]
4001 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4002 #[serde(default)]
4003 pub session: Option<SessionCapabilities>,
4004 #[serde_as(deserialize_as = "DefaultOnError")]
4011 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4012 #[serde(default)]
4013 pub auth: Option<AgentAuthCapabilities>,
4014 #[cfg(feature = "unstable_llm_providers")]
4023 #[serde_as(deserialize_as = "DefaultOnError")]
4024 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4025 #[serde(default)]
4026 pub providers: Option<ProvidersCapabilities>,
4027 #[cfg(feature = "unstable_nes")]
4036 #[serde_as(deserialize_as = "DefaultOnError")]
4037 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4038 #[serde(default)]
4039 pub nes: Option<NesCapabilities>,
4040 #[cfg(feature = "unstable_nes")]
4046 #[serde_as(deserialize_as = "DefaultOnError")]
4047 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4048 #[serde(default)]
4049 pub position_encoding: Option<PositionEncodingKind>,
4050 #[serde_as(deserialize_as = "DefaultOnError")]
4056 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4057 #[serde(default)]
4058 #[serde(rename = "_meta")]
4059 pub meta: Option<Meta>,
4060}
4061
4062impl AgentCapabilities {
4063 #[must_use]
4065 pub fn new() -> Self {
4066 Self::default()
4067 }
4068
4069 #[must_use]
4076 pub fn session(mut self, session: impl IntoOption<SessionCapabilities>) -> Self {
4077 self.session = session.into_option();
4078 self
4079 }
4080
4081 #[must_use]
4085 pub fn auth(mut self, auth: impl IntoOption<AgentAuthCapabilities>) -> Self {
4086 self.auth = auth.into_option();
4087 self
4088 }
4089
4090 #[cfg(feature = "unstable_llm_providers")]
4096 #[must_use]
4097 pub fn providers(mut self, providers: impl IntoOption<ProvidersCapabilities>) -> Self {
4098 self.providers = providers.into_option();
4099 self
4100 }
4101
4102 #[cfg(feature = "unstable_nes")]
4108 #[must_use]
4109 pub fn nes(mut self, nes: impl IntoOption<NesCapabilities>) -> Self {
4110 self.nes = nes.into_option();
4111 self
4112 }
4113
4114 #[cfg(feature = "unstable_nes")]
4118 #[must_use]
4119 pub fn position_encoding(
4120 mut self,
4121 position_encoding: impl IntoOption<PositionEncodingKind>,
4122 ) -> Self {
4123 self.position_encoding = position_encoding.into_option();
4124 self
4125 }
4126
4127 #[must_use]
4133 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4134 self.meta = meta.into_option();
4135 self
4136 }
4137}
4138
4139#[cfg(feature = "unstable_llm_providers")]
4147#[serde_as]
4148#[skip_serializing_none]
4149#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4150#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4151#[non_exhaustive]
4152pub struct ProvidersCapabilities {
4153 #[serde_as(deserialize_as = "DefaultOnError")]
4159 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4160 #[serde(default)]
4161 #[serde(rename = "_meta")]
4162 pub meta: Option<Meta>,
4163}
4164
4165#[cfg(feature = "unstable_llm_providers")]
4166impl ProvidersCapabilities {
4167 #[must_use]
4169 pub fn new() -> Self {
4170 Self::default()
4171 }
4172
4173 #[must_use]
4179 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4180 self.meta = meta.into_option();
4181 self
4182 }
4183}
4184
4185#[serde_as]
4197#[skip_serializing_none]
4198#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4199#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4200#[serde(rename_all = "camelCase")]
4201#[non_exhaustive]
4202pub struct SessionCapabilities {
4203 #[serde_as(deserialize_as = "DefaultOnError")]
4209 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4210 #[serde(default)]
4211 pub prompt: Option<PromptCapabilities>,
4212 #[serde_as(deserialize_as = "DefaultOnError")]
4217 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4218 #[serde(default)]
4219 pub mcp: Option<McpCapabilities>,
4220 #[serde_as(deserialize_as = "DefaultOnError")]
4225 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4226 #[serde(default)]
4227 pub delete: Option<SessionDeleteCapabilities>,
4228 #[serde_as(deserialize_as = "DefaultOnError")]
4237 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4238 #[serde(default)]
4239 pub additional_directories: Option<SessionAdditionalDirectoriesCapabilities>,
4240 #[cfg(feature = "unstable_session_fork")]
4249 #[serde_as(deserialize_as = "DefaultOnError")]
4250 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4251 #[serde(default)]
4252 pub fork: Option<SessionForkCapabilities>,
4253 #[serde_as(deserialize_as = "DefaultOnError")]
4259 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4260 #[serde(default)]
4261 #[serde(rename = "_meta")]
4262 pub meta: Option<Meta>,
4263}
4264
4265impl SessionCapabilities {
4266 #[must_use]
4268 pub fn new() -> Self {
4269 Self::default()
4270 }
4271
4272 #[must_use]
4278 pub fn prompt(mut self, prompt: impl IntoOption<PromptCapabilities>) -> Self {
4279 self.prompt = prompt.into_option();
4280 self
4281 }
4282
4283 #[must_use]
4288 pub fn mcp(mut self, mcp: impl IntoOption<McpCapabilities>) -> Self {
4289 self.mcp = mcp.into_option();
4290 self
4291 }
4292
4293 #[must_use]
4298 pub fn delete(mut self, delete: impl IntoOption<SessionDeleteCapabilities>) -> Self {
4299 self.delete = delete.into_option();
4300 self
4301 }
4302
4303 #[must_use]
4312 pub fn additional_directories(
4313 mut self,
4314 additional_directories: impl IntoOption<SessionAdditionalDirectoriesCapabilities>,
4315 ) -> Self {
4316 self.additional_directories = additional_directories.into_option();
4317 self
4318 }
4319
4320 #[cfg(feature = "unstable_session_fork")]
4321 #[must_use]
4326 pub fn fork(mut self, fork: impl IntoOption<SessionForkCapabilities>) -> Self {
4327 self.fork = fork.into_option();
4328 self
4329 }
4330
4331 #[must_use]
4337 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4338 self.meta = meta.into_option();
4339 self
4340 }
4341}
4342
4343#[serde_as]
4347#[skip_serializing_none]
4348#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4349#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4350#[non_exhaustive]
4351pub struct SessionDeleteCapabilities {
4352 #[serde_as(deserialize_as = "DefaultOnError")]
4358 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4359 #[serde(default)]
4360 #[serde(rename = "_meta")]
4361 pub meta: Option<Meta>,
4362}
4363
4364impl SessionDeleteCapabilities {
4365 #[must_use]
4367 pub fn new() -> Self {
4368 Self::default()
4369 }
4370
4371 #[must_use]
4377 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4378 self.meta = meta.into_option();
4379 self
4380 }
4381}
4382
4383#[serde_as]
4390#[skip_serializing_none]
4391#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4392#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4393#[non_exhaustive]
4394pub struct SessionAdditionalDirectoriesCapabilities {
4395 #[serde_as(deserialize_as = "DefaultOnError")]
4401 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4402 #[serde(default)]
4403 #[serde(rename = "_meta")]
4404 pub meta: Option<Meta>,
4405}
4406
4407impl SessionAdditionalDirectoriesCapabilities {
4408 #[must_use]
4410 pub fn new() -> Self {
4411 Self::default()
4412 }
4413
4414 #[must_use]
4420 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4421 self.meta = meta.into_option();
4422 self
4423 }
4424}
4425
4426#[cfg(feature = "unstable_session_fork")]
4434#[serde_as]
4435#[skip_serializing_none]
4436#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4437#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4438#[non_exhaustive]
4439pub struct SessionForkCapabilities {
4440 #[serde_as(deserialize_as = "DefaultOnError")]
4446 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4447 #[serde(default)]
4448 #[serde(rename = "_meta")]
4449 pub meta: Option<Meta>,
4450}
4451
4452#[cfg(feature = "unstable_session_fork")]
4453impl SessionForkCapabilities {
4454 #[must_use]
4456 pub fn new() -> Self {
4457 Self::default()
4458 }
4459
4460 #[must_use]
4466 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4467 self.meta = meta.into_option();
4468 self
4469 }
4470}
4471
4472#[serde_as]
4485#[skip_serializing_none]
4486#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4487#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4488#[serde(rename_all = "camelCase")]
4489#[non_exhaustive]
4490pub struct PromptCapabilities {
4491 #[serde_as(deserialize_as = "DefaultOnError")]
4496 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4497 #[serde(default)]
4498 pub image: Option<PromptImageCapabilities>,
4499 #[serde_as(deserialize_as = "DefaultOnError")]
4504 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4505 #[serde(default)]
4506 pub audio: Option<PromptAudioCapabilities>,
4507 #[serde_as(deserialize_as = "DefaultOnError")]
4515 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4516 #[serde(default)]
4517 pub embedded_context: Option<PromptEmbeddedContextCapabilities>,
4518 #[serde_as(deserialize_as = "DefaultOnError")]
4524 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4525 #[serde(default)]
4526 #[serde(rename = "_meta")]
4527 pub meta: Option<Meta>,
4528}
4529
4530impl PromptCapabilities {
4531 #[must_use]
4533 pub fn new() -> Self {
4534 Self::default()
4535 }
4536
4537 #[must_use]
4542 pub fn image(mut self, image: impl IntoOption<PromptImageCapabilities>) -> Self {
4543 self.image = image.into_option();
4544 self
4545 }
4546
4547 #[must_use]
4552 pub fn audio(mut self, audio: impl IntoOption<PromptAudioCapabilities>) -> Self {
4553 self.audio = audio.into_option();
4554 self
4555 }
4556
4557 #[must_use]
4565 pub fn embedded_context(
4566 mut self,
4567 embedded_context: impl IntoOption<PromptEmbeddedContextCapabilities>,
4568 ) -> Self {
4569 self.embedded_context = embedded_context.into_option();
4570 self
4571 }
4572
4573 #[must_use]
4579 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4580 self.meta = meta.into_option();
4581 self
4582 }
4583}
4584
4585#[serde_as]
4589#[skip_serializing_none]
4590#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4591#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4592#[non_exhaustive]
4593pub struct PromptImageCapabilities {
4594 #[serde_as(deserialize_as = "DefaultOnError")]
4600 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4601 #[serde(default)]
4602 #[serde(rename = "_meta")]
4603 pub meta: Option<Meta>,
4604}
4605
4606impl PromptImageCapabilities {
4607 #[must_use]
4609 pub fn new() -> Self {
4610 Self::default()
4611 }
4612
4613 #[must_use]
4619 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4620 self.meta = meta.into_option();
4621 self
4622 }
4623}
4624
4625#[serde_as]
4629#[skip_serializing_none]
4630#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4631#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4632#[non_exhaustive]
4633pub struct PromptAudioCapabilities {
4634 #[serde_as(deserialize_as = "DefaultOnError")]
4640 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4641 #[serde(default)]
4642 #[serde(rename = "_meta")]
4643 pub meta: Option<Meta>,
4644}
4645
4646impl PromptAudioCapabilities {
4647 #[must_use]
4649 pub fn new() -> Self {
4650 Self::default()
4651 }
4652
4653 #[must_use]
4659 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4660 self.meta = meta.into_option();
4661 self
4662 }
4663}
4664
4665#[serde_as]
4669#[skip_serializing_none]
4670#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4671#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4672#[non_exhaustive]
4673pub struct PromptEmbeddedContextCapabilities {
4674 #[serde_as(deserialize_as = "DefaultOnError")]
4680 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4681 #[serde(default)]
4682 #[serde(rename = "_meta")]
4683 pub meta: Option<Meta>,
4684}
4685
4686impl PromptEmbeddedContextCapabilities {
4687 #[must_use]
4689 pub fn new() -> Self {
4690 Self::default()
4691 }
4692
4693 #[must_use]
4699 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4700 self.meta = meta.into_option();
4701 self
4702 }
4703}
4704
4705#[serde_as]
4707#[skip_serializing_none]
4708#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4709#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4710#[serde(rename_all = "camelCase")]
4711#[non_exhaustive]
4712pub struct McpCapabilities {
4713 #[serde_as(deserialize_as = "DefaultOnError")]
4718 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4719 #[serde(default)]
4720 pub stdio: Option<McpStdioCapabilities>,
4721 #[serde_as(deserialize_as = "DefaultOnError")]
4726 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4727 #[serde(default)]
4728 pub http: Option<McpHttpCapabilities>,
4729 #[cfg(feature = "unstable_mcp_over_acp")]
4738 #[serde_as(deserialize_as = "DefaultOnError")]
4739 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4740 #[serde(default)]
4741 pub acp: Option<McpAcpCapabilities>,
4742 #[serde_as(deserialize_as = "DefaultOnError")]
4748 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4749 #[serde(default)]
4750 #[serde(rename = "_meta")]
4751 pub meta: Option<Meta>,
4752}
4753
4754impl McpCapabilities {
4755 #[must_use]
4757 pub fn new() -> Self {
4758 Self::default()
4759 }
4760
4761 #[must_use]
4766 pub fn stdio(mut self, stdio: impl IntoOption<McpStdioCapabilities>) -> Self {
4767 self.stdio = stdio.into_option();
4768 self
4769 }
4770
4771 #[must_use]
4776 pub fn http(mut self, http: impl IntoOption<McpHttpCapabilities>) -> Self {
4777 self.http = http.into_option();
4778 self
4779 }
4780
4781 #[cfg(feature = "unstable_mcp_over_acp")]
4787 #[must_use]
4791 pub fn acp(mut self, acp: impl IntoOption<McpAcpCapabilities>) -> Self {
4792 self.acp = acp.into_option();
4793 self
4794 }
4795
4796 #[must_use]
4802 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4803 self.meta = meta.into_option();
4804 self
4805 }
4806}
4807
4808#[serde_as]
4812#[skip_serializing_none]
4813#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4814#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4815#[non_exhaustive]
4816pub struct McpStdioCapabilities {
4817 #[serde_as(deserialize_as = "DefaultOnError")]
4823 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4824 #[serde(default)]
4825 #[serde(rename = "_meta")]
4826 pub meta: Option<Meta>,
4827}
4828
4829impl McpStdioCapabilities {
4830 #[must_use]
4832 pub fn new() -> Self {
4833 Self::default()
4834 }
4835
4836 #[must_use]
4842 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4843 self.meta = meta.into_option();
4844 self
4845 }
4846}
4847
4848#[serde_as]
4852#[skip_serializing_none]
4853#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4854#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4855#[non_exhaustive]
4856pub struct McpHttpCapabilities {
4857 #[serde_as(deserialize_as = "DefaultOnError")]
4863 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4864 #[serde(default)]
4865 #[serde(rename = "_meta")]
4866 pub meta: Option<Meta>,
4867}
4868
4869impl McpHttpCapabilities {
4870 #[must_use]
4872 pub fn new() -> Self {
4873 Self::default()
4874 }
4875
4876 #[must_use]
4882 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4883 self.meta = meta.into_option();
4884 self
4885 }
4886}
4887
4888#[cfg(feature = "unstable_mcp_over_acp")]
4896#[serde_as]
4897#[skip_serializing_none]
4898#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4899#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4900#[non_exhaustive]
4901pub struct McpAcpCapabilities {
4902 #[serde_as(deserialize_as = "DefaultOnError")]
4908 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4909 #[serde(default)]
4910 #[serde(rename = "_meta")]
4911 pub meta: Option<Meta>,
4912}
4913
4914#[cfg(feature = "unstable_mcp_over_acp")]
4915impl McpAcpCapabilities {
4916 #[must_use]
4918 pub fn new() -> Self {
4919 Self::default()
4920 }
4921
4922 #[must_use]
4928 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4929 self.meta = meta.into_option();
4930 self
4931 }
4932}
4933
4934#[serde_as]
4938#[skip_serializing_none]
4939#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4940#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4941#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_CANCEL_METHOD_NAME)))]
4942#[serde(rename_all = "camelCase")]
4943#[non_exhaustive]
4944pub struct CancelSessionNotification {
4945 pub session_id: SessionId,
4947 #[serde_as(deserialize_as = "DefaultOnError")]
4953 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4954 #[serde(default)]
4955 #[serde(rename = "_meta")]
4956 pub meta: Option<Meta>,
4957}
4958
4959impl CancelSessionNotification {
4960 #[must_use]
4962 pub fn new(session_id: impl Into<SessionId>) -> Self {
4963 Self {
4964 session_id: session_id.into(),
4965 meta: None,
4966 }
4967 }
4968
4969 #[must_use]
4975 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4976 self.meta = meta.into_option();
4977 self
4978 }
4979}
4980
4981#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4987#[non_exhaustive]
4988pub struct AgentMethodNames {
4989 pub initialize: &'static str,
4991 pub auth_login: &'static str,
4993 #[cfg(feature = "unstable_llm_providers")]
4995 pub providers_list: &'static str,
4996 #[cfg(feature = "unstable_llm_providers")]
4998 pub providers_set: &'static str,
4999 #[cfg(feature = "unstable_llm_providers")]
5001 pub providers_disable: &'static str,
5002 pub session_new: &'static str,
5004 pub session_set_config_option: &'static str,
5006 pub session_prompt: &'static str,
5008 pub session_cancel: &'static str,
5010 #[cfg(feature = "unstable_mcp_over_acp")]
5012 pub mcp_message: &'static str,
5013 pub session_list: &'static str,
5015 pub session_delete: &'static str,
5017 #[cfg(feature = "unstable_session_fork")]
5019 pub session_fork: &'static str,
5020 pub session_resume: &'static str,
5022 pub session_close: &'static str,
5024 pub auth_logout: &'static str,
5026 #[cfg(feature = "unstable_nes")]
5028 pub nes_start: &'static str,
5029 #[cfg(feature = "unstable_nes")]
5031 pub nes_suggest: &'static str,
5032 #[cfg(feature = "unstable_nes")]
5034 pub nes_accept: &'static str,
5035 #[cfg(feature = "unstable_nes")]
5037 pub nes_reject: &'static str,
5038 #[cfg(feature = "unstable_nes")]
5040 pub nes_close: &'static str,
5041 #[cfg(feature = "unstable_nes")]
5043 pub document_did_open: &'static str,
5044 #[cfg(feature = "unstable_nes")]
5046 pub document_did_change: &'static str,
5047 #[cfg(feature = "unstable_nes")]
5049 pub document_did_close: &'static str,
5050 #[cfg(feature = "unstable_nes")]
5052 pub document_did_save: &'static str,
5053 #[cfg(feature = "unstable_nes")]
5055 pub document_did_focus: &'static str,
5056}
5057
5058pub const AGENT_METHOD_NAMES: AgentMethodNames = AgentMethodNames {
5060 initialize: INITIALIZE_METHOD_NAME,
5061 auth_login: AUTH_LOGIN_METHOD_NAME,
5062 #[cfg(feature = "unstable_llm_providers")]
5063 providers_list: PROVIDERS_LIST_METHOD_NAME,
5064 #[cfg(feature = "unstable_llm_providers")]
5065 providers_set: PROVIDERS_SET_METHOD_NAME,
5066 #[cfg(feature = "unstable_llm_providers")]
5067 providers_disable: PROVIDERS_DISABLE_METHOD_NAME,
5068 session_new: SESSION_NEW_METHOD_NAME,
5069 session_set_config_option: SESSION_SET_CONFIG_OPTION_METHOD_NAME,
5070 session_prompt: SESSION_PROMPT_METHOD_NAME,
5071 session_cancel: SESSION_CANCEL_METHOD_NAME,
5072 #[cfg(feature = "unstable_mcp_over_acp")]
5073 mcp_message: MCP_MESSAGE_METHOD_NAME,
5074 session_list: SESSION_LIST_METHOD_NAME,
5075 session_delete: SESSION_DELETE_METHOD_NAME,
5076 #[cfg(feature = "unstable_session_fork")]
5077 session_fork: SESSION_FORK_METHOD_NAME,
5078 session_resume: SESSION_RESUME_METHOD_NAME,
5079 session_close: SESSION_CLOSE_METHOD_NAME,
5080 auth_logout: AUTH_LOGOUT_METHOD_NAME,
5081 #[cfg(feature = "unstable_nes")]
5082 nes_start: NES_START_METHOD_NAME,
5083 #[cfg(feature = "unstable_nes")]
5084 nes_suggest: NES_SUGGEST_METHOD_NAME,
5085 #[cfg(feature = "unstable_nes")]
5086 nes_accept: NES_ACCEPT_METHOD_NAME,
5087 #[cfg(feature = "unstable_nes")]
5088 nes_reject: NES_REJECT_METHOD_NAME,
5089 #[cfg(feature = "unstable_nes")]
5090 nes_close: NES_CLOSE_METHOD_NAME,
5091 #[cfg(feature = "unstable_nes")]
5092 document_did_open: DOCUMENT_DID_OPEN_METHOD_NAME,
5093 #[cfg(feature = "unstable_nes")]
5094 document_did_change: DOCUMENT_DID_CHANGE_METHOD_NAME,
5095 #[cfg(feature = "unstable_nes")]
5096 document_did_close: DOCUMENT_DID_CLOSE_METHOD_NAME,
5097 #[cfg(feature = "unstable_nes")]
5098 document_did_save: DOCUMENT_DID_SAVE_METHOD_NAME,
5099 #[cfg(feature = "unstable_nes")]
5100 document_did_focus: DOCUMENT_DID_FOCUS_METHOD_NAME,
5101};
5102
5103pub(crate) const INITIALIZE_METHOD_NAME: &str = "initialize";
5105pub(crate) const AUTH_LOGIN_METHOD_NAME: &str = "auth/login";
5107#[cfg(feature = "unstable_llm_providers")]
5109pub(crate) const PROVIDERS_LIST_METHOD_NAME: &str = "providers/list";
5110#[cfg(feature = "unstable_llm_providers")]
5112pub(crate) const PROVIDERS_SET_METHOD_NAME: &str = "providers/set";
5113#[cfg(feature = "unstable_llm_providers")]
5115pub(crate) const PROVIDERS_DISABLE_METHOD_NAME: &str = "providers/disable";
5116pub(crate) const SESSION_NEW_METHOD_NAME: &str = "session/new";
5118pub(crate) const SESSION_SET_CONFIG_OPTION_METHOD_NAME: &str = "session/set_config_option";
5120pub(crate) const SESSION_PROMPT_METHOD_NAME: &str = "session/prompt";
5122pub(crate) const SESSION_CANCEL_METHOD_NAME: &str = "session/cancel";
5124pub(crate) const SESSION_LIST_METHOD_NAME: &str = "session/list";
5126pub(crate) const SESSION_DELETE_METHOD_NAME: &str = "session/delete";
5128#[cfg(feature = "unstable_session_fork")]
5130pub(crate) const SESSION_FORK_METHOD_NAME: &str = "session/fork";
5131pub(crate) const SESSION_RESUME_METHOD_NAME: &str = "session/resume";
5133pub(crate) const SESSION_CLOSE_METHOD_NAME: &str = "session/close";
5135pub(crate) const AUTH_LOGOUT_METHOD_NAME: &str = "auth/logout";
5137
5138#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5145#[derive(Clone, Debug, Serialize, Deserialize)]
5146#[serde(untagged)]
5147#[cfg_attr(feature = "schemars", schemars(inline))]
5148#[non_exhaustive]
5149pub enum ClientRequest {
5150 InitializeRequest(Box<InitializeRequest>),
5161 LoginAuthRequest(Box<LoginAuthRequest>),
5176 #[cfg(feature = "unstable_llm_providers")]
5182 ListProvidersRequest(Box<ListProvidersRequest>),
5183 #[cfg(feature = "unstable_llm_providers")]
5189 SetProviderRequest(Box<SetProviderRequest>),
5190 #[cfg(feature = "unstable_llm_providers")]
5196 DisableProviderRequest(Box<DisableProviderRequest>),
5197 LogoutAuthRequest(Box<LogoutAuthRequest>),
5207 NewSessionRequest(Box<NewSessionRequest>),
5220 ListSessionsRequest(Box<ListSessionsRequest>),
5224 DeleteSessionRequest(Box<DeleteSessionRequest>),
5228 #[cfg(feature = "unstable_session_fork")]
5229 ForkSessionRequest(Box<ForkSessionRequest>),
5241 ResumeSessionRequest(Box<ResumeSessionRequest>),
5247 CloseSessionRequest(Box<CloseSessionRequest>),
5252 SetSessionConfigOptionRequest(Box<SetSessionConfigOptionRequest>),
5254 PromptRequest(Box<PromptRequest>),
5266 #[cfg(feature = "unstable_nes")]
5267 StartNesRequest(Box<StartNesRequest>),
5273 #[cfg(feature = "unstable_nes")]
5274 SuggestNesRequest(Box<SuggestNesRequest>),
5280 #[cfg(feature = "unstable_nes")]
5281 CloseNesRequest(Box<CloseNesRequest>),
5290 ExtMethodRequest(Box<ExtRequest>),
5297}
5298
5299impl ClientRequest {
5300 #[must_use]
5302 pub fn method(&self) -> &str {
5303 match self {
5304 Self::InitializeRequest(_) => AGENT_METHOD_NAMES.initialize,
5305 Self::LoginAuthRequest(_) => AGENT_METHOD_NAMES.auth_login,
5306 #[cfg(feature = "unstable_llm_providers")]
5307 Self::ListProvidersRequest(_) => AGENT_METHOD_NAMES.providers_list,
5308 #[cfg(feature = "unstable_llm_providers")]
5309 Self::SetProviderRequest(_) => AGENT_METHOD_NAMES.providers_set,
5310 #[cfg(feature = "unstable_llm_providers")]
5311 Self::DisableProviderRequest(_) => AGENT_METHOD_NAMES.providers_disable,
5312 Self::LogoutAuthRequest(_) => AGENT_METHOD_NAMES.auth_logout,
5313 Self::NewSessionRequest(_) => AGENT_METHOD_NAMES.session_new,
5314 Self::ListSessionsRequest(_) => AGENT_METHOD_NAMES.session_list,
5315 Self::DeleteSessionRequest(_) => AGENT_METHOD_NAMES.session_delete,
5316 #[cfg(feature = "unstable_session_fork")]
5317 Self::ForkSessionRequest(_) => AGENT_METHOD_NAMES.session_fork,
5318 Self::ResumeSessionRequest(_) => AGENT_METHOD_NAMES.session_resume,
5319 Self::CloseSessionRequest(_) => AGENT_METHOD_NAMES.session_close,
5320 Self::SetSessionConfigOptionRequest(_) => AGENT_METHOD_NAMES.session_set_config_option,
5321 Self::PromptRequest(_) => AGENT_METHOD_NAMES.session_prompt,
5322 #[cfg(feature = "unstable_nes")]
5323 Self::StartNesRequest(_) => AGENT_METHOD_NAMES.nes_start,
5324 #[cfg(feature = "unstable_nes")]
5325 Self::SuggestNesRequest(_) => AGENT_METHOD_NAMES.nes_suggest,
5326 #[cfg(feature = "unstable_nes")]
5327 Self::CloseNesRequest(_) => AGENT_METHOD_NAMES.nes_close,
5328 Self::ExtMethodRequest(ext_request) => &ext_request.method,
5329 }
5330 }
5331}
5332
5333#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5340#[derive(Clone, Debug, Serialize, Deserialize)]
5341#[serde(untagged)]
5342#[cfg_attr(feature = "schemars", schemars(inline))]
5343#[non_exhaustive]
5344pub enum AgentResponse {
5345 InitializeResponse(Box<InitializeResponse>),
5347 LoginAuthResponse(#[serde(default)] Box<LoginAuthResponse>),
5349 #[cfg(feature = "unstable_llm_providers")]
5351 ListProvidersResponse(Box<ListProvidersResponse>),
5352 #[cfg(feature = "unstable_llm_providers")]
5354 SetProviderResponse(#[serde(default)] Box<SetProviderResponse>),
5355 #[cfg(feature = "unstable_llm_providers")]
5357 DisableProviderResponse(#[serde(default)] Box<DisableProviderResponse>),
5358 LogoutAuthResponse(#[serde(default)] Box<LogoutAuthResponse>),
5360 NewSessionResponse(Box<NewSessionResponse>),
5362 ListSessionsResponse(Box<ListSessionsResponse>),
5364 DeleteSessionResponse(#[serde(default)] Box<DeleteSessionResponse>),
5366 #[cfg(feature = "unstable_session_fork")]
5368 ForkSessionResponse(Box<ForkSessionResponse>),
5369 ResumeSessionResponse(#[serde(default)] Box<ResumeSessionResponse>),
5371 CloseSessionResponse(#[serde(default)] Box<CloseSessionResponse>),
5373 SetSessionConfigOptionResponse(Box<SetSessionConfigOptionResponse>),
5375 PromptResponse(Box<PromptResponse>),
5377 #[cfg(feature = "unstable_nes")]
5379 StartNesResponse(Box<StartNesResponse>),
5380 #[cfg(feature = "unstable_nes")]
5382 SuggestNesResponse(Box<SuggestNesResponse>),
5383 #[cfg(feature = "unstable_nes")]
5385 CloseNesResponse(#[serde(default)] Box<CloseNesResponse>),
5386 ExtMethodResponse(Box<ExtResponse>),
5388}
5389
5390#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5397#[derive(Clone, Debug, Serialize, Deserialize)]
5398#[serde(untagged)]
5399#[cfg_attr(feature = "schemars", schemars(inline))]
5400#[non_exhaustive]
5401pub enum ClientNotification {
5402 CancelSessionNotification(Box<CancelSessionNotification>),
5416 #[cfg(feature = "unstable_nes")]
5417 DidOpenDocumentNotification(Box<DidOpenDocumentNotification>),
5421 #[cfg(feature = "unstable_nes")]
5422 DidChangeDocumentNotification(Box<DidChangeDocumentNotification>),
5426 #[cfg(feature = "unstable_nes")]
5427 DidCloseDocumentNotification(Box<DidCloseDocumentNotification>),
5431 #[cfg(feature = "unstable_nes")]
5432 DidSaveDocumentNotification(Box<DidSaveDocumentNotification>),
5436 #[cfg(feature = "unstable_nes")]
5437 DidFocusDocumentNotification(Box<DidFocusDocumentNotification>),
5441 #[cfg(feature = "unstable_nes")]
5442 AcceptNesNotification(Box<AcceptNesNotification>),
5446 #[cfg(feature = "unstable_nes")]
5447 RejectNesNotification(Box<RejectNesNotification>),
5451 #[cfg(feature = "unstable_mcp_over_acp")]
5457 MessageMcpNotification(Box<MessageMcpNotification>),
5458 ExtNotification(Box<ExtNotification>),
5465}
5466
5467impl ClientNotification {
5468 #[must_use]
5470 pub fn method(&self) -> &str {
5471 match self {
5472 Self::CancelSessionNotification(_) => AGENT_METHOD_NAMES.session_cancel,
5473 #[cfg(feature = "unstable_nes")]
5474 Self::DidOpenDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_open,
5475 #[cfg(feature = "unstable_nes")]
5476 Self::DidChangeDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_change,
5477 #[cfg(feature = "unstable_nes")]
5478 Self::DidCloseDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_close,
5479 #[cfg(feature = "unstable_nes")]
5480 Self::DidSaveDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_save,
5481 #[cfg(feature = "unstable_nes")]
5482 Self::DidFocusDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_focus,
5483 #[cfg(feature = "unstable_nes")]
5484 Self::AcceptNesNotification(_) => AGENT_METHOD_NAMES.nes_accept,
5485 #[cfg(feature = "unstable_nes")]
5486 Self::RejectNesNotification(_) => AGENT_METHOD_NAMES.nes_reject,
5487 #[cfg(feature = "unstable_mcp_over_acp")]
5488 Self::MessageMcpNotification(_) => AGENT_METHOD_NAMES.mcp_message,
5489 Self::ExtNotification(ext_notification) => &ext_notification.method,
5490 }
5491 }
5492}
5493
5494#[cfg(test)]
5495mod test_serialization {
5496 use std::path::PathBuf;
5497
5498 use super::*;
5499 use serde_json::json;
5500
5501 fn test_meta() -> Meta {
5502 json!({ "source": "test" }).as_object().unwrap().clone()
5503 }
5504
5505 fn serialized_meta_key_count(value: &impl serde::Serialize) -> usize {
5506 serde_json::to_string(value)
5507 .unwrap()
5508 .matches("\"_meta\"")
5509 .count()
5510 }
5511
5512 #[test]
5513 fn prompt_response_without_metadata_round_trips() {
5514 let response = PromptResponse::new(MessageId::new("message-1"));
5515 let serialized = serde_json::to_value(&response).unwrap();
5516 assert_eq!(serialized, json!({ "messageId": "message-1" }));
5517 assert_eq!(
5518 serde_json::from_value::<PromptResponse>(serialized).unwrap(),
5519 response
5520 );
5521 assert_eq!(
5522 serde_json::from_value::<PromptResponse>(json!({
5523 "messageId": "message-1",
5524 "_meta": null
5525 }))
5526 .unwrap(),
5527 response
5528 );
5529 }
5530
5531 #[test]
5532 fn prompt_response_requires_message_id_and_preserves_metadata_tolerance() {
5533 let response = PromptResponse::new("message-1").meta(test_meta());
5534 let serialized = serde_json::to_value(&response).unwrap();
5535 assert_eq!(
5536 serialized,
5537 json!({
5538 "messageId": "message-1",
5539 "_meta": { "source": "test" }
5540 })
5541 );
5542
5543 let deserialized: PromptResponse = serde_json::from_value(serialized).unwrap();
5544 assert_eq!(deserialized.message_id, MessageId::new("message-1"));
5545 assert_eq!(deserialized.meta, Some(test_meta()));
5546
5547 let malformed_meta: PromptResponse = serde_json::from_value(json!({
5548 "messageId": "message-2",
5549 "_meta": false
5550 }))
5551 .unwrap();
5552 assert_eq!(malformed_meta.message_id, MessageId::new("message-2"));
5553 assert_eq!(malformed_meta.meta, None);
5554
5555 for invalid in [
5556 json!({}),
5557 json!({ "messageId": null }),
5558 json!({ "messageId": 1 }),
5559 json!({ "messageId": false }),
5560 json!({ "messageId": {} }),
5561 json!({ "messageId": [] }),
5562 ] {
5563 assert!(
5564 serde_json::from_value::<PromptResponse>(invalid).is_err(),
5565 "missing, null, and non-string message IDs must be rejected"
5566 );
5567 }
5568 }
5569
5570 #[cfg(feature = "schemars")]
5571 #[test]
5572 fn prompt_response_schema_requires_non_null_string_message_id_reference() {
5573 let schema = serde_json::to_value(schemars::schema_for!(PromptResponse)).unwrap();
5574
5575 assert_eq!(schema["required"], json!(["messageId"]));
5576 assert_eq!(
5577 schema["properties"]["messageId"]["$ref"],
5578 "#/$defs/MessageId"
5579 );
5580 assert_eq!(schema["$defs"]["MessageId"]["type"], "string");
5581 }
5582
5583 #[test]
5584 fn test_initialize_capabilities_default_on_malformed_values() {
5585 let request: InitializeRequest = serde_json::from_value(json!({
5586 "protocolVersion": 2,
5587 "capabilities": false,
5588 "info": {
5589 "name": "client",
5590 "version": "1.0.0"
5591 }
5592 }))
5593 .unwrap();
5594 assert_eq!(request.capabilities, ClientCapabilities::default());
5595
5596 let response: InitializeResponse = serde_json::from_value(json!({
5597 "protocolVersion": 2,
5598 "capabilities": false,
5599 "info": {
5600 "name": "agent",
5601 "version": "1.0.0"
5602 }
5603 }))
5604 .unwrap();
5605 assert_eq!(response.capabilities, AgentCapabilities::default());
5606 }
5607
5608 #[test]
5609 fn test_agent_capabilities_default_on_malformed_values() {
5610 let capabilities: AgentCapabilities = serde_json::from_value(json!({
5611 "session": false,
5612 "auth": false
5613 }))
5614 .unwrap();
5615
5616 assert!(capabilities.session.is_none());
5617 assert_eq!(capabilities.auth, None);
5618 }
5619
5620 #[test]
5621 fn test_mcp_server_stdio_serialization() {
5622 let server = McpServer::Stdio(
5623 McpServerStdio::new("test-server", "/usr/bin/server")
5624 .args(vec!["--port".to_string(), "3000".to_string()])
5625 .env(vec![EnvVariable::new("API_KEY", "secret123")]),
5626 );
5627
5628 let json = serde_json::to_value(&server).unwrap();
5629 assert_eq!(
5630 json,
5631 json!({
5632 "type": "stdio",
5633 "name": "test-server",
5634 "command": "/usr/bin/server",
5635 "args": ["--port", "3000"],
5636 "env": [
5637 {
5638 "name": "API_KEY",
5639 "value": "secret123"
5640 }
5641 ]
5642 })
5643 );
5644
5645 let deserialized: McpServer = serde_json::from_value(json).unwrap();
5646 match deserialized {
5647 McpServer::Stdio(McpServerStdio {
5648 name,
5649 command,
5650 args,
5651 env,
5652 meta: _,
5653 }) => {
5654 assert_eq!(name, "test-server");
5655 assert_eq!(command, AbsolutePath::new("/usr/bin/server"));
5656 assert_eq!(args, vec!["--port", "3000"]);
5657 assert_eq!(env.len(), 1);
5658 assert_eq!(env[0].name, "API_KEY");
5659 assert_eq!(env[0].value, "secret123");
5660 }
5661 _ => panic!("Expected Stdio variant"),
5662 }
5663 }
5664
5665 #[test]
5666 fn test_mcp_server_empty_arrays_are_optional() {
5667 let stdio = McpServer::Stdio(McpServerStdio::new("test-server", "/usr/bin/server"));
5668 assert_eq!(
5669 serde_json::to_value(&stdio).unwrap(),
5670 json!({
5671 "type": "stdio",
5672 "name": "test-server",
5673 "command": "/usr/bin/server"
5674 })
5675 );
5676
5677 let McpServer::Stdio(McpServerStdio { args, env, .. }) =
5678 serde_json::from_value::<McpServer>(json!({
5679 "type": "stdio",
5680 "name": "test-server",
5681 "command": "/usr/bin/server"
5682 }))
5683 .unwrap()
5684 else {
5685 panic!("Expected Stdio variant");
5686 };
5687 assert!(args.is_empty());
5688 assert!(env.is_empty());
5689
5690 let http = McpServer::Http(McpServerHttp::new("http-server", "https://api.example.com"));
5691 assert_eq!(
5692 serde_json::to_value(&http).unwrap(),
5693 json!({
5694 "type": "http",
5695 "name": "http-server",
5696 "url": "https://api.example.com"
5697 })
5698 );
5699
5700 let McpServer::Http(McpServerHttp { headers, .. }) =
5701 serde_json::from_value::<McpServer>(json!({
5702 "type": "http",
5703 "name": "http-server",
5704 "url": "https://api.example.com"
5705 }))
5706 .unwrap()
5707 else {
5708 panic!("Expected Http variant");
5709 };
5710 assert!(headers.is_empty());
5711 }
5712
5713 #[test]
5714 fn test_mcp_server_unknown_transport_serialization() {
5715 let json = json!({
5716 "type": "websocket",
5717 "name": "future-server",
5718 "url": "wss://example.com/mcp",
5719 "protocolVersion": "2026-01-01"
5720 });
5721
5722 let deserialized: McpServer = serde_json::from_value(json.clone()).unwrap();
5723 let McpServer::Other(OtherMcpServer { type_, fields }) = &deserialized else {
5724 panic!("Expected Other variant");
5725 };
5726
5727 assert_eq!(type_, "websocket");
5728 assert_eq!(fields["name"], "future-server");
5729 assert_eq!(fields["url"], "wss://example.com/mcp");
5730 assert_eq!(fields["protocolVersion"], "2026-01-01");
5731 assert_eq!(serde_json::to_value(&deserialized).unwrap(), json);
5732 }
5733
5734 #[test]
5735 fn test_mcp_server_stdio_requires_type() {
5736 let result = serde_json::from_value::<McpServer>(json!({
5737 "name": "test-server",
5738 "command": "/usr/bin/server",
5739 "args": [],
5740 "env": []
5741 }));
5742
5743 assert!(result.is_err());
5744 }
5745
5746 #[test]
5747 fn test_mcp_server_unknown_does_not_hide_malformed_known_transport() {
5748 let result = serde_json::from_value::<McpServer>(json!({
5749 "type": "stdio",
5750 "name": "test-server",
5751 "args": [],
5752 "env": []
5753 }));
5754
5755 assert!(result.is_err());
5756 }
5757
5758 #[test]
5759 fn test_mcp_server_http_serialization() {
5760 let server = McpServer::Http(
5761 McpServerHttp::new("http-server", "https://api.example.com").headers(vec![
5762 HttpHeader::new("Authorization", "Bearer token123"),
5763 HttpHeader::new("Content-Type", "application/json"),
5764 ]),
5765 );
5766
5767 let json = serde_json::to_value(&server).unwrap();
5768 assert_eq!(
5769 json,
5770 json!({
5771 "type": "http",
5772 "name": "http-server",
5773 "url": "https://api.example.com",
5774 "headers": [
5775 {
5776 "name": "Authorization",
5777 "value": "Bearer token123"
5778 },
5779 {
5780 "name": "Content-Type",
5781 "value": "application/json"
5782 }
5783 ]
5784 })
5785 );
5786
5787 let deserialized: McpServer = serde_json::from_value(json).unwrap();
5788 match deserialized {
5789 McpServer::Http(McpServerHttp {
5790 name,
5791 url,
5792 headers,
5793 meta: _,
5794 }) => {
5795 assert_eq!(name, "http-server");
5796 assert_eq!(url, "https://api.example.com");
5797 assert_eq!(headers.len(), 2);
5798 assert_eq!(headers[0].name, "Authorization");
5799 assert_eq!(headers[0].value, "Bearer token123");
5800 assert_eq!(headers[1].name, "Content-Type");
5801 assert_eq!(headers[1].value, "application/json");
5802 }
5803 _ => panic!("Expected Http variant"),
5804 }
5805 }
5806
5807 #[cfg(feature = "schemars")]
5808 #[test]
5809 fn mcp_server_http_schema_marks_url_as_uri() {
5810 let schema = serde_json::to_value(schemars::schema_for!(McpServerHttp)).unwrap();
5811
5812 assert_eq!(schema["properties"]["url"]["format"], "uri");
5813 }
5814
5815 #[cfg(feature = "unstable_mcp_over_acp")]
5816 #[test]
5817 fn test_client_mcp_message_method_names() {
5818 use serde_json::json;
5819
5820 assert_eq!(AGENT_METHOD_NAMES.mcp_message, "mcp/message");
5821
5822 let notification =
5823 MessageMcpNotification::new("server-1", "req-1", "notifications/progress");
5824 assert_eq!(
5825 ClientNotification::MessageMcpNotification(Box::new(notification.clone())).method(),
5826 "mcp/message"
5827 );
5828 assert_eq!(
5829 serde_json::to_value(notification).unwrap(),
5830 json!({
5831 "serverId": "server-1",
5832 "requestId": "req-1",
5833 "method": "notifications/progress"
5834 })
5835 );
5836 let notification: MessageMcpNotification = serde_json::from_value(json!({
5837 "serverId": "server-1", "requestId": "req-1", "method": "notifications/progress",
5838 "params": null, "_meta": null
5839 }))
5840 .unwrap();
5841 assert_eq!(notification.params, None);
5842 assert_eq!(notification.meta, None);
5843 for key in ["serverId", "requestId", "method"] {
5844 let mut value = json!({"serverId":"server-1", "requestId":"req-1", "method":"notifications/progress"});
5845 value.as_object_mut().unwrap().remove(key);
5846 assert!(serde_json::from_value::<MessageMcpNotification>(value).is_err());
5847 }
5848 }
5849
5850 #[test]
5851 fn test_auth_method_names() {
5852 assert_eq!(AGENT_METHOD_NAMES.auth_login, "auth/login");
5853 assert_eq!(AGENT_METHOD_NAMES.auth_logout, "auth/logout");
5854
5855 assert_eq!(
5856 ClientRequest::LoginAuthRequest(Box::new(LoginAuthRequest::new("agent-login")))
5857 .method(),
5858 "auth/login"
5859 );
5860 assert_eq!(
5861 ClientRequest::LogoutAuthRequest(Box::new(LogoutAuthRequest::new())).method(),
5862 "auth/logout"
5863 );
5864 }
5865
5866 #[test]
5867 fn test_session_config_option_category_known_variants() {
5868 assert_eq!(
5870 serde_json::to_value(&SessionConfigOptionCategory::Mode).unwrap(),
5871 json!("mode")
5872 );
5873 assert_eq!(
5874 serde_json::to_value(&SessionConfigOptionCategory::Model).unwrap(),
5875 json!("model")
5876 );
5877 assert_eq!(
5878 serde_json::to_value(&SessionConfigOptionCategory::ModelConfig).unwrap(),
5879 json!("model_config")
5880 );
5881 assert_eq!(
5882 serde_json::to_value(&SessionConfigOptionCategory::ThoughtLevel).unwrap(),
5883 json!("thought_level")
5884 );
5885
5886 assert_eq!(
5888 serde_json::from_str::<SessionConfigOptionCategory>("\"mode\"").unwrap(),
5889 SessionConfigOptionCategory::Mode
5890 );
5891 assert_eq!(
5892 serde_json::from_str::<SessionConfigOptionCategory>("\"model\"").unwrap(),
5893 SessionConfigOptionCategory::Model
5894 );
5895 assert_eq!(
5896 serde_json::from_str::<SessionConfigOptionCategory>("\"model_config\"").unwrap(),
5897 SessionConfigOptionCategory::ModelConfig
5898 );
5899 assert_eq!(
5900 serde_json::from_str::<SessionConfigOptionCategory>("\"thought_level\"").unwrap(),
5901 SessionConfigOptionCategory::ThoughtLevel
5902 );
5903 }
5904
5905 #[test]
5906 fn test_session_config_option_category_unknown_variants() {
5907 let unknown: SessionConfigOptionCategory =
5909 serde_json::from_str("\"some_future_category\"").unwrap();
5910 assert_eq!(
5911 unknown,
5912 SessionConfigOptionCategory::Other("some_future_category".to_string())
5913 );
5914
5915 let json = serde_json::to_value(&unknown).unwrap();
5917 assert_eq!(json, json!("some_future_category"));
5918 }
5919
5920 #[test]
5921 fn test_session_config_option_category_custom_categories() {
5922 let custom: SessionConfigOptionCategory =
5924 serde_json::from_str("\"_my_custom_category\"").unwrap();
5925 assert_eq!(
5926 custom,
5927 SessionConfigOptionCategory::Other("_my_custom_category".to_string())
5928 );
5929
5930 let json = serde_json::to_value(&custom).unwrap();
5932 assert_eq!(json, json!("_my_custom_category"));
5933
5934 let deserialized: SessionConfigOptionCategory = serde_json::from_value(json).unwrap();
5936 assert_eq!(
5937 deserialized,
5938 SessionConfigOptionCategory::Other("_my_custom_category".to_string()),
5939 );
5940 }
5941
5942 fn test_config_option() -> SessionConfigOption {
5943 SessionConfigOption::select(
5944 "mode",
5945 "Mode",
5946 "ask",
5947 vec![SessionConfigSelectOption::new("ask", "Ask")],
5948 )
5949 }
5950
5951 #[test]
5952 fn test_session_response_initial_state_default_empty_and_skip_serializing() {
5953 assert_eq!(
5954 serde_json::to_value(NewSessionResponse::new("sess")).unwrap(),
5955 json!({ "sessionId": "sess" })
5956 );
5957 assert_eq!(
5958 serde_json::to_value(ResumeSessionResponse::new()).unwrap(),
5959 json!({})
5960 );
5961 #[cfg(feature = "unstable_session_fork")]
5962 assert_eq!(
5963 serde_json::to_value(ForkSessionResponse::new("fork")).unwrap(),
5964 json!({ "sessionId": "fork" })
5965 );
5966
5967 let json = serde_json::to_value(
5968 NewSessionResponse::new("sess").config_options(vec![test_config_option()]),
5969 )
5970 .unwrap();
5971 assert_eq!(json["configOptions"].as_array().unwrap().len(), 1);
5972 }
5973
5974 #[test]
5975 fn test_session_response_config_options_deserialize_missing_null_and_invalid() {
5976 let missing: NewSessionResponse =
5977 serde_json::from_value(json!({ "sessionId": "sess" })).unwrap();
5978 assert!(missing.config_options.is_empty());
5979
5980 let null: NewSessionResponse = serde_json::from_value(json!({
5981 "sessionId": "sess",
5982 "configOptions": null
5983 }))
5984 .unwrap();
5985 assert!(null.config_options.is_empty());
5986
5987 let wrong_shape: NewSessionResponse = serde_json::from_value(json!({
5988 "sessionId": "sess",
5989 "configOptions": "oops"
5990 }))
5991 .unwrap();
5992 assert!(wrong_shape.config_options.is_empty());
5993
5994 let valid_option = serde_json::to_value(test_config_option()).unwrap();
5995 let mixed: NewSessionResponse = serde_json::from_value(json!({
5996 "sessionId": "sess",
5997 "configOptions": ["oops", valid_option]
5998 }))
5999 .unwrap();
6000 assert_eq!(mixed.config_options.len(), 1);
6001
6002 let resume: ResumeSessionResponse = serde_json::from_value(json!({})).unwrap();
6003 assert!(resume.config_options.is_empty());
6004 #[cfg(feature = "unstable_session_fork")]
6005 {
6006 let fork: ForkSessionResponse =
6007 serde_json::from_value(json!({ "sessionId": "fork" })).unwrap();
6008 assert!(fork.config_options.is_empty());
6009 }
6010 }
6011
6012 #[test]
6013 fn test_session_response_available_commands_roundtrip_with_config_options() {
6014 use super::super::{AvailableCommandInput, TextCommandInput};
6015
6016 let commands = vec![
6017 AvailableCommand::new("web", "Search the web")
6018 .input(AvailableCommandInput::Text(TextCommandInput::new("Query"))),
6019 AvailableCommand::new("test", "Run project tests"),
6020 ];
6021 let mut expected = json!({
6022 "sessionId": "sess",
6023 "configOptions": [test_config_option()],
6024 "availableCommands": [
6025 {
6026 "name": "web",
6027 "description": "Search the web",
6028 "input": { "type": "text", "hint": "Query" }
6029 },
6030 { "name": "test", "description": "Run project tests" }
6031 ]
6032 });
6033
6034 let new = NewSessionResponse::new("sess")
6035 .config_options(vec![test_config_option()])
6036 .available_commands(commands.clone());
6037 assert_eq!(serde_json::to_value(&new).unwrap(), expected);
6038 assert_eq!(
6039 serde_json::from_value::<NewSessionResponse>(expected.clone()).unwrap(),
6040 new
6041 );
6042
6043 expected.as_object_mut().unwrap().remove("sessionId");
6044 let resume = ResumeSessionResponse::new()
6045 .config_options(vec![test_config_option()])
6046 .available_commands(commands.clone());
6047 assert_eq!(serde_json::to_value(&resume).unwrap(), expected);
6048 assert_eq!(
6049 serde_json::from_value::<ResumeSessionResponse>(expected.clone()).unwrap(),
6050 resume
6051 );
6052
6053 #[cfg(feature = "unstable_session_fork")]
6054 {
6055 expected["sessionId"] = json!("fork");
6056 let fork = ForkSessionResponse::new("fork")
6057 .config_options(vec![test_config_option()])
6058 .available_commands(commands);
6059 assert_eq!(serde_json::to_value(&fork).unwrap(), expected);
6060 assert_eq!(
6061 serde_json::from_value::<ForkSessionResponse>(expected).unwrap(),
6062 fork
6063 );
6064 }
6065 }
6066
6067 #[test]
6068 fn test_session_response_available_commands_deserialize_missing_null_and_invalid() {
6069 let command = AvailableCommand::new("test", "Run project tests");
6070 let config_options = vec![test_config_option()];
6071
6072 for (commands, expected) in [
6073 (None, vec![]),
6074 (Some(json!(null)), vec![]),
6075 (Some(json!([])), vec![]),
6076 (Some(json!("oops")), vec![]),
6077 (Some(json!({})), vec![]),
6078 (Some(json!([null, {}, { "name": "invalid" }])), vec![]),
6079 (
6080 Some(json!([null, command, { "name": "invalid" }])),
6081 vec![command.clone()],
6082 ),
6083 ] {
6084 let mut value = json!({
6085 "sessionId": "sess",
6086 "configOptions": config_options
6087 });
6088 if let Some(commands) = commands {
6089 value["availableCommands"] = commands;
6090 }
6091
6092 let new: NewSessionResponse = serde_json::from_value(value.clone()).unwrap();
6093 assert_eq!(new.available_commands, expected);
6094 assert_eq!(new.config_options, config_options);
6095
6096 #[cfg(feature = "unstable_session_fork")]
6097 {
6098 let fork: ForkSessionResponse = serde_json::from_value(value.clone()).unwrap();
6099 assert_eq!(fork.available_commands, expected);
6100 assert_eq!(fork.config_options, config_options);
6101 }
6102
6103 value.as_object_mut().unwrap().remove("sessionId");
6104 let resume: ResumeSessionResponse = serde_json::from_value(value).unwrap();
6105 assert_eq!(resume.available_commands, expected);
6106 assert_eq!(resume.config_options, config_options);
6107 }
6108
6109 let resume: ResumeSessionResponse = serde_json::from_value(json!(null)).unwrap();
6110 assert!(resume.available_commands.is_empty());
6111 }
6112
6113 #[test]
6114 fn test_resume_session_replay_from_serialization() {
6115 assert_eq!(
6116 serde_json::to_value(ResumeSessionRequest::new(
6117 "sess_abc123",
6118 "/home/user/project"
6119 ))
6120 .unwrap(),
6121 json!({
6122 "sessionId": "sess_abc123",
6123 "cwd": "/home/user/project"
6124 })
6125 );
6126 assert_eq!(
6127 serde_json::to_value(
6128 ResumeSessionRequest::new("sess_abc123", "/home/user/project")
6129 .replay_from(ReplayFrom::from(ReplayFromStart::new()))
6130 )
6131 .unwrap(),
6132 json!({
6133 "sessionId": "sess_abc123",
6134 "cwd": "/home/user/project",
6135 "replayFrom": {
6136 "type": "start"
6137 }
6138 })
6139 );
6140
6141 let replay: ResumeSessionRequest = serde_json::from_value(json!({
6142 "sessionId": "sess_abc123",
6143 "cwd": "/home/user/project",
6144 "replayFrom": {
6145 "type": "start"
6146 }
6147 }))
6148 .unwrap();
6149 assert!(matches!(replay.replay_from, Some(ReplayFrom::Start(_))));
6150
6151 let none: ResumeSessionRequest = serde_json::from_value(json!({
6152 "sessionId": "sess_abc123",
6153 "cwd": "/home/user/project",
6154 "replayFrom": null
6155 }))
6156 .unwrap();
6157 assert!(none.replay_from.is_none());
6158 }
6159
6160 #[test]
6161 fn test_auth_method_agent_serialization() {
6162 let method = AuthMethod::Agent(AuthMethodAgent::new("default-auth", "Default Auth"));
6163
6164 let json = serde_json::to_value(&method).unwrap();
6165 assert_eq!(
6166 json,
6167 json!({
6168 "methodId": "default-auth",
6169 "name": "Default Auth",
6170 "type": "agent"
6171 })
6172 );
6173 assert!(!json.as_object().unwrap().contains_key("description"));
6175
6176 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
6177 match deserialized {
6178 AuthMethod::Agent(AuthMethodAgent {
6179 method_id, name, ..
6180 }) => {
6181 assert_eq!(method_id.0.as_ref(), "default-auth");
6182 assert_eq!(name, "Default Auth");
6183 }
6184 _ => panic!("Expected Agent variant"),
6185 }
6186 }
6187
6188 #[test]
6189 fn test_auth_method_agent_deserialization() {
6190 let json = json!({
6191 "methodId": "agent-auth",
6192 "name": "Agent Auth",
6193 "type": "agent"
6194 });
6195
6196 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
6197 assert!(matches!(deserialized, AuthMethod::Agent(_)));
6198 }
6199
6200 #[test]
6201 fn test_auth_method_agent_requires_type() {
6202 assert!(
6203 serde_json::from_value::<AuthMethod>(json!({
6204 "methodId": "agent-auth",
6205 "name": "Agent Auth"
6206 }))
6207 .is_err()
6208 );
6209 }
6210
6211 #[test]
6212 fn test_auth_method_agent_rejects_null_type() {
6213 assert!(
6214 serde_json::from_value::<AuthMethod>(json!({
6215 "methodId": "agent-auth",
6216 "name": "Agent Auth",
6217 "type": null
6218 }))
6219 .is_err()
6220 );
6221 }
6222
6223 #[test]
6224 fn test_auth_method_unknown_does_not_hide_malformed_agent() {
6225 assert!(
6226 serde_json::from_value::<AuthMethod>(json!({
6227 "methodId": "agent-auth",
6228 "type": "agent"
6229 }))
6230 .is_err()
6231 );
6232 assert!(
6233 serde_json::from_value::<AuthMethod>(json!({
6234 "methodId": "api-key",
6235 "type": "env_var",
6236 "vars": [{"name": "API_KEY"}]
6237 }))
6238 .is_err()
6239 );
6240 }
6241
6242 #[test]
6243 fn test_auth_method_unknown_variant_roundtrip() {
6244 let method: AuthMethod = serde_json::from_value(json!({
6245 "methodId": "oauth",
6246 "name": "OAuth",
6247 "type": "_oauth",
6248 "authorizationUrl": "https://example.com/auth"
6249 }))
6250 .unwrap();
6251
6252 assert_eq!(method.method_id().0.as_ref(), "oauth");
6253 assert_eq!(method.name(), "OAuth");
6254 let AuthMethod::Other(unknown) = method else {
6255 panic!("expected unknown auth method");
6256 };
6257 assert_eq!(unknown.type_, "_oauth");
6258 assert_eq!(
6259 unknown.fields.get("authorizationUrl"),
6260 Some(&json!("https://example.com/auth"))
6261 );
6262
6263 assert_eq!(
6264 serde_json::to_value(AuthMethod::Other(unknown)).unwrap(),
6265 json!({
6266 "methodId": "oauth",
6267 "name": "OAuth",
6268 "type": "_oauth",
6269 "authorizationUrl": "https://example.com/auth"
6270 })
6271 );
6272 }
6273
6274 #[test]
6275 fn test_auth_method_unknown_does_not_hide_malformed_known_variant() {
6276 assert!(
6277 serde_json::from_value::<AuthMethod>(json!({
6278 "methodId": "terminal-auth",
6279 "type": "terminal"
6280 }))
6281 .is_err()
6282 );
6283 }
6284
6285 #[test]
6286 fn test_session_delete_serialization() {
6287 assert_eq!(AGENT_METHOD_NAMES.session_delete, "session/delete");
6288 assert_eq!(
6289 ClientRequest::DeleteSessionRequest(Box::new(DeleteSessionRequest::new("sess_abc123")))
6290 .method(),
6291 "session/delete"
6292 );
6293 assert_eq!(
6294 serde_json::to_value(DeleteSessionRequest::new("sess_abc123")).unwrap(),
6295 json!({
6296 "sessionId": "sess_abc123"
6297 })
6298 );
6299 assert_eq!(
6300 serde_json::to_value(DeleteSessionResponse::new()).unwrap(),
6301 json!({})
6302 );
6303 assert_eq!(
6304 serde_json::to_value(
6305 SessionCapabilities::new().delete(SessionDeleteCapabilities::new())
6306 )
6307 .unwrap(),
6308 json!({
6309 "delete": {}
6310 })
6311 );
6312 }
6313 #[test]
6314 fn test_session_additional_directories_serialization() {
6315 assert_eq!(
6316 serde_json::to_value(NewSessionRequest::new("/home/user/project")).unwrap(),
6317 json!({
6318 "cwd": "/home/user/project",
6319 })
6320 );
6321 assert_eq!(
6322 serde_json::to_value(
6323 NewSessionRequest::new("/home/user/project").additional_directories(vec![
6324 PathBuf::from("/home/user/shared-lib"),
6325 PathBuf::from("/home/user/product-docs"),
6326 ])
6327 )
6328 .unwrap(),
6329 json!({
6330 "cwd": "/home/user/project",
6331 "additionalDirectories": [
6332 "/home/user/shared-lib",
6333 "/home/user/product-docs"
6334 ],
6335 })
6336 );
6337 assert_eq!(
6338 serde_json::to_value(ResumeSessionRequest::new(
6339 "sess_abc123",
6340 "/home/user/project"
6341 ))
6342 .unwrap(),
6343 json!({
6344 "sessionId": "sess_abc123",
6345 "cwd": "/home/user/project",
6346 })
6347 );
6348 assert_eq!(
6349 serde_json::from_value::<ResumeSessionRequest>(json!({
6350 "sessionId": "sess_abc123",
6351 "cwd": "/home/user/project"
6352 }))
6353 .unwrap()
6354 .mcp_servers,
6355 Vec::<McpServer>::new()
6356 );
6357 assert_eq!(
6358 serde_json::from_value::<ResumeSessionRequest>(json!({
6359 "sessionId": "sess_abc123",
6360 "cwd": "/home/user/project",
6361 "mcpServers": null
6362 }))
6363 .unwrap()
6364 .mcp_servers,
6365 Vec::<McpServer>::new()
6366 );
6367 assert_eq!(
6368 serde_json::to_value(SessionInfo::new("sess_abc123", "/home/user/project")).unwrap(),
6369 json!({
6370 "sessionId": "sess_abc123",
6371 "cwd": "/home/user/project"
6372 })
6373 );
6374 assert_eq!(
6375 serde_json::to_value(
6376 SessionInfo::new("sess_abc123", "/home/user/project").additional_directories(vec![
6377 PathBuf::from("/home/user/shared-lib"),
6378 PathBuf::from("/home/user/product-docs"),
6379 ])
6380 )
6381 .unwrap(),
6382 json!({
6383 "sessionId": "sess_abc123",
6384 "cwd": "/home/user/project",
6385 "additionalDirectories": [
6386 "/home/user/shared-lib",
6387 "/home/user/product-docs"
6388 ]
6389 })
6390 );
6391 assert_eq!(
6392 serde_json::from_value::<SessionInfo>(json!({
6393 "sessionId": "sess_abc123",
6394 "cwd": "/home/user/project"
6395 }))
6396 .unwrap()
6397 .additional_directories,
6398 Vec::<AbsolutePath>::new()
6399 );
6400 }
6401 #[test]
6402 fn test_session_additional_directories_capabilities_serialization() {
6403 assert_eq!(
6404 serde_json::to_value(
6405 SessionCapabilities::new()
6406 .additional_directories(SessionAdditionalDirectoriesCapabilities::new())
6407 )
6408 .unwrap(),
6409 json!({
6410 "additionalDirectories": {}
6411 })
6412 );
6413 }
6414
6415 #[test]
6416 fn test_auth_method_terminal_serialization() {
6417 let method = AuthMethod::Terminal(AuthMethodTerminal::new("tui-auth", "Terminal Auth"));
6418
6419 let json = serde_json::to_value(&method).unwrap();
6420 assert_eq!(
6421 json,
6422 json!({
6423 "methodId": "tui-auth",
6424 "name": "Terminal Auth",
6425 "type": "terminal"
6426 })
6427 );
6428 assert!(!json.as_object().unwrap().contains_key("args"));
6430 assert!(!json.as_object().unwrap().contains_key("env"));
6431
6432 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
6433 match deserialized {
6434 AuthMethod::Terminal(AuthMethodTerminal { args, env, .. }) => {
6435 assert!(args.is_empty());
6436 assert!(env.is_empty());
6437 }
6438 _ => panic!("Expected Terminal variant"),
6439 }
6440 }
6441
6442 #[test]
6443 fn test_auth_method_terminal_with_args_and_env_serialization() {
6444 let method = AuthMethod::Terminal(
6445 AuthMethodTerminal::new("tui-auth", "Terminal Auth")
6446 .args(vec!["--interactive".to_string(), "--color".to_string()])
6447 .env(vec![EnvVariable::new("TERM", "xterm-256color")]),
6448 );
6449
6450 let json = serde_json::to_value(&method).unwrap();
6451 assert_eq!(
6452 json,
6453 json!({
6454 "methodId": "tui-auth",
6455 "name": "Terminal Auth",
6456 "type": "terminal",
6457 "args": ["--interactive", "--color"],
6458 "env": [
6459 {
6460 "name": "TERM",
6461 "value": "xterm-256color"
6462 }
6463 ]
6464 })
6465 );
6466
6467 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
6468 match deserialized {
6469 AuthMethod::Terminal(AuthMethodTerminal { args, env, .. }) => {
6470 assert_eq!(args, vec!["--interactive", "--color"]);
6471 assert_eq!(env.len(), 1);
6472 assert_eq!(env[0].name, "TERM");
6473 assert_eq!(env[0].value, "xterm-256color");
6474 }
6475 _ => panic!("Expected Terminal variant"),
6476 }
6477 }
6478
6479 #[test]
6480 fn test_session_config_option_id_serialize() {
6481 let val = SessionConfigOptionValue::id("model-1");
6482 let json = serde_json::to_value(&val).unwrap();
6483 assert_eq!(json, json!({ "type": "id", "value": "model-1" }));
6484 }
6485
6486 #[test]
6487 fn test_session_config_option_value_boolean_serialize() {
6488 let val = SessionConfigOptionValue::boolean(true);
6489 let json = serde_json::to_value(&val).unwrap();
6490 assert_eq!(json, json!({ "type": "boolean", "value": true }));
6491 }
6492
6493 #[test]
6494 fn test_session_config_option_value_deserialize_id() {
6495 let json = json!({ "type": "id", "value": "model-1" });
6496 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6497 assert_eq!(val, SessionConfigOptionValue::id("model-1"));
6498 assert_eq!(val.as_id().unwrap().to_string(), "model-1");
6499 }
6500
6501 #[test]
6502 fn test_session_config_option_value_deserialize_requires_type() {
6503 let json = json!({ "value": "model-1" });
6504 let result = serde_json::from_value::<SessionConfigOptionValue>(json);
6505 assert!(result.is_err());
6506 }
6507
6508 #[test]
6509 fn test_session_config_option_value_deserialize_boolean() {
6510 let json = json!({ "type": "boolean", "value": true });
6511 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6512 assert_eq!(val, SessionConfigOptionValue::boolean(true));
6513 assert_eq!(val.as_bool(), Some(true));
6514 }
6515
6516 #[test]
6517 fn test_session_config_option_value_deserialize_boolean_false() {
6518 let json = json!({ "type": "boolean", "value": false });
6519 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6520 assert_eq!(val, SessionConfigOptionValue::boolean(false));
6521 assert_eq!(val.as_bool(), Some(false));
6522 }
6523
6524 #[test]
6525 fn test_session_config_option_value_deserialize_unknown_type_with_string_value() {
6526 let json = json!({
6527 "type": "text",
6528 "value": "freeform input",
6529 "maxLength": 200
6530 });
6531 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6532 let SessionConfigOptionValue::Other(unknown) = val else {
6533 panic!("Expected Other variant");
6534 };
6535 assert_eq!(unknown.type_, "text");
6536 assert_eq!(unknown.value, json!("freeform input"));
6537 assert_eq!(unknown.fields["maxLength"], json!(200));
6538 }
6539
6540 #[test]
6541 fn test_session_config_option_value_deserialize_unknown_type_with_object_value() {
6542 let json = json!({
6543 "type": "range",
6544 "value": { "min": 1, "max": 5 }
6545 });
6546 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6547 let SessionConfigOptionValue::Other(unknown) = val else {
6548 panic!("Expected Other variant");
6549 };
6550 assert_eq!(unknown.type_, "range");
6551 assert_eq!(unknown.value, json!({ "min": 1, "max": 5 }));
6552 }
6553
6554 #[test]
6555 fn test_session_config_option_value_roundtrip_id() {
6556 let original = SessionConfigOptionValue::id("option-a");
6557 let json = serde_json::to_value(&original).unwrap();
6558 let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6559 assert_eq!(original, roundtripped);
6560 }
6561
6562 #[test]
6563 fn test_session_config_option_value_roundtrip_boolean() {
6564 let original = SessionConfigOptionValue::boolean(false);
6565 let json = serde_json::to_value(&original).unwrap();
6566 let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6567 assert_eq!(original, roundtripped);
6568 }
6569
6570 #[test]
6571 fn test_session_config_option_value_roundtrip_other() {
6572 let mut fields = BTreeMap::new();
6573 fields.insert("maxLength".to_string(), json!(200));
6574 let original = SessionConfigOptionValue::Other(OtherSessionConfigOptionValue::new(
6575 "text",
6576 json!("freeform input"),
6577 fields,
6578 ));
6579 let json = serde_json::to_value(&original).unwrap();
6580 let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6581 assert_eq!(original, roundtripped);
6582 }
6583
6584 #[test]
6585 fn test_session_config_option_value_type_mismatch_boolean_with_string() {
6586 let json = json!({ "type": "boolean", "value": "not a bool" });
6587 let result = serde_json::from_value::<SessionConfigOptionValue>(json);
6588 assert!(result.is_err());
6589 }
6590
6591 #[test]
6592 fn test_session_config_option_value_from_impls() {
6593 let from_str: SessionConfigOptionValue = "model-1".into();
6594 assert_eq!(from_str.as_id().unwrap().to_string(), "model-1");
6595
6596 let from_id: SessionConfigOptionValue = SessionConfigValueId::new("model-2").into();
6597 assert_eq!(from_id.as_id().unwrap().to_string(), "model-2");
6598
6599 let from_bool: SessionConfigOptionValue = true.into();
6600 assert_eq!(from_bool.as_bool(), Some(true));
6601 }
6602
6603 #[test]
6604 fn test_set_session_config_option_request_id() {
6605 let req = SetSessionConfigOptionRequest::new("sess_1", "model", "model-1");
6606 let json = serde_json::to_value(&req).unwrap();
6607 assert_eq!(
6608 json,
6609 json!({
6610 "sessionId": "sess_1",
6611 "configId": "model",
6612 "type": "id",
6613 "value": "model-1"
6614 })
6615 );
6616 }
6617
6618 #[test]
6619 fn test_set_session_config_option_request_boolean() {
6620 let req = SetSessionConfigOptionRequest::new("sess_1", "brave_mode", true);
6621 let json = serde_json::to_value(&req).unwrap();
6622 assert_eq!(
6623 json,
6624 json!({
6625 "sessionId": "sess_1",
6626 "configId": "brave_mode",
6627 "type": "boolean",
6628 "value": true
6629 })
6630 );
6631 }
6632
6633 #[test]
6634 fn test_set_session_config_option_request_deserialize_requires_type() {
6635 let json = json!({
6636 "sessionId": "sess_1",
6637 "configId": "model",
6638 "value": "model-1"
6639 });
6640 let result = serde_json::from_value::<SetSessionConfigOptionRequest>(json);
6641 assert!(result.is_err());
6642 }
6643
6644 #[test]
6645 fn test_set_session_config_option_request_deserialize_boolean() {
6646 let json = json!({
6647 "sessionId": "sess_1",
6648 "configId": "brave_mode",
6649 "type": "boolean",
6650 "value": true
6651 });
6652 let req: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
6653 assert_eq!(req.value.as_bool(), Some(true));
6654 }
6655
6656 #[test]
6657 fn test_set_session_config_option_request_roundtrip_id() {
6658 let original = SetSessionConfigOptionRequest::new("s", "c", "v");
6659 let json = serde_json::to_value(&original).unwrap();
6660 let roundtripped: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
6661 assert_eq!(original, roundtripped);
6662 }
6663
6664 #[test]
6665 fn test_set_session_config_option_request_roundtrip_boolean() {
6666 let original = SetSessionConfigOptionRequest::new("s", "c", false);
6667 let json = serde_json::to_value(&original).unwrap();
6668 let roundtripped: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
6669 assert_eq!(original, roundtripped);
6670 }
6671
6672 #[test]
6673 fn test_session_config_boolean_serialization() {
6674 let cfg = SessionConfigBoolean::new(true);
6675 let json = serde_json::to_value(&cfg).unwrap();
6676 assert_eq!(json, json!({ "currentValue": true }));
6677
6678 let deserialized: SessionConfigBoolean = serde_json::from_value(json).unwrap();
6679 assert!(deserialized.current_value);
6680 }
6681
6682 #[test]
6683 fn test_session_config_option_boolean_variant() {
6684 let opt = SessionConfigOption::boolean("brave_mode", "Brave Mode", false)
6685 .description("Skip confirmation prompts")
6686 .meta(test_meta());
6687 assert_eq!(serialized_meta_key_count(&opt), 1);
6688
6689 let json = serde_json::to_value(&opt).unwrap();
6690 assert_eq!(
6691 json,
6692 json!({
6693 "configId": "brave_mode",
6694 "name": "Brave Mode",
6695 "description": "Skip confirmation prompts",
6696 "type": "boolean",
6697 "currentValue": false,
6698 "_meta": {
6699 "source": "test"
6700 }
6701 })
6702 );
6703
6704 let deserialized: SessionConfigOption = serde_json::from_value(json).unwrap();
6705 assert_eq!(deserialized.config_id.to_string(), "brave_mode");
6706 assert_eq!(deserialized.name, "Brave Mode");
6707 match deserialized.kind {
6708 SessionConfigKind::Boolean(ref b) => assert!(!b.current_value),
6709 _ => panic!("Expected Boolean kind"),
6710 }
6711 }
6712
6713 #[test]
6714 fn test_session_config_option_select_still_works() {
6715 let opt = SessionConfigOption::select(
6717 "model",
6718 "Model",
6719 "model-1",
6720 vec![
6721 SessionConfigSelectOption::new("model-1", "Model 1"),
6722 SessionConfigSelectOption::new("model-2", "Model 2"),
6723 ],
6724 )
6725 .meta(test_meta());
6726 assert_eq!(serialized_meta_key_count(&opt), 1);
6727
6728 let json = serde_json::to_value(&opt).unwrap();
6729 assert_eq!(json["type"], "select");
6730 assert_eq!(json["currentValue"], "model-1");
6731 assert_eq!(json["options"].as_array().unwrap().len(), 2);
6732 assert_eq!(json["_meta"]["source"], "test");
6733
6734 let deserialized: SessionConfigOption = serde_json::from_value(json).unwrap();
6735 match deserialized.kind {
6736 SessionConfigKind::Select(ref s) => {
6737 assert_eq!(s.current_value.to_string(), "model-1");
6738 }
6739 _ => panic!("Expected Select kind"),
6740 }
6741 }
6742
6743 #[test]
6744 fn test_session_config_option_unknown_kind_roundtrip() {
6745 let option: SessionConfigOption = serde_json::from_value(json!({
6746 "configId": "verbosity",
6747 "name": "Verbosity",
6748 "type": "_slider",
6749 "currentValue": 3,
6750 "min": 0,
6751 "max": 5,
6752 "_meta": {
6753 "source": "test"
6754 }
6755 }))
6756 .unwrap();
6757
6758 assert_eq!(option.config_id.to_string(), "verbosity");
6759 assert_eq!(option.meta.as_ref().unwrap()["source"], "test");
6760 let SessionConfigKind::Other(unknown) = &option.kind else {
6761 panic!("expected unknown config kind");
6762 };
6763 assert_eq!(unknown.type_, "_slider");
6764 assert_eq!(unknown.fields.get("currentValue"), Some(&json!(3)));
6765 assert!(!unknown.fields.contains_key("_meta"));
6766 assert_eq!(serialized_meta_key_count(&option), 1);
6767
6768 let json = serde_json::to_value(&option).unwrap();
6769 assert_eq!(json["type"], "_slider");
6770 assert_eq!(json["currentValue"], 3);
6771 assert_eq!(json["min"], 0);
6772 assert_eq!(json["max"], 5);
6773 assert_eq!(json["_meta"]["source"], "test");
6774 }
6775
6776 #[test]
6777 fn test_session_config_option_unknown_kind_does_not_duplicate_flattened_meta() {
6778 let mut fields = std::collections::BTreeMap::new();
6779 fields.insert("currentValue".to_string(), json!(3));
6780 fields.insert("_meta".to_string(), json!({ "inner": "ignored" }));
6781
6782 let option = SessionConfigOption::new(
6783 "verbosity",
6784 "Verbosity",
6785 SessionConfigKind::Other(OtherSessionConfigKind::new("_slider", fields)),
6786 )
6787 .meta(test_meta());
6788
6789 let SessionConfigKind::Other(unknown) = &option.kind else {
6790 panic!("expected unknown config kind");
6791 };
6792 assert!(!unknown.fields.contains_key("_meta"));
6793 assert_eq!(serialized_meta_key_count(&option), 1);
6794
6795 let json = serde_json::to_value(&option).unwrap();
6796 assert_eq!(json["type"], "_slider");
6797 assert_eq!(json["currentValue"], 3);
6798 assert_eq!(json["_meta"]["source"], "test");
6799 }
6800
6801 #[test]
6802 fn test_session_config_option_unknown_does_not_hide_malformed_known_kind() {
6803 assert!(
6804 serde_json::from_value::<SessionConfigOption>(json!({
6805 "configId": "model",
6806 "name": "Model",
6807 "type": "select"
6808 }))
6809 .is_err()
6810 );
6811 }
6812
6813 #[cfg(feature = "unstable_llm_providers")]
6814 #[test]
6815 fn test_llm_protocol_known_variants() {
6816 assert_eq!(
6817 serde_json::to_value(&LlmProtocol::Anthropic).unwrap(),
6818 json!("anthropic")
6819 );
6820 assert_eq!(
6821 serde_json::to_value(&LlmProtocol::OpenAi).unwrap(),
6822 json!("openai")
6823 );
6824 assert_eq!(
6825 serde_json::to_value(&LlmProtocol::Azure).unwrap(),
6826 json!("azure")
6827 );
6828 assert_eq!(
6829 serde_json::to_value(&LlmProtocol::Vertex).unwrap(),
6830 json!("vertex")
6831 );
6832 assert_eq!(
6833 serde_json::to_value(&LlmProtocol::Bedrock).unwrap(),
6834 json!("bedrock")
6835 );
6836
6837 assert_eq!(
6838 serde_json::from_str::<LlmProtocol>("\"anthropic\"").unwrap(),
6839 LlmProtocol::Anthropic
6840 );
6841 assert_eq!(
6842 serde_json::from_str::<LlmProtocol>("\"openai\"").unwrap(),
6843 LlmProtocol::OpenAi
6844 );
6845 assert_eq!(
6846 serde_json::from_str::<LlmProtocol>("\"azure\"").unwrap(),
6847 LlmProtocol::Azure
6848 );
6849 assert_eq!(
6850 serde_json::from_str::<LlmProtocol>("\"vertex\"").unwrap(),
6851 LlmProtocol::Vertex
6852 );
6853 assert_eq!(
6854 serde_json::from_str::<LlmProtocol>("\"bedrock\"").unwrap(),
6855 LlmProtocol::Bedrock
6856 );
6857 }
6858
6859 #[cfg(feature = "unstable_llm_providers")]
6860 #[test]
6861 fn test_llm_protocol_unknown_variant() {
6862 let unknown: LlmProtocol = serde_json::from_str("\"cohere\"").unwrap();
6863 assert_eq!(unknown, LlmProtocol::Other("cohere".to_string()));
6864
6865 let json = serde_json::to_value(&unknown).unwrap();
6866 assert_eq!(json, json!("cohere"));
6867 }
6868
6869 #[cfg(feature = "unstable_llm_providers")]
6870 #[test]
6871 fn test_provider_current_config_serialization() {
6872 let config =
6873 ProviderCurrentConfig::new(LlmProtocol::Anthropic, "https://api.anthropic.com");
6874
6875 let json = serde_json::to_value(&config).unwrap();
6876 assert_eq!(
6877 json,
6878 json!({
6879 "apiType": "anthropic",
6880 "baseUrl": "https://api.anthropic.com"
6881 })
6882 );
6883
6884 let deserialized: ProviderCurrentConfig = serde_json::from_value(json).unwrap();
6885 assert_eq!(deserialized.api_type, LlmProtocol::Anthropic);
6886 assert_eq!(deserialized.base_url, "https://api.anthropic.com");
6887 }
6888
6889 #[cfg(feature = "unstable_llm_providers")]
6890 #[test]
6891 fn test_provider_info_with_current_config() {
6892 let info = ProviderInfo::new(
6893 "main",
6894 vec![LlmProtocol::Anthropic, LlmProtocol::OpenAi],
6895 true,
6896 Some(ProviderCurrentConfig::new(
6897 LlmProtocol::Anthropic,
6898 "https://api.anthropic.com",
6899 )),
6900 );
6901
6902 let json = serde_json::to_value(&info).unwrap();
6903 assert_eq!(
6904 json,
6905 json!({
6906 "providerId": "main",
6907 "supported": ["anthropic", "openai"],
6908 "required": true,
6909 "current": {
6910 "apiType": "anthropic",
6911 "baseUrl": "https://api.anthropic.com"
6912 }
6913 })
6914 );
6915
6916 let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
6917 assert_eq!(deserialized.provider_id.to_string(), "main");
6918 assert_eq!(deserialized.supported.len(), 2);
6919 assert!(deserialized.required);
6920 assert!(deserialized.current.is_some());
6921 assert_eq!(
6922 deserialized.current.as_ref().unwrap().api_type,
6923 LlmProtocol::Anthropic
6924 );
6925 }
6926
6927 #[cfg(feature = "unstable_llm_providers")]
6928 #[test]
6929 fn test_provider_info_disabled() {
6930 let info = ProviderInfo::new(
6931 "secondary",
6932 vec![LlmProtocol::OpenAi],
6933 false,
6934 None::<ProviderCurrentConfig>,
6935 );
6936
6937 let json = serde_json::to_value(&info).unwrap();
6938 assert_eq!(
6939 json,
6940 json!({
6941 "providerId": "secondary",
6942 "supported": ["openai"],
6943 "required": false
6944 })
6945 );
6946
6947 let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
6948 assert_eq!(deserialized.provider_id.to_string(), "secondary");
6949 assert!(!deserialized.required);
6950 assert!(deserialized.current.is_none());
6951 }
6952
6953 #[cfg(feature = "unstable_llm_providers")]
6954 #[test]
6955 fn test_provider_info_missing_current_defaults_to_none() {
6956 let json = json!({
6958 "providerId": "main",
6959 "supported": ["anthropic"],
6960 "required": true
6961 });
6962 let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
6963 assert!(deserialized.current.is_none());
6964 }
6965
6966 #[cfg(feature = "unstable_llm_providers")]
6967 #[test]
6968 fn test_provider_info_explicit_null_current_decodes_to_none() {
6969 let json = json!({
6973 "providerId": "main",
6974 "supported": ["anthropic"],
6975 "required": true,
6976 "current": null
6977 });
6978 let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
6979 assert!(deserialized.current.is_none());
6980 }
6981
6982 #[cfg(feature = "unstable_llm_providers")]
6983 #[test]
6984 fn test_list_providers_response_serialization() {
6985 let response = ListProvidersResponse::new(vec![ProviderInfo::new(
6986 "main",
6987 vec![LlmProtocol::Anthropic],
6988 true,
6989 Some(ProviderCurrentConfig::new(
6990 LlmProtocol::Anthropic,
6991 "https://api.anthropic.com",
6992 )),
6993 )]);
6994
6995 let json = serde_json::to_value(&response).unwrap();
6996 assert_eq!(json["providers"].as_array().unwrap().len(), 1);
6997 assert_eq!(json["providers"][0]["providerId"], "main");
6998
6999 let deserialized: ListProvidersResponse = serde_json::from_value(json).unwrap();
7000 assert_eq!(deserialized.providers.len(), 1);
7001 }
7002
7003 #[cfg(feature = "unstable_llm_providers")]
7004 #[test]
7005 fn test_set_provider_request_serialization() {
7006 use std::collections::HashMap;
7007
7008 let mut headers = HashMap::new();
7009 headers.insert("Authorization".to_string(), "Bearer sk-test".to_string());
7010
7011 let request =
7012 SetProviderRequest::new("main", LlmProtocol::OpenAi, "https://api.openai.com/v1")
7013 .headers(headers);
7014
7015 let json = serde_json::to_value(&request).unwrap();
7016 assert_eq!(
7017 json,
7018 json!({
7019 "providerId": "main",
7020 "apiType": "openai",
7021 "baseUrl": "https://api.openai.com/v1",
7022 "headers": {
7023 "Authorization": "Bearer sk-test"
7024 }
7025 })
7026 );
7027
7028 let deserialized: SetProviderRequest = serde_json::from_value(json).unwrap();
7029 assert_eq!(deserialized.provider_id.to_string(), "main");
7030 assert_eq!(deserialized.api_type, LlmProtocol::OpenAi);
7031 assert_eq!(deserialized.base_url, "https://api.openai.com/v1");
7032 assert_eq!(deserialized.headers.len(), 1);
7033 assert_eq!(
7034 deserialized.headers.get("Authorization").unwrap(),
7035 "Bearer sk-test"
7036 );
7037 }
7038
7039 #[cfg(feature = "unstable_llm_providers")]
7040 #[test]
7041 fn test_set_provider_request_omits_empty_headers() {
7042 let request =
7043 SetProviderRequest::new("main", LlmProtocol::Anthropic, "https://api.anthropic.com");
7044
7045 let json = serde_json::to_value(&request).unwrap();
7046 assert!(!json.as_object().unwrap().contains_key("headers"));
7048 }
7049
7050 #[cfg(feature = "unstable_llm_providers")]
7051 #[test]
7052 fn test_disable_provider_request_serialization() {
7053 let request = DisableProviderRequest::new("secondary");
7054
7055 let json = serde_json::to_value(&request).unwrap();
7056 assert_eq!(json, json!({ "providerId": "secondary" }));
7057
7058 let deserialized: DisableProviderRequest = serde_json::from_value(json).unwrap();
7059 assert_eq!(deserialized.provider_id.to_string(), "secondary");
7060 }
7061
7062 #[cfg(feature = "unstable_llm_providers")]
7063 #[test]
7064 fn test_providers_capabilities_serialization() {
7065 let caps = ProvidersCapabilities::new();
7066
7067 let json = serde_json::to_value(&caps).unwrap();
7068 assert_eq!(json, json!({}));
7069
7070 let deserialized: ProvidersCapabilities = serde_json::from_value(json).unwrap();
7071 assert!(deserialized.meta.is_none());
7072 }
7073
7074 #[cfg(feature = "unstable_llm_providers")]
7075 #[test]
7076 fn test_agent_capabilities_with_providers() {
7077 let caps = AgentCapabilities::new().providers(ProvidersCapabilities::new());
7078
7079 let json = serde_json::to_value(&caps).unwrap();
7080 assert_eq!(json["providers"], json!({}));
7081
7082 let deserialized: AgentCapabilities = serde_json::from_value(json).unwrap();
7083 assert!(deserialized.providers.is_some());
7084 }
7085
7086 #[test]
7087 fn test_agent_capabilities_session_is_explicit() {
7088 let json = serde_json::to_value(AgentCapabilities::new()).unwrap();
7089 assert!(json.get("session").is_none());
7090
7091 let caps = AgentCapabilities::new().session(
7092 SessionCapabilities::new()
7093 .prompt(PromptCapabilities::new().image(PromptImageCapabilities::new()))
7094 .mcp(McpCapabilities::new().stdio(McpStdioCapabilities::new())),
7095 );
7096
7097 assert_eq!(
7098 serde_json::to_value(&caps).unwrap(),
7099 json!({
7100 "session": {
7101 "prompt": {
7102 "image": {}
7103 },
7104 "mcp": {
7105 "stdio": {}
7106 }
7107 }
7108 })
7109 );
7110
7111 let deserialized: AgentCapabilities = serde_json::from_value(json!({
7112 "session": false
7113 }))
7114 .unwrap();
7115 assert!(deserialized.session.is_none());
7116 }
7117
7118 #[test]
7119 fn test_prompt_capabilities_serialize_supported_content_as_objects() {
7120 let caps = PromptCapabilities::new()
7121 .image(PromptImageCapabilities::new())
7122 .audio(PromptAudioCapabilities::new())
7123 .embedded_context(PromptEmbeddedContextCapabilities::new());
7124
7125 assert_eq!(
7126 serde_json::to_value(&caps).unwrap(),
7127 json!({
7128 "image": {},
7129 "audio": {},
7130 "embeddedContext": {}
7131 })
7132 );
7133
7134 let deserialized: PromptCapabilities = serde_json::from_value(json!({
7135 "image": null,
7136 "audio": false,
7137 "embeddedContext": {}
7138 }))
7139 .unwrap();
7140 assert!(deserialized.image.is_none());
7141 assert!(deserialized.audio.is_none());
7142 assert!(deserialized.embedded_context.is_some());
7143 }
7144
7145 #[test]
7146 fn test_mcp_capabilities_serialize_supported_transports_as_objects() {
7147 let caps = McpCapabilities::new()
7148 .stdio(McpStdioCapabilities::new())
7149 .http(McpHttpCapabilities::new());
7150
7151 assert_eq!(
7152 serde_json::to_value(&caps).unwrap(),
7153 json!({
7154 "stdio": {},
7155 "http": {}
7156 })
7157 );
7158
7159 let deserialized: McpCapabilities = serde_json::from_value(json!({
7160 "stdio": null,
7161 "http": false
7162 }))
7163 .unwrap();
7164 assert!(deserialized.stdio.is_none());
7165 assert!(deserialized.http.is_none());
7166 }
7167
7168 #[cfg(feature = "unstable_mcp_over_acp")]
7169 #[test]
7170 fn test_mcp_capabilities_serialize_acp_support_as_object() {
7171 let caps = McpCapabilities::new().acp(McpAcpCapabilities::new());
7172
7173 assert_eq!(
7174 serde_json::to_value(&caps).unwrap(),
7175 json!({
7176 "acp": {}
7177 })
7178 );
7179 }
7180
7181 #[test]
7182 fn prompt_request_rejects_malformed_content_block() {
7183 use serde_json::json;
7184
7185 assert!(
7186 serde_json::from_value::<PromptRequest>(json!({
7187 "sessionId": "sess-1",
7188 "prompt": [{"type": "text"}]
7189 }))
7190 .is_err()
7191 );
7192 }
7193
7194 #[test]
7195 fn prompt_request_rejects_non_array_prompt() {
7196 use serde_json::json;
7197
7198 assert!(
7199 serde_json::from_value::<PromptRequest>(json!({
7200 "sessionId": "sess-1",
7201 "prompt": "hello"
7202 }))
7203 .is_err()
7204 );
7205 }
7206}