1use std::{borrow::Cow, collections::BTreeMap, sync::Arc};
7
8#[cfg(feature = "unstable_llm_providers")]
9use std::collections::HashMap;
10
11use derive_more::{Display, From};
12#[cfg(feature = "schemars")]
13use schemars::Schema;
14use serde::{Deserialize, Serialize};
15use serde_with::{DefaultOnError, DefaultOnNull, VecSkipError, serde_as, skip_serializing_none};
16
17use super::{
18 AbsolutePath, AvailableCommand, ClientCapabilities, ContentBlock, Error, ExtNotification,
19 ExtRequest, ExtResponse, MessageId, Meta, SessionId, TerminalId, ToolCallId,
20};
21use crate::{IntoOption, ProtocolVersion};
22
23#[cfg(feature = "unstable_session_status")]
24use super::StateUpdate;
25
26#[cfg(all(feature = "unstable_auth_status", feature = "schemars"))]
27use super::AUTH_STATUS_UPDATE_NOTIFICATION;
28#[cfg(feature = "unstable_mcp_over_acp")]
29use super::mcp::{MCP_MESSAGE_METHOD_NAME, MessageMcpNotification};
30
31#[serde_as]
39#[skip_serializing_none]
40#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
41#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
42#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = INITIALIZE_METHOD_NAME)))]
43#[serde(rename_all = "camelCase")]
44#[non_exhaustive]
45pub struct InitializeRequest {
46 pub protocol_version: ProtocolVersion,
48 pub info: Implementation,
50 #[serde_as(deserialize_as = "DefaultOnError")]
52 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
53 #[serde(default)]
54 pub capabilities: ClientCapabilities,
55 #[serde_as(deserialize_as = "DefaultOnError")]
61 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
62 #[serde(default)]
63 #[serde(rename = "_meta")]
64 pub meta: Option<Meta>,
65}
66
67impl InitializeRequest {
68 #[must_use]
70 pub fn new(protocol_version: ProtocolVersion, info: Implementation) -> Self {
71 Self {
72 protocol_version,
73 capabilities: ClientCapabilities::default(),
74 info,
75 meta: None,
76 }
77 }
78
79 #[must_use]
81 pub fn capabilities(mut self, capabilities: ClientCapabilities) -> Self {
82 self.capabilities = capabilities;
83 self
84 }
85
86 #[must_use]
92 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
93 self.meta = meta.into_option();
94 self
95 }
96}
97
98#[serde_as]
104#[skip_serializing_none]
105#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
106#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
107#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = INITIALIZE_METHOD_NAME)))]
108#[serde(rename_all = "camelCase")]
109#[non_exhaustive]
110pub struct InitializeResponse {
111 pub protocol_version: ProtocolVersion,
118 pub info: Implementation,
120 #[serde_as(deserialize_as = "DefaultOnError")]
122 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
123 #[serde(default)]
124 pub capabilities: AgentCapabilities,
125 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
131 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
132 #[serde(default, skip_serializing_if = "Vec::is_empty")]
133 pub auth_methods: Vec<AuthMethod>,
134 #[serde_as(deserialize_as = "DefaultOnError")]
140 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
141 #[serde(default)]
142 #[serde(rename = "_meta")]
143 pub meta: Option<Meta>,
144}
145
146impl InitializeResponse {
147 #[must_use]
149 pub fn new(protocol_version: ProtocolVersion, info: Implementation) -> Self {
150 Self {
151 protocol_version,
152 capabilities: AgentCapabilities::default(),
153 auth_methods: vec![],
154 info,
155 meta: None,
156 }
157 }
158
159 #[must_use]
161 pub fn capabilities(mut self, capabilities: AgentCapabilities) -> Self {
162 self.capabilities = capabilities;
163 self
164 }
165
166 #[must_use]
171 pub fn auth_methods(mut self, auth_methods: Vec<AuthMethod>) -> Self {
172 self.auth_methods = auth_methods;
173 self
174 }
175
176 #[must_use]
182 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
183 self.meta = meta.into_option();
184 self
185 }
186}
187
188#[serde_as]
192#[skip_serializing_none]
193#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
194#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
195#[serde(rename_all = "camelCase")]
196#[non_exhaustive]
197pub struct Implementation {
198 pub name: String,
201 #[serde_as(deserialize_as = "DefaultOnError")]
208 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
209 #[serde(default)]
210 pub title: Option<String>,
211 pub version: String,
214 #[serde_as(deserialize_as = "DefaultOnError")]
220 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
221 #[serde(default)]
222 #[serde(rename = "_meta")]
223 pub meta: Option<Meta>,
224}
225
226impl Implementation {
227 #[must_use]
229 pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
230 Self {
231 name: name.into(),
232 title: None,
233 version: version.into(),
234 meta: None,
235 }
236 }
237
238 #[must_use]
243 pub fn title(mut self, title: impl IntoOption<String>) -> Self {
244 self.title = title.into_option();
245 self
246 }
247
248 #[must_use]
254 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
255 self.meta = meta.into_option();
256 self
257 }
258}
259
260#[serde_as]
270#[skip_serializing_none]
271#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
272#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
273#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGIN_METHOD_NAME)))]
274#[serde(rename_all = "camelCase")]
275#[non_exhaustive]
276pub struct LoginAuthRequest {
277 pub method_id: AuthMethodId,
280 #[serde_as(deserialize_as = "DefaultOnError")]
286 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
287 #[serde(default)]
288 #[serde(rename = "_meta")]
289 pub meta: Option<Meta>,
290}
291
292impl LoginAuthRequest {
293 #[must_use]
295 pub fn new(method_id: impl Into<AuthMethodId>) -> Self {
296 Self {
297 method_id: method_id.into(),
298 meta: None,
299 }
300 }
301
302 #[must_use]
308 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
309 self.meta = meta.into_option();
310 self
311 }
312}
313
314crate::serde_util::default_on_null! {
315 #[serde_as]
317 #[skip_serializing_none]
318 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
319 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
320 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGIN_METHOD_NAME)))]
321 #[serde(rename_all = "camelCase")]
322 #[non_exhaustive]
323 pub struct LoginAuthResponse {
324 #[serde_as(deserialize_as = "DefaultOnError")]
330 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
331 #[serde(default)]
332 #[serde(rename = "_meta")]
333 pub meta: Option<Meta>,
334 }
335}
336
337impl LoginAuthResponse {
338 #[must_use]
340 pub fn new() -> Self {
341 Self::default()
342 }
343
344 #[must_use]
350 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
351 self.meta = meta.into_option();
352 self
353 }
354}
355
356crate::serde_util::default_on_null! {
359 #[serde_as]
367 #[skip_serializing_none]
368 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
369 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
370 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGOUT_METHOD_NAME)))]
371 #[serde(rename_all = "camelCase")]
372 #[non_exhaustive]
373 pub struct LogoutAuthRequest {
374 #[serde_as(deserialize_as = "DefaultOnError")]
380 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
381 #[serde(default)]
382 #[serde(rename = "_meta")]
383 pub meta: Option<Meta>,
384 }
385}
386
387impl LogoutAuthRequest {
388 #[must_use]
390 pub fn new() -> Self {
391 Self::default()
392 }
393
394 #[must_use]
400 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
401 self.meta = meta.into_option();
402 self
403 }
404}
405
406crate::serde_util::default_on_null! {
407 #[serde_as]
409 #[skip_serializing_none]
410 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
411 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
412 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGOUT_METHOD_NAME)))]
413 #[serde(rename_all = "camelCase")]
414 #[non_exhaustive]
415 pub struct LogoutAuthResponse {
416 #[serde_as(deserialize_as = "DefaultOnError")]
422 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
423 #[serde(default)]
424 #[serde(rename = "_meta")]
425 pub meta: Option<Meta>,
426 }
427}
428
429impl LogoutAuthResponse {
430 #[must_use]
432 pub fn new() -> Self {
433 Self::default()
434 }
435
436 #[must_use]
442 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
443 self.meta = meta.into_option();
444 self
445 }
446}
447
448#[cfg(feature = "unstable_auth_status")]
451crate::serde_util::default_on_null! {
452 #[serde_as]
463 #[skip_serializing_none]
464 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
465 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
466 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_STATUS_METHOD_NAME)))]
467 #[serde(rename_all = "camelCase")]
468 #[non_exhaustive]
469 pub struct AuthStatusRequest {
470 #[serde_as(deserialize_as = "DefaultOnError")]
476 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
477 #[serde(default)]
478 #[serde(rename = "_meta")]
479 pub meta: Option<Meta>,
480 }
481}
482
483#[cfg(feature = "unstable_auth_status")]
484impl AuthStatusRequest {
485 #[must_use]
487 pub fn new() -> Self {
488 Self::default()
489 }
490
491 #[must_use]
497 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
498 self.meta = meta.into_option();
499 self
500 }
501}
502
503#[cfg(feature = "unstable_auth_status")]
509#[serde_as]
510#[skip_serializing_none]
511#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
512#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
513#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_STATUS_METHOD_NAME)))]
514#[serde(rename_all = "camelCase")]
515#[non_exhaustive]
516pub struct AuthStatusResponse {
517 pub state: AuthState,
522 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
528 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
529 #[serde(default, skip_serializing_if = "Vec::is_empty")]
530 pub accounts: Vec<AuthAccount>,
531 #[serde_as(deserialize_as = "DefaultOnError")]
537 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
538 #[serde(default)]
539 #[serde(rename = "_meta")]
540 pub meta: Option<Meta>,
541}
542
543#[cfg(feature = "unstable_auth_status")]
544impl AuthStatusResponse {
545 #[must_use]
547 pub fn new(state: AuthState) -> Self {
548 Self {
549 state,
550 accounts: vec![],
551 meta: None,
552 }
553 }
554
555 #[must_use]
557 pub fn accounts(mut self, accounts: Vec<AuthAccount>) -> Self {
558 self.accounts = accounts;
559 self
560 }
561
562 #[must_use]
568 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
569 self.meta = meta.into_option();
570 self
571 }
572}
573
574#[cfg(feature = "unstable_auth_status")]
584#[serde_as]
585#[skip_serializing_none]
586#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
587#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
588#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = AUTH_STATUS_UPDATE_NOTIFICATION)))]
589#[serde(rename_all = "camelCase")]
590#[non_exhaustive]
591pub struct AuthStatusUpdateNotification {
592 pub state: AuthState,
597 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
603 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
604 #[serde(default, skip_serializing_if = "Vec::is_empty")]
605 pub accounts: Vec<AuthAccount>,
606 #[serde_as(deserialize_as = "DefaultOnError")]
612 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
613 #[serde(default)]
614 #[serde(rename = "_meta")]
615 pub meta: Option<Meta>,
616}
617
618#[cfg(feature = "unstable_auth_status")]
619impl AuthStatusUpdateNotification {
620 #[must_use]
622 pub fn new(state: AuthState) -> Self {
623 Self {
624 state,
625 accounts: vec![],
626 meta: None,
627 }
628 }
629
630 #[must_use]
632 pub fn accounts(mut self, accounts: Vec<AuthAccount>) -> Self {
633 self.accounts = accounts;
634 self
635 }
636
637 #[must_use]
643 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
644 self.meta = meta.into_option();
645 self
646 }
647}
648
649#[cfg(feature = "unstable_auth_status")]
655#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
656#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
657#[serde(rename_all = "snake_case")]
658#[non_exhaustive]
659pub enum AuthState {
660 Authenticated,
662 Unauthenticated,
665 NotRequired,
667 Unknown,
669 #[serde(untagged)]
675 Other(String),
676}
677
678#[cfg(feature = "unstable_auth_status")]
689#[serde_as]
690#[skip_serializing_none]
691#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
692#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
693#[serde(rename_all = "camelCase")]
694#[non_exhaustive]
695pub struct AuthAccount {
696 pub label: String,
699 #[serde_as(deserialize_as = "DefaultOnError")]
705 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
706 #[serde(default)]
707 pub active: Option<bool>,
708 #[serde_as(deserialize_as = "DefaultOnError")]
712 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
713 #[serde(default)]
714 pub name: Option<String>,
715 #[serde_as(deserialize_as = "DefaultOnError")]
719 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
720 #[serde(default)]
721 pub email: Option<String>,
722 #[serde_as(deserialize_as = "DefaultOnError")]
726 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
727 #[serde(default)]
728 pub organization: Option<String>,
729 #[serde_as(deserialize_as = "DefaultOnError")]
735 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
736 #[serde(default)]
737 #[serde(rename = "_meta")]
738 pub meta: Option<Meta>,
739}
740
741#[cfg(feature = "unstable_auth_status")]
742impl AuthAccount {
743 #[must_use]
745 pub fn new(label: impl Into<String>) -> Self {
746 Self {
747 label: label.into(),
748 active: None,
749 name: None,
750 email: None,
751 organization: None,
752 meta: None,
753 }
754 }
755
756 #[must_use]
758 pub fn active(mut self, active: impl IntoOption<bool>) -> Self {
759 self.active = active.into_option();
760 self
761 }
762
763 #[must_use]
765 pub fn name(mut self, name: impl IntoOption<String>) -> Self {
766 self.name = name.into_option();
767 self
768 }
769
770 #[must_use]
772 pub fn email(mut self, email: impl IntoOption<String>) -> Self {
773 self.email = email.into_option();
774 self
775 }
776
777 #[must_use]
779 pub fn organization(mut self, organization: impl IntoOption<String>) -> Self {
780 self.organization = organization.into_option();
781 self
782 }
783
784 #[must_use]
790 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
791 self.meta = meta.into_option();
792 self
793 }
794}
795
796#[cfg(feature = "unstable_auth_status")]
804#[serde_as]
805#[skip_serializing_none]
806#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
807#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
808#[non_exhaustive]
809pub struct AuthStatusCapabilities {
810 #[serde_as(deserialize_as = "DefaultOnError")]
816 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
817 #[serde(default)]
818 #[serde(rename = "_meta")]
819 pub meta: Option<Meta>,
820}
821
822#[cfg(feature = "unstable_auth_status")]
823impl AuthStatusCapabilities {
824 #[must_use]
826 pub fn new() -> Self {
827 Self::default()
828 }
829
830 #[must_use]
836 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
837 self.meta = meta.into_option();
838 self
839 }
840}
841
842#[serde_as]
848#[skip_serializing_none]
849#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
850#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
851#[serde(rename_all = "camelCase")]
852#[non_exhaustive]
853pub struct AgentAuthCapabilities {
854 #[cfg(feature = "unstable_auth_status")]
863 #[serde_as(deserialize_as = "DefaultOnError")]
864 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
865 #[serde(default)]
866 pub status: Option<AuthStatusCapabilities>,
867 #[serde_as(deserialize_as = "DefaultOnError")]
873 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
874 #[serde(default)]
875 #[serde(rename = "_meta")]
876 pub meta: Option<Meta>,
877}
878
879impl AgentAuthCapabilities {
880 #[must_use]
882 pub fn new() -> Self {
883 Self::default()
884 }
885
886 #[cfg(feature = "unstable_auth_status")]
892 #[must_use]
893 pub fn status(mut self, status: impl IntoOption<AuthStatusCapabilities>) -> Self {
894 self.status = status.into_option();
895 self
896 }
897
898 #[must_use]
904 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
905 self.meta = meta.into_option();
906 self
907 }
908}
909
910#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
912#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
913#[serde(transparent)]
914#[from(forward)]
915#[non_exhaustive]
916pub struct AuthMethodId(pub Arc<str>);
917
918impl AuthMethodId {
919 #[must_use]
921 pub fn new(id: impl Into<Self>) -> Self {
922 id.into()
923 }
924}
925
926#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
930#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
931#[serde(tag = "type", rename_all = "snake_case")]
932#[non_exhaustive]
933pub enum AuthMethod {
934 Terminal(AuthMethodTerminal),
937 Agent(AuthMethodAgent),
941 #[serde(untagged)]
951 Other(OtherAuthMethod),
952}
953
954impl AuthMethod {
955 #[must_use]
957 pub fn method_id(&self) -> &AuthMethodId {
958 match self {
959 Self::Agent(a) => &a.method_id,
960 Self::Other(a) => &a.method_id,
961 Self::Terminal(t) => &t.method_id,
962 }
963 }
964
965 #[must_use]
967 pub fn name(&self) -> &str {
968 match self {
969 Self::Agent(a) => &a.name,
970 Self::Other(a) => &a.name,
971 Self::Terminal(t) => &t.name,
972 }
973 }
974
975 #[must_use]
977 pub fn description(&self) -> Option<&str> {
978 match self {
979 Self::Agent(a) => a.description.as_deref(),
980 Self::Other(a) => a
981 .fields
982 .get("description")
983 .and_then(serde_json::Value::as_str),
984 Self::Terminal(t) => t.description.as_deref(),
985 }
986 }
987
988 #[must_use]
994 pub fn meta(&self) -> Option<&Meta> {
995 match self {
996 Self::Agent(a) => a.meta.as_ref(),
997 Self::Other(a) => a.fields.get("_meta").and_then(serde_json::Value::as_object),
998 Self::Terminal(t) => t.meta.as_ref(),
999 }
1000 }
1001}
1002
1003#[serde_as]
1005#[skip_serializing_none]
1006#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1007#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
1008#[cfg_attr(feature = "schemars", schemars(inline))]
1009#[cfg_attr(feature = "schemars", schemars(transform = other_auth_method_schema))]
1010#[serde(rename_all = "camelCase")]
1011#[non_exhaustive]
1012pub struct OtherAuthMethod {
1013 #[serde(rename = "type")]
1019 pub type_: String,
1020 pub method_id: AuthMethodId,
1022 pub name: String,
1024 #[serde(flatten)]
1027 pub fields: BTreeMap<String, serde_json::Value>,
1028}
1029
1030impl OtherAuthMethod {
1031 #[must_use]
1033 pub fn new(
1034 type_: impl Into<String>,
1035 method_id: impl Into<AuthMethodId>,
1036 name: impl Into<String>,
1037 mut fields: BTreeMap<String, serde_json::Value>,
1038 ) -> Self {
1039 fields.remove("type");
1040 fields.remove("methodId");
1041 fields.remove("name");
1042 Self {
1043 type_: type_.into(),
1044 method_id: method_id.into(),
1045 name: name.into(),
1046 fields,
1047 }
1048 }
1049}
1050
1051impl<'de> Deserialize<'de> for OtherAuthMethod {
1052 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1053 where
1054 D: serde::Deserializer<'de>,
1055 {
1056 #[derive(Deserialize)]
1057 #[serde(rename_all = "camelCase")]
1058 struct RawOtherAuthMethod {
1059 #[serde(rename = "type")]
1060 type_: String,
1061 method_id: AuthMethodId,
1062 name: String,
1063 #[serde(flatten)]
1064 fields: BTreeMap<String, serde_json::Value>,
1065 }
1066
1067 let raw = RawOtherAuthMethod::deserialize(deserializer)?;
1068 if is_known_auth_method_type(&raw.type_) {
1069 return Err(serde::de::Error::custom(format!(
1070 "known authentication method `{}` did not match its schema",
1071 raw.type_
1072 )));
1073 }
1074
1075 Ok(Self {
1076 type_: raw.type_,
1077 method_id: raw.method_id,
1078 name: raw.name,
1079 fields: raw.fields,
1080 })
1081 }
1082}
1083
1084fn is_known_auth_method_type(type_: &str) -> bool {
1085 matches!(type_, "agent" | "terminal")
1086}
1087
1088#[cfg(feature = "schemars")]
1089fn other_auth_method_schema(schema: &mut Schema) {
1090 super::schema_util::reject_known_string_discriminators(schema, "type", &["agent", "terminal"]);
1091}
1092
1093#[serde_as]
1097#[skip_serializing_none]
1098#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1099#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1100#[serde(rename_all = "camelCase")]
1101#[non_exhaustive]
1102pub struct AuthMethodAgent {
1103 pub method_id: AuthMethodId,
1105 pub name: String,
1107 #[serde_as(deserialize_as = "DefaultOnError")]
1111 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1112 #[serde(default)]
1113 pub description: Option<String>,
1114 #[serde_as(deserialize_as = "DefaultOnError")]
1120 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1121 #[serde(default)]
1122 #[serde(rename = "_meta")]
1123 pub meta: Option<Meta>,
1124}
1125
1126impl AuthMethodAgent {
1127 #[must_use]
1129 pub fn new(method_id: impl Into<AuthMethodId>, name: impl Into<String>) -> Self {
1130 Self {
1131 method_id: method_id.into(),
1132 name: name.into(),
1133 description: None,
1134 meta: None,
1135 }
1136 }
1137
1138 #[must_use]
1140 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
1141 self.description = description.into_option();
1142 self
1143 }
1144
1145 #[must_use]
1151 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1152 self.meta = meta.into_option();
1153 self
1154 }
1155}
1156
1157#[serde_as]
1165#[skip_serializing_none]
1166#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1167#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1168#[serde(rename_all = "camelCase")]
1169#[non_exhaustive]
1170pub struct AuthMethodTerminal {
1171 pub method_id: AuthMethodId,
1173 pub name: String,
1175 #[serde_as(deserialize_as = "DefaultOnError")]
1179 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1180 #[serde(default)]
1181 pub description: Option<String>,
1182 #[serde_as(deserialize_as = "DefaultOnNull")]
1184 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
1185 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1186 pub args: Vec<String>,
1187 #[serde_as(deserialize_as = "DefaultOnNull")]
1191 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
1192 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1193 pub env: Vec<EnvVariable>,
1194 #[serde_as(deserialize_as = "DefaultOnError")]
1200 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1201 #[serde(default)]
1202 #[serde(rename = "_meta")]
1203 pub meta: Option<Meta>,
1204}
1205
1206impl AuthMethodTerminal {
1207 #[must_use]
1209 pub fn new(method_id: impl Into<AuthMethodId>, name: impl Into<String>) -> Self {
1210 Self {
1211 method_id: method_id.into(),
1212 name: name.into(),
1213 description: None,
1214 args: Vec::new(),
1215 env: Vec::new(),
1216 meta: None,
1217 }
1218 }
1219
1220 #[must_use]
1222 pub fn args(mut self, args: Vec<String>) -> Self {
1223 self.args = args;
1224 self
1225 }
1226
1227 #[must_use]
1231 pub fn env(mut self, env: Vec<EnvVariable>) -> Self {
1232 self.env = env;
1233 self
1234 }
1235
1236 #[must_use]
1238 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
1239 self.description = description.into_option();
1240 self
1241 }
1242
1243 #[must_use]
1249 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1250 self.meta = meta.into_option();
1251 self
1252 }
1253}
1254
1255#[serde_as]
1261#[skip_serializing_none]
1262#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1263#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1264#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_NEW_METHOD_NAME)))]
1265#[serde(rename_all = "camelCase")]
1266#[non_exhaustive]
1267pub struct NewSessionRequest {
1268 pub cwd: AbsolutePath,
1270 #[serde_as(deserialize_as = "DefaultOnNull")]
1276 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
1277 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1278 pub additional_directories: Vec<AbsolutePath>,
1279 #[serde_as(deserialize_as = "DefaultOnNull")]
1281 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
1282 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1283 pub mcp_servers: Vec<McpServer>,
1284 #[serde(default)]
1290 pub system_prompt: Option<SystemPrompt>,
1291 #[serde_as(deserialize_as = "DefaultOnError")]
1297 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1298 #[serde(default)]
1299 #[serde(rename = "_meta")]
1300 pub meta: Option<Meta>,
1301}
1302
1303impl NewSessionRequest {
1304 #[must_use]
1306 pub fn new(cwd: impl Into<AbsolutePath>) -> Self {
1307 Self {
1308 cwd: cwd.into(),
1309 additional_directories: vec![],
1310 mcp_servers: vec![],
1311 system_prompt: None,
1312 meta: None,
1313 }
1314 }
1315
1316 #[must_use]
1318 pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
1319 where
1320 I: IntoIterator<Item = P>,
1321 P: Into<AbsolutePath>,
1322 {
1323 self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
1324 self
1325 }
1326
1327 #[must_use]
1329 pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1330 self.mcp_servers = mcp_servers;
1331 self
1332 }
1333
1334 #[must_use]
1336 pub fn system_prompt(mut self, system_prompt: impl IntoOption<SystemPrompt>) -> Self {
1337 self.system_prompt = system_prompt.into_option();
1338 self
1339 }
1340
1341 #[must_use]
1347 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1348 self.meta = meta.into_option();
1349 self
1350 }
1351}
1352
1353#[serde_as]
1361#[skip_serializing_none]
1362#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1363#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1364#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_NEW_METHOD_NAME)))]
1365#[serde(rename_all = "camelCase")]
1366#[non_exhaustive]
1367pub struct NewSessionResponse {
1368 pub session_id: SessionId,
1372 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1378 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1379 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1380 pub config_options: Vec<SessionConfigOption>,
1381 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1387 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1388 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1389 pub available_commands: Vec<AvailableCommand>,
1390 #[serde_as(deserialize_as = "DefaultOnError")]
1396 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1397 #[serde(default)]
1398 #[serde(rename = "_meta")]
1399 pub meta: Option<Meta>,
1400}
1401
1402impl NewSessionResponse {
1403 #[must_use]
1405 pub fn new(session_id: impl Into<SessionId>) -> Self {
1406 Self {
1407 session_id: session_id.into(),
1408 config_options: Vec::new(),
1409 available_commands: Vec::new(),
1410 meta: None,
1411 }
1412 }
1413
1414 #[must_use]
1420 pub fn config_options(mut self, config_options: Vec<SessionConfigOption>) -> Self {
1421 self.config_options = config_options;
1422 self
1423 }
1424
1425 #[must_use]
1427 pub fn available_commands(mut self, available_commands: Vec<AvailableCommand>) -> Self {
1428 self.available_commands = available_commands;
1429 self
1430 }
1431
1432 #[must_use]
1438 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1439 self.meta = meta.into_option();
1440 self
1441 }
1442}
1443
1444#[cfg(feature = "unstable_session_fork")]
1457#[serde_as]
1458#[skip_serializing_none]
1459#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1460#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1461#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_FORK_METHOD_NAME)))]
1462#[serde(rename_all = "camelCase")]
1463#[non_exhaustive]
1464pub struct ForkSessionRequest {
1465 pub session_id: SessionId,
1467 pub cwd: AbsolutePath,
1469 #[serde_as(deserialize_as = "DefaultOnNull")]
1475 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
1476 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1477 pub additional_directories: Vec<AbsolutePath>,
1478 #[serde_as(deserialize_as = "DefaultOnNull")]
1480 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
1481 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1482 pub mcp_servers: Vec<McpServer>,
1483 #[serde(default)]
1491 pub system_prompt: Option<SystemPrompt>,
1492 #[serde_as(deserialize_as = "DefaultOnError")]
1498 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1499 #[serde(default)]
1500 #[serde(rename = "_meta")]
1501 pub meta: Option<Meta>,
1502}
1503
1504#[cfg(feature = "unstable_session_fork")]
1505impl ForkSessionRequest {
1506 #[must_use]
1508 pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<AbsolutePath>) -> Self {
1509 Self {
1510 session_id: session_id.into(),
1511 cwd: cwd.into(),
1512 additional_directories: vec![],
1513 mcp_servers: vec![],
1514 system_prompt: None,
1515 meta: None,
1516 }
1517 }
1518
1519 #[must_use]
1521 pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
1522 where
1523 I: IntoIterator<Item = P>,
1524 P: Into<AbsolutePath>,
1525 {
1526 self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
1527 self
1528 }
1529
1530 #[must_use]
1532 pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1533 self.mcp_servers = mcp_servers;
1534 self
1535 }
1536
1537 #[must_use]
1539 pub fn system_prompt(mut self, system_prompt: impl IntoOption<SystemPrompt>) -> Self {
1540 self.system_prompt = system_prompt.into_option();
1541 self
1542 }
1543
1544 #[must_use]
1550 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1551 self.meta = meta.into_option();
1552 self
1553 }
1554}
1555
1556#[cfg(feature = "unstable_session_fork")]
1562#[serde_as]
1563#[skip_serializing_none]
1564#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1565#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1566#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_FORK_METHOD_NAME)))]
1567#[serde(rename_all = "camelCase")]
1568#[non_exhaustive]
1569pub struct ForkSessionResponse {
1570 pub session_id: SessionId,
1572 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1578 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1579 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1580 pub config_options: Vec<SessionConfigOption>,
1581 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1587 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1588 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1589 pub available_commands: Vec<AvailableCommand>,
1590 #[serde_as(deserialize_as = "DefaultOnError")]
1596 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1597 #[serde(default)]
1598 #[serde(rename = "_meta")]
1599 pub meta: Option<Meta>,
1600}
1601
1602#[cfg(feature = "unstable_session_fork")]
1603impl ForkSessionResponse {
1604 #[must_use]
1606 pub fn new(session_id: impl Into<SessionId>) -> Self {
1607 Self {
1608 session_id: session_id.into(),
1609 config_options: Vec::new(),
1610 available_commands: Vec::new(),
1611 meta: None,
1612 }
1613 }
1614
1615 #[must_use]
1621 pub fn config_options(mut self, config_options: Vec<SessionConfigOption>) -> Self {
1622 self.config_options = config_options;
1623 self
1624 }
1625
1626 #[must_use]
1628 pub fn available_commands(mut self, available_commands: Vec<AvailableCommand>) -> Self {
1629 self.available_commands = available_commands;
1630 self
1631 }
1632
1633 #[must_use]
1639 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1640 self.meta = meta.into_option();
1641 self
1642 }
1643}
1644
1645#[serde_as]
1652#[skip_serializing_none]
1653#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1654#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1655#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_RESUME_METHOD_NAME)))]
1656#[serde(rename_all = "camelCase")]
1657#[non_exhaustive]
1658pub struct ResumeSessionRequest {
1659 pub session_id: SessionId,
1661 pub cwd: AbsolutePath,
1666 #[serde_as(deserialize_as = "DefaultOnNull")]
1673 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
1674 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1675 pub additional_directories: Vec<AbsolutePath>,
1676 #[serde_as(deserialize_as = "DefaultOnNull")]
1681 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
1682 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1683 pub mcp_servers: Vec<McpServer>,
1684 #[serde(default)]
1696 pub replay_from: Option<ReplayFrom>,
1697 #[serde(default)]
1705 pub system_prompt: Option<SystemPrompt>,
1706 #[serde_as(deserialize_as = "DefaultOnError")]
1712 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1713 #[serde(default)]
1714 #[serde(rename = "_meta")]
1715 pub meta: Option<Meta>,
1716}
1717
1718impl ResumeSessionRequest {
1719 #[must_use]
1721 pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<AbsolutePath>) -> Self {
1722 Self {
1723 session_id: session_id.into(),
1724 cwd: cwd.into(),
1725 additional_directories: vec![],
1726 mcp_servers: vec![],
1727 replay_from: None,
1728 system_prompt: None,
1729 meta: None,
1730 }
1731 }
1732
1733 #[must_use]
1735 pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
1736 where
1737 I: IntoIterator<Item = P>,
1738 P: Into<AbsolutePath>,
1739 {
1740 self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
1741 self
1742 }
1743
1744 #[must_use]
1746 pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1747 self.mcp_servers = mcp_servers;
1748 self
1749 }
1750
1751 #[must_use]
1759 pub fn replay_from(mut self, replay_from: impl IntoOption<ReplayFrom>) -> Self {
1760 self.replay_from = replay_from.into_option();
1761 self
1762 }
1763
1764 #[must_use]
1766 pub fn system_prompt(mut self, system_prompt: impl IntoOption<SystemPrompt>) -> Self {
1767 self.system_prompt = system_prompt.into_option();
1768 self
1769 }
1770
1771 #[must_use]
1777 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1778 self.meta = meta.into_option();
1779 self
1780 }
1781}
1782
1783#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1787#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1788#[serde(tag = "type", rename_all = "snake_case")]
1789#[non_exhaustive]
1790pub enum ReplayFrom {
1791 Start(ReplayFromStart),
1793 #[serde(untagged)]
1804 Other(OtherReplayFrom),
1805}
1806
1807impl From<ReplayFromStart> for ReplayFrom {
1808 fn from(replay_from: ReplayFromStart) -> Self {
1809 Self::Start(replay_from)
1810 }
1811}
1812
1813#[serde_as]
1815#[skip_serializing_none]
1816#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1817#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1818#[serde(rename_all = "camelCase")]
1819#[non_exhaustive]
1820pub struct ReplayFromStart {
1821 #[serde_as(deserialize_as = "DefaultOnError")]
1827 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1828 #[serde(default)]
1829 #[serde(rename = "_meta")]
1830 pub meta: Option<Meta>,
1831}
1832
1833impl ReplayFromStart {
1834 #[must_use]
1836 pub fn new() -> Self {
1837 Self::default()
1838 }
1839
1840 #[must_use]
1846 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1847 self.meta = meta.into_option();
1848 self
1849 }
1850}
1851
1852#[serde_as]
1854#[skip_serializing_none]
1855#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1856#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
1857#[cfg_attr(feature = "schemars", schemars(inline))]
1858#[cfg_attr(feature = "schemars", schemars(transform = other_replay_from_schema))]
1859#[serde(rename_all = "camelCase")]
1860#[non_exhaustive]
1861pub struct OtherReplayFrom {
1862 #[serde(rename = "type")]
1868 pub type_: String,
1869 #[serde(flatten)]
1872 pub fields: BTreeMap<String, serde_json::Value>,
1873}
1874
1875impl OtherReplayFrom {
1876 #[must_use]
1878 pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
1879 fields.remove("type");
1880 Self {
1881 type_: type_.into(),
1882 fields,
1883 }
1884 }
1885}
1886
1887impl<'de> Deserialize<'de> for OtherReplayFrom {
1888 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1889 where
1890 D: serde::Deserializer<'de>,
1891 {
1892 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
1893 let type_ = fields
1894 .remove("type")
1895 .ok_or_else(|| serde::de::Error::missing_field("type"))?;
1896 let serde_json::Value::String(type_) = type_ else {
1897 return Err(serde::de::Error::custom("`type` must be a string"));
1898 };
1899
1900 if is_known_replay_from_type(&type_) {
1901 return Err(serde::de::Error::custom(format!(
1902 "known replay cursor `{type_}` did not match its schema"
1903 )));
1904 }
1905
1906 Ok(Self { type_, fields })
1907 }
1908}
1909
1910fn is_known_replay_from_type(type_: &str) -> bool {
1911 matches!(type_, "start")
1912}
1913
1914#[cfg(feature = "schemars")]
1915fn other_replay_from_schema(schema: &mut Schema) {
1916 super::schema_util::reject_known_string_discriminators(schema, "type", &["start"]);
1917}
1918
1919crate::serde_util::default_on_null! {
1920 #[serde_as]
1928 #[skip_serializing_none]
1929 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1930 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
1931 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_RESUME_METHOD_NAME)))]
1932 #[serde(rename_all = "camelCase")]
1933 #[non_exhaustive]
1934 pub struct ResumeSessionResponse {
1935 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1941 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1942 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1943 pub config_options: Vec<SessionConfigOption>,
1944 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1950 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1951 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1952 pub available_commands: Vec<AvailableCommand>,
1953 #[serde_as(deserialize_as = "DefaultOnError")]
1959 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1960 #[serde(default)]
1961 #[serde(rename = "_meta")]
1962 pub meta: Option<Meta>,
1963 }
1964}
1965
1966impl ResumeSessionResponse {
1967 #[must_use]
1969 pub fn new() -> Self {
1970 Self::default()
1971 }
1972
1973 #[must_use]
1979 pub fn config_options(mut self, config_options: Vec<SessionConfigOption>) -> Self {
1980 self.config_options = config_options;
1981 self
1982 }
1983
1984 #[must_use]
1986 pub fn available_commands(mut self, available_commands: Vec<AvailableCommand>) -> Self {
1987 self.available_commands = available_commands;
1988 self
1989 }
1990
1991 #[must_use]
1997 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1998 self.meta = meta.into_option();
1999 self
2000 }
2001}
2002
2003#[serde_as]
2026#[skip_serializing_none]
2027#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2028#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2029#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_CLOSE_METHOD_NAME)))]
2030#[serde(rename_all = "camelCase")]
2031#[non_exhaustive]
2032pub struct CloseSessionRequest {
2033 pub session_id: SessionId,
2035 #[serde_as(deserialize_as = "DefaultOnError")]
2041 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2042 #[serde(default)]
2043 #[serde(rename = "_meta")]
2044 pub meta: Option<Meta>,
2045}
2046
2047impl CloseSessionRequest {
2048 #[must_use]
2050 pub fn new(session_id: impl Into<SessionId>) -> Self {
2051 Self {
2052 session_id: session_id.into(),
2053 meta: None,
2054 }
2055 }
2056
2057 #[must_use]
2063 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2064 self.meta = meta.into_option();
2065 self
2066 }
2067}
2068
2069crate::serde_util::default_on_null! {
2070 #[serde_as]
2072 #[skip_serializing_none]
2073 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2074 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
2075 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_CLOSE_METHOD_NAME)))]
2076 #[serde(rename_all = "camelCase")]
2077 #[non_exhaustive]
2078 pub struct CloseSessionResponse {
2079 #[serde_as(deserialize_as = "DefaultOnError")]
2085 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2086 #[serde(default)]
2087 #[serde(rename = "_meta")]
2088 pub meta: Option<Meta>,
2089 }
2090}
2091
2092impl CloseSessionResponse {
2093 #[must_use]
2095 pub fn new() -> Self {
2096 Self::default()
2097 }
2098
2099 #[must_use]
2105 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2106 self.meta = meta.into_option();
2107 self
2108 }
2109}
2110
2111#[cfg(feature = "unstable_session_status")]
2125#[serde_as]
2126#[skip_serializing_none]
2127#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2128#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2129#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_STATUS_METHOD_NAME)))]
2130#[serde(rename_all = "camelCase")]
2131#[non_exhaustive]
2132pub struct SessionStatusRequest {
2133 pub session_id: SessionId,
2135 #[serde_as(deserialize_as = "DefaultOnError")]
2141 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2142 #[serde(default)]
2143 #[serde(rename = "_meta")]
2144 pub meta: Option<Meta>,
2145}
2146
2147#[cfg(feature = "unstable_session_status")]
2148impl SessionStatusRequest {
2149 #[must_use]
2151 pub fn new(session_id: impl Into<SessionId>) -> Self {
2152 Self {
2153 session_id: session_id.into(),
2154 meta: None,
2155 }
2156 }
2157
2158 #[must_use]
2164 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2165 self.meta = meta.into_option();
2166 self
2167 }
2168}
2169
2170#[cfg(feature = "unstable_session_status")]
2179#[serde_as]
2180#[skip_serializing_none]
2181#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2182#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2183#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_STATUS_METHOD_NAME)))]
2184#[serde(rename_all = "camelCase")]
2185#[non_exhaustive]
2186pub struct SessionStatusResponse {
2187 pub status: SessionStatus,
2191 #[serde_as(deserialize_as = "DefaultOnError")]
2199 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2200 #[serde(default)]
2201 pub state: Option<StateUpdate>,
2202 #[serde_as(deserialize_as = "DefaultOnError")]
2208 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2209 #[serde(default)]
2210 #[serde(rename = "_meta")]
2211 pub meta: Option<Meta>,
2212}
2213
2214#[cfg(feature = "unstable_session_status")]
2215impl SessionStatusResponse {
2216 #[must_use]
2218 pub fn new(status: SessionStatus) -> Self {
2219 Self {
2220 status,
2221 state: None,
2222 meta: None,
2223 }
2224 }
2225
2226 #[must_use]
2229 pub fn state(mut self, state: impl IntoOption<StateUpdate>) -> Self {
2230 self.state = state.into_option();
2231 self
2232 }
2233
2234 #[must_use]
2240 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2241 self.meta = meta.into_option();
2242 self
2243 }
2244}
2245
2246#[cfg(feature = "unstable_session_status")]
2252#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2253#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2254#[serde(rename_all = "snake_case")]
2255#[non_exhaustive]
2256pub enum SessionStatus {
2257 Open,
2264 Saved,
2268 #[serde(untagged)]
2275 Other(String),
2276}
2277
2278#[serde_as]
2293#[skip_serializing_none]
2294#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2295#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2296#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = TOOL_CALL_CANCEL_METHOD_NAME)))]
2297#[serde(rename_all = "camelCase")]
2298#[non_exhaustive]
2299pub struct CancelToolCallRequest {
2300 pub session_id: SessionId,
2302 pub tool_call_id: ToolCallId,
2304 #[serde_as(deserialize_as = "DefaultOnError")]
2310 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2311 #[serde(default)]
2312 #[serde(rename = "_meta")]
2313 pub meta: Option<Meta>,
2314}
2315
2316impl CancelToolCallRequest {
2317 #[must_use]
2319 pub fn new(session_id: impl Into<SessionId>, tool_call_id: impl Into<ToolCallId>) -> Self {
2320 Self {
2321 session_id: session_id.into(),
2322 tool_call_id: tool_call_id.into(),
2323 meta: None,
2324 }
2325 }
2326
2327 #[must_use]
2333 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2334 self.meta = meta.into_option();
2335 self
2336 }
2337}
2338
2339crate::serde_util::default_on_null! {
2340 #[serde_as]
2342 #[skip_serializing_none]
2343 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2344 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
2345 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = TOOL_CALL_CANCEL_METHOD_NAME)))]
2346 #[serde(rename_all = "camelCase")]
2347 #[non_exhaustive]
2348 pub struct CancelToolCallResponse {
2349 #[serde_as(deserialize_as = "DefaultOnError")]
2355 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2356 #[serde(default)]
2357 #[serde(rename = "_meta")]
2358 pub meta: Option<Meta>,
2359 }
2360}
2361
2362impl CancelToolCallResponse {
2363 #[must_use]
2365 pub fn new() -> Self {
2366 Self::default()
2367 }
2368
2369 #[must_use]
2375 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2376 self.meta = meta.into_option();
2377 self
2378 }
2379}
2380
2381#[serde_as]
2397#[skip_serializing_none]
2398#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2399#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2400#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = TERMINAL_CANCEL_METHOD_NAME)))]
2401#[serde(rename_all = "camelCase")]
2402#[non_exhaustive]
2403pub struct CancelTerminalRequest {
2404 pub session_id: SessionId,
2406 pub terminal_id: TerminalId,
2408 #[serde_as(deserialize_as = "DefaultOnError")]
2414 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2415 #[serde(default)]
2416 #[serde(rename = "_meta")]
2417 pub meta: Option<Meta>,
2418}
2419
2420impl CancelTerminalRequest {
2421 #[must_use]
2423 pub fn new(session_id: impl Into<SessionId>, terminal_id: impl Into<TerminalId>) -> Self {
2424 Self {
2425 session_id: session_id.into(),
2426 terminal_id: terminal_id.into(),
2427 meta: None,
2428 }
2429 }
2430
2431 #[must_use]
2437 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2438 self.meta = meta.into_option();
2439 self
2440 }
2441}
2442
2443crate::serde_util::default_on_null! {
2444 #[serde_as]
2446 #[skip_serializing_none]
2447 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2448 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
2449 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = TERMINAL_CANCEL_METHOD_NAME)))]
2450 #[serde(rename_all = "camelCase")]
2451 #[non_exhaustive]
2452 pub struct CancelTerminalResponse {
2453 #[serde_as(deserialize_as = "DefaultOnError")]
2459 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2460 #[serde(default)]
2461 #[serde(rename = "_meta")]
2462 pub meta: Option<Meta>,
2463 }
2464}
2465
2466impl CancelTerminalResponse {
2467 #[must_use]
2469 pub fn new() -> Self {
2470 Self::default()
2471 }
2472
2473 #[must_use]
2479 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2480 self.meta = meta.into_option();
2481 self
2482 }
2483}
2484
2485#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2489#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
2490#[serde(transparent)]
2491#[from(Arc<str>, String, &str, &mut str, Box<str>, Cow<'_, str>)]
2492#[non_exhaustive]
2493pub struct SessionListCursor(pub Arc<str>);
2494
2495impl SessionListCursor {
2496 #[must_use]
2498 pub fn new(cursor: impl Into<Self>) -> Self {
2499 cursor.into()
2500 }
2501}
2502
2503impl AsRef<str> for SessionListCursor {
2504 fn as_ref(&self) -> &str {
2505 &self.0
2506 }
2507}
2508
2509impl From<&String> for SessionListCursor {
2510 fn from(cursor: &String) -> Self {
2511 Self(cursor.as_str().into())
2512 }
2513}
2514
2515macro_rules! impl_session_list_cursor_option_conversion {
2516 ($source:ty) => {
2517 impl IntoOption<SessionListCursor> for $source {
2518 fn into_option(self) -> Option<SessionListCursor> {
2519 Some(self.into())
2520 }
2521 }
2522 };
2523}
2524
2525impl_session_list_cursor_option_conversion!(Arc<str>);
2526impl_session_list_cursor_option_conversion!(String);
2527impl_session_list_cursor_option_conversion!(&str);
2528impl_session_list_cursor_option_conversion!(&mut str);
2529impl_session_list_cursor_option_conversion!(&String);
2530impl_session_list_cursor_option_conversion!(Box<str>);
2531impl_session_list_cursor_option_conversion!(Cow<'_, str>);
2532
2533crate::serde_util::default_on_null! {
2534 #[serde_as]
2536 #[skip_serializing_none]
2537 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2538 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
2539 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_LIST_METHOD_NAME)))]
2540 #[serde(rename_all = "camelCase")]
2541 #[non_exhaustive]
2542 pub struct ListSessionsRequest {
2543 #[serde(default)]
2550 pub cwd: Option<AbsolutePath>,
2551 #[serde(default)]
2555 pub cursor: Option<SessionListCursor>,
2556 #[cfg(feature = "unstable_session_archive")]
2566 #[serde(default)]
2567 pub archived: Option<SessionArchiveFilter>,
2568 #[serde_as(deserialize_as = "DefaultOnError")]
2574 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2575 #[serde(default)]
2576 #[serde(rename = "_meta")]
2577 pub meta: Option<Meta>,
2578 }
2579}
2580
2581impl ListSessionsRequest {
2582 #[must_use]
2584 pub fn new() -> Self {
2585 Self::default()
2586 }
2587
2588 #[must_use]
2590 pub fn cwd(mut self, cwd: impl IntoOption<AbsolutePath>) -> Self {
2591 self.cwd = cwd.into_option();
2592 self
2593 }
2594
2595 #[must_use]
2597 pub fn cursor(mut self, cursor: impl IntoOption<SessionListCursor>) -> Self {
2598 self.cursor = cursor.into_option();
2599 self
2600 }
2601
2602 #[cfg(feature = "unstable_session_archive")]
2608 #[must_use]
2609 pub fn archived(mut self, archived: impl IntoOption<SessionArchiveFilter>) -> Self {
2610 self.archived = archived.into_option();
2611 self
2612 }
2613
2614 #[must_use]
2620 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2621 self.meta = meta.into_option();
2622 self
2623 }
2624}
2625
2626#[serde_as]
2628#[skip_serializing_none]
2629#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2630#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2631#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_LIST_METHOD_NAME)))]
2632#[serde(rename_all = "camelCase")]
2633#[non_exhaustive]
2634pub struct ListSessionsResponse {
2635 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
2637 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
2638 pub sessions: Vec<SessionInfo>,
2639 #[serde(default)]
2643 pub next_cursor: Option<SessionListCursor>,
2644 #[serde_as(deserialize_as = "DefaultOnError")]
2650 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2651 #[serde(default)]
2652 #[serde(rename = "_meta")]
2653 pub meta: Option<Meta>,
2654}
2655
2656impl ListSessionsResponse {
2657 #[must_use]
2659 pub fn new(sessions: Vec<SessionInfo>) -> Self {
2660 Self {
2661 sessions,
2662 next_cursor: None,
2663 meta: None,
2664 }
2665 }
2666
2667 #[must_use]
2669 pub fn next_cursor(mut self, next_cursor: impl IntoOption<SessionListCursor>) -> Self {
2670 self.next_cursor = next_cursor.into_option();
2671 self
2672 }
2673
2674 #[must_use]
2680 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2681 self.meta = meta.into_option();
2682 self
2683 }
2684}
2685
2686#[serde_as]
2692#[skip_serializing_none]
2693#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2694#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2695#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_DELETE_METHOD_NAME)))]
2696#[serde(rename_all = "camelCase")]
2697#[non_exhaustive]
2698pub struct DeleteSessionRequest {
2699 pub session_id: SessionId,
2701 #[serde_as(deserialize_as = "DefaultOnError")]
2707 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2708 #[serde(default)]
2709 #[serde(rename = "_meta")]
2710 pub meta: Option<Meta>,
2711}
2712
2713impl DeleteSessionRequest {
2714 #[must_use]
2716 pub fn new(session_id: impl Into<SessionId>) -> Self {
2717 Self {
2718 session_id: session_id.into(),
2719 meta: None,
2720 }
2721 }
2722
2723 #[must_use]
2729 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2730 self.meta = meta.into_option();
2731 self
2732 }
2733}
2734
2735crate::serde_util::default_on_null! {
2736 #[serde_as]
2738 #[skip_serializing_none]
2739 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2740 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
2741 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_DELETE_METHOD_NAME)))]
2742 #[serde(rename_all = "camelCase")]
2743 #[non_exhaustive]
2744 pub struct DeleteSessionResponse {
2745 #[serde_as(deserialize_as = "DefaultOnError")]
2751 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2752 #[serde(default)]
2753 #[serde(rename = "_meta")]
2754 pub meta: Option<Meta>,
2755 }
2756}
2757
2758impl DeleteSessionResponse {
2759 #[must_use]
2761 pub fn new() -> Self {
2762 Self::default()
2763 }
2764
2765 #[must_use]
2771 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2772 self.meta = meta.into_option();
2773 self
2774 }
2775}
2776
2777#[cfg(feature = "unstable_session_archive")]
2793#[serde_as]
2794#[skip_serializing_none]
2795#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2796#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2797#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_ARCHIVE_METHOD_NAME)))]
2798#[serde(rename_all = "camelCase")]
2799#[non_exhaustive]
2800pub struct ArchiveSessionRequest {
2801 pub session_id: SessionId,
2803 #[serde_as(deserialize_as = "DefaultOnError")]
2809 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2810 #[serde(default)]
2811 #[serde(rename = "_meta")]
2812 pub meta: Option<Meta>,
2813}
2814
2815#[cfg(feature = "unstable_session_archive")]
2816impl ArchiveSessionRequest {
2817 #[must_use]
2819 pub fn new(session_id: impl Into<SessionId>) -> Self {
2820 Self {
2821 session_id: session_id.into(),
2822 meta: None,
2823 }
2824 }
2825
2826 #[must_use]
2832 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2833 self.meta = meta.into_option();
2834 self
2835 }
2836}
2837
2838#[cfg(feature = "unstable_session_archive")]
2839crate::serde_util::default_on_null! {
2840 #[serde_as]
2846 #[skip_serializing_none]
2847 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2848 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
2849 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_ARCHIVE_METHOD_NAME)))]
2850 #[serde(rename_all = "camelCase")]
2851 #[non_exhaustive]
2852 pub struct ArchiveSessionResponse {
2853 #[serde_as(deserialize_as = "DefaultOnError")]
2859 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2860 #[serde(default)]
2861 #[serde(rename = "_meta")]
2862 pub meta: Option<Meta>,
2863 }
2864}
2865
2866#[cfg(feature = "unstable_session_archive")]
2867impl ArchiveSessionResponse {
2868 #[must_use]
2870 pub fn new() -> Self {
2871 Self::default()
2872 }
2873
2874 #[must_use]
2880 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2881 self.meta = meta.into_option();
2882 self
2883 }
2884}
2885
2886#[cfg(feature = "unstable_session_archive")]
2901#[serde_as]
2902#[skip_serializing_none]
2903#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2904#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2905#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_UNARCHIVE_METHOD_NAME)))]
2906#[serde(rename_all = "camelCase")]
2907#[non_exhaustive]
2908pub struct UnarchiveSessionRequest {
2909 pub session_id: SessionId,
2911 #[serde_as(deserialize_as = "DefaultOnError")]
2917 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2918 #[serde(default)]
2919 #[serde(rename = "_meta")]
2920 pub meta: Option<Meta>,
2921}
2922
2923#[cfg(feature = "unstable_session_archive")]
2924impl UnarchiveSessionRequest {
2925 #[must_use]
2927 pub fn new(session_id: impl Into<SessionId>) -> Self {
2928 Self {
2929 session_id: session_id.into(),
2930 meta: None,
2931 }
2932 }
2933
2934 #[must_use]
2940 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2941 self.meta = meta.into_option();
2942 self
2943 }
2944}
2945
2946#[cfg(feature = "unstable_session_archive")]
2947crate::serde_util::default_on_null! {
2948 #[serde_as]
2954 #[skip_serializing_none]
2955 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2956 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
2957 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_UNARCHIVE_METHOD_NAME)))]
2958 #[serde(rename_all = "camelCase")]
2959 #[non_exhaustive]
2960 pub struct UnarchiveSessionResponse {
2961 #[serde_as(deserialize_as = "DefaultOnError")]
2967 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2968 #[serde(default)]
2969 #[serde(rename = "_meta")]
2970 pub meta: Option<Meta>,
2971 }
2972}
2973
2974#[cfg(feature = "unstable_session_archive")]
2975impl UnarchiveSessionResponse {
2976 #[must_use]
2978 pub fn new() -> Self {
2979 Self::default()
2980 }
2981
2982 #[must_use]
2988 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2989 self.meta = meta.into_option();
2990 self
2991 }
2992}
2993
2994#[cfg(feature = "unstable_session_archive")]
3003#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3004#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
3005#[serde(rename_all = "snake_case")]
3006#[non_exhaustive]
3007pub enum SessionArchiveFilter {
3008 #[default]
3010 Exclude,
3011 Include,
3013 Only,
3015 #[serde(untagged)]
3021 Other(String),
3022}
3023
3024#[serde_as]
3026#[skip_serializing_none]
3027#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3028#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3029#[serde(rename_all = "camelCase")]
3030#[non_exhaustive]
3031pub struct SessionInfo {
3032 pub session_id: SessionId,
3034 pub cwd: AbsolutePath,
3036 #[serde_as(deserialize_as = "DefaultOnNull")]
3042 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
3043 #[serde(default, skip_serializing_if = "Vec::is_empty")]
3044 pub additional_directories: Vec<AbsolutePath>,
3045
3046 #[serde_as(deserialize_as = "DefaultOnError")]
3050 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3051 #[serde(default)]
3052 pub title: Option<String>,
3053 #[serde_as(deserialize_as = "DefaultOnError")]
3057 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "format" = "date-time")))]
3058 #[serde(default)]
3059 pub updated_at: Option<String>,
3060 #[cfg(feature = "unstable_session_archive")]
3069 #[serde_as(deserialize_as = "DefaultOnError")]
3070 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3071 #[serde(default)]
3072 pub archived: Option<bool>,
3073 #[serde_as(deserialize_as = "DefaultOnError")]
3079 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3080 #[serde(default)]
3081 #[serde(rename = "_meta")]
3082 pub meta: Option<Meta>,
3083}
3084
3085impl SessionInfo {
3086 #[must_use]
3088 pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<AbsolutePath>) -> Self {
3089 Self {
3090 session_id: session_id.into(),
3091 cwd: cwd.into(),
3092 additional_directories: vec![],
3093 title: None,
3094 updated_at: None,
3095 #[cfg(feature = "unstable_session_archive")]
3096 archived: None,
3097 meta: None,
3098 }
3099 }
3100
3101 #[must_use]
3103 pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
3104 where
3105 I: IntoIterator<Item = P>,
3106 P: Into<AbsolutePath>,
3107 {
3108 self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
3109 self
3110 }
3111
3112 #[must_use]
3114 pub fn title(mut self, title: impl IntoOption<String>) -> Self {
3115 self.title = title.into_option();
3116 self
3117 }
3118
3119 #[must_use]
3121 pub fn updated_at(mut self, updated_at: impl IntoOption<String>) -> Self {
3122 self.updated_at = updated_at.into_option();
3123 self
3124 }
3125
3126 #[cfg(feature = "unstable_session_archive")]
3132 #[must_use]
3133 pub fn archived(mut self, archived: impl IntoOption<bool>) -> Self {
3134 self.archived = archived.into_option();
3135 self
3136 }
3137
3138 #[must_use]
3144 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3145 self.meta = meta.into_option();
3146 self
3147 }
3148}
3149
3150#[cfg(feature = "unstable_session_set_title")]
3166#[serde_as]
3167#[skip_serializing_none]
3168#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3169#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3170#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_TITLE_METHOD_NAME)))]
3171#[serde(rename_all = "camelCase")]
3172#[non_exhaustive]
3173pub struct SetSessionTitleRequest {
3174 pub session_id: SessionId,
3176 pub title: String,
3183 #[serde_as(deserialize_as = "DefaultOnError")]
3189 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3190 #[serde(default)]
3191 #[serde(rename = "_meta")]
3192 pub meta: Option<Meta>,
3193}
3194
3195#[cfg(feature = "unstable_session_set_title")]
3196impl SetSessionTitleRequest {
3197 #[must_use]
3199 pub fn new(session_id: impl Into<SessionId>, title: impl Into<String>) -> Self {
3200 Self {
3201 session_id: session_id.into(),
3202 title: title.into(),
3203 meta: None,
3204 }
3205 }
3206
3207 #[must_use]
3213 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3214 self.meta = meta.into_option();
3215 self
3216 }
3217}
3218
3219#[cfg(feature = "unstable_session_set_title")]
3225#[serde_as]
3226#[skip_serializing_none]
3227#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3228#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3229#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_TITLE_METHOD_NAME)))]
3230#[serde(rename_all = "camelCase")]
3231#[non_exhaustive]
3232pub struct SetSessionTitleResponse {
3233 pub title: String,
3238 #[serde_as(deserialize_as = "DefaultOnError")]
3244 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3245 #[serde(default)]
3246 #[serde(rename = "_meta")]
3247 pub meta: Option<Meta>,
3248}
3249
3250#[cfg(feature = "unstable_session_set_title")]
3251impl SetSessionTitleResponse {
3252 #[must_use]
3254 pub fn new(title: impl Into<String>) -> Self {
3255 Self {
3256 title: title.into(),
3257 meta: None,
3258 }
3259 }
3260
3261 #[must_use]
3267 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3268 self.meta = meta.into_option();
3269 self
3270 }
3271}
3272
3273#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3277#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, From, Display)]
3278#[serde(transparent)]
3279#[from(forward)]
3280#[non_exhaustive]
3281pub struct SessionConfigId(pub Arc<str>);
3282
3283impl SessionConfigId {
3284 #[must_use]
3286 pub fn new(id: impl Into<Self>) -> Self {
3287 id.into()
3288 }
3289}
3290
3291#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3293#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, From, Display)]
3294#[serde(transparent)]
3295#[from(forward)]
3296#[non_exhaustive]
3297pub struct SessionConfigValueId(pub Arc<str>);
3298
3299impl SessionConfigValueId {
3300 #[must_use]
3302 pub fn new(id: impl Into<Self>) -> Self {
3303 id.into()
3304 }
3305}
3306
3307#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3309#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, From, Display)]
3310#[serde(transparent)]
3311#[from(forward)]
3312#[non_exhaustive]
3313pub struct SessionConfigGroupId(pub Arc<str>);
3314
3315impl SessionConfigGroupId {
3316 #[must_use]
3318 pub fn new(id: impl Into<Self>) -> Self {
3319 id.into()
3320 }
3321}
3322
3323#[serde_as]
3325#[skip_serializing_none]
3326#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3327#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3328#[serde(rename_all = "camelCase")]
3329#[non_exhaustive]
3330pub struct SessionConfigSelectOption {
3331 pub value: SessionConfigValueId,
3333 pub name: String,
3335 #[serde_as(deserialize_as = "DefaultOnError")]
3339 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3340 #[serde(default)]
3341 pub description: Option<String>,
3342 #[serde_as(deserialize_as = "DefaultOnError")]
3348 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3349 #[serde(default)]
3350 #[serde(rename = "_meta")]
3351 pub meta: Option<Meta>,
3352}
3353
3354impl SessionConfigSelectOption {
3355 #[must_use]
3357 pub fn new(value: impl Into<SessionConfigValueId>, name: impl Into<String>) -> Self {
3358 Self {
3359 value: value.into(),
3360 name: name.into(),
3361 description: None,
3362 meta: None,
3363 }
3364 }
3365
3366 #[must_use]
3368 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
3369 self.description = description.into_option();
3370 self
3371 }
3372
3373 #[must_use]
3379 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3380 self.meta = meta.into_option();
3381 self
3382 }
3383}
3384
3385#[serde_as]
3387#[skip_serializing_none]
3388#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3389#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3390#[serde(rename_all = "camelCase")]
3391#[non_exhaustive]
3392pub struct SessionConfigSelectGroup {
3393 pub group_id: SessionConfigGroupId,
3395 pub name: String,
3397 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
3399 #[cfg_attr(feature = "schemars", schemars(length(min = 1), extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
3400 pub options: Vec<SessionConfigSelectOption>,
3401 #[serde_as(deserialize_as = "DefaultOnError")]
3407 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3408 #[serde(default)]
3409 #[serde(rename = "_meta")]
3410 pub meta: Option<Meta>,
3411}
3412
3413impl SessionConfigSelectGroup {
3414 #[must_use]
3416 pub fn new(
3417 group_id: impl Into<SessionConfigGroupId>,
3418 name: impl Into<String>,
3419 options: Vec<SessionConfigSelectOption>,
3420 ) -> Self {
3421 Self {
3422 group_id: group_id.into(),
3423 name: name.into(),
3424 options,
3425 meta: None,
3426 }
3427 }
3428
3429 #[must_use]
3435 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3436 self.meta = meta.into_option();
3437 self
3438 }
3439}
3440
3441#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3443#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3444#[serde(untagged)]
3445#[non_exhaustive]
3446pub enum SessionConfigSelectOptions {
3447 Ungrouped(
3449 #[cfg_attr(feature = "schemars", schemars(length(min = 1)))] Vec<SessionConfigSelectOption>,
3450 ),
3451 Grouped(
3453 #[cfg_attr(feature = "schemars", schemars(length(min = 1)))] Vec<SessionConfigSelectGroup>,
3454 ),
3455}
3456
3457impl From<Vec<SessionConfigSelectOption>> for SessionConfigSelectOptions {
3458 fn from(options: Vec<SessionConfigSelectOption>) -> Self {
3459 SessionConfigSelectOptions::Ungrouped(options)
3460 }
3461}
3462
3463impl From<Vec<SessionConfigSelectGroup>> for SessionConfigSelectOptions {
3464 fn from(groups: Vec<SessionConfigSelectGroup>) -> Self {
3465 SessionConfigSelectOptions::Grouped(groups)
3466 }
3467}
3468
3469#[skip_serializing_none]
3471#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3472#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3473#[serde(rename_all = "camelCase")]
3474#[non_exhaustive]
3475pub struct SessionConfigSelect {
3476 pub current_value: SessionConfigValueId,
3478 pub options: SessionConfigSelectOptions,
3481}
3482
3483impl SessionConfigSelect {
3484 #[must_use]
3486 pub fn new(
3487 current_value: impl Into<SessionConfigValueId>,
3488 options: impl Into<SessionConfigSelectOptions>,
3489 ) -> Self {
3490 Self {
3491 current_value: current_value.into(),
3492 options: options.into(),
3493 }
3494 }
3495}
3496
3497#[skip_serializing_none]
3499#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3500#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3501#[serde(rename_all = "camelCase")]
3502#[non_exhaustive]
3503pub struct SessionConfigBoolean {
3504 pub current_value: bool,
3506}
3507
3508impl SessionConfigBoolean {
3509 #[must_use]
3511 pub fn new(current_value: bool) -> Self {
3512 Self { current_value }
3513 }
3514}
3515
3516#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3526#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3527#[serde(rename_all = "snake_case")]
3528#[non_exhaustive]
3529pub enum SessionConfigOptionCategory {
3530 Mode,
3532 Model,
3534 ModelConfig,
3536 ThoughtLevel,
3538 #[serde(untagged)]
3544 Other(String),
3545}
3546
3547#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3549#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3550#[serde(tag = "type", rename_all = "snake_case")]
3551#[non_exhaustive]
3552pub enum SessionConfigKind {
3553 Select(SessionConfigSelect),
3555 Boolean(SessionConfigBoolean),
3557 #[serde(untagged)]
3567 Other(OtherSessionConfigKind),
3568}
3569
3570#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3572#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
3573#[cfg_attr(feature = "schemars", schemars(inline))]
3574#[cfg_attr(feature = "schemars", schemars(transform = other_session_config_kind_schema))]
3575#[serde(rename_all = "camelCase")]
3576#[non_exhaustive]
3577pub struct OtherSessionConfigKind {
3578 #[serde(rename = "type")]
3584 pub type_: String,
3585 #[serde(flatten)]
3587 pub fields: BTreeMap<String, serde_json::Value>,
3588}
3589
3590impl OtherSessionConfigKind {
3591 #[must_use]
3593 pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
3594 fields.remove("type");
3595 fields.remove("_meta");
3596 Self {
3597 type_: type_.into(),
3598 fields,
3599 }
3600 }
3601}
3602
3603impl<'de> Deserialize<'de> for OtherSessionConfigKind {
3604 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3605 where
3606 D: serde::Deserializer<'de>,
3607 {
3608 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
3609 let type_ = fields
3610 .remove("type")
3611 .ok_or_else(|| serde::de::Error::missing_field("type"))?;
3612 let serde_json::Value::String(type_) = type_ else {
3613 return Err(serde::de::Error::custom("`type` must be a string"));
3614 };
3615
3616 if is_known_session_config_kind_type(&type_) {
3617 return Err(serde::de::Error::custom(format!(
3618 "known session configuration option `{type_}` did not match its schema"
3619 )));
3620 }
3621
3622 Ok(Self { type_, fields })
3623 }
3624}
3625
3626fn is_known_session_config_kind_type(type_: &str) -> bool {
3627 matches!(type_, "select" | "boolean")
3628}
3629
3630#[cfg(feature = "schemars")]
3631fn other_session_config_kind_schema(schema: &mut Schema) {
3632 super::schema_util::reject_known_string_discriminators(schema, "type", &["select", "boolean"]);
3633}
3634
3635#[serde_as]
3637#[skip_serializing_none]
3638#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3639#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3640#[serde(rename_all = "camelCase")]
3641#[non_exhaustive]
3642pub struct SessionConfigOption {
3643 pub config_id: SessionConfigId,
3645 pub name: String,
3647 #[serde_as(deserialize_as = "DefaultOnError")]
3651 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3652 #[serde(default)]
3653 pub description: Option<String>,
3654 #[serde_as(deserialize_as = "DefaultOnError")]
3658 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3659 #[serde(default)]
3660 pub category: Option<SessionConfigOptionCategory>,
3661 #[serde(flatten)]
3663 pub kind: SessionConfigKind,
3664 #[serde_as(deserialize_as = "DefaultOnError")]
3670 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3671 #[serde(default)]
3672 #[serde(rename = "_meta")]
3673 pub meta: Option<Meta>,
3674}
3675
3676impl SessionConfigOption {
3677 #[must_use]
3679 pub fn new(
3680 config_id: impl Into<SessionConfigId>,
3681 name: impl Into<String>,
3682 kind: SessionConfigKind,
3683 ) -> Self {
3684 Self {
3685 config_id: config_id.into(),
3686 name: name.into(),
3687 description: None,
3688 category: None,
3689 kind,
3690 meta: None,
3691 }
3692 }
3693
3694 #[must_use]
3696 pub fn select(
3697 config_id: impl Into<SessionConfigId>,
3698 name: impl Into<String>,
3699 current_value: impl Into<SessionConfigValueId>,
3700 options: impl Into<SessionConfigSelectOptions>,
3701 ) -> Self {
3702 Self::new(
3703 config_id,
3704 name,
3705 SessionConfigKind::Select(SessionConfigSelect::new(current_value, options)),
3706 )
3707 }
3708
3709 #[must_use]
3711 pub fn boolean(
3712 config_id: impl Into<SessionConfigId>,
3713 name: impl Into<String>,
3714 current_value: bool,
3715 ) -> Self {
3716 Self::new(
3717 config_id,
3718 name,
3719 SessionConfigKind::Boolean(SessionConfigBoolean::new(current_value)),
3720 )
3721 }
3722
3723 #[must_use]
3725 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
3726 self.description = description.into_option();
3727 self
3728 }
3729
3730 #[must_use]
3732 pub fn category(mut self, category: impl IntoOption<SessionConfigOptionCategory>) -> Self {
3733 self.category = category.into_option();
3734 self
3735 }
3736
3737 #[must_use]
3743 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3744 self.meta = meta.into_option();
3745 self
3746 }
3747}
3748
3749#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3758#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
3759#[serde(tag = "type", rename_all = "snake_case")]
3760#[non_exhaustive]
3761pub enum SessionConfigOptionValue {
3762 Id {
3764 value: SessionConfigValueId,
3766 },
3767 Boolean {
3769 value: bool,
3771 },
3772 #[serde(untagged)]
3781 Other(OtherSessionConfigOptionValue),
3782}
3783
3784#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3786#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
3787#[cfg_attr(feature = "schemars", schemars(inline))]
3788#[cfg_attr(feature = "schemars", schemars(transform = other_session_config_option_value_schema))]
3789#[serde(rename_all = "camelCase")]
3790#[non_exhaustive]
3791pub struct OtherSessionConfigOptionValue {
3792 #[serde(rename = "type")]
3798 pub type_: String,
3799 pub value: serde_json::Value,
3801 #[serde(flatten)]
3803 pub fields: BTreeMap<String, serde_json::Value>,
3804}
3805
3806impl OtherSessionConfigOptionValue {
3807 #[must_use]
3809 pub fn new(
3810 type_: impl Into<String>,
3811 value: serde_json::Value,
3812 mut fields: BTreeMap<String, serde_json::Value>,
3813 ) -> Self {
3814 fields.remove("type");
3815 fields.remove("value");
3816 fields.remove("_meta");
3817 Self {
3818 type_: type_.into(),
3819 value,
3820 fields,
3821 }
3822 }
3823}
3824
3825impl<'de> Deserialize<'de> for OtherSessionConfigOptionValue {
3826 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3827 where
3828 D: serde::Deserializer<'de>,
3829 {
3830 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
3831 let type_ = fields
3832 .remove("type")
3833 .ok_or_else(|| serde::de::Error::missing_field("type"))?;
3834 let serde_json::Value::String(type_) = type_ else {
3835 return Err(serde::de::Error::custom("`type` must be a string"));
3836 };
3837
3838 if is_known_session_config_option_value_type(&type_) {
3839 return Err(serde::de::Error::custom(format!(
3840 "known session configuration option value `{type_}` did not match its schema"
3841 )));
3842 }
3843
3844 let value = fields
3845 .remove("value")
3846 .ok_or_else(|| serde::de::Error::missing_field("value"))?;
3847
3848 Ok(Self {
3849 type_,
3850 value,
3851 fields,
3852 })
3853 }
3854}
3855
3856impl<'de> Deserialize<'de> for SessionConfigOptionValue {
3857 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3858 where
3859 D: serde::Deserializer<'de>,
3860 {
3861 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
3862 let type_ = fields.remove("type");
3863 let value = fields
3864 .remove("value")
3865 .ok_or_else(|| serde::de::Error::missing_field("value"))?;
3866
3867 let type_ = type_.ok_or_else(|| serde::de::Error::missing_field("type"))?;
3868
3869 let serde_json::Value::String(type_) = type_ else {
3870 return Err(serde::de::Error::custom("`type` must be a string"));
3871 };
3872
3873 match type_.as_str() {
3874 "id" => {
3875 let value = serde_json::from_value(value).map_err(|error| {
3876 serde::de::Error::custom(format!(
3877 "`value` must be a string for `type: id`: {error}"
3878 ))
3879 })?;
3880 Ok(Self::Id { value })
3881 }
3882 "boolean" => {
3883 let value = serde_json::from_value(value).map_err(|error| {
3884 serde::de::Error::custom(format!(
3885 "`value` must be a boolean for `type: boolean`: {error}"
3886 ))
3887 })?;
3888 Ok(Self::Boolean { value })
3889 }
3890 _ => Ok(Self::Other(OtherSessionConfigOptionValue {
3891 type_,
3892 value,
3893 fields,
3894 })),
3895 }
3896 }
3897}
3898
3899fn is_known_session_config_option_value_type(type_: &str) -> bool {
3900 matches!(type_, "id" | "boolean")
3901}
3902
3903#[cfg(feature = "schemars")]
3904fn other_session_config_option_value_schema(schema: &mut Schema) {
3905 super::schema_util::reject_known_string_discriminators(schema, "type", &["id", "boolean"]);
3906}
3907
3908impl SessionConfigOptionValue {
3909 #[must_use]
3911 pub fn id(id: impl Into<SessionConfigValueId>) -> Self {
3912 Self::Id { value: id.into() }
3913 }
3914
3915 #[must_use]
3917 pub fn boolean(val: bool) -> Self {
3918 Self::Boolean { value: val }
3919 }
3920
3921 #[must_use]
3924 pub fn as_id(&self) -> Option<&SessionConfigValueId> {
3925 match self {
3926 Self::Id { value } => Some(value),
3927 _ => None,
3928 }
3929 }
3930
3931 #[must_use]
3933 pub fn as_bool(&self) -> Option<bool> {
3934 match self {
3935 Self::Boolean { value } => Some(*value),
3936 _ => None,
3937 }
3938 }
3939}
3940
3941impl From<SessionConfigValueId> for SessionConfigOptionValue {
3942 fn from(value: SessionConfigValueId) -> Self {
3943 Self::Id { value }
3944 }
3945}
3946
3947impl From<bool> for SessionConfigOptionValue {
3948 fn from(value: bool) -> Self {
3949 Self::Boolean { value }
3950 }
3951}
3952
3953impl From<&str> for SessionConfigOptionValue {
3954 fn from(value: &str) -> Self {
3955 Self::Id {
3956 value: SessionConfigValueId::new(value),
3957 }
3958 }
3959}
3960
3961#[serde_as]
3969#[skip_serializing_none]
3970#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3971#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3972#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_CONFIG_OPTION_METHOD_NAME)))]
3973#[serde(rename_all = "camelCase")]
3974#[non_exhaustive]
3975pub struct SetSessionConfigOptionRequest {
3976 pub session_id: SessionId,
3978 pub config_id: SessionConfigId,
3980 #[serde(flatten)]
3984 pub value: SessionConfigOptionValue,
3985 #[serde_as(deserialize_as = "DefaultOnError")]
3991 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3992 #[serde(default)]
3993 #[serde(rename = "_meta")]
3994 pub meta: Option<Meta>,
3995}
3996
3997impl SetSessionConfigOptionRequest {
3998 #[must_use]
4000 pub fn new(
4001 session_id: impl Into<SessionId>,
4002 config_id: impl Into<SessionConfigId>,
4003 value: impl Into<SessionConfigOptionValue>,
4004 ) -> Self {
4005 Self {
4006 session_id: session_id.into(),
4007 config_id: config_id.into(),
4008 value: value.into(),
4009 meta: None,
4010 }
4011 }
4012
4013 #[must_use]
4019 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4020 self.meta = meta.into_option();
4021 self
4022 }
4023}
4024
4025#[serde_as]
4027#[skip_serializing_none]
4028#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4029#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4030#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_CONFIG_OPTION_METHOD_NAME)))]
4031#[serde(rename_all = "camelCase")]
4032#[non_exhaustive]
4033pub struct SetSessionConfigOptionResponse {
4034 #[serde_as(deserialize_as = "VecSkipError<_>")]
4036 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-skip-invalid-items" = true)))]
4037 pub config_options: Vec<SessionConfigOption>,
4038 #[serde_as(deserialize_as = "DefaultOnError")]
4044 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4045 #[serde(default)]
4046 #[serde(rename = "_meta")]
4047 pub meta: Option<Meta>,
4048}
4049
4050impl SetSessionConfigOptionResponse {
4051 #[must_use]
4053 pub fn new(config_options: Vec<SessionConfigOption>) -> Self {
4054 Self {
4055 config_options,
4056 meta: None,
4057 }
4058 }
4059
4060 #[must_use]
4066 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4067 self.meta = meta.into_option();
4068 self
4069 }
4070}
4071
4072#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4081#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4082#[serde(tag = "type", rename_all = "snake_case")]
4083#[non_exhaustive]
4084pub enum McpServer {
4085 Http(McpServerHttp),
4089 #[cfg(feature = "unstable_mcp_over_acp")]
4098 Acp(McpServerAcp),
4099 Stdio(McpServerStdio),
4103 #[serde(untagged)]
4114 Other(OtherMcpServer),
4115}
4116
4117#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4119#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
4120#[cfg_attr(feature = "schemars", schemars(inline))]
4121#[cfg_attr(feature = "schemars", schemars(transform = other_mcp_server_schema))]
4122#[serde(rename_all = "camelCase")]
4123#[non_exhaustive]
4124pub struct OtherMcpServer {
4125 #[serde(rename = "type")]
4131 pub type_: String,
4132 #[serde(flatten)]
4134 pub fields: BTreeMap<String, serde_json::Value>,
4135}
4136
4137impl OtherMcpServer {
4138 #[must_use]
4140 pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
4141 fields.remove("type");
4142 Self {
4143 type_: type_.into(),
4144 fields,
4145 }
4146 }
4147}
4148
4149impl<'de> Deserialize<'de> for OtherMcpServer {
4150 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
4151 where
4152 D: serde::Deserializer<'de>,
4153 {
4154 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
4155 let type_ = fields
4156 .remove("type")
4157 .ok_or_else(|| serde::de::Error::missing_field("type"))?;
4158 let serde_json::Value::String(type_) = type_ else {
4159 return Err(serde::de::Error::custom("`type` must be a string"));
4160 };
4161
4162 if is_known_mcp_server_type(&type_) {
4163 return Err(serde::de::Error::custom(format!(
4164 "known MCP server transport `{type_}` did not match its schema"
4165 )));
4166 }
4167
4168 Ok(Self { type_, fields })
4169 }
4170}
4171
4172fn is_known_mcp_server_type(type_: &str) -> bool {
4173 match type_ {
4174 "http" | "stdio" => true,
4175 #[cfg(feature = "unstable_mcp_over_acp")]
4176 "acp" => true,
4177 _ => false,
4178 }
4179}
4180
4181#[cfg(feature = "schemars")]
4182fn other_mcp_server_schema(schema: &mut Schema) {
4183 super::schema_util::reject_known_string_discriminators(
4184 schema,
4185 "type",
4186 &[
4187 "http",
4188 "stdio",
4189 #[cfg(feature = "unstable_mcp_over_acp")]
4190 "acp",
4191 ],
4192 );
4193}
4194
4195#[serde_as]
4197#[skip_serializing_none]
4198#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4199#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4200#[serde(rename_all = "camelCase")]
4201#[non_exhaustive]
4202pub struct McpServerHttp {
4203 pub name: String,
4205 #[cfg_attr(feature = "schemars", schemars(url))]
4207 pub url: String,
4208 #[serde_as(deserialize_as = "DefaultOnNull")]
4211 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
4212 #[serde(default, skip_serializing_if = "Vec::is_empty")]
4213 pub headers: Vec<HttpHeader>,
4214 #[serde_as(deserialize_as = "DefaultOnError")]
4220 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4221 #[serde(default)]
4222 #[serde(rename = "_meta")]
4223 pub meta: Option<Meta>,
4224}
4225
4226impl McpServerHttp {
4227 #[must_use]
4229 pub fn new(name: impl Into<String>, url: impl Into<String>) -> Self {
4230 Self {
4231 name: name.into(),
4232 url: url.into(),
4233 headers: Vec::new(),
4234 meta: None,
4235 }
4236 }
4237
4238 #[must_use]
4241 pub fn headers(mut self, headers: Vec<HttpHeader>) -> Self {
4242 self.headers = headers;
4243 self
4244 }
4245
4246 #[must_use]
4252 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4253 self.meta = meta.into_option();
4254 self
4255 }
4256}
4257
4258#[cfg(feature = "unstable_mcp_over_acp")]
4267#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4268#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
4269#[serde(transparent)]
4270#[from(forward)]
4271#[non_exhaustive]
4272pub struct McpServerAcpId(pub Arc<str>);
4273
4274#[cfg(feature = "unstable_mcp_over_acp")]
4275impl McpServerAcpId {
4276 #[must_use]
4278 pub fn new(id: impl Into<Self>) -> Self {
4279 id.into()
4280 }
4281}
4282
4283#[serde_as]
4292#[skip_serializing_none]
4293#[cfg(feature = "unstable_mcp_over_acp")]
4294#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4295#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4296#[serde(rename_all = "camelCase")]
4297#[non_exhaustive]
4298pub struct McpServerAcp {
4299 pub name: String,
4301 pub server_id: McpServerAcpId,
4306 #[serde_as(deserialize_as = "DefaultOnError")]
4312 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4313 #[serde(default)]
4314 #[serde(rename = "_meta")]
4315 pub meta: Option<Meta>,
4316}
4317
4318#[cfg(feature = "unstable_mcp_over_acp")]
4319impl McpServerAcp {
4320 #[must_use]
4322 pub fn new(name: impl Into<String>, server_id: impl Into<McpServerAcpId>) -> Self {
4323 Self {
4324 name: name.into(),
4325 server_id: server_id.into(),
4326 meta: None,
4327 }
4328 }
4329
4330 #[must_use]
4336 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4337 self.meta = meta.into_option();
4338 self
4339 }
4340}
4341
4342#[serde_as]
4344#[skip_serializing_none]
4345#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4346#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4347#[serde(rename_all = "camelCase")]
4348#[non_exhaustive]
4349pub struct McpServerStdio {
4350 pub name: String,
4352 pub command: AbsolutePath,
4354 #[serde_as(deserialize_as = "DefaultOnNull")]
4356 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
4357 #[serde(default, skip_serializing_if = "Vec::is_empty")]
4358 pub args: Vec<String>,
4359 #[serde_as(deserialize_as = "DefaultOnNull")]
4362 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
4363 #[serde(default, skip_serializing_if = "Vec::is_empty")]
4364 pub env: Vec<EnvVariable>,
4365 #[serde_as(deserialize_as = "DefaultOnError")]
4371 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4372 #[serde(default)]
4373 #[serde(rename = "_meta")]
4374 pub meta: Option<Meta>,
4375}
4376
4377impl McpServerStdio {
4378 #[must_use]
4380 pub fn new(name: impl Into<String>, command: impl Into<AbsolutePath>) -> Self {
4381 Self {
4382 name: name.into(),
4383 command: command.into(),
4384 args: Vec::new(),
4385 env: Vec::new(),
4386 meta: None,
4387 }
4388 }
4389
4390 #[must_use]
4392 pub fn args(mut self, args: Vec<String>) -> Self {
4393 self.args = args;
4394 self
4395 }
4396
4397 #[must_use]
4400 pub fn env(mut self, env: Vec<EnvVariable>) -> Self {
4401 self.env = env;
4402 self
4403 }
4404
4405 #[must_use]
4411 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4412 self.meta = meta.into_option();
4413 self
4414 }
4415}
4416
4417#[serde_as]
4419#[skip_serializing_none]
4420#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4421#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4422#[serde(rename_all = "camelCase")]
4423#[non_exhaustive]
4424pub struct EnvVariable {
4425 pub name: String,
4427 pub value: String,
4429 #[serde_as(deserialize_as = "DefaultOnError")]
4435 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4436 #[serde(default)]
4437 #[serde(rename = "_meta")]
4438 pub meta: Option<Meta>,
4439}
4440
4441impl EnvVariable {
4442 #[must_use]
4444 pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
4445 Self {
4446 name: name.into(),
4447 value: value.into(),
4448 meta: None,
4449 }
4450 }
4451
4452 #[must_use]
4458 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4459 self.meta = meta.into_option();
4460 self
4461 }
4462}
4463
4464#[serde_as]
4466#[skip_serializing_none]
4467#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4468#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4469#[serde(rename_all = "camelCase")]
4470#[non_exhaustive]
4471pub struct HttpHeader {
4472 pub name: String,
4474 pub value: String,
4476 #[serde_as(deserialize_as = "DefaultOnError")]
4482 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4483 #[serde(default)]
4484 #[serde(rename = "_meta")]
4485 pub meta: Option<Meta>,
4486}
4487
4488impl HttpHeader {
4489 #[must_use]
4491 pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
4492 Self {
4493 name: name.into(),
4494 value: value.into(),
4495 meta: None,
4496 }
4497 }
4498
4499 #[must_use]
4505 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4506 self.meta = meta.into_option();
4507 self
4508 }
4509}
4510
4511#[serde_as]
4520#[skip_serializing_none]
4521#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4522#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4523#[serde(rename_all = "camelCase")]
4524#[non_exhaustive]
4525pub struct SystemPrompt {
4526 pub mode: SystemPromptMode,
4531 pub content: Vec<ContentBlock>,
4541 #[serde_as(deserialize_as = "DefaultOnError")]
4547 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4548 #[serde(default)]
4549 #[serde(rename = "_meta")]
4550 pub meta: Option<Meta>,
4551}
4552
4553impl SystemPrompt {
4554 #[must_use]
4559 pub fn new(content: Vec<ContentBlock>) -> Self {
4560 Self {
4561 mode: SystemPromptMode::default(),
4562 content,
4563 meta: None,
4564 }
4565 }
4566
4567 #[must_use]
4569 pub fn mode(mut self, mode: SystemPromptMode) -> Self {
4570 self.mode = mode;
4571 self
4572 }
4573
4574 #[must_use]
4580 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4581 self.meta = meta.into_option();
4582 self
4583 }
4584}
4585
4586#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4592#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
4593#[serde(rename_all = "snake_case")]
4594#[non_exhaustive]
4595pub enum SystemPromptMode {
4596 #[default]
4603 Append,
4604 Override,
4613 #[serde(untagged)]
4619 Other(String),
4620}
4621
4622#[serde_as]
4630#[skip_serializing_none]
4631#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4632#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4633#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_PROMPT_METHOD_NAME)))]
4634#[serde(rename_all = "camelCase")]
4635#[non_exhaustive]
4636pub struct PromptRequest {
4637 pub session_id: SessionId,
4639 #[cfg_attr(feature = "schemars", schemars(length(min = 1)))]
4656 pub prompt: Vec<ContentBlock>,
4657 #[serde_as(deserialize_as = "DefaultOnError")]
4663 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4664 #[serde(default)]
4665 #[serde(rename = "_meta")]
4666 pub meta: Option<Meta>,
4667}
4668
4669impl PromptRequest {
4670 #[must_use]
4672 pub fn new(session_id: impl Into<SessionId>, prompt: Vec<ContentBlock>) -> Self {
4673 Self {
4674 session_id: session_id.into(),
4675 prompt,
4676 meta: None,
4677 }
4678 }
4679
4680 #[must_use]
4686 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4687 self.meta = meta.into_option();
4688 self
4689 }
4690}
4691
4692#[serde_as]
4703#[skip_serializing_none]
4704#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4705#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4706#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_PROMPT_METHOD_NAME)))]
4707#[serde(rename_all = "camelCase")]
4708#[non_exhaustive]
4709pub struct PromptResponse {
4710 pub message_id: MessageId,
4718 #[serde_as(deserialize_as = "DefaultOnError")]
4724 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4725 #[serde(default)]
4726 #[serde(rename = "_meta")]
4727 pub meta: Option<Meta>,
4728}
4729
4730impl PromptResponse {
4731 #[must_use]
4733 pub fn new(message_id: impl Into<MessageId>) -> Self {
4734 Self {
4735 message_id: message_id.into(),
4736 meta: None,
4737 }
4738 }
4739
4740 #[must_use]
4746 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4747 self.meta = meta.into_option();
4748 self
4749 }
4750}
4751
4752#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4756#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
4757#[serde(tag = "stopReason", rename_all = "snake_case")]
4758#[non_exhaustive]
4759pub enum StopReason {
4760 EndTurn,
4763 MaxTokens,
4765 MaxTurnRequests,
4768 Refusal,
4770 Cancelled,
4781 Error(ErrorStopReason),
4787 #[serde(untagged)]
4793 Other(OtherStopReason),
4794}
4795
4796impl From<ErrorStopReason> for StopReason {
4797 fn from(value: ErrorStopReason) -> Self {
4798 Self::Error(value)
4799 }
4800}
4801
4802#[serde_as]
4804#[skip_serializing_none]
4805#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4806#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
4807#[serde(rename_all = "camelCase")]
4808#[non_exhaustive]
4809pub struct ErrorStopReason {
4810 #[serde_as(deserialize_as = "DefaultOnError")]
4815 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4816 #[serde(default)]
4817 pub error: Option<Box<Error>>,
4818}
4819
4820impl ErrorStopReason {
4821 #[must_use]
4823 pub fn new() -> Self {
4824 Self::default()
4825 }
4826
4827 #[must_use]
4829 pub fn error(mut self, error: impl IntoOption<Error>) -> Self {
4830 self.error = error.into_option().map(Box::new);
4831 self
4832 }
4833}
4834
4835#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4837#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
4838#[cfg_attr(feature = "schemars", schemars(inline))]
4839#[cfg_attr(feature = "schemars", schemars(transform = other_stop_reason_schema))]
4840#[serde(rename_all = "camelCase")]
4841#[non_exhaustive]
4842pub struct OtherStopReason {
4843 pub stop_reason: String,
4849 #[serde(flatten)]
4851 pub fields: BTreeMap<String, serde_json::Value>,
4852}
4853
4854impl OtherStopReason {
4855 #[must_use]
4857 pub fn new(
4858 stop_reason: impl Into<String>,
4859 mut fields: BTreeMap<String, serde_json::Value>,
4860 ) -> Self {
4861 remove_idle_state_update_fields(&mut fields);
4862 Self {
4863 stop_reason: stop_reason.into(),
4864 fields,
4865 }
4866 }
4867}
4868
4869impl<'de> Deserialize<'de> for OtherStopReason {
4870 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
4871 where
4872 D: serde::Deserializer<'de>,
4873 {
4874 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
4875 let stop_reason = fields
4876 .remove("stopReason")
4877 .ok_or_else(|| serde::de::Error::missing_field("stopReason"))?;
4878 let serde_json::Value::String(stop_reason) = stop_reason else {
4879 return Err(serde::de::Error::custom("`stopReason` must be a string"));
4880 };
4881
4882 if KNOWN_STOP_REASONS.contains(&stop_reason.as_str()) {
4883 return Err(serde::de::Error::custom(format!(
4884 "known stop reason `{stop_reason}` did not match its schema"
4885 )));
4886 }
4887 remove_idle_state_update_fields(&mut fields);
4888
4889 Ok(Self {
4890 stop_reason,
4891 fields,
4892 })
4893 }
4894}
4895
4896const KNOWN_STOP_REASONS: &[&str] = &[
4897 "end_turn",
4898 "max_tokens",
4899 "max_turn_requests",
4900 "refusal",
4901 "cancelled",
4902 "error",
4903];
4904
4905fn remove_idle_state_update_fields(fields: &mut BTreeMap<String, serde_json::Value>) {
4907 fields.remove("stopReason");
4908 fields.remove("usage");
4909 fields.remove("_meta");
4910}
4911
4912#[cfg(feature = "schemars")]
4913fn other_stop_reason_schema(schema: &mut Schema) {
4914 super::schema_util::reject_known_string_discriminators(
4915 schema,
4916 "stopReason",
4917 KNOWN_STOP_REASONS,
4918 );
4919}
4920
4921#[cfg(feature = "unstable_end_turn_token_usage")]
4927#[serde_as]
4928#[skip_serializing_none]
4929#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4930#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4931#[serde(rename_all = "camelCase")]
4932#[non_exhaustive]
4933pub struct Usage {
4934 pub total_tokens: u64,
4936 pub input_tokens: u64,
4938 pub output_tokens: u64,
4940 #[serde_as(deserialize_as = "DefaultOnError")]
4944 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4945 #[serde(default)]
4946 pub thought_tokens: Option<u64>,
4947 #[serde_as(deserialize_as = "DefaultOnError")]
4951 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4952 #[serde(default)]
4953 pub cached_read_tokens: Option<u64>,
4954 #[serde_as(deserialize_as = "DefaultOnError")]
4958 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4959 #[serde(default)]
4960 pub cached_write_tokens: Option<u64>,
4961 #[serde_as(deserialize_as = "DefaultOnError")]
4967 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4968 #[serde(default)]
4969 #[serde(rename = "_meta")]
4970 pub meta: Option<Meta>,
4971}
4972
4973#[cfg(feature = "unstable_end_turn_token_usage")]
4974impl Usage {
4975 #[must_use]
4977 pub fn new(total_tokens: u64, input_tokens: u64, output_tokens: u64) -> Self {
4978 Self {
4979 total_tokens,
4980 input_tokens,
4981 output_tokens,
4982 thought_tokens: None,
4983 cached_read_tokens: None,
4984 cached_write_tokens: None,
4985 meta: None,
4986 }
4987 }
4988
4989 #[must_use]
4991 pub fn thought_tokens(mut self, thought_tokens: impl IntoOption<u64>) -> Self {
4992 self.thought_tokens = thought_tokens.into_option();
4993 self
4994 }
4995
4996 #[must_use]
4998 pub fn cached_read_tokens(mut self, cached_read_tokens: impl IntoOption<u64>) -> Self {
4999 self.cached_read_tokens = cached_read_tokens.into_option();
5000 self
5001 }
5002
5003 #[must_use]
5005 pub fn cached_write_tokens(mut self, cached_write_tokens: impl IntoOption<u64>) -> Self {
5006 self.cached_write_tokens = cached_write_tokens.into_option();
5007 self
5008 }
5009
5010 #[must_use]
5016 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5017 self.meta = meta.into_option();
5018 self
5019 }
5020}
5021
5022#[cfg(feature = "unstable_llm_providers")]
5035#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5036#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5037#[serde(rename_all = "snake_case")]
5038#[non_exhaustive]
5039#[expect(clippy::doc_markdown)]
5040pub enum LlmProtocol {
5041 Anthropic,
5043 #[serde(rename = "openai")]
5045 OpenAi,
5046 Azure,
5048 Vertex,
5050 Bedrock,
5052 #[serde(untagged)]
5058 Other(String),
5059}
5060
5061#[cfg(feature = "unstable_llm_providers")]
5067#[serde_as]
5068#[skip_serializing_none]
5069#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5070#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5071#[serde(rename_all = "camelCase")]
5072#[non_exhaustive]
5073pub struct ProviderCurrentConfig {
5074 pub api_type: LlmProtocol,
5076 #[cfg_attr(feature = "schemars", schemars(url))]
5078 pub base_url: String,
5079 #[serde_as(deserialize_as = "DefaultOnError")]
5085 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5086 #[serde(default)]
5087 #[serde(rename = "_meta")]
5088 pub meta: Option<Meta>,
5089}
5090
5091#[cfg(feature = "unstable_llm_providers")]
5092impl ProviderCurrentConfig {
5093 #[must_use]
5095 pub fn new(api_type: LlmProtocol, base_url: impl Into<String>) -> Self {
5096 Self {
5097 api_type,
5098 base_url: base_url.into(),
5099 meta: None,
5100 }
5101 }
5102
5103 #[must_use]
5109 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5110 self.meta = meta.into_option();
5111 self
5112 }
5113}
5114
5115#[cfg(feature = "unstable_llm_providers")]
5121#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5122#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
5123#[serde(transparent)]
5124#[from(forward)]
5125#[non_exhaustive]
5126pub struct ProviderId(pub Arc<str>);
5127
5128#[cfg(feature = "unstable_llm_providers")]
5129impl ProviderId {
5130 #[must_use]
5132 pub fn new(id: impl Into<Self>) -> Self {
5133 id.into()
5134 }
5135}
5136
5137#[cfg(feature = "unstable_llm_providers")]
5143#[serde_as]
5144#[skip_serializing_none]
5145#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5146#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5147#[serde(rename_all = "camelCase")]
5148#[non_exhaustive]
5149pub struct ProviderInfo {
5150 pub provider_id: ProviderId,
5152 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
5154 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
5155 pub supported: Vec<LlmProtocol>,
5156 pub required: bool,
5159 #[serde(default)]
5162 pub current: Option<ProviderCurrentConfig>,
5163 #[serde_as(deserialize_as = "DefaultOnError")]
5169 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5170 #[serde(default)]
5171 #[serde(rename = "_meta")]
5172 pub meta: Option<Meta>,
5173}
5174
5175#[cfg(feature = "unstable_llm_providers")]
5176impl ProviderInfo {
5177 #[must_use]
5179 pub fn new(
5180 provider_id: impl Into<ProviderId>,
5181 supported: Vec<LlmProtocol>,
5182 required: bool,
5183 current: impl IntoOption<ProviderCurrentConfig>,
5184 ) -> Self {
5185 Self {
5186 provider_id: provider_id.into(),
5187 supported,
5188 required,
5189 current: current.into_option(),
5190 meta: None,
5191 }
5192 }
5193
5194 #[must_use]
5200 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5201 self.meta = meta.into_option();
5202 self
5203 }
5204}
5205
5206#[cfg(feature = "unstable_llm_providers")]
5207crate::serde_util::default_on_null! {
5208 #[serde_as]
5214 #[skip_serializing_none]
5215 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5216 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
5217 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_LIST_METHOD_NAME)))]
5218 #[serde(rename_all = "camelCase")]
5219 #[non_exhaustive]
5220 pub struct ListProvidersRequest {
5221 #[serde_as(deserialize_as = "DefaultOnError")]
5227 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5228 #[serde(default)]
5229 #[serde(rename = "_meta")]
5230 pub meta: Option<Meta>,
5231 }
5232}
5233
5234#[cfg(feature = "unstable_llm_providers")]
5235impl ListProvidersRequest {
5236 #[must_use]
5238 pub fn new() -> Self {
5239 Self::default()
5240 }
5241
5242 #[must_use]
5248 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5249 self.meta = meta.into_option();
5250 self
5251 }
5252}
5253
5254#[cfg(feature = "unstable_llm_providers")]
5260#[serde_as]
5261#[skip_serializing_none]
5262#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5263#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5264#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_LIST_METHOD_NAME)))]
5265#[serde(rename_all = "camelCase")]
5266#[non_exhaustive]
5267pub struct ListProvidersResponse {
5268 pub providers: Vec<ProviderInfo>,
5270 #[serde_as(deserialize_as = "DefaultOnError")]
5276 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5277 #[serde(default)]
5278 #[serde(rename = "_meta")]
5279 pub meta: Option<Meta>,
5280}
5281
5282#[cfg(feature = "unstable_llm_providers")]
5283impl ListProvidersResponse {
5284 #[must_use]
5286 pub fn new(providers: Vec<ProviderInfo>) -> Self {
5287 Self {
5288 providers,
5289 meta: None,
5290 }
5291 }
5292
5293 #[must_use]
5299 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5300 self.meta = meta.into_option();
5301 self
5302 }
5303}
5304
5305#[cfg(feature = "unstable_llm_providers")]
5313#[serde_as]
5314#[skip_serializing_none]
5315#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5316#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
5317#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_SET_METHOD_NAME)))]
5318#[serde(rename_all = "camelCase")]
5319#[non_exhaustive]
5320pub struct SetProviderRequest {
5321 pub provider_id: ProviderId,
5323 pub api_type: LlmProtocol,
5325 #[cfg_attr(feature = "schemars", schemars(url))]
5327 pub base_url: String,
5328 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
5331 pub headers: HashMap<String, String>,
5332 #[serde_as(deserialize_as = "DefaultOnError")]
5338 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5339 #[serde(default)]
5340 #[serde(rename = "_meta")]
5341 pub meta: Option<Meta>,
5342}
5343
5344#[cfg(feature = "unstable_llm_providers")]
5345impl SetProviderRequest {
5346 #[must_use]
5348 pub fn new(
5349 provider_id: impl Into<ProviderId>,
5350 api_type: LlmProtocol,
5351 base_url: impl Into<String>,
5352 ) -> Self {
5353 Self {
5354 provider_id: provider_id.into(),
5355 api_type,
5356 base_url: base_url.into(),
5357 headers: HashMap::new(),
5358 meta: None,
5359 }
5360 }
5361
5362 #[must_use]
5365 pub fn headers(mut self, headers: HashMap<String, String>) -> Self {
5366 self.headers = headers;
5367 self
5368 }
5369
5370 #[must_use]
5376 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5377 self.meta = meta.into_option();
5378 self
5379 }
5380}
5381
5382#[cfg(feature = "unstable_llm_providers")]
5383crate::serde_util::default_on_null! {
5384 #[serde_as]
5390 #[skip_serializing_none]
5391 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5392 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
5393 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_SET_METHOD_NAME)))]
5394 #[serde(rename_all = "camelCase")]
5395 #[non_exhaustive]
5396 pub struct SetProviderResponse {
5397 #[serde_as(deserialize_as = "DefaultOnError")]
5403 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5404 #[serde(default)]
5405 #[serde(rename = "_meta")]
5406 pub meta: Option<Meta>,
5407 }
5408}
5409
5410#[cfg(feature = "unstable_llm_providers")]
5411impl SetProviderResponse {
5412 #[must_use]
5414 pub fn new() -> Self {
5415 Self::default()
5416 }
5417
5418 #[must_use]
5424 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5425 self.meta = meta.into_option();
5426 self
5427 }
5428}
5429
5430#[cfg(feature = "unstable_llm_providers")]
5436#[serde_as]
5437#[skip_serializing_none]
5438#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5439#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5440#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_DISABLE_METHOD_NAME)))]
5441#[serde(rename_all = "camelCase")]
5442#[non_exhaustive]
5443pub struct DisableProviderRequest {
5444 pub provider_id: ProviderId,
5446 #[serde_as(deserialize_as = "DefaultOnError")]
5452 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5453 #[serde(default)]
5454 #[serde(rename = "_meta")]
5455 pub meta: Option<Meta>,
5456}
5457
5458#[cfg(feature = "unstable_llm_providers")]
5459impl DisableProviderRequest {
5460 #[must_use]
5462 pub fn new(provider_id: impl Into<ProviderId>) -> Self {
5463 Self {
5464 provider_id: provider_id.into(),
5465 meta: None,
5466 }
5467 }
5468
5469 #[must_use]
5475 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5476 self.meta = meta.into_option();
5477 self
5478 }
5479}
5480
5481#[cfg(feature = "unstable_llm_providers")]
5482crate::serde_util::default_on_null! {
5483 #[serde_as]
5489 #[skip_serializing_none]
5490 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5491 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
5492 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_DISABLE_METHOD_NAME)))]
5493 #[serde(rename_all = "camelCase")]
5494 #[non_exhaustive]
5495 pub struct DisableProviderResponse {
5496 #[serde_as(deserialize_as = "DefaultOnError")]
5502 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5503 #[serde(default)]
5504 #[serde(rename = "_meta")]
5505 pub meta: Option<Meta>,
5506 }
5507}
5508
5509#[cfg(feature = "unstable_llm_providers")]
5510impl DisableProviderResponse {
5511 #[must_use]
5513 pub fn new() -> Self {
5514 Self::default()
5515 }
5516
5517 #[must_use]
5523 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5524 self.meta = meta.into_option();
5525 self
5526 }
5527}
5528
5529#[serde_as]
5538#[skip_serializing_none]
5539#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5540#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5541#[serde(rename_all = "camelCase")]
5542#[non_exhaustive]
5543pub struct AgentCapabilities {
5544 #[serde_as(deserialize_as = "DefaultOnError")]
5552 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5553 #[serde(default)]
5554 pub session: Option<SessionCapabilities>,
5555 #[serde_as(deserialize_as = "DefaultOnError")]
5562 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5563 #[serde(default)]
5564 pub auth: Option<AgentAuthCapabilities>,
5565 #[cfg(feature = "unstable_llm_providers")]
5574 #[serde_as(deserialize_as = "DefaultOnError")]
5575 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5576 #[serde(default)]
5577 pub providers: Option<ProvidersCapabilities>,
5578 #[serde_as(deserialize_as = "DefaultOnError")]
5584 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5585 #[serde(default)]
5586 #[serde(rename = "_meta")]
5587 pub meta: Option<Meta>,
5588}
5589
5590impl AgentCapabilities {
5591 #[must_use]
5593 pub fn new() -> Self {
5594 Self::default()
5595 }
5596
5597 #[must_use]
5605 pub fn session(mut self, session: impl IntoOption<SessionCapabilities>) -> Self {
5606 self.session = session.into_option();
5607 self
5608 }
5609
5610 #[must_use]
5614 pub fn auth(mut self, auth: impl IntoOption<AgentAuthCapabilities>) -> Self {
5615 self.auth = auth.into_option();
5616 self
5617 }
5618
5619 #[cfg(feature = "unstable_llm_providers")]
5625 #[must_use]
5626 pub fn providers(mut self, providers: impl IntoOption<ProvidersCapabilities>) -> Self {
5627 self.providers = providers.into_option();
5628 self
5629 }
5630
5631 #[must_use]
5637 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5638 self.meta = meta.into_option();
5639 self
5640 }
5641}
5642
5643#[cfg(feature = "unstable_llm_providers")]
5651#[serde_as]
5652#[skip_serializing_none]
5653#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5654#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5655#[non_exhaustive]
5656pub struct ProvidersCapabilities {
5657 #[serde_as(deserialize_as = "DefaultOnError")]
5663 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5664 #[serde(default)]
5665 #[serde(rename = "_meta")]
5666 pub meta: Option<Meta>,
5667}
5668
5669#[cfg(feature = "unstable_llm_providers")]
5670impl ProvidersCapabilities {
5671 #[must_use]
5673 pub fn new() -> Self {
5674 Self::default()
5675 }
5676
5677 #[must_use]
5683 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5684 self.meta = meta.into_option();
5685 self
5686 }
5687}
5688
5689#[serde_as]
5701#[skip_serializing_none]
5702#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5703#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5704#[serde(rename_all = "camelCase")]
5705#[non_exhaustive]
5706pub struct SessionCapabilities {
5707 #[serde_as(deserialize_as = "DefaultOnError")]
5713 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5714 #[serde(default)]
5715 pub prompt: Option<PromptCapabilities>,
5716 #[serde_as(deserialize_as = "DefaultOnError")]
5721 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5722 #[serde(default)]
5723 pub mcp: Option<McpCapabilities>,
5724 #[serde_as(deserialize_as = "DefaultOnError")]
5729 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5730 #[serde(default)]
5731 pub delete: Option<SessionDeleteCapabilities>,
5732 #[serde_as(deserialize_as = "DefaultOnError")]
5741 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5742 #[serde(default)]
5743 pub additional_directories: Option<SessionAdditionalDirectoriesCapabilities>,
5744 #[serde_as(deserialize_as = "DefaultOnError")]
5749 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5750 #[serde(default)]
5751 pub system_prompt: Option<SessionSystemPromptCapabilities>,
5752 #[cfg(feature = "unstable_session_fork")]
5761 #[serde_as(deserialize_as = "DefaultOnError")]
5762 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5763 #[serde(default)]
5764 pub fork: Option<SessionForkCapabilities>,
5765 #[cfg(feature = "unstable_session_archive")]
5775 #[serde_as(deserialize_as = "DefaultOnError")]
5776 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5777 #[serde(default)]
5778 pub archive: Option<SessionArchiveCapabilities>,
5779 #[cfg(feature = "unstable_session_status")]
5788 #[serde_as(deserialize_as = "DefaultOnError")]
5789 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5790 #[serde(default)]
5791 pub status: Option<SessionStatusCapabilities>,
5792 #[cfg(feature = "unstable_session_set_title")]
5804 #[serde_as(deserialize_as = "DefaultOnError")]
5805 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5806 #[serde(default)]
5807 pub title: Option<SessionTitleCapabilities>,
5808 #[serde_as(deserialize_as = "DefaultOnError")]
5814 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5815 #[serde(default)]
5816 #[serde(rename = "_meta")]
5817 pub meta: Option<Meta>,
5818}
5819
5820impl SessionCapabilities {
5821 #[must_use]
5823 pub fn new() -> Self {
5824 Self::default()
5825 }
5826
5827 #[must_use]
5833 pub fn prompt(mut self, prompt: impl IntoOption<PromptCapabilities>) -> Self {
5834 self.prompt = prompt.into_option();
5835 self
5836 }
5837
5838 #[must_use]
5843 pub fn mcp(mut self, mcp: impl IntoOption<McpCapabilities>) -> Self {
5844 self.mcp = mcp.into_option();
5845 self
5846 }
5847
5848 #[must_use]
5853 pub fn delete(mut self, delete: impl IntoOption<SessionDeleteCapabilities>) -> Self {
5854 self.delete = delete.into_option();
5855 self
5856 }
5857
5858 #[must_use]
5867 pub fn additional_directories(
5868 mut self,
5869 additional_directories: impl IntoOption<SessionAdditionalDirectoriesCapabilities>,
5870 ) -> Self {
5871 self.additional_directories = additional_directories.into_option();
5872 self
5873 }
5874
5875 #[must_use]
5880 pub fn system_prompt(
5881 mut self,
5882 system_prompt: impl IntoOption<SessionSystemPromptCapabilities>,
5883 ) -> Self {
5884 self.system_prompt = system_prompt.into_option();
5885 self
5886 }
5887
5888 #[cfg(feature = "unstable_session_fork")]
5889 #[must_use]
5894 pub fn fork(mut self, fork: impl IntoOption<SessionForkCapabilities>) -> Self {
5895 self.fork = fork.into_option();
5896 self
5897 }
5898
5899 #[cfg(feature = "unstable_session_archive")]
5909 #[must_use]
5910 pub fn archive(mut self, archive: impl IntoOption<SessionArchiveCapabilities>) -> Self {
5911 self.archive = archive.into_option();
5912 self
5913 }
5914
5915 #[cfg(feature = "unstable_session_status")]
5924 #[must_use]
5925 pub fn status(mut self, status: impl IntoOption<SessionStatusCapabilities>) -> Self {
5926 self.status = status.into_option();
5927 self
5928 }
5929
5930 #[cfg(feature = "unstable_session_set_title")]
5942 #[must_use]
5943 pub fn title(mut self, title: impl IntoOption<SessionTitleCapabilities>) -> Self {
5944 self.title = title.into_option();
5945 self
5946 }
5947
5948 #[must_use]
5954 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5955 self.meta = meta.into_option();
5956 self
5957 }
5958}
5959
5960#[serde_as]
5964#[skip_serializing_none]
5965#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5966#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5967#[non_exhaustive]
5968pub struct SessionDeleteCapabilities {
5969 #[serde_as(deserialize_as = "DefaultOnError")]
5975 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5976 #[serde(default)]
5977 #[serde(rename = "_meta")]
5978 pub meta: Option<Meta>,
5979}
5980
5981impl SessionDeleteCapabilities {
5982 #[must_use]
5984 pub fn new() -> Self {
5985 Self::default()
5986 }
5987
5988 #[must_use]
5994 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5995 self.meta = meta.into_option();
5996 self
5997 }
5998}
5999
6000#[serde_as]
6007#[skip_serializing_none]
6008#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6009#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6010#[non_exhaustive]
6011pub struct SessionAdditionalDirectoriesCapabilities {
6012 #[serde_as(deserialize_as = "DefaultOnError")]
6018 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6019 #[serde(default)]
6020 #[serde(rename = "_meta")]
6021 pub meta: Option<Meta>,
6022}
6023
6024impl SessionAdditionalDirectoriesCapabilities {
6025 #[must_use]
6027 pub fn new() -> Self {
6028 Self::default()
6029 }
6030
6031 #[must_use]
6037 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6038 self.meta = meta.into_option();
6039 self
6040 }
6041}
6042
6043#[serde_as]
6048#[skip_serializing_none]
6049#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6050#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6051#[serde(rename_all = "camelCase")]
6052#[non_exhaustive]
6053pub struct SessionSystemPromptCapabilities {
6054 #[serde_as(deserialize_as = "DefaultOnError")]
6058 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6059 #[serde(default)]
6060 pub mode: Option<SessionSystemPromptModeCapabilities>,
6061 #[serde_as(deserialize_as = "DefaultOnError")]
6067 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6068 #[serde(default)]
6069 #[serde(rename = "_meta")]
6070 pub meta: Option<Meta>,
6071}
6072
6073impl SessionSystemPromptCapabilities {
6074 #[must_use]
6076 pub fn new() -> Self {
6077 Self::default()
6078 }
6079
6080 #[must_use]
6084 pub fn mode(mut self, mode: impl IntoOption<SessionSystemPromptModeCapabilities>) -> Self {
6085 self.mode = mode.into_option();
6086 self
6087 }
6088
6089 #[must_use]
6095 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6096 self.meta = meta.into_option();
6097 self
6098 }
6099}
6100
6101#[serde_as]
6106#[skip_serializing_none]
6107#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6108#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6109#[serde(rename_all = "camelCase")]
6110#[non_exhaustive]
6111pub struct SessionSystemPromptModeCapabilities {
6112 #[serde_as(deserialize_as = "DefaultOnError")]
6118 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6119 #[serde(default, rename = "override")]
6120 pub override_: Option<SessionSystemPromptOverrideCapabilities>,
6121 #[serde_as(deserialize_as = "DefaultOnError")]
6127 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6128 #[serde(default)]
6129 #[serde(rename = "_meta")]
6130 pub meta: Option<Meta>,
6131}
6132
6133impl SessionSystemPromptModeCapabilities {
6134 #[must_use]
6136 pub fn new() -> Self {
6137 Self::default()
6138 }
6139
6140 #[must_use]
6146 pub fn override_(
6147 mut self,
6148 override_: impl IntoOption<SessionSystemPromptOverrideCapabilities>,
6149 ) -> Self {
6150 self.override_ = override_.into_option();
6151 self
6152 }
6153
6154 #[must_use]
6160 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6161 self.meta = meta.into_option();
6162 self
6163 }
6164}
6165
6166#[serde_as]
6171#[skip_serializing_none]
6172#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6173#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6174#[non_exhaustive]
6175pub struct SessionSystemPromptOverrideCapabilities {
6176 #[serde_as(deserialize_as = "DefaultOnError")]
6182 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6183 #[serde(default)]
6184 #[serde(rename = "_meta")]
6185 pub meta: Option<Meta>,
6186}
6187
6188impl SessionSystemPromptOverrideCapabilities {
6189 #[must_use]
6191 pub fn new() -> Self {
6192 Self::default()
6193 }
6194
6195 #[must_use]
6201 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6202 self.meta = meta.into_option();
6203 self
6204 }
6205}
6206
6207#[cfg(feature = "unstable_session_fork")]
6215#[serde_as]
6216#[skip_serializing_none]
6217#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6218#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6219#[non_exhaustive]
6220pub struct SessionForkCapabilities {
6221 #[serde_as(deserialize_as = "DefaultOnError")]
6227 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6228 #[serde(default)]
6229 #[serde(rename = "_meta")]
6230 pub meta: Option<Meta>,
6231}
6232
6233#[cfg(feature = "unstable_session_fork")]
6234impl SessionForkCapabilities {
6235 #[must_use]
6237 pub fn new() -> Self {
6238 Self::default()
6239 }
6240
6241 #[must_use]
6247 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6248 self.meta = meta.into_option();
6249 self
6250 }
6251}
6252
6253#[cfg(feature = "unstable_session_archive")]
6262#[serde_as]
6263#[skip_serializing_none]
6264#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6265#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6266#[non_exhaustive]
6267pub struct SessionArchiveCapabilities {
6268 #[serde_as(deserialize_as = "DefaultOnError")]
6274 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6275 #[serde(default)]
6276 #[serde(rename = "_meta")]
6277 pub meta: Option<Meta>,
6278}
6279
6280#[cfg(feature = "unstable_session_archive")]
6281impl SessionArchiveCapabilities {
6282 #[must_use]
6284 pub fn new() -> Self {
6285 Self::default()
6286 }
6287
6288 #[must_use]
6294 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6295 self.meta = meta.into_option();
6296 self
6297 }
6298}
6299
6300#[cfg(feature = "unstable_session_status")]
6308#[serde_as]
6309#[skip_serializing_none]
6310#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6311#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6312#[non_exhaustive]
6313pub struct SessionStatusCapabilities {
6314 #[serde_as(deserialize_as = "DefaultOnError")]
6320 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6321 #[serde(default)]
6322 #[serde(rename = "_meta")]
6323 pub meta: Option<Meta>,
6324}
6325
6326#[cfg(feature = "unstable_session_status")]
6327impl SessionStatusCapabilities {
6328 #[must_use]
6330 pub fn new() -> Self {
6331 Self::default()
6332 }
6333
6334 #[must_use]
6340 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6341 self.meta = meta.into_option();
6342 self
6343 }
6344}
6345
6346#[cfg(feature = "unstable_session_set_title")]
6355#[serde_as]
6356#[skip_serializing_none]
6357#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6358#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6359#[non_exhaustive]
6360pub struct SessionTitleCapabilities {
6361 #[serde_as(deserialize_as = "DefaultOnError")]
6367 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6368 #[serde(default)]
6369 #[serde(rename = "_meta")]
6370 pub meta: Option<Meta>,
6371}
6372
6373#[cfg(feature = "unstable_session_set_title")]
6374impl SessionTitleCapabilities {
6375 #[must_use]
6377 pub fn new() -> Self {
6378 Self::default()
6379 }
6380
6381 #[must_use]
6387 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6388 self.meta = meta.into_option();
6389 self
6390 }
6391}
6392
6393#[serde_as]
6406#[skip_serializing_none]
6407#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6408#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6409#[serde(rename_all = "camelCase")]
6410#[non_exhaustive]
6411pub struct PromptCapabilities {
6412 #[serde_as(deserialize_as = "DefaultOnError")]
6417 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6418 #[serde(default)]
6419 pub image: Option<PromptImageCapabilities>,
6420 #[serde_as(deserialize_as = "DefaultOnError")]
6425 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6426 #[serde(default)]
6427 pub audio: Option<PromptAudioCapabilities>,
6428 #[serde_as(deserialize_as = "DefaultOnError")]
6436 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6437 #[serde(default)]
6438 pub embedded_context: Option<PromptEmbeddedContextCapabilities>,
6439 #[serde_as(deserialize_as = "DefaultOnError")]
6445 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6446 #[serde(default)]
6447 #[serde(rename = "_meta")]
6448 pub meta: Option<Meta>,
6449}
6450
6451impl PromptCapabilities {
6452 #[must_use]
6454 pub fn new() -> Self {
6455 Self::default()
6456 }
6457
6458 #[must_use]
6463 pub fn image(mut self, image: impl IntoOption<PromptImageCapabilities>) -> Self {
6464 self.image = image.into_option();
6465 self
6466 }
6467
6468 #[must_use]
6473 pub fn audio(mut self, audio: impl IntoOption<PromptAudioCapabilities>) -> Self {
6474 self.audio = audio.into_option();
6475 self
6476 }
6477
6478 #[must_use]
6486 pub fn embedded_context(
6487 mut self,
6488 embedded_context: impl IntoOption<PromptEmbeddedContextCapabilities>,
6489 ) -> Self {
6490 self.embedded_context = embedded_context.into_option();
6491 self
6492 }
6493
6494 #[must_use]
6500 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6501 self.meta = meta.into_option();
6502 self
6503 }
6504}
6505
6506#[serde_as]
6510#[skip_serializing_none]
6511#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6512#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6513#[non_exhaustive]
6514pub struct PromptImageCapabilities {
6515 #[serde_as(deserialize_as = "DefaultOnError")]
6521 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6522 #[serde(default)]
6523 #[serde(rename = "_meta")]
6524 pub meta: Option<Meta>,
6525}
6526
6527impl PromptImageCapabilities {
6528 #[must_use]
6530 pub fn new() -> Self {
6531 Self::default()
6532 }
6533
6534 #[must_use]
6540 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6541 self.meta = meta.into_option();
6542 self
6543 }
6544}
6545
6546#[serde_as]
6550#[skip_serializing_none]
6551#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6552#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6553#[non_exhaustive]
6554pub struct PromptAudioCapabilities {
6555 #[serde_as(deserialize_as = "DefaultOnError")]
6561 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6562 #[serde(default)]
6563 #[serde(rename = "_meta")]
6564 pub meta: Option<Meta>,
6565}
6566
6567impl PromptAudioCapabilities {
6568 #[must_use]
6570 pub fn new() -> Self {
6571 Self::default()
6572 }
6573
6574 #[must_use]
6580 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6581 self.meta = meta.into_option();
6582 self
6583 }
6584}
6585
6586#[serde_as]
6590#[skip_serializing_none]
6591#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6592#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6593#[non_exhaustive]
6594pub struct PromptEmbeddedContextCapabilities {
6595 #[serde_as(deserialize_as = "DefaultOnError")]
6601 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6602 #[serde(default)]
6603 #[serde(rename = "_meta")]
6604 pub meta: Option<Meta>,
6605}
6606
6607impl PromptEmbeddedContextCapabilities {
6608 #[must_use]
6610 pub fn new() -> Self {
6611 Self::default()
6612 }
6613
6614 #[must_use]
6620 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6621 self.meta = meta.into_option();
6622 self
6623 }
6624}
6625
6626#[serde_as]
6628#[skip_serializing_none]
6629#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6630#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6631#[serde(rename_all = "camelCase")]
6632#[non_exhaustive]
6633pub struct McpCapabilities {
6634 #[serde_as(deserialize_as = "DefaultOnError")]
6639 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6640 #[serde(default)]
6641 pub stdio: Option<McpStdioCapabilities>,
6642 #[serde_as(deserialize_as = "DefaultOnError")]
6647 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6648 #[serde(default)]
6649 pub http: Option<McpHttpCapabilities>,
6650 #[cfg(feature = "unstable_mcp_over_acp")]
6659 #[serde_as(deserialize_as = "DefaultOnError")]
6660 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6661 #[serde(default)]
6662 pub acp: Option<McpAcpCapabilities>,
6663 #[serde_as(deserialize_as = "DefaultOnError")]
6669 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6670 #[serde(default)]
6671 #[serde(rename = "_meta")]
6672 pub meta: Option<Meta>,
6673}
6674
6675impl McpCapabilities {
6676 #[must_use]
6678 pub fn new() -> Self {
6679 Self::default()
6680 }
6681
6682 #[must_use]
6687 pub fn stdio(mut self, stdio: impl IntoOption<McpStdioCapabilities>) -> Self {
6688 self.stdio = stdio.into_option();
6689 self
6690 }
6691
6692 #[must_use]
6697 pub fn http(mut self, http: impl IntoOption<McpHttpCapabilities>) -> Self {
6698 self.http = http.into_option();
6699 self
6700 }
6701
6702 #[cfg(feature = "unstable_mcp_over_acp")]
6708 #[must_use]
6712 pub fn acp(mut self, acp: impl IntoOption<McpAcpCapabilities>) -> Self {
6713 self.acp = acp.into_option();
6714 self
6715 }
6716
6717 #[must_use]
6723 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6724 self.meta = meta.into_option();
6725 self
6726 }
6727}
6728
6729#[serde_as]
6733#[skip_serializing_none]
6734#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6735#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6736#[non_exhaustive]
6737pub struct McpStdioCapabilities {
6738 #[serde_as(deserialize_as = "DefaultOnError")]
6744 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6745 #[serde(default)]
6746 #[serde(rename = "_meta")]
6747 pub meta: Option<Meta>,
6748}
6749
6750impl McpStdioCapabilities {
6751 #[must_use]
6753 pub fn new() -> Self {
6754 Self::default()
6755 }
6756
6757 #[must_use]
6763 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6764 self.meta = meta.into_option();
6765 self
6766 }
6767}
6768
6769#[serde_as]
6773#[skip_serializing_none]
6774#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6775#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6776#[non_exhaustive]
6777pub struct McpHttpCapabilities {
6778 #[serde_as(deserialize_as = "DefaultOnError")]
6784 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6785 #[serde(default)]
6786 #[serde(rename = "_meta")]
6787 pub meta: Option<Meta>,
6788}
6789
6790impl McpHttpCapabilities {
6791 #[must_use]
6793 pub fn new() -> Self {
6794 Self::default()
6795 }
6796
6797 #[must_use]
6803 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6804 self.meta = meta.into_option();
6805 self
6806 }
6807}
6808
6809#[cfg(feature = "unstable_mcp_over_acp")]
6817#[serde_as]
6818#[skip_serializing_none]
6819#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6820#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6821#[non_exhaustive]
6822pub struct McpAcpCapabilities {
6823 #[serde_as(deserialize_as = "DefaultOnError")]
6829 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6830 #[serde(default)]
6831 #[serde(rename = "_meta")]
6832 pub meta: Option<Meta>,
6833}
6834
6835#[cfg(feature = "unstable_mcp_over_acp")]
6836impl McpAcpCapabilities {
6837 #[must_use]
6839 pub fn new() -> Self {
6840 Self::default()
6841 }
6842
6843 #[must_use]
6849 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6850 self.meta = meta.into_option();
6851 self
6852 }
6853}
6854
6855#[serde_as]
6859#[skip_serializing_none]
6860#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6861#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6862#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_CANCEL_METHOD_NAME)))]
6863#[serde(rename_all = "camelCase")]
6864#[non_exhaustive]
6865pub struct CancelSessionNotification {
6866 pub session_id: SessionId,
6868 #[serde_as(deserialize_as = "DefaultOnError")]
6874 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6875 #[serde(default)]
6876 #[serde(rename = "_meta")]
6877 pub meta: Option<Meta>,
6878}
6879
6880impl CancelSessionNotification {
6881 #[must_use]
6883 pub fn new(session_id: impl Into<SessionId>) -> Self {
6884 Self {
6885 session_id: session_id.into(),
6886 meta: None,
6887 }
6888 }
6889
6890 #[must_use]
6896 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6897 self.meta = meta.into_option();
6898 self
6899 }
6900}
6901
6902#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6908#[non_exhaustive]
6909pub struct AgentMethodNames {
6910 pub initialize: &'static str,
6912 pub auth_login: &'static str,
6914 #[cfg(feature = "unstable_llm_providers")]
6916 pub providers_list: &'static str,
6917 #[cfg(feature = "unstable_llm_providers")]
6919 pub providers_set: &'static str,
6920 #[cfg(feature = "unstable_llm_providers")]
6922 pub providers_disable: &'static str,
6923 pub session_new: &'static str,
6925 pub session_set_config_option: &'static str,
6927 pub session_prompt: &'static str,
6929 pub session_cancel: &'static str,
6931 #[cfg(feature = "unstable_mcp_over_acp")]
6933 pub mcp_message: &'static str,
6934 pub session_list: &'static str,
6936 pub session_delete: &'static str,
6938 #[cfg(feature = "unstable_session_fork")]
6940 pub session_fork: &'static str,
6941 #[cfg(feature = "unstable_session_archive")]
6943 pub session_archive: &'static str,
6944 #[cfg(feature = "unstable_session_archive")]
6946 pub session_unarchive: &'static str,
6947 #[cfg(feature = "unstable_session_status")]
6949 pub session_status: &'static str,
6950 #[cfg(feature = "unstable_session_set_title")]
6952 pub session_set_title: &'static str,
6953 pub session_resume: &'static str,
6955 pub session_close: &'static str,
6957 pub tool_call_cancel: &'static str,
6959 pub terminal_cancel: &'static str,
6961 pub auth_logout: &'static str,
6963 #[cfg(feature = "unstable_auth_status")]
6965 pub auth_status: &'static str,
6966}
6967
6968pub const AGENT_METHOD_NAMES: AgentMethodNames = AgentMethodNames {
6970 initialize: INITIALIZE_METHOD_NAME,
6971 auth_login: AUTH_LOGIN_METHOD_NAME,
6972 #[cfg(feature = "unstable_llm_providers")]
6973 providers_list: PROVIDERS_LIST_METHOD_NAME,
6974 #[cfg(feature = "unstable_llm_providers")]
6975 providers_set: PROVIDERS_SET_METHOD_NAME,
6976 #[cfg(feature = "unstable_llm_providers")]
6977 providers_disable: PROVIDERS_DISABLE_METHOD_NAME,
6978 session_new: SESSION_NEW_METHOD_NAME,
6979 session_set_config_option: SESSION_SET_CONFIG_OPTION_METHOD_NAME,
6980 session_prompt: SESSION_PROMPT_METHOD_NAME,
6981 session_cancel: SESSION_CANCEL_METHOD_NAME,
6982 #[cfg(feature = "unstable_mcp_over_acp")]
6983 mcp_message: MCP_MESSAGE_METHOD_NAME,
6984 session_list: SESSION_LIST_METHOD_NAME,
6985 session_delete: SESSION_DELETE_METHOD_NAME,
6986 #[cfg(feature = "unstable_session_fork")]
6987 session_fork: SESSION_FORK_METHOD_NAME,
6988 #[cfg(feature = "unstable_session_archive")]
6989 session_archive: SESSION_ARCHIVE_METHOD_NAME,
6990 #[cfg(feature = "unstable_session_archive")]
6991 session_unarchive: SESSION_UNARCHIVE_METHOD_NAME,
6992 #[cfg(feature = "unstable_session_status")]
6993 session_status: SESSION_STATUS_METHOD_NAME,
6994 #[cfg(feature = "unstable_session_set_title")]
6995 session_set_title: SESSION_SET_TITLE_METHOD_NAME,
6996 session_resume: SESSION_RESUME_METHOD_NAME,
6997 session_close: SESSION_CLOSE_METHOD_NAME,
6998 tool_call_cancel: TOOL_CALL_CANCEL_METHOD_NAME,
6999 terminal_cancel: TERMINAL_CANCEL_METHOD_NAME,
7000 auth_logout: AUTH_LOGOUT_METHOD_NAME,
7001 #[cfg(feature = "unstable_auth_status")]
7002 auth_status: AUTH_STATUS_METHOD_NAME,
7003};
7004
7005pub(crate) const INITIALIZE_METHOD_NAME: &str = "initialize";
7007pub(crate) const AUTH_LOGIN_METHOD_NAME: &str = "auth/login";
7009#[cfg(feature = "unstable_llm_providers")]
7011pub(crate) const PROVIDERS_LIST_METHOD_NAME: &str = "providers/list";
7012#[cfg(feature = "unstable_llm_providers")]
7014pub(crate) const PROVIDERS_SET_METHOD_NAME: &str = "providers/set";
7015#[cfg(feature = "unstable_llm_providers")]
7017pub(crate) const PROVIDERS_DISABLE_METHOD_NAME: &str = "providers/disable";
7018pub(crate) const SESSION_NEW_METHOD_NAME: &str = "session/new";
7020pub(crate) const SESSION_SET_CONFIG_OPTION_METHOD_NAME: &str = "session/set_config_option";
7022pub(crate) const SESSION_PROMPT_METHOD_NAME: &str = "session/prompt";
7024pub(crate) const SESSION_CANCEL_METHOD_NAME: &str = "session/cancel";
7026pub(crate) const SESSION_LIST_METHOD_NAME: &str = "session/list";
7028pub(crate) const SESSION_DELETE_METHOD_NAME: &str = "session/delete";
7030#[cfg(feature = "unstable_session_fork")]
7032pub(crate) const SESSION_FORK_METHOD_NAME: &str = "session/fork";
7033#[cfg(feature = "unstable_session_archive")]
7035pub(crate) const SESSION_ARCHIVE_METHOD_NAME: &str = "session/archive";
7036#[cfg(feature = "unstable_session_archive")]
7038pub(crate) const SESSION_UNARCHIVE_METHOD_NAME: &str = "session/unarchive";
7039#[cfg(feature = "unstable_session_status")]
7041pub(crate) const SESSION_STATUS_METHOD_NAME: &str = "session/status";
7042#[cfg(feature = "unstable_session_set_title")]
7044pub(crate) const SESSION_SET_TITLE_METHOD_NAME: &str = "session/set_title";
7045pub(crate) const SESSION_RESUME_METHOD_NAME: &str = "session/resume";
7047pub(crate) const SESSION_CLOSE_METHOD_NAME: &str = "session/close";
7049pub(crate) const TOOL_CALL_CANCEL_METHOD_NAME: &str = "tool_call/cancel";
7051pub(crate) const TERMINAL_CANCEL_METHOD_NAME: &str = "terminal/cancel";
7053pub(crate) const AUTH_LOGOUT_METHOD_NAME: &str = "auth/logout";
7055#[cfg(feature = "unstable_auth_status")]
7057pub(crate) const AUTH_STATUS_METHOD_NAME: &str = "auth/status";
7058
7059#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
7066#[derive(Clone, Debug, Serialize, Deserialize)]
7067#[serde(untagged)]
7068#[cfg_attr(feature = "schemars", schemars(inline))]
7069#[non_exhaustive]
7070pub enum ClientRequest {
7071 InitializeRequest(Box<InitializeRequest>),
7085 LoginAuthRequest(Box<LoginAuthRequest>),
7100 #[cfg(feature = "unstable_llm_providers")]
7106 ListProvidersRequest(Box<ListProvidersRequest>),
7107 #[cfg(feature = "unstable_llm_providers")]
7113 SetProviderRequest(Box<SetProviderRequest>),
7114 #[cfg(feature = "unstable_llm_providers")]
7120 DisableProviderRequest(Box<DisableProviderRequest>),
7121 LogoutAuthRequest(Box<LogoutAuthRequest>),
7131 #[cfg(feature = "unstable_auth_status")]
7140 AuthStatusRequest(Box<AuthStatusRequest>),
7141 NewSessionRequest(Box<NewSessionRequest>),
7154 ListSessionsRequest(Box<ListSessionsRequest>),
7158 DeleteSessionRequest(Box<DeleteSessionRequest>),
7162 #[cfg(feature = "unstable_session_fork")]
7163 ForkSessionRequest(Box<ForkSessionRequest>),
7179 #[cfg(feature = "unstable_session_archive")]
7180 ArchiveSessionRequest(Box<ArchiveSessionRequest>),
7188 #[cfg(feature = "unstable_session_archive")]
7189 UnarchiveSessionRequest(Box<UnarchiveSessionRequest>),
7197 #[cfg(feature = "unstable_session_status")]
7198 SessionStatusRequest(Box<SessionStatusRequest>),
7206 #[cfg(feature = "unstable_session_set_title")]
7207 SetSessionTitleRequest(Box<SetSessionTitleRequest>),
7218 ResumeSessionRequest(Box<ResumeSessionRequest>),
7224 CloseSessionRequest(Box<CloseSessionRequest>),
7229 CancelToolCallRequest(Box<CancelToolCallRequest>),
7235 CancelTerminalRequest(Box<CancelTerminalRequest>),
7241 SetSessionConfigOptionRequest(Box<SetSessionConfigOptionRequest>),
7243 PromptRequest(Box<PromptRequest>),
7265 ExtMethodRequest(Box<ExtRequest>),
7272}
7273
7274impl ClientRequest {
7275 #[must_use]
7277 pub fn method(&self) -> &str {
7278 match self {
7279 Self::InitializeRequest(_) => AGENT_METHOD_NAMES.initialize,
7280 Self::LoginAuthRequest(_) => AGENT_METHOD_NAMES.auth_login,
7281 #[cfg(feature = "unstable_llm_providers")]
7282 Self::ListProvidersRequest(_) => AGENT_METHOD_NAMES.providers_list,
7283 #[cfg(feature = "unstable_llm_providers")]
7284 Self::SetProviderRequest(_) => AGENT_METHOD_NAMES.providers_set,
7285 #[cfg(feature = "unstable_llm_providers")]
7286 Self::DisableProviderRequest(_) => AGENT_METHOD_NAMES.providers_disable,
7287 Self::LogoutAuthRequest(_) => AGENT_METHOD_NAMES.auth_logout,
7288 #[cfg(feature = "unstable_auth_status")]
7289 Self::AuthStatusRequest(_) => AGENT_METHOD_NAMES.auth_status,
7290 Self::NewSessionRequest(_) => AGENT_METHOD_NAMES.session_new,
7291 Self::ListSessionsRequest(_) => AGENT_METHOD_NAMES.session_list,
7292 Self::DeleteSessionRequest(_) => AGENT_METHOD_NAMES.session_delete,
7293 #[cfg(feature = "unstable_session_fork")]
7294 Self::ForkSessionRequest(_) => AGENT_METHOD_NAMES.session_fork,
7295 #[cfg(feature = "unstable_session_archive")]
7296 Self::ArchiveSessionRequest(_) => AGENT_METHOD_NAMES.session_archive,
7297 #[cfg(feature = "unstable_session_archive")]
7298 Self::UnarchiveSessionRequest(_) => AGENT_METHOD_NAMES.session_unarchive,
7299 #[cfg(feature = "unstable_session_status")]
7300 Self::SessionStatusRequest(_) => AGENT_METHOD_NAMES.session_status,
7301 #[cfg(feature = "unstable_session_set_title")]
7302 Self::SetSessionTitleRequest(_) => AGENT_METHOD_NAMES.session_set_title,
7303 Self::ResumeSessionRequest(_) => AGENT_METHOD_NAMES.session_resume,
7304 Self::CloseSessionRequest(_) => AGENT_METHOD_NAMES.session_close,
7305 Self::CancelToolCallRequest(_) => AGENT_METHOD_NAMES.tool_call_cancel,
7306 Self::CancelTerminalRequest(_) => AGENT_METHOD_NAMES.terminal_cancel,
7307 Self::SetSessionConfigOptionRequest(_) => AGENT_METHOD_NAMES.session_set_config_option,
7308 Self::PromptRequest(_) => AGENT_METHOD_NAMES.session_prompt,
7309 Self::ExtMethodRequest(ext_request) => &ext_request.method,
7310 }
7311 }
7312}
7313
7314#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
7321#[derive(Clone, Debug, Serialize, Deserialize)]
7322#[serde(untagged)]
7323#[cfg_attr(feature = "schemars", schemars(inline))]
7324#[non_exhaustive]
7325pub enum AgentResponse {
7326 InitializeResponse(Box<InitializeResponse>),
7328 LoginAuthResponse(#[serde(default)] Box<LoginAuthResponse>),
7330 #[cfg(feature = "unstable_llm_providers")]
7332 ListProvidersResponse(Box<ListProvidersResponse>),
7333 #[cfg(feature = "unstable_llm_providers")]
7335 SetProviderResponse(#[serde(default)] Box<SetProviderResponse>),
7336 #[cfg(feature = "unstable_llm_providers")]
7338 DisableProviderResponse(#[serde(default)] Box<DisableProviderResponse>),
7339 LogoutAuthResponse(#[serde(default)] Box<LogoutAuthResponse>),
7341 #[cfg(feature = "unstable_auth_status")]
7343 AuthStatusResponse(Box<AuthStatusResponse>),
7344 NewSessionResponse(Box<NewSessionResponse>),
7346 ListSessionsResponse(Box<ListSessionsResponse>),
7348 DeleteSessionResponse(#[serde(default)] Box<DeleteSessionResponse>),
7350 #[cfg(feature = "unstable_session_fork")]
7352 ForkSessionResponse(Box<ForkSessionResponse>),
7353 #[cfg(feature = "unstable_session_archive")]
7355 ArchiveSessionResponse(#[serde(default)] Box<ArchiveSessionResponse>),
7356 #[cfg(feature = "unstable_session_archive")]
7358 UnarchiveSessionResponse(#[serde(default)] Box<UnarchiveSessionResponse>),
7359 #[cfg(feature = "unstable_session_status")]
7361 SessionStatusResponse(Box<SessionStatusResponse>),
7362 #[cfg(feature = "unstable_session_set_title")]
7364 SetSessionTitleResponse(Box<SetSessionTitleResponse>),
7365 ResumeSessionResponse(#[serde(default)] Box<ResumeSessionResponse>),
7367 CloseSessionResponse(#[serde(default)] Box<CloseSessionResponse>),
7369 CancelToolCallResponse(#[serde(default)] Box<CancelToolCallResponse>),
7371 CancelTerminalResponse(#[serde(default)] Box<CancelTerminalResponse>),
7373 SetSessionConfigOptionResponse(Box<SetSessionConfigOptionResponse>),
7375 PromptResponse(Box<PromptResponse>),
7377 ExtMethodResponse(Box<ExtResponse>),
7379}
7380
7381#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
7388#[derive(Clone, Debug, Serialize, Deserialize)]
7389#[serde(untagged)]
7390#[cfg_attr(feature = "schemars", schemars(inline))]
7391#[non_exhaustive]
7392pub enum ClientNotification {
7393 CancelSessionNotification(Box<CancelSessionNotification>),
7424 #[cfg(feature = "unstable_mcp_over_acp")]
7430 MessageMcpNotification(Box<MessageMcpNotification>),
7431 ExtNotification(Box<ExtNotification>),
7438}
7439
7440impl ClientNotification {
7441 #[must_use]
7443 pub fn method(&self) -> &str {
7444 match self {
7445 Self::CancelSessionNotification(_) => AGENT_METHOD_NAMES.session_cancel,
7446 #[cfg(feature = "unstable_mcp_over_acp")]
7447 Self::MessageMcpNotification(_) => AGENT_METHOD_NAMES.mcp_message,
7448 Self::ExtNotification(ext_notification) => &ext_notification.method,
7449 }
7450 }
7451}
7452
7453#[cfg(test)]
7454mod test_serialization {
7455 use std::path::PathBuf;
7456
7457 use super::*;
7458 use serde_json::json;
7459
7460 fn test_meta() -> Meta {
7461 json!({ "source": "test" }).as_object().unwrap().clone()
7462 }
7463
7464 #[test]
7465 fn list_sessions_cursor_and_session_directories_are_strict() {
7466 for cursor in [json!(null), json!("next")] {
7467 let response = json!({ "sessions": [], "nextCursor": cursor });
7468 serde_json::from_value::<ListSessionsResponse>(response).unwrap();
7469 }
7470 let malformed = json!({ "sessions": [], "nextCursor": 42 });
7471 assert!(serde_json::from_value::<ListSessionsResponse>(malformed).is_err());
7472
7473 let session = |dirs: serde_json::Value| json!({ "sessionId": "s", "cwd": "/w", "additionalDirectories": dirs });
7474 let info: SessionInfo = serde_json::from_value(session(json!(null))).unwrap();
7475 assert_eq!(info.additional_directories, Vec::<AbsolutePath>::new());
7476 for bad in [json!(["/a", 42]), json!("/a")] {
7477 assert!(serde_json::from_value::<SessionInfo>(session(bad)).is_err());
7478 }
7479 let response = json!({ "sessions": [session(json!(["/a", 42]))] });
7481 let response: ListSessionsResponse = serde_json::from_value(response).unwrap();
7482 assert_eq!(response.sessions, Vec::<SessionInfo>::new());
7483 }
7484
7485 #[test]
7486 fn other_fallbacks_preserve_optional_fields_verbatim() {
7487 let valid = json!({ "type": "_custom", "methodId": "m", "name": "M",
7488 "description": "d", "_meta": { "k": 1 } });
7489 let method: AuthMethod = serde_json::from_value(valid.clone()).unwrap();
7490 assert_eq!(method.description(), Some("d"));
7491 assert_eq!(
7492 method.meta(),
7493 Some(&json!({ "k": 1 }).as_object().unwrap().clone())
7494 );
7495 assert_eq!(serde_json::to_value(&method).unwrap(), valid);
7496
7497 let malformed = json!({ "type": "_custom", "methodId": "m", "name": "M",
7499 "description": 42, "_meta": "bad", "extra": true });
7500 let method: AuthMethod = serde_json::from_value(malformed.clone()).unwrap();
7501 assert_eq!(method.description(), None);
7502 assert_eq!(method.meta(), None);
7503 assert_eq!(serde_json::to_value(&method).unwrap(), malformed);
7504
7505 let replay = json!({ "type": "_custom", "_meta": "bad", "cursor": 3 });
7506 let decoded: ReplayFrom = serde_json::from_value(replay.clone()).unwrap();
7507 assert_eq!(serde_json::to_value(&decoded).unwrap(), replay);
7508 }
7509
7510 fn serialized_meta_key_count(value: &impl serde::Serialize) -> usize {
7511 serde_json::to_string(value)
7512 .unwrap()
7513 .matches("\"_meta\"")
7514 .count()
7515 }
7516
7517 #[test]
7518 fn prompt_response_without_metadata_round_trips() {
7519 let response = PromptResponse::new(MessageId::new("message-1"));
7520 let serialized = serde_json::to_value(&response).unwrap();
7521 assert_eq!(serialized, json!({ "messageId": "message-1" }));
7522 assert_eq!(
7523 serde_json::from_value::<PromptResponse>(serialized).unwrap(),
7524 response
7525 );
7526 assert_eq!(
7527 serde_json::from_value::<PromptResponse>(json!({
7528 "messageId": "message-1",
7529 "_meta": null
7530 }))
7531 .unwrap(),
7532 response
7533 );
7534 }
7535
7536 #[test]
7537 fn prompt_response_requires_message_id_and_preserves_metadata_tolerance() {
7538 let response = PromptResponse::new("message-1").meta(test_meta());
7539 let serialized = serde_json::to_value(&response).unwrap();
7540 assert_eq!(
7541 serialized,
7542 json!({
7543 "messageId": "message-1",
7544 "_meta": { "source": "test" }
7545 })
7546 );
7547
7548 let deserialized: PromptResponse = serde_json::from_value(serialized).unwrap();
7549 assert_eq!(deserialized.message_id, MessageId::new("message-1"));
7550 assert_eq!(deserialized.meta, Some(test_meta()));
7551
7552 let malformed_meta: PromptResponse = serde_json::from_value(json!({
7553 "messageId": "message-2",
7554 "_meta": false
7555 }))
7556 .unwrap();
7557 assert_eq!(malformed_meta.message_id, MessageId::new("message-2"));
7558 assert_eq!(malformed_meta.meta, None);
7559
7560 for invalid in [
7561 json!({}),
7562 json!({ "messageId": null }),
7563 json!({ "messageId": 1 }),
7564 json!({ "messageId": false }),
7565 json!({ "messageId": {} }),
7566 json!({ "messageId": [] }),
7567 ] {
7568 assert!(
7569 serde_json::from_value::<PromptResponse>(invalid).is_err(),
7570 "missing, null, and non-string message IDs must be rejected"
7571 );
7572 }
7573 }
7574
7575 #[cfg(feature = "schemars")]
7576 #[test]
7577 fn prompt_response_schema_requires_non_null_string_message_id_reference() {
7578 let schema = serde_json::to_value(schemars::schema_for!(PromptResponse)).unwrap();
7579
7580 assert_eq!(schema["required"], json!(["messageId"]));
7581 assert_eq!(
7582 schema["properties"]["messageId"]["$ref"],
7583 "#/$defs/MessageId"
7584 );
7585 assert_eq!(schema["$defs"]["MessageId"]["type"], "string");
7586 }
7587
7588 #[test]
7589 fn test_initialize_capabilities_default_on_malformed_values() {
7590 let request: InitializeRequest = serde_json::from_value(json!({
7591 "protocolVersion": 2,
7592 "capabilities": false,
7593 "info": {
7594 "name": "client",
7595 "version": "1.0.0"
7596 }
7597 }))
7598 .unwrap();
7599 assert_eq!(request.capabilities, ClientCapabilities::default());
7600
7601 let response: InitializeResponse = serde_json::from_value(json!({
7602 "protocolVersion": 2,
7603 "capabilities": false,
7604 "info": {
7605 "name": "agent",
7606 "version": "1.0.0"
7607 }
7608 }))
7609 .unwrap();
7610 assert_eq!(response.capabilities, AgentCapabilities::default());
7611 }
7612
7613 #[test]
7614 fn test_agent_capabilities_default_on_malformed_values() {
7615 let capabilities: AgentCapabilities = serde_json::from_value(json!({
7616 "session": false,
7617 "auth": false
7618 }))
7619 .unwrap();
7620
7621 assert!(capabilities.session.is_none());
7622 assert_eq!(capabilities.auth, None);
7623 }
7624
7625 #[test]
7626 fn test_mcp_server_stdio_serialization() {
7627 let server = McpServer::Stdio(
7628 McpServerStdio::new("test-server", "/usr/bin/server")
7629 .args(vec!["--port".to_string(), "3000".to_string()])
7630 .env(vec![EnvVariable::new("API_KEY", "secret123")]),
7631 );
7632
7633 let json = serde_json::to_value(&server).unwrap();
7634 assert_eq!(
7635 json,
7636 json!({
7637 "type": "stdio",
7638 "name": "test-server",
7639 "command": "/usr/bin/server",
7640 "args": ["--port", "3000"],
7641 "env": [
7642 {
7643 "name": "API_KEY",
7644 "value": "secret123"
7645 }
7646 ]
7647 })
7648 );
7649
7650 let deserialized: McpServer = serde_json::from_value(json).unwrap();
7651 match deserialized {
7652 McpServer::Stdio(McpServerStdio {
7653 name,
7654 command,
7655 args,
7656 env,
7657 meta: _,
7658 }) => {
7659 assert_eq!(name, "test-server");
7660 assert_eq!(command, AbsolutePath::new("/usr/bin/server"));
7661 assert_eq!(args, vec!["--port", "3000"]);
7662 assert_eq!(env.len(), 1);
7663 assert_eq!(env[0].name, "API_KEY");
7664 assert_eq!(env[0].value, "secret123");
7665 }
7666 _ => panic!("Expected Stdio variant"),
7667 }
7668 }
7669
7670 #[test]
7671 fn test_mcp_server_empty_arrays_are_optional() {
7672 let stdio = McpServer::Stdio(McpServerStdio::new("test-server", "/usr/bin/server"));
7673 assert_eq!(
7674 serde_json::to_value(&stdio).unwrap(),
7675 json!({
7676 "type": "stdio",
7677 "name": "test-server",
7678 "command": "/usr/bin/server"
7679 })
7680 );
7681
7682 let McpServer::Stdio(McpServerStdio { args, env, .. }) =
7683 serde_json::from_value::<McpServer>(json!({
7684 "type": "stdio",
7685 "name": "test-server",
7686 "command": "/usr/bin/server"
7687 }))
7688 .unwrap()
7689 else {
7690 panic!("Expected Stdio variant");
7691 };
7692 assert_eq!(args, Vec::<String>::new());
7693 assert_eq!(env, []);
7694
7695 let http = McpServer::Http(McpServerHttp::new("http-server", "https://api.example.com"));
7696 assert_eq!(
7697 serde_json::to_value(&http).unwrap(),
7698 json!({
7699 "type": "http",
7700 "name": "http-server",
7701 "url": "https://api.example.com"
7702 })
7703 );
7704
7705 let McpServer::Http(McpServerHttp { headers, .. }) =
7706 serde_json::from_value::<McpServer>(json!({
7707 "type": "http",
7708 "name": "http-server",
7709 "url": "https://api.example.com"
7710 }))
7711 .unwrap()
7712 else {
7713 panic!("Expected Http variant");
7714 };
7715 assert_eq!(headers, []);
7716 }
7717
7718 #[test]
7719 fn test_mcp_server_unknown_transport_serialization() {
7720 let json = json!({
7721 "type": "websocket",
7722 "name": "future-server",
7723 "url": "wss://example.com/mcp",
7724 "protocolVersion": "2026-01-01"
7725 });
7726
7727 let deserialized: McpServer = serde_json::from_value(json.clone()).unwrap();
7728 let McpServer::Other(OtherMcpServer { type_, fields }) = &deserialized else {
7729 panic!("Expected Other variant");
7730 };
7731
7732 assert_eq!(type_, "websocket");
7733 assert_eq!(fields["name"], "future-server");
7734 assert_eq!(fields["url"], "wss://example.com/mcp");
7735 assert_eq!(fields["protocolVersion"], "2026-01-01");
7736 assert_eq!(serde_json::to_value(&deserialized).unwrap(), json);
7737 }
7738
7739 #[test]
7740 fn test_mcp_server_stdio_requires_type() {
7741 let result = serde_json::from_value::<McpServer>(json!({
7742 "name": "test-server",
7743 "command": "/usr/bin/server",
7744 "args": [],
7745 "env": []
7746 }));
7747
7748 assert!(result.is_err());
7749 }
7750
7751 #[test]
7752 fn test_mcp_server_unknown_does_not_hide_malformed_known_transport() {
7753 let result = serde_json::from_value::<McpServer>(json!({
7754 "type": "stdio",
7755 "name": "test-server",
7756 "args": [],
7757 "env": []
7758 }));
7759
7760 assert!(result.is_err());
7761 }
7762
7763 #[test]
7764 fn test_mcp_server_http_serialization() {
7765 let server = McpServer::Http(
7766 McpServerHttp::new("http-server", "https://api.example.com").headers(vec![
7767 HttpHeader::new("Authorization", "Bearer token123"),
7768 HttpHeader::new("Content-Type", "application/json"),
7769 ]),
7770 );
7771
7772 let json = serde_json::to_value(&server).unwrap();
7773 assert_eq!(
7774 json,
7775 json!({
7776 "type": "http",
7777 "name": "http-server",
7778 "url": "https://api.example.com",
7779 "headers": [
7780 {
7781 "name": "Authorization",
7782 "value": "Bearer token123"
7783 },
7784 {
7785 "name": "Content-Type",
7786 "value": "application/json"
7787 }
7788 ]
7789 })
7790 );
7791
7792 let deserialized: McpServer = serde_json::from_value(json).unwrap();
7793 match deserialized {
7794 McpServer::Http(McpServerHttp {
7795 name,
7796 url,
7797 headers,
7798 meta: _,
7799 }) => {
7800 assert_eq!(name, "http-server");
7801 assert_eq!(url, "https://api.example.com");
7802 assert_eq!(headers.len(), 2);
7803 assert_eq!(headers[0].name, "Authorization");
7804 assert_eq!(headers[0].value, "Bearer token123");
7805 assert_eq!(headers[1].name, "Content-Type");
7806 assert_eq!(headers[1].value, "application/json");
7807 }
7808 _ => panic!("Expected Http variant"),
7809 }
7810 }
7811
7812 #[cfg(feature = "schemars")]
7813 #[test]
7814 fn mcp_server_http_schema_marks_url_as_uri() {
7815 let schema = serde_json::to_value(schemars::schema_for!(McpServerHttp)).unwrap();
7816
7817 assert_eq!(schema["properties"]["url"]["format"], "uri");
7818 }
7819
7820 #[cfg(feature = "unstable_mcp_over_acp")]
7821 #[test]
7822 fn test_client_mcp_message_method_names() {
7823 use serde_json::json;
7824
7825 assert_eq!(AGENT_METHOD_NAMES.mcp_message, "mcp/message");
7826
7827 let notification =
7828 MessageMcpNotification::new("server-1", "req-1", "notifications/progress");
7829 assert_eq!(
7830 ClientNotification::MessageMcpNotification(Box::new(notification.clone())).method(),
7831 "mcp/message"
7832 );
7833 assert_eq!(
7834 serde_json::to_value(notification).unwrap(),
7835 json!({
7836 "serverId": "server-1",
7837 "requestId": "req-1",
7838 "method": "notifications/progress"
7839 })
7840 );
7841 let notification: MessageMcpNotification = serde_json::from_value(json!({
7842 "serverId": "server-1", "requestId": "req-1", "method": "notifications/progress",
7843 "params": null, "_meta": null
7844 }))
7845 .unwrap();
7846 assert_eq!(notification.params, None);
7847 assert_eq!(notification.meta, None);
7848 for key in ["serverId", "requestId", "method"] {
7849 let mut value = json!({"serverId":"server-1", "requestId":"req-1", "method":"notifications/progress"});
7850 value.as_object_mut().unwrap().remove(key);
7851 assert!(serde_json::from_value::<MessageMcpNotification>(value).is_err());
7852 }
7853 }
7854
7855 #[test]
7856 fn test_auth_method_names() {
7857 assert_eq!(AGENT_METHOD_NAMES.auth_login, "auth/login");
7858 assert_eq!(AGENT_METHOD_NAMES.auth_logout, "auth/logout");
7859
7860 assert_eq!(
7861 ClientRequest::LoginAuthRequest(Box::new(LoginAuthRequest::new("agent-login")))
7862 .method(),
7863 "auth/login"
7864 );
7865 assert_eq!(
7866 ClientRequest::LogoutAuthRequest(Box::new(LogoutAuthRequest::new())).method(),
7867 "auth/logout"
7868 );
7869 }
7870
7871 #[test]
7872 fn test_session_config_option_category_known_variants() {
7873 assert_eq!(
7875 serde_json::to_value(&SessionConfigOptionCategory::Mode).unwrap(),
7876 json!("mode")
7877 );
7878 assert_eq!(
7879 serde_json::to_value(&SessionConfigOptionCategory::Model).unwrap(),
7880 json!("model")
7881 );
7882 assert_eq!(
7883 serde_json::to_value(&SessionConfigOptionCategory::ModelConfig).unwrap(),
7884 json!("model_config")
7885 );
7886 assert_eq!(
7887 serde_json::to_value(&SessionConfigOptionCategory::ThoughtLevel).unwrap(),
7888 json!("thought_level")
7889 );
7890
7891 assert_eq!(
7893 serde_json::from_str::<SessionConfigOptionCategory>("\"mode\"").unwrap(),
7894 SessionConfigOptionCategory::Mode
7895 );
7896 assert_eq!(
7897 serde_json::from_str::<SessionConfigOptionCategory>("\"model\"").unwrap(),
7898 SessionConfigOptionCategory::Model
7899 );
7900 assert_eq!(
7901 serde_json::from_str::<SessionConfigOptionCategory>("\"model_config\"").unwrap(),
7902 SessionConfigOptionCategory::ModelConfig
7903 );
7904 assert_eq!(
7905 serde_json::from_str::<SessionConfigOptionCategory>("\"thought_level\"").unwrap(),
7906 SessionConfigOptionCategory::ThoughtLevel
7907 );
7908 }
7909
7910 #[test]
7911 fn test_session_config_option_category_unknown_variants() {
7912 let unknown: SessionConfigOptionCategory =
7914 serde_json::from_str("\"some_future_category\"").unwrap();
7915 assert_eq!(
7916 unknown,
7917 SessionConfigOptionCategory::Other("some_future_category".to_string())
7918 );
7919
7920 let json = serde_json::to_value(&unknown).unwrap();
7922 assert_eq!(json, json!("some_future_category"));
7923 }
7924
7925 #[test]
7926 fn test_session_config_option_category_custom_categories() {
7927 let custom: SessionConfigOptionCategory =
7929 serde_json::from_str("\"_my_custom_category\"").unwrap();
7930 assert_eq!(
7931 custom,
7932 SessionConfigOptionCategory::Other("_my_custom_category".to_string())
7933 );
7934
7935 let json = serde_json::to_value(&custom).unwrap();
7937 assert_eq!(json, json!("_my_custom_category"));
7938
7939 let deserialized: SessionConfigOptionCategory = serde_json::from_value(json).unwrap();
7941 assert_eq!(
7942 deserialized,
7943 SessionConfigOptionCategory::Other("_my_custom_category".to_string()),
7944 );
7945 }
7946
7947 fn test_config_option() -> SessionConfigOption {
7948 SessionConfigOption::select(
7949 "mode",
7950 "Mode",
7951 "ask",
7952 vec![SessionConfigSelectOption::new("ask", "Ask")],
7953 )
7954 }
7955
7956 #[test]
7957 fn test_session_response_initial_state_default_empty_and_skip_serializing() {
7958 assert_eq!(
7959 serde_json::to_value(NewSessionResponse::new("sess")).unwrap(),
7960 json!({ "sessionId": "sess" })
7961 );
7962 assert_eq!(
7963 serde_json::to_value(ResumeSessionResponse::new()).unwrap(),
7964 json!({})
7965 );
7966 #[cfg(feature = "unstable_session_fork")]
7967 assert_eq!(
7968 serde_json::to_value(ForkSessionResponse::new("fork")).unwrap(),
7969 json!({ "sessionId": "fork" })
7970 );
7971
7972 let json = serde_json::to_value(
7973 NewSessionResponse::new("sess").config_options(vec![test_config_option()]),
7974 )
7975 .unwrap();
7976 assert_eq!(json["configOptions"].as_array().unwrap().len(), 1);
7977 }
7978
7979 #[test]
7980 fn test_session_response_config_options_deserialize_missing_null_and_invalid() {
7981 let missing: NewSessionResponse =
7982 serde_json::from_value(json!({ "sessionId": "sess" })).unwrap();
7983 assert_eq!(missing.config_options, []);
7984
7985 let null: NewSessionResponse = serde_json::from_value(json!({
7986 "sessionId": "sess",
7987 "configOptions": null
7988 }))
7989 .unwrap();
7990 assert_eq!(null.config_options, []);
7991
7992 let wrong_shape: NewSessionResponse = serde_json::from_value(json!({
7993 "sessionId": "sess",
7994 "configOptions": "oops"
7995 }))
7996 .unwrap();
7997 assert_eq!(wrong_shape.config_options, []);
7998
7999 let valid_option = serde_json::to_value(test_config_option()).unwrap();
8000 let mixed: NewSessionResponse = serde_json::from_value(json!({
8001 "sessionId": "sess",
8002 "configOptions": ["oops", valid_option]
8003 }))
8004 .unwrap();
8005 assert_eq!(mixed.config_options.len(), 1);
8006
8007 let resume: ResumeSessionResponse = serde_json::from_value(json!({})).unwrap();
8008 assert_eq!(resume.config_options, []);
8009 #[cfg(feature = "unstable_session_fork")]
8010 {
8011 let fork: ForkSessionResponse =
8012 serde_json::from_value(json!({ "sessionId": "fork" })).unwrap();
8013 assert_eq!(fork.config_options, []);
8014 }
8015 }
8016
8017 #[test]
8018 fn test_session_response_available_commands_roundtrip_with_config_options() {
8019 use super::super::{AvailableCommandInput, Command, TextCommandInput};
8020
8021 let commands = vec![
8022 AvailableCommand::Command(
8023 Command::new("web", "Search the web")
8024 .input(AvailableCommandInput::Text(TextCommandInput::new("Query"))),
8025 ),
8026 AvailableCommand::Command(Command::new("test", "Run project tests")),
8027 ];
8028 let mut expected = json!({
8029 "sessionId": "sess",
8030 "configOptions": [test_config_option()],
8031 "availableCommands": [
8032 {
8033 "type": "command",
8034 "name": "web",
8035 "description": "Search the web",
8036 "input": { "type": "text", "hint": "Query" }
8037 },
8038 { "type": "command", "name": "test", "description": "Run project tests" }
8039 ]
8040 });
8041
8042 let new = NewSessionResponse::new("sess")
8043 .config_options(vec![test_config_option()])
8044 .available_commands(commands.clone());
8045 assert_eq!(serde_json::to_value(&new).unwrap(), expected);
8046 assert_eq!(
8047 serde_json::from_value::<NewSessionResponse>(expected.clone()).unwrap(),
8048 new
8049 );
8050
8051 expected.as_object_mut().unwrap().remove("sessionId");
8052 let resume = ResumeSessionResponse::new()
8053 .config_options(vec![test_config_option()])
8054 .available_commands(commands.clone());
8055 assert_eq!(serde_json::to_value(&resume).unwrap(), expected);
8056 assert_eq!(
8057 serde_json::from_value::<ResumeSessionResponse>(expected.clone()).unwrap(),
8058 resume
8059 );
8060
8061 #[cfg(feature = "unstable_session_fork")]
8062 {
8063 expected["sessionId"] = json!("fork");
8064 let fork = ForkSessionResponse::new("fork")
8065 .config_options(vec![test_config_option()])
8066 .available_commands(commands);
8067 assert_eq!(serde_json::to_value(&fork).unwrap(), expected);
8068 assert_eq!(
8069 serde_json::from_value::<ForkSessionResponse>(expected).unwrap(),
8070 fork
8071 );
8072 }
8073 }
8074
8075 #[test]
8076 fn test_session_response_available_commands_deserialize_missing_null_and_invalid() {
8077 let command =
8078 AvailableCommand::Command(super::super::Command::new("test", "Run project tests"));
8079 let config_options = vec![test_config_option()];
8080
8081 for (commands, expected) in [
8082 (None, vec![]),
8083 (Some(json!(null)), vec![]),
8084 (Some(json!([])), vec![]),
8085 (Some(json!("oops")), vec![]),
8086 (Some(json!({})), vec![]),
8087 (Some(json!([null, {}, { "name": "invalid" }])), vec![]),
8088 (
8089 Some(json!([{ "name": "test", "description": "Run project tests" }])),
8090 vec![],
8091 ),
8092 (
8093 Some(json!([null, command, { "name": "invalid" }])),
8094 vec![command.clone()],
8095 ),
8096 ] {
8097 let mut value = json!({
8098 "sessionId": "sess",
8099 "configOptions": config_options
8100 });
8101 if let Some(commands) = commands {
8102 value["availableCommands"] = commands;
8103 }
8104
8105 let new: NewSessionResponse = serde_json::from_value(value.clone()).unwrap();
8106 assert_eq!(new.available_commands, expected);
8107 assert_eq!(new.config_options, config_options);
8108
8109 #[cfg(feature = "unstable_session_fork")]
8110 {
8111 let fork: ForkSessionResponse = serde_json::from_value(value.clone()).unwrap();
8112 assert_eq!(fork.available_commands, expected);
8113 assert_eq!(fork.config_options, config_options);
8114 }
8115
8116 value.as_object_mut().unwrap().remove("sessionId");
8117 let resume: ResumeSessionResponse = serde_json::from_value(value).unwrap();
8118 assert_eq!(resume.available_commands, expected);
8119 assert_eq!(resume.config_options, config_options);
8120 }
8121
8122 let resume: ResumeSessionResponse = serde_json::from_value(json!(null)).unwrap();
8123 assert_eq!(resume.available_commands, []);
8124 }
8125
8126 #[test]
8127 fn test_resume_session_replay_from_serialization() {
8128 assert_eq!(
8129 serde_json::to_value(ResumeSessionRequest::new(
8130 "sess_abc123",
8131 "/home/user/project"
8132 ))
8133 .unwrap(),
8134 json!({
8135 "sessionId": "sess_abc123",
8136 "cwd": "/home/user/project"
8137 })
8138 );
8139 assert_eq!(
8140 serde_json::to_value(
8141 ResumeSessionRequest::new("sess_abc123", "/home/user/project")
8142 .replay_from(ReplayFrom::from(ReplayFromStart::new()))
8143 )
8144 .unwrap(),
8145 json!({
8146 "sessionId": "sess_abc123",
8147 "cwd": "/home/user/project",
8148 "replayFrom": {
8149 "type": "start"
8150 }
8151 })
8152 );
8153
8154 let replay: ResumeSessionRequest = serde_json::from_value(json!({
8155 "sessionId": "sess_abc123",
8156 "cwd": "/home/user/project",
8157 "replayFrom": {
8158 "type": "start"
8159 }
8160 }))
8161 .unwrap();
8162 assert!(matches!(replay.replay_from, Some(ReplayFrom::Start(_))));
8163
8164 let none: ResumeSessionRequest = serde_json::from_value(json!({
8165 "sessionId": "sess_abc123",
8166 "cwd": "/home/user/project",
8167 "replayFrom": null
8168 }))
8169 .unwrap();
8170 assert!(none.replay_from.is_none());
8171 }
8172
8173 #[test]
8174 fn test_auth_method_agent_serialization() {
8175 let method = AuthMethod::Agent(AuthMethodAgent::new("default-auth", "Default Auth"));
8176
8177 let json = serde_json::to_value(&method).unwrap();
8178 assert_eq!(
8179 json,
8180 json!({
8181 "methodId": "default-auth",
8182 "name": "Default Auth",
8183 "type": "agent"
8184 })
8185 );
8186 assert!(!json.as_object().unwrap().contains_key("description"));
8188
8189 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
8190 match deserialized {
8191 AuthMethod::Agent(AuthMethodAgent {
8192 method_id, name, ..
8193 }) => {
8194 assert_eq!(method_id.0.as_ref(), "default-auth");
8195 assert_eq!(name, "Default Auth");
8196 }
8197 _ => panic!("Expected Agent variant"),
8198 }
8199 }
8200
8201 #[test]
8202 fn test_auth_method_agent_deserialization() {
8203 let json = json!({
8204 "methodId": "agent-auth",
8205 "name": "Agent Auth",
8206 "type": "agent"
8207 });
8208
8209 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
8210 assert!(matches!(deserialized, AuthMethod::Agent(_)));
8211 }
8212
8213 #[test]
8214 fn test_auth_method_agent_requires_type() {
8215 assert!(
8216 serde_json::from_value::<AuthMethod>(json!({
8217 "methodId": "agent-auth",
8218 "name": "Agent Auth"
8219 }))
8220 .is_err()
8221 );
8222 }
8223
8224 #[test]
8225 fn test_auth_method_agent_rejects_null_type() {
8226 assert!(
8227 serde_json::from_value::<AuthMethod>(json!({
8228 "methodId": "agent-auth",
8229 "name": "Agent Auth",
8230 "type": null
8231 }))
8232 .is_err()
8233 );
8234 }
8235
8236 #[test]
8237 fn test_auth_method_unknown_does_not_hide_malformed_agent() {
8238 assert!(
8239 serde_json::from_value::<AuthMethod>(json!({
8240 "methodId": "agent-auth",
8241 "type": "agent"
8242 }))
8243 .is_err()
8244 );
8245 assert!(
8246 serde_json::from_value::<AuthMethod>(json!({
8247 "methodId": "api-key",
8248 "type": "env_var",
8249 "vars": [{"name": "API_KEY"}]
8250 }))
8251 .is_err()
8252 );
8253 }
8254
8255 #[test]
8256 fn test_auth_method_unknown_variant_roundtrip() {
8257 let method: AuthMethod = serde_json::from_value(json!({
8258 "methodId": "oauth",
8259 "name": "OAuth",
8260 "type": "_oauth",
8261 "authorizationUrl": "https://example.com/auth"
8262 }))
8263 .unwrap();
8264
8265 assert_eq!(method.method_id().0.as_ref(), "oauth");
8266 assert_eq!(method.name(), "OAuth");
8267 let AuthMethod::Other(unknown) = method else {
8268 panic!("expected unknown auth method");
8269 };
8270 assert_eq!(unknown.type_, "_oauth");
8271 assert_eq!(
8272 unknown.fields.get("authorizationUrl"),
8273 Some(&json!("https://example.com/auth"))
8274 );
8275
8276 assert_eq!(
8277 serde_json::to_value(AuthMethod::Other(unknown)).unwrap(),
8278 json!({
8279 "methodId": "oauth",
8280 "name": "OAuth",
8281 "type": "_oauth",
8282 "authorizationUrl": "https://example.com/auth"
8283 })
8284 );
8285 }
8286
8287 #[test]
8288 fn test_auth_method_unknown_does_not_hide_malformed_known_variant() {
8289 assert!(
8290 serde_json::from_value::<AuthMethod>(json!({
8291 "methodId": "terminal-auth",
8292 "type": "terminal"
8293 }))
8294 .is_err()
8295 );
8296 }
8297
8298 #[test]
8299 fn test_tool_call_and_terminal_cancel_serialization() {
8300 assert_eq!(AGENT_METHOD_NAMES.tool_call_cancel, "tool_call/cancel");
8301 assert_eq!(AGENT_METHOD_NAMES.terminal_cancel, "terminal/cancel");
8302 assert_eq!(
8303 ClientRequest::CancelToolCallRequest(Box::new(CancelToolCallRequest::new(
8304 "sess_abc123",
8305 "call_012"
8306 )))
8307 .method(),
8308 "tool_call/cancel"
8309 );
8310 assert_eq!(
8311 ClientRequest::CancelTerminalRequest(Box::new(CancelTerminalRequest::new(
8312 "sess_abc123",
8313 "term_003"
8314 )))
8315 .method(),
8316 "terminal/cancel"
8317 );
8318 assert_eq!(
8319 serde_json::to_value(CancelToolCallRequest::new("sess_abc123", "call_012")).unwrap(),
8320 json!({"sessionId": "sess_abc123", "toolCallId": "call_012"})
8321 );
8322 assert_eq!(
8323 serde_json::to_value(CancelTerminalRequest::new("sess_abc123", "term_003")).unwrap(),
8324 json!({"sessionId": "sess_abc123", "terminalId": "term_003"})
8325 );
8326 assert!(
8327 serde_json::from_value::<CancelToolCallRequest>(json!({"sessionId": "sess_abc123"}))
8328 .is_err()
8329 );
8330 assert_eq!(
8331 serde_json::to_value(CancelTerminalResponse::new()).unwrap(),
8332 json!({})
8333 );
8334 }
8335
8336 #[test]
8337 fn test_session_delete_serialization() {
8338 assert_eq!(AGENT_METHOD_NAMES.session_delete, "session/delete");
8339 assert_eq!(
8340 ClientRequest::DeleteSessionRequest(Box::new(DeleteSessionRequest::new("sess_abc123")))
8341 .method(),
8342 "session/delete"
8343 );
8344 assert_eq!(
8345 serde_json::to_value(DeleteSessionRequest::new("sess_abc123")).unwrap(),
8346 json!({
8347 "sessionId": "sess_abc123"
8348 })
8349 );
8350 assert_eq!(
8351 serde_json::to_value(DeleteSessionResponse::new()).unwrap(),
8352 json!({})
8353 );
8354 assert_eq!(
8355 serde_json::to_value(
8356 SessionCapabilities::new().delete(SessionDeleteCapabilities::new())
8357 )
8358 .unwrap(),
8359 json!({
8360 "delete": {}
8361 })
8362 );
8363 }
8364 #[cfg(feature = "unstable_session_set_title")]
8365 #[test]
8366 fn test_session_set_title_serialization() {
8367 assert_eq!(AGENT_METHOD_NAMES.session_set_title, "session/set_title");
8368 assert_eq!(
8369 ClientRequest::SetSessionTitleRequest(Box::new(SetSessionTitleRequest::new(
8370 "sess_abc123",
8371 "Investigate OAuth timeout"
8372 )))
8373 .method(),
8374 "session/set_title"
8375 );
8376 assert_eq!(
8377 serde_json::to_value(SetSessionTitleRequest::new(
8378 "sess_abc123",
8379 "Investigate OAuth timeout"
8380 ))
8381 .unwrap(),
8382 json!({
8383 "sessionId": "sess_abc123",
8384 "title": "Investigate OAuth timeout"
8385 })
8386 );
8387 assert_eq!(
8388 serde_json::to_value(SetSessionTitleResponse::new("Investigate OAuth timeout"))
8389 .unwrap(),
8390 json!({
8391 "title": "Investigate OAuth timeout"
8392 })
8393 );
8394 assert_eq!(
8395 serde_json::to_value(SessionCapabilities::new().title(SessionTitleCapabilities::new()))
8396 .unwrap(),
8397 json!({
8398 "title": {}
8399 })
8400 );
8401 }
8402
8403 #[cfg(feature = "unstable_session_set_title")]
8404 #[test]
8405 fn test_session_set_title_requires_title() {
8406 for params in [
8407 json!({"sessionId": "sess_abc123"}),
8408 json!({"sessionId": "sess_abc123", "title": null}),
8409 json!({"sessionId": "sess_abc123", "title": 42}),
8410 ] {
8411 assert!(serde_json::from_value::<SetSessionTitleRequest>(params).is_err());
8412 }
8413 for result in [json!({}), json!({"title": null})] {
8414 assert!(serde_json::from_value::<SetSessionTitleResponse>(result).is_err());
8415 }
8416 }
8417
8418 #[cfg(feature = "unstable_session_status")]
8419 #[test]
8420 fn test_session_status_serialization() {
8421 assert_eq!(AGENT_METHOD_NAMES.session_status, "session/status");
8422 assert_eq!(
8423 ClientRequest::SessionStatusRequest(Box::new(SessionStatusRequest::new("sess_abc123")))
8424 .method(),
8425 "session/status"
8426 );
8427 assert_eq!(
8428 serde_json::to_value(SessionStatusRequest::new("sess_abc123")).unwrap(),
8429 json!({"sessionId": "sess_abc123"})
8430 );
8431 assert_eq!(
8432 serde_json::to_value(SessionStatusResponse::new(SessionStatus::Saved)).unwrap(),
8433 json!({"status": "saved"})
8434 );
8435 assert_eq!(
8436 serde_json::to_value(
8437 SessionCapabilities::new().status(SessionStatusCapabilities::new())
8438 )
8439 .unwrap(),
8440 json!({"status": {}})
8441 );
8442 assert_eq!(
8443 serde_json::to_value(SessionStatusResponse::new(SessionStatus::Open).state(
8444 StateUpdate::RequiresAction(crate::v2::RequiresActionStateUpdate::new())
8445 ))
8446 .unwrap(),
8447 json!({
8448 "status": "open",
8449 "state": {"state": "requires_action"}
8450 })
8451 );
8452 for state in [json!(null), json!("running"), json!({"state": 7})] {
8453 let response = serde_json::from_value::<SessionStatusResponse>(
8454 json!({"status": "open", "state": state}),
8455 )
8456 .unwrap();
8457 assert_eq!(response.state, None);
8458 }
8459 }
8460
8461 #[cfg(feature = "unstable_session_status")]
8462 #[test]
8463 fn test_session_status_is_strict_and_open() {
8464 let custom =
8465 serde_json::from_value::<SessionStatusResponse>(json!({"status": "_suspended"}))
8466 .unwrap();
8467 assert_eq!(
8468 custom.status,
8469 SessionStatus::Other("_suspended".to_string())
8470 );
8471 assert_eq!(
8472 serde_json::to_value(&custom).unwrap(),
8473 json!({"status": "_suspended"})
8474 );
8475 for result in [json!({}), json!({"status": null}), json!({"status": 1})] {
8476 assert!(serde_json::from_value::<SessionStatusResponse>(result).is_err());
8477 }
8478 for params in [json!({}), json!({"sessionId": null})] {
8479 assert!(serde_json::from_value::<SessionStatusRequest>(params).is_err());
8480 }
8481 }
8482
8483 #[cfg(feature = "unstable_session_archive")]
8484 #[test]
8485 fn test_session_archive_serialization() {
8486 assert_eq!(AGENT_METHOD_NAMES.session_archive, "session/archive");
8487 assert_eq!(AGENT_METHOD_NAMES.session_unarchive, "session/unarchive");
8488 assert_eq!(
8489 ClientRequest::ArchiveSessionRequest(Box::new(ArchiveSessionRequest::new(
8490 "sess_abc123"
8491 )))
8492 .method(),
8493 "session/archive"
8494 );
8495 assert_eq!(
8496 ClientRequest::UnarchiveSessionRequest(Box::new(UnarchiveSessionRequest::new(
8497 "sess_abc123"
8498 )))
8499 .method(),
8500 "session/unarchive"
8501 );
8502 assert_eq!(
8503 serde_json::to_value(ArchiveSessionRequest::new("sess_abc123")).unwrap(),
8504 json!({"sessionId": "sess_abc123"})
8505 );
8506 assert_eq!(
8507 serde_json::to_value(UnarchiveSessionResponse::new()).unwrap(),
8508 json!({})
8509 );
8510 assert_eq!(
8511 serde_json::to_value(
8512 SessionCapabilities::new().archive(SessionArchiveCapabilities::new())
8513 )
8514 .unwrap(),
8515 json!({"archive": {}})
8516 );
8517 assert_eq!(
8518 serde_json::to_value(
8519 ListSessionsRequest::new()
8520 .cwd("/home/user/project")
8521 .archived(SessionArchiveFilter::Only)
8522 )
8523 .unwrap(),
8524 json!({"cwd": "/home/user/project", "archived": "only"})
8525 );
8526 assert_eq!(
8527 serde_json::to_value(
8528 SessionInfo::new("sess_abc123", "/home/user/project").archived(true)
8529 )
8530 .unwrap(),
8531 json!({"sessionId": "sess_abc123", "cwd": "/home/user/project", "archived": true})
8532 );
8533 }
8534
8535 #[cfg(feature = "unstable_session_archive")]
8536 #[test]
8537 fn test_session_archive_filter_is_strict_and_open() {
8538 for (value, expected) in [
8539 (json!(null), None),
8540 (json!("include"), Some(SessionArchiveFilter::Include)),
8541 (
8542 json!("_recent"),
8543 Some(SessionArchiveFilter::Other("_recent".to_string())),
8544 ),
8545 ] {
8546 let request =
8547 serde_json::from_value::<ListSessionsRequest>(json!({"archived": value})).unwrap();
8548 assert_eq!(request.archived, expected);
8549 }
8550 for value in [json!(true), json!(1), json!({})] {
8551 assert!(
8552 serde_json::from_value::<ListSessionsRequest>(json!({"archived": value})).is_err()
8553 );
8554 }
8555 let info = serde_json::from_value::<SessionInfo>(
8556 json!({"sessionId": "sess_abc123", "cwd": "/home/user/project", "archived": "yes"}),
8557 )
8558 .unwrap();
8559 assert_eq!(info.archived, None);
8560 }
8561
8562 #[test]
8563 fn test_session_additional_directories_serialization() {
8564 assert_eq!(
8565 serde_json::to_value(NewSessionRequest::new("/home/user/project")).unwrap(),
8566 json!({
8567 "cwd": "/home/user/project",
8568 })
8569 );
8570 assert_eq!(
8571 serde_json::to_value(
8572 NewSessionRequest::new("/home/user/project").additional_directories(vec![
8573 PathBuf::from("/home/user/shared-lib"),
8574 PathBuf::from("/home/user/product-docs"),
8575 ])
8576 )
8577 .unwrap(),
8578 json!({
8579 "cwd": "/home/user/project",
8580 "additionalDirectories": [
8581 "/home/user/shared-lib",
8582 "/home/user/product-docs"
8583 ],
8584 })
8585 );
8586 assert_eq!(
8587 serde_json::to_value(ResumeSessionRequest::new(
8588 "sess_abc123",
8589 "/home/user/project"
8590 ))
8591 .unwrap(),
8592 json!({
8593 "sessionId": "sess_abc123",
8594 "cwd": "/home/user/project",
8595 })
8596 );
8597 assert_eq!(
8598 serde_json::from_value::<ResumeSessionRequest>(json!({
8599 "sessionId": "sess_abc123",
8600 "cwd": "/home/user/project"
8601 }))
8602 .unwrap()
8603 .mcp_servers,
8604 Vec::<McpServer>::new()
8605 );
8606 assert_eq!(
8607 serde_json::from_value::<ResumeSessionRequest>(json!({
8608 "sessionId": "sess_abc123",
8609 "cwd": "/home/user/project",
8610 "mcpServers": null
8611 }))
8612 .unwrap()
8613 .mcp_servers,
8614 Vec::<McpServer>::new()
8615 );
8616 assert_eq!(
8617 serde_json::to_value(SessionInfo::new("sess_abc123", "/home/user/project")).unwrap(),
8618 json!({
8619 "sessionId": "sess_abc123",
8620 "cwd": "/home/user/project"
8621 })
8622 );
8623 assert_eq!(
8624 serde_json::to_value(
8625 SessionInfo::new("sess_abc123", "/home/user/project").additional_directories(vec![
8626 PathBuf::from("/home/user/shared-lib"),
8627 PathBuf::from("/home/user/product-docs"),
8628 ])
8629 )
8630 .unwrap(),
8631 json!({
8632 "sessionId": "sess_abc123",
8633 "cwd": "/home/user/project",
8634 "additionalDirectories": [
8635 "/home/user/shared-lib",
8636 "/home/user/product-docs"
8637 ]
8638 })
8639 );
8640 assert_eq!(
8641 serde_json::from_value::<SessionInfo>(json!({
8642 "sessionId": "sess_abc123",
8643 "cwd": "/home/user/project"
8644 }))
8645 .unwrap()
8646 .additional_directories,
8647 Vec::<AbsolutePath>::new()
8648 );
8649 }
8650 #[test]
8651 fn test_session_additional_directories_capabilities_serialization() {
8652 assert_eq!(
8653 serde_json::to_value(
8654 SessionCapabilities::new()
8655 .additional_directories(SessionAdditionalDirectoriesCapabilities::new())
8656 )
8657 .unwrap(),
8658 json!({
8659 "additionalDirectories": {}
8660 })
8661 );
8662 }
8663
8664 #[test]
8665 fn test_session_system_prompt_capabilities_serialization() {
8666 assert_eq!(
8667 serde_json::to_value(
8668 SessionCapabilities::new().system_prompt(SessionSystemPromptCapabilities::new())
8669 )
8670 .unwrap(),
8671 json!({ "systemPrompt": {} })
8672 );
8673 assert_eq!(
8674 serde_json::to_value(
8675 SessionCapabilities::new().system_prompt(
8676 SessionSystemPromptCapabilities::new().mode(
8677 SessionSystemPromptModeCapabilities::new()
8678 .override_(SessionSystemPromptOverrideCapabilities::new())
8679 )
8680 )
8681 )
8682 .unwrap(),
8683 json!({ "systemPrompt": { "mode": { "override": {} } } })
8684 );
8685 assert_eq!(
8686 serde_json::from_value::<SessionCapabilities>(
8687 json!({ "systemPrompt": { "mode": { "override": {} } } })
8688 )
8689 .unwrap()
8690 .system_prompt
8691 .and_then(|capabilities| capabilities.mode)
8692 .and_then(|mode| mode.override_),
8693 Some(SessionSystemPromptOverrideCapabilities::new())
8694 );
8695
8696 assert_eq!(
8698 serde_json::from_value::<SessionCapabilities>(json!({ "systemPrompt": "yes" }))
8699 .unwrap()
8700 .system_prompt,
8701 None
8702 );
8703 assert_eq!(
8704 serde_json::from_value::<SessionCapabilities>(
8705 json!({ "systemPrompt": { "mode": { "override": true } } })
8706 )
8707 .unwrap()
8708 .system_prompt,
8709 Some(
8710 SessionSystemPromptCapabilities::new()
8711 .mode(SessionSystemPromptModeCapabilities::new())
8712 )
8713 );
8714 assert_eq!(
8715 serde_json::from_value::<SessionCapabilities>(
8716 json!({ "systemPrompt": { "mode": "override" } })
8717 )
8718 .unwrap()
8719 .system_prompt,
8720 Some(SessionSystemPromptCapabilities::new())
8721 );
8722 }
8723
8724 #[test]
8725 fn test_system_prompt_lifecycle_request_serialization() {
8726 let system_prompt = SystemPrompt::new(vec![ContentBlock::from(
8727 "You are a code review specialist.",
8728 )]);
8729 let system_prompt_json = json!({
8730 "mode": "append",
8731 "content": [{ "type": "text", "text": "You are a code review specialist." }]
8732 });
8733
8734 let request =
8735 NewSessionRequest::new("/home/user/project").system_prompt(system_prompt.clone());
8736 let request_json = json!({
8737 "cwd": "/home/user/project",
8738 "systemPrompt": system_prompt_json
8739 });
8740 assert_eq!(serde_json::to_value(&request).unwrap(), request_json);
8741 assert_eq!(
8742 serde_json::from_value::<NewSessionRequest>(request_json).unwrap(),
8743 request
8744 );
8745
8746 assert_eq!(
8747 serde_json::to_value(
8748 ResumeSessionRequest::new("sess_abc123", "/home/user/project")
8749 .system_prompt(system_prompt.clone())
8750 )
8751 .unwrap()["systemPrompt"],
8752 system_prompt_json
8753 );
8754 #[cfg(feature = "unstable_session_fork")]
8755 assert_eq!(
8756 serde_json::to_value(
8757 ForkSessionRequest::new("sess_abc123", "/home/user/project")
8758 .system_prompt(system_prompt.clone())
8759 )
8760 .unwrap()["systemPrompt"],
8761 system_prompt_json
8762 );
8763
8764 let override_prompt =
8765 SystemPrompt::new(vec![ContentBlock::from("...")]).mode(SystemPromptMode::Override);
8766 let override_json = json!({
8767 "mode": "override",
8768 "content": [{ "type": "text", "text": "..." }]
8769 });
8770 assert_eq!(
8771 serde_json::to_value(&override_prompt).unwrap(),
8772 override_json
8773 );
8774 assert_eq!(
8775 serde_json::from_value::<SystemPrompt>(override_json).unwrap(),
8776 override_prompt
8777 );
8778 }
8779
8780 #[test]
8783 fn test_system_prompt_new_defaults_to_append() {
8784 let system_prompt = SystemPrompt::new(vec![ContentBlock::from("...")]);
8785 assert_eq!(system_prompt.mode, SystemPromptMode::Append);
8786 assert_eq!(
8787 serde_json::to_value(&system_prompt).unwrap(),
8788 json!({ "mode": "append", "content": [{ "type": "text", "text": "..." }] })
8789 );
8790 }
8791
8792 #[test]
8796 fn test_system_prompt_preserves_unknown_modes() {
8797 for mode in ["_vendor_mode", "prepend"] {
8798 let system_prompt_json =
8799 json!({ "mode": mode, "content": [{ "type": "text", "text": "..." }] });
8800 let system_prompt: SystemPrompt =
8801 serde_json::from_value(system_prompt_json.clone()).unwrap();
8802 assert_eq!(
8803 system_prompt.mode,
8804 SystemPromptMode::Other(mode.to_string())
8805 );
8806 assert_eq!(
8807 serde_json::to_value(&system_prompt).unwrap(),
8808 system_prompt_json
8809 );
8810 }
8811 }
8812
8813 fn assert_system_prompt_is_strict<T>(base: &serde_json::Value)
8817 where
8818 T: serde::de::DeserializeOwned + PartialEq + std::fmt::Debug,
8819 {
8820 let name = std::any::type_name::<T>();
8821 let with = |system_prompt: serde_json::Value| {
8822 let mut request = base.clone();
8823 request["systemPrompt"] = system_prompt;
8824 serde_json::from_value::<T>(request)
8825 };
8826
8827 for system_prompt in [
8828 json!("You are a code review specialist."),
8829 json!({ "content": [{ "type": "text", "text": "..." }] }),
8830 json!({ "mode": null, "content": [{ "type": "text", "text": "..." }] }),
8831 json!({ "mode": 1, "content": [{ "type": "text", "text": "..." }] }),
8832 json!({ "mode": "append" }),
8833 json!({ "mode": "append", "content": "..." }),
8834 json!({ "mode": "append", "content": [{ "type": "text" }] }),
8835 ] {
8836 assert!(
8837 with(system_prompt.clone()).is_err(),
8838 "{name}.systemPrompt: {system_prompt}"
8839 );
8840 }
8841
8842 assert_eq!(
8843 with(serde_json::Value::Null).unwrap(),
8844 serde_json::from_value::<T>(base.clone()).unwrap(),
8845 "{name}.systemPrompt"
8846 );
8847 }
8848
8849 #[test]
8850 fn test_session_setup_requests_reject_malformed_system_prompts() {
8851 assert!(
8852 serde_json::to_value(NewSessionRequest::new("/repo"))
8853 .unwrap()
8854 .get("systemPrompt")
8855 .is_none()
8856 );
8857 assert_system_prompt_is_strict::<NewSessionRequest>(&json!({ "cwd": "/repo" }));
8858 assert_system_prompt_is_strict::<ResumeSessionRequest>(&json!({
8859 "sessionId": "sess-1",
8860 "cwd": "/repo"
8861 }));
8862 #[cfg(feature = "unstable_session_fork")]
8863 assert_system_prompt_is_strict::<ForkSessionRequest>(&json!({
8864 "sessionId": "sess-1",
8865 "cwd": "/repo"
8866 }));
8867 }
8868
8869 #[test]
8872 fn test_system_prompt_preserves_unknown_content_blocks() {
8873 let request: NewSessionRequest = serde_json::from_value(json!({
8874 "cwd": "/home/user/project",
8875 "systemPrompt": {
8876 "mode": "append",
8877 "content": [{ "type": "_vendor_video", "uri": "https://example.com/intro.mp4" }]
8878 }
8879 }))
8880 .unwrap();
8881 let content = request.system_prompt.unwrap().content;
8882 assert_eq!(content.len(), 1);
8883 assert!(matches!(content[0], ContentBlock::Other(_)));
8884 }
8885
8886 #[test]
8887 fn test_auth_method_terminal_serialization() {
8888 let method = AuthMethod::Terminal(AuthMethodTerminal::new("tui-auth", "Terminal Auth"));
8889
8890 let json = serde_json::to_value(&method).unwrap();
8891 assert_eq!(
8892 json,
8893 json!({
8894 "methodId": "tui-auth",
8895 "name": "Terminal Auth",
8896 "type": "terminal"
8897 })
8898 );
8899 assert!(!json.as_object().unwrap().contains_key("args"));
8901 assert!(!json.as_object().unwrap().contains_key("env"));
8902
8903 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
8904 match deserialized {
8905 AuthMethod::Terminal(AuthMethodTerminal { args, env, .. }) => {
8906 assert_eq!(args, Vec::<String>::new());
8907 assert_eq!(env, []);
8908 }
8909 _ => panic!("Expected Terminal variant"),
8910 }
8911 }
8912
8913 #[test]
8914 fn test_auth_method_terminal_with_args_and_env_serialization() {
8915 let method = AuthMethod::Terminal(
8916 AuthMethodTerminal::new("tui-auth", "Terminal Auth")
8917 .args(vec!["--interactive".to_string(), "--color".to_string()])
8918 .env(vec![EnvVariable::new("TERM", "xterm-256color")]),
8919 );
8920
8921 let json = serde_json::to_value(&method).unwrap();
8922 assert_eq!(
8923 json,
8924 json!({
8925 "methodId": "tui-auth",
8926 "name": "Terminal Auth",
8927 "type": "terminal",
8928 "args": ["--interactive", "--color"],
8929 "env": [
8930 {
8931 "name": "TERM",
8932 "value": "xterm-256color"
8933 }
8934 ]
8935 })
8936 );
8937
8938 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
8939 match deserialized {
8940 AuthMethod::Terminal(AuthMethodTerminal { args, env, .. }) => {
8941 assert_eq!(args, vec!["--interactive", "--color"]);
8942 assert_eq!(env.len(), 1);
8943 assert_eq!(env[0].name, "TERM");
8944 assert_eq!(env[0].value, "xterm-256color");
8945 }
8946 _ => panic!("Expected Terminal variant"),
8947 }
8948 }
8949
8950 #[test]
8951 fn test_session_config_option_id_serialize() {
8952 let val = SessionConfigOptionValue::id("model-1");
8953 let json = serde_json::to_value(&val).unwrap();
8954 assert_eq!(json, json!({ "type": "id", "value": "model-1" }));
8955 }
8956
8957 #[test]
8958 fn test_session_config_option_value_boolean_serialize() {
8959 let val = SessionConfigOptionValue::boolean(true);
8960 let json = serde_json::to_value(&val).unwrap();
8961 assert_eq!(json, json!({ "type": "boolean", "value": true }));
8962 }
8963
8964 #[test]
8965 fn test_session_config_option_value_deserialize_id() {
8966 let json = json!({ "type": "id", "value": "model-1" });
8967 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
8968 assert_eq!(val, SessionConfigOptionValue::id("model-1"));
8969 assert_eq!(val.as_id().unwrap().to_string(), "model-1");
8970 }
8971
8972 #[test]
8973 fn test_session_config_option_value_deserialize_requires_type() {
8974 let json = json!({ "value": "model-1" });
8975 let result = serde_json::from_value::<SessionConfigOptionValue>(json);
8976 assert!(result.is_err());
8977 }
8978
8979 #[test]
8980 fn test_session_config_option_value_deserialize_boolean() {
8981 let json = json!({ "type": "boolean", "value": true });
8982 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
8983 assert_eq!(val, SessionConfigOptionValue::boolean(true));
8984 assert_eq!(val.as_bool(), Some(true));
8985 }
8986
8987 #[test]
8988 fn test_session_config_option_value_deserialize_boolean_false() {
8989 let json = json!({ "type": "boolean", "value": false });
8990 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
8991 assert_eq!(val, SessionConfigOptionValue::boolean(false));
8992 assert_eq!(val.as_bool(), Some(false));
8993 }
8994
8995 #[test]
8996 fn test_session_config_option_value_deserialize_unknown_type_with_string_value() {
8997 let json = json!({
8998 "type": "text",
8999 "value": "freeform input",
9000 "maxLength": 200
9001 });
9002 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
9003 let SessionConfigOptionValue::Other(unknown) = val else {
9004 panic!("Expected Other variant");
9005 };
9006 assert_eq!(unknown.type_, "text");
9007 assert_eq!(unknown.value, json!("freeform input"));
9008 assert_eq!(unknown.fields["maxLength"], json!(200));
9009 }
9010
9011 #[test]
9012 fn test_session_config_option_value_deserialize_unknown_type_with_object_value() {
9013 let json = json!({
9014 "type": "range",
9015 "value": { "min": 1, "max": 5 }
9016 });
9017 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
9018 let SessionConfigOptionValue::Other(unknown) = val else {
9019 panic!("Expected Other variant");
9020 };
9021 assert_eq!(unknown.type_, "range");
9022 assert_eq!(unknown.value, json!({ "min": 1, "max": 5 }));
9023 }
9024
9025 #[test]
9026 fn test_session_config_option_value_roundtrip_id() {
9027 let original = SessionConfigOptionValue::id("option-a");
9028 let json = serde_json::to_value(&original).unwrap();
9029 let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
9030 assert_eq!(original, roundtripped);
9031 }
9032
9033 #[test]
9034 fn test_session_config_option_value_roundtrip_boolean() {
9035 let original = SessionConfigOptionValue::boolean(false);
9036 let json = serde_json::to_value(&original).unwrap();
9037 let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
9038 assert_eq!(original, roundtripped);
9039 }
9040
9041 #[test]
9042 fn test_session_config_option_value_roundtrip_other() {
9043 let mut fields = BTreeMap::new();
9044 fields.insert("maxLength".to_string(), json!(200));
9045 let original = SessionConfigOptionValue::Other(OtherSessionConfigOptionValue::new(
9046 "text",
9047 json!("freeform input"),
9048 fields,
9049 ));
9050 let json = serde_json::to_value(&original).unwrap();
9051 let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
9052 assert_eq!(original, roundtripped);
9053 }
9054
9055 #[test]
9056 fn test_session_config_option_value_type_mismatch_boolean_with_string() {
9057 let json = json!({ "type": "boolean", "value": "not a bool" });
9058 let result = serde_json::from_value::<SessionConfigOptionValue>(json);
9059 assert!(result.is_err());
9060 }
9061
9062 #[test]
9063 fn test_session_config_option_value_from_impls() {
9064 let from_str: SessionConfigOptionValue = "model-1".into();
9065 assert_eq!(from_str.as_id().unwrap().to_string(), "model-1");
9066
9067 let from_id: SessionConfigOptionValue = SessionConfigValueId::new("model-2").into();
9068 assert_eq!(from_id.as_id().unwrap().to_string(), "model-2");
9069
9070 let from_bool: SessionConfigOptionValue = true.into();
9071 assert_eq!(from_bool.as_bool(), Some(true));
9072 }
9073
9074 #[test]
9075 fn test_set_session_config_option_request_id() {
9076 let req = SetSessionConfigOptionRequest::new("sess_1", "model", "model-1");
9077 let json = serde_json::to_value(&req).unwrap();
9078 assert_eq!(
9079 json,
9080 json!({
9081 "sessionId": "sess_1",
9082 "configId": "model",
9083 "type": "id",
9084 "value": "model-1"
9085 })
9086 );
9087 }
9088
9089 #[test]
9090 fn test_set_session_config_option_request_boolean() {
9091 let req = SetSessionConfigOptionRequest::new("sess_1", "brave_mode", true);
9092 let json = serde_json::to_value(&req).unwrap();
9093 assert_eq!(
9094 json,
9095 json!({
9096 "sessionId": "sess_1",
9097 "configId": "brave_mode",
9098 "type": "boolean",
9099 "value": true
9100 })
9101 );
9102 }
9103
9104 #[test]
9105 fn test_set_session_config_option_request_deserialize_requires_type() {
9106 let json = json!({
9107 "sessionId": "sess_1",
9108 "configId": "model",
9109 "value": "model-1"
9110 });
9111 let result = serde_json::from_value::<SetSessionConfigOptionRequest>(json);
9112 assert!(result.is_err());
9113 }
9114
9115 #[test]
9116 fn test_set_session_config_option_request_deserialize_boolean() {
9117 let json = json!({
9118 "sessionId": "sess_1",
9119 "configId": "brave_mode",
9120 "type": "boolean",
9121 "value": true
9122 });
9123 let req: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
9124 assert_eq!(req.value.as_bool(), Some(true));
9125 }
9126
9127 #[test]
9128 fn test_set_session_config_option_request_roundtrip_id() {
9129 let original = SetSessionConfigOptionRequest::new("s", "c", "v");
9130 let json = serde_json::to_value(&original).unwrap();
9131 let roundtripped: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
9132 assert_eq!(original, roundtripped);
9133 }
9134
9135 #[test]
9136 fn test_set_session_config_option_request_roundtrip_boolean() {
9137 let original = SetSessionConfigOptionRequest::new("s", "c", false);
9138 let json = serde_json::to_value(&original).unwrap();
9139 let roundtripped: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
9140 assert_eq!(original, roundtripped);
9141 }
9142
9143 #[test]
9144 fn test_session_config_boolean_serialization() {
9145 let cfg = SessionConfigBoolean::new(true);
9146 let json = serde_json::to_value(&cfg).unwrap();
9147 assert_eq!(json, json!({ "currentValue": true }));
9148
9149 let deserialized: SessionConfigBoolean = serde_json::from_value(json).unwrap();
9150 assert!(deserialized.current_value);
9151 }
9152
9153 #[test]
9154 fn test_session_config_option_boolean_variant() {
9155 let opt = SessionConfigOption::boolean("brave_mode", "Brave Mode", false)
9156 .description("Skip confirmation prompts")
9157 .meta(test_meta());
9158 assert_eq!(serialized_meta_key_count(&opt), 1);
9159
9160 let json = serde_json::to_value(&opt).unwrap();
9161 assert_eq!(
9162 json,
9163 json!({
9164 "configId": "brave_mode",
9165 "name": "Brave Mode",
9166 "description": "Skip confirmation prompts",
9167 "type": "boolean",
9168 "currentValue": false,
9169 "_meta": {
9170 "source": "test"
9171 }
9172 })
9173 );
9174
9175 let deserialized: SessionConfigOption = serde_json::from_value(json).unwrap();
9176 assert_eq!(deserialized.config_id.to_string(), "brave_mode");
9177 assert_eq!(deserialized.name, "Brave Mode");
9178 match deserialized.kind {
9179 SessionConfigKind::Boolean(ref b) => assert!(!b.current_value),
9180 _ => panic!("Expected Boolean kind"),
9181 }
9182 }
9183
9184 #[test]
9185 fn test_session_config_option_select_still_works() {
9186 let opt = SessionConfigOption::select(
9188 "model",
9189 "Model",
9190 "model-1",
9191 vec![
9192 SessionConfigSelectOption::new("model-1", "Model 1"),
9193 SessionConfigSelectOption::new("model-2", "Model 2"),
9194 ],
9195 )
9196 .meta(test_meta());
9197 assert_eq!(serialized_meta_key_count(&opt), 1);
9198
9199 let json = serde_json::to_value(&opt).unwrap();
9200 assert_eq!(json["type"], "select");
9201 assert_eq!(json["currentValue"], "model-1");
9202 assert_eq!(json["options"].as_array().unwrap().len(), 2);
9203 assert_eq!(json["_meta"]["source"], "test");
9204
9205 let deserialized: SessionConfigOption = serde_json::from_value(json).unwrap();
9206 match deserialized.kind {
9207 SessionConfigKind::Select(ref s) => {
9208 assert_eq!(s.current_value.to_string(), "model-1");
9209 }
9210 _ => panic!("Expected Select kind"),
9211 }
9212 }
9213
9214 #[test]
9215 fn test_session_config_option_unknown_kind_roundtrip() {
9216 let option: SessionConfigOption = serde_json::from_value(json!({
9217 "configId": "verbosity",
9218 "name": "Verbosity",
9219 "type": "_slider",
9220 "currentValue": 3,
9221 "min": 0,
9222 "max": 5,
9223 "_meta": {
9224 "source": "test"
9225 }
9226 }))
9227 .unwrap();
9228
9229 assert_eq!(option.config_id.to_string(), "verbosity");
9230 assert_eq!(option.meta.as_ref().unwrap()["source"], "test");
9231 let SessionConfigKind::Other(unknown) = &option.kind else {
9232 panic!("expected unknown config kind");
9233 };
9234 assert_eq!(unknown.type_, "_slider");
9235 assert_eq!(unknown.fields.get("currentValue"), Some(&json!(3)));
9236 assert!(!unknown.fields.contains_key("_meta"));
9237 assert_eq!(serialized_meta_key_count(&option), 1);
9238
9239 let json = serde_json::to_value(&option).unwrap();
9240 assert_eq!(json["type"], "_slider");
9241 assert_eq!(json["currentValue"], 3);
9242 assert_eq!(json["min"], 0);
9243 assert_eq!(json["max"], 5);
9244 assert_eq!(json["_meta"]["source"], "test");
9245 }
9246
9247 #[test]
9248 fn test_session_config_option_unknown_kind_does_not_duplicate_flattened_meta() {
9249 let mut fields = std::collections::BTreeMap::new();
9250 fields.insert("currentValue".to_string(), json!(3));
9251 fields.insert("_meta".to_string(), json!({ "inner": "ignored" }));
9252
9253 let option = SessionConfigOption::new(
9254 "verbosity",
9255 "Verbosity",
9256 SessionConfigKind::Other(OtherSessionConfigKind::new("_slider", fields)),
9257 )
9258 .meta(test_meta());
9259
9260 let SessionConfigKind::Other(unknown) = &option.kind else {
9261 panic!("expected unknown config kind");
9262 };
9263 assert!(!unknown.fields.contains_key("_meta"));
9264 assert_eq!(serialized_meta_key_count(&option), 1);
9265
9266 let json = serde_json::to_value(&option).unwrap();
9267 assert_eq!(json["type"], "_slider");
9268 assert_eq!(json["currentValue"], 3);
9269 assert_eq!(json["_meta"]["source"], "test");
9270 }
9271
9272 #[test]
9273 fn test_session_config_option_unknown_does_not_hide_malformed_known_kind() {
9274 assert!(
9275 serde_json::from_value::<SessionConfigOption>(json!({
9276 "configId": "model",
9277 "name": "Model",
9278 "type": "select"
9279 }))
9280 .is_err()
9281 );
9282 }
9283
9284 #[cfg(feature = "unstable_llm_providers")]
9285 #[test]
9286 fn test_llm_protocol_known_variants() {
9287 assert_eq!(
9288 serde_json::to_value(&LlmProtocol::Anthropic).unwrap(),
9289 json!("anthropic")
9290 );
9291 assert_eq!(
9292 serde_json::to_value(&LlmProtocol::OpenAi).unwrap(),
9293 json!("openai")
9294 );
9295 assert_eq!(
9296 serde_json::to_value(&LlmProtocol::Azure).unwrap(),
9297 json!("azure")
9298 );
9299 assert_eq!(
9300 serde_json::to_value(&LlmProtocol::Vertex).unwrap(),
9301 json!("vertex")
9302 );
9303 assert_eq!(
9304 serde_json::to_value(&LlmProtocol::Bedrock).unwrap(),
9305 json!("bedrock")
9306 );
9307
9308 assert_eq!(
9309 serde_json::from_str::<LlmProtocol>("\"anthropic\"").unwrap(),
9310 LlmProtocol::Anthropic
9311 );
9312 assert_eq!(
9313 serde_json::from_str::<LlmProtocol>("\"openai\"").unwrap(),
9314 LlmProtocol::OpenAi
9315 );
9316 assert_eq!(
9317 serde_json::from_str::<LlmProtocol>("\"azure\"").unwrap(),
9318 LlmProtocol::Azure
9319 );
9320 assert_eq!(
9321 serde_json::from_str::<LlmProtocol>("\"vertex\"").unwrap(),
9322 LlmProtocol::Vertex
9323 );
9324 assert_eq!(
9325 serde_json::from_str::<LlmProtocol>("\"bedrock\"").unwrap(),
9326 LlmProtocol::Bedrock
9327 );
9328 }
9329
9330 #[cfg(feature = "unstable_llm_providers")]
9331 #[test]
9332 fn test_llm_protocol_unknown_variant() {
9333 let unknown: LlmProtocol = serde_json::from_str("\"cohere\"").unwrap();
9334 assert_eq!(unknown, LlmProtocol::Other("cohere".to_string()));
9335
9336 let json = serde_json::to_value(&unknown).unwrap();
9337 assert_eq!(json, json!("cohere"));
9338 }
9339
9340 #[cfg(feature = "unstable_llm_providers")]
9341 #[test]
9342 fn test_provider_current_config_serialization() {
9343 let config =
9344 ProviderCurrentConfig::new(LlmProtocol::Anthropic, "https://api.anthropic.com");
9345
9346 let json = serde_json::to_value(&config).unwrap();
9347 assert_eq!(
9348 json,
9349 json!({
9350 "apiType": "anthropic",
9351 "baseUrl": "https://api.anthropic.com"
9352 })
9353 );
9354
9355 let deserialized: ProviderCurrentConfig = serde_json::from_value(json).unwrap();
9356 assert_eq!(deserialized.api_type, LlmProtocol::Anthropic);
9357 assert_eq!(deserialized.base_url, "https://api.anthropic.com");
9358 }
9359
9360 #[cfg(feature = "unstable_llm_providers")]
9361 #[test]
9362 fn test_provider_info_with_current_config() {
9363 let info = ProviderInfo::new(
9364 "main",
9365 vec![LlmProtocol::Anthropic, LlmProtocol::OpenAi],
9366 true,
9367 Some(ProviderCurrentConfig::new(
9368 LlmProtocol::Anthropic,
9369 "https://api.anthropic.com",
9370 )),
9371 );
9372
9373 let json = serde_json::to_value(&info).unwrap();
9374 assert_eq!(
9375 json,
9376 json!({
9377 "providerId": "main",
9378 "supported": ["anthropic", "openai"],
9379 "required": true,
9380 "current": {
9381 "apiType": "anthropic",
9382 "baseUrl": "https://api.anthropic.com"
9383 }
9384 })
9385 );
9386
9387 let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
9388 assert_eq!(deserialized.provider_id.to_string(), "main");
9389 assert_eq!(deserialized.supported.len(), 2);
9390 assert!(deserialized.required);
9391 assert!(deserialized.current.is_some());
9392 assert_eq!(
9393 deserialized.current.as_ref().unwrap().api_type,
9394 LlmProtocol::Anthropic
9395 );
9396 }
9397
9398 #[cfg(feature = "unstable_llm_providers")]
9399 #[test]
9400 fn test_provider_info_disabled() {
9401 let info = ProviderInfo::new(
9402 "secondary",
9403 vec![LlmProtocol::OpenAi],
9404 false,
9405 None::<ProviderCurrentConfig>,
9406 );
9407
9408 let json = serde_json::to_value(&info).unwrap();
9409 assert_eq!(
9410 json,
9411 json!({
9412 "providerId": "secondary",
9413 "supported": ["openai"],
9414 "required": false
9415 })
9416 );
9417
9418 let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
9419 assert_eq!(deserialized.provider_id.to_string(), "secondary");
9420 assert!(!deserialized.required);
9421 assert!(deserialized.current.is_none());
9422 }
9423
9424 #[cfg(feature = "unstable_llm_providers")]
9425 #[test]
9426 fn test_provider_info_missing_current_defaults_to_none() {
9427 let json = json!({
9429 "providerId": "main",
9430 "supported": ["anthropic"],
9431 "required": true
9432 });
9433 let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
9434 assert!(deserialized.current.is_none());
9435 }
9436
9437 #[cfg(feature = "unstable_llm_providers")]
9438 #[test]
9439 fn test_provider_info_explicit_null_current_decodes_to_none() {
9440 let json = json!({
9444 "providerId": "main",
9445 "supported": ["anthropic"],
9446 "required": true,
9447 "current": null
9448 });
9449 let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
9450 assert!(deserialized.current.is_none());
9451 }
9452
9453 #[cfg(feature = "unstable_llm_providers")]
9454 #[test]
9455 fn test_list_providers_response_serialization() {
9456 let response = ListProvidersResponse::new(vec![ProviderInfo::new(
9457 "main",
9458 vec![LlmProtocol::Anthropic],
9459 true,
9460 Some(ProviderCurrentConfig::new(
9461 LlmProtocol::Anthropic,
9462 "https://api.anthropic.com",
9463 )),
9464 )]);
9465
9466 let json = serde_json::to_value(&response).unwrap();
9467 assert_eq!(json["providers"].as_array().unwrap().len(), 1);
9468 assert_eq!(json["providers"][0]["providerId"], "main");
9469
9470 let deserialized: ListProvidersResponse = serde_json::from_value(json).unwrap();
9471 assert_eq!(deserialized.providers.len(), 1);
9472 }
9473
9474 #[cfg(feature = "unstable_llm_providers")]
9475 #[test]
9476 fn test_set_provider_request_serialization() {
9477 use std::collections::HashMap;
9478
9479 let mut headers = HashMap::new();
9480 headers.insert("Authorization".to_string(), "Bearer sk-test".to_string());
9481
9482 let request =
9483 SetProviderRequest::new("main", LlmProtocol::OpenAi, "https://api.openai.com/v1")
9484 .headers(headers);
9485
9486 let json = serde_json::to_value(&request).unwrap();
9487 assert_eq!(
9488 json,
9489 json!({
9490 "providerId": "main",
9491 "apiType": "openai",
9492 "baseUrl": "https://api.openai.com/v1",
9493 "headers": {
9494 "Authorization": "Bearer sk-test"
9495 }
9496 })
9497 );
9498
9499 let deserialized: SetProviderRequest = serde_json::from_value(json).unwrap();
9500 assert_eq!(deserialized.provider_id.to_string(), "main");
9501 assert_eq!(deserialized.api_type, LlmProtocol::OpenAi);
9502 assert_eq!(deserialized.base_url, "https://api.openai.com/v1");
9503 assert_eq!(deserialized.headers.len(), 1);
9504 assert_eq!(
9505 deserialized.headers.get("Authorization").unwrap(),
9506 "Bearer sk-test"
9507 );
9508 }
9509
9510 #[cfg(feature = "unstable_llm_providers")]
9511 #[test]
9512 fn test_set_provider_request_omits_empty_headers() {
9513 let request =
9514 SetProviderRequest::new("main", LlmProtocol::Anthropic, "https://api.anthropic.com");
9515
9516 let json = serde_json::to_value(&request).unwrap();
9517 assert!(!json.as_object().unwrap().contains_key("headers"));
9519 }
9520
9521 #[cfg(feature = "unstable_llm_providers")]
9522 #[test]
9523 fn test_disable_provider_request_serialization() {
9524 let request = DisableProviderRequest::new("secondary");
9525
9526 let json = serde_json::to_value(&request).unwrap();
9527 assert_eq!(json, json!({ "providerId": "secondary" }));
9528
9529 let deserialized: DisableProviderRequest = serde_json::from_value(json).unwrap();
9530 assert_eq!(deserialized.provider_id.to_string(), "secondary");
9531 }
9532
9533 #[cfg(feature = "unstable_llm_providers")]
9534 #[test]
9535 fn test_providers_capabilities_serialization() {
9536 let caps = ProvidersCapabilities::new();
9537
9538 let json = serde_json::to_value(&caps).unwrap();
9539 assert_eq!(json, json!({}));
9540
9541 let deserialized: ProvidersCapabilities = serde_json::from_value(json).unwrap();
9542 assert!(deserialized.meta.is_none());
9543 }
9544
9545 #[cfg(feature = "unstable_llm_providers")]
9546 #[test]
9547 fn test_agent_capabilities_with_providers() {
9548 let caps = AgentCapabilities::new().providers(ProvidersCapabilities::new());
9549
9550 let json = serde_json::to_value(&caps).unwrap();
9551 assert_eq!(json["providers"], json!({}));
9552
9553 let deserialized: AgentCapabilities = serde_json::from_value(json).unwrap();
9554 assert!(deserialized.providers.is_some());
9555 }
9556
9557 #[test]
9558 fn test_agent_capabilities_session_is_explicit() {
9559 let json = serde_json::to_value(AgentCapabilities::new()).unwrap();
9560 assert!(json.get("session").is_none());
9561
9562 let caps = AgentCapabilities::new().session(
9563 SessionCapabilities::new()
9564 .prompt(PromptCapabilities::new().image(PromptImageCapabilities::new()))
9565 .mcp(McpCapabilities::new().stdio(McpStdioCapabilities::new())),
9566 );
9567
9568 assert_eq!(
9569 serde_json::to_value(&caps).unwrap(),
9570 json!({
9571 "session": {
9572 "prompt": {
9573 "image": {}
9574 },
9575 "mcp": {
9576 "stdio": {}
9577 }
9578 }
9579 })
9580 );
9581
9582 let deserialized: AgentCapabilities = serde_json::from_value(json!({
9583 "session": false
9584 }))
9585 .unwrap();
9586 assert!(deserialized.session.is_none());
9587 }
9588
9589 #[test]
9590 fn test_prompt_capabilities_serialize_supported_content_as_objects() {
9591 let caps = PromptCapabilities::new()
9592 .image(PromptImageCapabilities::new())
9593 .audio(PromptAudioCapabilities::new())
9594 .embedded_context(PromptEmbeddedContextCapabilities::new());
9595
9596 assert_eq!(
9597 serde_json::to_value(&caps).unwrap(),
9598 json!({
9599 "image": {},
9600 "audio": {},
9601 "embeddedContext": {}
9602 })
9603 );
9604
9605 let deserialized: PromptCapabilities = serde_json::from_value(json!({
9606 "image": null,
9607 "audio": false,
9608 "embeddedContext": {}
9609 }))
9610 .unwrap();
9611 assert!(deserialized.image.is_none());
9612 assert!(deserialized.audio.is_none());
9613 assert!(deserialized.embedded_context.is_some());
9614 }
9615
9616 #[test]
9617 fn test_mcp_capabilities_serialize_supported_transports_as_objects() {
9618 let caps = McpCapabilities::new()
9619 .stdio(McpStdioCapabilities::new())
9620 .http(McpHttpCapabilities::new());
9621
9622 assert_eq!(
9623 serde_json::to_value(&caps).unwrap(),
9624 json!({
9625 "stdio": {},
9626 "http": {}
9627 })
9628 );
9629
9630 let deserialized: McpCapabilities = serde_json::from_value(json!({
9631 "stdio": null,
9632 "http": false
9633 }))
9634 .unwrap();
9635 assert!(deserialized.stdio.is_none());
9636 assert!(deserialized.http.is_none());
9637 }
9638
9639 #[cfg(feature = "unstable_mcp_over_acp")]
9640 #[test]
9641 fn test_mcp_capabilities_serialize_acp_support_as_object() {
9642 let caps = McpCapabilities::new().acp(McpAcpCapabilities::new());
9643
9644 assert_eq!(
9645 serde_json::to_value(&caps).unwrap(),
9646 json!({
9647 "acp": {}
9648 })
9649 );
9650 }
9651
9652 #[test]
9653 fn prompt_request_rejects_malformed_content_block() {
9654 use serde_json::json;
9655
9656 assert!(
9657 serde_json::from_value::<PromptRequest>(json!({
9658 "sessionId": "sess-1",
9659 "prompt": [{"type": "text"}]
9660 }))
9661 .is_err()
9662 );
9663 }
9664
9665 #[test]
9666 fn prompt_request_rejects_non_array_prompt() {
9667 use serde_json::json;
9668
9669 assert!(
9670 serde_json::from_value::<PromptRequest>(json!({
9671 "sessionId": "sess-1",
9672 "prompt": "hello"
9673 }))
9674 .is_err()
9675 );
9676 }
9677
9678 fn assert_session_setup_lists_are_strict<T>(base: &serde_json::Value)
9679 where
9680 T: serde::de::DeserializeOwned + PartialEq + std::fmt::Debug,
9681 {
9682 let name = std::any::type_name::<T>();
9683 let with = |field: &str, value: serde_json::Value| {
9684 let mut request = base.clone();
9685 request[field] = value;
9686 serde_json::from_value::<T>(request)
9687 };
9688
9689 for (field, value) in [
9690 (
9692 "mcpServers",
9693 json!([
9694 {"type": "stdio", "name": "github", "command": "/usr/bin/gh-mcp", "env": [{"name": "GITHUB_TOKEN", "value": null}]},
9695 {"type": "stdio", "name": "fs", "command": "/usr/bin/fs-mcp"}
9696 ]),
9697 ),
9698 ("mcpServers", json!({"type": "stdio", "name": "fs"})),
9699 ("additionalDirectories", json!(["/repo/lib", 42])),
9700 ("additionalDirectories", json!("/repo/lib")),
9701 ] {
9702 assert!(
9703 with(field, value.clone()).is_err(),
9704 "{name}.{field}: {value}"
9705 );
9706 }
9707
9708 let empty = serde_json::from_value::<T>(base.clone()).unwrap();
9709 for field in ["mcpServers", "additionalDirectories"] {
9710 assert_eq!(
9711 with(field, serde_json::Value::Null).unwrap(),
9712 empty,
9713 "{name}.{field}"
9714 );
9715 }
9716 }
9717
9718 #[test]
9719 fn session_setup_requests_reject_malformed_mcp_servers_and_directories() {
9720 assert_session_setup_lists_are_strict::<NewSessionRequest>(&json!({"cwd": "/repo"}));
9721 assert_session_setup_lists_are_strict::<ResumeSessionRequest>(&json!({
9722 "sessionId": "sess-1",
9723 "cwd": "/repo"
9724 }));
9725 #[cfg(feature = "unstable_session_fork")]
9726 assert_session_setup_lists_are_strict::<ForkSessionRequest>(&json!({
9727 "sessionId": "sess-1",
9728 "cwd": "/repo"
9729 }));
9730 }
9731
9732 #[test]
9733 fn resume_session_request_rejects_malformed_replay_from() {
9734 let request = |replay_from: serde_json::Value| {
9735 serde_json::from_value::<ResumeSessionRequest>(json!({
9736 "sessionId": "sess-1",
9737 "cwd": "/repo",
9738 "replayFrom": replay_from
9739 }))
9740 };
9741
9742 assert!(request(json!("start")).is_err());
9744 assert_eq!(request(serde_json::Value::Null).unwrap().replay_from, None);
9745 assert!(matches!(
9746 request(json!({"type": "start"})).unwrap().replay_from,
9747 Some(ReplayFrom::Start(_))
9748 ));
9749 }
9750
9751 #[test]
9752 fn mcp_server_lists_treat_null_as_empty() {
9753 let servers: Vec<McpServer> = serde_json::from_value(json!([
9754 {"type": "stdio", "name": "fs", "command": "/usr/bin/fs-mcp", "args": null, "env": null},
9755 {"type": "http", "name": "api", "url": "https://example.com/mcp", "headers": null}
9756 ]))
9757 .unwrap();
9758
9759 assert_eq!(
9760 servers,
9761 vec![
9762 McpServer::Stdio(McpServerStdio::new("fs", "/usr/bin/fs-mcp")),
9763 McpServer::Http(McpServerHttp::new("api", "https://example.com/mcp")),
9764 ]
9765 );
9766 }
9767
9768 #[test]
9769 fn terminal_auth_methods_reject_malformed_launch_config() {
9770 let response: InitializeResponse = serde_json::from_value(json!({
9774 "protocolVersion": 2,
9775 "info": {"name": "agent", "version": "1.0.0"},
9776 "authMethods": [
9777 {"type": "terminal", "methodId": "bad-args", "name": "Log in", "args": ["auth", "--port", 8123]},
9778 {"type": "terminal", "methodId": "bad-env", "name": "Log in", "env": [{"name": "DEBUG", "value": 1}]},
9779 {"type": "terminal", "methodId": "null-config", "name": "Log in", "args": null, "env": null},
9780 {"type": "agent", "methodId": "agent", "name": "Agent login"}
9781 ]
9782 }))
9783 .unwrap();
9784
9785 assert_eq!(
9786 response.auth_methods,
9787 vec![
9788 AuthMethod::Terminal(AuthMethodTerminal::new("null-config", "Log in")),
9789 AuthMethod::Agent(AuthMethodAgent::new("agent", "Agent login")),
9790 ]
9791 );
9792 }
9793
9794 #[cfg(feature = "unstable_auth_status")]
9795 #[test]
9796 fn auth_status_response_round_trips() {
9797 let response = AuthStatusResponse::new(AuthState::Authenticated).accounts(vec![
9798 AuthAccount::new("Anthropic API key").active(true),
9799 AuthAccount::new("Claude Max").email("user@example.com".to_string()),
9800 ]);
9801 let json = serde_json::to_value(&response).unwrap();
9802 assert_eq!(
9803 json,
9804 json!({
9805 "state": "authenticated",
9806 "accounts": [
9807 {
9808 "label": "Anthropic API key",
9809 "active": true
9810 },
9811 {
9812 "label": "Claude Max",
9813 "email": "user@example.com"
9814 }
9815 ]
9816 })
9817 );
9818 assert_eq!(
9819 serde_json::from_value::<AuthStatusResponse>(json).unwrap(),
9820 response
9821 );
9822 assert_eq!(
9823 serde_json::to_value(AuthStatusResponse::new(AuthState::NotRequired)).unwrap(),
9824 json!({"state": "not_required"})
9825 );
9826 }
9827
9828 #[cfg(feature = "unstable_auth_status")]
9829 #[test]
9830 fn auth_status_state_is_strict_and_accounts_are_lenient() {
9831 assert!(serde_json::from_value::<AuthStatusResponse>(json!({})).is_err());
9832 assert!(serde_json::from_value::<AuthStatusResponse>(json!({"state": null})).is_err());
9833 assert!(serde_json::from_value::<AuthStatusResponse>(json!({"state": 1})).is_err());
9834
9835 let response: AuthStatusResponse = serde_json::from_value(json!({
9836 "state": "_vendor_state",
9837 "accounts": [
9838 {"active": true},
9839 {
9840 "label": "Vertex AI",
9841 "active": "yes",
9842 "email": ["not", "a", "string"]
9843 }
9844 ]
9845 }))
9846 .unwrap();
9847 assert_eq!(
9848 response,
9849 AuthStatusResponse::new(AuthState::Other("_vendor_state".to_string()))
9850 .accounts(vec![AuthAccount::new("Vertex AI")])
9851 );
9852 assert_eq!(
9853 serde_json::from_value::<AuthStatusResponse>(
9854 json!({"state": "unknown", "accounts": null})
9855 )
9856 .unwrap(),
9857 AuthStatusResponse::new(AuthState::Unknown)
9858 );
9859 }
9860
9861 #[cfg(feature = "unstable_auth_status")]
9862 #[test]
9863 fn auth_status_update_notification_routes() {
9864 use crate::v2::{AgentNotification, CLIENT_METHOD_NAMES};
9865
9866 assert_eq!(AGENT_METHOD_NAMES.auth_status, "auth/status");
9867 assert_eq!(CLIENT_METHOD_NAMES.auth_status_update, "auth/status_update");
9868 let notification = AgentNotification::AuthStatusUpdateNotification(Box::new(
9869 AuthStatusUpdateNotification::new(AuthState::Unauthenticated),
9870 ));
9871 assert_eq!(notification.method(), "auth/status_update");
9872 assert_eq!(
9873 serde_json::to_value(¬ification).unwrap(),
9874 json!({"state": "unauthenticated"})
9875 );
9876 }
9877}