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 #[cfg(feature = "unstable_session_system_prompt")]
1294 #[serde(default)]
1295 pub system_prompt: Option<SystemPrompt>,
1296 #[serde_as(deserialize_as = "DefaultOnError")]
1302 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1303 #[serde(default)]
1304 #[serde(rename = "_meta")]
1305 pub meta: Option<Meta>,
1306}
1307
1308impl NewSessionRequest {
1309 #[must_use]
1311 pub fn new(cwd: impl Into<AbsolutePath>) -> Self {
1312 Self {
1313 cwd: cwd.into(),
1314 additional_directories: vec![],
1315 mcp_servers: vec![],
1316 #[cfg(feature = "unstable_session_system_prompt")]
1317 system_prompt: None,
1318 meta: None,
1319 }
1320 }
1321
1322 #[must_use]
1324 pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
1325 where
1326 I: IntoIterator<Item = P>,
1327 P: Into<AbsolutePath>,
1328 {
1329 self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
1330 self
1331 }
1332
1333 #[must_use]
1335 pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1336 self.mcp_servers = mcp_servers;
1337 self
1338 }
1339
1340 #[cfg(feature = "unstable_session_system_prompt")]
1346 #[must_use]
1347 pub fn system_prompt(mut self, system_prompt: impl IntoOption<SystemPrompt>) -> Self {
1348 self.system_prompt = system_prompt.into_option();
1349 self
1350 }
1351
1352 #[must_use]
1358 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1359 self.meta = meta.into_option();
1360 self
1361 }
1362}
1363
1364#[serde_as]
1372#[skip_serializing_none]
1373#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1374#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1375#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_NEW_METHOD_NAME)))]
1376#[serde(rename_all = "camelCase")]
1377#[non_exhaustive]
1378pub struct NewSessionResponse {
1379 pub session_id: SessionId,
1383 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1389 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1390 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1391 pub config_options: Vec<SessionConfigOption>,
1392 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1398 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1399 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1400 pub available_commands: Vec<AvailableCommand>,
1401 #[serde_as(deserialize_as = "DefaultOnError")]
1407 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1408 #[serde(default)]
1409 #[serde(rename = "_meta")]
1410 pub meta: Option<Meta>,
1411}
1412
1413impl NewSessionResponse {
1414 #[must_use]
1416 pub fn new(session_id: impl Into<SessionId>) -> Self {
1417 Self {
1418 session_id: session_id.into(),
1419 config_options: Vec::new(),
1420 available_commands: Vec::new(),
1421 meta: None,
1422 }
1423 }
1424
1425 #[must_use]
1431 pub fn config_options(mut self, config_options: Vec<SessionConfigOption>) -> Self {
1432 self.config_options = config_options;
1433 self
1434 }
1435
1436 #[must_use]
1438 pub fn available_commands(mut self, available_commands: Vec<AvailableCommand>) -> Self {
1439 self.available_commands = available_commands;
1440 self
1441 }
1442
1443 #[must_use]
1449 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1450 self.meta = meta.into_option();
1451 self
1452 }
1453}
1454
1455#[cfg(feature = "unstable_session_fork")]
1468#[serde_as]
1469#[skip_serializing_none]
1470#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1471#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1472#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_FORK_METHOD_NAME)))]
1473#[serde(rename_all = "camelCase")]
1474#[non_exhaustive]
1475pub struct ForkSessionRequest {
1476 pub session_id: SessionId,
1478 pub cwd: AbsolutePath,
1480 #[serde_as(deserialize_as = "DefaultOnNull")]
1486 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
1487 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1488 pub additional_directories: Vec<AbsolutePath>,
1489 #[serde_as(deserialize_as = "DefaultOnNull")]
1491 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
1492 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1493 pub mcp_servers: Vec<McpServer>,
1494 #[cfg(feature = "unstable_session_system_prompt")]
1506 #[serde(default)]
1507 pub system_prompt: Option<SystemPrompt>,
1508 #[serde_as(deserialize_as = "DefaultOnError")]
1514 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1515 #[serde(default)]
1516 #[serde(rename = "_meta")]
1517 pub meta: Option<Meta>,
1518}
1519
1520#[cfg(feature = "unstable_session_fork")]
1521impl ForkSessionRequest {
1522 #[must_use]
1524 pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<AbsolutePath>) -> Self {
1525 Self {
1526 session_id: session_id.into(),
1527 cwd: cwd.into(),
1528 additional_directories: vec![],
1529 mcp_servers: vec![],
1530 #[cfg(feature = "unstable_session_system_prompt")]
1531 system_prompt: None,
1532 meta: None,
1533 }
1534 }
1535
1536 #[must_use]
1538 pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
1539 where
1540 I: IntoIterator<Item = P>,
1541 P: Into<AbsolutePath>,
1542 {
1543 self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
1544 self
1545 }
1546
1547 #[must_use]
1549 pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1550 self.mcp_servers = mcp_servers;
1551 self
1552 }
1553
1554 #[cfg(feature = "unstable_session_system_prompt")]
1560 #[must_use]
1561 pub fn system_prompt(mut self, system_prompt: impl IntoOption<SystemPrompt>) -> Self {
1562 self.system_prompt = system_prompt.into_option();
1563 self
1564 }
1565
1566 #[must_use]
1572 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1573 self.meta = meta.into_option();
1574 self
1575 }
1576}
1577
1578#[cfg(feature = "unstable_session_fork")]
1584#[serde_as]
1585#[skip_serializing_none]
1586#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1587#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1588#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_FORK_METHOD_NAME)))]
1589#[serde(rename_all = "camelCase")]
1590#[non_exhaustive]
1591pub struct ForkSessionResponse {
1592 pub session_id: SessionId,
1594 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1600 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1601 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1602 pub config_options: Vec<SessionConfigOption>,
1603 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1609 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1610 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1611 pub available_commands: Vec<AvailableCommand>,
1612 #[serde_as(deserialize_as = "DefaultOnError")]
1618 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1619 #[serde(default)]
1620 #[serde(rename = "_meta")]
1621 pub meta: Option<Meta>,
1622}
1623
1624#[cfg(feature = "unstable_session_fork")]
1625impl ForkSessionResponse {
1626 #[must_use]
1628 pub fn new(session_id: impl Into<SessionId>) -> Self {
1629 Self {
1630 session_id: session_id.into(),
1631 config_options: Vec::new(),
1632 available_commands: Vec::new(),
1633 meta: None,
1634 }
1635 }
1636
1637 #[must_use]
1643 pub fn config_options(mut self, config_options: Vec<SessionConfigOption>) -> Self {
1644 self.config_options = config_options;
1645 self
1646 }
1647
1648 #[must_use]
1650 pub fn available_commands(mut self, available_commands: Vec<AvailableCommand>) -> Self {
1651 self.available_commands = available_commands;
1652 self
1653 }
1654
1655 #[must_use]
1661 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1662 self.meta = meta.into_option();
1663 self
1664 }
1665}
1666
1667#[serde_as]
1674#[skip_serializing_none]
1675#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1676#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1677#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_RESUME_METHOD_NAME)))]
1678#[serde(rename_all = "camelCase")]
1679#[non_exhaustive]
1680pub struct ResumeSessionRequest {
1681 pub session_id: SessionId,
1683 pub cwd: AbsolutePath,
1688 #[serde_as(deserialize_as = "DefaultOnNull")]
1695 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
1696 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1697 pub additional_directories: Vec<AbsolutePath>,
1698 #[serde_as(deserialize_as = "DefaultOnNull")]
1703 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
1704 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1705 pub mcp_servers: Vec<McpServer>,
1706 #[serde(default)]
1718 pub replay_from: Option<ReplayFrom>,
1719 #[cfg(feature = "unstable_session_system_prompt")]
1731 #[serde(default)]
1732 pub system_prompt: Option<SystemPrompt>,
1733 #[serde_as(deserialize_as = "DefaultOnError")]
1739 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1740 #[serde(default)]
1741 #[serde(rename = "_meta")]
1742 pub meta: Option<Meta>,
1743}
1744
1745impl ResumeSessionRequest {
1746 #[must_use]
1748 pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<AbsolutePath>) -> Self {
1749 Self {
1750 session_id: session_id.into(),
1751 cwd: cwd.into(),
1752 additional_directories: vec![],
1753 mcp_servers: vec![],
1754 replay_from: None,
1755 #[cfg(feature = "unstable_session_system_prompt")]
1756 system_prompt: None,
1757 meta: None,
1758 }
1759 }
1760
1761 #[must_use]
1763 pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
1764 where
1765 I: IntoIterator<Item = P>,
1766 P: Into<AbsolutePath>,
1767 {
1768 self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
1769 self
1770 }
1771
1772 #[must_use]
1774 pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1775 self.mcp_servers = mcp_servers;
1776 self
1777 }
1778
1779 #[must_use]
1787 pub fn replay_from(mut self, replay_from: impl IntoOption<ReplayFrom>) -> Self {
1788 self.replay_from = replay_from.into_option();
1789 self
1790 }
1791
1792 #[cfg(feature = "unstable_session_system_prompt")]
1798 #[must_use]
1799 pub fn system_prompt(mut self, system_prompt: impl IntoOption<SystemPrompt>) -> Self {
1800 self.system_prompt = system_prompt.into_option();
1801 self
1802 }
1803
1804 #[must_use]
1810 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1811 self.meta = meta.into_option();
1812 self
1813 }
1814}
1815
1816#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1820#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1821#[serde(tag = "type", rename_all = "snake_case")]
1822#[non_exhaustive]
1823pub enum ReplayFrom {
1824 Start(ReplayFromStart),
1826 #[serde(untagged)]
1837 Other(OtherReplayFrom),
1838}
1839
1840impl From<ReplayFromStart> for ReplayFrom {
1841 fn from(replay_from: ReplayFromStart) -> Self {
1842 Self::Start(replay_from)
1843 }
1844}
1845
1846#[serde_as]
1848#[skip_serializing_none]
1849#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1850#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1851#[serde(rename_all = "camelCase")]
1852#[non_exhaustive]
1853pub struct ReplayFromStart {
1854 #[serde_as(deserialize_as = "DefaultOnError")]
1860 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1861 #[serde(default)]
1862 #[serde(rename = "_meta")]
1863 pub meta: Option<Meta>,
1864}
1865
1866impl ReplayFromStart {
1867 #[must_use]
1869 pub fn new() -> Self {
1870 Self::default()
1871 }
1872
1873 #[must_use]
1879 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1880 self.meta = meta.into_option();
1881 self
1882 }
1883}
1884
1885#[serde_as]
1887#[skip_serializing_none]
1888#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1889#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
1890#[cfg_attr(feature = "schemars", schemars(inline))]
1891#[cfg_attr(feature = "schemars", schemars(transform = other_replay_from_schema))]
1892#[serde(rename_all = "camelCase")]
1893#[non_exhaustive]
1894pub struct OtherReplayFrom {
1895 #[serde(rename = "type")]
1901 pub type_: String,
1902 #[serde(flatten)]
1905 pub fields: BTreeMap<String, serde_json::Value>,
1906}
1907
1908impl OtherReplayFrom {
1909 #[must_use]
1911 pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
1912 fields.remove("type");
1913 Self {
1914 type_: type_.into(),
1915 fields,
1916 }
1917 }
1918}
1919
1920impl<'de> Deserialize<'de> for OtherReplayFrom {
1921 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1922 where
1923 D: serde::Deserializer<'de>,
1924 {
1925 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
1926 let type_ = fields
1927 .remove("type")
1928 .ok_or_else(|| serde::de::Error::missing_field("type"))?;
1929 let serde_json::Value::String(type_) = type_ else {
1930 return Err(serde::de::Error::custom("`type` must be a string"));
1931 };
1932
1933 if is_known_replay_from_type(&type_) {
1934 return Err(serde::de::Error::custom(format!(
1935 "known replay cursor `{type_}` did not match its schema"
1936 )));
1937 }
1938
1939 Ok(Self { type_, fields })
1940 }
1941}
1942
1943fn is_known_replay_from_type(type_: &str) -> bool {
1944 matches!(type_, "start")
1945}
1946
1947#[cfg(feature = "schemars")]
1948fn other_replay_from_schema(schema: &mut Schema) {
1949 super::schema_util::reject_known_string_discriminators(schema, "type", &["start"]);
1950}
1951
1952crate::serde_util::default_on_null! {
1953 #[serde_as]
1961 #[skip_serializing_none]
1962 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1963 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
1964 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_RESUME_METHOD_NAME)))]
1965 #[serde(rename_all = "camelCase")]
1966 #[non_exhaustive]
1967 pub struct ResumeSessionResponse {
1968 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1974 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1975 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1976 pub config_options: Vec<SessionConfigOption>,
1977 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1983 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1984 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1985 pub available_commands: Vec<AvailableCommand>,
1986 #[serde_as(deserialize_as = "DefaultOnError")]
1992 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1993 #[serde(default)]
1994 #[serde(rename = "_meta")]
1995 pub meta: Option<Meta>,
1996 }
1997}
1998
1999impl ResumeSessionResponse {
2000 #[must_use]
2002 pub fn new() -> Self {
2003 Self::default()
2004 }
2005
2006 #[must_use]
2012 pub fn config_options(mut self, config_options: Vec<SessionConfigOption>) -> Self {
2013 self.config_options = config_options;
2014 self
2015 }
2016
2017 #[must_use]
2019 pub fn available_commands(mut self, available_commands: Vec<AvailableCommand>) -> Self {
2020 self.available_commands = available_commands;
2021 self
2022 }
2023
2024 #[must_use]
2030 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2031 self.meta = meta.into_option();
2032 self
2033 }
2034}
2035
2036#[serde_as]
2059#[skip_serializing_none]
2060#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2061#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2062#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_CLOSE_METHOD_NAME)))]
2063#[serde(rename_all = "camelCase")]
2064#[non_exhaustive]
2065pub struct CloseSessionRequest {
2066 pub session_id: SessionId,
2068 #[serde_as(deserialize_as = "DefaultOnError")]
2074 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2075 #[serde(default)]
2076 #[serde(rename = "_meta")]
2077 pub meta: Option<Meta>,
2078}
2079
2080impl CloseSessionRequest {
2081 #[must_use]
2083 pub fn new(session_id: impl Into<SessionId>) -> Self {
2084 Self {
2085 session_id: session_id.into(),
2086 meta: None,
2087 }
2088 }
2089
2090 #[must_use]
2096 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2097 self.meta = meta.into_option();
2098 self
2099 }
2100}
2101
2102crate::serde_util::default_on_null! {
2103 #[serde_as]
2105 #[skip_serializing_none]
2106 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2107 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
2108 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_CLOSE_METHOD_NAME)))]
2109 #[serde(rename_all = "camelCase")]
2110 #[non_exhaustive]
2111 pub struct CloseSessionResponse {
2112 #[serde_as(deserialize_as = "DefaultOnError")]
2118 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2119 #[serde(default)]
2120 #[serde(rename = "_meta")]
2121 pub meta: Option<Meta>,
2122 }
2123}
2124
2125impl CloseSessionResponse {
2126 #[must_use]
2128 pub fn new() -> Self {
2129 Self::default()
2130 }
2131
2132 #[must_use]
2138 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2139 self.meta = meta.into_option();
2140 self
2141 }
2142}
2143
2144#[cfg(feature = "unstable_session_status")]
2158#[serde_as]
2159#[skip_serializing_none]
2160#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2161#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2162#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_STATUS_METHOD_NAME)))]
2163#[serde(rename_all = "camelCase")]
2164#[non_exhaustive]
2165pub struct SessionStatusRequest {
2166 pub session_id: SessionId,
2168 #[serde_as(deserialize_as = "DefaultOnError")]
2174 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2175 #[serde(default)]
2176 #[serde(rename = "_meta")]
2177 pub meta: Option<Meta>,
2178}
2179
2180#[cfg(feature = "unstable_session_status")]
2181impl SessionStatusRequest {
2182 #[must_use]
2184 pub fn new(session_id: impl Into<SessionId>) -> Self {
2185 Self {
2186 session_id: session_id.into(),
2187 meta: None,
2188 }
2189 }
2190
2191 #[must_use]
2197 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2198 self.meta = meta.into_option();
2199 self
2200 }
2201}
2202
2203#[cfg(feature = "unstable_session_status")]
2212#[serde_as]
2213#[skip_serializing_none]
2214#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2215#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2216#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_STATUS_METHOD_NAME)))]
2217#[serde(rename_all = "camelCase")]
2218#[non_exhaustive]
2219pub struct SessionStatusResponse {
2220 pub status: SessionStatus,
2224 #[serde_as(deserialize_as = "DefaultOnError")]
2232 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2233 #[serde(default)]
2234 pub state: Option<StateUpdate>,
2235 #[serde_as(deserialize_as = "DefaultOnError")]
2241 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2242 #[serde(default)]
2243 #[serde(rename = "_meta")]
2244 pub meta: Option<Meta>,
2245}
2246
2247#[cfg(feature = "unstable_session_status")]
2248impl SessionStatusResponse {
2249 #[must_use]
2251 pub fn new(status: SessionStatus) -> Self {
2252 Self {
2253 status,
2254 state: None,
2255 meta: None,
2256 }
2257 }
2258
2259 #[must_use]
2262 pub fn state(mut self, state: impl IntoOption<StateUpdate>) -> Self {
2263 self.state = state.into_option();
2264 self
2265 }
2266
2267 #[must_use]
2273 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2274 self.meta = meta.into_option();
2275 self
2276 }
2277}
2278
2279#[cfg(feature = "unstable_session_status")]
2285#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2286#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2287#[serde(rename_all = "snake_case")]
2288#[non_exhaustive]
2289pub enum SessionStatus {
2290 Open,
2297 Saved,
2301 #[serde(untagged)]
2308 Other(String),
2309}
2310
2311#[serde_as]
2326#[skip_serializing_none]
2327#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2328#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2329#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = TOOL_CALL_CANCEL_METHOD_NAME)))]
2330#[serde(rename_all = "camelCase")]
2331#[non_exhaustive]
2332pub struct CancelToolCallRequest {
2333 pub session_id: SessionId,
2335 pub tool_call_id: ToolCallId,
2337 #[serde_as(deserialize_as = "DefaultOnError")]
2343 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2344 #[serde(default)]
2345 #[serde(rename = "_meta")]
2346 pub meta: Option<Meta>,
2347}
2348
2349impl CancelToolCallRequest {
2350 #[must_use]
2352 pub fn new(session_id: impl Into<SessionId>, tool_call_id: impl Into<ToolCallId>) -> Self {
2353 Self {
2354 session_id: session_id.into(),
2355 tool_call_id: tool_call_id.into(),
2356 meta: None,
2357 }
2358 }
2359
2360 #[must_use]
2366 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2367 self.meta = meta.into_option();
2368 self
2369 }
2370}
2371
2372crate::serde_util::default_on_null! {
2373 #[serde_as]
2375 #[skip_serializing_none]
2376 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2377 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
2378 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = TOOL_CALL_CANCEL_METHOD_NAME)))]
2379 #[serde(rename_all = "camelCase")]
2380 #[non_exhaustive]
2381 pub struct CancelToolCallResponse {
2382 #[serde_as(deserialize_as = "DefaultOnError")]
2388 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2389 #[serde(default)]
2390 #[serde(rename = "_meta")]
2391 pub meta: Option<Meta>,
2392 }
2393}
2394
2395impl CancelToolCallResponse {
2396 #[must_use]
2398 pub fn new() -> Self {
2399 Self::default()
2400 }
2401
2402 #[must_use]
2408 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2409 self.meta = meta.into_option();
2410 self
2411 }
2412}
2413
2414#[serde_as]
2430#[skip_serializing_none]
2431#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2432#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2433#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = TERMINAL_CANCEL_METHOD_NAME)))]
2434#[serde(rename_all = "camelCase")]
2435#[non_exhaustive]
2436pub struct CancelTerminalRequest {
2437 pub session_id: SessionId,
2439 pub terminal_id: TerminalId,
2441 #[serde_as(deserialize_as = "DefaultOnError")]
2447 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2448 #[serde(default)]
2449 #[serde(rename = "_meta")]
2450 pub meta: Option<Meta>,
2451}
2452
2453impl CancelTerminalRequest {
2454 #[must_use]
2456 pub fn new(session_id: impl Into<SessionId>, terminal_id: impl Into<TerminalId>) -> Self {
2457 Self {
2458 session_id: session_id.into(),
2459 terminal_id: terminal_id.into(),
2460 meta: None,
2461 }
2462 }
2463
2464 #[must_use]
2470 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2471 self.meta = meta.into_option();
2472 self
2473 }
2474}
2475
2476crate::serde_util::default_on_null! {
2477 #[serde_as]
2479 #[skip_serializing_none]
2480 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2481 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
2482 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = TERMINAL_CANCEL_METHOD_NAME)))]
2483 #[serde(rename_all = "camelCase")]
2484 #[non_exhaustive]
2485 pub struct CancelTerminalResponse {
2486 #[serde_as(deserialize_as = "DefaultOnError")]
2492 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2493 #[serde(default)]
2494 #[serde(rename = "_meta")]
2495 pub meta: Option<Meta>,
2496 }
2497}
2498
2499impl CancelTerminalResponse {
2500 #[must_use]
2502 pub fn new() -> Self {
2503 Self::default()
2504 }
2505
2506 #[must_use]
2512 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2513 self.meta = meta.into_option();
2514 self
2515 }
2516}
2517
2518#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2522#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
2523#[serde(transparent)]
2524#[from(Arc<str>, String, &str, &mut str, Box<str>, Cow<'_, str>)]
2525#[non_exhaustive]
2526pub struct SessionListCursor(pub Arc<str>);
2527
2528impl SessionListCursor {
2529 #[must_use]
2531 pub fn new(cursor: impl Into<Self>) -> Self {
2532 cursor.into()
2533 }
2534}
2535
2536impl AsRef<str> for SessionListCursor {
2537 fn as_ref(&self) -> &str {
2538 &self.0
2539 }
2540}
2541
2542impl From<&String> for SessionListCursor {
2543 fn from(cursor: &String) -> Self {
2544 Self(cursor.as_str().into())
2545 }
2546}
2547
2548macro_rules! impl_session_list_cursor_option_conversion {
2549 ($source:ty) => {
2550 impl IntoOption<SessionListCursor> for $source {
2551 fn into_option(self) -> Option<SessionListCursor> {
2552 Some(self.into())
2553 }
2554 }
2555 };
2556}
2557
2558impl_session_list_cursor_option_conversion!(Arc<str>);
2559impl_session_list_cursor_option_conversion!(String);
2560impl_session_list_cursor_option_conversion!(&str);
2561impl_session_list_cursor_option_conversion!(&mut str);
2562impl_session_list_cursor_option_conversion!(&String);
2563impl_session_list_cursor_option_conversion!(Box<str>);
2564impl_session_list_cursor_option_conversion!(Cow<'_, str>);
2565
2566crate::serde_util::default_on_null! {
2567 #[serde_as]
2569 #[skip_serializing_none]
2570 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2571 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
2572 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_LIST_METHOD_NAME)))]
2573 #[serde(rename_all = "camelCase")]
2574 #[non_exhaustive]
2575 pub struct ListSessionsRequest {
2576 #[serde(default)]
2583 pub cwd: Option<AbsolutePath>,
2584 #[serde(default)]
2588 pub cursor: Option<SessionListCursor>,
2589 #[cfg(feature = "unstable_session_archive")]
2599 #[serde(default)]
2600 pub archived: Option<SessionArchiveFilter>,
2601 #[serde_as(deserialize_as = "DefaultOnError")]
2607 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2608 #[serde(default)]
2609 #[serde(rename = "_meta")]
2610 pub meta: Option<Meta>,
2611 }
2612}
2613
2614impl ListSessionsRequest {
2615 #[must_use]
2617 pub fn new() -> Self {
2618 Self::default()
2619 }
2620
2621 #[must_use]
2623 pub fn cwd(mut self, cwd: impl IntoOption<AbsolutePath>) -> Self {
2624 self.cwd = cwd.into_option();
2625 self
2626 }
2627
2628 #[must_use]
2630 pub fn cursor(mut self, cursor: impl IntoOption<SessionListCursor>) -> Self {
2631 self.cursor = cursor.into_option();
2632 self
2633 }
2634
2635 #[cfg(feature = "unstable_session_archive")]
2641 #[must_use]
2642 pub fn archived(mut self, archived: impl IntoOption<SessionArchiveFilter>) -> Self {
2643 self.archived = archived.into_option();
2644 self
2645 }
2646
2647 #[must_use]
2653 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2654 self.meta = meta.into_option();
2655 self
2656 }
2657}
2658
2659#[serde_as]
2661#[skip_serializing_none]
2662#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2663#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2664#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_LIST_METHOD_NAME)))]
2665#[serde(rename_all = "camelCase")]
2666#[non_exhaustive]
2667pub struct ListSessionsResponse {
2668 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
2670 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
2671 pub sessions: Vec<SessionInfo>,
2672 #[serde(default)]
2676 pub next_cursor: Option<SessionListCursor>,
2677 #[serde_as(deserialize_as = "DefaultOnError")]
2683 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2684 #[serde(default)]
2685 #[serde(rename = "_meta")]
2686 pub meta: Option<Meta>,
2687}
2688
2689impl ListSessionsResponse {
2690 #[must_use]
2692 pub fn new(sessions: Vec<SessionInfo>) -> Self {
2693 Self {
2694 sessions,
2695 next_cursor: None,
2696 meta: None,
2697 }
2698 }
2699
2700 #[must_use]
2702 pub fn next_cursor(mut self, next_cursor: impl IntoOption<SessionListCursor>) -> Self {
2703 self.next_cursor = next_cursor.into_option();
2704 self
2705 }
2706
2707 #[must_use]
2713 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2714 self.meta = meta.into_option();
2715 self
2716 }
2717}
2718
2719#[serde_as]
2725#[skip_serializing_none]
2726#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2727#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2728#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_DELETE_METHOD_NAME)))]
2729#[serde(rename_all = "camelCase")]
2730#[non_exhaustive]
2731pub struct DeleteSessionRequest {
2732 pub session_id: SessionId,
2734 #[serde_as(deserialize_as = "DefaultOnError")]
2740 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2741 #[serde(default)]
2742 #[serde(rename = "_meta")]
2743 pub meta: Option<Meta>,
2744}
2745
2746impl DeleteSessionRequest {
2747 #[must_use]
2749 pub fn new(session_id: impl Into<SessionId>) -> Self {
2750 Self {
2751 session_id: session_id.into(),
2752 meta: None,
2753 }
2754 }
2755
2756 #[must_use]
2762 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2763 self.meta = meta.into_option();
2764 self
2765 }
2766}
2767
2768crate::serde_util::default_on_null! {
2769 #[serde_as]
2771 #[skip_serializing_none]
2772 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2773 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
2774 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_DELETE_METHOD_NAME)))]
2775 #[serde(rename_all = "camelCase")]
2776 #[non_exhaustive]
2777 pub struct DeleteSessionResponse {
2778 #[serde_as(deserialize_as = "DefaultOnError")]
2784 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2785 #[serde(default)]
2786 #[serde(rename = "_meta")]
2787 pub meta: Option<Meta>,
2788 }
2789}
2790
2791impl DeleteSessionResponse {
2792 #[must_use]
2794 pub fn new() -> Self {
2795 Self::default()
2796 }
2797
2798 #[must_use]
2804 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2805 self.meta = meta.into_option();
2806 self
2807 }
2808}
2809
2810#[cfg(feature = "unstable_session_archive")]
2826#[serde_as]
2827#[skip_serializing_none]
2828#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2829#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2830#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_ARCHIVE_METHOD_NAME)))]
2831#[serde(rename_all = "camelCase")]
2832#[non_exhaustive]
2833pub struct ArchiveSessionRequest {
2834 pub session_id: SessionId,
2836 #[serde_as(deserialize_as = "DefaultOnError")]
2842 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2843 #[serde(default)]
2844 #[serde(rename = "_meta")]
2845 pub meta: Option<Meta>,
2846}
2847
2848#[cfg(feature = "unstable_session_archive")]
2849impl ArchiveSessionRequest {
2850 #[must_use]
2852 pub fn new(session_id: impl Into<SessionId>) -> Self {
2853 Self {
2854 session_id: session_id.into(),
2855 meta: None,
2856 }
2857 }
2858
2859 #[must_use]
2865 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2866 self.meta = meta.into_option();
2867 self
2868 }
2869}
2870
2871#[cfg(feature = "unstable_session_archive")]
2872crate::serde_util::default_on_null! {
2873 #[serde_as]
2879 #[skip_serializing_none]
2880 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2881 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
2882 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_ARCHIVE_METHOD_NAME)))]
2883 #[serde(rename_all = "camelCase")]
2884 #[non_exhaustive]
2885 pub struct ArchiveSessionResponse {
2886 #[serde_as(deserialize_as = "DefaultOnError")]
2892 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2893 #[serde(default)]
2894 #[serde(rename = "_meta")]
2895 pub meta: Option<Meta>,
2896 }
2897}
2898
2899#[cfg(feature = "unstable_session_archive")]
2900impl ArchiveSessionResponse {
2901 #[must_use]
2903 pub fn new() -> Self {
2904 Self::default()
2905 }
2906
2907 #[must_use]
2913 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2914 self.meta = meta.into_option();
2915 self
2916 }
2917}
2918
2919#[cfg(feature = "unstable_session_archive")]
2934#[serde_as]
2935#[skip_serializing_none]
2936#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2937#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2938#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_UNARCHIVE_METHOD_NAME)))]
2939#[serde(rename_all = "camelCase")]
2940#[non_exhaustive]
2941pub struct UnarchiveSessionRequest {
2942 pub session_id: SessionId,
2944 #[serde_as(deserialize_as = "DefaultOnError")]
2950 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2951 #[serde(default)]
2952 #[serde(rename = "_meta")]
2953 pub meta: Option<Meta>,
2954}
2955
2956#[cfg(feature = "unstable_session_archive")]
2957impl UnarchiveSessionRequest {
2958 #[must_use]
2960 pub fn new(session_id: impl Into<SessionId>) -> Self {
2961 Self {
2962 session_id: session_id.into(),
2963 meta: None,
2964 }
2965 }
2966
2967 #[must_use]
2973 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2974 self.meta = meta.into_option();
2975 self
2976 }
2977}
2978
2979#[cfg(feature = "unstable_session_archive")]
2980crate::serde_util::default_on_null! {
2981 #[serde_as]
2987 #[skip_serializing_none]
2988 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2989 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
2990 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_UNARCHIVE_METHOD_NAME)))]
2991 #[serde(rename_all = "camelCase")]
2992 #[non_exhaustive]
2993 pub struct UnarchiveSessionResponse {
2994 #[serde_as(deserialize_as = "DefaultOnError")]
3000 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3001 #[serde(default)]
3002 #[serde(rename = "_meta")]
3003 pub meta: Option<Meta>,
3004 }
3005}
3006
3007#[cfg(feature = "unstable_session_archive")]
3008impl UnarchiveSessionResponse {
3009 #[must_use]
3011 pub fn new() -> Self {
3012 Self::default()
3013 }
3014
3015 #[must_use]
3021 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3022 self.meta = meta.into_option();
3023 self
3024 }
3025}
3026
3027#[cfg(feature = "unstable_session_archive")]
3036#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3037#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
3038#[serde(rename_all = "snake_case")]
3039#[non_exhaustive]
3040pub enum SessionArchiveFilter {
3041 #[default]
3043 Exclude,
3044 Include,
3046 Only,
3048 #[serde(untagged)]
3054 Other(String),
3055}
3056
3057#[serde_as]
3059#[skip_serializing_none]
3060#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3061#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3062#[serde(rename_all = "camelCase")]
3063#[non_exhaustive]
3064pub struct SessionInfo {
3065 pub session_id: SessionId,
3067 pub cwd: AbsolutePath,
3069 #[serde_as(deserialize_as = "DefaultOnNull")]
3075 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
3076 #[serde(default, skip_serializing_if = "Vec::is_empty")]
3077 pub additional_directories: Vec<AbsolutePath>,
3078
3079 #[serde_as(deserialize_as = "DefaultOnError")]
3083 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3084 #[serde(default)]
3085 pub title: Option<String>,
3086 #[serde_as(deserialize_as = "DefaultOnError")]
3090 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "format" = "date-time")))]
3091 #[serde(default)]
3092 pub updated_at: Option<String>,
3093 #[cfg(feature = "unstable_session_archive")]
3102 #[serde_as(deserialize_as = "DefaultOnError")]
3103 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3104 #[serde(default)]
3105 pub archived: Option<bool>,
3106 #[serde_as(deserialize_as = "DefaultOnError")]
3112 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3113 #[serde(default)]
3114 #[serde(rename = "_meta")]
3115 pub meta: Option<Meta>,
3116}
3117
3118impl SessionInfo {
3119 #[must_use]
3121 pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<AbsolutePath>) -> Self {
3122 Self {
3123 session_id: session_id.into(),
3124 cwd: cwd.into(),
3125 additional_directories: vec![],
3126 title: None,
3127 updated_at: None,
3128 #[cfg(feature = "unstable_session_archive")]
3129 archived: None,
3130 meta: None,
3131 }
3132 }
3133
3134 #[must_use]
3136 pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
3137 where
3138 I: IntoIterator<Item = P>,
3139 P: Into<AbsolutePath>,
3140 {
3141 self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
3142 self
3143 }
3144
3145 #[must_use]
3147 pub fn title(mut self, title: impl IntoOption<String>) -> Self {
3148 self.title = title.into_option();
3149 self
3150 }
3151
3152 #[must_use]
3154 pub fn updated_at(mut self, updated_at: impl IntoOption<String>) -> Self {
3155 self.updated_at = updated_at.into_option();
3156 self
3157 }
3158
3159 #[cfg(feature = "unstable_session_archive")]
3165 #[must_use]
3166 pub fn archived(mut self, archived: impl IntoOption<bool>) -> Self {
3167 self.archived = archived.into_option();
3168 self
3169 }
3170
3171 #[must_use]
3177 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3178 self.meta = meta.into_option();
3179 self
3180 }
3181}
3182
3183#[cfg(feature = "unstable_session_set_title")]
3199#[serde_as]
3200#[skip_serializing_none]
3201#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3202#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3203#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_TITLE_METHOD_NAME)))]
3204#[serde(rename_all = "camelCase")]
3205#[non_exhaustive]
3206pub struct SetSessionTitleRequest {
3207 pub session_id: SessionId,
3209 pub title: String,
3216 #[serde_as(deserialize_as = "DefaultOnError")]
3222 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3223 #[serde(default)]
3224 #[serde(rename = "_meta")]
3225 pub meta: Option<Meta>,
3226}
3227
3228#[cfg(feature = "unstable_session_set_title")]
3229impl SetSessionTitleRequest {
3230 #[must_use]
3232 pub fn new(session_id: impl Into<SessionId>, title: impl Into<String>) -> Self {
3233 Self {
3234 session_id: session_id.into(),
3235 title: title.into(),
3236 meta: None,
3237 }
3238 }
3239
3240 #[must_use]
3246 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3247 self.meta = meta.into_option();
3248 self
3249 }
3250}
3251
3252#[cfg(feature = "unstable_session_set_title")]
3258#[serde_as]
3259#[skip_serializing_none]
3260#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3261#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3262#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_TITLE_METHOD_NAME)))]
3263#[serde(rename_all = "camelCase")]
3264#[non_exhaustive]
3265pub struct SetSessionTitleResponse {
3266 pub title: String,
3271 #[serde_as(deserialize_as = "DefaultOnError")]
3277 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3278 #[serde(default)]
3279 #[serde(rename = "_meta")]
3280 pub meta: Option<Meta>,
3281}
3282
3283#[cfg(feature = "unstable_session_set_title")]
3284impl SetSessionTitleResponse {
3285 #[must_use]
3287 pub fn new(title: impl Into<String>) -> Self {
3288 Self {
3289 title: title.into(),
3290 meta: None,
3291 }
3292 }
3293
3294 #[must_use]
3300 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3301 self.meta = meta.into_option();
3302 self
3303 }
3304}
3305
3306#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3310#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, From, Display)]
3311#[serde(transparent)]
3312#[from(forward)]
3313#[non_exhaustive]
3314pub struct SessionConfigId(pub Arc<str>);
3315
3316impl SessionConfigId {
3317 #[must_use]
3319 pub fn new(id: impl Into<Self>) -> Self {
3320 id.into()
3321 }
3322}
3323
3324#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3326#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, From, Display)]
3327#[serde(transparent)]
3328#[from(forward)]
3329#[non_exhaustive]
3330pub struct SessionConfigValueId(pub Arc<str>);
3331
3332impl SessionConfigValueId {
3333 #[must_use]
3335 pub fn new(id: impl Into<Self>) -> Self {
3336 id.into()
3337 }
3338}
3339
3340#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3342#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, From, Display)]
3343#[serde(transparent)]
3344#[from(forward)]
3345#[non_exhaustive]
3346pub struct SessionConfigGroupId(pub Arc<str>);
3347
3348impl SessionConfigGroupId {
3349 #[must_use]
3351 pub fn new(id: impl Into<Self>) -> Self {
3352 id.into()
3353 }
3354}
3355
3356#[serde_as]
3358#[skip_serializing_none]
3359#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3360#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3361#[serde(rename_all = "camelCase")]
3362#[non_exhaustive]
3363pub struct SessionConfigSelectOption {
3364 pub value: SessionConfigValueId,
3366 pub name: String,
3368 #[serde_as(deserialize_as = "DefaultOnError")]
3372 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3373 #[serde(default)]
3374 pub description: Option<String>,
3375 #[serde_as(deserialize_as = "DefaultOnError")]
3381 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3382 #[serde(default)]
3383 #[serde(rename = "_meta")]
3384 pub meta: Option<Meta>,
3385}
3386
3387impl SessionConfigSelectOption {
3388 #[must_use]
3390 pub fn new(value: impl Into<SessionConfigValueId>, name: impl Into<String>) -> Self {
3391 Self {
3392 value: value.into(),
3393 name: name.into(),
3394 description: None,
3395 meta: None,
3396 }
3397 }
3398
3399 #[must_use]
3401 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
3402 self.description = description.into_option();
3403 self
3404 }
3405
3406 #[must_use]
3412 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3413 self.meta = meta.into_option();
3414 self
3415 }
3416}
3417
3418#[serde_as]
3420#[skip_serializing_none]
3421#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3422#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3423#[serde(rename_all = "camelCase")]
3424#[non_exhaustive]
3425pub struct SessionConfigSelectGroup {
3426 pub group_id: SessionConfigGroupId,
3428 pub name: String,
3430 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
3432 #[cfg_attr(feature = "schemars", schemars(length(min = 1), extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
3433 pub options: Vec<SessionConfigSelectOption>,
3434 #[serde_as(deserialize_as = "DefaultOnError")]
3440 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3441 #[serde(default)]
3442 #[serde(rename = "_meta")]
3443 pub meta: Option<Meta>,
3444}
3445
3446impl SessionConfigSelectGroup {
3447 #[must_use]
3449 pub fn new(
3450 group_id: impl Into<SessionConfigGroupId>,
3451 name: impl Into<String>,
3452 options: Vec<SessionConfigSelectOption>,
3453 ) -> Self {
3454 Self {
3455 group_id: group_id.into(),
3456 name: name.into(),
3457 options,
3458 meta: None,
3459 }
3460 }
3461
3462 #[must_use]
3468 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3469 self.meta = meta.into_option();
3470 self
3471 }
3472}
3473
3474#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3476#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3477#[serde(untagged)]
3478#[non_exhaustive]
3479pub enum SessionConfigSelectOptions {
3480 Ungrouped(
3482 #[cfg_attr(feature = "schemars", schemars(length(min = 1)))] Vec<SessionConfigSelectOption>,
3483 ),
3484 Grouped(
3486 #[cfg_attr(feature = "schemars", schemars(length(min = 1)))] Vec<SessionConfigSelectGroup>,
3487 ),
3488}
3489
3490impl From<Vec<SessionConfigSelectOption>> for SessionConfigSelectOptions {
3491 fn from(options: Vec<SessionConfigSelectOption>) -> Self {
3492 SessionConfigSelectOptions::Ungrouped(options)
3493 }
3494}
3495
3496impl From<Vec<SessionConfigSelectGroup>> for SessionConfigSelectOptions {
3497 fn from(groups: Vec<SessionConfigSelectGroup>) -> Self {
3498 SessionConfigSelectOptions::Grouped(groups)
3499 }
3500}
3501
3502#[skip_serializing_none]
3504#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3505#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3506#[serde(rename_all = "camelCase")]
3507#[non_exhaustive]
3508pub struct SessionConfigSelect {
3509 pub current_value: SessionConfigValueId,
3511 pub options: SessionConfigSelectOptions,
3514}
3515
3516impl SessionConfigSelect {
3517 #[must_use]
3519 pub fn new(
3520 current_value: impl Into<SessionConfigValueId>,
3521 options: impl Into<SessionConfigSelectOptions>,
3522 ) -> Self {
3523 Self {
3524 current_value: current_value.into(),
3525 options: options.into(),
3526 }
3527 }
3528}
3529
3530#[skip_serializing_none]
3532#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3533#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3534#[serde(rename_all = "camelCase")]
3535#[non_exhaustive]
3536pub struct SessionConfigBoolean {
3537 pub current_value: bool,
3539}
3540
3541impl SessionConfigBoolean {
3542 #[must_use]
3544 pub fn new(current_value: bool) -> Self {
3545 Self { current_value }
3546 }
3547}
3548
3549#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3559#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3560#[serde(rename_all = "snake_case")]
3561#[non_exhaustive]
3562pub enum SessionConfigOptionCategory {
3563 Mode,
3565 Model,
3567 ModelConfig,
3569 ThoughtLevel,
3571 #[serde(untagged)]
3577 Other(String),
3578}
3579
3580#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3582#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3583#[serde(tag = "type", rename_all = "snake_case")]
3584#[non_exhaustive]
3585pub enum SessionConfigKind {
3586 Select(SessionConfigSelect),
3588 Boolean(SessionConfigBoolean),
3590 #[serde(untagged)]
3600 Other(OtherSessionConfigKind),
3601}
3602
3603#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3605#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
3606#[cfg_attr(feature = "schemars", schemars(inline))]
3607#[cfg_attr(feature = "schemars", schemars(transform = other_session_config_kind_schema))]
3608#[serde(rename_all = "camelCase")]
3609#[non_exhaustive]
3610pub struct OtherSessionConfigKind {
3611 #[serde(rename = "type")]
3617 pub type_: String,
3618 #[serde(flatten)]
3620 pub fields: BTreeMap<String, serde_json::Value>,
3621}
3622
3623impl OtherSessionConfigKind {
3624 #[must_use]
3626 pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
3627 fields.remove("type");
3628 fields.remove("_meta");
3629 Self {
3630 type_: type_.into(),
3631 fields,
3632 }
3633 }
3634}
3635
3636impl<'de> Deserialize<'de> for OtherSessionConfigKind {
3637 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3638 where
3639 D: serde::Deserializer<'de>,
3640 {
3641 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
3642 let type_ = fields
3643 .remove("type")
3644 .ok_or_else(|| serde::de::Error::missing_field("type"))?;
3645 let serde_json::Value::String(type_) = type_ else {
3646 return Err(serde::de::Error::custom("`type` must be a string"));
3647 };
3648
3649 if is_known_session_config_kind_type(&type_) {
3650 return Err(serde::de::Error::custom(format!(
3651 "known session configuration option `{type_}` did not match its schema"
3652 )));
3653 }
3654
3655 Ok(Self { type_, fields })
3656 }
3657}
3658
3659fn is_known_session_config_kind_type(type_: &str) -> bool {
3660 matches!(type_, "select" | "boolean")
3661}
3662
3663#[cfg(feature = "schemars")]
3664fn other_session_config_kind_schema(schema: &mut Schema) {
3665 super::schema_util::reject_known_string_discriminators(schema, "type", &["select", "boolean"]);
3666}
3667
3668#[serde_as]
3670#[skip_serializing_none]
3671#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3672#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3673#[serde(rename_all = "camelCase")]
3674#[non_exhaustive]
3675pub struct SessionConfigOption {
3676 pub config_id: SessionConfigId,
3678 pub name: String,
3680 #[serde_as(deserialize_as = "DefaultOnError")]
3684 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3685 #[serde(default)]
3686 pub description: Option<String>,
3687 #[serde_as(deserialize_as = "DefaultOnError")]
3691 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3692 #[serde(default)]
3693 pub category: Option<SessionConfigOptionCategory>,
3694 #[serde(flatten)]
3696 pub kind: SessionConfigKind,
3697 #[serde_as(deserialize_as = "DefaultOnError")]
3703 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3704 #[serde(default)]
3705 #[serde(rename = "_meta")]
3706 pub meta: Option<Meta>,
3707}
3708
3709impl SessionConfigOption {
3710 #[must_use]
3712 pub fn new(
3713 config_id: impl Into<SessionConfigId>,
3714 name: impl Into<String>,
3715 kind: SessionConfigKind,
3716 ) -> Self {
3717 Self {
3718 config_id: config_id.into(),
3719 name: name.into(),
3720 description: None,
3721 category: None,
3722 kind,
3723 meta: None,
3724 }
3725 }
3726
3727 #[must_use]
3729 pub fn select(
3730 config_id: impl Into<SessionConfigId>,
3731 name: impl Into<String>,
3732 current_value: impl Into<SessionConfigValueId>,
3733 options: impl Into<SessionConfigSelectOptions>,
3734 ) -> Self {
3735 Self::new(
3736 config_id,
3737 name,
3738 SessionConfigKind::Select(SessionConfigSelect::new(current_value, options)),
3739 )
3740 }
3741
3742 #[must_use]
3744 pub fn boolean(
3745 config_id: impl Into<SessionConfigId>,
3746 name: impl Into<String>,
3747 current_value: bool,
3748 ) -> Self {
3749 Self::new(
3750 config_id,
3751 name,
3752 SessionConfigKind::Boolean(SessionConfigBoolean::new(current_value)),
3753 )
3754 }
3755
3756 #[must_use]
3758 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
3759 self.description = description.into_option();
3760 self
3761 }
3762
3763 #[must_use]
3765 pub fn category(mut self, category: impl IntoOption<SessionConfigOptionCategory>) -> Self {
3766 self.category = category.into_option();
3767 self
3768 }
3769
3770 #[must_use]
3776 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3777 self.meta = meta.into_option();
3778 self
3779 }
3780}
3781
3782#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3791#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
3792#[serde(tag = "type", rename_all = "snake_case")]
3793#[non_exhaustive]
3794pub enum SessionConfigOptionValue {
3795 Id {
3797 value: SessionConfigValueId,
3799 },
3800 Boolean {
3802 value: bool,
3804 },
3805 #[serde(untagged)]
3814 Other(OtherSessionConfigOptionValue),
3815}
3816
3817#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3819#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
3820#[cfg_attr(feature = "schemars", schemars(inline))]
3821#[cfg_attr(feature = "schemars", schemars(transform = other_session_config_option_value_schema))]
3822#[serde(rename_all = "camelCase")]
3823#[non_exhaustive]
3824pub struct OtherSessionConfigOptionValue {
3825 #[serde(rename = "type")]
3831 pub type_: String,
3832 pub value: serde_json::Value,
3834 #[serde(flatten)]
3836 pub fields: BTreeMap<String, serde_json::Value>,
3837}
3838
3839impl OtherSessionConfigOptionValue {
3840 #[must_use]
3842 pub fn new(
3843 type_: impl Into<String>,
3844 value: serde_json::Value,
3845 mut fields: BTreeMap<String, serde_json::Value>,
3846 ) -> Self {
3847 fields.remove("type");
3848 fields.remove("value");
3849 fields.remove("_meta");
3850 Self {
3851 type_: type_.into(),
3852 value,
3853 fields,
3854 }
3855 }
3856}
3857
3858impl<'de> Deserialize<'de> for OtherSessionConfigOptionValue {
3859 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3860 where
3861 D: serde::Deserializer<'de>,
3862 {
3863 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
3864 let type_ = fields
3865 .remove("type")
3866 .ok_or_else(|| serde::de::Error::missing_field("type"))?;
3867 let serde_json::Value::String(type_) = type_ else {
3868 return Err(serde::de::Error::custom("`type` must be a string"));
3869 };
3870
3871 if is_known_session_config_option_value_type(&type_) {
3872 return Err(serde::de::Error::custom(format!(
3873 "known session configuration option value `{type_}` did not match its schema"
3874 )));
3875 }
3876
3877 let value = fields
3878 .remove("value")
3879 .ok_or_else(|| serde::de::Error::missing_field("value"))?;
3880
3881 Ok(Self {
3882 type_,
3883 value,
3884 fields,
3885 })
3886 }
3887}
3888
3889impl<'de> Deserialize<'de> for SessionConfigOptionValue {
3890 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3891 where
3892 D: serde::Deserializer<'de>,
3893 {
3894 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
3895 let type_ = fields.remove("type");
3896 let value = fields
3897 .remove("value")
3898 .ok_or_else(|| serde::de::Error::missing_field("value"))?;
3899
3900 let type_ = type_.ok_or_else(|| serde::de::Error::missing_field("type"))?;
3901
3902 let serde_json::Value::String(type_) = type_ else {
3903 return Err(serde::de::Error::custom("`type` must be a string"));
3904 };
3905
3906 match type_.as_str() {
3907 "id" => {
3908 let value = serde_json::from_value(value).map_err(|error| {
3909 serde::de::Error::custom(format!(
3910 "`value` must be a string for `type: id`: {error}"
3911 ))
3912 })?;
3913 Ok(Self::Id { value })
3914 }
3915 "boolean" => {
3916 let value = serde_json::from_value(value).map_err(|error| {
3917 serde::de::Error::custom(format!(
3918 "`value` must be a boolean for `type: boolean`: {error}"
3919 ))
3920 })?;
3921 Ok(Self::Boolean { value })
3922 }
3923 _ => Ok(Self::Other(OtherSessionConfigOptionValue {
3924 type_,
3925 value,
3926 fields,
3927 })),
3928 }
3929 }
3930}
3931
3932fn is_known_session_config_option_value_type(type_: &str) -> bool {
3933 matches!(type_, "id" | "boolean")
3934}
3935
3936#[cfg(feature = "schemars")]
3937fn other_session_config_option_value_schema(schema: &mut Schema) {
3938 super::schema_util::reject_known_string_discriminators(schema, "type", &["id", "boolean"]);
3939}
3940
3941impl SessionConfigOptionValue {
3942 #[must_use]
3944 pub fn id(id: impl Into<SessionConfigValueId>) -> Self {
3945 Self::Id { value: id.into() }
3946 }
3947
3948 #[must_use]
3950 pub fn boolean(val: bool) -> Self {
3951 Self::Boolean { value: val }
3952 }
3953
3954 #[must_use]
3957 pub fn as_id(&self) -> Option<&SessionConfigValueId> {
3958 match self {
3959 Self::Id { value } => Some(value),
3960 _ => None,
3961 }
3962 }
3963
3964 #[must_use]
3966 pub fn as_bool(&self) -> Option<bool> {
3967 match self {
3968 Self::Boolean { value } => Some(*value),
3969 _ => None,
3970 }
3971 }
3972}
3973
3974impl From<SessionConfigValueId> for SessionConfigOptionValue {
3975 fn from(value: SessionConfigValueId) -> Self {
3976 Self::Id { value }
3977 }
3978}
3979
3980impl From<bool> for SessionConfigOptionValue {
3981 fn from(value: bool) -> Self {
3982 Self::Boolean { value }
3983 }
3984}
3985
3986impl From<&str> for SessionConfigOptionValue {
3987 fn from(value: &str) -> Self {
3988 Self::Id {
3989 value: SessionConfigValueId::new(value),
3990 }
3991 }
3992}
3993
3994#[serde_as]
4002#[skip_serializing_none]
4003#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4004#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4005#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_CONFIG_OPTION_METHOD_NAME)))]
4006#[serde(rename_all = "camelCase")]
4007#[non_exhaustive]
4008pub struct SetSessionConfigOptionRequest {
4009 pub session_id: SessionId,
4011 pub config_id: SessionConfigId,
4013 #[serde(flatten)]
4017 pub value: SessionConfigOptionValue,
4018 #[serde_as(deserialize_as = "DefaultOnError")]
4024 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4025 #[serde(default)]
4026 #[serde(rename = "_meta")]
4027 pub meta: Option<Meta>,
4028}
4029
4030impl SetSessionConfigOptionRequest {
4031 #[must_use]
4033 pub fn new(
4034 session_id: impl Into<SessionId>,
4035 config_id: impl Into<SessionConfigId>,
4036 value: impl Into<SessionConfigOptionValue>,
4037 ) -> Self {
4038 Self {
4039 session_id: session_id.into(),
4040 config_id: config_id.into(),
4041 value: value.into(),
4042 meta: None,
4043 }
4044 }
4045
4046 #[must_use]
4052 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4053 self.meta = meta.into_option();
4054 self
4055 }
4056}
4057
4058#[serde_as]
4060#[skip_serializing_none]
4061#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4062#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4063#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_CONFIG_OPTION_METHOD_NAME)))]
4064#[serde(rename_all = "camelCase")]
4065#[non_exhaustive]
4066pub struct SetSessionConfigOptionResponse {
4067 #[serde_as(deserialize_as = "VecSkipError<_>")]
4069 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-skip-invalid-items" = true)))]
4070 pub config_options: Vec<SessionConfigOption>,
4071 #[serde_as(deserialize_as = "DefaultOnError")]
4077 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4078 #[serde(default)]
4079 #[serde(rename = "_meta")]
4080 pub meta: Option<Meta>,
4081}
4082
4083impl SetSessionConfigOptionResponse {
4084 #[must_use]
4086 pub fn new(config_options: Vec<SessionConfigOption>) -> Self {
4087 Self {
4088 config_options,
4089 meta: None,
4090 }
4091 }
4092
4093 #[must_use]
4099 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4100 self.meta = meta.into_option();
4101 self
4102 }
4103}
4104
4105#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4114#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4115#[serde(tag = "type", rename_all = "snake_case")]
4116#[non_exhaustive]
4117pub enum McpServer {
4118 Http(McpServerHttp),
4122 #[cfg(feature = "unstable_mcp_over_acp")]
4131 Acp(McpServerAcp),
4132 Stdio(McpServerStdio),
4136 #[serde(untagged)]
4147 Other(OtherMcpServer),
4148}
4149
4150#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4152#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
4153#[cfg_attr(feature = "schemars", schemars(inline))]
4154#[cfg_attr(feature = "schemars", schemars(transform = other_mcp_server_schema))]
4155#[serde(rename_all = "camelCase")]
4156#[non_exhaustive]
4157pub struct OtherMcpServer {
4158 #[serde(rename = "type")]
4164 pub type_: String,
4165 #[serde(flatten)]
4167 pub fields: BTreeMap<String, serde_json::Value>,
4168}
4169
4170impl OtherMcpServer {
4171 #[must_use]
4173 pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
4174 fields.remove("type");
4175 Self {
4176 type_: type_.into(),
4177 fields,
4178 }
4179 }
4180}
4181
4182impl<'de> Deserialize<'de> for OtherMcpServer {
4183 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
4184 where
4185 D: serde::Deserializer<'de>,
4186 {
4187 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
4188 let type_ = fields
4189 .remove("type")
4190 .ok_or_else(|| serde::de::Error::missing_field("type"))?;
4191 let serde_json::Value::String(type_) = type_ else {
4192 return Err(serde::de::Error::custom("`type` must be a string"));
4193 };
4194
4195 if is_known_mcp_server_type(&type_) {
4196 return Err(serde::de::Error::custom(format!(
4197 "known MCP server transport `{type_}` did not match its schema"
4198 )));
4199 }
4200
4201 Ok(Self { type_, fields })
4202 }
4203}
4204
4205fn is_known_mcp_server_type(type_: &str) -> bool {
4206 match type_ {
4207 "http" | "stdio" => true,
4208 #[cfg(feature = "unstable_mcp_over_acp")]
4209 "acp" => true,
4210 _ => false,
4211 }
4212}
4213
4214#[cfg(feature = "schemars")]
4215fn other_mcp_server_schema(schema: &mut Schema) {
4216 super::schema_util::reject_known_string_discriminators(
4217 schema,
4218 "type",
4219 &[
4220 "http",
4221 "stdio",
4222 #[cfg(feature = "unstable_mcp_over_acp")]
4223 "acp",
4224 ],
4225 );
4226}
4227
4228#[serde_as]
4230#[skip_serializing_none]
4231#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4232#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4233#[serde(rename_all = "camelCase")]
4234#[non_exhaustive]
4235pub struct McpServerHttp {
4236 pub name: String,
4238 #[cfg_attr(feature = "schemars", schemars(url))]
4240 pub url: String,
4241 #[serde_as(deserialize_as = "DefaultOnNull")]
4244 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
4245 #[serde(default, skip_serializing_if = "Vec::is_empty")]
4246 pub headers: Vec<HttpHeader>,
4247 #[serde_as(deserialize_as = "DefaultOnError")]
4253 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4254 #[serde(default)]
4255 #[serde(rename = "_meta")]
4256 pub meta: Option<Meta>,
4257}
4258
4259impl McpServerHttp {
4260 #[must_use]
4262 pub fn new(name: impl Into<String>, url: impl Into<String>) -> Self {
4263 Self {
4264 name: name.into(),
4265 url: url.into(),
4266 headers: Vec::new(),
4267 meta: None,
4268 }
4269 }
4270
4271 #[must_use]
4274 pub fn headers(mut self, headers: Vec<HttpHeader>) -> Self {
4275 self.headers = headers;
4276 self
4277 }
4278
4279 #[must_use]
4285 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4286 self.meta = meta.into_option();
4287 self
4288 }
4289}
4290
4291#[cfg(feature = "unstable_mcp_over_acp")]
4300#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4301#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
4302#[serde(transparent)]
4303#[from(forward)]
4304#[non_exhaustive]
4305pub struct McpServerAcpId(pub Arc<str>);
4306
4307#[cfg(feature = "unstable_mcp_over_acp")]
4308impl McpServerAcpId {
4309 #[must_use]
4311 pub fn new(id: impl Into<Self>) -> Self {
4312 id.into()
4313 }
4314}
4315
4316#[serde_as]
4325#[skip_serializing_none]
4326#[cfg(feature = "unstable_mcp_over_acp")]
4327#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4328#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4329#[serde(rename_all = "camelCase")]
4330#[non_exhaustive]
4331pub struct McpServerAcp {
4332 pub name: String,
4334 pub server_id: McpServerAcpId,
4339 #[serde_as(deserialize_as = "DefaultOnError")]
4345 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4346 #[serde(default)]
4347 #[serde(rename = "_meta")]
4348 pub meta: Option<Meta>,
4349}
4350
4351#[cfg(feature = "unstable_mcp_over_acp")]
4352impl McpServerAcp {
4353 #[must_use]
4355 pub fn new(name: impl Into<String>, server_id: impl Into<McpServerAcpId>) -> Self {
4356 Self {
4357 name: name.into(),
4358 server_id: server_id.into(),
4359 meta: None,
4360 }
4361 }
4362
4363 #[must_use]
4369 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4370 self.meta = meta.into_option();
4371 self
4372 }
4373}
4374
4375#[serde_as]
4377#[skip_serializing_none]
4378#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4379#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4380#[serde(rename_all = "camelCase")]
4381#[non_exhaustive]
4382pub struct McpServerStdio {
4383 pub name: String,
4385 pub command: AbsolutePath,
4387 #[serde_as(deserialize_as = "DefaultOnNull")]
4389 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
4390 #[serde(default, skip_serializing_if = "Vec::is_empty")]
4391 pub args: Vec<String>,
4392 #[serde_as(deserialize_as = "DefaultOnNull")]
4395 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
4396 #[serde(default, skip_serializing_if = "Vec::is_empty")]
4397 pub env: Vec<EnvVariable>,
4398 #[serde_as(deserialize_as = "DefaultOnError")]
4404 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4405 #[serde(default)]
4406 #[serde(rename = "_meta")]
4407 pub meta: Option<Meta>,
4408}
4409
4410impl McpServerStdio {
4411 #[must_use]
4413 pub fn new(name: impl Into<String>, command: impl Into<AbsolutePath>) -> Self {
4414 Self {
4415 name: name.into(),
4416 command: command.into(),
4417 args: Vec::new(),
4418 env: Vec::new(),
4419 meta: None,
4420 }
4421 }
4422
4423 #[must_use]
4425 pub fn args(mut self, args: Vec<String>) -> Self {
4426 self.args = args;
4427 self
4428 }
4429
4430 #[must_use]
4433 pub fn env(mut self, env: Vec<EnvVariable>) -> Self {
4434 self.env = env;
4435 self
4436 }
4437
4438 #[must_use]
4444 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4445 self.meta = meta.into_option();
4446 self
4447 }
4448}
4449
4450#[serde_as]
4452#[skip_serializing_none]
4453#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4454#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4455#[serde(rename_all = "camelCase")]
4456#[non_exhaustive]
4457pub struct EnvVariable {
4458 pub name: String,
4460 pub value: String,
4462 #[serde_as(deserialize_as = "DefaultOnError")]
4468 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4469 #[serde(default)]
4470 #[serde(rename = "_meta")]
4471 pub meta: Option<Meta>,
4472}
4473
4474impl EnvVariable {
4475 #[must_use]
4477 pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
4478 Self {
4479 name: name.into(),
4480 value: value.into(),
4481 meta: None,
4482 }
4483 }
4484
4485 #[must_use]
4491 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4492 self.meta = meta.into_option();
4493 self
4494 }
4495}
4496
4497#[serde_as]
4499#[skip_serializing_none]
4500#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4501#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4502#[serde(rename_all = "camelCase")]
4503#[non_exhaustive]
4504pub struct HttpHeader {
4505 pub name: String,
4507 pub value: String,
4509 #[serde_as(deserialize_as = "DefaultOnError")]
4515 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4516 #[serde(default)]
4517 #[serde(rename = "_meta")]
4518 pub meta: Option<Meta>,
4519}
4520
4521impl HttpHeader {
4522 #[must_use]
4524 pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
4525 Self {
4526 name: name.into(),
4527 value: value.into(),
4528 meta: None,
4529 }
4530 }
4531
4532 #[must_use]
4538 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4539 self.meta = meta.into_option();
4540 self
4541 }
4542}
4543
4544#[cfg(feature = "unstable_session_system_prompt")]
4557#[serde_as]
4558#[skip_serializing_none]
4559#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4560#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4561#[serde(rename_all = "camelCase")]
4562#[non_exhaustive]
4563pub struct SystemPrompt {
4564 pub mode: SystemPromptMode,
4569 pub content: Vec<ContentBlock>,
4579 #[serde_as(deserialize_as = "DefaultOnError")]
4585 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4586 #[serde(default)]
4587 #[serde(rename = "_meta")]
4588 pub meta: Option<Meta>,
4589}
4590
4591#[cfg(feature = "unstable_session_system_prompt")]
4592impl SystemPrompt {
4593 #[must_use]
4598 pub fn new(content: Vec<ContentBlock>) -> Self {
4599 Self {
4600 mode: SystemPromptMode::default(),
4601 content,
4602 meta: None,
4603 }
4604 }
4605
4606 #[must_use]
4608 pub fn mode(mut self, mode: SystemPromptMode) -> Self {
4609 self.mode = mode;
4610 self
4611 }
4612
4613 #[must_use]
4619 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4620 self.meta = meta.into_option();
4621 self
4622 }
4623}
4624
4625#[cfg(feature = "unstable_session_system_prompt")]
4635#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4636#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
4637#[serde(rename_all = "snake_case")]
4638#[non_exhaustive]
4639pub enum SystemPromptMode {
4640 #[default]
4647 Append,
4648 Override,
4657 #[serde(untagged)]
4663 Other(String),
4664}
4665
4666#[serde_as]
4674#[skip_serializing_none]
4675#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4676#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4677#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_PROMPT_METHOD_NAME)))]
4678#[serde(rename_all = "camelCase")]
4679#[non_exhaustive]
4680pub struct PromptRequest {
4681 pub session_id: SessionId,
4683 #[cfg_attr(feature = "schemars", schemars(length(min = 1)))]
4700 pub prompt: Vec<ContentBlock>,
4701 #[serde_as(deserialize_as = "DefaultOnError")]
4707 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4708 #[serde(default)]
4709 #[serde(rename = "_meta")]
4710 pub meta: Option<Meta>,
4711}
4712
4713impl PromptRequest {
4714 #[must_use]
4716 pub fn new(session_id: impl Into<SessionId>, prompt: Vec<ContentBlock>) -> Self {
4717 Self {
4718 session_id: session_id.into(),
4719 prompt,
4720 meta: None,
4721 }
4722 }
4723
4724 #[must_use]
4730 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4731 self.meta = meta.into_option();
4732 self
4733 }
4734}
4735
4736#[serde_as]
4747#[skip_serializing_none]
4748#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4749#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4750#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_PROMPT_METHOD_NAME)))]
4751#[serde(rename_all = "camelCase")]
4752#[non_exhaustive]
4753pub struct PromptResponse {
4754 pub message_id: MessageId,
4762 #[serde_as(deserialize_as = "DefaultOnError")]
4768 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4769 #[serde(default)]
4770 #[serde(rename = "_meta")]
4771 pub meta: Option<Meta>,
4772}
4773
4774impl PromptResponse {
4775 #[must_use]
4777 pub fn new(message_id: impl Into<MessageId>) -> Self {
4778 Self {
4779 message_id: message_id.into(),
4780 meta: None,
4781 }
4782 }
4783
4784 #[must_use]
4790 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4791 self.meta = meta.into_option();
4792 self
4793 }
4794}
4795
4796#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4800#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
4801#[serde(tag = "stopReason", rename_all = "snake_case")]
4802#[non_exhaustive]
4803pub enum StopReason {
4804 EndTurn,
4807 MaxTokens,
4809 MaxTurnRequests,
4812 Refusal,
4814 Cancelled,
4825 Error(ErrorStopReason),
4831 #[serde(untagged)]
4837 Other(OtherStopReason),
4838}
4839
4840impl From<ErrorStopReason> for StopReason {
4841 fn from(value: ErrorStopReason) -> Self {
4842 Self::Error(value)
4843 }
4844}
4845
4846#[serde_as]
4848#[skip_serializing_none]
4849#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4850#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
4851#[serde(rename_all = "camelCase")]
4852#[non_exhaustive]
4853pub struct ErrorStopReason {
4854 #[serde_as(deserialize_as = "DefaultOnError")]
4859 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4860 #[serde(default)]
4861 pub error: Option<Box<Error>>,
4862}
4863
4864impl ErrorStopReason {
4865 #[must_use]
4867 pub fn new() -> Self {
4868 Self::default()
4869 }
4870
4871 #[must_use]
4873 pub fn error(mut self, error: impl IntoOption<Error>) -> Self {
4874 self.error = error.into_option().map(Box::new);
4875 self
4876 }
4877}
4878
4879#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4881#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
4882#[cfg_attr(feature = "schemars", schemars(inline))]
4883#[cfg_attr(feature = "schemars", schemars(transform = other_stop_reason_schema))]
4884#[serde(rename_all = "camelCase")]
4885#[non_exhaustive]
4886pub struct OtherStopReason {
4887 pub stop_reason: String,
4893 #[serde(flatten)]
4895 pub fields: BTreeMap<String, serde_json::Value>,
4896}
4897
4898impl OtherStopReason {
4899 #[must_use]
4901 pub fn new(
4902 stop_reason: impl Into<String>,
4903 mut fields: BTreeMap<String, serde_json::Value>,
4904 ) -> Self {
4905 remove_idle_state_update_fields(&mut fields);
4906 Self {
4907 stop_reason: stop_reason.into(),
4908 fields,
4909 }
4910 }
4911}
4912
4913impl<'de> Deserialize<'de> for OtherStopReason {
4914 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
4915 where
4916 D: serde::Deserializer<'de>,
4917 {
4918 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
4919 let stop_reason = fields
4920 .remove("stopReason")
4921 .ok_or_else(|| serde::de::Error::missing_field("stopReason"))?;
4922 let serde_json::Value::String(stop_reason) = stop_reason else {
4923 return Err(serde::de::Error::custom("`stopReason` must be a string"));
4924 };
4925
4926 if KNOWN_STOP_REASONS.contains(&stop_reason.as_str()) {
4927 return Err(serde::de::Error::custom(format!(
4928 "known stop reason `{stop_reason}` did not match its schema"
4929 )));
4930 }
4931 remove_idle_state_update_fields(&mut fields);
4932
4933 Ok(Self {
4934 stop_reason,
4935 fields,
4936 })
4937 }
4938}
4939
4940const KNOWN_STOP_REASONS: &[&str] = &[
4941 "end_turn",
4942 "max_tokens",
4943 "max_turn_requests",
4944 "refusal",
4945 "cancelled",
4946 "error",
4947];
4948
4949fn remove_idle_state_update_fields(fields: &mut BTreeMap<String, serde_json::Value>) {
4951 fields.remove("stopReason");
4952 fields.remove("usage");
4953 fields.remove("_meta");
4954}
4955
4956#[cfg(feature = "schemars")]
4957fn other_stop_reason_schema(schema: &mut Schema) {
4958 super::schema_util::reject_known_string_discriminators(
4959 schema,
4960 "stopReason",
4961 KNOWN_STOP_REASONS,
4962 );
4963}
4964
4965#[cfg(feature = "unstable_end_turn_token_usage")]
4971#[serde_as]
4972#[skip_serializing_none]
4973#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4974#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4975#[serde(rename_all = "camelCase")]
4976#[non_exhaustive]
4977pub struct Usage {
4978 pub total_tokens: u64,
4980 pub input_tokens: u64,
4982 pub output_tokens: u64,
4984 #[serde_as(deserialize_as = "DefaultOnError")]
4988 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4989 #[serde(default)]
4990 pub thought_tokens: Option<u64>,
4991 #[serde_as(deserialize_as = "DefaultOnError")]
4995 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4996 #[serde(default)]
4997 pub cached_read_tokens: Option<u64>,
4998 #[serde_as(deserialize_as = "DefaultOnError")]
5002 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5003 #[serde(default)]
5004 pub cached_write_tokens: Option<u64>,
5005 #[serde_as(deserialize_as = "DefaultOnError")]
5011 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5012 #[serde(default)]
5013 #[serde(rename = "_meta")]
5014 pub meta: Option<Meta>,
5015}
5016
5017#[cfg(feature = "unstable_end_turn_token_usage")]
5018impl Usage {
5019 #[must_use]
5021 pub fn new(total_tokens: u64, input_tokens: u64, output_tokens: u64) -> Self {
5022 Self {
5023 total_tokens,
5024 input_tokens,
5025 output_tokens,
5026 thought_tokens: None,
5027 cached_read_tokens: None,
5028 cached_write_tokens: None,
5029 meta: None,
5030 }
5031 }
5032
5033 #[must_use]
5035 pub fn thought_tokens(mut self, thought_tokens: impl IntoOption<u64>) -> Self {
5036 self.thought_tokens = thought_tokens.into_option();
5037 self
5038 }
5039
5040 #[must_use]
5042 pub fn cached_read_tokens(mut self, cached_read_tokens: impl IntoOption<u64>) -> Self {
5043 self.cached_read_tokens = cached_read_tokens.into_option();
5044 self
5045 }
5046
5047 #[must_use]
5049 pub fn cached_write_tokens(mut self, cached_write_tokens: impl IntoOption<u64>) -> Self {
5050 self.cached_write_tokens = cached_write_tokens.into_option();
5051 self
5052 }
5053
5054 #[must_use]
5060 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5061 self.meta = meta.into_option();
5062 self
5063 }
5064}
5065
5066#[cfg(feature = "unstable_llm_providers")]
5079#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5080#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5081#[serde(rename_all = "snake_case")]
5082#[non_exhaustive]
5083#[expect(clippy::doc_markdown)]
5084pub enum LlmProtocol {
5085 Anthropic,
5087 #[serde(rename = "openai")]
5089 OpenAi,
5090 Azure,
5092 Vertex,
5094 Bedrock,
5096 #[serde(untagged)]
5102 Other(String),
5103}
5104
5105#[cfg(feature = "unstable_llm_providers")]
5111#[serde_as]
5112#[skip_serializing_none]
5113#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5114#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5115#[serde(rename_all = "camelCase")]
5116#[non_exhaustive]
5117pub struct ProviderCurrentConfig {
5118 pub api_type: LlmProtocol,
5120 #[cfg_attr(feature = "schemars", schemars(url))]
5122 pub base_url: String,
5123 #[serde_as(deserialize_as = "DefaultOnError")]
5129 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5130 #[serde(default)]
5131 #[serde(rename = "_meta")]
5132 pub meta: Option<Meta>,
5133}
5134
5135#[cfg(feature = "unstable_llm_providers")]
5136impl ProviderCurrentConfig {
5137 #[must_use]
5139 pub fn new(api_type: LlmProtocol, base_url: impl Into<String>) -> Self {
5140 Self {
5141 api_type,
5142 base_url: base_url.into(),
5143 meta: None,
5144 }
5145 }
5146
5147 #[must_use]
5153 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5154 self.meta = meta.into_option();
5155 self
5156 }
5157}
5158
5159#[cfg(feature = "unstable_llm_providers")]
5165#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5166#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
5167#[serde(transparent)]
5168#[from(forward)]
5169#[non_exhaustive]
5170pub struct ProviderId(pub Arc<str>);
5171
5172#[cfg(feature = "unstable_llm_providers")]
5173impl ProviderId {
5174 #[must_use]
5176 pub fn new(id: impl Into<Self>) -> Self {
5177 id.into()
5178 }
5179}
5180
5181#[cfg(feature = "unstable_llm_providers")]
5187#[serde_as]
5188#[skip_serializing_none]
5189#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5190#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5191#[serde(rename_all = "camelCase")]
5192#[non_exhaustive]
5193pub struct ProviderInfo {
5194 pub provider_id: ProviderId,
5196 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
5198 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
5199 pub supported: Vec<LlmProtocol>,
5200 pub required: bool,
5203 #[serde(default)]
5206 pub current: Option<ProviderCurrentConfig>,
5207 #[serde_as(deserialize_as = "DefaultOnError")]
5213 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5214 #[serde(default)]
5215 #[serde(rename = "_meta")]
5216 pub meta: Option<Meta>,
5217}
5218
5219#[cfg(feature = "unstable_llm_providers")]
5220impl ProviderInfo {
5221 #[must_use]
5223 pub fn new(
5224 provider_id: impl Into<ProviderId>,
5225 supported: Vec<LlmProtocol>,
5226 required: bool,
5227 current: impl IntoOption<ProviderCurrentConfig>,
5228 ) -> Self {
5229 Self {
5230 provider_id: provider_id.into(),
5231 supported,
5232 required,
5233 current: current.into_option(),
5234 meta: None,
5235 }
5236 }
5237
5238 #[must_use]
5244 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5245 self.meta = meta.into_option();
5246 self
5247 }
5248}
5249
5250#[cfg(feature = "unstable_llm_providers")]
5251crate::serde_util::default_on_null! {
5252 #[serde_as]
5258 #[skip_serializing_none]
5259 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5260 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
5261 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_LIST_METHOD_NAME)))]
5262 #[serde(rename_all = "camelCase")]
5263 #[non_exhaustive]
5264 pub struct ListProvidersRequest {
5265 #[serde_as(deserialize_as = "DefaultOnError")]
5271 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5272 #[serde(default)]
5273 #[serde(rename = "_meta")]
5274 pub meta: Option<Meta>,
5275 }
5276}
5277
5278#[cfg(feature = "unstable_llm_providers")]
5279impl ListProvidersRequest {
5280 #[must_use]
5282 pub fn new() -> Self {
5283 Self::default()
5284 }
5285
5286 #[must_use]
5292 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5293 self.meta = meta.into_option();
5294 self
5295 }
5296}
5297
5298#[cfg(feature = "unstable_llm_providers")]
5304#[serde_as]
5305#[skip_serializing_none]
5306#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5307#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5308#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_LIST_METHOD_NAME)))]
5309#[serde(rename_all = "camelCase")]
5310#[non_exhaustive]
5311pub struct ListProvidersResponse {
5312 pub providers: Vec<ProviderInfo>,
5314 #[serde_as(deserialize_as = "DefaultOnError")]
5320 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5321 #[serde(default)]
5322 #[serde(rename = "_meta")]
5323 pub meta: Option<Meta>,
5324}
5325
5326#[cfg(feature = "unstable_llm_providers")]
5327impl ListProvidersResponse {
5328 #[must_use]
5330 pub fn new(providers: Vec<ProviderInfo>) -> Self {
5331 Self {
5332 providers,
5333 meta: None,
5334 }
5335 }
5336
5337 #[must_use]
5343 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5344 self.meta = meta.into_option();
5345 self
5346 }
5347}
5348
5349#[cfg(feature = "unstable_llm_providers")]
5357#[serde_as]
5358#[skip_serializing_none]
5359#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5360#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
5361#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_SET_METHOD_NAME)))]
5362#[serde(rename_all = "camelCase")]
5363#[non_exhaustive]
5364pub struct SetProviderRequest {
5365 pub provider_id: ProviderId,
5367 pub api_type: LlmProtocol,
5369 #[cfg_attr(feature = "schemars", schemars(url))]
5371 pub base_url: String,
5372 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
5375 pub headers: HashMap<String, String>,
5376 #[serde_as(deserialize_as = "DefaultOnError")]
5382 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5383 #[serde(default)]
5384 #[serde(rename = "_meta")]
5385 pub meta: Option<Meta>,
5386}
5387
5388#[cfg(feature = "unstable_llm_providers")]
5389impl SetProviderRequest {
5390 #[must_use]
5392 pub fn new(
5393 provider_id: impl Into<ProviderId>,
5394 api_type: LlmProtocol,
5395 base_url: impl Into<String>,
5396 ) -> Self {
5397 Self {
5398 provider_id: provider_id.into(),
5399 api_type,
5400 base_url: base_url.into(),
5401 headers: HashMap::new(),
5402 meta: None,
5403 }
5404 }
5405
5406 #[must_use]
5409 pub fn headers(mut self, headers: HashMap<String, String>) -> Self {
5410 self.headers = headers;
5411 self
5412 }
5413
5414 #[must_use]
5420 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5421 self.meta = meta.into_option();
5422 self
5423 }
5424}
5425
5426#[cfg(feature = "unstable_llm_providers")]
5427crate::serde_util::default_on_null! {
5428 #[serde_as]
5434 #[skip_serializing_none]
5435 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5436 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
5437 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_SET_METHOD_NAME)))]
5438 #[serde(rename_all = "camelCase")]
5439 #[non_exhaustive]
5440 pub struct SetProviderResponse {
5441 #[serde_as(deserialize_as = "DefaultOnError")]
5447 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5448 #[serde(default)]
5449 #[serde(rename = "_meta")]
5450 pub meta: Option<Meta>,
5451 }
5452}
5453
5454#[cfg(feature = "unstable_llm_providers")]
5455impl SetProviderResponse {
5456 #[must_use]
5458 pub fn new() -> Self {
5459 Self::default()
5460 }
5461
5462 #[must_use]
5468 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5469 self.meta = meta.into_option();
5470 self
5471 }
5472}
5473
5474#[cfg(feature = "unstable_llm_providers")]
5480#[serde_as]
5481#[skip_serializing_none]
5482#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5483#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5484#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_DISABLE_METHOD_NAME)))]
5485#[serde(rename_all = "camelCase")]
5486#[non_exhaustive]
5487pub struct DisableProviderRequest {
5488 pub provider_id: ProviderId,
5490 #[serde_as(deserialize_as = "DefaultOnError")]
5496 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5497 #[serde(default)]
5498 #[serde(rename = "_meta")]
5499 pub meta: Option<Meta>,
5500}
5501
5502#[cfg(feature = "unstable_llm_providers")]
5503impl DisableProviderRequest {
5504 #[must_use]
5506 pub fn new(provider_id: impl Into<ProviderId>) -> Self {
5507 Self {
5508 provider_id: provider_id.into(),
5509 meta: None,
5510 }
5511 }
5512
5513 #[must_use]
5519 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5520 self.meta = meta.into_option();
5521 self
5522 }
5523}
5524
5525#[cfg(feature = "unstable_llm_providers")]
5526crate::serde_util::default_on_null! {
5527 #[serde_as]
5533 #[skip_serializing_none]
5534 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5535 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
5536 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_DISABLE_METHOD_NAME)))]
5537 #[serde(rename_all = "camelCase")]
5538 #[non_exhaustive]
5539 pub struct DisableProviderResponse {
5540 #[serde_as(deserialize_as = "DefaultOnError")]
5546 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5547 #[serde(default)]
5548 #[serde(rename = "_meta")]
5549 pub meta: Option<Meta>,
5550 }
5551}
5552
5553#[cfg(feature = "unstable_llm_providers")]
5554impl DisableProviderResponse {
5555 #[must_use]
5557 pub fn new() -> Self {
5558 Self::default()
5559 }
5560
5561 #[must_use]
5567 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5568 self.meta = meta.into_option();
5569 self
5570 }
5571}
5572
5573#[serde_as]
5582#[skip_serializing_none]
5583#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5584#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5585#[serde(rename_all = "camelCase")]
5586#[non_exhaustive]
5587pub struct AgentCapabilities {
5588 #[serde_as(deserialize_as = "DefaultOnError")]
5596 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5597 #[serde(default)]
5598 pub session: Option<SessionCapabilities>,
5599 #[serde_as(deserialize_as = "DefaultOnError")]
5606 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5607 #[serde(default)]
5608 pub auth: Option<AgentAuthCapabilities>,
5609 #[cfg(feature = "unstable_llm_providers")]
5618 #[serde_as(deserialize_as = "DefaultOnError")]
5619 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5620 #[serde(default)]
5621 pub providers: Option<ProvidersCapabilities>,
5622 #[serde_as(deserialize_as = "DefaultOnError")]
5628 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5629 #[serde(default)]
5630 #[serde(rename = "_meta")]
5631 pub meta: Option<Meta>,
5632}
5633
5634impl AgentCapabilities {
5635 #[must_use]
5637 pub fn new() -> Self {
5638 Self::default()
5639 }
5640
5641 #[must_use]
5649 pub fn session(mut self, session: impl IntoOption<SessionCapabilities>) -> Self {
5650 self.session = session.into_option();
5651 self
5652 }
5653
5654 #[must_use]
5658 pub fn auth(mut self, auth: impl IntoOption<AgentAuthCapabilities>) -> Self {
5659 self.auth = auth.into_option();
5660 self
5661 }
5662
5663 #[cfg(feature = "unstable_llm_providers")]
5669 #[must_use]
5670 pub fn providers(mut self, providers: impl IntoOption<ProvidersCapabilities>) -> Self {
5671 self.providers = providers.into_option();
5672 self
5673 }
5674
5675 #[must_use]
5681 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5682 self.meta = meta.into_option();
5683 self
5684 }
5685}
5686
5687#[cfg(feature = "unstable_llm_providers")]
5695#[serde_as]
5696#[skip_serializing_none]
5697#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5698#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5699#[non_exhaustive]
5700pub struct ProvidersCapabilities {
5701 #[serde_as(deserialize_as = "DefaultOnError")]
5707 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5708 #[serde(default)]
5709 #[serde(rename = "_meta")]
5710 pub meta: Option<Meta>,
5711}
5712
5713#[cfg(feature = "unstable_llm_providers")]
5714impl ProvidersCapabilities {
5715 #[must_use]
5717 pub fn new() -> Self {
5718 Self::default()
5719 }
5720
5721 #[must_use]
5727 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5728 self.meta = meta.into_option();
5729 self
5730 }
5731}
5732
5733#[serde_as]
5745#[skip_serializing_none]
5746#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5747#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5748#[serde(rename_all = "camelCase")]
5749#[non_exhaustive]
5750pub struct SessionCapabilities {
5751 #[serde_as(deserialize_as = "DefaultOnError")]
5757 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5758 #[serde(default)]
5759 pub prompt: Option<PromptCapabilities>,
5760 #[serde_as(deserialize_as = "DefaultOnError")]
5765 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5766 #[serde(default)]
5767 pub mcp: Option<McpCapabilities>,
5768 #[serde_as(deserialize_as = "DefaultOnError")]
5773 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5774 #[serde(default)]
5775 pub delete: Option<SessionDeleteCapabilities>,
5776 #[serde_as(deserialize_as = "DefaultOnError")]
5785 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5786 #[serde(default)]
5787 pub additional_directories: Option<SessionAdditionalDirectoriesCapabilities>,
5788 #[cfg(feature = "unstable_session_system_prompt")]
5797 #[serde_as(deserialize_as = "DefaultOnError")]
5798 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5799 #[serde(default)]
5800 pub system_prompt: Option<SessionSystemPromptCapabilities>,
5801 #[cfg(feature = "unstable_session_fork")]
5810 #[serde_as(deserialize_as = "DefaultOnError")]
5811 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5812 #[serde(default)]
5813 pub fork: Option<SessionForkCapabilities>,
5814 #[cfg(feature = "unstable_session_archive")]
5824 #[serde_as(deserialize_as = "DefaultOnError")]
5825 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5826 #[serde(default)]
5827 pub archive: Option<SessionArchiveCapabilities>,
5828 #[cfg(feature = "unstable_session_status")]
5837 #[serde_as(deserialize_as = "DefaultOnError")]
5838 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5839 #[serde(default)]
5840 pub status: Option<SessionStatusCapabilities>,
5841 #[cfg(feature = "unstable_session_set_title")]
5853 #[serde_as(deserialize_as = "DefaultOnError")]
5854 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5855 #[serde(default)]
5856 pub title: Option<SessionTitleCapabilities>,
5857 #[serde_as(deserialize_as = "DefaultOnError")]
5863 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5864 #[serde(default)]
5865 #[serde(rename = "_meta")]
5866 pub meta: Option<Meta>,
5867}
5868
5869impl SessionCapabilities {
5870 #[must_use]
5872 pub fn new() -> Self {
5873 Self::default()
5874 }
5875
5876 #[must_use]
5882 pub fn prompt(mut self, prompt: impl IntoOption<PromptCapabilities>) -> Self {
5883 self.prompt = prompt.into_option();
5884 self
5885 }
5886
5887 #[must_use]
5892 pub fn mcp(mut self, mcp: impl IntoOption<McpCapabilities>) -> Self {
5893 self.mcp = mcp.into_option();
5894 self
5895 }
5896
5897 #[must_use]
5902 pub fn delete(mut self, delete: impl IntoOption<SessionDeleteCapabilities>) -> Self {
5903 self.delete = delete.into_option();
5904 self
5905 }
5906
5907 #[must_use]
5916 pub fn additional_directories(
5917 mut self,
5918 additional_directories: impl IntoOption<SessionAdditionalDirectoriesCapabilities>,
5919 ) -> Self {
5920 self.additional_directories = additional_directories.into_option();
5921 self
5922 }
5923
5924 #[cfg(feature = "unstable_session_system_prompt")]
5933 #[must_use]
5934 pub fn system_prompt(
5935 mut self,
5936 system_prompt: impl IntoOption<SessionSystemPromptCapabilities>,
5937 ) -> Self {
5938 self.system_prompt = system_prompt.into_option();
5939 self
5940 }
5941
5942 #[cfg(feature = "unstable_session_fork")]
5943 #[must_use]
5948 pub fn fork(mut self, fork: impl IntoOption<SessionForkCapabilities>) -> Self {
5949 self.fork = fork.into_option();
5950 self
5951 }
5952
5953 #[cfg(feature = "unstable_session_archive")]
5963 #[must_use]
5964 pub fn archive(mut self, archive: impl IntoOption<SessionArchiveCapabilities>) -> Self {
5965 self.archive = archive.into_option();
5966 self
5967 }
5968
5969 #[cfg(feature = "unstable_session_status")]
5978 #[must_use]
5979 pub fn status(mut self, status: impl IntoOption<SessionStatusCapabilities>) -> Self {
5980 self.status = status.into_option();
5981 self
5982 }
5983
5984 #[cfg(feature = "unstable_session_set_title")]
5996 #[must_use]
5997 pub fn title(mut self, title: impl IntoOption<SessionTitleCapabilities>) -> Self {
5998 self.title = title.into_option();
5999 self
6000 }
6001
6002 #[must_use]
6008 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6009 self.meta = meta.into_option();
6010 self
6011 }
6012}
6013
6014#[serde_as]
6018#[skip_serializing_none]
6019#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6020#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6021#[non_exhaustive]
6022pub struct SessionDeleteCapabilities {
6023 #[serde_as(deserialize_as = "DefaultOnError")]
6029 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6030 #[serde(default)]
6031 #[serde(rename = "_meta")]
6032 pub meta: Option<Meta>,
6033}
6034
6035impl SessionDeleteCapabilities {
6036 #[must_use]
6038 pub fn new() -> Self {
6039 Self::default()
6040 }
6041
6042 #[must_use]
6048 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6049 self.meta = meta.into_option();
6050 self
6051 }
6052}
6053
6054#[serde_as]
6061#[skip_serializing_none]
6062#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6063#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6064#[non_exhaustive]
6065pub struct SessionAdditionalDirectoriesCapabilities {
6066 #[serde_as(deserialize_as = "DefaultOnError")]
6072 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6073 #[serde(default)]
6074 #[serde(rename = "_meta")]
6075 pub meta: Option<Meta>,
6076}
6077
6078impl SessionAdditionalDirectoriesCapabilities {
6079 #[must_use]
6081 pub fn new() -> Self {
6082 Self::default()
6083 }
6084
6085 #[must_use]
6091 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6092 self.meta = meta.into_option();
6093 self
6094 }
6095}
6096
6097#[cfg(feature = "unstable_session_system_prompt")]
6106#[serde_as]
6107#[skip_serializing_none]
6108#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6109#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6110#[serde(rename_all = "camelCase")]
6111#[non_exhaustive]
6112pub struct SessionSystemPromptCapabilities {
6113 #[serde_as(deserialize_as = "DefaultOnError")]
6117 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6118 #[serde(default)]
6119 pub mode: Option<SessionSystemPromptModeCapabilities>,
6120 #[serde_as(deserialize_as = "DefaultOnError")]
6126 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6127 #[serde(default)]
6128 #[serde(rename = "_meta")]
6129 pub meta: Option<Meta>,
6130}
6131
6132#[cfg(feature = "unstable_session_system_prompt")]
6133impl SessionSystemPromptCapabilities {
6134 #[must_use]
6136 pub fn new() -> Self {
6137 Self::default()
6138 }
6139
6140 #[must_use]
6144 pub fn mode(mut self, mode: impl IntoOption<SessionSystemPromptModeCapabilities>) -> Self {
6145 self.mode = mode.into_option();
6146 self
6147 }
6148
6149 #[must_use]
6155 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6156 self.meta = meta.into_option();
6157 self
6158 }
6159}
6160
6161#[cfg(feature = "unstable_session_system_prompt")]
6170#[serde_as]
6171#[skip_serializing_none]
6172#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6173#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6174#[serde(rename_all = "camelCase")]
6175#[non_exhaustive]
6176pub struct SessionSystemPromptModeCapabilities {
6177 #[serde_as(deserialize_as = "DefaultOnError")]
6183 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6184 #[serde(default, rename = "override")]
6185 pub override_: Option<SessionSystemPromptOverrideCapabilities>,
6186 #[serde_as(deserialize_as = "DefaultOnError")]
6192 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6193 #[serde(default)]
6194 #[serde(rename = "_meta")]
6195 pub meta: Option<Meta>,
6196}
6197
6198#[cfg(feature = "unstable_session_system_prompt")]
6199impl SessionSystemPromptModeCapabilities {
6200 #[must_use]
6202 pub fn new() -> Self {
6203 Self::default()
6204 }
6205
6206 #[must_use]
6212 pub fn override_(
6213 mut self,
6214 override_: impl IntoOption<SessionSystemPromptOverrideCapabilities>,
6215 ) -> Self {
6216 self.override_ = override_.into_option();
6217 self
6218 }
6219
6220 #[must_use]
6226 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6227 self.meta = meta.into_option();
6228 self
6229 }
6230}
6231
6232#[cfg(feature = "unstable_session_system_prompt")]
6241#[serde_as]
6242#[skip_serializing_none]
6243#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6244#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6245#[non_exhaustive]
6246pub struct SessionSystemPromptOverrideCapabilities {
6247 #[serde_as(deserialize_as = "DefaultOnError")]
6253 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6254 #[serde(default)]
6255 #[serde(rename = "_meta")]
6256 pub meta: Option<Meta>,
6257}
6258
6259#[cfg(feature = "unstable_session_system_prompt")]
6260impl SessionSystemPromptOverrideCapabilities {
6261 #[must_use]
6263 pub fn new() -> Self {
6264 Self::default()
6265 }
6266
6267 #[must_use]
6273 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6274 self.meta = meta.into_option();
6275 self
6276 }
6277}
6278
6279#[cfg(feature = "unstable_session_fork")]
6287#[serde_as]
6288#[skip_serializing_none]
6289#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6290#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6291#[non_exhaustive]
6292pub struct SessionForkCapabilities {
6293 #[serde_as(deserialize_as = "DefaultOnError")]
6299 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6300 #[serde(default)]
6301 #[serde(rename = "_meta")]
6302 pub meta: Option<Meta>,
6303}
6304
6305#[cfg(feature = "unstable_session_fork")]
6306impl SessionForkCapabilities {
6307 #[must_use]
6309 pub fn new() -> Self {
6310 Self::default()
6311 }
6312
6313 #[must_use]
6319 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6320 self.meta = meta.into_option();
6321 self
6322 }
6323}
6324
6325#[cfg(feature = "unstable_session_archive")]
6334#[serde_as]
6335#[skip_serializing_none]
6336#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6337#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6338#[non_exhaustive]
6339pub struct SessionArchiveCapabilities {
6340 #[serde_as(deserialize_as = "DefaultOnError")]
6346 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6347 #[serde(default)]
6348 #[serde(rename = "_meta")]
6349 pub meta: Option<Meta>,
6350}
6351
6352#[cfg(feature = "unstable_session_archive")]
6353impl SessionArchiveCapabilities {
6354 #[must_use]
6356 pub fn new() -> Self {
6357 Self::default()
6358 }
6359
6360 #[must_use]
6366 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6367 self.meta = meta.into_option();
6368 self
6369 }
6370}
6371
6372#[cfg(feature = "unstable_session_status")]
6380#[serde_as]
6381#[skip_serializing_none]
6382#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6383#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6384#[non_exhaustive]
6385pub struct SessionStatusCapabilities {
6386 #[serde_as(deserialize_as = "DefaultOnError")]
6392 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6393 #[serde(default)]
6394 #[serde(rename = "_meta")]
6395 pub meta: Option<Meta>,
6396}
6397
6398#[cfg(feature = "unstable_session_status")]
6399impl SessionStatusCapabilities {
6400 #[must_use]
6402 pub fn new() -> Self {
6403 Self::default()
6404 }
6405
6406 #[must_use]
6412 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6413 self.meta = meta.into_option();
6414 self
6415 }
6416}
6417
6418#[cfg(feature = "unstable_session_set_title")]
6427#[serde_as]
6428#[skip_serializing_none]
6429#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6430#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6431#[non_exhaustive]
6432pub struct SessionTitleCapabilities {
6433 #[serde_as(deserialize_as = "DefaultOnError")]
6439 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6440 #[serde(default)]
6441 #[serde(rename = "_meta")]
6442 pub meta: Option<Meta>,
6443}
6444
6445#[cfg(feature = "unstable_session_set_title")]
6446impl SessionTitleCapabilities {
6447 #[must_use]
6449 pub fn new() -> Self {
6450 Self::default()
6451 }
6452
6453 #[must_use]
6459 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6460 self.meta = meta.into_option();
6461 self
6462 }
6463}
6464
6465#[serde_as]
6478#[skip_serializing_none]
6479#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6480#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6481#[serde(rename_all = "camelCase")]
6482#[non_exhaustive]
6483pub struct PromptCapabilities {
6484 #[serde_as(deserialize_as = "DefaultOnError")]
6489 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6490 #[serde(default)]
6491 pub image: Option<PromptImageCapabilities>,
6492 #[serde_as(deserialize_as = "DefaultOnError")]
6497 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6498 #[serde(default)]
6499 pub audio: Option<PromptAudioCapabilities>,
6500 #[serde_as(deserialize_as = "DefaultOnError")]
6508 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6509 #[serde(default)]
6510 pub embedded_context: Option<PromptEmbeddedContextCapabilities>,
6511 #[serde_as(deserialize_as = "DefaultOnError")]
6517 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6518 #[serde(default)]
6519 #[serde(rename = "_meta")]
6520 pub meta: Option<Meta>,
6521}
6522
6523impl PromptCapabilities {
6524 #[must_use]
6526 pub fn new() -> Self {
6527 Self::default()
6528 }
6529
6530 #[must_use]
6535 pub fn image(mut self, image: impl IntoOption<PromptImageCapabilities>) -> Self {
6536 self.image = image.into_option();
6537 self
6538 }
6539
6540 #[must_use]
6545 pub fn audio(mut self, audio: impl IntoOption<PromptAudioCapabilities>) -> Self {
6546 self.audio = audio.into_option();
6547 self
6548 }
6549
6550 #[must_use]
6558 pub fn embedded_context(
6559 mut self,
6560 embedded_context: impl IntoOption<PromptEmbeddedContextCapabilities>,
6561 ) -> Self {
6562 self.embedded_context = embedded_context.into_option();
6563 self
6564 }
6565
6566 #[must_use]
6572 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6573 self.meta = meta.into_option();
6574 self
6575 }
6576}
6577
6578#[serde_as]
6582#[skip_serializing_none]
6583#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6584#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6585#[non_exhaustive]
6586pub struct PromptImageCapabilities {
6587 #[serde_as(deserialize_as = "DefaultOnError")]
6593 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6594 #[serde(default)]
6595 #[serde(rename = "_meta")]
6596 pub meta: Option<Meta>,
6597}
6598
6599impl PromptImageCapabilities {
6600 #[must_use]
6602 pub fn new() -> Self {
6603 Self::default()
6604 }
6605
6606 #[must_use]
6612 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6613 self.meta = meta.into_option();
6614 self
6615 }
6616}
6617
6618#[serde_as]
6622#[skip_serializing_none]
6623#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6624#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6625#[non_exhaustive]
6626pub struct PromptAudioCapabilities {
6627 #[serde_as(deserialize_as = "DefaultOnError")]
6633 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6634 #[serde(default)]
6635 #[serde(rename = "_meta")]
6636 pub meta: Option<Meta>,
6637}
6638
6639impl PromptAudioCapabilities {
6640 #[must_use]
6642 pub fn new() -> Self {
6643 Self::default()
6644 }
6645
6646 #[must_use]
6652 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6653 self.meta = meta.into_option();
6654 self
6655 }
6656}
6657
6658#[serde_as]
6662#[skip_serializing_none]
6663#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6664#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6665#[non_exhaustive]
6666pub struct PromptEmbeddedContextCapabilities {
6667 #[serde_as(deserialize_as = "DefaultOnError")]
6673 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6674 #[serde(default)]
6675 #[serde(rename = "_meta")]
6676 pub meta: Option<Meta>,
6677}
6678
6679impl PromptEmbeddedContextCapabilities {
6680 #[must_use]
6682 pub fn new() -> Self {
6683 Self::default()
6684 }
6685
6686 #[must_use]
6692 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6693 self.meta = meta.into_option();
6694 self
6695 }
6696}
6697
6698#[serde_as]
6700#[skip_serializing_none]
6701#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6702#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6703#[serde(rename_all = "camelCase")]
6704#[non_exhaustive]
6705pub struct McpCapabilities {
6706 #[serde_as(deserialize_as = "DefaultOnError")]
6711 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6712 #[serde(default)]
6713 pub stdio: Option<McpStdioCapabilities>,
6714 #[serde_as(deserialize_as = "DefaultOnError")]
6719 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6720 #[serde(default)]
6721 pub http: Option<McpHttpCapabilities>,
6722 #[cfg(feature = "unstable_mcp_over_acp")]
6731 #[serde_as(deserialize_as = "DefaultOnError")]
6732 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6733 #[serde(default)]
6734 pub acp: Option<McpAcpCapabilities>,
6735 #[serde_as(deserialize_as = "DefaultOnError")]
6741 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6742 #[serde(default)]
6743 #[serde(rename = "_meta")]
6744 pub meta: Option<Meta>,
6745}
6746
6747impl McpCapabilities {
6748 #[must_use]
6750 pub fn new() -> Self {
6751 Self::default()
6752 }
6753
6754 #[must_use]
6759 pub fn stdio(mut self, stdio: impl IntoOption<McpStdioCapabilities>) -> Self {
6760 self.stdio = stdio.into_option();
6761 self
6762 }
6763
6764 #[must_use]
6769 pub fn http(mut self, http: impl IntoOption<McpHttpCapabilities>) -> Self {
6770 self.http = http.into_option();
6771 self
6772 }
6773
6774 #[cfg(feature = "unstable_mcp_over_acp")]
6780 #[must_use]
6784 pub fn acp(mut self, acp: impl IntoOption<McpAcpCapabilities>) -> Self {
6785 self.acp = acp.into_option();
6786 self
6787 }
6788
6789 #[must_use]
6795 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6796 self.meta = meta.into_option();
6797 self
6798 }
6799}
6800
6801#[serde_as]
6805#[skip_serializing_none]
6806#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6807#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6808#[non_exhaustive]
6809pub struct McpStdioCapabilities {
6810 #[serde_as(deserialize_as = "DefaultOnError")]
6816 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6817 #[serde(default)]
6818 #[serde(rename = "_meta")]
6819 pub meta: Option<Meta>,
6820}
6821
6822impl McpStdioCapabilities {
6823 #[must_use]
6825 pub fn new() -> Self {
6826 Self::default()
6827 }
6828
6829 #[must_use]
6835 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6836 self.meta = meta.into_option();
6837 self
6838 }
6839}
6840
6841#[serde_as]
6845#[skip_serializing_none]
6846#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6847#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6848#[non_exhaustive]
6849pub struct McpHttpCapabilities {
6850 #[serde_as(deserialize_as = "DefaultOnError")]
6856 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6857 #[serde(default)]
6858 #[serde(rename = "_meta")]
6859 pub meta: Option<Meta>,
6860}
6861
6862impl McpHttpCapabilities {
6863 #[must_use]
6865 pub fn new() -> Self {
6866 Self::default()
6867 }
6868
6869 #[must_use]
6875 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6876 self.meta = meta.into_option();
6877 self
6878 }
6879}
6880
6881#[cfg(feature = "unstable_mcp_over_acp")]
6889#[serde_as]
6890#[skip_serializing_none]
6891#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6892#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6893#[non_exhaustive]
6894pub struct McpAcpCapabilities {
6895 #[serde_as(deserialize_as = "DefaultOnError")]
6901 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6902 #[serde(default)]
6903 #[serde(rename = "_meta")]
6904 pub meta: Option<Meta>,
6905}
6906
6907#[cfg(feature = "unstable_mcp_over_acp")]
6908impl McpAcpCapabilities {
6909 #[must_use]
6911 pub fn new() -> Self {
6912 Self::default()
6913 }
6914
6915 #[must_use]
6921 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6922 self.meta = meta.into_option();
6923 self
6924 }
6925}
6926
6927#[serde_as]
6931#[skip_serializing_none]
6932#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6933#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6934#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_CANCEL_METHOD_NAME)))]
6935#[serde(rename_all = "camelCase")]
6936#[non_exhaustive]
6937pub struct CancelSessionNotification {
6938 pub session_id: SessionId,
6940 #[serde_as(deserialize_as = "DefaultOnError")]
6946 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6947 #[serde(default)]
6948 #[serde(rename = "_meta")]
6949 pub meta: Option<Meta>,
6950}
6951
6952impl CancelSessionNotification {
6953 #[must_use]
6955 pub fn new(session_id: impl Into<SessionId>) -> Self {
6956 Self {
6957 session_id: session_id.into(),
6958 meta: None,
6959 }
6960 }
6961
6962 #[must_use]
6968 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6969 self.meta = meta.into_option();
6970 self
6971 }
6972}
6973
6974#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6980#[non_exhaustive]
6981pub struct AgentMethodNames {
6982 pub initialize: &'static str,
6984 pub auth_login: &'static str,
6986 #[cfg(feature = "unstable_llm_providers")]
6988 pub providers_list: &'static str,
6989 #[cfg(feature = "unstable_llm_providers")]
6991 pub providers_set: &'static str,
6992 #[cfg(feature = "unstable_llm_providers")]
6994 pub providers_disable: &'static str,
6995 pub session_new: &'static str,
6997 pub session_set_config_option: &'static str,
6999 pub session_prompt: &'static str,
7001 pub session_cancel: &'static str,
7003 #[cfg(feature = "unstable_mcp_over_acp")]
7005 pub mcp_message: &'static str,
7006 pub session_list: &'static str,
7008 pub session_delete: &'static str,
7010 #[cfg(feature = "unstable_session_fork")]
7012 pub session_fork: &'static str,
7013 #[cfg(feature = "unstable_session_archive")]
7015 pub session_archive: &'static str,
7016 #[cfg(feature = "unstable_session_archive")]
7018 pub session_unarchive: &'static str,
7019 #[cfg(feature = "unstable_session_status")]
7021 pub session_status: &'static str,
7022 #[cfg(feature = "unstable_session_set_title")]
7024 pub session_set_title: &'static str,
7025 pub session_resume: &'static str,
7027 pub session_close: &'static str,
7029 pub tool_call_cancel: &'static str,
7031 pub terminal_cancel: &'static str,
7033 pub auth_logout: &'static str,
7035 #[cfg(feature = "unstable_auth_status")]
7037 pub auth_status: &'static str,
7038}
7039
7040pub const AGENT_METHOD_NAMES: AgentMethodNames = AgentMethodNames {
7042 initialize: INITIALIZE_METHOD_NAME,
7043 auth_login: AUTH_LOGIN_METHOD_NAME,
7044 #[cfg(feature = "unstable_llm_providers")]
7045 providers_list: PROVIDERS_LIST_METHOD_NAME,
7046 #[cfg(feature = "unstable_llm_providers")]
7047 providers_set: PROVIDERS_SET_METHOD_NAME,
7048 #[cfg(feature = "unstable_llm_providers")]
7049 providers_disable: PROVIDERS_DISABLE_METHOD_NAME,
7050 session_new: SESSION_NEW_METHOD_NAME,
7051 session_set_config_option: SESSION_SET_CONFIG_OPTION_METHOD_NAME,
7052 session_prompt: SESSION_PROMPT_METHOD_NAME,
7053 session_cancel: SESSION_CANCEL_METHOD_NAME,
7054 #[cfg(feature = "unstable_mcp_over_acp")]
7055 mcp_message: MCP_MESSAGE_METHOD_NAME,
7056 session_list: SESSION_LIST_METHOD_NAME,
7057 session_delete: SESSION_DELETE_METHOD_NAME,
7058 #[cfg(feature = "unstable_session_fork")]
7059 session_fork: SESSION_FORK_METHOD_NAME,
7060 #[cfg(feature = "unstable_session_archive")]
7061 session_archive: SESSION_ARCHIVE_METHOD_NAME,
7062 #[cfg(feature = "unstable_session_archive")]
7063 session_unarchive: SESSION_UNARCHIVE_METHOD_NAME,
7064 #[cfg(feature = "unstable_session_status")]
7065 session_status: SESSION_STATUS_METHOD_NAME,
7066 #[cfg(feature = "unstable_session_set_title")]
7067 session_set_title: SESSION_SET_TITLE_METHOD_NAME,
7068 session_resume: SESSION_RESUME_METHOD_NAME,
7069 session_close: SESSION_CLOSE_METHOD_NAME,
7070 tool_call_cancel: TOOL_CALL_CANCEL_METHOD_NAME,
7071 terminal_cancel: TERMINAL_CANCEL_METHOD_NAME,
7072 auth_logout: AUTH_LOGOUT_METHOD_NAME,
7073 #[cfg(feature = "unstable_auth_status")]
7074 auth_status: AUTH_STATUS_METHOD_NAME,
7075};
7076
7077pub(crate) const INITIALIZE_METHOD_NAME: &str = "initialize";
7079pub(crate) const AUTH_LOGIN_METHOD_NAME: &str = "auth/login";
7081#[cfg(feature = "unstable_llm_providers")]
7083pub(crate) const PROVIDERS_LIST_METHOD_NAME: &str = "providers/list";
7084#[cfg(feature = "unstable_llm_providers")]
7086pub(crate) const PROVIDERS_SET_METHOD_NAME: &str = "providers/set";
7087#[cfg(feature = "unstable_llm_providers")]
7089pub(crate) const PROVIDERS_DISABLE_METHOD_NAME: &str = "providers/disable";
7090pub(crate) const SESSION_NEW_METHOD_NAME: &str = "session/new";
7092pub(crate) const SESSION_SET_CONFIG_OPTION_METHOD_NAME: &str = "session/set_config_option";
7094pub(crate) const SESSION_PROMPT_METHOD_NAME: &str = "session/prompt";
7096pub(crate) const SESSION_CANCEL_METHOD_NAME: &str = "session/cancel";
7098pub(crate) const SESSION_LIST_METHOD_NAME: &str = "session/list";
7100pub(crate) const SESSION_DELETE_METHOD_NAME: &str = "session/delete";
7102#[cfg(feature = "unstable_session_fork")]
7104pub(crate) const SESSION_FORK_METHOD_NAME: &str = "session/fork";
7105#[cfg(feature = "unstable_session_archive")]
7107pub(crate) const SESSION_ARCHIVE_METHOD_NAME: &str = "session/archive";
7108#[cfg(feature = "unstable_session_archive")]
7110pub(crate) const SESSION_UNARCHIVE_METHOD_NAME: &str = "session/unarchive";
7111#[cfg(feature = "unstable_session_status")]
7113pub(crate) const SESSION_STATUS_METHOD_NAME: &str = "session/status";
7114#[cfg(feature = "unstable_session_set_title")]
7116pub(crate) const SESSION_SET_TITLE_METHOD_NAME: &str = "session/set_title";
7117pub(crate) const SESSION_RESUME_METHOD_NAME: &str = "session/resume";
7119pub(crate) const SESSION_CLOSE_METHOD_NAME: &str = "session/close";
7121pub(crate) const TOOL_CALL_CANCEL_METHOD_NAME: &str = "tool_call/cancel";
7123pub(crate) const TERMINAL_CANCEL_METHOD_NAME: &str = "terminal/cancel";
7125pub(crate) const AUTH_LOGOUT_METHOD_NAME: &str = "auth/logout";
7127#[cfg(feature = "unstable_auth_status")]
7129pub(crate) const AUTH_STATUS_METHOD_NAME: &str = "auth/status";
7130
7131#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
7138#[derive(Clone, Debug, Serialize, Deserialize)]
7139#[serde(untagged)]
7140#[cfg_attr(feature = "schemars", schemars(inline))]
7141#[non_exhaustive]
7142pub enum ClientRequest {
7143 InitializeRequest(Box<InitializeRequest>),
7157 LoginAuthRequest(Box<LoginAuthRequest>),
7172 #[cfg(feature = "unstable_llm_providers")]
7178 ListProvidersRequest(Box<ListProvidersRequest>),
7179 #[cfg(feature = "unstable_llm_providers")]
7185 SetProviderRequest(Box<SetProviderRequest>),
7186 #[cfg(feature = "unstable_llm_providers")]
7192 DisableProviderRequest(Box<DisableProviderRequest>),
7193 LogoutAuthRequest(Box<LogoutAuthRequest>),
7203 #[cfg(feature = "unstable_auth_status")]
7212 AuthStatusRequest(Box<AuthStatusRequest>),
7213 NewSessionRequest(Box<NewSessionRequest>),
7226 ListSessionsRequest(Box<ListSessionsRequest>),
7230 DeleteSessionRequest(Box<DeleteSessionRequest>),
7234 #[cfg(feature = "unstable_session_fork")]
7235 ForkSessionRequest(Box<ForkSessionRequest>),
7251 #[cfg(feature = "unstable_session_archive")]
7252 ArchiveSessionRequest(Box<ArchiveSessionRequest>),
7260 #[cfg(feature = "unstable_session_archive")]
7261 UnarchiveSessionRequest(Box<UnarchiveSessionRequest>),
7269 #[cfg(feature = "unstable_session_status")]
7270 SessionStatusRequest(Box<SessionStatusRequest>),
7278 #[cfg(feature = "unstable_session_set_title")]
7279 SetSessionTitleRequest(Box<SetSessionTitleRequest>),
7290 ResumeSessionRequest(Box<ResumeSessionRequest>),
7296 CloseSessionRequest(Box<CloseSessionRequest>),
7301 CancelToolCallRequest(Box<CancelToolCallRequest>),
7307 CancelTerminalRequest(Box<CancelTerminalRequest>),
7313 SetSessionConfigOptionRequest(Box<SetSessionConfigOptionRequest>),
7315 PromptRequest(Box<PromptRequest>),
7337 ExtMethodRequest(Box<ExtRequest>),
7344}
7345
7346impl ClientRequest {
7347 #[must_use]
7349 pub fn method(&self) -> &str {
7350 match self {
7351 Self::InitializeRequest(_) => AGENT_METHOD_NAMES.initialize,
7352 Self::LoginAuthRequest(_) => AGENT_METHOD_NAMES.auth_login,
7353 #[cfg(feature = "unstable_llm_providers")]
7354 Self::ListProvidersRequest(_) => AGENT_METHOD_NAMES.providers_list,
7355 #[cfg(feature = "unstable_llm_providers")]
7356 Self::SetProviderRequest(_) => AGENT_METHOD_NAMES.providers_set,
7357 #[cfg(feature = "unstable_llm_providers")]
7358 Self::DisableProviderRequest(_) => AGENT_METHOD_NAMES.providers_disable,
7359 Self::LogoutAuthRequest(_) => AGENT_METHOD_NAMES.auth_logout,
7360 #[cfg(feature = "unstable_auth_status")]
7361 Self::AuthStatusRequest(_) => AGENT_METHOD_NAMES.auth_status,
7362 Self::NewSessionRequest(_) => AGENT_METHOD_NAMES.session_new,
7363 Self::ListSessionsRequest(_) => AGENT_METHOD_NAMES.session_list,
7364 Self::DeleteSessionRequest(_) => AGENT_METHOD_NAMES.session_delete,
7365 #[cfg(feature = "unstable_session_fork")]
7366 Self::ForkSessionRequest(_) => AGENT_METHOD_NAMES.session_fork,
7367 #[cfg(feature = "unstable_session_archive")]
7368 Self::ArchiveSessionRequest(_) => AGENT_METHOD_NAMES.session_archive,
7369 #[cfg(feature = "unstable_session_archive")]
7370 Self::UnarchiveSessionRequest(_) => AGENT_METHOD_NAMES.session_unarchive,
7371 #[cfg(feature = "unstable_session_status")]
7372 Self::SessionStatusRequest(_) => AGENT_METHOD_NAMES.session_status,
7373 #[cfg(feature = "unstable_session_set_title")]
7374 Self::SetSessionTitleRequest(_) => AGENT_METHOD_NAMES.session_set_title,
7375 Self::ResumeSessionRequest(_) => AGENT_METHOD_NAMES.session_resume,
7376 Self::CloseSessionRequest(_) => AGENT_METHOD_NAMES.session_close,
7377 Self::CancelToolCallRequest(_) => AGENT_METHOD_NAMES.tool_call_cancel,
7378 Self::CancelTerminalRequest(_) => AGENT_METHOD_NAMES.terminal_cancel,
7379 Self::SetSessionConfigOptionRequest(_) => AGENT_METHOD_NAMES.session_set_config_option,
7380 Self::PromptRequest(_) => AGENT_METHOD_NAMES.session_prompt,
7381 Self::ExtMethodRequest(ext_request) => &ext_request.method,
7382 }
7383 }
7384}
7385
7386#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
7393#[derive(Clone, Debug, Serialize, Deserialize)]
7394#[serde(untagged)]
7395#[cfg_attr(feature = "schemars", schemars(inline))]
7396#[non_exhaustive]
7397pub enum AgentResponse {
7398 InitializeResponse(Box<InitializeResponse>),
7400 LoginAuthResponse(#[serde(default)] Box<LoginAuthResponse>),
7402 #[cfg(feature = "unstable_llm_providers")]
7404 ListProvidersResponse(Box<ListProvidersResponse>),
7405 #[cfg(feature = "unstable_llm_providers")]
7407 SetProviderResponse(#[serde(default)] Box<SetProviderResponse>),
7408 #[cfg(feature = "unstable_llm_providers")]
7410 DisableProviderResponse(#[serde(default)] Box<DisableProviderResponse>),
7411 LogoutAuthResponse(#[serde(default)] Box<LogoutAuthResponse>),
7413 #[cfg(feature = "unstable_auth_status")]
7415 AuthStatusResponse(Box<AuthStatusResponse>),
7416 NewSessionResponse(Box<NewSessionResponse>),
7418 ListSessionsResponse(Box<ListSessionsResponse>),
7420 DeleteSessionResponse(#[serde(default)] Box<DeleteSessionResponse>),
7422 #[cfg(feature = "unstable_session_fork")]
7424 ForkSessionResponse(Box<ForkSessionResponse>),
7425 #[cfg(feature = "unstable_session_archive")]
7427 ArchiveSessionResponse(#[serde(default)] Box<ArchiveSessionResponse>),
7428 #[cfg(feature = "unstable_session_archive")]
7430 UnarchiveSessionResponse(#[serde(default)] Box<UnarchiveSessionResponse>),
7431 #[cfg(feature = "unstable_session_status")]
7433 SessionStatusResponse(Box<SessionStatusResponse>),
7434 #[cfg(feature = "unstable_session_set_title")]
7436 SetSessionTitleResponse(Box<SetSessionTitleResponse>),
7437 ResumeSessionResponse(#[serde(default)] Box<ResumeSessionResponse>),
7439 CloseSessionResponse(#[serde(default)] Box<CloseSessionResponse>),
7441 CancelToolCallResponse(#[serde(default)] Box<CancelToolCallResponse>),
7443 CancelTerminalResponse(#[serde(default)] Box<CancelTerminalResponse>),
7445 SetSessionConfigOptionResponse(Box<SetSessionConfigOptionResponse>),
7447 PromptResponse(Box<PromptResponse>),
7449 ExtMethodResponse(Box<ExtResponse>),
7451}
7452
7453#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
7460#[derive(Clone, Debug, Serialize, Deserialize)]
7461#[serde(untagged)]
7462#[cfg_attr(feature = "schemars", schemars(inline))]
7463#[non_exhaustive]
7464pub enum ClientNotification {
7465 CancelSessionNotification(Box<CancelSessionNotification>),
7496 #[cfg(feature = "unstable_mcp_over_acp")]
7502 MessageMcpNotification(Box<MessageMcpNotification>),
7503 ExtNotification(Box<ExtNotification>),
7510}
7511
7512impl ClientNotification {
7513 #[must_use]
7515 pub fn method(&self) -> &str {
7516 match self {
7517 Self::CancelSessionNotification(_) => AGENT_METHOD_NAMES.session_cancel,
7518 #[cfg(feature = "unstable_mcp_over_acp")]
7519 Self::MessageMcpNotification(_) => AGENT_METHOD_NAMES.mcp_message,
7520 Self::ExtNotification(ext_notification) => &ext_notification.method,
7521 }
7522 }
7523}
7524
7525#[cfg(test)]
7526mod test_serialization {
7527 use std::path::PathBuf;
7528
7529 use super::*;
7530 use serde_json::json;
7531
7532 fn test_meta() -> Meta {
7533 json!({ "source": "test" }).as_object().unwrap().clone()
7534 }
7535
7536 #[test]
7537 fn list_sessions_cursor_and_session_directories_are_strict() {
7538 for cursor in [json!(null), json!("next")] {
7539 let response = json!({ "sessions": [], "nextCursor": cursor });
7540 serde_json::from_value::<ListSessionsResponse>(response).unwrap();
7541 }
7542 let malformed = json!({ "sessions": [], "nextCursor": 42 });
7543 assert!(serde_json::from_value::<ListSessionsResponse>(malformed).is_err());
7544
7545 let session = |dirs: serde_json::Value| json!({ "sessionId": "s", "cwd": "/w", "additionalDirectories": dirs });
7546 let info: SessionInfo = serde_json::from_value(session(json!(null))).unwrap();
7547 assert_eq!(info.additional_directories, Vec::<AbsolutePath>::new());
7548 for bad in [json!(["/a", 42]), json!("/a")] {
7549 assert!(serde_json::from_value::<SessionInfo>(session(bad)).is_err());
7550 }
7551 let response = json!({ "sessions": [session(json!(["/a", 42]))] });
7553 let response: ListSessionsResponse = serde_json::from_value(response).unwrap();
7554 assert_eq!(response.sessions, Vec::<SessionInfo>::new());
7555 }
7556
7557 #[test]
7558 fn other_fallbacks_preserve_optional_fields_verbatim() {
7559 let valid = json!({ "type": "_custom", "methodId": "m", "name": "M",
7560 "description": "d", "_meta": { "k": 1 } });
7561 let method: AuthMethod = serde_json::from_value(valid.clone()).unwrap();
7562 assert_eq!(method.description(), Some("d"));
7563 assert_eq!(
7564 method.meta(),
7565 Some(&json!({ "k": 1 }).as_object().unwrap().clone())
7566 );
7567 assert_eq!(serde_json::to_value(&method).unwrap(), valid);
7568
7569 let malformed = json!({ "type": "_custom", "methodId": "m", "name": "M",
7571 "description": 42, "_meta": "bad", "extra": true });
7572 let method: AuthMethod = serde_json::from_value(malformed.clone()).unwrap();
7573 assert_eq!(method.description(), None);
7574 assert_eq!(method.meta(), None);
7575 assert_eq!(serde_json::to_value(&method).unwrap(), malformed);
7576
7577 let replay = json!({ "type": "_custom", "_meta": "bad", "cursor": 3 });
7578 let decoded: ReplayFrom = serde_json::from_value(replay.clone()).unwrap();
7579 assert_eq!(serde_json::to_value(&decoded).unwrap(), replay);
7580 }
7581
7582 fn serialized_meta_key_count(value: &impl serde::Serialize) -> usize {
7583 serde_json::to_string(value)
7584 .unwrap()
7585 .matches("\"_meta\"")
7586 .count()
7587 }
7588
7589 #[test]
7590 fn prompt_response_without_metadata_round_trips() {
7591 let response = PromptResponse::new(MessageId::new("message-1"));
7592 let serialized = serde_json::to_value(&response).unwrap();
7593 assert_eq!(serialized, json!({ "messageId": "message-1" }));
7594 assert_eq!(
7595 serde_json::from_value::<PromptResponse>(serialized).unwrap(),
7596 response
7597 );
7598 assert_eq!(
7599 serde_json::from_value::<PromptResponse>(json!({
7600 "messageId": "message-1",
7601 "_meta": null
7602 }))
7603 .unwrap(),
7604 response
7605 );
7606 }
7607
7608 #[test]
7609 fn prompt_response_requires_message_id_and_preserves_metadata_tolerance() {
7610 let response = PromptResponse::new("message-1").meta(test_meta());
7611 let serialized = serde_json::to_value(&response).unwrap();
7612 assert_eq!(
7613 serialized,
7614 json!({
7615 "messageId": "message-1",
7616 "_meta": { "source": "test" }
7617 })
7618 );
7619
7620 let deserialized: PromptResponse = serde_json::from_value(serialized).unwrap();
7621 assert_eq!(deserialized.message_id, MessageId::new("message-1"));
7622 assert_eq!(deserialized.meta, Some(test_meta()));
7623
7624 let malformed_meta: PromptResponse = serde_json::from_value(json!({
7625 "messageId": "message-2",
7626 "_meta": false
7627 }))
7628 .unwrap();
7629 assert_eq!(malformed_meta.message_id, MessageId::new("message-2"));
7630 assert_eq!(malformed_meta.meta, None);
7631
7632 for invalid in [
7633 json!({}),
7634 json!({ "messageId": null }),
7635 json!({ "messageId": 1 }),
7636 json!({ "messageId": false }),
7637 json!({ "messageId": {} }),
7638 json!({ "messageId": [] }),
7639 ] {
7640 assert!(
7641 serde_json::from_value::<PromptResponse>(invalid).is_err(),
7642 "missing, null, and non-string message IDs must be rejected"
7643 );
7644 }
7645 }
7646
7647 #[cfg(feature = "schemars")]
7648 #[test]
7649 fn prompt_response_schema_requires_non_null_string_message_id_reference() {
7650 let schema = serde_json::to_value(schemars::schema_for!(PromptResponse)).unwrap();
7651
7652 assert_eq!(schema["required"], json!(["messageId"]));
7653 assert_eq!(
7654 schema["properties"]["messageId"]["$ref"],
7655 "#/$defs/MessageId"
7656 );
7657 assert_eq!(schema["$defs"]["MessageId"]["type"], "string");
7658 }
7659
7660 #[test]
7661 fn test_initialize_capabilities_default_on_malformed_values() {
7662 let request: InitializeRequest = serde_json::from_value(json!({
7663 "protocolVersion": 2,
7664 "capabilities": false,
7665 "info": {
7666 "name": "client",
7667 "version": "1.0.0"
7668 }
7669 }))
7670 .unwrap();
7671 assert_eq!(request.capabilities, ClientCapabilities::default());
7672
7673 let response: InitializeResponse = serde_json::from_value(json!({
7674 "protocolVersion": 2,
7675 "capabilities": false,
7676 "info": {
7677 "name": "agent",
7678 "version": "1.0.0"
7679 }
7680 }))
7681 .unwrap();
7682 assert_eq!(response.capabilities, AgentCapabilities::default());
7683 }
7684
7685 #[test]
7686 fn test_agent_capabilities_default_on_malformed_values() {
7687 let capabilities: AgentCapabilities = serde_json::from_value(json!({
7688 "session": false,
7689 "auth": false
7690 }))
7691 .unwrap();
7692
7693 assert!(capabilities.session.is_none());
7694 assert_eq!(capabilities.auth, None);
7695 }
7696
7697 #[test]
7698 fn test_mcp_server_stdio_serialization() {
7699 let server = McpServer::Stdio(
7700 McpServerStdio::new("test-server", "/usr/bin/server")
7701 .args(vec!["--port".to_string(), "3000".to_string()])
7702 .env(vec![EnvVariable::new("API_KEY", "secret123")]),
7703 );
7704
7705 let json = serde_json::to_value(&server).unwrap();
7706 assert_eq!(
7707 json,
7708 json!({
7709 "type": "stdio",
7710 "name": "test-server",
7711 "command": "/usr/bin/server",
7712 "args": ["--port", "3000"],
7713 "env": [
7714 {
7715 "name": "API_KEY",
7716 "value": "secret123"
7717 }
7718 ]
7719 })
7720 );
7721
7722 let deserialized: McpServer = serde_json::from_value(json).unwrap();
7723 match deserialized {
7724 McpServer::Stdio(McpServerStdio {
7725 name,
7726 command,
7727 args,
7728 env,
7729 meta: _,
7730 }) => {
7731 assert_eq!(name, "test-server");
7732 assert_eq!(command, AbsolutePath::new("/usr/bin/server"));
7733 assert_eq!(args, vec!["--port", "3000"]);
7734 assert_eq!(env.len(), 1);
7735 assert_eq!(env[0].name, "API_KEY");
7736 assert_eq!(env[0].value, "secret123");
7737 }
7738 _ => panic!("Expected Stdio variant"),
7739 }
7740 }
7741
7742 #[test]
7743 fn test_mcp_server_empty_arrays_are_optional() {
7744 let stdio = McpServer::Stdio(McpServerStdio::new("test-server", "/usr/bin/server"));
7745 assert_eq!(
7746 serde_json::to_value(&stdio).unwrap(),
7747 json!({
7748 "type": "stdio",
7749 "name": "test-server",
7750 "command": "/usr/bin/server"
7751 })
7752 );
7753
7754 let McpServer::Stdio(McpServerStdio { args, env, .. }) =
7755 serde_json::from_value::<McpServer>(json!({
7756 "type": "stdio",
7757 "name": "test-server",
7758 "command": "/usr/bin/server"
7759 }))
7760 .unwrap()
7761 else {
7762 panic!("Expected Stdio variant");
7763 };
7764 assert_eq!(args, Vec::<String>::new());
7765 assert_eq!(env, []);
7766
7767 let http = McpServer::Http(McpServerHttp::new("http-server", "https://api.example.com"));
7768 assert_eq!(
7769 serde_json::to_value(&http).unwrap(),
7770 json!({
7771 "type": "http",
7772 "name": "http-server",
7773 "url": "https://api.example.com"
7774 })
7775 );
7776
7777 let McpServer::Http(McpServerHttp { headers, .. }) =
7778 serde_json::from_value::<McpServer>(json!({
7779 "type": "http",
7780 "name": "http-server",
7781 "url": "https://api.example.com"
7782 }))
7783 .unwrap()
7784 else {
7785 panic!("Expected Http variant");
7786 };
7787 assert_eq!(headers, []);
7788 }
7789
7790 #[test]
7791 fn test_mcp_server_unknown_transport_serialization() {
7792 let json = json!({
7793 "type": "websocket",
7794 "name": "future-server",
7795 "url": "wss://example.com/mcp",
7796 "protocolVersion": "2026-01-01"
7797 });
7798
7799 let deserialized: McpServer = serde_json::from_value(json.clone()).unwrap();
7800 let McpServer::Other(OtherMcpServer { type_, fields }) = &deserialized else {
7801 panic!("Expected Other variant");
7802 };
7803
7804 assert_eq!(type_, "websocket");
7805 assert_eq!(fields["name"], "future-server");
7806 assert_eq!(fields["url"], "wss://example.com/mcp");
7807 assert_eq!(fields["protocolVersion"], "2026-01-01");
7808 assert_eq!(serde_json::to_value(&deserialized).unwrap(), json);
7809 }
7810
7811 #[test]
7812 fn test_mcp_server_stdio_requires_type() {
7813 let result = serde_json::from_value::<McpServer>(json!({
7814 "name": "test-server",
7815 "command": "/usr/bin/server",
7816 "args": [],
7817 "env": []
7818 }));
7819
7820 assert!(result.is_err());
7821 }
7822
7823 #[test]
7824 fn test_mcp_server_unknown_does_not_hide_malformed_known_transport() {
7825 let result = serde_json::from_value::<McpServer>(json!({
7826 "type": "stdio",
7827 "name": "test-server",
7828 "args": [],
7829 "env": []
7830 }));
7831
7832 assert!(result.is_err());
7833 }
7834
7835 #[test]
7836 fn test_mcp_server_http_serialization() {
7837 let server = McpServer::Http(
7838 McpServerHttp::new("http-server", "https://api.example.com").headers(vec![
7839 HttpHeader::new("Authorization", "Bearer token123"),
7840 HttpHeader::new("Content-Type", "application/json"),
7841 ]),
7842 );
7843
7844 let json = serde_json::to_value(&server).unwrap();
7845 assert_eq!(
7846 json,
7847 json!({
7848 "type": "http",
7849 "name": "http-server",
7850 "url": "https://api.example.com",
7851 "headers": [
7852 {
7853 "name": "Authorization",
7854 "value": "Bearer token123"
7855 },
7856 {
7857 "name": "Content-Type",
7858 "value": "application/json"
7859 }
7860 ]
7861 })
7862 );
7863
7864 let deserialized: McpServer = serde_json::from_value(json).unwrap();
7865 match deserialized {
7866 McpServer::Http(McpServerHttp {
7867 name,
7868 url,
7869 headers,
7870 meta: _,
7871 }) => {
7872 assert_eq!(name, "http-server");
7873 assert_eq!(url, "https://api.example.com");
7874 assert_eq!(headers.len(), 2);
7875 assert_eq!(headers[0].name, "Authorization");
7876 assert_eq!(headers[0].value, "Bearer token123");
7877 assert_eq!(headers[1].name, "Content-Type");
7878 assert_eq!(headers[1].value, "application/json");
7879 }
7880 _ => panic!("Expected Http variant"),
7881 }
7882 }
7883
7884 #[cfg(feature = "schemars")]
7885 #[test]
7886 fn mcp_server_http_schema_marks_url_as_uri() {
7887 let schema = serde_json::to_value(schemars::schema_for!(McpServerHttp)).unwrap();
7888
7889 assert_eq!(schema["properties"]["url"]["format"], "uri");
7890 }
7891
7892 #[cfg(feature = "unstable_mcp_over_acp")]
7893 #[test]
7894 fn test_client_mcp_message_method_names() {
7895 use serde_json::json;
7896
7897 assert_eq!(AGENT_METHOD_NAMES.mcp_message, "mcp/message");
7898
7899 let notification =
7900 MessageMcpNotification::new("server-1", "req-1", "notifications/progress");
7901 assert_eq!(
7902 ClientNotification::MessageMcpNotification(Box::new(notification.clone())).method(),
7903 "mcp/message"
7904 );
7905 assert_eq!(
7906 serde_json::to_value(notification).unwrap(),
7907 json!({
7908 "serverId": "server-1",
7909 "requestId": "req-1",
7910 "method": "notifications/progress"
7911 })
7912 );
7913 let notification: MessageMcpNotification = serde_json::from_value(json!({
7914 "serverId": "server-1", "requestId": "req-1", "method": "notifications/progress",
7915 "params": null, "_meta": null
7916 }))
7917 .unwrap();
7918 assert_eq!(notification.params, None);
7919 assert_eq!(notification.meta, None);
7920 for key in ["serverId", "requestId", "method"] {
7921 let mut value = json!({"serverId":"server-1", "requestId":"req-1", "method":"notifications/progress"});
7922 value.as_object_mut().unwrap().remove(key);
7923 assert!(serde_json::from_value::<MessageMcpNotification>(value).is_err());
7924 }
7925 }
7926
7927 #[test]
7928 fn test_auth_method_names() {
7929 assert_eq!(AGENT_METHOD_NAMES.auth_login, "auth/login");
7930 assert_eq!(AGENT_METHOD_NAMES.auth_logout, "auth/logout");
7931
7932 assert_eq!(
7933 ClientRequest::LoginAuthRequest(Box::new(LoginAuthRequest::new("agent-login")))
7934 .method(),
7935 "auth/login"
7936 );
7937 assert_eq!(
7938 ClientRequest::LogoutAuthRequest(Box::new(LogoutAuthRequest::new())).method(),
7939 "auth/logout"
7940 );
7941 }
7942
7943 #[test]
7944 fn test_session_config_option_category_known_variants() {
7945 assert_eq!(
7947 serde_json::to_value(&SessionConfigOptionCategory::Mode).unwrap(),
7948 json!("mode")
7949 );
7950 assert_eq!(
7951 serde_json::to_value(&SessionConfigOptionCategory::Model).unwrap(),
7952 json!("model")
7953 );
7954 assert_eq!(
7955 serde_json::to_value(&SessionConfigOptionCategory::ModelConfig).unwrap(),
7956 json!("model_config")
7957 );
7958 assert_eq!(
7959 serde_json::to_value(&SessionConfigOptionCategory::ThoughtLevel).unwrap(),
7960 json!("thought_level")
7961 );
7962
7963 assert_eq!(
7965 serde_json::from_str::<SessionConfigOptionCategory>("\"mode\"").unwrap(),
7966 SessionConfigOptionCategory::Mode
7967 );
7968 assert_eq!(
7969 serde_json::from_str::<SessionConfigOptionCategory>("\"model\"").unwrap(),
7970 SessionConfigOptionCategory::Model
7971 );
7972 assert_eq!(
7973 serde_json::from_str::<SessionConfigOptionCategory>("\"model_config\"").unwrap(),
7974 SessionConfigOptionCategory::ModelConfig
7975 );
7976 assert_eq!(
7977 serde_json::from_str::<SessionConfigOptionCategory>("\"thought_level\"").unwrap(),
7978 SessionConfigOptionCategory::ThoughtLevel
7979 );
7980 }
7981
7982 #[test]
7983 fn test_session_config_option_category_unknown_variants() {
7984 let unknown: SessionConfigOptionCategory =
7986 serde_json::from_str("\"some_future_category\"").unwrap();
7987 assert_eq!(
7988 unknown,
7989 SessionConfigOptionCategory::Other("some_future_category".to_string())
7990 );
7991
7992 let json = serde_json::to_value(&unknown).unwrap();
7994 assert_eq!(json, json!("some_future_category"));
7995 }
7996
7997 #[test]
7998 fn test_session_config_option_category_custom_categories() {
7999 let custom: SessionConfigOptionCategory =
8001 serde_json::from_str("\"_my_custom_category\"").unwrap();
8002 assert_eq!(
8003 custom,
8004 SessionConfigOptionCategory::Other("_my_custom_category".to_string())
8005 );
8006
8007 let json = serde_json::to_value(&custom).unwrap();
8009 assert_eq!(json, json!("_my_custom_category"));
8010
8011 let deserialized: SessionConfigOptionCategory = serde_json::from_value(json).unwrap();
8013 assert_eq!(
8014 deserialized,
8015 SessionConfigOptionCategory::Other("_my_custom_category".to_string()),
8016 );
8017 }
8018
8019 fn test_config_option() -> SessionConfigOption {
8020 SessionConfigOption::select(
8021 "mode",
8022 "Mode",
8023 "ask",
8024 vec![SessionConfigSelectOption::new("ask", "Ask")],
8025 )
8026 }
8027
8028 #[test]
8029 fn test_session_response_initial_state_default_empty_and_skip_serializing() {
8030 assert_eq!(
8031 serde_json::to_value(NewSessionResponse::new("sess")).unwrap(),
8032 json!({ "sessionId": "sess" })
8033 );
8034 assert_eq!(
8035 serde_json::to_value(ResumeSessionResponse::new()).unwrap(),
8036 json!({})
8037 );
8038 #[cfg(feature = "unstable_session_fork")]
8039 assert_eq!(
8040 serde_json::to_value(ForkSessionResponse::new("fork")).unwrap(),
8041 json!({ "sessionId": "fork" })
8042 );
8043
8044 let json = serde_json::to_value(
8045 NewSessionResponse::new("sess").config_options(vec![test_config_option()]),
8046 )
8047 .unwrap();
8048 assert_eq!(json["configOptions"].as_array().unwrap().len(), 1);
8049 }
8050
8051 #[test]
8052 fn test_session_response_config_options_deserialize_missing_null_and_invalid() {
8053 let missing: NewSessionResponse =
8054 serde_json::from_value(json!({ "sessionId": "sess" })).unwrap();
8055 assert_eq!(missing.config_options, []);
8056
8057 let null: NewSessionResponse = serde_json::from_value(json!({
8058 "sessionId": "sess",
8059 "configOptions": null
8060 }))
8061 .unwrap();
8062 assert_eq!(null.config_options, []);
8063
8064 let wrong_shape: NewSessionResponse = serde_json::from_value(json!({
8065 "sessionId": "sess",
8066 "configOptions": "oops"
8067 }))
8068 .unwrap();
8069 assert_eq!(wrong_shape.config_options, []);
8070
8071 let valid_option = serde_json::to_value(test_config_option()).unwrap();
8072 let mixed: NewSessionResponse = serde_json::from_value(json!({
8073 "sessionId": "sess",
8074 "configOptions": ["oops", valid_option]
8075 }))
8076 .unwrap();
8077 assert_eq!(mixed.config_options.len(), 1);
8078
8079 let resume: ResumeSessionResponse = serde_json::from_value(json!({})).unwrap();
8080 assert_eq!(resume.config_options, []);
8081 #[cfg(feature = "unstable_session_fork")]
8082 {
8083 let fork: ForkSessionResponse =
8084 serde_json::from_value(json!({ "sessionId": "fork" })).unwrap();
8085 assert_eq!(fork.config_options, []);
8086 }
8087 }
8088
8089 #[test]
8090 fn test_session_response_available_commands_roundtrip_with_config_options() {
8091 use super::super::{AvailableCommandInput, Command, TextCommandInput};
8092
8093 let commands = vec![
8094 AvailableCommand::Command(
8095 Command::new("web", "Search the web")
8096 .input(AvailableCommandInput::Text(TextCommandInput::new("Query"))),
8097 ),
8098 AvailableCommand::Command(Command::new("test", "Run project tests")),
8099 ];
8100 let mut expected = json!({
8101 "sessionId": "sess",
8102 "configOptions": [test_config_option()],
8103 "availableCommands": [
8104 {
8105 "type": "command",
8106 "name": "web",
8107 "description": "Search the web",
8108 "input": { "type": "text", "hint": "Query" }
8109 },
8110 { "type": "command", "name": "test", "description": "Run project tests" }
8111 ]
8112 });
8113
8114 let new = NewSessionResponse::new("sess")
8115 .config_options(vec![test_config_option()])
8116 .available_commands(commands.clone());
8117 assert_eq!(serde_json::to_value(&new).unwrap(), expected);
8118 assert_eq!(
8119 serde_json::from_value::<NewSessionResponse>(expected.clone()).unwrap(),
8120 new
8121 );
8122
8123 expected.as_object_mut().unwrap().remove("sessionId");
8124 let resume = ResumeSessionResponse::new()
8125 .config_options(vec![test_config_option()])
8126 .available_commands(commands.clone());
8127 assert_eq!(serde_json::to_value(&resume).unwrap(), expected);
8128 assert_eq!(
8129 serde_json::from_value::<ResumeSessionResponse>(expected.clone()).unwrap(),
8130 resume
8131 );
8132
8133 #[cfg(feature = "unstable_session_fork")]
8134 {
8135 expected["sessionId"] = json!("fork");
8136 let fork = ForkSessionResponse::new("fork")
8137 .config_options(vec![test_config_option()])
8138 .available_commands(commands);
8139 assert_eq!(serde_json::to_value(&fork).unwrap(), expected);
8140 assert_eq!(
8141 serde_json::from_value::<ForkSessionResponse>(expected).unwrap(),
8142 fork
8143 );
8144 }
8145 }
8146
8147 #[test]
8148 fn test_session_response_available_commands_deserialize_missing_null_and_invalid() {
8149 let command =
8150 AvailableCommand::Command(super::super::Command::new("test", "Run project tests"));
8151 let config_options = vec![test_config_option()];
8152
8153 for (commands, expected) in [
8154 (None, vec![]),
8155 (Some(json!(null)), vec![]),
8156 (Some(json!([])), vec![]),
8157 (Some(json!("oops")), vec![]),
8158 (Some(json!({})), vec![]),
8159 (Some(json!([null, {}, { "name": "invalid" }])), vec![]),
8160 (
8161 Some(json!([{ "name": "test", "description": "Run project tests" }])),
8162 vec![],
8163 ),
8164 (
8165 Some(json!([null, command, { "name": "invalid" }])),
8166 vec![command.clone()],
8167 ),
8168 ] {
8169 let mut value = json!({
8170 "sessionId": "sess",
8171 "configOptions": config_options
8172 });
8173 if let Some(commands) = commands {
8174 value["availableCommands"] = commands;
8175 }
8176
8177 let new: NewSessionResponse = serde_json::from_value(value.clone()).unwrap();
8178 assert_eq!(new.available_commands, expected);
8179 assert_eq!(new.config_options, config_options);
8180
8181 #[cfg(feature = "unstable_session_fork")]
8182 {
8183 let fork: ForkSessionResponse = serde_json::from_value(value.clone()).unwrap();
8184 assert_eq!(fork.available_commands, expected);
8185 assert_eq!(fork.config_options, config_options);
8186 }
8187
8188 value.as_object_mut().unwrap().remove("sessionId");
8189 let resume: ResumeSessionResponse = serde_json::from_value(value).unwrap();
8190 assert_eq!(resume.available_commands, expected);
8191 assert_eq!(resume.config_options, config_options);
8192 }
8193
8194 let resume: ResumeSessionResponse = serde_json::from_value(json!(null)).unwrap();
8195 assert_eq!(resume.available_commands, []);
8196 }
8197
8198 #[test]
8199 fn test_resume_session_replay_from_serialization() {
8200 assert_eq!(
8201 serde_json::to_value(ResumeSessionRequest::new(
8202 "sess_abc123",
8203 "/home/user/project"
8204 ))
8205 .unwrap(),
8206 json!({
8207 "sessionId": "sess_abc123",
8208 "cwd": "/home/user/project"
8209 })
8210 );
8211 assert_eq!(
8212 serde_json::to_value(
8213 ResumeSessionRequest::new("sess_abc123", "/home/user/project")
8214 .replay_from(ReplayFrom::from(ReplayFromStart::new()))
8215 )
8216 .unwrap(),
8217 json!({
8218 "sessionId": "sess_abc123",
8219 "cwd": "/home/user/project",
8220 "replayFrom": {
8221 "type": "start"
8222 }
8223 })
8224 );
8225
8226 let replay: ResumeSessionRequest = serde_json::from_value(json!({
8227 "sessionId": "sess_abc123",
8228 "cwd": "/home/user/project",
8229 "replayFrom": {
8230 "type": "start"
8231 }
8232 }))
8233 .unwrap();
8234 assert!(matches!(replay.replay_from, Some(ReplayFrom::Start(_))));
8235
8236 let none: ResumeSessionRequest = serde_json::from_value(json!({
8237 "sessionId": "sess_abc123",
8238 "cwd": "/home/user/project",
8239 "replayFrom": null
8240 }))
8241 .unwrap();
8242 assert!(none.replay_from.is_none());
8243 }
8244
8245 #[test]
8246 fn test_auth_method_agent_serialization() {
8247 let method = AuthMethod::Agent(AuthMethodAgent::new("default-auth", "Default Auth"));
8248
8249 let json = serde_json::to_value(&method).unwrap();
8250 assert_eq!(
8251 json,
8252 json!({
8253 "methodId": "default-auth",
8254 "name": "Default Auth",
8255 "type": "agent"
8256 })
8257 );
8258 assert!(!json.as_object().unwrap().contains_key("description"));
8260
8261 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
8262 match deserialized {
8263 AuthMethod::Agent(AuthMethodAgent {
8264 method_id, name, ..
8265 }) => {
8266 assert_eq!(method_id.0.as_ref(), "default-auth");
8267 assert_eq!(name, "Default Auth");
8268 }
8269 _ => panic!("Expected Agent variant"),
8270 }
8271 }
8272
8273 #[test]
8274 fn test_auth_method_agent_deserialization() {
8275 let json = json!({
8276 "methodId": "agent-auth",
8277 "name": "Agent Auth",
8278 "type": "agent"
8279 });
8280
8281 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
8282 assert!(matches!(deserialized, AuthMethod::Agent(_)));
8283 }
8284
8285 #[test]
8286 fn test_auth_method_agent_requires_type() {
8287 assert!(
8288 serde_json::from_value::<AuthMethod>(json!({
8289 "methodId": "agent-auth",
8290 "name": "Agent Auth"
8291 }))
8292 .is_err()
8293 );
8294 }
8295
8296 #[test]
8297 fn test_auth_method_agent_rejects_null_type() {
8298 assert!(
8299 serde_json::from_value::<AuthMethod>(json!({
8300 "methodId": "agent-auth",
8301 "name": "Agent Auth",
8302 "type": null
8303 }))
8304 .is_err()
8305 );
8306 }
8307
8308 #[test]
8309 fn test_auth_method_unknown_does_not_hide_malformed_agent() {
8310 assert!(
8311 serde_json::from_value::<AuthMethod>(json!({
8312 "methodId": "agent-auth",
8313 "type": "agent"
8314 }))
8315 .is_err()
8316 );
8317 assert!(
8318 serde_json::from_value::<AuthMethod>(json!({
8319 "methodId": "api-key",
8320 "type": "env_var",
8321 "vars": [{"name": "API_KEY"}]
8322 }))
8323 .is_err()
8324 );
8325 }
8326
8327 #[test]
8328 fn test_auth_method_unknown_variant_roundtrip() {
8329 let method: AuthMethod = serde_json::from_value(json!({
8330 "methodId": "oauth",
8331 "name": "OAuth",
8332 "type": "_oauth",
8333 "authorizationUrl": "https://example.com/auth"
8334 }))
8335 .unwrap();
8336
8337 assert_eq!(method.method_id().0.as_ref(), "oauth");
8338 assert_eq!(method.name(), "OAuth");
8339 let AuthMethod::Other(unknown) = method else {
8340 panic!("expected unknown auth method");
8341 };
8342 assert_eq!(unknown.type_, "_oauth");
8343 assert_eq!(
8344 unknown.fields.get("authorizationUrl"),
8345 Some(&json!("https://example.com/auth"))
8346 );
8347
8348 assert_eq!(
8349 serde_json::to_value(AuthMethod::Other(unknown)).unwrap(),
8350 json!({
8351 "methodId": "oauth",
8352 "name": "OAuth",
8353 "type": "_oauth",
8354 "authorizationUrl": "https://example.com/auth"
8355 })
8356 );
8357 }
8358
8359 #[test]
8360 fn test_auth_method_unknown_does_not_hide_malformed_known_variant() {
8361 assert!(
8362 serde_json::from_value::<AuthMethod>(json!({
8363 "methodId": "terminal-auth",
8364 "type": "terminal"
8365 }))
8366 .is_err()
8367 );
8368 }
8369
8370 #[test]
8371 fn test_tool_call_and_terminal_cancel_serialization() {
8372 assert_eq!(AGENT_METHOD_NAMES.tool_call_cancel, "tool_call/cancel");
8373 assert_eq!(AGENT_METHOD_NAMES.terminal_cancel, "terminal/cancel");
8374 assert_eq!(
8375 ClientRequest::CancelToolCallRequest(Box::new(CancelToolCallRequest::new(
8376 "sess_abc123",
8377 "call_012"
8378 )))
8379 .method(),
8380 "tool_call/cancel"
8381 );
8382 assert_eq!(
8383 ClientRequest::CancelTerminalRequest(Box::new(CancelTerminalRequest::new(
8384 "sess_abc123",
8385 "term_003"
8386 )))
8387 .method(),
8388 "terminal/cancel"
8389 );
8390 assert_eq!(
8391 serde_json::to_value(CancelToolCallRequest::new("sess_abc123", "call_012")).unwrap(),
8392 json!({"sessionId": "sess_abc123", "toolCallId": "call_012"})
8393 );
8394 assert_eq!(
8395 serde_json::to_value(CancelTerminalRequest::new("sess_abc123", "term_003")).unwrap(),
8396 json!({"sessionId": "sess_abc123", "terminalId": "term_003"})
8397 );
8398 assert!(
8399 serde_json::from_value::<CancelToolCallRequest>(json!({"sessionId": "sess_abc123"}))
8400 .is_err()
8401 );
8402 assert_eq!(
8403 serde_json::to_value(CancelTerminalResponse::new()).unwrap(),
8404 json!({})
8405 );
8406 }
8407
8408 #[test]
8409 fn test_session_delete_serialization() {
8410 assert_eq!(AGENT_METHOD_NAMES.session_delete, "session/delete");
8411 assert_eq!(
8412 ClientRequest::DeleteSessionRequest(Box::new(DeleteSessionRequest::new("sess_abc123")))
8413 .method(),
8414 "session/delete"
8415 );
8416 assert_eq!(
8417 serde_json::to_value(DeleteSessionRequest::new("sess_abc123")).unwrap(),
8418 json!({
8419 "sessionId": "sess_abc123"
8420 })
8421 );
8422 assert_eq!(
8423 serde_json::to_value(DeleteSessionResponse::new()).unwrap(),
8424 json!({})
8425 );
8426 assert_eq!(
8427 serde_json::to_value(
8428 SessionCapabilities::new().delete(SessionDeleteCapabilities::new())
8429 )
8430 .unwrap(),
8431 json!({
8432 "delete": {}
8433 })
8434 );
8435 }
8436 #[cfg(feature = "unstable_session_set_title")]
8437 #[test]
8438 fn test_session_set_title_serialization() {
8439 assert_eq!(AGENT_METHOD_NAMES.session_set_title, "session/set_title");
8440 assert_eq!(
8441 ClientRequest::SetSessionTitleRequest(Box::new(SetSessionTitleRequest::new(
8442 "sess_abc123",
8443 "Investigate OAuth timeout"
8444 )))
8445 .method(),
8446 "session/set_title"
8447 );
8448 assert_eq!(
8449 serde_json::to_value(SetSessionTitleRequest::new(
8450 "sess_abc123",
8451 "Investigate OAuth timeout"
8452 ))
8453 .unwrap(),
8454 json!({
8455 "sessionId": "sess_abc123",
8456 "title": "Investigate OAuth timeout"
8457 })
8458 );
8459 assert_eq!(
8460 serde_json::to_value(SetSessionTitleResponse::new("Investigate OAuth timeout"))
8461 .unwrap(),
8462 json!({
8463 "title": "Investigate OAuth timeout"
8464 })
8465 );
8466 assert_eq!(
8467 serde_json::to_value(SessionCapabilities::new().title(SessionTitleCapabilities::new()))
8468 .unwrap(),
8469 json!({
8470 "title": {}
8471 })
8472 );
8473 }
8474
8475 #[cfg(feature = "unstable_session_set_title")]
8476 #[test]
8477 fn test_session_set_title_requires_title() {
8478 for params in [
8479 json!({"sessionId": "sess_abc123"}),
8480 json!({"sessionId": "sess_abc123", "title": null}),
8481 json!({"sessionId": "sess_abc123", "title": 42}),
8482 ] {
8483 assert!(serde_json::from_value::<SetSessionTitleRequest>(params).is_err());
8484 }
8485 for result in [json!({}), json!({"title": null})] {
8486 assert!(serde_json::from_value::<SetSessionTitleResponse>(result).is_err());
8487 }
8488 }
8489
8490 #[cfg(feature = "unstable_session_status")]
8491 #[test]
8492 fn test_session_status_serialization() {
8493 assert_eq!(AGENT_METHOD_NAMES.session_status, "session/status");
8494 assert_eq!(
8495 ClientRequest::SessionStatusRequest(Box::new(SessionStatusRequest::new("sess_abc123")))
8496 .method(),
8497 "session/status"
8498 );
8499 assert_eq!(
8500 serde_json::to_value(SessionStatusRequest::new("sess_abc123")).unwrap(),
8501 json!({"sessionId": "sess_abc123"})
8502 );
8503 assert_eq!(
8504 serde_json::to_value(SessionStatusResponse::new(SessionStatus::Saved)).unwrap(),
8505 json!({"status": "saved"})
8506 );
8507 assert_eq!(
8508 serde_json::to_value(
8509 SessionCapabilities::new().status(SessionStatusCapabilities::new())
8510 )
8511 .unwrap(),
8512 json!({"status": {}})
8513 );
8514 assert_eq!(
8515 serde_json::to_value(SessionStatusResponse::new(SessionStatus::Open).state(
8516 StateUpdate::RequiresAction(crate::v2::RequiresActionStateUpdate::new())
8517 ))
8518 .unwrap(),
8519 json!({
8520 "status": "open",
8521 "state": {"state": "requires_action"}
8522 })
8523 );
8524 for state in [json!(null), json!("running"), json!({"state": 7})] {
8525 let response = serde_json::from_value::<SessionStatusResponse>(
8526 json!({"status": "open", "state": state}),
8527 )
8528 .unwrap();
8529 assert_eq!(response.state, None);
8530 }
8531 }
8532
8533 #[cfg(feature = "unstable_session_status")]
8534 #[test]
8535 fn test_session_status_is_strict_and_open() {
8536 let custom =
8537 serde_json::from_value::<SessionStatusResponse>(json!({"status": "_suspended"}))
8538 .unwrap();
8539 assert_eq!(
8540 custom.status,
8541 SessionStatus::Other("_suspended".to_string())
8542 );
8543 assert_eq!(
8544 serde_json::to_value(&custom).unwrap(),
8545 json!({"status": "_suspended"})
8546 );
8547 for result in [json!({}), json!({"status": null}), json!({"status": 1})] {
8548 assert!(serde_json::from_value::<SessionStatusResponse>(result).is_err());
8549 }
8550 for params in [json!({}), json!({"sessionId": null})] {
8551 assert!(serde_json::from_value::<SessionStatusRequest>(params).is_err());
8552 }
8553 }
8554
8555 #[cfg(feature = "unstable_session_archive")]
8556 #[test]
8557 fn test_session_archive_serialization() {
8558 assert_eq!(AGENT_METHOD_NAMES.session_archive, "session/archive");
8559 assert_eq!(AGENT_METHOD_NAMES.session_unarchive, "session/unarchive");
8560 assert_eq!(
8561 ClientRequest::ArchiveSessionRequest(Box::new(ArchiveSessionRequest::new(
8562 "sess_abc123"
8563 )))
8564 .method(),
8565 "session/archive"
8566 );
8567 assert_eq!(
8568 ClientRequest::UnarchiveSessionRequest(Box::new(UnarchiveSessionRequest::new(
8569 "sess_abc123"
8570 )))
8571 .method(),
8572 "session/unarchive"
8573 );
8574 assert_eq!(
8575 serde_json::to_value(ArchiveSessionRequest::new("sess_abc123")).unwrap(),
8576 json!({"sessionId": "sess_abc123"})
8577 );
8578 assert_eq!(
8579 serde_json::to_value(UnarchiveSessionResponse::new()).unwrap(),
8580 json!({})
8581 );
8582 assert_eq!(
8583 serde_json::to_value(
8584 SessionCapabilities::new().archive(SessionArchiveCapabilities::new())
8585 )
8586 .unwrap(),
8587 json!({"archive": {}})
8588 );
8589 assert_eq!(
8590 serde_json::to_value(
8591 ListSessionsRequest::new()
8592 .cwd("/home/user/project")
8593 .archived(SessionArchiveFilter::Only)
8594 )
8595 .unwrap(),
8596 json!({"cwd": "/home/user/project", "archived": "only"})
8597 );
8598 assert_eq!(
8599 serde_json::to_value(
8600 SessionInfo::new("sess_abc123", "/home/user/project").archived(true)
8601 )
8602 .unwrap(),
8603 json!({"sessionId": "sess_abc123", "cwd": "/home/user/project", "archived": true})
8604 );
8605 }
8606
8607 #[cfg(feature = "unstable_session_archive")]
8608 #[test]
8609 fn test_session_archive_filter_is_strict_and_open() {
8610 for (value, expected) in [
8611 (json!(null), None),
8612 (json!("include"), Some(SessionArchiveFilter::Include)),
8613 (
8614 json!("_recent"),
8615 Some(SessionArchiveFilter::Other("_recent".to_string())),
8616 ),
8617 ] {
8618 let request =
8619 serde_json::from_value::<ListSessionsRequest>(json!({"archived": value})).unwrap();
8620 assert_eq!(request.archived, expected);
8621 }
8622 for value in [json!(true), json!(1), json!({})] {
8623 assert!(
8624 serde_json::from_value::<ListSessionsRequest>(json!({"archived": value})).is_err()
8625 );
8626 }
8627 let info = serde_json::from_value::<SessionInfo>(
8628 json!({"sessionId": "sess_abc123", "cwd": "/home/user/project", "archived": "yes"}),
8629 )
8630 .unwrap();
8631 assert_eq!(info.archived, None);
8632 }
8633
8634 #[test]
8635 fn test_session_additional_directories_serialization() {
8636 assert_eq!(
8637 serde_json::to_value(NewSessionRequest::new("/home/user/project")).unwrap(),
8638 json!({
8639 "cwd": "/home/user/project",
8640 })
8641 );
8642 assert_eq!(
8643 serde_json::to_value(
8644 NewSessionRequest::new("/home/user/project").additional_directories(vec![
8645 PathBuf::from("/home/user/shared-lib"),
8646 PathBuf::from("/home/user/product-docs"),
8647 ])
8648 )
8649 .unwrap(),
8650 json!({
8651 "cwd": "/home/user/project",
8652 "additionalDirectories": [
8653 "/home/user/shared-lib",
8654 "/home/user/product-docs"
8655 ],
8656 })
8657 );
8658 assert_eq!(
8659 serde_json::to_value(ResumeSessionRequest::new(
8660 "sess_abc123",
8661 "/home/user/project"
8662 ))
8663 .unwrap(),
8664 json!({
8665 "sessionId": "sess_abc123",
8666 "cwd": "/home/user/project",
8667 })
8668 );
8669 assert_eq!(
8670 serde_json::from_value::<ResumeSessionRequest>(json!({
8671 "sessionId": "sess_abc123",
8672 "cwd": "/home/user/project"
8673 }))
8674 .unwrap()
8675 .mcp_servers,
8676 Vec::<McpServer>::new()
8677 );
8678 assert_eq!(
8679 serde_json::from_value::<ResumeSessionRequest>(json!({
8680 "sessionId": "sess_abc123",
8681 "cwd": "/home/user/project",
8682 "mcpServers": null
8683 }))
8684 .unwrap()
8685 .mcp_servers,
8686 Vec::<McpServer>::new()
8687 );
8688 assert_eq!(
8689 serde_json::to_value(SessionInfo::new("sess_abc123", "/home/user/project")).unwrap(),
8690 json!({
8691 "sessionId": "sess_abc123",
8692 "cwd": "/home/user/project"
8693 })
8694 );
8695 assert_eq!(
8696 serde_json::to_value(
8697 SessionInfo::new("sess_abc123", "/home/user/project").additional_directories(vec![
8698 PathBuf::from("/home/user/shared-lib"),
8699 PathBuf::from("/home/user/product-docs"),
8700 ])
8701 )
8702 .unwrap(),
8703 json!({
8704 "sessionId": "sess_abc123",
8705 "cwd": "/home/user/project",
8706 "additionalDirectories": [
8707 "/home/user/shared-lib",
8708 "/home/user/product-docs"
8709 ]
8710 })
8711 );
8712 assert_eq!(
8713 serde_json::from_value::<SessionInfo>(json!({
8714 "sessionId": "sess_abc123",
8715 "cwd": "/home/user/project"
8716 }))
8717 .unwrap()
8718 .additional_directories,
8719 Vec::<AbsolutePath>::new()
8720 );
8721 }
8722 #[test]
8723 fn test_session_additional_directories_capabilities_serialization() {
8724 assert_eq!(
8725 serde_json::to_value(
8726 SessionCapabilities::new()
8727 .additional_directories(SessionAdditionalDirectoriesCapabilities::new())
8728 )
8729 .unwrap(),
8730 json!({
8731 "additionalDirectories": {}
8732 })
8733 );
8734 }
8735
8736 #[cfg(feature = "unstable_session_system_prompt")]
8737 #[test]
8738 fn test_session_system_prompt_capabilities_serialization() {
8739 assert_eq!(
8740 serde_json::to_value(
8741 SessionCapabilities::new().system_prompt(SessionSystemPromptCapabilities::new())
8742 )
8743 .unwrap(),
8744 json!({ "systemPrompt": {} })
8745 );
8746 assert_eq!(
8747 serde_json::to_value(
8748 SessionCapabilities::new().system_prompt(
8749 SessionSystemPromptCapabilities::new().mode(
8750 SessionSystemPromptModeCapabilities::new()
8751 .override_(SessionSystemPromptOverrideCapabilities::new())
8752 )
8753 )
8754 )
8755 .unwrap(),
8756 json!({ "systemPrompt": { "mode": { "override": {} } } })
8757 );
8758 assert_eq!(
8759 serde_json::from_value::<SessionCapabilities>(
8760 json!({ "systemPrompt": { "mode": { "override": {} } } })
8761 )
8762 .unwrap()
8763 .system_prompt
8764 .and_then(|capabilities| capabilities.mode)
8765 .and_then(|mode| mode.override_),
8766 Some(SessionSystemPromptOverrideCapabilities::new())
8767 );
8768
8769 assert_eq!(
8771 serde_json::from_value::<SessionCapabilities>(json!({ "systemPrompt": "yes" }))
8772 .unwrap()
8773 .system_prompt,
8774 None
8775 );
8776 assert_eq!(
8777 serde_json::from_value::<SessionCapabilities>(
8778 json!({ "systemPrompt": { "mode": { "override": true } } })
8779 )
8780 .unwrap()
8781 .system_prompt,
8782 Some(
8783 SessionSystemPromptCapabilities::new()
8784 .mode(SessionSystemPromptModeCapabilities::new())
8785 )
8786 );
8787 assert_eq!(
8788 serde_json::from_value::<SessionCapabilities>(
8789 json!({ "systemPrompt": { "mode": "override" } })
8790 )
8791 .unwrap()
8792 .system_prompt,
8793 Some(SessionSystemPromptCapabilities::new())
8794 );
8795 }
8796
8797 #[cfg(feature = "unstable_session_system_prompt")]
8798 #[test]
8799 fn test_system_prompt_lifecycle_request_serialization() {
8800 let system_prompt = SystemPrompt::new(vec![ContentBlock::from(
8801 "You are a code review specialist.",
8802 )]);
8803 let system_prompt_json = json!({
8804 "mode": "append",
8805 "content": [{ "type": "text", "text": "You are a code review specialist." }]
8806 });
8807
8808 let request =
8809 NewSessionRequest::new("/home/user/project").system_prompt(system_prompt.clone());
8810 let request_json = json!({
8811 "cwd": "/home/user/project",
8812 "systemPrompt": system_prompt_json
8813 });
8814 assert_eq!(serde_json::to_value(&request).unwrap(), request_json);
8815 assert_eq!(
8816 serde_json::from_value::<NewSessionRequest>(request_json).unwrap(),
8817 request
8818 );
8819
8820 assert_eq!(
8821 serde_json::to_value(
8822 ResumeSessionRequest::new("sess_abc123", "/home/user/project")
8823 .system_prompt(system_prompt.clone())
8824 )
8825 .unwrap()["systemPrompt"],
8826 system_prompt_json
8827 );
8828 #[cfg(feature = "unstable_session_fork")]
8829 assert_eq!(
8830 serde_json::to_value(
8831 ForkSessionRequest::new("sess_abc123", "/home/user/project")
8832 .system_prompt(system_prompt.clone())
8833 )
8834 .unwrap()["systemPrompt"],
8835 system_prompt_json
8836 );
8837
8838 let override_prompt =
8839 SystemPrompt::new(vec![ContentBlock::from("...")]).mode(SystemPromptMode::Override);
8840 let override_json = json!({
8841 "mode": "override",
8842 "content": [{ "type": "text", "text": "..." }]
8843 });
8844 assert_eq!(
8845 serde_json::to_value(&override_prompt).unwrap(),
8846 override_json
8847 );
8848 assert_eq!(
8849 serde_json::from_value::<SystemPrompt>(override_json).unwrap(),
8850 override_prompt
8851 );
8852 }
8853
8854 #[cfg(feature = "unstable_session_system_prompt")]
8857 #[test]
8858 fn test_system_prompt_new_defaults_to_append() {
8859 let system_prompt = SystemPrompt::new(vec![ContentBlock::from("...")]);
8860 assert_eq!(system_prompt.mode, SystemPromptMode::Append);
8861 assert_eq!(
8862 serde_json::to_value(&system_prompt).unwrap(),
8863 json!({ "mode": "append", "content": [{ "type": "text", "text": "..." }] })
8864 );
8865 }
8866
8867 #[cfg(feature = "unstable_session_system_prompt")]
8871 #[test]
8872 fn test_system_prompt_preserves_unknown_modes() {
8873 for mode in ["_vendor_mode", "prepend"] {
8874 let system_prompt_json =
8875 json!({ "mode": mode, "content": [{ "type": "text", "text": "..." }] });
8876 let system_prompt: SystemPrompt =
8877 serde_json::from_value(system_prompt_json.clone()).unwrap();
8878 assert_eq!(
8879 system_prompt.mode,
8880 SystemPromptMode::Other(mode.to_string())
8881 );
8882 assert_eq!(
8883 serde_json::to_value(&system_prompt).unwrap(),
8884 system_prompt_json
8885 );
8886 }
8887 }
8888
8889 #[cfg(feature = "unstable_session_system_prompt")]
8893 fn assert_system_prompt_is_strict<T>(base: &serde_json::Value)
8894 where
8895 T: serde::de::DeserializeOwned + PartialEq + std::fmt::Debug,
8896 {
8897 let name = std::any::type_name::<T>();
8898 let with = |system_prompt: serde_json::Value| {
8899 let mut request = base.clone();
8900 request["systemPrompt"] = system_prompt;
8901 serde_json::from_value::<T>(request)
8902 };
8903
8904 for system_prompt in [
8905 json!("You are a code review specialist."),
8906 json!({ "content": [{ "type": "text", "text": "..." }] }),
8907 json!({ "mode": null, "content": [{ "type": "text", "text": "..." }] }),
8908 json!({ "mode": 1, "content": [{ "type": "text", "text": "..." }] }),
8909 json!({ "mode": "append" }),
8910 json!({ "mode": "append", "content": "..." }),
8911 json!({ "mode": "append", "content": [{ "type": "text" }] }),
8912 ] {
8913 assert!(
8914 with(system_prompt.clone()).is_err(),
8915 "{name}.systemPrompt: {system_prompt}"
8916 );
8917 }
8918
8919 assert_eq!(
8920 with(serde_json::Value::Null).unwrap(),
8921 serde_json::from_value::<T>(base.clone()).unwrap(),
8922 "{name}.systemPrompt"
8923 );
8924 }
8925
8926 #[cfg(feature = "unstable_session_system_prompt")]
8927 #[test]
8928 fn test_session_setup_requests_reject_malformed_system_prompts() {
8929 assert!(
8930 serde_json::to_value(NewSessionRequest::new("/repo"))
8931 .unwrap()
8932 .get("systemPrompt")
8933 .is_none()
8934 );
8935 assert_system_prompt_is_strict::<NewSessionRequest>(&json!({ "cwd": "/repo" }));
8936 assert_system_prompt_is_strict::<ResumeSessionRequest>(&json!({
8937 "sessionId": "sess-1",
8938 "cwd": "/repo"
8939 }));
8940 #[cfg(feature = "unstable_session_fork")]
8941 assert_system_prompt_is_strict::<ForkSessionRequest>(&json!({
8942 "sessionId": "sess-1",
8943 "cwd": "/repo"
8944 }));
8945 }
8946
8947 #[cfg(feature = "unstable_session_system_prompt")]
8950 #[test]
8951 fn test_system_prompt_preserves_unknown_content_blocks() {
8952 let request: NewSessionRequest = serde_json::from_value(json!({
8953 "cwd": "/home/user/project",
8954 "systemPrompt": {
8955 "mode": "append",
8956 "content": [{ "type": "_vendor_video", "uri": "https://example.com/intro.mp4" }]
8957 }
8958 }))
8959 .unwrap();
8960 let content = request.system_prompt.unwrap().content;
8961 assert_eq!(content.len(), 1);
8962 assert!(matches!(content[0], ContentBlock::Other(_)));
8963 }
8964
8965 #[test]
8966 fn test_auth_method_terminal_serialization() {
8967 let method = AuthMethod::Terminal(AuthMethodTerminal::new("tui-auth", "Terminal Auth"));
8968
8969 let json = serde_json::to_value(&method).unwrap();
8970 assert_eq!(
8971 json,
8972 json!({
8973 "methodId": "tui-auth",
8974 "name": "Terminal Auth",
8975 "type": "terminal"
8976 })
8977 );
8978 assert!(!json.as_object().unwrap().contains_key("args"));
8980 assert!(!json.as_object().unwrap().contains_key("env"));
8981
8982 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
8983 match deserialized {
8984 AuthMethod::Terminal(AuthMethodTerminal { args, env, .. }) => {
8985 assert_eq!(args, Vec::<String>::new());
8986 assert_eq!(env, []);
8987 }
8988 _ => panic!("Expected Terminal variant"),
8989 }
8990 }
8991
8992 #[test]
8993 fn test_auth_method_terminal_with_args_and_env_serialization() {
8994 let method = AuthMethod::Terminal(
8995 AuthMethodTerminal::new("tui-auth", "Terminal Auth")
8996 .args(vec!["--interactive".to_string(), "--color".to_string()])
8997 .env(vec![EnvVariable::new("TERM", "xterm-256color")]),
8998 );
8999
9000 let json = serde_json::to_value(&method).unwrap();
9001 assert_eq!(
9002 json,
9003 json!({
9004 "methodId": "tui-auth",
9005 "name": "Terminal Auth",
9006 "type": "terminal",
9007 "args": ["--interactive", "--color"],
9008 "env": [
9009 {
9010 "name": "TERM",
9011 "value": "xterm-256color"
9012 }
9013 ]
9014 })
9015 );
9016
9017 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
9018 match deserialized {
9019 AuthMethod::Terminal(AuthMethodTerminal { args, env, .. }) => {
9020 assert_eq!(args, vec!["--interactive", "--color"]);
9021 assert_eq!(env.len(), 1);
9022 assert_eq!(env[0].name, "TERM");
9023 assert_eq!(env[0].value, "xterm-256color");
9024 }
9025 _ => panic!("Expected Terminal variant"),
9026 }
9027 }
9028
9029 #[test]
9030 fn test_session_config_option_id_serialize() {
9031 let val = SessionConfigOptionValue::id("model-1");
9032 let json = serde_json::to_value(&val).unwrap();
9033 assert_eq!(json, json!({ "type": "id", "value": "model-1" }));
9034 }
9035
9036 #[test]
9037 fn test_session_config_option_value_boolean_serialize() {
9038 let val = SessionConfigOptionValue::boolean(true);
9039 let json = serde_json::to_value(&val).unwrap();
9040 assert_eq!(json, json!({ "type": "boolean", "value": true }));
9041 }
9042
9043 #[test]
9044 fn test_session_config_option_value_deserialize_id() {
9045 let json = json!({ "type": "id", "value": "model-1" });
9046 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
9047 assert_eq!(val, SessionConfigOptionValue::id("model-1"));
9048 assert_eq!(val.as_id().unwrap().to_string(), "model-1");
9049 }
9050
9051 #[test]
9052 fn test_session_config_option_value_deserialize_requires_type() {
9053 let json = json!({ "value": "model-1" });
9054 let result = serde_json::from_value::<SessionConfigOptionValue>(json);
9055 assert!(result.is_err());
9056 }
9057
9058 #[test]
9059 fn test_session_config_option_value_deserialize_boolean() {
9060 let json = json!({ "type": "boolean", "value": true });
9061 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
9062 assert_eq!(val, SessionConfigOptionValue::boolean(true));
9063 assert_eq!(val.as_bool(), Some(true));
9064 }
9065
9066 #[test]
9067 fn test_session_config_option_value_deserialize_boolean_false() {
9068 let json = json!({ "type": "boolean", "value": false });
9069 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
9070 assert_eq!(val, SessionConfigOptionValue::boolean(false));
9071 assert_eq!(val.as_bool(), Some(false));
9072 }
9073
9074 #[test]
9075 fn test_session_config_option_value_deserialize_unknown_type_with_string_value() {
9076 let json = json!({
9077 "type": "text",
9078 "value": "freeform input",
9079 "maxLength": 200
9080 });
9081 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
9082 let SessionConfigOptionValue::Other(unknown) = val else {
9083 panic!("Expected Other variant");
9084 };
9085 assert_eq!(unknown.type_, "text");
9086 assert_eq!(unknown.value, json!("freeform input"));
9087 assert_eq!(unknown.fields["maxLength"], json!(200));
9088 }
9089
9090 #[test]
9091 fn test_session_config_option_value_deserialize_unknown_type_with_object_value() {
9092 let json = json!({
9093 "type": "range",
9094 "value": { "min": 1, "max": 5 }
9095 });
9096 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
9097 let SessionConfigOptionValue::Other(unknown) = val else {
9098 panic!("Expected Other variant");
9099 };
9100 assert_eq!(unknown.type_, "range");
9101 assert_eq!(unknown.value, json!({ "min": 1, "max": 5 }));
9102 }
9103
9104 #[test]
9105 fn test_session_config_option_value_roundtrip_id() {
9106 let original = SessionConfigOptionValue::id("option-a");
9107 let json = serde_json::to_value(&original).unwrap();
9108 let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
9109 assert_eq!(original, roundtripped);
9110 }
9111
9112 #[test]
9113 fn test_session_config_option_value_roundtrip_boolean() {
9114 let original = SessionConfigOptionValue::boolean(false);
9115 let json = serde_json::to_value(&original).unwrap();
9116 let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
9117 assert_eq!(original, roundtripped);
9118 }
9119
9120 #[test]
9121 fn test_session_config_option_value_roundtrip_other() {
9122 let mut fields = BTreeMap::new();
9123 fields.insert("maxLength".to_string(), json!(200));
9124 let original = SessionConfigOptionValue::Other(OtherSessionConfigOptionValue::new(
9125 "text",
9126 json!("freeform input"),
9127 fields,
9128 ));
9129 let json = serde_json::to_value(&original).unwrap();
9130 let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
9131 assert_eq!(original, roundtripped);
9132 }
9133
9134 #[test]
9135 fn test_session_config_option_value_type_mismatch_boolean_with_string() {
9136 let json = json!({ "type": "boolean", "value": "not a bool" });
9137 let result = serde_json::from_value::<SessionConfigOptionValue>(json);
9138 assert!(result.is_err());
9139 }
9140
9141 #[test]
9142 fn test_session_config_option_value_from_impls() {
9143 let from_str: SessionConfigOptionValue = "model-1".into();
9144 assert_eq!(from_str.as_id().unwrap().to_string(), "model-1");
9145
9146 let from_id: SessionConfigOptionValue = SessionConfigValueId::new("model-2").into();
9147 assert_eq!(from_id.as_id().unwrap().to_string(), "model-2");
9148
9149 let from_bool: SessionConfigOptionValue = true.into();
9150 assert_eq!(from_bool.as_bool(), Some(true));
9151 }
9152
9153 #[test]
9154 fn test_set_session_config_option_request_id() {
9155 let req = SetSessionConfigOptionRequest::new("sess_1", "model", "model-1");
9156 let json = serde_json::to_value(&req).unwrap();
9157 assert_eq!(
9158 json,
9159 json!({
9160 "sessionId": "sess_1",
9161 "configId": "model",
9162 "type": "id",
9163 "value": "model-1"
9164 })
9165 );
9166 }
9167
9168 #[test]
9169 fn test_set_session_config_option_request_boolean() {
9170 let req = SetSessionConfigOptionRequest::new("sess_1", "brave_mode", true);
9171 let json = serde_json::to_value(&req).unwrap();
9172 assert_eq!(
9173 json,
9174 json!({
9175 "sessionId": "sess_1",
9176 "configId": "brave_mode",
9177 "type": "boolean",
9178 "value": true
9179 })
9180 );
9181 }
9182
9183 #[test]
9184 fn test_set_session_config_option_request_deserialize_requires_type() {
9185 let json = json!({
9186 "sessionId": "sess_1",
9187 "configId": "model",
9188 "value": "model-1"
9189 });
9190 let result = serde_json::from_value::<SetSessionConfigOptionRequest>(json);
9191 assert!(result.is_err());
9192 }
9193
9194 #[test]
9195 fn test_set_session_config_option_request_deserialize_boolean() {
9196 let json = json!({
9197 "sessionId": "sess_1",
9198 "configId": "brave_mode",
9199 "type": "boolean",
9200 "value": true
9201 });
9202 let req: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
9203 assert_eq!(req.value.as_bool(), Some(true));
9204 }
9205
9206 #[test]
9207 fn test_set_session_config_option_request_roundtrip_id() {
9208 let original = SetSessionConfigOptionRequest::new("s", "c", "v");
9209 let json = serde_json::to_value(&original).unwrap();
9210 let roundtripped: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
9211 assert_eq!(original, roundtripped);
9212 }
9213
9214 #[test]
9215 fn test_set_session_config_option_request_roundtrip_boolean() {
9216 let original = SetSessionConfigOptionRequest::new("s", "c", false);
9217 let json = serde_json::to_value(&original).unwrap();
9218 let roundtripped: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
9219 assert_eq!(original, roundtripped);
9220 }
9221
9222 #[test]
9223 fn test_session_config_boolean_serialization() {
9224 let cfg = SessionConfigBoolean::new(true);
9225 let json = serde_json::to_value(&cfg).unwrap();
9226 assert_eq!(json, json!({ "currentValue": true }));
9227
9228 let deserialized: SessionConfigBoolean = serde_json::from_value(json).unwrap();
9229 assert!(deserialized.current_value);
9230 }
9231
9232 #[test]
9233 fn test_session_config_option_boolean_variant() {
9234 let opt = SessionConfigOption::boolean("brave_mode", "Brave Mode", false)
9235 .description("Skip confirmation prompts")
9236 .meta(test_meta());
9237 assert_eq!(serialized_meta_key_count(&opt), 1);
9238
9239 let json = serde_json::to_value(&opt).unwrap();
9240 assert_eq!(
9241 json,
9242 json!({
9243 "configId": "brave_mode",
9244 "name": "Brave Mode",
9245 "description": "Skip confirmation prompts",
9246 "type": "boolean",
9247 "currentValue": false,
9248 "_meta": {
9249 "source": "test"
9250 }
9251 })
9252 );
9253
9254 let deserialized: SessionConfigOption = serde_json::from_value(json).unwrap();
9255 assert_eq!(deserialized.config_id.to_string(), "brave_mode");
9256 assert_eq!(deserialized.name, "Brave Mode");
9257 match deserialized.kind {
9258 SessionConfigKind::Boolean(ref b) => assert!(!b.current_value),
9259 _ => panic!("Expected Boolean kind"),
9260 }
9261 }
9262
9263 #[test]
9264 fn test_session_config_option_select_still_works() {
9265 let opt = SessionConfigOption::select(
9267 "model",
9268 "Model",
9269 "model-1",
9270 vec![
9271 SessionConfigSelectOption::new("model-1", "Model 1"),
9272 SessionConfigSelectOption::new("model-2", "Model 2"),
9273 ],
9274 )
9275 .meta(test_meta());
9276 assert_eq!(serialized_meta_key_count(&opt), 1);
9277
9278 let json = serde_json::to_value(&opt).unwrap();
9279 assert_eq!(json["type"], "select");
9280 assert_eq!(json["currentValue"], "model-1");
9281 assert_eq!(json["options"].as_array().unwrap().len(), 2);
9282 assert_eq!(json["_meta"]["source"], "test");
9283
9284 let deserialized: SessionConfigOption = serde_json::from_value(json).unwrap();
9285 match deserialized.kind {
9286 SessionConfigKind::Select(ref s) => {
9287 assert_eq!(s.current_value.to_string(), "model-1");
9288 }
9289 _ => panic!("Expected Select kind"),
9290 }
9291 }
9292
9293 #[test]
9294 fn test_session_config_option_unknown_kind_roundtrip() {
9295 let option: SessionConfigOption = serde_json::from_value(json!({
9296 "configId": "verbosity",
9297 "name": "Verbosity",
9298 "type": "_slider",
9299 "currentValue": 3,
9300 "min": 0,
9301 "max": 5,
9302 "_meta": {
9303 "source": "test"
9304 }
9305 }))
9306 .unwrap();
9307
9308 assert_eq!(option.config_id.to_string(), "verbosity");
9309 assert_eq!(option.meta.as_ref().unwrap()["source"], "test");
9310 let SessionConfigKind::Other(unknown) = &option.kind else {
9311 panic!("expected unknown config kind");
9312 };
9313 assert_eq!(unknown.type_, "_slider");
9314 assert_eq!(unknown.fields.get("currentValue"), Some(&json!(3)));
9315 assert!(!unknown.fields.contains_key("_meta"));
9316 assert_eq!(serialized_meta_key_count(&option), 1);
9317
9318 let json = serde_json::to_value(&option).unwrap();
9319 assert_eq!(json["type"], "_slider");
9320 assert_eq!(json["currentValue"], 3);
9321 assert_eq!(json["min"], 0);
9322 assert_eq!(json["max"], 5);
9323 assert_eq!(json["_meta"]["source"], "test");
9324 }
9325
9326 #[test]
9327 fn test_session_config_option_unknown_kind_does_not_duplicate_flattened_meta() {
9328 let mut fields = std::collections::BTreeMap::new();
9329 fields.insert("currentValue".to_string(), json!(3));
9330 fields.insert("_meta".to_string(), json!({ "inner": "ignored" }));
9331
9332 let option = SessionConfigOption::new(
9333 "verbosity",
9334 "Verbosity",
9335 SessionConfigKind::Other(OtherSessionConfigKind::new("_slider", fields)),
9336 )
9337 .meta(test_meta());
9338
9339 let SessionConfigKind::Other(unknown) = &option.kind else {
9340 panic!("expected unknown config kind");
9341 };
9342 assert!(!unknown.fields.contains_key("_meta"));
9343 assert_eq!(serialized_meta_key_count(&option), 1);
9344
9345 let json = serde_json::to_value(&option).unwrap();
9346 assert_eq!(json["type"], "_slider");
9347 assert_eq!(json["currentValue"], 3);
9348 assert_eq!(json["_meta"]["source"], "test");
9349 }
9350
9351 #[test]
9352 fn test_session_config_option_unknown_does_not_hide_malformed_known_kind() {
9353 assert!(
9354 serde_json::from_value::<SessionConfigOption>(json!({
9355 "configId": "model",
9356 "name": "Model",
9357 "type": "select"
9358 }))
9359 .is_err()
9360 );
9361 }
9362
9363 #[cfg(feature = "unstable_llm_providers")]
9364 #[test]
9365 fn test_llm_protocol_known_variants() {
9366 assert_eq!(
9367 serde_json::to_value(&LlmProtocol::Anthropic).unwrap(),
9368 json!("anthropic")
9369 );
9370 assert_eq!(
9371 serde_json::to_value(&LlmProtocol::OpenAi).unwrap(),
9372 json!("openai")
9373 );
9374 assert_eq!(
9375 serde_json::to_value(&LlmProtocol::Azure).unwrap(),
9376 json!("azure")
9377 );
9378 assert_eq!(
9379 serde_json::to_value(&LlmProtocol::Vertex).unwrap(),
9380 json!("vertex")
9381 );
9382 assert_eq!(
9383 serde_json::to_value(&LlmProtocol::Bedrock).unwrap(),
9384 json!("bedrock")
9385 );
9386
9387 assert_eq!(
9388 serde_json::from_str::<LlmProtocol>("\"anthropic\"").unwrap(),
9389 LlmProtocol::Anthropic
9390 );
9391 assert_eq!(
9392 serde_json::from_str::<LlmProtocol>("\"openai\"").unwrap(),
9393 LlmProtocol::OpenAi
9394 );
9395 assert_eq!(
9396 serde_json::from_str::<LlmProtocol>("\"azure\"").unwrap(),
9397 LlmProtocol::Azure
9398 );
9399 assert_eq!(
9400 serde_json::from_str::<LlmProtocol>("\"vertex\"").unwrap(),
9401 LlmProtocol::Vertex
9402 );
9403 assert_eq!(
9404 serde_json::from_str::<LlmProtocol>("\"bedrock\"").unwrap(),
9405 LlmProtocol::Bedrock
9406 );
9407 }
9408
9409 #[cfg(feature = "unstable_llm_providers")]
9410 #[test]
9411 fn test_llm_protocol_unknown_variant() {
9412 let unknown: LlmProtocol = serde_json::from_str("\"cohere\"").unwrap();
9413 assert_eq!(unknown, LlmProtocol::Other("cohere".to_string()));
9414
9415 let json = serde_json::to_value(&unknown).unwrap();
9416 assert_eq!(json, json!("cohere"));
9417 }
9418
9419 #[cfg(feature = "unstable_llm_providers")]
9420 #[test]
9421 fn test_provider_current_config_serialization() {
9422 let config =
9423 ProviderCurrentConfig::new(LlmProtocol::Anthropic, "https://api.anthropic.com");
9424
9425 let json = serde_json::to_value(&config).unwrap();
9426 assert_eq!(
9427 json,
9428 json!({
9429 "apiType": "anthropic",
9430 "baseUrl": "https://api.anthropic.com"
9431 })
9432 );
9433
9434 let deserialized: ProviderCurrentConfig = serde_json::from_value(json).unwrap();
9435 assert_eq!(deserialized.api_type, LlmProtocol::Anthropic);
9436 assert_eq!(deserialized.base_url, "https://api.anthropic.com");
9437 }
9438
9439 #[cfg(feature = "unstable_llm_providers")]
9440 #[test]
9441 fn test_provider_info_with_current_config() {
9442 let info = ProviderInfo::new(
9443 "main",
9444 vec![LlmProtocol::Anthropic, LlmProtocol::OpenAi],
9445 true,
9446 Some(ProviderCurrentConfig::new(
9447 LlmProtocol::Anthropic,
9448 "https://api.anthropic.com",
9449 )),
9450 );
9451
9452 let json = serde_json::to_value(&info).unwrap();
9453 assert_eq!(
9454 json,
9455 json!({
9456 "providerId": "main",
9457 "supported": ["anthropic", "openai"],
9458 "required": true,
9459 "current": {
9460 "apiType": "anthropic",
9461 "baseUrl": "https://api.anthropic.com"
9462 }
9463 })
9464 );
9465
9466 let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
9467 assert_eq!(deserialized.provider_id.to_string(), "main");
9468 assert_eq!(deserialized.supported.len(), 2);
9469 assert!(deserialized.required);
9470 assert!(deserialized.current.is_some());
9471 assert_eq!(
9472 deserialized.current.as_ref().unwrap().api_type,
9473 LlmProtocol::Anthropic
9474 );
9475 }
9476
9477 #[cfg(feature = "unstable_llm_providers")]
9478 #[test]
9479 fn test_provider_info_disabled() {
9480 let info = ProviderInfo::new(
9481 "secondary",
9482 vec![LlmProtocol::OpenAi],
9483 false,
9484 None::<ProviderCurrentConfig>,
9485 );
9486
9487 let json = serde_json::to_value(&info).unwrap();
9488 assert_eq!(
9489 json,
9490 json!({
9491 "providerId": "secondary",
9492 "supported": ["openai"],
9493 "required": false
9494 })
9495 );
9496
9497 let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
9498 assert_eq!(deserialized.provider_id.to_string(), "secondary");
9499 assert!(!deserialized.required);
9500 assert!(deserialized.current.is_none());
9501 }
9502
9503 #[cfg(feature = "unstable_llm_providers")]
9504 #[test]
9505 fn test_provider_info_missing_current_defaults_to_none() {
9506 let json = json!({
9508 "providerId": "main",
9509 "supported": ["anthropic"],
9510 "required": true
9511 });
9512 let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
9513 assert!(deserialized.current.is_none());
9514 }
9515
9516 #[cfg(feature = "unstable_llm_providers")]
9517 #[test]
9518 fn test_provider_info_explicit_null_current_decodes_to_none() {
9519 let json = json!({
9523 "providerId": "main",
9524 "supported": ["anthropic"],
9525 "required": true,
9526 "current": null
9527 });
9528 let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
9529 assert!(deserialized.current.is_none());
9530 }
9531
9532 #[cfg(feature = "unstable_llm_providers")]
9533 #[test]
9534 fn test_list_providers_response_serialization() {
9535 let response = ListProvidersResponse::new(vec![ProviderInfo::new(
9536 "main",
9537 vec![LlmProtocol::Anthropic],
9538 true,
9539 Some(ProviderCurrentConfig::new(
9540 LlmProtocol::Anthropic,
9541 "https://api.anthropic.com",
9542 )),
9543 )]);
9544
9545 let json = serde_json::to_value(&response).unwrap();
9546 assert_eq!(json["providers"].as_array().unwrap().len(), 1);
9547 assert_eq!(json["providers"][0]["providerId"], "main");
9548
9549 let deserialized: ListProvidersResponse = serde_json::from_value(json).unwrap();
9550 assert_eq!(deserialized.providers.len(), 1);
9551 }
9552
9553 #[cfg(feature = "unstable_llm_providers")]
9554 #[test]
9555 fn test_set_provider_request_serialization() {
9556 use std::collections::HashMap;
9557
9558 let mut headers = HashMap::new();
9559 headers.insert("Authorization".to_string(), "Bearer sk-test".to_string());
9560
9561 let request =
9562 SetProviderRequest::new("main", LlmProtocol::OpenAi, "https://api.openai.com/v1")
9563 .headers(headers);
9564
9565 let json = serde_json::to_value(&request).unwrap();
9566 assert_eq!(
9567 json,
9568 json!({
9569 "providerId": "main",
9570 "apiType": "openai",
9571 "baseUrl": "https://api.openai.com/v1",
9572 "headers": {
9573 "Authorization": "Bearer sk-test"
9574 }
9575 })
9576 );
9577
9578 let deserialized: SetProviderRequest = serde_json::from_value(json).unwrap();
9579 assert_eq!(deserialized.provider_id.to_string(), "main");
9580 assert_eq!(deserialized.api_type, LlmProtocol::OpenAi);
9581 assert_eq!(deserialized.base_url, "https://api.openai.com/v1");
9582 assert_eq!(deserialized.headers.len(), 1);
9583 assert_eq!(
9584 deserialized.headers.get("Authorization").unwrap(),
9585 "Bearer sk-test"
9586 );
9587 }
9588
9589 #[cfg(feature = "unstable_llm_providers")]
9590 #[test]
9591 fn test_set_provider_request_omits_empty_headers() {
9592 let request =
9593 SetProviderRequest::new("main", LlmProtocol::Anthropic, "https://api.anthropic.com");
9594
9595 let json = serde_json::to_value(&request).unwrap();
9596 assert!(!json.as_object().unwrap().contains_key("headers"));
9598 }
9599
9600 #[cfg(feature = "unstable_llm_providers")]
9601 #[test]
9602 fn test_disable_provider_request_serialization() {
9603 let request = DisableProviderRequest::new("secondary");
9604
9605 let json = serde_json::to_value(&request).unwrap();
9606 assert_eq!(json, json!({ "providerId": "secondary" }));
9607
9608 let deserialized: DisableProviderRequest = serde_json::from_value(json).unwrap();
9609 assert_eq!(deserialized.provider_id.to_string(), "secondary");
9610 }
9611
9612 #[cfg(feature = "unstable_llm_providers")]
9613 #[test]
9614 fn test_providers_capabilities_serialization() {
9615 let caps = ProvidersCapabilities::new();
9616
9617 let json = serde_json::to_value(&caps).unwrap();
9618 assert_eq!(json, json!({}));
9619
9620 let deserialized: ProvidersCapabilities = serde_json::from_value(json).unwrap();
9621 assert!(deserialized.meta.is_none());
9622 }
9623
9624 #[cfg(feature = "unstable_llm_providers")]
9625 #[test]
9626 fn test_agent_capabilities_with_providers() {
9627 let caps = AgentCapabilities::new().providers(ProvidersCapabilities::new());
9628
9629 let json = serde_json::to_value(&caps).unwrap();
9630 assert_eq!(json["providers"], json!({}));
9631
9632 let deserialized: AgentCapabilities = serde_json::from_value(json).unwrap();
9633 assert!(deserialized.providers.is_some());
9634 }
9635
9636 #[test]
9637 fn test_agent_capabilities_session_is_explicit() {
9638 let json = serde_json::to_value(AgentCapabilities::new()).unwrap();
9639 assert!(json.get("session").is_none());
9640
9641 let caps = AgentCapabilities::new().session(
9642 SessionCapabilities::new()
9643 .prompt(PromptCapabilities::new().image(PromptImageCapabilities::new()))
9644 .mcp(McpCapabilities::new().stdio(McpStdioCapabilities::new())),
9645 );
9646
9647 assert_eq!(
9648 serde_json::to_value(&caps).unwrap(),
9649 json!({
9650 "session": {
9651 "prompt": {
9652 "image": {}
9653 },
9654 "mcp": {
9655 "stdio": {}
9656 }
9657 }
9658 })
9659 );
9660
9661 let deserialized: AgentCapabilities = serde_json::from_value(json!({
9662 "session": false
9663 }))
9664 .unwrap();
9665 assert!(deserialized.session.is_none());
9666 }
9667
9668 #[test]
9669 fn test_prompt_capabilities_serialize_supported_content_as_objects() {
9670 let caps = PromptCapabilities::new()
9671 .image(PromptImageCapabilities::new())
9672 .audio(PromptAudioCapabilities::new())
9673 .embedded_context(PromptEmbeddedContextCapabilities::new());
9674
9675 assert_eq!(
9676 serde_json::to_value(&caps).unwrap(),
9677 json!({
9678 "image": {},
9679 "audio": {},
9680 "embeddedContext": {}
9681 })
9682 );
9683
9684 let deserialized: PromptCapabilities = serde_json::from_value(json!({
9685 "image": null,
9686 "audio": false,
9687 "embeddedContext": {}
9688 }))
9689 .unwrap();
9690 assert!(deserialized.image.is_none());
9691 assert!(deserialized.audio.is_none());
9692 assert!(deserialized.embedded_context.is_some());
9693 }
9694
9695 #[test]
9696 fn test_mcp_capabilities_serialize_supported_transports_as_objects() {
9697 let caps = McpCapabilities::new()
9698 .stdio(McpStdioCapabilities::new())
9699 .http(McpHttpCapabilities::new());
9700
9701 assert_eq!(
9702 serde_json::to_value(&caps).unwrap(),
9703 json!({
9704 "stdio": {},
9705 "http": {}
9706 })
9707 );
9708
9709 let deserialized: McpCapabilities = serde_json::from_value(json!({
9710 "stdio": null,
9711 "http": false
9712 }))
9713 .unwrap();
9714 assert!(deserialized.stdio.is_none());
9715 assert!(deserialized.http.is_none());
9716 }
9717
9718 #[cfg(feature = "unstable_mcp_over_acp")]
9719 #[test]
9720 fn test_mcp_capabilities_serialize_acp_support_as_object() {
9721 let caps = McpCapabilities::new().acp(McpAcpCapabilities::new());
9722
9723 assert_eq!(
9724 serde_json::to_value(&caps).unwrap(),
9725 json!({
9726 "acp": {}
9727 })
9728 );
9729 }
9730
9731 #[test]
9732 fn prompt_request_rejects_malformed_content_block() {
9733 use serde_json::json;
9734
9735 assert!(
9736 serde_json::from_value::<PromptRequest>(json!({
9737 "sessionId": "sess-1",
9738 "prompt": [{"type": "text"}]
9739 }))
9740 .is_err()
9741 );
9742 }
9743
9744 #[test]
9745 fn prompt_request_rejects_non_array_prompt() {
9746 use serde_json::json;
9747
9748 assert!(
9749 serde_json::from_value::<PromptRequest>(json!({
9750 "sessionId": "sess-1",
9751 "prompt": "hello"
9752 }))
9753 .is_err()
9754 );
9755 }
9756
9757 fn assert_session_setup_lists_are_strict<T>(base: &serde_json::Value)
9758 where
9759 T: serde::de::DeserializeOwned + PartialEq + std::fmt::Debug,
9760 {
9761 let name = std::any::type_name::<T>();
9762 let with = |field: &str, value: serde_json::Value| {
9763 let mut request = base.clone();
9764 request[field] = value;
9765 serde_json::from_value::<T>(request)
9766 };
9767
9768 for (field, value) in [
9769 (
9771 "mcpServers",
9772 json!([
9773 {"type": "stdio", "name": "github", "command": "/usr/bin/gh-mcp", "env": [{"name": "GITHUB_TOKEN", "value": null}]},
9774 {"type": "stdio", "name": "fs", "command": "/usr/bin/fs-mcp"}
9775 ]),
9776 ),
9777 ("mcpServers", json!({"type": "stdio", "name": "fs"})),
9778 ("additionalDirectories", json!(["/repo/lib", 42])),
9779 ("additionalDirectories", json!("/repo/lib")),
9780 ] {
9781 assert!(
9782 with(field, value.clone()).is_err(),
9783 "{name}.{field}: {value}"
9784 );
9785 }
9786
9787 let empty = serde_json::from_value::<T>(base.clone()).unwrap();
9788 for field in ["mcpServers", "additionalDirectories"] {
9789 assert_eq!(
9790 with(field, serde_json::Value::Null).unwrap(),
9791 empty,
9792 "{name}.{field}"
9793 );
9794 }
9795 }
9796
9797 #[test]
9798 fn session_setup_requests_reject_malformed_mcp_servers_and_directories() {
9799 assert_session_setup_lists_are_strict::<NewSessionRequest>(&json!({"cwd": "/repo"}));
9800 assert_session_setup_lists_are_strict::<ResumeSessionRequest>(&json!({
9801 "sessionId": "sess-1",
9802 "cwd": "/repo"
9803 }));
9804 #[cfg(feature = "unstable_session_fork")]
9805 assert_session_setup_lists_are_strict::<ForkSessionRequest>(&json!({
9806 "sessionId": "sess-1",
9807 "cwd": "/repo"
9808 }));
9809 }
9810
9811 #[test]
9812 fn resume_session_request_rejects_malformed_replay_from() {
9813 let request = |replay_from: serde_json::Value| {
9814 serde_json::from_value::<ResumeSessionRequest>(json!({
9815 "sessionId": "sess-1",
9816 "cwd": "/repo",
9817 "replayFrom": replay_from
9818 }))
9819 };
9820
9821 assert!(request(json!("start")).is_err());
9823 assert_eq!(request(serde_json::Value::Null).unwrap().replay_from, None);
9824 assert!(matches!(
9825 request(json!({"type": "start"})).unwrap().replay_from,
9826 Some(ReplayFrom::Start(_))
9827 ));
9828 }
9829
9830 #[test]
9831 fn mcp_server_lists_treat_null_as_empty() {
9832 let servers: Vec<McpServer> = serde_json::from_value(json!([
9833 {"type": "stdio", "name": "fs", "command": "/usr/bin/fs-mcp", "args": null, "env": null},
9834 {"type": "http", "name": "api", "url": "https://example.com/mcp", "headers": null}
9835 ]))
9836 .unwrap();
9837
9838 assert_eq!(
9839 servers,
9840 vec![
9841 McpServer::Stdio(McpServerStdio::new("fs", "/usr/bin/fs-mcp")),
9842 McpServer::Http(McpServerHttp::new("api", "https://example.com/mcp")),
9843 ]
9844 );
9845 }
9846
9847 #[test]
9848 fn terminal_auth_methods_reject_malformed_launch_config() {
9849 let response: InitializeResponse = serde_json::from_value(json!({
9853 "protocolVersion": 2,
9854 "info": {"name": "agent", "version": "1.0.0"},
9855 "authMethods": [
9856 {"type": "terminal", "methodId": "bad-args", "name": "Log in", "args": ["auth", "--port", 8123]},
9857 {"type": "terminal", "methodId": "bad-env", "name": "Log in", "env": [{"name": "DEBUG", "value": 1}]},
9858 {"type": "terminal", "methodId": "null-config", "name": "Log in", "args": null, "env": null},
9859 {"type": "agent", "methodId": "agent", "name": "Agent login"}
9860 ]
9861 }))
9862 .unwrap();
9863
9864 assert_eq!(
9865 response.auth_methods,
9866 vec![
9867 AuthMethod::Terminal(AuthMethodTerminal::new("null-config", "Log in")),
9868 AuthMethod::Agent(AuthMethodAgent::new("agent", "Agent login")),
9869 ]
9870 );
9871 }
9872
9873 #[cfg(feature = "unstable_auth_status")]
9874 #[test]
9875 fn auth_status_response_round_trips() {
9876 let response = AuthStatusResponse::new(AuthState::Authenticated).accounts(vec![
9877 AuthAccount::new("Anthropic API key").active(true),
9878 AuthAccount::new("Claude Max").email("user@example.com".to_string()),
9879 ]);
9880 let json = serde_json::to_value(&response).unwrap();
9881 assert_eq!(
9882 json,
9883 json!({
9884 "state": "authenticated",
9885 "accounts": [
9886 {
9887 "label": "Anthropic API key",
9888 "active": true
9889 },
9890 {
9891 "label": "Claude Max",
9892 "email": "user@example.com"
9893 }
9894 ]
9895 })
9896 );
9897 assert_eq!(
9898 serde_json::from_value::<AuthStatusResponse>(json).unwrap(),
9899 response
9900 );
9901 assert_eq!(
9902 serde_json::to_value(AuthStatusResponse::new(AuthState::NotRequired)).unwrap(),
9903 json!({"state": "not_required"})
9904 );
9905 }
9906
9907 #[cfg(feature = "unstable_auth_status")]
9908 #[test]
9909 fn auth_status_state_is_strict_and_accounts_are_lenient() {
9910 assert!(serde_json::from_value::<AuthStatusResponse>(json!({})).is_err());
9911 assert!(serde_json::from_value::<AuthStatusResponse>(json!({"state": null})).is_err());
9912 assert!(serde_json::from_value::<AuthStatusResponse>(json!({"state": 1})).is_err());
9913
9914 let response: AuthStatusResponse = serde_json::from_value(json!({
9915 "state": "_vendor_state",
9916 "accounts": [
9917 {"active": true},
9918 {
9919 "label": "Vertex AI",
9920 "active": "yes",
9921 "email": ["not", "a", "string"]
9922 }
9923 ]
9924 }))
9925 .unwrap();
9926 assert_eq!(
9927 response,
9928 AuthStatusResponse::new(AuthState::Other("_vendor_state".to_string()))
9929 .accounts(vec![AuthAccount::new("Vertex AI")])
9930 );
9931 assert_eq!(
9932 serde_json::from_value::<AuthStatusResponse>(
9933 json!({"state": "unknown", "accounts": null})
9934 )
9935 .unwrap(),
9936 AuthStatusResponse::new(AuthState::Unknown)
9937 );
9938 }
9939
9940 #[cfg(feature = "unstable_auth_status")]
9941 #[test]
9942 fn auth_status_update_notification_routes() {
9943 use crate::v2::{AgentNotification, CLIENT_METHOD_NAMES};
9944
9945 assert_eq!(AGENT_METHOD_NAMES.auth_status, "auth/status");
9946 assert_eq!(CLIENT_METHOD_NAMES.auth_status_update, "auth/status_update");
9947 let notification = AgentNotification::AuthStatusUpdateNotification(Box::new(
9948 AuthStatusUpdateNotification::new(AuthState::Unauthenticated),
9949 ));
9950 assert_eq!(notification.method(), "auth/status_update");
9951 assert_eq!(
9952 serde_json::to_value(¬ification).unwrap(),
9953 json!({"state": "unauthenticated"})
9954 );
9955 }
9956}