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};
12use schemars::{JsonSchema, Schema};
13use serde::{Deserialize, Serialize};
14use serde_with::{DefaultOnError, VecSkipError, serde_as, skip_serializing_none};
15
16use super::{
17 AbsolutePath, ClientCapabilities, ContentBlock, ExtNotification, ExtRequest, ExtResponse, Meta,
18 SessionId,
19};
20#[cfg(feature = "unstable_auth_methods")]
21use crate::DefaultTrueOnError;
22use crate::{IntoOption, ProtocolVersion, SkipListener};
23
24#[cfg(feature = "unstable_mcp_over_acp")]
25use super::mcp::{
26 MCP_MESSAGE_METHOD_NAME, MessageMcpNotification, MessageMcpRequest, MessageMcpResponse,
27};
28
29#[cfg(feature = "unstable_nes")]
30use super::{
31 AcceptNesNotification, CloseNesRequest, CloseNesResponse, DidChangeDocumentNotification,
32 DidCloseDocumentNotification, DidFocusDocumentNotification, DidOpenDocumentNotification,
33 DidSaveDocumentNotification, NesCapabilities, PositionEncodingKind, RejectNesNotification,
34 StartNesRequest, StartNesResponse, SuggestNesRequest, SuggestNesResponse,
35};
36
37#[cfg(feature = "unstable_nes")]
38use super::{
39 DOCUMENT_DID_CHANGE_METHOD_NAME, DOCUMENT_DID_CLOSE_METHOD_NAME,
40 DOCUMENT_DID_FOCUS_METHOD_NAME, DOCUMENT_DID_OPEN_METHOD_NAME, DOCUMENT_DID_SAVE_METHOD_NAME,
41 NES_ACCEPT_METHOD_NAME, NES_CLOSE_METHOD_NAME, NES_REJECT_METHOD_NAME, NES_START_METHOD_NAME,
42 NES_SUGGEST_METHOD_NAME,
43};
44
45#[serde_as]
53#[skip_serializing_none]
54#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
55#[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 #[schemars(extend("x-deserialize-default-on-error" = true))]
66 #[serde(default)]
67 pub capabilities: ClientCapabilities,
68 #[serde_as(deserialize_as = "DefaultOnError")]
74 #[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#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
119#[schemars(extend("x-side" = "agent", "x-method" = INITIALIZE_METHOD_NAME))]
120#[serde(rename_all = "camelCase")]
121#[non_exhaustive]
122pub struct InitializeResponse {
123 pub protocol_version: ProtocolVersion,
128 pub info: Implementation,
130 #[serde_as(deserialize_as = "DefaultOnError")]
132 #[schemars(extend("x-deserialize-default-on-error" = true))]
133 #[serde(default)]
134 pub capabilities: AgentCapabilities,
135 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
141 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
142 #[serde(default, skip_serializing_if = "Vec::is_empty")]
143 pub auth_methods: Vec<AuthMethod>,
144 #[serde_as(deserialize_as = "DefaultOnError")]
150 #[schemars(extend("x-deserialize-default-on-error" = true))]
151 #[serde(default)]
152 #[serde(rename = "_meta")]
153 pub meta: Option<Meta>,
154}
155
156impl InitializeResponse {
157 #[must_use]
159 pub fn new(protocol_version: ProtocolVersion, info: Implementation) -> Self {
160 Self {
161 protocol_version,
162 capabilities: AgentCapabilities::default(),
163 auth_methods: vec![],
164 info,
165 meta: None,
166 }
167 }
168
169 #[must_use]
171 pub fn capabilities(mut self, capabilities: AgentCapabilities) -> Self {
172 self.capabilities = capabilities;
173 self
174 }
175
176 #[must_use]
181 pub fn auth_methods(mut self, auth_methods: Vec<AuthMethod>) -> Self {
182 self.auth_methods = auth_methods;
183 self
184 }
185
186 #[must_use]
192 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
193 self.meta = meta.into_option();
194 self
195 }
196}
197
198#[serde_as]
202#[skip_serializing_none]
203#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
204#[serde(rename_all = "camelCase")]
205#[non_exhaustive]
206pub struct Implementation {
207 pub name: String,
210 #[serde_as(deserialize_as = "DefaultOnError")]
215 #[schemars(extend("x-deserialize-default-on-error" = true))]
216 #[serde(default)]
217 pub title: Option<String>,
218 pub version: String,
221 #[serde_as(deserialize_as = "DefaultOnError")]
227 #[schemars(extend("x-deserialize-default-on-error" = true))]
228 #[serde(default)]
229 #[serde(rename = "_meta")]
230 pub meta: Option<Meta>,
231}
232
233impl Implementation {
234 #[must_use]
236 pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
237 Self {
238 name: name.into(),
239 title: None,
240 version: version.into(),
241 meta: None,
242 }
243 }
244
245 #[must_use]
250 pub fn title(mut self, title: impl IntoOption<String>) -> Self {
251 self.title = title.into_option();
252 self
253 }
254
255 #[must_use]
261 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
262 self.meta = meta.into_option();
263 self
264 }
265}
266
267#[serde_as]
277#[skip_serializing_none]
278#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
279#[schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGIN_METHOD_NAME))]
280#[serde(rename_all = "camelCase")]
281#[non_exhaustive]
282pub struct LoginAuthRequest {
283 pub method_id: AuthMethodId,
286 #[serde_as(deserialize_as = "DefaultOnError")]
292 #[schemars(extend("x-deserialize-default-on-error" = true))]
293 #[serde(default)]
294 #[serde(rename = "_meta")]
295 pub meta: Option<Meta>,
296}
297
298impl LoginAuthRequest {
299 #[must_use]
301 pub fn new(method_id: impl Into<AuthMethodId>) -> Self {
302 Self {
303 method_id: method_id.into(),
304 meta: None,
305 }
306 }
307
308 #[must_use]
314 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
315 self.meta = meta.into_option();
316 self
317 }
318}
319
320#[serde_as]
322#[skip_serializing_none]
323#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
324#[schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGIN_METHOD_NAME))]
325#[serde(rename_all = "camelCase")]
326#[non_exhaustive]
327pub struct LoginAuthResponse {
328 #[serde_as(deserialize_as = "DefaultOnError")]
334 #[schemars(extend("x-deserialize-default-on-error" = true))]
335 #[serde(default)]
336 #[serde(rename = "_meta")]
337 pub meta: Option<Meta>,
338}
339
340impl LoginAuthResponse {
341 #[must_use]
343 pub fn new() -> Self {
344 Self::default()
345 }
346
347 #[must_use]
353 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
354 self.meta = meta.into_option();
355 self
356 }
357}
358
359#[serde_as]
369#[skip_serializing_none]
370#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
371#[schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGOUT_METHOD_NAME))]
372#[serde(rename_all = "camelCase")]
373#[non_exhaustive]
374pub struct LogoutAuthRequest {
375 #[serde_as(deserialize_as = "DefaultOnError")]
381 #[schemars(extend("x-deserialize-default-on-error" = true))]
382 #[serde(default)]
383 #[serde(rename = "_meta")]
384 pub meta: Option<Meta>,
385}
386
387impl LogoutAuthRequest {
388 #[must_use]
390 pub fn new() -> Self {
391 Self::default()
392 }
393
394 #[must_use]
400 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
401 self.meta = meta.into_option();
402 self
403 }
404}
405
406#[serde_as]
408#[skip_serializing_none]
409#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
410#[schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGOUT_METHOD_NAME))]
411#[serde(rename_all = "camelCase")]
412#[non_exhaustive]
413pub struct LogoutAuthResponse {
414 #[serde_as(deserialize_as = "DefaultOnError")]
420 #[schemars(extend("x-deserialize-default-on-error" = true))]
421 #[serde(default)]
422 #[serde(rename = "_meta")]
423 pub meta: Option<Meta>,
424}
425
426impl LogoutAuthResponse {
427 #[must_use]
429 pub fn new() -> Self {
430 Self::default()
431 }
432
433 #[must_use]
439 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
440 self.meta = meta.into_option();
441 self
442 }
443}
444
445#[serde_as]
451#[skip_serializing_none]
452#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
453#[serde(rename_all = "camelCase")]
454#[non_exhaustive]
455pub struct AgentAuthCapabilities {
456 #[serde_as(deserialize_as = "DefaultOnError")]
462 #[schemars(extend("x-deserialize-default-on-error" = true))]
463 #[serde(default)]
464 #[serde(rename = "_meta")]
465 pub meta: Option<Meta>,
466}
467
468impl AgentAuthCapabilities {
469 #[must_use]
471 pub fn new() -> Self {
472 Self::default()
473 }
474
475 #[must_use]
481 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
482 self.meta = meta.into_option();
483 self
484 }
485}
486
487#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, Display, From)]
489#[serde(transparent)]
490#[from(forward)]
491#[non_exhaustive]
492pub struct AuthMethodId(pub Arc<str>);
493
494impl AuthMethodId {
495 #[must_use]
497 pub fn new(id: impl Into<Self>) -> Self {
498 id.into()
499 }
500}
501
502#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
506#[serde(tag = "type", rename_all = "snake_case")]
507#[non_exhaustive]
508pub enum AuthMethod {
509 #[cfg(feature = "unstable_auth_methods")]
515 EnvVar(AuthMethodEnvVar),
516 #[cfg(feature = "unstable_auth_methods")]
522 Terminal(AuthMethodTerminal),
523 Agent(AuthMethodAgent),
527 #[serde(untagged)]
537 Other(OtherAuthMethod),
538}
539
540impl AuthMethod {
541 #[must_use]
543 pub fn method_id(&self) -> &AuthMethodId {
544 match self {
545 Self::Agent(a) => &a.method_id,
546 Self::Other(a) => &a.method_id,
547 #[cfg(feature = "unstable_auth_methods")]
548 Self::EnvVar(e) => &e.method_id,
549 #[cfg(feature = "unstable_auth_methods")]
550 Self::Terminal(t) => &t.method_id,
551 }
552 }
553
554 #[must_use]
556 pub fn name(&self) -> &str {
557 match self {
558 Self::Agent(a) => &a.name,
559 Self::Other(a) => &a.name,
560 #[cfg(feature = "unstable_auth_methods")]
561 Self::EnvVar(e) => &e.name,
562 #[cfg(feature = "unstable_auth_methods")]
563 Self::Terminal(t) => &t.name,
564 }
565 }
566
567 #[must_use]
569 pub fn description(&self) -> Option<&str> {
570 match self {
571 Self::Agent(a) => a.description.as_deref(),
572 Self::Other(a) => a.description.as_deref(),
573 #[cfg(feature = "unstable_auth_methods")]
574 Self::EnvVar(e) => e.description.as_deref(),
575 #[cfg(feature = "unstable_auth_methods")]
576 Self::Terminal(t) => t.description.as_deref(),
577 }
578 }
579
580 #[must_use]
586 pub fn meta(&self) -> Option<&Meta> {
587 match self {
588 Self::Agent(a) => a.meta.as_ref(),
589 Self::Other(a) => a.meta.as_ref(),
590 #[cfg(feature = "unstable_auth_methods")]
591 Self::EnvVar(e) => e.meta.as_ref(),
592 #[cfg(feature = "unstable_auth_methods")]
593 Self::Terminal(t) => t.meta.as_ref(),
594 }
595 }
596}
597
598#[serde_as]
600#[skip_serializing_none]
601#[derive(Debug, Clone, Serialize, JsonSchema, PartialEq, Eq)]
602#[schemars(inline)]
603#[schemars(transform = other_auth_method_schema)]
604#[serde(rename_all = "camelCase")]
605#[non_exhaustive]
606pub struct OtherAuthMethod {
607 #[serde(rename = "type")]
613 pub type_: String,
614 pub method_id: AuthMethodId,
616 pub name: String,
618 #[serde_as(deserialize_as = "DefaultOnError")]
620 #[schemars(extend("x-deserialize-default-on-error" = true))]
621 #[serde(default)]
622 pub description: Option<String>,
623 #[serde_as(deserialize_as = "DefaultOnError")]
629 #[schemars(extend("x-deserialize-default-on-error" = true))]
630 #[serde(default)]
631 #[serde(rename = "_meta")]
632 pub meta: Option<Meta>,
633 #[serde(flatten)]
635 pub fields: BTreeMap<String, serde_json::Value>,
636}
637
638impl OtherAuthMethod {
639 #[must_use]
641 pub fn new(
642 type_: impl Into<String>,
643 method_id: impl Into<AuthMethodId>,
644 name: impl Into<String>,
645 mut fields: BTreeMap<String, serde_json::Value>,
646 ) -> Self {
647 fields.remove("type");
648 fields.remove("methodId");
649 fields.remove("name");
650 fields.remove("description");
651 fields.remove("_meta");
652 Self {
653 type_: type_.into(),
654 method_id: method_id.into(),
655 name: name.into(),
656 description: None,
657 meta: None,
658 fields,
659 }
660 }
661
662 #[must_use]
664 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
665 self.description = description.into_option();
666 self
667 }
668
669 #[must_use]
675 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
676 self.meta = meta.into_option();
677 self
678 }
679}
680
681impl<'de> Deserialize<'de> for OtherAuthMethod {
682 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
683 where
684 D: serde::Deserializer<'de>,
685 {
686 #[derive(Deserialize)]
687 #[serde(rename_all = "camelCase")]
688 struct RawOtherAuthMethod {
689 #[serde(rename = "type")]
690 type_: String,
691 method_id: AuthMethodId,
692 name: String,
693 description: Option<String>,
694 #[serde(rename = "_meta")]
695 meta: Option<Meta>,
696 #[serde(flatten)]
697 fields: BTreeMap<String, serde_json::Value>,
698 }
699
700 let raw = RawOtherAuthMethod::deserialize(deserializer)?;
701 if is_known_auth_method_type(&raw.type_) {
702 return Err(serde::de::Error::custom(format!(
703 "known authentication method `{}` did not match its schema",
704 raw.type_
705 )));
706 }
707
708 Ok(Self {
709 type_: raw.type_,
710 method_id: raw.method_id,
711 name: raw.name,
712 description: raw.description,
713 meta: raw.meta,
714 fields: raw.fields,
715 })
716 }
717}
718
719fn is_known_auth_method_type(type_: &str) -> bool {
720 match type_ {
721 "agent" => true,
722 #[cfg(feature = "unstable_auth_methods")]
723 "env_var" | "terminal" => true,
724 _ => false,
725 }
726}
727
728fn other_auth_method_schema(schema: &mut Schema) {
729 super::schema_util::reject_known_string_discriminators(
730 schema,
731 "type",
732 &[
733 "agent",
734 #[cfg(feature = "unstable_auth_methods")]
735 "env_var",
736 #[cfg(feature = "unstable_auth_methods")]
737 "terminal",
738 ],
739 );
740}
741
742#[serde_as]
746#[skip_serializing_none]
747#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
748#[serde(rename_all = "camelCase")]
749#[non_exhaustive]
750pub struct AuthMethodAgent {
751 pub method_id: AuthMethodId,
753 pub name: String,
755 #[serde_as(deserialize_as = "DefaultOnError")]
757 #[schemars(extend("x-deserialize-default-on-error" = true))]
758 #[serde(default)]
759 pub description: Option<String>,
760 #[serde_as(deserialize_as = "DefaultOnError")]
766 #[schemars(extend("x-deserialize-default-on-error" = true))]
767 #[serde(default)]
768 #[serde(rename = "_meta")]
769 pub meta: Option<Meta>,
770}
771
772impl AuthMethodAgent {
773 #[must_use]
775 pub fn new(method_id: impl Into<AuthMethodId>, name: impl Into<String>) -> Self {
776 Self {
777 method_id: method_id.into(),
778 name: name.into(),
779 description: None,
780 meta: None,
781 }
782 }
783
784 #[must_use]
786 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
787 self.description = description.into_option();
788 self
789 }
790
791 #[must_use]
797 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
798 self.meta = meta.into_option();
799 self
800 }
801}
802
803#[cfg(feature = "unstable_auth_methods")]
811#[serde_as]
812#[skip_serializing_none]
813#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
814#[serde(rename_all = "camelCase")]
815#[non_exhaustive]
816pub struct AuthMethodEnvVar {
817 pub method_id: AuthMethodId,
819 pub name: String,
821 #[serde_as(deserialize_as = "DefaultOnError")]
823 #[schemars(extend("x-deserialize-default-on-error" = true))]
824 #[serde(default)]
825 pub description: Option<String>,
826 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
828 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
829 pub vars: Vec<AuthEnvVar>,
830 #[serde_as(deserialize_as = "DefaultOnError")]
832 #[schemars(extend("x-deserialize-default-on-error" = true))]
833 #[schemars(url)]
834 #[serde(default)]
835 pub link: Option<String>,
836 #[serde_as(deserialize_as = "DefaultOnError")]
842 #[schemars(extend("x-deserialize-default-on-error" = true))]
843 #[serde(default)]
844 #[serde(rename = "_meta")]
845 pub meta: Option<Meta>,
846}
847
848#[cfg(feature = "unstable_auth_methods")]
849impl AuthMethodEnvVar {
850 #[must_use]
852 pub fn new(
853 method_id: impl Into<AuthMethodId>,
854 name: impl Into<String>,
855 vars: Vec<AuthEnvVar>,
856 ) -> Self {
857 Self {
858 method_id: method_id.into(),
859 name: name.into(),
860 description: None,
861 vars,
862 link: None,
863 meta: None,
864 }
865 }
866
867 #[must_use]
869 pub fn link(mut self, link: impl IntoOption<String>) -> Self {
870 self.link = link.into_option();
871 self
872 }
873
874 #[must_use]
876 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
877 self.description = description.into_option();
878 self
879 }
880
881 #[must_use]
887 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
888 self.meta = meta.into_option();
889 self
890 }
891}
892
893#[cfg(feature = "unstable_auth_methods")]
899#[serde_as]
900#[skip_serializing_none]
901#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
902#[serde(rename_all = "camelCase")]
903#[non_exhaustive]
904pub struct AuthEnvVar {
905 pub name: String,
907 #[serde_as(deserialize_as = "DefaultOnError")]
909 #[schemars(extend("x-deserialize-default-on-error" = true))]
910 #[serde(default)]
911 pub label: Option<String>,
912 #[serde_as(deserialize_as = "DefaultTrueOnError")]
917 #[schemars(extend("x-deserialize-default-on-error" = true))]
918 #[serde(default = "default_true", skip_serializing_if = "is_true")]
919 #[schemars(extend("default" = true))]
920 pub secret: bool,
921 #[serde_as(deserialize_as = "DefaultOnError")]
925 #[schemars(extend("x-deserialize-default-on-error" = true))]
926 #[serde(default, skip_serializing_if = "is_false")]
927 #[schemars(extend("default" = false))]
928 pub optional: bool,
929 #[serde_as(deserialize_as = "DefaultOnError")]
935 #[schemars(extend("x-deserialize-default-on-error" = true))]
936 #[serde(default)]
937 #[serde(rename = "_meta")]
938 pub meta: Option<Meta>,
939}
940
941#[cfg(feature = "unstable_auth_methods")]
942fn default_true() -> bool {
943 true
944}
945
946#[cfg(feature = "unstable_auth_methods")]
947#[expect(clippy::trivially_copy_pass_by_ref)]
948fn is_true(v: &bool) -> bool {
949 *v
950}
951
952#[cfg(feature = "unstable_auth_methods")]
953#[expect(clippy::trivially_copy_pass_by_ref)]
954fn is_false(v: &bool) -> bool {
955 !*v
956}
957
958#[cfg(feature = "unstable_auth_methods")]
959impl AuthEnvVar {
960 #[must_use]
962 pub fn new(name: impl Into<String>) -> Self {
963 Self {
964 name: name.into(),
965 label: None,
966 secret: true,
967 optional: false,
968 meta: None,
969 }
970 }
971
972 #[must_use]
974 pub fn label(mut self, label: impl IntoOption<String>) -> Self {
975 self.label = label.into_option();
976 self
977 }
978
979 #[must_use]
982 pub fn secret(mut self, secret: bool) -> Self {
983 self.secret = secret;
984 self
985 }
986
987 #[must_use]
989 pub fn optional(mut self, optional: bool) -> Self {
990 self.optional = optional;
991 self
992 }
993
994 #[must_use]
1000 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1001 self.meta = meta.into_option();
1002 self
1003 }
1004}
1005
1006#[cfg(feature = "unstable_auth_methods")]
1014#[serde_as]
1015#[skip_serializing_none]
1016#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1017#[serde(rename_all = "camelCase")]
1018#[non_exhaustive]
1019pub struct AuthMethodTerminal {
1020 pub method_id: AuthMethodId,
1022 pub name: String,
1024 #[serde_as(deserialize_as = "DefaultOnError")]
1026 #[schemars(extend("x-deserialize-default-on-error" = true))]
1027 #[serde(default)]
1028 pub description: Option<String>,
1029 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1031 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
1032 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1033 pub args: Vec<String>,
1034 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1036 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
1037 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1038 pub env: Vec<EnvVariable>,
1039 #[serde_as(deserialize_as = "DefaultOnError")]
1045 #[schemars(extend("x-deserialize-default-on-error" = true))]
1046 #[serde(default)]
1047 #[serde(rename = "_meta")]
1048 pub meta: Option<Meta>,
1049}
1050
1051#[cfg(feature = "unstable_auth_methods")]
1052impl AuthMethodTerminal {
1053 #[must_use]
1055 pub fn new(method_id: impl Into<AuthMethodId>, name: impl Into<String>) -> Self {
1056 Self {
1057 method_id: method_id.into(),
1058 name: name.into(),
1059 description: None,
1060 args: Vec::new(),
1061 env: Vec::new(),
1062 meta: None,
1063 }
1064 }
1065
1066 #[must_use]
1068 pub fn args(mut self, args: Vec<String>) -> Self {
1069 self.args = args;
1070 self
1071 }
1072
1073 #[must_use]
1075 pub fn env(mut self, env: Vec<EnvVariable>) -> Self {
1076 self.env = env;
1077 self
1078 }
1079
1080 #[must_use]
1082 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
1083 self.description = description.into_option();
1084 self
1085 }
1086
1087 #[must_use]
1093 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1094 self.meta = meta.into_option();
1095 self
1096 }
1097}
1098
1099#[serde_as]
1105#[skip_serializing_none]
1106#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1107#[schemars(extend("x-side" = "agent", "x-method" = SESSION_NEW_METHOD_NAME))]
1108#[serde(rename_all = "camelCase")]
1109#[non_exhaustive]
1110pub struct NewSessionRequest {
1111 pub cwd: AbsolutePath,
1113 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1119 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
1120 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1121 pub additional_directories: Vec<AbsolutePath>,
1122 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1124 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
1125 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1126 pub mcp_servers: Vec<McpServer>,
1127 #[serde_as(deserialize_as = "DefaultOnError")]
1133 #[schemars(extend("x-deserialize-default-on-error" = true))]
1134 #[serde(default)]
1135 #[serde(rename = "_meta")]
1136 pub meta: Option<Meta>,
1137}
1138
1139impl NewSessionRequest {
1140 #[must_use]
1142 pub fn new(cwd: impl Into<AbsolutePath>) -> Self {
1143 Self {
1144 cwd: cwd.into(),
1145 additional_directories: vec![],
1146 mcp_servers: vec![],
1147 meta: None,
1148 }
1149 }
1150
1151 #[must_use]
1153 pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
1154 where
1155 I: IntoIterator<Item = P>,
1156 P: Into<AbsolutePath>,
1157 {
1158 self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
1159 self
1160 }
1161
1162 #[must_use]
1164 pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1165 self.mcp_servers = mcp_servers;
1166 self
1167 }
1168
1169 #[must_use]
1175 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1176 self.meta = meta.into_option();
1177 self
1178 }
1179}
1180
1181#[serde_as]
1185#[skip_serializing_none]
1186#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1187#[schemars(extend("x-side" = "agent", "x-method" = SESSION_NEW_METHOD_NAME))]
1188#[serde(rename_all = "camelCase")]
1189#[non_exhaustive]
1190pub struct NewSessionResponse {
1191 pub session_id: SessionId,
1195 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1197 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
1198 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1199 pub config_options: Vec<SessionConfigOption>,
1200 #[serde_as(deserialize_as = "DefaultOnError")]
1206 #[schemars(extend("x-deserialize-default-on-error" = true))]
1207 #[serde(default)]
1208 #[serde(rename = "_meta")]
1209 pub meta: Option<Meta>,
1210}
1211
1212impl NewSessionResponse {
1213 #[must_use]
1215 pub fn new(session_id: impl Into<SessionId>) -> Self {
1216 Self {
1217 session_id: session_id.into(),
1218 config_options: Vec::new(),
1219 meta: None,
1220 }
1221 }
1222
1223 #[must_use]
1225 pub fn config_options(mut self, config_options: Vec<SessionConfigOption>) -> Self {
1226 self.config_options = config_options;
1227 self
1228 }
1229
1230 #[must_use]
1236 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1237 self.meta = meta.into_option();
1238 self
1239 }
1240}
1241
1242#[cfg(feature = "unstable_session_fork")]
1255#[serde_as]
1256#[skip_serializing_none]
1257#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1258#[schemars(extend("x-side" = "agent", "x-method" = SESSION_FORK_METHOD_NAME))]
1259#[serde(rename_all = "camelCase")]
1260#[non_exhaustive]
1261pub struct ForkSessionRequest {
1262 pub session_id: SessionId,
1264 pub cwd: AbsolutePath,
1266 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1272 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
1273 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1274 pub additional_directories: Vec<AbsolutePath>,
1275 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1277 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
1278 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1279 pub mcp_servers: Vec<McpServer>,
1280 #[serde_as(deserialize_as = "DefaultOnError")]
1286 #[schemars(extend("x-deserialize-default-on-error" = true))]
1287 #[serde(default)]
1288 #[serde(rename = "_meta")]
1289 pub meta: Option<Meta>,
1290}
1291
1292#[cfg(feature = "unstable_session_fork")]
1293impl ForkSessionRequest {
1294 #[must_use]
1296 pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<AbsolutePath>) -> Self {
1297 Self {
1298 session_id: session_id.into(),
1299 cwd: cwd.into(),
1300 additional_directories: vec![],
1301 mcp_servers: vec![],
1302 meta: None,
1303 }
1304 }
1305
1306 #[must_use]
1308 pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
1309 where
1310 I: IntoIterator<Item = P>,
1311 P: Into<AbsolutePath>,
1312 {
1313 self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
1314 self
1315 }
1316
1317 #[must_use]
1319 pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1320 self.mcp_servers = mcp_servers;
1321 self
1322 }
1323
1324 #[must_use]
1330 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1331 self.meta = meta.into_option();
1332 self
1333 }
1334}
1335
1336#[cfg(feature = "unstable_session_fork")]
1342#[serde_as]
1343#[skip_serializing_none]
1344#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1345#[schemars(extend("x-side" = "agent", "x-method" = SESSION_FORK_METHOD_NAME))]
1346#[serde(rename_all = "camelCase")]
1347#[non_exhaustive]
1348pub struct ForkSessionResponse {
1349 pub session_id: SessionId,
1351 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1353 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
1354 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1355 pub config_options: Vec<SessionConfigOption>,
1356 #[serde_as(deserialize_as = "DefaultOnError")]
1362 #[schemars(extend("x-deserialize-default-on-error" = true))]
1363 #[serde(default)]
1364 #[serde(rename = "_meta")]
1365 pub meta: Option<Meta>,
1366}
1367
1368#[cfg(feature = "unstable_session_fork")]
1369impl ForkSessionResponse {
1370 #[must_use]
1372 pub fn new(session_id: impl Into<SessionId>) -> Self {
1373 Self {
1374 session_id: session_id.into(),
1375 config_options: Vec::new(),
1376 meta: None,
1377 }
1378 }
1379
1380 #[must_use]
1382 pub fn config_options(mut self, config_options: Vec<SessionConfigOption>) -> Self {
1383 self.config_options = config_options;
1384 self
1385 }
1386
1387 #[must_use]
1393 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1394 self.meta = meta.into_option();
1395 self
1396 }
1397}
1398
1399#[serde_as]
1406#[skip_serializing_none]
1407#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1408#[schemars(extend("x-side" = "agent", "x-method" = SESSION_RESUME_METHOD_NAME))]
1409#[serde(rename_all = "camelCase")]
1410#[non_exhaustive]
1411pub struct ResumeSessionRequest {
1412 pub session_id: SessionId,
1414 pub cwd: AbsolutePath,
1416 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1423 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
1424 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1425 pub additional_directories: Vec<AbsolutePath>,
1426 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1428 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
1429 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1430 pub mcp_servers: Vec<McpServer>,
1431 #[serde_as(deserialize_as = "DefaultOnError")]
1439 #[schemars(extend("x-deserialize-default-on-error" = true))]
1440 #[serde(default)]
1441 pub replay_from: Option<ReplayFrom>,
1442 #[serde_as(deserialize_as = "DefaultOnError")]
1448 #[schemars(extend("x-deserialize-default-on-error" = true))]
1449 #[serde(default)]
1450 #[serde(rename = "_meta")]
1451 pub meta: Option<Meta>,
1452}
1453
1454impl ResumeSessionRequest {
1455 #[must_use]
1457 pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<AbsolutePath>) -> Self {
1458 Self {
1459 session_id: session_id.into(),
1460 cwd: cwd.into(),
1461 additional_directories: vec![],
1462 mcp_servers: vec![],
1463 replay_from: None,
1464 meta: None,
1465 }
1466 }
1467
1468 #[must_use]
1470 pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
1471 where
1472 I: IntoIterator<Item = P>,
1473 P: Into<AbsolutePath>,
1474 {
1475 self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
1476 self
1477 }
1478
1479 #[must_use]
1481 pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1482 self.mcp_servers = mcp_servers;
1483 self
1484 }
1485
1486 #[must_use]
1494 pub fn replay_from(mut self, replay_from: impl IntoOption<ReplayFrom>) -> Self {
1495 self.replay_from = replay_from.into_option();
1496 self
1497 }
1498
1499 #[must_use]
1505 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1506 self.meta = meta.into_option();
1507 self
1508 }
1509}
1510
1511#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1515#[serde(tag = "type", rename_all = "snake_case")]
1516#[non_exhaustive]
1517pub enum ReplayFrom {
1518 Start(ReplayFromStart),
1520 #[serde(untagged)]
1530 Other(OtherReplayFrom),
1531}
1532
1533impl From<ReplayFromStart> for ReplayFrom {
1534 fn from(replay_from: ReplayFromStart) -> Self {
1535 Self::Start(replay_from)
1536 }
1537}
1538
1539#[serde_as]
1541#[skip_serializing_none]
1542#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1543#[serde(rename_all = "camelCase")]
1544#[non_exhaustive]
1545pub struct ReplayFromStart {
1546 #[serde_as(deserialize_as = "DefaultOnError")]
1552 #[schemars(extend("x-deserialize-default-on-error" = true))]
1553 #[serde(default)]
1554 #[serde(rename = "_meta")]
1555 pub meta: Option<Meta>,
1556}
1557
1558impl ReplayFromStart {
1559 #[must_use]
1561 pub fn new() -> Self {
1562 Self::default()
1563 }
1564
1565 #[must_use]
1571 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1572 self.meta = meta.into_option();
1573 self
1574 }
1575}
1576
1577#[serde_as]
1579#[skip_serializing_none]
1580#[derive(Debug, Clone, Serialize, JsonSchema, PartialEq, Eq)]
1581#[schemars(inline)]
1582#[schemars(transform = other_replay_from_schema)]
1583#[serde(rename_all = "camelCase")]
1584#[non_exhaustive]
1585pub struct OtherReplayFrom {
1586 #[serde(rename = "type")]
1592 pub type_: String,
1593 #[serde_as(deserialize_as = "DefaultOnError")]
1599 #[schemars(extend("x-deserialize-default-on-error" = true))]
1600 #[serde(default)]
1601 #[serde(rename = "_meta")]
1602 pub meta: Option<Meta>,
1603 #[serde(flatten)]
1605 pub fields: BTreeMap<String, serde_json::Value>,
1606}
1607
1608impl OtherReplayFrom {
1609 #[must_use]
1611 pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
1612 fields.remove("type");
1613 fields.remove("_meta");
1614 Self {
1615 type_: type_.into(),
1616 meta: None,
1617 fields,
1618 }
1619 }
1620
1621 #[must_use]
1627 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1628 self.meta = meta.into_option();
1629 self
1630 }
1631}
1632
1633impl<'de> Deserialize<'de> for OtherReplayFrom {
1634 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1635 where
1636 D: serde::Deserializer<'de>,
1637 {
1638 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
1639 let type_ = fields
1640 .remove("type")
1641 .ok_or_else(|| serde::de::Error::missing_field("type"))?;
1642 let serde_json::Value::String(type_) = type_ else {
1643 return Err(serde::de::Error::custom("`type` must be a string"));
1644 };
1645
1646 if is_known_replay_from_type(&type_) {
1647 return Err(serde::de::Error::custom(format!(
1648 "known replay cursor `{type_}` did not match its schema"
1649 )));
1650 }
1651
1652 let meta = fields
1653 .remove("_meta")
1654 .and_then(|value| serde_json::from_value(value).ok());
1655
1656 Ok(Self {
1657 type_,
1658 meta,
1659 fields,
1660 })
1661 }
1662}
1663
1664fn is_known_replay_from_type(type_: &str) -> bool {
1665 matches!(type_, "start")
1666}
1667
1668fn other_replay_from_schema(schema: &mut Schema) {
1669 super::schema_util::reject_known_string_discriminators(schema, "type", &["start"]);
1670}
1671
1672#[serde_as]
1674#[skip_serializing_none]
1675#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1676#[schemars(extend("x-side" = "agent", "x-method" = SESSION_RESUME_METHOD_NAME))]
1677#[serde(rename_all = "camelCase")]
1678#[non_exhaustive]
1679pub struct ResumeSessionResponse {
1680 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1682 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
1683 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1684 pub config_options: Vec<SessionConfigOption>,
1685 #[serde_as(deserialize_as = "DefaultOnError")]
1691 #[schemars(extend("x-deserialize-default-on-error" = true))]
1692 #[serde(default)]
1693 #[serde(rename = "_meta")]
1694 pub meta: Option<Meta>,
1695}
1696
1697impl ResumeSessionResponse {
1698 #[must_use]
1700 pub fn new() -> Self {
1701 Self::default()
1702 }
1703
1704 #[must_use]
1706 pub fn config_options(mut self, config_options: Vec<SessionConfigOption>) -> Self {
1707 self.config_options = config_options;
1708 self
1709 }
1710
1711 #[must_use]
1717 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1718 self.meta = meta.into_option();
1719 self
1720 }
1721}
1722
1723#[serde_as]
1731#[skip_serializing_none]
1732#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1733#[schemars(extend("x-side" = "agent", "x-method" = SESSION_CLOSE_METHOD_NAME))]
1734#[serde(rename_all = "camelCase")]
1735#[non_exhaustive]
1736pub struct CloseSessionRequest {
1737 pub session_id: SessionId,
1739 #[serde_as(deserialize_as = "DefaultOnError")]
1745 #[schemars(extend("x-deserialize-default-on-error" = true))]
1746 #[serde(default)]
1747 #[serde(rename = "_meta")]
1748 pub meta: Option<Meta>,
1749}
1750
1751impl CloseSessionRequest {
1752 #[must_use]
1754 pub fn new(session_id: impl Into<SessionId>) -> Self {
1755 Self {
1756 session_id: session_id.into(),
1757 meta: None,
1758 }
1759 }
1760
1761 #[must_use]
1767 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1768 self.meta = meta.into_option();
1769 self
1770 }
1771}
1772
1773#[serde_as]
1775#[skip_serializing_none]
1776#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1777#[schemars(extend("x-side" = "agent", "x-method" = SESSION_CLOSE_METHOD_NAME))]
1778#[serde(rename_all = "camelCase")]
1779#[non_exhaustive]
1780pub struct CloseSessionResponse {
1781 #[serde_as(deserialize_as = "DefaultOnError")]
1787 #[schemars(extend("x-deserialize-default-on-error" = true))]
1788 #[serde(default)]
1789 #[serde(rename = "_meta")]
1790 pub meta: Option<Meta>,
1791}
1792
1793impl CloseSessionResponse {
1794 #[must_use]
1796 pub fn new() -> Self {
1797 Self::default()
1798 }
1799
1800 #[must_use]
1806 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1807 self.meta = meta.into_option();
1808 self
1809 }
1810}
1811
1812#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, Display, From)]
1816#[serde(transparent)]
1817#[from(Arc<str>, String, &str, &mut str, Box<str>, Cow<'_, str>)]
1818#[non_exhaustive]
1819pub struct SessionListCursor(pub Arc<str>);
1820
1821impl SessionListCursor {
1822 #[must_use]
1824 pub fn new(cursor: impl Into<Self>) -> Self {
1825 cursor.into()
1826 }
1827}
1828
1829impl AsRef<str> for SessionListCursor {
1830 fn as_ref(&self) -> &str {
1831 &self.0
1832 }
1833}
1834
1835impl From<&String> for SessionListCursor {
1836 fn from(cursor: &String) -> Self {
1837 Self(cursor.as_str().into())
1838 }
1839}
1840
1841macro_rules! impl_session_list_cursor_option_conversion {
1842 ($source:ty) => {
1843 impl IntoOption<SessionListCursor> for $source {
1844 fn into_option(self) -> Option<SessionListCursor> {
1845 Some(self.into())
1846 }
1847 }
1848 };
1849}
1850
1851impl_session_list_cursor_option_conversion!(Arc<str>);
1852impl_session_list_cursor_option_conversion!(String);
1853impl_session_list_cursor_option_conversion!(&str);
1854impl_session_list_cursor_option_conversion!(&mut str);
1855impl_session_list_cursor_option_conversion!(&String);
1856impl_session_list_cursor_option_conversion!(Box<str>);
1857impl_session_list_cursor_option_conversion!(Cow<'_, str>);
1858
1859#[serde_as]
1861#[skip_serializing_none]
1862#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1863#[schemars(extend("x-side" = "agent", "x-method" = SESSION_LIST_METHOD_NAME))]
1864#[serde(rename_all = "camelCase")]
1865#[non_exhaustive]
1866pub struct ListSessionsRequest {
1867 #[serde(default)]
1869 pub cwd: Option<AbsolutePath>,
1870 #[serde(default)]
1872 pub cursor: Option<SessionListCursor>,
1873 #[serde_as(deserialize_as = "DefaultOnError")]
1879 #[schemars(extend("x-deserialize-default-on-error" = true))]
1880 #[serde(default)]
1881 #[serde(rename = "_meta")]
1882 pub meta: Option<Meta>,
1883}
1884
1885impl ListSessionsRequest {
1886 #[must_use]
1888 pub fn new() -> Self {
1889 Self::default()
1890 }
1891
1892 #[must_use]
1894 pub fn cwd(mut self, cwd: impl IntoOption<AbsolutePath>) -> Self {
1895 self.cwd = cwd.into_option();
1896 self
1897 }
1898
1899 #[must_use]
1901 pub fn cursor(mut self, cursor: impl IntoOption<SessionListCursor>) -> Self {
1902 self.cursor = cursor.into_option();
1903 self
1904 }
1905
1906 #[must_use]
1912 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1913 self.meta = meta.into_option();
1914 self
1915 }
1916}
1917
1918#[serde_as]
1920#[skip_serializing_none]
1921#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1922#[schemars(extend("x-side" = "agent", "x-method" = SESSION_LIST_METHOD_NAME))]
1923#[serde(rename_all = "camelCase")]
1924#[non_exhaustive]
1925pub struct ListSessionsResponse {
1926 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1928 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
1929 pub sessions: Vec<SessionInfo>,
1930 #[serde_as(deserialize_as = "DefaultOnError")]
1933 #[schemars(extend("x-deserialize-default-on-error" = true))]
1934 #[serde(default)]
1935 pub next_cursor: Option<SessionListCursor>,
1936 #[serde_as(deserialize_as = "DefaultOnError")]
1942 #[schemars(extend("x-deserialize-default-on-error" = true))]
1943 #[serde(default)]
1944 #[serde(rename = "_meta")]
1945 pub meta: Option<Meta>,
1946}
1947
1948impl ListSessionsResponse {
1949 #[must_use]
1951 pub fn new(sessions: Vec<SessionInfo>) -> Self {
1952 Self {
1953 sessions,
1954 next_cursor: None,
1955 meta: None,
1956 }
1957 }
1958
1959 #[must_use]
1961 pub fn next_cursor(mut self, next_cursor: impl IntoOption<SessionListCursor>) -> Self {
1962 self.next_cursor = next_cursor.into_option();
1963 self
1964 }
1965
1966 #[must_use]
1972 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1973 self.meta = meta.into_option();
1974 self
1975 }
1976}
1977
1978#[serde_as]
1984#[skip_serializing_none]
1985#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1986#[schemars(extend("x-side" = "agent", "x-method" = SESSION_DELETE_METHOD_NAME))]
1987#[serde(rename_all = "camelCase")]
1988#[non_exhaustive]
1989pub struct DeleteSessionRequest {
1990 pub session_id: SessionId,
1992 #[serde_as(deserialize_as = "DefaultOnError")]
1998 #[schemars(extend("x-deserialize-default-on-error" = true))]
1999 #[serde(default)]
2000 #[serde(rename = "_meta")]
2001 pub meta: Option<Meta>,
2002}
2003
2004impl DeleteSessionRequest {
2005 #[must_use]
2007 pub fn new(session_id: impl Into<SessionId>) -> Self {
2008 Self {
2009 session_id: session_id.into(),
2010 meta: None,
2011 }
2012 }
2013
2014 #[must_use]
2020 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2021 self.meta = meta.into_option();
2022 self
2023 }
2024}
2025
2026#[serde_as]
2028#[skip_serializing_none]
2029#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2030#[schemars(extend("x-side" = "agent", "x-method" = SESSION_DELETE_METHOD_NAME))]
2031#[serde(rename_all = "camelCase")]
2032#[non_exhaustive]
2033pub struct DeleteSessionResponse {
2034 #[serde_as(deserialize_as = "DefaultOnError")]
2040 #[schemars(extend("x-deserialize-default-on-error" = true))]
2041 #[serde(default)]
2042 #[serde(rename = "_meta")]
2043 pub meta: Option<Meta>,
2044}
2045
2046impl DeleteSessionResponse {
2047 #[must_use]
2049 pub fn new() -> Self {
2050 Self::default()
2051 }
2052
2053 #[must_use]
2059 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2060 self.meta = meta.into_option();
2061 self
2062 }
2063}
2064
2065#[serde_as]
2067#[skip_serializing_none]
2068#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2069#[serde(rename_all = "camelCase")]
2070#[non_exhaustive]
2071pub struct SessionInfo {
2072 pub session_id: SessionId,
2074 pub cwd: AbsolutePath,
2076 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
2082 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
2083 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2084 pub additional_directories: Vec<AbsolutePath>,
2085
2086 #[serde_as(deserialize_as = "DefaultOnError")]
2088 #[schemars(extend("x-deserialize-default-on-error" = true))]
2089 #[serde(default)]
2090 pub title: Option<String>,
2091 #[serde_as(deserialize_as = "DefaultOnError")]
2093 #[schemars(extend("x-deserialize-default-on-error" = true, "format" = "date-time"))]
2094 #[serde(default)]
2095 pub updated_at: Option<String>,
2096 #[serde_as(deserialize_as = "DefaultOnError")]
2102 #[schemars(extend("x-deserialize-default-on-error" = true))]
2103 #[serde(default)]
2104 #[serde(rename = "_meta")]
2105 pub meta: Option<Meta>,
2106}
2107
2108impl SessionInfo {
2109 #[must_use]
2111 pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<AbsolutePath>) -> Self {
2112 Self {
2113 session_id: session_id.into(),
2114 cwd: cwd.into(),
2115 additional_directories: vec![],
2116 title: None,
2117 updated_at: None,
2118 meta: None,
2119 }
2120 }
2121
2122 #[must_use]
2124 pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
2125 where
2126 I: IntoIterator<Item = P>,
2127 P: Into<AbsolutePath>,
2128 {
2129 self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
2130 self
2131 }
2132
2133 #[must_use]
2135 pub fn title(mut self, title: impl IntoOption<String>) -> Self {
2136 self.title = title.into_option();
2137 self
2138 }
2139
2140 #[must_use]
2142 pub fn updated_at(mut self, updated_at: impl IntoOption<String>) -> Self {
2143 self.updated_at = updated_at.into_option();
2144 self
2145 }
2146
2147 #[must_use]
2153 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2154 self.meta = meta.into_option();
2155 self
2156 }
2157}
2158
2159#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, From, Display)]
2163#[serde(transparent)]
2164#[from(forward)]
2165#[non_exhaustive]
2166pub struct SessionConfigId(pub Arc<str>);
2167
2168impl SessionConfigId {
2169 #[must_use]
2171 pub fn new(id: impl Into<Self>) -> Self {
2172 id.into()
2173 }
2174}
2175
2176#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, From, Display)]
2178#[serde(transparent)]
2179#[from(forward)]
2180#[non_exhaustive]
2181pub struct SessionConfigValueId(pub Arc<str>);
2182
2183impl SessionConfigValueId {
2184 #[must_use]
2186 pub fn new(id: impl Into<Self>) -> Self {
2187 id.into()
2188 }
2189}
2190
2191#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, From, Display)]
2193#[serde(transparent)]
2194#[from(forward)]
2195#[non_exhaustive]
2196pub struct SessionConfigGroupId(pub Arc<str>);
2197
2198impl SessionConfigGroupId {
2199 #[must_use]
2201 pub fn new(id: impl Into<Self>) -> Self {
2202 id.into()
2203 }
2204}
2205
2206#[serde_as]
2208#[skip_serializing_none]
2209#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2210#[serde(rename_all = "camelCase")]
2211#[non_exhaustive]
2212pub struct SessionConfigSelectOption {
2213 pub value: SessionConfigValueId,
2215 pub name: String,
2217 #[serde_as(deserialize_as = "DefaultOnError")]
2219 #[schemars(extend("x-deserialize-default-on-error" = true))]
2220 #[serde(default)]
2221 pub description: Option<String>,
2222 #[serde_as(deserialize_as = "DefaultOnError")]
2228 #[schemars(extend("x-deserialize-default-on-error" = true))]
2229 #[serde(default)]
2230 #[serde(rename = "_meta")]
2231 pub meta: Option<Meta>,
2232}
2233
2234impl SessionConfigSelectOption {
2235 #[must_use]
2237 pub fn new(value: impl Into<SessionConfigValueId>, name: impl Into<String>) -> Self {
2238 Self {
2239 value: value.into(),
2240 name: name.into(),
2241 description: None,
2242 meta: None,
2243 }
2244 }
2245
2246 #[must_use]
2248 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
2249 self.description = description.into_option();
2250 self
2251 }
2252
2253 #[must_use]
2259 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2260 self.meta = meta.into_option();
2261 self
2262 }
2263}
2264
2265#[serde_as]
2267#[skip_serializing_none]
2268#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2269#[serde(rename_all = "camelCase")]
2270#[non_exhaustive]
2271pub struct SessionConfigSelectGroup {
2272 pub group_id: SessionConfigGroupId,
2274 pub name: String,
2276 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
2278 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
2279 pub options: Vec<SessionConfigSelectOption>,
2280 #[serde_as(deserialize_as = "DefaultOnError")]
2286 #[schemars(extend("x-deserialize-default-on-error" = true))]
2287 #[serde(default)]
2288 #[serde(rename = "_meta")]
2289 pub meta: Option<Meta>,
2290}
2291
2292impl SessionConfigSelectGroup {
2293 #[must_use]
2295 pub fn new(
2296 group_id: impl Into<SessionConfigGroupId>,
2297 name: impl Into<String>,
2298 options: Vec<SessionConfigSelectOption>,
2299 ) -> Self {
2300 Self {
2301 group_id: group_id.into(),
2302 name: name.into(),
2303 options,
2304 meta: None,
2305 }
2306 }
2307
2308 #[must_use]
2314 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2315 self.meta = meta.into_option();
2316 self
2317 }
2318}
2319
2320#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2322#[serde(untagged)]
2323#[non_exhaustive]
2324pub enum SessionConfigSelectOptions {
2325 Ungrouped(Vec<SessionConfigSelectOption>),
2327 Grouped(Vec<SessionConfigSelectGroup>),
2329}
2330
2331impl From<Vec<SessionConfigSelectOption>> for SessionConfigSelectOptions {
2332 fn from(options: Vec<SessionConfigSelectOption>) -> Self {
2333 SessionConfigSelectOptions::Ungrouped(options)
2334 }
2335}
2336
2337impl From<Vec<SessionConfigSelectGroup>> for SessionConfigSelectOptions {
2338 fn from(groups: Vec<SessionConfigSelectGroup>) -> Self {
2339 SessionConfigSelectOptions::Grouped(groups)
2340 }
2341}
2342
2343#[skip_serializing_none]
2345#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2346#[serde(rename_all = "camelCase")]
2347#[non_exhaustive]
2348pub struct SessionConfigSelect {
2349 pub current_value: SessionConfigValueId,
2351 pub options: SessionConfigSelectOptions,
2353}
2354
2355impl SessionConfigSelect {
2356 #[must_use]
2358 pub fn new(
2359 current_value: impl Into<SessionConfigValueId>,
2360 options: impl Into<SessionConfigSelectOptions>,
2361 ) -> Self {
2362 Self {
2363 current_value: current_value.into(),
2364 options: options.into(),
2365 }
2366 }
2367}
2368
2369#[skip_serializing_none]
2371#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2372#[serde(rename_all = "camelCase")]
2373#[non_exhaustive]
2374pub struct SessionConfigBoolean {
2375 pub current_value: bool,
2377}
2378
2379impl SessionConfigBoolean {
2380 #[must_use]
2382 pub fn new(current_value: bool) -> Self {
2383 Self { current_value }
2384 }
2385}
2386
2387#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2397#[serde(rename_all = "snake_case")]
2398#[non_exhaustive]
2399pub enum SessionConfigOptionCategory {
2400 Mode,
2402 Model,
2404 ModelConfig,
2406 ThoughtLevel,
2408 #[serde(untagged)]
2414 Other(String),
2415}
2416
2417#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2419#[serde(tag = "type", rename_all = "snake_case")]
2420#[non_exhaustive]
2421pub enum SessionConfigKind {
2422 Select(SessionConfigSelect),
2424 Boolean(SessionConfigBoolean),
2426 #[serde(untagged)]
2436 Other(OtherSessionConfigKind),
2437}
2438
2439#[derive(Debug, Clone, Serialize, JsonSchema, PartialEq, Eq)]
2441#[schemars(inline)]
2442#[schemars(transform = other_session_config_kind_schema)]
2443#[serde(rename_all = "camelCase")]
2444#[non_exhaustive]
2445pub struct OtherSessionConfigKind {
2446 #[serde(rename = "type")]
2452 pub type_: String,
2453 #[serde(flatten)]
2455 pub fields: BTreeMap<String, serde_json::Value>,
2456}
2457
2458impl OtherSessionConfigKind {
2459 #[must_use]
2461 pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
2462 fields.remove("type");
2463 fields.remove("_meta");
2464 Self {
2465 type_: type_.into(),
2466 fields,
2467 }
2468 }
2469}
2470
2471impl<'de> Deserialize<'de> for OtherSessionConfigKind {
2472 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2473 where
2474 D: serde::Deserializer<'de>,
2475 {
2476 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
2477 let type_ = fields
2478 .remove("type")
2479 .ok_or_else(|| serde::de::Error::missing_field("type"))?;
2480 let serde_json::Value::String(type_) = type_ else {
2481 return Err(serde::de::Error::custom("`type` must be a string"));
2482 };
2483
2484 if is_known_session_config_kind_type(&type_) {
2485 return Err(serde::de::Error::custom(format!(
2486 "known session configuration option `{type_}` did not match its schema"
2487 )));
2488 }
2489
2490 Ok(Self { type_, fields })
2491 }
2492}
2493
2494fn is_known_session_config_kind_type(type_: &str) -> bool {
2495 matches!(type_, "select" | "boolean")
2496}
2497
2498fn other_session_config_kind_schema(schema: &mut Schema) {
2499 super::schema_util::reject_known_string_discriminators(schema, "type", &["select", "boolean"]);
2500}
2501
2502#[serde_as]
2504#[skip_serializing_none]
2505#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2506#[serde(rename_all = "camelCase")]
2507#[non_exhaustive]
2508pub struct SessionConfigOption {
2509 pub config_id: SessionConfigId,
2511 pub name: String,
2513 #[serde_as(deserialize_as = "DefaultOnError")]
2515 #[schemars(extend("x-deserialize-default-on-error" = true))]
2516 #[serde(default)]
2517 pub description: Option<String>,
2518 #[serde_as(deserialize_as = "DefaultOnError")]
2520 #[schemars(extend("x-deserialize-default-on-error" = true))]
2521 #[serde(default)]
2522 pub category: Option<SessionConfigOptionCategory>,
2523 #[serde(flatten)]
2525 pub kind: SessionConfigKind,
2526 #[serde_as(deserialize_as = "DefaultOnError")]
2532 #[schemars(extend("x-deserialize-default-on-error" = true))]
2533 #[serde(default)]
2534 #[serde(rename = "_meta")]
2535 pub meta: Option<Meta>,
2536}
2537
2538impl SessionConfigOption {
2539 #[must_use]
2541 pub fn new(
2542 config_id: impl Into<SessionConfigId>,
2543 name: impl Into<String>,
2544 kind: SessionConfigKind,
2545 ) -> Self {
2546 Self {
2547 config_id: config_id.into(),
2548 name: name.into(),
2549 description: None,
2550 category: None,
2551 kind,
2552 meta: None,
2553 }
2554 }
2555
2556 #[must_use]
2558 pub fn select(
2559 config_id: impl Into<SessionConfigId>,
2560 name: impl Into<String>,
2561 current_value: impl Into<SessionConfigValueId>,
2562 options: impl Into<SessionConfigSelectOptions>,
2563 ) -> Self {
2564 Self::new(
2565 config_id,
2566 name,
2567 SessionConfigKind::Select(SessionConfigSelect::new(current_value, options)),
2568 )
2569 }
2570
2571 #[must_use]
2573 pub fn boolean(
2574 config_id: impl Into<SessionConfigId>,
2575 name: impl Into<String>,
2576 current_value: bool,
2577 ) -> Self {
2578 Self::new(
2579 config_id,
2580 name,
2581 SessionConfigKind::Boolean(SessionConfigBoolean::new(current_value)),
2582 )
2583 }
2584
2585 #[must_use]
2587 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
2588 self.description = description.into_option();
2589 self
2590 }
2591
2592 #[must_use]
2594 pub fn category(mut self, category: impl IntoOption<SessionConfigOptionCategory>) -> Self {
2595 self.category = category.into_option();
2596 self
2597 }
2598
2599 #[must_use]
2605 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2606 self.meta = meta.into_option();
2607 self
2608 }
2609}
2610
2611#[derive(Debug, Clone, Serialize, JsonSchema, PartialEq, Eq)]
2620#[serde(tag = "type", rename_all = "snake_case")]
2621#[non_exhaustive]
2622pub enum SessionConfigOptionValue {
2623 Id {
2625 value: SessionConfigValueId,
2627 },
2628 Boolean {
2630 value: bool,
2632 },
2633 #[serde(untagged)]
2639 Other(OtherSessionConfigOptionValue),
2640}
2641
2642#[derive(Debug, Clone, Serialize, JsonSchema, PartialEq, Eq)]
2644#[schemars(inline)]
2645#[schemars(transform = other_session_config_option_value_schema)]
2646#[serde(rename_all = "camelCase")]
2647#[non_exhaustive]
2648pub struct OtherSessionConfigOptionValue {
2649 #[serde(rename = "type")]
2655 pub type_: String,
2656 pub value: serde_json::Value,
2658 #[serde(flatten)]
2660 pub fields: BTreeMap<String, serde_json::Value>,
2661}
2662
2663impl OtherSessionConfigOptionValue {
2664 #[must_use]
2666 pub fn new(
2667 type_: impl Into<String>,
2668 value: serde_json::Value,
2669 mut fields: BTreeMap<String, serde_json::Value>,
2670 ) -> Self {
2671 fields.remove("type");
2672 fields.remove("value");
2673 fields.remove("_meta");
2674 Self {
2675 type_: type_.into(),
2676 value,
2677 fields,
2678 }
2679 }
2680}
2681
2682impl<'de> Deserialize<'de> for OtherSessionConfigOptionValue {
2683 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2684 where
2685 D: serde::Deserializer<'de>,
2686 {
2687 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
2688 let type_ = fields
2689 .remove("type")
2690 .ok_or_else(|| serde::de::Error::missing_field("type"))?;
2691 let serde_json::Value::String(type_) = type_ else {
2692 return Err(serde::de::Error::custom("`type` must be a string"));
2693 };
2694
2695 if is_known_session_config_option_value_type(&type_) {
2696 return Err(serde::de::Error::custom(format!(
2697 "known session configuration option value `{type_}` did not match its schema"
2698 )));
2699 }
2700
2701 let value = fields
2702 .remove("value")
2703 .ok_or_else(|| serde::de::Error::missing_field("value"))?;
2704
2705 Ok(Self {
2706 type_,
2707 value,
2708 fields,
2709 })
2710 }
2711}
2712
2713impl<'de> Deserialize<'de> for SessionConfigOptionValue {
2714 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2715 where
2716 D: serde::Deserializer<'de>,
2717 {
2718 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
2719 let type_ = fields.remove("type");
2720 let value = fields
2721 .remove("value")
2722 .ok_or_else(|| serde::de::Error::missing_field("value"))?;
2723
2724 let type_ = type_.ok_or_else(|| serde::de::Error::missing_field("type"))?;
2725
2726 let serde_json::Value::String(type_) = type_ else {
2727 return Err(serde::de::Error::custom("`type` must be a string"));
2728 };
2729
2730 match type_.as_str() {
2731 "id" => {
2732 let value = serde_json::from_value(value).map_err(|error| {
2733 serde::de::Error::custom(format!(
2734 "`value` must be a string for `type: id`: {error}"
2735 ))
2736 })?;
2737 Ok(Self::Id { value })
2738 }
2739 "boolean" => {
2740 let value = serde_json::from_value(value).map_err(|error| {
2741 serde::de::Error::custom(format!(
2742 "`value` must be a boolean for `type: boolean`: {error}"
2743 ))
2744 })?;
2745 Ok(Self::Boolean { value })
2746 }
2747 _ => Ok(Self::Other(OtherSessionConfigOptionValue {
2748 type_,
2749 value,
2750 fields,
2751 })),
2752 }
2753 }
2754}
2755
2756fn is_known_session_config_option_value_type(type_: &str) -> bool {
2757 matches!(type_, "id" | "boolean")
2758}
2759
2760fn other_session_config_option_value_schema(schema: &mut Schema) {
2761 super::schema_util::reject_known_string_discriminators(schema, "type", &["id", "boolean"]);
2762}
2763
2764impl SessionConfigOptionValue {
2765 #[must_use]
2767 pub fn id(id: impl Into<SessionConfigValueId>) -> Self {
2768 Self::Id { value: id.into() }
2769 }
2770
2771 #[must_use]
2773 pub fn boolean(val: bool) -> Self {
2774 Self::Boolean { value: val }
2775 }
2776
2777 #[must_use]
2780 pub fn as_id(&self) -> Option<&SessionConfigValueId> {
2781 match self {
2782 Self::Id { value } => Some(value),
2783 _ => None,
2784 }
2785 }
2786
2787 #[must_use]
2789 pub fn as_bool(&self) -> Option<bool> {
2790 match self {
2791 Self::Boolean { value } => Some(*value),
2792 _ => None,
2793 }
2794 }
2795}
2796
2797impl From<SessionConfigValueId> for SessionConfigOptionValue {
2798 fn from(value: SessionConfigValueId) -> Self {
2799 Self::Id { value }
2800 }
2801}
2802
2803impl From<bool> for SessionConfigOptionValue {
2804 fn from(value: bool) -> Self {
2805 Self::Boolean { value }
2806 }
2807}
2808
2809impl From<&str> for SessionConfigOptionValue {
2810 fn from(value: &str) -> Self {
2811 Self::Id {
2812 value: SessionConfigValueId::new(value),
2813 }
2814 }
2815}
2816
2817#[serde_as]
2819#[skip_serializing_none]
2820#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2821#[schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_CONFIG_OPTION_METHOD_NAME))]
2822#[serde(rename_all = "camelCase")]
2823#[non_exhaustive]
2824pub struct SetSessionConfigOptionRequest {
2825 pub session_id: SessionId,
2827 pub config_id: SessionConfigId,
2829 #[serde(flatten)]
2833 pub value: SessionConfigOptionValue,
2834 #[serde_as(deserialize_as = "DefaultOnError")]
2840 #[schemars(extend("x-deserialize-default-on-error" = true))]
2841 #[serde(default)]
2842 #[serde(rename = "_meta")]
2843 pub meta: Option<Meta>,
2844}
2845
2846impl SetSessionConfigOptionRequest {
2847 #[must_use]
2849 pub fn new(
2850 session_id: impl Into<SessionId>,
2851 config_id: impl Into<SessionConfigId>,
2852 value: impl Into<SessionConfigOptionValue>,
2853 ) -> Self {
2854 Self {
2855 session_id: session_id.into(),
2856 config_id: config_id.into(),
2857 value: value.into(),
2858 meta: None,
2859 }
2860 }
2861
2862 #[must_use]
2868 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2869 self.meta = meta.into_option();
2870 self
2871 }
2872}
2873
2874#[serde_as]
2876#[skip_serializing_none]
2877#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2878#[schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_CONFIG_OPTION_METHOD_NAME))]
2879#[serde(rename_all = "camelCase")]
2880#[non_exhaustive]
2881pub struct SetSessionConfigOptionResponse {
2882 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
2884 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
2885 pub config_options: Vec<SessionConfigOption>,
2886 #[serde_as(deserialize_as = "DefaultOnError")]
2892 #[schemars(extend("x-deserialize-default-on-error" = true))]
2893 #[serde(default)]
2894 #[serde(rename = "_meta")]
2895 pub meta: Option<Meta>,
2896}
2897
2898impl SetSessionConfigOptionResponse {
2899 #[must_use]
2901 pub fn new(config_options: Vec<SessionConfigOption>) -> Self {
2902 Self {
2903 config_options,
2904 meta: None,
2905 }
2906 }
2907
2908 #[must_use]
2914 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2915 self.meta = meta.into_option();
2916 self
2917 }
2918}
2919
2920#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2929#[serde(tag = "type", rename_all = "snake_case")]
2930#[non_exhaustive]
2931pub enum McpServer {
2932 Http(McpServerHttp),
2936 #[cfg(feature = "unstable_mcp_over_acp")]
2945 Acp(McpServerAcp),
2946 Stdio(McpServerStdio),
2950 #[serde(untagged)]
2960 Other(OtherMcpServer),
2961}
2962
2963#[derive(Debug, Clone, Serialize, JsonSchema, PartialEq, Eq)]
2965#[schemars(inline)]
2966#[schemars(transform = other_mcp_server_schema)]
2967#[serde(rename_all = "camelCase")]
2968#[non_exhaustive]
2969pub struct OtherMcpServer {
2970 #[serde(rename = "type")]
2976 pub type_: String,
2977 #[serde(flatten)]
2979 pub fields: BTreeMap<String, serde_json::Value>,
2980}
2981
2982impl OtherMcpServer {
2983 #[must_use]
2985 pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
2986 fields.remove("type");
2987 Self {
2988 type_: type_.into(),
2989 fields,
2990 }
2991 }
2992}
2993
2994impl<'de> Deserialize<'de> for OtherMcpServer {
2995 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2996 where
2997 D: serde::Deserializer<'de>,
2998 {
2999 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
3000 let type_ = fields
3001 .remove("type")
3002 .ok_or_else(|| serde::de::Error::missing_field("type"))?;
3003 let serde_json::Value::String(type_) = type_ else {
3004 return Err(serde::de::Error::custom("`type` must be a string"));
3005 };
3006
3007 if is_known_mcp_server_type(&type_) {
3008 return Err(serde::de::Error::custom(format!(
3009 "known MCP server transport `{type_}` did not match its schema"
3010 )));
3011 }
3012
3013 Ok(Self { type_, fields })
3014 }
3015}
3016
3017fn is_known_mcp_server_type(type_: &str) -> bool {
3018 match type_ {
3019 "http" | "stdio" => true,
3020 #[cfg(feature = "unstable_mcp_over_acp")]
3021 "acp" => true,
3022 _ => false,
3023 }
3024}
3025
3026fn other_mcp_server_schema(schema: &mut Schema) {
3027 super::schema_util::reject_known_string_discriminators(
3028 schema,
3029 "type",
3030 &[
3031 "http",
3032 "stdio",
3033 #[cfg(feature = "unstable_mcp_over_acp")]
3034 "acp",
3035 ],
3036 );
3037}
3038
3039#[serde_as]
3041#[skip_serializing_none]
3042#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3043#[serde(rename_all = "camelCase")]
3044#[non_exhaustive]
3045pub struct McpServerHttp {
3046 pub name: String,
3048 #[schemars(url)]
3050 pub url: String,
3051 #[serde(default, skip_serializing_if = "Vec::is_empty")]
3053 pub headers: Vec<HttpHeader>,
3054 #[serde_as(deserialize_as = "DefaultOnError")]
3060 #[schemars(extend("x-deserialize-default-on-error" = true))]
3061 #[serde(default)]
3062 #[serde(rename = "_meta")]
3063 pub meta: Option<Meta>,
3064}
3065
3066impl McpServerHttp {
3067 #[must_use]
3069 pub fn new(name: impl Into<String>, url: impl Into<String>) -> Self {
3070 Self {
3071 name: name.into(),
3072 url: url.into(),
3073 headers: Vec::new(),
3074 meta: None,
3075 }
3076 }
3077
3078 #[must_use]
3080 pub fn headers(mut self, headers: Vec<HttpHeader>) -> Self {
3081 self.headers = headers;
3082 self
3083 }
3084
3085 #[must_use]
3091 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3092 self.meta = meta.into_option();
3093 self
3094 }
3095}
3096
3097#[cfg(feature = "unstable_mcp_over_acp")]
3107#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, Display, From)]
3108#[serde(transparent)]
3109#[from(forward)]
3110#[non_exhaustive]
3111pub struct McpServerAcpId(pub Arc<str>);
3112
3113#[cfg(feature = "unstable_mcp_over_acp")]
3114impl McpServerAcpId {
3115 #[must_use]
3117 pub fn new(id: impl Into<Self>) -> Self {
3118 id.into()
3119 }
3120}
3121
3122#[serde_as]
3131#[skip_serializing_none]
3132#[cfg(feature = "unstable_mcp_over_acp")]
3133#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3134#[serde(rename_all = "camelCase")]
3135#[non_exhaustive]
3136pub struct McpServerAcp {
3137 pub name: String,
3139 pub server_id: McpServerAcpId,
3144 #[serde_as(deserialize_as = "DefaultOnError")]
3150 #[schemars(extend("x-deserialize-default-on-error" = true))]
3151 #[serde(default)]
3152 #[serde(rename = "_meta")]
3153 pub meta: Option<Meta>,
3154}
3155
3156#[cfg(feature = "unstable_mcp_over_acp")]
3157impl McpServerAcp {
3158 #[must_use]
3160 pub fn new(name: impl Into<String>, server_id: impl Into<McpServerAcpId>) -> Self {
3161 Self {
3162 name: name.into(),
3163 server_id: server_id.into(),
3164 meta: None,
3165 }
3166 }
3167
3168 #[must_use]
3174 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3175 self.meta = meta.into_option();
3176 self
3177 }
3178}
3179
3180#[serde_as]
3182#[skip_serializing_none]
3183#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3184#[serde(rename_all = "camelCase")]
3185#[non_exhaustive]
3186pub struct McpServerStdio {
3187 pub name: String,
3189 pub command: AbsolutePath,
3191 #[serde(default, skip_serializing_if = "Vec::is_empty")]
3193 pub args: Vec<String>,
3194 #[serde(default, skip_serializing_if = "Vec::is_empty")]
3196 pub env: Vec<EnvVariable>,
3197 #[serde_as(deserialize_as = "DefaultOnError")]
3203 #[schemars(extend("x-deserialize-default-on-error" = true))]
3204 #[serde(default)]
3205 #[serde(rename = "_meta")]
3206 pub meta: Option<Meta>,
3207}
3208
3209impl McpServerStdio {
3210 #[must_use]
3212 pub fn new(name: impl Into<String>, command: impl Into<AbsolutePath>) -> Self {
3213 Self {
3214 name: name.into(),
3215 command: command.into(),
3216 args: Vec::new(),
3217 env: Vec::new(),
3218 meta: None,
3219 }
3220 }
3221
3222 #[must_use]
3224 pub fn args(mut self, args: Vec<String>) -> Self {
3225 self.args = args;
3226 self
3227 }
3228
3229 #[must_use]
3231 pub fn env(mut self, env: Vec<EnvVariable>) -> Self {
3232 self.env = env;
3233 self
3234 }
3235
3236 #[must_use]
3242 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3243 self.meta = meta.into_option();
3244 self
3245 }
3246}
3247
3248#[serde_as]
3250#[skip_serializing_none]
3251#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3252#[serde(rename_all = "camelCase")]
3253#[non_exhaustive]
3254pub struct EnvVariable {
3255 pub name: String,
3257 pub value: String,
3259 #[serde_as(deserialize_as = "DefaultOnError")]
3265 #[schemars(extend("x-deserialize-default-on-error" = true))]
3266 #[serde(default)]
3267 #[serde(rename = "_meta")]
3268 pub meta: Option<Meta>,
3269}
3270
3271impl EnvVariable {
3272 #[must_use]
3274 pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
3275 Self {
3276 name: name.into(),
3277 value: value.into(),
3278 meta: None,
3279 }
3280 }
3281
3282 #[must_use]
3288 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3289 self.meta = meta.into_option();
3290 self
3291 }
3292}
3293
3294#[serde_as]
3296#[skip_serializing_none]
3297#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3298#[serde(rename_all = "camelCase")]
3299#[non_exhaustive]
3300pub struct HttpHeader {
3301 pub name: String,
3303 pub value: String,
3305 #[serde_as(deserialize_as = "DefaultOnError")]
3311 #[schemars(extend("x-deserialize-default-on-error" = true))]
3312 #[serde(default)]
3313 #[serde(rename = "_meta")]
3314 pub meta: Option<Meta>,
3315}
3316
3317impl HttpHeader {
3318 #[must_use]
3320 pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
3321 Self {
3322 name: name.into(),
3323 value: value.into(),
3324 meta: None,
3325 }
3326 }
3327
3328 #[must_use]
3334 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3335 self.meta = meta.into_option();
3336 self
3337 }
3338}
3339
3340#[serde_as]
3348#[skip_serializing_none]
3349#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
3350#[schemars(extend("x-side" = "agent", "x-method" = SESSION_PROMPT_METHOD_NAME))]
3351#[serde(rename_all = "camelCase")]
3352#[non_exhaustive]
3353pub struct PromptRequest {
3354 pub session_id: SessionId,
3356 pub prompt: Vec<ContentBlock>,
3370 #[serde_as(deserialize_as = "DefaultOnError")]
3376 #[schemars(extend("x-deserialize-default-on-error" = true))]
3377 #[serde(default)]
3378 #[serde(rename = "_meta")]
3379 pub meta: Option<Meta>,
3380}
3381
3382impl PromptRequest {
3383 #[must_use]
3385 pub fn new(session_id: impl Into<SessionId>, prompt: Vec<ContentBlock>) -> Self {
3386 Self {
3387 session_id: session_id.into(),
3388 prompt,
3389 meta: None,
3390 }
3391 }
3392
3393 #[must_use]
3399 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3400 self.meta = meta.into_option();
3401 self
3402 }
3403}
3404
3405#[serde_as]
3412#[skip_serializing_none]
3413#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3414#[schemars(extend("x-side" = "agent", "x-method" = SESSION_PROMPT_METHOD_NAME))]
3415#[serde(rename_all = "camelCase")]
3416#[non_exhaustive]
3417pub struct PromptResponse {
3418 #[serde_as(deserialize_as = "DefaultOnError")]
3424 #[schemars(extend("x-deserialize-default-on-error" = true))]
3425 #[serde(default)]
3426 #[serde(rename = "_meta")]
3427 pub meta: Option<Meta>,
3428}
3429
3430impl PromptResponse {
3431 #[must_use]
3433 pub fn new() -> Self {
3434 Self::default()
3435 }
3436
3437 #[must_use]
3443 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3444 self.meta = meta.into_option();
3445 self
3446 }
3447}
3448
3449#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
3453#[serde(rename_all = "snake_case")]
3454#[non_exhaustive]
3455pub enum StopReason {
3456 EndTurn,
3458 MaxTokens,
3460 MaxTurnRequests,
3463 Refusal,
3467 Cancelled,
3473 #[serde(untagged)]
3479 Other(String),
3480}
3481
3482#[cfg(feature = "unstable_end_turn_token_usage")]
3488#[serde_as]
3489#[skip_serializing_none]
3490#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3491#[serde(rename_all = "camelCase")]
3492#[non_exhaustive]
3493pub struct Usage {
3494 pub total_tokens: u64,
3496 pub input_tokens: u64,
3498 pub output_tokens: u64,
3500 #[serde_as(deserialize_as = "DefaultOnError")]
3502 #[schemars(extend("x-deserialize-default-on-error" = true))]
3503 #[serde(default)]
3504 pub thought_tokens: Option<u64>,
3505 #[serde_as(deserialize_as = "DefaultOnError")]
3507 #[schemars(extend("x-deserialize-default-on-error" = true))]
3508 #[serde(default)]
3509 pub cached_read_tokens: Option<u64>,
3510 #[serde_as(deserialize_as = "DefaultOnError")]
3512 #[schemars(extend("x-deserialize-default-on-error" = true))]
3513 #[serde(default)]
3514 pub cached_write_tokens: Option<u64>,
3515 #[serde_as(deserialize_as = "DefaultOnError")]
3521 #[schemars(extend("x-deserialize-default-on-error" = true))]
3522 #[serde(default)]
3523 #[serde(rename = "_meta")]
3524 pub meta: Option<Meta>,
3525}
3526
3527#[cfg(feature = "unstable_end_turn_token_usage")]
3528impl Usage {
3529 #[must_use]
3531 pub fn new(total_tokens: u64, input_tokens: u64, output_tokens: u64) -> Self {
3532 Self {
3533 total_tokens,
3534 input_tokens,
3535 output_tokens,
3536 thought_tokens: None,
3537 cached_read_tokens: None,
3538 cached_write_tokens: None,
3539 meta: None,
3540 }
3541 }
3542
3543 #[must_use]
3545 pub fn thought_tokens(mut self, thought_tokens: impl IntoOption<u64>) -> Self {
3546 self.thought_tokens = thought_tokens.into_option();
3547 self
3548 }
3549
3550 #[must_use]
3552 pub fn cached_read_tokens(mut self, cached_read_tokens: impl IntoOption<u64>) -> Self {
3553 self.cached_read_tokens = cached_read_tokens.into_option();
3554 self
3555 }
3556
3557 #[must_use]
3559 pub fn cached_write_tokens(mut self, cached_write_tokens: impl IntoOption<u64>) -> Self {
3560 self.cached_write_tokens = cached_write_tokens.into_option();
3561 self
3562 }
3563
3564 #[must_use]
3570 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3571 self.meta = meta.into_option();
3572 self
3573 }
3574}
3575
3576#[cfg(feature = "unstable_llm_providers")]
3589#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3590#[serde(rename_all = "snake_case")]
3591#[non_exhaustive]
3592#[expect(clippy::doc_markdown)]
3593pub enum LlmProtocol {
3594 Anthropic,
3596 #[serde(rename = "openai")]
3598 OpenAi,
3599 Azure,
3601 Vertex,
3603 Bedrock,
3605 #[serde(untagged)]
3611 Other(String),
3612}
3613
3614#[cfg(feature = "unstable_llm_providers")]
3620#[serde_as]
3621#[skip_serializing_none]
3622#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3623#[serde(rename_all = "camelCase")]
3624#[non_exhaustive]
3625pub struct ProviderCurrentConfig {
3626 pub api_type: LlmProtocol,
3628 #[schemars(url)]
3630 pub base_url: String,
3631 #[serde_as(deserialize_as = "DefaultOnError")]
3637 #[schemars(extend("x-deserialize-default-on-error" = true))]
3638 #[serde(default)]
3639 #[serde(rename = "_meta")]
3640 pub meta: Option<Meta>,
3641}
3642
3643#[cfg(feature = "unstable_llm_providers")]
3644impl ProviderCurrentConfig {
3645 #[must_use]
3647 pub fn new(api_type: LlmProtocol, base_url: impl Into<String>) -> Self {
3648 Self {
3649 api_type,
3650 base_url: base_url.into(),
3651 meta: None,
3652 }
3653 }
3654
3655 #[must_use]
3661 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3662 self.meta = meta.into_option();
3663 self
3664 }
3665}
3666
3667#[cfg(feature = "unstable_llm_providers")]
3673#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, Display, From)]
3674#[serde(transparent)]
3675#[from(forward)]
3676#[non_exhaustive]
3677pub struct ProviderId(pub Arc<str>);
3678
3679#[cfg(feature = "unstable_llm_providers")]
3680impl ProviderId {
3681 #[must_use]
3683 pub fn new(id: impl Into<Self>) -> Self {
3684 id.into()
3685 }
3686}
3687
3688#[cfg(feature = "unstable_llm_providers")]
3694#[serde_as]
3695#[skip_serializing_none]
3696#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3697#[serde(rename_all = "camelCase")]
3698#[non_exhaustive]
3699pub struct ProviderInfo {
3700 pub provider_id: ProviderId,
3702 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
3704 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
3705 pub supported: Vec<LlmProtocol>,
3706 pub required: bool,
3709 #[serde(default)]
3712 pub current: Option<ProviderCurrentConfig>,
3713 #[serde_as(deserialize_as = "DefaultOnError")]
3719 #[schemars(extend("x-deserialize-default-on-error" = true))]
3720 #[serde(default)]
3721 #[serde(rename = "_meta")]
3722 pub meta: Option<Meta>,
3723}
3724
3725#[cfg(feature = "unstable_llm_providers")]
3726impl ProviderInfo {
3727 #[must_use]
3729 pub fn new(
3730 provider_id: impl Into<ProviderId>,
3731 supported: Vec<LlmProtocol>,
3732 required: bool,
3733 current: impl IntoOption<ProviderCurrentConfig>,
3734 ) -> Self {
3735 Self {
3736 provider_id: provider_id.into(),
3737 supported,
3738 required,
3739 current: current.into_option(),
3740 meta: None,
3741 }
3742 }
3743
3744 #[must_use]
3750 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3751 self.meta = meta.into_option();
3752 self
3753 }
3754}
3755
3756#[cfg(feature = "unstable_llm_providers")]
3762#[serde_as]
3763#[skip_serializing_none]
3764#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3765#[schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_LIST_METHOD_NAME))]
3766#[serde(rename_all = "camelCase")]
3767#[non_exhaustive]
3768pub struct ListProvidersRequest {
3769 #[serde_as(deserialize_as = "DefaultOnError")]
3775 #[schemars(extend("x-deserialize-default-on-error" = true))]
3776 #[serde(default)]
3777 #[serde(rename = "_meta")]
3778 pub meta: Option<Meta>,
3779}
3780
3781#[cfg(feature = "unstable_llm_providers")]
3782impl ListProvidersRequest {
3783 #[must_use]
3785 pub fn new() -> Self {
3786 Self::default()
3787 }
3788
3789 #[must_use]
3795 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3796 self.meta = meta.into_option();
3797 self
3798 }
3799}
3800
3801#[cfg(feature = "unstable_llm_providers")]
3807#[serde_as]
3808#[skip_serializing_none]
3809#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3810#[schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_LIST_METHOD_NAME))]
3811#[serde(rename_all = "camelCase")]
3812#[non_exhaustive]
3813pub struct ListProvidersResponse {
3814 pub providers: Vec<ProviderInfo>,
3816 #[serde_as(deserialize_as = "DefaultOnError")]
3822 #[schemars(extend("x-deserialize-default-on-error" = true))]
3823 #[serde(default)]
3824 #[serde(rename = "_meta")]
3825 pub meta: Option<Meta>,
3826}
3827
3828#[cfg(feature = "unstable_llm_providers")]
3829impl ListProvidersResponse {
3830 #[must_use]
3832 pub fn new(providers: Vec<ProviderInfo>) -> Self {
3833 Self {
3834 providers,
3835 meta: None,
3836 }
3837 }
3838
3839 #[must_use]
3845 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3846 self.meta = meta.into_option();
3847 self
3848 }
3849}
3850
3851#[cfg(feature = "unstable_llm_providers")]
3859#[serde_as]
3860#[skip_serializing_none]
3861#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3862#[schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_SET_METHOD_NAME))]
3863#[serde(rename_all = "camelCase")]
3864#[non_exhaustive]
3865pub struct SetProviderRequest {
3866 pub provider_id: ProviderId,
3868 pub api_type: LlmProtocol,
3870 #[schemars(url)]
3872 pub base_url: String,
3873 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
3876 pub headers: HashMap<String, String>,
3877 #[serde_as(deserialize_as = "DefaultOnError")]
3883 #[schemars(extend("x-deserialize-default-on-error" = true))]
3884 #[serde(default)]
3885 #[serde(rename = "_meta")]
3886 pub meta: Option<Meta>,
3887}
3888
3889#[cfg(feature = "unstable_llm_providers")]
3890impl SetProviderRequest {
3891 #[must_use]
3893 pub fn new(
3894 provider_id: impl Into<ProviderId>,
3895 api_type: LlmProtocol,
3896 base_url: impl Into<String>,
3897 ) -> Self {
3898 Self {
3899 provider_id: provider_id.into(),
3900 api_type,
3901 base_url: base_url.into(),
3902 headers: HashMap::new(),
3903 meta: None,
3904 }
3905 }
3906
3907 #[must_use]
3910 pub fn headers(mut self, headers: HashMap<String, String>) -> Self {
3911 self.headers = headers;
3912 self
3913 }
3914
3915 #[must_use]
3921 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3922 self.meta = meta.into_option();
3923 self
3924 }
3925}
3926
3927#[cfg(feature = "unstable_llm_providers")]
3933#[serde_as]
3934#[skip_serializing_none]
3935#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3936#[schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_SET_METHOD_NAME))]
3937#[serde(rename_all = "camelCase")]
3938#[non_exhaustive]
3939pub struct SetProviderResponse {
3940 #[serde_as(deserialize_as = "DefaultOnError")]
3946 #[schemars(extend("x-deserialize-default-on-error" = true))]
3947 #[serde(default)]
3948 #[serde(rename = "_meta")]
3949 pub meta: Option<Meta>,
3950}
3951
3952#[cfg(feature = "unstable_llm_providers")]
3953impl SetProviderResponse {
3954 #[must_use]
3956 pub fn new() -> Self {
3957 Self::default()
3958 }
3959
3960 #[must_use]
3966 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3967 self.meta = meta.into_option();
3968 self
3969 }
3970}
3971
3972#[cfg(feature = "unstable_llm_providers")]
3978#[serde_as]
3979#[skip_serializing_none]
3980#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3981#[schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_DISABLE_METHOD_NAME))]
3982#[serde(rename_all = "camelCase")]
3983#[non_exhaustive]
3984pub struct DisableProviderRequest {
3985 pub provider_id: ProviderId,
3987 #[serde_as(deserialize_as = "DefaultOnError")]
3993 #[schemars(extend("x-deserialize-default-on-error" = true))]
3994 #[serde(default)]
3995 #[serde(rename = "_meta")]
3996 pub meta: Option<Meta>,
3997}
3998
3999#[cfg(feature = "unstable_llm_providers")]
4000impl DisableProviderRequest {
4001 #[must_use]
4003 pub fn new(provider_id: impl Into<ProviderId>) -> Self {
4004 Self {
4005 provider_id: provider_id.into(),
4006 meta: None,
4007 }
4008 }
4009
4010 #[must_use]
4016 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4017 self.meta = meta.into_option();
4018 self
4019 }
4020}
4021
4022#[cfg(feature = "unstable_llm_providers")]
4028#[serde_as]
4029#[skip_serializing_none]
4030#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
4031#[schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_DISABLE_METHOD_NAME))]
4032#[serde(rename_all = "camelCase")]
4033#[non_exhaustive]
4034pub struct DisableProviderResponse {
4035 #[serde_as(deserialize_as = "DefaultOnError")]
4041 #[schemars(extend("x-deserialize-default-on-error" = true))]
4042 #[serde(default)]
4043 #[serde(rename = "_meta")]
4044 pub meta: Option<Meta>,
4045}
4046
4047#[cfg(feature = "unstable_llm_providers")]
4048impl DisableProviderResponse {
4049 #[must_use]
4051 pub fn new() -> Self {
4052 Self::default()
4053 }
4054
4055 #[must_use]
4061 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4062 self.meta = meta.into_option();
4063 self
4064 }
4065}
4066
4067#[serde_as]
4076#[skip_serializing_none]
4077#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
4078#[serde(rename_all = "camelCase")]
4079#[non_exhaustive]
4080pub struct AgentCapabilities {
4081 #[serde_as(deserialize_as = "DefaultOnError")]
4088 #[schemars(extend("x-deserialize-default-on-error" = true))]
4089 #[serde(default)]
4090 pub session: Option<SessionCapabilities>,
4091 #[serde_as(deserialize_as = "DefaultOnError")]
4098 #[schemars(extend("x-deserialize-default-on-error" = true))]
4099 #[serde(default)]
4100 pub auth: Option<AgentAuthCapabilities>,
4101 #[cfg(feature = "unstable_llm_providers")]
4110 #[serde_as(deserialize_as = "DefaultOnError")]
4111 #[schemars(extend("x-deserialize-default-on-error" = true))]
4112 #[serde(default)]
4113 pub providers: Option<ProvidersCapabilities>,
4114 #[cfg(feature = "unstable_nes")]
4123 #[serde_as(deserialize_as = "DefaultOnError")]
4124 #[schemars(extend("x-deserialize-default-on-error" = true))]
4125 #[serde(default)]
4126 pub nes: Option<NesCapabilities>,
4127 #[cfg(feature = "unstable_nes")]
4133 #[serde_as(deserialize_as = "DefaultOnError")]
4134 #[schemars(extend("x-deserialize-default-on-error" = true))]
4135 #[serde(default)]
4136 pub position_encoding: Option<PositionEncodingKind>,
4137 #[serde_as(deserialize_as = "DefaultOnError")]
4143 #[schemars(extend("x-deserialize-default-on-error" = true))]
4144 #[serde(default)]
4145 #[serde(rename = "_meta")]
4146 pub meta: Option<Meta>,
4147}
4148
4149impl AgentCapabilities {
4150 #[must_use]
4152 pub fn new() -> Self {
4153 Self::default()
4154 }
4155
4156 #[must_use]
4163 pub fn session(mut self, session: impl IntoOption<SessionCapabilities>) -> Self {
4164 self.session = session.into_option();
4165 self
4166 }
4167
4168 #[must_use]
4172 pub fn auth(mut self, auth: impl IntoOption<AgentAuthCapabilities>) -> Self {
4173 self.auth = auth.into_option();
4174 self
4175 }
4176
4177 #[cfg(feature = "unstable_llm_providers")]
4183 #[must_use]
4184 pub fn providers(mut self, providers: impl IntoOption<ProvidersCapabilities>) -> Self {
4185 self.providers = providers.into_option();
4186 self
4187 }
4188
4189 #[cfg(feature = "unstable_nes")]
4195 #[must_use]
4196 pub fn nes(mut self, nes: impl IntoOption<NesCapabilities>) -> Self {
4197 self.nes = nes.into_option();
4198 self
4199 }
4200
4201 #[cfg(feature = "unstable_nes")]
4205 #[must_use]
4206 pub fn position_encoding(
4207 mut self,
4208 position_encoding: impl IntoOption<PositionEncodingKind>,
4209 ) -> Self {
4210 self.position_encoding = position_encoding.into_option();
4211 self
4212 }
4213
4214 #[must_use]
4220 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4221 self.meta = meta.into_option();
4222 self
4223 }
4224}
4225
4226#[cfg(feature = "unstable_llm_providers")]
4234#[serde_as]
4235#[skip_serializing_none]
4236#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
4237#[non_exhaustive]
4238pub struct ProvidersCapabilities {
4239 #[serde_as(deserialize_as = "DefaultOnError")]
4245 #[schemars(extend("x-deserialize-default-on-error" = true))]
4246 #[serde(default)]
4247 #[serde(rename = "_meta")]
4248 pub meta: Option<Meta>,
4249}
4250
4251#[cfg(feature = "unstable_llm_providers")]
4252impl ProvidersCapabilities {
4253 #[must_use]
4255 pub fn new() -> Self {
4256 Self::default()
4257 }
4258
4259 #[must_use]
4265 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4266 self.meta = meta.into_option();
4267 self
4268 }
4269}
4270
4271#[serde_as]
4283#[skip_serializing_none]
4284#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
4285#[serde(rename_all = "camelCase")]
4286#[non_exhaustive]
4287pub struct SessionCapabilities {
4288 #[serde_as(deserialize_as = "DefaultOnError")]
4294 #[schemars(extend("x-deserialize-default-on-error" = true))]
4295 #[serde(default)]
4296 pub prompt: Option<PromptCapabilities>,
4297 #[serde_as(deserialize_as = "DefaultOnError")]
4302 #[schemars(extend("x-deserialize-default-on-error" = true))]
4303 #[serde(default)]
4304 pub mcp: Option<McpCapabilities>,
4305 #[serde_as(deserialize_as = "DefaultOnError")]
4310 #[schemars(extend("x-deserialize-default-on-error" = true))]
4311 #[serde(default)]
4312 pub delete: Option<SessionDeleteCapabilities>,
4313 #[serde_as(deserialize_as = "DefaultOnError")]
4322 #[schemars(extend("x-deserialize-default-on-error" = true))]
4323 #[serde(default)]
4324 pub additional_directories: Option<SessionAdditionalDirectoriesCapabilities>,
4325 #[cfg(feature = "unstable_session_fork")]
4334 #[serde_as(deserialize_as = "DefaultOnError")]
4335 #[schemars(extend("x-deserialize-default-on-error" = true))]
4336 #[serde(default)]
4337 pub fork: Option<SessionForkCapabilities>,
4338 #[serde_as(deserialize_as = "DefaultOnError")]
4344 #[schemars(extend("x-deserialize-default-on-error" = true))]
4345 #[serde(default)]
4346 #[serde(rename = "_meta")]
4347 pub meta: Option<Meta>,
4348}
4349
4350impl SessionCapabilities {
4351 #[must_use]
4353 pub fn new() -> Self {
4354 Self::default()
4355 }
4356
4357 #[must_use]
4363 pub fn prompt(mut self, prompt: impl IntoOption<PromptCapabilities>) -> Self {
4364 self.prompt = prompt.into_option();
4365 self
4366 }
4367
4368 #[must_use]
4373 pub fn mcp(mut self, mcp: impl IntoOption<McpCapabilities>) -> Self {
4374 self.mcp = mcp.into_option();
4375 self
4376 }
4377
4378 #[must_use]
4383 pub fn delete(mut self, delete: impl IntoOption<SessionDeleteCapabilities>) -> Self {
4384 self.delete = delete.into_option();
4385 self
4386 }
4387
4388 #[must_use]
4397 pub fn additional_directories(
4398 mut self,
4399 additional_directories: impl IntoOption<SessionAdditionalDirectoriesCapabilities>,
4400 ) -> Self {
4401 self.additional_directories = additional_directories.into_option();
4402 self
4403 }
4404
4405 #[cfg(feature = "unstable_session_fork")]
4406 #[must_use]
4411 pub fn fork(mut self, fork: impl IntoOption<SessionForkCapabilities>) -> Self {
4412 self.fork = fork.into_option();
4413 self
4414 }
4415
4416 #[must_use]
4422 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4423 self.meta = meta.into_option();
4424 self
4425 }
4426}
4427
4428#[serde_as]
4432#[skip_serializing_none]
4433#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
4434#[non_exhaustive]
4435pub struct SessionDeleteCapabilities {
4436 #[serde_as(deserialize_as = "DefaultOnError")]
4442 #[schemars(extend("x-deserialize-default-on-error" = true))]
4443 #[serde(default)]
4444 #[serde(rename = "_meta")]
4445 pub meta: Option<Meta>,
4446}
4447
4448impl SessionDeleteCapabilities {
4449 #[must_use]
4451 pub fn new() -> Self {
4452 Self::default()
4453 }
4454
4455 #[must_use]
4461 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4462 self.meta = meta.into_option();
4463 self
4464 }
4465}
4466
4467#[serde_as]
4474#[skip_serializing_none]
4475#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
4476#[non_exhaustive]
4477pub struct SessionAdditionalDirectoriesCapabilities {
4478 #[serde_as(deserialize_as = "DefaultOnError")]
4484 #[schemars(extend("x-deserialize-default-on-error" = true))]
4485 #[serde(default)]
4486 #[serde(rename = "_meta")]
4487 pub meta: Option<Meta>,
4488}
4489
4490impl SessionAdditionalDirectoriesCapabilities {
4491 #[must_use]
4493 pub fn new() -> Self {
4494 Self::default()
4495 }
4496
4497 #[must_use]
4503 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4504 self.meta = meta.into_option();
4505 self
4506 }
4507}
4508
4509#[cfg(feature = "unstable_session_fork")]
4517#[serde_as]
4518#[skip_serializing_none]
4519#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
4520#[non_exhaustive]
4521pub struct SessionForkCapabilities {
4522 #[serde_as(deserialize_as = "DefaultOnError")]
4528 #[schemars(extend("x-deserialize-default-on-error" = true))]
4529 #[serde(default)]
4530 #[serde(rename = "_meta")]
4531 pub meta: Option<Meta>,
4532}
4533
4534#[cfg(feature = "unstable_session_fork")]
4535impl SessionForkCapabilities {
4536 #[must_use]
4538 pub fn new() -> Self {
4539 Self::default()
4540 }
4541
4542 #[must_use]
4548 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4549 self.meta = meta.into_option();
4550 self
4551 }
4552}
4553
4554#[serde_as]
4567#[skip_serializing_none]
4568#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
4569#[serde(rename_all = "camelCase")]
4570#[non_exhaustive]
4571pub struct PromptCapabilities {
4572 #[serde_as(deserialize_as = "DefaultOnError")]
4577 #[schemars(extend("x-deserialize-default-on-error" = true))]
4578 #[serde(default)]
4579 pub image: Option<PromptImageCapabilities>,
4580 #[serde_as(deserialize_as = "DefaultOnError")]
4585 #[schemars(extend("x-deserialize-default-on-error" = true))]
4586 #[serde(default)]
4587 pub audio: Option<PromptAudioCapabilities>,
4588 #[serde_as(deserialize_as = "DefaultOnError")]
4596 #[schemars(extend("x-deserialize-default-on-error" = true))]
4597 #[serde(default)]
4598 pub embedded_context: Option<PromptEmbeddedContextCapabilities>,
4599 #[serde_as(deserialize_as = "DefaultOnError")]
4605 #[schemars(extend("x-deserialize-default-on-error" = true))]
4606 #[serde(default)]
4607 #[serde(rename = "_meta")]
4608 pub meta: Option<Meta>,
4609}
4610
4611impl PromptCapabilities {
4612 #[must_use]
4614 pub fn new() -> Self {
4615 Self::default()
4616 }
4617
4618 #[must_use]
4623 pub fn image(mut self, image: impl IntoOption<PromptImageCapabilities>) -> Self {
4624 self.image = image.into_option();
4625 self
4626 }
4627
4628 #[must_use]
4633 pub fn audio(mut self, audio: impl IntoOption<PromptAudioCapabilities>) -> Self {
4634 self.audio = audio.into_option();
4635 self
4636 }
4637
4638 #[must_use]
4646 pub fn embedded_context(
4647 mut self,
4648 embedded_context: impl IntoOption<PromptEmbeddedContextCapabilities>,
4649 ) -> Self {
4650 self.embedded_context = embedded_context.into_option();
4651 self
4652 }
4653
4654 #[must_use]
4660 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4661 self.meta = meta.into_option();
4662 self
4663 }
4664}
4665
4666#[serde_as]
4670#[skip_serializing_none]
4671#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
4672#[non_exhaustive]
4673pub struct PromptImageCapabilities {
4674 #[serde_as(deserialize_as = "DefaultOnError")]
4680 #[schemars(extend("x-deserialize-default-on-error" = true))]
4681 #[serde(default)]
4682 #[serde(rename = "_meta")]
4683 pub meta: Option<Meta>,
4684}
4685
4686impl PromptImageCapabilities {
4687 #[must_use]
4689 pub fn new() -> Self {
4690 Self::default()
4691 }
4692
4693 #[must_use]
4699 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4700 self.meta = meta.into_option();
4701 self
4702 }
4703}
4704
4705#[serde_as]
4709#[skip_serializing_none]
4710#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
4711#[non_exhaustive]
4712pub struct PromptAudioCapabilities {
4713 #[serde_as(deserialize_as = "DefaultOnError")]
4719 #[schemars(extend("x-deserialize-default-on-error" = true))]
4720 #[serde(default)]
4721 #[serde(rename = "_meta")]
4722 pub meta: Option<Meta>,
4723}
4724
4725impl PromptAudioCapabilities {
4726 #[must_use]
4728 pub fn new() -> Self {
4729 Self::default()
4730 }
4731
4732 #[must_use]
4738 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4739 self.meta = meta.into_option();
4740 self
4741 }
4742}
4743
4744#[serde_as]
4748#[skip_serializing_none]
4749#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
4750#[non_exhaustive]
4751pub struct PromptEmbeddedContextCapabilities {
4752 #[serde_as(deserialize_as = "DefaultOnError")]
4758 #[schemars(extend("x-deserialize-default-on-error" = true))]
4759 #[serde(default)]
4760 #[serde(rename = "_meta")]
4761 pub meta: Option<Meta>,
4762}
4763
4764impl PromptEmbeddedContextCapabilities {
4765 #[must_use]
4767 pub fn new() -> Self {
4768 Self::default()
4769 }
4770
4771 #[must_use]
4777 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4778 self.meta = meta.into_option();
4779 self
4780 }
4781}
4782
4783#[serde_as]
4785#[skip_serializing_none]
4786#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
4787#[serde(rename_all = "camelCase")]
4788#[non_exhaustive]
4789pub struct McpCapabilities {
4790 #[serde_as(deserialize_as = "DefaultOnError")]
4795 #[schemars(extend("x-deserialize-default-on-error" = true))]
4796 #[serde(default)]
4797 pub stdio: Option<McpStdioCapabilities>,
4798 #[serde_as(deserialize_as = "DefaultOnError")]
4803 #[schemars(extend("x-deserialize-default-on-error" = true))]
4804 #[serde(default)]
4805 pub http: Option<McpHttpCapabilities>,
4806 #[cfg(feature = "unstable_mcp_over_acp")]
4815 #[serde_as(deserialize_as = "DefaultOnError")]
4816 #[schemars(extend("x-deserialize-default-on-error" = true))]
4817 #[serde(default)]
4818 pub acp: Option<McpAcpCapabilities>,
4819 #[serde_as(deserialize_as = "DefaultOnError")]
4825 #[schemars(extend("x-deserialize-default-on-error" = true))]
4826 #[serde(default)]
4827 #[serde(rename = "_meta")]
4828 pub meta: Option<Meta>,
4829}
4830
4831impl McpCapabilities {
4832 #[must_use]
4834 pub fn new() -> Self {
4835 Self::default()
4836 }
4837
4838 #[must_use]
4843 pub fn stdio(mut self, stdio: impl IntoOption<McpStdioCapabilities>) -> Self {
4844 self.stdio = stdio.into_option();
4845 self
4846 }
4847
4848 #[must_use]
4853 pub fn http(mut self, http: impl IntoOption<McpHttpCapabilities>) -> Self {
4854 self.http = http.into_option();
4855 self
4856 }
4857
4858 #[cfg(feature = "unstable_mcp_over_acp")]
4864 #[must_use]
4868 pub fn acp(mut self, acp: impl IntoOption<McpAcpCapabilities>) -> Self {
4869 self.acp = acp.into_option();
4870 self
4871 }
4872
4873 #[must_use]
4879 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4880 self.meta = meta.into_option();
4881 self
4882 }
4883}
4884
4885#[serde_as]
4889#[skip_serializing_none]
4890#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
4891#[non_exhaustive]
4892pub struct McpStdioCapabilities {
4893 #[serde_as(deserialize_as = "DefaultOnError")]
4899 #[schemars(extend("x-deserialize-default-on-error" = true))]
4900 #[serde(default)]
4901 #[serde(rename = "_meta")]
4902 pub meta: Option<Meta>,
4903}
4904
4905impl McpStdioCapabilities {
4906 #[must_use]
4908 pub fn new() -> Self {
4909 Self::default()
4910 }
4911
4912 #[must_use]
4918 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4919 self.meta = meta.into_option();
4920 self
4921 }
4922}
4923
4924#[serde_as]
4928#[skip_serializing_none]
4929#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
4930#[non_exhaustive]
4931pub struct McpHttpCapabilities {
4932 #[serde_as(deserialize_as = "DefaultOnError")]
4938 #[schemars(extend("x-deserialize-default-on-error" = true))]
4939 #[serde(default)]
4940 #[serde(rename = "_meta")]
4941 pub meta: Option<Meta>,
4942}
4943
4944impl McpHttpCapabilities {
4945 #[must_use]
4947 pub fn new() -> Self {
4948 Self::default()
4949 }
4950
4951 #[must_use]
4957 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4958 self.meta = meta.into_option();
4959 self
4960 }
4961}
4962
4963#[cfg(feature = "unstable_mcp_over_acp")]
4971#[serde_as]
4972#[skip_serializing_none]
4973#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
4974#[non_exhaustive]
4975pub struct McpAcpCapabilities {
4976 #[serde_as(deserialize_as = "DefaultOnError")]
4982 #[schemars(extend("x-deserialize-default-on-error" = true))]
4983 #[serde(default)]
4984 #[serde(rename = "_meta")]
4985 pub meta: Option<Meta>,
4986}
4987
4988#[cfg(feature = "unstable_mcp_over_acp")]
4989impl McpAcpCapabilities {
4990 #[must_use]
4992 pub fn new() -> Self {
4993 Self::default()
4994 }
4995
4996 #[must_use]
5002 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5003 self.meta = meta.into_option();
5004 self
5005 }
5006}
5007
5008#[serde_as]
5012#[skip_serializing_none]
5013#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
5014#[schemars(extend("x-side" = "agent", "x-method" = SESSION_CANCEL_METHOD_NAME))]
5015#[serde(rename_all = "camelCase")]
5016#[non_exhaustive]
5017pub struct CancelSessionNotification {
5018 pub session_id: SessionId,
5020 #[serde_as(deserialize_as = "DefaultOnError")]
5026 #[schemars(extend("x-deserialize-default-on-error" = true))]
5027 #[serde(default)]
5028 #[serde(rename = "_meta")]
5029 pub meta: Option<Meta>,
5030}
5031
5032impl CancelSessionNotification {
5033 #[must_use]
5035 pub fn new(session_id: impl Into<SessionId>) -> Self {
5036 Self {
5037 session_id: session_id.into(),
5038 meta: None,
5039 }
5040 }
5041
5042 #[must_use]
5048 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5049 self.meta = meta.into_option();
5050 self
5051 }
5052}
5053
5054#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5060#[non_exhaustive]
5061pub struct AgentMethodNames {
5062 pub initialize: &'static str,
5064 pub auth_login: &'static str,
5066 #[cfg(feature = "unstable_llm_providers")]
5068 pub providers_list: &'static str,
5069 #[cfg(feature = "unstable_llm_providers")]
5071 pub providers_set: &'static str,
5072 #[cfg(feature = "unstable_llm_providers")]
5074 pub providers_disable: &'static str,
5075 pub session_new: &'static str,
5077 pub session_set_config_option: &'static str,
5079 pub session_prompt: &'static str,
5081 pub session_cancel: &'static str,
5083 #[cfg(feature = "unstable_mcp_over_acp")]
5085 pub mcp_message: &'static str,
5086 pub session_list: &'static str,
5088 pub session_delete: &'static str,
5090 #[cfg(feature = "unstable_session_fork")]
5092 pub session_fork: &'static str,
5093 pub session_resume: &'static str,
5095 pub session_close: &'static str,
5097 pub auth_logout: &'static str,
5099 #[cfg(feature = "unstable_nes")]
5101 pub nes_start: &'static str,
5102 #[cfg(feature = "unstable_nes")]
5104 pub nes_suggest: &'static str,
5105 #[cfg(feature = "unstable_nes")]
5107 pub nes_accept: &'static str,
5108 #[cfg(feature = "unstable_nes")]
5110 pub nes_reject: &'static str,
5111 #[cfg(feature = "unstable_nes")]
5113 pub nes_close: &'static str,
5114 #[cfg(feature = "unstable_nes")]
5116 pub document_did_open: &'static str,
5117 #[cfg(feature = "unstable_nes")]
5119 pub document_did_change: &'static str,
5120 #[cfg(feature = "unstable_nes")]
5122 pub document_did_close: &'static str,
5123 #[cfg(feature = "unstable_nes")]
5125 pub document_did_save: &'static str,
5126 #[cfg(feature = "unstable_nes")]
5128 pub document_did_focus: &'static str,
5129}
5130
5131pub const AGENT_METHOD_NAMES: AgentMethodNames = AgentMethodNames {
5133 initialize: INITIALIZE_METHOD_NAME,
5134 auth_login: AUTH_LOGIN_METHOD_NAME,
5135 #[cfg(feature = "unstable_llm_providers")]
5136 providers_list: PROVIDERS_LIST_METHOD_NAME,
5137 #[cfg(feature = "unstable_llm_providers")]
5138 providers_set: PROVIDERS_SET_METHOD_NAME,
5139 #[cfg(feature = "unstable_llm_providers")]
5140 providers_disable: PROVIDERS_DISABLE_METHOD_NAME,
5141 session_new: SESSION_NEW_METHOD_NAME,
5142 session_set_config_option: SESSION_SET_CONFIG_OPTION_METHOD_NAME,
5143 session_prompt: SESSION_PROMPT_METHOD_NAME,
5144 session_cancel: SESSION_CANCEL_METHOD_NAME,
5145 #[cfg(feature = "unstable_mcp_over_acp")]
5146 mcp_message: MCP_MESSAGE_METHOD_NAME,
5147 session_list: SESSION_LIST_METHOD_NAME,
5148 session_delete: SESSION_DELETE_METHOD_NAME,
5149 #[cfg(feature = "unstable_session_fork")]
5150 session_fork: SESSION_FORK_METHOD_NAME,
5151 session_resume: SESSION_RESUME_METHOD_NAME,
5152 session_close: SESSION_CLOSE_METHOD_NAME,
5153 auth_logout: AUTH_LOGOUT_METHOD_NAME,
5154 #[cfg(feature = "unstable_nes")]
5155 nes_start: NES_START_METHOD_NAME,
5156 #[cfg(feature = "unstable_nes")]
5157 nes_suggest: NES_SUGGEST_METHOD_NAME,
5158 #[cfg(feature = "unstable_nes")]
5159 nes_accept: NES_ACCEPT_METHOD_NAME,
5160 #[cfg(feature = "unstable_nes")]
5161 nes_reject: NES_REJECT_METHOD_NAME,
5162 #[cfg(feature = "unstable_nes")]
5163 nes_close: NES_CLOSE_METHOD_NAME,
5164 #[cfg(feature = "unstable_nes")]
5165 document_did_open: DOCUMENT_DID_OPEN_METHOD_NAME,
5166 #[cfg(feature = "unstable_nes")]
5167 document_did_change: DOCUMENT_DID_CHANGE_METHOD_NAME,
5168 #[cfg(feature = "unstable_nes")]
5169 document_did_close: DOCUMENT_DID_CLOSE_METHOD_NAME,
5170 #[cfg(feature = "unstable_nes")]
5171 document_did_save: DOCUMENT_DID_SAVE_METHOD_NAME,
5172 #[cfg(feature = "unstable_nes")]
5173 document_did_focus: DOCUMENT_DID_FOCUS_METHOD_NAME,
5174};
5175
5176pub(crate) const INITIALIZE_METHOD_NAME: &str = "initialize";
5178pub(crate) const AUTH_LOGIN_METHOD_NAME: &str = "auth/login";
5180#[cfg(feature = "unstable_llm_providers")]
5182pub(crate) const PROVIDERS_LIST_METHOD_NAME: &str = "providers/list";
5183#[cfg(feature = "unstable_llm_providers")]
5185pub(crate) const PROVIDERS_SET_METHOD_NAME: &str = "providers/set";
5186#[cfg(feature = "unstable_llm_providers")]
5188pub(crate) const PROVIDERS_DISABLE_METHOD_NAME: &str = "providers/disable";
5189pub(crate) const SESSION_NEW_METHOD_NAME: &str = "session/new";
5191pub(crate) const SESSION_SET_CONFIG_OPTION_METHOD_NAME: &str = "session/set_config_option";
5193pub(crate) const SESSION_PROMPT_METHOD_NAME: &str = "session/prompt";
5195pub(crate) const SESSION_CANCEL_METHOD_NAME: &str = "session/cancel";
5197pub(crate) const SESSION_LIST_METHOD_NAME: &str = "session/list";
5199pub(crate) const SESSION_DELETE_METHOD_NAME: &str = "session/delete";
5201#[cfg(feature = "unstable_session_fork")]
5203pub(crate) const SESSION_FORK_METHOD_NAME: &str = "session/fork";
5204pub(crate) const SESSION_RESUME_METHOD_NAME: &str = "session/resume";
5206pub(crate) const SESSION_CLOSE_METHOD_NAME: &str = "session/close";
5208pub(crate) const AUTH_LOGOUT_METHOD_NAME: &str = "auth/logout";
5210
5211#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
5218#[serde(untagged)]
5219#[schemars(inline)]
5220#[non_exhaustive]
5221pub enum ClientRequest {
5222 InitializeRequest(Box<InitializeRequest>),
5233 LoginAuthRequest(Box<LoginAuthRequest>),
5247 #[cfg(feature = "unstable_llm_providers")]
5253 ListProvidersRequest(Box<ListProvidersRequest>),
5254 #[cfg(feature = "unstable_llm_providers")]
5260 SetProviderRequest(Box<SetProviderRequest>),
5261 #[cfg(feature = "unstable_llm_providers")]
5267 DisableProviderRequest(Box<DisableProviderRequest>),
5268 LogoutAuthRequest(Box<LogoutAuthRequest>),
5278 NewSessionRequest(Box<NewSessionRequest>),
5291 ListSessionsRequest(Box<ListSessionsRequest>),
5295 DeleteSessionRequest(Box<DeleteSessionRequest>),
5299 #[cfg(feature = "unstable_session_fork")]
5300 ForkSessionRequest(Box<ForkSessionRequest>),
5312 ResumeSessionRequest(Box<ResumeSessionRequest>),
5318 CloseSessionRequest(Box<CloseSessionRequest>),
5323 SetSessionConfigOptionRequest(Box<SetSessionConfigOptionRequest>),
5325 PromptRequest(Box<PromptRequest>),
5337 #[cfg(feature = "unstable_nes")]
5338 StartNesRequest(Box<StartNesRequest>),
5344 #[cfg(feature = "unstable_nes")]
5345 SuggestNesRequest(Box<SuggestNesRequest>),
5351 #[cfg(feature = "unstable_nes")]
5352 CloseNesRequest(Box<CloseNesRequest>),
5361 #[cfg(feature = "unstable_mcp_over_acp")]
5367 MessageMcpRequest(Box<MessageMcpRequest>),
5368 ExtMethodRequest(Box<ExtRequest>),
5375}
5376
5377impl ClientRequest {
5378 #[must_use]
5380 pub fn method(&self) -> &str {
5381 match self {
5382 Self::InitializeRequest(_) => AGENT_METHOD_NAMES.initialize,
5383 Self::LoginAuthRequest(_) => AGENT_METHOD_NAMES.auth_login,
5384 #[cfg(feature = "unstable_llm_providers")]
5385 Self::ListProvidersRequest(_) => AGENT_METHOD_NAMES.providers_list,
5386 #[cfg(feature = "unstable_llm_providers")]
5387 Self::SetProviderRequest(_) => AGENT_METHOD_NAMES.providers_set,
5388 #[cfg(feature = "unstable_llm_providers")]
5389 Self::DisableProviderRequest(_) => AGENT_METHOD_NAMES.providers_disable,
5390 Self::LogoutAuthRequest(_) => AGENT_METHOD_NAMES.auth_logout,
5391 Self::NewSessionRequest(_) => AGENT_METHOD_NAMES.session_new,
5392 Self::ListSessionsRequest(_) => AGENT_METHOD_NAMES.session_list,
5393 Self::DeleteSessionRequest(_) => AGENT_METHOD_NAMES.session_delete,
5394 #[cfg(feature = "unstable_session_fork")]
5395 Self::ForkSessionRequest(_) => AGENT_METHOD_NAMES.session_fork,
5396 Self::ResumeSessionRequest(_) => AGENT_METHOD_NAMES.session_resume,
5397 Self::CloseSessionRequest(_) => AGENT_METHOD_NAMES.session_close,
5398 Self::SetSessionConfigOptionRequest(_) => AGENT_METHOD_NAMES.session_set_config_option,
5399 Self::PromptRequest(_) => AGENT_METHOD_NAMES.session_prompt,
5400 #[cfg(feature = "unstable_nes")]
5401 Self::StartNesRequest(_) => AGENT_METHOD_NAMES.nes_start,
5402 #[cfg(feature = "unstable_nes")]
5403 Self::SuggestNesRequest(_) => AGENT_METHOD_NAMES.nes_suggest,
5404 #[cfg(feature = "unstable_nes")]
5405 Self::CloseNesRequest(_) => AGENT_METHOD_NAMES.nes_close,
5406 #[cfg(feature = "unstable_mcp_over_acp")]
5407 Self::MessageMcpRequest(_) => AGENT_METHOD_NAMES.mcp_message,
5408 Self::ExtMethodRequest(ext_request) => &ext_request.method,
5409 }
5410 }
5411}
5412
5413#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
5420#[serde(untagged)]
5421#[schemars(inline)]
5422#[non_exhaustive]
5423pub enum AgentResponse {
5424 InitializeResponse(Box<InitializeResponse>),
5426 LoginAuthResponse(#[serde(default)] Box<LoginAuthResponse>),
5428 #[cfg(feature = "unstable_llm_providers")]
5430 ListProvidersResponse(Box<ListProvidersResponse>),
5431 #[cfg(feature = "unstable_llm_providers")]
5433 SetProviderResponse(#[serde(default)] Box<SetProviderResponse>),
5434 #[cfg(feature = "unstable_llm_providers")]
5436 DisableProviderResponse(#[serde(default)] Box<DisableProviderResponse>),
5437 LogoutAuthResponse(#[serde(default)] Box<LogoutAuthResponse>),
5439 NewSessionResponse(Box<NewSessionResponse>),
5441 ListSessionsResponse(Box<ListSessionsResponse>),
5443 DeleteSessionResponse(#[serde(default)] Box<DeleteSessionResponse>),
5445 #[cfg(feature = "unstable_session_fork")]
5447 ForkSessionResponse(Box<ForkSessionResponse>),
5448 ResumeSessionResponse(#[serde(default)] Box<ResumeSessionResponse>),
5450 CloseSessionResponse(#[serde(default)] Box<CloseSessionResponse>),
5452 SetSessionConfigOptionResponse(Box<SetSessionConfigOptionResponse>),
5454 PromptResponse(Box<PromptResponse>),
5456 #[cfg(feature = "unstable_nes")]
5458 StartNesResponse(Box<StartNesResponse>),
5459 #[cfg(feature = "unstable_nes")]
5461 SuggestNesResponse(Box<SuggestNesResponse>),
5462 #[cfg(feature = "unstable_nes")]
5464 CloseNesResponse(#[serde(default)] Box<CloseNesResponse>),
5465 ExtMethodResponse(Box<ExtResponse>),
5467 #[cfg(feature = "unstable_mcp_over_acp")]
5469 MessageMcpResponse(Box<MessageMcpResponse>),
5470}
5471
5472#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
5479#[serde(untagged)]
5480#[schemars(inline)]
5481#[non_exhaustive]
5482pub enum ClientNotification {
5483 CancelSessionNotification(Box<CancelSessionNotification>),
5497 #[cfg(feature = "unstable_nes")]
5498 DidOpenDocumentNotification(Box<DidOpenDocumentNotification>),
5502 #[cfg(feature = "unstable_nes")]
5503 DidChangeDocumentNotification(Box<DidChangeDocumentNotification>),
5507 #[cfg(feature = "unstable_nes")]
5508 DidCloseDocumentNotification(Box<DidCloseDocumentNotification>),
5512 #[cfg(feature = "unstable_nes")]
5513 DidSaveDocumentNotification(Box<DidSaveDocumentNotification>),
5517 #[cfg(feature = "unstable_nes")]
5518 DidFocusDocumentNotification(Box<DidFocusDocumentNotification>),
5522 #[cfg(feature = "unstable_nes")]
5523 AcceptNesNotification(Box<AcceptNesNotification>),
5527 #[cfg(feature = "unstable_nes")]
5528 RejectNesNotification(Box<RejectNesNotification>),
5532 #[cfg(feature = "unstable_mcp_over_acp")]
5538 MessageMcpNotification(Box<MessageMcpNotification>),
5539 ExtNotification(Box<ExtNotification>),
5546}
5547
5548impl ClientNotification {
5549 #[must_use]
5551 pub fn method(&self) -> &str {
5552 match self {
5553 Self::CancelSessionNotification(_) => AGENT_METHOD_NAMES.session_cancel,
5554 #[cfg(feature = "unstable_nes")]
5555 Self::DidOpenDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_open,
5556 #[cfg(feature = "unstable_nes")]
5557 Self::DidChangeDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_change,
5558 #[cfg(feature = "unstable_nes")]
5559 Self::DidCloseDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_close,
5560 #[cfg(feature = "unstable_nes")]
5561 Self::DidSaveDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_save,
5562 #[cfg(feature = "unstable_nes")]
5563 Self::DidFocusDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_focus,
5564 #[cfg(feature = "unstable_nes")]
5565 Self::AcceptNesNotification(_) => AGENT_METHOD_NAMES.nes_accept,
5566 #[cfg(feature = "unstable_nes")]
5567 Self::RejectNesNotification(_) => AGENT_METHOD_NAMES.nes_reject,
5568 #[cfg(feature = "unstable_mcp_over_acp")]
5569 Self::MessageMcpNotification(_) => AGENT_METHOD_NAMES.mcp_message,
5570 Self::ExtNotification(ext_notification) => &ext_notification.method,
5571 }
5572 }
5573}
5574
5575#[cfg(test)]
5576mod test_serialization {
5577 use std::path::PathBuf;
5578
5579 use super::*;
5580 use serde_json::json;
5581
5582 fn test_meta() -> Meta {
5583 json!({ "source": "test" }).as_object().unwrap().clone()
5584 }
5585
5586 fn serialized_meta_key_count(value: &impl serde::Serialize) -> usize {
5587 serde_json::to_string(value)
5588 .unwrap()
5589 .matches("\"_meta\"")
5590 .count()
5591 }
5592
5593 #[test]
5594 fn test_initialize_capabilities_default_on_malformed_values() {
5595 let request: InitializeRequest = serde_json::from_value(json!({
5596 "protocolVersion": 2,
5597 "capabilities": false,
5598 "info": {
5599 "name": "client",
5600 "version": "1.0.0"
5601 }
5602 }))
5603 .unwrap();
5604 assert_eq!(request.capabilities, ClientCapabilities::default());
5605
5606 let response: InitializeResponse = serde_json::from_value(json!({
5607 "protocolVersion": 2,
5608 "capabilities": false,
5609 "info": {
5610 "name": "agent",
5611 "version": "1.0.0"
5612 }
5613 }))
5614 .unwrap();
5615 assert_eq!(response.capabilities, AgentCapabilities::default());
5616 }
5617
5618 #[test]
5619 fn test_agent_capabilities_default_on_malformed_values() {
5620 let capabilities: AgentCapabilities = serde_json::from_value(json!({
5621 "session": false,
5622 "auth": false
5623 }))
5624 .unwrap();
5625
5626 assert!(capabilities.session.is_none());
5627 assert_eq!(capabilities.auth, None);
5628 }
5629
5630 #[test]
5631 fn test_mcp_server_stdio_serialization() {
5632 let server = McpServer::Stdio(
5633 McpServerStdio::new("test-server", "/usr/bin/server")
5634 .args(vec!["--port".to_string(), "3000".to_string()])
5635 .env(vec![EnvVariable::new("API_KEY", "secret123")]),
5636 );
5637
5638 let json = serde_json::to_value(&server).unwrap();
5639 assert_eq!(
5640 json,
5641 json!({
5642 "type": "stdio",
5643 "name": "test-server",
5644 "command": "/usr/bin/server",
5645 "args": ["--port", "3000"],
5646 "env": [
5647 {
5648 "name": "API_KEY",
5649 "value": "secret123"
5650 }
5651 ]
5652 })
5653 );
5654
5655 let deserialized: McpServer = serde_json::from_value(json).unwrap();
5656 match deserialized {
5657 McpServer::Stdio(McpServerStdio {
5658 name,
5659 command,
5660 args,
5661 env,
5662 meta: _,
5663 }) => {
5664 assert_eq!(name, "test-server");
5665 assert_eq!(command, AbsolutePath::new("/usr/bin/server"));
5666 assert_eq!(args, vec!["--port", "3000"]);
5667 assert_eq!(env.len(), 1);
5668 assert_eq!(env[0].name, "API_KEY");
5669 assert_eq!(env[0].value, "secret123");
5670 }
5671 _ => panic!("Expected Stdio variant"),
5672 }
5673 }
5674
5675 #[test]
5676 fn test_mcp_server_empty_arrays_are_optional() {
5677 let stdio = McpServer::Stdio(McpServerStdio::new("test-server", "/usr/bin/server"));
5678 assert_eq!(
5679 serde_json::to_value(&stdio).unwrap(),
5680 json!({
5681 "type": "stdio",
5682 "name": "test-server",
5683 "command": "/usr/bin/server"
5684 })
5685 );
5686
5687 let McpServer::Stdio(McpServerStdio { args, env, .. }) =
5688 serde_json::from_value::<McpServer>(json!({
5689 "type": "stdio",
5690 "name": "test-server",
5691 "command": "/usr/bin/server"
5692 }))
5693 .unwrap()
5694 else {
5695 panic!("Expected Stdio variant");
5696 };
5697 assert!(args.is_empty());
5698 assert!(env.is_empty());
5699
5700 let http = McpServer::Http(McpServerHttp::new("http-server", "https://api.example.com"));
5701 assert_eq!(
5702 serde_json::to_value(&http).unwrap(),
5703 json!({
5704 "type": "http",
5705 "name": "http-server",
5706 "url": "https://api.example.com"
5707 })
5708 );
5709
5710 let McpServer::Http(McpServerHttp { headers, .. }) =
5711 serde_json::from_value::<McpServer>(json!({
5712 "type": "http",
5713 "name": "http-server",
5714 "url": "https://api.example.com"
5715 }))
5716 .unwrap()
5717 else {
5718 panic!("Expected Http variant");
5719 };
5720 assert!(headers.is_empty());
5721 }
5722
5723 #[test]
5724 fn test_mcp_server_unknown_transport_serialization() {
5725 let json = json!({
5726 "type": "websocket",
5727 "name": "future-server",
5728 "url": "wss://example.com/mcp",
5729 "protocolVersion": "2026-01-01"
5730 });
5731
5732 let deserialized: McpServer = serde_json::from_value(json.clone()).unwrap();
5733 let McpServer::Other(OtherMcpServer { type_, fields }) = &deserialized else {
5734 panic!("Expected Other variant");
5735 };
5736
5737 assert_eq!(type_, "websocket");
5738 assert_eq!(fields["name"], "future-server");
5739 assert_eq!(fields["url"], "wss://example.com/mcp");
5740 assert_eq!(fields["protocolVersion"], "2026-01-01");
5741 assert_eq!(serde_json::to_value(&deserialized).unwrap(), json);
5742 }
5743
5744 #[test]
5745 fn test_mcp_server_stdio_requires_type() {
5746 let result = serde_json::from_value::<McpServer>(json!({
5747 "name": "test-server",
5748 "command": "/usr/bin/server",
5749 "args": [],
5750 "env": []
5751 }));
5752
5753 assert!(result.is_err());
5754 }
5755
5756 #[test]
5757 fn test_mcp_server_unknown_does_not_hide_malformed_known_transport() {
5758 let result = serde_json::from_value::<McpServer>(json!({
5759 "type": "stdio",
5760 "name": "test-server",
5761 "args": [],
5762 "env": []
5763 }));
5764
5765 assert!(result.is_err());
5766 }
5767
5768 #[test]
5769 fn test_mcp_server_http_serialization() {
5770 let server = McpServer::Http(
5771 McpServerHttp::new("http-server", "https://api.example.com").headers(vec![
5772 HttpHeader::new("Authorization", "Bearer token123"),
5773 HttpHeader::new("Content-Type", "application/json"),
5774 ]),
5775 );
5776
5777 let json = serde_json::to_value(&server).unwrap();
5778 assert_eq!(
5779 json,
5780 json!({
5781 "type": "http",
5782 "name": "http-server",
5783 "url": "https://api.example.com",
5784 "headers": [
5785 {
5786 "name": "Authorization",
5787 "value": "Bearer token123"
5788 },
5789 {
5790 "name": "Content-Type",
5791 "value": "application/json"
5792 }
5793 ]
5794 })
5795 );
5796
5797 let deserialized: McpServer = serde_json::from_value(json).unwrap();
5798 match deserialized {
5799 McpServer::Http(McpServerHttp {
5800 name,
5801 url,
5802 headers,
5803 meta: _,
5804 }) => {
5805 assert_eq!(name, "http-server");
5806 assert_eq!(url, "https://api.example.com");
5807 assert_eq!(headers.len(), 2);
5808 assert_eq!(headers[0].name, "Authorization");
5809 assert_eq!(headers[0].value, "Bearer token123");
5810 assert_eq!(headers[1].name, "Content-Type");
5811 assert_eq!(headers[1].value, "application/json");
5812 }
5813 _ => panic!("Expected Http variant"),
5814 }
5815 }
5816
5817 #[test]
5818 fn mcp_server_http_schema_marks_url_as_uri() {
5819 let schema = serde_json::to_value(schemars::schema_for!(McpServerHttp)).unwrap();
5820
5821 assert_eq!(schema["properties"]["url"]["format"], "uri");
5822 }
5823
5824 #[cfg(feature = "unstable_mcp_over_acp")]
5825 #[test]
5826 fn test_client_mcp_message_method_names() {
5827 assert_eq!(AGENT_METHOD_NAMES.mcp_message, "mcp/message");
5828
5829 assert_eq!(
5830 ClientRequest::MessageMcpRequest(Box::new(MessageMcpRequest::new(
5831 "conn-1",
5832 "tools/list"
5833 )))
5834 .method(),
5835 "mcp/message"
5836 );
5837 assert_eq!(
5838 ClientNotification::MessageMcpNotification(Box::new(MessageMcpNotification::new(
5839 "conn-1",
5840 "notifications/progress"
5841 )))
5842 .method(),
5843 "mcp/message"
5844 );
5845 }
5846
5847 #[test]
5848 fn test_auth_method_names() {
5849 assert_eq!(AGENT_METHOD_NAMES.auth_login, "auth/login");
5850 assert_eq!(AGENT_METHOD_NAMES.auth_logout, "auth/logout");
5851
5852 assert_eq!(
5853 ClientRequest::LoginAuthRequest(Box::new(LoginAuthRequest::new("agent-login")))
5854 .method(),
5855 "auth/login"
5856 );
5857 assert_eq!(
5858 ClientRequest::LogoutAuthRequest(Box::new(LogoutAuthRequest::new())).method(),
5859 "auth/logout"
5860 );
5861 }
5862
5863 #[test]
5864 fn test_session_config_option_category_known_variants() {
5865 assert_eq!(
5867 serde_json::to_value(&SessionConfigOptionCategory::Mode).unwrap(),
5868 json!("mode")
5869 );
5870 assert_eq!(
5871 serde_json::to_value(&SessionConfigOptionCategory::Model).unwrap(),
5872 json!("model")
5873 );
5874 assert_eq!(
5875 serde_json::to_value(&SessionConfigOptionCategory::ModelConfig).unwrap(),
5876 json!("model_config")
5877 );
5878 assert_eq!(
5879 serde_json::to_value(&SessionConfigOptionCategory::ThoughtLevel).unwrap(),
5880 json!("thought_level")
5881 );
5882
5883 assert_eq!(
5885 serde_json::from_str::<SessionConfigOptionCategory>("\"mode\"").unwrap(),
5886 SessionConfigOptionCategory::Mode
5887 );
5888 assert_eq!(
5889 serde_json::from_str::<SessionConfigOptionCategory>("\"model\"").unwrap(),
5890 SessionConfigOptionCategory::Model
5891 );
5892 assert_eq!(
5893 serde_json::from_str::<SessionConfigOptionCategory>("\"model_config\"").unwrap(),
5894 SessionConfigOptionCategory::ModelConfig
5895 );
5896 assert_eq!(
5897 serde_json::from_str::<SessionConfigOptionCategory>("\"thought_level\"").unwrap(),
5898 SessionConfigOptionCategory::ThoughtLevel
5899 );
5900 }
5901
5902 #[test]
5903 fn test_session_config_option_category_unknown_variants() {
5904 let unknown: SessionConfigOptionCategory =
5906 serde_json::from_str("\"some_future_category\"").unwrap();
5907 assert_eq!(
5908 unknown,
5909 SessionConfigOptionCategory::Other("some_future_category".to_string())
5910 );
5911
5912 let json = serde_json::to_value(&unknown).unwrap();
5914 assert_eq!(json, json!("some_future_category"));
5915 }
5916
5917 #[test]
5918 fn test_session_config_option_category_custom_categories() {
5919 let custom: SessionConfigOptionCategory =
5921 serde_json::from_str("\"_my_custom_category\"").unwrap();
5922 assert_eq!(
5923 custom,
5924 SessionConfigOptionCategory::Other("_my_custom_category".to_string())
5925 );
5926
5927 let json = serde_json::to_value(&custom).unwrap();
5929 assert_eq!(json, json!("_my_custom_category"));
5930
5931 let deserialized: SessionConfigOptionCategory = serde_json::from_value(json).unwrap();
5933 assert_eq!(
5934 deserialized,
5935 SessionConfigOptionCategory::Other("_my_custom_category".to_string()),
5936 );
5937 }
5938
5939 fn test_config_option() -> SessionConfigOption {
5940 SessionConfigOption::select(
5941 "mode",
5942 "Mode",
5943 "ask",
5944 vec![SessionConfigSelectOption::new("ask", "Ask")],
5945 )
5946 }
5947
5948 #[test]
5949 fn test_session_response_config_options_default_empty_and_skip_serializing() {
5950 assert_eq!(
5951 serde_json::to_value(NewSessionResponse::new("sess")).unwrap(),
5952 json!({ "sessionId": "sess" })
5953 );
5954 assert_eq!(
5955 serde_json::to_value(ResumeSessionResponse::new()).unwrap(),
5956 json!({})
5957 );
5958 #[cfg(feature = "unstable_session_fork")]
5959 assert_eq!(
5960 serde_json::to_value(ForkSessionResponse::new("fork")).unwrap(),
5961 json!({ "sessionId": "fork" })
5962 );
5963
5964 let json = serde_json::to_value(
5965 NewSessionResponse::new("sess").config_options(vec![test_config_option()]),
5966 )
5967 .unwrap();
5968 assert_eq!(json["configOptions"].as_array().unwrap().len(), 1);
5969 }
5970
5971 #[test]
5972 fn test_session_response_config_options_deserialize_missing_null_and_invalid() {
5973 let missing: NewSessionResponse =
5974 serde_json::from_value(json!({ "sessionId": "sess" })).unwrap();
5975 assert!(missing.config_options.is_empty());
5976
5977 let null: NewSessionResponse = serde_json::from_value(json!({
5978 "sessionId": "sess",
5979 "configOptions": null
5980 }))
5981 .unwrap();
5982 assert!(null.config_options.is_empty());
5983
5984 let wrong_shape: NewSessionResponse = serde_json::from_value(json!({
5985 "sessionId": "sess",
5986 "configOptions": "oops"
5987 }))
5988 .unwrap();
5989 assert!(wrong_shape.config_options.is_empty());
5990
5991 let valid_option = serde_json::to_value(test_config_option()).unwrap();
5992 let mixed: NewSessionResponse = serde_json::from_value(json!({
5993 "sessionId": "sess",
5994 "configOptions": ["oops", valid_option]
5995 }))
5996 .unwrap();
5997 assert_eq!(mixed.config_options.len(), 1);
5998
5999 let resume: ResumeSessionResponse = serde_json::from_value(json!({})).unwrap();
6000 assert!(resume.config_options.is_empty());
6001 #[cfg(feature = "unstable_session_fork")]
6002 {
6003 let fork: ForkSessionResponse =
6004 serde_json::from_value(json!({ "sessionId": "fork" })).unwrap();
6005 assert!(fork.config_options.is_empty());
6006 }
6007 }
6008
6009 #[test]
6010 fn test_resume_session_replay_from_serialization() {
6011 assert_eq!(
6012 serde_json::to_value(ResumeSessionRequest::new(
6013 "sess_abc123",
6014 "/home/user/project"
6015 ))
6016 .unwrap(),
6017 json!({
6018 "sessionId": "sess_abc123",
6019 "cwd": "/home/user/project"
6020 })
6021 );
6022 assert_eq!(
6023 serde_json::to_value(
6024 ResumeSessionRequest::new("sess_abc123", "/home/user/project")
6025 .replay_from(ReplayFrom::from(ReplayFromStart::new()))
6026 )
6027 .unwrap(),
6028 json!({
6029 "sessionId": "sess_abc123",
6030 "cwd": "/home/user/project",
6031 "replayFrom": {
6032 "type": "start"
6033 }
6034 })
6035 );
6036
6037 let replay: ResumeSessionRequest = serde_json::from_value(json!({
6038 "sessionId": "sess_abc123",
6039 "cwd": "/home/user/project",
6040 "replayFrom": {
6041 "type": "start"
6042 }
6043 }))
6044 .unwrap();
6045 assert!(matches!(replay.replay_from, Some(ReplayFrom::Start(_))));
6046
6047 let none: ResumeSessionRequest = serde_json::from_value(json!({
6048 "sessionId": "sess_abc123",
6049 "cwd": "/home/user/project",
6050 "replayFrom": null
6051 }))
6052 .unwrap();
6053 assert!(none.replay_from.is_none());
6054 }
6055
6056 #[test]
6057 fn test_auth_method_agent_serialization() {
6058 let method = AuthMethod::Agent(AuthMethodAgent::new("default-auth", "Default Auth"));
6059
6060 let json = serde_json::to_value(&method).unwrap();
6061 assert_eq!(
6062 json,
6063 json!({
6064 "methodId": "default-auth",
6065 "name": "Default Auth",
6066 "type": "agent"
6067 })
6068 );
6069 assert!(!json.as_object().unwrap().contains_key("description"));
6071
6072 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
6073 match deserialized {
6074 AuthMethod::Agent(AuthMethodAgent {
6075 method_id, name, ..
6076 }) => {
6077 assert_eq!(method_id.0.as_ref(), "default-auth");
6078 assert_eq!(name, "Default Auth");
6079 }
6080 _ => panic!("Expected Agent variant"),
6081 }
6082 }
6083
6084 #[test]
6085 fn test_auth_method_agent_deserialization() {
6086 let json = json!({
6087 "methodId": "agent-auth",
6088 "name": "Agent Auth",
6089 "type": "agent"
6090 });
6091
6092 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
6093 assert!(matches!(deserialized, AuthMethod::Agent(_)));
6094 }
6095
6096 #[test]
6097 fn test_auth_method_agent_requires_type() {
6098 assert!(
6099 serde_json::from_value::<AuthMethod>(json!({
6100 "methodId": "agent-auth",
6101 "name": "Agent Auth"
6102 }))
6103 .is_err()
6104 );
6105 }
6106
6107 #[test]
6108 fn test_auth_method_agent_rejects_null_type() {
6109 assert!(
6110 serde_json::from_value::<AuthMethod>(json!({
6111 "methodId": "agent-auth",
6112 "name": "Agent Auth",
6113 "type": null
6114 }))
6115 .is_err()
6116 );
6117 }
6118
6119 #[test]
6120 fn test_auth_method_unknown_does_not_hide_malformed_agent() {
6121 assert!(
6122 serde_json::from_value::<AuthMethod>(json!({
6123 "methodId": "agent-auth",
6124 "type": "agent"
6125 }))
6126 .is_err()
6127 );
6128 }
6129
6130 #[test]
6131 fn test_auth_method_unknown_variant_roundtrip() {
6132 let method: AuthMethod = serde_json::from_value(json!({
6133 "methodId": "oauth",
6134 "name": "OAuth",
6135 "type": "_oauth",
6136 "authorizationUrl": "https://example.com/auth"
6137 }))
6138 .unwrap();
6139
6140 assert_eq!(method.method_id().0.as_ref(), "oauth");
6141 assert_eq!(method.name(), "OAuth");
6142 let AuthMethod::Other(unknown) = method else {
6143 panic!("expected unknown auth method");
6144 };
6145 assert_eq!(unknown.type_, "_oauth");
6146 assert_eq!(
6147 unknown.fields.get("authorizationUrl"),
6148 Some(&json!("https://example.com/auth"))
6149 );
6150
6151 assert_eq!(
6152 serde_json::to_value(AuthMethod::Other(unknown)).unwrap(),
6153 json!({
6154 "methodId": "oauth",
6155 "name": "OAuth",
6156 "type": "_oauth",
6157 "authorizationUrl": "https://example.com/auth"
6158 })
6159 );
6160 }
6161
6162 #[cfg(feature = "unstable_auth_methods")]
6163 #[test]
6164 fn test_auth_method_unknown_does_not_hide_malformed_known_variant() {
6165 assert!(
6166 serde_json::from_value::<AuthMethod>(json!({
6167 "methodId": "api-key",
6168 "name": "API Key",
6169 "type": "env_var"
6170 }))
6171 .is_err()
6172 );
6173 }
6174
6175 #[test]
6176 fn test_session_delete_serialization() {
6177 assert_eq!(AGENT_METHOD_NAMES.session_delete, "session/delete");
6178 assert_eq!(
6179 ClientRequest::DeleteSessionRequest(Box::new(DeleteSessionRequest::new("sess_abc123")))
6180 .method(),
6181 "session/delete"
6182 );
6183 assert_eq!(
6184 serde_json::to_value(DeleteSessionRequest::new("sess_abc123")).unwrap(),
6185 json!({
6186 "sessionId": "sess_abc123"
6187 })
6188 );
6189 assert_eq!(
6190 serde_json::to_value(DeleteSessionResponse::new()).unwrap(),
6191 json!({})
6192 );
6193 assert_eq!(
6194 serde_json::to_value(
6195 SessionCapabilities::new().delete(SessionDeleteCapabilities::new())
6196 )
6197 .unwrap(),
6198 json!({
6199 "delete": {}
6200 })
6201 );
6202 }
6203 #[test]
6204 fn test_session_additional_directories_serialization() {
6205 assert_eq!(
6206 serde_json::to_value(NewSessionRequest::new("/home/user/project")).unwrap(),
6207 json!({
6208 "cwd": "/home/user/project",
6209 })
6210 );
6211 assert_eq!(
6212 serde_json::to_value(
6213 NewSessionRequest::new("/home/user/project").additional_directories(vec![
6214 PathBuf::from("/home/user/shared-lib"),
6215 PathBuf::from("/home/user/product-docs"),
6216 ])
6217 )
6218 .unwrap(),
6219 json!({
6220 "cwd": "/home/user/project",
6221 "additionalDirectories": [
6222 "/home/user/shared-lib",
6223 "/home/user/product-docs"
6224 ],
6225 })
6226 );
6227 assert_eq!(
6228 serde_json::to_value(ResumeSessionRequest::new(
6229 "sess_abc123",
6230 "/home/user/project"
6231 ))
6232 .unwrap(),
6233 json!({
6234 "sessionId": "sess_abc123",
6235 "cwd": "/home/user/project",
6236 })
6237 );
6238 assert_eq!(
6239 serde_json::from_value::<ResumeSessionRequest>(json!({
6240 "sessionId": "sess_abc123",
6241 "cwd": "/home/user/project"
6242 }))
6243 .unwrap()
6244 .mcp_servers,
6245 Vec::<McpServer>::new()
6246 );
6247 assert_eq!(
6248 serde_json::from_value::<ResumeSessionRequest>(json!({
6249 "sessionId": "sess_abc123",
6250 "cwd": "/home/user/project",
6251 "mcpServers": null
6252 }))
6253 .unwrap()
6254 .mcp_servers,
6255 Vec::<McpServer>::new()
6256 );
6257 assert_eq!(
6258 serde_json::to_value(SessionInfo::new("sess_abc123", "/home/user/project")).unwrap(),
6259 json!({
6260 "sessionId": "sess_abc123",
6261 "cwd": "/home/user/project"
6262 })
6263 );
6264 assert_eq!(
6265 serde_json::to_value(
6266 SessionInfo::new("sess_abc123", "/home/user/project").additional_directories(vec![
6267 PathBuf::from("/home/user/shared-lib"),
6268 PathBuf::from("/home/user/product-docs"),
6269 ])
6270 )
6271 .unwrap(),
6272 json!({
6273 "sessionId": "sess_abc123",
6274 "cwd": "/home/user/project",
6275 "additionalDirectories": [
6276 "/home/user/shared-lib",
6277 "/home/user/product-docs"
6278 ]
6279 })
6280 );
6281 assert_eq!(
6282 serde_json::from_value::<SessionInfo>(json!({
6283 "sessionId": "sess_abc123",
6284 "cwd": "/home/user/project"
6285 }))
6286 .unwrap()
6287 .additional_directories,
6288 Vec::<AbsolutePath>::new()
6289 );
6290 }
6291 #[test]
6292 fn test_session_additional_directories_capabilities_serialization() {
6293 assert_eq!(
6294 serde_json::to_value(
6295 SessionCapabilities::new()
6296 .additional_directories(SessionAdditionalDirectoriesCapabilities::new())
6297 )
6298 .unwrap(),
6299 json!({
6300 "additionalDirectories": {}
6301 })
6302 );
6303 }
6304
6305 #[cfg(feature = "unstable_auth_methods")]
6306 #[test]
6307 fn test_auth_method_env_var_serialization() {
6308 let method = AuthMethod::EnvVar(AuthMethodEnvVar::new(
6309 "api-key",
6310 "API Key",
6311 vec![AuthEnvVar::new("API_KEY")],
6312 ));
6313
6314 let json = serde_json::to_value(&method).unwrap();
6315 assert_eq!(
6316 json,
6317 json!({
6318 "methodId": "api-key",
6319 "name": "API Key",
6320 "type": "env_var",
6321 "vars": [{"name": "API_KEY"}]
6322 })
6323 );
6324 assert!(!json["vars"][0].as_object().unwrap().contains_key("secret"));
6326 assert!(
6327 !json["vars"][0]
6328 .as_object()
6329 .unwrap()
6330 .contains_key("optional")
6331 );
6332
6333 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
6334 match deserialized {
6335 AuthMethod::EnvVar(AuthMethodEnvVar {
6336 method_id,
6337 name: method_name,
6338 vars,
6339 link,
6340 ..
6341 }) => {
6342 assert_eq!(method_id.0.as_ref(), "api-key");
6343 assert_eq!(method_name, "API Key");
6344 assert_eq!(vars.len(), 1);
6345 assert_eq!(vars[0].name, "API_KEY");
6346 assert!(vars[0].secret);
6347 assert!(!vars[0].optional);
6348 assert!(link.is_none());
6349 }
6350 _ => panic!("Expected EnvVar variant"),
6351 }
6352 }
6353
6354 #[cfg(feature = "unstable_auth_methods")]
6355 #[test]
6356 fn test_auth_method_env_var_with_link_serialization() {
6357 let method = AuthMethod::EnvVar(
6358 AuthMethodEnvVar::new("api-key", "API Key", vec![AuthEnvVar::new("API_KEY")])
6359 .link("https://example.com/keys"),
6360 );
6361
6362 let json = serde_json::to_value(&method).unwrap();
6363 assert_eq!(
6364 json,
6365 json!({
6366 "methodId": "api-key",
6367 "name": "API Key",
6368 "type": "env_var",
6369 "vars": [{"name": "API_KEY"}],
6370 "link": "https://example.com/keys"
6371 })
6372 );
6373
6374 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
6375 match deserialized {
6376 AuthMethod::EnvVar(AuthMethodEnvVar { link, .. }) => {
6377 assert_eq!(link.as_deref(), Some("https://example.com/keys"));
6378 }
6379 _ => panic!("Expected EnvVar variant"),
6380 }
6381 }
6382
6383 #[cfg(feature = "unstable_auth_methods")]
6384 #[test]
6385 fn test_auth_method_env_var_multiple_vars() {
6386 let method = AuthMethod::EnvVar(AuthMethodEnvVar::new(
6387 "azure-openai",
6388 "Azure OpenAI",
6389 vec![
6390 AuthEnvVar::new("AZURE_OPENAI_API_KEY").label("API Key"),
6391 AuthEnvVar::new("AZURE_OPENAI_ENDPOINT")
6392 .label("Endpoint URL")
6393 .secret(false),
6394 AuthEnvVar::new("AZURE_OPENAI_API_VERSION")
6395 .label("API Version")
6396 .secret(false)
6397 .optional(true),
6398 ],
6399 ));
6400
6401 let json = serde_json::to_value(&method).unwrap();
6402 assert_eq!(
6403 json,
6404 json!({
6405 "methodId": "azure-openai",
6406 "name": "Azure OpenAI",
6407 "type": "env_var",
6408 "vars": [
6409 {"name": "AZURE_OPENAI_API_KEY", "label": "API Key"},
6410 {"name": "AZURE_OPENAI_ENDPOINT", "label": "Endpoint URL", "secret": false},
6411 {"name": "AZURE_OPENAI_API_VERSION", "label": "API Version", "secret": false, "optional": true}
6412 ]
6413 })
6414 );
6415
6416 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
6417 match deserialized {
6418 AuthMethod::EnvVar(AuthMethodEnvVar { vars, .. }) => {
6419 assert_eq!(vars.len(), 3);
6420 assert_eq!(vars[0].name, "AZURE_OPENAI_API_KEY");
6422 assert_eq!(vars[0].label.as_deref(), Some("API Key"));
6423 assert!(vars[0].secret);
6424 assert!(!vars[0].optional);
6425 assert_eq!(vars[1].name, "AZURE_OPENAI_ENDPOINT");
6427 assert!(!vars[1].secret);
6428 assert!(!vars[1].optional);
6429 assert_eq!(vars[2].name, "AZURE_OPENAI_API_VERSION");
6431 assert!(!vars[2].secret);
6432 assert!(vars[2].optional);
6433 }
6434 _ => panic!("Expected EnvVar variant"),
6435 }
6436 }
6437
6438 #[cfg(feature = "unstable_auth_methods")]
6439 #[test]
6440 fn test_auth_method_terminal_serialization() {
6441 let method = AuthMethod::Terminal(AuthMethodTerminal::new("tui-auth", "Terminal Auth"));
6442
6443 let json = serde_json::to_value(&method).unwrap();
6444 assert_eq!(
6445 json,
6446 json!({
6447 "methodId": "tui-auth",
6448 "name": "Terminal Auth",
6449 "type": "terminal"
6450 })
6451 );
6452 assert!(!json.as_object().unwrap().contains_key("args"));
6454 assert!(!json.as_object().unwrap().contains_key("env"));
6455
6456 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
6457 match deserialized {
6458 AuthMethod::Terminal(AuthMethodTerminal { args, env, .. }) => {
6459 assert!(args.is_empty());
6460 assert!(env.is_empty());
6461 }
6462 _ => panic!("Expected Terminal variant"),
6463 }
6464 }
6465
6466 #[cfg(feature = "unstable_auth_methods")]
6467 #[test]
6468 fn test_auth_method_terminal_with_args_and_env_serialization() {
6469 let method = AuthMethod::Terminal(
6470 AuthMethodTerminal::new("tui-auth", "Terminal Auth")
6471 .args(vec!["--interactive".to_string(), "--color".to_string()])
6472 .env(vec![EnvVariable::new("TERM", "xterm-256color")]),
6473 );
6474
6475 let json = serde_json::to_value(&method).unwrap();
6476 assert_eq!(
6477 json,
6478 json!({
6479 "methodId": "tui-auth",
6480 "name": "Terminal Auth",
6481 "type": "terminal",
6482 "args": ["--interactive", "--color"],
6483 "env": [
6484 {
6485 "name": "TERM",
6486 "value": "xterm-256color"
6487 }
6488 ]
6489 })
6490 );
6491
6492 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
6493 match deserialized {
6494 AuthMethod::Terminal(AuthMethodTerminal { args, env, .. }) => {
6495 assert_eq!(args, vec!["--interactive", "--color"]);
6496 assert_eq!(env.len(), 1);
6497 assert_eq!(env[0].name, "TERM");
6498 assert_eq!(env[0].value, "xterm-256color");
6499 }
6500 _ => panic!("Expected Terminal variant"),
6501 }
6502 }
6503
6504 #[test]
6505 fn test_session_config_option_id_serialize() {
6506 let val = SessionConfigOptionValue::id("model-1");
6507 let json = serde_json::to_value(&val).unwrap();
6508 assert_eq!(json, json!({ "type": "id", "value": "model-1" }));
6509 }
6510
6511 #[test]
6512 fn test_session_config_option_value_boolean_serialize() {
6513 let val = SessionConfigOptionValue::boolean(true);
6514 let json = serde_json::to_value(&val).unwrap();
6515 assert_eq!(json, json!({ "type": "boolean", "value": true }));
6516 }
6517
6518 #[test]
6519 fn test_session_config_option_value_deserialize_id() {
6520 let json = json!({ "type": "id", "value": "model-1" });
6521 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6522 assert_eq!(val, SessionConfigOptionValue::id("model-1"));
6523 assert_eq!(val.as_id().unwrap().to_string(), "model-1");
6524 }
6525
6526 #[test]
6527 fn test_session_config_option_value_deserialize_requires_type() {
6528 let json = json!({ "value": "model-1" });
6529 let result = serde_json::from_value::<SessionConfigOptionValue>(json);
6530 assert!(result.is_err());
6531 }
6532
6533 #[test]
6534 fn test_session_config_option_value_deserialize_boolean() {
6535 let json = json!({ "type": "boolean", "value": true });
6536 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6537 assert_eq!(val, SessionConfigOptionValue::boolean(true));
6538 assert_eq!(val.as_bool(), Some(true));
6539 }
6540
6541 #[test]
6542 fn test_session_config_option_value_deserialize_boolean_false() {
6543 let json = json!({ "type": "boolean", "value": false });
6544 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6545 assert_eq!(val, SessionConfigOptionValue::boolean(false));
6546 assert_eq!(val.as_bool(), Some(false));
6547 }
6548
6549 #[test]
6550 fn test_session_config_option_value_deserialize_unknown_type_with_string_value() {
6551 let json = json!({
6552 "type": "text",
6553 "value": "freeform input",
6554 "maxLength": 200
6555 });
6556 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6557 let SessionConfigOptionValue::Other(unknown) = val else {
6558 panic!("Expected Other variant");
6559 };
6560 assert_eq!(unknown.type_, "text");
6561 assert_eq!(unknown.value, json!("freeform input"));
6562 assert_eq!(unknown.fields["maxLength"], json!(200));
6563 }
6564
6565 #[test]
6566 fn test_session_config_option_value_deserialize_unknown_type_with_object_value() {
6567 let json = json!({
6568 "type": "range",
6569 "value": { "min": 1, "max": 5 }
6570 });
6571 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6572 let SessionConfigOptionValue::Other(unknown) = val else {
6573 panic!("Expected Other variant");
6574 };
6575 assert_eq!(unknown.type_, "range");
6576 assert_eq!(unknown.value, json!({ "min": 1, "max": 5 }));
6577 }
6578
6579 #[test]
6580 fn test_session_config_option_value_roundtrip_id() {
6581 let original = SessionConfigOptionValue::id("option-a");
6582 let json = serde_json::to_value(&original).unwrap();
6583 let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6584 assert_eq!(original, roundtripped);
6585 }
6586
6587 #[test]
6588 fn test_session_config_option_value_roundtrip_boolean() {
6589 let original = SessionConfigOptionValue::boolean(false);
6590 let json = serde_json::to_value(&original).unwrap();
6591 let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6592 assert_eq!(original, roundtripped);
6593 }
6594
6595 #[test]
6596 fn test_session_config_option_value_roundtrip_other() {
6597 let mut fields = BTreeMap::new();
6598 fields.insert("maxLength".to_string(), json!(200));
6599 let original = SessionConfigOptionValue::Other(OtherSessionConfigOptionValue::new(
6600 "text",
6601 json!("freeform input"),
6602 fields,
6603 ));
6604 let json = serde_json::to_value(&original).unwrap();
6605 let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6606 assert_eq!(original, roundtripped);
6607 }
6608
6609 #[test]
6610 fn test_session_config_option_value_type_mismatch_boolean_with_string() {
6611 let json = json!({ "type": "boolean", "value": "not a bool" });
6612 let result = serde_json::from_value::<SessionConfigOptionValue>(json);
6613 assert!(result.is_err());
6614 }
6615
6616 #[test]
6617 fn test_session_config_option_value_from_impls() {
6618 let from_str: SessionConfigOptionValue = "model-1".into();
6619 assert_eq!(from_str.as_id().unwrap().to_string(), "model-1");
6620
6621 let from_id: SessionConfigOptionValue = SessionConfigValueId::new("model-2").into();
6622 assert_eq!(from_id.as_id().unwrap().to_string(), "model-2");
6623
6624 let from_bool: SessionConfigOptionValue = true.into();
6625 assert_eq!(from_bool.as_bool(), Some(true));
6626 }
6627
6628 #[test]
6629 fn test_set_session_config_option_request_id() {
6630 let req = SetSessionConfigOptionRequest::new("sess_1", "model", "model-1");
6631 let json = serde_json::to_value(&req).unwrap();
6632 assert_eq!(
6633 json,
6634 json!({
6635 "sessionId": "sess_1",
6636 "configId": "model",
6637 "type": "id",
6638 "value": "model-1"
6639 })
6640 );
6641 }
6642
6643 #[test]
6644 fn test_set_session_config_option_request_boolean() {
6645 let req = SetSessionConfigOptionRequest::new("sess_1", "brave_mode", true);
6646 let json = serde_json::to_value(&req).unwrap();
6647 assert_eq!(
6648 json,
6649 json!({
6650 "sessionId": "sess_1",
6651 "configId": "brave_mode",
6652 "type": "boolean",
6653 "value": true
6654 })
6655 );
6656 }
6657
6658 #[test]
6659 fn test_set_session_config_option_request_deserialize_requires_type() {
6660 let json = json!({
6661 "sessionId": "sess_1",
6662 "configId": "model",
6663 "value": "model-1"
6664 });
6665 let result = serde_json::from_value::<SetSessionConfigOptionRequest>(json);
6666 assert!(result.is_err());
6667 }
6668
6669 #[test]
6670 fn test_set_session_config_option_request_deserialize_boolean() {
6671 let json = json!({
6672 "sessionId": "sess_1",
6673 "configId": "brave_mode",
6674 "type": "boolean",
6675 "value": true
6676 });
6677 let req: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
6678 assert_eq!(req.value.as_bool(), Some(true));
6679 }
6680
6681 #[test]
6682 fn test_set_session_config_option_request_roundtrip_id() {
6683 let original = SetSessionConfigOptionRequest::new("s", "c", "v");
6684 let json = serde_json::to_value(&original).unwrap();
6685 let roundtripped: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
6686 assert_eq!(original, roundtripped);
6687 }
6688
6689 #[test]
6690 fn test_set_session_config_option_request_roundtrip_boolean() {
6691 let original = SetSessionConfigOptionRequest::new("s", "c", false);
6692 let json = serde_json::to_value(&original).unwrap();
6693 let roundtripped: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
6694 assert_eq!(original, roundtripped);
6695 }
6696
6697 #[test]
6698 fn test_session_config_boolean_serialization() {
6699 let cfg = SessionConfigBoolean::new(true);
6700 let json = serde_json::to_value(&cfg).unwrap();
6701 assert_eq!(json, json!({ "currentValue": true }));
6702
6703 let deserialized: SessionConfigBoolean = serde_json::from_value(json).unwrap();
6704 assert!(deserialized.current_value);
6705 }
6706
6707 #[test]
6708 fn test_session_config_option_boolean_variant() {
6709 let opt = SessionConfigOption::boolean("brave_mode", "Brave Mode", false)
6710 .description("Skip confirmation prompts")
6711 .meta(test_meta());
6712 assert_eq!(serialized_meta_key_count(&opt), 1);
6713
6714 let json = serde_json::to_value(&opt).unwrap();
6715 assert_eq!(
6716 json,
6717 json!({
6718 "configId": "brave_mode",
6719 "name": "Brave Mode",
6720 "description": "Skip confirmation prompts",
6721 "type": "boolean",
6722 "currentValue": false,
6723 "_meta": {
6724 "source": "test"
6725 }
6726 })
6727 );
6728
6729 let deserialized: SessionConfigOption = serde_json::from_value(json).unwrap();
6730 assert_eq!(deserialized.config_id.to_string(), "brave_mode");
6731 assert_eq!(deserialized.name, "Brave Mode");
6732 match deserialized.kind {
6733 SessionConfigKind::Boolean(ref b) => assert!(!b.current_value),
6734 _ => panic!("Expected Boolean kind"),
6735 }
6736 }
6737
6738 #[test]
6739 fn test_session_config_option_select_still_works() {
6740 let opt = SessionConfigOption::select(
6742 "model",
6743 "Model",
6744 "model-1",
6745 vec![
6746 SessionConfigSelectOption::new("model-1", "Model 1"),
6747 SessionConfigSelectOption::new("model-2", "Model 2"),
6748 ],
6749 )
6750 .meta(test_meta());
6751 assert_eq!(serialized_meta_key_count(&opt), 1);
6752
6753 let json = serde_json::to_value(&opt).unwrap();
6754 assert_eq!(json["type"], "select");
6755 assert_eq!(json["currentValue"], "model-1");
6756 assert_eq!(json["options"].as_array().unwrap().len(), 2);
6757 assert_eq!(json["_meta"]["source"], "test");
6758
6759 let deserialized: SessionConfigOption = serde_json::from_value(json).unwrap();
6760 match deserialized.kind {
6761 SessionConfigKind::Select(ref s) => {
6762 assert_eq!(s.current_value.to_string(), "model-1");
6763 }
6764 _ => panic!("Expected Select kind"),
6765 }
6766 }
6767
6768 #[test]
6769 fn test_session_config_option_unknown_kind_roundtrip() {
6770 let option: SessionConfigOption = serde_json::from_value(json!({
6771 "configId": "verbosity",
6772 "name": "Verbosity",
6773 "type": "_slider",
6774 "currentValue": 3,
6775 "min": 0,
6776 "max": 5,
6777 "_meta": {
6778 "source": "test"
6779 }
6780 }))
6781 .unwrap();
6782
6783 assert_eq!(option.config_id.to_string(), "verbosity");
6784 assert_eq!(option.meta.as_ref().unwrap()["source"], "test");
6785 let SessionConfigKind::Other(unknown) = &option.kind else {
6786 panic!("expected unknown config kind");
6787 };
6788 assert_eq!(unknown.type_, "_slider");
6789 assert_eq!(unknown.fields.get("currentValue"), Some(&json!(3)));
6790 assert!(!unknown.fields.contains_key("_meta"));
6791 assert_eq!(serialized_meta_key_count(&option), 1);
6792
6793 let json = serde_json::to_value(&option).unwrap();
6794 assert_eq!(json["type"], "_slider");
6795 assert_eq!(json["currentValue"], 3);
6796 assert_eq!(json["min"], 0);
6797 assert_eq!(json["max"], 5);
6798 assert_eq!(json["_meta"]["source"], "test");
6799 }
6800
6801 #[test]
6802 fn test_session_config_option_unknown_kind_does_not_duplicate_flattened_meta() {
6803 let mut fields = std::collections::BTreeMap::new();
6804 fields.insert("currentValue".to_string(), json!(3));
6805 fields.insert("_meta".to_string(), json!({ "inner": "ignored" }));
6806
6807 let option = SessionConfigOption::new(
6808 "verbosity",
6809 "Verbosity",
6810 SessionConfigKind::Other(OtherSessionConfigKind::new("_slider", fields)),
6811 )
6812 .meta(test_meta());
6813
6814 let SessionConfigKind::Other(unknown) = &option.kind else {
6815 panic!("expected unknown config kind");
6816 };
6817 assert!(!unknown.fields.contains_key("_meta"));
6818 assert_eq!(serialized_meta_key_count(&option), 1);
6819
6820 let json = serde_json::to_value(&option).unwrap();
6821 assert_eq!(json["type"], "_slider");
6822 assert_eq!(json["currentValue"], 3);
6823 assert_eq!(json["_meta"]["source"], "test");
6824 }
6825
6826 #[test]
6827 fn test_session_config_option_unknown_does_not_hide_malformed_known_kind() {
6828 assert!(
6829 serde_json::from_value::<SessionConfigOption>(json!({
6830 "configId": "model",
6831 "name": "Model",
6832 "type": "select"
6833 }))
6834 .is_err()
6835 );
6836 }
6837
6838 #[cfg(feature = "unstable_llm_providers")]
6839 #[test]
6840 fn test_llm_protocol_known_variants() {
6841 assert_eq!(
6842 serde_json::to_value(&LlmProtocol::Anthropic).unwrap(),
6843 json!("anthropic")
6844 );
6845 assert_eq!(
6846 serde_json::to_value(&LlmProtocol::OpenAi).unwrap(),
6847 json!("openai")
6848 );
6849 assert_eq!(
6850 serde_json::to_value(&LlmProtocol::Azure).unwrap(),
6851 json!("azure")
6852 );
6853 assert_eq!(
6854 serde_json::to_value(&LlmProtocol::Vertex).unwrap(),
6855 json!("vertex")
6856 );
6857 assert_eq!(
6858 serde_json::to_value(&LlmProtocol::Bedrock).unwrap(),
6859 json!("bedrock")
6860 );
6861
6862 assert_eq!(
6863 serde_json::from_str::<LlmProtocol>("\"anthropic\"").unwrap(),
6864 LlmProtocol::Anthropic
6865 );
6866 assert_eq!(
6867 serde_json::from_str::<LlmProtocol>("\"openai\"").unwrap(),
6868 LlmProtocol::OpenAi
6869 );
6870 assert_eq!(
6871 serde_json::from_str::<LlmProtocol>("\"azure\"").unwrap(),
6872 LlmProtocol::Azure
6873 );
6874 assert_eq!(
6875 serde_json::from_str::<LlmProtocol>("\"vertex\"").unwrap(),
6876 LlmProtocol::Vertex
6877 );
6878 assert_eq!(
6879 serde_json::from_str::<LlmProtocol>("\"bedrock\"").unwrap(),
6880 LlmProtocol::Bedrock
6881 );
6882 }
6883
6884 #[cfg(feature = "unstable_llm_providers")]
6885 #[test]
6886 fn test_llm_protocol_unknown_variant() {
6887 let unknown: LlmProtocol = serde_json::from_str("\"cohere\"").unwrap();
6888 assert_eq!(unknown, LlmProtocol::Other("cohere".to_string()));
6889
6890 let json = serde_json::to_value(&unknown).unwrap();
6891 assert_eq!(json, json!("cohere"));
6892 }
6893
6894 #[cfg(feature = "unstable_llm_providers")]
6895 #[test]
6896 fn test_provider_current_config_serialization() {
6897 let config =
6898 ProviderCurrentConfig::new(LlmProtocol::Anthropic, "https://api.anthropic.com");
6899
6900 let json = serde_json::to_value(&config).unwrap();
6901 assert_eq!(
6902 json,
6903 json!({
6904 "apiType": "anthropic",
6905 "baseUrl": "https://api.anthropic.com"
6906 })
6907 );
6908
6909 let deserialized: ProviderCurrentConfig = serde_json::from_value(json).unwrap();
6910 assert_eq!(deserialized.api_type, LlmProtocol::Anthropic);
6911 assert_eq!(deserialized.base_url, "https://api.anthropic.com");
6912 }
6913
6914 #[cfg(feature = "unstable_llm_providers")]
6915 #[test]
6916 fn test_provider_info_with_current_config() {
6917 let info = ProviderInfo::new(
6918 "main",
6919 vec![LlmProtocol::Anthropic, LlmProtocol::OpenAi],
6920 true,
6921 Some(ProviderCurrentConfig::new(
6922 LlmProtocol::Anthropic,
6923 "https://api.anthropic.com",
6924 )),
6925 );
6926
6927 let json = serde_json::to_value(&info).unwrap();
6928 assert_eq!(
6929 json,
6930 json!({
6931 "providerId": "main",
6932 "supported": ["anthropic", "openai"],
6933 "required": true,
6934 "current": {
6935 "apiType": "anthropic",
6936 "baseUrl": "https://api.anthropic.com"
6937 }
6938 })
6939 );
6940
6941 let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
6942 assert_eq!(deserialized.provider_id.to_string(), "main");
6943 assert_eq!(deserialized.supported.len(), 2);
6944 assert!(deserialized.required);
6945 assert!(deserialized.current.is_some());
6946 assert_eq!(
6947 deserialized.current.as_ref().unwrap().api_type,
6948 LlmProtocol::Anthropic
6949 );
6950 }
6951
6952 #[cfg(feature = "unstable_llm_providers")]
6953 #[test]
6954 fn test_provider_info_disabled() {
6955 let info = ProviderInfo::new(
6956 "secondary",
6957 vec![LlmProtocol::OpenAi],
6958 false,
6959 None::<ProviderCurrentConfig>,
6960 );
6961
6962 let json = serde_json::to_value(&info).unwrap();
6963 assert_eq!(
6964 json,
6965 json!({
6966 "providerId": "secondary",
6967 "supported": ["openai"],
6968 "required": false
6969 })
6970 );
6971
6972 let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
6973 assert_eq!(deserialized.provider_id.to_string(), "secondary");
6974 assert!(!deserialized.required);
6975 assert!(deserialized.current.is_none());
6976 }
6977
6978 #[cfg(feature = "unstable_llm_providers")]
6979 #[test]
6980 fn test_provider_info_missing_current_defaults_to_none() {
6981 let json = json!({
6983 "providerId": "main",
6984 "supported": ["anthropic"],
6985 "required": true
6986 });
6987 let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
6988 assert!(deserialized.current.is_none());
6989 }
6990
6991 #[cfg(feature = "unstable_llm_providers")]
6992 #[test]
6993 fn test_provider_info_explicit_null_current_decodes_to_none() {
6994 let json = json!({
6998 "providerId": "main",
6999 "supported": ["anthropic"],
7000 "required": true,
7001 "current": null
7002 });
7003 let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
7004 assert!(deserialized.current.is_none());
7005 }
7006
7007 #[cfg(feature = "unstable_llm_providers")]
7008 #[test]
7009 fn test_list_providers_response_serialization() {
7010 let response = ListProvidersResponse::new(vec![ProviderInfo::new(
7011 "main",
7012 vec![LlmProtocol::Anthropic],
7013 true,
7014 Some(ProviderCurrentConfig::new(
7015 LlmProtocol::Anthropic,
7016 "https://api.anthropic.com",
7017 )),
7018 )]);
7019
7020 let json = serde_json::to_value(&response).unwrap();
7021 assert_eq!(json["providers"].as_array().unwrap().len(), 1);
7022 assert_eq!(json["providers"][0]["providerId"], "main");
7023
7024 let deserialized: ListProvidersResponse = serde_json::from_value(json).unwrap();
7025 assert_eq!(deserialized.providers.len(), 1);
7026 }
7027
7028 #[cfg(feature = "unstable_llm_providers")]
7029 #[test]
7030 fn test_set_provider_request_serialization() {
7031 use std::collections::HashMap;
7032
7033 let mut headers = HashMap::new();
7034 headers.insert("Authorization".to_string(), "Bearer sk-test".to_string());
7035
7036 let request =
7037 SetProviderRequest::new("main", LlmProtocol::OpenAi, "https://api.openai.com/v1")
7038 .headers(headers);
7039
7040 let json = serde_json::to_value(&request).unwrap();
7041 assert_eq!(
7042 json,
7043 json!({
7044 "providerId": "main",
7045 "apiType": "openai",
7046 "baseUrl": "https://api.openai.com/v1",
7047 "headers": {
7048 "Authorization": "Bearer sk-test"
7049 }
7050 })
7051 );
7052
7053 let deserialized: SetProviderRequest = serde_json::from_value(json).unwrap();
7054 assert_eq!(deserialized.provider_id.to_string(), "main");
7055 assert_eq!(deserialized.api_type, LlmProtocol::OpenAi);
7056 assert_eq!(deserialized.base_url, "https://api.openai.com/v1");
7057 assert_eq!(deserialized.headers.len(), 1);
7058 assert_eq!(
7059 deserialized.headers.get("Authorization").unwrap(),
7060 "Bearer sk-test"
7061 );
7062 }
7063
7064 #[cfg(feature = "unstable_llm_providers")]
7065 #[test]
7066 fn test_set_provider_request_omits_empty_headers() {
7067 let request =
7068 SetProviderRequest::new("main", LlmProtocol::Anthropic, "https://api.anthropic.com");
7069
7070 let json = serde_json::to_value(&request).unwrap();
7071 assert!(!json.as_object().unwrap().contains_key("headers"));
7073 }
7074
7075 #[cfg(feature = "unstable_llm_providers")]
7076 #[test]
7077 fn test_disable_provider_request_serialization() {
7078 let request = DisableProviderRequest::new("secondary");
7079
7080 let json = serde_json::to_value(&request).unwrap();
7081 assert_eq!(json, json!({ "providerId": "secondary" }));
7082
7083 let deserialized: DisableProviderRequest = serde_json::from_value(json).unwrap();
7084 assert_eq!(deserialized.provider_id.to_string(), "secondary");
7085 }
7086
7087 #[cfg(feature = "unstable_llm_providers")]
7088 #[test]
7089 fn test_providers_capabilities_serialization() {
7090 let caps = ProvidersCapabilities::new();
7091
7092 let json = serde_json::to_value(&caps).unwrap();
7093 assert_eq!(json, json!({}));
7094
7095 let deserialized: ProvidersCapabilities = serde_json::from_value(json).unwrap();
7096 assert!(deserialized.meta.is_none());
7097 }
7098
7099 #[cfg(feature = "unstable_llm_providers")]
7100 #[test]
7101 fn test_agent_capabilities_with_providers() {
7102 let caps = AgentCapabilities::new().providers(ProvidersCapabilities::new());
7103
7104 let json = serde_json::to_value(&caps).unwrap();
7105 assert_eq!(json["providers"], json!({}));
7106
7107 let deserialized: AgentCapabilities = serde_json::from_value(json).unwrap();
7108 assert!(deserialized.providers.is_some());
7109 }
7110
7111 #[test]
7112 fn test_agent_capabilities_session_is_explicit() {
7113 let json = serde_json::to_value(AgentCapabilities::new()).unwrap();
7114 assert!(json.get("session").is_none());
7115
7116 let caps = AgentCapabilities::new().session(
7117 SessionCapabilities::new()
7118 .prompt(PromptCapabilities::new().image(PromptImageCapabilities::new()))
7119 .mcp(McpCapabilities::new().stdio(McpStdioCapabilities::new())),
7120 );
7121
7122 assert_eq!(
7123 serde_json::to_value(&caps).unwrap(),
7124 json!({
7125 "session": {
7126 "prompt": {
7127 "image": {}
7128 },
7129 "mcp": {
7130 "stdio": {}
7131 }
7132 }
7133 })
7134 );
7135
7136 let deserialized: AgentCapabilities = serde_json::from_value(json!({
7137 "session": false
7138 }))
7139 .unwrap();
7140 assert!(deserialized.session.is_none());
7141 }
7142
7143 #[test]
7144 fn test_prompt_capabilities_serialize_supported_content_as_objects() {
7145 let caps = PromptCapabilities::new()
7146 .image(PromptImageCapabilities::new())
7147 .audio(PromptAudioCapabilities::new())
7148 .embedded_context(PromptEmbeddedContextCapabilities::new());
7149
7150 assert_eq!(
7151 serde_json::to_value(&caps).unwrap(),
7152 json!({
7153 "image": {},
7154 "audio": {},
7155 "embeddedContext": {}
7156 })
7157 );
7158
7159 let deserialized: PromptCapabilities = serde_json::from_value(json!({
7160 "image": null,
7161 "audio": false,
7162 "embeddedContext": {}
7163 }))
7164 .unwrap();
7165 assert!(deserialized.image.is_none());
7166 assert!(deserialized.audio.is_none());
7167 assert!(deserialized.embedded_context.is_some());
7168 }
7169
7170 #[test]
7171 fn test_mcp_capabilities_serialize_supported_transports_as_objects() {
7172 let caps = McpCapabilities::new()
7173 .stdio(McpStdioCapabilities::new())
7174 .http(McpHttpCapabilities::new());
7175
7176 assert_eq!(
7177 serde_json::to_value(&caps).unwrap(),
7178 json!({
7179 "stdio": {},
7180 "http": {}
7181 })
7182 );
7183
7184 let deserialized: McpCapabilities = serde_json::from_value(json!({
7185 "stdio": null,
7186 "http": false
7187 }))
7188 .unwrap();
7189 assert!(deserialized.stdio.is_none());
7190 assert!(deserialized.http.is_none());
7191 }
7192
7193 #[cfg(feature = "unstable_mcp_over_acp")]
7194 #[test]
7195 fn test_mcp_capabilities_serialize_acp_support_as_object() {
7196 let caps = McpCapabilities::new().acp(McpAcpCapabilities::new());
7197
7198 assert_eq!(
7199 serde_json::to_value(&caps).unwrap(),
7200 json!({
7201 "acp": {}
7202 })
7203 );
7204 }
7205
7206 #[test]
7207 fn prompt_request_rejects_malformed_content_block() {
7208 use serde_json::json;
7209
7210 assert!(
7211 serde_json::from_value::<PromptRequest>(json!({
7212 "sessionId": "sess-1",
7213 "prompt": [{"type": "text"}]
7214 }))
7215 .is_err()
7216 );
7217 }
7218
7219 #[test]
7220 fn prompt_request_rejects_non_array_prompt() {
7221 use serde_json::json;
7222
7223 assert!(
7224 serde_json::from_value::<PromptRequest>(json!({
7225 "sessionId": "sess-1",
7226 "prompt": "hello"
7227 }))
7228 .is_err()
7229 );
7230 }
7231}