1use std::{borrow::Cow, collections::BTreeMap, sync::Arc};
7
8#[cfg(feature = "unstable_llm_providers")]
9use std::collections::HashMap;
10
11use derive_more::{Display, From};
12#[cfg(feature = "schemars")]
13use schemars::Schema;
14use serde::{Deserialize, Serialize};
15use serde_with::{DefaultOnError, VecSkipError, serde_as, skip_serializing_none};
16
17use super::{
18 AbsolutePath, ClientCapabilities, ContentBlock, ExtNotification, ExtRequest, ExtResponse, Meta,
19 SessionId,
20};
21use crate::{IntoOption, ProtocolVersion, SkipListener};
22
23#[cfg(feature = "unstable_mcp_over_acp")]
24use super::mcp::{
25 MCP_MESSAGE_METHOD_NAME, MessageMcpNotification, MessageMcpRequest, MessageMcpResponse,
26};
27
28#[cfg(feature = "unstable_nes")]
29use super::{
30 AcceptNesNotification, CloseNesRequest, CloseNesResponse, DidChangeDocumentNotification,
31 DidCloseDocumentNotification, DidFocusDocumentNotification, DidOpenDocumentNotification,
32 DidSaveDocumentNotification, NesCapabilities, PositionEncodingKind, RejectNesNotification,
33 StartNesRequest, StartNesResponse, SuggestNesRequest, SuggestNesResponse,
34};
35
36#[cfg(feature = "unstable_nes")]
37use super::{
38 DOCUMENT_DID_CHANGE_METHOD_NAME, DOCUMENT_DID_CLOSE_METHOD_NAME,
39 DOCUMENT_DID_FOCUS_METHOD_NAME, DOCUMENT_DID_OPEN_METHOD_NAME, DOCUMENT_DID_SAVE_METHOD_NAME,
40 NES_ACCEPT_METHOD_NAME, NES_CLOSE_METHOD_NAME, NES_REJECT_METHOD_NAME, NES_START_METHOD_NAME,
41 NES_SUGGEST_METHOD_NAME,
42};
43
44#[serde_as]
52#[skip_serializing_none]
53#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
54#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
55#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = INITIALIZE_METHOD_NAME)))]
56#[serde(rename_all = "camelCase")]
57#[non_exhaustive]
58pub struct InitializeRequest {
59 pub protocol_version: ProtocolVersion,
61 pub info: Implementation,
63 #[serde_as(deserialize_as = "DefaultOnError")]
65 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
66 #[serde(default)]
67 pub capabilities: ClientCapabilities,
68 #[serde_as(deserialize_as = "DefaultOnError")]
74 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
75 #[serde(default)]
76 #[serde(rename = "_meta")]
77 pub meta: Option<Meta>,
78}
79
80impl InitializeRequest {
81 #[must_use]
83 pub fn new(protocol_version: ProtocolVersion, info: Implementation) -> Self {
84 Self {
85 protocol_version,
86 capabilities: ClientCapabilities::default(),
87 info,
88 meta: None,
89 }
90 }
91
92 #[must_use]
94 pub fn capabilities(mut self, capabilities: ClientCapabilities) -> Self {
95 self.capabilities = capabilities;
96 self
97 }
98
99 #[must_use]
105 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
106 self.meta = meta.into_option();
107 self
108 }
109}
110
111#[serde_as]
117#[skip_serializing_none]
118#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
119#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
120#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = INITIALIZE_METHOD_NAME)))]
121#[serde(rename_all = "camelCase")]
122#[non_exhaustive]
123pub struct InitializeResponse {
124 pub protocol_version: ProtocolVersion,
129 pub info: Implementation,
131 #[serde_as(deserialize_as = "DefaultOnError")]
133 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
134 #[serde(default)]
135 pub capabilities: AgentCapabilities,
136 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
142 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
143 #[serde(default, skip_serializing_if = "Vec::is_empty")]
144 pub auth_methods: Vec<AuthMethod>,
145 #[serde_as(deserialize_as = "DefaultOnError")]
151 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
152 #[serde(default)]
153 #[serde(rename = "_meta")]
154 pub meta: Option<Meta>,
155}
156
157impl InitializeResponse {
158 #[must_use]
160 pub fn new(protocol_version: ProtocolVersion, info: Implementation) -> Self {
161 Self {
162 protocol_version,
163 capabilities: AgentCapabilities::default(),
164 auth_methods: vec![],
165 info,
166 meta: None,
167 }
168 }
169
170 #[must_use]
172 pub fn capabilities(mut self, capabilities: AgentCapabilities) -> Self {
173 self.capabilities = capabilities;
174 self
175 }
176
177 #[must_use]
182 pub fn auth_methods(mut self, auth_methods: Vec<AuthMethod>) -> Self {
183 self.auth_methods = auth_methods;
184 self
185 }
186
187 #[must_use]
193 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
194 self.meta = meta.into_option();
195 self
196 }
197}
198
199#[serde_as]
203#[skip_serializing_none]
204#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
205#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
206#[serde(rename_all = "camelCase")]
207#[non_exhaustive]
208pub struct Implementation {
209 pub name: String,
212 #[serde_as(deserialize_as = "DefaultOnError")]
217 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
218 #[serde(default)]
219 pub title: Option<String>,
220 pub version: String,
223 #[serde_as(deserialize_as = "DefaultOnError")]
229 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
230 #[serde(default)]
231 #[serde(rename = "_meta")]
232 pub meta: Option<Meta>,
233}
234
235impl Implementation {
236 #[must_use]
238 pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
239 Self {
240 name: name.into(),
241 title: None,
242 version: version.into(),
243 meta: None,
244 }
245 }
246
247 #[must_use]
252 pub fn title(mut self, title: impl IntoOption<String>) -> Self {
253 self.title = title.into_option();
254 self
255 }
256
257 #[must_use]
263 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
264 self.meta = meta.into_option();
265 self
266 }
267}
268
269#[serde_as]
279#[skip_serializing_none]
280#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
281#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
282#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGIN_METHOD_NAME)))]
283#[serde(rename_all = "camelCase")]
284#[non_exhaustive]
285pub struct LoginAuthRequest {
286 pub method_id: AuthMethodId,
289 #[serde_as(deserialize_as = "DefaultOnError")]
295 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
296 #[serde(default)]
297 #[serde(rename = "_meta")]
298 pub meta: Option<Meta>,
299}
300
301impl LoginAuthRequest {
302 #[must_use]
304 pub fn new(method_id: impl Into<AuthMethodId>) -> Self {
305 Self {
306 method_id: method_id.into(),
307 meta: None,
308 }
309 }
310
311 #[must_use]
317 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
318 self.meta = meta.into_option();
319 self
320 }
321}
322
323#[serde_as]
325#[skip_serializing_none]
326#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
327#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
328#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGIN_METHOD_NAME)))]
329#[serde(rename_all = "camelCase")]
330#[non_exhaustive]
331pub struct LoginAuthResponse {
332 #[serde_as(deserialize_as = "DefaultOnError")]
338 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
339 #[serde(default)]
340 #[serde(rename = "_meta")]
341 pub meta: Option<Meta>,
342}
343
344impl LoginAuthResponse {
345 #[must_use]
347 pub fn new() -> Self {
348 Self::default()
349 }
350
351 #[must_use]
357 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
358 self.meta = meta.into_option();
359 self
360 }
361}
362
363#[serde_as]
373#[skip_serializing_none]
374#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
375#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
376#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGOUT_METHOD_NAME)))]
377#[serde(rename_all = "camelCase")]
378#[non_exhaustive]
379pub struct LogoutAuthRequest {
380 #[serde_as(deserialize_as = "DefaultOnError")]
386 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
387 #[serde(default)]
388 #[serde(rename = "_meta")]
389 pub meta: Option<Meta>,
390}
391
392impl LogoutAuthRequest {
393 #[must_use]
395 pub fn new() -> Self {
396 Self::default()
397 }
398
399 #[must_use]
405 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
406 self.meta = meta.into_option();
407 self
408 }
409}
410
411#[serde_as]
413#[skip_serializing_none]
414#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
415#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
416#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGOUT_METHOD_NAME)))]
417#[serde(rename_all = "camelCase")]
418#[non_exhaustive]
419pub struct LogoutAuthResponse {
420 #[serde_as(deserialize_as = "DefaultOnError")]
426 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
427 #[serde(default)]
428 #[serde(rename = "_meta")]
429 pub meta: Option<Meta>,
430}
431
432impl LogoutAuthResponse {
433 #[must_use]
435 pub fn new() -> Self {
436 Self::default()
437 }
438
439 #[must_use]
445 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
446 self.meta = meta.into_option();
447 self
448 }
449}
450
451#[serde_as]
457#[skip_serializing_none]
458#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
459#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
460#[serde(rename_all = "camelCase")]
461#[non_exhaustive]
462pub struct AgentAuthCapabilities {
463 #[serde_as(deserialize_as = "DefaultOnError")]
469 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
470 #[serde(default)]
471 #[serde(rename = "_meta")]
472 pub meta: Option<Meta>,
473}
474
475impl AgentAuthCapabilities {
476 #[must_use]
478 pub fn new() -> Self {
479 Self::default()
480 }
481
482 #[must_use]
488 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
489 self.meta = meta.into_option();
490 self
491 }
492}
493
494#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
496#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
497#[serde(transparent)]
498#[from(forward)]
499#[non_exhaustive]
500pub struct AuthMethodId(pub Arc<str>);
501
502impl AuthMethodId {
503 #[must_use]
505 pub fn new(id: impl Into<Self>) -> Self {
506 id.into()
507 }
508}
509
510#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
514#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
515#[serde(tag = "type", rename_all = "snake_case")]
516#[non_exhaustive]
517pub enum AuthMethod {
518 Terminal(AuthMethodTerminal),
521 Agent(AuthMethodAgent),
525 #[serde(untagged)]
535 Other(OtherAuthMethod),
536}
537
538impl AuthMethod {
539 #[must_use]
541 pub fn method_id(&self) -> &AuthMethodId {
542 match self {
543 Self::Agent(a) => &a.method_id,
544 Self::Other(a) => &a.method_id,
545 Self::Terminal(t) => &t.method_id,
546 }
547 }
548
549 #[must_use]
551 pub fn name(&self) -> &str {
552 match self {
553 Self::Agent(a) => &a.name,
554 Self::Other(a) => &a.name,
555 Self::Terminal(t) => &t.name,
556 }
557 }
558
559 #[must_use]
561 pub fn description(&self) -> Option<&str> {
562 match self {
563 Self::Agent(a) => a.description.as_deref(),
564 Self::Other(a) => a.description.as_deref(),
565 Self::Terminal(t) => t.description.as_deref(),
566 }
567 }
568
569 #[must_use]
575 pub fn meta(&self) -> Option<&Meta> {
576 match self {
577 Self::Agent(a) => a.meta.as_ref(),
578 Self::Other(a) => a.meta.as_ref(),
579 Self::Terminal(t) => t.meta.as_ref(),
580 }
581 }
582}
583
584#[serde_as]
586#[skip_serializing_none]
587#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
588#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
589#[cfg_attr(feature = "schemars", schemars(inline))]
590#[cfg_attr(feature = "schemars", schemars(transform = other_auth_method_schema))]
591#[serde(rename_all = "camelCase")]
592#[non_exhaustive]
593pub struct OtherAuthMethod {
594 #[serde(rename = "type")]
600 pub type_: String,
601 pub method_id: AuthMethodId,
603 pub name: String,
605 #[serde_as(deserialize_as = "DefaultOnError")]
607 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
608 #[serde(default)]
609 pub description: Option<String>,
610 #[serde_as(deserialize_as = "DefaultOnError")]
616 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
617 #[serde(default)]
618 #[serde(rename = "_meta")]
619 pub meta: Option<Meta>,
620 #[serde(flatten)]
622 pub fields: BTreeMap<String, serde_json::Value>,
623}
624
625impl OtherAuthMethod {
626 #[must_use]
628 pub fn new(
629 type_: impl Into<String>,
630 method_id: impl Into<AuthMethodId>,
631 name: impl Into<String>,
632 mut fields: BTreeMap<String, serde_json::Value>,
633 ) -> Self {
634 fields.remove("type");
635 fields.remove("methodId");
636 fields.remove("name");
637 fields.remove("description");
638 fields.remove("_meta");
639 Self {
640 type_: type_.into(),
641 method_id: method_id.into(),
642 name: name.into(),
643 description: None,
644 meta: None,
645 fields,
646 }
647 }
648
649 #[must_use]
651 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
652 self.description = description.into_option();
653 self
654 }
655
656 #[must_use]
662 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
663 self.meta = meta.into_option();
664 self
665 }
666}
667
668impl<'de> Deserialize<'de> for OtherAuthMethod {
669 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
670 where
671 D: serde::Deserializer<'de>,
672 {
673 #[derive(Deserialize)]
674 #[serde(rename_all = "camelCase")]
675 struct RawOtherAuthMethod {
676 #[serde(rename = "type")]
677 type_: String,
678 method_id: AuthMethodId,
679 name: String,
680 description: Option<String>,
681 #[serde(rename = "_meta")]
682 meta: Option<Meta>,
683 #[serde(flatten)]
684 fields: BTreeMap<String, serde_json::Value>,
685 }
686
687 let raw = RawOtherAuthMethod::deserialize(deserializer)?;
688 if is_known_auth_method_type(&raw.type_) {
689 return Err(serde::de::Error::custom(format!(
690 "known authentication method `{}` did not match its schema",
691 raw.type_
692 )));
693 }
694
695 Ok(Self {
696 type_: raw.type_,
697 method_id: raw.method_id,
698 name: raw.name,
699 description: raw.description,
700 meta: raw.meta,
701 fields: raw.fields,
702 })
703 }
704}
705
706fn is_known_auth_method_type(type_: &str) -> bool {
707 matches!(type_, "agent" | "terminal")
708}
709
710#[cfg(feature = "schemars")]
711fn other_auth_method_schema(schema: &mut Schema) {
712 super::schema_util::reject_known_string_discriminators(schema, "type", &["agent", "terminal"]);
713}
714
715#[serde_as]
719#[skip_serializing_none]
720#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
721#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
722#[serde(rename_all = "camelCase")]
723#[non_exhaustive]
724pub struct AuthMethodAgent {
725 pub method_id: AuthMethodId,
727 pub name: String,
729 #[serde_as(deserialize_as = "DefaultOnError")]
731 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
732 #[serde(default)]
733 pub description: Option<String>,
734 #[serde_as(deserialize_as = "DefaultOnError")]
740 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
741 #[serde(default)]
742 #[serde(rename = "_meta")]
743 pub meta: Option<Meta>,
744}
745
746impl AuthMethodAgent {
747 #[must_use]
749 pub fn new(method_id: impl Into<AuthMethodId>, name: impl Into<String>) -> Self {
750 Self {
751 method_id: method_id.into(),
752 name: name.into(),
753 description: None,
754 meta: None,
755 }
756 }
757
758 #[must_use]
760 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
761 self.description = description.into_option();
762 self
763 }
764
765 #[must_use]
771 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
772 self.meta = meta.into_option();
773 self
774 }
775}
776
777#[serde_as]
785#[skip_serializing_none]
786#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
787#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
788#[serde(rename_all = "camelCase")]
789#[non_exhaustive]
790pub struct AuthMethodTerminal {
791 pub method_id: AuthMethodId,
793 pub name: String,
795 #[serde_as(deserialize_as = "DefaultOnError")]
797 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
798 #[serde(default)]
799 pub description: Option<String>,
800 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
802 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
803 #[serde(default, skip_serializing_if = "Vec::is_empty")]
804 pub args: Vec<String>,
805 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
809 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
810 #[serde(default, skip_serializing_if = "Vec::is_empty")]
811 pub env: Vec<EnvVariable>,
812 #[serde_as(deserialize_as = "DefaultOnError")]
818 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
819 #[serde(default)]
820 #[serde(rename = "_meta")]
821 pub meta: Option<Meta>,
822}
823
824impl AuthMethodTerminal {
825 #[must_use]
827 pub fn new(method_id: impl Into<AuthMethodId>, name: impl Into<String>) -> Self {
828 Self {
829 method_id: method_id.into(),
830 name: name.into(),
831 description: None,
832 args: Vec::new(),
833 env: Vec::new(),
834 meta: None,
835 }
836 }
837
838 #[must_use]
840 pub fn args(mut self, args: Vec<String>) -> Self {
841 self.args = args;
842 self
843 }
844
845 #[must_use]
849 pub fn env(mut self, env: Vec<EnvVariable>) -> Self {
850 self.env = env;
851 self
852 }
853
854 #[must_use]
856 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
857 self.description = description.into_option();
858 self
859 }
860
861 #[must_use]
867 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
868 self.meta = meta.into_option();
869 self
870 }
871}
872
873#[serde_as]
879#[skip_serializing_none]
880#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
881#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
882#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_NEW_METHOD_NAME)))]
883#[serde(rename_all = "camelCase")]
884#[non_exhaustive]
885pub struct NewSessionRequest {
886 pub cwd: AbsolutePath,
888 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
894 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
895 #[serde(default, skip_serializing_if = "Vec::is_empty")]
896 pub additional_directories: Vec<AbsolutePath>,
897 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
899 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
900 #[serde(default, skip_serializing_if = "Vec::is_empty")]
901 pub mcp_servers: Vec<McpServer>,
902 #[serde_as(deserialize_as = "DefaultOnError")]
908 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
909 #[serde(default)]
910 #[serde(rename = "_meta")]
911 pub meta: Option<Meta>,
912}
913
914impl NewSessionRequest {
915 #[must_use]
917 pub fn new(cwd: impl Into<AbsolutePath>) -> Self {
918 Self {
919 cwd: cwd.into(),
920 additional_directories: vec![],
921 mcp_servers: vec![],
922 meta: None,
923 }
924 }
925
926 #[must_use]
928 pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
929 where
930 I: IntoIterator<Item = P>,
931 P: Into<AbsolutePath>,
932 {
933 self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
934 self
935 }
936
937 #[must_use]
939 pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
940 self.mcp_servers = mcp_servers;
941 self
942 }
943
944 #[must_use]
950 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
951 self.meta = meta.into_option();
952 self
953 }
954}
955
956#[serde_as]
960#[skip_serializing_none]
961#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
962#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
963#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_NEW_METHOD_NAME)))]
964#[serde(rename_all = "camelCase")]
965#[non_exhaustive]
966pub struct NewSessionResponse {
967 pub session_id: SessionId,
971 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
973 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
974 #[serde(default, skip_serializing_if = "Vec::is_empty")]
975 pub config_options: Vec<SessionConfigOption>,
976 #[serde_as(deserialize_as = "DefaultOnError")]
982 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
983 #[serde(default)]
984 #[serde(rename = "_meta")]
985 pub meta: Option<Meta>,
986}
987
988impl NewSessionResponse {
989 #[must_use]
991 pub fn new(session_id: impl Into<SessionId>) -> Self {
992 Self {
993 session_id: session_id.into(),
994 config_options: Vec::new(),
995 meta: None,
996 }
997 }
998
999 #[must_use]
1001 pub fn config_options(mut self, config_options: Vec<SessionConfigOption>) -> Self {
1002 self.config_options = config_options;
1003 self
1004 }
1005
1006 #[must_use]
1012 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1013 self.meta = meta.into_option();
1014 self
1015 }
1016}
1017
1018#[cfg(feature = "unstable_session_fork")]
1031#[serde_as]
1032#[skip_serializing_none]
1033#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1034#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1035#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_FORK_METHOD_NAME)))]
1036#[serde(rename_all = "camelCase")]
1037#[non_exhaustive]
1038pub struct ForkSessionRequest {
1039 pub session_id: SessionId,
1041 pub cwd: AbsolutePath,
1043 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1049 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1050 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1051 pub additional_directories: Vec<AbsolutePath>,
1052 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1054 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1055 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1056 pub mcp_servers: Vec<McpServer>,
1057 #[serde_as(deserialize_as = "DefaultOnError")]
1063 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1064 #[serde(default)]
1065 #[serde(rename = "_meta")]
1066 pub meta: Option<Meta>,
1067}
1068
1069#[cfg(feature = "unstable_session_fork")]
1070impl ForkSessionRequest {
1071 #[must_use]
1073 pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<AbsolutePath>) -> Self {
1074 Self {
1075 session_id: session_id.into(),
1076 cwd: cwd.into(),
1077 additional_directories: vec![],
1078 mcp_servers: vec![],
1079 meta: None,
1080 }
1081 }
1082
1083 #[must_use]
1085 pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
1086 where
1087 I: IntoIterator<Item = P>,
1088 P: Into<AbsolutePath>,
1089 {
1090 self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
1091 self
1092 }
1093
1094 #[must_use]
1096 pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1097 self.mcp_servers = mcp_servers;
1098 self
1099 }
1100
1101 #[must_use]
1107 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1108 self.meta = meta.into_option();
1109 self
1110 }
1111}
1112
1113#[cfg(feature = "unstable_session_fork")]
1119#[serde_as]
1120#[skip_serializing_none]
1121#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1122#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1123#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_FORK_METHOD_NAME)))]
1124#[serde(rename_all = "camelCase")]
1125#[non_exhaustive]
1126pub struct ForkSessionResponse {
1127 pub session_id: SessionId,
1129 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1131 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1132 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1133 pub config_options: Vec<SessionConfigOption>,
1134 #[serde_as(deserialize_as = "DefaultOnError")]
1140 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1141 #[serde(default)]
1142 #[serde(rename = "_meta")]
1143 pub meta: Option<Meta>,
1144}
1145
1146#[cfg(feature = "unstable_session_fork")]
1147impl ForkSessionResponse {
1148 #[must_use]
1150 pub fn new(session_id: impl Into<SessionId>) -> Self {
1151 Self {
1152 session_id: session_id.into(),
1153 config_options: Vec::new(),
1154 meta: None,
1155 }
1156 }
1157
1158 #[must_use]
1160 pub fn config_options(mut self, config_options: Vec<SessionConfigOption>) -> Self {
1161 self.config_options = config_options;
1162 self
1163 }
1164
1165 #[must_use]
1171 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1172 self.meta = meta.into_option();
1173 self
1174 }
1175}
1176
1177#[serde_as]
1184#[skip_serializing_none]
1185#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1186#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1187#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_RESUME_METHOD_NAME)))]
1188#[serde(rename_all = "camelCase")]
1189#[non_exhaustive]
1190pub struct ResumeSessionRequest {
1191 pub session_id: SessionId,
1193 pub cwd: AbsolutePath,
1195 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1202 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1203 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1204 pub additional_directories: Vec<AbsolutePath>,
1205 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1207 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1208 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1209 pub mcp_servers: Vec<McpServer>,
1210 #[serde_as(deserialize_as = "DefaultOnError")]
1218 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1219 #[serde(default)]
1220 pub replay_from: Option<ReplayFrom>,
1221 #[serde_as(deserialize_as = "DefaultOnError")]
1227 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1228 #[serde(default)]
1229 #[serde(rename = "_meta")]
1230 pub meta: Option<Meta>,
1231}
1232
1233impl ResumeSessionRequest {
1234 #[must_use]
1236 pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<AbsolutePath>) -> Self {
1237 Self {
1238 session_id: session_id.into(),
1239 cwd: cwd.into(),
1240 additional_directories: vec![],
1241 mcp_servers: vec![],
1242 replay_from: None,
1243 meta: None,
1244 }
1245 }
1246
1247 #[must_use]
1249 pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
1250 where
1251 I: IntoIterator<Item = P>,
1252 P: Into<AbsolutePath>,
1253 {
1254 self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
1255 self
1256 }
1257
1258 #[must_use]
1260 pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1261 self.mcp_servers = mcp_servers;
1262 self
1263 }
1264
1265 #[must_use]
1273 pub fn replay_from(mut self, replay_from: impl IntoOption<ReplayFrom>) -> Self {
1274 self.replay_from = replay_from.into_option();
1275 self
1276 }
1277
1278 #[must_use]
1284 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1285 self.meta = meta.into_option();
1286 self
1287 }
1288}
1289
1290#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1294#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1295#[serde(tag = "type", rename_all = "snake_case")]
1296#[non_exhaustive]
1297pub enum ReplayFrom {
1298 Start(ReplayFromStart),
1300 #[serde(untagged)]
1310 Other(OtherReplayFrom),
1311}
1312
1313impl From<ReplayFromStart> for ReplayFrom {
1314 fn from(replay_from: ReplayFromStart) -> Self {
1315 Self::Start(replay_from)
1316 }
1317}
1318
1319#[serde_as]
1321#[skip_serializing_none]
1322#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1323#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1324#[serde(rename_all = "camelCase")]
1325#[non_exhaustive]
1326pub struct ReplayFromStart {
1327 #[serde_as(deserialize_as = "DefaultOnError")]
1333 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1334 #[serde(default)]
1335 #[serde(rename = "_meta")]
1336 pub meta: Option<Meta>,
1337}
1338
1339impl ReplayFromStart {
1340 #[must_use]
1342 pub fn new() -> Self {
1343 Self::default()
1344 }
1345
1346 #[must_use]
1352 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1353 self.meta = meta.into_option();
1354 self
1355 }
1356}
1357
1358#[serde_as]
1360#[skip_serializing_none]
1361#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1362#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
1363#[cfg_attr(feature = "schemars", schemars(inline))]
1364#[cfg_attr(feature = "schemars", schemars(transform = other_replay_from_schema))]
1365#[serde(rename_all = "camelCase")]
1366#[non_exhaustive]
1367pub struct OtherReplayFrom {
1368 #[serde(rename = "type")]
1374 pub type_: String,
1375 #[serde_as(deserialize_as = "DefaultOnError")]
1381 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1382 #[serde(default)]
1383 #[serde(rename = "_meta")]
1384 pub meta: Option<Meta>,
1385 #[serde(flatten)]
1387 pub fields: BTreeMap<String, serde_json::Value>,
1388}
1389
1390impl OtherReplayFrom {
1391 #[must_use]
1393 pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
1394 fields.remove("type");
1395 fields.remove("_meta");
1396 Self {
1397 type_: type_.into(),
1398 meta: None,
1399 fields,
1400 }
1401 }
1402
1403 #[must_use]
1409 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1410 self.meta = meta.into_option();
1411 self
1412 }
1413}
1414
1415impl<'de> Deserialize<'de> for OtherReplayFrom {
1416 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1417 where
1418 D: serde::Deserializer<'de>,
1419 {
1420 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
1421 let type_ = fields
1422 .remove("type")
1423 .ok_or_else(|| serde::de::Error::missing_field("type"))?;
1424 let serde_json::Value::String(type_) = type_ else {
1425 return Err(serde::de::Error::custom("`type` must be a string"));
1426 };
1427
1428 if is_known_replay_from_type(&type_) {
1429 return Err(serde::de::Error::custom(format!(
1430 "known replay cursor `{type_}` did not match its schema"
1431 )));
1432 }
1433
1434 let meta = fields
1435 .remove("_meta")
1436 .and_then(|value| serde_json::from_value(value).ok());
1437
1438 Ok(Self {
1439 type_,
1440 meta,
1441 fields,
1442 })
1443 }
1444}
1445
1446fn is_known_replay_from_type(type_: &str) -> bool {
1447 matches!(type_, "start")
1448}
1449
1450#[cfg(feature = "schemars")]
1451fn other_replay_from_schema(schema: &mut Schema) {
1452 super::schema_util::reject_known_string_discriminators(schema, "type", &["start"]);
1453}
1454
1455#[serde_as]
1457#[skip_serializing_none]
1458#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1459#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1460#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_RESUME_METHOD_NAME)))]
1461#[serde(rename_all = "camelCase")]
1462#[non_exhaustive]
1463pub struct ResumeSessionResponse {
1464 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1466 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1467 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1468 pub config_options: Vec<SessionConfigOption>,
1469 #[serde_as(deserialize_as = "DefaultOnError")]
1475 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1476 #[serde(default)]
1477 #[serde(rename = "_meta")]
1478 pub meta: Option<Meta>,
1479}
1480
1481impl ResumeSessionResponse {
1482 #[must_use]
1484 pub fn new() -> Self {
1485 Self::default()
1486 }
1487
1488 #[must_use]
1490 pub fn config_options(mut self, config_options: Vec<SessionConfigOption>) -> Self {
1491 self.config_options = config_options;
1492 self
1493 }
1494
1495 #[must_use]
1501 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1502 self.meta = meta.into_option();
1503 self
1504 }
1505}
1506
1507#[serde_as]
1515#[skip_serializing_none]
1516#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1517#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1518#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_CLOSE_METHOD_NAME)))]
1519#[serde(rename_all = "camelCase")]
1520#[non_exhaustive]
1521pub struct CloseSessionRequest {
1522 pub session_id: SessionId,
1524 #[serde_as(deserialize_as = "DefaultOnError")]
1530 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1531 #[serde(default)]
1532 #[serde(rename = "_meta")]
1533 pub meta: Option<Meta>,
1534}
1535
1536impl CloseSessionRequest {
1537 #[must_use]
1539 pub fn new(session_id: impl Into<SessionId>) -> Self {
1540 Self {
1541 session_id: session_id.into(),
1542 meta: None,
1543 }
1544 }
1545
1546 #[must_use]
1552 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1553 self.meta = meta.into_option();
1554 self
1555 }
1556}
1557
1558#[serde_as]
1560#[skip_serializing_none]
1561#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1562#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1563#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_CLOSE_METHOD_NAME)))]
1564#[serde(rename_all = "camelCase")]
1565#[non_exhaustive]
1566pub struct CloseSessionResponse {
1567 #[serde_as(deserialize_as = "DefaultOnError")]
1573 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1574 #[serde(default)]
1575 #[serde(rename = "_meta")]
1576 pub meta: Option<Meta>,
1577}
1578
1579impl CloseSessionResponse {
1580 #[must_use]
1582 pub fn new() -> Self {
1583 Self::default()
1584 }
1585
1586 #[must_use]
1592 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1593 self.meta = meta.into_option();
1594 self
1595 }
1596}
1597
1598#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1602#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
1603#[serde(transparent)]
1604#[from(Arc<str>, String, &str, &mut str, Box<str>, Cow<'_, str>)]
1605#[non_exhaustive]
1606pub struct SessionListCursor(pub Arc<str>);
1607
1608impl SessionListCursor {
1609 #[must_use]
1611 pub fn new(cursor: impl Into<Self>) -> Self {
1612 cursor.into()
1613 }
1614}
1615
1616impl AsRef<str> for SessionListCursor {
1617 fn as_ref(&self) -> &str {
1618 &self.0
1619 }
1620}
1621
1622impl From<&String> for SessionListCursor {
1623 fn from(cursor: &String) -> Self {
1624 Self(cursor.as_str().into())
1625 }
1626}
1627
1628macro_rules! impl_session_list_cursor_option_conversion {
1629 ($source:ty) => {
1630 impl IntoOption<SessionListCursor> for $source {
1631 fn into_option(self) -> Option<SessionListCursor> {
1632 Some(self.into())
1633 }
1634 }
1635 };
1636}
1637
1638impl_session_list_cursor_option_conversion!(Arc<str>);
1639impl_session_list_cursor_option_conversion!(String);
1640impl_session_list_cursor_option_conversion!(&str);
1641impl_session_list_cursor_option_conversion!(&mut str);
1642impl_session_list_cursor_option_conversion!(&String);
1643impl_session_list_cursor_option_conversion!(Box<str>);
1644impl_session_list_cursor_option_conversion!(Cow<'_, str>);
1645
1646#[serde_as]
1648#[skip_serializing_none]
1649#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1650#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1651#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_LIST_METHOD_NAME)))]
1652#[serde(rename_all = "camelCase")]
1653#[non_exhaustive]
1654pub struct ListSessionsRequest {
1655 #[serde(default)]
1657 pub cwd: Option<AbsolutePath>,
1658 #[serde(default)]
1660 pub cursor: Option<SessionListCursor>,
1661 #[serde_as(deserialize_as = "DefaultOnError")]
1667 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1668 #[serde(default)]
1669 #[serde(rename = "_meta")]
1670 pub meta: Option<Meta>,
1671}
1672
1673impl ListSessionsRequest {
1674 #[must_use]
1676 pub fn new() -> Self {
1677 Self::default()
1678 }
1679
1680 #[must_use]
1682 pub fn cwd(mut self, cwd: impl IntoOption<AbsolutePath>) -> Self {
1683 self.cwd = cwd.into_option();
1684 self
1685 }
1686
1687 #[must_use]
1689 pub fn cursor(mut self, cursor: impl IntoOption<SessionListCursor>) -> Self {
1690 self.cursor = cursor.into_option();
1691 self
1692 }
1693
1694 #[must_use]
1700 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1701 self.meta = meta.into_option();
1702 self
1703 }
1704}
1705
1706#[serde_as]
1708#[skip_serializing_none]
1709#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1710#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1711#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_LIST_METHOD_NAME)))]
1712#[serde(rename_all = "camelCase")]
1713#[non_exhaustive]
1714pub struct ListSessionsResponse {
1715 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1717 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1718 pub sessions: Vec<SessionInfo>,
1719 #[serde_as(deserialize_as = "DefaultOnError")]
1722 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1723 #[serde(default)]
1724 pub next_cursor: Option<SessionListCursor>,
1725 #[serde_as(deserialize_as = "DefaultOnError")]
1731 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1732 #[serde(default)]
1733 #[serde(rename = "_meta")]
1734 pub meta: Option<Meta>,
1735}
1736
1737impl ListSessionsResponse {
1738 #[must_use]
1740 pub fn new(sessions: Vec<SessionInfo>) -> Self {
1741 Self {
1742 sessions,
1743 next_cursor: None,
1744 meta: None,
1745 }
1746 }
1747
1748 #[must_use]
1750 pub fn next_cursor(mut self, next_cursor: impl IntoOption<SessionListCursor>) -> Self {
1751 self.next_cursor = next_cursor.into_option();
1752 self
1753 }
1754
1755 #[must_use]
1761 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1762 self.meta = meta.into_option();
1763 self
1764 }
1765}
1766
1767#[serde_as]
1773#[skip_serializing_none]
1774#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1775#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1776#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_DELETE_METHOD_NAME)))]
1777#[serde(rename_all = "camelCase")]
1778#[non_exhaustive]
1779pub struct DeleteSessionRequest {
1780 pub session_id: SessionId,
1782 #[serde_as(deserialize_as = "DefaultOnError")]
1788 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1789 #[serde(default)]
1790 #[serde(rename = "_meta")]
1791 pub meta: Option<Meta>,
1792}
1793
1794impl DeleteSessionRequest {
1795 #[must_use]
1797 pub fn new(session_id: impl Into<SessionId>) -> Self {
1798 Self {
1799 session_id: session_id.into(),
1800 meta: None,
1801 }
1802 }
1803
1804 #[must_use]
1810 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1811 self.meta = meta.into_option();
1812 self
1813 }
1814}
1815
1816#[serde_as]
1818#[skip_serializing_none]
1819#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1820#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1821#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_DELETE_METHOD_NAME)))]
1822#[serde(rename_all = "camelCase")]
1823#[non_exhaustive]
1824pub struct DeleteSessionResponse {
1825 #[serde_as(deserialize_as = "DefaultOnError")]
1831 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1832 #[serde(default)]
1833 #[serde(rename = "_meta")]
1834 pub meta: Option<Meta>,
1835}
1836
1837impl DeleteSessionResponse {
1838 #[must_use]
1840 pub fn new() -> Self {
1841 Self::default()
1842 }
1843
1844 #[must_use]
1850 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1851 self.meta = meta.into_option();
1852 self
1853 }
1854}
1855
1856#[serde_as]
1858#[skip_serializing_none]
1859#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1860#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1861#[serde(rename_all = "camelCase")]
1862#[non_exhaustive]
1863pub struct SessionInfo {
1864 pub session_id: SessionId,
1866 pub cwd: AbsolutePath,
1868 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1874 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1875 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1876 pub additional_directories: Vec<AbsolutePath>,
1877
1878 #[serde_as(deserialize_as = "DefaultOnError")]
1880 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1881 #[serde(default)]
1882 pub title: Option<String>,
1883 #[serde_as(deserialize_as = "DefaultOnError")]
1885 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "format" = "date-time")))]
1886 #[serde(default)]
1887 pub updated_at: Option<String>,
1888 #[serde_as(deserialize_as = "DefaultOnError")]
1894 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1895 #[serde(default)]
1896 #[serde(rename = "_meta")]
1897 pub meta: Option<Meta>,
1898}
1899
1900impl SessionInfo {
1901 #[must_use]
1903 pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<AbsolutePath>) -> Self {
1904 Self {
1905 session_id: session_id.into(),
1906 cwd: cwd.into(),
1907 additional_directories: vec![],
1908 title: None,
1909 updated_at: None,
1910 meta: None,
1911 }
1912 }
1913
1914 #[must_use]
1916 pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
1917 where
1918 I: IntoIterator<Item = P>,
1919 P: Into<AbsolutePath>,
1920 {
1921 self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
1922 self
1923 }
1924
1925 #[must_use]
1927 pub fn title(mut self, title: impl IntoOption<String>) -> Self {
1928 self.title = title.into_option();
1929 self
1930 }
1931
1932 #[must_use]
1934 pub fn updated_at(mut self, updated_at: impl IntoOption<String>) -> Self {
1935 self.updated_at = updated_at.into_option();
1936 self
1937 }
1938
1939 #[must_use]
1945 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1946 self.meta = meta.into_option();
1947 self
1948 }
1949}
1950
1951#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1955#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, From, Display)]
1956#[serde(transparent)]
1957#[from(forward)]
1958#[non_exhaustive]
1959pub struct SessionConfigId(pub Arc<str>);
1960
1961impl SessionConfigId {
1962 #[must_use]
1964 pub fn new(id: impl Into<Self>) -> Self {
1965 id.into()
1966 }
1967}
1968
1969#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1971#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, From, Display)]
1972#[serde(transparent)]
1973#[from(forward)]
1974#[non_exhaustive]
1975pub struct SessionConfigValueId(pub Arc<str>);
1976
1977impl SessionConfigValueId {
1978 #[must_use]
1980 pub fn new(id: impl Into<Self>) -> Self {
1981 id.into()
1982 }
1983}
1984
1985#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1987#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, From, Display)]
1988#[serde(transparent)]
1989#[from(forward)]
1990#[non_exhaustive]
1991pub struct SessionConfigGroupId(pub Arc<str>);
1992
1993impl SessionConfigGroupId {
1994 #[must_use]
1996 pub fn new(id: impl Into<Self>) -> Self {
1997 id.into()
1998 }
1999}
2000
2001#[serde_as]
2003#[skip_serializing_none]
2004#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2005#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2006#[serde(rename_all = "camelCase")]
2007#[non_exhaustive]
2008pub struct SessionConfigSelectOption {
2009 pub value: SessionConfigValueId,
2011 pub name: String,
2013 #[serde_as(deserialize_as = "DefaultOnError")]
2015 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2016 #[serde(default)]
2017 pub description: Option<String>,
2018 #[serde_as(deserialize_as = "DefaultOnError")]
2024 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2025 #[serde(default)]
2026 #[serde(rename = "_meta")]
2027 pub meta: Option<Meta>,
2028}
2029
2030impl SessionConfigSelectOption {
2031 #[must_use]
2033 pub fn new(value: impl Into<SessionConfigValueId>, name: impl Into<String>) -> Self {
2034 Self {
2035 value: value.into(),
2036 name: name.into(),
2037 description: None,
2038 meta: None,
2039 }
2040 }
2041
2042 #[must_use]
2044 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
2045 self.description = description.into_option();
2046 self
2047 }
2048
2049 #[must_use]
2055 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2056 self.meta = meta.into_option();
2057 self
2058 }
2059}
2060
2061#[serde_as]
2063#[skip_serializing_none]
2064#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2065#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2066#[serde(rename_all = "camelCase")]
2067#[non_exhaustive]
2068pub struct SessionConfigSelectGroup {
2069 pub group_id: SessionConfigGroupId,
2071 pub name: String,
2073 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
2075 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
2076 pub options: Vec<SessionConfigSelectOption>,
2077 #[serde_as(deserialize_as = "DefaultOnError")]
2083 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2084 #[serde(default)]
2085 #[serde(rename = "_meta")]
2086 pub meta: Option<Meta>,
2087}
2088
2089impl SessionConfigSelectGroup {
2090 #[must_use]
2092 pub fn new(
2093 group_id: impl Into<SessionConfigGroupId>,
2094 name: impl Into<String>,
2095 options: Vec<SessionConfigSelectOption>,
2096 ) -> Self {
2097 Self {
2098 group_id: group_id.into(),
2099 name: name.into(),
2100 options,
2101 meta: None,
2102 }
2103 }
2104
2105 #[must_use]
2111 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2112 self.meta = meta.into_option();
2113 self
2114 }
2115}
2116
2117#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2119#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2120#[serde(untagged)]
2121#[non_exhaustive]
2122pub enum SessionConfigSelectOptions {
2123 Ungrouped(Vec<SessionConfigSelectOption>),
2125 Grouped(Vec<SessionConfigSelectGroup>),
2127}
2128
2129impl From<Vec<SessionConfigSelectOption>> for SessionConfigSelectOptions {
2130 fn from(options: Vec<SessionConfigSelectOption>) -> Self {
2131 SessionConfigSelectOptions::Ungrouped(options)
2132 }
2133}
2134
2135impl From<Vec<SessionConfigSelectGroup>> for SessionConfigSelectOptions {
2136 fn from(groups: Vec<SessionConfigSelectGroup>) -> Self {
2137 SessionConfigSelectOptions::Grouped(groups)
2138 }
2139}
2140
2141#[skip_serializing_none]
2143#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2144#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2145#[serde(rename_all = "camelCase")]
2146#[non_exhaustive]
2147pub struct SessionConfigSelect {
2148 pub current_value: SessionConfigValueId,
2150 pub options: SessionConfigSelectOptions,
2152}
2153
2154impl SessionConfigSelect {
2155 #[must_use]
2157 pub fn new(
2158 current_value: impl Into<SessionConfigValueId>,
2159 options: impl Into<SessionConfigSelectOptions>,
2160 ) -> Self {
2161 Self {
2162 current_value: current_value.into(),
2163 options: options.into(),
2164 }
2165 }
2166}
2167
2168#[skip_serializing_none]
2170#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2171#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2172#[serde(rename_all = "camelCase")]
2173#[non_exhaustive]
2174pub struct SessionConfigBoolean {
2175 pub current_value: bool,
2177}
2178
2179impl SessionConfigBoolean {
2180 #[must_use]
2182 pub fn new(current_value: bool) -> Self {
2183 Self { current_value }
2184 }
2185}
2186
2187#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2197#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2198#[serde(rename_all = "snake_case")]
2199#[non_exhaustive]
2200pub enum SessionConfigOptionCategory {
2201 Mode,
2203 Model,
2205 ModelConfig,
2207 ThoughtLevel,
2209 #[serde(untagged)]
2215 Other(String),
2216}
2217
2218#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2220#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2221#[serde(tag = "type", rename_all = "snake_case")]
2222#[non_exhaustive]
2223pub enum SessionConfigKind {
2224 Select(SessionConfigSelect),
2226 Boolean(SessionConfigBoolean),
2228 #[serde(untagged)]
2238 Other(OtherSessionConfigKind),
2239}
2240
2241#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2243#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
2244#[cfg_attr(feature = "schemars", schemars(inline))]
2245#[cfg_attr(feature = "schemars", schemars(transform = other_session_config_kind_schema))]
2246#[serde(rename_all = "camelCase")]
2247#[non_exhaustive]
2248pub struct OtherSessionConfigKind {
2249 #[serde(rename = "type")]
2255 pub type_: String,
2256 #[serde(flatten)]
2258 pub fields: BTreeMap<String, serde_json::Value>,
2259}
2260
2261impl OtherSessionConfigKind {
2262 #[must_use]
2264 pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
2265 fields.remove("type");
2266 fields.remove("_meta");
2267 Self {
2268 type_: type_.into(),
2269 fields,
2270 }
2271 }
2272}
2273
2274impl<'de> Deserialize<'de> for OtherSessionConfigKind {
2275 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2276 where
2277 D: serde::Deserializer<'de>,
2278 {
2279 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
2280 let type_ = fields
2281 .remove("type")
2282 .ok_or_else(|| serde::de::Error::missing_field("type"))?;
2283 let serde_json::Value::String(type_) = type_ else {
2284 return Err(serde::de::Error::custom("`type` must be a string"));
2285 };
2286
2287 if is_known_session_config_kind_type(&type_) {
2288 return Err(serde::de::Error::custom(format!(
2289 "known session configuration option `{type_}` did not match its schema"
2290 )));
2291 }
2292
2293 Ok(Self { type_, fields })
2294 }
2295}
2296
2297fn is_known_session_config_kind_type(type_: &str) -> bool {
2298 matches!(type_, "select" | "boolean")
2299}
2300
2301#[cfg(feature = "schemars")]
2302fn other_session_config_kind_schema(schema: &mut Schema) {
2303 super::schema_util::reject_known_string_discriminators(schema, "type", &["select", "boolean"]);
2304}
2305
2306#[serde_as]
2308#[skip_serializing_none]
2309#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2310#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2311#[serde(rename_all = "camelCase")]
2312#[non_exhaustive]
2313pub struct SessionConfigOption {
2314 pub config_id: SessionConfigId,
2316 pub name: String,
2318 #[serde_as(deserialize_as = "DefaultOnError")]
2320 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2321 #[serde(default)]
2322 pub description: Option<String>,
2323 #[serde_as(deserialize_as = "DefaultOnError")]
2325 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2326 #[serde(default)]
2327 pub category: Option<SessionConfigOptionCategory>,
2328 #[serde(flatten)]
2330 pub kind: SessionConfigKind,
2331 #[serde_as(deserialize_as = "DefaultOnError")]
2337 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2338 #[serde(default)]
2339 #[serde(rename = "_meta")]
2340 pub meta: Option<Meta>,
2341}
2342
2343impl SessionConfigOption {
2344 #[must_use]
2346 pub fn new(
2347 config_id: impl Into<SessionConfigId>,
2348 name: impl Into<String>,
2349 kind: SessionConfigKind,
2350 ) -> Self {
2351 Self {
2352 config_id: config_id.into(),
2353 name: name.into(),
2354 description: None,
2355 category: None,
2356 kind,
2357 meta: None,
2358 }
2359 }
2360
2361 #[must_use]
2363 pub fn select(
2364 config_id: impl Into<SessionConfigId>,
2365 name: impl Into<String>,
2366 current_value: impl Into<SessionConfigValueId>,
2367 options: impl Into<SessionConfigSelectOptions>,
2368 ) -> Self {
2369 Self::new(
2370 config_id,
2371 name,
2372 SessionConfigKind::Select(SessionConfigSelect::new(current_value, options)),
2373 )
2374 }
2375
2376 #[must_use]
2378 pub fn boolean(
2379 config_id: impl Into<SessionConfigId>,
2380 name: impl Into<String>,
2381 current_value: bool,
2382 ) -> Self {
2383 Self::new(
2384 config_id,
2385 name,
2386 SessionConfigKind::Boolean(SessionConfigBoolean::new(current_value)),
2387 )
2388 }
2389
2390 #[must_use]
2392 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
2393 self.description = description.into_option();
2394 self
2395 }
2396
2397 #[must_use]
2399 pub fn category(mut self, category: impl IntoOption<SessionConfigOptionCategory>) -> Self {
2400 self.category = category.into_option();
2401 self
2402 }
2403
2404 #[must_use]
2410 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2411 self.meta = meta.into_option();
2412 self
2413 }
2414}
2415
2416#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2425#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
2426#[serde(tag = "type", rename_all = "snake_case")]
2427#[non_exhaustive]
2428pub enum SessionConfigOptionValue {
2429 Id {
2431 value: SessionConfigValueId,
2433 },
2434 Boolean {
2436 value: bool,
2438 },
2439 #[serde(untagged)]
2445 Other(OtherSessionConfigOptionValue),
2446}
2447
2448#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2450#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
2451#[cfg_attr(feature = "schemars", schemars(inline))]
2452#[cfg_attr(feature = "schemars", schemars(transform = other_session_config_option_value_schema))]
2453#[serde(rename_all = "camelCase")]
2454#[non_exhaustive]
2455pub struct OtherSessionConfigOptionValue {
2456 #[serde(rename = "type")]
2462 pub type_: String,
2463 pub value: serde_json::Value,
2465 #[serde(flatten)]
2467 pub fields: BTreeMap<String, serde_json::Value>,
2468}
2469
2470impl OtherSessionConfigOptionValue {
2471 #[must_use]
2473 pub fn new(
2474 type_: impl Into<String>,
2475 value: serde_json::Value,
2476 mut fields: BTreeMap<String, serde_json::Value>,
2477 ) -> Self {
2478 fields.remove("type");
2479 fields.remove("value");
2480 fields.remove("_meta");
2481 Self {
2482 type_: type_.into(),
2483 value,
2484 fields,
2485 }
2486 }
2487}
2488
2489impl<'de> Deserialize<'de> for OtherSessionConfigOptionValue {
2490 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2491 where
2492 D: serde::Deserializer<'de>,
2493 {
2494 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
2495 let type_ = fields
2496 .remove("type")
2497 .ok_or_else(|| serde::de::Error::missing_field("type"))?;
2498 let serde_json::Value::String(type_) = type_ else {
2499 return Err(serde::de::Error::custom("`type` must be a string"));
2500 };
2501
2502 if is_known_session_config_option_value_type(&type_) {
2503 return Err(serde::de::Error::custom(format!(
2504 "known session configuration option value `{type_}` did not match its schema"
2505 )));
2506 }
2507
2508 let value = fields
2509 .remove("value")
2510 .ok_or_else(|| serde::de::Error::missing_field("value"))?;
2511
2512 Ok(Self {
2513 type_,
2514 value,
2515 fields,
2516 })
2517 }
2518}
2519
2520impl<'de> Deserialize<'de> for SessionConfigOptionValue {
2521 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2522 where
2523 D: serde::Deserializer<'de>,
2524 {
2525 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
2526 let type_ = fields.remove("type");
2527 let value = fields
2528 .remove("value")
2529 .ok_or_else(|| serde::de::Error::missing_field("value"))?;
2530
2531 let type_ = type_.ok_or_else(|| serde::de::Error::missing_field("type"))?;
2532
2533 let serde_json::Value::String(type_) = type_ else {
2534 return Err(serde::de::Error::custom("`type` must be a string"));
2535 };
2536
2537 match type_.as_str() {
2538 "id" => {
2539 let value = serde_json::from_value(value).map_err(|error| {
2540 serde::de::Error::custom(format!(
2541 "`value` must be a string for `type: id`: {error}"
2542 ))
2543 })?;
2544 Ok(Self::Id { value })
2545 }
2546 "boolean" => {
2547 let value = serde_json::from_value(value).map_err(|error| {
2548 serde::de::Error::custom(format!(
2549 "`value` must be a boolean for `type: boolean`: {error}"
2550 ))
2551 })?;
2552 Ok(Self::Boolean { value })
2553 }
2554 _ => Ok(Self::Other(OtherSessionConfigOptionValue {
2555 type_,
2556 value,
2557 fields,
2558 })),
2559 }
2560 }
2561}
2562
2563fn is_known_session_config_option_value_type(type_: &str) -> bool {
2564 matches!(type_, "id" | "boolean")
2565}
2566
2567#[cfg(feature = "schemars")]
2568fn other_session_config_option_value_schema(schema: &mut Schema) {
2569 super::schema_util::reject_known_string_discriminators(schema, "type", &["id", "boolean"]);
2570}
2571
2572impl SessionConfigOptionValue {
2573 #[must_use]
2575 pub fn id(id: impl Into<SessionConfigValueId>) -> Self {
2576 Self::Id { value: id.into() }
2577 }
2578
2579 #[must_use]
2581 pub fn boolean(val: bool) -> Self {
2582 Self::Boolean { value: val }
2583 }
2584
2585 #[must_use]
2588 pub fn as_id(&self) -> Option<&SessionConfigValueId> {
2589 match self {
2590 Self::Id { value } => Some(value),
2591 _ => None,
2592 }
2593 }
2594
2595 #[must_use]
2597 pub fn as_bool(&self) -> Option<bool> {
2598 match self {
2599 Self::Boolean { value } => Some(*value),
2600 _ => None,
2601 }
2602 }
2603}
2604
2605impl From<SessionConfigValueId> for SessionConfigOptionValue {
2606 fn from(value: SessionConfigValueId) -> Self {
2607 Self::Id { value }
2608 }
2609}
2610
2611impl From<bool> for SessionConfigOptionValue {
2612 fn from(value: bool) -> Self {
2613 Self::Boolean { value }
2614 }
2615}
2616
2617impl From<&str> for SessionConfigOptionValue {
2618 fn from(value: &str) -> Self {
2619 Self::Id {
2620 value: SessionConfigValueId::new(value),
2621 }
2622 }
2623}
2624
2625#[serde_as]
2627#[skip_serializing_none]
2628#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2629#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2630#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_CONFIG_OPTION_METHOD_NAME)))]
2631#[serde(rename_all = "camelCase")]
2632#[non_exhaustive]
2633pub struct SetSessionConfigOptionRequest {
2634 pub session_id: SessionId,
2636 pub config_id: SessionConfigId,
2638 #[serde(flatten)]
2642 pub value: SessionConfigOptionValue,
2643 #[serde_as(deserialize_as = "DefaultOnError")]
2649 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2650 #[serde(default)]
2651 #[serde(rename = "_meta")]
2652 pub meta: Option<Meta>,
2653}
2654
2655impl SetSessionConfigOptionRequest {
2656 #[must_use]
2658 pub fn new(
2659 session_id: impl Into<SessionId>,
2660 config_id: impl Into<SessionConfigId>,
2661 value: impl Into<SessionConfigOptionValue>,
2662 ) -> Self {
2663 Self {
2664 session_id: session_id.into(),
2665 config_id: config_id.into(),
2666 value: value.into(),
2667 meta: None,
2668 }
2669 }
2670
2671 #[must_use]
2677 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2678 self.meta = meta.into_option();
2679 self
2680 }
2681}
2682
2683#[serde_as]
2685#[skip_serializing_none]
2686#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2687#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2688#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_CONFIG_OPTION_METHOD_NAME)))]
2689#[serde(rename_all = "camelCase")]
2690#[non_exhaustive]
2691pub struct SetSessionConfigOptionResponse {
2692 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
2694 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
2695 pub config_options: Vec<SessionConfigOption>,
2696 #[serde_as(deserialize_as = "DefaultOnError")]
2702 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2703 #[serde(default)]
2704 #[serde(rename = "_meta")]
2705 pub meta: Option<Meta>,
2706}
2707
2708impl SetSessionConfigOptionResponse {
2709 #[must_use]
2711 pub fn new(config_options: Vec<SessionConfigOption>) -> Self {
2712 Self {
2713 config_options,
2714 meta: None,
2715 }
2716 }
2717
2718 #[must_use]
2724 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2725 self.meta = meta.into_option();
2726 self
2727 }
2728}
2729
2730#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2739#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2740#[serde(tag = "type", rename_all = "snake_case")]
2741#[non_exhaustive]
2742pub enum McpServer {
2743 Http(McpServerHttp),
2747 #[cfg(feature = "unstable_mcp_over_acp")]
2756 Acp(McpServerAcp),
2757 Stdio(McpServerStdio),
2761 #[serde(untagged)]
2771 Other(OtherMcpServer),
2772}
2773
2774#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2776#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
2777#[cfg_attr(feature = "schemars", schemars(inline))]
2778#[cfg_attr(feature = "schemars", schemars(transform = other_mcp_server_schema))]
2779#[serde(rename_all = "camelCase")]
2780#[non_exhaustive]
2781pub struct OtherMcpServer {
2782 #[serde(rename = "type")]
2788 pub type_: String,
2789 #[serde(flatten)]
2791 pub fields: BTreeMap<String, serde_json::Value>,
2792}
2793
2794impl OtherMcpServer {
2795 #[must_use]
2797 pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
2798 fields.remove("type");
2799 Self {
2800 type_: type_.into(),
2801 fields,
2802 }
2803 }
2804}
2805
2806impl<'de> Deserialize<'de> for OtherMcpServer {
2807 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2808 where
2809 D: serde::Deserializer<'de>,
2810 {
2811 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
2812 let type_ = fields
2813 .remove("type")
2814 .ok_or_else(|| serde::de::Error::missing_field("type"))?;
2815 let serde_json::Value::String(type_) = type_ else {
2816 return Err(serde::de::Error::custom("`type` must be a string"));
2817 };
2818
2819 if is_known_mcp_server_type(&type_) {
2820 return Err(serde::de::Error::custom(format!(
2821 "known MCP server transport `{type_}` did not match its schema"
2822 )));
2823 }
2824
2825 Ok(Self { type_, fields })
2826 }
2827}
2828
2829fn is_known_mcp_server_type(type_: &str) -> bool {
2830 match type_ {
2831 "http" | "stdio" => true,
2832 #[cfg(feature = "unstable_mcp_over_acp")]
2833 "acp" => true,
2834 _ => false,
2835 }
2836}
2837
2838#[cfg(feature = "schemars")]
2839fn other_mcp_server_schema(schema: &mut Schema) {
2840 super::schema_util::reject_known_string_discriminators(
2841 schema,
2842 "type",
2843 &[
2844 "http",
2845 "stdio",
2846 #[cfg(feature = "unstable_mcp_over_acp")]
2847 "acp",
2848 ],
2849 );
2850}
2851
2852#[serde_as]
2854#[skip_serializing_none]
2855#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2856#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2857#[serde(rename_all = "camelCase")]
2858#[non_exhaustive]
2859pub struct McpServerHttp {
2860 pub name: String,
2862 #[cfg_attr(feature = "schemars", schemars(url))]
2864 pub url: String,
2865 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2867 pub headers: Vec<HttpHeader>,
2868 #[serde_as(deserialize_as = "DefaultOnError")]
2874 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2875 #[serde(default)]
2876 #[serde(rename = "_meta")]
2877 pub meta: Option<Meta>,
2878}
2879
2880impl McpServerHttp {
2881 #[must_use]
2883 pub fn new(name: impl Into<String>, url: impl Into<String>) -> Self {
2884 Self {
2885 name: name.into(),
2886 url: url.into(),
2887 headers: Vec::new(),
2888 meta: None,
2889 }
2890 }
2891
2892 #[must_use]
2894 pub fn headers(mut self, headers: Vec<HttpHeader>) -> Self {
2895 self.headers = headers;
2896 self
2897 }
2898
2899 #[must_use]
2905 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2906 self.meta = meta.into_option();
2907 self
2908 }
2909}
2910
2911#[cfg(feature = "unstable_mcp_over_acp")]
2921#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2922#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
2923#[serde(transparent)]
2924#[from(forward)]
2925#[non_exhaustive]
2926pub struct McpServerAcpId(pub Arc<str>);
2927
2928#[cfg(feature = "unstable_mcp_over_acp")]
2929impl McpServerAcpId {
2930 #[must_use]
2932 pub fn new(id: impl Into<Self>) -> Self {
2933 id.into()
2934 }
2935}
2936
2937#[serde_as]
2946#[skip_serializing_none]
2947#[cfg(feature = "unstable_mcp_over_acp")]
2948#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2949#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2950#[serde(rename_all = "camelCase")]
2951#[non_exhaustive]
2952pub struct McpServerAcp {
2953 pub name: String,
2955 pub server_id: McpServerAcpId,
2960 #[serde_as(deserialize_as = "DefaultOnError")]
2966 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2967 #[serde(default)]
2968 #[serde(rename = "_meta")]
2969 pub meta: Option<Meta>,
2970}
2971
2972#[cfg(feature = "unstable_mcp_over_acp")]
2973impl McpServerAcp {
2974 #[must_use]
2976 pub fn new(name: impl Into<String>, server_id: impl Into<McpServerAcpId>) -> Self {
2977 Self {
2978 name: name.into(),
2979 server_id: server_id.into(),
2980 meta: None,
2981 }
2982 }
2983
2984 #[must_use]
2990 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2991 self.meta = meta.into_option();
2992 self
2993 }
2994}
2995
2996#[serde_as]
2998#[skip_serializing_none]
2999#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3000#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3001#[serde(rename_all = "camelCase")]
3002#[non_exhaustive]
3003pub struct McpServerStdio {
3004 pub name: String,
3006 pub command: AbsolutePath,
3008 #[serde(default, skip_serializing_if = "Vec::is_empty")]
3010 pub args: Vec<String>,
3011 #[serde(default, skip_serializing_if = "Vec::is_empty")]
3013 pub env: Vec<EnvVariable>,
3014 #[serde_as(deserialize_as = "DefaultOnError")]
3020 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3021 #[serde(default)]
3022 #[serde(rename = "_meta")]
3023 pub meta: Option<Meta>,
3024}
3025
3026impl McpServerStdio {
3027 #[must_use]
3029 pub fn new(name: impl Into<String>, command: impl Into<AbsolutePath>) -> Self {
3030 Self {
3031 name: name.into(),
3032 command: command.into(),
3033 args: Vec::new(),
3034 env: Vec::new(),
3035 meta: None,
3036 }
3037 }
3038
3039 #[must_use]
3041 pub fn args(mut self, args: Vec<String>) -> Self {
3042 self.args = args;
3043 self
3044 }
3045
3046 #[must_use]
3048 pub fn env(mut self, env: Vec<EnvVariable>) -> Self {
3049 self.env = env;
3050 self
3051 }
3052
3053 #[must_use]
3059 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3060 self.meta = meta.into_option();
3061 self
3062 }
3063}
3064
3065#[serde_as]
3067#[skip_serializing_none]
3068#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3069#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3070#[serde(rename_all = "camelCase")]
3071#[non_exhaustive]
3072pub struct EnvVariable {
3073 pub name: String,
3075 pub value: String,
3077 #[serde_as(deserialize_as = "DefaultOnError")]
3083 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3084 #[serde(default)]
3085 #[serde(rename = "_meta")]
3086 pub meta: Option<Meta>,
3087}
3088
3089impl EnvVariable {
3090 #[must_use]
3092 pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
3093 Self {
3094 name: name.into(),
3095 value: value.into(),
3096 meta: None,
3097 }
3098 }
3099
3100 #[must_use]
3106 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3107 self.meta = meta.into_option();
3108 self
3109 }
3110}
3111
3112#[serde_as]
3114#[skip_serializing_none]
3115#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3116#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3117#[serde(rename_all = "camelCase")]
3118#[non_exhaustive]
3119pub struct HttpHeader {
3120 pub name: String,
3122 pub value: String,
3124 #[serde_as(deserialize_as = "DefaultOnError")]
3130 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3131 #[serde(default)]
3132 #[serde(rename = "_meta")]
3133 pub meta: Option<Meta>,
3134}
3135
3136impl HttpHeader {
3137 #[must_use]
3139 pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
3140 Self {
3141 name: name.into(),
3142 value: value.into(),
3143 meta: None,
3144 }
3145 }
3146
3147 #[must_use]
3153 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3154 self.meta = meta.into_option();
3155 self
3156 }
3157}
3158
3159#[serde_as]
3167#[skip_serializing_none]
3168#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3169#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
3170#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_PROMPT_METHOD_NAME)))]
3171#[serde(rename_all = "camelCase")]
3172#[non_exhaustive]
3173pub struct PromptRequest {
3174 pub session_id: SessionId,
3176 pub prompt: Vec<ContentBlock>,
3190 #[serde_as(deserialize_as = "DefaultOnError")]
3196 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3197 #[serde(default)]
3198 #[serde(rename = "_meta")]
3199 pub meta: Option<Meta>,
3200}
3201
3202impl PromptRequest {
3203 #[must_use]
3205 pub fn new(session_id: impl Into<SessionId>, prompt: Vec<ContentBlock>) -> Self {
3206 Self {
3207 session_id: session_id.into(),
3208 prompt,
3209 meta: None,
3210 }
3211 }
3212
3213 #[must_use]
3219 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3220 self.meta = meta.into_option();
3221 self
3222 }
3223}
3224
3225#[serde_as]
3232#[skip_serializing_none]
3233#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3234#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3235#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_PROMPT_METHOD_NAME)))]
3236#[serde(rename_all = "camelCase")]
3237#[non_exhaustive]
3238pub struct PromptResponse {
3239 #[serde_as(deserialize_as = "DefaultOnError")]
3245 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3246 #[serde(default)]
3247 #[serde(rename = "_meta")]
3248 pub meta: Option<Meta>,
3249}
3250
3251impl PromptResponse {
3252 #[must_use]
3254 pub fn new() -> Self {
3255 Self::default()
3256 }
3257
3258 #[must_use]
3264 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3265 self.meta = meta.into_option();
3266 self
3267 }
3268}
3269
3270#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3274#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
3275#[serde(rename_all = "snake_case")]
3276#[non_exhaustive]
3277pub enum StopReason {
3278 EndTurn,
3280 MaxTokens,
3282 MaxTurnRequests,
3285 Refusal,
3289 Cancelled,
3295 #[serde(untagged)]
3301 Other(String),
3302}
3303
3304#[cfg(feature = "unstable_end_turn_token_usage")]
3310#[serde_as]
3311#[skip_serializing_none]
3312#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3313#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3314#[serde(rename_all = "camelCase")]
3315#[non_exhaustive]
3316pub struct Usage {
3317 pub total_tokens: u64,
3319 pub input_tokens: u64,
3321 pub output_tokens: u64,
3323 #[serde_as(deserialize_as = "DefaultOnError")]
3325 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3326 #[serde(default)]
3327 pub thought_tokens: Option<u64>,
3328 #[serde_as(deserialize_as = "DefaultOnError")]
3330 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3331 #[serde(default)]
3332 pub cached_read_tokens: Option<u64>,
3333 #[serde_as(deserialize_as = "DefaultOnError")]
3335 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3336 #[serde(default)]
3337 pub cached_write_tokens: Option<u64>,
3338 #[serde_as(deserialize_as = "DefaultOnError")]
3344 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3345 #[serde(default)]
3346 #[serde(rename = "_meta")]
3347 pub meta: Option<Meta>,
3348}
3349
3350#[cfg(feature = "unstable_end_turn_token_usage")]
3351impl Usage {
3352 #[must_use]
3354 pub fn new(total_tokens: u64, input_tokens: u64, output_tokens: u64) -> Self {
3355 Self {
3356 total_tokens,
3357 input_tokens,
3358 output_tokens,
3359 thought_tokens: None,
3360 cached_read_tokens: None,
3361 cached_write_tokens: None,
3362 meta: None,
3363 }
3364 }
3365
3366 #[must_use]
3368 pub fn thought_tokens(mut self, thought_tokens: impl IntoOption<u64>) -> Self {
3369 self.thought_tokens = thought_tokens.into_option();
3370 self
3371 }
3372
3373 #[must_use]
3375 pub fn cached_read_tokens(mut self, cached_read_tokens: impl IntoOption<u64>) -> Self {
3376 self.cached_read_tokens = cached_read_tokens.into_option();
3377 self
3378 }
3379
3380 #[must_use]
3382 pub fn cached_write_tokens(mut self, cached_write_tokens: impl IntoOption<u64>) -> Self {
3383 self.cached_write_tokens = cached_write_tokens.into_option();
3384 self
3385 }
3386
3387 #[must_use]
3393 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3394 self.meta = meta.into_option();
3395 self
3396 }
3397}
3398
3399#[cfg(feature = "unstable_llm_providers")]
3412#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3413#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3414#[serde(rename_all = "snake_case")]
3415#[non_exhaustive]
3416#[expect(clippy::doc_markdown)]
3417pub enum LlmProtocol {
3418 Anthropic,
3420 #[serde(rename = "openai")]
3422 OpenAi,
3423 Azure,
3425 Vertex,
3427 Bedrock,
3429 #[serde(untagged)]
3435 Other(String),
3436}
3437
3438#[cfg(feature = "unstable_llm_providers")]
3444#[serde_as]
3445#[skip_serializing_none]
3446#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3447#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3448#[serde(rename_all = "camelCase")]
3449#[non_exhaustive]
3450pub struct ProviderCurrentConfig {
3451 pub api_type: LlmProtocol,
3453 #[cfg_attr(feature = "schemars", schemars(url))]
3455 pub base_url: String,
3456 #[serde_as(deserialize_as = "DefaultOnError")]
3462 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3463 #[serde(default)]
3464 #[serde(rename = "_meta")]
3465 pub meta: Option<Meta>,
3466}
3467
3468#[cfg(feature = "unstable_llm_providers")]
3469impl ProviderCurrentConfig {
3470 #[must_use]
3472 pub fn new(api_type: LlmProtocol, base_url: impl Into<String>) -> Self {
3473 Self {
3474 api_type,
3475 base_url: base_url.into(),
3476 meta: None,
3477 }
3478 }
3479
3480 #[must_use]
3486 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3487 self.meta = meta.into_option();
3488 self
3489 }
3490}
3491
3492#[cfg(feature = "unstable_llm_providers")]
3498#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3499#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
3500#[serde(transparent)]
3501#[from(forward)]
3502#[non_exhaustive]
3503pub struct ProviderId(pub Arc<str>);
3504
3505#[cfg(feature = "unstable_llm_providers")]
3506impl ProviderId {
3507 #[must_use]
3509 pub fn new(id: impl Into<Self>) -> Self {
3510 id.into()
3511 }
3512}
3513
3514#[cfg(feature = "unstable_llm_providers")]
3520#[serde_as]
3521#[skip_serializing_none]
3522#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3523#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3524#[serde(rename_all = "camelCase")]
3525#[non_exhaustive]
3526pub struct ProviderInfo {
3527 pub provider_id: ProviderId,
3529 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
3531 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
3532 pub supported: Vec<LlmProtocol>,
3533 pub required: bool,
3536 #[serde(default)]
3539 pub current: Option<ProviderCurrentConfig>,
3540 #[serde_as(deserialize_as = "DefaultOnError")]
3546 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3547 #[serde(default)]
3548 #[serde(rename = "_meta")]
3549 pub meta: Option<Meta>,
3550}
3551
3552#[cfg(feature = "unstable_llm_providers")]
3553impl ProviderInfo {
3554 #[must_use]
3556 pub fn new(
3557 provider_id: impl Into<ProviderId>,
3558 supported: Vec<LlmProtocol>,
3559 required: bool,
3560 current: impl IntoOption<ProviderCurrentConfig>,
3561 ) -> Self {
3562 Self {
3563 provider_id: provider_id.into(),
3564 supported,
3565 required,
3566 current: current.into_option(),
3567 meta: None,
3568 }
3569 }
3570
3571 #[must_use]
3577 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3578 self.meta = meta.into_option();
3579 self
3580 }
3581}
3582
3583#[cfg(feature = "unstable_llm_providers")]
3589#[serde_as]
3590#[skip_serializing_none]
3591#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3592#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3593#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_LIST_METHOD_NAME)))]
3594#[serde(rename_all = "camelCase")]
3595#[non_exhaustive]
3596pub struct ListProvidersRequest {
3597 #[serde_as(deserialize_as = "DefaultOnError")]
3603 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3604 #[serde(default)]
3605 #[serde(rename = "_meta")]
3606 pub meta: Option<Meta>,
3607}
3608
3609#[cfg(feature = "unstable_llm_providers")]
3610impl ListProvidersRequest {
3611 #[must_use]
3613 pub fn new() -> Self {
3614 Self::default()
3615 }
3616
3617 #[must_use]
3623 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3624 self.meta = meta.into_option();
3625 self
3626 }
3627}
3628
3629#[cfg(feature = "unstable_llm_providers")]
3635#[serde_as]
3636#[skip_serializing_none]
3637#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3638#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3639#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_LIST_METHOD_NAME)))]
3640#[serde(rename_all = "camelCase")]
3641#[non_exhaustive]
3642pub struct ListProvidersResponse {
3643 pub providers: Vec<ProviderInfo>,
3645 #[serde_as(deserialize_as = "DefaultOnError")]
3651 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3652 #[serde(default)]
3653 #[serde(rename = "_meta")]
3654 pub meta: Option<Meta>,
3655}
3656
3657#[cfg(feature = "unstable_llm_providers")]
3658impl ListProvidersResponse {
3659 #[must_use]
3661 pub fn new(providers: Vec<ProviderInfo>) -> Self {
3662 Self {
3663 providers,
3664 meta: None,
3665 }
3666 }
3667
3668 #[must_use]
3674 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3675 self.meta = meta.into_option();
3676 self
3677 }
3678}
3679
3680#[cfg(feature = "unstable_llm_providers")]
3688#[serde_as]
3689#[skip_serializing_none]
3690#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3691#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
3692#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_SET_METHOD_NAME)))]
3693#[serde(rename_all = "camelCase")]
3694#[non_exhaustive]
3695pub struct SetProviderRequest {
3696 pub provider_id: ProviderId,
3698 pub api_type: LlmProtocol,
3700 #[cfg_attr(feature = "schemars", schemars(url))]
3702 pub base_url: String,
3703 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
3706 pub headers: HashMap<String, String>,
3707 #[serde_as(deserialize_as = "DefaultOnError")]
3713 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3714 #[serde(default)]
3715 #[serde(rename = "_meta")]
3716 pub meta: Option<Meta>,
3717}
3718
3719#[cfg(feature = "unstable_llm_providers")]
3720impl SetProviderRequest {
3721 #[must_use]
3723 pub fn new(
3724 provider_id: impl Into<ProviderId>,
3725 api_type: LlmProtocol,
3726 base_url: impl Into<String>,
3727 ) -> Self {
3728 Self {
3729 provider_id: provider_id.into(),
3730 api_type,
3731 base_url: base_url.into(),
3732 headers: HashMap::new(),
3733 meta: None,
3734 }
3735 }
3736
3737 #[must_use]
3740 pub fn headers(mut self, headers: HashMap<String, String>) -> Self {
3741 self.headers = headers;
3742 self
3743 }
3744
3745 #[must_use]
3751 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3752 self.meta = meta.into_option();
3753 self
3754 }
3755}
3756
3757#[cfg(feature = "unstable_llm_providers")]
3763#[serde_as]
3764#[skip_serializing_none]
3765#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3766#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3767#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_SET_METHOD_NAME)))]
3768#[serde(rename_all = "camelCase")]
3769#[non_exhaustive]
3770pub struct SetProviderResponse {
3771 #[serde_as(deserialize_as = "DefaultOnError")]
3777 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3778 #[serde(default)]
3779 #[serde(rename = "_meta")]
3780 pub meta: Option<Meta>,
3781}
3782
3783#[cfg(feature = "unstable_llm_providers")]
3784impl SetProviderResponse {
3785 #[must_use]
3787 pub fn new() -> Self {
3788 Self::default()
3789 }
3790
3791 #[must_use]
3797 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3798 self.meta = meta.into_option();
3799 self
3800 }
3801}
3802
3803#[cfg(feature = "unstable_llm_providers")]
3809#[serde_as]
3810#[skip_serializing_none]
3811#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3812#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3813#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_DISABLE_METHOD_NAME)))]
3814#[serde(rename_all = "camelCase")]
3815#[non_exhaustive]
3816pub struct DisableProviderRequest {
3817 pub provider_id: ProviderId,
3819 #[serde_as(deserialize_as = "DefaultOnError")]
3825 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3826 #[serde(default)]
3827 #[serde(rename = "_meta")]
3828 pub meta: Option<Meta>,
3829}
3830
3831#[cfg(feature = "unstable_llm_providers")]
3832impl DisableProviderRequest {
3833 #[must_use]
3835 pub fn new(provider_id: impl Into<ProviderId>) -> Self {
3836 Self {
3837 provider_id: provider_id.into(),
3838 meta: None,
3839 }
3840 }
3841
3842 #[must_use]
3848 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3849 self.meta = meta.into_option();
3850 self
3851 }
3852}
3853
3854#[cfg(feature = "unstable_llm_providers")]
3860#[serde_as]
3861#[skip_serializing_none]
3862#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3863#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3864#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_DISABLE_METHOD_NAME)))]
3865#[serde(rename_all = "camelCase")]
3866#[non_exhaustive]
3867pub struct DisableProviderResponse {
3868 #[serde_as(deserialize_as = "DefaultOnError")]
3874 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3875 #[serde(default)]
3876 #[serde(rename = "_meta")]
3877 pub meta: Option<Meta>,
3878}
3879
3880#[cfg(feature = "unstable_llm_providers")]
3881impl DisableProviderResponse {
3882 #[must_use]
3884 pub fn new() -> Self {
3885 Self::default()
3886 }
3887
3888 #[must_use]
3894 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3895 self.meta = meta.into_option();
3896 self
3897 }
3898}
3899
3900#[serde_as]
3909#[skip_serializing_none]
3910#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3911#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3912#[serde(rename_all = "camelCase")]
3913#[non_exhaustive]
3914pub struct AgentCapabilities {
3915 #[serde_as(deserialize_as = "DefaultOnError")]
3922 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3923 #[serde(default)]
3924 pub session: Option<SessionCapabilities>,
3925 #[serde_as(deserialize_as = "DefaultOnError")]
3932 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3933 #[serde(default)]
3934 pub auth: Option<AgentAuthCapabilities>,
3935 #[cfg(feature = "unstable_llm_providers")]
3944 #[serde_as(deserialize_as = "DefaultOnError")]
3945 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3946 #[serde(default)]
3947 pub providers: Option<ProvidersCapabilities>,
3948 #[cfg(feature = "unstable_nes")]
3957 #[serde_as(deserialize_as = "DefaultOnError")]
3958 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3959 #[serde(default)]
3960 pub nes: Option<NesCapabilities>,
3961 #[cfg(feature = "unstable_nes")]
3967 #[serde_as(deserialize_as = "DefaultOnError")]
3968 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3969 #[serde(default)]
3970 pub position_encoding: Option<PositionEncodingKind>,
3971 #[serde_as(deserialize_as = "DefaultOnError")]
3977 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3978 #[serde(default)]
3979 #[serde(rename = "_meta")]
3980 pub meta: Option<Meta>,
3981}
3982
3983impl AgentCapabilities {
3984 #[must_use]
3986 pub fn new() -> Self {
3987 Self::default()
3988 }
3989
3990 #[must_use]
3997 pub fn session(mut self, session: impl IntoOption<SessionCapabilities>) -> Self {
3998 self.session = session.into_option();
3999 self
4000 }
4001
4002 #[must_use]
4006 pub fn auth(mut self, auth: impl IntoOption<AgentAuthCapabilities>) -> Self {
4007 self.auth = auth.into_option();
4008 self
4009 }
4010
4011 #[cfg(feature = "unstable_llm_providers")]
4017 #[must_use]
4018 pub fn providers(mut self, providers: impl IntoOption<ProvidersCapabilities>) -> Self {
4019 self.providers = providers.into_option();
4020 self
4021 }
4022
4023 #[cfg(feature = "unstable_nes")]
4029 #[must_use]
4030 pub fn nes(mut self, nes: impl IntoOption<NesCapabilities>) -> Self {
4031 self.nes = nes.into_option();
4032 self
4033 }
4034
4035 #[cfg(feature = "unstable_nes")]
4039 #[must_use]
4040 pub fn position_encoding(
4041 mut self,
4042 position_encoding: impl IntoOption<PositionEncodingKind>,
4043 ) -> Self {
4044 self.position_encoding = position_encoding.into_option();
4045 self
4046 }
4047
4048 #[must_use]
4054 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4055 self.meta = meta.into_option();
4056 self
4057 }
4058}
4059
4060#[cfg(feature = "unstable_llm_providers")]
4068#[serde_as]
4069#[skip_serializing_none]
4070#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4071#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4072#[non_exhaustive]
4073pub struct ProvidersCapabilities {
4074 #[serde_as(deserialize_as = "DefaultOnError")]
4080 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4081 #[serde(default)]
4082 #[serde(rename = "_meta")]
4083 pub meta: Option<Meta>,
4084}
4085
4086#[cfg(feature = "unstable_llm_providers")]
4087impl ProvidersCapabilities {
4088 #[must_use]
4090 pub fn new() -> Self {
4091 Self::default()
4092 }
4093
4094 #[must_use]
4100 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4101 self.meta = meta.into_option();
4102 self
4103 }
4104}
4105
4106#[serde_as]
4118#[skip_serializing_none]
4119#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4120#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4121#[serde(rename_all = "camelCase")]
4122#[non_exhaustive]
4123pub struct SessionCapabilities {
4124 #[serde_as(deserialize_as = "DefaultOnError")]
4130 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4131 #[serde(default)]
4132 pub prompt: Option<PromptCapabilities>,
4133 #[serde_as(deserialize_as = "DefaultOnError")]
4138 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4139 #[serde(default)]
4140 pub mcp: Option<McpCapabilities>,
4141 #[serde_as(deserialize_as = "DefaultOnError")]
4146 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4147 #[serde(default)]
4148 pub delete: Option<SessionDeleteCapabilities>,
4149 #[serde_as(deserialize_as = "DefaultOnError")]
4158 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4159 #[serde(default)]
4160 pub additional_directories: Option<SessionAdditionalDirectoriesCapabilities>,
4161 #[cfg(feature = "unstable_session_fork")]
4170 #[serde_as(deserialize_as = "DefaultOnError")]
4171 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4172 #[serde(default)]
4173 pub fork: Option<SessionForkCapabilities>,
4174 #[serde_as(deserialize_as = "DefaultOnError")]
4180 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4181 #[serde(default)]
4182 #[serde(rename = "_meta")]
4183 pub meta: Option<Meta>,
4184}
4185
4186impl SessionCapabilities {
4187 #[must_use]
4189 pub fn new() -> Self {
4190 Self::default()
4191 }
4192
4193 #[must_use]
4199 pub fn prompt(mut self, prompt: impl IntoOption<PromptCapabilities>) -> Self {
4200 self.prompt = prompt.into_option();
4201 self
4202 }
4203
4204 #[must_use]
4209 pub fn mcp(mut self, mcp: impl IntoOption<McpCapabilities>) -> Self {
4210 self.mcp = mcp.into_option();
4211 self
4212 }
4213
4214 #[must_use]
4219 pub fn delete(mut self, delete: impl IntoOption<SessionDeleteCapabilities>) -> Self {
4220 self.delete = delete.into_option();
4221 self
4222 }
4223
4224 #[must_use]
4233 pub fn additional_directories(
4234 mut self,
4235 additional_directories: impl IntoOption<SessionAdditionalDirectoriesCapabilities>,
4236 ) -> Self {
4237 self.additional_directories = additional_directories.into_option();
4238 self
4239 }
4240
4241 #[cfg(feature = "unstable_session_fork")]
4242 #[must_use]
4247 pub fn fork(mut self, fork: impl IntoOption<SessionForkCapabilities>) -> Self {
4248 self.fork = fork.into_option();
4249 self
4250 }
4251
4252 #[must_use]
4258 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4259 self.meta = meta.into_option();
4260 self
4261 }
4262}
4263
4264#[serde_as]
4268#[skip_serializing_none]
4269#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4270#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4271#[non_exhaustive]
4272pub struct SessionDeleteCapabilities {
4273 #[serde_as(deserialize_as = "DefaultOnError")]
4279 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4280 #[serde(default)]
4281 #[serde(rename = "_meta")]
4282 pub meta: Option<Meta>,
4283}
4284
4285impl SessionDeleteCapabilities {
4286 #[must_use]
4288 pub fn new() -> Self {
4289 Self::default()
4290 }
4291
4292 #[must_use]
4298 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4299 self.meta = meta.into_option();
4300 self
4301 }
4302}
4303
4304#[serde_as]
4311#[skip_serializing_none]
4312#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4313#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4314#[non_exhaustive]
4315pub struct SessionAdditionalDirectoriesCapabilities {
4316 #[serde_as(deserialize_as = "DefaultOnError")]
4322 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4323 #[serde(default)]
4324 #[serde(rename = "_meta")]
4325 pub meta: Option<Meta>,
4326}
4327
4328impl SessionAdditionalDirectoriesCapabilities {
4329 #[must_use]
4331 pub fn new() -> Self {
4332 Self::default()
4333 }
4334
4335 #[must_use]
4341 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4342 self.meta = meta.into_option();
4343 self
4344 }
4345}
4346
4347#[cfg(feature = "unstable_session_fork")]
4355#[serde_as]
4356#[skip_serializing_none]
4357#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4358#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4359#[non_exhaustive]
4360pub struct SessionForkCapabilities {
4361 #[serde_as(deserialize_as = "DefaultOnError")]
4367 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4368 #[serde(default)]
4369 #[serde(rename = "_meta")]
4370 pub meta: Option<Meta>,
4371}
4372
4373#[cfg(feature = "unstable_session_fork")]
4374impl SessionForkCapabilities {
4375 #[must_use]
4377 pub fn new() -> Self {
4378 Self::default()
4379 }
4380
4381 #[must_use]
4387 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4388 self.meta = meta.into_option();
4389 self
4390 }
4391}
4392
4393#[serde_as]
4406#[skip_serializing_none]
4407#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4408#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4409#[serde(rename_all = "camelCase")]
4410#[non_exhaustive]
4411pub struct PromptCapabilities {
4412 #[serde_as(deserialize_as = "DefaultOnError")]
4417 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4418 #[serde(default)]
4419 pub image: Option<PromptImageCapabilities>,
4420 #[serde_as(deserialize_as = "DefaultOnError")]
4425 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4426 #[serde(default)]
4427 pub audio: Option<PromptAudioCapabilities>,
4428 #[serde_as(deserialize_as = "DefaultOnError")]
4436 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4437 #[serde(default)]
4438 pub embedded_context: Option<PromptEmbeddedContextCapabilities>,
4439 #[serde_as(deserialize_as = "DefaultOnError")]
4445 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4446 #[serde(default)]
4447 #[serde(rename = "_meta")]
4448 pub meta: Option<Meta>,
4449}
4450
4451impl PromptCapabilities {
4452 #[must_use]
4454 pub fn new() -> Self {
4455 Self::default()
4456 }
4457
4458 #[must_use]
4463 pub fn image(mut self, image: impl IntoOption<PromptImageCapabilities>) -> Self {
4464 self.image = image.into_option();
4465 self
4466 }
4467
4468 #[must_use]
4473 pub fn audio(mut self, audio: impl IntoOption<PromptAudioCapabilities>) -> Self {
4474 self.audio = audio.into_option();
4475 self
4476 }
4477
4478 #[must_use]
4486 pub fn embedded_context(
4487 mut self,
4488 embedded_context: impl IntoOption<PromptEmbeddedContextCapabilities>,
4489 ) -> Self {
4490 self.embedded_context = embedded_context.into_option();
4491 self
4492 }
4493
4494 #[must_use]
4500 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4501 self.meta = meta.into_option();
4502 self
4503 }
4504}
4505
4506#[serde_as]
4510#[skip_serializing_none]
4511#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4512#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4513#[non_exhaustive]
4514pub struct PromptImageCapabilities {
4515 #[serde_as(deserialize_as = "DefaultOnError")]
4521 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4522 #[serde(default)]
4523 #[serde(rename = "_meta")]
4524 pub meta: Option<Meta>,
4525}
4526
4527impl PromptImageCapabilities {
4528 #[must_use]
4530 pub fn new() -> Self {
4531 Self::default()
4532 }
4533
4534 #[must_use]
4540 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4541 self.meta = meta.into_option();
4542 self
4543 }
4544}
4545
4546#[serde_as]
4550#[skip_serializing_none]
4551#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4552#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4553#[non_exhaustive]
4554pub struct PromptAudioCapabilities {
4555 #[serde_as(deserialize_as = "DefaultOnError")]
4561 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4562 #[serde(default)]
4563 #[serde(rename = "_meta")]
4564 pub meta: Option<Meta>,
4565}
4566
4567impl PromptAudioCapabilities {
4568 #[must_use]
4570 pub fn new() -> Self {
4571 Self::default()
4572 }
4573
4574 #[must_use]
4580 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4581 self.meta = meta.into_option();
4582 self
4583 }
4584}
4585
4586#[serde_as]
4590#[skip_serializing_none]
4591#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4592#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4593#[non_exhaustive]
4594pub struct PromptEmbeddedContextCapabilities {
4595 #[serde_as(deserialize_as = "DefaultOnError")]
4601 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4602 #[serde(default)]
4603 #[serde(rename = "_meta")]
4604 pub meta: Option<Meta>,
4605}
4606
4607impl PromptEmbeddedContextCapabilities {
4608 #[must_use]
4610 pub fn new() -> Self {
4611 Self::default()
4612 }
4613
4614 #[must_use]
4620 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4621 self.meta = meta.into_option();
4622 self
4623 }
4624}
4625
4626#[serde_as]
4628#[skip_serializing_none]
4629#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4630#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4631#[serde(rename_all = "camelCase")]
4632#[non_exhaustive]
4633pub struct McpCapabilities {
4634 #[serde_as(deserialize_as = "DefaultOnError")]
4639 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4640 #[serde(default)]
4641 pub stdio: Option<McpStdioCapabilities>,
4642 #[serde_as(deserialize_as = "DefaultOnError")]
4647 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4648 #[serde(default)]
4649 pub http: Option<McpHttpCapabilities>,
4650 #[cfg(feature = "unstable_mcp_over_acp")]
4659 #[serde_as(deserialize_as = "DefaultOnError")]
4660 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4661 #[serde(default)]
4662 pub acp: Option<McpAcpCapabilities>,
4663 #[serde_as(deserialize_as = "DefaultOnError")]
4669 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4670 #[serde(default)]
4671 #[serde(rename = "_meta")]
4672 pub meta: Option<Meta>,
4673}
4674
4675impl McpCapabilities {
4676 #[must_use]
4678 pub fn new() -> Self {
4679 Self::default()
4680 }
4681
4682 #[must_use]
4687 pub fn stdio(mut self, stdio: impl IntoOption<McpStdioCapabilities>) -> Self {
4688 self.stdio = stdio.into_option();
4689 self
4690 }
4691
4692 #[must_use]
4697 pub fn http(mut self, http: impl IntoOption<McpHttpCapabilities>) -> Self {
4698 self.http = http.into_option();
4699 self
4700 }
4701
4702 #[cfg(feature = "unstable_mcp_over_acp")]
4708 #[must_use]
4712 pub fn acp(mut self, acp: impl IntoOption<McpAcpCapabilities>) -> Self {
4713 self.acp = acp.into_option();
4714 self
4715 }
4716
4717 #[must_use]
4723 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4724 self.meta = meta.into_option();
4725 self
4726 }
4727}
4728
4729#[serde_as]
4733#[skip_serializing_none]
4734#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4735#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4736#[non_exhaustive]
4737pub struct McpStdioCapabilities {
4738 #[serde_as(deserialize_as = "DefaultOnError")]
4744 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4745 #[serde(default)]
4746 #[serde(rename = "_meta")]
4747 pub meta: Option<Meta>,
4748}
4749
4750impl McpStdioCapabilities {
4751 #[must_use]
4753 pub fn new() -> Self {
4754 Self::default()
4755 }
4756
4757 #[must_use]
4763 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4764 self.meta = meta.into_option();
4765 self
4766 }
4767}
4768
4769#[serde_as]
4773#[skip_serializing_none]
4774#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4775#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4776#[non_exhaustive]
4777pub struct McpHttpCapabilities {
4778 #[serde_as(deserialize_as = "DefaultOnError")]
4784 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4785 #[serde(default)]
4786 #[serde(rename = "_meta")]
4787 pub meta: Option<Meta>,
4788}
4789
4790impl McpHttpCapabilities {
4791 #[must_use]
4793 pub fn new() -> Self {
4794 Self::default()
4795 }
4796
4797 #[must_use]
4803 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4804 self.meta = meta.into_option();
4805 self
4806 }
4807}
4808
4809#[cfg(feature = "unstable_mcp_over_acp")]
4817#[serde_as]
4818#[skip_serializing_none]
4819#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4820#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4821#[non_exhaustive]
4822pub struct McpAcpCapabilities {
4823 #[serde_as(deserialize_as = "DefaultOnError")]
4829 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4830 #[serde(default)]
4831 #[serde(rename = "_meta")]
4832 pub meta: Option<Meta>,
4833}
4834
4835#[cfg(feature = "unstable_mcp_over_acp")]
4836impl McpAcpCapabilities {
4837 #[must_use]
4839 pub fn new() -> Self {
4840 Self::default()
4841 }
4842
4843 #[must_use]
4849 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4850 self.meta = meta.into_option();
4851 self
4852 }
4853}
4854
4855#[serde_as]
4859#[skip_serializing_none]
4860#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4861#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4862#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_CANCEL_METHOD_NAME)))]
4863#[serde(rename_all = "camelCase")]
4864#[non_exhaustive]
4865pub struct CancelSessionNotification {
4866 pub session_id: SessionId,
4868 #[serde_as(deserialize_as = "DefaultOnError")]
4874 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4875 #[serde(default)]
4876 #[serde(rename = "_meta")]
4877 pub meta: Option<Meta>,
4878}
4879
4880impl CancelSessionNotification {
4881 #[must_use]
4883 pub fn new(session_id: impl Into<SessionId>) -> Self {
4884 Self {
4885 session_id: session_id.into(),
4886 meta: None,
4887 }
4888 }
4889
4890 #[must_use]
4896 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4897 self.meta = meta.into_option();
4898 self
4899 }
4900}
4901
4902#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4908#[non_exhaustive]
4909pub struct AgentMethodNames {
4910 pub initialize: &'static str,
4912 pub auth_login: &'static str,
4914 #[cfg(feature = "unstable_llm_providers")]
4916 pub providers_list: &'static str,
4917 #[cfg(feature = "unstable_llm_providers")]
4919 pub providers_set: &'static str,
4920 #[cfg(feature = "unstable_llm_providers")]
4922 pub providers_disable: &'static str,
4923 pub session_new: &'static str,
4925 pub session_set_config_option: &'static str,
4927 pub session_prompt: &'static str,
4929 pub session_cancel: &'static str,
4931 #[cfg(feature = "unstable_mcp_over_acp")]
4933 pub mcp_message: &'static str,
4934 pub session_list: &'static str,
4936 pub session_delete: &'static str,
4938 #[cfg(feature = "unstable_session_fork")]
4940 pub session_fork: &'static str,
4941 pub session_resume: &'static str,
4943 pub session_close: &'static str,
4945 pub auth_logout: &'static str,
4947 #[cfg(feature = "unstable_nes")]
4949 pub nes_start: &'static str,
4950 #[cfg(feature = "unstable_nes")]
4952 pub nes_suggest: &'static str,
4953 #[cfg(feature = "unstable_nes")]
4955 pub nes_accept: &'static str,
4956 #[cfg(feature = "unstable_nes")]
4958 pub nes_reject: &'static str,
4959 #[cfg(feature = "unstable_nes")]
4961 pub nes_close: &'static str,
4962 #[cfg(feature = "unstable_nes")]
4964 pub document_did_open: &'static str,
4965 #[cfg(feature = "unstable_nes")]
4967 pub document_did_change: &'static str,
4968 #[cfg(feature = "unstable_nes")]
4970 pub document_did_close: &'static str,
4971 #[cfg(feature = "unstable_nes")]
4973 pub document_did_save: &'static str,
4974 #[cfg(feature = "unstable_nes")]
4976 pub document_did_focus: &'static str,
4977}
4978
4979pub const AGENT_METHOD_NAMES: AgentMethodNames = AgentMethodNames {
4981 initialize: INITIALIZE_METHOD_NAME,
4982 auth_login: AUTH_LOGIN_METHOD_NAME,
4983 #[cfg(feature = "unstable_llm_providers")]
4984 providers_list: PROVIDERS_LIST_METHOD_NAME,
4985 #[cfg(feature = "unstable_llm_providers")]
4986 providers_set: PROVIDERS_SET_METHOD_NAME,
4987 #[cfg(feature = "unstable_llm_providers")]
4988 providers_disable: PROVIDERS_DISABLE_METHOD_NAME,
4989 session_new: SESSION_NEW_METHOD_NAME,
4990 session_set_config_option: SESSION_SET_CONFIG_OPTION_METHOD_NAME,
4991 session_prompt: SESSION_PROMPT_METHOD_NAME,
4992 session_cancel: SESSION_CANCEL_METHOD_NAME,
4993 #[cfg(feature = "unstable_mcp_over_acp")]
4994 mcp_message: MCP_MESSAGE_METHOD_NAME,
4995 session_list: SESSION_LIST_METHOD_NAME,
4996 session_delete: SESSION_DELETE_METHOD_NAME,
4997 #[cfg(feature = "unstable_session_fork")]
4998 session_fork: SESSION_FORK_METHOD_NAME,
4999 session_resume: SESSION_RESUME_METHOD_NAME,
5000 session_close: SESSION_CLOSE_METHOD_NAME,
5001 auth_logout: AUTH_LOGOUT_METHOD_NAME,
5002 #[cfg(feature = "unstable_nes")]
5003 nes_start: NES_START_METHOD_NAME,
5004 #[cfg(feature = "unstable_nes")]
5005 nes_suggest: NES_SUGGEST_METHOD_NAME,
5006 #[cfg(feature = "unstable_nes")]
5007 nes_accept: NES_ACCEPT_METHOD_NAME,
5008 #[cfg(feature = "unstable_nes")]
5009 nes_reject: NES_REJECT_METHOD_NAME,
5010 #[cfg(feature = "unstable_nes")]
5011 nes_close: NES_CLOSE_METHOD_NAME,
5012 #[cfg(feature = "unstable_nes")]
5013 document_did_open: DOCUMENT_DID_OPEN_METHOD_NAME,
5014 #[cfg(feature = "unstable_nes")]
5015 document_did_change: DOCUMENT_DID_CHANGE_METHOD_NAME,
5016 #[cfg(feature = "unstable_nes")]
5017 document_did_close: DOCUMENT_DID_CLOSE_METHOD_NAME,
5018 #[cfg(feature = "unstable_nes")]
5019 document_did_save: DOCUMENT_DID_SAVE_METHOD_NAME,
5020 #[cfg(feature = "unstable_nes")]
5021 document_did_focus: DOCUMENT_DID_FOCUS_METHOD_NAME,
5022};
5023
5024pub(crate) const INITIALIZE_METHOD_NAME: &str = "initialize";
5026pub(crate) const AUTH_LOGIN_METHOD_NAME: &str = "auth/login";
5028#[cfg(feature = "unstable_llm_providers")]
5030pub(crate) const PROVIDERS_LIST_METHOD_NAME: &str = "providers/list";
5031#[cfg(feature = "unstable_llm_providers")]
5033pub(crate) const PROVIDERS_SET_METHOD_NAME: &str = "providers/set";
5034#[cfg(feature = "unstable_llm_providers")]
5036pub(crate) const PROVIDERS_DISABLE_METHOD_NAME: &str = "providers/disable";
5037pub(crate) const SESSION_NEW_METHOD_NAME: &str = "session/new";
5039pub(crate) const SESSION_SET_CONFIG_OPTION_METHOD_NAME: &str = "session/set_config_option";
5041pub(crate) const SESSION_PROMPT_METHOD_NAME: &str = "session/prompt";
5043pub(crate) const SESSION_CANCEL_METHOD_NAME: &str = "session/cancel";
5045pub(crate) const SESSION_LIST_METHOD_NAME: &str = "session/list";
5047pub(crate) const SESSION_DELETE_METHOD_NAME: &str = "session/delete";
5049#[cfg(feature = "unstable_session_fork")]
5051pub(crate) const SESSION_FORK_METHOD_NAME: &str = "session/fork";
5052pub(crate) const SESSION_RESUME_METHOD_NAME: &str = "session/resume";
5054pub(crate) const SESSION_CLOSE_METHOD_NAME: &str = "session/close";
5056pub(crate) const AUTH_LOGOUT_METHOD_NAME: &str = "auth/logout";
5058
5059#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5066#[derive(Clone, Debug, Serialize, Deserialize)]
5067#[serde(untagged)]
5068#[cfg_attr(feature = "schemars", schemars(inline))]
5069#[non_exhaustive]
5070pub enum ClientRequest {
5071 InitializeRequest(Box<InitializeRequest>),
5082 LoginAuthRequest(Box<LoginAuthRequest>),
5097 #[cfg(feature = "unstable_llm_providers")]
5103 ListProvidersRequest(Box<ListProvidersRequest>),
5104 #[cfg(feature = "unstable_llm_providers")]
5110 SetProviderRequest(Box<SetProviderRequest>),
5111 #[cfg(feature = "unstable_llm_providers")]
5117 DisableProviderRequest(Box<DisableProviderRequest>),
5118 LogoutAuthRequest(Box<LogoutAuthRequest>),
5128 NewSessionRequest(Box<NewSessionRequest>),
5141 ListSessionsRequest(Box<ListSessionsRequest>),
5145 DeleteSessionRequest(Box<DeleteSessionRequest>),
5149 #[cfg(feature = "unstable_session_fork")]
5150 ForkSessionRequest(Box<ForkSessionRequest>),
5162 ResumeSessionRequest(Box<ResumeSessionRequest>),
5168 CloseSessionRequest(Box<CloseSessionRequest>),
5173 SetSessionConfigOptionRequest(Box<SetSessionConfigOptionRequest>),
5175 PromptRequest(Box<PromptRequest>),
5187 #[cfg(feature = "unstable_nes")]
5188 StartNesRequest(Box<StartNesRequest>),
5194 #[cfg(feature = "unstable_nes")]
5195 SuggestNesRequest(Box<SuggestNesRequest>),
5201 #[cfg(feature = "unstable_nes")]
5202 CloseNesRequest(Box<CloseNesRequest>),
5211 #[cfg(feature = "unstable_mcp_over_acp")]
5217 MessageMcpRequest(Box<MessageMcpRequest>),
5218 ExtMethodRequest(Box<ExtRequest>),
5225}
5226
5227impl ClientRequest {
5228 #[must_use]
5230 pub fn method(&self) -> &str {
5231 match self {
5232 Self::InitializeRequest(_) => AGENT_METHOD_NAMES.initialize,
5233 Self::LoginAuthRequest(_) => AGENT_METHOD_NAMES.auth_login,
5234 #[cfg(feature = "unstable_llm_providers")]
5235 Self::ListProvidersRequest(_) => AGENT_METHOD_NAMES.providers_list,
5236 #[cfg(feature = "unstable_llm_providers")]
5237 Self::SetProviderRequest(_) => AGENT_METHOD_NAMES.providers_set,
5238 #[cfg(feature = "unstable_llm_providers")]
5239 Self::DisableProviderRequest(_) => AGENT_METHOD_NAMES.providers_disable,
5240 Self::LogoutAuthRequest(_) => AGENT_METHOD_NAMES.auth_logout,
5241 Self::NewSessionRequest(_) => AGENT_METHOD_NAMES.session_new,
5242 Self::ListSessionsRequest(_) => AGENT_METHOD_NAMES.session_list,
5243 Self::DeleteSessionRequest(_) => AGENT_METHOD_NAMES.session_delete,
5244 #[cfg(feature = "unstable_session_fork")]
5245 Self::ForkSessionRequest(_) => AGENT_METHOD_NAMES.session_fork,
5246 Self::ResumeSessionRequest(_) => AGENT_METHOD_NAMES.session_resume,
5247 Self::CloseSessionRequest(_) => AGENT_METHOD_NAMES.session_close,
5248 Self::SetSessionConfigOptionRequest(_) => AGENT_METHOD_NAMES.session_set_config_option,
5249 Self::PromptRequest(_) => AGENT_METHOD_NAMES.session_prompt,
5250 #[cfg(feature = "unstable_nes")]
5251 Self::StartNesRequest(_) => AGENT_METHOD_NAMES.nes_start,
5252 #[cfg(feature = "unstable_nes")]
5253 Self::SuggestNesRequest(_) => AGENT_METHOD_NAMES.nes_suggest,
5254 #[cfg(feature = "unstable_nes")]
5255 Self::CloseNesRequest(_) => AGENT_METHOD_NAMES.nes_close,
5256 #[cfg(feature = "unstable_mcp_over_acp")]
5257 Self::MessageMcpRequest(_) => AGENT_METHOD_NAMES.mcp_message,
5258 Self::ExtMethodRequest(ext_request) => &ext_request.method,
5259 }
5260 }
5261}
5262
5263#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5270#[derive(Clone, Debug, Serialize, Deserialize)]
5271#[serde(untagged)]
5272#[cfg_attr(feature = "schemars", schemars(inline))]
5273#[non_exhaustive]
5274pub enum AgentResponse {
5275 InitializeResponse(Box<InitializeResponse>),
5277 LoginAuthResponse(#[serde(default)] Box<LoginAuthResponse>),
5279 #[cfg(feature = "unstable_llm_providers")]
5281 ListProvidersResponse(Box<ListProvidersResponse>),
5282 #[cfg(feature = "unstable_llm_providers")]
5284 SetProviderResponse(#[serde(default)] Box<SetProviderResponse>),
5285 #[cfg(feature = "unstable_llm_providers")]
5287 DisableProviderResponse(#[serde(default)] Box<DisableProviderResponse>),
5288 LogoutAuthResponse(#[serde(default)] Box<LogoutAuthResponse>),
5290 NewSessionResponse(Box<NewSessionResponse>),
5292 ListSessionsResponse(Box<ListSessionsResponse>),
5294 DeleteSessionResponse(#[serde(default)] Box<DeleteSessionResponse>),
5296 #[cfg(feature = "unstable_session_fork")]
5298 ForkSessionResponse(Box<ForkSessionResponse>),
5299 ResumeSessionResponse(#[serde(default)] Box<ResumeSessionResponse>),
5301 CloseSessionResponse(#[serde(default)] Box<CloseSessionResponse>),
5303 SetSessionConfigOptionResponse(Box<SetSessionConfigOptionResponse>),
5305 PromptResponse(Box<PromptResponse>),
5307 #[cfg(feature = "unstable_nes")]
5309 StartNesResponse(Box<StartNesResponse>),
5310 #[cfg(feature = "unstable_nes")]
5312 SuggestNesResponse(Box<SuggestNesResponse>),
5313 #[cfg(feature = "unstable_nes")]
5315 CloseNesResponse(#[serde(default)] Box<CloseNesResponse>),
5316 ExtMethodResponse(Box<ExtResponse>),
5318 #[cfg(feature = "unstable_mcp_over_acp")]
5320 MessageMcpResponse(Box<MessageMcpResponse>),
5321}
5322
5323#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5330#[derive(Clone, Debug, Serialize, Deserialize)]
5331#[serde(untagged)]
5332#[cfg_attr(feature = "schemars", schemars(inline))]
5333#[non_exhaustive]
5334pub enum ClientNotification {
5335 CancelSessionNotification(Box<CancelSessionNotification>),
5349 #[cfg(feature = "unstable_nes")]
5350 DidOpenDocumentNotification(Box<DidOpenDocumentNotification>),
5354 #[cfg(feature = "unstable_nes")]
5355 DidChangeDocumentNotification(Box<DidChangeDocumentNotification>),
5359 #[cfg(feature = "unstable_nes")]
5360 DidCloseDocumentNotification(Box<DidCloseDocumentNotification>),
5364 #[cfg(feature = "unstable_nes")]
5365 DidSaveDocumentNotification(Box<DidSaveDocumentNotification>),
5369 #[cfg(feature = "unstable_nes")]
5370 DidFocusDocumentNotification(Box<DidFocusDocumentNotification>),
5374 #[cfg(feature = "unstable_nes")]
5375 AcceptNesNotification(Box<AcceptNesNotification>),
5379 #[cfg(feature = "unstable_nes")]
5380 RejectNesNotification(Box<RejectNesNotification>),
5384 #[cfg(feature = "unstable_mcp_over_acp")]
5390 MessageMcpNotification(Box<MessageMcpNotification>),
5391 ExtNotification(Box<ExtNotification>),
5398}
5399
5400impl ClientNotification {
5401 #[must_use]
5403 pub fn method(&self) -> &str {
5404 match self {
5405 Self::CancelSessionNotification(_) => AGENT_METHOD_NAMES.session_cancel,
5406 #[cfg(feature = "unstable_nes")]
5407 Self::DidOpenDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_open,
5408 #[cfg(feature = "unstable_nes")]
5409 Self::DidChangeDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_change,
5410 #[cfg(feature = "unstable_nes")]
5411 Self::DidCloseDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_close,
5412 #[cfg(feature = "unstable_nes")]
5413 Self::DidSaveDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_save,
5414 #[cfg(feature = "unstable_nes")]
5415 Self::DidFocusDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_focus,
5416 #[cfg(feature = "unstable_nes")]
5417 Self::AcceptNesNotification(_) => AGENT_METHOD_NAMES.nes_accept,
5418 #[cfg(feature = "unstable_nes")]
5419 Self::RejectNesNotification(_) => AGENT_METHOD_NAMES.nes_reject,
5420 #[cfg(feature = "unstable_mcp_over_acp")]
5421 Self::MessageMcpNotification(_) => AGENT_METHOD_NAMES.mcp_message,
5422 Self::ExtNotification(ext_notification) => &ext_notification.method,
5423 }
5424 }
5425}
5426
5427#[cfg(test)]
5428mod test_serialization {
5429 use std::path::PathBuf;
5430
5431 use super::*;
5432 use serde_json::json;
5433
5434 fn test_meta() -> Meta {
5435 json!({ "source": "test" }).as_object().unwrap().clone()
5436 }
5437
5438 fn serialized_meta_key_count(value: &impl serde::Serialize) -> usize {
5439 serde_json::to_string(value)
5440 .unwrap()
5441 .matches("\"_meta\"")
5442 .count()
5443 }
5444
5445 #[test]
5446 fn test_initialize_capabilities_default_on_malformed_values() {
5447 let request: InitializeRequest = serde_json::from_value(json!({
5448 "protocolVersion": 2,
5449 "capabilities": false,
5450 "info": {
5451 "name": "client",
5452 "version": "1.0.0"
5453 }
5454 }))
5455 .unwrap();
5456 assert_eq!(request.capabilities, ClientCapabilities::default());
5457
5458 let response: InitializeResponse = serde_json::from_value(json!({
5459 "protocolVersion": 2,
5460 "capabilities": false,
5461 "info": {
5462 "name": "agent",
5463 "version": "1.0.0"
5464 }
5465 }))
5466 .unwrap();
5467 assert_eq!(response.capabilities, AgentCapabilities::default());
5468 }
5469
5470 #[test]
5471 fn test_agent_capabilities_default_on_malformed_values() {
5472 let capabilities: AgentCapabilities = serde_json::from_value(json!({
5473 "session": false,
5474 "auth": false
5475 }))
5476 .unwrap();
5477
5478 assert!(capabilities.session.is_none());
5479 assert_eq!(capabilities.auth, None);
5480 }
5481
5482 #[test]
5483 fn test_mcp_server_stdio_serialization() {
5484 let server = McpServer::Stdio(
5485 McpServerStdio::new("test-server", "/usr/bin/server")
5486 .args(vec!["--port".to_string(), "3000".to_string()])
5487 .env(vec![EnvVariable::new("API_KEY", "secret123")]),
5488 );
5489
5490 let json = serde_json::to_value(&server).unwrap();
5491 assert_eq!(
5492 json,
5493 json!({
5494 "type": "stdio",
5495 "name": "test-server",
5496 "command": "/usr/bin/server",
5497 "args": ["--port", "3000"],
5498 "env": [
5499 {
5500 "name": "API_KEY",
5501 "value": "secret123"
5502 }
5503 ]
5504 })
5505 );
5506
5507 let deserialized: McpServer = serde_json::from_value(json).unwrap();
5508 match deserialized {
5509 McpServer::Stdio(McpServerStdio {
5510 name,
5511 command,
5512 args,
5513 env,
5514 meta: _,
5515 }) => {
5516 assert_eq!(name, "test-server");
5517 assert_eq!(command, AbsolutePath::new("/usr/bin/server"));
5518 assert_eq!(args, vec!["--port", "3000"]);
5519 assert_eq!(env.len(), 1);
5520 assert_eq!(env[0].name, "API_KEY");
5521 assert_eq!(env[0].value, "secret123");
5522 }
5523 _ => panic!("Expected Stdio variant"),
5524 }
5525 }
5526
5527 #[test]
5528 fn test_mcp_server_empty_arrays_are_optional() {
5529 let stdio = McpServer::Stdio(McpServerStdio::new("test-server", "/usr/bin/server"));
5530 assert_eq!(
5531 serde_json::to_value(&stdio).unwrap(),
5532 json!({
5533 "type": "stdio",
5534 "name": "test-server",
5535 "command": "/usr/bin/server"
5536 })
5537 );
5538
5539 let McpServer::Stdio(McpServerStdio { args, env, .. }) =
5540 serde_json::from_value::<McpServer>(json!({
5541 "type": "stdio",
5542 "name": "test-server",
5543 "command": "/usr/bin/server"
5544 }))
5545 .unwrap()
5546 else {
5547 panic!("Expected Stdio variant");
5548 };
5549 assert!(args.is_empty());
5550 assert!(env.is_empty());
5551
5552 let http = McpServer::Http(McpServerHttp::new("http-server", "https://api.example.com"));
5553 assert_eq!(
5554 serde_json::to_value(&http).unwrap(),
5555 json!({
5556 "type": "http",
5557 "name": "http-server",
5558 "url": "https://api.example.com"
5559 })
5560 );
5561
5562 let McpServer::Http(McpServerHttp { headers, .. }) =
5563 serde_json::from_value::<McpServer>(json!({
5564 "type": "http",
5565 "name": "http-server",
5566 "url": "https://api.example.com"
5567 }))
5568 .unwrap()
5569 else {
5570 panic!("Expected Http variant");
5571 };
5572 assert!(headers.is_empty());
5573 }
5574
5575 #[test]
5576 fn test_mcp_server_unknown_transport_serialization() {
5577 let json = json!({
5578 "type": "websocket",
5579 "name": "future-server",
5580 "url": "wss://example.com/mcp",
5581 "protocolVersion": "2026-01-01"
5582 });
5583
5584 let deserialized: McpServer = serde_json::from_value(json.clone()).unwrap();
5585 let McpServer::Other(OtherMcpServer { type_, fields }) = &deserialized else {
5586 panic!("Expected Other variant");
5587 };
5588
5589 assert_eq!(type_, "websocket");
5590 assert_eq!(fields["name"], "future-server");
5591 assert_eq!(fields["url"], "wss://example.com/mcp");
5592 assert_eq!(fields["protocolVersion"], "2026-01-01");
5593 assert_eq!(serde_json::to_value(&deserialized).unwrap(), json);
5594 }
5595
5596 #[test]
5597 fn test_mcp_server_stdio_requires_type() {
5598 let result = serde_json::from_value::<McpServer>(json!({
5599 "name": "test-server",
5600 "command": "/usr/bin/server",
5601 "args": [],
5602 "env": []
5603 }));
5604
5605 assert!(result.is_err());
5606 }
5607
5608 #[test]
5609 fn test_mcp_server_unknown_does_not_hide_malformed_known_transport() {
5610 let result = serde_json::from_value::<McpServer>(json!({
5611 "type": "stdio",
5612 "name": "test-server",
5613 "args": [],
5614 "env": []
5615 }));
5616
5617 assert!(result.is_err());
5618 }
5619
5620 #[test]
5621 fn test_mcp_server_http_serialization() {
5622 let server = McpServer::Http(
5623 McpServerHttp::new("http-server", "https://api.example.com").headers(vec![
5624 HttpHeader::new("Authorization", "Bearer token123"),
5625 HttpHeader::new("Content-Type", "application/json"),
5626 ]),
5627 );
5628
5629 let json = serde_json::to_value(&server).unwrap();
5630 assert_eq!(
5631 json,
5632 json!({
5633 "type": "http",
5634 "name": "http-server",
5635 "url": "https://api.example.com",
5636 "headers": [
5637 {
5638 "name": "Authorization",
5639 "value": "Bearer token123"
5640 },
5641 {
5642 "name": "Content-Type",
5643 "value": "application/json"
5644 }
5645 ]
5646 })
5647 );
5648
5649 let deserialized: McpServer = serde_json::from_value(json).unwrap();
5650 match deserialized {
5651 McpServer::Http(McpServerHttp {
5652 name,
5653 url,
5654 headers,
5655 meta: _,
5656 }) => {
5657 assert_eq!(name, "http-server");
5658 assert_eq!(url, "https://api.example.com");
5659 assert_eq!(headers.len(), 2);
5660 assert_eq!(headers[0].name, "Authorization");
5661 assert_eq!(headers[0].value, "Bearer token123");
5662 assert_eq!(headers[1].name, "Content-Type");
5663 assert_eq!(headers[1].value, "application/json");
5664 }
5665 _ => panic!("Expected Http variant"),
5666 }
5667 }
5668
5669 #[cfg(feature = "schemars")]
5670 #[test]
5671 fn mcp_server_http_schema_marks_url_as_uri() {
5672 let schema = serde_json::to_value(schemars::schema_for!(McpServerHttp)).unwrap();
5673
5674 assert_eq!(schema["properties"]["url"]["format"], "uri");
5675 }
5676
5677 #[cfg(feature = "unstable_mcp_over_acp")]
5678 #[test]
5679 fn test_client_mcp_message_method_names() {
5680 assert_eq!(AGENT_METHOD_NAMES.mcp_message, "mcp/message");
5681
5682 assert_eq!(
5683 ClientRequest::MessageMcpRequest(Box::new(MessageMcpRequest::new(
5684 "conn-1",
5685 "tools/list"
5686 )))
5687 .method(),
5688 "mcp/message"
5689 );
5690 assert_eq!(
5691 ClientNotification::MessageMcpNotification(Box::new(MessageMcpNotification::new(
5692 "conn-1",
5693 "notifications/progress"
5694 )))
5695 .method(),
5696 "mcp/message"
5697 );
5698 }
5699
5700 #[test]
5701 fn test_auth_method_names() {
5702 assert_eq!(AGENT_METHOD_NAMES.auth_login, "auth/login");
5703 assert_eq!(AGENT_METHOD_NAMES.auth_logout, "auth/logout");
5704
5705 assert_eq!(
5706 ClientRequest::LoginAuthRequest(Box::new(LoginAuthRequest::new("agent-login")))
5707 .method(),
5708 "auth/login"
5709 );
5710 assert_eq!(
5711 ClientRequest::LogoutAuthRequest(Box::new(LogoutAuthRequest::new())).method(),
5712 "auth/logout"
5713 );
5714 }
5715
5716 #[test]
5717 fn test_session_config_option_category_known_variants() {
5718 assert_eq!(
5720 serde_json::to_value(&SessionConfigOptionCategory::Mode).unwrap(),
5721 json!("mode")
5722 );
5723 assert_eq!(
5724 serde_json::to_value(&SessionConfigOptionCategory::Model).unwrap(),
5725 json!("model")
5726 );
5727 assert_eq!(
5728 serde_json::to_value(&SessionConfigOptionCategory::ModelConfig).unwrap(),
5729 json!("model_config")
5730 );
5731 assert_eq!(
5732 serde_json::to_value(&SessionConfigOptionCategory::ThoughtLevel).unwrap(),
5733 json!("thought_level")
5734 );
5735
5736 assert_eq!(
5738 serde_json::from_str::<SessionConfigOptionCategory>("\"mode\"").unwrap(),
5739 SessionConfigOptionCategory::Mode
5740 );
5741 assert_eq!(
5742 serde_json::from_str::<SessionConfigOptionCategory>("\"model\"").unwrap(),
5743 SessionConfigOptionCategory::Model
5744 );
5745 assert_eq!(
5746 serde_json::from_str::<SessionConfigOptionCategory>("\"model_config\"").unwrap(),
5747 SessionConfigOptionCategory::ModelConfig
5748 );
5749 assert_eq!(
5750 serde_json::from_str::<SessionConfigOptionCategory>("\"thought_level\"").unwrap(),
5751 SessionConfigOptionCategory::ThoughtLevel
5752 );
5753 }
5754
5755 #[test]
5756 fn test_session_config_option_category_unknown_variants() {
5757 let unknown: SessionConfigOptionCategory =
5759 serde_json::from_str("\"some_future_category\"").unwrap();
5760 assert_eq!(
5761 unknown,
5762 SessionConfigOptionCategory::Other("some_future_category".to_string())
5763 );
5764
5765 let json = serde_json::to_value(&unknown).unwrap();
5767 assert_eq!(json, json!("some_future_category"));
5768 }
5769
5770 #[test]
5771 fn test_session_config_option_category_custom_categories() {
5772 let custom: SessionConfigOptionCategory =
5774 serde_json::from_str("\"_my_custom_category\"").unwrap();
5775 assert_eq!(
5776 custom,
5777 SessionConfigOptionCategory::Other("_my_custom_category".to_string())
5778 );
5779
5780 let json = serde_json::to_value(&custom).unwrap();
5782 assert_eq!(json, json!("_my_custom_category"));
5783
5784 let deserialized: SessionConfigOptionCategory = serde_json::from_value(json).unwrap();
5786 assert_eq!(
5787 deserialized,
5788 SessionConfigOptionCategory::Other("_my_custom_category".to_string()),
5789 );
5790 }
5791
5792 fn test_config_option() -> SessionConfigOption {
5793 SessionConfigOption::select(
5794 "mode",
5795 "Mode",
5796 "ask",
5797 vec![SessionConfigSelectOption::new("ask", "Ask")],
5798 )
5799 }
5800
5801 #[test]
5802 fn test_session_response_config_options_default_empty_and_skip_serializing() {
5803 assert_eq!(
5804 serde_json::to_value(NewSessionResponse::new("sess")).unwrap(),
5805 json!({ "sessionId": "sess" })
5806 );
5807 assert_eq!(
5808 serde_json::to_value(ResumeSessionResponse::new()).unwrap(),
5809 json!({})
5810 );
5811 #[cfg(feature = "unstable_session_fork")]
5812 assert_eq!(
5813 serde_json::to_value(ForkSessionResponse::new("fork")).unwrap(),
5814 json!({ "sessionId": "fork" })
5815 );
5816
5817 let json = serde_json::to_value(
5818 NewSessionResponse::new("sess").config_options(vec![test_config_option()]),
5819 )
5820 .unwrap();
5821 assert_eq!(json["configOptions"].as_array().unwrap().len(), 1);
5822 }
5823
5824 #[test]
5825 fn test_session_response_config_options_deserialize_missing_null_and_invalid() {
5826 let missing: NewSessionResponse =
5827 serde_json::from_value(json!({ "sessionId": "sess" })).unwrap();
5828 assert!(missing.config_options.is_empty());
5829
5830 let null: NewSessionResponse = serde_json::from_value(json!({
5831 "sessionId": "sess",
5832 "configOptions": null
5833 }))
5834 .unwrap();
5835 assert!(null.config_options.is_empty());
5836
5837 let wrong_shape: NewSessionResponse = serde_json::from_value(json!({
5838 "sessionId": "sess",
5839 "configOptions": "oops"
5840 }))
5841 .unwrap();
5842 assert!(wrong_shape.config_options.is_empty());
5843
5844 let valid_option = serde_json::to_value(test_config_option()).unwrap();
5845 let mixed: NewSessionResponse = serde_json::from_value(json!({
5846 "sessionId": "sess",
5847 "configOptions": ["oops", valid_option]
5848 }))
5849 .unwrap();
5850 assert_eq!(mixed.config_options.len(), 1);
5851
5852 let resume: ResumeSessionResponse = serde_json::from_value(json!({})).unwrap();
5853 assert!(resume.config_options.is_empty());
5854 #[cfg(feature = "unstable_session_fork")]
5855 {
5856 let fork: ForkSessionResponse =
5857 serde_json::from_value(json!({ "sessionId": "fork" })).unwrap();
5858 assert!(fork.config_options.is_empty());
5859 }
5860 }
5861
5862 #[test]
5863 fn test_resume_session_replay_from_serialization() {
5864 assert_eq!(
5865 serde_json::to_value(ResumeSessionRequest::new(
5866 "sess_abc123",
5867 "/home/user/project"
5868 ))
5869 .unwrap(),
5870 json!({
5871 "sessionId": "sess_abc123",
5872 "cwd": "/home/user/project"
5873 })
5874 );
5875 assert_eq!(
5876 serde_json::to_value(
5877 ResumeSessionRequest::new("sess_abc123", "/home/user/project")
5878 .replay_from(ReplayFrom::from(ReplayFromStart::new()))
5879 )
5880 .unwrap(),
5881 json!({
5882 "sessionId": "sess_abc123",
5883 "cwd": "/home/user/project",
5884 "replayFrom": {
5885 "type": "start"
5886 }
5887 })
5888 );
5889
5890 let replay: ResumeSessionRequest = serde_json::from_value(json!({
5891 "sessionId": "sess_abc123",
5892 "cwd": "/home/user/project",
5893 "replayFrom": {
5894 "type": "start"
5895 }
5896 }))
5897 .unwrap();
5898 assert!(matches!(replay.replay_from, Some(ReplayFrom::Start(_))));
5899
5900 let none: ResumeSessionRequest = serde_json::from_value(json!({
5901 "sessionId": "sess_abc123",
5902 "cwd": "/home/user/project",
5903 "replayFrom": null
5904 }))
5905 .unwrap();
5906 assert!(none.replay_from.is_none());
5907 }
5908
5909 #[test]
5910 fn test_auth_method_agent_serialization() {
5911 let method = AuthMethod::Agent(AuthMethodAgent::new("default-auth", "Default Auth"));
5912
5913 let json = serde_json::to_value(&method).unwrap();
5914 assert_eq!(
5915 json,
5916 json!({
5917 "methodId": "default-auth",
5918 "name": "Default Auth",
5919 "type": "agent"
5920 })
5921 );
5922 assert!(!json.as_object().unwrap().contains_key("description"));
5924
5925 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
5926 match deserialized {
5927 AuthMethod::Agent(AuthMethodAgent {
5928 method_id, name, ..
5929 }) => {
5930 assert_eq!(method_id.0.as_ref(), "default-auth");
5931 assert_eq!(name, "Default Auth");
5932 }
5933 _ => panic!("Expected Agent variant"),
5934 }
5935 }
5936
5937 #[test]
5938 fn test_auth_method_agent_deserialization() {
5939 let json = json!({
5940 "methodId": "agent-auth",
5941 "name": "Agent Auth",
5942 "type": "agent"
5943 });
5944
5945 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
5946 assert!(matches!(deserialized, AuthMethod::Agent(_)));
5947 }
5948
5949 #[test]
5950 fn test_auth_method_agent_requires_type() {
5951 assert!(
5952 serde_json::from_value::<AuthMethod>(json!({
5953 "methodId": "agent-auth",
5954 "name": "Agent Auth"
5955 }))
5956 .is_err()
5957 );
5958 }
5959
5960 #[test]
5961 fn test_auth_method_agent_rejects_null_type() {
5962 assert!(
5963 serde_json::from_value::<AuthMethod>(json!({
5964 "methodId": "agent-auth",
5965 "name": "Agent Auth",
5966 "type": null
5967 }))
5968 .is_err()
5969 );
5970 }
5971
5972 #[test]
5973 fn test_auth_method_unknown_does_not_hide_malformed_agent() {
5974 assert!(
5975 serde_json::from_value::<AuthMethod>(json!({
5976 "methodId": "agent-auth",
5977 "type": "agent"
5978 }))
5979 .is_err()
5980 );
5981 assert!(
5982 serde_json::from_value::<AuthMethod>(json!({
5983 "methodId": "api-key",
5984 "type": "env_var",
5985 "vars": [{"name": "API_KEY"}]
5986 }))
5987 .is_err()
5988 );
5989 }
5990
5991 #[test]
5992 fn test_auth_method_unknown_variant_roundtrip() {
5993 let method: AuthMethod = serde_json::from_value(json!({
5994 "methodId": "oauth",
5995 "name": "OAuth",
5996 "type": "_oauth",
5997 "authorizationUrl": "https://example.com/auth"
5998 }))
5999 .unwrap();
6000
6001 assert_eq!(method.method_id().0.as_ref(), "oauth");
6002 assert_eq!(method.name(), "OAuth");
6003 let AuthMethod::Other(unknown) = method else {
6004 panic!("expected unknown auth method");
6005 };
6006 assert_eq!(unknown.type_, "_oauth");
6007 assert_eq!(
6008 unknown.fields.get("authorizationUrl"),
6009 Some(&json!("https://example.com/auth"))
6010 );
6011
6012 assert_eq!(
6013 serde_json::to_value(AuthMethod::Other(unknown)).unwrap(),
6014 json!({
6015 "methodId": "oauth",
6016 "name": "OAuth",
6017 "type": "_oauth",
6018 "authorizationUrl": "https://example.com/auth"
6019 })
6020 );
6021 }
6022
6023 #[test]
6024 fn test_auth_method_unknown_does_not_hide_malformed_known_variant() {
6025 assert!(
6026 serde_json::from_value::<AuthMethod>(json!({
6027 "methodId": "terminal-auth",
6028 "type": "terminal"
6029 }))
6030 .is_err()
6031 );
6032 }
6033
6034 #[test]
6035 fn test_session_delete_serialization() {
6036 assert_eq!(AGENT_METHOD_NAMES.session_delete, "session/delete");
6037 assert_eq!(
6038 ClientRequest::DeleteSessionRequest(Box::new(DeleteSessionRequest::new("sess_abc123")))
6039 .method(),
6040 "session/delete"
6041 );
6042 assert_eq!(
6043 serde_json::to_value(DeleteSessionRequest::new("sess_abc123")).unwrap(),
6044 json!({
6045 "sessionId": "sess_abc123"
6046 })
6047 );
6048 assert_eq!(
6049 serde_json::to_value(DeleteSessionResponse::new()).unwrap(),
6050 json!({})
6051 );
6052 assert_eq!(
6053 serde_json::to_value(
6054 SessionCapabilities::new().delete(SessionDeleteCapabilities::new())
6055 )
6056 .unwrap(),
6057 json!({
6058 "delete": {}
6059 })
6060 );
6061 }
6062 #[test]
6063 fn test_session_additional_directories_serialization() {
6064 assert_eq!(
6065 serde_json::to_value(NewSessionRequest::new("/home/user/project")).unwrap(),
6066 json!({
6067 "cwd": "/home/user/project",
6068 })
6069 );
6070 assert_eq!(
6071 serde_json::to_value(
6072 NewSessionRequest::new("/home/user/project").additional_directories(vec![
6073 PathBuf::from("/home/user/shared-lib"),
6074 PathBuf::from("/home/user/product-docs"),
6075 ])
6076 )
6077 .unwrap(),
6078 json!({
6079 "cwd": "/home/user/project",
6080 "additionalDirectories": [
6081 "/home/user/shared-lib",
6082 "/home/user/product-docs"
6083 ],
6084 })
6085 );
6086 assert_eq!(
6087 serde_json::to_value(ResumeSessionRequest::new(
6088 "sess_abc123",
6089 "/home/user/project"
6090 ))
6091 .unwrap(),
6092 json!({
6093 "sessionId": "sess_abc123",
6094 "cwd": "/home/user/project",
6095 })
6096 );
6097 assert_eq!(
6098 serde_json::from_value::<ResumeSessionRequest>(json!({
6099 "sessionId": "sess_abc123",
6100 "cwd": "/home/user/project"
6101 }))
6102 .unwrap()
6103 .mcp_servers,
6104 Vec::<McpServer>::new()
6105 );
6106 assert_eq!(
6107 serde_json::from_value::<ResumeSessionRequest>(json!({
6108 "sessionId": "sess_abc123",
6109 "cwd": "/home/user/project",
6110 "mcpServers": null
6111 }))
6112 .unwrap()
6113 .mcp_servers,
6114 Vec::<McpServer>::new()
6115 );
6116 assert_eq!(
6117 serde_json::to_value(SessionInfo::new("sess_abc123", "/home/user/project")).unwrap(),
6118 json!({
6119 "sessionId": "sess_abc123",
6120 "cwd": "/home/user/project"
6121 })
6122 );
6123 assert_eq!(
6124 serde_json::to_value(
6125 SessionInfo::new("sess_abc123", "/home/user/project").additional_directories(vec![
6126 PathBuf::from("/home/user/shared-lib"),
6127 PathBuf::from("/home/user/product-docs"),
6128 ])
6129 )
6130 .unwrap(),
6131 json!({
6132 "sessionId": "sess_abc123",
6133 "cwd": "/home/user/project",
6134 "additionalDirectories": [
6135 "/home/user/shared-lib",
6136 "/home/user/product-docs"
6137 ]
6138 })
6139 );
6140 assert_eq!(
6141 serde_json::from_value::<SessionInfo>(json!({
6142 "sessionId": "sess_abc123",
6143 "cwd": "/home/user/project"
6144 }))
6145 .unwrap()
6146 .additional_directories,
6147 Vec::<AbsolutePath>::new()
6148 );
6149 }
6150 #[test]
6151 fn test_session_additional_directories_capabilities_serialization() {
6152 assert_eq!(
6153 serde_json::to_value(
6154 SessionCapabilities::new()
6155 .additional_directories(SessionAdditionalDirectoriesCapabilities::new())
6156 )
6157 .unwrap(),
6158 json!({
6159 "additionalDirectories": {}
6160 })
6161 );
6162 }
6163
6164 #[test]
6165 fn test_auth_method_terminal_serialization() {
6166 let method = AuthMethod::Terminal(AuthMethodTerminal::new("tui-auth", "Terminal Auth"));
6167
6168 let json = serde_json::to_value(&method).unwrap();
6169 assert_eq!(
6170 json,
6171 json!({
6172 "methodId": "tui-auth",
6173 "name": "Terminal Auth",
6174 "type": "terminal"
6175 })
6176 );
6177 assert!(!json.as_object().unwrap().contains_key("args"));
6179 assert!(!json.as_object().unwrap().contains_key("env"));
6180
6181 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
6182 match deserialized {
6183 AuthMethod::Terminal(AuthMethodTerminal { args, env, .. }) => {
6184 assert!(args.is_empty());
6185 assert!(env.is_empty());
6186 }
6187 _ => panic!("Expected Terminal variant"),
6188 }
6189 }
6190
6191 #[test]
6192 fn test_auth_method_terminal_with_args_and_env_serialization() {
6193 let method = AuthMethod::Terminal(
6194 AuthMethodTerminal::new("tui-auth", "Terminal Auth")
6195 .args(vec!["--interactive".to_string(), "--color".to_string()])
6196 .env(vec![EnvVariable::new("TERM", "xterm-256color")]),
6197 );
6198
6199 let json = serde_json::to_value(&method).unwrap();
6200 assert_eq!(
6201 json,
6202 json!({
6203 "methodId": "tui-auth",
6204 "name": "Terminal Auth",
6205 "type": "terminal",
6206 "args": ["--interactive", "--color"],
6207 "env": [
6208 {
6209 "name": "TERM",
6210 "value": "xterm-256color"
6211 }
6212 ]
6213 })
6214 );
6215
6216 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
6217 match deserialized {
6218 AuthMethod::Terminal(AuthMethodTerminal { args, env, .. }) => {
6219 assert_eq!(args, vec!["--interactive", "--color"]);
6220 assert_eq!(env.len(), 1);
6221 assert_eq!(env[0].name, "TERM");
6222 assert_eq!(env[0].value, "xterm-256color");
6223 }
6224 _ => panic!("Expected Terminal variant"),
6225 }
6226 }
6227
6228 #[test]
6229 fn test_session_config_option_id_serialize() {
6230 let val = SessionConfigOptionValue::id("model-1");
6231 let json = serde_json::to_value(&val).unwrap();
6232 assert_eq!(json, json!({ "type": "id", "value": "model-1" }));
6233 }
6234
6235 #[test]
6236 fn test_session_config_option_value_boolean_serialize() {
6237 let val = SessionConfigOptionValue::boolean(true);
6238 let json = serde_json::to_value(&val).unwrap();
6239 assert_eq!(json, json!({ "type": "boolean", "value": true }));
6240 }
6241
6242 #[test]
6243 fn test_session_config_option_value_deserialize_id() {
6244 let json = json!({ "type": "id", "value": "model-1" });
6245 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6246 assert_eq!(val, SessionConfigOptionValue::id("model-1"));
6247 assert_eq!(val.as_id().unwrap().to_string(), "model-1");
6248 }
6249
6250 #[test]
6251 fn test_session_config_option_value_deserialize_requires_type() {
6252 let json = json!({ "value": "model-1" });
6253 let result = serde_json::from_value::<SessionConfigOptionValue>(json);
6254 assert!(result.is_err());
6255 }
6256
6257 #[test]
6258 fn test_session_config_option_value_deserialize_boolean() {
6259 let json = json!({ "type": "boolean", "value": true });
6260 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6261 assert_eq!(val, SessionConfigOptionValue::boolean(true));
6262 assert_eq!(val.as_bool(), Some(true));
6263 }
6264
6265 #[test]
6266 fn test_session_config_option_value_deserialize_boolean_false() {
6267 let json = json!({ "type": "boolean", "value": false });
6268 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6269 assert_eq!(val, SessionConfigOptionValue::boolean(false));
6270 assert_eq!(val.as_bool(), Some(false));
6271 }
6272
6273 #[test]
6274 fn test_session_config_option_value_deserialize_unknown_type_with_string_value() {
6275 let json = json!({
6276 "type": "text",
6277 "value": "freeform input",
6278 "maxLength": 200
6279 });
6280 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6281 let SessionConfigOptionValue::Other(unknown) = val else {
6282 panic!("Expected Other variant");
6283 };
6284 assert_eq!(unknown.type_, "text");
6285 assert_eq!(unknown.value, json!("freeform input"));
6286 assert_eq!(unknown.fields["maxLength"], json!(200));
6287 }
6288
6289 #[test]
6290 fn test_session_config_option_value_deserialize_unknown_type_with_object_value() {
6291 let json = json!({
6292 "type": "range",
6293 "value": { "min": 1, "max": 5 }
6294 });
6295 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6296 let SessionConfigOptionValue::Other(unknown) = val else {
6297 panic!("Expected Other variant");
6298 };
6299 assert_eq!(unknown.type_, "range");
6300 assert_eq!(unknown.value, json!({ "min": 1, "max": 5 }));
6301 }
6302
6303 #[test]
6304 fn test_session_config_option_value_roundtrip_id() {
6305 let original = SessionConfigOptionValue::id("option-a");
6306 let json = serde_json::to_value(&original).unwrap();
6307 let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6308 assert_eq!(original, roundtripped);
6309 }
6310
6311 #[test]
6312 fn test_session_config_option_value_roundtrip_boolean() {
6313 let original = SessionConfigOptionValue::boolean(false);
6314 let json = serde_json::to_value(&original).unwrap();
6315 let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6316 assert_eq!(original, roundtripped);
6317 }
6318
6319 #[test]
6320 fn test_session_config_option_value_roundtrip_other() {
6321 let mut fields = BTreeMap::new();
6322 fields.insert("maxLength".to_string(), json!(200));
6323 let original = SessionConfigOptionValue::Other(OtherSessionConfigOptionValue::new(
6324 "text",
6325 json!("freeform input"),
6326 fields,
6327 ));
6328 let json = serde_json::to_value(&original).unwrap();
6329 let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6330 assert_eq!(original, roundtripped);
6331 }
6332
6333 #[test]
6334 fn test_session_config_option_value_type_mismatch_boolean_with_string() {
6335 let json = json!({ "type": "boolean", "value": "not a bool" });
6336 let result = serde_json::from_value::<SessionConfigOptionValue>(json);
6337 assert!(result.is_err());
6338 }
6339
6340 #[test]
6341 fn test_session_config_option_value_from_impls() {
6342 let from_str: SessionConfigOptionValue = "model-1".into();
6343 assert_eq!(from_str.as_id().unwrap().to_string(), "model-1");
6344
6345 let from_id: SessionConfigOptionValue = SessionConfigValueId::new("model-2").into();
6346 assert_eq!(from_id.as_id().unwrap().to_string(), "model-2");
6347
6348 let from_bool: SessionConfigOptionValue = true.into();
6349 assert_eq!(from_bool.as_bool(), Some(true));
6350 }
6351
6352 #[test]
6353 fn test_set_session_config_option_request_id() {
6354 let req = SetSessionConfigOptionRequest::new("sess_1", "model", "model-1");
6355 let json = serde_json::to_value(&req).unwrap();
6356 assert_eq!(
6357 json,
6358 json!({
6359 "sessionId": "sess_1",
6360 "configId": "model",
6361 "type": "id",
6362 "value": "model-1"
6363 })
6364 );
6365 }
6366
6367 #[test]
6368 fn test_set_session_config_option_request_boolean() {
6369 let req = SetSessionConfigOptionRequest::new("sess_1", "brave_mode", true);
6370 let json = serde_json::to_value(&req).unwrap();
6371 assert_eq!(
6372 json,
6373 json!({
6374 "sessionId": "sess_1",
6375 "configId": "brave_mode",
6376 "type": "boolean",
6377 "value": true
6378 })
6379 );
6380 }
6381
6382 #[test]
6383 fn test_set_session_config_option_request_deserialize_requires_type() {
6384 let json = json!({
6385 "sessionId": "sess_1",
6386 "configId": "model",
6387 "value": "model-1"
6388 });
6389 let result = serde_json::from_value::<SetSessionConfigOptionRequest>(json);
6390 assert!(result.is_err());
6391 }
6392
6393 #[test]
6394 fn test_set_session_config_option_request_deserialize_boolean() {
6395 let json = json!({
6396 "sessionId": "sess_1",
6397 "configId": "brave_mode",
6398 "type": "boolean",
6399 "value": true
6400 });
6401 let req: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
6402 assert_eq!(req.value.as_bool(), Some(true));
6403 }
6404
6405 #[test]
6406 fn test_set_session_config_option_request_roundtrip_id() {
6407 let original = SetSessionConfigOptionRequest::new("s", "c", "v");
6408 let json = serde_json::to_value(&original).unwrap();
6409 let roundtripped: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
6410 assert_eq!(original, roundtripped);
6411 }
6412
6413 #[test]
6414 fn test_set_session_config_option_request_roundtrip_boolean() {
6415 let original = SetSessionConfigOptionRequest::new("s", "c", false);
6416 let json = serde_json::to_value(&original).unwrap();
6417 let roundtripped: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
6418 assert_eq!(original, roundtripped);
6419 }
6420
6421 #[test]
6422 fn test_session_config_boolean_serialization() {
6423 let cfg = SessionConfigBoolean::new(true);
6424 let json = serde_json::to_value(&cfg).unwrap();
6425 assert_eq!(json, json!({ "currentValue": true }));
6426
6427 let deserialized: SessionConfigBoolean = serde_json::from_value(json).unwrap();
6428 assert!(deserialized.current_value);
6429 }
6430
6431 #[test]
6432 fn test_session_config_option_boolean_variant() {
6433 let opt = SessionConfigOption::boolean("brave_mode", "Brave Mode", false)
6434 .description("Skip confirmation prompts")
6435 .meta(test_meta());
6436 assert_eq!(serialized_meta_key_count(&opt), 1);
6437
6438 let json = serde_json::to_value(&opt).unwrap();
6439 assert_eq!(
6440 json,
6441 json!({
6442 "configId": "brave_mode",
6443 "name": "Brave Mode",
6444 "description": "Skip confirmation prompts",
6445 "type": "boolean",
6446 "currentValue": false,
6447 "_meta": {
6448 "source": "test"
6449 }
6450 })
6451 );
6452
6453 let deserialized: SessionConfigOption = serde_json::from_value(json).unwrap();
6454 assert_eq!(deserialized.config_id.to_string(), "brave_mode");
6455 assert_eq!(deserialized.name, "Brave Mode");
6456 match deserialized.kind {
6457 SessionConfigKind::Boolean(ref b) => assert!(!b.current_value),
6458 _ => panic!("Expected Boolean kind"),
6459 }
6460 }
6461
6462 #[test]
6463 fn test_session_config_option_select_still_works() {
6464 let opt = SessionConfigOption::select(
6466 "model",
6467 "Model",
6468 "model-1",
6469 vec![
6470 SessionConfigSelectOption::new("model-1", "Model 1"),
6471 SessionConfigSelectOption::new("model-2", "Model 2"),
6472 ],
6473 )
6474 .meta(test_meta());
6475 assert_eq!(serialized_meta_key_count(&opt), 1);
6476
6477 let json = serde_json::to_value(&opt).unwrap();
6478 assert_eq!(json["type"], "select");
6479 assert_eq!(json["currentValue"], "model-1");
6480 assert_eq!(json["options"].as_array().unwrap().len(), 2);
6481 assert_eq!(json["_meta"]["source"], "test");
6482
6483 let deserialized: SessionConfigOption = serde_json::from_value(json).unwrap();
6484 match deserialized.kind {
6485 SessionConfigKind::Select(ref s) => {
6486 assert_eq!(s.current_value.to_string(), "model-1");
6487 }
6488 _ => panic!("Expected Select kind"),
6489 }
6490 }
6491
6492 #[test]
6493 fn test_session_config_option_unknown_kind_roundtrip() {
6494 let option: SessionConfigOption = serde_json::from_value(json!({
6495 "configId": "verbosity",
6496 "name": "Verbosity",
6497 "type": "_slider",
6498 "currentValue": 3,
6499 "min": 0,
6500 "max": 5,
6501 "_meta": {
6502 "source": "test"
6503 }
6504 }))
6505 .unwrap();
6506
6507 assert_eq!(option.config_id.to_string(), "verbosity");
6508 assert_eq!(option.meta.as_ref().unwrap()["source"], "test");
6509 let SessionConfigKind::Other(unknown) = &option.kind else {
6510 panic!("expected unknown config kind");
6511 };
6512 assert_eq!(unknown.type_, "_slider");
6513 assert_eq!(unknown.fields.get("currentValue"), Some(&json!(3)));
6514 assert!(!unknown.fields.contains_key("_meta"));
6515 assert_eq!(serialized_meta_key_count(&option), 1);
6516
6517 let json = serde_json::to_value(&option).unwrap();
6518 assert_eq!(json["type"], "_slider");
6519 assert_eq!(json["currentValue"], 3);
6520 assert_eq!(json["min"], 0);
6521 assert_eq!(json["max"], 5);
6522 assert_eq!(json["_meta"]["source"], "test");
6523 }
6524
6525 #[test]
6526 fn test_session_config_option_unknown_kind_does_not_duplicate_flattened_meta() {
6527 let mut fields = std::collections::BTreeMap::new();
6528 fields.insert("currentValue".to_string(), json!(3));
6529 fields.insert("_meta".to_string(), json!({ "inner": "ignored" }));
6530
6531 let option = SessionConfigOption::new(
6532 "verbosity",
6533 "Verbosity",
6534 SessionConfigKind::Other(OtherSessionConfigKind::new("_slider", fields)),
6535 )
6536 .meta(test_meta());
6537
6538 let SessionConfigKind::Other(unknown) = &option.kind else {
6539 panic!("expected unknown config kind");
6540 };
6541 assert!(!unknown.fields.contains_key("_meta"));
6542 assert_eq!(serialized_meta_key_count(&option), 1);
6543
6544 let json = serde_json::to_value(&option).unwrap();
6545 assert_eq!(json["type"], "_slider");
6546 assert_eq!(json["currentValue"], 3);
6547 assert_eq!(json["_meta"]["source"], "test");
6548 }
6549
6550 #[test]
6551 fn test_session_config_option_unknown_does_not_hide_malformed_known_kind() {
6552 assert!(
6553 serde_json::from_value::<SessionConfigOption>(json!({
6554 "configId": "model",
6555 "name": "Model",
6556 "type": "select"
6557 }))
6558 .is_err()
6559 );
6560 }
6561
6562 #[cfg(feature = "unstable_llm_providers")]
6563 #[test]
6564 fn test_llm_protocol_known_variants() {
6565 assert_eq!(
6566 serde_json::to_value(&LlmProtocol::Anthropic).unwrap(),
6567 json!("anthropic")
6568 );
6569 assert_eq!(
6570 serde_json::to_value(&LlmProtocol::OpenAi).unwrap(),
6571 json!("openai")
6572 );
6573 assert_eq!(
6574 serde_json::to_value(&LlmProtocol::Azure).unwrap(),
6575 json!("azure")
6576 );
6577 assert_eq!(
6578 serde_json::to_value(&LlmProtocol::Vertex).unwrap(),
6579 json!("vertex")
6580 );
6581 assert_eq!(
6582 serde_json::to_value(&LlmProtocol::Bedrock).unwrap(),
6583 json!("bedrock")
6584 );
6585
6586 assert_eq!(
6587 serde_json::from_str::<LlmProtocol>("\"anthropic\"").unwrap(),
6588 LlmProtocol::Anthropic
6589 );
6590 assert_eq!(
6591 serde_json::from_str::<LlmProtocol>("\"openai\"").unwrap(),
6592 LlmProtocol::OpenAi
6593 );
6594 assert_eq!(
6595 serde_json::from_str::<LlmProtocol>("\"azure\"").unwrap(),
6596 LlmProtocol::Azure
6597 );
6598 assert_eq!(
6599 serde_json::from_str::<LlmProtocol>("\"vertex\"").unwrap(),
6600 LlmProtocol::Vertex
6601 );
6602 assert_eq!(
6603 serde_json::from_str::<LlmProtocol>("\"bedrock\"").unwrap(),
6604 LlmProtocol::Bedrock
6605 );
6606 }
6607
6608 #[cfg(feature = "unstable_llm_providers")]
6609 #[test]
6610 fn test_llm_protocol_unknown_variant() {
6611 let unknown: LlmProtocol = serde_json::from_str("\"cohere\"").unwrap();
6612 assert_eq!(unknown, LlmProtocol::Other("cohere".to_string()));
6613
6614 let json = serde_json::to_value(&unknown).unwrap();
6615 assert_eq!(json, json!("cohere"));
6616 }
6617
6618 #[cfg(feature = "unstable_llm_providers")]
6619 #[test]
6620 fn test_provider_current_config_serialization() {
6621 let config =
6622 ProviderCurrentConfig::new(LlmProtocol::Anthropic, "https://api.anthropic.com");
6623
6624 let json = serde_json::to_value(&config).unwrap();
6625 assert_eq!(
6626 json,
6627 json!({
6628 "apiType": "anthropic",
6629 "baseUrl": "https://api.anthropic.com"
6630 })
6631 );
6632
6633 let deserialized: ProviderCurrentConfig = serde_json::from_value(json).unwrap();
6634 assert_eq!(deserialized.api_type, LlmProtocol::Anthropic);
6635 assert_eq!(deserialized.base_url, "https://api.anthropic.com");
6636 }
6637
6638 #[cfg(feature = "unstable_llm_providers")]
6639 #[test]
6640 fn test_provider_info_with_current_config() {
6641 let info = ProviderInfo::new(
6642 "main",
6643 vec![LlmProtocol::Anthropic, LlmProtocol::OpenAi],
6644 true,
6645 Some(ProviderCurrentConfig::new(
6646 LlmProtocol::Anthropic,
6647 "https://api.anthropic.com",
6648 )),
6649 );
6650
6651 let json = serde_json::to_value(&info).unwrap();
6652 assert_eq!(
6653 json,
6654 json!({
6655 "providerId": "main",
6656 "supported": ["anthropic", "openai"],
6657 "required": true,
6658 "current": {
6659 "apiType": "anthropic",
6660 "baseUrl": "https://api.anthropic.com"
6661 }
6662 })
6663 );
6664
6665 let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
6666 assert_eq!(deserialized.provider_id.to_string(), "main");
6667 assert_eq!(deserialized.supported.len(), 2);
6668 assert!(deserialized.required);
6669 assert!(deserialized.current.is_some());
6670 assert_eq!(
6671 deserialized.current.as_ref().unwrap().api_type,
6672 LlmProtocol::Anthropic
6673 );
6674 }
6675
6676 #[cfg(feature = "unstable_llm_providers")]
6677 #[test]
6678 fn test_provider_info_disabled() {
6679 let info = ProviderInfo::new(
6680 "secondary",
6681 vec![LlmProtocol::OpenAi],
6682 false,
6683 None::<ProviderCurrentConfig>,
6684 );
6685
6686 let json = serde_json::to_value(&info).unwrap();
6687 assert_eq!(
6688 json,
6689 json!({
6690 "providerId": "secondary",
6691 "supported": ["openai"],
6692 "required": false
6693 })
6694 );
6695
6696 let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
6697 assert_eq!(deserialized.provider_id.to_string(), "secondary");
6698 assert!(!deserialized.required);
6699 assert!(deserialized.current.is_none());
6700 }
6701
6702 #[cfg(feature = "unstable_llm_providers")]
6703 #[test]
6704 fn test_provider_info_missing_current_defaults_to_none() {
6705 let json = json!({
6707 "providerId": "main",
6708 "supported": ["anthropic"],
6709 "required": true
6710 });
6711 let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
6712 assert!(deserialized.current.is_none());
6713 }
6714
6715 #[cfg(feature = "unstable_llm_providers")]
6716 #[test]
6717 fn test_provider_info_explicit_null_current_decodes_to_none() {
6718 let json = json!({
6722 "providerId": "main",
6723 "supported": ["anthropic"],
6724 "required": true,
6725 "current": null
6726 });
6727 let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
6728 assert!(deserialized.current.is_none());
6729 }
6730
6731 #[cfg(feature = "unstable_llm_providers")]
6732 #[test]
6733 fn test_list_providers_response_serialization() {
6734 let response = ListProvidersResponse::new(vec![ProviderInfo::new(
6735 "main",
6736 vec![LlmProtocol::Anthropic],
6737 true,
6738 Some(ProviderCurrentConfig::new(
6739 LlmProtocol::Anthropic,
6740 "https://api.anthropic.com",
6741 )),
6742 )]);
6743
6744 let json = serde_json::to_value(&response).unwrap();
6745 assert_eq!(json["providers"].as_array().unwrap().len(), 1);
6746 assert_eq!(json["providers"][0]["providerId"], "main");
6747
6748 let deserialized: ListProvidersResponse = serde_json::from_value(json).unwrap();
6749 assert_eq!(deserialized.providers.len(), 1);
6750 }
6751
6752 #[cfg(feature = "unstable_llm_providers")]
6753 #[test]
6754 fn test_set_provider_request_serialization() {
6755 use std::collections::HashMap;
6756
6757 let mut headers = HashMap::new();
6758 headers.insert("Authorization".to_string(), "Bearer sk-test".to_string());
6759
6760 let request =
6761 SetProviderRequest::new("main", LlmProtocol::OpenAi, "https://api.openai.com/v1")
6762 .headers(headers);
6763
6764 let json = serde_json::to_value(&request).unwrap();
6765 assert_eq!(
6766 json,
6767 json!({
6768 "providerId": "main",
6769 "apiType": "openai",
6770 "baseUrl": "https://api.openai.com/v1",
6771 "headers": {
6772 "Authorization": "Bearer sk-test"
6773 }
6774 })
6775 );
6776
6777 let deserialized: SetProviderRequest = serde_json::from_value(json).unwrap();
6778 assert_eq!(deserialized.provider_id.to_string(), "main");
6779 assert_eq!(deserialized.api_type, LlmProtocol::OpenAi);
6780 assert_eq!(deserialized.base_url, "https://api.openai.com/v1");
6781 assert_eq!(deserialized.headers.len(), 1);
6782 assert_eq!(
6783 deserialized.headers.get("Authorization").unwrap(),
6784 "Bearer sk-test"
6785 );
6786 }
6787
6788 #[cfg(feature = "unstable_llm_providers")]
6789 #[test]
6790 fn test_set_provider_request_omits_empty_headers() {
6791 let request =
6792 SetProviderRequest::new("main", LlmProtocol::Anthropic, "https://api.anthropic.com");
6793
6794 let json = serde_json::to_value(&request).unwrap();
6795 assert!(!json.as_object().unwrap().contains_key("headers"));
6797 }
6798
6799 #[cfg(feature = "unstable_llm_providers")]
6800 #[test]
6801 fn test_disable_provider_request_serialization() {
6802 let request = DisableProviderRequest::new("secondary");
6803
6804 let json = serde_json::to_value(&request).unwrap();
6805 assert_eq!(json, json!({ "providerId": "secondary" }));
6806
6807 let deserialized: DisableProviderRequest = serde_json::from_value(json).unwrap();
6808 assert_eq!(deserialized.provider_id.to_string(), "secondary");
6809 }
6810
6811 #[cfg(feature = "unstable_llm_providers")]
6812 #[test]
6813 fn test_providers_capabilities_serialization() {
6814 let caps = ProvidersCapabilities::new();
6815
6816 let json = serde_json::to_value(&caps).unwrap();
6817 assert_eq!(json, json!({}));
6818
6819 let deserialized: ProvidersCapabilities = serde_json::from_value(json).unwrap();
6820 assert!(deserialized.meta.is_none());
6821 }
6822
6823 #[cfg(feature = "unstable_llm_providers")]
6824 #[test]
6825 fn test_agent_capabilities_with_providers() {
6826 let caps = AgentCapabilities::new().providers(ProvidersCapabilities::new());
6827
6828 let json = serde_json::to_value(&caps).unwrap();
6829 assert_eq!(json["providers"], json!({}));
6830
6831 let deserialized: AgentCapabilities = serde_json::from_value(json).unwrap();
6832 assert!(deserialized.providers.is_some());
6833 }
6834
6835 #[test]
6836 fn test_agent_capabilities_session_is_explicit() {
6837 let json = serde_json::to_value(AgentCapabilities::new()).unwrap();
6838 assert!(json.get("session").is_none());
6839
6840 let caps = AgentCapabilities::new().session(
6841 SessionCapabilities::new()
6842 .prompt(PromptCapabilities::new().image(PromptImageCapabilities::new()))
6843 .mcp(McpCapabilities::new().stdio(McpStdioCapabilities::new())),
6844 );
6845
6846 assert_eq!(
6847 serde_json::to_value(&caps).unwrap(),
6848 json!({
6849 "session": {
6850 "prompt": {
6851 "image": {}
6852 },
6853 "mcp": {
6854 "stdio": {}
6855 }
6856 }
6857 })
6858 );
6859
6860 let deserialized: AgentCapabilities = serde_json::from_value(json!({
6861 "session": false
6862 }))
6863 .unwrap();
6864 assert!(deserialized.session.is_none());
6865 }
6866
6867 #[test]
6868 fn test_prompt_capabilities_serialize_supported_content_as_objects() {
6869 let caps = PromptCapabilities::new()
6870 .image(PromptImageCapabilities::new())
6871 .audio(PromptAudioCapabilities::new())
6872 .embedded_context(PromptEmbeddedContextCapabilities::new());
6873
6874 assert_eq!(
6875 serde_json::to_value(&caps).unwrap(),
6876 json!({
6877 "image": {},
6878 "audio": {},
6879 "embeddedContext": {}
6880 })
6881 );
6882
6883 let deserialized: PromptCapabilities = serde_json::from_value(json!({
6884 "image": null,
6885 "audio": false,
6886 "embeddedContext": {}
6887 }))
6888 .unwrap();
6889 assert!(deserialized.image.is_none());
6890 assert!(deserialized.audio.is_none());
6891 assert!(deserialized.embedded_context.is_some());
6892 }
6893
6894 #[test]
6895 fn test_mcp_capabilities_serialize_supported_transports_as_objects() {
6896 let caps = McpCapabilities::new()
6897 .stdio(McpStdioCapabilities::new())
6898 .http(McpHttpCapabilities::new());
6899
6900 assert_eq!(
6901 serde_json::to_value(&caps).unwrap(),
6902 json!({
6903 "stdio": {},
6904 "http": {}
6905 })
6906 );
6907
6908 let deserialized: McpCapabilities = serde_json::from_value(json!({
6909 "stdio": null,
6910 "http": false
6911 }))
6912 .unwrap();
6913 assert!(deserialized.stdio.is_none());
6914 assert!(deserialized.http.is_none());
6915 }
6916
6917 #[cfg(feature = "unstable_mcp_over_acp")]
6918 #[test]
6919 fn test_mcp_capabilities_serialize_acp_support_as_object() {
6920 let caps = McpCapabilities::new().acp(McpAcpCapabilities::new());
6921
6922 assert_eq!(
6923 serde_json::to_value(&caps).unwrap(),
6924 json!({
6925 "acp": {}
6926 })
6927 );
6928 }
6929
6930 #[test]
6931 fn prompt_request_rejects_malformed_content_block() {
6932 use serde_json::json;
6933
6934 assert!(
6935 serde_json::from_value::<PromptRequest>(json!({
6936 "sessionId": "sess-1",
6937 "prompt": [{"type": "text"}]
6938 }))
6939 .is_err()
6940 );
6941 }
6942
6943 #[test]
6944 fn prompt_request_rejects_non_array_prompt() {
6945 use serde_json::json;
6946
6947 assert!(
6948 serde_json::from_value::<PromptRequest>(json!({
6949 "sessionId": "sess-1",
6950 "prompt": "hello"
6951 }))
6952 .is_err()
6953 );
6954 }
6955}