1use std::{path::PathBuf, sync::Arc};
7
8#[cfg(any(feature = "unstable_auth_methods", feature = "unstable_llm_providers"))]
9use std::collections::HashMap;
10
11use derive_more::{Display, From};
12use schemars::JsonSchema;
13use serde::{Deserialize, Serialize};
14use serde_with::{DefaultOnError, VecSkipError, serde_as, skip_serializing_none};
15
16use crate::{
17 ClientCapabilities, ContentBlock, ExtNotification, ExtRequest, ExtResponse, IntoOption, Meta,
18 ProtocolVersion, SessionId, SkipListener,
19};
20
21#[cfg(feature = "unstable_mcp_over_acp")]
22use super::mcp::{
23 MCP_MESSAGE_METHOD_NAME, MessageMcpNotification, MessageMcpRequest, MessageMcpResponse,
24};
25
26#[cfg(feature = "unstable_nes")]
27use crate::{
28 AcceptNesNotification, CloseNesRequest, CloseNesResponse, DidChangeDocumentNotification,
29 DidCloseDocumentNotification, DidFocusDocumentNotification, DidOpenDocumentNotification,
30 DidSaveDocumentNotification, NesCapabilities, PositionEncodingKind, RejectNesNotification,
31 StartNesRequest, StartNesResponse, SuggestNesRequest, SuggestNesResponse,
32};
33
34#[cfg(feature = "unstable_nes")]
35use crate::{
36 DOCUMENT_DID_CHANGE_METHOD_NAME, DOCUMENT_DID_CLOSE_METHOD_NAME,
37 DOCUMENT_DID_FOCUS_METHOD_NAME, DOCUMENT_DID_OPEN_METHOD_NAME, DOCUMENT_DID_SAVE_METHOD_NAME,
38 NES_ACCEPT_METHOD_NAME, NES_CLOSE_METHOD_NAME, NES_REJECT_METHOD_NAME, NES_START_METHOD_NAME,
39 NES_SUGGEST_METHOD_NAME,
40};
41
42#[serde_as]
50#[skip_serializing_none]
51#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
52#[schemars(extend("x-side" = "agent", "x-method" = INITIALIZE_METHOD_NAME))]
53#[serde(rename_all = "camelCase")]
54#[non_exhaustive]
55pub struct InitializeRequest {
56 pub protocol_version: ProtocolVersion,
58 #[serde(default)]
60 pub client_capabilities: ClientCapabilities,
61 #[serde_as(deserialize_as = "DefaultOnError")]
65 #[schemars(extend("x-deserialize-default-on-error" = true))]
66 #[serde(default)]
67 pub client_info: Option<Implementation>,
68 #[serde(rename = "_meta")]
74 pub meta: Option<Meta>,
75}
76
77impl InitializeRequest {
78 #[must_use]
79 pub fn new(protocol_version: ProtocolVersion) -> Self {
80 Self {
81 protocol_version,
82 client_capabilities: ClientCapabilities::default(),
83 client_info: None,
84 meta: None,
85 }
86 }
87
88 #[must_use]
90 pub fn client_capabilities(mut self, client_capabilities: ClientCapabilities) -> Self {
91 self.client_capabilities = client_capabilities;
92 self
93 }
94
95 #[must_use]
97 pub fn client_info(mut self, client_info: impl IntoOption<Implementation>) -> Self {
98 self.client_info = client_info.into_option();
99 self
100 }
101
102 #[must_use]
108 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
109 self.meta = meta.into_option();
110 self
111 }
112}
113
114#[serde_as]
120#[skip_serializing_none]
121#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
122#[schemars(extend("x-side" = "agent", "x-method" = INITIALIZE_METHOD_NAME))]
123#[serde(rename_all = "camelCase")]
124#[non_exhaustive]
125pub struct InitializeResponse {
126 pub protocol_version: ProtocolVersion,
131 #[serde(default)]
133 pub agent_capabilities: AgentCapabilities,
134 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
136 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
137 #[serde(default)]
138 pub auth_methods: Vec<AuthMethod>,
139 #[serde_as(deserialize_as = "DefaultOnError")]
143 #[schemars(extend("x-deserialize-default-on-error" = true))]
144 #[serde(default)]
145 pub agent_info: Option<Implementation>,
146 #[serde(rename = "_meta")]
152 pub meta: Option<Meta>,
153}
154
155impl InitializeResponse {
156 #[must_use]
157 pub fn new(protocol_version: ProtocolVersion) -> Self {
158 Self {
159 protocol_version,
160 agent_capabilities: AgentCapabilities::default(),
161 auth_methods: vec![],
162 agent_info: None,
163 meta: None,
164 }
165 }
166
167 #[must_use]
169 pub fn agent_capabilities(mut self, agent_capabilities: AgentCapabilities) -> Self {
170 self.agent_capabilities = agent_capabilities;
171 self
172 }
173
174 #[must_use]
176 pub fn auth_methods(mut self, auth_methods: Vec<AuthMethod>) -> Self {
177 self.auth_methods = auth_methods;
178 self
179 }
180
181 #[must_use]
183 pub fn agent_info(mut self, agent_info: impl IntoOption<Implementation>) -> Self {
184 self.agent_info = agent_info.into_option();
185 self
186 }
187
188 #[must_use]
194 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
195 self.meta = meta.into_option();
196 self
197 }
198}
199
200#[skip_serializing_none]
204#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
205#[serde(rename_all = "camelCase")]
206#[non_exhaustive]
207pub struct Implementation {
208 pub name: String,
211 pub title: Option<String>,
216 pub version: String,
219 #[serde(rename = "_meta")]
225 pub meta: Option<Meta>,
226}
227
228impl Implementation {
229 #[must_use]
230 pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
231 Self {
232 name: name.into(),
233 title: None,
234 version: version.into(),
235 meta: None,
236 }
237 }
238
239 #[must_use]
244 pub fn title(mut self, title: impl IntoOption<String>) -> Self {
245 self.title = title.into_option();
246 self
247 }
248
249 #[must_use]
255 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
256 self.meta = meta.into_option();
257 self
258 }
259}
260
261#[skip_serializing_none]
267#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
268#[schemars(extend("x-side" = "agent", "x-method" = AUTHENTICATE_METHOD_NAME))]
269#[serde(rename_all = "camelCase")]
270#[non_exhaustive]
271pub struct AuthenticateRequest {
272 pub method_id: AuthMethodId,
275 #[serde(rename = "_meta")]
281 pub meta: Option<Meta>,
282}
283
284impl AuthenticateRequest {
285 #[must_use]
286 pub fn new(method_id: impl Into<AuthMethodId>) -> Self {
287 Self {
288 method_id: method_id.into(),
289 meta: None,
290 }
291 }
292
293 #[must_use]
299 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
300 self.meta = meta.into_option();
301 self
302 }
303}
304
305#[skip_serializing_none]
307#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
308#[schemars(extend("x-side" = "agent", "x-method" = AUTHENTICATE_METHOD_NAME))]
309#[serde(rename_all = "camelCase")]
310#[non_exhaustive]
311pub struct AuthenticateResponse {
312 #[serde(rename = "_meta")]
318 pub meta: Option<Meta>,
319}
320
321impl AuthenticateResponse {
322 #[must_use]
323 pub fn new() -> Self {
324 Self::default()
325 }
326
327 #[must_use]
333 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
334 self.meta = meta.into_option();
335 self
336 }
337}
338
339#[skip_serializing_none]
345#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
346#[schemars(extend("x-side" = "agent", "x-method" = LOGOUT_METHOD_NAME))]
347#[serde(rename_all = "camelCase")]
348#[non_exhaustive]
349pub struct LogoutRequest {
350 #[serde(rename = "_meta")]
356 pub meta: Option<Meta>,
357}
358
359impl LogoutRequest {
360 #[must_use]
361 pub fn new() -> Self {
362 Self::default()
363 }
364
365 #[must_use]
371 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
372 self.meta = meta.into_option();
373 self
374 }
375}
376
377#[skip_serializing_none]
379#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
380#[schemars(extend("x-side" = "agent", "x-method" = LOGOUT_METHOD_NAME))]
381#[serde(rename_all = "camelCase")]
382#[non_exhaustive]
383pub struct LogoutResponse {
384 #[serde(rename = "_meta")]
390 pub meta: Option<Meta>,
391}
392
393impl LogoutResponse {
394 #[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#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
415#[serde(rename_all = "camelCase")]
416#[non_exhaustive]
417pub struct AgentAuthCapabilities {
418 #[serde_as(deserialize_as = "DefaultOnError")]
422 #[schemars(extend("x-deserialize-default-on-error" = true))]
423 #[serde(default)]
424 pub logout: Option<LogoutCapabilities>,
425 #[serde(rename = "_meta")]
431 pub meta: Option<Meta>,
432}
433
434impl AgentAuthCapabilities {
435 #[must_use]
436 pub fn new() -> Self {
437 Self::default()
438 }
439
440 #[must_use]
442 pub fn logout(mut self, logout: impl IntoOption<LogoutCapabilities>) -> Self {
443 self.logout = logout.into_option();
444 self
445 }
446
447 #[must_use]
453 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
454 self.meta = meta.into_option();
455 self
456 }
457}
458
459#[skip_serializing_none]
463#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
464#[non_exhaustive]
465pub struct LogoutCapabilities {
466 #[serde(rename = "_meta")]
472 pub meta: Option<Meta>,
473}
474
475impl LogoutCapabilities {
476 #[must_use]
477 pub fn new() -> Self {
478 Self::default()
479 }
480
481 #[must_use]
487 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
488 self.meta = meta.into_option();
489 self
490 }
491}
492
493#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, Display, From)]
494#[serde(transparent)]
495#[from(Arc<str>, String, &'static str)]
496#[non_exhaustive]
497pub struct AuthMethodId(pub Arc<str>);
498
499impl AuthMethodId {
500 #[must_use]
501 pub fn new(id: impl Into<Arc<str>>) -> Self {
502 Self(id.into())
503 }
504}
505
506#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
511#[serde(tag = "type", rename_all = "snake_case")]
512#[non_exhaustive]
513pub enum AuthMethod {
514 #[cfg(feature = "unstable_auth_methods")]
520 EnvVar(AuthMethodEnvVar),
521 #[cfg(feature = "unstable_auth_methods")]
527 Terminal(AuthMethodTerminal),
528 #[serde(untagged)]
532 Agent(AuthMethodAgent),
533}
534
535impl AuthMethod {
536 #[must_use]
538 pub fn id(&self) -> &AuthMethodId {
539 match self {
540 Self::Agent(a) => &a.id,
541 #[cfg(feature = "unstable_auth_methods")]
542 Self::EnvVar(e) => &e.id,
543 #[cfg(feature = "unstable_auth_methods")]
544 Self::Terminal(t) => &t.id,
545 }
546 }
547
548 #[must_use]
550 pub fn name(&self) -> &str {
551 match self {
552 Self::Agent(a) => &a.name,
553 #[cfg(feature = "unstable_auth_methods")]
554 Self::EnvVar(e) => &e.name,
555 #[cfg(feature = "unstable_auth_methods")]
556 Self::Terminal(t) => &t.name,
557 }
558 }
559
560 #[must_use]
562 pub fn description(&self) -> Option<&str> {
563 match self {
564 Self::Agent(a) => a.description.as_deref(),
565 #[cfg(feature = "unstable_auth_methods")]
566 Self::EnvVar(e) => e.description.as_deref(),
567 #[cfg(feature = "unstable_auth_methods")]
568 Self::Terminal(t) => t.description.as_deref(),
569 }
570 }
571
572 #[must_use]
578 pub fn meta(&self) -> Option<&Meta> {
579 match self {
580 Self::Agent(a) => a.meta.as_ref(),
581 #[cfg(feature = "unstable_auth_methods")]
582 Self::EnvVar(e) => e.meta.as_ref(),
583 #[cfg(feature = "unstable_auth_methods")]
584 Self::Terminal(t) => t.meta.as_ref(),
585 }
586 }
587}
588
589#[skip_serializing_none]
593#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
594#[serde(rename_all = "camelCase")]
595#[non_exhaustive]
596pub struct AuthMethodAgent {
597 pub id: AuthMethodId,
599 pub name: String,
601 pub description: Option<String>,
603 #[serde(rename = "_meta")]
609 pub meta: Option<Meta>,
610}
611
612impl AuthMethodAgent {
613 #[must_use]
614 pub fn new(id: impl Into<AuthMethodId>, name: impl Into<String>) -> Self {
615 Self {
616 id: id.into(),
617 name: name.into(),
618 description: None,
619 meta: None,
620 }
621 }
622
623 #[must_use]
625 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
626 self.description = description.into_option();
627 self
628 }
629
630 #[must_use]
636 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
637 self.meta = meta.into_option();
638 self
639 }
640}
641
642#[cfg(feature = "unstable_auth_methods")]
650#[skip_serializing_none]
651#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
652#[serde(rename_all = "camelCase")]
653#[non_exhaustive]
654pub struct AuthMethodEnvVar {
655 pub id: AuthMethodId,
657 pub name: String,
659 pub description: Option<String>,
661 pub vars: Vec<AuthEnvVar>,
663 pub link: Option<String>,
665 #[serde(rename = "_meta")]
671 pub meta: Option<Meta>,
672}
673
674#[cfg(feature = "unstable_auth_methods")]
675impl AuthMethodEnvVar {
676 #[must_use]
677 pub fn new(
678 id: impl Into<AuthMethodId>,
679 name: impl Into<String>,
680 vars: Vec<AuthEnvVar>,
681 ) -> Self {
682 Self {
683 id: id.into(),
684 name: name.into(),
685 description: None,
686 vars,
687 link: None,
688 meta: None,
689 }
690 }
691
692 #[must_use]
694 pub fn link(mut self, link: impl IntoOption<String>) -> Self {
695 self.link = link.into_option();
696 self
697 }
698
699 #[must_use]
701 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
702 self.description = description.into_option();
703 self
704 }
705
706 #[must_use]
712 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
713 self.meta = meta.into_option();
714 self
715 }
716}
717
718#[cfg(feature = "unstable_auth_methods")]
724#[skip_serializing_none]
725#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
726#[serde(rename_all = "camelCase")]
727#[non_exhaustive]
728pub struct AuthEnvVar {
729 pub name: String,
731 pub label: Option<String>,
733 #[serde(default = "default_true", skip_serializing_if = "is_true")]
738 #[schemars(extend("default" = true))]
739 pub secret: bool,
740 #[serde(default, skip_serializing_if = "is_false")]
744 #[schemars(extend("default" = false))]
745 pub optional: bool,
746 #[serde(rename = "_meta")]
752 pub meta: Option<Meta>,
753}
754
755#[cfg(feature = "unstable_auth_methods")]
756fn default_true() -> bool {
757 true
758}
759
760#[cfg(feature = "unstable_auth_methods")]
761#[expect(clippy::trivially_copy_pass_by_ref)]
762fn is_true(v: &bool) -> bool {
763 *v
764}
765
766#[cfg(feature = "unstable_auth_methods")]
767#[expect(clippy::trivially_copy_pass_by_ref)]
768fn is_false(v: &bool) -> bool {
769 !*v
770}
771
772#[cfg(feature = "unstable_auth_methods")]
773impl AuthEnvVar {
774 #[must_use]
776 pub fn new(name: impl Into<String>) -> Self {
777 Self {
778 name: name.into(),
779 label: None,
780 secret: true,
781 optional: false,
782 meta: None,
783 }
784 }
785
786 #[must_use]
788 pub fn label(mut self, label: impl IntoOption<String>) -> Self {
789 self.label = label.into_option();
790 self
791 }
792
793 #[must_use]
796 pub fn secret(mut self, secret: bool) -> Self {
797 self.secret = secret;
798 self
799 }
800
801 #[must_use]
803 pub fn optional(mut self, optional: bool) -> Self {
804 self.optional = optional;
805 self
806 }
807
808 #[must_use]
814 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
815 self.meta = meta.into_option();
816 self
817 }
818}
819
820#[cfg(feature = "unstable_auth_methods")]
828#[skip_serializing_none]
829#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
830#[serde(rename_all = "camelCase")]
831#[non_exhaustive]
832pub struct AuthMethodTerminal {
833 pub id: AuthMethodId,
835 pub name: String,
837 pub description: Option<String>,
839 #[serde(default, skip_serializing_if = "Vec::is_empty")]
841 pub args: Vec<String>,
842 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
844 pub env: HashMap<String, String>,
845 #[serde(rename = "_meta")]
851 pub meta: Option<Meta>,
852}
853
854#[cfg(feature = "unstable_auth_methods")]
855impl AuthMethodTerminal {
856 #[must_use]
857 pub fn new(id: impl Into<AuthMethodId>, name: impl Into<String>) -> Self {
858 Self {
859 id: id.into(),
860 name: name.into(),
861 description: None,
862 args: Vec::new(),
863 env: HashMap::new(),
864 meta: None,
865 }
866 }
867
868 #[must_use]
870 pub fn args(mut self, args: Vec<String>) -> Self {
871 self.args = args;
872 self
873 }
874
875 #[must_use]
877 pub fn env(mut self, env: HashMap<String, String>) -> Self {
878 self.env = env;
879 self
880 }
881
882 #[must_use]
884 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
885 self.description = description.into_option();
886 self
887 }
888
889 #[must_use]
895 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
896 self.meta = meta.into_option();
897 self
898 }
899}
900
901#[skip_serializing_none]
907#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
908#[schemars(extend("x-side" = "agent", "x-method" = SESSION_NEW_METHOD_NAME))]
909#[serde(rename_all = "camelCase")]
910#[non_exhaustive]
911pub struct NewSessionRequest {
912 pub cwd: PathBuf,
914 #[serde(default, skip_serializing_if = "Vec::is_empty")]
920 pub additional_directories: Vec<PathBuf>,
921 pub mcp_servers: Vec<McpServer>,
923 #[serde(rename = "_meta")]
929 pub meta: Option<Meta>,
930}
931
932impl NewSessionRequest {
933 #[must_use]
934 pub fn new(cwd: impl Into<PathBuf>) -> Self {
935 Self {
936 cwd: cwd.into(),
937 additional_directories: vec![],
938 mcp_servers: vec![],
939 meta: None,
940 }
941 }
942
943 #[must_use]
945 pub fn additional_directories(mut self, additional_directories: Vec<PathBuf>) -> Self {
946 self.additional_directories = additional_directories;
947 self
948 }
949
950 #[must_use]
952 pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
953 self.mcp_servers = mcp_servers;
954 self
955 }
956
957 #[must_use]
963 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
964 self.meta = meta.into_option();
965 self
966 }
967}
968
969#[serde_as]
973#[skip_serializing_none]
974#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
975#[schemars(extend("x-side" = "agent", "x-method" = SESSION_NEW_METHOD_NAME))]
976#[serde(rename_all = "camelCase")]
977#[non_exhaustive]
978pub struct NewSessionResponse {
979 pub session_id: SessionId,
983 #[serde_as(deserialize_as = "DefaultOnError")]
987 #[schemars(extend("x-deserialize-default-on-error" = true))]
988 #[serde(default)]
989 pub modes: Option<SessionModeState>,
990 #[serde_as(deserialize_as = "DefaultOnError<Option<VecSkipError<_, SkipListener>>>")]
992 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
993 #[serde(default)]
994 pub config_options: Option<Vec<SessionConfigOption>>,
995 #[serde(rename = "_meta")]
1001 pub meta: Option<Meta>,
1002}
1003
1004impl NewSessionResponse {
1005 #[must_use]
1006 pub fn new(session_id: impl Into<SessionId>) -> Self {
1007 Self {
1008 session_id: session_id.into(),
1009 modes: None,
1010 config_options: None,
1011 meta: None,
1012 }
1013 }
1014
1015 #[must_use]
1019 pub fn modes(mut self, modes: impl IntoOption<SessionModeState>) -> Self {
1020 self.modes = modes.into_option();
1021 self
1022 }
1023
1024 #[must_use]
1026 pub fn config_options(
1027 mut self,
1028 config_options: impl IntoOption<Vec<SessionConfigOption>>,
1029 ) -> Self {
1030 self.config_options = config_options.into_option();
1031 self
1032 }
1033
1034 #[must_use]
1040 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1041 self.meta = meta.into_option();
1042 self
1043 }
1044}
1045
1046#[skip_serializing_none]
1054#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1055#[schemars(extend("x-side" = "agent", "x-method" = SESSION_LOAD_METHOD_NAME))]
1056#[serde(rename_all = "camelCase")]
1057#[non_exhaustive]
1058pub struct LoadSessionRequest {
1059 pub mcp_servers: Vec<McpServer>,
1061 pub cwd: PathBuf,
1063 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1070 pub additional_directories: Vec<PathBuf>,
1071 pub session_id: SessionId,
1073 #[serde(rename = "_meta")]
1079 pub meta: Option<Meta>,
1080}
1081
1082impl LoadSessionRequest {
1083 #[must_use]
1084 pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<PathBuf>) -> Self {
1085 Self {
1086 mcp_servers: vec![],
1087 cwd: cwd.into(),
1088 additional_directories: vec![],
1089 session_id: session_id.into(),
1090 meta: None,
1091 }
1092 }
1093
1094 #[must_use]
1096 pub fn additional_directories(mut self, additional_directories: Vec<PathBuf>) -> Self {
1097 self.additional_directories = additional_directories;
1098 self
1099 }
1100
1101 #[must_use]
1103 pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1104 self.mcp_servers = mcp_servers;
1105 self
1106 }
1107
1108 #[must_use]
1114 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1115 self.meta = meta.into_option();
1116 self
1117 }
1118}
1119
1120#[serde_as]
1122#[skip_serializing_none]
1123#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1124#[schemars(extend("x-side" = "agent", "x-method" = SESSION_LOAD_METHOD_NAME))]
1125#[serde(rename_all = "camelCase")]
1126#[non_exhaustive]
1127pub struct LoadSessionResponse {
1128 #[serde_as(deserialize_as = "DefaultOnError")]
1132 #[schemars(extend("x-deserialize-default-on-error" = true))]
1133 #[serde(default)]
1134 pub modes: Option<SessionModeState>,
1135 #[serde_as(deserialize_as = "DefaultOnError<Option<VecSkipError<_, SkipListener>>>")]
1137 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
1138 #[serde(default)]
1139 pub config_options: Option<Vec<SessionConfigOption>>,
1140 #[serde(rename = "_meta")]
1146 pub meta: Option<Meta>,
1147}
1148
1149impl LoadSessionResponse {
1150 #[must_use]
1151 pub fn new() -> Self {
1152 Self::default()
1153 }
1154
1155 #[must_use]
1159 pub fn modes(mut self, modes: impl IntoOption<SessionModeState>) -> Self {
1160 self.modes = modes.into_option();
1161 self
1162 }
1163
1164 #[must_use]
1166 pub fn config_options(
1167 mut self,
1168 config_options: impl IntoOption<Vec<SessionConfigOption>>,
1169 ) -> Self {
1170 self.config_options = config_options.into_option();
1171 self
1172 }
1173
1174 #[must_use]
1180 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1181 self.meta = meta.into_option();
1182 self
1183 }
1184}
1185
1186#[cfg(feature = "unstable_session_fork")]
1199#[skip_serializing_none]
1200#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1201#[schemars(extend("x-side" = "agent", "x-method" = SESSION_FORK_METHOD_NAME))]
1202#[serde(rename_all = "camelCase")]
1203#[non_exhaustive]
1204pub struct ForkSessionRequest {
1205 pub session_id: SessionId,
1207 pub cwd: PathBuf,
1209 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1215 pub additional_directories: Vec<PathBuf>,
1216 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1218 pub mcp_servers: Vec<McpServer>,
1219 #[serde(rename = "_meta")]
1225 pub meta: Option<Meta>,
1226}
1227
1228#[cfg(feature = "unstable_session_fork")]
1229impl ForkSessionRequest {
1230 #[must_use]
1231 pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<PathBuf>) -> Self {
1232 Self {
1233 session_id: session_id.into(),
1234 cwd: cwd.into(),
1235 additional_directories: vec![],
1236 mcp_servers: vec![],
1237 meta: None,
1238 }
1239 }
1240
1241 #[must_use]
1243 pub fn additional_directories(mut self, additional_directories: Vec<PathBuf>) -> Self {
1244 self.additional_directories = additional_directories;
1245 self
1246 }
1247
1248 #[must_use]
1250 pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1251 self.mcp_servers = mcp_servers;
1252 self
1253 }
1254
1255 #[must_use]
1261 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1262 self.meta = meta.into_option();
1263 self
1264 }
1265}
1266
1267#[cfg(feature = "unstable_session_fork")]
1273#[serde_as]
1274#[skip_serializing_none]
1275#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1276#[schemars(extend("x-side" = "agent", "x-method" = SESSION_FORK_METHOD_NAME))]
1277#[serde(rename_all = "camelCase")]
1278#[non_exhaustive]
1279pub struct ForkSessionResponse {
1280 pub session_id: SessionId,
1282 #[serde_as(deserialize_as = "DefaultOnError")]
1286 #[schemars(extend("x-deserialize-default-on-error" = true))]
1287 #[serde(default)]
1288 pub modes: Option<SessionModeState>,
1289 #[serde_as(deserialize_as = "DefaultOnError<Option<VecSkipError<_, SkipListener>>>")]
1291 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
1292 #[serde(default)]
1293 pub config_options: Option<Vec<SessionConfigOption>>,
1294 #[serde(rename = "_meta")]
1300 pub meta: Option<Meta>,
1301}
1302
1303#[cfg(feature = "unstable_session_fork")]
1304impl ForkSessionResponse {
1305 #[must_use]
1306 pub fn new(session_id: impl Into<SessionId>) -> Self {
1307 Self {
1308 session_id: session_id.into(),
1309 modes: None,
1310 config_options: None,
1311 meta: None,
1312 }
1313 }
1314
1315 #[must_use]
1319 pub fn modes(mut self, modes: impl IntoOption<SessionModeState>) -> Self {
1320 self.modes = modes.into_option();
1321 self
1322 }
1323
1324 #[must_use]
1326 pub fn config_options(
1327 mut self,
1328 config_options: impl IntoOption<Vec<SessionConfigOption>>,
1329 ) -> Self {
1330 self.config_options = config_options.into_option();
1331 self
1332 }
1333
1334 #[must_use]
1340 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1341 self.meta = meta.into_option();
1342 self
1343 }
1344}
1345
1346#[skip_serializing_none]
1355#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1356#[schemars(extend("x-side" = "agent", "x-method" = SESSION_RESUME_METHOD_NAME))]
1357#[serde(rename_all = "camelCase")]
1358#[non_exhaustive]
1359pub struct ResumeSessionRequest {
1360 pub session_id: SessionId,
1362 pub cwd: PathBuf,
1364 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1371 pub additional_directories: Vec<PathBuf>,
1372 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1374 pub mcp_servers: Vec<McpServer>,
1375 #[serde(rename = "_meta")]
1381 pub meta: Option<Meta>,
1382}
1383
1384impl ResumeSessionRequest {
1385 #[must_use]
1386 pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<PathBuf>) -> Self {
1387 Self {
1388 session_id: session_id.into(),
1389 cwd: cwd.into(),
1390 additional_directories: vec![],
1391 mcp_servers: vec![],
1392 meta: None,
1393 }
1394 }
1395
1396 #[must_use]
1398 pub fn additional_directories(mut self, additional_directories: Vec<PathBuf>) -> Self {
1399 self.additional_directories = additional_directories;
1400 self
1401 }
1402
1403 #[must_use]
1405 pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1406 self.mcp_servers = mcp_servers;
1407 self
1408 }
1409
1410 #[must_use]
1416 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1417 self.meta = meta.into_option();
1418 self
1419 }
1420}
1421
1422#[serde_as]
1424#[skip_serializing_none]
1425#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1426#[schemars(extend("x-side" = "agent", "x-method" = SESSION_RESUME_METHOD_NAME))]
1427#[serde(rename_all = "camelCase")]
1428#[non_exhaustive]
1429pub struct ResumeSessionResponse {
1430 #[serde_as(deserialize_as = "DefaultOnError")]
1434 #[schemars(extend("x-deserialize-default-on-error" = true))]
1435 #[serde(default)]
1436 pub modes: Option<SessionModeState>,
1437 #[serde_as(deserialize_as = "DefaultOnError<Option<VecSkipError<_, SkipListener>>>")]
1439 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
1440 #[serde(default)]
1441 pub config_options: Option<Vec<SessionConfigOption>>,
1442 #[serde(rename = "_meta")]
1448 pub meta: Option<Meta>,
1449}
1450
1451impl ResumeSessionResponse {
1452 #[must_use]
1453 pub fn new() -> Self {
1454 Self::default()
1455 }
1456
1457 #[must_use]
1461 pub fn modes(mut self, modes: impl IntoOption<SessionModeState>) -> Self {
1462 self.modes = modes.into_option();
1463 self
1464 }
1465
1466 #[must_use]
1468 pub fn config_options(
1469 mut self,
1470 config_options: impl IntoOption<Vec<SessionConfigOption>>,
1471 ) -> Self {
1472 self.config_options = config_options.into_option();
1473 self
1474 }
1475
1476 #[must_use]
1482 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1483 self.meta = meta.into_option();
1484 self
1485 }
1486}
1487
1488#[skip_serializing_none]
1498#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1499#[schemars(extend("x-side" = "agent", "x-method" = SESSION_CLOSE_METHOD_NAME))]
1500#[serde(rename_all = "camelCase")]
1501#[non_exhaustive]
1502pub struct CloseSessionRequest {
1503 pub session_id: SessionId,
1505 #[serde(rename = "_meta")]
1511 pub meta: Option<Meta>,
1512}
1513
1514impl CloseSessionRequest {
1515 #[must_use]
1516 pub fn new(session_id: impl Into<SessionId>) -> Self {
1517 Self {
1518 session_id: session_id.into(),
1519 meta: None,
1520 }
1521 }
1522
1523 #[must_use]
1529 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1530 self.meta = meta.into_option();
1531 self
1532 }
1533}
1534
1535#[skip_serializing_none]
1537#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1538#[schemars(extend("x-side" = "agent", "x-method" = SESSION_CLOSE_METHOD_NAME))]
1539#[serde(rename_all = "camelCase")]
1540#[non_exhaustive]
1541pub struct CloseSessionResponse {
1542 #[serde(rename = "_meta")]
1548 pub meta: Option<Meta>,
1549}
1550
1551impl CloseSessionResponse {
1552 #[must_use]
1553 pub fn new() -> Self {
1554 Self::default()
1555 }
1556
1557 #[must_use]
1563 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1564 self.meta = meta.into_option();
1565 self
1566 }
1567}
1568
1569#[skip_serializing_none]
1575#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1576#[schemars(extend("x-side" = "agent", "x-method" = SESSION_LIST_METHOD_NAME))]
1577#[serde(rename_all = "camelCase")]
1578#[non_exhaustive]
1579pub struct ListSessionsRequest {
1580 pub cwd: Option<PathBuf>,
1582 pub cursor: Option<String>,
1584 #[serde(rename = "_meta")]
1590 pub meta: Option<Meta>,
1591}
1592
1593impl ListSessionsRequest {
1594 #[must_use]
1595 pub fn new() -> Self {
1596 Self::default()
1597 }
1598
1599 #[must_use]
1601 pub fn cwd(mut self, cwd: impl IntoOption<PathBuf>) -> Self {
1602 self.cwd = cwd.into_option();
1603 self
1604 }
1605
1606 #[must_use]
1608 pub fn cursor(mut self, cursor: impl IntoOption<String>) -> Self {
1609 self.cursor = cursor.into_option();
1610 self
1611 }
1612
1613 #[must_use]
1619 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1620 self.meta = meta.into_option();
1621 self
1622 }
1623}
1624
1625#[serde_as]
1627#[skip_serializing_none]
1628#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1629#[schemars(extend("x-side" = "agent", "x-method" = SESSION_LIST_METHOD_NAME))]
1630#[serde(rename_all = "camelCase")]
1631#[non_exhaustive]
1632pub struct ListSessionsResponse {
1633 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1635 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
1636 pub sessions: Vec<SessionInfo>,
1637 pub next_cursor: Option<String>,
1640 #[serde(rename = "_meta")]
1646 pub meta: Option<Meta>,
1647}
1648
1649impl ListSessionsResponse {
1650 #[must_use]
1651 pub fn new(sessions: Vec<SessionInfo>) -> Self {
1652 Self {
1653 sessions,
1654 next_cursor: None,
1655 meta: None,
1656 }
1657 }
1658
1659 #[must_use]
1660 pub fn next_cursor(mut self, next_cursor: impl IntoOption<String>) -> Self {
1661 self.next_cursor = next_cursor.into_option();
1662 self
1663 }
1664
1665 #[must_use]
1671 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1672 self.meta = meta.into_option();
1673 self
1674 }
1675}
1676
1677#[skip_serializing_none]
1683#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1684#[schemars(extend("x-side" = "agent", "x-method" = SESSION_DELETE_METHOD_NAME))]
1685#[serde(rename_all = "camelCase")]
1686#[non_exhaustive]
1687pub struct DeleteSessionRequest {
1688 pub session_id: SessionId,
1690 #[serde(rename = "_meta")]
1696 pub meta: Option<Meta>,
1697}
1698
1699impl DeleteSessionRequest {
1700 #[must_use]
1701 pub fn new(session_id: impl Into<SessionId>) -> Self {
1702 Self {
1703 session_id: session_id.into(),
1704 meta: None,
1705 }
1706 }
1707
1708 #[must_use]
1714 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1715 self.meta = meta.into_option();
1716 self
1717 }
1718}
1719
1720#[skip_serializing_none]
1722#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1723#[schemars(extend("x-side" = "agent", "x-method" = SESSION_DELETE_METHOD_NAME))]
1724#[serde(rename_all = "camelCase")]
1725#[non_exhaustive]
1726pub struct DeleteSessionResponse {
1727 #[serde(rename = "_meta")]
1733 pub meta: Option<Meta>,
1734}
1735
1736impl DeleteSessionResponse {
1737 #[must_use]
1738 pub fn new() -> Self {
1739 Self::default()
1740 }
1741
1742 #[must_use]
1748 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1749 self.meta = meta.into_option();
1750 self
1751 }
1752}
1753
1754#[serde_as]
1756#[skip_serializing_none]
1757#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1758#[serde(rename_all = "camelCase")]
1759#[non_exhaustive]
1760pub struct SessionInfo {
1761 pub session_id: SessionId,
1763 pub cwd: PathBuf,
1765 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1771 pub additional_directories: Vec<PathBuf>,
1772
1773 #[serde_as(deserialize_as = "DefaultOnError")]
1775 #[schemars(extend("x-deserialize-default-on-error" = true))]
1776 #[serde(default)]
1777 pub title: Option<String>,
1778 #[serde_as(deserialize_as = "DefaultOnError")]
1780 #[schemars(extend("x-deserialize-default-on-error" = true))]
1781 #[serde(default)]
1782 pub updated_at: Option<String>,
1783 #[serde(rename = "_meta")]
1789 pub meta: Option<Meta>,
1790}
1791
1792impl SessionInfo {
1793 #[must_use]
1794 pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<PathBuf>) -> Self {
1795 Self {
1796 session_id: session_id.into(),
1797 cwd: cwd.into(),
1798 additional_directories: vec![],
1799 title: None,
1800 updated_at: None,
1801 meta: None,
1802 }
1803 }
1804
1805 #[must_use]
1807 pub fn additional_directories(mut self, additional_directories: Vec<PathBuf>) -> Self {
1808 self.additional_directories = additional_directories;
1809 self
1810 }
1811
1812 #[must_use]
1814 pub fn title(mut self, title: impl IntoOption<String>) -> Self {
1815 self.title = title.into_option();
1816 self
1817 }
1818
1819 #[must_use]
1821 pub fn updated_at(mut self, updated_at: impl IntoOption<String>) -> Self {
1822 self.updated_at = updated_at.into_option();
1823 self
1824 }
1825
1826 #[must_use]
1832 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1833 self.meta = meta.into_option();
1834 self
1835 }
1836}
1837
1838#[serde_as]
1842#[skip_serializing_none]
1843#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1844#[serde(rename_all = "camelCase")]
1845#[non_exhaustive]
1846pub struct SessionModeState {
1847 pub current_mode_id: SessionModeId,
1849 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1851 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
1852 pub available_modes: Vec<SessionMode>,
1853 #[serde(rename = "_meta")]
1859 pub meta: Option<Meta>,
1860}
1861
1862impl SessionModeState {
1863 #[must_use]
1864 pub fn new(
1865 current_mode_id: impl Into<SessionModeId>,
1866 available_modes: Vec<SessionMode>,
1867 ) -> Self {
1868 Self {
1869 current_mode_id: current_mode_id.into(),
1870 available_modes,
1871 meta: None,
1872 }
1873 }
1874
1875 #[must_use]
1881 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1882 self.meta = meta.into_option();
1883 self
1884 }
1885}
1886
1887#[skip_serializing_none]
1891#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1892#[serde(rename_all = "camelCase")]
1893#[non_exhaustive]
1894pub struct SessionMode {
1895 pub id: SessionModeId,
1896 pub name: String,
1897 #[serde(default)]
1898 pub description: Option<String>,
1899 #[serde(rename = "_meta")]
1905 pub meta: Option<Meta>,
1906}
1907
1908impl SessionMode {
1909 #[must_use]
1910 pub fn new(id: impl Into<SessionModeId>, name: impl Into<String>) -> Self {
1911 Self {
1912 id: id.into(),
1913 name: name.into(),
1914 description: None,
1915 meta: None,
1916 }
1917 }
1918
1919 #[must_use]
1920 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
1921 self.description = description.into_option();
1922 self
1923 }
1924
1925 #[must_use]
1931 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1932 self.meta = meta.into_option();
1933 self
1934 }
1935}
1936
1937#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, From, Display)]
1939#[serde(transparent)]
1940#[from(Arc<str>, String, &'static str)]
1941#[non_exhaustive]
1942pub struct SessionModeId(pub Arc<str>);
1943
1944impl SessionModeId {
1945 #[must_use]
1946 pub fn new(id: impl Into<Arc<str>>) -> Self {
1947 Self(id.into())
1948 }
1949}
1950
1951#[skip_serializing_none]
1953#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1954#[schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_MODE_METHOD_NAME))]
1955#[serde(rename_all = "camelCase")]
1956#[non_exhaustive]
1957pub struct SetSessionModeRequest {
1958 pub session_id: SessionId,
1960 pub mode_id: SessionModeId,
1962 #[serde(rename = "_meta")]
1968 pub meta: Option<Meta>,
1969}
1970
1971impl SetSessionModeRequest {
1972 #[must_use]
1973 pub fn new(session_id: impl Into<SessionId>, mode_id: impl Into<SessionModeId>) -> Self {
1974 Self {
1975 session_id: session_id.into(),
1976 mode_id: mode_id.into(),
1977 meta: None,
1978 }
1979 }
1980
1981 #[must_use]
1982 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1983 self.meta = meta.into_option();
1984 self
1985 }
1986}
1987
1988#[skip_serializing_none]
1990#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1991#[schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_MODE_METHOD_NAME))]
1992#[serde(rename_all = "camelCase")]
1993#[non_exhaustive]
1994pub struct SetSessionModeResponse {
1995 #[serde(rename = "_meta")]
2001 pub meta: Option<Meta>,
2002}
2003
2004impl SetSessionModeResponse {
2005 #[must_use]
2006 pub fn new() -> Self {
2007 Self::default()
2008 }
2009
2010 #[must_use]
2016 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2017 self.meta = meta.into_option();
2018 self
2019 }
2020}
2021
2022#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, From, Display)]
2026#[serde(transparent)]
2027#[from(Arc<str>, String, &'static str)]
2028#[non_exhaustive]
2029pub struct SessionConfigId(pub Arc<str>);
2030
2031impl SessionConfigId {
2032 #[must_use]
2033 pub fn new(id: impl Into<Arc<str>>) -> Self {
2034 Self(id.into())
2035 }
2036}
2037
2038#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, From, Display)]
2040#[serde(transparent)]
2041#[from(Arc<str>, String, &'static str)]
2042#[non_exhaustive]
2043pub struct SessionConfigValueId(pub Arc<str>);
2044
2045impl SessionConfigValueId {
2046 #[must_use]
2047 pub fn new(id: impl Into<Arc<str>>) -> Self {
2048 Self(id.into())
2049 }
2050}
2051
2052#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, From, Display)]
2054#[serde(transparent)]
2055#[from(Arc<str>, String, &'static str)]
2056#[non_exhaustive]
2057pub struct SessionConfigGroupId(pub Arc<str>);
2058
2059impl SessionConfigGroupId {
2060 #[must_use]
2061 pub fn new(id: impl Into<Arc<str>>) -> Self {
2062 Self(id.into())
2063 }
2064}
2065
2066#[skip_serializing_none]
2068#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2069#[serde(rename_all = "camelCase")]
2070#[non_exhaustive]
2071pub struct SessionConfigSelectOption {
2072 pub value: SessionConfigValueId,
2074 pub name: String,
2076 #[serde(default)]
2078 pub description: Option<String>,
2079 #[serde(rename = "_meta")]
2085 pub meta: Option<Meta>,
2086}
2087
2088impl SessionConfigSelectOption {
2089 #[must_use]
2090 pub fn new(value: impl Into<SessionConfigValueId>, name: impl Into<String>) -> Self {
2091 Self {
2092 value: value.into(),
2093 name: name.into(),
2094 description: None,
2095 meta: None,
2096 }
2097 }
2098
2099 #[must_use]
2100 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
2101 self.description = description.into_option();
2102 self
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#[skip_serializing_none]
2119#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2120#[serde(rename_all = "camelCase")]
2121#[non_exhaustive]
2122pub struct SessionConfigSelectGroup {
2123 pub group: SessionConfigGroupId,
2125 pub name: String,
2127 pub options: Vec<SessionConfigSelectOption>,
2129 #[serde(rename = "_meta")]
2135 pub meta: Option<Meta>,
2136}
2137
2138impl SessionConfigSelectGroup {
2139 #[must_use]
2140 pub fn new(
2141 group: impl Into<SessionConfigGroupId>,
2142 name: impl Into<String>,
2143 options: Vec<SessionConfigSelectOption>,
2144 ) -> Self {
2145 Self {
2146 group: group.into(),
2147 name: name.into(),
2148 options,
2149 meta: None,
2150 }
2151 }
2152
2153 #[must_use]
2159 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2160 self.meta = meta.into_option();
2161 self
2162 }
2163}
2164
2165#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2167#[serde(untagged)]
2168#[non_exhaustive]
2169pub enum SessionConfigSelectOptions {
2170 Ungrouped(Vec<SessionConfigSelectOption>),
2172 Grouped(Vec<SessionConfigSelectGroup>),
2174}
2175
2176impl From<Vec<SessionConfigSelectOption>> for SessionConfigSelectOptions {
2177 fn from(options: Vec<SessionConfigSelectOption>) -> Self {
2178 SessionConfigSelectOptions::Ungrouped(options)
2179 }
2180}
2181
2182impl From<Vec<SessionConfigSelectGroup>> for SessionConfigSelectOptions {
2183 fn from(groups: Vec<SessionConfigSelectGroup>) -> Self {
2184 SessionConfigSelectOptions::Grouped(groups)
2185 }
2186}
2187
2188#[skip_serializing_none]
2190#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2191#[serde(rename_all = "camelCase")]
2192#[non_exhaustive]
2193pub struct SessionConfigSelect {
2194 pub current_value: SessionConfigValueId,
2196 pub options: SessionConfigSelectOptions,
2198}
2199
2200impl SessionConfigSelect {
2201 #[must_use]
2202 pub fn new(
2203 current_value: impl Into<SessionConfigValueId>,
2204 options: impl Into<SessionConfigSelectOptions>,
2205 ) -> Self {
2206 Self {
2207 current_value: current_value.into(),
2208 options: options.into(),
2209 }
2210 }
2211}
2212
2213#[cfg(feature = "unstable_boolean_config")]
2219#[skip_serializing_none]
2220#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2221#[serde(rename_all = "camelCase")]
2222#[non_exhaustive]
2223pub struct SessionConfigBoolean {
2224 pub current_value: bool,
2226}
2227
2228#[cfg(feature = "unstable_boolean_config")]
2229impl SessionConfigBoolean {
2230 #[must_use]
2231 pub fn new(current_value: bool) -> Self {
2232 Self { current_value }
2233 }
2234}
2235
2236#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2246#[serde(rename_all = "snake_case")]
2247#[non_exhaustive]
2248pub enum SessionConfigOptionCategory {
2249 Mode,
2251 Model,
2253 ThoughtLevel,
2255 #[serde(untagged)]
2257 Other(String),
2258}
2259
2260#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2262#[serde(tag = "type", rename_all = "snake_case")]
2263#[schemars(extend("discriminator" = {"propertyName": "type"}))]
2264#[non_exhaustive]
2265pub enum SessionConfigKind {
2266 Select(SessionConfigSelect),
2268 #[cfg(feature = "unstable_boolean_config")]
2274 Boolean(SessionConfigBoolean),
2275}
2276
2277#[serde_as]
2279#[skip_serializing_none]
2280#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2281#[serde(rename_all = "camelCase")]
2282#[non_exhaustive]
2283pub struct SessionConfigOption {
2284 pub id: SessionConfigId,
2286 pub name: String,
2288 #[serde(default)]
2290 pub description: Option<String>,
2291 #[serde_as(deserialize_as = "DefaultOnError")]
2293 #[schemars(extend("x-deserialize-default-on-error" = true))]
2294 #[serde(default)]
2295 pub category: Option<SessionConfigOptionCategory>,
2296 #[serde(flatten)]
2298 pub kind: SessionConfigKind,
2299 #[serde(rename = "_meta")]
2305 pub meta: Option<Meta>,
2306}
2307
2308impl SessionConfigOption {
2309 #[must_use]
2310 pub fn new(
2311 id: impl Into<SessionConfigId>,
2312 name: impl Into<String>,
2313 kind: SessionConfigKind,
2314 ) -> Self {
2315 Self {
2316 id: id.into(),
2317 name: name.into(),
2318 description: None,
2319 category: None,
2320 kind,
2321 meta: None,
2322 }
2323 }
2324
2325 #[must_use]
2326 pub fn select(
2327 id: impl Into<SessionConfigId>,
2328 name: impl Into<String>,
2329 current_value: impl Into<SessionConfigValueId>,
2330 options: impl Into<SessionConfigSelectOptions>,
2331 ) -> Self {
2332 Self::new(
2333 id,
2334 name,
2335 SessionConfigKind::Select(SessionConfigSelect::new(current_value, options)),
2336 )
2337 }
2338
2339 #[cfg(feature = "unstable_boolean_config")]
2343 #[must_use]
2344 pub fn boolean(
2345 id: impl Into<SessionConfigId>,
2346 name: impl Into<String>,
2347 current_value: bool,
2348 ) -> Self {
2349 Self::new(
2350 id,
2351 name,
2352 SessionConfigKind::Boolean(SessionConfigBoolean::new(current_value)),
2353 )
2354 }
2355
2356 #[must_use]
2357 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
2358 self.description = description.into_option();
2359 self
2360 }
2361
2362 #[must_use]
2363 pub fn category(mut self, category: impl IntoOption<SessionConfigOptionCategory>) -> Self {
2364 self.category = category.into_option();
2365 self
2366 }
2367
2368 #[must_use]
2374 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2375 self.meta = meta.into_option();
2376 self
2377 }
2378}
2379
2380#[cfg(feature = "unstable_boolean_config")]
2395#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2396#[serde(tag = "type", rename_all = "snake_case")]
2397#[non_exhaustive]
2398pub enum SessionConfigOptionValue {
2399 Boolean {
2401 value: bool,
2403 },
2404 #[serde(untagged)]
2410 ValueId {
2411 value: SessionConfigValueId,
2413 },
2414}
2415
2416#[cfg(feature = "unstable_boolean_config")]
2417impl SessionConfigOptionValue {
2418 #[must_use]
2420 pub fn value_id(id: impl Into<SessionConfigValueId>) -> Self {
2421 Self::ValueId { value: id.into() }
2422 }
2423
2424 #[must_use]
2426 pub fn boolean(val: bool) -> Self {
2427 Self::Boolean { value: val }
2428 }
2429
2430 #[must_use]
2433 pub fn as_value_id(&self) -> Option<&SessionConfigValueId> {
2434 match self {
2435 Self::ValueId { value } => Some(value),
2436 _ => None,
2437 }
2438 }
2439
2440 #[must_use]
2442 pub fn as_bool(&self) -> Option<bool> {
2443 match self {
2444 Self::Boolean { value } => Some(*value),
2445 _ => None,
2446 }
2447 }
2448}
2449
2450#[cfg(feature = "unstable_boolean_config")]
2451impl From<SessionConfigValueId> for SessionConfigOptionValue {
2452 fn from(value: SessionConfigValueId) -> Self {
2453 Self::ValueId { value }
2454 }
2455}
2456
2457#[cfg(feature = "unstable_boolean_config")]
2458impl From<bool> for SessionConfigOptionValue {
2459 fn from(value: bool) -> Self {
2460 Self::Boolean { value }
2461 }
2462}
2463
2464#[cfg(feature = "unstable_boolean_config")]
2465impl From<&str> for SessionConfigOptionValue {
2466 fn from(value: &str) -> Self {
2467 Self::ValueId {
2468 value: SessionConfigValueId::new(value),
2469 }
2470 }
2471}
2472
2473#[skip_serializing_none]
2475#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2476#[schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_CONFIG_OPTION_METHOD_NAME))]
2477#[serde(rename_all = "camelCase")]
2478#[non_exhaustive]
2479pub struct SetSessionConfigOptionRequest {
2480 pub session_id: SessionId,
2482 pub config_id: SessionConfigId,
2484 #[cfg(feature = "unstable_boolean_config")]
2489 #[serde(flatten)]
2490 pub value: SessionConfigOptionValue,
2491 #[cfg(not(feature = "unstable_boolean_config"))]
2493 pub value: SessionConfigValueId,
2494 #[serde(rename = "_meta")]
2500 pub meta: Option<Meta>,
2501}
2502
2503impl SetSessionConfigOptionRequest {
2504 #[cfg(feature = "unstable_boolean_config")]
2505 #[must_use]
2506 pub fn new(
2507 session_id: impl Into<SessionId>,
2508 config_id: impl Into<SessionConfigId>,
2509 value: impl Into<SessionConfigOptionValue>,
2510 ) -> Self {
2511 Self {
2512 session_id: session_id.into(),
2513 config_id: config_id.into(),
2514 value: value.into(),
2515 meta: None,
2516 }
2517 }
2518
2519 #[cfg(not(feature = "unstable_boolean_config"))]
2520 #[must_use]
2521 pub fn new(
2522 session_id: impl Into<SessionId>,
2523 config_id: impl Into<SessionConfigId>,
2524 value: impl Into<SessionConfigValueId>,
2525 ) -> Self {
2526 Self {
2527 session_id: session_id.into(),
2528 config_id: config_id.into(),
2529 value: value.into(),
2530 meta: None,
2531 }
2532 }
2533
2534 #[must_use]
2540 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2541 self.meta = meta.into_option();
2542 self
2543 }
2544}
2545
2546#[serde_as]
2548#[skip_serializing_none]
2549#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2550#[schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_CONFIG_OPTION_METHOD_NAME))]
2551#[serde(rename_all = "camelCase")]
2552#[non_exhaustive]
2553pub struct SetSessionConfigOptionResponse {
2554 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
2556 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
2557 pub config_options: Vec<SessionConfigOption>,
2558 #[serde(rename = "_meta")]
2564 pub meta: Option<Meta>,
2565}
2566
2567impl SetSessionConfigOptionResponse {
2568 #[must_use]
2569 pub fn new(config_options: Vec<SessionConfigOption>) -> Self {
2570 Self {
2571 config_options,
2572 meta: None,
2573 }
2574 }
2575
2576 #[must_use]
2582 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2583 self.meta = meta.into_option();
2584 self
2585 }
2586}
2587
2588#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2597#[serde(tag = "type", rename_all = "snake_case")]
2598#[non_exhaustive]
2599pub enum McpServer {
2600 Http(McpServerHttp),
2604 Sse(McpServerSse),
2608 #[cfg(feature = "unstable_mcp_over_acp")]
2617 Acp(McpServerAcp),
2618 #[serde(untagged)]
2622 Stdio(McpServerStdio),
2623}
2624
2625#[skip_serializing_none]
2627#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2628#[serde(rename_all = "camelCase")]
2629#[non_exhaustive]
2630pub struct McpServerHttp {
2631 pub name: String,
2633 pub url: String,
2635 pub headers: Vec<HttpHeader>,
2637 #[serde(rename = "_meta")]
2643 pub meta: Option<Meta>,
2644}
2645
2646impl McpServerHttp {
2647 #[must_use]
2648 pub fn new(name: impl Into<String>, url: impl Into<String>) -> Self {
2649 Self {
2650 name: name.into(),
2651 url: url.into(),
2652 headers: Vec::new(),
2653 meta: None,
2654 }
2655 }
2656
2657 #[must_use]
2659 pub fn headers(mut self, headers: Vec<HttpHeader>) -> Self {
2660 self.headers = headers;
2661 self
2662 }
2663
2664 #[must_use]
2670 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2671 self.meta = meta.into_option();
2672 self
2673 }
2674}
2675
2676#[skip_serializing_none]
2678#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2679#[serde(rename_all = "camelCase")]
2680#[non_exhaustive]
2681pub struct McpServerSse {
2682 pub name: String,
2684 pub url: String,
2686 pub headers: Vec<HttpHeader>,
2688 #[serde(rename = "_meta")]
2694 pub meta: Option<Meta>,
2695}
2696
2697impl McpServerSse {
2698 #[must_use]
2699 pub fn new(name: impl Into<String>, url: impl Into<String>) -> Self {
2700 Self {
2701 name: name.into(),
2702 url: url.into(),
2703 headers: Vec::new(),
2704 meta: None,
2705 }
2706 }
2707
2708 #[must_use]
2710 pub fn headers(mut self, headers: Vec<HttpHeader>) -> Self {
2711 self.headers = headers;
2712 self
2713 }
2714
2715 #[must_use]
2721 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2722 self.meta = meta.into_option();
2723 self
2724 }
2725}
2726
2727#[cfg(feature = "unstable_mcp_over_acp")]
2737#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, Display, From)]
2738#[serde(transparent)]
2739#[from(Arc<str>, String, &'static str)]
2740#[non_exhaustive]
2741pub struct McpServerAcpId(pub Arc<str>);
2742
2743#[cfg(feature = "unstable_mcp_over_acp")]
2744impl McpServerAcpId {
2745 #[must_use]
2746 pub fn new(id: impl Into<Arc<str>>) -> Self {
2747 Self(id.into())
2748 }
2749}
2750
2751#[skip_serializing_none]
2760#[cfg(feature = "unstable_mcp_over_acp")]
2761#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2762#[serde(rename_all = "camelCase")]
2763#[non_exhaustive]
2764pub struct McpServerAcp {
2765 pub name: String,
2767 pub id: McpServerAcpId,
2772 #[serde(rename = "_meta")]
2778 pub meta: Option<Meta>,
2779}
2780
2781#[cfg(feature = "unstable_mcp_over_acp")]
2782impl McpServerAcp {
2783 #[must_use]
2784 pub fn new(name: impl Into<String>, id: impl Into<McpServerAcpId>) -> Self {
2785 Self {
2786 name: name.into(),
2787 id: id.into(),
2788 meta: None,
2789 }
2790 }
2791
2792 #[must_use]
2798 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2799 self.meta = meta.into_option();
2800 self
2801 }
2802}
2803
2804#[skip_serializing_none]
2806#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2807#[serde(rename_all = "camelCase")]
2808#[non_exhaustive]
2809pub struct McpServerStdio {
2810 pub name: String,
2812 pub command: PathBuf,
2814 pub args: Vec<String>,
2816 pub env: Vec<EnvVariable>,
2818 #[serde(rename = "_meta")]
2824 pub meta: Option<Meta>,
2825}
2826
2827impl McpServerStdio {
2828 #[must_use]
2829 pub fn new(name: impl Into<String>, command: impl Into<PathBuf>) -> Self {
2830 Self {
2831 name: name.into(),
2832 command: command.into(),
2833 args: Vec::new(),
2834 env: Vec::new(),
2835 meta: None,
2836 }
2837 }
2838
2839 #[must_use]
2841 pub fn args(mut self, args: Vec<String>) -> Self {
2842 self.args = args;
2843 self
2844 }
2845
2846 #[must_use]
2848 pub fn env(mut self, env: Vec<EnvVariable>) -> Self {
2849 self.env = env;
2850 self
2851 }
2852
2853 #[must_use]
2859 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2860 self.meta = meta.into_option();
2861 self
2862 }
2863}
2864
2865#[skip_serializing_none]
2867#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2868#[serde(rename_all = "camelCase")]
2869#[non_exhaustive]
2870pub struct EnvVariable {
2871 pub name: String,
2873 pub value: String,
2875 #[serde(rename = "_meta")]
2881 pub meta: Option<Meta>,
2882}
2883
2884impl EnvVariable {
2885 #[must_use]
2886 pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
2887 Self {
2888 name: name.into(),
2889 value: value.into(),
2890 meta: None,
2891 }
2892 }
2893
2894 #[must_use]
2900 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2901 self.meta = meta.into_option();
2902 self
2903 }
2904}
2905
2906#[skip_serializing_none]
2908#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2909#[serde(rename_all = "camelCase")]
2910#[non_exhaustive]
2911pub struct HttpHeader {
2912 pub name: String,
2914 pub value: String,
2916 #[serde(rename = "_meta")]
2922 pub meta: Option<Meta>,
2923}
2924
2925impl HttpHeader {
2926 #[must_use]
2927 pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
2928 Self {
2929 name: name.into(),
2930 value: value.into(),
2931 meta: None,
2932 }
2933 }
2934
2935 #[must_use]
2941 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2942 self.meta = meta.into_option();
2943 self
2944 }
2945}
2946
2947#[skip_serializing_none]
2955#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
2956#[schemars(extend("x-side" = "agent", "x-method" = SESSION_PROMPT_METHOD_NAME))]
2957#[serde(rename_all = "camelCase")]
2958#[non_exhaustive]
2959pub struct PromptRequest {
2960 pub session_id: SessionId,
2962 pub prompt: Vec<ContentBlock>,
2976 #[serde(rename = "_meta")]
2982 pub meta: Option<Meta>,
2983}
2984
2985impl PromptRequest {
2986 #[must_use]
2987 pub fn new(session_id: impl Into<SessionId>, prompt: Vec<ContentBlock>) -> Self {
2988 Self {
2989 session_id: session_id.into(),
2990 prompt,
2991 meta: None,
2992 }
2993 }
2994
2995 #[must_use]
3001 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3002 self.meta = meta.into_option();
3003 self
3004 }
3005}
3006
3007#[serde_as]
3011#[skip_serializing_none]
3012#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3013#[schemars(extend("x-side" = "agent", "x-method" = SESSION_PROMPT_METHOD_NAME))]
3014#[serde(rename_all = "camelCase")]
3015#[non_exhaustive]
3016pub struct PromptResponse {
3017 pub stop_reason: StopReason,
3019 #[cfg(feature = "unstable_end_turn_token_usage")]
3025 #[serde_as(deserialize_as = "DefaultOnError")]
3026 #[schemars(extend("x-deserialize-default-on-error" = true))]
3027 #[serde(default)]
3028 pub usage: Option<Usage>,
3029 #[serde(rename = "_meta")]
3035 pub meta: Option<Meta>,
3036}
3037
3038impl PromptResponse {
3039 #[must_use]
3040 pub fn new(stop_reason: StopReason) -> Self {
3041 Self {
3042 stop_reason,
3043 #[cfg(feature = "unstable_end_turn_token_usage")]
3044 usage: None,
3045 meta: None,
3046 }
3047 }
3048
3049 #[cfg(feature = "unstable_end_turn_token_usage")]
3055 #[must_use]
3056 pub fn usage(mut self, usage: impl IntoOption<Usage>) -> Self {
3057 self.usage = usage.into_option();
3058 self
3059 }
3060
3061 #[must_use]
3067 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3068 self.meta = meta.into_option();
3069 self
3070 }
3071}
3072
3073#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
3077#[serde(rename_all = "snake_case")]
3078#[non_exhaustive]
3079pub enum StopReason {
3080 EndTurn,
3082 MaxTokens,
3084 MaxTurnRequests,
3087 Refusal,
3091 Cancelled,
3098}
3099
3100#[cfg(feature = "unstable_end_turn_token_usage")]
3106#[skip_serializing_none]
3107#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3108#[serde(rename_all = "camelCase")]
3109#[non_exhaustive]
3110pub struct Usage {
3111 pub total_tokens: u64,
3113 pub input_tokens: u64,
3115 pub output_tokens: u64,
3117 pub thought_tokens: Option<u64>,
3119 pub cached_read_tokens: Option<u64>,
3121 pub cached_write_tokens: Option<u64>,
3123 #[serde(rename = "_meta")]
3129 pub meta: Option<Meta>,
3130}
3131
3132#[cfg(feature = "unstable_end_turn_token_usage")]
3133impl Usage {
3134 #[must_use]
3135 pub fn new(total_tokens: u64, input_tokens: u64, output_tokens: u64) -> Self {
3136 Self {
3137 total_tokens,
3138 input_tokens,
3139 output_tokens,
3140 thought_tokens: None,
3141 cached_read_tokens: None,
3142 cached_write_tokens: None,
3143 meta: None,
3144 }
3145 }
3146
3147 #[must_use]
3149 pub fn thought_tokens(mut self, thought_tokens: impl IntoOption<u64>) -> Self {
3150 self.thought_tokens = thought_tokens.into_option();
3151 self
3152 }
3153
3154 #[must_use]
3156 pub fn cached_read_tokens(mut self, cached_read_tokens: impl IntoOption<u64>) -> Self {
3157 self.cached_read_tokens = cached_read_tokens.into_option();
3158 self
3159 }
3160
3161 #[must_use]
3163 pub fn cached_write_tokens(mut self, cached_write_tokens: impl IntoOption<u64>) -> Self {
3164 self.cached_write_tokens = cached_write_tokens.into_option();
3165 self
3166 }
3167
3168 #[must_use]
3174 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3175 self.meta = meta.into_option();
3176 self
3177 }
3178}
3179
3180#[cfg(feature = "unstable_llm_providers")]
3193#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3194#[serde(rename_all = "snake_case")]
3195#[non_exhaustive]
3196#[expect(clippy::doc_markdown)]
3197pub enum LlmProtocol {
3198 Anthropic,
3200 #[serde(rename = "openai")]
3202 OpenAi,
3203 Azure,
3205 Vertex,
3207 Bedrock,
3209 #[serde(untagged)]
3211 Other(String),
3212}
3213
3214#[cfg(feature = "unstable_llm_providers")]
3220#[skip_serializing_none]
3221#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3222#[serde(rename_all = "camelCase")]
3223#[non_exhaustive]
3224pub struct ProviderCurrentConfig {
3225 pub api_type: LlmProtocol,
3227 pub base_url: String,
3229 #[serde(rename = "_meta")]
3235 pub meta: Option<Meta>,
3236}
3237
3238#[cfg(feature = "unstable_llm_providers")]
3239impl ProviderCurrentConfig {
3240 #[must_use]
3241 pub fn new(api_type: LlmProtocol, base_url: impl Into<String>) -> Self {
3242 Self {
3243 api_type,
3244 base_url: base_url.into(),
3245 meta: None,
3246 }
3247 }
3248
3249 #[must_use]
3255 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3256 self.meta = meta.into_option();
3257 self
3258 }
3259}
3260
3261#[cfg(feature = "unstable_llm_providers")]
3267#[serde_as]
3268#[skip_serializing_none]
3269#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3270#[serde(rename_all = "camelCase")]
3271#[non_exhaustive]
3272pub struct ProviderInfo {
3273 pub id: String,
3275 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
3277 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
3278 pub supported: Vec<LlmProtocol>,
3279 pub required: bool,
3282 pub current: Option<ProviderCurrentConfig>,
3285 #[serde(rename = "_meta")]
3291 pub meta: Option<Meta>,
3292}
3293
3294#[cfg(feature = "unstable_llm_providers")]
3295impl ProviderInfo {
3296 #[must_use]
3297 pub fn new(
3298 id: impl Into<String>,
3299 supported: Vec<LlmProtocol>,
3300 required: bool,
3301 current: impl IntoOption<ProviderCurrentConfig>,
3302 ) -> Self {
3303 Self {
3304 id: id.into(),
3305 supported,
3306 required,
3307 current: current.into_option(),
3308 meta: None,
3309 }
3310 }
3311
3312 #[must_use]
3318 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3319 self.meta = meta.into_option();
3320 self
3321 }
3322}
3323
3324#[cfg(feature = "unstable_llm_providers")]
3330#[skip_serializing_none]
3331#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3332#[schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_LIST_METHOD_NAME))]
3333#[serde(rename_all = "camelCase")]
3334#[non_exhaustive]
3335pub struct ListProvidersRequest {
3336 #[serde(rename = "_meta")]
3342 pub meta: Option<Meta>,
3343}
3344
3345#[cfg(feature = "unstable_llm_providers")]
3346impl ListProvidersRequest {
3347 #[must_use]
3348 pub fn new() -> Self {
3349 Self::default()
3350 }
3351
3352 #[must_use]
3358 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3359 self.meta = meta.into_option();
3360 self
3361 }
3362}
3363
3364#[cfg(feature = "unstable_llm_providers")]
3370#[serde_as]
3371#[skip_serializing_none]
3372#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3373#[schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_LIST_METHOD_NAME))]
3374#[serde(rename_all = "camelCase")]
3375#[non_exhaustive]
3376pub struct ListProvidersResponse {
3377 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
3379 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
3380 pub providers: Vec<ProviderInfo>,
3381 #[serde(rename = "_meta")]
3387 pub meta: Option<Meta>,
3388}
3389
3390#[cfg(feature = "unstable_llm_providers")]
3391impl ListProvidersResponse {
3392 #[must_use]
3393 pub fn new(providers: Vec<ProviderInfo>) -> Self {
3394 Self {
3395 providers,
3396 meta: None,
3397 }
3398 }
3399
3400 #[must_use]
3406 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3407 self.meta = meta.into_option();
3408 self
3409 }
3410}
3411
3412#[cfg(feature = "unstable_llm_providers")]
3420#[skip_serializing_none]
3421#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3422#[schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_SET_METHOD_NAME))]
3423#[serde(rename_all = "camelCase")]
3424#[non_exhaustive]
3425pub struct SetProviderRequest {
3426 pub id: String,
3428 pub api_type: LlmProtocol,
3430 pub base_url: String,
3432 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
3435 pub headers: HashMap<String, String>,
3436 #[serde(rename = "_meta")]
3442 pub meta: Option<Meta>,
3443}
3444
3445#[cfg(feature = "unstable_llm_providers")]
3446impl SetProviderRequest {
3447 #[must_use]
3448 pub fn new(id: impl Into<String>, api_type: LlmProtocol, base_url: impl Into<String>) -> Self {
3449 Self {
3450 id: id.into(),
3451 api_type,
3452 base_url: base_url.into(),
3453 headers: HashMap::new(),
3454 meta: None,
3455 }
3456 }
3457
3458 #[must_use]
3461 pub fn headers(mut self, headers: HashMap<String, String>) -> Self {
3462 self.headers = headers;
3463 self
3464 }
3465
3466 #[must_use]
3472 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3473 self.meta = meta.into_option();
3474 self
3475 }
3476}
3477
3478#[cfg(feature = "unstable_llm_providers")]
3484#[skip_serializing_none]
3485#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3486#[schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_SET_METHOD_NAME))]
3487#[serde(rename_all = "camelCase")]
3488#[non_exhaustive]
3489pub struct SetProviderResponse {
3490 #[serde(rename = "_meta")]
3496 pub meta: Option<Meta>,
3497}
3498
3499#[cfg(feature = "unstable_llm_providers")]
3500impl SetProviderResponse {
3501 #[must_use]
3502 pub fn new() -> Self {
3503 Self::default()
3504 }
3505
3506 #[must_use]
3512 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3513 self.meta = meta.into_option();
3514 self
3515 }
3516}
3517
3518#[cfg(feature = "unstable_llm_providers")]
3524#[skip_serializing_none]
3525#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3526#[schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_DISABLE_METHOD_NAME))]
3527#[serde(rename_all = "camelCase")]
3528#[non_exhaustive]
3529pub struct DisableProviderRequest {
3530 pub id: String,
3532 #[serde(rename = "_meta")]
3538 pub meta: Option<Meta>,
3539}
3540
3541#[cfg(feature = "unstable_llm_providers")]
3542impl DisableProviderRequest {
3543 #[must_use]
3544 pub fn new(id: impl Into<String>) -> Self {
3545 Self {
3546 id: id.into(),
3547 meta: None,
3548 }
3549 }
3550
3551 #[must_use]
3557 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3558 self.meta = meta.into_option();
3559 self
3560 }
3561}
3562
3563#[cfg(feature = "unstable_llm_providers")]
3569#[skip_serializing_none]
3570#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3571#[schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_DISABLE_METHOD_NAME))]
3572#[serde(rename_all = "camelCase")]
3573#[non_exhaustive]
3574pub struct DisableProviderResponse {
3575 #[serde(rename = "_meta")]
3581 pub meta: Option<Meta>,
3582}
3583
3584#[cfg(feature = "unstable_llm_providers")]
3585impl DisableProviderResponse {
3586 #[must_use]
3587 pub fn new() -> Self {
3588 Self::default()
3589 }
3590
3591 #[must_use]
3597 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3598 self.meta = meta.into_option();
3599 self
3600 }
3601}
3602
3603#[serde_as]
3612#[skip_serializing_none]
3613#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3614#[serde(rename_all = "camelCase")]
3615#[non_exhaustive]
3616pub struct AgentCapabilities {
3617 #[serde(default)]
3619 pub load_session: bool,
3620 #[serde(default)]
3622 pub prompt_capabilities: PromptCapabilities,
3623 #[serde(default)]
3625 pub mcp_capabilities: McpCapabilities,
3626 #[serde(default)]
3627 pub session_capabilities: SessionCapabilities,
3628 #[serde(default)]
3630 pub auth: AgentAuthCapabilities,
3631 #[cfg(feature = "unstable_llm_providers")]
3639 #[serde_as(deserialize_as = "DefaultOnError")]
3640 #[schemars(extend("x-deserialize-default-on-error" = true))]
3641 #[serde(default)]
3642 pub providers: Option<ProvidersCapabilities>,
3643 #[cfg(feature = "unstable_nes")]
3649 #[serde_as(deserialize_as = "DefaultOnError")]
3650 #[schemars(extend("x-deserialize-default-on-error" = true))]
3651 #[serde(default)]
3652 pub nes: Option<NesCapabilities>,
3653 #[cfg(feature = "unstable_nes")]
3659 #[serde_as(deserialize_as = "DefaultOnError")]
3660 #[schemars(extend("x-deserialize-default-on-error" = true))]
3661 #[serde(default)]
3662 pub position_encoding: Option<PositionEncodingKind>,
3663 #[serde(rename = "_meta")]
3669 pub meta: Option<Meta>,
3670}
3671
3672impl AgentCapabilities {
3673 #[must_use]
3674 pub fn new() -> Self {
3675 Self::default()
3676 }
3677
3678 #[must_use]
3680 pub fn load_session(mut self, load_session: bool) -> Self {
3681 self.load_session = load_session;
3682 self
3683 }
3684
3685 #[must_use]
3687 pub fn prompt_capabilities(mut self, prompt_capabilities: PromptCapabilities) -> Self {
3688 self.prompt_capabilities = prompt_capabilities;
3689 self
3690 }
3691
3692 #[must_use]
3694 pub fn mcp_capabilities(mut self, mcp_capabilities: McpCapabilities) -> Self {
3695 self.mcp_capabilities = mcp_capabilities;
3696 self
3697 }
3698
3699 #[must_use]
3701 pub fn session_capabilities(mut self, session_capabilities: SessionCapabilities) -> Self {
3702 self.session_capabilities = session_capabilities;
3703 self
3704 }
3705
3706 #[must_use]
3708 pub fn auth(mut self, auth: AgentAuthCapabilities) -> Self {
3709 self.auth = auth;
3710 self
3711 }
3712
3713 #[cfg(feature = "unstable_llm_providers")]
3719 #[must_use]
3720 pub fn providers(mut self, providers: impl IntoOption<ProvidersCapabilities>) -> Self {
3721 self.providers = providers.into_option();
3722 self
3723 }
3724
3725 #[cfg(feature = "unstable_nes")]
3731 #[must_use]
3732 pub fn nes(mut self, nes: impl IntoOption<NesCapabilities>) -> Self {
3733 self.nes = nes.into_option();
3734 self
3735 }
3736
3737 #[cfg(feature = "unstable_nes")]
3741 #[must_use]
3742 pub fn position_encoding(
3743 mut self,
3744 position_encoding: impl IntoOption<PositionEncodingKind>,
3745 ) -> Self {
3746 self.position_encoding = position_encoding.into_option();
3747 self
3748 }
3749
3750 #[must_use]
3756 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3757 self.meta = meta.into_option();
3758 self
3759 }
3760}
3761
3762#[cfg(feature = "unstable_llm_providers")]
3770#[skip_serializing_none]
3771#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3772#[non_exhaustive]
3773pub struct ProvidersCapabilities {
3774 #[serde(rename = "_meta")]
3780 pub meta: Option<Meta>,
3781}
3782
3783#[cfg(feature = "unstable_llm_providers")]
3784impl ProvidersCapabilities {
3785 #[must_use]
3786 pub fn new() -> Self {
3787 Self::default()
3788 }
3789
3790 #[must_use]
3796 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3797 self.meta = meta.into_option();
3798 self
3799 }
3800}
3801
3802#[serde_as]
3812#[skip_serializing_none]
3813#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3814#[serde(rename_all = "camelCase")]
3815#[non_exhaustive]
3816pub struct SessionCapabilities {
3817 #[serde_as(deserialize_as = "DefaultOnError")]
3819 #[schemars(extend("x-deserialize-default-on-error" = true))]
3820 #[serde(default)]
3821 pub list: Option<SessionListCapabilities>,
3822 #[serde_as(deserialize_as = "DefaultOnError")]
3827 #[schemars(extend("x-deserialize-default-on-error" = true))]
3828 #[serde(default)]
3829 pub delete: Option<SessionDeleteCapabilities>,
3830 #[serde_as(deserialize_as = "DefaultOnError")]
3836 #[schemars(extend("x-deserialize-default-on-error" = true))]
3837 #[serde(default)]
3838 pub additional_directories: Option<SessionAdditionalDirectoriesCapabilities>,
3839 #[cfg(feature = "unstable_session_fork")]
3845 #[serde_as(deserialize_as = "DefaultOnError")]
3846 #[schemars(extend("x-deserialize-default-on-error" = true))]
3847 #[serde(default)]
3848 pub fork: Option<SessionForkCapabilities>,
3849 #[serde_as(deserialize_as = "DefaultOnError")]
3851 #[schemars(extend("x-deserialize-default-on-error" = true))]
3852 #[serde(default)]
3853 pub resume: Option<SessionResumeCapabilities>,
3854 #[serde_as(deserialize_as = "DefaultOnError")]
3856 #[schemars(extend("x-deserialize-default-on-error" = true))]
3857 #[serde(default)]
3858 pub close: Option<SessionCloseCapabilities>,
3859 #[serde(rename = "_meta")]
3865 pub meta: Option<Meta>,
3866}
3867
3868impl SessionCapabilities {
3869 #[must_use]
3870 pub fn new() -> Self {
3871 Self::default()
3872 }
3873
3874 #[must_use]
3876 pub fn list(mut self, list: impl IntoOption<SessionListCapabilities>) -> Self {
3877 self.list = list.into_option();
3878 self
3879 }
3880
3881 #[must_use]
3886 pub fn delete(mut self, delete: impl IntoOption<SessionDeleteCapabilities>) -> Self {
3887 self.delete = delete.into_option();
3888 self
3889 }
3890
3891 #[must_use]
3897 pub fn additional_directories(
3898 mut self,
3899 additional_directories: impl IntoOption<SessionAdditionalDirectoriesCapabilities>,
3900 ) -> Self {
3901 self.additional_directories = additional_directories.into_option();
3902 self
3903 }
3904
3905 #[cfg(feature = "unstable_session_fork")]
3906 #[must_use]
3908 pub fn fork(mut self, fork: impl IntoOption<SessionForkCapabilities>) -> Self {
3909 self.fork = fork.into_option();
3910 self
3911 }
3912
3913 #[must_use]
3915 pub fn resume(mut self, resume: impl IntoOption<SessionResumeCapabilities>) -> Self {
3916 self.resume = resume.into_option();
3917 self
3918 }
3919
3920 #[must_use]
3922 pub fn close(mut self, close: impl IntoOption<SessionCloseCapabilities>) -> Self {
3923 self.close = close.into_option();
3924 self
3925 }
3926
3927 #[must_use]
3933 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3934 self.meta = meta.into_option();
3935 self
3936 }
3937}
3938
3939#[skip_serializing_none]
3943#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3944#[non_exhaustive]
3945pub struct SessionListCapabilities {
3946 #[serde(rename = "_meta")]
3952 pub meta: Option<Meta>,
3953}
3954
3955impl SessionListCapabilities {
3956 #[must_use]
3957 pub fn new() -> Self {
3958 Self::default()
3959 }
3960
3961 #[must_use]
3967 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3968 self.meta = meta.into_option();
3969 self
3970 }
3971}
3972
3973#[skip_serializing_none]
3977#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3978#[non_exhaustive]
3979pub struct SessionDeleteCapabilities {
3980 #[serde(rename = "_meta")]
3986 pub meta: Option<Meta>,
3987}
3988
3989impl SessionDeleteCapabilities {
3990 #[must_use]
3991 pub fn new() -> Self {
3992 Self::default()
3993 }
3994
3995 #[must_use]
4001 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4002 self.meta = meta.into_option();
4003 self
4004 }
4005}
4006
4007#[skip_serializing_none]
4014#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
4015#[non_exhaustive]
4016pub struct SessionAdditionalDirectoriesCapabilities {
4017 #[serde(rename = "_meta")]
4023 pub meta: Option<Meta>,
4024}
4025
4026impl SessionAdditionalDirectoriesCapabilities {
4027 #[must_use]
4028 pub fn new() -> Self {
4029 Self::default()
4030 }
4031
4032 #[must_use]
4038 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4039 self.meta = meta.into_option();
4040 self
4041 }
4042}
4043
4044#[cfg(feature = "unstable_session_fork")]
4052#[skip_serializing_none]
4053#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
4054#[non_exhaustive]
4055pub struct SessionForkCapabilities {
4056 #[serde(rename = "_meta")]
4062 pub meta: Option<Meta>,
4063}
4064
4065#[cfg(feature = "unstable_session_fork")]
4066impl SessionForkCapabilities {
4067 #[must_use]
4068 pub fn new() -> Self {
4069 Self::default()
4070 }
4071
4072 #[must_use]
4078 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4079 self.meta = meta.into_option();
4080 self
4081 }
4082}
4083
4084#[skip_serializing_none]
4088#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
4089#[non_exhaustive]
4090pub struct SessionResumeCapabilities {
4091 #[serde(rename = "_meta")]
4097 pub meta: Option<Meta>,
4098}
4099
4100impl SessionResumeCapabilities {
4101 #[must_use]
4102 pub fn new() -> Self {
4103 Self::default()
4104 }
4105
4106 #[must_use]
4112 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4113 self.meta = meta.into_option();
4114 self
4115 }
4116}
4117
4118#[skip_serializing_none]
4122#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
4123#[non_exhaustive]
4124pub struct SessionCloseCapabilities {
4125 #[serde(rename = "_meta")]
4131 pub meta: Option<Meta>,
4132}
4133
4134impl SessionCloseCapabilities {
4135 #[must_use]
4136 pub fn new() -> Self {
4137 Self::default()
4138 }
4139
4140 #[must_use]
4146 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4147 self.meta = meta.into_option();
4148 self
4149 }
4150}
4151
4152#[skip_serializing_none]
4165#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
4166#[serde(rename_all = "camelCase")]
4167#[non_exhaustive]
4168pub struct PromptCapabilities {
4169 #[serde(default)]
4171 pub image: bool,
4172 #[serde(default)]
4174 pub audio: bool,
4175 #[serde(default)]
4180 pub embedded_context: bool,
4181 #[serde(rename = "_meta")]
4187 pub meta: Option<Meta>,
4188}
4189
4190impl PromptCapabilities {
4191 #[must_use]
4192 pub fn new() -> Self {
4193 Self::default()
4194 }
4195
4196 #[must_use]
4198 pub fn image(mut self, image: bool) -> Self {
4199 self.image = image;
4200 self
4201 }
4202
4203 #[must_use]
4205 pub fn audio(mut self, audio: bool) -> Self {
4206 self.audio = audio;
4207 self
4208 }
4209
4210 #[must_use]
4215 pub fn embedded_context(mut self, embedded_context: bool) -> Self {
4216 self.embedded_context = embedded_context;
4217 self
4218 }
4219
4220 #[must_use]
4226 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4227 self.meta = meta.into_option();
4228 self
4229 }
4230}
4231
4232#[skip_serializing_none]
4234#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
4235#[serde(rename_all = "camelCase")]
4236#[non_exhaustive]
4237pub struct McpCapabilities {
4238 #[serde(default)]
4240 pub http: bool,
4241 #[serde(default)]
4243 pub sse: bool,
4244 #[cfg(feature = "unstable_mcp_over_acp")]
4250 #[serde(default)]
4251 pub acp: bool,
4252 #[serde(rename = "_meta")]
4258 pub meta: Option<Meta>,
4259}
4260
4261impl McpCapabilities {
4262 #[must_use]
4263 pub fn new() -> Self {
4264 Self::default()
4265 }
4266
4267 #[must_use]
4269 pub fn http(mut self, http: bool) -> Self {
4270 self.http = http;
4271 self
4272 }
4273
4274 #[must_use]
4276 pub fn sse(mut self, sse: bool) -> Self {
4277 self.sse = sse;
4278 self
4279 }
4280
4281 #[cfg(feature = "unstable_mcp_over_acp")]
4287 #[must_use]
4288 pub fn acp(mut self, acp: bool) -> Self {
4289 self.acp = acp;
4290 self
4291 }
4292
4293 #[must_use]
4299 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4300 self.meta = meta.into_option();
4301 self
4302 }
4303}
4304
4305#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4311#[non_exhaustive]
4312pub struct AgentMethodNames {
4313 pub initialize: &'static str,
4315 pub authenticate: &'static str,
4317 #[cfg(feature = "unstable_llm_providers")]
4319 pub providers_list: &'static str,
4320 #[cfg(feature = "unstable_llm_providers")]
4322 pub providers_set: &'static str,
4323 #[cfg(feature = "unstable_llm_providers")]
4325 pub providers_disable: &'static str,
4326 pub session_new: &'static str,
4328 pub session_load: &'static str,
4330 pub session_set_mode: &'static str,
4332 pub session_set_config_option: &'static str,
4334 pub session_prompt: &'static str,
4336 pub session_cancel: &'static str,
4338 #[cfg(feature = "unstable_mcp_over_acp")]
4340 pub mcp_message: &'static str,
4341 pub session_list: &'static str,
4343 pub session_delete: &'static str,
4345 #[cfg(feature = "unstable_session_fork")]
4347 pub session_fork: &'static str,
4348 pub session_resume: &'static str,
4350 pub session_close: &'static str,
4352 pub logout: &'static str,
4354 #[cfg(feature = "unstable_nes")]
4356 pub nes_start: &'static str,
4357 #[cfg(feature = "unstable_nes")]
4359 pub nes_suggest: &'static str,
4360 #[cfg(feature = "unstable_nes")]
4362 pub nes_accept: &'static str,
4363 #[cfg(feature = "unstable_nes")]
4365 pub nes_reject: &'static str,
4366 #[cfg(feature = "unstable_nes")]
4368 pub nes_close: &'static str,
4369 #[cfg(feature = "unstable_nes")]
4371 pub document_did_open: &'static str,
4372 #[cfg(feature = "unstable_nes")]
4374 pub document_did_change: &'static str,
4375 #[cfg(feature = "unstable_nes")]
4377 pub document_did_close: &'static str,
4378 #[cfg(feature = "unstable_nes")]
4380 pub document_did_save: &'static str,
4381 #[cfg(feature = "unstable_nes")]
4383 pub document_did_focus: &'static str,
4384}
4385
4386pub const AGENT_METHOD_NAMES: AgentMethodNames = AgentMethodNames {
4388 initialize: INITIALIZE_METHOD_NAME,
4389 authenticate: AUTHENTICATE_METHOD_NAME,
4390 #[cfg(feature = "unstable_llm_providers")]
4391 providers_list: PROVIDERS_LIST_METHOD_NAME,
4392 #[cfg(feature = "unstable_llm_providers")]
4393 providers_set: PROVIDERS_SET_METHOD_NAME,
4394 #[cfg(feature = "unstable_llm_providers")]
4395 providers_disable: PROVIDERS_DISABLE_METHOD_NAME,
4396 session_new: SESSION_NEW_METHOD_NAME,
4397 session_load: SESSION_LOAD_METHOD_NAME,
4398 session_set_mode: SESSION_SET_MODE_METHOD_NAME,
4399 session_set_config_option: SESSION_SET_CONFIG_OPTION_METHOD_NAME,
4400 session_prompt: SESSION_PROMPT_METHOD_NAME,
4401 session_cancel: SESSION_CANCEL_METHOD_NAME,
4402 #[cfg(feature = "unstable_mcp_over_acp")]
4403 mcp_message: MCP_MESSAGE_METHOD_NAME,
4404 session_list: SESSION_LIST_METHOD_NAME,
4405 session_delete: SESSION_DELETE_METHOD_NAME,
4406 #[cfg(feature = "unstable_session_fork")]
4407 session_fork: SESSION_FORK_METHOD_NAME,
4408 session_resume: SESSION_RESUME_METHOD_NAME,
4409 session_close: SESSION_CLOSE_METHOD_NAME,
4410 logout: LOGOUT_METHOD_NAME,
4411 #[cfg(feature = "unstable_nes")]
4412 nes_start: NES_START_METHOD_NAME,
4413 #[cfg(feature = "unstable_nes")]
4414 nes_suggest: NES_SUGGEST_METHOD_NAME,
4415 #[cfg(feature = "unstable_nes")]
4416 nes_accept: NES_ACCEPT_METHOD_NAME,
4417 #[cfg(feature = "unstable_nes")]
4418 nes_reject: NES_REJECT_METHOD_NAME,
4419 #[cfg(feature = "unstable_nes")]
4420 nes_close: NES_CLOSE_METHOD_NAME,
4421 #[cfg(feature = "unstable_nes")]
4422 document_did_open: DOCUMENT_DID_OPEN_METHOD_NAME,
4423 #[cfg(feature = "unstable_nes")]
4424 document_did_change: DOCUMENT_DID_CHANGE_METHOD_NAME,
4425 #[cfg(feature = "unstable_nes")]
4426 document_did_close: DOCUMENT_DID_CLOSE_METHOD_NAME,
4427 #[cfg(feature = "unstable_nes")]
4428 document_did_save: DOCUMENT_DID_SAVE_METHOD_NAME,
4429 #[cfg(feature = "unstable_nes")]
4430 document_did_focus: DOCUMENT_DID_FOCUS_METHOD_NAME,
4431};
4432
4433pub(crate) const INITIALIZE_METHOD_NAME: &str = "initialize";
4435pub(crate) const AUTHENTICATE_METHOD_NAME: &str = "authenticate";
4437#[cfg(feature = "unstable_llm_providers")]
4439pub(crate) const PROVIDERS_LIST_METHOD_NAME: &str = "providers/list";
4440#[cfg(feature = "unstable_llm_providers")]
4442pub(crate) const PROVIDERS_SET_METHOD_NAME: &str = "providers/set";
4443#[cfg(feature = "unstable_llm_providers")]
4445pub(crate) const PROVIDERS_DISABLE_METHOD_NAME: &str = "providers/disable";
4446pub(crate) const SESSION_NEW_METHOD_NAME: &str = "session/new";
4448pub(crate) const SESSION_LOAD_METHOD_NAME: &str = "session/load";
4450pub(crate) const SESSION_SET_MODE_METHOD_NAME: &str = "session/set_mode";
4452pub(crate) const SESSION_SET_CONFIG_OPTION_METHOD_NAME: &str = "session/set_config_option";
4454pub(crate) const SESSION_PROMPT_METHOD_NAME: &str = "session/prompt";
4456pub(crate) const SESSION_CANCEL_METHOD_NAME: &str = "session/cancel";
4458pub(crate) const SESSION_LIST_METHOD_NAME: &str = "session/list";
4460pub(crate) const SESSION_DELETE_METHOD_NAME: &str = "session/delete";
4462#[cfg(feature = "unstable_session_fork")]
4464pub(crate) const SESSION_FORK_METHOD_NAME: &str = "session/fork";
4465pub(crate) const SESSION_RESUME_METHOD_NAME: &str = "session/resume";
4467pub(crate) const SESSION_CLOSE_METHOD_NAME: &str = "session/close";
4469pub(crate) const LOGOUT_METHOD_NAME: &str = "logout";
4471
4472#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
4479#[serde(untagged)]
4480#[schemars(inline)]
4481#[non_exhaustive]
4482#[allow(clippy::large_enum_variant)]
4483pub enum ClientRequest {
4484 InitializeRequest(InitializeRequest),
4495 AuthenticateRequest(AuthenticateRequest),
4505 #[cfg(feature = "unstable_llm_providers")]
4511 ListProvidersRequest(ListProvidersRequest),
4512 #[cfg(feature = "unstable_llm_providers")]
4518 SetProviderRequest(SetProviderRequest),
4519 #[cfg(feature = "unstable_llm_providers")]
4525 DisableProviderRequest(DisableProviderRequest),
4526 LogoutRequest(LogoutRequest),
4531 NewSessionRequest(NewSessionRequest),
4544 LoadSessionRequest(LoadSessionRequest),
4555 ListSessionsRequest(ListSessionsRequest),
4561 DeleteSessionRequest(DeleteSessionRequest),
4565 #[cfg(feature = "unstable_session_fork")]
4566 ForkSessionRequest(ForkSessionRequest),
4578 ResumeSessionRequest(ResumeSessionRequest),
4585 CloseSessionRequest(CloseSessionRequest),
4592 SetSessionModeRequest(SetSessionModeRequest),
4606 SetSessionConfigOptionRequest(SetSessionConfigOptionRequest),
4608 PromptRequest(PromptRequest),
4620 #[cfg(feature = "unstable_nes")]
4621 StartNesRequest(StartNesRequest),
4627 #[cfg(feature = "unstable_nes")]
4628 SuggestNesRequest(SuggestNesRequest),
4634 #[cfg(feature = "unstable_nes")]
4635 CloseNesRequest(CloseNesRequest),
4644 #[cfg(feature = "unstable_mcp_over_acp")]
4650 MessageMcpRequest(MessageMcpRequest),
4651 ExtMethodRequest(ExtRequest),
4658}
4659
4660impl ClientRequest {
4661 #[must_use]
4663 pub fn method(&self) -> &str {
4664 match self {
4665 Self::InitializeRequest(_) => AGENT_METHOD_NAMES.initialize,
4666 Self::AuthenticateRequest(_) => AGENT_METHOD_NAMES.authenticate,
4667 #[cfg(feature = "unstable_llm_providers")]
4668 Self::ListProvidersRequest(_) => AGENT_METHOD_NAMES.providers_list,
4669 #[cfg(feature = "unstable_llm_providers")]
4670 Self::SetProviderRequest(_) => AGENT_METHOD_NAMES.providers_set,
4671 #[cfg(feature = "unstable_llm_providers")]
4672 Self::DisableProviderRequest(_) => AGENT_METHOD_NAMES.providers_disable,
4673 Self::LogoutRequest(_) => AGENT_METHOD_NAMES.logout,
4674 Self::NewSessionRequest(_) => AGENT_METHOD_NAMES.session_new,
4675 Self::LoadSessionRequest(_) => AGENT_METHOD_NAMES.session_load,
4676 Self::ListSessionsRequest(_) => AGENT_METHOD_NAMES.session_list,
4677 Self::DeleteSessionRequest(_) => AGENT_METHOD_NAMES.session_delete,
4678 #[cfg(feature = "unstable_session_fork")]
4679 Self::ForkSessionRequest(_) => AGENT_METHOD_NAMES.session_fork,
4680 Self::ResumeSessionRequest(_) => AGENT_METHOD_NAMES.session_resume,
4681 Self::CloseSessionRequest(_) => AGENT_METHOD_NAMES.session_close,
4682 Self::SetSessionModeRequest(_) => AGENT_METHOD_NAMES.session_set_mode,
4683 Self::SetSessionConfigOptionRequest(_) => AGENT_METHOD_NAMES.session_set_config_option,
4684 Self::PromptRequest(_) => AGENT_METHOD_NAMES.session_prompt,
4685 #[cfg(feature = "unstable_nes")]
4686 Self::StartNesRequest(_) => AGENT_METHOD_NAMES.nes_start,
4687 #[cfg(feature = "unstable_nes")]
4688 Self::SuggestNesRequest(_) => AGENT_METHOD_NAMES.nes_suggest,
4689 #[cfg(feature = "unstable_nes")]
4690 Self::CloseNesRequest(_) => AGENT_METHOD_NAMES.nes_close,
4691 #[cfg(feature = "unstable_mcp_over_acp")]
4692 Self::MessageMcpRequest(_) => AGENT_METHOD_NAMES.mcp_message,
4693 Self::ExtMethodRequest(ext_request) => &ext_request.method,
4694 }
4695 }
4696}
4697
4698#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
4705#[serde(untagged)]
4706#[schemars(inline)]
4707#[non_exhaustive]
4708#[allow(clippy::large_enum_variant)]
4709pub enum AgentResponse {
4710 InitializeResponse(InitializeResponse),
4711 AuthenticateResponse(#[serde(default)] AuthenticateResponse),
4712 #[cfg(feature = "unstable_llm_providers")]
4713 ListProvidersResponse(ListProvidersResponse),
4714 #[cfg(feature = "unstable_llm_providers")]
4715 SetProviderResponse(#[serde(default)] SetProviderResponse),
4716 #[cfg(feature = "unstable_llm_providers")]
4717 DisableProviderResponse(#[serde(default)] DisableProviderResponse),
4718 LogoutResponse(#[serde(default)] LogoutResponse),
4719 NewSessionResponse(NewSessionResponse),
4720 LoadSessionResponse(#[serde(default)] LoadSessionResponse),
4721 ListSessionsResponse(ListSessionsResponse),
4722 DeleteSessionResponse(#[serde(default)] DeleteSessionResponse),
4723 #[cfg(feature = "unstable_session_fork")]
4724 ForkSessionResponse(ForkSessionResponse),
4725 ResumeSessionResponse(#[serde(default)] ResumeSessionResponse),
4726 CloseSessionResponse(#[serde(default)] CloseSessionResponse),
4727 SetSessionModeResponse(#[serde(default)] SetSessionModeResponse),
4728 SetSessionConfigOptionResponse(SetSessionConfigOptionResponse),
4729 PromptResponse(PromptResponse),
4730 #[cfg(feature = "unstable_nes")]
4731 StartNesResponse(StartNesResponse),
4732 #[cfg(feature = "unstable_nes")]
4733 SuggestNesResponse(SuggestNesResponse),
4734 #[cfg(feature = "unstable_nes")]
4735 CloseNesResponse(#[serde(default)] CloseNesResponse),
4736 ExtMethodResponse(ExtResponse),
4737 #[cfg(feature = "unstable_mcp_over_acp")]
4738 MessageMcpResponse(MessageMcpResponse),
4739}
4740
4741#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
4748#[serde(untagged)]
4749#[schemars(inline)]
4750#[non_exhaustive]
4751#[allow(clippy::large_enum_variant)]
4752pub enum ClientNotification {
4753 CancelNotification(CancelNotification),
4765 #[cfg(feature = "unstable_nes")]
4766 DidOpenDocumentNotification(DidOpenDocumentNotification),
4770 #[cfg(feature = "unstable_nes")]
4771 DidChangeDocumentNotification(DidChangeDocumentNotification),
4775 #[cfg(feature = "unstable_nes")]
4776 DidCloseDocumentNotification(DidCloseDocumentNotification),
4780 #[cfg(feature = "unstable_nes")]
4781 DidSaveDocumentNotification(DidSaveDocumentNotification),
4785 #[cfg(feature = "unstable_nes")]
4786 DidFocusDocumentNotification(DidFocusDocumentNotification),
4790 #[cfg(feature = "unstable_nes")]
4791 AcceptNesNotification(AcceptNesNotification),
4795 #[cfg(feature = "unstable_nes")]
4796 RejectNesNotification(RejectNesNotification),
4800 #[cfg(feature = "unstable_mcp_over_acp")]
4806 MessageMcpNotification(MessageMcpNotification),
4807 ExtNotification(ExtNotification),
4814}
4815
4816impl ClientNotification {
4817 #[must_use]
4819 pub fn method(&self) -> &str {
4820 match self {
4821 Self::CancelNotification(_) => AGENT_METHOD_NAMES.session_cancel,
4822 #[cfg(feature = "unstable_nes")]
4823 Self::DidOpenDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_open,
4824 #[cfg(feature = "unstable_nes")]
4825 Self::DidChangeDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_change,
4826 #[cfg(feature = "unstable_nes")]
4827 Self::DidCloseDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_close,
4828 #[cfg(feature = "unstable_nes")]
4829 Self::DidSaveDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_save,
4830 #[cfg(feature = "unstable_nes")]
4831 Self::DidFocusDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_focus,
4832 #[cfg(feature = "unstable_nes")]
4833 Self::AcceptNesNotification(_) => AGENT_METHOD_NAMES.nes_accept,
4834 #[cfg(feature = "unstable_nes")]
4835 Self::RejectNesNotification(_) => AGENT_METHOD_NAMES.nes_reject,
4836 #[cfg(feature = "unstable_mcp_over_acp")]
4837 Self::MessageMcpNotification(_) => AGENT_METHOD_NAMES.mcp_message,
4838 Self::ExtNotification(ext_notification) => &ext_notification.method,
4839 }
4840 }
4841}
4842
4843#[skip_serializing_none]
4847#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
4848#[schemars(extend("x-side" = "agent", "x-method" = SESSION_CANCEL_METHOD_NAME))]
4849#[serde(rename_all = "camelCase")]
4850#[non_exhaustive]
4851pub struct CancelNotification {
4852 pub session_id: SessionId,
4854 #[serde(rename = "_meta")]
4860 pub meta: Option<Meta>,
4861}
4862
4863impl CancelNotification {
4864 #[must_use]
4865 pub fn new(session_id: impl Into<SessionId>) -> Self {
4866 Self {
4867 session_id: session_id.into(),
4868 meta: None,
4869 }
4870 }
4871
4872 #[must_use]
4878 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4879 self.meta = meta.into_option();
4880 self
4881 }
4882}
4883
4884#[cfg(test)]
4885mod test_serialization {
4886 use super::*;
4887 use serde_json::json;
4888
4889 fn test_meta() -> Meta {
4890 json!({ "source": "test" }).as_object().unwrap().clone()
4891 }
4892
4893 fn serialized_meta_key_count(value: &impl serde::Serialize) -> usize {
4894 serde_json::to_string(value)
4895 .unwrap()
4896 .matches("\"_meta\"")
4897 .count()
4898 }
4899
4900 #[test]
4901 fn test_mcp_server_stdio_serialization() {
4902 let server = McpServer::Stdio(
4903 McpServerStdio::new("test-server", "/usr/bin/server")
4904 .args(vec!["--port".to_string(), "3000".to_string()])
4905 .env(vec![EnvVariable::new("API_KEY", "secret123")]),
4906 );
4907
4908 let json = serde_json::to_value(&server).unwrap();
4909 assert_eq!(
4910 json,
4911 json!({
4912 "name": "test-server",
4913 "command": "/usr/bin/server",
4914 "args": ["--port", "3000"],
4915 "env": [
4916 {
4917 "name": "API_KEY",
4918 "value": "secret123"
4919 }
4920 ]
4921 })
4922 );
4923
4924 let deserialized: McpServer = serde_json::from_value(json).unwrap();
4925 match deserialized {
4926 McpServer::Stdio(McpServerStdio {
4927 name,
4928 command,
4929 args,
4930 env,
4931 meta: _,
4932 }) => {
4933 assert_eq!(name, "test-server");
4934 assert_eq!(command, PathBuf::from("/usr/bin/server"));
4935 assert_eq!(args, vec!["--port", "3000"]);
4936 assert_eq!(env.len(), 1);
4937 assert_eq!(env[0].name, "API_KEY");
4938 assert_eq!(env[0].value, "secret123");
4939 }
4940 _ => panic!("Expected Stdio variant"),
4941 }
4942 }
4943
4944 #[test]
4945 fn test_mcp_server_http_serialization() {
4946 let server = McpServer::Http(
4947 McpServerHttp::new("http-server", "https://api.example.com").headers(vec![
4948 HttpHeader::new("Authorization", "Bearer token123"),
4949 HttpHeader::new("Content-Type", "application/json"),
4950 ]),
4951 );
4952
4953 let json = serde_json::to_value(&server).unwrap();
4954 assert_eq!(
4955 json,
4956 json!({
4957 "type": "http",
4958 "name": "http-server",
4959 "url": "https://api.example.com",
4960 "headers": [
4961 {
4962 "name": "Authorization",
4963 "value": "Bearer token123"
4964 },
4965 {
4966 "name": "Content-Type",
4967 "value": "application/json"
4968 }
4969 ]
4970 })
4971 );
4972
4973 let deserialized: McpServer = serde_json::from_value(json).unwrap();
4974 match deserialized {
4975 McpServer::Http(McpServerHttp {
4976 name,
4977 url,
4978 headers,
4979 meta: _,
4980 }) => {
4981 assert_eq!(name, "http-server");
4982 assert_eq!(url, "https://api.example.com");
4983 assert_eq!(headers.len(), 2);
4984 assert_eq!(headers[0].name, "Authorization");
4985 assert_eq!(headers[0].value, "Bearer token123");
4986 assert_eq!(headers[1].name, "Content-Type");
4987 assert_eq!(headers[1].value, "application/json");
4988 }
4989 _ => panic!("Expected Http variant"),
4990 }
4991 }
4992
4993 #[cfg(feature = "unstable_mcp_over_acp")]
4994 #[test]
4995 fn test_mcp_server_acp_serialization() {
4996 let server = McpServer::Acp(McpServerAcp::new("project-tools", "project-tools-id"));
4997
4998 let json = serde_json::to_value(&server).unwrap();
4999 assert_eq!(
5000 json,
5001 json!({
5002 "type": "acp",
5003 "name": "project-tools",
5004 "id": "project-tools-id"
5005 })
5006 );
5007
5008 let deserialized: McpServer = serde_json::from_value(json).unwrap();
5009 match deserialized {
5010 McpServer::Acp(McpServerAcp { name, id, meta: _ }) => {
5011 assert_eq!(name, "project-tools");
5012 assert_eq!(id, McpServerAcpId::new("project-tools-id"));
5013 }
5014 _ => panic!("Expected Acp variant"),
5015 }
5016 }
5017
5018 #[cfg(feature = "unstable_mcp_over_acp")]
5019 #[test]
5020 fn test_client_mcp_message_method_names() {
5021 assert_eq!(AGENT_METHOD_NAMES.mcp_message, "mcp/message");
5022
5023 assert_eq!(
5024 ClientRequest::MessageMcpRequest(MessageMcpRequest::new("conn-1", "tools/list"))
5025 .method(),
5026 "mcp/message"
5027 );
5028 assert_eq!(
5029 ClientNotification::MessageMcpNotification(MessageMcpNotification::new(
5030 "conn-1",
5031 "notifications/progress"
5032 ))
5033 .method(),
5034 "mcp/message"
5035 );
5036 }
5037
5038 #[cfg(feature = "unstable_mcp_over_acp")]
5039 #[test]
5040 fn test_mcp_server_acp_schema() {
5041 let mcp_server_schema = serde_json::to_value(schemars::schema_for!(McpServer)).unwrap();
5042 assert!(json_contains_entry(
5043 &mcp_server_schema,
5044 "const",
5045 &json!("acp")
5046 ));
5047 assert!(json_contains_entry(
5048 &mcp_server_schema,
5049 "$ref",
5050 &json!("#/$defs/McpServerAcp")
5051 ));
5052
5053 let capabilities_schema =
5054 serde_json::to_value(schemars::schema_for!(McpCapabilities)).unwrap();
5055 assert!(json_contains_key(&capabilities_schema, "acp"));
5056 }
5057
5058 #[cfg(feature = "unstable_mcp_over_acp")]
5059 fn json_contains_entry(
5060 value: &serde_json::Value,
5061 key: &str,
5062 expected: &serde_json::Value,
5063 ) -> bool {
5064 match value {
5065 serde_json::Value::Object(map) => {
5066 map.get(key) == Some(expected)
5067 || map
5068 .values()
5069 .any(|value| json_contains_entry(value, key, expected))
5070 }
5071 serde_json::Value::Array(values) => values
5072 .iter()
5073 .any(|value| json_contains_entry(value, key, expected)),
5074 _ => false,
5075 }
5076 }
5077
5078 #[cfg(feature = "unstable_mcp_over_acp")]
5079 fn json_contains_key(value: &serde_json::Value, key: &str) -> bool {
5080 match value {
5081 serde_json::Value::Object(map) => {
5082 map.contains_key(key) || map.values().any(|value| json_contains_key(value, key))
5083 }
5084 serde_json::Value::Array(values) => {
5085 values.iter().any(|value| json_contains_key(value, key))
5086 }
5087 _ => false,
5088 }
5089 }
5090
5091 #[test]
5092 fn test_mcp_server_sse_serialization() {
5093 let server = McpServer::Sse(
5094 McpServerSse::new("sse-server", "https://sse.example.com/events")
5095 .headers(vec![HttpHeader::new("X-API-Key", "apikey456")]),
5096 );
5097
5098 let json = serde_json::to_value(&server).unwrap();
5099 assert_eq!(
5100 json,
5101 json!({
5102 "type": "sse",
5103 "name": "sse-server",
5104 "url": "https://sse.example.com/events",
5105 "headers": [
5106 {
5107 "name": "X-API-Key",
5108 "value": "apikey456"
5109 }
5110 ]
5111 })
5112 );
5113
5114 let deserialized: McpServer = serde_json::from_value(json).unwrap();
5115 match deserialized {
5116 McpServer::Sse(McpServerSse {
5117 name,
5118 url,
5119 headers,
5120 meta: _,
5121 }) => {
5122 assert_eq!(name, "sse-server");
5123 assert_eq!(url, "https://sse.example.com/events");
5124 assert_eq!(headers.len(), 1);
5125 assert_eq!(headers[0].name, "X-API-Key");
5126 assert_eq!(headers[0].value, "apikey456");
5127 }
5128 _ => panic!("Expected Sse variant"),
5129 }
5130 }
5131
5132 #[test]
5133 fn test_session_config_option_category_known_variants() {
5134 assert_eq!(
5136 serde_json::to_value(&SessionConfigOptionCategory::Mode).unwrap(),
5137 json!("mode")
5138 );
5139 assert_eq!(
5140 serde_json::to_value(&SessionConfigOptionCategory::Model).unwrap(),
5141 json!("model")
5142 );
5143 assert_eq!(
5144 serde_json::to_value(&SessionConfigOptionCategory::ThoughtLevel).unwrap(),
5145 json!("thought_level")
5146 );
5147
5148 assert_eq!(
5150 serde_json::from_str::<SessionConfigOptionCategory>("\"mode\"").unwrap(),
5151 SessionConfigOptionCategory::Mode
5152 );
5153 assert_eq!(
5154 serde_json::from_str::<SessionConfigOptionCategory>("\"model\"").unwrap(),
5155 SessionConfigOptionCategory::Model
5156 );
5157 assert_eq!(
5158 serde_json::from_str::<SessionConfigOptionCategory>("\"thought_level\"").unwrap(),
5159 SessionConfigOptionCategory::ThoughtLevel
5160 );
5161 }
5162
5163 #[test]
5164 fn test_session_config_option_category_unknown_variants() {
5165 let unknown: SessionConfigOptionCategory =
5167 serde_json::from_str("\"some_future_category\"").unwrap();
5168 assert_eq!(
5169 unknown,
5170 SessionConfigOptionCategory::Other("some_future_category".to_string())
5171 );
5172
5173 let json = serde_json::to_value(&unknown).unwrap();
5175 assert_eq!(json, json!("some_future_category"));
5176 }
5177
5178 #[test]
5179 fn test_session_config_option_category_custom_categories() {
5180 let custom: SessionConfigOptionCategory =
5182 serde_json::from_str("\"_my_custom_category\"").unwrap();
5183 assert_eq!(
5184 custom,
5185 SessionConfigOptionCategory::Other("_my_custom_category".to_string())
5186 );
5187
5188 let json = serde_json::to_value(&custom).unwrap();
5190 assert_eq!(json, json!("_my_custom_category"));
5191
5192 let deserialized: SessionConfigOptionCategory = serde_json::from_value(json).unwrap();
5194 assert_eq!(
5195 deserialized,
5196 SessionConfigOptionCategory::Other("_my_custom_category".to_string()),
5197 );
5198 }
5199
5200 #[test]
5201 fn test_auth_method_agent_serialization() {
5202 let method = AuthMethod::Agent(AuthMethodAgent::new("default-auth", "Default Auth"));
5203
5204 let json = serde_json::to_value(&method).unwrap();
5205 assert_eq!(
5206 json,
5207 json!({
5208 "id": "default-auth",
5209 "name": "Default Auth"
5210 })
5211 );
5212 assert!(!json.as_object().unwrap().contains_key("description"));
5214 assert!(!json.as_object().unwrap().contains_key("type"));
5216
5217 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
5218 match deserialized {
5219 AuthMethod::Agent(AuthMethodAgent { id, name, .. }) => {
5220 assert_eq!(id.0.as_ref(), "default-auth");
5221 assert_eq!(name, "Default Auth");
5222 }
5223 #[cfg(feature = "unstable_auth_methods")]
5224 _ => panic!("Expected Agent variant"),
5225 }
5226 }
5227
5228 #[test]
5229 fn test_auth_method_explicit_agent_deserialization() {
5230 let json = json!({
5232 "id": "agent-auth",
5233 "name": "Agent Auth",
5234 "type": "agent"
5235 });
5236
5237 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
5238 assert!(matches!(deserialized, AuthMethod::Agent(_)));
5239 }
5240
5241 #[test]
5242 fn test_session_delete_serialization() {
5243 assert_eq!(AGENT_METHOD_NAMES.session_delete, "session/delete");
5244 assert_eq!(
5245 ClientRequest::DeleteSessionRequest(DeleteSessionRequest::new("sess_abc123")).method(),
5246 "session/delete"
5247 );
5248 assert_eq!(
5249 serde_json::to_value(DeleteSessionRequest::new("sess_abc123")).unwrap(),
5250 json!({
5251 "sessionId": "sess_abc123"
5252 })
5253 );
5254 assert_eq!(
5255 serde_json::to_value(DeleteSessionResponse::new()).unwrap(),
5256 json!({})
5257 );
5258 assert_eq!(
5259 serde_json::to_value(
5260 SessionCapabilities::new().delete(SessionDeleteCapabilities::new())
5261 )
5262 .unwrap(),
5263 json!({
5264 "delete": {}
5265 })
5266 );
5267 }
5268 #[test]
5269 fn test_session_additional_directories_serialization() {
5270 assert_eq!(
5271 serde_json::to_value(NewSessionRequest::new("/home/user/project")).unwrap(),
5272 json!({
5273 "cwd": "/home/user/project",
5274 "mcpServers": []
5275 })
5276 );
5277 assert_eq!(
5278 serde_json::to_value(
5279 NewSessionRequest::new("/home/user/project").additional_directories(vec![
5280 PathBuf::from("/home/user/shared-lib"),
5281 PathBuf::from("/home/user/product-docs"),
5282 ])
5283 )
5284 .unwrap(),
5285 json!({
5286 "cwd": "/home/user/project",
5287 "additionalDirectories": [
5288 "/home/user/shared-lib",
5289 "/home/user/product-docs"
5290 ],
5291 "mcpServers": []
5292 })
5293 );
5294 assert_eq!(
5295 serde_json::to_value(SessionInfo::new("sess_abc123", "/home/user/project")).unwrap(),
5296 json!({
5297 "sessionId": "sess_abc123",
5298 "cwd": "/home/user/project"
5299 })
5300 );
5301 assert_eq!(
5302 serde_json::to_value(
5303 SessionInfo::new("sess_abc123", "/home/user/project").additional_directories(vec![
5304 PathBuf::from("/home/user/shared-lib"),
5305 PathBuf::from("/home/user/product-docs"),
5306 ])
5307 )
5308 .unwrap(),
5309 json!({
5310 "sessionId": "sess_abc123",
5311 "cwd": "/home/user/project",
5312 "additionalDirectories": [
5313 "/home/user/shared-lib",
5314 "/home/user/product-docs"
5315 ]
5316 })
5317 );
5318 assert_eq!(
5319 serde_json::from_value::<SessionInfo>(json!({
5320 "sessionId": "sess_abc123",
5321 "cwd": "/home/user/project"
5322 }))
5323 .unwrap()
5324 .additional_directories,
5325 Vec::<PathBuf>::new()
5326 );
5327 }
5328 #[test]
5329 fn test_session_additional_directories_capabilities_serialization() {
5330 assert_eq!(
5331 serde_json::to_value(
5332 SessionCapabilities::new()
5333 .additional_directories(SessionAdditionalDirectoriesCapabilities::new())
5334 )
5335 .unwrap(),
5336 json!({
5337 "additionalDirectories": {}
5338 })
5339 );
5340 }
5341
5342 #[cfg(feature = "unstable_auth_methods")]
5343 #[test]
5344 fn test_auth_method_env_var_serialization() {
5345 let method = AuthMethod::EnvVar(AuthMethodEnvVar::new(
5346 "api-key",
5347 "API Key",
5348 vec![AuthEnvVar::new("API_KEY")],
5349 ));
5350
5351 let json = serde_json::to_value(&method).unwrap();
5352 assert_eq!(
5353 json,
5354 json!({
5355 "id": "api-key",
5356 "name": "API Key",
5357 "type": "env_var",
5358 "vars": [{"name": "API_KEY"}]
5359 })
5360 );
5361 assert!(!json["vars"][0].as_object().unwrap().contains_key("secret"));
5363 assert!(
5364 !json["vars"][0]
5365 .as_object()
5366 .unwrap()
5367 .contains_key("optional")
5368 );
5369
5370 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
5371 match deserialized {
5372 AuthMethod::EnvVar(AuthMethodEnvVar {
5373 id,
5374 name: method_name,
5375 vars,
5376 link,
5377 ..
5378 }) => {
5379 assert_eq!(id.0.as_ref(), "api-key");
5380 assert_eq!(method_name, "API Key");
5381 assert_eq!(vars.len(), 1);
5382 assert_eq!(vars[0].name, "API_KEY");
5383 assert!(vars[0].secret);
5384 assert!(!vars[0].optional);
5385 assert!(link.is_none());
5386 }
5387 _ => panic!("Expected EnvVar variant"),
5388 }
5389 }
5390
5391 #[cfg(feature = "unstable_auth_methods")]
5392 #[test]
5393 fn test_auth_method_env_var_with_link_serialization() {
5394 let method = AuthMethod::EnvVar(
5395 AuthMethodEnvVar::new("api-key", "API Key", vec![AuthEnvVar::new("API_KEY")])
5396 .link("https://example.com/keys"),
5397 );
5398
5399 let json = serde_json::to_value(&method).unwrap();
5400 assert_eq!(
5401 json,
5402 json!({
5403 "id": "api-key",
5404 "name": "API Key",
5405 "type": "env_var",
5406 "vars": [{"name": "API_KEY"}],
5407 "link": "https://example.com/keys"
5408 })
5409 );
5410
5411 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
5412 match deserialized {
5413 AuthMethod::EnvVar(AuthMethodEnvVar { link, .. }) => {
5414 assert_eq!(link.as_deref(), Some("https://example.com/keys"));
5415 }
5416 _ => panic!("Expected EnvVar variant"),
5417 }
5418 }
5419
5420 #[cfg(feature = "unstable_auth_methods")]
5421 #[test]
5422 fn test_auth_method_env_var_multiple_vars() {
5423 let method = AuthMethod::EnvVar(AuthMethodEnvVar::new(
5424 "azure-openai",
5425 "Azure OpenAI",
5426 vec![
5427 AuthEnvVar::new("AZURE_OPENAI_API_KEY").label("API Key"),
5428 AuthEnvVar::new("AZURE_OPENAI_ENDPOINT")
5429 .label("Endpoint URL")
5430 .secret(false),
5431 AuthEnvVar::new("AZURE_OPENAI_API_VERSION")
5432 .label("API Version")
5433 .secret(false)
5434 .optional(true),
5435 ],
5436 ));
5437
5438 let json = serde_json::to_value(&method).unwrap();
5439 assert_eq!(
5440 json,
5441 json!({
5442 "id": "azure-openai",
5443 "name": "Azure OpenAI",
5444 "type": "env_var",
5445 "vars": [
5446 {"name": "AZURE_OPENAI_API_KEY", "label": "API Key"},
5447 {"name": "AZURE_OPENAI_ENDPOINT", "label": "Endpoint URL", "secret": false},
5448 {"name": "AZURE_OPENAI_API_VERSION", "label": "API Version", "secret": false, "optional": true}
5449 ]
5450 })
5451 );
5452
5453 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
5454 match deserialized {
5455 AuthMethod::EnvVar(AuthMethodEnvVar { vars, .. }) => {
5456 assert_eq!(vars.len(), 3);
5457 assert_eq!(vars[0].name, "AZURE_OPENAI_API_KEY");
5459 assert_eq!(vars[0].label.as_deref(), Some("API Key"));
5460 assert!(vars[0].secret);
5461 assert!(!vars[0].optional);
5462 assert_eq!(vars[1].name, "AZURE_OPENAI_ENDPOINT");
5464 assert!(!vars[1].secret);
5465 assert!(!vars[1].optional);
5466 assert_eq!(vars[2].name, "AZURE_OPENAI_API_VERSION");
5468 assert!(!vars[2].secret);
5469 assert!(vars[2].optional);
5470 }
5471 _ => panic!("Expected EnvVar variant"),
5472 }
5473 }
5474
5475 #[cfg(feature = "unstable_auth_methods")]
5476 #[test]
5477 fn test_auth_method_terminal_serialization() {
5478 let method = AuthMethod::Terminal(AuthMethodTerminal::new("tui-auth", "Terminal Auth"));
5479
5480 let json = serde_json::to_value(&method).unwrap();
5481 assert_eq!(
5482 json,
5483 json!({
5484 "id": "tui-auth",
5485 "name": "Terminal Auth",
5486 "type": "terminal"
5487 })
5488 );
5489 assert!(!json.as_object().unwrap().contains_key("args"));
5491 assert!(!json.as_object().unwrap().contains_key("env"));
5492
5493 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
5494 match deserialized {
5495 AuthMethod::Terminal(AuthMethodTerminal { args, env, .. }) => {
5496 assert!(args.is_empty());
5497 assert!(env.is_empty());
5498 }
5499 _ => panic!("Expected Terminal variant"),
5500 }
5501 }
5502
5503 #[cfg(feature = "unstable_auth_methods")]
5504 #[test]
5505 fn test_auth_method_terminal_with_args_and_env_serialization() {
5506 use std::collections::HashMap;
5507
5508 let mut env = HashMap::new();
5509 env.insert("TERM".to_string(), "xterm-256color".to_string());
5510
5511 let method = AuthMethod::Terminal(
5512 AuthMethodTerminal::new("tui-auth", "Terminal Auth")
5513 .args(vec!["--interactive".to_string(), "--color".to_string()])
5514 .env(env),
5515 );
5516
5517 let json = serde_json::to_value(&method).unwrap();
5518 assert_eq!(
5519 json,
5520 json!({
5521 "id": "tui-auth",
5522 "name": "Terminal Auth",
5523 "type": "terminal",
5524 "args": ["--interactive", "--color"],
5525 "env": {
5526 "TERM": "xterm-256color"
5527 }
5528 })
5529 );
5530
5531 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
5532 match deserialized {
5533 AuthMethod::Terminal(AuthMethodTerminal { args, env, .. }) => {
5534 assert_eq!(args, vec!["--interactive", "--color"]);
5535 assert_eq!(env.len(), 1);
5536 assert_eq!(env.get("TERM").unwrap(), "xterm-256color");
5537 }
5538 _ => panic!("Expected Terminal variant"),
5539 }
5540 }
5541
5542 #[cfg(feature = "unstable_boolean_config")]
5543 #[test]
5544 fn test_session_config_option_value_id_serialize() {
5545 let val = SessionConfigOptionValue::value_id("model-1");
5546 let json = serde_json::to_value(&val).unwrap();
5547 assert_eq!(json, json!({ "value": "model-1" }));
5549 assert!(!json.as_object().unwrap().contains_key("type"));
5550 }
5551
5552 #[cfg(feature = "unstable_boolean_config")]
5553 #[test]
5554 fn test_session_config_option_value_boolean_serialize() {
5555 let val = SessionConfigOptionValue::boolean(true);
5556 let json = serde_json::to_value(&val).unwrap();
5557 assert_eq!(json, json!({ "type": "boolean", "value": true }));
5558 }
5559
5560 #[cfg(feature = "unstable_boolean_config")]
5561 #[test]
5562 fn test_session_config_option_value_deserialize_no_type() {
5563 let json = json!({ "value": "model-1" });
5565 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
5566 assert_eq!(val, SessionConfigOptionValue::value_id("model-1"));
5567 assert_eq!(val.as_value_id().unwrap().to_string(), "model-1");
5568 }
5569
5570 #[cfg(feature = "unstable_boolean_config")]
5571 #[test]
5572 fn test_session_config_option_value_deserialize_boolean() {
5573 let json = json!({ "type": "boolean", "value": true });
5574 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
5575 assert_eq!(val, SessionConfigOptionValue::boolean(true));
5576 assert_eq!(val.as_bool(), Some(true));
5577 }
5578
5579 #[cfg(feature = "unstable_boolean_config")]
5580 #[test]
5581 fn test_session_config_option_value_deserialize_boolean_false() {
5582 let json = json!({ "type": "boolean", "value": false });
5583 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
5584 assert_eq!(val, SessionConfigOptionValue::boolean(false));
5585 assert_eq!(val.as_bool(), Some(false));
5586 }
5587
5588 #[cfg(feature = "unstable_boolean_config")]
5589 #[test]
5590 fn test_session_config_option_value_deserialize_unknown_type_with_string_value() {
5591 let json = json!({ "type": "text", "value": "freeform input" });
5593 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
5594 assert_eq!(val.as_value_id().unwrap().to_string(), "freeform input");
5595 }
5596
5597 #[cfg(feature = "unstable_boolean_config")]
5598 #[test]
5599 fn test_session_config_option_value_roundtrip_value_id() {
5600 let original = SessionConfigOptionValue::value_id("option-a");
5601 let json = serde_json::to_value(&original).unwrap();
5602 let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
5603 assert_eq!(original, roundtripped);
5604 }
5605
5606 #[cfg(feature = "unstable_boolean_config")]
5607 #[test]
5608 fn test_session_config_option_value_roundtrip_boolean() {
5609 let original = SessionConfigOptionValue::boolean(false);
5610 let json = serde_json::to_value(&original).unwrap();
5611 let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
5612 assert_eq!(original, roundtripped);
5613 }
5614
5615 #[cfg(feature = "unstable_boolean_config")]
5616 #[test]
5617 fn test_session_config_option_value_type_mismatch_boolean_with_string() {
5618 let json = json!({ "type": "boolean", "value": "not a bool" });
5620 let result = serde_json::from_value::<SessionConfigOptionValue>(json);
5621 assert!(result.is_ok());
5623 assert_eq!(
5624 result.unwrap().as_value_id().unwrap().to_string(),
5625 "not a bool"
5626 );
5627 }
5628
5629 #[cfg(feature = "unstable_boolean_config")]
5630 #[test]
5631 fn test_session_config_option_value_from_impls() {
5632 let from_str: SessionConfigOptionValue = "model-1".into();
5633 assert_eq!(from_str.as_value_id().unwrap().to_string(), "model-1");
5634
5635 let from_id: SessionConfigOptionValue = SessionConfigValueId::new("model-2").into();
5636 assert_eq!(from_id.as_value_id().unwrap().to_string(), "model-2");
5637
5638 let from_bool: SessionConfigOptionValue = true.into();
5639 assert_eq!(from_bool.as_bool(), Some(true));
5640 }
5641
5642 #[cfg(feature = "unstable_boolean_config")]
5643 #[test]
5644 fn test_set_session_config_option_request_value_id() {
5645 let req = SetSessionConfigOptionRequest::new("sess_1", "model", "model-1");
5646 let json = serde_json::to_value(&req).unwrap();
5647 assert_eq!(
5648 json,
5649 json!({
5650 "sessionId": "sess_1",
5651 "configId": "model",
5652 "value": "model-1"
5653 })
5654 );
5655 assert!(!json.as_object().unwrap().contains_key("type"));
5657 }
5658
5659 #[cfg(feature = "unstable_boolean_config")]
5660 #[test]
5661 fn test_set_session_config_option_request_boolean() {
5662 let req = SetSessionConfigOptionRequest::new("sess_1", "brave_mode", true);
5663 let json = serde_json::to_value(&req).unwrap();
5664 assert_eq!(
5665 json,
5666 json!({
5667 "sessionId": "sess_1",
5668 "configId": "brave_mode",
5669 "type": "boolean",
5670 "value": true
5671 })
5672 );
5673 }
5674
5675 #[cfg(feature = "unstable_boolean_config")]
5676 #[test]
5677 fn test_set_session_config_option_request_deserialize_no_type() {
5678 let json = json!({
5680 "sessionId": "sess_1",
5681 "configId": "model",
5682 "value": "model-1"
5683 });
5684 let req: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
5685 assert_eq!(req.session_id.to_string(), "sess_1");
5686 assert_eq!(req.config_id.to_string(), "model");
5687 assert_eq!(req.value.as_value_id().unwrap().to_string(), "model-1");
5688 }
5689
5690 #[cfg(feature = "unstable_boolean_config")]
5691 #[test]
5692 fn test_set_session_config_option_request_deserialize_boolean() {
5693 let json = json!({
5694 "sessionId": "sess_1",
5695 "configId": "brave_mode",
5696 "type": "boolean",
5697 "value": true
5698 });
5699 let req: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
5700 assert_eq!(req.value.as_bool(), Some(true));
5701 }
5702
5703 #[cfg(feature = "unstable_boolean_config")]
5704 #[test]
5705 fn test_set_session_config_option_request_roundtrip_value_id() {
5706 let original = SetSessionConfigOptionRequest::new("s", "c", "v");
5707 let json = serde_json::to_value(&original).unwrap();
5708 let roundtripped: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
5709 assert_eq!(original, roundtripped);
5710 }
5711
5712 #[cfg(feature = "unstable_boolean_config")]
5713 #[test]
5714 fn test_set_session_config_option_request_roundtrip_boolean() {
5715 let original = SetSessionConfigOptionRequest::new("s", "c", false);
5716 let json = serde_json::to_value(&original).unwrap();
5717 let roundtripped: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
5718 assert_eq!(original, roundtripped);
5719 }
5720
5721 #[cfg(feature = "unstable_boolean_config")]
5722 #[test]
5723 fn test_session_config_boolean_serialization() {
5724 let cfg = SessionConfigBoolean::new(true);
5725 let json = serde_json::to_value(&cfg).unwrap();
5726 assert_eq!(json, json!({ "currentValue": true }));
5727
5728 let deserialized: SessionConfigBoolean = serde_json::from_value(json).unwrap();
5729 assert!(deserialized.current_value);
5730 }
5731
5732 #[cfg(feature = "unstable_boolean_config")]
5733 #[test]
5734 fn test_session_config_option_boolean_variant() {
5735 let opt = SessionConfigOption::boolean("brave_mode", "Brave Mode", false)
5736 .description("Skip confirmation prompts")
5737 .meta(test_meta());
5738 assert_eq!(serialized_meta_key_count(&opt), 1);
5739
5740 let json = serde_json::to_value(&opt).unwrap();
5741 assert_eq!(
5742 json,
5743 json!({
5744 "id": "brave_mode",
5745 "name": "Brave Mode",
5746 "description": "Skip confirmation prompts",
5747 "type": "boolean",
5748 "currentValue": false,
5749 "_meta": {
5750 "source": "test"
5751 }
5752 })
5753 );
5754
5755 let deserialized: SessionConfigOption = serde_json::from_value(json).unwrap();
5756 assert_eq!(deserialized.id.to_string(), "brave_mode");
5757 assert_eq!(deserialized.name, "Brave Mode");
5758 match deserialized.kind {
5759 SessionConfigKind::Boolean(ref b) => assert!(!b.current_value),
5760 _ => panic!("Expected Boolean kind"),
5761 }
5762 }
5763
5764 #[cfg(feature = "unstable_boolean_config")]
5765 #[test]
5766 fn test_session_config_option_select_still_works() {
5767 let opt = SessionConfigOption::select(
5769 "model",
5770 "Model",
5771 "model-1",
5772 vec![
5773 SessionConfigSelectOption::new("model-1", "Model 1"),
5774 SessionConfigSelectOption::new("model-2", "Model 2"),
5775 ],
5776 )
5777 .meta(test_meta());
5778 assert_eq!(serialized_meta_key_count(&opt), 1);
5779
5780 let json = serde_json::to_value(&opt).unwrap();
5781 assert_eq!(json["type"], "select");
5782 assert_eq!(json["currentValue"], "model-1");
5783 assert_eq!(json["options"].as_array().unwrap().len(), 2);
5784 assert_eq!(json["_meta"]["source"], "test");
5785
5786 let deserialized: SessionConfigOption = serde_json::from_value(json).unwrap();
5787 match deserialized.kind {
5788 SessionConfigKind::Select(ref s) => {
5789 assert_eq!(s.current_value.to_string(), "model-1");
5790 }
5791 _ => panic!("Expected Select kind"),
5792 }
5793 }
5794
5795 #[cfg(feature = "unstable_llm_providers")]
5796 #[test]
5797 fn test_llm_protocol_known_variants() {
5798 assert_eq!(
5799 serde_json::to_value(&LlmProtocol::Anthropic).unwrap(),
5800 json!("anthropic")
5801 );
5802 assert_eq!(
5803 serde_json::to_value(&LlmProtocol::OpenAi).unwrap(),
5804 json!("openai")
5805 );
5806 assert_eq!(
5807 serde_json::to_value(&LlmProtocol::Azure).unwrap(),
5808 json!("azure")
5809 );
5810 assert_eq!(
5811 serde_json::to_value(&LlmProtocol::Vertex).unwrap(),
5812 json!("vertex")
5813 );
5814 assert_eq!(
5815 serde_json::to_value(&LlmProtocol::Bedrock).unwrap(),
5816 json!("bedrock")
5817 );
5818
5819 assert_eq!(
5820 serde_json::from_str::<LlmProtocol>("\"anthropic\"").unwrap(),
5821 LlmProtocol::Anthropic
5822 );
5823 assert_eq!(
5824 serde_json::from_str::<LlmProtocol>("\"openai\"").unwrap(),
5825 LlmProtocol::OpenAi
5826 );
5827 assert_eq!(
5828 serde_json::from_str::<LlmProtocol>("\"azure\"").unwrap(),
5829 LlmProtocol::Azure
5830 );
5831 assert_eq!(
5832 serde_json::from_str::<LlmProtocol>("\"vertex\"").unwrap(),
5833 LlmProtocol::Vertex
5834 );
5835 assert_eq!(
5836 serde_json::from_str::<LlmProtocol>("\"bedrock\"").unwrap(),
5837 LlmProtocol::Bedrock
5838 );
5839 }
5840
5841 #[cfg(feature = "unstable_llm_providers")]
5842 #[test]
5843 fn test_llm_protocol_unknown_variant() {
5844 let unknown: LlmProtocol = serde_json::from_str("\"cohere\"").unwrap();
5845 assert_eq!(unknown, LlmProtocol::Other("cohere".to_string()));
5846
5847 let json = serde_json::to_value(&unknown).unwrap();
5848 assert_eq!(json, json!("cohere"));
5849 }
5850
5851 #[cfg(feature = "unstable_llm_providers")]
5852 #[test]
5853 fn test_provider_current_config_serialization() {
5854 let config =
5855 ProviderCurrentConfig::new(LlmProtocol::Anthropic, "https://api.anthropic.com");
5856
5857 let json = serde_json::to_value(&config).unwrap();
5858 assert_eq!(
5859 json,
5860 json!({
5861 "apiType": "anthropic",
5862 "baseUrl": "https://api.anthropic.com"
5863 })
5864 );
5865
5866 let deserialized: ProviderCurrentConfig = serde_json::from_value(json).unwrap();
5867 assert_eq!(deserialized.api_type, LlmProtocol::Anthropic);
5868 assert_eq!(deserialized.base_url, "https://api.anthropic.com");
5869 }
5870
5871 #[cfg(feature = "unstable_llm_providers")]
5872 #[test]
5873 fn test_provider_info_with_current_config() {
5874 let info = ProviderInfo::new(
5875 "main",
5876 vec![LlmProtocol::Anthropic, LlmProtocol::OpenAi],
5877 true,
5878 Some(ProviderCurrentConfig::new(
5879 LlmProtocol::Anthropic,
5880 "https://api.anthropic.com",
5881 )),
5882 );
5883
5884 let json = serde_json::to_value(&info).unwrap();
5885 assert_eq!(
5886 json,
5887 json!({
5888 "id": "main",
5889 "supported": ["anthropic", "openai"],
5890 "required": true,
5891 "current": {
5892 "apiType": "anthropic",
5893 "baseUrl": "https://api.anthropic.com"
5894 }
5895 })
5896 );
5897
5898 let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
5899 assert_eq!(deserialized.id, "main");
5900 assert_eq!(deserialized.supported.len(), 2);
5901 assert!(deserialized.required);
5902 assert!(deserialized.current.is_some());
5903 assert_eq!(
5904 deserialized.current.as_ref().unwrap().api_type,
5905 LlmProtocol::Anthropic
5906 );
5907 }
5908
5909 #[cfg(feature = "unstable_llm_providers")]
5910 #[test]
5911 fn test_provider_info_disabled() {
5912 let info = ProviderInfo::new(
5913 "secondary",
5914 vec![LlmProtocol::OpenAi],
5915 false,
5916 None::<ProviderCurrentConfig>,
5917 );
5918
5919 let json = serde_json::to_value(&info).unwrap();
5920 assert_eq!(
5921 json,
5922 json!({
5923 "id": "secondary",
5924 "supported": ["openai"],
5925 "required": false
5926 })
5927 );
5928
5929 let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
5930 assert_eq!(deserialized.id, "secondary");
5931 assert!(!deserialized.required);
5932 assert!(deserialized.current.is_none());
5933 }
5934
5935 #[cfg(feature = "unstable_llm_providers")]
5936 #[test]
5937 fn test_provider_info_missing_current_defaults_to_none() {
5938 let json = json!({
5940 "id": "main",
5941 "supported": ["anthropic"],
5942 "required": true
5943 });
5944 let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
5945 assert!(deserialized.current.is_none());
5946 }
5947
5948 #[cfg(feature = "unstable_llm_providers")]
5949 #[test]
5950 fn test_provider_info_explicit_null_current_decodes_to_none() {
5951 let json = json!({
5955 "id": "main",
5956 "supported": ["anthropic"],
5957 "required": true,
5958 "current": null
5959 });
5960 let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
5961 assert!(deserialized.current.is_none());
5962 }
5963
5964 #[cfg(feature = "unstable_llm_providers")]
5965 #[test]
5966 fn test_list_providers_response_serialization() {
5967 let response = ListProvidersResponse::new(vec![ProviderInfo::new(
5968 "main",
5969 vec![LlmProtocol::Anthropic],
5970 true,
5971 Some(ProviderCurrentConfig::new(
5972 LlmProtocol::Anthropic,
5973 "https://api.anthropic.com",
5974 )),
5975 )]);
5976
5977 let json = serde_json::to_value(&response).unwrap();
5978 assert_eq!(json["providers"].as_array().unwrap().len(), 1);
5979 assert_eq!(json["providers"][0]["id"], "main");
5980
5981 let deserialized: ListProvidersResponse = serde_json::from_value(json).unwrap();
5982 assert_eq!(deserialized.providers.len(), 1);
5983 }
5984
5985 #[cfg(feature = "unstable_llm_providers")]
5986 #[test]
5987 fn test_set_provider_request_serialization() {
5988 use std::collections::HashMap;
5989
5990 let mut headers = HashMap::new();
5991 headers.insert("Authorization".to_string(), "Bearer sk-test".to_string());
5992
5993 let request =
5994 SetProviderRequest::new("main", LlmProtocol::OpenAi, "https://api.openai.com/v1")
5995 .headers(headers);
5996
5997 let json = serde_json::to_value(&request).unwrap();
5998 assert_eq!(
5999 json,
6000 json!({
6001 "id": "main",
6002 "apiType": "openai",
6003 "baseUrl": "https://api.openai.com/v1",
6004 "headers": {
6005 "Authorization": "Bearer sk-test"
6006 }
6007 })
6008 );
6009
6010 let deserialized: SetProviderRequest = serde_json::from_value(json).unwrap();
6011 assert_eq!(deserialized.id, "main");
6012 assert_eq!(deserialized.api_type, LlmProtocol::OpenAi);
6013 assert_eq!(deserialized.base_url, "https://api.openai.com/v1");
6014 assert_eq!(deserialized.headers.len(), 1);
6015 assert_eq!(
6016 deserialized.headers.get("Authorization").unwrap(),
6017 "Bearer sk-test"
6018 );
6019 }
6020
6021 #[cfg(feature = "unstable_llm_providers")]
6022 #[test]
6023 fn test_set_provider_request_omits_empty_headers() {
6024 let request =
6025 SetProviderRequest::new("main", LlmProtocol::Anthropic, "https://api.anthropic.com");
6026
6027 let json = serde_json::to_value(&request).unwrap();
6028 assert!(!json.as_object().unwrap().contains_key("headers"));
6030 }
6031
6032 #[cfg(feature = "unstable_llm_providers")]
6033 #[test]
6034 fn test_disable_provider_request_serialization() {
6035 let request = DisableProviderRequest::new("secondary");
6036
6037 let json = serde_json::to_value(&request).unwrap();
6038 assert_eq!(json, json!({ "id": "secondary" }));
6039
6040 let deserialized: DisableProviderRequest = serde_json::from_value(json).unwrap();
6041 assert_eq!(deserialized.id, "secondary");
6042 }
6043
6044 #[cfg(feature = "unstable_llm_providers")]
6045 #[test]
6046 fn test_providers_capabilities_serialization() {
6047 let caps = ProvidersCapabilities::new();
6048
6049 let json = serde_json::to_value(&caps).unwrap();
6050 assert_eq!(json, json!({}));
6051
6052 let deserialized: ProvidersCapabilities = serde_json::from_value(json).unwrap();
6053 assert!(deserialized.meta.is_none());
6054 }
6055
6056 #[cfg(feature = "unstable_llm_providers")]
6057 #[test]
6058 fn test_agent_capabilities_with_providers() {
6059 let caps = AgentCapabilities::new().providers(ProvidersCapabilities::new());
6060
6061 let json = serde_json::to_value(&caps).unwrap();
6062 assert_eq!(json["providers"], json!({}));
6063
6064 let deserialized: AgentCapabilities = serde_json::from_value(json).unwrap();
6065 assert!(deserialized.providers.is_some());
6066 }
6067}