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, ClientCapabilities, ContentBlock, ExtNotification, ExtRequest, ExtResponse,
19 MessageId, Meta, SessionId,
20};
21use crate::{IntoOption, ProtocolVersion, SkipListener};
22
23#[cfg(feature = "unstable_mcp_over_acp")]
24use super::mcp::{
25 MCP_MESSAGE_METHOD_NAME, MessageMcpNotification, MessageMcpRequest, MessageMcpResponse,
26};
27
28#[cfg(feature = "unstable_nes")]
29use super::{
30 AcceptNesNotification, CloseNesRequest, CloseNesResponse, DidChangeDocumentNotification,
31 DidCloseDocumentNotification, DidFocusDocumentNotification, DidOpenDocumentNotification,
32 DidSaveDocumentNotification, NesCapabilities, PositionEncodingKind, RejectNesNotification,
33 StartNesRequest, StartNesResponse, SuggestNesRequest, SuggestNesResponse,
34};
35
36#[cfg(feature = "unstable_nes")]
37use super::{
38 DOCUMENT_DID_CHANGE_METHOD_NAME, DOCUMENT_DID_CLOSE_METHOD_NAME,
39 DOCUMENT_DID_FOCUS_METHOD_NAME, DOCUMENT_DID_OPEN_METHOD_NAME, DOCUMENT_DID_SAVE_METHOD_NAME,
40 NES_ACCEPT_METHOD_NAME, NES_CLOSE_METHOD_NAME, NES_REJECT_METHOD_NAME, NES_START_METHOD_NAME,
41 NES_SUGGEST_METHOD_NAME,
42};
43
44#[serde_as]
52#[skip_serializing_none]
53#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
54#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
55#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = INITIALIZE_METHOD_NAME)))]
56#[serde(rename_all = "camelCase")]
57#[non_exhaustive]
58pub struct InitializeRequest {
59 pub protocol_version: ProtocolVersion,
61 pub info: Implementation,
63 #[serde_as(deserialize_as = "DefaultOnError")]
65 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
66 #[serde(default)]
67 pub capabilities: ClientCapabilities,
68 #[serde_as(deserialize_as = "DefaultOnError")]
74 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
75 #[serde(default)]
76 #[serde(rename = "_meta")]
77 pub meta: Option<Meta>,
78}
79
80impl InitializeRequest {
81 #[must_use]
83 pub fn new(protocol_version: ProtocolVersion, info: Implementation) -> Self {
84 Self {
85 protocol_version,
86 capabilities: ClientCapabilities::default(),
87 info,
88 meta: None,
89 }
90 }
91
92 #[must_use]
94 pub fn capabilities(mut self, capabilities: ClientCapabilities) -> Self {
95 self.capabilities = capabilities;
96 self
97 }
98
99 #[must_use]
105 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
106 self.meta = meta.into_option();
107 self
108 }
109}
110
111#[serde_as]
117#[skip_serializing_none]
118#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
119#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
120#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = INITIALIZE_METHOD_NAME)))]
121#[serde(rename_all = "camelCase")]
122#[non_exhaustive]
123pub struct InitializeResponse {
124 pub protocol_version: ProtocolVersion,
129 pub info: Implementation,
131 #[serde_as(deserialize_as = "DefaultOnError")]
133 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
134 #[serde(default)]
135 pub capabilities: AgentCapabilities,
136 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
142 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
143 #[serde(default, skip_serializing_if = "Vec::is_empty")]
144 pub auth_methods: Vec<AuthMethod>,
145 #[serde_as(deserialize_as = "DefaultOnError")]
151 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
152 #[serde(default)]
153 #[serde(rename = "_meta")]
154 pub meta: Option<Meta>,
155}
156
157impl InitializeResponse {
158 #[must_use]
160 pub fn new(protocol_version: ProtocolVersion, info: Implementation) -> Self {
161 Self {
162 protocol_version,
163 capabilities: AgentCapabilities::default(),
164 auth_methods: vec![],
165 info,
166 meta: None,
167 }
168 }
169
170 #[must_use]
172 pub fn capabilities(mut self, capabilities: AgentCapabilities) -> Self {
173 self.capabilities = capabilities;
174 self
175 }
176
177 #[must_use]
182 pub fn auth_methods(mut self, auth_methods: Vec<AuthMethod>) -> Self {
183 self.auth_methods = auth_methods;
184 self
185 }
186
187 #[must_use]
193 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
194 self.meta = meta.into_option();
195 self
196 }
197}
198
199#[serde_as]
203#[skip_serializing_none]
204#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
205#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
206#[serde(rename_all = "camelCase")]
207#[non_exhaustive]
208pub struct Implementation {
209 pub name: String,
212 #[serde_as(deserialize_as = "DefaultOnError")]
217 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
218 #[serde(default)]
219 pub title: Option<String>,
220 pub version: String,
223 #[serde_as(deserialize_as = "DefaultOnError")]
229 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
230 #[serde(default)]
231 #[serde(rename = "_meta")]
232 pub meta: Option<Meta>,
233}
234
235impl Implementation {
236 #[must_use]
238 pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
239 Self {
240 name: name.into(),
241 title: None,
242 version: version.into(),
243 meta: None,
244 }
245 }
246
247 #[must_use]
252 pub fn title(mut self, title: impl IntoOption<String>) -> Self {
253 self.title = title.into_option();
254 self
255 }
256
257 #[must_use]
263 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
264 self.meta = meta.into_option();
265 self
266 }
267}
268
269#[serde_as]
279#[skip_serializing_none]
280#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
281#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
282#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGIN_METHOD_NAME)))]
283#[serde(rename_all = "camelCase")]
284#[non_exhaustive]
285pub struct LoginAuthRequest {
286 pub method_id: AuthMethodId,
289 #[serde_as(deserialize_as = "DefaultOnError")]
295 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
296 #[serde(default)]
297 #[serde(rename = "_meta")]
298 pub meta: Option<Meta>,
299}
300
301impl LoginAuthRequest {
302 #[must_use]
304 pub fn new(method_id: impl Into<AuthMethodId>) -> Self {
305 Self {
306 method_id: method_id.into(),
307 meta: None,
308 }
309 }
310
311 #[must_use]
317 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
318 self.meta = meta.into_option();
319 self
320 }
321}
322
323crate::serde_util::default_on_null! {
324 #[serde_as]
326 #[skip_serializing_none]
327 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
328 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
329 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGIN_METHOD_NAME)))]
330 #[serde(rename_all = "camelCase")]
331 #[non_exhaustive]
332 pub struct LoginAuthResponse {
333 #[serde_as(deserialize_as = "DefaultOnError")]
339 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
340 #[serde(default)]
341 #[serde(rename = "_meta")]
342 pub meta: Option<Meta>,
343 }
344}
345
346impl LoginAuthResponse {
347 #[must_use]
349 pub fn new() -> Self {
350 Self::default()
351 }
352
353 #[must_use]
359 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
360 self.meta = meta.into_option();
361 self
362 }
363}
364
365crate::serde_util::default_on_null! {
368 #[serde_as]
376 #[skip_serializing_none]
377 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
378 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
379 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGOUT_METHOD_NAME)))]
380 #[serde(rename_all = "camelCase")]
381 #[non_exhaustive]
382 pub struct LogoutAuthRequest {
383 #[serde_as(deserialize_as = "DefaultOnError")]
389 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
390 #[serde(default)]
391 #[serde(rename = "_meta")]
392 pub meta: Option<Meta>,
393 }
394}
395
396impl LogoutAuthRequest {
397 #[must_use]
399 pub fn new() -> Self {
400 Self::default()
401 }
402
403 #[must_use]
409 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
410 self.meta = meta.into_option();
411 self
412 }
413}
414
415crate::serde_util::default_on_null! {
416 #[serde_as]
418 #[skip_serializing_none]
419 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
420 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
421 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGOUT_METHOD_NAME)))]
422 #[serde(rename_all = "camelCase")]
423 #[non_exhaustive]
424 pub struct LogoutAuthResponse {
425 #[serde_as(deserialize_as = "DefaultOnError")]
431 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
432 #[serde(default)]
433 #[serde(rename = "_meta")]
434 pub meta: Option<Meta>,
435 }
436}
437
438impl LogoutAuthResponse {
439 #[must_use]
441 pub fn new() -> Self {
442 Self::default()
443 }
444
445 #[must_use]
451 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
452 self.meta = meta.into_option();
453 self
454 }
455}
456
457#[serde_as]
463#[skip_serializing_none]
464#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
465#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
466#[serde(rename_all = "camelCase")]
467#[non_exhaustive]
468pub struct AgentAuthCapabilities {
469 #[serde_as(deserialize_as = "DefaultOnError")]
475 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
476 #[serde(default)]
477 #[serde(rename = "_meta")]
478 pub meta: Option<Meta>,
479}
480
481impl AgentAuthCapabilities {
482 #[must_use]
484 pub fn new() -> Self {
485 Self::default()
486 }
487
488 #[must_use]
494 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
495 self.meta = meta.into_option();
496 self
497 }
498}
499
500#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
502#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
503#[serde(transparent)]
504#[from(forward)]
505#[non_exhaustive]
506pub struct AuthMethodId(pub Arc<str>);
507
508impl AuthMethodId {
509 #[must_use]
511 pub fn new(id: impl Into<Self>) -> Self {
512 id.into()
513 }
514}
515
516#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
520#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
521#[serde(tag = "type", rename_all = "snake_case")]
522#[non_exhaustive]
523pub enum AuthMethod {
524 Terminal(AuthMethodTerminal),
527 Agent(AuthMethodAgent),
531 #[serde(untagged)]
541 Other(OtherAuthMethod),
542}
543
544impl AuthMethod {
545 #[must_use]
547 pub fn method_id(&self) -> &AuthMethodId {
548 match self {
549 Self::Agent(a) => &a.method_id,
550 Self::Other(a) => &a.method_id,
551 Self::Terminal(t) => &t.method_id,
552 }
553 }
554
555 #[must_use]
557 pub fn name(&self) -> &str {
558 match self {
559 Self::Agent(a) => &a.name,
560 Self::Other(a) => &a.name,
561 Self::Terminal(t) => &t.name,
562 }
563 }
564
565 #[must_use]
567 pub fn description(&self) -> Option<&str> {
568 match self {
569 Self::Agent(a) => a.description.as_deref(),
570 Self::Other(a) => a.description.as_deref(),
571 Self::Terminal(t) => t.description.as_deref(),
572 }
573 }
574
575 #[must_use]
581 pub fn meta(&self) -> Option<&Meta> {
582 match self {
583 Self::Agent(a) => a.meta.as_ref(),
584 Self::Other(a) => a.meta.as_ref(),
585 Self::Terminal(t) => t.meta.as_ref(),
586 }
587 }
588}
589
590#[serde_as]
592#[skip_serializing_none]
593#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
594#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
595#[cfg_attr(feature = "schemars", schemars(inline))]
596#[cfg_attr(feature = "schemars", schemars(transform = other_auth_method_schema))]
597#[serde(rename_all = "camelCase")]
598#[non_exhaustive]
599pub struct OtherAuthMethod {
600 #[serde(rename = "type")]
606 pub type_: String,
607 pub method_id: AuthMethodId,
609 pub name: String,
611 #[serde_as(deserialize_as = "DefaultOnError")]
613 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
614 #[serde(default)]
615 pub description: Option<String>,
616 #[serde_as(deserialize_as = "DefaultOnError")]
622 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
623 #[serde(default)]
624 #[serde(rename = "_meta")]
625 pub meta: Option<Meta>,
626 #[serde(flatten)]
628 pub fields: BTreeMap<String, serde_json::Value>,
629}
630
631impl OtherAuthMethod {
632 #[must_use]
634 pub fn new(
635 type_: impl Into<String>,
636 method_id: impl Into<AuthMethodId>,
637 name: impl Into<String>,
638 mut fields: BTreeMap<String, serde_json::Value>,
639 ) -> Self {
640 fields.remove("type");
641 fields.remove("methodId");
642 fields.remove("name");
643 fields.remove("description");
644 fields.remove("_meta");
645 Self {
646 type_: type_.into(),
647 method_id: method_id.into(),
648 name: name.into(),
649 description: None,
650 meta: None,
651 fields,
652 }
653 }
654
655 #[must_use]
657 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
658 self.description = description.into_option();
659 self
660 }
661
662 #[must_use]
668 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
669 self.meta = meta.into_option();
670 self
671 }
672}
673
674impl<'de> Deserialize<'de> for OtherAuthMethod {
675 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
676 where
677 D: serde::Deserializer<'de>,
678 {
679 #[derive(Deserialize)]
680 #[serde(rename_all = "camelCase")]
681 struct RawOtherAuthMethod {
682 #[serde(rename = "type")]
683 type_: String,
684 method_id: AuthMethodId,
685 name: String,
686 description: Option<String>,
687 #[serde(rename = "_meta")]
688 meta: Option<Meta>,
689 #[serde(flatten)]
690 fields: BTreeMap<String, serde_json::Value>,
691 }
692
693 let raw = RawOtherAuthMethod::deserialize(deserializer)?;
694 if is_known_auth_method_type(&raw.type_) {
695 return Err(serde::de::Error::custom(format!(
696 "known authentication method `{}` did not match its schema",
697 raw.type_
698 )));
699 }
700
701 Ok(Self {
702 type_: raw.type_,
703 method_id: raw.method_id,
704 name: raw.name,
705 description: raw.description,
706 meta: raw.meta,
707 fields: raw.fields,
708 })
709 }
710}
711
712fn is_known_auth_method_type(type_: &str) -> bool {
713 matches!(type_, "agent" | "terminal")
714}
715
716#[cfg(feature = "schemars")]
717fn other_auth_method_schema(schema: &mut Schema) {
718 super::schema_util::reject_known_string_discriminators(schema, "type", &["agent", "terminal"]);
719}
720
721#[serde_as]
725#[skip_serializing_none]
726#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
727#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
728#[serde(rename_all = "camelCase")]
729#[non_exhaustive]
730pub struct AuthMethodAgent {
731 pub method_id: AuthMethodId,
733 pub name: String,
735 #[serde_as(deserialize_as = "DefaultOnError")]
737 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
738 #[serde(default)]
739 pub description: Option<String>,
740 #[serde_as(deserialize_as = "DefaultOnError")]
746 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
747 #[serde(default)]
748 #[serde(rename = "_meta")]
749 pub meta: Option<Meta>,
750}
751
752impl AuthMethodAgent {
753 #[must_use]
755 pub fn new(method_id: impl Into<AuthMethodId>, name: impl Into<String>) -> Self {
756 Self {
757 method_id: method_id.into(),
758 name: name.into(),
759 description: None,
760 meta: None,
761 }
762 }
763
764 #[must_use]
766 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
767 self.description = description.into_option();
768 self
769 }
770
771 #[must_use]
777 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
778 self.meta = meta.into_option();
779 self
780 }
781}
782
783#[serde_as]
791#[skip_serializing_none]
792#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
793#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
794#[serde(rename_all = "camelCase")]
795#[non_exhaustive]
796pub struct AuthMethodTerminal {
797 pub method_id: AuthMethodId,
799 pub name: String,
801 #[serde_as(deserialize_as = "DefaultOnError")]
803 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
804 #[serde(default)]
805 pub description: Option<String>,
806 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
808 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
809 #[serde(default, skip_serializing_if = "Vec::is_empty")]
810 pub args: Vec<String>,
811 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
815 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
816 #[serde(default, skip_serializing_if = "Vec::is_empty")]
817 pub env: Vec<EnvVariable>,
818 #[serde_as(deserialize_as = "DefaultOnError")]
824 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
825 #[serde(default)]
826 #[serde(rename = "_meta")]
827 pub meta: Option<Meta>,
828}
829
830impl AuthMethodTerminal {
831 #[must_use]
833 pub fn new(method_id: impl Into<AuthMethodId>, name: impl Into<String>) -> Self {
834 Self {
835 method_id: method_id.into(),
836 name: name.into(),
837 description: None,
838 args: Vec::new(),
839 env: Vec::new(),
840 meta: None,
841 }
842 }
843
844 #[must_use]
846 pub fn args(mut self, args: Vec<String>) -> Self {
847 self.args = args;
848 self
849 }
850
851 #[must_use]
855 pub fn env(mut self, env: Vec<EnvVariable>) -> Self {
856 self.env = env;
857 self
858 }
859
860 #[must_use]
862 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
863 self.description = description.into_option();
864 self
865 }
866
867 #[must_use]
873 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
874 self.meta = meta.into_option();
875 self
876 }
877}
878
879#[serde_as]
885#[skip_serializing_none]
886#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
887#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
888#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_NEW_METHOD_NAME)))]
889#[serde(rename_all = "camelCase")]
890#[non_exhaustive]
891pub struct NewSessionRequest {
892 pub cwd: AbsolutePath,
894 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
900 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
901 #[serde(default, skip_serializing_if = "Vec::is_empty")]
902 pub additional_directories: Vec<AbsolutePath>,
903 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
905 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
906 #[serde(default, skip_serializing_if = "Vec::is_empty")]
907 pub mcp_servers: Vec<McpServer>,
908 #[serde_as(deserialize_as = "DefaultOnError")]
914 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
915 #[serde(default)]
916 #[serde(rename = "_meta")]
917 pub meta: Option<Meta>,
918}
919
920impl NewSessionRequest {
921 #[must_use]
923 pub fn new(cwd: impl Into<AbsolutePath>) -> Self {
924 Self {
925 cwd: cwd.into(),
926 additional_directories: vec![],
927 mcp_servers: vec![],
928 meta: None,
929 }
930 }
931
932 #[must_use]
934 pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
935 where
936 I: IntoIterator<Item = P>,
937 P: Into<AbsolutePath>,
938 {
939 self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
940 self
941 }
942
943 #[must_use]
945 pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
946 self.mcp_servers = mcp_servers;
947 self
948 }
949
950 #[must_use]
956 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
957 self.meta = meta.into_option();
958 self
959 }
960}
961
962#[serde_as]
966#[skip_serializing_none]
967#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
968#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
969#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_NEW_METHOD_NAME)))]
970#[serde(rename_all = "camelCase")]
971#[non_exhaustive]
972pub struct NewSessionResponse {
973 pub session_id: SessionId,
977 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
979 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
980 #[serde(default, skip_serializing_if = "Vec::is_empty")]
981 pub config_options: Vec<SessionConfigOption>,
982 #[serde_as(deserialize_as = "DefaultOnError")]
988 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
989 #[serde(default)]
990 #[serde(rename = "_meta")]
991 pub meta: Option<Meta>,
992}
993
994impl NewSessionResponse {
995 #[must_use]
997 pub fn new(session_id: impl Into<SessionId>) -> Self {
998 Self {
999 session_id: session_id.into(),
1000 config_options: Vec::new(),
1001 meta: None,
1002 }
1003 }
1004
1005 #[must_use]
1007 pub fn config_options(mut self, config_options: Vec<SessionConfigOption>) -> Self {
1008 self.config_options = config_options;
1009 self
1010 }
1011
1012 #[must_use]
1018 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1019 self.meta = meta.into_option();
1020 self
1021 }
1022}
1023
1024#[cfg(feature = "unstable_session_fork")]
1037#[serde_as]
1038#[skip_serializing_none]
1039#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1040#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1041#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_FORK_METHOD_NAME)))]
1042#[serde(rename_all = "camelCase")]
1043#[non_exhaustive]
1044pub struct ForkSessionRequest {
1045 pub session_id: SessionId,
1047 pub cwd: AbsolutePath,
1049 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1055 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1056 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1057 pub additional_directories: Vec<AbsolutePath>,
1058 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1060 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1061 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1062 pub mcp_servers: Vec<McpServer>,
1063 #[serde_as(deserialize_as = "DefaultOnError")]
1069 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1070 #[serde(default)]
1071 #[serde(rename = "_meta")]
1072 pub meta: Option<Meta>,
1073}
1074
1075#[cfg(feature = "unstable_session_fork")]
1076impl ForkSessionRequest {
1077 #[must_use]
1079 pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<AbsolutePath>) -> Self {
1080 Self {
1081 session_id: session_id.into(),
1082 cwd: cwd.into(),
1083 additional_directories: vec![],
1084 mcp_servers: vec![],
1085 meta: None,
1086 }
1087 }
1088
1089 #[must_use]
1091 pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
1092 where
1093 I: IntoIterator<Item = P>,
1094 P: Into<AbsolutePath>,
1095 {
1096 self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
1097 self
1098 }
1099
1100 #[must_use]
1102 pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1103 self.mcp_servers = mcp_servers;
1104 self
1105 }
1106
1107 #[must_use]
1113 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1114 self.meta = meta.into_option();
1115 self
1116 }
1117}
1118
1119#[cfg(feature = "unstable_session_fork")]
1125#[serde_as]
1126#[skip_serializing_none]
1127#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1128#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1129#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_FORK_METHOD_NAME)))]
1130#[serde(rename_all = "camelCase")]
1131#[non_exhaustive]
1132pub struct ForkSessionResponse {
1133 pub session_id: SessionId,
1135 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1137 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1138 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1139 pub config_options: Vec<SessionConfigOption>,
1140 #[serde_as(deserialize_as = "DefaultOnError")]
1146 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1147 #[serde(default)]
1148 #[serde(rename = "_meta")]
1149 pub meta: Option<Meta>,
1150}
1151
1152#[cfg(feature = "unstable_session_fork")]
1153impl ForkSessionResponse {
1154 #[must_use]
1156 pub fn new(session_id: impl Into<SessionId>) -> Self {
1157 Self {
1158 session_id: session_id.into(),
1159 config_options: Vec::new(),
1160 meta: None,
1161 }
1162 }
1163
1164 #[must_use]
1166 pub fn config_options(mut self, config_options: Vec<SessionConfigOption>) -> Self {
1167 self.config_options = config_options;
1168 self
1169 }
1170
1171 #[must_use]
1177 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1178 self.meta = meta.into_option();
1179 self
1180 }
1181}
1182
1183#[serde_as]
1190#[skip_serializing_none]
1191#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1192#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1193#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_RESUME_METHOD_NAME)))]
1194#[serde(rename_all = "camelCase")]
1195#[non_exhaustive]
1196pub struct ResumeSessionRequest {
1197 pub session_id: SessionId,
1199 pub cwd: AbsolutePath,
1201 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1208 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1209 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1210 pub additional_directories: Vec<AbsolutePath>,
1211 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1213 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1214 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1215 pub mcp_servers: Vec<McpServer>,
1216 #[serde_as(deserialize_as = "DefaultOnError")]
1224 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1225 #[serde(default)]
1226 pub replay_from: Option<ReplayFrom>,
1227 #[serde_as(deserialize_as = "DefaultOnError")]
1233 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1234 #[serde(default)]
1235 #[serde(rename = "_meta")]
1236 pub meta: Option<Meta>,
1237}
1238
1239impl ResumeSessionRequest {
1240 #[must_use]
1242 pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<AbsolutePath>) -> Self {
1243 Self {
1244 session_id: session_id.into(),
1245 cwd: cwd.into(),
1246 additional_directories: vec![],
1247 mcp_servers: vec![],
1248 replay_from: None,
1249 meta: None,
1250 }
1251 }
1252
1253 #[must_use]
1255 pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
1256 where
1257 I: IntoIterator<Item = P>,
1258 P: Into<AbsolutePath>,
1259 {
1260 self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
1261 self
1262 }
1263
1264 #[must_use]
1266 pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1267 self.mcp_servers = mcp_servers;
1268 self
1269 }
1270
1271 #[must_use]
1279 pub fn replay_from(mut self, replay_from: impl IntoOption<ReplayFrom>) -> Self {
1280 self.replay_from = replay_from.into_option();
1281 self
1282 }
1283
1284 #[must_use]
1290 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1291 self.meta = meta.into_option();
1292 self
1293 }
1294}
1295
1296#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1300#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1301#[serde(tag = "type", rename_all = "snake_case")]
1302#[non_exhaustive]
1303pub enum ReplayFrom {
1304 Start(ReplayFromStart),
1306 #[serde(untagged)]
1316 Other(OtherReplayFrom),
1317}
1318
1319impl From<ReplayFromStart> for ReplayFrom {
1320 fn from(replay_from: ReplayFromStart) -> Self {
1321 Self::Start(replay_from)
1322 }
1323}
1324
1325#[serde_as]
1327#[skip_serializing_none]
1328#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1329#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1330#[serde(rename_all = "camelCase")]
1331#[non_exhaustive]
1332pub struct ReplayFromStart {
1333 #[serde_as(deserialize_as = "DefaultOnError")]
1339 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1340 #[serde(default)]
1341 #[serde(rename = "_meta")]
1342 pub meta: Option<Meta>,
1343}
1344
1345impl ReplayFromStart {
1346 #[must_use]
1348 pub fn new() -> Self {
1349 Self::default()
1350 }
1351
1352 #[must_use]
1358 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1359 self.meta = meta.into_option();
1360 self
1361 }
1362}
1363
1364#[serde_as]
1366#[skip_serializing_none]
1367#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1368#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
1369#[cfg_attr(feature = "schemars", schemars(inline))]
1370#[cfg_attr(feature = "schemars", schemars(transform = other_replay_from_schema))]
1371#[serde(rename_all = "camelCase")]
1372#[non_exhaustive]
1373pub struct OtherReplayFrom {
1374 #[serde(rename = "type")]
1380 pub type_: String,
1381 #[serde_as(deserialize_as = "DefaultOnError")]
1387 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1388 #[serde(default)]
1389 #[serde(rename = "_meta")]
1390 pub meta: Option<Meta>,
1391 #[serde(flatten)]
1393 pub fields: BTreeMap<String, serde_json::Value>,
1394}
1395
1396impl OtherReplayFrom {
1397 #[must_use]
1399 pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
1400 fields.remove("type");
1401 fields.remove("_meta");
1402 Self {
1403 type_: type_.into(),
1404 meta: None,
1405 fields,
1406 }
1407 }
1408
1409 #[must_use]
1415 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1416 self.meta = meta.into_option();
1417 self
1418 }
1419}
1420
1421impl<'de> Deserialize<'de> for OtherReplayFrom {
1422 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1423 where
1424 D: serde::Deserializer<'de>,
1425 {
1426 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
1427 let type_ = fields
1428 .remove("type")
1429 .ok_or_else(|| serde::de::Error::missing_field("type"))?;
1430 let serde_json::Value::String(type_) = type_ else {
1431 return Err(serde::de::Error::custom("`type` must be a string"));
1432 };
1433
1434 if is_known_replay_from_type(&type_) {
1435 return Err(serde::de::Error::custom(format!(
1436 "known replay cursor `{type_}` did not match its schema"
1437 )));
1438 }
1439
1440 let meta = fields
1441 .remove("_meta")
1442 .and_then(|value| serde_json::from_value(value).ok());
1443
1444 Ok(Self {
1445 type_,
1446 meta,
1447 fields,
1448 })
1449 }
1450}
1451
1452fn is_known_replay_from_type(type_: &str) -> bool {
1453 matches!(type_, "start")
1454}
1455
1456#[cfg(feature = "schemars")]
1457fn other_replay_from_schema(schema: &mut Schema) {
1458 super::schema_util::reject_known_string_discriminators(schema, "type", &["start"]);
1459}
1460
1461crate::serde_util::default_on_null! {
1462 #[serde_as]
1464 #[skip_serializing_none]
1465 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1466 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
1467 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_RESUME_METHOD_NAME)))]
1468 #[serde(rename_all = "camelCase")]
1469 #[non_exhaustive]
1470 pub struct ResumeSessionResponse {
1471 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1473 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1474 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1475 pub config_options: Vec<SessionConfigOption>,
1476 #[serde_as(deserialize_as = "DefaultOnError")]
1482 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1483 #[serde(default)]
1484 #[serde(rename = "_meta")]
1485 pub meta: Option<Meta>,
1486 }
1487}
1488
1489impl ResumeSessionResponse {
1490 #[must_use]
1492 pub fn new() -> Self {
1493 Self::default()
1494 }
1495
1496 #[must_use]
1498 pub fn config_options(mut self, config_options: Vec<SessionConfigOption>) -> Self {
1499 self.config_options = config_options;
1500 self
1501 }
1502
1503 #[must_use]
1509 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1510 self.meta = meta.into_option();
1511 self
1512 }
1513}
1514
1515#[serde_as]
1523#[skip_serializing_none]
1524#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1525#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1526#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_CLOSE_METHOD_NAME)))]
1527#[serde(rename_all = "camelCase")]
1528#[non_exhaustive]
1529pub struct CloseSessionRequest {
1530 pub session_id: SessionId,
1532 #[serde_as(deserialize_as = "DefaultOnError")]
1538 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1539 #[serde(default)]
1540 #[serde(rename = "_meta")]
1541 pub meta: Option<Meta>,
1542}
1543
1544impl CloseSessionRequest {
1545 #[must_use]
1547 pub fn new(session_id: impl Into<SessionId>) -> Self {
1548 Self {
1549 session_id: session_id.into(),
1550 meta: None,
1551 }
1552 }
1553
1554 #[must_use]
1560 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1561 self.meta = meta.into_option();
1562 self
1563 }
1564}
1565
1566crate::serde_util::default_on_null! {
1567 #[serde_as]
1569 #[skip_serializing_none]
1570 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1571 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
1572 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_CLOSE_METHOD_NAME)))]
1573 #[serde(rename_all = "camelCase")]
1574 #[non_exhaustive]
1575 pub struct CloseSessionResponse {
1576 #[serde_as(deserialize_as = "DefaultOnError")]
1582 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1583 #[serde(default)]
1584 #[serde(rename = "_meta")]
1585 pub meta: Option<Meta>,
1586 }
1587}
1588
1589impl CloseSessionResponse {
1590 #[must_use]
1592 pub fn new() -> Self {
1593 Self::default()
1594 }
1595
1596 #[must_use]
1602 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1603 self.meta = meta.into_option();
1604 self
1605 }
1606}
1607
1608#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1612#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
1613#[serde(transparent)]
1614#[from(Arc<str>, String, &str, &mut str, Box<str>, Cow<'_, str>)]
1615#[non_exhaustive]
1616pub struct SessionListCursor(pub Arc<str>);
1617
1618impl SessionListCursor {
1619 #[must_use]
1621 pub fn new(cursor: impl Into<Self>) -> Self {
1622 cursor.into()
1623 }
1624}
1625
1626impl AsRef<str> for SessionListCursor {
1627 fn as_ref(&self) -> &str {
1628 &self.0
1629 }
1630}
1631
1632impl From<&String> for SessionListCursor {
1633 fn from(cursor: &String) -> Self {
1634 Self(cursor.as_str().into())
1635 }
1636}
1637
1638macro_rules! impl_session_list_cursor_option_conversion {
1639 ($source:ty) => {
1640 impl IntoOption<SessionListCursor> for $source {
1641 fn into_option(self) -> Option<SessionListCursor> {
1642 Some(self.into())
1643 }
1644 }
1645 };
1646}
1647
1648impl_session_list_cursor_option_conversion!(Arc<str>);
1649impl_session_list_cursor_option_conversion!(String);
1650impl_session_list_cursor_option_conversion!(&str);
1651impl_session_list_cursor_option_conversion!(&mut str);
1652impl_session_list_cursor_option_conversion!(&String);
1653impl_session_list_cursor_option_conversion!(Box<str>);
1654impl_session_list_cursor_option_conversion!(Cow<'_, str>);
1655
1656crate::serde_util::default_on_null! {
1657 #[serde_as]
1659 #[skip_serializing_none]
1660 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1661 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
1662 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_LIST_METHOD_NAME)))]
1663 #[serde(rename_all = "camelCase")]
1664 #[non_exhaustive]
1665 pub struct ListSessionsRequest {
1666 #[serde(default)]
1668 pub cwd: Option<AbsolutePath>,
1669 #[serde(default)]
1671 pub cursor: Option<SessionListCursor>,
1672 #[serde_as(deserialize_as = "DefaultOnError")]
1678 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1679 #[serde(default)]
1680 #[serde(rename = "_meta")]
1681 pub meta: Option<Meta>,
1682 }
1683}
1684
1685impl ListSessionsRequest {
1686 #[must_use]
1688 pub fn new() -> Self {
1689 Self::default()
1690 }
1691
1692 #[must_use]
1694 pub fn cwd(mut self, cwd: impl IntoOption<AbsolutePath>) -> Self {
1695 self.cwd = cwd.into_option();
1696 self
1697 }
1698
1699 #[must_use]
1701 pub fn cursor(mut self, cursor: impl IntoOption<SessionListCursor>) -> Self {
1702 self.cursor = cursor.into_option();
1703 self
1704 }
1705
1706 #[must_use]
1712 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1713 self.meta = meta.into_option();
1714 self
1715 }
1716}
1717
1718#[serde_as]
1720#[skip_serializing_none]
1721#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1722#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1723#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_LIST_METHOD_NAME)))]
1724#[serde(rename_all = "camelCase")]
1725#[non_exhaustive]
1726pub struct ListSessionsResponse {
1727 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1729 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1730 pub sessions: Vec<SessionInfo>,
1731 #[serde_as(deserialize_as = "DefaultOnError")]
1734 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1735 #[serde(default)]
1736 pub next_cursor: Option<SessionListCursor>,
1737 #[serde_as(deserialize_as = "DefaultOnError")]
1743 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1744 #[serde(default)]
1745 #[serde(rename = "_meta")]
1746 pub meta: Option<Meta>,
1747}
1748
1749impl ListSessionsResponse {
1750 #[must_use]
1752 pub fn new(sessions: Vec<SessionInfo>) -> Self {
1753 Self {
1754 sessions,
1755 next_cursor: None,
1756 meta: None,
1757 }
1758 }
1759
1760 #[must_use]
1762 pub fn next_cursor(mut self, next_cursor: impl IntoOption<SessionListCursor>) -> Self {
1763 self.next_cursor = next_cursor.into_option();
1764 self
1765 }
1766
1767 #[must_use]
1773 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1774 self.meta = meta.into_option();
1775 self
1776 }
1777}
1778
1779#[serde_as]
1785#[skip_serializing_none]
1786#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1787#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1788#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_DELETE_METHOD_NAME)))]
1789#[serde(rename_all = "camelCase")]
1790#[non_exhaustive]
1791pub struct DeleteSessionRequest {
1792 pub session_id: SessionId,
1794 #[serde_as(deserialize_as = "DefaultOnError")]
1800 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1801 #[serde(default)]
1802 #[serde(rename = "_meta")]
1803 pub meta: Option<Meta>,
1804}
1805
1806impl DeleteSessionRequest {
1807 #[must_use]
1809 pub fn new(session_id: impl Into<SessionId>) -> Self {
1810 Self {
1811 session_id: session_id.into(),
1812 meta: None,
1813 }
1814 }
1815
1816 #[must_use]
1822 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1823 self.meta = meta.into_option();
1824 self
1825 }
1826}
1827
1828crate::serde_util::default_on_null! {
1829 #[serde_as]
1831 #[skip_serializing_none]
1832 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1833 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
1834 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_DELETE_METHOD_NAME)))]
1835 #[serde(rename_all = "camelCase")]
1836 #[non_exhaustive]
1837 pub struct DeleteSessionResponse {
1838 #[serde_as(deserialize_as = "DefaultOnError")]
1844 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1845 #[serde(default)]
1846 #[serde(rename = "_meta")]
1847 pub meta: Option<Meta>,
1848 }
1849}
1850
1851impl DeleteSessionResponse {
1852 #[must_use]
1854 pub fn new() -> Self {
1855 Self::default()
1856 }
1857
1858 #[must_use]
1864 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1865 self.meta = meta.into_option();
1866 self
1867 }
1868}
1869
1870#[serde_as]
1872#[skip_serializing_none]
1873#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1874#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1875#[serde(rename_all = "camelCase")]
1876#[non_exhaustive]
1877pub struct SessionInfo {
1878 pub session_id: SessionId,
1880 pub cwd: AbsolutePath,
1882 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1888 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1889 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1890 pub additional_directories: Vec<AbsolutePath>,
1891
1892 #[serde_as(deserialize_as = "DefaultOnError")]
1894 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1895 #[serde(default)]
1896 pub title: Option<String>,
1897 #[serde_as(deserialize_as = "DefaultOnError")]
1899 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "format" = "date-time")))]
1900 #[serde(default)]
1901 pub updated_at: Option<String>,
1902 #[serde_as(deserialize_as = "DefaultOnError")]
1908 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1909 #[serde(default)]
1910 #[serde(rename = "_meta")]
1911 pub meta: Option<Meta>,
1912}
1913
1914impl SessionInfo {
1915 #[must_use]
1917 pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<AbsolutePath>) -> Self {
1918 Self {
1919 session_id: session_id.into(),
1920 cwd: cwd.into(),
1921 additional_directories: vec![],
1922 title: None,
1923 updated_at: None,
1924 meta: None,
1925 }
1926 }
1927
1928 #[must_use]
1930 pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
1931 where
1932 I: IntoIterator<Item = P>,
1933 P: Into<AbsolutePath>,
1934 {
1935 self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
1936 self
1937 }
1938
1939 #[must_use]
1941 pub fn title(mut self, title: impl IntoOption<String>) -> Self {
1942 self.title = title.into_option();
1943 self
1944 }
1945
1946 #[must_use]
1948 pub fn updated_at(mut self, updated_at: impl IntoOption<String>) -> Self {
1949 self.updated_at = updated_at.into_option();
1950 self
1951 }
1952
1953 #[must_use]
1959 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1960 self.meta = meta.into_option();
1961 self
1962 }
1963}
1964
1965#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1969#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, From, Display)]
1970#[serde(transparent)]
1971#[from(forward)]
1972#[non_exhaustive]
1973pub struct SessionConfigId(pub Arc<str>);
1974
1975impl SessionConfigId {
1976 #[must_use]
1978 pub fn new(id: impl Into<Self>) -> Self {
1979 id.into()
1980 }
1981}
1982
1983#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1985#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, From, Display)]
1986#[serde(transparent)]
1987#[from(forward)]
1988#[non_exhaustive]
1989pub struct SessionConfigValueId(pub Arc<str>);
1990
1991impl SessionConfigValueId {
1992 #[must_use]
1994 pub fn new(id: impl Into<Self>) -> Self {
1995 id.into()
1996 }
1997}
1998
1999#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2001#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, From, Display)]
2002#[serde(transparent)]
2003#[from(forward)]
2004#[non_exhaustive]
2005pub struct SessionConfigGroupId(pub Arc<str>);
2006
2007impl SessionConfigGroupId {
2008 #[must_use]
2010 pub fn new(id: impl Into<Self>) -> Self {
2011 id.into()
2012 }
2013}
2014
2015#[serde_as]
2017#[skip_serializing_none]
2018#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2019#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2020#[serde(rename_all = "camelCase")]
2021#[non_exhaustive]
2022pub struct SessionConfigSelectOption {
2023 pub value: SessionConfigValueId,
2025 pub name: String,
2027 #[serde_as(deserialize_as = "DefaultOnError")]
2029 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2030 #[serde(default)]
2031 pub description: Option<String>,
2032 #[serde_as(deserialize_as = "DefaultOnError")]
2038 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2039 #[serde(default)]
2040 #[serde(rename = "_meta")]
2041 pub meta: Option<Meta>,
2042}
2043
2044impl SessionConfigSelectOption {
2045 #[must_use]
2047 pub fn new(value: impl Into<SessionConfigValueId>, name: impl Into<String>) -> Self {
2048 Self {
2049 value: value.into(),
2050 name: name.into(),
2051 description: None,
2052 meta: None,
2053 }
2054 }
2055
2056 #[must_use]
2058 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
2059 self.description = description.into_option();
2060 self
2061 }
2062
2063 #[must_use]
2069 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2070 self.meta = meta.into_option();
2071 self
2072 }
2073}
2074
2075#[serde_as]
2077#[skip_serializing_none]
2078#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2079#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2080#[serde(rename_all = "camelCase")]
2081#[non_exhaustive]
2082pub struct SessionConfigSelectGroup {
2083 pub group_id: SessionConfigGroupId,
2085 pub name: String,
2087 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
2089 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
2090 pub options: Vec<SessionConfigSelectOption>,
2091 #[serde_as(deserialize_as = "DefaultOnError")]
2097 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2098 #[serde(default)]
2099 #[serde(rename = "_meta")]
2100 pub meta: Option<Meta>,
2101}
2102
2103impl SessionConfigSelectGroup {
2104 #[must_use]
2106 pub fn new(
2107 group_id: impl Into<SessionConfigGroupId>,
2108 name: impl Into<String>,
2109 options: Vec<SessionConfigSelectOption>,
2110 ) -> Self {
2111 Self {
2112 group_id: group_id.into(),
2113 name: name.into(),
2114 options,
2115 meta: None,
2116 }
2117 }
2118
2119 #[must_use]
2125 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2126 self.meta = meta.into_option();
2127 self
2128 }
2129}
2130
2131#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2133#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2134#[serde(untagged)]
2135#[non_exhaustive]
2136pub enum SessionConfigSelectOptions {
2137 Ungrouped(Vec<SessionConfigSelectOption>),
2139 Grouped(Vec<SessionConfigSelectGroup>),
2141}
2142
2143impl From<Vec<SessionConfigSelectOption>> for SessionConfigSelectOptions {
2144 fn from(options: Vec<SessionConfigSelectOption>) -> Self {
2145 SessionConfigSelectOptions::Ungrouped(options)
2146 }
2147}
2148
2149impl From<Vec<SessionConfigSelectGroup>> for SessionConfigSelectOptions {
2150 fn from(groups: Vec<SessionConfigSelectGroup>) -> Self {
2151 SessionConfigSelectOptions::Grouped(groups)
2152 }
2153}
2154
2155#[skip_serializing_none]
2157#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2158#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2159#[serde(rename_all = "camelCase")]
2160#[non_exhaustive]
2161pub struct SessionConfigSelect {
2162 pub current_value: SessionConfigValueId,
2164 pub options: SessionConfigSelectOptions,
2166}
2167
2168impl SessionConfigSelect {
2169 #[must_use]
2171 pub fn new(
2172 current_value: impl Into<SessionConfigValueId>,
2173 options: impl Into<SessionConfigSelectOptions>,
2174 ) -> Self {
2175 Self {
2176 current_value: current_value.into(),
2177 options: options.into(),
2178 }
2179 }
2180}
2181
2182#[skip_serializing_none]
2184#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2185#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2186#[serde(rename_all = "camelCase")]
2187#[non_exhaustive]
2188pub struct SessionConfigBoolean {
2189 pub current_value: bool,
2191}
2192
2193impl SessionConfigBoolean {
2194 #[must_use]
2196 pub fn new(current_value: bool) -> Self {
2197 Self { current_value }
2198 }
2199}
2200
2201#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2211#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2212#[serde(rename_all = "snake_case")]
2213#[non_exhaustive]
2214pub enum SessionConfigOptionCategory {
2215 Mode,
2217 Model,
2219 ModelConfig,
2221 ThoughtLevel,
2223 #[serde(untagged)]
2229 Other(String),
2230}
2231
2232#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2234#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2235#[serde(tag = "type", rename_all = "snake_case")]
2236#[non_exhaustive]
2237pub enum SessionConfigKind {
2238 Select(SessionConfigSelect),
2240 Boolean(SessionConfigBoolean),
2242 #[serde(untagged)]
2252 Other(OtherSessionConfigKind),
2253}
2254
2255#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2257#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
2258#[cfg_attr(feature = "schemars", schemars(inline))]
2259#[cfg_attr(feature = "schemars", schemars(transform = other_session_config_kind_schema))]
2260#[serde(rename_all = "camelCase")]
2261#[non_exhaustive]
2262pub struct OtherSessionConfigKind {
2263 #[serde(rename = "type")]
2269 pub type_: String,
2270 #[serde(flatten)]
2272 pub fields: BTreeMap<String, serde_json::Value>,
2273}
2274
2275impl OtherSessionConfigKind {
2276 #[must_use]
2278 pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
2279 fields.remove("type");
2280 fields.remove("_meta");
2281 Self {
2282 type_: type_.into(),
2283 fields,
2284 }
2285 }
2286}
2287
2288impl<'de> Deserialize<'de> for OtherSessionConfigKind {
2289 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2290 where
2291 D: serde::Deserializer<'de>,
2292 {
2293 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
2294 let type_ = fields
2295 .remove("type")
2296 .ok_or_else(|| serde::de::Error::missing_field("type"))?;
2297 let serde_json::Value::String(type_) = type_ else {
2298 return Err(serde::de::Error::custom("`type` must be a string"));
2299 };
2300
2301 if is_known_session_config_kind_type(&type_) {
2302 return Err(serde::de::Error::custom(format!(
2303 "known session configuration option `{type_}` did not match its schema"
2304 )));
2305 }
2306
2307 Ok(Self { type_, fields })
2308 }
2309}
2310
2311fn is_known_session_config_kind_type(type_: &str) -> bool {
2312 matches!(type_, "select" | "boolean")
2313}
2314
2315#[cfg(feature = "schemars")]
2316fn other_session_config_kind_schema(schema: &mut Schema) {
2317 super::schema_util::reject_known_string_discriminators(schema, "type", &["select", "boolean"]);
2318}
2319
2320#[serde_as]
2322#[skip_serializing_none]
2323#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2324#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2325#[serde(rename_all = "camelCase")]
2326#[non_exhaustive]
2327pub struct SessionConfigOption {
2328 pub config_id: SessionConfigId,
2330 pub name: String,
2332 #[serde_as(deserialize_as = "DefaultOnError")]
2334 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2335 #[serde(default)]
2336 pub description: Option<String>,
2337 #[serde_as(deserialize_as = "DefaultOnError")]
2339 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2340 #[serde(default)]
2341 pub category: Option<SessionConfigOptionCategory>,
2342 #[serde(flatten)]
2344 pub kind: SessionConfigKind,
2345 #[serde_as(deserialize_as = "DefaultOnError")]
2351 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2352 #[serde(default)]
2353 #[serde(rename = "_meta")]
2354 pub meta: Option<Meta>,
2355}
2356
2357impl SessionConfigOption {
2358 #[must_use]
2360 pub fn new(
2361 config_id: impl Into<SessionConfigId>,
2362 name: impl Into<String>,
2363 kind: SessionConfigKind,
2364 ) -> Self {
2365 Self {
2366 config_id: config_id.into(),
2367 name: name.into(),
2368 description: None,
2369 category: None,
2370 kind,
2371 meta: None,
2372 }
2373 }
2374
2375 #[must_use]
2377 pub fn select(
2378 config_id: impl Into<SessionConfigId>,
2379 name: impl Into<String>,
2380 current_value: impl Into<SessionConfigValueId>,
2381 options: impl Into<SessionConfigSelectOptions>,
2382 ) -> Self {
2383 Self::new(
2384 config_id,
2385 name,
2386 SessionConfigKind::Select(SessionConfigSelect::new(current_value, options)),
2387 )
2388 }
2389
2390 #[must_use]
2392 pub fn boolean(
2393 config_id: impl Into<SessionConfigId>,
2394 name: impl Into<String>,
2395 current_value: bool,
2396 ) -> Self {
2397 Self::new(
2398 config_id,
2399 name,
2400 SessionConfigKind::Boolean(SessionConfigBoolean::new(current_value)),
2401 )
2402 }
2403
2404 #[must_use]
2406 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
2407 self.description = description.into_option();
2408 self
2409 }
2410
2411 #[must_use]
2413 pub fn category(mut self, category: impl IntoOption<SessionConfigOptionCategory>) -> Self {
2414 self.category = category.into_option();
2415 self
2416 }
2417
2418 #[must_use]
2424 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2425 self.meta = meta.into_option();
2426 self
2427 }
2428}
2429
2430#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2439#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
2440#[serde(tag = "type", rename_all = "snake_case")]
2441#[non_exhaustive]
2442pub enum SessionConfigOptionValue {
2443 Id {
2445 value: SessionConfigValueId,
2447 },
2448 Boolean {
2450 value: bool,
2452 },
2453 #[serde(untagged)]
2459 Other(OtherSessionConfigOptionValue),
2460}
2461
2462#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2464#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
2465#[cfg_attr(feature = "schemars", schemars(inline))]
2466#[cfg_attr(feature = "schemars", schemars(transform = other_session_config_option_value_schema))]
2467#[serde(rename_all = "camelCase")]
2468#[non_exhaustive]
2469pub struct OtherSessionConfigOptionValue {
2470 #[serde(rename = "type")]
2476 pub type_: String,
2477 pub value: serde_json::Value,
2479 #[serde(flatten)]
2481 pub fields: BTreeMap<String, serde_json::Value>,
2482}
2483
2484impl OtherSessionConfigOptionValue {
2485 #[must_use]
2487 pub fn new(
2488 type_: impl Into<String>,
2489 value: serde_json::Value,
2490 mut fields: BTreeMap<String, serde_json::Value>,
2491 ) -> Self {
2492 fields.remove("type");
2493 fields.remove("value");
2494 fields.remove("_meta");
2495 Self {
2496 type_: type_.into(),
2497 value,
2498 fields,
2499 }
2500 }
2501}
2502
2503impl<'de> Deserialize<'de> for OtherSessionConfigOptionValue {
2504 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2505 where
2506 D: serde::Deserializer<'de>,
2507 {
2508 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
2509 let type_ = fields
2510 .remove("type")
2511 .ok_or_else(|| serde::de::Error::missing_field("type"))?;
2512 let serde_json::Value::String(type_) = type_ else {
2513 return Err(serde::de::Error::custom("`type` must be a string"));
2514 };
2515
2516 if is_known_session_config_option_value_type(&type_) {
2517 return Err(serde::de::Error::custom(format!(
2518 "known session configuration option value `{type_}` did not match its schema"
2519 )));
2520 }
2521
2522 let value = fields
2523 .remove("value")
2524 .ok_or_else(|| serde::de::Error::missing_field("value"))?;
2525
2526 Ok(Self {
2527 type_,
2528 value,
2529 fields,
2530 })
2531 }
2532}
2533
2534impl<'de> Deserialize<'de> for SessionConfigOptionValue {
2535 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2536 where
2537 D: serde::Deserializer<'de>,
2538 {
2539 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
2540 let type_ = fields.remove("type");
2541 let value = fields
2542 .remove("value")
2543 .ok_or_else(|| serde::de::Error::missing_field("value"))?;
2544
2545 let type_ = type_.ok_or_else(|| serde::de::Error::missing_field("type"))?;
2546
2547 let serde_json::Value::String(type_) = type_ else {
2548 return Err(serde::de::Error::custom("`type` must be a string"));
2549 };
2550
2551 match type_.as_str() {
2552 "id" => {
2553 let value = serde_json::from_value(value).map_err(|error| {
2554 serde::de::Error::custom(format!(
2555 "`value` must be a string for `type: id`: {error}"
2556 ))
2557 })?;
2558 Ok(Self::Id { value })
2559 }
2560 "boolean" => {
2561 let value = serde_json::from_value(value).map_err(|error| {
2562 serde::de::Error::custom(format!(
2563 "`value` must be a boolean for `type: boolean`: {error}"
2564 ))
2565 })?;
2566 Ok(Self::Boolean { value })
2567 }
2568 _ => Ok(Self::Other(OtherSessionConfigOptionValue {
2569 type_,
2570 value,
2571 fields,
2572 })),
2573 }
2574 }
2575}
2576
2577fn is_known_session_config_option_value_type(type_: &str) -> bool {
2578 matches!(type_, "id" | "boolean")
2579}
2580
2581#[cfg(feature = "schemars")]
2582fn other_session_config_option_value_schema(schema: &mut Schema) {
2583 super::schema_util::reject_known_string_discriminators(schema, "type", &["id", "boolean"]);
2584}
2585
2586impl SessionConfigOptionValue {
2587 #[must_use]
2589 pub fn id(id: impl Into<SessionConfigValueId>) -> Self {
2590 Self::Id { value: id.into() }
2591 }
2592
2593 #[must_use]
2595 pub fn boolean(val: bool) -> Self {
2596 Self::Boolean { value: val }
2597 }
2598
2599 #[must_use]
2602 pub fn as_id(&self) -> Option<&SessionConfigValueId> {
2603 match self {
2604 Self::Id { value } => Some(value),
2605 _ => None,
2606 }
2607 }
2608
2609 #[must_use]
2611 pub fn as_bool(&self) -> Option<bool> {
2612 match self {
2613 Self::Boolean { value } => Some(*value),
2614 _ => None,
2615 }
2616 }
2617}
2618
2619impl From<SessionConfigValueId> for SessionConfigOptionValue {
2620 fn from(value: SessionConfigValueId) -> Self {
2621 Self::Id { value }
2622 }
2623}
2624
2625impl From<bool> for SessionConfigOptionValue {
2626 fn from(value: bool) -> Self {
2627 Self::Boolean { value }
2628 }
2629}
2630
2631impl From<&str> for SessionConfigOptionValue {
2632 fn from(value: &str) -> Self {
2633 Self::Id {
2634 value: SessionConfigValueId::new(value),
2635 }
2636 }
2637}
2638
2639#[serde_as]
2641#[skip_serializing_none]
2642#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2643#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2644#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_CONFIG_OPTION_METHOD_NAME)))]
2645#[serde(rename_all = "camelCase")]
2646#[non_exhaustive]
2647pub struct SetSessionConfigOptionRequest {
2648 pub session_id: SessionId,
2650 pub config_id: SessionConfigId,
2652 #[serde(flatten)]
2656 pub value: SessionConfigOptionValue,
2657 #[serde_as(deserialize_as = "DefaultOnError")]
2663 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2664 #[serde(default)]
2665 #[serde(rename = "_meta")]
2666 pub meta: Option<Meta>,
2667}
2668
2669impl SetSessionConfigOptionRequest {
2670 #[must_use]
2672 pub fn new(
2673 session_id: impl Into<SessionId>,
2674 config_id: impl Into<SessionConfigId>,
2675 value: impl Into<SessionConfigOptionValue>,
2676 ) -> Self {
2677 Self {
2678 session_id: session_id.into(),
2679 config_id: config_id.into(),
2680 value: value.into(),
2681 meta: None,
2682 }
2683 }
2684
2685 #[must_use]
2691 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2692 self.meta = meta.into_option();
2693 self
2694 }
2695}
2696
2697#[serde_as]
2699#[skip_serializing_none]
2700#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2701#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2702#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_CONFIG_OPTION_METHOD_NAME)))]
2703#[serde(rename_all = "camelCase")]
2704#[non_exhaustive]
2705pub struct SetSessionConfigOptionResponse {
2706 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
2708 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
2709 pub config_options: Vec<SessionConfigOption>,
2710 #[serde_as(deserialize_as = "DefaultOnError")]
2716 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2717 #[serde(default)]
2718 #[serde(rename = "_meta")]
2719 pub meta: Option<Meta>,
2720}
2721
2722impl SetSessionConfigOptionResponse {
2723 #[must_use]
2725 pub fn new(config_options: Vec<SessionConfigOption>) -> Self {
2726 Self {
2727 config_options,
2728 meta: None,
2729 }
2730 }
2731
2732 #[must_use]
2738 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2739 self.meta = meta.into_option();
2740 self
2741 }
2742}
2743
2744#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2753#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2754#[serde(tag = "type", rename_all = "snake_case")]
2755#[non_exhaustive]
2756pub enum McpServer {
2757 Http(McpServerHttp),
2761 #[cfg(feature = "unstable_mcp_over_acp")]
2770 Acp(McpServerAcp),
2771 Stdio(McpServerStdio),
2775 #[serde(untagged)]
2785 Other(OtherMcpServer),
2786}
2787
2788#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2790#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
2791#[cfg_attr(feature = "schemars", schemars(inline))]
2792#[cfg_attr(feature = "schemars", schemars(transform = other_mcp_server_schema))]
2793#[serde(rename_all = "camelCase")]
2794#[non_exhaustive]
2795pub struct OtherMcpServer {
2796 #[serde(rename = "type")]
2802 pub type_: String,
2803 #[serde(flatten)]
2805 pub fields: BTreeMap<String, serde_json::Value>,
2806}
2807
2808impl OtherMcpServer {
2809 #[must_use]
2811 pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
2812 fields.remove("type");
2813 Self {
2814 type_: type_.into(),
2815 fields,
2816 }
2817 }
2818}
2819
2820impl<'de> Deserialize<'de> for OtherMcpServer {
2821 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2822 where
2823 D: serde::Deserializer<'de>,
2824 {
2825 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
2826 let type_ = fields
2827 .remove("type")
2828 .ok_or_else(|| serde::de::Error::missing_field("type"))?;
2829 let serde_json::Value::String(type_) = type_ else {
2830 return Err(serde::de::Error::custom("`type` must be a string"));
2831 };
2832
2833 if is_known_mcp_server_type(&type_) {
2834 return Err(serde::de::Error::custom(format!(
2835 "known MCP server transport `{type_}` did not match its schema"
2836 )));
2837 }
2838
2839 Ok(Self { type_, fields })
2840 }
2841}
2842
2843fn is_known_mcp_server_type(type_: &str) -> bool {
2844 match type_ {
2845 "http" | "stdio" => true,
2846 #[cfg(feature = "unstable_mcp_over_acp")]
2847 "acp" => true,
2848 _ => false,
2849 }
2850}
2851
2852#[cfg(feature = "schemars")]
2853fn other_mcp_server_schema(schema: &mut Schema) {
2854 super::schema_util::reject_known_string_discriminators(
2855 schema,
2856 "type",
2857 &[
2858 "http",
2859 "stdio",
2860 #[cfg(feature = "unstable_mcp_over_acp")]
2861 "acp",
2862 ],
2863 );
2864}
2865
2866#[serde_as]
2868#[skip_serializing_none]
2869#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2870#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2871#[serde(rename_all = "camelCase")]
2872#[non_exhaustive]
2873pub struct McpServerHttp {
2874 pub name: String,
2876 #[cfg_attr(feature = "schemars", schemars(url))]
2878 pub url: String,
2879 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2881 pub headers: Vec<HttpHeader>,
2882 #[serde_as(deserialize_as = "DefaultOnError")]
2888 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2889 #[serde(default)]
2890 #[serde(rename = "_meta")]
2891 pub meta: Option<Meta>,
2892}
2893
2894impl McpServerHttp {
2895 #[must_use]
2897 pub fn new(name: impl Into<String>, url: impl Into<String>) -> Self {
2898 Self {
2899 name: name.into(),
2900 url: url.into(),
2901 headers: Vec::new(),
2902 meta: None,
2903 }
2904 }
2905
2906 #[must_use]
2908 pub fn headers(mut self, headers: Vec<HttpHeader>) -> Self {
2909 self.headers = headers;
2910 self
2911 }
2912
2913 #[must_use]
2919 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2920 self.meta = meta.into_option();
2921 self
2922 }
2923}
2924
2925#[cfg(feature = "unstable_mcp_over_acp")]
2935#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2936#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
2937#[serde(transparent)]
2938#[from(forward)]
2939#[non_exhaustive]
2940pub struct McpServerAcpId(pub Arc<str>);
2941
2942#[cfg(feature = "unstable_mcp_over_acp")]
2943impl McpServerAcpId {
2944 #[must_use]
2946 pub fn new(id: impl Into<Self>) -> Self {
2947 id.into()
2948 }
2949}
2950
2951#[serde_as]
2960#[skip_serializing_none]
2961#[cfg(feature = "unstable_mcp_over_acp")]
2962#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2963#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2964#[serde(rename_all = "camelCase")]
2965#[non_exhaustive]
2966pub struct McpServerAcp {
2967 pub name: String,
2969 pub server_id: McpServerAcpId,
2974 #[serde_as(deserialize_as = "DefaultOnError")]
2980 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2981 #[serde(default)]
2982 #[serde(rename = "_meta")]
2983 pub meta: Option<Meta>,
2984}
2985
2986#[cfg(feature = "unstable_mcp_over_acp")]
2987impl McpServerAcp {
2988 #[must_use]
2990 pub fn new(name: impl Into<String>, server_id: impl Into<McpServerAcpId>) -> Self {
2991 Self {
2992 name: name.into(),
2993 server_id: server_id.into(),
2994 meta: None,
2995 }
2996 }
2997
2998 #[must_use]
3004 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3005 self.meta = meta.into_option();
3006 self
3007 }
3008}
3009
3010#[serde_as]
3012#[skip_serializing_none]
3013#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3014#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3015#[serde(rename_all = "camelCase")]
3016#[non_exhaustive]
3017pub struct McpServerStdio {
3018 pub name: String,
3020 pub command: AbsolutePath,
3022 #[serde(default, skip_serializing_if = "Vec::is_empty")]
3024 pub args: Vec<String>,
3025 #[serde(default, skip_serializing_if = "Vec::is_empty")]
3027 pub env: Vec<EnvVariable>,
3028 #[serde_as(deserialize_as = "DefaultOnError")]
3034 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3035 #[serde(default)]
3036 #[serde(rename = "_meta")]
3037 pub meta: Option<Meta>,
3038}
3039
3040impl McpServerStdio {
3041 #[must_use]
3043 pub fn new(name: impl Into<String>, command: impl Into<AbsolutePath>) -> Self {
3044 Self {
3045 name: name.into(),
3046 command: command.into(),
3047 args: Vec::new(),
3048 env: Vec::new(),
3049 meta: None,
3050 }
3051 }
3052
3053 #[must_use]
3055 pub fn args(mut self, args: Vec<String>) -> Self {
3056 self.args = args;
3057 self
3058 }
3059
3060 #[must_use]
3062 pub fn env(mut self, env: Vec<EnvVariable>) -> Self {
3063 self.env = env;
3064 self
3065 }
3066
3067 #[must_use]
3073 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3074 self.meta = meta.into_option();
3075 self
3076 }
3077}
3078
3079#[serde_as]
3081#[skip_serializing_none]
3082#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3083#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3084#[serde(rename_all = "camelCase")]
3085#[non_exhaustive]
3086pub struct EnvVariable {
3087 pub name: String,
3089 pub value: String,
3091 #[serde_as(deserialize_as = "DefaultOnError")]
3097 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3098 #[serde(default)]
3099 #[serde(rename = "_meta")]
3100 pub meta: Option<Meta>,
3101}
3102
3103impl EnvVariable {
3104 #[must_use]
3106 pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
3107 Self {
3108 name: name.into(),
3109 value: value.into(),
3110 meta: None,
3111 }
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 HttpHeader {
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 HttpHeader {
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]
3181#[skip_serializing_none]
3182#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3183#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
3184#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_PROMPT_METHOD_NAME)))]
3185#[serde(rename_all = "camelCase")]
3186#[non_exhaustive]
3187pub struct PromptRequest {
3188 pub session_id: SessionId,
3190 pub prompt: Vec<ContentBlock>,
3204 #[serde_as(deserialize_as = "DefaultOnError")]
3210 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3211 #[serde(default)]
3212 #[serde(rename = "_meta")]
3213 pub meta: Option<Meta>,
3214}
3215
3216impl PromptRequest {
3217 #[must_use]
3219 pub fn new(session_id: impl Into<SessionId>, prompt: Vec<ContentBlock>) -> Self {
3220 Self {
3221 session_id: session_id.into(),
3222 prompt,
3223 meta: None,
3224 }
3225 }
3226
3227 #[must_use]
3233 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3234 self.meta = meta.into_option();
3235 self
3236 }
3237}
3238
3239#[serde_as]
3247#[skip_serializing_none]
3248#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3249#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3250#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_PROMPT_METHOD_NAME)))]
3251#[serde(rename_all = "camelCase")]
3252#[non_exhaustive]
3253pub struct PromptResponse {
3254 pub message_id: MessageId,
3262 #[serde_as(deserialize_as = "DefaultOnError")]
3268 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3269 #[serde(default)]
3270 #[serde(rename = "_meta")]
3271 pub meta: Option<Meta>,
3272}
3273
3274impl PromptResponse {
3275 #[must_use]
3277 pub fn new(message_id: impl Into<MessageId>) -> Self {
3278 Self {
3279 message_id: message_id.into(),
3280 meta: None,
3281 }
3282 }
3283
3284 #[must_use]
3290 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3291 self.meta = meta.into_option();
3292 self
3293 }
3294}
3295
3296#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3300#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
3301#[serde(rename_all = "snake_case")]
3302#[non_exhaustive]
3303pub enum StopReason {
3304 EndTurn,
3306 MaxTokens,
3308 MaxTurnRequests,
3311 Refusal,
3315 Cancelled,
3321 #[serde(untagged)]
3327 Other(String),
3328}
3329
3330#[cfg(feature = "unstable_end_turn_token_usage")]
3336#[serde_as]
3337#[skip_serializing_none]
3338#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3339#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3340#[serde(rename_all = "camelCase")]
3341#[non_exhaustive]
3342pub struct Usage {
3343 pub total_tokens: u64,
3345 pub input_tokens: u64,
3347 pub output_tokens: u64,
3349 #[serde_as(deserialize_as = "DefaultOnError")]
3351 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3352 #[serde(default)]
3353 pub thought_tokens: Option<u64>,
3354 #[serde_as(deserialize_as = "DefaultOnError")]
3356 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3357 #[serde(default)]
3358 pub cached_read_tokens: Option<u64>,
3359 #[serde_as(deserialize_as = "DefaultOnError")]
3361 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3362 #[serde(default)]
3363 pub cached_write_tokens: Option<u64>,
3364 #[serde_as(deserialize_as = "DefaultOnError")]
3370 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3371 #[serde(default)]
3372 #[serde(rename = "_meta")]
3373 pub meta: Option<Meta>,
3374}
3375
3376#[cfg(feature = "unstable_end_turn_token_usage")]
3377impl Usage {
3378 #[must_use]
3380 pub fn new(total_tokens: u64, input_tokens: u64, output_tokens: u64) -> Self {
3381 Self {
3382 total_tokens,
3383 input_tokens,
3384 output_tokens,
3385 thought_tokens: None,
3386 cached_read_tokens: None,
3387 cached_write_tokens: None,
3388 meta: None,
3389 }
3390 }
3391
3392 #[must_use]
3394 pub fn thought_tokens(mut self, thought_tokens: impl IntoOption<u64>) -> Self {
3395 self.thought_tokens = thought_tokens.into_option();
3396 self
3397 }
3398
3399 #[must_use]
3401 pub fn cached_read_tokens(mut self, cached_read_tokens: impl IntoOption<u64>) -> Self {
3402 self.cached_read_tokens = cached_read_tokens.into_option();
3403 self
3404 }
3405
3406 #[must_use]
3408 pub fn cached_write_tokens(mut self, cached_write_tokens: impl IntoOption<u64>) -> Self {
3409 self.cached_write_tokens = cached_write_tokens.into_option();
3410 self
3411 }
3412
3413 #[must_use]
3419 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3420 self.meta = meta.into_option();
3421 self
3422 }
3423}
3424
3425#[cfg(feature = "unstable_llm_providers")]
3438#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3439#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3440#[serde(rename_all = "snake_case")]
3441#[non_exhaustive]
3442#[expect(clippy::doc_markdown)]
3443pub enum LlmProtocol {
3444 Anthropic,
3446 #[serde(rename = "openai")]
3448 OpenAi,
3449 Azure,
3451 Vertex,
3453 Bedrock,
3455 #[serde(untagged)]
3461 Other(String),
3462}
3463
3464#[cfg(feature = "unstable_llm_providers")]
3470#[serde_as]
3471#[skip_serializing_none]
3472#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3473#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3474#[serde(rename_all = "camelCase")]
3475#[non_exhaustive]
3476pub struct ProviderCurrentConfig {
3477 pub api_type: LlmProtocol,
3479 #[cfg_attr(feature = "schemars", schemars(url))]
3481 pub base_url: String,
3482 #[serde_as(deserialize_as = "DefaultOnError")]
3488 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3489 #[serde(default)]
3490 #[serde(rename = "_meta")]
3491 pub meta: Option<Meta>,
3492}
3493
3494#[cfg(feature = "unstable_llm_providers")]
3495impl ProviderCurrentConfig {
3496 #[must_use]
3498 pub fn new(api_type: LlmProtocol, base_url: impl Into<String>) -> Self {
3499 Self {
3500 api_type,
3501 base_url: base_url.into(),
3502 meta: None,
3503 }
3504 }
3505
3506 #[must_use]
3512 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3513 self.meta = meta.into_option();
3514 self
3515 }
3516}
3517
3518#[cfg(feature = "unstable_llm_providers")]
3524#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3525#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
3526#[serde(transparent)]
3527#[from(forward)]
3528#[non_exhaustive]
3529pub struct ProviderId(pub Arc<str>);
3530
3531#[cfg(feature = "unstable_llm_providers")]
3532impl ProviderId {
3533 #[must_use]
3535 pub fn new(id: impl Into<Self>) -> Self {
3536 id.into()
3537 }
3538}
3539
3540#[cfg(feature = "unstable_llm_providers")]
3546#[serde_as]
3547#[skip_serializing_none]
3548#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3549#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3550#[serde(rename_all = "camelCase")]
3551#[non_exhaustive]
3552pub struct ProviderInfo {
3553 pub provider_id: ProviderId,
3555 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
3557 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
3558 pub supported: Vec<LlmProtocol>,
3559 pub required: bool,
3562 #[serde(default)]
3565 pub current: Option<ProviderCurrentConfig>,
3566 #[serde_as(deserialize_as = "DefaultOnError")]
3572 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3573 #[serde(default)]
3574 #[serde(rename = "_meta")]
3575 pub meta: Option<Meta>,
3576}
3577
3578#[cfg(feature = "unstable_llm_providers")]
3579impl ProviderInfo {
3580 #[must_use]
3582 pub fn new(
3583 provider_id: impl Into<ProviderId>,
3584 supported: Vec<LlmProtocol>,
3585 required: bool,
3586 current: impl IntoOption<ProviderCurrentConfig>,
3587 ) -> Self {
3588 Self {
3589 provider_id: provider_id.into(),
3590 supported,
3591 required,
3592 current: current.into_option(),
3593 meta: None,
3594 }
3595 }
3596
3597 #[must_use]
3603 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3604 self.meta = meta.into_option();
3605 self
3606 }
3607}
3608
3609#[cfg(feature = "unstable_llm_providers")]
3610crate::serde_util::default_on_null! {
3611 #[serde_as]
3617 #[skip_serializing_none]
3618 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3619 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
3620 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_LIST_METHOD_NAME)))]
3621 #[serde(rename_all = "camelCase")]
3622 #[non_exhaustive]
3623 pub struct ListProvidersRequest {
3624 #[serde_as(deserialize_as = "DefaultOnError")]
3630 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3631 #[serde(default)]
3632 #[serde(rename = "_meta")]
3633 pub meta: Option<Meta>,
3634 }
3635}
3636
3637#[cfg(feature = "unstable_llm_providers")]
3638impl ListProvidersRequest {
3639 #[must_use]
3641 pub fn new() -> Self {
3642 Self::default()
3643 }
3644
3645 #[must_use]
3651 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3652 self.meta = meta.into_option();
3653 self
3654 }
3655}
3656
3657#[cfg(feature = "unstable_llm_providers")]
3663#[serde_as]
3664#[skip_serializing_none]
3665#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3666#[derive(Debug, Clone, Serialize, Deserialize, 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]
3670pub struct ListProvidersResponse {
3671 pub providers: Vec<ProviderInfo>,
3673 #[serde_as(deserialize_as = "DefaultOnError")]
3679 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3680 #[serde(default)]
3681 #[serde(rename = "_meta")]
3682 pub meta: Option<Meta>,
3683}
3684
3685#[cfg(feature = "unstable_llm_providers")]
3686impl ListProvidersResponse {
3687 #[must_use]
3689 pub fn new(providers: Vec<ProviderInfo>) -> Self {
3690 Self {
3691 providers,
3692 meta: None,
3693 }
3694 }
3695
3696 #[must_use]
3702 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3703 self.meta = meta.into_option();
3704 self
3705 }
3706}
3707
3708#[cfg(feature = "unstable_llm_providers")]
3716#[serde_as]
3717#[skip_serializing_none]
3718#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3719#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
3720#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_SET_METHOD_NAME)))]
3721#[serde(rename_all = "camelCase")]
3722#[non_exhaustive]
3723pub struct SetProviderRequest {
3724 pub provider_id: ProviderId,
3726 pub api_type: LlmProtocol,
3728 #[cfg_attr(feature = "schemars", schemars(url))]
3730 pub base_url: String,
3731 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
3734 pub headers: HashMap<String, String>,
3735 #[serde_as(deserialize_as = "DefaultOnError")]
3741 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3742 #[serde(default)]
3743 #[serde(rename = "_meta")]
3744 pub meta: Option<Meta>,
3745}
3746
3747#[cfg(feature = "unstable_llm_providers")]
3748impl SetProviderRequest {
3749 #[must_use]
3751 pub fn new(
3752 provider_id: impl Into<ProviderId>,
3753 api_type: LlmProtocol,
3754 base_url: impl Into<String>,
3755 ) -> Self {
3756 Self {
3757 provider_id: provider_id.into(),
3758 api_type,
3759 base_url: base_url.into(),
3760 headers: HashMap::new(),
3761 meta: None,
3762 }
3763 }
3764
3765 #[must_use]
3768 pub fn headers(mut self, headers: HashMap<String, String>) -> Self {
3769 self.headers = headers;
3770 self
3771 }
3772
3773 #[must_use]
3779 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3780 self.meta = meta.into_option();
3781 self
3782 }
3783}
3784
3785#[cfg(feature = "unstable_llm_providers")]
3786crate::serde_util::default_on_null! {
3787 #[serde_as]
3793 #[skip_serializing_none]
3794 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3795 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
3796 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_SET_METHOD_NAME)))]
3797 #[serde(rename_all = "camelCase")]
3798 #[non_exhaustive]
3799 pub struct SetProviderResponse {
3800 #[serde_as(deserialize_as = "DefaultOnError")]
3806 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3807 #[serde(default)]
3808 #[serde(rename = "_meta")]
3809 pub meta: Option<Meta>,
3810 }
3811}
3812
3813#[cfg(feature = "unstable_llm_providers")]
3814impl SetProviderResponse {
3815 #[must_use]
3817 pub fn new() -> Self {
3818 Self::default()
3819 }
3820
3821 #[must_use]
3827 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3828 self.meta = meta.into_option();
3829 self
3830 }
3831}
3832
3833#[cfg(feature = "unstable_llm_providers")]
3839#[serde_as]
3840#[skip_serializing_none]
3841#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3842#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3843#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_DISABLE_METHOD_NAME)))]
3844#[serde(rename_all = "camelCase")]
3845#[non_exhaustive]
3846pub struct DisableProviderRequest {
3847 pub provider_id: ProviderId,
3849 #[serde_as(deserialize_as = "DefaultOnError")]
3855 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3856 #[serde(default)]
3857 #[serde(rename = "_meta")]
3858 pub meta: Option<Meta>,
3859}
3860
3861#[cfg(feature = "unstable_llm_providers")]
3862impl DisableProviderRequest {
3863 #[must_use]
3865 pub fn new(provider_id: impl Into<ProviderId>) -> Self {
3866 Self {
3867 provider_id: provider_id.into(),
3868 meta: None,
3869 }
3870 }
3871
3872 #[must_use]
3878 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3879 self.meta = meta.into_option();
3880 self
3881 }
3882}
3883
3884#[cfg(feature = "unstable_llm_providers")]
3885crate::serde_util::default_on_null! {
3886 #[serde_as]
3892 #[skip_serializing_none]
3893 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3894 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
3895 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_DISABLE_METHOD_NAME)))]
3896 #[serde(rename_all = "camelCase")]
3897 #[non_exhaustive]
3898 pub struct DisableProviderResponse {
3899 #[serde_as(deserialize_as = "DefaultOnError")]
3905 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3906 #[serde(default)]
3907 #[serde(rename = "_meta")]
3908 pub meta: Option<Meta>,
3909 }
3910}
3911
3912#[cfg(feature = "unstable_llm_providers")]
3913impl DisableProviderResponse {
3914 #[must_use]
3916 pub fn new() -> Self {
3917 Self::default()
3918 }
3919
3920 #[must_use]
3926 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3927 self.meta = meta.into_option();
3928 self
3929 }
3930}
3931
3932#[serde_as]
3941#[skip_serializing_none]
3942#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3943#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3944#[serde(rename_all = "camelCase")]
3945#[non_exhaustive]
3946pub struct AgentCapabilities {
3947 #[serde_as(deserialize_as = "DefaultOnError")]
3954 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3955 #[serde(default)]
3956 pub session: Option<SessionCapabilities>,
3957 #[serde_as(deserialize_as = "DefaultOnError")]
3964 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3965 #[serde(default)]
3966 pub auth: Option<AgentAuthCapabilities>,
3967 #[cfg(feature = "unstable_llm_providers")]
3976 #[serde_as(deserialize_as = "DefaultOnError")]
3977 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3978 #[serde(default)]
3979 pub providers: Option<ProvidersCapabilities>,
3980 #[cfg(feature = "unstable_nes")]
3989 #[serde_as(deserialize_as = "DefaultOnError")]
3990 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3991 #[serde(default)]
3992 pub nes: Option<NesCapabilities>,
3993 #[cfg(feature = "unstable_nes")]
3999 #[serde_as(deserialize_as = "DefaultOnError")]
4000 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4001 #[serde(default)]
4002 pub position_encoding: Option<PositionEncodingKind>,
4003 #[serde_as(deserialize_as = "DefaultOnError")]
4009 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4010 #[serde(default)]
4011 #[serde(rename = "_meta")]
4012 pub meta: Option<Meta>,
4013}
4014
4015impl AgentCapabilities {
4016 #[must_use]
4018 pub fn new() -> Self {
4019 Self::default()
4020 }
4021
4022 #[must_use]
4029 pub fn session(mut self, session: impl IntoOption<SessionCapabilities>) -> Self {
4030 self.session = session.into_option();
4031 self
4032 }
4033
4034 #[must_use]
4038 pub fn auth(mut self, auth: impl IntoOption<AgentAuthCapabilities>) -> Self {
4039 self.auth = auth.into_option();
4040 self
4041 }
4042
4043 #[cfg(feature = "unstable_llm_providers")]
4049 #[must_use]
4050 pub fn providers(mut self, providers: impl IntoOption<ProvidersCapabilities>) -> Self {
4051 self.providers = providers.into_option();
4052 self
4053 }
4054
4055 #[cfg(feature = "unstable_nes")]
4061 #[must_use]
4062 pub fn nes(mut self, nes: impl IntoOption<NesCapabilities>) -> Self {
4063 self.nes = nes.into_option();
4064 self
4065 }
4066
4067 #[cfg(feature = "unstable_nes")]
4071 #[must_use]
4072 pub fn position_encoding(
4073 mut self,
4074 position_encoding: impl IntoOption<PositionEncodingKind>,
4075 ) -> Self {
4076 self.position_encoding = position_encoding.into_option();
4077 self
4078 }
4079
4080 #[must_use]
4086 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4087 self.meta = meta.into_option();
4088 self
4089 }
4090}
4091
4092#[cfg(feature = "unstable_llm_providers")]
4100#[serde_as]
4101#[skip_serializing_none]
4102#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4103#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4104#[non_exhaustive]
4105pub struct ProvidersCapabilities {
4106 #[serde_as(deserialize_as = "DefaultOnError")]
4112 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4113 #[serde(default)]
4114 #[serde(rename = "_meta")]
4115 pub meta: Option<Meta>,
4116}
4117
4118#[cfg(feature = "unstable_llm_providers")]
4119impl ProvidersCapabilities {
4120 #[must_use]
4122 pub fn new() -> Self {
4123 Self::default()
4124 }
4125
4126 #[must_use]
4132 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4133 self.meta = meta.into_option();
4134 self
4135 }
4136}
4137
4138#[serde_as]
4150#[skip_serializing_none]
4151#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4152#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4153#[serde(rename_all = "camelCase")]
4154#[non_exhaustive]
4155pub struct SessionCapabilities {
4156 #[serde_as(deserialize_as = "DefaultOnError")]
4162 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4163 #[serde(default)]
4164 pub prompt: Option<PromptCapabilities>,
4165 #[serde_as(deserialize_as = "DefaultOnError")]
4170 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4171 #[serde(default)]
4172 pub mcp: Option<McpCapabilities>,
4173 #[serde_as(deserialize_as = "DefaultOnError")]
4178 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4179 #[serde(default)]
4180 pub delete: Option<SessionDeleteCapabilities>,
4181 #[serde_as(deserialize_as = "DefaultOnError")]
4190 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4191 #[serde(default)]
4192 pub additional_directories: Option<SessionAdditionalDirectoriesCapabilities>,
4193 #[cfg(feature = "unstable_session_fork")]
4202 #[serde_as(deserialize_as = "DefaultOnError")]
4203 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4204 #[serde(default)]
4205 pub fork: Option<SessionForkCapabilities>,
4206 #[serde_as(deserialize_as = "DefaultOnError")]
4212 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4213 #[serde(default)]
4214 #[serde(rename = "_meta")]
4215 pub meta: Option<Meta>,
4216}
4217
4218impl SessionCapabilities {
4219 #[must_use]
4221 pub fn new() -> Self {
4222 Self::default()
4223 }
4224
4225 #[must_use]
4231 pub fn prompt(mut self, prompt: impl IntoOption<PromptCapabilities>) -> Self {
4232 self.prompt = prompt.into_option();
4233 self
4234 }
4235
4236 #[must_use]
4241 pub fn mcp(mut self, mcp: impl IntoOption<McpCapabilities>) -> Self {
4242 self.mcp = mcp.into_option();
4243 self
4244 }
4245
4246 #[must_use]
4251 pub fn delete(mut self, delete: impl IntoOption<SessionDeleteCapabilities>) -> Self {
4252 self.delete = delete.into_option();
4253 self
4254 }
4255
4256 #[must_use]
4265 pub fn additional_directories(
4266 mut self,
4267 additional_directories: impl IntoOption<SessionAdditionalDirectoriesCapabilities>,
4268 ) -> Self {
4269 self.additional_directories = additional_directories.into_option();
4270 self
4271 }
4272
4273 #[cfg(feature = "unstable_session_fork")]
4274 #[must_use]
4279 pub fn fork(mut self, fork: impl IntoOption<SessionForkCapabilities>) -> Self {
4280 self.fork = fork.into_option();
4281 self
4282 }
4283
4284 #[must_use]
4290 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4291 self.meta = meta.into_option();
4292 self
4293 }
4294}
4295
4296#[serde_as]
4300#[skip_serializing_none]
4301#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4302#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4303#[non_exhaustive]
4304pub struct SessionDeleteCapabilities {
4305 #[serde_as(deserialize_as = "DefaultOnError")]
4311 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4312 #[serde(default)]
4313 #[serde(rename = "_meta")]
4314 pub meta: Option<Meta>,
4315}
4316
4317impl SessionDeleteCapabilities {
4318 #[must_use]
4320 pub fn new() -> Self {
4321 Self::default()
4322 }
4323
4324 #[must_use]
4330 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4331 self.meta = meta.into_option();
4332 self
4333 }
4334}
4335
4336#[serde_as]
4343#[skip_serializing_none]
4344#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4345#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4346#[non_exhaustive]
4347pub struct SessionAdditionalDirectoriesCapabilities {
4348 #[serde_as(deserialize_as = "DefaultOnError")]
4354 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4355 #[serde(default)]
4356 #[serde(rename = "_meta")]
4357 pub meta: Option<Meta>,
4358}
4359
4360impl SessionAdditionalDirectoriesCapabilities {
4361 #[must_use]
4363 pub fn new() -> Self {
4364 Self::default()
4365 }
4366
4367 #[must_use]
4373 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4374 self.meta = meta.into_option();
4375 self
4376 }
4377}
4378
4379#[cfg(feature = "unstable_session_fork")]
4387#[serde_as]
4388#[skip_serializing_none]
4389#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4390#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4391#[non_exhaustive]
4392pub struct SessionForkCapabilities {
4393 #[serde_as(deserialize_as = "DefaultOnError")]
4399 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4400 #[serde(default)]
4401 #[serde(rename = "_meta")]
4402 pub meta: Option<Meta>,
4403}
4404
4405#[cfg(feature = "unstable_session_fork")]
4406impl SessionForkCapabilities {
4407 #[must_use]
4409 pub fn new() -> Self {
4410 Self::default()
4411 }
4412
4413 #[must_use]
4419 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4420 self.meta = meta.into_option();
4421 self
4422 }
4423}
4424
4425#[serde_as]
4438#[skip_serializing_none]
4439#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4440#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4441#[serde(rename_all = "camelCase")]
4442#[non_exhaustive]
4443pub struct PromptCapabilities {
4444 #[serde_as(deserialize_as = "DefaultOnError")]
4449 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4450 #[serde(default)]
4451 pub image: Option<PromptImageCapabilities>,
4452 #[serde_as(deserialize_as = "DefaultOnError")]
4457 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4458 #[serde(default)]
4459 pub audio: Option<PromptAudioCapabilities>,
4460 #[serde_as(deserialize_as = "DefaultOnError")]
4468 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4469 #[serde(default)]
4470 pub embedded_context: Option<PromptEmbeddedContextCapabilities>,
4471 #[serde_as(deserialize_as = "DefaultOnError")]
4477 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4478 #[serde(default)]
4479 #[serde(rename = "_meta")]
4480 pub meta: Option<Meta>,
4481}
4482
4483impl PromptCapabilities {
4484 #[must_use]
4486 pub fn new() -> Self {
4487 Self::default()
4488 }
4489
4490 #[must_use]
4495 pub fn image(mut self, image: impl IntoOption<PromptImageCapabilities>) -> Self {
4496 self.image = image.into_option();
4497 self
4498 }
4499
4500 #[must_use]
4505 pub fn audio(mut self, audio: impl IntoOption<PromptAudioCapabilities>) -> Self {
4506 self.audio = audio.into_option();
4507 self
4508 }
4509
4510 #[must_use]
4518 pub fn embedded_context(
4519 mut self,
4520 embedded_context: impl IntoOption<PromptEmbeddedContextCapabilities>,
4521 ) -> Self {
4522 self.embedded_context = embedded_context.into_option();
4523 self
4524 }
4525
4526 #[must_use]
4532 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4533 self.meta = meta.into_option();
4534 self
4535 }
4536}
4537
4538#[serde_as]
4542#[skip_serializing_none]
4543#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4544#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4545#[non_exhaustive]
4546pub struct PromptImageCapabilities {
4547 #[serde_as(deserialize_as = "DefaultOnError")]
4553 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4554 #[serde(default)]
4555 #[serde(rename = "_meta")]
4556 pub meta: Option<Meta>,
4557}
4558
4559impl PromptImageCapabilities {
4560 #[must_use]
4562 pub fn new() -> Self {
4563 Self::default()
4564 }
4565
4566 #[must_use]
4572 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4573 self.meta = meta.into_option();
4574 self
4575 }
4576}
4577
4578#[serde_as]
4582#[skip_serializing_none]
4583#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4584#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4585#[non_exhaustive]
4586pub struct PromptAudioCapabilities {
4587 #[serde_as(deserialize_as = "DefaultOnError")]
4593 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4594 #[serde(default)]
4595 #[serde(rename = "_meta")]
4596 pub meta: Option<Meta>,
4597}
4598
4599impl PromptAudioCapabilities {
4600 #[must_use]
4602 pub fn new() -> Self {
4603 Self::default()
4604 }
4605
4606 #[must_use]
4612 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4613 self.meta = meta.into_option();
4614 self
4615 }
4616}
4617
4618#[serde_as]
4622#[skip_serializing_none]
4623#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4624#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4625#[non_exhaustive]
4626pub struct PromptEmbeddedContextCapabilities {
4627 #[serde_as(deserialize_as = "DefaultOnError")]
4633 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4634 #[serde(default)]
4635 #[serde(rename = "_meta")]
4636 pub meta: Option<Meta>,
4637}
4638
4639impl PromptEmbeddedContextCapabilities {
4640 #[must_use]
4642 pub fn new() -> Self {
4643 Self::default()
4644 }
4645
4646 #[must_use]
4652 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4653 self.meta = meta.into_option();
4654 self
4655 }
4656}
4657
4658#[serde_as]
4660#[skip_serializing_none]
4661#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4662#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4663#[serde(rename_all = "camelCase")]
4664#[non_exhaustive]
4665pub struct McpCapabilities {
4666 #[serde_as(deserialize_as = "DefaultOnError")]
4671 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4672 #[serde(default)]
4673 pub stdio: Option<McpStdioCapabilities>,
4674 #[serde_as(deserialize_as = "DefaultOnError")]
4679 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4680 #[serde(default)]
4681 pub http: Option<McpHttpCapabilities>,
4682 #[cfg(feature = "unstable_mcp_over_acp")]
4691 #[serde_as(deserialize_as = "DefaultOnError")]
4692 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4693 #[serde(default)]
4694 pub acp: Option<McpAcpCapabilities>,
4695 #[serde_as(deserialize_as = "DefaultOnError")]
4701 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4702 #[serde(default)]
4703 #[serde(rename = "_meta")]
4704 pub meta: Option<Meta>,
4705}
4706
4707impl McpCapabilities {
4708 #[must_use]
4710 pub fn new() -> Self {
4711 Self::default()
4712 }
4713
4714 #[must_use]
4719 pub fn stdio(mut self, stdio: impl IntoOption<McpStdioCapabilities>) -> Self {
4720 self.stdio = stdio.into_option();
4721 self
4722 }
4723
4724 #[must_use]
4729 pub fn http(mut self, http: impl IntoOption<McpHttpCapabilities>) -> Self {
4730 self.http = http.into_option();
4731 self
4732 }
4733
4734 #[cfg(feature = "unstable_mcp_over_acp")]
4740 #[must_use]
4744 pub fn acp(mut self, acp: impl IntoOption<McpAcpCapabilities>) -> Self {
4745 self.acp = acp.into_option();
4746 self
4747 }
4748
4749 #[must_use]
4755 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4756 self.meta = meta.into_option();
4757 self
4758 }
4759}
4760
4761#[serde_as]
4765#[skip_serializing_none]
4766#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4767#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4768#[non_exhaustive]
4769pub struct McpStdioCapabilities {
4770 #[serde_as(deserialize_as = "DefaultOnError")]
4776 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4777 #[serde(default)]
4778 #[serde(rename = "_meta")]
4779 pub meta: Option<Meta>,
4780}
4781
4782impl McpStdioCapabilities {
4783 #[must_use]
4785 pub fn new() -> Self {
4786 Self::default()
4787 }
4788
4789 #[must_use]
4795 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4796 self.meta = meta.into_option();
4797 self
4798 }
4799}
4800
4801#[serde_as]
4805#[skip_serializing_none]
4806#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4807#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4808#[non_exhaustive]
4809pub struct McpHttpCapabilities {
4810 #[serde_as(deserialize_as = "DefaultOnError")]
4816 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4817 #[serde(default)]
4818 #[serde(rename = "_meta")]
4819 pub meta: Option<Meta>,
4820}
4821
4822impl McpHttpCapabilities {
4823 #[must_use]
4825 pub fn new() -> Self {
4826 Self::default()
4827 }
4828
4829 #[must_use]
4835 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4836 self.meta = meta.into_option();
4837 self
4838 }
4839}
4840
4841#[cfg(feature = "unstable_mcp_over_acp")]
4849#[serde_as]
4850#[skip_serializing_none]
4851#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4852#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4853#[non_exhaustive]
4854pub struct McpAcpCapabilities {
4855 #[serde_as(deserialize_as = "DefaultOnError")]
4861 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4862 #[serde(default)]
4863 #[serde(rename = "_meta")]
4864 pub meta: Option<Meta>,
4865}
4866
4867#[cfg(feature = "unstable_mcp_over_acp")]
4868impl McpAcpCapabilities {
4869 #[must_use]
4871 pub fn new() -> Self {
4872 Self::default()
4873 }
4874
4875 #[must_use]
4881 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4882 self.meta = meta.into_option();
4883 self
4884 }
4885}
4886
4887#[serde_as]
4891#[skip_serializing_none]
4892#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4893#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4894#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_CANCEL_METHOD_NAME)))]
4895#[serde(rename_all = "camelCase")]
4896#[non_exhaustive]
4897pub struct CancelSessionNotification {
4898 pub session_id: SessionId,
4900 #[serde_as(deserialize_as = "DefaultOnError")]
4906 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4907 #[serde(default)]
4908 #[serde(rename = "_meta")]
4909 pub meta: Option<Meta>,
4910}
4911
4912impl CancelSessionNotification {
4913 #[must_use]
4915 pub fn new(session_id: impl Into<SessionId>) -> Self {
4916 Self {
4917 session_id: session_id.into(),
4918 meta: None,
4919 }
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#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4940#[non_exhaustive]
4941pub struct AgentMethodNames {
4942 pub initialize: &'static str,
4944 pub auth_login: &'static str,
4946 #[cfg(feature = "unstable_llm_providers")]
4948 pub providers_list: &'static str,
4949 #[cfg(feature = "unstable_llm_providers")]
4951 pub providers_set: &'static str,
4952 #[cfg(feature = "unstable_llm_providers")]
4954 pub providers_disable: &'static str,
4955 pub session_new: &'static str,
4957 pub session_set_config_option: &'static str,
4959 pub session_prompt: &'static str,
4961 pub session_cancel: &'static str,
4963 #[cfg(feature = "unstable_mcp_over_acp")]
4965 pub mcp_message: &'static str,
4966 pub session_list: &'static str,
4968 pub session_delete: &'static str,
4970 #[cfg(feature = "unstable_session_fork")]
4972 pub session_fork: &'static str,
4973 pub session_resume: &'static str,
4975 pub session_close: &'static str,
4977 pub auth_logout: &'static str,
4979 #[cfg(feature = "unstable_nes")]
4981 pub nes_start: &'static str,
4982 #[cfg(feature = "unstable_nes")]
4984 pub nes_suggest: &'static str,
4985 #[cfg(feature = "unstable_nes")]
4987 pub nes_accept: &'static str,
4988 #[cfg(feature = "unstable_nes")]
4990 pub nes_reject: &'static str,
4991 #[cfg(feature = "unstable_nes")]
4993 pub nes_close: &'static str,
4994 #[cfg(feature = "unstable_nes")]
4996 pub document_did_open: &'static str,
4997 #[cfg(feature = "unstable_nes")]
4999 pub document_did_change: &'static str,
5000 #[cfg(feature = "unstable_nes")]
5002 pub document_did_close: &'static str,
5003 #[cfg(feature = "unstable_nes")]
5005 pub document_did_save: &'static str,
5006 #[cfg(feature = "unstable_nes")]
5008 pub document_did_focus: &'static str,
5009}
5010
5011pub const AGENT_METHOD_NAMES: AgentMethodNames = AgentMethodNames {
5013 initialize: INITIALIZE_METHOD_NAME,
5014 auth_login: AUTH_LOGIN_METHOD_NAME,
5015 #[cfg(feature = "unstable_llm_providers")]
5016 providers_list: PROVIDERS_LIST_METHOD_NAME,
5017 #[cfg(feature = "unstable_llm_providers")]
5018 providers_set: PROVIDERS_SET_METHOD_NAME,
5019 #[cfg(feature = "unstable_llm_providers")]
5020 providers_disable: PROVIDERS_DISABLE_METHOD_NAME,
5021 session_new: SESSION_NEW_METHOD_NAME,
5022 session_set_config_option: SESSION_SET_CONFIG_OPTION_METHOD_NAME,
5023 session_prompt: SESSION_PROMPT_METHOD_NAME,
5024 session_cancel: SESSION_CANCEL_METHOD_NAME,
5025 #[cfg(feature = "unstable_mcp_over_acp")]
5026 mcp_message: MCP_MESSAGE_METHOD_NAME,
5027 session_list: SESSION_LIST_METHOD_NAME,
5028 session_delete: SESSION_DELETE_METHOD_NAME,
5029 #[cfg(feature = "unstable_session_fork")]
5030 session_fork: SESSION_FORK_METHOD_NAME,
5031 session_resume: SESSION_RESUME_METHOD_NAME,
5032 session_close: SESSION_CLOSE_METHOD_NAME,
5033 auth_logout: AUTH_LOGOUT_METHOD_NAME,
5034 #[cfg(feature = "unstable_nes")]
5035 nes_start: NES_START_METHOD_NAME,
5036 #[cfg(feature = "unstable_nes")]
5037 nes_suggest: NES_SUGGEST_METHOD_NAME,
5038 #[cfg(feature = "unstable_nes")]
5039 nes_accept: NES_ACCEPT_METHOD_NAME,
5040 #[cfg(feature = "unstable_nes")]
5041 nes_reject: NES_REJECT_METHOD_NAME,
5042 #[cfg(feature = "unstable_nes")]
5043 nes_close: NES_CLOSE_METHOD_NAME,
5044 #[cfg(feature = "unstable_nes")]
5045 document_did_open: DOCUMENT_DID_OPEN_METHOD_NAME,
5046 #[cfg(feature = "unstable_nes")]
5047 document_did_change: DOCUMENT_DID_CHANGE_METHOD_NAME,
5048 #[cfg(feature = "unstable_nes")]
5049 document_did_close: DOCUMENT_DID_CLOSE_METHOD_NAME,
5050 #[cfg(feature = "unstable_nes")]
5051 document_did_save: DOCUMENT_DID_SAVE_METHOD_NAME,
5052 #[cfg(feature = "unstable_nes")]
5053 document_did_focus: DOCUMENT_DID_FOCUS_METHOD_NAME,
5054};
5055
5056pub(crate) const INITIALIZE_METHOD_NAME: &str = "initialize";
5058pub(crate) const AUTH_LOGIN_METHOD_NAME: &str = "auth/login";
5060#[cfg(feature = "unstable_llm_providers")]
5062pub(crate) const PROVIDERS_LIST_METHOD_NAME: &str = "providers/list";
5063#[cfg(feature = "unstable_llm_providers")]
5065pub(crate) const PROVIDERS_SET_METHOD_NAME: &str = "providers/set";
5066#[cfg(feature = "unstable_llm_providers")]
5068pub(crate) const PROVIDERS_DISABLE_METHOD_NAME: &str = "providers/disable";
5069pub(crate) const SESSION_NEW_METHOD_NAME: &str = "session/new";
5071pub(crate) const SESSION_SET_CONFIG_OPTION_METHOD_NAME: &str = "session/set_config_option";
5073pub(crate) const SESSION_PROMPT_METHOD_NAME: &str = "session/prompt";
5075pub(crate) const SESSION_CANCEL_METHOD_NAME: &str = "session/cancel";
5077pub(crate) const SESSION_LIST_METHOD_NAME: &str = "session/list";
5079pub(crate) const SESSION_DELETE_METHOD_NAME: &str = "session/delete";
5081#[cfg(feature = "unstable_session_fork")]
5083pub(crate) const SESSION_FORK_METHOD_NAME: &str = "session/fork";
5084pub(crate) const SESSION_RESUME_METHOD_NAME: &str = "session/resume";
5086pub(crate) const SESSION_CLOSE_METHOD_NAME: &str = "session/close";
5088pub(crate) const AUTH_LOGOUT_METHOD_NAME: &str = "auth/logout";
5090
5091#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5098#[derive(Clone, Debug, Serialize, Deserialize)]
5099#[serde(untagged)]
5100#[cfg_attr(feature = "schemars", schemars(inline))]
5101#[non_exhaustive]
5102pub enum ClientRequest {
5103 InitializeRequest(Box<InitializeRequest>),
5114 LoginAuthRequest(Box<LoginAuthRequest>),
5129 #[cfg(feature = "unstable_llm_providers")]
5135 ListProvidersRequest(Box<ListProvidersRequest>),
5136 #[cfg(feature = "unstable_llm_providers")]
5142 SetProviderRequest(Box<SetProviderRequest>),
5143 #[cfg(feature = "unstable_llm_providers")]
5149 DisableProviderRequest(Box<DisableProviderRequest>),
5150 LogoutAuthRequest(Box<LogoutAuthRequest>),
5160 NewSessionRequest(Box<NewSessionRequest>),
5173 ListSessionsRequest(Box<ListSessionsRequest>),
5177 DeleteSessionRequest(Box<DeleteSessionRequest>),
5181 #[cfg(feature = "unstable_session_fork")]
5182 ForkSessionRequest(Box<ForkSessionRequest>),
5194 ResumeSessionRequest(Box<ResumeSessionRequest>),
5200 CloseSessionRequest(Box<CloseSessionRequest>),
5205 SetSessionConfigOptionRequest(Box<SetSessionConfigOptionRequest>),
5207 PromptRequest(Box<PromptRequest>),
5219 #[cfg(feature = "unstable_nes")]
5220 StartNesRequest(Box<StartNesRequest>),
5226 #[cfg(feature = "unstable_nes")]
5227 SuggestNesRequest(Box<SuggestNesRequest>),
5233 #[cfg(feature = "unstable_nes")]
5234 CloseNesRequest(Box<CloseNesRequest>),
5243 #[cfg(feature = "unstable_mcp_over_acp")]
5249 MessageMcpRequest(Box<MessageMcpRequest>),
5250 ExtMethodRequest(Box<ExtRequest>),
5257}
5258
5259impl ClientRequest {
5260 #[must_use]
5262 pub fn method(&self) -> &str {
5263 match self {
5264 Self::InitializeRequest(_) => AGENT_METHOD_NAMES.initialize,
5265 Self::LoginAuthRequest(_) => AGENT_METHOD_NAMES.auth_login,
5266 #[cfg(feature = "unstable_llm_providers")]
5267 Self::ListProvidersRequest(_) => AGENT_METHOD_NAMES.providers_list,
5268 #[cfg(feature = "unstable_llm_providers")]
5269 Self::SetProviderRequest(_) => AGENT_METHOD_NAMES.providers_set,
5270 #[cfg(feature = "unstable_llm_providers")]
5271 Self::DisableProviderRequest(_) => AGENT_METHOD_NAMES.providers_disable,
5272 Self::LogoutAuthRequest(_) => AGENT_METHOD_NAMES.auth_logout,
5273 Self::NewSessionRequest(_) => AGENT_METHOD_NAMES.session_new,
5274 Self::ListSessionsRequest(_) => AGENT_METHOD_NAMES.session_list,
5275 Self::DeleteSessionRequest(_) => AGENT_METHOD_NAMES.session_delete,
5276 #[cfg(feature = "unstable_session_fork")]
5277 Self::ForkSessionRequest(_) => AGENT_METHOD_NAMES.session_fork,
5278 Self::ResumeSessionRequest(_) => AGENT_METHOD_NAMES.session_resume,
5279 Self::CloseSessionRequest(_) => AGENT_METHOD_NAMES.session_close,
5280 Self::SetSessionConfigOptionRequest(_) => AGENT_METHOD_NAMES.session_set_config_option,
5281 Self::PromptRequest(_) => AGENT_METHOD_NAMES.session_prompt,
5282 #[cfg(feature = "unstable_nes")]
5283 Self::StartNesRequest(_) => AGENT_METHOD_NAMES.nes_start,
5284 #[cfg(feature = "unstable_nes")]
5285 Self::SuggestNesRequest(_) => AGENT_METHOD_NAMES.nes_suggest,
5286 #[cfg(feature = "unstable_nes")]
5287 Self::CloseNesRequest(_) => AGENT_METHOD_NAMES.nes_close,
5288 #[cfg(feature = "unstable_mcp_over_acp")]
5289 Self::MessageMcpRequest(_) => AGENT_METHOD_NAMES.mcp_message,
5290 Self::ExtMethodRequest(ext_request) => &ext_request.method,
5291 }
5292 }
5293}
5294
5295#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5302#[derive(Clone, Debug, Serialize, Deserialize)]
5303#[serde(untagged)]
5304#[cfg_attr(feature = "schemars", schemars(inline))]
5305#[non_exhaustive]
5306pub enum AgentResponse {
5307 InitializeResponse(Box<InitializeResponse>),
5309 LoginAuthResponse(#[serde(default)] Box<LoginAuthResponse>),
5311 #[cfg(feature = "unstable_llm_providers")]
5313 ListProvidersResponse(Box<ListProvidersResponse>),
5314 #[cfg(feature = "unstable_llm_providers")]
5316 SetProviderResponse(#[serde(default)] Box<SetProviderResponse>),
5317 #[cfg(feature = "unstable_llm_providers")]
5319 DisableProviderResponse(#[serde(default)] Box<DisableProviderResponse>),
5320 LogoutAuthResponse(#[serde(default)] Box<LogoutAuthResponse>),
5322 NewSessionResponse(Box<NewSessionResponse>),
5324 ListSessionsResponse(Box<ListSessionsResponse>),
5326 DeleteSessionResponse(#[serde(default)] Box<DeleteSessionResponse>),
5328 #[cfg(feature = "unstable_session_fork")]
5330 ForkSessionResponse(Box<ForkSessionResponse>),
5331 ResumeSessionResponse(#[serde(default)] Box<ResumeSessionResponse>),
5333 CloseSessionResponse(#[serde(default)] Box<CloseSessionResponse>),
5335 SetSessionConfigOptionResponse(Box<SetSessionConfigOptionResponse>),
5337 PromptResponse(Box<PromptResponse>),
5339 #[cfg(feature = "unstable_nes")]
5341 StartNesResponse(Box<StartNesResponse>),
5342 #[cfg(feature = "unstable_nes")]
5344 SuggestNesResponse(Box<SuggestNesResponse>),
5345 #[cfg(feature = "unstable_nes")]
5347 CloseNesResponse(#[serde(default)] Box<CloseNesResponse>),
5348 ExtMethodResponse(Box<ExtResponse>),
5350 #[cfg(feature = "unstable_mcp_over_acp")]
5352 MessageMcpResponse(Box<MessageMcpResponse>),
5353}
5354
5355#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5362#[derive(Clone, Debug, Serialize, Deserialize)]
5363#[serde(untagged)]
5364#[cfg_attr(feature = "schemars", schemars(inline))]
5365#[non_exhaustive]
5366pub enum ClientNotification {
5367 CancelSessionNotification(Box<CancelSessionNotification>),
5381 #[cfg(feature = "unstable_nes")]
5382 DidOpenDocumentNotification(Box<DidOpenDocumentNotification>),
5386 #[cfg(feature = "unstable_nes")]
5387 DidChangeDocumentNotification(Box<DidChangeDocumentNotification>),
5391 #[cfg(feature = "unstable_nes")]
5392 DidCloseDocumentNotification(Box<DidCloseDocumentNotification>),
5396 #[cfg(feature = "unstable_nes")]
5397 DidSaveDocumentNotification(Box<DidSaveDocumentNotification>),
5401 #[cfg(feature = "unstable_nes")]
5402 DidFocusDocumentNotification(Box<DidFocusDocumentNotification>),
5406 #[cfg(feature = "unstable_nes")]
5407 AcceptNesNotification(Box<AcceptNesNotification>),
5411 #[cfg(feature = "unstable_nes")]
5412 RejectNesNotification(Box<RejectNesNotification>),
5416 #[cfg(feature = "unstable_mcp_over_acp")]
5422 MessageMcpNotification(Box<MessageMcpNotification>),
5423 ExtNotification(Box<ExtNotification>),
5430}
5431
5432impl ClientNotification {
5433 #[must_use]
5435 pub fn method(&self) -> &str {
5436 match self {
5437 Self::CancelSessionNotification(_) => AGENT_METHOD_NAMES.session_cancel,
5438 #[cfg(feature = "unstable_nes")]
5439 Self::DidOpenDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_open,
5440 #[cfg(feature = "unstable_nes")]
5441 Self::DidChangeDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_change,
5442 #[cfg(feature = "unstable_nes")]
5443 Self::DidCloseDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_close,
5444 #[cfg(feature = "unstable_nes")]
5445 Self::DidSaveDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_save,
5446 #[cfg(feature = "unstable_nes")]
5447 Self::DidFocusDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_focus,
5448 #[cfg(feature = "unstable_nes")]
5449 Self::AcceptNesNotification(_) => AGENT_METHOD_NAMES.nes_accept,
5450 #[cfg(feature = "unstable_nes")]
5451 Self::RejectNesNotification(_) => AGENT_METHOD_NAMES.nes_reject,
5452 #[cfg(feature = "unstable_mcp_over_acp")]
5453 Self::MessageMcpNotification(_) => AGENT_METHOD_NAMES.mcp_message,
5454 Self::ExtNotification(ext_notification) => &ext_notification.method,
5455 }
5456 }
5457}
5458
5459#[cfg(test)]
5460mod test_serialization {
5461 use std::path::PathBuf;
5462
5463 use super::*;
5464 use serde_json::json;
5465
5466 fn test_meta() -> Meta {
5467 json!({ "source": "test" }).as_object().unwrap().clone()
5468 }
5469
5470 fn serialized_meta_key_count(value: &impl serde::Serialize) -> usize {
5471 serde_json::to_string(value)
5472 .unwrap()
5473 .matches("\"_meta\"")
5474 .count()
5475 }
5476
5477 #[test]
5478 fn prompt_response_without_metadata_round_trips() {
5479 let response = PromptResponse::new(MessageId::new("message-1"));
5480 let serialized = serde_json::to_value(&response).unwrap();
5481 assert_eq!(serialized, json!({ "messageId": "message-1" }));
5482 assert_eq!(
5483 serde_json::from_value::<PromptResponse>(serialized).unwrap(),
5484 response
5485 );
5486 assert_eq!(
5487 serde_json::from_value::<PromptResponse>(json!({
5488 "messageId": "message-1",
5489 "_meta": null
5490 }))
5491 .unwrap(),
5492 response
5493 );
5494 }
5495
5496 #[test]
5497 fn prompt_response_requires_message_id_and_preserves_metadata_tolerance() {
5498 let response = PromptResponse::new("message-1").meta(test_meta());
5499 let serialized = serde_json::to_value(&response).unwrap();
5500 assert_eq!(
5501 serialized,
5502 json!({
5503 "messageId": "message-1",
5504 "_meta": { "source": "test" }
5505 })
5506 );
5507
5508 let deserialized: PromptResponse = serde_json::from_value(serialized).unwrap();
5509 assert_eq!(deserialized.message_id, MessageId::new("message-1"));
5510 assert_eq!(deserialized.meta, Some(test_meta()));
5511
5512 let malformed_meta: PromptResponse = serde_json::from_value(json!({
5513 "messageId": "message-2",
5514 "_meta": false
5515 }))
5516 .unwrap();
5517 assert_eq!(malformed_meta.message_id, MessageId::new("message-2"));
5518 assert_eq!(malformed_meta.meta, None);
5519
5520 for invalid in [
5521 json!({}),
5522 json!({ "messageId": null }),
5523 json!({ "messageId": 1 }),
5524 json!({ "messageId": false }),
5525 json!({ "messageId": {} }),
5526 json!({ "messageId": [] }),
5527 ] {
5528 assert!(
5529 serde_json::from_value::<PromptResponse>(invalid).is_err(),
5530 "missing, null, and non-string message IDs must be rejected"
5531 );
5532 }
5533 }
5534
5535 #[cfg(feature = "schemars")]
5536 #[test]
5537 fn prompt_response_schema_requires_non_null_string_message_id_reference() {
5538 let schema = serde_json::to_value(schemars::schema_for!(PromptResponse)).unwrap();
5539
5540 assert_eq!(schema["required"], json!(["messageId"]));
5541 assert_eq!(
5542 schema["properties"]["messageId"]["$ref"],
5543 "#/$defs/MessageId"
5544 );
5545 assert_eq!(schema["$defs"]["MessageId"]["type"], "string");
5546 }
5547
5548 #[test]
5549 fn test_initialize_capabilities_default_on_malformed_values() {
5550 let request: InitializeRequest = serde_json::from_value(json!({
5551 "protocolVersion": 2,
5552 "capabilities": false,
5553 "info": {
5554 "name": "client",
5555 "version": "1.0.0"
5556 }
5557 }))
5558 .unwrap();
5559 assert_eq!(request.capabilities, ClientCapabilities::default());
5560
5561 let response: InitializeResponse = serde_json::from_value(json!({
5562 "protocolVersion": 2,
5563 "capabilities": false,
5564 "info": {
5565 "name": "agent",
5566 "version": "1.0.0"
5567 }
5568 }))
5569 .unwrap();
5570 assert_eq!(response.capabilities, AgentCapabilities::default());
5571 }
5572
5573 #[test]
5574 fn test_agent_capabilities_default_on_malformed_values() {
5575 let capabilities: AgentCapabilities = serde_json::from_value(json!({
5576 "session": false,
5577 "auth": false
5578 }))
5579 .unwrap();
5580
5581 assert!(capabilities.session.is_none());
5582 assert_eq!(capabilities.auth, None);
5583 }
5584
5585 #[test]
5586 fn test_mcp_server_stdio_serialization() {
5587 let server = McpServer::Stdio(
5588 McpServerStdio::new("test-server", "/usr/bin/server")
5589 .args(vec!["--port".to_string(), "3000".to_string()])
5590 .env(vec![EnvVariable::new("API_KEY", "secret123")]),
5591 );
5592
5593 let json = serde_json::to_value(&server).unwrap();
5594 assert_eq!(
5595 json,
5596 json!({
5597 "type": "stdio",
5598 "name": "test-server",
5599 "command": "/usr/bin/server",
5600 "args": ["--port", "3000"],
5601 "env": [
5602 {
5603 "name": "API_KEY",
5604 "value": "secret123"
5605 }
5606 ]
5607 })
5608 );
5609
5610 let deserialized: McpServer = serde_json::from_value(json).unwrap();
5611 match deserialized {
5612 McpServer::Stdio(McpServerStdio {
5613 name,
5614 command,
5615 args,
5616 env,
5617 meta: _,
5618 }) => {
5619 assert_eq!(name, "test-server");
5620 assert_eq!(command, AbsolutePath::new("/usr/bin/server"));
5621 assert_eq!(args, vec!["--port", "3000"]);
5622 assert_eq!(env.len(), 1);
5623 assert_eq!(env[0].name, "API_KEY");
5624 assert_eq!(env[0].value, "secret123");
5625 }
5626 _ => panic!("Expected Stdio variant"),
5627 }
5628 }
5629
5630 #[test]
5631 fn test_mcp_server_empty_arrays_are_optional() {
5632 let stdio = McpServer::Stdio(McpServerStdio::new("test-server", "/usr/bin/server"));
5633 assert_eq!(
5634 serde_json::to_value(&stdio).unwrap(),
5635 json!({
5636 "type": "stdio",
5637 "name": "test-server",
5638 "command": "/usr/bin/server"
5639 })
5640 );
5641
5642 let McpServer::Stdio(McpServerStdio { args, env, .. }) =
5643 serde_json::from_value::<McpServer>(json!({
5644 "type": "stdio",
5645 "name": "test-server",
5646 "command": "/usr/bin/server"
5647 }))
5648 .unwrap()
5649 else {
5650 panic!("Expected Stdio variant");
5651 };
5652 assert!(args.is_empty());
5653 assert!(env.is_empty());
5654
5655 let http = McpServer::Http(McpServerHttp::new("http-server", "https://api.example.com"));
5656 assert_eq!(
5657 serde_json::to_value(&http).unwrap(),
5658 json!({
5659 "type": "http",
5660 "name": "http-server",
5661 "url": "https://api.example.com"
5662 })
5663 );
5664
5665 let McpServer::Http(McpServerHttp { headers, .. }) =
5666 serde_json::from_value::<McpServer>(json!({
5667 "type": "http",
5668 "name": "http-server",
5669 "url": "https://api.example.com"
5670 }))
5671 .unwrap()
5672 else {
5673 panic!("Expected Http variant");
5674 };
5675 assert!(headers.is_empty());
5676 }
5677
5678 #[test]
5679 fn test_mcp_server_unknown_transport_serialization() {
5680 let json = json!({
5681 "type": "websocket",
5682 "name": "future-server",
5683 "url": "wss://example.com/mcp",
5684 "protocolVersion": "2026-01-01"
5685 });
5686
5687 let deserialized: McpServer = serde_json::from_value(json.clone()).unwrap();
5688 let McpServer::Other(OtherMcpServer { type_, fields }) = &deserialized else {
5689 panic!("Expected Other variant");
5690 };
5691
5692 assert_eq!(type_, "websocket");
5693 assert_eq!(fields["name"], "future-server");
5694 assert_eq!(fields["url"], "wss://example.com/mcp");
5695 assert_eq!(fields["protocolVersion"], "2026-01-01");
5696 assert_eq!(serde_json::to_value(&deserialized).unwrap(), json);
5697 }
5698
5699 #[test]
5700 fn test_mcp_server_stdio_requires_type() {
5701 let result = serde_json::from_value::<McpServer>(json!({
5702 "name": "test-server",
5703 "command": "/usr/bin/server",
5704 "args": [],
5705 "env": []
5706 }));
5707
5708 assert!(result.is_err());
5709 }
5710
5711 #[test]
5712 fn test_mcp_server_unknown_does_not_hide_malformed_known_transport() {
5713 let result = serde_json::from_value::<McpServer>(json!({
5714 "type": "stdio",
5715 "name": "test-server",
5716 "args": [],
5717 "env": []
5718 }));
5719
5720 assert!(result.is_err());
5721 }
5722
5723 #[test]
5724 fn test_mcp_server_http_serialization() {
5725 let server = McpServer::Http(
5726 McpServerHttp::new("http-server", "https://api.example.com").headers(vec![
5727 HttpHeader::new("Authorization", "Bearer token123"),
5728 HttpHeader::new("Content-Type", "application/json"),
5729 ]),
5730 );
5731
5732 let json = serde_json::to_value(&server).unwrap();
5733 assert_eq!(
5734 json,
5735 json!({
5736 "type": "http",
5737 "name": "http-server",
5738 "url": "https://api.example.com",
5739 "headers": [
5740 {
5741 "name": "Authorization",
5742 "value": "Bearer token123"
5743 },
5744 {
5745 "name": "Content-Type",
5746 "value": "application/json"
5747 }
5748 ]
5749 })
5750 );
5751
5752 let deserialized: McpServer = serde_json::from_value(json).unwrap();
5753 match deserialized {
5754 McpServer::Http(McpServerHttp {
5755 name,
5756 url,
5757 headers,
5758 meta: _,
5759 }) => {
5760 assert_eq!(name, "http-server");
5761 assert_eq!(url, "https://api.example.com");
5762 assert_eq!(headers.len(), 2);
5763 assert_eq!(headers[0].name, "Authorization");
5764 assert_eq!(headers[0].value, "Bearer token123");
5765 assert_eq!(headers[1].name, "Content-Type");
5766 assert_eq!(headers[1].value, "application/json");
5767 }
5768 _ => panic!("Expected Http variant"),
5769 }
5770 }
5771
5772 #[cfg(feature = "schemars")]
5773 #[test]
5774 fn mcp_server_http_schema_marks_url_as_uri() {
5775 let schema = serde_json::to_value(schemars::schema_for!(McpServerHttp)).unwrap();
5776
5777 assert_eq!(schema["properties"]["url"]["format"], "uri");
5778 }
5779
5780 #[cfg(feature = "unstable_mcp_over_acp")]
5781 #[test]
5782 fn test_client_mcp_message_method_names() {
5783 assert_eq!(AGENT_METHOD_NAMES.mcp_message, "mcp/message");
5784
5785 assert_eq!(
5786 ClientRequest::MessageMcpRequest(Box::new(MessageMcpRequest::new(
5787 "conn-1",
5788 "tools/list"
5789 )))
5790 .method(),
5791 "mcp/message"
5792 );
5793 assert_eq!(
5794 ClientNotification::MessageMcpNotification(Box::new(MessageMcpNotification::new(
5795 "conn-1",
5796 "notifications/progress"
5797 )))
5798 .method(),
5799 "mcp/message"
5800 );
5801 }
5802
5803 #[test]
5804 fn test_auth_method_names() {
5805 assert_eq!(AGENT_METHOD_NAMES.auth_login, "auth/login");
5806 assert_eq!(AGENT_METHOD_NAMES.auth_logout, "auth/logout");
5807
5808 assert_eq!(
5809 ClientRequest::LoginAuthRequest(Box::new(LoginAuthRequest::new("agent-login")))
5810 .method(),
5811 "auth/login"
5812 );
5813 assert_eq!(
5814 ClientRequest::LogoutAuthRequest(Box::new(LogoutAuthRequest::new())).method(),
5815 "auth/logout"
5816 );
5817 }
5818
5819 #[test]
5820 fn test_session_config_option_category_known_variants() {
5821 assert_eq!(
5823 serde_json::to_value(&SessionConfigOptionCategory::Mode).unwrap(),
5824 json!("mode")
5825 );
5826 assert_eq!(
5827 serde_json::to_value(&SessionConfigOptionCategory::Model).unwrap(),
5828 json!("model")
5829 );
5830 assert_eq!(
5831 serde_json::to_value(&SessionConfigOptionCategory::ModelConfig).unwrap(),
5832 json!("model_config")
5833 );
5834 assert_eq!(
5835 serde_json::to_value(&SessionConfigOptionCategory::ThoughtLevel).unwrap(),
5836 json!("thought_level")
5837 );
5838
5839 assert_eq!(
5841 serde_json::from_str::<SessionConfigOptionCategory>("\"mode\"").unwrap(),
5842 SessionConfigOptionCategory::Mode
5843 );
5844 assert_eq!(
5845 serde_json::from_str::<SessionConfigOptionCategory>("\"model\"").unwrap(),
5846 SessionConfigOptionCategory::Model
5847 );
5848 assert_eq!(
5849 serde_json::from_str::<SessionConfigOptionCategory>("\"model_config\"").unwrap(),
5850 SessionConfigOptionCategory::ModelConfig
5851 );
5852 assert_eq!(
5853 serde_json::from_str::<SessionConfigOptionCategory>("\"thought_level\"").unwrap(),
5854 SessionConfigOptionCategory::ThoughtLevel
5855 );
5856 }
5857
5858 #[test]
5859 fn test_session_config_option_category_unknown_variants() {
5860 let unknown: SessionConfigOptionCategory =
5862 serde_json::from_str("\"some_future_category\"").unwrap();
5863 assert_eq!(
5864 unknown,
5865 SessionConfigOptionCategory::Other("some_future_category".to_string())
5866 );
5867
5868 let json = serde_json::to_value(&unknown).unwrap();
5870 assert_eq!(json, json!("some_future_category"));
5871 }
5872
5873 #[test]
5874 fn test_session_config_option_category_custom_categories() {
5875 let custom: SessionConfigOptionCategory =
5877 serde_json::from_str("\"_my_custom_category\"").unwrap();
5878 assert_eq!(
5879 custom,
5880 SessionConfigOptionCategory::Other("_my_custom_category".to_string())
5881 );
5882
5883 let json = serde_json::to_value(&custom).unwrap();
5885 assert_eq!(json, json!("_my_custom_category"));
5886
5887 let deserialized: SessionConfigOptionCategory = serde_json::from_value(json).unwrap();
5889 assert_eq!(
5890 deserialized,
5891 SessionConfigOptionCategory::Other("_my_custom_category".to_string()),
5892 );
5893 }
5894
5895 fn test_config_option() -> SessionConfigOption {
5896 SessionConfigOption::select(
5897 "mode",
5898 "Mode",
5899 "ask",
5900 vec![SessionConfigSelectOption::new("ask", "Ask")],
5901 )
5902 }
5903
5904 #[test]
5905 fn test_session_response_config_options_default_empty_and_skip_serializing() {
5906 assert_eq!(
5907 serde_json::to_value(NewSessionResponse::new("sess")).unwrap(),
5908 json!({ "sessionId": "sess" })
5909 );
5910 assert_eq!(
5911 serde_json::to_value(ResumeSessionResponse::new()).unwrap(),
5912 json!({})
5913 );
5914 #[cfg(feature = "unstable_session_fork")]
5915 assert_eq!(
5916 serde_json::to_value(ForkSessionResponse::new("fork")).unwrap(),
5917 json!({ "sessionId": "fork" })
5918 );
5919
5920 let json = serde_json::to_value(
5921 NewSessionResponse::new("sess").config_options(vec![test_config_option()]),
5922 )
5923 .unwrap();
5924 assert_eq!(json["configOptions"].as_array().unwrap().len(), 1);
5925 }
5926
5927 #[test]
5928 fn test_session_response_config_options_deserialize_missing_null_and_invalid() {
5929 let missing: NewSessionResponse =
5930 serde_json::from_value(json!({ "sessionId": "sess" })).unwrap();
5931 assert!(missing.config_options.is_empty());
5932
5933 let null: NewSessionResponse = serde_json::from_value(json!({
5934 "sessionId": "sess",
5935 "configOptions": null
5936 }))
5937 .unwrap();
5938 assert!(null.config_options.is_empty());
5939
5940 let wrong_shape: NewSessionResponse = serde_json::from_value(json!({
5941 "sessionId": "sess",
5942 "configOptions": "oops"
5943 }))
5944 .unwrap();
5945 assert!(wrong_shape.config_options.is_empty());
5946
5947 let valid_option = serde_json::to_value(test_config_option()).unwrap();
5948 let mixed: NewSessionResponse = serde_json::from_value(json!({
5949 "sessionId": "sess",
5950 "configOptions": ["oops", valid_option]
5951 }))
5952 .unwrap();
5953 assert_eq!(mixed.config_options.len(), 1);
5954
5955 let resume: ResumeSessionResponse = serde_json::from_value(json!({})).unwrap();
5956 assert!(resume.config_options.is_empty());
5957 #[cfg(feature = "unstable_session_fork")]
5958 {
5959 let fork: ForkSessionResponse =
5960 serde_json::from_value(json!({ "sessionId": "fork" })).unwrap();
5961 assert!(fork.config_options.is_empty());
5962 }
5963 }
5964
5965 #[test]
5966 fn test_resume_session_replay_from_serialization() {
5967 assert_eq!(
5968 serde_json::to_value(ResumeSessionRequest::new(
5969 "sess_abc123",
5970 "/home/user/project"
5971 ))
5972 .unwrap(),
5973 json!({
5974 "sessionId": "sess_abc123",
5975 "cwd": "/home/user/project"
5976 })
5977 );
5978 assert_eq!(
5979 serde_json::to_value(
5980 ResumeSessionRequest::new("sess_abc123", "/home/user/project")
5981 .replay_from(ReplayFrom::from(ReplayFromStart::new()))
5982 )
5983 .unwrap(),
5984 json!({
5985 "sessionId": "sess_abc123",
5986 "cwd": "/home/user/project",
5987 "replayFrom": {
5988 "type": "start"
5989 }
5990 })
5991 );
5992
5993 let replay: ResumeSessionRequest = serde_json::from_value(json!({
5994 "sessionId": "sess_abc123",
5995 "cwd": "/home/user/project",
5996 "replayFrom": {
5997 "type": "start"
5998 }
5999 }))
6000 .unwrap();
6001 assert!(matches!(replay.replay_from, Some(ReplayFrom::Start(_))));
6002
6003 let none: ResumeSessionRequest = serde_json::from_value(json!({
6004 "sessionId": "sess_abc123",
6005 "cwd": "/home/user/project",
6006 "replayFrom": null
6007 }))
6008 .unwrap();
6009 assert!(none.replay_from.is_none());
6010 }
6011
6012 #[test]
6013 fn test_auth_method_agent_serialization() {
6014 let method = AuthMethod::Agent(AuthMethodAgent::new("default-auth", "Default Auth"));
6015
6016 let json = serde_json::to_value(&method).unwrap();
6017 assert_eq!(
6018 json,
6019 json!({
6020 "methodId": "default-auth",
6021 "name": "Default Auth",
6022 "type": "agent"
6023 })
6024 );
6025 assert!(!json.as_object().unwrap().contains_key("description"));
6027
6028 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
6029 match deserialized {
6030 AuthMethod::Agent(AuthMethodAgent {
6031 method_id, name, ..
6032 }) => {
6033 assert_eq!(method_id.0.as_ref(), "default-auth");
6034 assert_eq!(name, "Default Auth");
6035 }
6036 _ => panic!("Expected Agent variant"),
6037 }
6038 }
6039
6040 #[test]
6041 fn test_auth_method_agent_deserialization() {
6042 let json = json!({
6043 "methodId": "agent-auth",
6044 "name": "Agent Auth",
6045 "type": "agent"
6046 });
6047
6048 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
6049 assert!(matches!(deserialized, AuthMethod::Agent(_)));
6050 }
6051
6052 #[test]
6053 fn test_auth_method_agent_requires_type() {
6054 assert!(
6055 serde_json::from_value::<AuthMethod>(json!({
6056 "methodId": "agent-auth",
6057 "name": "Agent Auth"
6058 }))
6059 .is_err()
6060 );
6061 }
6062
6063 #[test]
6064 fn test_auth_method_agent_rejects_null_type() {
6065 assert!(
6066 serde_json::from_value::<AuthMethod>(json!({
6067 "methodId": "agent-auth",
6068 "name": "Agent Auth",
6069 "type": null
6070 }))
6071 .is_err()
6072 );
6073 }
6074
6075 #[test]
6076 fn test_auth_method_unknown_does_not_hide_malformed_agent() {
6077 assert!(
6078 serde_json::from_value::<AuthMethod>(json!({
6079 "methodId": "agent-auth",
6080 "type": "agent"
6081 }))
6082 .is_err()
6083 );
6084 assert!(
6085 serde_json::from_value::<AuthMethod>(json!({
6086 "methodId": "api-key",
6087 "type": "env_var",
6088 "vars": [{"name": "API_KEY"}]
6089 }))
6090 .is_err()
6091 );
6092 }
6093
6094 #[test]
6095 fn test_auth_method_unknown_variant_roundtrip() {
6096 let method: AuthMethod = serde_json::from_value(json!({
6097 "methodId": "oauth",
6098 "name": "OAuth",
6099 "type": "_oauth",
6100 "authorizationUrl": "https://example.com/auth"
6101 }))
6102 .unwrap();
6103
6104 assert_eq!(method.method_id().0.as_ref(), "oauth");
6105 assert_eq!(method.name(), "OAuth");
6106 let AuthMethod::Other(unknown) = method else {
6107 panic!("expected unknown auth method");
6108 };
6109 assert_eq!(unknown.type_, "_oauth");
6110 assert_eq!(
6111 unknown.fields.get("authorizationUrl"),
6112 Some(&json!("https://example.com/auth"))
6113 );
6114
6115 assert_eq!(
6116 serde_json::to_value(AuthMethod::Other(unknown)).unwrap(),
6117 json!({
6118 "methodId": "oauth",
6119 "name": "OAuth",
6120 "type": "_oauth",
6121 "authorizationUrl": "https://example.com/auth"
6122 })
6123 );
6124 }
6125
6126 #[test]
6127 fn test_auth_method_unknown_does_not_hide_malformed_known_variant() {
6128 assert!(
6129 serde_json::from_value::<AuthMethod>(json!({
6130 "methodId": "terminal-auth",
6131 "type": "terminal"
6132 }))
6133 .is_err()
6134 );
6135 }
6136
6137 #[test]
6138 fn test_session_delete_serialization() {
6139 assert_eq!(AGENT_METHOD_NAMES.session_delete, "session/delete");
6140 assert_eq!(
6141 ClientRequest::DeleteSessionRequest(Box::new(DeleteSessionRequest::new("sess_abc123")))
6142 .method(),
6143 "session/delete"
6144 );
6145 assert_eq!(
6146 serde_json::to_value(DeleteSessionRequest::new("sess_abc123")).unwrap(),
6147 json!({
6148 "sessionId": "sess_abc123"
6149 })
6150 );
6151 assert_eq!(
6152 serde_json::to_value(DeleteSessionResponse::new()).unwrap(),
6153 json!({})
6154 );
6155 assert_eq!(
6156 serde_json::to_value(
6157 SessionCapabilities::new().delete(SessionDeleteCapabilities::new())
6158 )
6159 .unwrap(),
6160 json!({
6161 "delete": {}
6162 })
6163 );
6164 }
6165 #[test]
6166 fn test_session_additional_directories_serialization() {
6167 assert_eq!(
6168 serde_json::to_value(NewSessionRequest::new("/home/user/project")).unwrap(),
6169 json!({
6170 "cwd": "/home/user/project",
6171 })
6172 );
6173 assert_eq!(
6174 serde_json::to_value(
6175 NewSessionRequest::new("/home/user/project").additional_directories(vec![
6176 PathBuf::from("/home/user/shared-lib"),
6177 PathBuf::from("/home/user/product-docs"),
6178 ])
6179 )
6180 .unwrap(),
6181 json!({
6182 "cwd": "/home/user/project",
6183 "additionalDirectories": [
6184 "/home/user/shared-lib",
6185 "/home/user/product-docs"
6186 ],
6187 })
6188 );
6189 assert_eq!(
6190 serde_json::to_value(ResumeSessionRequest::new(
6191 "sess_abc123",
6192 "/home/user/project"
6193 ))
6194 .unwrap(),
6195 json!({
6196 "sessionId": "sess_abc123",
6197 "cwd": "/home/user/project",
6198 })
6199 );
6200 assert_eq!(
6201 serde_json::from_value::<ResumeSessionRequest>(json!({
6202 "sessionId": "sess_abc123",
6203 "cwd": "/home/user/project"
6204 }))
6205 .unwrap()
6206 .mcp_servers,
6207 Vec::<McpServer>::new()
6208 );
6209 assert_eq!(
6210 serde_json::from_value::<ResumeSessionRequest>(json!({
6211 "sessionId": "sess_abc123",
6212 "cwd": "/home/user/project",
6213 "mcpServers": null
6214 }))
6215 .unwrap()
6216 .mcp_servers,
6217 Vec::<McpServer>::new()
6218 );
6219 assert_eq!(
6220 serde_json::to_value(SessionInfo::new("sess_abc123", "/home/user/project")).unwrap(),
6221 json!({
6222 "sessionId": "sess_abc123",
6223 "cwd": "/home/user/project"
6224 })
6225 );
6226 assert_eq!(
6227 serde_json::to_value(
6228 SessionInfo::new("sess_abc123", "/home/user/project").additional_directories(vec![
6229 PathBuf::from("/home/user/shared-lib"),
6230 PathBuf::from("/home/user/product-docs"),
6231 ])
6232 )
6233 .unwrap(),
6234 json!({
6235 "sessionId": "sess_abc123",
6236 "cwd": "/home/user/project",
6237 "additionalDirectories": [
6238 "/home/user/shared-lib",
6239 "/home/user/product-docs"
6240 ]
6241 })
6242 );
6243 assert_eq!(
6244 serde_json::from_value::<SessionInfo>(json!({
6245 "sessionId": "sess_abc123",
6246 "cwd": "/home/user/project"
6247 }))
6248 .unwrap()
6249 .additional_directories,
6250 Vec::<AbsolutePath>::new()
6251 );
6252 }
6253 #[test]
6254 fn test_session_additional_directories_capabilities_serialization() {
6255 assert_eq!(
6256 serde_json::to_value(
6257 SessionCapabilities::new()
6258 .additional_directories(SessionAdditionalDirectoriesCapabilities::new())
6259 )
6260 .unwrap(),
6261 json!({
6262 "additionalDirectories": {}
6263 })
6264 );
6265 }
6266
6267 #[test]
6268 fn test_auth_method_terminal_serialization() {
6269 let method = AuthMethod::Terminal(AuthMethodTerminal::new("tui-auth", "Terminal Auth"));
6270
6271 let json = serde_json::to_value(&method).unwrap();
6272 assert_eq!(
6273 json,
6274 json!({
6275 "methodId": "tui-auth",
6276 "name": "Terminal Auth",
6277 "type": "terminal"
6278 })
6279 );
6280 assert!(!json.as_object().unwrap().contains_key("args"));
6282 assert!(!json.as_object().unwrap().contains_key("env"));
6283
6284 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
6285 match deserialized {
6286 AuthMethod::Terminal(AuthMethodTerminal { args, env, .. }) => {
6287 assert!(args.is_empty());
6288 assert!(env.is_empty());
6289 }
6290 _ => panic!("Expected Terminal variant"),
6291 }
6292 }
6293
6294 #[test]
6295 fn test_auth_method_terminal_with_args_and_env_serialization() {
6296 let method = AuthMethod::Terminal(
6297 AuthMethodTerminal::new("tui-auth", "Terminal Auth")
6298 .args(vec!["--interactive".to_string(), "--color".to_string()])
6299 .env(vec![EnvVariable::new("TERM", "xterm-256color")]),
6300 );
6301
6302 let json = serde_json::to_value(&method).unwrap();
6303 assert_eq!(
6304 json,
6305 json!({
6306 "methodId": "tui-auth",
6307 "name": "Terminal Auth",
6308 "type": "terminal",
6309 "args": ["--interactive", "--color"],
6310 "env": [
6311 {
6312 "name": "TERM",
6313 "value": "xterm-256color"
6314 }
6315 ]
6316 })
6317 );
6318
6319 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
6320 match deserialized {
6321 AuthMethod::Terminal(AuthMethodTerminal { args, env, .. }) => {
6322 assert_eq!(args, vec!["--interactive", "--color"]);
6323 assert_eq!(env.len(), 1);
6324 assert_eq!(env[0].name, "TERM");
6325 assert_eq!(env[0].value, "xterm-256color");
6326 }
6327 _ => panic!("Expected Terminal variant"),
6328 }
6329 }
6330
6331 #[test]
6332 fn test_session_config_option_id_serialize() {
6333 let val = SessionConfigOptionValue::id("model-1");
6334 let json = serde_json::to_value(&val).unwrap();
6335 assert_eq!(json, json!({ "type": "id", "value": "model-1" }));
6336 }
6337
6338 #[test]
6339 fn test_session_config_option_value_boolean_serialize() {
6340 let val = SessionConfigOptionValue::boolean(true);
6341 let json = serde_json::to_value(&val).unwrap();
6342 assert_eq!(json, json!({ "type": "boolean", "value": true }));
6343 }
6344
6345 #[test]
6346 fn test_session_config_option_value_deserialize_id() {
6347 let json = json!({ "type": "id", "value": "model-1" });
6348 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6349 assert_eq!(val, SessionConfigOptionValue::id("model-1"));
6350 assert_eq!(val.as_id().unwrap().to_string(), "model-1");
6351 }
6352
6353 #[test]
6354 fn test_session_config_option_value_deserialize_requires_type() {
6355 let json = json!({ "value": "model-1" });
6356 let result = serde_json::from_value::<SessionConfigOptionValue>(json);
6357 assert!(result.is_err());
6358 }
6359
6360 #[test]
6361 fn test_session_config_option_value_deserialize_boolean() {
6362 let json = json!({ "type": "boolean", "value": true });
6363 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6364 assert_eq!(val, SessionConfigOptionValue::boolean(true));
6365 assert_eq!(val.as_bool(), Some(true));
6366 }
6367
6368 #[test]
6369 fn test_session_config_option_value_deserialize_boolean_false() {
6370 let json = json!({ "type": "boolean", "value": false });
6371 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6372 assert_eq!(val, SessionConfigOptionValue::boolean(false));
6373 assert_eq!(val.as_bool(), Some(false));
6374 }
6375
6376 #[test]
6377 fn test_session_config_option_value_deserialize_unknown_type_with_string_value() {
6378 let json = json!({
6379 "type": "text",
6380 "value": "freeform input",
6381 "maxLength": 200
6382 });
6383 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6384 let SessionConfigOptionValue::Other(unknown) = val else {
6385 panic!("Expected Other variant");
6386 };
6387 assert_eq!(unknown.type_, "text");
6388 assert_eq!(unknown.value, json!("freeform input"));
6389 assert_eq!(unknown.fields["maxLength"], json!(200));
6390 }
6391
6392 #[test]
6393 fn test_session_config_option_value_deserialize_unknown_type_with_object_value() {
6394 let json = json!({
6395 "type": "range",
6396 "value": { "min": 1, "max": 5 }
6397 });
6398 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6399 let SessionConfigOptionValue::Other(unknown) = val else {
6400 panic!("Expected Other variant");
6401 };
6402 assert_eq!(unknown.type_, "range");
6403 assert_eq!(unknown.value, json!({ "min": 1, "max": 5 }));
6404 }
6405
6406 #[test]
6407 fn test_session_config_option_value_roundtrip_id() {
6408 let original = SessionConfigOptionValue::id("option-a");
6409 let json = serde_json::to_value(&original).unwrap();
6410 let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6411 assert_eq!(original, roundtripped);
6412 }
6413
6414 #[test]
6415 fn test_session_config_option_value_roundtrip_boolean() {
6416 let original = SessionConfigOptionValue::boolean(false);
6417 let json = serde_json::to_value(&original).unwrap();
6418 let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6419 assert_eq!(original, roundtripped);
6420 }
6421
6422 #[test]
6423 fn test_session_config_option_value_roundtrip_other() {
6424 let mut fields = BTreeMap::new();
6425 fields.insert("maxLength".to_string(), json!(200));
6426 let original = SessionConfigOptionValue::Other(OtherSessionConfigOptionValue::new(
6427 "text",
6428 json!("freeform input"),
6429 fields,
6430 ));
6431 let json = serde_json::to_value(&original).unwrap();
6432 let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6433 assert_eq!(original, roundtripped);
6434 }
6435
6436 #[test]
6437 fn test_session_config_option_value_type_mismatch_boolean_with_string() {
6438 let json = json!({ "type": "boolean", "value": "not a bool" });
6439 let result = serde_json::from_value::<SessionConfigOptionValue>(json);
6440 assert!(result.is_err());
6441 }
6442
6443 #[test]
6444 fn test_session_config_option_value_from_impls() {
6445 let from_str: SessionConfigOptionValue = "model-1".into();
6446 assert_eq!(from_str.as_id().unwrap().to_string(), "model-1");
6447
6448 let from_id: SessionConfigOptionValue = SessionConfigValueId::new("model-2").into();
6449 assert_eq!(from_id.as_id().unwrap().to_string(), "model-2");
6450
6451 let from_bool: SessionConfigOptionValue = true.into();
6452 assert_eq!(from_bool.as_bool(), Some(true));
6453 }
6454
6455 #[test]
6456 fn test_set_session_config_option_request_id() {
6457 let req = SetSessionConfigOptionRequest::new("sess_1", "model", "model-1");
6458 let json = serde_json::to_value(&req).unwrap();
6459 assert_eq!(
6460 json,
6461 json!({
6462 "sessionId": "sess_1",
6463 "configId": "model",
6464 "type": "id",
6465 "value": "model-1"
6466 })
6467 );
6468 }
6469
6470 #[test]
6471 fn test_set_session_config_option_request_boolean() {
6472 let req = SetSessionConfigOptionRequest::new("sess_1", "brave_mode", true);
6473 let json = serde_json::to_value(&req).unwrap();
6474 assert_eq!(
6475 json,
6476 json!({
6477 "sessionId": "sess_1",
6478 "configId": "brave_mode",
6479 "type": "boolean",
6480 "value": true
6481 })
6482 );
6483 }
6484
6485 #[test]
6486 fn test_set_session_config_option_request_deserialize_requires_type() {
6487 let json = json!({
6488 "sessionId": "sess_1",
6489 "configId": "model",
6490 "value": "model-1"
6491 });
6492 let result = serde_json::from_value::<SetSessionConfigOptionRequest>(json);
6493 assert!(result.is_err());
6494 }
6495
6496 #[test]
6497 fn test_set_session_config_option_request_deserialize_boolean() {
6498 let json = json!({
6499 "sessionId": "sess_1",
6500 "configId": "brave_mode",
6501 "type": "boolean",
6502 "value": true
6503 });
6504 let req: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
6505 assert_eq!(req.value.as_bool(), Some(true));
6506 }
6507
6508 #[test]
6509 fn test_set_session_config_option_request_roundtrip_id() {
6510 let original = SetSessionConfigOptionRequest::new("s", "c", "v");
6511 let json = serde_json::to_value(&original).unwrap();
6512 let roundtripped: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
6513 assert_eq!(original, roundtripped);
6514 }
6515
6516 #[test]
6517 fn test_set_session_config_option_request_roundtrip_boolean() {
6518 let original = SetSessionConfigOptionRequest::new("s", "c", false);
6519 let json = serde_json::to_value(&original).unwrap();
6520 let roundtripped: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
6521 assert_eq!(original, roundtripped);
6522 }
6523
6524 #[test]
6525 fn test_session_config_boolean_serialization() {
6526 let cfg = SessionConfigBoolean::new(true);
6527 let json = serde_json::to_value(&cfg).unwrap();
6528 assert_eq!(json, json!({ "currentValue": true }));
6529
6530 let deserialized: SessionConfigBoolean = serde_json::from_value(json).unwrap();
6531 assert!(deserialized.current_value);
6532 }
6533
6534 #[test]
6535 fn test_session_config_option_boolean_variant() {
6536 let opt = SessionConfigOption::boolean("brave_mode", "Brave Mode", false)
6537 .description("Skip confirmation prompts")
6538 .meta(test_meta());
6539 assert_eq!(serialized_meta_key_count(&opt), 1);
6540
6541 let json = serde_json::to_value(&opt).unwrap();
6542 assert_eq!(
6543 json,
6544 json!({
6545 "configId": "brave_mode",
6546 "name": "Brave Mode",
6547 "description": "Skip confirmation prompts",
6548 "type": "boolean",
6549 "currentValue": false,
6550 "_meta": {
6551 "source": "test"
6552 }
6553 })
6554 );
6555
6556 let deserialized: SessionConfigOption = serde_json::from_value(json).unwrap();
6557 assert_eq!(deserialized.config_id.to_string(), "brave_mode");
6558 assert_eq!(deserialized.name, "Brave Mode");
6559 match deserialized.kind {
6560 SessionConfigKind::Boolean(ref b) => assert!(!b.current_value),
6561 _ => panic!("Expected Boolean kind"),
6562 }
6563 }
6564
6565 #[test]
6566 fn test_session_config_option_select_still_works() {
6567 let opt = SessionConfigOption::select(
6569 "model",
6570 "Model",
6571 "model-1",
6572 vec![
6573 SessionConfigSelectOption::new("model-1", "Model 1"),
6574 SessionConfigSelectOption::new("model-2", "Model 2"),
6575 ],
6576 )
6577 .meta(test_meta());
6578 assert_eq!(serialized_meta_key_count(&opt), 1);
6579
6580 let json = serde_json::to_value(&opt).unwrap();
6581 assert_eq!(json["type"], "select");
6582 assert_eq!(json["currentValue"], "model-1");
6583 assert_eq!(json["options"].as_array().unwrap().len(), 2);
6584 assert_eq!(json["_meta"]["source"], "test");
6585
6586 let deserialized: SessionConfigOption = serde_json::from_value(json).unwrap();
6587 match deserialized.kind {
6588 SessionConfigKind::Select(ref s) => {
6589 assert_eq!(s.current_value.to_string(), "model-1");
6590 }
6591 _ => panic!("Expected Select kind"),
6592 }
6593 }
6594
6595 #[test]
6596 fn test_session_config_option_unknown_kind_roundtrip() {
6597 let option: SessionConfigOption = serde_json::from_value(json!({
6598 "configId": "verbosity",
6599 "name": "Verbosity",
6600 "type": "_slider",
6601 "currentValue": 3,
6602 "min": 0,
6603 "max": 5,
6604 "_meta": {
6605 "source": "test"
6606 }
6607 }))
6608 .unwrap();
6609
6610 assert_eq!(option.config_id.to_string(), "verbosity");
6611 assert_eq!(option.meta.as_ref().unwrap()["source"], "test");
6612 let SessionConfigKind::Other(unknown) = &option.kind else {
6613 panic!("expected unknown config kind");
6614 };
6615 assert_eq!(unknown.type_, "_slider");
6616 assert_eq!(unknown.fields.get("currentValue"), Some(&json!(3)));
6617 assert!(!unknown.fields.contains_key("_meta"));
6618 assert_eq!(serialized_meta_key_count(&option), 1);
6619
6620 let json = serde_json::to_value(&option).unwrap();
6621 assert_eq!(json["type"], "_slider");
6622 assert_eq!(json["currentValue"], 3);
6623 assert_eq!(json["min"], 0);
6624 assert_eq!(json["max"], 5);
6625 assert_eq!(json["_meta"]["source"], "test");
6626 }
6627
6628 #[test]
6629 fn test_session_config_option_unknown_kind_does_not_duplicate_flattened_meta() {
6630 let mut fields = std::collections::BTreeMap::new();
6631 fields.insert("currentValue".to_string(), json!(3));
6632 fields.insert("_meta".to_string(), json!({ "inner": "ignored" }));
6633
6634 let option = SessionConfigOption::new(
6635 "verbosity",
6636 "Verbosity",
6637 SessionConfigKind::Other(OtherSessionConfigKind::new("_slider", fields)),
6638 )
6639 .meta(test_meta());
6640
6641 let SessionConfigKind::Other(unknown) = &option.kind else {
6642 panic!("expected unknown config kind");
6643 };
6644 assert!(!unknown.fields.contains_key("_meta"));
6645 assert_eq!(serialized_meta_key_count(&option), 1);
6646
6647 let json = serde_json::to_value(&option).unwrap();
6648 assert_eq!(json["type"], "_slider");
6649 assert_eq!(json["currentValue"], 3);
6650 assert_eq!(json["_meta"]["source"], "test");
6651 }
6652
6653 #[test]
6654 fn test_session_config_option_unknown_does_not_hide_malformed_known_kind() {
6655 assert!(
6656 serde_json::from_value::<SessionConfigOption>(json!({
6657 "configId": "model",
6658 "name": "Model",
6659 "type": "select"
6660 }))
6661 .is_err()
6662 );
6663 }
6664
6665 #[cfg(feature = "unstable_llm_providers")]
6666 #[test]
6667 fn test_llm_protocol_known_variants() {
6668 assert_eq!(
6669 serde_json::to_value(&LlmProtocol::Anthropic).unwrap(),
6670 json!("anthropic")
6671 );
6672 assert_eq!(
6673 serde_json::to_value(&LlmProtocol::OpenAi).unwrap(),
6674 json!("openai")
6675 );
6676 assert_eq!(
6677 serde_json::to_value(&LlmProtocol::Azure).unwrap(),
6678 json!("azure")
6679 );
6680 assert_eq!(
6681 serde_json::to_value(&LlmProtocol::Vertex).unwrap(),
6682 json!("vertex")
6683 );
6684 assert_eq!(
6685 serde_json::to_value(&LlmProtocol::Bedrock).unwrap(),
6686 json!("bedrock")
6687 );
6688
6689 assert_eq!(
6690 serde_json::from_str::<LlmProtocol>("\"anthropic\"").unwrap(),
6691 LlmProtocol::Anthropic
6692 );
6693 assert_eq!(
6694 serde_json::from_str::<LlmProtocol>("\"openai\"").unwrap(),
6695 LlmProtocol::OpenAi
6696 );
6697 assert_eq!(
6698 serde_json::from_str::<LlmProtocol>("\"azure\"").unwrap(),
6699 LlmProtocol::Azure
6700 );
6701 assert_eq!(
6702 serde_json::from_str::<LlmProtocol>("\"vertex\"").unwrap(),
6703 LlmProtocol::Vertex
6704 );
6705 assert_eq!(
6706 serde_json::from_str::<LlmProtocol>("\"bedrock\"").unwrap(),
6707 LlmProtocol::Bedrock
6708 );
6709 }
6710
6711 #[cfg(feature = "unstable_llm_providers")]
6712 #[test]
6713 fn test_llm_protocol_unknown_variant() {
6714 let unknown: LlmProtocol = serde_json::from_str("\"cohere\"").unwrap();
6715 assert_eq!(unknown, LlmProtocol::Other("cohere".to_string()));
6716
6717 let json = serde_json::to_value(&unknown).unwrap();
6718 assert_eq!(json, json!("cohere"));
6719 }
6720
6721 #[cfg(feature = "unstable_llm_providers")]
6722 #[test]
6723 fn test_provider_current_config_serialization() {
6724 let config =
6725 ProviderCurrentConfig::new(LlmProtocol::Anthropic, "https://api.anthropic.com");
6726
6727 let json = serde_json::to_value(&config).unwrap();
6728 assert_eq!(
6729 json,
6730 json!({
6731 "apiType": "anthropic",
6732 "baseUrl": "https://api.anthropic.com"
6733 })
6734 );
6735
6736 let deserialized: ProviderCurrentConfig = serde_json::from_value(json).unwrap();
6737 assert_eq!(deserialized.api_type, LlmProtocol::Anthropic);
6738 assert_eq!(deserialized.base_url, "https://api.anthropic.com");
6739 }
6740
6741 #[cfg(feature = "unstable_llm_providers")]
6742 #[test]
6743 fn test_provider_info_with_current_config() {
6744 let info = ProviderInfo::new(
6745 "main",
6746 vec![LlmProtocol::Anthropic, LlmProtocol::OpenAi],
6747 true,
6748 Some(ProviderCurrentConfig::new(
6749 LlmProtocol::Anthropic,
6750 "https://api.anthropic.com",
6751 )),
6752 );
6753
6754 let json = serde_json::to_value(&info).unwrap();
6755 assert_eq!(
6756 json,
6757 json!({
6758 "providerId": "main",
6759 "supported": ["anthropic", "openai"],
6760 "required": true,
6761 "current": {
6762 "apiType": "anthropic",
6763 "baseUrl": "https://api.anthropic.com"
6764 }
6765 })
6766 );
6767
6768 let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
6769 assert_eq!(deserialized.provider_id.to_string(), "main");
6770 assert_eq!(deserialized.supported.len(), 2);
6771 assert!(deserialized.required);
6772 assert!(deserialized.current.is_some());
6773 assert_eq!(
6774 deserialized.current.as_ref().unwrap().api_type,
6775 LlmProtocol::Anthropic
6776 );
6777 }
6778
6779 #[cfg(feature = "unstable_llm_providers")]
6780 #[test]
6781 fn test_provider_info_disabled() {
6782 let info = ProviderInfo::new(
6783 "secondary",
6784 vec![LlmProtocol::OpenAi],
6785 false,
6786 None::<ProviderCurrentConfig>,
6787 );
6788
6789 let json = serde_json::to_value(&info).unwrap();
6790 assert_eq!(
6791 json,
6792 json!({
6793 "providerId": "secondary",
6794 "supported": ["openai"],
6795 "required": false
6796 })
6797 );
6798
6799 let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
6800 assert_eq!(deserialized.provider_id.to_string(), "secondary");
6801 assert!(!deserialized.required);
6802 assert!(deserialized.current.is_none());
6803 }
6804
6805 #[cfg(feature = "unstable_llm_providers")]
6806 #[test]
6807 fn test_provider_info_missing_current_defaults_to_none() {
6808 let json = json!({
6810 "providerId": "main",
6811 "supported": ["anthropic"],
6812 "required": true
6813 });
6814 let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
6815 assert!(deserialized.current.is_none());
6816 }
6817
6818 #[cfg(feature = "unstable_llm_providers")]
6819 #[test]
6820 fn test_provider_info_explicit_null_current_decodes_to_none() {
6821 let json = json!({
6825 "providerId": "main",
6826 "supported": ["anthropic"],
6827 "required": true,
6828 "current": null
6829 });
6830 let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
6831 assert!(deserialized.current.is_none());
6832 }
6833
6834 #[cfg(feature = "unstable_llm_providers")]
6835 #[test]
6836 fn test_list_providers_response_serialization() {
6837 let response = ListProvidersResponse::new(vec![ProviderInfo::new(
6838 "main",
6839 vec![LlmProtocol::Anthropic],
6840 true,
6841 Some(ProviderCurrentConfig::new(
6842 LlmProtocol::Anthropic,
6843 "https://api.anthropic.com",
6844 )),
6845 )]);
6846
6847 let json = serde_json::to_value(&response).unwrap();
6848 assert_eq!(json["providers"].as_array().unwrap().len(), 1);
6849 assert_eq!(json["providers"][0]["providerId"], "main");
6850
6851 let deserialized: ListProvidersResponse = serde_json::from_value(json).unwrap();
6852 assert_eq!(deserialized.providers.len(), 1);
6853 }
6854
6855 #[cfg(feature = "unstable_llm_providers")]
6856 #[test]
6857 fn test_set_provider_request_serialization() {
6858 use std::collections::HashMap;
6859
6860 let mut headers = HashMap::new();
6861 headers.insert("Authorization".to_string(), "Bearer sk-test".to_string());
6862
6863 let request =
6864 SetProviderRequest::new("main", LlmProtocol::OpenAi, "https://api.openai.com/v1")
6865 .headers(headers);
6866
6867 let json = serde_json::to_value(&request).unwrap();
6868 assert_eq!(
6869 json,
6870 json!({
6871 "providerId": "main",
6872 "apiType": "openai",
6873 "baseUrl": "https://api.openai.com/v1",
6874 "headers": {
6875 "Authorization": "Bearer sk-test"
6876 }
6877 })
6878 );
6879
6880 let deserialized: SetProviderRequest = serde_json::from_value(json).unwrap();
6881 assert_eq!(deserialized.provider_id.to_string(), "main");
6882 assert_eq!(deserialized.api_type, LlmProtocol::OpenAi);
6883 assert_eq!(deserialized.base_url, "https://api.openai.com/v1");
6884 assert_eq!(deserialized.headers.len(), 1);
6885 assert_eq!(
6886 deserialized.headers.get("Authorization").unwrap(),
6887 "Bearer sk-test"
6888 );
6889 }
6890
6891 #[cfg(feature = "unstable_llm_providers")]
6892 #[test]
6893 fn test_set_provider_request_omits_empty_headers() {
6894 let request =
6895 SetProviderRequest::new("main", LlmProtocol::Anthropic, "https://api.anthropic.com");
6896
6897 let json = serde_json::to_value(&request).unwrap();
6898 assert!(!json.as_object().unwrap().contains_key("headers"));
6900 }
6901
6902 #[cfg(feature = "unstable_llm_providers")]
6903 #[test]
6904 fn test_disable_provider_request_serialization() {
6905 let request = DisableProviderRequest::new("secondary");
6906
6907 let json = serde_json::to_value(&request).unwrap();
6908 assert_eq!(json, json!({ "providerId": "secondary" }));
6909
6910 let deserialized: DisableProviderRequest = serde_json::from_value(json).unwrap();
6911 assert_eq!(deserialized.provider_id.to_string(), "secondary");
6912 }
6913
6914 #[cfg(feature = "unstable_llm_providers")]
6915 #[test]
6916 fn test_providers_capabilities_serialization() {
6917 let caps = ProvidersCapabilities::new();
6918
6919 let json = serde_json::to_value(&caps).unwrap();
6920 assert_eq!(json, json!({}));
6921
6922 let deserialized: ProvidersCapabilities = serde_json::from_value(json).unwrap();
6923 assert!(deserialized.meta.is_none());
6924 }
6925
6926 #[cfg(feature = "unstable_llm_providers")]
6927 #[test]
6928 fn test_agent_capabilities_with_providers() {
6929 let caps = AgentCapabilities::new().providers(ProvidersCapabilities::new());
6930
6931 let json = serde_json::to_value(&caps).unwrap();
6932 assert_eq!(json["providers"], json!({}));
6933
6934 let deserialized: AgentCapabilities = serde_json::from_value(json).unwrap();
6935 assert!(deserialized.providers.is_some());
6936 }
6937
6938 #[test]
6939 fn test_agent_capabilities_session_is_explicit() {
6940 let json = serde_json::to_value(AgentCapabilities::new()).unwrap();
6941 assert!(json.get("session").is_none());
6942
6943 let caps = AgentCapabilities::new().session(
6944 SessionCapabilities::new()
6945 .prompt(PromptCapabilities::new().image(PromptImageCapabilities::new()))
6946 .mcp(McpCapabilities::new().stdio(McpStdioCapabilities::new())),
6947 );
6948
6949 assert_eq!(
6950 serde_json::to_value(&caps).unwrap(),
6951 json!({
6952 "session": {
6953 "prompt": {
6954 "image": {}
6955 },
6956 "mcp": {
6957 "stdio": {}
6958 }
6959 }
6960 })
6961 );
6962
6963 let deserialized: AgentCapabilities = serde_json::from_value(json!({
6964 "session": false
6965 }))
6966 .unwrap();
6967 assert!(deserialized.session.is_none());
6968 }
6969
6970 #[test]
6971 fn test_prompt_capabilities_serialize_supported_content_as_objects() {
6972 let caps = PromptCapabilities::new()
6973 .image(PromptImageCapabilities::new())
6974 .audio(PromptAudioCapabilities::new())
6975 .embedded_context(PromptEmbeddedContextCapabilities::new());
6976
6977 assert_eq!(
6978 serde_json::to_value(&caps).unwrap(),
6979 json!({
6980 "image": {},
6981 "audio": {},
6982 "embeddedContext": {}
6983 })
6984 );
6985
6986 let deserialized: PromptCapabilities = serde_json::from_value(json!({
6987 "image": null,
6988 "audio": false,
6989 "embeddedContext": {}
6990 }))
6991 .unwrap();
6992 assert!(deserialized.image.is_none());
6993 assert!(deserialized.audio.is_none());
6994 assert!(deserialized.embedded_context.is_some());
6995 }
6996
6997 #[test]
6998 fn test_mcp_capabilities_serialize_supported_transports_as_objects() {
6999 let caps = McpCapabilities::new()
7000 .stdio(McpStdioCapabilities::new())
7001 .http(McpHttpCapabilities::new());
7002
7003 assert_eq!(
7004 serde_json::to_value(&caps).unwrap(),
7005 json!({
7006 "stdio": {},
7007 "http": {}
7008 })
7009 );
7010
7011 let deserialized: McpCapabilities = serde_json::from_value(json!({
7012 "stdio": null,
7013 "http": false
7014 }))
7015 .unwrap();
7016 assert!(deserialized.stdio.is_none());
7017 assert!(deserialized.http.is_none());
7018 }
7019
7020 #[cfg(feature = "unstable_mcp_over_acp")]
7021 #[test]
7022 fn test_mcp_capabilities_serialize_acp_support_as_object() {
7023 let caps = McpCapabilities::new().acp(McpAcpCapabilities::new());
7024
7025 assert_eq!(
7026 serde_json::to_value(&caps).unwrap(),
7027 json!({
7028 "acp": {}
7029 })
7030 );
7031 }
7032
7033 #[test]
7034 fn prompt_request_rejects_malformed_content_block() {
7035 use serde_json::json;
7036
7037 assert!(
7038 serde_json::from_value::<PromptRequest>(json!({
7039 "sessionId": "sess-1",
7040 "prompt": [{"type": "text"}]
7041 }))
7042 .is_err()
7043 );
7044 }
7045
7046 #[test]
7047 fn prompt_request_rejects_non_array_prompt() {
7048 use serde_json::json;
7049
7050 assert!(
7051 serde_json::from_value::<PromptRequest>(json!({
7052 "sessionId": "sess-1",
7053 "prompt": "hello"
7054 }))
7055 .is_err()
7056 );
7057 }
7058}