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")]
207 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
208 #[serde(default)]
209 pub title: Option<String>,
210 pub version: String,
213 #[serde_as(deserialize_as = "DefaultOnError")]
219 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
220 #[serde(default)]
221 #[serde(rename = "_meta")]
222 pub meta: Option<Meta>,
223}
224
225impl Implementation {
226 #[must_use]
228 pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
229 Self {
230 name: name.into(),
231 title: None,
232 version: version.into(),
233 meta: None,
234 }
235 }
236
237 #[must_use]
241 pub fn title(mut self, title: impl IntoOption<String>) -> Self {
242 self.title = title.into_option();
243 self
244 }
245
246 #[must_use]
252 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
253 self.meta = meta.into_option();
254 self
255 }
256}
257
258#[serde_as]
268#[skip_serializing_none]
269#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
270#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
271#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGIN_METHOD_NAME)))]
272#[serde(rename_all = "camelCase")]
273#[non_exhaustive]
274pub struct LoginAuthRequest {
275 pub method_id: AuthMethodId,
278 #[serde_as(deserialize_as = "DefaultOnError")]
284 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
285 #[serde(default)]
286 #[serde(rename = "_meta")]
287 pub meta: Option<Meta>,
288}
289
290impl LoginAuthRequest {
291 #[must_use]
293 pub fn new(method_id: impl Into<AuthMethodId>) -> Self {
294 Self {
295 method_id: method_id.into(),
296 meta: None,
297 }
298 }
299
300 #[must_use]
306 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
307 self.meta = meta.into_option();
308 self
309 }
310}
311
312crate::serde_util::default_on_null! {
313 #[serde_as]
315 #[skip_serializing_none]
316 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
317 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
318 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGIN_METHOD_NAME)))]
319 #[serde(rename_all = "camelCase")]
320 #[non_exhaustive]
321 pub struct LoginAuthResponse {
322 #[serde_as(deserialize_as = "DefaultOnError")]
328 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
329 #[serde(default)]
330 #[serde(rename = "_meta")]
331 pub meta: Option<Meta>,
332 }
333}
334
335impl LoginAuthResponse {
336 #[must_use]
338 pub fn new() -> Self {
339 Self::default()
340 }
341
342 #[must_use]
348 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
349 self.meta = meta.into_option();
350 self
351 }
352}
353
354crate::serde_util::default_on_null! {
357 #[serde_as]
365 #[skip_serializing_none]
366 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
367 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
368 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGOUT_METHOD_NAME)))]
369 #[serde(rename_all = "camelCase")]
370 #[non_exhaustive]
371 pub struct LogoutAuthRequest {
372 #[serde_as(deserialize_as = "DefaultOnError")]
378 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
379 #[serde(default)]
380 #[serde(rename = "_meta")]
381 pub meta: Option<Meta>,
382 }
383}
384
385impl LogoutAuthRequest {
386 #[must_use]
388 pub fn new() -> Self {
389 Self::default()
390 }
391
392 #[must_use]
398 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
399 self.meta = meta.into_option();
400 self
401 }
402}
403
404crate::serde_util::default_on_null! {
405 #[serde_as]
407 #[skip_serializing_none]
408 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
409 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
410 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGOUT_METHOD_NAME)))]
411 #[serde(rename_all = "camelCase")]
412 #[non_exhaustive]
413 pub struct LogoutAuthResponse {
414 #[serde_as(deserialize_as = "DefaultOnError")]
420 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
421 #[serde(default)]
422 #[serde(rename = "_meta")]
423 pub meta: Option<Meta>,
424 }
425}
426
427impl LogoutAuthResponse {
428 #[must_use]
430 pub fn new() -> Self {
431 Self::default()
432 }
433
434 #[must_use]
440 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
441 self.meta = meta.into_option();
442 self
443 }
444}
445
446#[cfg(feature = "unstable_auth_status")]
449crate::serde_util::default_on_null! {
450 #[serde_as]
458 #[skip_serializing_none]
459 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
460 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
461 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_STATUS_METHOD_NAME)))]
462 #[serde(rename_all = "camelCase")]
463 #[non_exhaustive]
464 pub struct AuthStatusRequest {
465 #[serde_as(deserialize_as = "DefaultOnError")]
471 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
472 #[serde(default)]
473 #[serde(rename = "_meta")]
474 pub meta: Option<Meta>,
475 }
476}
477
478#[cfg(feature = "unstable_auth_status")]
479impl AuthStatusRequest {
480 #[must_use]
482 pub fn new() -> Self {
483 Self::default()
484 }
485
486 #[must_use]
492 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
493 self.meta = meta.into_option();
494 self
495 }
496}
497
498#[cfg(feature = "unstable_auth_status")]
504#[serde_as]
505#[skip_serializing_none]
506#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
507#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
508#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_STATUS_METHOD_NAME)))]
509#[serde(rename_all = "camelCase")]
510#[non_exhaustive]
511pub struct AuthStatusResponse {
512 pub state: AuthState,
517 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
523 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
524 #[serde(default, skip_serializing_if = "Vec::is_empty")]
525 pub accounts: Vec<AuthAccount>,
526 #[serde_as(deserialize_as = "DefaultOnError")]
532 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
533 #[serde(default)]
534 #[serde(rename = "_meta")]
535 pub meta: Option<Meta>,
536}
537
538#[cfg(feature = "unstable_auth_status")]
539impl AuthStatusResponse {
540 #[must_use]
542 pub fn new(state: AuthState) -> Self {
543 Self {
544 state,
545 accounts: vec![],
546 meta: None,
547 }
548 }
549
550 #[must_use]
552 pub fn accounts(mut self, accounts: Vec<AuthAccount>) -> Self {
553 self.accounts = accounts;
554 self
555 }
556
557 #[must_use]
563 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
564 self.meta = meta.into_option();
565 self
566 }
567}
568
569#[cfg(feature = "unstable_auth_status")]
579#[serde_as]
580#[skip_serializing_none]
581#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
582#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
583#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = AUTH_STATUS_UPDATE_NOTIFICATION)))]
584#[serde(rename_all = "camelCase")]
585#[non_exhaustive]
586pub struct AuthStatusUpdateNotification {
587 pub state: AuthState,
592 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
598 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
599 #[serde(default, skip_serializing_if = "Vec::is_empty")]
600 pub accounts: Vec<AuthAccount>,
601 #[serde_as(deserialize_as = "DefaultOnError")]
607 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
608 #[serde(default)]
609 #[serde(rename = "_meta")]
610 pub meta: Option<Meta>,
611}
612
613#[cfg(feature = "unstable_auth_status")]
614impl AuthStatusUpdateNotification {
615 #[must_use]
617 pub fn new(state: AuthState) -> Self {
618 Self {
619 state,
620 accounts: vec![],
621 meta: None,
622 }
623 }
624
625 #[must_use]
627 pub fn accounts(mut self, accounts: Vec<AuthAccount>) -> Self {
628 self.accounts = accounts;
629 self
630 }
631
632 #[must_use]
638 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
639 self.meta = meta.into_option();
640 self
641 }
642}
643
644#[cfg(feature = "unstable_auth_status")]
650#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
651#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
652#[serde(rename_all = "snake_case")]
653#[non_exhaustive]
654pub enum AuthState {
655 Authenticated,
657 Unauthenticated,
660 NotRequired,
662 Unknown,
664 #[serde(untagged)]
670 Other(String),
671}
672
673#[cfg(feature = "unstable_auth_status")]
684#[serde_as]
685#[skip_serializing_none]
686#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
687#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
688#[serde(rename_all = "camelCase")]
689#[non_exhaustive]
690pub struct AuthAccount {
691 pub label: String,
694 #[serde_as(deserialize_as = "DefaultOnError")]
700 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
701 #[serde(default)]
702 pub active: Option<bool>,
703 #[serde_as(deserialize_as = "DefaultOnError")]
707 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
708 #[serde(default)]
709 pub name: Option<String>,
710 #[serde_as(deserialize_as = "DefaultOnError")]
714 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
715 #[serde(default)]
716 pub email: Option<String>,
717 #[serde_as(deserialize_as = "DefaultOnError")]
721 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
722 #[serde(default)]
723 pub organization: Option<String>,
724 #[serde_as(deserialize_as = "DefaultOnError")]
730 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
731 #[serde(default)]
732 #[serde(rename = "_meta")]
733 pub meta: Option<Meta>,
734}
735
736#[cfg(feature = "unstable_auth_status")]
737impl AuthAccount {
738 #[must_use]
740 pub fn new(label: impl Into<String>) -> Self {
741 Self {
742 label: label.into(),
743 active: None,
744 name: None,
745 email: None,
746 organization: None,
747 meta: None,
748 }
749 }
750
751 #[must_use]
753 pub fn active(mut self, active: impl IntoOption<bool>) -> Self {
754 self.active = active.into_option();
755 self
756 }
757
758 #[must_use]
760 pub fn name(mut self, name: impl IntoOption<String>) -> Self {
761 self.name = name.into_option();
762 self
763 }
764
765 #[must_use]
767 pub fn email(mut self, email: impl IntoOption<String>) -> Self {
768 self.email = email.into_option();
769 self
770 }
771
772 #[must_use]
774 pub fn organization(mut self, organization: impl IntoOption<String>) -> Self {
775 self.organization = organization.into_option();
776 self
777 }
778
779 #[must_use]
785 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
786 self.meta = meta.into_option();
787 self
788 }
789}
790
791#[cfg(feature = "unstable_auth_status")]
799#[serde_as]
800#[skip_serializing_none]
801#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
802#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
803#[non_exhaustive]
804pub struct AuthStatusCapabilities {
805 #[serde_as(deserialize_as = "DefaultOnError")]
811 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
812 #[serde(default)]
813 #[serde(rename = "_meta")]
814 pub meta: Option<Meta>,
815}
816
817#[cfg(feature = "unstable_auth_status")]
818impl AuthStatusCapabilities {
819 #[must_use]
821 pub fn new() -> Self {
822 Self::default()
823 }
824
825 #[must_use]
831 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
832 self.meta = meta.into_option();
833 self
834 }
835}
836
837#[serde_as]
843#[skip_serializing_none]
844#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
845#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
846#[serde(rename_all = "camelCase")]
847#[non_exhaustive]
848pub struct AgentAuthCapabilities {
849 #[cfg(feature = "unstable_auth_status")]
858 #[serde_as(deserialize_as = "DefaultOnError")]
859 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
860 #[serde(default)]
861 pub status: Option<AuthStatusCapabilities>,
862 #[serde_as(deserialize_as = "DefaultOnError")]
868 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
869 #[serde(default)]
870 #[serde(rename = "_meta")]
871 pub meta: Option<Meta>,
872}
873
874impl AgentAuthCapabilities {
875 #[must_use]
877 pub fn new() -> Self {
878 Self::default()
879 }
880
881 #[cfg(feature = "unstable_auth_status")]
887 #[must_use]
888 pub fn status(mut self, status: impl IntoOption<AuthStatusCapabilities>) -> Self {
889 self.status = status.into_option();
890 self
891 }
892
893 #[must_use]
899 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
900 self.meta = meta.into_option();
901 self
902 }
903}
904
905#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
907#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
908#[serde(transparent)]
909#[from(forward)]
910#[non_exhaustive]
911pub struct AuthMethodId(pub Arc<str>);
912
913impl AuthMethodId {
914 #[must_use]
916 pub fn new(id: impl Into<Self>) -> Self {
917 id.into()
918 }
919}
920
921#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
925#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
926#[serde(tag = "type", rename_all = "snake_case")]
927#[non_exhaustive]
928pub enum AuthMethod {
929 Terminal(AuthMethodTerminal),
932 Agent(AuthMethodAgent),
936 #[serde(untagged)]
946 Other(OtherAuthMethod),
947}
948
949impl AuthMethod {
950 #[must_use]
952 pub fn method_id(&self) -> &AuthMethodId {
953 match self {
954 Self::Agent(a) => &a.method_id,
955 Self::Other(a) => &a.method_id,
956 Self::Terminal(t) => &t.method_id,
957 }
958 }
959
960 #[must_use]
962 pub fn name(&self) -> &str {
963 match self {
964 Self::Agent(a) => &a.name,
965 Self::Other(a) => &a.name,
966 Self::Terminal(t) => &t.name,
967 }
968 }
969
970 #[must_use]
972 pub fn description(&self) -> Option<&str> {
973 match self {
974 Self::Agent(a) => a.description.as_deref(),
975 Self::Other(a) => a
976 .fields
977 .get("description")
978 .and_then(serde_json::Value::as_str),
979 Self::Terminal(t) => t.description.as_deref(),
980 }
981 }
982
983 #[must_use]
989 pub fn meta(&self) -> Option<&Meta> {
990 match self {
991 Self::Agent(a) => a.meta.as_ref(),
992 Self::Other(a) => a.fields.get("_meta").and_then(serde_json::Value::as_object),
993 Self::Terminal(t) => t.meta.as_ref(),
994 }
995 }
996}
997
998#[serde_as]
1000#[skip_serializing_none]
1001#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1002#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
1003#[cfg_attr(feature = "schemars", schemars(inline))]
1004#[cfg_attr(feature = "schemars", schemars(transform = other_auth_method_schema))]
1005#[serde(rename_all = "camelCase")]
1006#[non_exhaustive]
1007pub struct OtherAuthMethod {
1008 #[serde(rename = "type")]
1014 pub type_: String,
1015 pub method_id: AuthMethodId,
1017 pub name: String,
1019 #[serde(flatten)]
1022 pub fields: BTreeMap<String, serde_json::Value>,
1023}
1024
1025impl OtherAuthMethod {
1026 #[must_use]
1028 pub fn new(
1029 type_: impl Into<String>,
1030 method_id: impl Into<AuthMethodId>,
1031 name: impl Into<String>,
1032 mut fields: BTreeMap<String, serde_json::Value>,
1033 ) -> Self {
1034 fields.remove("type");
1035 fields.remove("methodId");
1036 fields.remove("name");
1037 Self {
1038 type_: type_.into(),
1039 method_id: method_id.into(),
1040 name: name.into(),
1041 fields,
1042 }
1043 }
1044}
1045
1046impl<'de> Deserialize<'de> for OtherAuthMethod {
1047 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1048 where
1049 D: serde::Deserializer<'de>,
1050 {
1051 #[derive(Deserialize)]
1052 #[serde(rename_all = "camelCase")]
1053 struct RawOtherAuthMethod {
1054 #[serde(rename = "type")]
1055 type_: String,
1056 method_id: AuthMethodId,
1057 name: String,
1058 #[serde(flatten)]
1059 fields: BTreeMap<String, serde_json::Value>,
1060 }
1061
1062 let raw = RawOtherAuthMethod::deserialize(deserializer)?;
1063 if is_known_auth_method_type(&raw.type_) {
1064 return Err(serde::de::Error::custom(format!(
1065 "known authentication method `{}` did not match its schema",
1066 raw.type_
1067 )));
1068 }
1069
1070 Ok(Self {
1071 type_: raw.type_,
1072 method_id: raw.method_id,
1073 name: raw.name,
1074 fields: raw.fields,
1075 })
1076 }
1077}
1078
1079fn is_known_auth_method_type(type_: &str) -> bool {
1080 matches!(type_, "agent" | "terminal")
1081}
1082
1083#[cfg(feature = "schemars")]
1084fn other_auth_method_schema(schema: &mut Schema) {
1085 super::schema_util::reject_known_string_discriminators(schema, "type", &["agent", "terminal"]);
1086}
1087
1088#[serde_as]
1092#[skip_serializing_none]
1093#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1094#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1095#[serde(rename_all = "camelCase")]
1096#[non_exhaustive]
1097pub struct AuthMethodAgent {
1098 pub method_id: AuthMethodId,
1100 pub name: String,
1102 #[serde_as(deserialize_as = "DefaultOnError")]
1106 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1107 #[serde(default)]
1108 pub description: Option<String>,
1109 #[serde_as(deserialize_as = "DefaultOnError")]
1115 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1116 #[serde(default)]
1117 #[serde(rename = "_meta")]
1118 pub meta: Option<Meta>,
1119}
1120
1121impl AuthMethodAgent {
1122 #[must_use]
1124 pub fn new(method_id: impl Into<AuthMethodId>, name: impl Into<String>) -> Self {
1125 Self {
1126 method_id: method_id.into(),
1127 name: name.into(),
1128 description: None,
1129 meta: None,
1130 }
1131 }
1132
1133 #[must_use]
1135 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
1136 self.description = description.into_option();
1137 self
1138 }
1139
1140 #[must_use]
1146 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1147 self.meta = meta.into_option();
1148 self
1149 }
1150}
1151
1152#[serde_as]
1160#[skip_serializing_none]
1161#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1162#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1163#[serde(rename_all = "camelCase")]
1164#[non_exhaustive]
1165pub struct AuthMethodTerminal {
1166 pub method_id: AuthMethodId,
1168 pub name: String,
1170 #[serde_as(deserialize_as = "DefaultOnError")]
1174 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1175 #[serde(default)]
1176 pub description: Option<String>,
1177 #[serde_as(deserialize_as = "DefaultOnNull")]
1179 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
1180 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1181 pub args: Vec<String>,
1182 #[serde_as(deserialize_as = "DefaultOnNull")]
1186 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
1187 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1188 pub env: Vec<EnvVariable>,
1189 #[serde_as(deserialize_as = "DefaultOnError")]
1195 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1196 #[serde(default)]
1197 #[serde(rename = "_meta")]
1198 pub meta: Option<Meta>,
1199}
1200
1201impl AuthMethodTerminal {
1202 #[must_use]
1204 pub fn new(method_id: impl Into<AuthMethodId>, name: impl Into<String>) -> Self {
1205 Self {
1206 method_id: method_id.into(),
1207 name: name.into(),
1208 description: None,
1209 args: Vec::new(),
1210 env: Vec::new(),
1211 meta: None,
1212 }
1213 }
1214
1215 #[must_use]
1217 pub fn args(mut self, args: Vec<String>) -> Self {
1218 self.args = args;
1219 self
1220 }
1221
1222 #[must_use]
1226 pub fn env(mut self, env: Vec<EnvVariable>) -> Self {
1227 self.env = env;
1228 self
1229 }
1230
1231 #[must_use]
1233 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
1234 self.description = description.into_option();
1235 self
1236 }
1237
1238 #[must_use]
1244 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1245 self.meta = meta.into_option();
1246 self
1247 }
1248}
1249
1250#[serde_as]
1256#[skip_serializing_none]
1257#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1258#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1259#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_NEW_METHOD_NAME)))]
1260#[serde(rename_all = "camelCase")]
1261#[non_exhaustive]
1262pub struct NewSessionRequest {
1263 pub cwd: AbsolutePath,
1265 #[serde_as(deserialize_as = "DefaultOnNull")]
1271 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
1272 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1273 pub additional_directories: Vec<AbsolutePath>,
1274 #[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 mcp_servers: Vec<McpServer>,
1279 #[cfg(feature = "unstable_session_system_prompt")]
1289 #[serde(default)]
1290 pub system_prompt: Option<SystemPrompt>,
1291 #[serde_as(deserialize_as = "DefaultOnError")]
1297 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1298 #[serde(default)]
1299 #[serde(rename = "_meta")]
1300 pub meta: Option<Meta>,
1301}
1302
1303impl NewSessionRequest {
1304 #[must_use]
1306 pub fn new(cwd: impl Into<AbsolutePath>) -> Self {
1307 Self {
1308 cwd: cwd.into(),
1309 additional_directories: vec![],
1310 mcp_servers: vec![],
1311 #[cfg(feature = "unstable_session_system_prompt")]
1312 system_prompt: None,
1313 meta: None,
1314 }
1315 }
1316
1317 #[must_use]
1319 pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
1320 where
1321 I: IntoIterator<Item = P>,
1322 P: Into<AbsolutePath>,
1323 {
1324 self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
1325 self
1326 }
1327
1328 #[must_use]
1330 pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1331 self.mcp_servers = mcp_servers;
1332 self
1333 }
1334
1335 #[cfg(feature = "unstable_session_system_prompt")]
1341 #[must_use]
1342 pub fn system_prompt(mut self, system_prompt: impl IntoOption<SystemPrompt>) -> Self {
1343 self.system_prompt = system_prompt.into_option();
1344 self
1345 }
1346
1347 #[must_use]
1353 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1354 self.meta = meta.into_option();
1355 self
1356 }
1357}
1358
1359#[serde_as]
1367#[skip_serializing_none]
1368#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1369#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1370#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_NEW_METHOD_NAME)))]
1371#[serde(rename_all = "camelCase")]
1372#[non_exhaustive]
1373pub struct NewSessionResponse {
1374 pub session_id: SessionId,
1378 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1384 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1385 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1386 pub config_options: Vec<SessionConfigOption>,
1387 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1393 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1394 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1395 pub available_commands: Vec<AvailableCommand>,
1396 #[serde_as(deserialize_as = "DefaultOnError")]
1402 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1403 #[serde(default)]
1404 #[serde(rename = "_meta")]
1405 pub meta: Option<Meta>,
1406}
1407
1408impl NewSessionResponse {
1409 #[must_use]
1411 pub fn new(session_id: impl Into<SessionId>) -> Self {
1412 Self {
1413 session_id: session_id.into(),
1414 config_options: Vec::new(),
1415 available_commands: Vec::new(),
1416 meta: None,
1417 }
1418 }
1419
1420 #[must_use]
1426 pub fn config_options(mut self, config_options: Vec<SessionConfigOption>) -> Self {
1427 self.config_options = config_options;
1428 self
1429 }
1430
1431 #[must_use]
1433 pub fn available_commands(mut self, available_commands: Vec<AvailableCommand>) -> Self {
1434 self.available_commands = available_commands;
1435 self
1436 }
1437
1438 #[must_use]
1444 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1445 self.meta = meta.into_option();
1446 self
1447 }
1448}
1449
1450#[cfg(feature = "unstable_session_fork")]
1463#[serde_as]
1464#[skip_serializing_none]
1465#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1466#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1467#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_FORK_METHOD_NAME)))]
1468#[serde(rename_all = "camelCase")]
1469#[non_exhaustive]
1470pub struct ForkSessionRequest {
1471 pub session_id: SessionId,
1473 pub cwd: AbsolutePath,
1475 #[serde_as(deserialize_as = "DefaultOnNull")]
1481 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
1482 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1483 pub additional_directories: Vec<AbsolutePath>,
1484 #[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 mcp_servers: Vec<McpServer>,
1489 #[cfg(feature = "unstable_session_system_prompt")]
1501 #[serde(default)]
1502 pub system_prompt: Option<SystemPrompt>,
1503 #[serde_as(deserialize_as = "DefaultOnError")]
1509 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1510 #[serde(default)]
1511 #[serde(rename = "_meta")]
1512 pub meta: Option<Meta>,
1513}
1514
1515#[cfg(feature = "unstable_session_fork")]
1516impl ForkSessionRequest {
1517 #[must_use]
1519 pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<AbsolutePath>) -> Self {
1520 Self {
1521 session_id: session_id.into(),
1522 cwd: cwd.into(),
1523 additional_directories: vec![],
1524 mcp_servers: vec![],
1525 #[cfg(feature = "unstable_session_system_prompt")]
1526 system_prompt: None,
1527 meta: None,
1528 }
1529 }
1530
1531 #[must_use]
1533 pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
1534 where
1535 I: IntoIterator<Item = P>,
1536 P: Into<AbsolutePath>,
1537 {
1538 self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
1539 self
1540 }
1541
1542 #[must_use]
1544 pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1545 self.mcp_servers = mcp_servers;
1546 self
1547 }
1548
1549 #[cfg(feature = "unstable_session_system_prompt")]
1555 #[must_use]
1556 pub fn system_prompt(mut self, system_prompt: impl IntoOption<SystemPrompt>) -> Self {
1557 self.system_prompt = system_prompt.into_option();
1558 self
1559 }
1560
1561 #[must_use]
1567 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1568 self.meta = meta.into_option();
1569 self
1570 }
1571}
1572
1573#[cfg(feature = "unstable_session_fork")]
1579#[serde_as]
1580#[skip_serializing_none]
1581#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1582#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1583#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_FORK_METHOD_NAME)))]
1584#[serde(rename_all = "camelCase")]
1585#[non_exhaustive]
1586pub struct ForkSessionResponse {
1587 pub session_id: SessionId,
1589 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1595 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1596 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1597 pub config_options: Vec<SessionConfigOption>,
1598 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1604 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1605 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1606 pub available_commands: Vec<AvailableCommand>,
1607 #[serde_as(deserialize_as = "DefaultOnError")]
1613 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1614 #[serde(default)]
1615 #[serde(rename = "_meta")]
1616 pub meta: Option<Meta>,
1617}
1618
1619#[cfg(feature = "unstable_session_fork")]
1620impl ForkSessionResponse {
1621 #[must_use]
1623 pub fn new(session_id: impl Into<SessionId>) -> Self {
1624 Self {
1625 session_id: session_id.into(),
1626 config_options: Vec::new(),
1627 available_commands: Vec::new(),
1628 meta: None,
1629 }
1630 }
1631
1632 #[must_use]
1638 pub fn config_options(mut self, config_options: Vec<SessionConfigOption>) -> Self {
1639 self.config_options = config_options;
1640 self
1641 }
1642
1643 #[must_use]
1645 pub fn available_commands(mut self, available_commands: Vec<AvailableCommand>) -> Self {
1646 self.available_commands = available_commands;
1647 self
1648 }
1649
1650 #[must_use]
1656 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1657 self.meta = meta.into_option();
1658 self
1659 }
1660}
1661
1662#[serde_as]
1669#[skip_serializing_none]
1670#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1671#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1672#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_RESUME_METHOD_NAME)))]
1673#[serde(rename_all = "camelCase")]
1674#[non_exhaustive]
1675pub struct ResumeSessionRequest {
1676 pub session_id: SessionId,
1678 pub cwd: AbsolutePath,
1683 #[serde_as(deserialize_as = "DefaultOnNull")]
1690 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
1691 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1692 pub additional_directories: Vec<AbsolutePath>,
1693 #[serde_as(deserialize_as = "DefaultOnNull")]
1698 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
1699 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1700 pub mcp_servers: Vec<McpServer>,
1701 #[serde(default)]
1713 pub replay_from: Option<ReplayFrom>,
1714 #[cfg(feature = "unstable_session_system_prompt")]
1726 #[serde(default)]
1727 pub system_prompt: Option<SystemPrompt>,
1728 #[serde_as(deserialize_as = "DefaultOnError")]
1734 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1735 #[serde(default)]
1736 #[serde(rename = "_meta")]
1737 pub meta: Option<Meta>,
1738}
1739
1740impl ResumeSessionRequest {
1741 #[must_use]
1743 pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<AbsolutePath>) -> Self {
1744 Self {
1745 session_id: session_id.into(),
1746 cwd: cwd.into(),
1747 additional_directories: vec![],
1748 mcp_servers: vec![],
1749 replay_from: None,
1750 #[cfg(feature = "unstable_session_system_prompt")]
1751 system_prompt: None,
1752 meta: None,
1753 }
1754 }
1755
1756 #[must_use]
1758 pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
1759 where
1760 I: IntoIterator<Item = P>,
1761 P: Into<AbsolutePath>,
1762 {
1763 self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
1764 self
1765 }
1766
1767 #[must_use]
1769 pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1770 self.mcp_servers = mcp_servers;
1771 self
1772 }
1773
1774 #[must_use]
1782 pub fn replay_from(mut self, replay_from: impl IntoOption<ReplayFrom>) -> Self {
1783 self.replay_from = replay_from.into_option();
1784 self
1785 }
1786
1787 #[cfg(feature = "unstable_session_system_prompt")]
1793 #[must_use]
1794 pub fn system_prompt(mut self, system_prompt: impl IntoOption<SystemPrompt>) -> Self {
1795 self.system_prompt = system_prompt.into_option();
1796 self
1797 }
1798
1799 #[must_use]
1805 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1806 self.meta = meta.into_option();
1807 self
1808 }
1809}
1810
1811#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1815#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1816#[serde(tag = "type", rename_all = "snake_case")]
1817#[non_exhaustive]
1818pub enum ReplayFrom {
1819 Start(ReplayFromStart),
1821 #[serde(untagged)]
1832 Other(OtherReplayFrom),
1833}
1834
1835impl From<ReplayFromStart> for ReplayFrom {
1836 fn from(replay_from: ReplayFromStart) -> Self {
1837 Self::Start(replay_from)
1838 }
1839}
1840
1841#[serde_as]
1843#[skip_serializing_none]
1844#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1845#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1846#[serde(rename_all = "camelCase")]
1847#[non_exhaustive]
1848pub struct ReplayFromStart {
1849 #[serde_as(deserialize_as = "DefaultOnError")]
1855 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1856 #[serde(default)]
1857 #[serde(rename = "_meta")]
1858 pub meta: Option<Meta>,
1859}
1860
1861impl ReplayFromStart {
1862 #[must_use]
1864 pub fn new() -> Self {
1865 Self::default()
1866 }
1867
1868 #[must_use]
1874 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1875 self.meta = meta.into_option();
1876 self
1877 }
1878}
1879
1880#[serde_as]
1882#[skip_serializing_none]
1883#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1884#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
1885#[cfg_attr(feature = "schemars", schemars(inline))]
1886#[cfg_attr(feature = "schemars", schemars(transform = other_replay_from_schema))]
1887#[serde(rename_all = "camelCase")]
1888#[non_exhaustive]
1889pub struct OtherReplayFrom {
1890 #[serde(rename = "type")]
1896 pub type_: String,
1897 #[serde(flatten)]
1900 pub fields: BTreeMap<String, serde_json::Value>,
1901}
1902
1903impl OtherReplayFrom {
1904 #[must_use]
1906 pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
1907 fields.remove("type");
1908 Self {
1909 type_: type_.into(),
1910 fields,
1911 }
1912 }
1913}
1914
1915impl<'de> Deserialize<'de> for OtherReplayFrom {
1916 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1917 where
1918 D: serde::Deserializer<'de>,
1919 {
1920 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
1921 let type_ = fields
1922 .remove("type")
1923 .ok_or_else(|| serde::de::Error::missing_field("type"))?;
1924 let serde_json::Value::String(type_) = type_ else {
1925 return Err(serde::de::Error::custom("`type` must be a string"));
1926 };
1927
1928 if is_known_replay_from_type(&type_) {
1929 return Err(serde::de::Error::custom(format!(
1930 "known replay cursor `{type_}` did not match its schema"
1931 )));
1932 }
1933
1934 Ok(Self { type_, fields })
1935 }
1936}
1937
1938fn is_known_replay_from_type(type_: &str) -> bool {
1939 matches!(type_, "start")
1940}
1941
1942#[cfg(feature = "schemars")]
1943fn other_replay_from_schema(schema: &mut Schema) {
1944 super::schema_util::reject_known_string_discriminators(schema, "type", &["start"]);
1945}
1946
1947crate::serde_util::default_on_null! {
1948 #[serde_as]
1956 #[skip_serializing_none]
1957 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1958 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
1959 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_RESUME_METHOD_NAME)))]
1960 #[serde(rename_all = "camelCase")]
1961 #[non_exhaustive]
1962 pub struct ResumeSessionResponse {
1963 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1969 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1970 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1971 pub config_options: Vec<SessionConfigOption>,
1972 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1978 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1979 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1980 pub available_commands: Vec<AvailableCommand>,
1981 #[serde_as(deserialize_as = "DefaultOnError")]
1987 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1988 #[serde(default)]
1989 #[serde(rename = "_meta")]
1990 pub meta: Option<Meta>,
1991 }
1992}
1993
1994impl ResumeSessionResponse {
1995 #[must_use]
1997 pub fn new() -> Self {
1998 Self::default()
1999 }
2000
2001 #[must_use]
2007 pub fn config_options(mut self, config_options: Vec<SessionConfigOption>) -> Self {
2008 self.config_options = config_options;
2009 self
2010 }
2011
2012 #[must_use]
2014 pub fn available_commands(mut self, available_commands: Vec<AvailableCommand>) -> Self {
2015 self.available_commands = available_commands;
2016 self
2017 }
2018
2019 #[must_use]
2025 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2026 self.meta = meta.into_option();
2027 self
2028 }
2029}
2030
2031#[serde_as]
2054#[skip_serializing_none]
2055#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2056#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2057#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_CLOSE_METHOD_NAME)))]
2058#[serde(rename_all = "camelCase")]
2059#[non_exhaustive]
2060pub struct CloseSessionRequest {
2061 pub session_id: SessionId,
2063 #[serde_as(deserialize_as = "DefaultOnError")]
2069 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2070 #[serde(default)]
2071 #[serde(rename = "_meta")]
2072 pub meta: Option<Meta>,
2073}
2074
2075impl CloseSessionRequest {
2076 #[must_use]
2078 pub fn new(session_id: impl Into<SessionId>) -> Self {
2079 Self {
2080 session_id: session_id.into(),
2081 meta: None,
2082 }
2083 }
2084
2085 #[must_use]
2091 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2092 self.meta = meta.into_option();
2093 self
2094 }
2095}
2096
2097crate::serde_util::default_on_null! {
2098 #[serde_as]
2100 #[skip_serializing_none]
2101 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2102 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
2103 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_CLOSE_METHOD_NAME)))]
2104 #[serde(rename_all = "camelCase")]
2105 #[non_exhaustive]
2106 pub struct CloseSessionResponse {
2107 #[serde_as(deserialize_as = "DefaultOnError")]
2113 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2114 #[serde(default)]
2115 #[serde(rename = "_meta")]
2116 pub meta: Option<Meta>,
2117 }
2118}
2119
2120impl CloseSessionResponse {
2121 #[must_use]
2123 pub fn new() -> Self {
2124 Self::default()
2125 }
2126
2127 #[must_use]
2133 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2134 self.meta = meta.into_option();
2135 self
2136 }
2137}
2138
2139#[cfg(feature = "unstable_session_status")]
2153#[serde_as]
2154#[skip_serializing_none]
2155#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2156#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2157#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_STATUS_METHOD_NAME)))]
2158#[serde(rename_all = "camelCase")]
2159#[non_exhaustive]
2160pub struct SessionStatusRequest {
2161 pub session_id: SessionId,
2163 #[serde_as(deserialize_as = "DefaultOnError")]
2169 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2170 #[serde(default)]
2171 #[serde(rename = "_meta")]
2172 pub meta: Option<Meta>,
2173}
2174
2175#[cfg(feature = "unstable_session_status")]
2176impl SessionStatusRequest {
2177 #[must_use]
2179 pub fn new(session_id: impl Into<SessionId>) -> Self {
2180 Self {
2181 session_id: session_id.into(),
2182 meta: None,
2183 }
2184 }
2185
2186 #[must_use]
2192 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2193 self.meta = meta.into_option();
2194 self
2195 }
2196}
2197
2198#[cfg(feature = "unstable_session_status")]
2207#[serde_as]
2208#[skip_serializing_none]
2209#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2210#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2211#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_STATUS_METHOD_NAME)))]
2212#[serde(rename_all = "camelCase")]
2213#[non_exhaustive]
2214pub struct SessionStatusResponse {
2215 pub status: SessionStatus,
2219 #[serde_as(deserialize_as = "DefaultOnError")]
2227 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2228 #[serde(default)]
2229 pub state: Option<StateUpdate>,
2230 #[serde_as(deserialize_as = "DefaultOnError")]
2236 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2237 #[serde(default)]
2238 #[serde(rename = "_meta")]
2239 pub meta: Option<Meta>,
2240}
2241
2242#[cfg(feature = "unstable_session_status")]
2243impl SessionStatusResponse {
2244 #[must_use]
2246 pub fn new(status: SessionStatus) -> Self {
2247 Self {
2248 status,
2249 state: None,
2250 meta: None,
2251 }
2252 }
2253
2254 #[must_use]
2257 pub fn state(mut self, state: impl IntoOption<StateUpdate>) -> Self {
2258 self.state = state.into_option();
2259 self
2260 }
2261
2262 #[must_use]
2268 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2269 self.meta = meta.into_option();
2270 self
2271 }
2272}
2273
2274#[cfg(feature = "unstable_session_status")]
2280#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2281#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2282#[serde(rename_all = "snake_case")]
2283#[non_exhaustive]
2284pub enum SessionStatus {
2285 Open,
2292 Saved,
2296 #[serde(untagged)]
2303 Other(String),
2304}
2305
2306#[serde_as]
2321#[skip_serializing_none]
2322#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2323#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2324#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = TOOL_CALL_CANCEL_METHOD_NAME)))]
2325#[serde(rename_all = "camelCase")]
2326#[non_exhaustive]
2327pub struct CancelToolCallRequest {
2328 pub session_id: SessionId,
2330 pub tool_call_id: ToolCallId,
2332 #[serde_as(deserialize_as = "DefaultOnError")]
2338 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2339 #[serde(default)]
2340 #[serde(rename = "_meta")]
2341 pub meta: Option<Meta>,
2342}
2343
2344impl CancelToolCallRequest {
2345 #[must_use]
2347 pub fn new(session_id: impl Into<SessionId>, tool_call_id: impl Into<ToolCallId>) -> Self {
2348 Self {
2349 session_id: session_id.into(),
2350 tool_call_id: tool_call_id.into(),
2351 meta: None,
2352 }
2353 }
2354
2355 #[must_use]
2361 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2362 self.meta = meta.into_option();
2363 self
2364 }
2365}
2366
2367crate::serde_util::default_on_null! {
2368 #[serde_as]
2370 #[skip_serializing_none]
2371 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2372 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
2373 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = TOOL_CALL_CANCEL_METHOD_NAME)))]
2374 #[serde(rename_all = "camelCase")]
2375 #[non_exhaustive]
2376 pub struct CancelToolCallResponse {
2377 #[serde_as(deserialize_as = "DefaultOnError")]
2383 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2384 #[serde(default)]
2385 #[serde(rename = "_meta")]
2386 pub meta: Option<Meta>,
2387 }
2388}
2389
2390impl CancelToolCallResponse {
2391 #[must_use]
2393 pub fn new() -> Self {
2394 Self::default()
2395 }
2396
2397 #[must_use]
2403 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2404 self.meta = meta.into_option();
2405 self
2406 }
2407}
2408
2409#[serde_as]
2425#[skip_serializing_none]
2426#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2427#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2428#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = TERMINAL_CANCEL_METHOD_NAME)))]
2429#[serde(rename_all = "camelCase")]
2430#[non_exhaustive]
2431pub struct CancelTerminalRequest {
2432 pub session_id: SessionId,
2434 pub terminal_id: TerminalId,
2436 #[serde_as(deserialize_as = "DefaultOnError")]
2442 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2443 #[serde(default)]
2444 #[serde(rename = "_meta")]
2445 pub meta: Option<Meta>,
2446}
2447
2448impl CancelTerminalRequest {
2449 #[must_use]
2451 pub fn new(session_id: impl Into<SessionId>, terminal_id: impl Into<TerminalId>) -> Self {
2452 Self {
2453 session_id: session_id.into(),
2454 terminal_id: terminal_id.into(),
2455 meta: None,
2456 }
2457 }
2458
2459 #[must_use]
2465 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2466 self.meta = meta.into_option();
2467 self
2468 }
2469}
2470
2471crate::serde_util::default_on_null! {
2472 #[serde_as]
2474 #[skip_serializing_none]
2475 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2476 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
2477 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = TERMINAL_CANCEL_METHOD_NAME)))]
2478 #[serde(rename_all = "camelCase")]
2479 #[non_exhaustive]
2480 pub struct CancelTerminalResponse {
2481 #[serde_as(deserialize_as = "DefaultOnError")]
2487 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2488 #[serde(default)]
2489 #[serde(rename = "_meta")]
2490 pub meta: Option<Meta>,
2491 }
2492}
2493
2494impl CancelTerminalResponse {
2495 #[must_use]
2497 pub fn new() -> Self {
2498 Self::default()
2499 }
2500
2501 #[must_use]
2507 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2508 self.meta = meta.into_option();
2509 self
2510 }
2511}
2512
2513#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2517#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
2518#[serde(transparent)]
2519#[from(Arc<str>, String, &str, &mut str, Box<str>, Cow<'_, str>)]
2520#[non_exhaustive]
2521pub struct SessionListCursor(pub Arc<str>);
2522
2523impl SessionListCursor {
2524 #[must_use]
2526 pub fn new(cursor: impl Into<Self>) -> Self {
2527 cursor.into()
2528 }
2529}
2530
2531impl AsRef<str> for SessionListCursor {
2532 fn as_ref(&self) -> &str {
2533 &self.0
2534 }
2535}
2536
2537impl From<&String> for SessionListCursor {
2538 fn from(cursor: &String) -> Self {
2539 Self(cursor.as_str().into())
2540 }
2541}
2542
2543macro_rules! impl_session_list_cursor_option_conversion {
2544 ($source:ty) => {
2545 impl IntoOption<SessionListCursor> for $source {
2546 fn into_option(self) -> Option<SessionListCursor> {
2547 Some(self.into())
2548 }
2549 }
2550 };
2551}
2552
2553impl_session_list_cursor_option_conversion!(Arc<str>);
2554impl_session_list_cursor_option_conversion!(String);
2555impl_session_list_cursor_option_conversion!(&str);
2556impl_session_list_cursor_option_conversion!(&mut str);
2557impl_session_list_cursor_option_conversion!(&String);
2558impl_session_list_cursor_option_conversion!(Box<str>);
2559impl_session_list_cursor_option_conversion!(Cow<'_, str>);
2560
2561crate::serde_util::default_on_null! {
2562 #[serde_as]
2564 #[skip_serializing_none]
2565 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2566 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
2567 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_LIST_METHOD_NAME)))]
2568 #[serde(rename_all = "camelCase")]
2569 #[non_exhaustive]
2570 pub struct ListSessionsRequest {
2571 #[serde(default)]
2578 pub cwd: Option<AbsolutePath>,
2579 #[serde(default)]
2583 pub cursor: Option<SessionListCursor>,
2584 #[cfg(feature = "unstable_session_archive")]
2594 #[serde(default)]
2595 pub archived: Option<SessionArchiveFilter>,
2596 #[serde_as(deserialize_as = "DefaultOnError")]
2602 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2603 #[serde(default)]
2604 #[serde(rename = "_meta")]
2605 pub meta: Option<Meta>,
2606 }
2607}
2608
2609impl ListSessionsRequest {
2610 #[must_use]
2612 pub fn new() -> Self {
2613 Self::default()
2614 }
2615
2616 #[must_use]
2618 pub fn cwd(mut self, cwd: impl IntoOption<AbsolutePath>) -> Self {
2619 self.cwd = cwd.into_option();
2620 self
2621 }
2622
2623 #[must_use]
2625 pub fn cursor(mut self, cursor: impl IntoOption<SessionListCursor>) -> Self {
2626 self.cursor = cursor.into_option();
2627 self
2628 }
2629
2630 #[cfg(feature = "unstable_session_archive")]
2636 #[must_use]
2637 pub fn archived(mut self, archived: impl IntoOption<SessionArchiveFilter>) -> Self {
2638 self.archived = archived.into_option();
2639 self
2640 }
2641
2642 #[must_use]
2648 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2649 self.meta = meta.into_option();
2650 self
2651 }
2652}
2653
2654#[serde_as]
2656#[skip_serializing_none]
2657#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2658#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2659#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_LIST_METHOD_NAME)))]
2660#[serde(rename_all = "camelCase")]
2661#[non_exhaustive]
2662pub struct ListSessionsResponse {
2663 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
2665 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
2666 pub sessions: Vec<SessionInfo>,
2667 #[serde(default)]
2671 pub next_cursor: Option<SessionListCursor>,
2672 #[serde_as(deserialize_as = "DefaultOnError")]
2678 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2679 #[serde(default)]
2680 #[serde(rename = "_meta")]
2681 pub meta: Option<Meta>,
2682}
2683
2684impl ListSessionsResponse {
2685 #[must_use]
2687 pub fn new(sessions: Vec<SessionInfo>) -> Self {
2688 Self {
2689 sessions,
2690 next_cursor: None,
2691 meta: None,
2692 }
2693 }
2694
2695 #[must_use]
2697 pub fn next_cursor(mut self, next_cursor: impl IntoOption<SessionListCursor>) -> Self {
2698 self.next_cursor = next_cursor.into_option();
2699 self
2700 }
2701
2702 #[must_use]
2708 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2709 self.meta = meta.into_option();
2710 self
2711 }
2712}
2713
2714#[serde_as]
2720#[skip_serializing_none]
2721#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2722#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2723#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_DELETE_METHOD_NAME)))]
2724#[serde(rename_all = "camelCase")]
2725#[non_exhaustive]
2726pub struct DeleteSessionRequest {
2727 pub session_id: SessionId,
2729 #[serde_as(deserialize_as = "DefaultOnError")]
2735 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2736 #[serde(default)]
2737 #[serde(rename = "_meta")]
2738 pub meta: Option<Meta>,
2739}
2740
2741impl DeleteSessionRequest {
2742 #[must_use]
2744 pub fn new(session_id: impl Into<SessionId>) -> Self {
2745 Self {
2746 session_id: session_id.into(),
2747 meta: None,
2748 }
2749 }
2750
2751 #[must_use]
2757 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2758 self.meta = meta.into_option();
2759 self
2760 }
2761}
2762
2763crate::serde_util::default_on_null! {
2764 #[serde_as]
2766 #[skip_serializing_none]
2767 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2768 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
2769 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_DELETE_METHOD_NAME)))]
2770 #[serde(rename_all = "camelCase")]
2771 #[non_exhaustive]
2772 pub struct DeleteSessionResponse {
2773 #[serde_as(deserialize_as = "DefaultOnError")]
2779 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2780 #[serde(default)]
2781 #[serde(rename = "_meta")]
2782 pub meta: Option<Meta>,
2783 }
2784}
2785
2786impl DeleteSessionResponse {
2787 #[must_use]
2789 pub fn new() -> Self {
2790 Self::default()
2791 }
2792
2793 #[must_use]
2799 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2800 self.meta = meta.into_option();
2801 self
2802 }
2803}
2804
2805#[cfg(feature = "unstable_session_archive")]
2821#[serde_as]
2822#[skip_serializing_none]
2823#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2824#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2825#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_ARCHIVE_METHOD_NAME)))]
2826#[serde(rename_all = "camelCase")]
2827#[non_exhaustive]
2828pub struct ArchiveSessionRequest {
2829 pub session_id: SessionId,
2831 #[serde_as(deserialize_as = "DefaultOnError")]
2837 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2838 #[serde(default)]
2839 #[serde(rename = "_meta")]
2840 pub meta: Option<Meta>,
2841}
2842
2843#[cfg(feature = "unstable_session_archive")]
2844impl ArchiveSessionRequest {
2845 #[must_use]
2847 pub fn new(session_id: impl Into<SessionId>) -> Self {
2848 Self {
2849 session_id: session_id.into(),
2850 meta: None,
2851 }
2852 }
2853
2854 #[must_use]
2860 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2861 self.meta = meta.into_option();
2862 self
2863 }
2864}
2865
2866#[cfg(feature = "unstable_session_archive")]
2867crate::serde_util::default_on_null! {
2868 #[serde_as]
2874 #[skip_serializing_none]
2875 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2876 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
2877 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_ARCHIVE_METHOD_NAME)))]
2878 #[serde(rename_all = "camelCase")]
2879 #[non_exhaustive]
2880 pub struct ArchiveSessionResponse {
2881 #[serde_as(deserialize_as = "DefaultOnError")]
2887 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2888 #[serde(default)]
2889 #[serde(rename = "_meta")]
2890 pub meta: Option<Meta>,
2891 }
2892}
2893
2894#[cfg(feature = "unstable_session_archive")]
2895impl ArchiveSessionResponse {
2896 #[must_use]
2898 pub fn new() -> Self {
2899 Self::default()
2900 }
2901
2902 #[must_use]
2908 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2909 self.meta = meta.into_option();
2910 self
2911 }
2912}
2913
2914#[cfg(feature = "unstable_session_archive")]
2929#[serde_as]
2930#[skip_serializing_none]
2931#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2932#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2933#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_UNARCHIVE_METHOD_NAME)))]
2934#[serde(rename_all = "camelCase")]
2935#[non_exhaustive]
2936pub struct UnarchiveSessionRequest {
2937 pub session_id: SessionId,
2939 #[serde_as(deserialize_as = "DefaultOnError")]
2945 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2946 #[serde(default)]
2947 #[serde(rename = "_meta")]
2948 pub meta: Option<Meta>,
2949}
2950
2951#[cfg(feature = "unstable_session_archive")]
2952impl UnarchiveSessionRequest {
2953 #[must_use]
2955 pub fn new(session_id: impl Into<SessionId>) -> Self {
2956 Self {
2957 session_id: session_id.into(),
2958 meta: None,
2959 }
2960 }
2961
2962 #[must_use]
2968 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2969 self.meta = meta.into_option();
2970 self
2971 }
2972}
2973
2974#[cfg(feature = "unstable_session_archive")]
2975crate::serde_util::default_on_null! {
2976 #[serde_as]
2982 #[skip_serializing_none]
2983 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2984 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
2985 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_UNARCHIVE_METHOD_NAME)))]
2986 #[serde(rename_all = "camelCase")]
2987 #[non_exhaustive]
2988 pub struct UnarchiveSessionResponse {
2989 #[serde_as(deserialize_as = "DefaultOnError")]
2995 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2996 #[serde(default)]
2997 #[serde(rename = "_meta")]
2998 pub meta: Option<Meta>,
2999 }
3000}
3001
3002#[cfg(feature = "unstable_session_archive")]
3003impl UnarchiveSessionResponse {
3004 #[must_use]
3006 pub fn new() -> Self {
3007 Self::default()
3008 }
3009
3010 #[must_use]
3016 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3017 self.meta = meta.into_option();
3018 self
3019 }
3020}
3021
3022#[cfg(feature = "unstable_session_archive")]
3031#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3032#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
3033#[serde(rename_all = "snake_case")]
3034#[non_exhaustive]
3035pub enum SessionArchiveFilter {
3036 #[default]
3038 Exclude,
3039 Include,
3041 Only,
3043 #[serde(untagged)]
3049 Other(String),
3050}
3051
3052#[serde_as]
3054#[skip_serializing_none]
3055#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3056#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3057#[serde(rename_all = "camelCase")]
3058#[non_exhaustive]
3059pub struct SessionInfo {
3060 pub session_id: SessionId,
3062 pub cwd: AbsolutePath,
3064 #[serde_as(deserialize_as = "DefaultOnNull")]
3070 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
3071 #[serde(default, skip_serializing_if = "Vec::is_empty")]
3072 pub additional_directories: Vec<AbsolutePath>,
3073
3074 #[serde_as(deserialize_as = "DefaultOnError")]
3078 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3079 #[serde(default)]
3080 pub title: Option<String>,
3081 #[serde_as(deserialize_as = "DefaultOnError")]
3085 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "format" = "date-time")))]
3086 #[serde(default)]
3087 pub updated_at: Option<String>,
3088 #[cfg(feature = "unstable_session_archive")]
3097 #[serde_as(deserialize_as = "DefaultOnError")]
3098 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3099 #[serde(default)]
3100 pub archived: Option<bool>,
3101 #[serde_as(deserialize_as = "DefaultOnError")]
3107 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3108 #[serde(default)]
3109 #[serde(rename = "_meta")]
3110 pub meta: Option<Meta>,
3111}
3112
3113impl SessionInfo {
3114 #[must_use]
3116 pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<AbsolutePath>) -> Self {
3117 Self {
3118 session_id: session_id.into(),
3119 cwd: cwd.into(),
3120 additional_directories: vec![],
3121 title: None,
3122 updated_at: None,
3123 #[cfg(feature = "unstable_session_archive")]
3124 archived: None,
3125 meta: None,
3126 }
3127 }
3128
3129 #[must_use]
3131 pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
3132 where
3133 I: IntoIterator<Item = P>,
3134 P: Into<AbsolutePath>,
3135 {
3136 self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
3137 self
3138 }
3139
3140 #[must_use]
3142 pub fn title(mut self, title: impl IntoOption<String>) -> Self {
3143 self.title = title.into_option();
3144 self
3145 }
3146
3147 #[must_use]
3149 pub fn updated_at(mut self, updated_at: impl IntoOption<String>) -> Self {
3150 self.updated_at = updated_at.into_option();
3151 self
3152 }
3153
3154 #[cfg(feature = "unstable_session_archive")]
3160 #[must_use]
3161 pub fn archived(mut self, archived: impl IntoOption<bool>) -> Self {
3162 self.archived = archived.into_option();
3163 self
3164 }
3165
3166 #[must_use]
3172 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3173 self.meta = meta.into_option();
3174 self
3175 }
3176}
3177
3178#[cfg(feature = "unstable_session_set_title")]
3194#[serde_as]
3195#[skip_serializing_none]
3196#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3197#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3198#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_TITLE_METHOD_NAME)))]
3199#[serde(rename_all = "camelCase")]
3200#[non_exhaustive]
3201pub struct SetSessionTitleRequest {
3202 pub session_id: SessionId,
3204 pub title: String,
3211 #[serde_as(deserialize_as = "DefaultOnError")]
3217 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3218 #[serde(default)]
3219 #[serde(rename = "_meta")]
3220 pub meta: Option<Meta>,
3221}
3222
3223#[cfg(feature = "unstable_session_set_title")]
3224impl SetSessionTitleRequest {
3225 #[must_use]
3227 pub fn new(session_id: impl Into<SessionId>, title: impl Into<String>) -> Self {
3228 Self {
3229 session_id: session_id.into(),
3230 title: title.into(),
3231 meta: None,
3232 }
3233 }
3234
3235 #[must_use]
3241 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3242 self.meta = meta.into_option();
3243 self
3244 }
3245}
3246
3247#[cfg(feature = "unstable_session_set_title")]
3253#[serde_as]
3254#[skip_serializing_none]
3255#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3256#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3257#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_TITLE_METHOD_NAME)))]
3258#[serde(rename_all = "camelCase")]
3259#[non_exhaustive]
3260pub struct SetSessionTitleResponse {
3261 pub title: String,
3266 #[serde_as(deserialize_as = "DefaultOnError")]
3272 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3273 #[serde(default)]
3274 #[serde(rename = "_meta")]
3275 pub meta: Option<Meta>,
3276}
3277
3278#[cfg(feature = "unstable_session_set_title")]
3279impl SetSessionTitleResponse {
3280 #[must_use]
3282 pub fn new(title: impl Into<String>) -> Self {
3283 Self {
3284 title: title.into(),
3285 meta: None,
3286 }
3287 }
3288
3289 #[must_use]
3295 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3296 self.meta = meta.into_option();
3297 self
3298 }
3299}
3300
3301#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3305#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, From, Display)]
3306#[serde(transparent)]
3307#[from(forward)]
3308#[non_exhaustive]
3309pub struct SessionConfigId(pub Arc<str>);
3310
3311impl SessionConfigId {
3312 #[must_use]
3314 pub fn new(id: impl Into<Self>) -> Self {
3315 id.into()
3316 }
3317}
3318
3319#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3321#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, From, Display)]
3322#[serde(transparent)]
3323#[from(forward)]
3324#[non_exhaustive]
3325pub struct SessionConfigValueId(pub Arc<str>);
3326
3327impl SessionConfigValueId {
3328 #[must_use]
3330 pub fn new(id: impl Into<Self>) -> Self {
3331 id.into()
3332 }
3333}
3334
3335#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3337#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, From, Display)]
3338#[serde(transparent)]
3339#[from(forward)]
3340#[non_exhaustive]
3341pub struct SessionConfigGroupId(pub Arc<str>);
3342
3343impl SessionConfigGroupId {
3344 #[must_use]
3346 pub fn new(id: impl Into<Self>) -> Self {
3347 id.into()
3348 }
3349}
3350
3351#[serde_as]
3353#[skip_serializing_none]
3354#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3355#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3356#[serde(rename_all = "camelCase")]
3357#[non_exhaustive]
3358pub struct SessionConfigSelectOption {
3359 pub value: SessionConfigValueId,
3361 pub name: String,
3363 #[serde_as(deserialize_as = "DefaultOnError")]
3367 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3368 #[serde(default)]
3369 pub description: Option<String>,
3370 #[serde_as(deserialize_as = "DefaultOnError")]
3376 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3377 #[serde(default)]
3378 #[serde(rename = "_meta")]
3379 pub meta: Option<Meta>,
3380}
3381
3382impl SessionConfigSelectOption {
3383 #[must_use]
3385 pub fn new(value: impl Into<SessionConfigValueId>, name: impl Into<String>) -> Self {
3386 Self {
3387 value: value.into(),
3388 name: name.into(),
3389 description: None,
3390 meta: None,
3391 }
3392 }
3393
3394 #[must_use]
3396 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
3397 self.description = description.into_option();
3398 self
3399 }
3400
3401 #[must_use]
3407 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3408 self.meta = meta.into_option();
3409 self
3410 }
3411}
3412
3413#[serde_as]
3415#[skip_serializing_none]
3416#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3417#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3418#[serde(rename_all = "camelCase")]
3419#[non_exhaustive]
3420pub struct SessionConfigSelectGroup {
3421 pub group_id: SessionConfigGroupId,
3423 pub name: String,
3425 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
3427 #[cfg_attr(feature = "schemars", schemars(length(min = 1), extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
3428 pub options: Vec<SessionConfigSelectOption>,
3429 #[serde_as(deserialize_as = "DefaultOnError")]
3435 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3436 #[serde(default)]
3437 #[serde(rename = "_meta")]
3438 pub meta: Option<Meta>,
3439}
3440
3441impl SessionConfigSelectGroup {
3442 #[must_use]
3444 pub fn new(
3445 group_id: impl Into<SessionConfigGroupId>,
3446 name: impl Into<String>,
3447 options: Vec<SessionConfigSelectOption>,
3448 ) -> Self {
3449 Self {
3450 group_id: group_id.into(),
3451 name: name.into(),
3452 options,
3453 meta: None,
3454 }
3455 }
3456
3457 #[must_use]
3463 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3464 self.meta = meta.into_option();
3465 self
3466 }
3467}
3468
3469#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3471#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3472#[serde(untagged)]
3473#[non_exhaustive]
3474pub enum SessionConfigSelectOptions {
3475 Ungrouped(
3477 #[cfg_attr(feature = "schemars", schemars(length(min = 1)))] Vec<SessionConfigSelectOption>,
3478 ),
3479 Grouped(
3481 #[cfg_attr(feature = "schemars", schemars(length(min = 1)))] Vec<SessionConfigSelectGroup>,
3482 ),
3483}
3484
3485impl From<Vec<SessionConfigSelectOption>> for SessionConfigSelectOptions {
3486 fn from(options: Vec<SessionConfigSelectOption>) -> Self {
3487 SessionConfigSelectOptions::Ungrouped(options)
3488 }
3489}
3490
3491impl From<Vec<SessionConfigSelectGroup>> for SessionConfigSelectOptions {
3492 fn from(groups: Vec<SessionConfigSelectGroup>) -> Self {
3493 SessionConfigSelectOptions::Grouped(groups)
3494 }
3495}
3496
3497#[skip_serializing_none]
3499#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3500#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3501#[serde(rename_all = "camelCase")]
3502#[non_exhaustive]
3503pub struct SessionConfigSelect {
3504 pub current_value: SessionConfigValueId,
3506 pub options: SessionConfigSelectOptions,
3509}
3510
3511impl SessionConfigSelect {
3512 #[must_use]
3514 pub fn new(
3515 current_value: impl Into<SessionConfigValueId>,
3516 options: impl Into<SessionConfigSelectOptions>,
3517 ) -> Self {
3518 Self {
3519 current_value: current_value.into(),
3520 options: options.into(),
3521 }
3522 }
3523}
3524
3525#[skip_serializing_none]
3527#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3528#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3529#[serde(rename_all = "camelCase")]
3530#[non_exhaustive]
3531pub struct SessionConfigBoolean {
3532 pub current_value: bool,
3534}
3535
3536impl SessionConfigBoolean {
3537 #[must_use]
3539 pub fn new(current_value: bool) -> Self {
3540 Self { current_value }
3541 }
3542}
3543
3544#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3554#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3555#[serde(rename_all = "snake_case")]
3556#[non_exhaustive]
3557pub enum SessionConfigOptionCategory {
3558 Mode,
3560 Model,
3562 ModelConfig,
3564 ThoughtLevel,
3566 #[serde(untagged)]
3572 Other(String),
3573}
3574
3575#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3577#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3578#[serde(tag = "type", rename_all = "snake_case")]
3579#[non_exhaustive]
3580pub enum SessionConfigKind {
3581 Select(SessionConfigSelect),
3583 Boolean(SessionConfigBoolean),
3585 #[serde(untagged)]
3595 Other(OtherSessionConfigKind),
3596}
3597
3598#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3600#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
3601#[cfg_attr(feature = "schemars", schemars(inline))]
3602#[cfg_attr(feature = "schemars", schemars(transform = other_session_config_kind_schema))]
3603#[serde(rename_all = "camelCase")]
3604#[non_exhaustive]
3605pub struct OtherSessionConfigKind {
3606 #[serde(rename = "type")]
3612 pub type_: String,
3613 #[serde(flatten)]
3615 pub fields: BTreeMap<String, serde_json::Value>,
3616}
3617
3618impl OtherSessionConfigKind {
3619 #[must_use]
3621 pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
3622 fields.remove("type");
3623 fields.remove("_meta");
3624 Self {
3625 type_: type_.into(),
3626 fields,
3627 }
3628 }
3629}
3630
3631impl<'de> Deserialize<'de> for OtherSessionConfigKind {
3632 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3633 where
3634 D: serde::Deserializer<'de>,
3635 {
3636 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
3637 let type_ = fields
3638 .remove("type")
3639 .ok_or_else(|| serde::de::Error::missing_field("type"))?;
3640 let serde_json::Value::String(type_) = type_ else {
3641 return Err(serde::de::Error::custom("`type` must be a string"));
3642 };
3643
3644 if is_known_session_config_kind_type(&type_) {
3645 return Err(serde::de::Error::custom(format!(
3646 "known session configuration option `{type_}` did not match its schema"
3647 )));
3648 }
3649
3650 Ok(Self { type_, fields })
3651 }
3652}
3653
3654fn is_known_session_config_kind_type(type_: &str) -> bool {
3655 matches!(type_, "select" | "boolean")
3656}
3657
3658#[cfg(feature = "schemars")]
3659fn other_session_config_kind_schema(schema: &mut Schema) {
3660 super::schema_util::reject_known_string_discriminators(schema, "type", &["select", "boolean"]);
3661}
3662
3663#[serde_as]
3665#[skip_serializing_none]
3666#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3667#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3668#[serde(rename_all = "camelCase")]
3669#[non_exhaustive]
3670pub struct SessionConfigOption {
3671 pub config_id: SessionConfigId,
3673 pub name: String,
3675 #[serde_as(deserialize_as = "DefaultOnError")]
3679 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3680 #[serde(default)]
3681 pub description: Option<String>,
3682 #[serde_as(deserialize_as = "DefaultOnError")]
3686 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3687 #[serde(default)]
3688 pub category: Option<SessionConfigOptionCategory>,
3689 #[serde(flatten)]
3691 pub kind: SessionConfigKind,
3692 #[serde_as(deserialize_as = "DefaultOnError")]
3698 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3699 #[serde(default)]
3700 #[serde(rename = "_meta")]
3701 pub meta: Option<Meta>,
3702}
3703
3704impl SessionConfigOption {
3705 #[must_use]
3707 pub fn new(
3708 config_id: impl Into<SessionConfigId>,
3709 name: impl Into<String>,
3710 kind: SessionConfigKind,
3711 ) -> Self {
3712 Self {
3713 config_id: config_id.into(),
3714 name: name.into(),
3715 description: None,
3716 category: None,
3717 kind,
3718 meta: None,
3719 }
3720 }
3721
3722 #[must_use]
3724 pub fn select(
3725 config_id: impl Into<SessionConfigId>,
3726 name: impl Into<String>,
3727 current_value: impl Into<SessionConfigValueId>,
3728 options: impl Into<SessionConfigSelectOptions>,
3729 ) -> Self {
3730 Self::new(
3731 config_id,
3732 name,
3733 SessionConfigKind::Select(SessionConfigSelect::new(current_value, options)),
3734 )
3735 }
3736
3737 #[must_use]
3739 pub fn boolean(
3740 config_id: impl Into<SessionConfigId>,
3741 name: impl Into<String>,
3742 current_value: bool,
3743 ) -> Self {
3744 Self::new(
3745 config_id,
3746 name,
3747 SessionConfigKind::Boolean(SessionConfigBoolean::new(current_value)),
3748 )
3749 }
3750
3751 #[must_use]
3753 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
3754 self.description = description.into_option();
3755 self
3756 }
3757
3758 #[must_use]
3760 pub fn category(mut self, category: impl IntoOption<SessionConfigOptionCategory>) -> Self {
3761 self.category = category.into_option();
3762 self
3763 }
3764
3765 #[must_use]
3771 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3772 self.meta = meta.into_option();
3773 self
3774 }
3775}
3776
3777#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3786#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
3787#[serde(tag = "type", rename_all = "snake_case")]
3788#[non_exhaustive]
3789pub enum SessionConfigOptionValue {
3790 Id {
3792 value: SessionConfigValueId,
3794 },
3795 Boolean {
3797 value: bool,
3799 },
3800 #[serde(untagged)]
3809 Other(OtherSessionConfigOptionValue),
3810}
3811
3812#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3814#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
3815#[cfg_attr(feature = "schemars", schemars(inline))]
3816#[cfg_attr(feature = "schemars", schemars(transform = other_session_config_option_value_schema))]
3817#[serde(rename_all = "camelCase")]
3818#[non_exhaustive]
3819pub struct OtherSessionConfigOptionValue {
3820 #[serde(rename = "type")]
3826 pub type_: String,
3827 pub value: serde_json::Value,
3829 #[serde(flatten)]
3831 pub fields: BTreeMap<String, serde_json::Value>,
3832}
3833
3834impl OtherSessionConfigOptionValue {
3835 #[must_use]
3837 pub fn new(
3838 type_: impl Into<String>,
3839 value: serde_json::Value,
3840 mut fields: BTreeMap<String, serde_json::Value>,
3841 ) -> Self {
3842 fields.remove("type");
3843 fields.remove("value");
3844 fields.remove("_meta");
3845 Self {
3846 type_: type_.into(),
3847 value,
3848 fields,
3849 }
3850 }
3851}
3852
3853impl<'de> Deserialize<'de> for OtherSessionConfigOptionValue {
3854 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3855 where
3856 D: serde::Deserializer<'de>,
3857 {
3858 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
3859 let type_ = fields
3860 .remove("type")
3861 .ok_or_else(|| serde::de::Error::missing_field("type"))?;
3862 let serde_json::Value::String(type_) = type_ else {
3863 return Err(serde::de::Error::custom("`type` must be a string"));
3864 };
3865
3866 if is_known_session_config_option_value_type(&type_) {
3867 return Err(serde::de::Error::custom(format!(
3868 "known session configuration option value `{type_}` did not match its schema"
3869 )));
3870 }
3871
3872 let value = fields
3873 .remove("value")
3874 .ok_or_else(|| serde::de::Error::missing_field("value"))?;
3875
3876 Ok(Self {
3877 type_,
3878 value,
3879 fields,
3880 })
3881 }
3882}
3883
3884impl<'de> Deserialize<'de> for SessionConfigOptionValue {
3885 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3886 where
3887 D: serde::Deserializer<'de>,
3888 {
3889 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
3890 let type_ = fields.remove("type");
3891 let value = fields
3892 .remove("value")
3893 .ok_or_else(|| serde::de::Error::missing_field("value"))?;
3894
3895 let type_ = type_.ok_or_else(|| serde::de::Error::missing_field("type"))?;
3896
3897 let serde_json::Value::String(type_) = type_ else {
3898 return Err(serde::de::Error::custom("`type` must be a string"));
3899 };
3900
3901 match type_.as_str() {
3902 "id" => {
3903 let value = serde_json::from_value(value).map_err(|error| {
3904 serde::de::Error::custom(format!(
3905 "`value` must be a string for `type: id`: {error}"
3906 ))
3907 })?;
3908 Ok(Self::Id { value })
3909 }
3910 "boolean" => {
3911 let value = serde_json::from_value(value).map_err(|error| {
3912 serde::de::Error::custom(format!(
3913 "`value` must be a boolean for `type: boolean`: {error}"
3914 ))
3915 })?;
3916 Ok(Self::Boolean { value })
3917 }
3918 _ => Ok(Self::Other(OtherSessionConfigOptionValue {
3919 type_,
3920 value,
3921 fields,
3922 })),
3923 }
3924 }
3925}
3926
3927fn is_known_session_config_option_value_type(type_: &str) -> bool {
3928 matches!(type_, "id" | "boolean")
3929}
3930
3931#[cfg(feature = "schemars")]
3932fn other_session_config_option_value_schema(schema: &mut Schema) {
3933 super::schema_util::reject_known_string_discriminators(schema, "type", &["id", "boolean"]);
3934}
3935
3936impl SessionConfigOptionValue {
3937 #[must_use]
3939 pub fn id(id: impl Into<SessionConfigValueId>) -> Self {
3940 Self::Id { value: id.into() }
3941 }
3942
3943 #[must_use]
3945 pub fn boolean(val: bool) -> Self {
3946 Self::Boolean { value: val }
3947 }
3948
3949 #[must_use]
3952 pub fn as_id(&self) -> Option<&SessionConfigValueId> {
3953 match self {
3954 Self::Id { value } => Some(value),
3955 _ => None,
3956 }
3957 }
3958
3959 #[must_use]
3961 pub fn as_bool(&self) -> Option<bool> {
3962 match self {
3963 Self::Boolean { value } => Some(*value),
3964 _ => None,
3965 }
3966 }
3967}
3968
3969impl From<SessionConfigValueId> for SessionConfigOptionValue {
3970 fn from(value: SessionConfigValueId) -> Self {
3971 Self::Id { value }
3972 }
3973}
3974
3975impl From<bool> for SessionConfigOptionValue {
3976 fn from(value: bool) -> Self {
3977 Self::Boolean { value }
3978 }
3979}
3980
3981impl From<&str> for SessionConfigOptionValue {
3982 fn from(value: &str) -> Self {
3983 Self::Id {
3984 value: SessionConfigValueId::new(value),
3985 }
3986 }
3987}
3988
3989#[serde_as]
3997#[skip_serializing_none]
3998#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3999#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4000#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_CONFIG_OPTION_METHOD_NAME)))]
4001#[serde(rename_all = "camelCase")]
4002#[non_exhaustive]
4003pub struct SetSessionConfigOptionRequest {
4004 pub session_id: SessionId,
4006 pub config_id: SessionConfigId,
4008 #[serde(flatten)]
4012 pub value: SessionConfigOptionValue,
4013 #[serde_as(deserialize_as = "DefaultOnError")]
4019 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4020 #[serde(default)]
4021 #[serde(rename = "_meta")]
4022 pub meta: Option<Meta>,
4023}
4024
4025impl SetSessionConfigOptionRequest {
4026 #[must_use]
4028 pub fn new(
4029 session_id: impl Into<SessionId>,
4030 config_id: impl Into<SessionConfigId>,
4031 value: impl Into<SessionConfigOptionValue>,
4032 ) -> Self {
4033 Self {
4034 session_id: session_id.into(),
4035 config_id: config_id.into(),
4036 value: value.into(),
4037 meta: None,
4038 }
4039 }
4040
4041 #[must_use]
4047 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4048 self.meta = meta.into_option();
4049 self
4050 }
4051}
4052
4053#[serde_as]
4055#[skip_serializing_none]
4056#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4057#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4058#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_CONFIG_OPTION_METHOD_NAME)))]
4059#[serde(rename_all = "camelCase")]
4060#[non_exhaustive]
4061pub struct SetSessionConfigOptionResponse {
4062 #[serde_as(deserialize_as = "VecSkipError<_>")]
4064 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-skip-invalid-items" = true)))]
4065 pub config_options: Vec<SessionConfigOption>,
4066 #[serde_as(deserialize_as = "DefaultOnError")]
4072 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4073 #[serde(default)]
4074 #[serde(rename = "_meta")]
4075 pub meta: Option<Meta>,
4076}
4077
4078impl SetSessionConfigOptionResponse {
4079 #[must_use]
4081 pub fn new(config_options: Vec<SessionConfigOption>) -> Self {
4082 Self {
4083 config_options,
4084 meta: None,
4085 }
4086 }
4087
4088 #[must_use]
4094 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4095 self.meta = meta.into_option();
4096 self
4097 }
4098}
4099
4100#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4109#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4110#[serde(tag = "type", rename_all = "snake_case")]
4111#[non_exhaustive]
4112pub enum McpServer {
4113 Http(McpServerHttp),
4117 #[cfg(feature = "unstable_mcp_over_acp")]
4126 Acp(McpServerAcp),
4127 Stdio(McpServerStdio),
4131 #[serde(untagged)]
4142 Other(OtherMcpServer),
4143}
4144
4145#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4147#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
4148#[cfg_attr(feature = "schemars", schemars(inline))]
4149#[cfg_attr(feature = "schemars", schemars(transform = other_mcp_server_schema))]
4150#[serde(rename_all = "camelCase")]
4151#[non_exhaustive]
4152pub struct OtherMcpServer {
4153 #[serde(rename = "type")]
4159 pub type_: String,
4160 #[serde(flatten)]
4162 pub fields: BTreeMap<String, serde_json::Value>,
4163}
4164
4165impl OtherMcpServer {
4166 #[must_use]
4168 pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
4169 fields.remove("type");
4170 Self {
4171 type_: type_.into(),
4172 fields,
4173 }
4174 }
4175}
4176
4177impl<'de> Deserialize<'de> for OtherMcpServer {
4178 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
4179 where
4180 D: serde::Deserializer<'de>,
4181 {
4182 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
4183 let type_ = fields
4184 .remove("type")
4185 .ok_or_else(|| serde::de::Error::missing_field("type"))?;
4186 let serde_json::Value::String(type_) = type_ else {
4187 return Err(serde::de::Error::custom("`type` must be a string"));
4188 };
4189
4190 if is_known_mcp_server_type(&type_) {
4191 return Err(serde::de::Error::custom(format!(
4192 "known MCP server transport `{type_}` did not match its schema"
4193 )));
4194 }
4195
4196 Ok(Self { type_, fields })
4197 }
4198}
4199
4200fn is_known_mcp_server_type(type_: &str) -> bool {
4201 match type_ {
4202 "http" | "stdio" => true,
4203 #[cfg(feature = "unstable_mcp_over_acp")]
4204 "acp" => true,
4205 _ => false,
4206 }
4207}
4208
4209#[cfg(feature = "schemars")]
4210fn other_mcp_server_schema(schema: &mut Schema) {
4211 super::schema_util::reject_known_string_discriminators(
4212 schema,
4213 "type",
4214 &[
4215 "http",
4216 "stdio",
4217 #[cfg(feature = "unstable_mcp_over_acp")]
4218 "acp",
4219 ],
4220 );
4221}
4222
4223#[serde_as]
4225#[skip_serializing_none]
4226#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4227#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4228#[serde(rename_all = "camelCase")]
4229#[non_exhaustive]
4230pub struct McpServerHttp {
4231 pub name: String,
4233 #[cfg_attr(feature = "schemars", schemars(url))]
4235 pub url: String,
4236 #[serde_as(deserialize_as = "DefaultOnNull")]
4239 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
4240 #[serde(default, skip_serializing_if = "Vec::is_empty")]
4241 pub headers: Vec<HttpHeader>,
4242 #[serde_as(deserialize_as = "DefaultOnError")]
4248 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4249 #[serde(default)]
4250 #[serde(rename = "_meta")]
4251 pub meta: Option<Meta>,
4252}
4253
4254impl McpServerHttp {
4255 #[must_use]
4257 pub fn new(name: impl Into<String>, url: impl Into<String>) -> Self {
4258 Self {
4259 name: name.into(),
4260 url: url.into(),
4261 headers: Vec::new(),
4262 meta: None,
4263 }
4264 }
4265
4266 #[must_use]
4269 pub fn headers(mut self, headers: Vec<HttpHeader>) -> Self {
4270 self.headers = headers;
4271 self
4272 }
4273
4274 #[must_use]
4280 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4281 self.meta = meta.into_option();
4282 self
4283 }
4284}
4285
4286#[cfg(feature = "unstable_mcp_over_acp")]
4295#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4296#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
4297#[serde(transparent)]
4298#[from(forward)]
4299#[non_exhaustive]
4300pub struct McpServerAcpId(pub Arc<str>);
4301
4302#[cfg(feature = "unstable_mcp_over_acp")]
4303impl McpServerAcpId {
4304 #[must_use]
4306 pub fn new(id: impl Into<Self>) -> Self {
4307 id.into()
4308 }
4309}
4310
4311#[serde_as]
4320#[skip_serializing_none]
4321#[cfg(feature = "unstable_mcp_over_acp")]
4322#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4323#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4324#[serde(rename_all = "camelCase")]
4325#[non_exhaustive]
4326pub struct McpServerAcp {
4327 pub name: String,
4329 pub server_id: McpServerAcpId,
4334 #[serde_as(deserialize_as = "DefaultOnError")]
4340 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4341 #[serde(default)]
4342 #[serde(rename = "_meta")]
4343 pub meta: Option<Meta>,
4344}
4345
4346#[cfg(feature = "unstable_mcp_over_acp")]
4347impl McpServerAcp {
4348 #[must_use]
4350 pub fn new(name: impl Into<String>, server_id: impl Into<McpServerAcpId>) -> Self {
4351 Self {
4352 name: name.into(),
4353 server_id: server_id.into(),
4354 meta: None,
4355 }
4356 }
4357
4358 #[must_use]
4364 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4365 self.meta = meta.into_option();
4366 self
4367 }
4368}
4369
4370#[serde_as]
4372#[skip_serializing_none]
4373#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4374#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4375#[serde(rename_all = "camelCase")]
4376#[non_exhaustive]
4377pub struct McpServerStdio {
4378 pub name: String,
4380 pub command: AbsolutePath,
4382 #[serde_as(deserialize_as = "DefaultOnNull")]
4384 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
4385 #[serde(default, skip_serializing_if = "Vec::is_empty")]
4386 pub args: Vec<String>,
4387 #[serde_as(deserialize_as = "DefaultOnNull")]
4390 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
4391 #[serde(default, skip_serializing_if = "Vec::is_empty")]
4392 pub env: Vec<EnvVariable>,
4393 #[serde_as(deserialize_as = "DefaultOnError")]
4399 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4400 #[serde(default)]
4401 #[serde(rename = "_meta")]
4402 pub meta: Option<Meta>,
4403}
4404
4405impl McpServerStdio {
4406 #[must_use]
4408 pub fn new(name: impl Into<String>, command: impl Into<AbsolutePath>) -> Self {
4409 Self {
4410 name: name.into(),
4411 command: command.into(),
4412 args: Vec::new(),
4413 env: Vec::new(),
4414 meta: None,
4415 }
4416 }
4417
4418 #[must_use]
4420 pub fn args(mut self, args: Vec<String>) -> Self {
4421 self.args = args;
4422 self
4423 }
4424
4425 #[must_use]
4428 pub fn env(mut self, env: Vec<EnvVariable>) -> Self {
4429 self.env = env;
4430 self
4431 }
4432
4433 #[must_use]
4439 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4440 self.meta = meta.into_option();
4441 self
4442 }
4443}
4444
4445#[serde_as]
4447#[skip_serializing_none]
4448#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4449#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4450#[serde(rename_all = "camelCase")]
4451#[non_exhaustive]
4452pub struct EnvVariable {
4453 pub name: String,
4455 pub value: String,
4457 #[serde_as(deserialize_as = "DefaultOnError")]
4463 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4464 #[serde(default)]
4465 #[serde(rename = "_meta")]
4466 pub meta: Option<Meta>,
4467}
4468
4469impl EnvVariable {
4470 #[must_use]
4472 pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
4473 Self {
4474 name: name.into(),
4475 value: value.into(),
4476 meta: None,
4477 }
4478 }
4479
4480 #[must_use]
4486 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4487 self.meta = meta.into_option();
4488 self
4489 }
4490}
4491
4492#[serde_as]
4494#[skip_serializing_none]
4495#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4496#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4497#[serde(rename_all = "camelCase")]
4498#[non_exhaustive]
4499pub struct HttpHeader {
4500 pub name: String,
4502 pub value: String,
4504 #[serde_as(deserialize_as = "DefaultOnError")]
4510 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4511 #[serde(default)]
4512 #[serde(rename = "_meta")]
4513 pub meta: Option<Meta>,
4514}
4515
4516impl HttpHeader {
4517 #[must_use]
4519 pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
4520 Self {
4521 name: name.into(),
4522 value: value.into(),
4523 meta: None,
4524 }
4525 }
4526
4527 #[must_use]
4533 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4534 self.meta = meta.into_option();
4535 self
4536 }
4537}
4538
4539#[cfg(feature = "unstable_session_system_prompt")]
4552#[serde_as]
4553#[skip_serializing_none]
4554#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4555#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4556#[serde(rename_all = "camelCase")]
4557#[non_exhaustive]
4558pub struct SystemPrompt {
4559 pub mode: SystemPromptMode,
4564 pub content: Vec<ContentBlock>,
4574 #[serde_as(deserialize_as = "DefaultOnError")]
4580 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4581 #[serde(default)]
4582 #[serde(rename = "_meta")]
4583 pub meta: Option<Meta>,
4584}
4585
4586#[cfg(feature = "unstable_session_system_prompt")]
4587impl SystemPrompt {
4588 #[must_use]
4593 pub fn new(content: Vec<ContentBlock>) -> Self {
4594 Self {
4595 mode: SystemPromptMode::default(),
4596 content,
4597 meta: None,
4598 }
4599 }
4600
4601 #[must_use]
4603 pub fn mode(mut self, mode: SystemPromptMode) -> Self {
4604 self.mode = mode;
4605 self
4606 }
4607
4608 #[must_use]
4614 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4615 self.meta = meta.into_option();
4616 self
4617 }
4618}
4619
4620#[cfg(feature = "unstable_session_system_prompt")]
4630#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4631#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
4632#[serde(rename_all = "snake_case")]
4633#[non_exhaustive]
4634pub enum SystemPromptMode {
4635 #[default]
4642 Append,
4643 Override,
4652 #[serde(untagged)]
4658 Other(String),
4659}
4660
4661#[serde_as]
4669#[skip_serializing_none]
4670#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4671#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4672#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_PROMPT_METHOD_NAME)))]
4673#[serde(rename_all = "camelCase")]
4674#[non_exhaustive]
4675pub struct PromptRequest {
4676 pub session_id: SessionId,
4678 #[cfg_attr(feature = "schemars", schemars(length(min = 1)))]
4695 pub prompt: Vec<ContentBlock>,
4696 #[serde_as(deserialize_as = "DefaultOnError")]
4702 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4703 #[serde(default)]
4704 #[serde(rename = "_meta")]
4705 pub meta: Option<Meta>,
4706}
4707
4708impl PromptRequest {
4709 #[must_use]
4711 pub fn new(session_id: impl Into<SessionId>, prompt: Vec<ContentBlock>) -> Self {
4712 Self {
4713 session_id: session_id.into(),
4714 prompt,
4715 meta: None,
4716 }
4717 }
4718
4719 #[must_use]
4725 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4726 self.meta = meta.into_option();
4727 self
4728 }
4729}
4730
4731#[serde_as]
4742#[skip_serializing_none]
4743#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4744#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4745#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_PROMPT_METHOD_NAME)))]
4746#[serde(rename_all = "camelCase")]
4747#[non_exhaustive]
4748pub struct PromptResponse {
4749 pub message_id: MessageId,
4757 #[serde_as(deserialize_as = "DefaultOnError")]
4763 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4764 #[serde(default)]
4765 #[serde(rename = "_meta")]
4766 pub meta: Option<Meta>,
4767}
4768
4769impl PromptResponse {
4770 #[must_use]
4772 pub fn new(message_id: impl Into<MessageId>) -> Self {
4773 Self {
4774 message_id: message_id.into(),
4775 meta: None,
4776 }
4777 }
4778
4779 #[must_use]
4785 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4786 self.meta = meta.into_option();
4787 self
4788 }
4789}
4790
4791#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4795#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
4796#[serde(tag = "stopReason", rename_all = "snake_case")]
4797#[non_exhaustive]
4798pub enum StopReason {
4799 EndTurn,
4802 MaxTokens,
4804 MaxTurnRequests,
4807 Refusal,
4809 Cancelled,
4820 Error(ErrorStopReason),
4826 #[serde(untagged)]
4832 Other(OtherStopReason),
4833}
4834
4835impl From<ErrorStopReason> for StopReason {
4836 fn from(value: ErrorStopReason) -> Self {
4837 Self::Error(value)
4838 }
4839}
4840
4841#[serde_as]
4843#[skip_serializing_none]
4844#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4845#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
4846#[serde(rename_all = "camelCase")]
4847#[non_exhaustive]
4848pub struct ErrorStopReason {
4849 #[serde_as(deserialize_as = "DefaultOnError")]
4854 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4855 #[serde(default)]
4856 pub error: Option<Box<Error>>,
4857}
4858
4859impl ErrorStopReason {
4860 #[must_use]
4862 pub fn new() -> Self {
4863 Self::default()
4864 }
4865
4866 #[must_use]
4868 pub fn error(mut self, error: impl IntoOption<Error>) -> Self {
4869 self.error = error.into_option().map(Box::new);
4870 self
4871 }
4872}
4873
4874#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4876#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
4877#[cfg_attr(feature = "schemars", schemars(inline))]
4878#[cfg_attr(feature = "schemars", schemars(transform = other_stop_reason_schema))]
4879#[serde(rename_all = "camelCase")]
4880#[non_exhaustive]
4881pub struct OtherStopReason {
4882 pub stop_reason: String,
4888 #[serde(flatten)]
4890 pub fields: BTreeMap<String, serde_json::Value>,
4891}
4892
4893impl OtherStopReason {
4894 #[must_use]
4896 pub fn new(
4897 stop_reason: impl Into<String>,
4898 mut fields: BTreeMap<String, serde_json::Value>,
4899 ) -> Self {
4900 remove_idle_state_update_fields(&mut fields);
4901 Self {
4902 stop_reason: stop_reason.into(),
4903 fields,
4904 }
4905 }
4906}
4907
4908impl<'de> Deserialize<'de> for OtherStopReason {
4909 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
4910 where
4911 D: serde::Deserializer<'de>,
4912 {
4913 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
4914 let stop_reason = fields
4915 .remove("stopReason")
4916 .ok_or_else(|| serde::de::Error::missing_field("stopReason"))?;
4917 let serde_json::Value::String(stop_reason) = stop_reason else {
4918 return Err(serde::de::Error::custom("`stopReason` must be a string"));
4919 };
4920
4921 if KNOWN_STOP_REASONS.contains(&stop_reason.as_str()) {
4922 return Err(serde::de::Error::custom(format!(
4923 "known stop reason `{stop_reason}` did not match its schema"
4924 )));
4925 }
4926 remove_idle_state_update_fields(&mut fields);
4927
4928 Ok(Self {
4929 stop_reason,
4930 fields,
4931 })
4932 }
4933}
4934
4935const KNOWN_STOP_REASONS: &[&str] = &[
4936 "end_turn",
4937 "max_tokens",
4938 "max_turn_requests",
4939 "refusal",
4940 "cancelled",
4941 "error",
4942];
4943
4944fn remove_idle_state_update_fields(fields: &mut BTreeMap<String, serde_json::Value>) {
4946 fields.remove("stopReason");
4947 fields.remove("usage");
4948 fields.remove("_meta");
4949}
4950
4951#[cfg(feature = "schemars")]
4952fn other_stop_reason_schema(schema: &mut Schema) {
4953 super::schema_util::reject_known_string_discriminators(
4954 schema,
4955 "stopReason",
4956 KNOWN_STOP_REASONS,
4957 );
4958}
4959
4960#[cfg(feature = "unstable_end_turn_token_usage")]
4966#[serde_as]
4967#[skip_serializing_none]
4968#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4969#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4970#[serde(rename_all = "camelCase")]
4971#[non_exhaustive]
4972pub struct Usage {
4973 pub total_tokens: u64,
4975 pub input_tokens: u64,
4977 pub output_tokens: u64,
4979 #[serde_as(deserialize_as = "DefaultOnError")]
4983 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4984 #[serde(default)]
4985 pub thought_tokens: Option<u64>,
4986 #[serde_as(deserialize_as = "DefaultOnError")]
4990 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4991 #[serde(default)]
4992 pub cached_read_tokens: Option<u64>,
4993 #[serde_as(deserialize_as = "DefaultOnError")]
4997 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4998 #[serde(default)]
4999 pub cached_write_tokens: Option<u64>,
5000 #[serde_as(deserialize_as = "DefaultOnError")]
5006 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5007 #[serde(default)]
5008 #[serde(rename = "_meta")]
5009 pub meta: Option<Meta>,
5010}
5011
5012#[cfg(feature = "unstable_end_turn_token_usage")]
5013impl Usage {
5014 #[must_use]
5016 pub fn new(total_tokens: u64, input_tokens: u64, output_tokens: u64) -> Self {
5017 Self {
5018 total_tokens,
5019 input_tokens,
5020 output_tokens,
5021 thought_tokens: None,
5022 cached_read_tokens: None,
5023 cached_write_tokens: None,
5024 meta: None,
5025 }
5026 }
5027
5028 #[must_use]
5030 pub fn thought_tokens(mut self, thought_tokens: impl IntoOption<u64>) -> Self {
5031 self.thought_tokens = thought_tokens.into_option();
5032 self
5033 }
5034
5035 #[must_use]
5037 pub fn cached_read_tokens(mut self, cached_read_tokens: impl IntoOption<u64>) -> Self {
5038 self.cached_read_tokens = cached_read_tokens.into_option();
5039 self
5040 }
5041
5042 #[must_use]
5044 pub fn cached_write_tokens(mut self, cached_write_tokens: impl IntoOption<u64>) -> Self {
5045 self.cached_write_tokens = cached_write_tokens.into_option();
5046 self
5047 }
5048
5049 #[must_use]
5055 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5056 self.meta = meta.into_option();
5057 self
5058 }
5059}
5060
5061#[cfg(feature = "unstable_llm_providers")]
5074#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5075#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5076#[serde(rename_all = "snake_case")]
5077#[non_exhaustive]
5078#[expect(clippy::doc_markdown)]
5079pub enum LlmProtocol {
5080 Anthropic,
5082 #[serde(rename = "openai")]
5084 OpenAi,
5085 Azure,
5087 Vertex,
5089 Bedrock,
5091 #[serde(untagged)]
5097 Other(String),
5098}
5099
5100#[cfg(feature = "unstable_llm_providers")]
5106#[serde_as]
5107#[skip_serializing_none]
5108#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5109#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5110#[serde(rename_all = "camelCase")]
5111#[non_exhaustive]
5112pub struct ProviderCurrentConfig {
5113 pub api_type: LlmProtocol,
5115 #[cfg_attr(feature = "schemars", schemars(url))]
5117 pub base_url: String,
5118 #[serde_as(deserialize_as = "DefaultOnError")]
5124 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5125 #[serde(default)]
5126 #[serde(rename = "_meta")]
5127 pub meta: Option<Meta>,
5128}
5129
5130#[cfg(feature = "unstable_llm_providers")]
5131impl ProviderCurrentConfig {
5132 #[must_use]
5134 pub fn new(api_type: LlmProtocol, base_url: impl Into<String>) -> Self {
5135 Self {
5136 api_type,
5137 base_url: base_url.into(),
5138 meta: None,
5139 }
5140 }
5141
5142 #[must_use]
5148 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5149 self.meta = meta.into_option();
5150 self
5151 }
5152}
5153
5154#[cfg(feature = "unstable_llm_providers")]
5160#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5161#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
5162#[serde(transparent)]
5163#[from(forward)]
5164#[non_exhaustive]
5165pub struct ProviderId(pub Arc<str>);
5166
5167#[cfg(feature = "unstable_llm_providers")]
5168impl ProviderId {
5169 #[must_use]
5171 pub fn new(id: impl Into<Self>) -> Self {
5172 id.into()
5173 }
5174}
5175
5176#[cfg(feature = "unstable_llm_providers")]
5182#[serde_as]
5183#[skip_serializing_none]
5184#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5185#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5186#[serde(rename_all = "camelCase")]
5187#[non_exhaustive]
5188pub struct ProviderInfo {
5189 pub provider_id: ProviderId,
5191 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
5193 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
5194 pub supported: Vec<LlmProtocol>,
5195 pub required: bool,
5198 #[serde(default)]
5201 pub current: Option<ProviderCurrentConfig>,
5202 #[serde_as(deserialize_as = "DefaultOnError")]
5208 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5209 #[serde(default)]
5210 #[serde(rename = "_meta")]
5211 pub meta: Option<Meta>,
5212}
5213
5214#[cfg(feature = "unstable_llm_providers")]
5215impl ProviderInfo {
5216 #[must_use]
5218 pub fn new(
5219 provider_id: impl Into<ProviderId>,
5220 supported: Vec<LlmProtocol>,
5221 required: bool,
5222 current: impl IntoOption<ProviderCurrentConfig>,
5223 ) -> Self {
5224 Self {
5225 provider_id: provider_id.into(),
5226 supported,
5227 required,
5228 current: current.into_option(),
5229 meta: None,
5230 }
5231 }
5232
5233 #[must_use]
5239 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5240 self.meta = meta.into_option();
5241 self
5242 }
5243}
5244
5245#[cfg(feature = "unstable_llm_providers")]
5246crate::serde_util::default_on_null! {
5247 #[serde_as]
5253 #[skip_serializing_none]
5254 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5255 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
5256 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_LIST_METHOD_NAME)))]
5257 #[serde(rename_all = "camelCase")]
5258 #[non_exhaustive]
5259 pub struct ListProvidersRequest {
5260 #[serde_as(deserialize_as = "DefaultOnError")]
5266 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5267 #[serde(default)]
5268 #[serde(rename = "_meta")]
5269 pub meta: Option<Meta>,
5270 }
5271}
5272
5273#[cfg(feature = "unstable_llm_providers")]
5274impl ListProvidersRequest {
5275 #[must_use]
5277 pub fn new() -> Self {
5278 Self::default()
5279 }
5280
5281 #[must_use]
5287 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5288 self.meta = meta.into_option();
5289 self
5290 }
5291}
5292
5293#[cfg(feature = "unstable_llm_providers")]
5299#[serde_as]
5300#[skip_serializing_none]
5301#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5302#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5303#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_LIST_METHOD_NAME)))]
5304#[serde(rename_all = "camelCase")]
5305#[non_exhaustive]
5306pub struct ListProvidersResponse {
5307 pub providers: Vec<ProviderInfo>,
5309 #[serde_as(deserialize_as = "DefaultOnError")]
5315 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5316 #[serde(default)]
5317 #[serde(rename = "_meta")]
5318 pub meta: Option<Meta>,
5319}
5320
5321#[cfg(feature = "unstable_llm_providers")]
5322impl ListProvidersResponse {
5323 #[must_use]
5325 pub fn new(providers: Vec<ProviderInfo>) -> Self {
5326 Self {
5327 providers,
5328 meta: None,
5329 }
5330 }
5331
5332 #[must_use]
5338 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5339 self.meta = meta.into_option();
5340 self
5341 }
5342}
5343
5344#[cfg(feature = "unstable_llm_providers")]
5352#[serde_as]
5353#[skip_serializing_none]
5354#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5355#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
5356#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_SET_METHOD_NAME)))]
5357#[serde(rename_all = "camelCase")]
5358#[non_exhaustive]
5359pub struct SetProviderRequest {
5360 pub provider_id: ProviderId,
5362 pub api_type: LlmProtocol,
5364 #[cfg_attr(feature = "schemars", schemars(url))]
5366 pub base_url: String,
5367 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
5370 pub headers: HashMap<String, String>,
5371 #[serde_as(deserialize_as = "DefaultOnError")]
5377 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5378 #[serde(default)]
5379 #[serde(rename = "_meta")]
5380 pub meta: Option<Meta>,
5381}
5382
5383#[cfg(feature = "unstable_llm_providers")]
5384impl SetProviderRequest {
5385 #[must_use]
5387 pub fn new(
5388 provider_id: impl Into<ProviderId>,
5389 api_type: LlmProtocol,
5390 base_url: impl Into<String>,
5391 ) -> Self {
5392 Self {
5393 provider_id: provider_id.into(),
5394 api_type,
5395 base_url: base_url.into(),
5396 headers: HashMap::new(),
5397 meta: None,
5398 }
5399 }
5400
5401 #[must_use]
5404 pub fn headers(mut self, headers: HashMap<String, String>) -> Self {
5405 self.headers = headers;
5406 self
5407 }
5408
5409 #[must_use]
5415 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5416 self.meta = meta.into_option();
5417 self
5418 }
5419}
5420
5421#[cfg(feature = "unstable_llm_providers")]
5422crate::serde_util::default_on_null! {
5423 #[serde_as]
5429 #[skip_serializing_none]
5430 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5431 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
5432 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_SET_METHOD_NAME)))]
5433 #[serde(rename_all = "camelCase")]
5434 #[non_exhaustive]
5435 pub struct SetProviderResponse {
5436 #[serde_as(deserialize_as = "DefaultOnError")]
5442 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5443 #[serde(default)]
5444 #[serde(rename = "_meta")]
5445 pub meta: Option<Meta>,
5446 }
5447}
5448
5449#[cfg(feature = "unstable_llm_providers")]
5450impl SetProviderResponse {
5451 #[must_use]
5453 pub fn new() -> Self {
5454 Self::default()
5455 }
5456
5457 #[must_use]
5463 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5464 self.meta = meta.into_option();
5465 self
5466 }
5467}
5468
5469#[cfg(feature = "unstable_llm_providers")]
5475#[serde_as]
5476#[skip_serializing_none]
5477#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5478#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5479#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_DISABLE_METHOD_NAME)))]
5480#[serde(rename_all = "camelCase")]
5481#[non_exhaustive]
5482pub struct DisableProviderRequest {
5483 pub provider_id: ProviderId,
5485 #[serde_as(deserialize_as = "DefaultOnError")]
5491 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5492 #[serde(default)]
5493 #[serde(rename = "_meta")]
5494 pub meta: Option<Meta>,
5495}
5496
5497#[cfg(feature = "unstable_llm_providers")]
5498impl DisableProviderRequest {
5499 #[must_use]
5501 pub fn new(provider_id: impl Into<ProviderId>) -> Self {
5502 Self {
5503 provider_id: provider_id.into(),
5504 meta: None,
5505 }
5506 }
5507
5508 #[must_use]
5514 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5515 self.meta = meta.into_option();
5516 self
5517 }
5518}
5519
5520#[cfg(feature = "unstable_llm_providers")]
5521crate::serde_util::default_on_null! {
5522 #[serde_as]
5528 #[skip_serializing_none]
5529 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5530 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
5531 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_DISABLE_METHOD_NAME)))]
5532 #[serde(rename_all = "camelCase")]
5533 #[non_exhaustive]
5534 pub struct DisableProviderResponse {
5535 #[serde_as(deserialize_as = "DefaultOnError")]
5541 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5542 #[serde(default)]
5543 #[serde(rename = "_meta")]
5544 pub meta: Option<Meta>,
5545 }
5546}
5547
5548#[cfg(feature = "unstable_llm_providers")]
5549impl DisableProviderResponse {
5550 #[must_use]
5552 pub fn new() -> Self {
5553 Self::default()
5554 }
5555
5556 #[must_use]
5562 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5563 self.meta = meta.into_option();
5564 self
5565 }
5566}
5567
5568#[serde_as]
5577#[skip_serializing_none]
5578#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5579#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5580#[serde(rename_all = "camelCase")]
5581#[non_exhaustive]
5582pub struct AgentCapabilities {
5583 #[serde_as(deserialize_as = "DefaultOnError")]
5591 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5592 #[serde(default)]
5593 pub session: Option<SessionCapabilities>,
5594 #[serde_as(deserialize_as = "DefaultOnError")]
5601 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5602 #[serde(default)]
5603 pub auth: Option<AgentAuthCapabilities>,
5604 #[cfg(feature = "unstable_llm_providers")]
5613 #[serde_as(deserialize_as = "DefaultOnError")]
5614 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5615 #[serde(default)]
5616 pub providers: Option<ProvidersCapabilities>,
5617 #[serde_as(deserialize_as = "DefaultOnError")]
5623 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5624 #[serde(default)]
5625 #[serde(rename = "_meta")]
5626 pub meta: Option<Meta>,
5627}
5628
5629impl AgentCapabilities {
5630 #[must_use]
5632 pub fn new() -> Self {
5633 Self::default()
5634 }
5635
5636 #[must_use]
5644 pub fn session(mut self, session: impl IntoOption<SessionCapabilities>) -> Self {
5645 self.session = session.into_option();
5646 self
5647 }
5648
5649 #[must_use]
5653 pub fn auth(mut self, auth: impl IntoOption<AgentAuthCapabilities>) -> Self {
5654 self.auth = auth.into_option();
5655 self
5656 }
5657
5658 #[cfg(feature = "unstable_llm_providers")]
5664 #[must_use]
5665 pub fn providers(mut self, providers: impl IntoOption<ProvidersCapabilities>) -> Self {
5666 self.providers = providers.into_option();
5667 self
5668 }
5669
5670 #[must_use]
5676 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5677 self.meta = meta.into_option();
5678 self
5679 }
5680}
5681
5682#[cfg(feature = "unstable_llm_providers")]
5690#[serde_as]
5691#[skip_serializing_none]
5692#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5693#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5694#[non_exhaustive]
5695pub struct ProvidersCapabilities {
5696 #[serde_as(deserialize_as = "DefaultOnError")]
5702 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5703 #[serde(default)]
5704 #[serde(rename = "_meta")]
5705 pub meta: Option<Meta>,
5706}
5707
5708#[cfg(feature = "unstable_llm_providers")]
5709impl ProvidersCapabilities {
5710 #[must_use]
5712 pub fn new() -> Self {
5713 Self::default()
5714 }
5715
5716 #[must_use]
5722 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5723 self.meta = meta.into_option();
5724 self
5725 }
5726}
5727
5728#[serde_as]
5740#[skip_serializing_none]
5741#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5742#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5743#[serde(rename_all = "camelCase")]
5744#[non_exhaustive]
5745pub struct SessionCapabilities {
5746 #[serde_as(deserialize_as = "DefaultOnError")]
5752 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5753 #[serde(default)]
5754 pub prompt: Option<PromptCapabilities>,
5755 #[serde_as(deserialize_as = "DefaultOnError")]
5760 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5761 #[serde(default)]
5762 pub mcp: Option<McpCapabilities>,
5763 #[serde_as(deserialize_as = "DefaultOnError")]
5768 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5769 #[serde(default)]
5770 pub delete: Option<SessionDeleteCapabilities>,
5771 #[serde_as(deserialize_as = "DefaultOnError")]
5780 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5781 #[serde(default)]
5782 pub additional_directories: Option<SessionAdditionalDirectoriesCapabilities>,
5783 #[cfg(feature = "unstable_session_system_prompt")]
5792 #[serde_as(deserialize_as = "DefaultOnError")]
5793 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5794 #[serde(default)]
5795 pub system_prompt: Option<SessionSystemPromptCapabilities>,
5796 #[cfg(feature = "unstable_session_fork")]
5805 #[serde_as(deserialize_as = "DefaultOnError")]
5806 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5807 #[serde(default)]
5808 pub fork: Option<SessionForkCapabilities>,
5809 #[cfg(feature = "unstable_session_archive")]
5819 #[serde_as(deserialize_as = "DefaultOnError")]
5820 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5821 #[serde(default)]
5822 pub archive: Option<SessionArchiveCapabilities>,
5823 #[cfg(feature = "unstable_session_status")]
5832 #[serde_as(deserialize_as = "DefaultOnError")]
5833 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5834 #[serde(default)]
5835 pub status: Option<SessionStatusCapabilities>,
5836 #[cfg(feature = "unstable_session_set_title")]
5848 #[serde_as(deserialize_as = "DefaultOnError")]
5849 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5850 #[serde(default)]
5851 pub title: Option<SessionTitleCapabilities>,
5852 #[serde_as(deserialize_as = "DefaultOnError")]
5858 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5859 #[serde(default)]
5860 #[serde(rename = "_meta")]
5861 pub meta: Option<Meta>,
5862}
5863
5864impl SessionCapabilities {
5865 #[must_use]
5867 pub fn new() -> Self {
5868 Self::default()
5869 }
5870
5871 #[must_use]
5877 pub fn prompt(mut self, prompt: impl IntoOption<PromptCapabilities>) -> Self {
5878 self.prompt = prompt.into_option();
5879 self
5880 }
5881
5882 #[must_use]
5887 pub fn mcp(mut self, mcp: impl IntoOption<McpCapabilities>) -> Self {
5888 self.mcp = mcp.into_option();
5889 self
5890 }
5891
5892 #[must_use]
5897 pub fn delete(mut self, delete: impl IntoOption<SessionDeleteCapabilities>) -> Self {
5898 self.delete = delete.into_option();
5899 self
5900 }
5901
5902 #[must_use]
5911 pub fn additional_directories(
5912 mut self,
5913 additional_directories: impl IntoOption<SessionAdditionalDirectoriesCapabilities>,
5914 ) -> Self {
5915 self.additional_directories = additional_directories.into_option();
5916 self
5917 }
5918
5919 #[cfg(feature = "unstable_session_system_prompt")]
5928 #[must_use]
5929 pub fn system_prompt(
5930 mut self,
5931 system_prompt: impl IntoOption<SessionSystemPromptCapabilities>,
5932 ) -> Self {
5933 self.system_prompt = system_prompt.into_option();
5934 self
5935 }
5936
5937 #[cfg(feature = "unstable_session_fork")]
5938 #[must_use]
5943 pub fn fork(mut self, fork: impl IntoOption<SessionForkCapabilities>) -> Self {
5944 self.fork = fork.into_option();
5945 self
5946 }
5947
5948 #[cfg(feature = "unstable_session_archive")]
5958 #[must_use]
5959 pub fn archive(mut self, archive: impl IntoOption<SessionArchiveCapabilities>) -> Self {
5960 self.archive = archive.into_option();
5961 self
5962 }
5963
5964 #[cfg(feature = "unstable_session_status")]
5973 #[must_use]
5974 pub fn status(mut self, status: impl IntoOption<SessionStatusCapabilities>) -> Self {
5975 self.status = status.into_option();
5976 self
5977 }
5978
5979 #[cfg(feature = "unstable_session_set_title")]
5991 #[must_use]
5992 pub fn title(mut self, title: impl IntoOption<SessionTitleCapabilities>) -> Self {
5993 self.title = title.into_option();
5994 self
5995 }
5996
5997 #[must_use]
6003 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6004 self.meta = meta.into_option();
6005 self
6006 }
6007}
6008
6009#[serde_as]
6013#[skip_serializing_none]
6014#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6015#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6016#[non_exhaustive]
6017pub struct SessionDeleteCapabilities {
6018 #[serde_as(deserialize_as = "DefaultOnError")]
6024 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6025 #[serde(default)]
6026 #[serde(rename = "_meta")]
6027 pub meta: Option<Meta>,
6028}
6029
6030impl SessionDeleteCapabilities {
6031 #[must_use]
6033 pub fn new() -> Self {
6034 Self::default()
6035 }
6036
6037 #[must_use]
6043 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6044 self.meta = meta.into_option();
6045 self
6046 }
6047}
6048
6049#[serde_as]
6056#[skip_serializing_none]
6057#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6058#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6059#[non_exhaustive]
6060pub struct SessionAdditionalDirectoriesCapabilities {
6061 #[serde_as(deserialize_as = "DefaultOnError")]
6067 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6068 #[serde(default)]
6069 #[serde(rename = "_meta")]
6070 pub meta: Option<Meta>,
6071}
6072
6073impl SessionAdditionalDirectoriesCapabilities {
6074 #[must_use]
6076 pub fn new() -> Self {
6077 Self::default()
6078 }
6079
6080 #[must_use]
6086 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6087 self.meta = meta.into_option();
6088 self
6089 }
6090}
6091
6092#[cfg(feature = "unstable_session_system_prompt")]
6101#[serde_as]
6102#[skip_serializing_none]
6103#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6104#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6105#[serde(rename_all = "camelCase")]
6106#[non_exhaustive]
6107pub struct SessionSystemPromptCapabilities {
6108 #[serde_as(deserialize_as = "DefaultOnError")]
6112 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6113 #[serde(default)]
6114 pub mode: Option<SessionSystemPromptModeCapabilities>,
6115 #[serde_as(deserialize_as = "DefaultOnError")]
6121 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6122 #[serde(default)]
6123 #[serde(rename = "_meta")]
6124 pub meta: Option<Meta>,
6125}
6126
6127#[cfg(feature = "unstable_session_system_prompt")]
6128impl SessionSystemPromptCapabilities {
6129 #[must_use]
6131 pub fn new() -> Self {
6132 Self::default()
6133 }
6134
6135 #[must_use]
6139 pub fn mode(mut self, mode: impl IntoOption<SessionSystemPromptModeCapabilities>) -> Self {
6140 self.mode = mode.into_option();
6141 self
6142 }
6143
6144 #[must_use]
6150 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6151 self.meta = meta.into_option();
6152 self
6153 }
6154}
6155
6156#[cfg(feature = "unstable_session_system_prompt")]
6165#[serde_as]
6166#[skip_serializing_none]
6167#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6168#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6169#[serde(rename_all = "camelCase")]
6170#[non_exhaustive]
6171pub struct SessionSystemPromptModeCapabilities {
6172 #[serde_as(deserialize_as = "DefaultOnError")]
6178 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6179 #[serde(default, rename = "override")]
6180 pub override_: Option<SessionSystemPromptOverrideCapabilities>,
6181 #[serde_as(deserialize_as = "DefaultOnError")]
6187 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6188 #[serde(default)]
6189 #[serde(rename = "_meta")]
6190 pub meta: Option<Meta>,
6191}
6192
6193#[cfg(feature = "unstable_session_system_prompt")]
6194impl SessionSystemPromptModeCapabilities {
6195 #[must_use]
6197 pub fn new() -> Self {
6198 Self::default()
6199 }
6200
6201 #[must_use]
6207 pub fn override_(
6208 mut self,
6209 override_: impl IntoOption<SessionSystemPromptOverrideCapabilities>,
6210 ) -> Self {
6211 self.override_ = override_.into_option();
6212 self
6213 }
6214
6215 #[must_use]
6221 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6222 self.meta = meta.into_option();
6223 self
6224 }
6225}
6226
6227#[cfg(feature = "unstable_session_system_prompt")]
6236#[serde_as]
6237#[skip_serializing_none]
6238#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6239#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6240#[non_exhaustive]
6241pub struct SessionSystemPromptOverrideCapabilities {
6242 #[serde_as(deserialize_as = "DefaultOnError")]
6248 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6249 #[serde(default)]
6250 #[serde(rename = "_meta")]
6251 pub meta: Option<Meta>,
6252}
6253
6254#[cfg(feature = "unstable_session_system_prompt")]
6255impl SessionSystemPromptOverrideCapabilities {
6256 #[must_use]
6258 pub fn new() -> Self {
6259 Self::default()
6260 }
6261
6262 #[must_use]
6268 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6269 self.meta = meta.into_option();
6270 self
6271 }
6272}
6273
6274#[cfg(feature = "unstable_session_fork")]
6282#[serde_as]
6283#[skip_serializing_none]
6284#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6285#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6286#[non_exhaustive]
6287pub struct SessionForkCapabilities {
6288 #[serde_as(deserialize_as = "DefaultOnError")]
6294 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6295 #[serde(default)]
6296 #[serde(rename = "_meta")]
6297 pub meta: Option<Meta>,
6298}
6299
6300#[cfg(feature = "unstable_session_fork")]
6301impl SessionForkCapabilities {
6302 #[must_use]
6304 pub fn new() -> Self {
6305 Self::default()
6306 }
6307
6308 #[must_use]
6314 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6315 self.meta = meta.into_option();
6316 self
6317 }
6318}
6319
6320#[cfg(feature = "unstable_session_archive")]
6329#[serde_as]
6330#[skip_serializing_none]
6331#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6332#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6333#[non_exhaustive]
6334pub struct SessionArchiveCapabilities {
6335 #[serde_as(deserialize_as = "DefaultOnError")]
6341 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6342 #[serde(default)]
6343 #[serde(rename = "_meta")]
6344 pub meta: Option<Meta>,
6345}
6346
6347#[cfg(feature = "unstable_session_archive")]
6348impl SessionArchiveCapabilities {
6349 #[must_use]
6351 pub fn new() -> Self {
6352 Self::default()
6353 }
6354
6355 #[must_use]
6361 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6362 self.meta = meta.into_option();
6363 self
6364 }
6365}
6366
6367#[cfg(feature = "unstable_session_status")]
6375#[serde_as]
6376#[skip_serializing_none]
6377#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6378#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6379#[non_exhaustive]
6380pub struct SessionStatusCapabilities {
6381 #[serde_as(deserialize_as = "DefaultOnError")]
6387 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6388 #[serde(default)]
6389 #[serde(rename = "_meta")]
6390 pub meta: Option<Meta>,
6391}
6392
6393#[cfg(feature = "unstable_session_status")]
6394impl SessionStatusCapabilities {
6395 #[must_use]
6397 pub fn new() -> Self {
6398 Self::default()
6399 }
6400
6401 #[must_use]
6407 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6408 self.meta = meta.into_option();
6409 self
6410 }
6411}
6412
6413#[cfg(feature = "unstable_session_set_title")]
6422#[serde_as]
6423#[skip_serializing_none]
6424#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6425#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6426#[non_exhaustive]
6427pub struct SessionTitleCapabilities {
6428 #[serde_as(deserialize_as = "DefaultOnError")]
6434 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6435 #[serde(default)]
6436 #[serde(rename = "_meta")]
6437 pub meta: Option<Meta>,
6438}
6439
6440#[cfg(feature = "unstable_session_set_title")]
6441impl SessionTitleCapabilities {
6442 #[must_use]
6444 pub fn new() -> Self {
6445 Self::default()
6446 }
6447
6448 #[must_use]
6454 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6455 self.meta = meta.into_option();
6456 self
6457 }
6458}
6459
6460#[serde_as]
6473#[skip_serializing_none]
6474#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6475#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6476#[serde(rename_all = "camelCase")]
6477#[non_exhaustive]
6478pub struct PromptCapabilities {
6479 #[serde_as(deserialize_as = "DefaultOnError")]
6484 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6485 #[serde(default)]
6486 pub image: Option<PromptImageCapabilities>,
6487 #[serde_as(deserialize_as = "DefaultOnError")]
6492 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6493 #[serde(default)]
6494 pub audio: Option<PromptAudioCapabilities>,
6495 #[serde_as(deserialize_as = "DefaultOnError")]
6503 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6504 #[serde(default)]
6505 pub embedded_context: Option<PromptEmbeddedContextCapabilities>,
6506 #[serde_as(deserialize_as = "DefaultOnError")]
6512 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6513 #[serde(default)]
6514 #[serde(rename = "_meta")]
6515 pub meta: Option<Meta>,
6516}
6517
6518impl PromptCapabilities {
6519 #[must_use]
6521 pub fn new() -> Self {
6522 Self::default()
6523 }
6524
6525 #[must_use]
6530 pub fn image(mut self, image: impl IntoOption<PromptImageCapabilities>) -> Self {
6531 self.image = image.into_option();
6532 self
6533 }
6534
6535 #[must_use]
6540 pub fn audio(mut self, audio: impl IntoOption<PromptAudioCapabilities>) -> Self {
6541 self.audio = audio.into_option();
6542 self
6543 }
6544
6545 #[must_use]
6553 pub fn embedded_context(
6554 mut self,
6555 embedded_context: impl IntoOption<PromptEmbeddedContextCapabilities>,
6556 ) -> Self {
6557 self.embedded_context = embedded_context.into_option();
6558 self
6559 }
6560
6561 #[must_use]
6567 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6568 self.meta = meta.into_option();
6569 self
6570 }
6571}
6572
6573#[serde_as]
6577#[skip_serializing_none]
6578#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6579#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6580#[non_exhaustive]
6581pub struct PromptImageCapabilities {
6582 #[serde_as(deserialize_as = "DefaultOnError")]
6588 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6589 #[serde(default)]
6590 #[serde(rename = "_meta")]
6591 pub meta: Option<Meta>,
6592}
6593
6594impl PromptImageCapabilities {
6595 #[must_use]
6597 pub fn new() -> Self {
6598 Self::default()
6599 }
6600
6601 #[must_use]
6607 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6608 self.meta = meta.into_option();
6609 self
6610 }
6611}
6612
6613#[serde_as]
6617#[skip_serializing_none]
6618#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6619#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6620#[non_exhaustive]
6621pub struct PromptAudioCapabilities {
6622 #[serde_as(deserialize_as = "DefaultOnError")]
6628 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6629 #[serde(default)]
6630 #[serde(rename = "_meta")]
6631 pub meta: Option<Meta>,
6632}
6633
6634impl PromptAudioCapabilities {
6635 #[must_use]
6637 pub fn new() -> Self {
6638 Self::default()
6639 }
6640
6641 #[must_use]
6647 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6648 self.meta = meta.into_option();
6649 self
6650 }
6651}
6652
6653#[serde_as]
6657#[skip_serializing_none]
6658#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6659#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6660#[non_exhaustive]
6661pub struct PromptEmbeddedContextCapabilities {
6662 #[serde_as(deserialize_as = "DefaultOnError")]
6668 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6669 #[serde(default)]
6670 #[serde(rename = "_meta")]
6671 pub meta: Option<Meta>,
6672}
6673
6674impl PromptEmbeddedContextCapabilities {
6675 #[must_use]
6677 pub fn new() -> Self {
6678 Self::default()
6679 }
6680
6681 #[must_use]
6687 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6688 self.meta = meta.into_option();
6689 self
6690 }
6691}
6692
6693#[serde_as]
6695#[skip_serializing_none]
6696#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6697#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6698#[serde(rename_all = "camelCase")]
6699#[non_exhaustive]
6700pub struct McpCapabilities {
6701 #[serde_as(deserialize_as = "DefaultOnError")]
6706 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6707 #[serde(default)]
6708 pub stdio: Option<McpStdioCapabilities>,
6709 #[serde_as(deserialize_as = "DefaultOnError")]
6714 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6715 #[serde(default)]
6716 pub http: Option<McpHttpCapabilities>,
6717 #[cfg(feature = "unstable_mcp_over_acp")]
6726 #[serde_as(deserialize_as = "DefaultOnError")]
6727 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6728 #[serde(default)]
6729 pub acp: Option<McpAcpCapabilities>,
6730 #[serde_as(deserialize_as = "DefaultOnError")]
6736 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6737 #[serde(default)]
6738 #[serde(rename = "_meta")]
6739 pub meta: Option<Meta>,
6740}
6741
6742impl McpCapabilities {
6743 #[must_use]
6745 pub fn new() -> Self {
6746 Self::default()
6747 }
6748
6749 #[must_use]
6754 pub fn stdio(mut self, stdio: impl IntoOption<McpStdioCapabilities>) -> Self {
6755 self.stdio = stdio.into_option();
6756 self
6757 }
6758
6759 #[must_use]
6764 pub fn http(mut self, http: impl IntoOption<McpHttpCapabilities>) -> Self {
6765 self.http = http.into_option();
6766 self
6767 }
6768
6769 #[cfg(feature = "unstable_mcp_over_acp")]
6775 #[must_use]
6779 pub fn acp(mut self, acp: impl IntoOption<McpAcpCapabilities>) -> Self {
6780 self.acp = acp.into_option();
6781 self
6782 }
6783
6784 #[must_use]
6790 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6791 self.meta = meta.into_option();
6792 self
6793 }
6794}
6795
6796#[serde_as]
6800#[skip_serializing_none]
6801#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6802#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6803#[non_exhaustive]
6804pub struct McpStdioCapabilities {
6805 #[serde_as(deserialize_as = "DefaultOnError")]
6811 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6812 #[serde(default)]
6813 #[serde(rename = "_meta")]
6814 pub meta: Option<Meta>,
6815}
6816
6817impl McpStdioCapabilities {
6818 #[must_use]
6820 pub fn new() -> Self {
6821 Self::default()
6822 }
6823
6824 #[must_use]
6830 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6831 self.meta = meta.into_option();
6832 self
6833 }
6834}
6835
6836#[serde_as]
6840#[skip_serializing_none]
6841#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6842#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6843#[non_exhaustive]
6844pub struct McpHttpCapabilities {
6845 #[serde_as(deserialize_as = "DefaultOnError")]
6851 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6852 #[serde(default)]
6853 #[serde(rename = "_meta")]
6854 pub meta: Option<Meta>,
6855}
6856
6857impl McpHttpCapabilities {
6858 #[must_use]
6860 pub fn new() -> Self {
6861 Self::default()
6862 }
6863
6864 #[must_use]
6870 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6871 self.meta = meta.into_option();
6872 self
6873 }
6874}
6875
6876#[cfg(feature = "unstable_mcp_over_acp")]
6884#[serde_as]
6885#[skip_serializing_none]
6886#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6887#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6888#[non_exhaustive]
6889pub struct McpAcpCapabilities {
6890 #[serde_as(deserialize_as = "DefaultOnError")]
6896 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6897 #[serde(default)]
6898 #[serde(rename = "_meta")]
6899 pub meta: Option<Meta>,
6900}
6901
6902#[cfg(feature = "unstable_mcp_over_acp")]
6903impl McpAcpCapabilities {
6904 #[must_use]
6906 pub fn new() -> Self {
6907 Self::default()
6908 }
6909
6910 #[must_use]
6916 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6917 self.meta = meta.into_option();
6918 self
6919 }
6920}
6921
6922#[serde_as]
6926#[skip_serializing_none]
6927#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6928#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6929#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_CANCEL_METHOD_NAME)))]
6930#[serde(rename_all = "camelCase")]
6931#[non_exhaustive]
6932pub struct CancelSessionNotification {
6933 pub session_id: SessionId,
6935 #[serde_as(deserialize_as = "DefaultOnError")]
6941 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6942 #[serde(default)]
6943 #[serde(rename = "_meta")]
6944 pub meta: Option<Meta>,
6945}
6946
6947impl CancelSessionNotification {
6948 #[must_use]
6950 pub fn new(session_id: impl Into<SessionId>) -> Self {
6951 Self {
6952 session_id: session_id.into(),
6953 meta: None,
6954 }
6955 }
6956
6957 #[must_use]
6963 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6964 self.meta = meta.into_option();
6965 self
6966 }
6967}
6968
6969#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6975#[non_exhaustive]
6976pub struct AgentMethodNames {
6977 pub initialize: &'static str,
6979 pub auth_login: &'static str,
6981 #[cfg(feature = "unstable_llm_providers")]
6983 pub providers_list: &'static str,
6984 #[cfg(feature = "unstable_llm_providers")]
6986 pub providers_set: &'static str,
6987 #[cfg(feature = "unstable_llm_providers")]
6989 pub providers_disable: &'static str,
6990 pub session_new: &'static str,
6992 pub session_set_config_option: &'static str,
6994 pub session_prompt: &'static str,
6996 pub session_cancel: &'static str,
6998 #[cfg(feature = "unstable_mcp_over_acp")]
7000 pub mcp_message: &'static str,
7001 pub session_list: &'static str,
7003 pub session_delete: &'static str,
7005 #[cfg(feature = "unstable_session_fork")]
7007 pub session_fork: &'static str,
7008 #[cfg(feature = "unstable_session_archive")]
7010 pub session_archive: &'static str,
7011 #[cfg(feature = "unstable_session_archive")]
7013 pub session_unarchive: &'static str,
7014 #[cfg(feature = "unstable_session_status")]
7016 pub session_status: &'static str,
7017 #[cfg(feature = "unstable_session_set_title")]
7019 pub session_set_title: &'static str,
7020 pub session_resume: &'static str,
7022 pub session_close: &'static str,
7024 pub tool_call_cancel: &'static str,
7026 pub terminal_cancel: &'static str,
7028 pub auth_logout: &'static str,
7030 #[cfg(feature = "unstable_auth_status")]
7032 pub auth_status: &'static str,
7033}
7034
7035pub const AGENT_METHOD_NAMES: AgentMethodNames = AgentMethodNames {
7037 initialize: INITIALIZE_METHOD_NAME,
7038 auth_login: AUTH_LOGIN_METHOD_NAME,
7039 #[cfg(feature = "unstable_llm_providers")]
7040 providers_list: PROVIDERS_LIST_METHOD_NAME,
7041 #[cfg(feature = "unstable_llm_providers")]
7042 providers_set: PROVIDERS_SET_METHOD_NAME,
7043 #[cfg(feature = "unstable_llm_providers")]
7044 providers_disable: PROVIDERS_DISABLE_METHOD_NAME,
7045 session_new: SESSION_NEW_METHOD_NAME,
7046 session_set_config_option: SESSION_SET_CONFIG_OPTION_METHOD_NAME,
7047 session_prompt: SESSION_PROMPT_METHOD_NAME,
7048 session_cancel: SESSION_CANCEL_METHOD_NAME,
7049 #[cfg(feature = "unstable_mcp_over_acp")]
7050 mcp_message: MCP_MESSAGE_METHOD_NAME,
7051 session_list: SESSION_LIST_METHOD_NAME,
7052 session_delete: SESSION_DELETE_METHOD_NAME,
7053 #[cfg(feature = "unstable_session_fork")]
7054 session_fork: SESSION_FORK_METHOD_NAME,
7055 #[cfg(feature = "unstable_session_archive")]
7056 session_archive: SESSION_ARCHIVE_METHOD_NAME,
7057 #[cfg(feature = "unstable_session_archive")]
7058 session_unarchive: SESSION_UNARCHIVE_METHOD_NAME,
7059 #[cfg(feature = "unstable_session_status")]
7060 session_status: SESSION_STATUS_METHOD_NAME,
7061 #[cfg(feature = "unstable_session_set_title")]
7062 session_set_title: SESSION_SET_TITLE_METHOD_NAME,
7063 session_resume: SESSION_RESUME_METHOD_NAME,
7064 session_close: SESSION_CLOSE_METHOD_NAME,
7065 tool_call_cancel: TOOL_CALL_CANCEL_METHOD_NAME,
7066 terminal_cancel: TERMINAL_CANCEL_METHOD_NAME,
7067 auth_logout: AUTH_LOGOUT_METHOD_NAME,
7068 #[cfg(feature = "unstable_auth_status")]
7069 auth_status: AUTH_STATUS_METHOD_NAME,
7070};
7071
7072pub(crate) const INITIALIZE_METHOD_NAME: &str = "initialize";
7074pub(crate) const AUTH_LOGIN_METHOD_NAME: &str = "auth/login";
7076#[cfg(feature = "unstable_llm_providers")]
7078pub(crate) const PROVIDERS_LIST_METHOD_NAME: &str = "providers/list";
7079#[cfg(feature = "unstable_llm_providers")]
7081pub(crate) const PROVIDERS_SET_METHOD_NAME: &str = "providers/set";
7082#[cfg(feature = "unstable_llm_providers")]
7084pub(crate) const PROVIDERS_DISABLE_METHOD_NAME: &str = "providers/disable";
7085pub(crate) const SESSION_NEW_METHOD_NAME: &str = "session/new";
7087pub(crate) const SESSION_SET_CONFIG_OPTION_METHOD_NAME: &str = "session/set_config_option";
7089pub(crate) const SESSION_PROMPT_METHOD_NAME: &str = "session/prompt";
7091pub(crate) const SESSION_CANCEL_METHOD_NAME: &str = "session/cancel";
7093pub(crate) const SESSION_LIST_METHOD_NAME: &str = "session/list";
7095pub(crate) const SESSION_DELETE_METHOD_NAME: &str = "session/delete";
7097#[cfg(feature = "unstable_session_fork")]
7099pub(crate) const SESSION_FORK_METHOD_NAME: &str = "session/fork";
7100#[cfg(feature = "unstable_session_archive")]
7102pub(crate) const SESSION_ARCHIVE_METHOD_NAME: &str = "session/archive";
7103#[cfg(feature = "unstable_session_archive")]
7105pub(crate) const SESSION_UNARCHIVE_METHOD_NAME: &str = "session/unarchive";
7106#[cfg(feature = "unstable_session_status")]
7108pub(crate) const SESSION_STATUS_METHOD_NAME: &str = "session/status";
7109#[cfg(feature = "unstable_session_set_title")]
7111pub(crate) const SESSION_SET_TITLE_METHOD_NAME: &str = "session/set_title";
7112pub(crate) const SESSION_RESUME_METHOD_NAME: &str = "session/resume";
7114pub(crate) const SESSION_CLOSE_METHOD_NAME: &str = "session/close";
7116pub(crate) const TOOL_CALL_CANCEL_METHOD_NAME: &str = "tool_call/cancel";
7118pub(crate) const TERMINAL_CANCEL_METHOD_NAME: &str = "terminal/cancel";
7120pub(crate) const AUTH_LOGOUT_METHOD_NAME: &str = "auth/logout";
7122#[cfg(feature = "unstable_auth_status")]
7124pub(crate) const AUTH_STATUS_METHOD_NAME: &str = "auth/status";
7125
7126#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
7133#[derive(Clone, Debug, Serialize, Deserialize)]
7134#[serde(untagged)]
7135#[cfg_attr(feature = "schemars", schemars(inline))]
7136#[non_exhaustive]
7137pub enum ClientRequest {
7138 InitializeRequest(Box<InitializeRequest>),
7152 LoginAuthRequest(Box<LoginAuthRequest>),
7167 #[cfg(feature = "unstable_llm_providers")]
7173 ListProvidersRequest(Box<ListProvidersRequest>),
7174 #[cfg(feature = "unstable_llm_providers")]
7180 SetProviderRequest(Box<SetProviderRequest>),
7181 #[cfg(feature = "unstable_llm_providers")]
7187 DisableProviderRequest(Box<DisableProviderRequest>),
7188 LogoutAuthRequest(Box<LogoutAuthRequest>),
7198 #[cfg(feature = "unstable_auth_status")]
7207 AuthStatusRequest(Box<AuthStatusRequest>),
7208 NewSessionRequest(Box<NewSessionRequest>),
7221 ListSessionsRequest(Box<ListSessionsRequest>),
7225 DeleteSessionRequest(Box<DeleteSessionRequest>),
7229 #[cfg(feature = "unstable_session_fork")]
7230 ForkSessionRequest(Box<ForkSessionRequest>),
7246 #[cfg(feature = "unstable_session_archive")]
7247 ArchiveSessionRequest(Box<ArchiveSessionRequest>),
7255 #[cfg(feature = "unstable_session_archive")]
7256 UnarchiveSessionRequest(Box<UnarchiveSessionRequest>),
7264 #[cfg(feature = "unstable_session_status")]
7265 SessionStatusRequest(Box<SessionStatusRequest>),
7273 #[cfg(feature = "unstable_session_set_title")]
7274 SetSessionTitleRequest(Box<SetSessionTitleRequest>),
7285 ResumeSessionRequest(Box<ResumeSessionRequest>),
7291 CloseSessionRequest(Box<CloseSessionRequest>),
7296 CancelToolCallRequest(Box<CancelToolCallRequest>),
7302 CancelTerminalRequest(Box<CancelTerminalRequest>),
7308 SetSessionConfigOptionRequest(Box<SetSessionConfigOptionRequest>),
7310 PromptRequest(Box<PromptRequest>),
7332 ExtMethodRequest(Box<ExtRequest>),
7339}
7340
7341impl ClientRequest {
7342 #[must_use]
7344 pub fn method(&self) -> &str {
7345 match self {
7346 Self::InitializeRequest(_) => AGENT_METHOD_NAMES.initialize,
7347 Self::LoginAuthRequest(_) => AGENT_METHOD_NAMES.auth_login,
7348 #[cfg(feature = "unstable_llm_providers")]
7349 Self::ListProvidersRequest(_) => AGENT_METHOD_NAMES.providers_list,
7350 #[cfg(feature = "unstable_llm_providers")]
7351 Self::SetProviderRequest(_) => AGENT_METHOD_NAMES.providers_set,
7352 #[cfg(feature = "unstable_llm_providers")]
7353 Self::DisableProviderRequest(_) => AGENT_METHOD_NAMES.providers_disable,
7354 Self::LogoutAuthRequest(_) => AGENT_METHOD_NAMES.auth_logout,
7355 #[cfg(feature = "unstable_auth_status")]
7356 Self::AuthStatusRequest(_) => AGENT_METHOD_NAMES.auth_status,
7357 Self::NewSessionRequest(_) => AGENT_METHOD_NAMES.session_new,
7358 Self::ListSessionsRequest(_) => AGENT_METHOD_NAMES.session_list,
7359 Self::DeleteSessionRequest(_) => AGENT_METHOD_NAMES.session_delete,
7360 #[cfg(feature = "unstable_session_fork")]
7361 Self::ForkSessionRequest(_) => AGENT_METHOD_NAMES.session_fork,
7362 #[cfg(feature = "unstable_session_archive")]
7363 Self::ArchiveSessionRequest(_) => AGENT_METHOD_NAMES.session_archive,
7364 #[cfg(feature = "unstable_session_archive")]
7365 Self::UnarchiveSessionRequest(_) => AGENT_METHOD_NAMES.session_unarchive,
7366 #[cfg(feature = "unstable_session_status")]
7367 Self::SessionStatusRequest(_) => AGENT_METHOD_NAMES.session_status,
7368 #[cfg(feature = "unstable_session_set_title")]
7369 Self::SetSessionTitleRequest(_) => AGENT_METHOD_NAMES.session_set_title,
7370 Self::ResumeSessionRequest(_) => AGENT_METHOD_NAMES.session_resume,
7371 Self::CloseSessionRequest(_) => AGENT_METHOD_NAMES.session_close,
7372 Self::CancelToolCallRequest(_) => AGENT_METHOD_NAMES.tool_call_cancel,
7373 Self::CancelTerminalRequest(_) => AGENT_METHOD_NAMES.terminal_cancel,
7374 Self::SetSessionConfigOptionRequest(_) => AGENT_METHOD_NAMES.session_set_config_option,
7375 Self::PromptRequest(_) => AGENT_METHOD_NAMES.session_prompt,
7376 Self::ExtMethodRequest(ext_request) => &ext_request.method,
7377 }
7378 }
7379}
7380
7381#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
7388#[derive(Clone, Debug, Serialize, Deserialize)]
7389#[serde(untagged)]
7390#[cfg_attr(feature = "schemars", schemars(inline))]
7391#[non_exhaustive]
7392pub enum AgentResponse {
7393 InitializeResponse(Box<InitializeResponse>),
7395 LoginAuthResponse(#[serde(default)] Box<LoginAuthResponse>),
7397 #[cfg(feature = "unstable_llm_providers")]
7399 ListProvidersResponse(Box<ListProvidersResponse>),
7400 #[cfg(feature = "unstable_llm_providers")]
7402 SetProviderResponse(#[serde(default)] Box<SetProviderResponse>),
7403 #[cfg(feature = "unstable_llm_providers")]
7405 DisableProviderResponse(#[serde(default)] Box<DisableProviderResponse>),
7406 LogoutAuthResponse(#[serde(default)] Box<LogoutAuthResponse>),
7408 #[cfg(feature = "unstable_auth_status")]
7410 AuthStatusResponse(Box<AuthStatusResponse>),
7411 NewSessionResponse(Box<NewSessionResponse>),
7413 ListSessionsResponse(Box<ListSessionsResponse>),
7415 DeleteSessionResponse(#[serde(default)] Box<DeleteSessionResponse>),
7417 #[cfg(feature = "unstable_session_fork")]
7419 ForkSessionResponse(Box<ForkSessionResponse>),
7420 #[cfg(feature = "unstable_session_archive")]
7422 ArchiveSessionResponse(#[serde(default)] Box<ArchiveSessionResponse>),
7423 #[cfg(feature = "unstable_session_archive")]
7425 UnarchiveSessionResponse(#[serde(default)] Box<UnarchiveSessionResponse>),
7426 #[cfg(feature = "unstable_session_status")]
7428 SessionStatusResponse(Box<SessionStatusResponse>),
7429 #[cfg(feature = "unstable_session_set_title")]
7431 SetSessionTitleResponse(Box<SetSessionTitleResponse>),
7432 ResumeSessionResponse(#[serde(default)] Box<ResumeSessionResponse>),
7434 CloseSessionResponse(#[serde(default)] Box<CloseSessionResponse>),
7436 CancelToolCallResponse(#[serde(default)] Box<CancelToolCallResponse>),
7438 CancelTerminalResponse(#[serde(default)] Box<CancelTerminalResponse>),
7440 SetSessionConfigOptionResponse(Box<SetSessionConfigOptionResponse>),
7442 PromptResponse(Box<PromptResponse>),
7444 ExtMethodResponse(Box<ExtResponse>),
7446}
7447
7448#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
7455#[derive(Clone, Debug, Serialize, Deserialize)]
7456#[serde(untagged)]
7457#[cfg_attr(feature = "schemars", schemars(inline))]
7458#[non_exhaustive]
7459pub enum ClientNotification {
7460 CancelSessionNotification(Box<CancelSessionNotification>),
7485 #[cfg(feature = "unstable_mcp_over_acp")]
7491 MessageMcpNotification(Box<MessageMcpNotification>),
7492 ExtNotification(Box<ExtNotification>),
7499}
7500
7501impl ClientNotification {
7502 #[must_use]
7504 pub fn method(&self) -> &str {
7505 match self {
7506 Self::CancelSessionNotification(_) => AGENT_METHOD_NAMES.session_cancel,
7507 #[cfg(feature = "unstable_mcp_over_acp")]
7508 Self::MessageMcpNotification(_) => AGENT_METHOD_NAMES.mcp_message,
7509 Self::ExtNotification(ext_notification) => &ext_notification.method,
7510 }
7511 }
7512}
7513
7514#[cfg(test)]
7515mod test_serialization {
7516 use std::path::PathBuf;
7517
7518 use super::*;
7519 use serde_json::json;
7520
7521 fn test_meta() -> Meta {
7522 json!({ "source": "test" }).as_object().unwrap().clone()
7523 }
7524
7525 #[test]
7526 fn list_sessions_cursor_and_session_directories_are_strict() {
7527 for cursor in [json!(null), json!("next")] {
7528 let response = json!({ "sessions": [], "nextCursor": cursor });
7529 serde_json::from_value::<ListSessionsResponse>(response).unwrap();
7530 }
7531 let malformed = json!({ "sessions": [], "nextCursor": 42 });
7532 assert!(serde_json::from_value::<ListSessionsResponse>(malformed).is_err());
7533
7534 let session = |dirs: serde_json::Value| json!({ "sessionId": "s", "cwd": "/w", "additionalDirectories": dirs });
7535 let info: SessionInfo = serde_json::from_value(session(json!(null))).unwrap();
7536 assert_eq!(info.additional_directories, Vec::<AbsolutePath>::new());
7537 for bad in [json!(["/a", 42]), json!("/a")] {
7538 assert!(serde_json::from_value::<SessionInfo>(session(bad)).is_err());
7539 }
7540 let response = json!({ "sessions": [session(json!(["/a", 42]))] });
7542 let response: ListSessionsResponse = serde_json::from_value(response).unwrap();
7543 assert_eq!(response.sessions, Vec::<SessionInfo>::new());
7544 }
7545
7546 #[test]
7547 fn other_fallbacks_preserve_optional_fields_verbatim() {
7548 let valid = json!({ "type": "_custom", "methodId": "m", "name": "M",
7549 "description": "d", "_meta": { "k": 1 } });
7550 let method: AuthMethod = serde_json::from_value(valid.clone()).unwrap();
7551 assert_eq!(method.description(), Some("d"));
7552 assert_eq!(
7553 method.meta(),
7554 Some(&json!({ "k": 1 }).as_object().unwrap().clone())
7555 );
7556 assert_eq!(serde_json::to_value(&method).unwrap(), valid);
7557
7558 let malformed = json!({ "type": "_custom", "methodId": "m", "name": "M",
7560 "description": 42, "_meta": "bad", "extra": true });
7561 let method: AuthMethod = serde_json::from_value(malformed.clone()).unwrap();
7562 assert_eq!(method.description(), None);
7563 assert_eq!(method.meta(), None);
7564 assert_eq!(serde_json::to_value(&method).unwrap(), malformed);
7565
7566 let replay = json!({ "type": "_custom", "_meta": "bad", "cursor": 3 });
7567 let decoded: ReplayFrom = serde_json::from_value(replay.clone()).unwrap();
7568 assert_eq!(serde_json::to_value(&decoded).unwrap(), replay);
7569 }
7570
7571 fn serialized_meta_key_count(value: &impl serde::Serialize) -> usize {
7572 serde_json::to_string(value)
7573 .unwrap()
7574 .matches("\"_meta\"")
7575 .count()
7576 }
7577
7578 #[test]
7579 fn prompt_response_without_metadata_round_trips() {
7580 let response = PromptResponse::new(MessageId::new("message-1"));
7581 let serialized = serde_json::to_value(&response).unwrap();
7582 assert_eq!(serialized, json!({ "messageId": "message-1" }));
7583 assert_eq!(
7584 serde_json::from_value::<PromptResponse>(serialized).unwrap(),
7585 response
7586 );
7587 assert_eq!(
7588 serde_json::from_value::<PromptResponse>(json!({
7589 "messageId": "message-1",
7590 "_meta": null
7591 }))
7592 .unwrap(),
7593 response
7594 );
7595 }
7596
7597 #[test]
7598 fn prompt_response_requires_message_id_and_preserves_metadata_tolerance() {
7599 let response = PromptResponse::new("message-1").meta(test_meta());
7600 let serialized = serde_json::to_value(&response).unwrap();
7601 assert_eq!(
7602 serialized,
7603 json!({
7604 "messageId": "message-1",
7605 "_meta": { "source": "test" }
7606 })
7607 );
7608
7609 let deserialized: PromptResponse = serde_json::from_value(serialized).unwrap();
7610 assert_eq!(deserialized.message_id, MessageId::new("message-1"));
7611 assert_eq!(deserialized.meta, Some(test_meta()));
7612
7613 let malformed_meta: PromptResponse = serde_json::from_value(json!({
7614 "messageId": "message-2",
7615 "_meta": false
7616 }))
7617 .unwrap();
7618 assert_eq!(malformed_meta.message_id, MessageId::new("message-2"));
7619 assert_eq!(malformed_meta.meta, None);
7620
7621 for invalid in [
7622 json!({}),
7623 json!({ "messageId": null }),
7624 json!({ "messageId": 1 }),
7625 json!({ "messageId": false }),
7626 json!({ "messageId": {} }),
7627 json!({ "messageId": [] }),
7628 ] {
7629 assert!(
7630 serde_json::from_value::<PromptResponse>(invalid).is_err(),
7631 "missing, null, and non-string message IDs must be rejected"
7632 );
7633 }
7634 }
7635
7636 #[cfg(feature = "schemars")]
7637 #[test]
7638 fn prompt_response_schema_requires_non_null_string_message_id_reference() {
7639 let schema = serde_json::to_value(schemars::schema_for!(PromptResponse)).unwrap();
7640
7641 assert_eq!(schema["required"], json!(["messageId"]));
7642 assert_eq!(
7643 schema["properties"]["messageId"]["$ref"],
7644 "#/$defs/MessageId"
7645 );
7646 assert_eq!(schema["$defs"]["MessageId"]["type"], "string");
7647 }
7648
7649 #[test]
7650 fn test_initialize_capabilities_default_on_malformed_values() {
7651 let request: InitializeRequest = serde_json::from_value(json!({
7652 "protocolVersion": 2,
7653 "capabilities": false,
7654 "info": {
7655 "name": "client",
7656 "version": "1.0.0"
7657 }
7658 }))
7659 .unwrap();
7660 assert_eq!(request.capabilities, ClientCapabilities::default());
7661
7662 let response: InitializeResponse = serde_json::from_value(json!({
7663 "protocolVersion": 2,
7664 "capabilities": false,
7665 "info": {
7666 "name": "agent",
7667 "version": "1.0.0"
7668 }
7669 }))
7670 .unwrap();
7671 assert_eq!(response.capabilities, AgentCapabilities::default());
7672 }
7673
7674 #[test]
7675 fn test_agent_capabilities_default_on_malformed_values() {
7676 let capabilities: AgentCapabilities = serde_json::from_value(json!({
7677 "session": false,
7678 "auth": false
7679 }))
7680 .unwrap();
7681
7682 assert!(capabilities.session.is_none());
7683 assert_eq!(capabilities.auth, None);
7684 }
7685
7686 #[test]
7687 fn test_mcp_server_stdio_serialization() {
7688 let server = McpServer::Stdio(
7689 McpServerStdio::new("test-server", "/usr/bin/server")
7690 .args(vec!["--port".to_string(), "3000".to_string()])
7691 .env(vec![EnvVariable::new("API_KEY", "secret123")]),
7692 );
7693
7694 let json = serde_json::to_value(&server).unwrap();
7695 assert_eq!(
7696 json,
7697 json!({
7698 "type": "stdio",
7699 "name": "test-server",
7700 "command": "/usr/bin/server",
7701 "args": ["--port", "3000"],
7702 "env": [
7703 {
7704 "name": "API_KEY",
7705 "value": "secret123"
7706 }
7707 ]
7708 })
7709 );
7710
7711 let deserialized: McpServer = serde_json::from_value(json).unwrap();
7712 match deserialized {
7713 McpServer::Stdio(McpServerStdio {
7714 name,
7715 command,
7716 args,
7717 env,
7718 meta: _,
7719 }) => {
7720 assert_eq!(name, "test-server");
7721 assert_eq!(command, AbsolutePath::new("/usr/bin/server"));
7722 assert_eq!(args, vec!["--port", "3000"]);
7723 assert_eq!(env.len(), 1);
7724 assert_eq!(env[0].name, "API_KEY");
7725 assert_eq!(env[0].value, "secret123");
7726 }
7727 _ => panic!("Expected Stdio variant"),
7728 }
7729 }
7730
7731 #[test]
7732 fn test_mcp_server_empty_arrays_are_optional() {
7733 let stdio = McpServer::Stdio(McpServerStdio::new("test-server", "/usr/bin/server"));
7734 assert_eq!(
7735 serde_json::to_value(&stdio).unwrap(),
7736 json!({
7737 "type": "stdio",
7738 "name": "test-server",
7739 "command": "/usr/bin/server"
7740 })
7741 );
7742
7743 let McpServer::Stdio(McpServerStdio { args, env, .. }) =
7744 serde_json::from_value::<McpServer>(json!({
7745 "type": "stdio",
7746 "name": "test-server",
7747 "command": "/usr/bin/server"
7748 }))
7749 .unwrap()
7750 else {
7751 panic!("Expected Stdio variant");
7752 };
7753 assert_eq!(args, Vec::<String>::new());
7754 assert_eq!(env, []);
7755
7756 let http = McpServer::Http(McpServerHttp::new("http-server", "https://api.example.com"));
7757 assert_eq!(
7758 serde_json::to_value(&http).unwrap(),
7759 json!({
7760 "type": "http",
7761 "name": "http-server",
7762 "url": "https://api.example.com"
7763 })
7764 );
7765
7766 let McpServer::Http(McpServerHttp { headers, .. }) =
7767 serde_json::from_value::<McpServer>(json!({
7768 "type": "http",
7769 "name": "http-server",
7770 "url": "https://api.example.com"
7771 }))
7772 .unwrap()
7773 else {
7774 panic!("Expected Http variant");
7775 };
7776 assert_eq!(headers, []);
7777 }
7778
7779 #[test]
7780 fn test_mcp_server_unknown_transport_serialization() {
7781 let json = json!({
7782 "type": "websocket",
7783 "name": "future-server",
7784 "url": "wss://example.com/mcp",
7785 "protocolVersion": "2026-01-01"
7786 });
7787
7788 let deserialized: McpServer = serde_json::from_value(json.clone()).unwrap();
7789 let McpServer::Other(OtherMcpServer { type_, fields }) = &deserialized else {
7790 panic!("Expected Other variant");
7791 };
7792
7793 assert_eq!(type_, "websocket");
7794 assert_eq!(fields["name"], "future-server");
7795 assert_eq!(fields["url"], "wss://example.com/mcp");
7796 assert_eq!(fields["protocolVersion"], "2026-01-01");
7797 assert_eq!(serde_json::to_value(&deserialized).unwrap(), json);
7798 }
7799
7800 #[test]
7801 fn test_mcp_server_stdio_requires_type() {
7802 let result = serde_json::from_value::<McpServer>(json!({
7803 "name": "test-server",
7804 "command": "/usr/bin/server",
7805 "args": [],
7806 "env": []
7807 }));
7808
7809 assert!(result.is_err());
7810 }
7811
7812 #[test]
7813 fn test_mcp_server_unknown_does_not_hide_malformed_known_transport() {
7814 let result = serde_json::from_value::<McpServer>(json!({
7815 "type": "stdio",
7816 "name": "test-server",
7817 "args": [],
7818 "env": []
7819 }));
7820
7821 assert!(result.is_err());
7822 }
7823
7824 #[test]
7825 fn test_mcp_server_http_serialization() {
7826 let server = McpServer::Http(
7827 McpServerHttp::new("http-server", "https://api.example.com").headers(vec![
7828 HttpHeader::new("Authorization", "Bearer token123"),
7829 HttpHeader::new("Content-Type", "application/json"),
7830 ]),
7831 );
7832
7833 let json = serde_json::to_value(&server).unwrap();
7834 assert_eq!(
7835 json,
7836 json!({
7837 "type": "http",
7838 "name": "http-server",
7839 "url": "https://api.example.com",
7840 "headers": [
7841 {
7842 "name": "Authorization",
7843 "value": "Bearer token123"
7844 },
7845 {
7846 "name": "Content-Type",
7847 "value": "application/json"
7848 }
7849 ]
7850 })
7851 );
7852
7853 let deserialized: McpServer = serde_json::from_value(json).unwrap();
7854 match deserialized {
7855 McpServer::Http(McpServerHttp {
7856 name,
7857 url,
7858 headers,
7859 meta: _,
7860 }) => {
7861 assert_eq!(name, "http-server");
7862 assert_eq!(url, "https://api.example.com");
7863 assert_eq!(headers.len(), 2);
7864 assert_eq!(headers[0].name, "Authorization");
7865 assert_eq!(headers[0].value, "Bearer token123");
7866 assert_eq!(headers[1].name, "Content-Type");
7867 assert_eq!(headers[1].value, "application/json");
7868 }
7869 _ => panic!("Expected Http variant"),
7870 }
7871 }
7872
7873 #[cfg(feature = "schemars")]
7874 #[test]
7875 fn mcp_server_http_schema_marks_url_as_uri() {
7876 let schema = serde_json::to_value(schemars::schema_for!(McpServerHttp)).unwrap();
7877
7878 assert_eq!(schema["properties"]["url"]["format"], "uri");
7879 }
7880
7881 #[cfg(feature = "unstable_mcp_over_acp")]
7882 #[test]
7883 fn test_client_mcp_message_method_names() {
7884 use serde_json::json;
7885
7886 assert_eq!(AGENT_METHOD_NAMES.mcp_message, "mcp/message");
7887
7888 let notification =
7889 MessageMcpNotification::new("server-1", "req-1", "notifications/progress");
7890 assert_eq!(
7891 ClientNotification::MessageMcpNotification(Box::new(notification.clone())).method(),
7892 "mcp/message"
7893 );
7894 assert_eq!(
7895 serde_json::to_value(notification).unwrap(),
7896 json!({
7897 "serverId": "server-1",
7898 "requestId": "req-1",
7899 "method": "notifications/progress"
7900 })
7901 );
7902 let notification: MessageMcpNotification = serde_json::from_value(json!({
7903 "serverId": "server-1", "requestId": "req-1", "method": "notifications/progress",
7904 "params": null, "_meta": null
7905 }))
7906 .unwrap();
7907 assert_eq!(notification.params, None);
7908 assert_eq!(notification.meta, None);
7909 for key in ["serverId", "requestId", "method"] {
7910 let mut value = json!({"serverId":"server-1", "requestId":"req-1", "method":"notifications/progress"});
7911 value.as_object_mut().unwrap().remove(key);
7912 assert!(serde_json::from_value::<MessageMcpNotification>(value).is_err());
7913 }
7914 }
7915
7916 #[test]
7917 fn test_auth_method_names() {
7918 assert_eq!(AGENT_METHOD_NAMES.auth_login, "auth/login");
7919 assert_eq!(AGENT_METHOD_NAMES.auth_logout, "auth/logout");
7920
7921 assert_eq!(
7922 ClientRequest::LoginAuthRequest(Box::new(LoginAuthRequest::new("agent-login")))
7923 .method(),
7924 "auth/login"
7925 );
7926 assert_eq!(
7927 ClientRequest::LogoutAuthRequest(Box::new(LogoutAuthRequest::new())).method(),
7928 "auth/logout"
7929 );
7930 }
7931
7932 #[test]
7933 fn test_session_config_option_category_known_variants() {
7934 assert_eq!(
7936 serde_json::to_value(&SessionConfigOptionCategory::Mode).unwrap(),
7937 json!("mode")
7938 );
7939 assert_eq!(
7940 serde_json::to_value(&SessionConfigOptionCategory::Model).unwrap(),
7941 json!("model")
7942 );
7943 assert_eq!(
7944 serde_json::to_value(&SessionConfigOptionCategory::ModelConfig).unwrap(),
7945 json!("model_config")
7946 );
7947 assert_eq!(
7948 serde_json::to_value(&SessionConfigOptionCategory::ThoughtLevel).unwrap(),
7949 json!("thought_level")
7950 );
7951
7952 assert_eq!(
7954 serde_json::from_str::<SessionConfigOptionCategory>("\"mode\"").unwrap(),
7955 SessionConfigOptionCategory::Mode
7956 );
7957 assert_eq!(
7958 serde_json::from_str::<SessionConfigOptionCategory>("\"model\"").unwrap(),
7959 SessionConfigOptionCategory::Model
7960 );
7961 assert_eq!(
7962 serde_json::from_str::<SessionConfigOptionCategory>("\"model_config\"").unwrap(),
7963 SessionConfigOptionCategory::ModelConfig
7964 );
7965 assert_eq!(
7966 serde_json::from_str::<SessionConfigOptionCategory>("\"thought_level\"").unwrap(),
7967 SessionConfigOptionCategory::ThoughtLevel
7968 );
7969 }
7970
7971 #[test]
7972 fn test_session_config_option_category_unknown_variants() {
7973 let unknown: SessionConfigOptionCategory =
7975 serde_json::from_str("\"some_future_category\"").unwrap();
7976 assert_eq!(
7977 unknown,
7978 SessionConfigOptionCategory::Other("some_future_category".to_string())
7979 );
7980
7981 let json = serde_json::to_value(&unknown).unwrap();
7983 assert_eq!(json, json!("some_future_category"));
7984 }
7985
7986 #[test]
7987 fn test_session_config_option_category_custom_categories() {
7988 let custom: SessionConfigOptionCategory =
7990 serde_json::from_str("\"_my_custom_category\"").unwrap();
7991 assert_eq!(
7992 custom,
7993 SessionConfigOptionCategory::Other("_my_custom_category".to_string())
7994 );
7995
7996 let json = serde_json::to_value(&custom).unwrap();
7998 assert_eq!(json, json!("_my_custom_category"));
7999
8000 let deserialized: SessionConfigOptionCategory = serde_json::from_value(json).unwrap();
8002 assert_eq!(
8003 deserialized,
8004 SessionConfigOptionCategory::Other("_my_custom_category".to_string()),
8005 );
8006 }
8007
8008 fn test_config_option() -> SessionConfigOption {
8009 SessionConfigOption::select(
8010 "mode",
8011 "Mode",
8012 "ask",
8013 vec![SessionConfigSelectOption::new("ask", "Ask")],
8014 )
8015 }
8016
8017 #[test]
8018 fn test_session_response_initial_state_default_empty_and_skip_serializing() {
8019 assert_eq!(
8020 serde_json::to_value(NewSessionResponse::new("sess")).unwrap(),
8021 json!({ "sessionId": "sess" })
8022 );
8023 assert_eq!(
8024 serde_json::to_value(ResumeSessionResponse::new()).unwrap(),
8025 json!({})
8026 );
8027 #[cfg(feature = "unstable_session_fork")]
8028 assert_eq!(
8029 serde_json::to_value(ForkSessionResponse::new("fork")).unwrap(),
8030 json!({ "sessionId": "fork" })
8031 );
8032
8033 let json = serde_json::to_value(
8034 NewSessionResponse::new("sess").config_options(vec![test_config_option()]),
8035 )
8036 .unwrap();
8037 assert_eq!(json["configOptions"].as_array().unwrap().len(), 1);
8038 }
8039
8040 #[test]
8041 fn test_session_response_config_options_deserialize_missing_null_and_invalid() {
8042 let missing: NewSessionResponse =
8043 serde_json::from_value(json!({ "sessionId": "sess" })).unwrap();
8044 assert_eq!(missing.config_options, []);
8045
8046 let null: NewSessionResponse = serde_json::from_value(json!({
8047 "sessionId": "sess",
8048 "configOptions": null
8049 }))
8050 .unwrap();
8051 assert_eq!(null.config_options, []);
8052
8053 let wrong_shape: NewSessionResponse = serde_json::from_value(json!({
8054 "sessionId": "sess",
8055 "configOptions": "oops"
8056 }))
8057 .unwrap();
8058 assert_eq!(wrong_shape.config_options, []);
8059
8060 let valid_option = serde_json::to_value(test_config_option()).unwrap();
8061 let mixed: NewSessionResponse = serde_json::from_value(json!({
8062 "sessionId": "sess",
8063 "configOptions": ["oops", valid_option]
8064 }))
8065 .unwrap();
8066 assert_eq!(mixed.config_options.len(), 1);
8067
8068 let resume: ResumeSessionResponse = serde_json::from_value(json!({})).unwrap();
8069 assert_eq!(resume.config_options, []);
8070 #[cfg(feature = "unstable_session_fork")]
8071 {
8072 let fork: ForkSessionResponse =
8073 serde_json::from_value(json!({ "sessionId": "fork" })).unwrap();
8074 assert_eq!(fork.config_options, []);
8075 }
8076 }
8077
8078 #[test]
8079 fn test_session_response_available_commands_roundtrip_with_config_options() {
8080 use super::super::{AvailableCommandInput, Command, TextCommandInput};
8081
8082 let commands = vec![
8083 AvailableCommand::Command(
8084 Command::new("web", "Search the web")
8085 .input(AvailableCommandInput::Text(TextCommandInput::new("Query"))),
8086 ),
8087 AvailableCommand::Command(Command::new("test", "Run project tests")),
8088 ];
8089 let mut expected = json!({
8090 "sessionId": "sess",
8091 "configOptions": [test_config_option()],
8092 "availableCommands": [
8093 {
8094 "type": "command",
8095 "name": "web",
8096 "description": "Search the web",
8097 "input": { "type": "text", "hint": "Query" }
8098 },
8099 { "type": "command", "name": "test", "description": "Run project tests" }
8100 ]
8101 });
8102
8103 let new = NewSessionResponse::new("sess")
8104 .config_options(vec![test_config_option()])
8105 .available_commands(commands.clone());
8106 assert_eq!(serde_json::to_value(&new).unwrap(), expected);
8107 assert_eq!(
8108 serde_json::from_value::<NewSessionResponse>(expected.clone()).unwrap(),
8109 new
8110 );
8111
8112 expected.as_object_mut().unwrap().remove("sessionId");
8113 let resume = ResumeSessionResponse::new()
8114 .config_options(vec![test_config_option()])
8115 .available_commands(commands.clone());
8116 assert_eq!(serde_json::to_value(&resume).unwrap(), expected);
8117 assert_eq!(
8118 serde_json::from_value::<ResumeSessionResponse>(expected.clone()).unwrap(),
8119 resume
8120 );
8121
8122 #[cfg(feature = "unstable_session_fork")]
8123 {
8124 expected["sessionId"] = json!("fork");
8125 let fork = ForkSessionResponse::new("fork")
8126 .config_options(vec![test_config_option()])
8127 .available_commands(commands);
8128 assert_eq!(serde_json::to_value(&fork).unwrap(), expected);
8129 assert_eq!(
8130 serde_json::from_value::<ForkSessionResponse>(expected).unwrap(),
8131 fork
8132 );
8133 }
8134 }
8135
8136 #[test]
8137 fn test_session_response_available_commands_deserialize_missing_null_and_invalid() {
8138 let command =
8139 AvailableCommand::Command(super::super::Command::new("test", "Run project tests"));
8140 let config_options = vec![test_config_option()];
8141
8142 for (commands, expected) in [
8143 (None, vec![]),
8144 (Some(json!(null)), vec![]),
8145 (Some(json!([])), vec![]),
8146 (Some(json!("oops")), vec![]),
8147 (Some(json!({})), vec![]),
8148 (Some(json!([null, {}, { "name": "invalid" }])), vec![]),
8149 (
8150 Some(json!([{ "name": "test", "description": "Run project tests" }])),
8151 vec![],
8152 ),
8153 (
8154 Some(json!([null, command, { "name": "invalid" }])),
8155 vec![command.clone()],
8156 ),
8157 ] {
8158 let mut value = json!({
8159 "sessionId": "sess",
8160 "configOptions": config_options
8161 });
8162 if let Some(commands) = commands {
8163 value["availableCommands"] = commands;
8164 }
8165
8166 let new: NewSessionResponse = serde_json::from_value(value.clone()).unwrap();
8167 assert_eq!(new.available_commands, expected);
8168 assert_eq!(new.config_options, config_options);
8169
8170 #[cfg(feature = "unstable_session_fork")]
8171 {
8172 let fork: ForkSessionResponse = serde_json::from_value(value.clone()).unwrap();
8173 assert_eq!(fork.available_commands, expected);
8174 assert_eq!(fork.config_options, config_options);
8175 }
8176
8177 value.as_object_mut().unwrap().remove("sessionId");
8178 let resume: ResumeSessionResponse = serde_json::from_value(value).unwrap();
8179 assert_eq!(resume.available_commands, expected);
8180 assert_eq!(resume.config_options, config_options);
8181 }
8182
8183 let resume: ResumeSessionResponse = serde_json::from_value(json!(null)).unwrap();
8184 assert_eq!(resume.available_commands, []);
8185 }
8186
8187 #[test]
8188 fn test_resume_session_replay_from_serialization() {
8189 assert_eq!(
8190 serde_json::to_value(ResumeSessionRequest::new(
8191 "sess_abc123",
8192 "/home/user/project"
8193 ))
8194 .unwrap(),
8195 json!({
8196 "sessionId": "sess_abc123",
8197 "cwd": "/home/user/project"
8198 })
8199 );
8200 assert_eq!(
8201 serde_json::to_value(
8202 ResumeSessionRequest::new("sess_abc123", "/home/user/project")
8203 .replay_from(ReplayFrom::from(ReplayFromStart::new()))
8204 )
8205 .unwrap(),
8206 json!({
8207 "sessionId": "sess_abc123",
8208 "cwd": "/home/user/project",
8209 "replayFrom": {
8210 "type": "start"
8211 }
8212 })
8213 );
8214
8215 let replay: ResumeSessionRequest = serde_json::from_value(json!({
8216 "sessionId": "sess_abc123",
8217 "cwd": "/home/user/project",
8218 "replayFrom": {
8219 "type": "start"
8220 }
8221 }))
8222 .unwrap();
8223 assert!(matches!(replay.replay_from, Some(ReplayFrom::Start(_))));
8224
8225 let none: ResumeSessionRequest = serde_json::from_value(json!({
8226 "sessionId": "sess_abc123",
8227 "cwd": "/home/user/project",
8228 "replayFrom": null
8229 }))
8230 .unwrap();
8231 assert!(none.replay_from.is_none());
8232 }
8233
8234 #[test]
8235 fn test_auth_method_agent_serialization() {
8236 let method = AuthMethod::Agent(AuthMethodAgent::new("default-auth", "Default Auth"));
8237
8238 let json = serde_json::to_value(&method).unwrap();
8239 assert_eq!(
8240 json,
8241 json!({
8242 "methodId": "default-auth",
8243 "name": "Default Auth",
8244 "type": "agent"
8245 })
8246 );
8247 assert!(!json.as_object().unwrap().contains_key("description"));
8249
8250 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
8251 match deserialized {
8252 AuthMethod::Agent(AuthMethodAgent {
8253 method_id, name, ..
8254 }) => {
8255 assert_eq!(method_id.0.as_ref(), "default-auth");
8256 assert_eq!(name, "Default Auth");
8257 }
8258 _ => panic!("Expected Agent variant"),
8259 }
8260 }
8261
8262 #[test]
8263 fn test_auth_method_agent_deserialization() {
8264 let json = json!({
8265 "methodId": "agent-auth",
8266 "name": "Agent Auth",
8267 "type": "agent"
8268 });
8269
8270 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
8271 assert!(matches!(deserialized, AuthMethod::Agent(_)));
8272 }
8273
8274 #[test]
8275 fn test_auth_method_agent_requires_type() {
8276 assert!(
8277 serde_json::from_value::<AuthMethod>(json!({
8278 "methodId": "agent-auth",
8279 "name": "Agent Auth"
8280 }))
8281 .is_err()
8282 );
8283 }
8284
8285 #[test]
8286 fn test_auth_method_agent_rejects_null_type() {
8287 assert!(
8288 serde_json::from_value::<AuthMethod>(json!({
8289 "methodId": "agent-auth",
8290 "name": "Agent Auth",
8291 "type": null
8292 }))
8293 .is_err()
8294 );
8295 }
8296
8297 #[test]
8298 fn test_auth_method_unknown_does_not_hide_malformed_agent() {
8299 assert!(
8300 serde_json::from_value::<AuthMethod>(json!({
8301 "methodId": "agent-auth",
8302 "type": "agent"
8303 }))
8304 .is_err()
8305 );
8306 assert!(
8307 serde_json::from_value::<AuthMethod>(json!({
8308 "methodId": "api-key",
8309 "type": "env_var",
8310 "vars": [{"name": "API_KEY"}]
8311 }))
8312 .is_err()
8313 );
8314 }
8315
8316 #[test]
8317 fn test_auth_method_unknown_variant_roundtrip() {
8318 let method: AuthMethod = serde_json::from_value(json!({
8319 "methodId": "oauth",
8320 "name": "OAuth",
8321 "type": "_oauth",
8322 "authorizationUrl": "https://example.com/auth"
8323 }))
8324 .unwrap();
8325
8326 assert_eq!(method.method_id().0.as_ref(), "oauth");
8327 assert_eq!(method.name(), "OAuth");
8328 let AuthMethod::Other(unknown) = method else {
8329 panic!("expected unknown auth method");
8330 };
8331 assert_eq!(unknown.type_, "_oauth");
8332 assert_eq!(
8333 unknown.fields.get("authorizationUrl"),
8334 Some(&json!("https://example.com/auth"))
8335 );
8336
8337 assert_eq!(
8338 serde_json::to_value(AuthMethod::Other(unknown)).unwrap(),
8339 json!({
8340 "methodId": "oauth",
8341 "name": "OAuth",
8342 "type": "_oauth",
8343 "authorizationUrl": "https://example.com/auth"
8344 })
8345 );
8346 }
8347
8348 #[test]
8349 fn test_auth_method_unknown_does_not_hide_malformed_known_variant() {
8350 assert!(
8351 serde_json::from_value::<AuthMethod>(json!({
8352 "methodId": "terminal-auth",
8353 "type": "terminal"
8354 }))
8355 .is_err()
8356 );
8357 }
8358
8359 #[test]
8360 fn test_tool_call_and_terminal_cancel_serialization() {
8361 assert_eq!(AGENT_METHOD_NAMES.tool_call_cancel, "tool_call/cancel");
8362 assert_eq!(AGENT_METHOD_NAMES.terminal_cancel, "terminal/cancel");
8363 assert_eq!(
8364 ClientRequest::CancelToolCallRequest(Box::new(CancelToolCallRequest::new(
8365 "sess_abc123",
8366 "call_012"
8367 )))
8368 .method(),
8369 "tool_call/cancel"
8370 );
8371 assert_eq!(
8372 ClientRequest::CancelTerminalRequest(Box::new(CancelTerminalRequest::new(
8373 "sess_abc123",
8374 "term_003"
8375 )))
8376 .method(),
8377 "terminal/cancel"
8378 );
8379 assert_eq!(
8380 serde_json::to_value(CancelToolCallRequest::new("sess_abc123", "call_012")).unwrap(),
8381 json!({"sessionId": "sess_abc123", "toolCallId": "call_012"})
8382 );
8383 assert_eq!(
8384 serde_json::to_value(CancelTerminalRequest::new("sess_abc123", "term_003")).unwrap(),
8385 json!({"sessionId": "sess_abc123", "terminalId": "term_003"})
8386 );
8387 assert!(
8388 serde_json::from_value::<CancelToolCallRequest>(json!({"sessionId": "sess_abc123"}))
8389 .is_err()
8390 );
8391 assert_eq!(
8392 serde_json::to_value(CancelTerminalResponse::new()).unwrap(),
8393 json!({})
8394 );
8395 }
8396
8397 #[test]
8398 fn test_session_delete_serialization() {
8399 assert_eq!(AGENT_METHOD_NAMES.session_delete, "session/delete");
8400 assert_eq!(
8401 ClientRequest::DeleteSessionRequest(Box::new(DeleteSessionRequest::new("sess_abc123")))
8402 .method(),
8403 "session/delete"
8404 );
8405 assert_eq!(
8406 serde_json::to_value(DeleteSessionRequest::new("sess_abc123")).unwrap(),
8407 json!({
8408 "sessionId": "sess_abc123"
8409 })
8410 );
8411 assert_eq!(
8412 serde_json::to_value(DeleteSessionResponse::new()).unwrap(),
8413 json!({})
8414 );
8415 assert_eq!(
8416 serde_json::to_value(
8417 SessionCapabilities::new().delete(SessionDeleteCapabilities::new())
8418 )
8419 .unwrap(),
8420 json!({
8421 "delete": {}
8422 })
8423 );
8424 }
8425 #[cfg(feature = "unstable_session_set_title")]
8426 #[test]
8427 fn test_session_set_title_serialization() {
8428 assert_eq!(AGENT_METHOD_NAMES.session_set_title, "session/set_title");
8429 assert_eq!(
8430 ClientRequest::SetSessionTitleRequest(Box::new(SetSessionTitleRequest::new(
8431 "sess_abc123",
8432 "Investigate OAuth timeout"
8433 )))
8434 .method(),
8435 "session/set_title"
8436 );
8437 assert_eq!(
8438 serde_json::to_value(SetSessionTitleRequest::new(
8439 "sess_abc123",
8440 "Investigate OAuth timeout"
8441 ))
8442 .unwrap(),
8443 json!({
8444 "sessionId": "sess_abc123",
8445 "title": "Investigate OAuth timeout"
8446 })
8447 );
8448 assert_eq!(
8449 serde_json::to_value(SetSessionTitleResponse::new("Investigate OAuth timeout"))
8450 .unwrap(),
8451 json!({
8452 "title": "Investigate OAuth timeout"
8453 })
8454 );
8455 assert_eq!(
8456 serde_json::to_value(SessionCapabilities::new().title(SessionTitleCapabilities::new()))
8457 .unwrap(),
8458 json!({
8459 "title": {}
8460 })
8461 );
8462 }
8463
8464 #[cfg(feature = "unstable_session_set_title")]
8465 #[test]
8466 fn test_session_set_title_requires_title() {
8467 for params in [
8468 json!({"sessionId": "sess_abc123"}),
8469 json!({"sessionId": "sess_abc123", "title": null}),
8470 json!({"sessionId": "sess_abc123", "title": 42}),
8471 ] {
8472 assert!(serde_json::from_value::<SetSessionTitleRequest>(params).is_err());
8473 }
8474 for result in [json!({}), json!({"title": null})] {
8475 assert!(serde_json::from_value::<SetSessionTitleResponse>(result).is_err());
8476 }
8477 }
8478
8479 #[cfg(feature = "unstable_session_status")]
8480 #[test]
8481 fn test_session_status_serialization() {
8482 assert_eq!(AGENT_METHOD_NAMES.session_status, "session/status");
8483 assert_eq!(
8484 ClientRequest::SessionStatusRequest(Box::new(SessionStatusRequest::new("sess_abc123")))
8485 .method(),
8486 "session/status"
8487 );
8488 assert_eq!(
8489 serde_json::to_value(SessionStatusRequest::new("sess_abc123")).unwrap(),
8490 json!({"sessionId": "sess_abc123"})
8491 );
8492 assert_eq!(
8493 serde_json::to_value(SessionStatusResponse::new(SessionStatus::Saved)).unwrap(),
8494 json!({"status": "saved"})
8495 );
8496 assert_eq!(
8497 serde_json::to_value(
8498 SessionCapabilities::new().status(SessionStatusCapabilities::new())
8499 )
8500 .unwrap(),
8501 json!({"status": {}})
8502 );
8503 assert_eq!(
8504 serde_json::to_value(SessionStatusResponse::new(SessionStatus::Open).state(
8505 StateUpdate::RequiresAction(crate::v2::RequiresActionStateUpdate::new())
8506 ))
8507 .unwrap(),
8508 json!({
8509 "status": "open",
8510 "state": {"state": "requires_action"}
8511 })
8512 );
8513 for state in [json!(null), json!("running"), json!({"state": 7})] {
8514 let response = serde_json::from_value::<SessionStatusResponse>(
8515 json!({"status": "open", "state": state}),
8516 )
8517 .unwrap();
8518 assert_eq!(response.state, None);
8519 }
8520 }
8521
8522 #[cfg(feature = "unstable_session_status")]
8523 #[test]
8524 fn test_session_status_is_strict_and_open() {
8525 let custom =
8526 serde_json::from_value::<SessionStatusResponse>(json!({"status": "_suspended"}))
8527 .unwrap();
8528 assert_eq!(
8529 custom.status,
8530 SessionStatus::Other("_suspended".to_string())
8531 );
8532 assert_eq!(
8533 serde_json::to_value(&custom).unwrap(),
8534 json!({"status": "_suspended"})
8535 );
8536 for result in [json!({}), json!({"status": null}), json!({"status": 1})] {
8537 assert!(serde_json::from_value::<SessionStatusResponse>(result).is_err());
8538 }
8539 for params in [json!({}), json!({"sessionId": null})] {
8540 assert!(serde_json::from_value::<SessionStatusRequest>(params).is_err());
8541 }
8542 }
8543
8544 #[cfg(feature = "unstable_session_archive")]
8545 #[test]
8546 fn test_session_archive_serialization() {
8547 assert_eq!(AGENT_METHOD_NAMES.session_archive, "session/archive");
8548 assert_eq!(AGENT_METHOD_NAMES.session_unarchive, "session/unarchive");
8549 assert_eq!(
8550 ClientRequest::ArchiveSessionRequest(Box::new(ArchiveSessionRequest::new(
8551 "sess_abc123"
8552 )))
8553 .method(),
8554 "session/archive"
8555 );
8556 assert_eq!(
8557 ClientRequest::UnarchiveSessionRequest(Box::new(UnarchiveSessionRequest::new(
8558 "sess_abc123"
8559 )))
8560 .method(),
8561 "session/unarchive"
8562 );
8563 assert_eq!(
8564 serde_json::to_value(ArchiveSessionRequest::new("sess_abc123")).unwrap(),
8565 json!({"sessionId": "sess_abc123"})
8566 );
8567 assert_eq!(
8568 serde_json::to_value(UnarchiveSessionResponse::new()).unwrap(),
8569 json!({})
8570 );
8571 assert_eq!(
8572 serde_json::to_value(
8573 SessionCapabilities::new().archive(SessionArchiveCapabilities::new())
8574 )
8575 .unwrap(),
8576 json!({"archive": {}})
8577 );
8578 assert_eq!(
8579 serde_json::to_value(
8580 ListSessionsRequest::new()
8581 .cwd("/home/user/project")
8582 .archived(SessionArchiveFilter::Only)
8583 )
8584 .unwrap(),
8585 json!({"cwd": "/home/user/project", "archived": "only"})
8586 );
8587 assert_eq!(
8588 serde_json::to_value(
8589 SessionInfo::new("sess_abc123", "/home/user/project").archived(true)
8590 )
8591 .unwrap(),
8592 json!({"sessionId": "sess_abc123", "cwd": "/home/user/project", "archived": true})
8593 );
8594 }
8595
8596 #[cfg(feature = "unstable_session_archive")]
8597 #[test]
8598 fn test_session_archive_filter_is_strict_and_open() {
8599 for (value, expected) in [
8600 (json!(null), None),
8601 (json!("include"), Some(SessionArchiveFilter::Include)),
8602 (
8603 json!("_recent"),
8604 Some(SessionArchiveFilter::Other("_recent".to_string())),
8605 ),
8606 ] {
8607 let request =
8608 serde_json::from_value::<ListSessionsRequest>(json!({"archived": value})).unwrap();
8609 assert_eq!(request.archived, expected);
8610 }
8611 for value in [json!(true), json!(1), json!({})] {
8612 assert!(
8613 serde_json::from_value::<ListSessionsRequest>(json!({"archived": value})).is_err()
8614 );
8615 }
8616 let info = serde_json::from_value::<SessionInfo>(
8617 json!({"sessionId": "sess_abc123", "cwd": "/home/user/project", "archived": "yes"}),
8618 )
8619 .unwrap();
8620 assert_eq!(info.archived, None);
8621 }
8622
8623 #[test]
8624 fn test_session_additional_directories_serialization() {
8625 assert_eq!(
8626 serde_json::to_value(NewSessionRequest::new("/home/user/project")).unwrap(),
8627 json!({
8628 "cwd": "/home/user/project",
8629 })
8630 );
8631 assert_eq!(
8632 serde_json::to_value(
8633 NewSessionRequest::new("/home/user/project").additional_directories(vec![
8634 PathBuf::from("/home/user/shared-lib"),
8635 PathBuf::from("/home/user/product-docs"),
8636 ])
8637 )
8638 .unwrap(),
8639 json!({
8640 "cwd": "/home/user/project",
8641 "additionalDirectories": [
8642 "/home/user/shared-lib",
8643 "/home/user/product-docs"
8644 ],
8645 })
8646 );
8647 assert_eq!(
8648 serde_json::to_value(ResumeSessionRequest::new(
8649 "sess_abc123",
8650 "/home/user/project"
8651 ))
8652 .unwrap(),
8653 json!({
8654 "sessionId": "sess_abc123",
8655 "cwd": "/home/user/project",
8656 })
8657 );
8658 assert_eq!(
8659 serde_json::from_value::<ResumeSessionRequest>(json!({
8660 "sessionId": "sess_abc123",
8661 "cwd": "/home/user/project"
8662 }))
8663 .unwrap()
8664 .mcp_servers,
8665 Vec::<McpServer>::new()
8666 );
8667 assert_eq!(
8668 serde_json::from_value::<ResumeSessionRequest>(json!({
8669 "sessionId": "sess_abc123",
8670 "cwd": "/home/user/project",
8671 "mcpServers": null
8672 }))
8673 .unwrap()
8674 .mcp_servers,
8675 Vec::<McpServer>::new()
8676 );
8677 assert_eq!(
8678 serde_json::to_value(SessionInfo::new("sess_abc123", "/home/user/project")).unwrap(),
8679 json!({
8680 "sessionId": "sess_abc123",
8681 "cwd": "/home/user/project"
8682 })
8683 );
8684 assert_eq!(
8685 serde_json::to_value(
8686 SessionInfo::new("sess_abc123", "/home/user/project").additional_directories(vec![
8687 PathBuf::from("/home/user/shared-lib"),
8688 PathBuf::from("/home/user/product-docs"),
8689 ])
8690 )
8691 .unwrap(),
8692 json!({
8693 "sessionId": "sess_abc123",
8694 "cwd": "/home/user/project",
8695 "additionalDirectories": [
8696 "/home/user/shared-lib",
8697 "/home/user/product-docs"
8698 ]
8699 })
8700 );
8701 assert_eq!(
8702 serde_json::from_value::<SessionInfo>(json!({
8703 "sessionId": "sess_abc123",
8704 "cwd": "/home/user/project"
8705 }))
8706 .unwrap()
8707 .additional_directories,
8708 Vec::<AbsolutePath>::new()
8709 );
8710 }
8711 #[test]
8712 fn test_session_additional_directories_capabilities_serialization() {
8713 assert_eq!(
8714 serde_json::to_value(
8715 SessionCapabilities::new()
8716 .additional_directories(SessionAdditionalDirectoriesCapabilities::new())
8717 )
8718 .unwrap(),
8719 json!({
8720 "additionalDirectories": {}
8721 })
8722 );
8723 }
8724
8725 #[cfg(feature = "unstable_session_system_prompt")]
8726 #[test]
8727 fn test_session_system_prompt_capabilities_serialization() {
8728 assert_eq!(
8729 serde_json::to_value(
8730 SessionCapabilities::new().system_prompt(SessionSystemPromptCapabilities::new())
8731 )
8732 .unwrap(),
8733 json!({ "systemPrompt": {} })
8734 );
8735 assert_eq!(
8736 serde_json::to_value(
8737 SessionCapabilities::new().system_prompt(
8738 SessionSystemPromptCapabilities::new().mode(
8739 SessionSystemPromptModeCapabilities::new()
8740 .override_(SessionSystemPromptOverrideCapabilities::new())
8741 )
8742 )
8743 )
8744 .unwrap(),
8745 json!({ "systemPrompt": { "mode": { "override": {} } } })
8746 );
8747 assert_eq!(
8748 serde_json::from_value::<SessionCapabilities>(
8749 json!({ "systemPrompt": { "mode": { "override": {} } } })
8750 )
8751 .unwrap()
8752 .system_prompt
8753 .and_then(|capabilities| capabilities.mode)
8754 .and_then(|mode| mode.override_),
8755 Some(SessionSystemPromptOverrideCapabilities::new())
8756 );
8757
8758 assert_eq!(
8760 serde_json::from_value::<SessionCapabilities>(json!({ "systemPrompt": "yes" }))
8761 .unwrap()
8762 .system_prompt,
8763 None
8764 );
8765 assert_eq!(
8766 serde_json::from_value::<SessionCapabilities>(
8767 json!({ "systemPrompt": { "mode": { "override": true } } })
8768 )
8769 .unwrap()
8770 .system_prompt,
8771 Some(
8772 SessionSystemPromptCapabilities::new()
8773 .mode(SessionSystemPromptModeCapabilities::new())
8774 )
8775 );
8776 assert_eq!(
8777 serde_json::from_value::<SessionCapabilities>(
8778 json!({ "systemPrompt": { "mode": "override" } })
8779 )
8780 .unwrap()
8781 .system_prompt,
8782 Some(SessionSystemPromptCapabilities::new())
8783 );
8784 }
8785
8786 #[cfg(feature = "unstable_session_system_prompt")]
8787 #[test]
8788 fn test_system_prompt_lifecycle_request_serialization() {
8789 let system_prompt = SystemPrompt::new(vec![ContentBlock::from(
8790 "You are a code review specialist.",
8791 )]);
8792 let system_prompt_json = json!({
8793 "mode": "append",
8794 "content": [{ "type": "text", "text": "You are a code review specialist." }]
8795 });
8796
8797 let request =
8798 NewSessionRequest::new("/home/user/project").system_prompt(system_prompt.clone());
8799 let request_json = json!({
8800 "cwd": "/home/user/project",
8801 "systemPrompt": system_prompt_json
8802 });
8803 assert_eq!(serde_json::to_value(&request).unwrap(), request_json);
8804 assert_eq!(
8805 serde_json::from_value::<NewSessionRequest>(request_json).unwrap(),
8806 request
8807 );
8808
8809 assert_eq!(
8810 serde_json::to_value(
8811 ResumeSessionRequest::new("sess_abc123", "/home/user/project")
8812 .system_prompt(system_prompt.clone())
8813 )
8814 .unwrap()["systemPrompt"],
8815 system_prompt_json
8816 );
8817 #[cfg(feature = "unstable_session_fork")]
8818 assert_eq!(
8819 serde_json::to_value(
8820 ForkSessionRequest::new("sess_abc123", "/home/user/project")
8821 .system_prompt(system_prompt.clone())
8822 )
8823 .unwrap()["systemPrompt"],
8824 system_prompt_json
8825 );
8826
8827 let override_prompt =
8828 SystemPrompt::new(vec![ContentBlock::from("...")]).mode(SystemPromptMode::Override);
8829 let override_json = json!({
8830 "mode": "override",
8831 "content": [{ "type": "text", "text": "..." }]
8832 });
8833 assert_eq!(
8834 serde_json::to_value(&override_prompt).unwrap(),
8835 override_json
8836 );
8837 assert_eq!(
8838 serde_json::from_value::<SystemPrompt>(override_json).unwrap(),
8839 override_prompt
8840 );
8841 }
8842
8843 #[cfg(feature = "unstable_session_system_prompt")]
8846 #[test]
8847 fn test_system_prompt_new_defaults_to_append() {
8848 let system_prompt = SystemPrompt::new(vec![ContentBlock::from("...")]);
8849 assert_eq!(system_prompt.mode, SystemPromptMode::Append);
8850 assert_eq!(
8851 serde_json::to_value(&system_prompt).unwrap(),
8852 json!({ "mode": "append", "content": [{ "type": "text", "text": "..." }] })
8853 );
8854 }
8855
8856 #[cfg(feature = "unstable_session_system_prompt")]
8860 #[test]
8861 fn test_system_prompt_preserves_unknown_modes() {
8862 for mode in ["_vendor_mode", "prepend"] {
8863 let system_prompt_json =
8864 json!({ "mode": mode, "content": [{ "type": "text", "text": "..." }] });
8865 let system_prompt: SystemPrompt =
8866 serde_json::from_value(system_prompt_json.clone()).unwrap();
8867 assert_eq!(
8868 system_prompt.mode,
8869 SystemPromptMode::Other(mode.to_string())
8870 );
8871 assert_eq!(
8872 serde_json::to_value(&system_prompt).unwrap(),
8873 system_prompt_json
8874 );
8875 }
8876 }
8877
8878 #[cfg(feature = "unstable_session_system_prompt")]
8882 fn assert_system_prompt_is_strict<T>(base: &serde_json::Value)
8883 where
8884 T: serde::de::DeserializeOwned + PartialEq + std::fmt::Debug,
8885 {
8886 let name = std::any::type_name::<T>();
8887 let with = |system_prompt: serde_json::Value| {
8888 let mut request = base.clone();
8889 request["systemPrompt"] = system_prompt;
8890 serde_json::from_value::<T>(request)
8891 };
8892
8893 for system_prompt in [
8894 json!("You are a code review specialist."),
8895 json!({ "content": [{ "type": "text", "text": "..." }] }),
8896 json!({ "mode": null, "content": [{ "type": "text", "text": "..." }] }),
8897 json!({ "mode": 1, "content": [{ "type": "text", "text": "..." }] }),
8898 json!({ "mode": "append" }),
8899 json!({ "mode": "append", "content": "..." }),
8900 json!({ "mode": "append", "content": [{ "type": "text" }] }),
8901 ] {
8902 assert!(
8903 with(system_prompt.clone()).is_err(),
8904 "{name}.systemPrompt: {system_prompt}"
8905 );
8906 }
8907
8908 assert_eq!(
8909 with(serde_json::Value::Null).unwrap(),
8910 serde_json::from_value::<T>(base.clone()).unwrap(),
8911 "{name}.systemPrompt"
8912 );
8913 }
8914
8915 #[cfg(feature = "unstable_session_system_prompt")]
8916 #[test]
8917 fn test_session_setup_requests_reject_malformed_system_prompts() {
8918 assert!(
8919 serde_json::to_value(NewSessionRequest::new("/repo"))
8920 .unwrap()
8921 .get("systemPrompt")
8922 .is_none()
8923 );
8924 assert_system_prompt_is_strict::<NewSessionRequest>(&json!({ "cwd": "/repo" }));
8925 assert_system_prompt_is_strict::<ResumeSessionRequest>(&json!({
8926 "sessionId": "sess-1",
8927 "cwd": "/repo"
8928 }));
8929 #[cfg(feature = "unstable_session_fork")]
8930 assert_system_prompt_is_strict::<ForkSessionRequest>(&json!({
8931 "sessionId": "sess-1",
8932 "cwd": "/repo"
8933 }));
8934 }
8935
8936 #[cfg(feature = "unstable_session_system_prompt")]
8939 #[test]
8940 fn test_system_prompt_preserves_unknown_content_blocks() {
8941 let request: NewSessionRequest = serde_json::from_value(json!({
8942 "cwd": "/home/user/project",
8943 "systemPrompt": {
8944 "mode": "append",
8945 "content": [{ "type": "_vendor_video", "uri": "https://example.com/intro.mp4" }]
8946 }
8947 }))
8948 .unwrap();
8949 let content = request.system_prompt.unwrap().content;
8950 assert_eq!(content.len(), 1);
8951 assert!(matches!(content[0], ContentBlock::Other(_)));
8952 }
8953
8954 #[test]
8955 fn test_auth_method_terminal_serialization() {
8956 let method = AuthMethod::Terminal(AuthMethodTerminal::new("tui-auth", "Terminal Auth"));
8957
8958 let json = serde_json::to_value(&method).unwrap();
8959 assert_eq!(
8960 json,
8961 json!({
8962 "methodId": "tui-auth",
8963 "name": "Terminal Auth",
8964 "type": "terminal"
8965 })
8966 );
8967 assert!(!json.as_object().unwrap().contains_key("args"));
8969 assert!(!json.as_object().unwrap().contains_key("env"));
8970
8971 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
8972 match deserialized {
8973 AuthMethod::Terminal(AuthMethodTerminal { args, env, .. }) => {
8974 assert_eq!(args, Vec::<String>::new());
8975 assert_eq!(env, []);
8976 }
8977 _ => panic!("Expected Terminal variant"),
8978 }
8979 }
8980
8981 #[test]
8982 fn test_auth_method_terminal_with_args_and_env_serialization() {
8983 let method = AuthMethod::Terminal(
8984 AuthMethodTerminal::new("tui-auth", "Terminal Auth")
8985 .args(vec!["--interactive".to_string(), "--color".to_string()])
8986 .env(vec![EnvVariable::new("TERM", "xterm-256color")]),
8987 );
8988
8989 let json = serde_json::to_value(&method).unwrap();
8990 assert_eq!(
8991 json,
8992 json!({
8993 "methodId": "tui-auth",
8994 "name": "Terminal Auth",
8995 "type": "terminal",
8996 "args": ["--interactive", "--color"],
8997 "env": [
8998 {
8999 "name": "TERM",
9000 "value": "xterm-256color"
9001 }
9002 ]
9003 })
9004 );
9005
9006 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
9007 match deserialized {
9008 AuthMethod::Terminal(AuthMethodTerminal { args, env, .. }) => {
9009 assert_eq!(args, vec!["--interactive", "--color"]);
9010 assert_eq!(env.len(), 1);
9011 assert_eq!(env[0].name, "TERM");
9012 assert_eq!(env[0].value, "xterm-256color");
9013 }
9014 _ => panic!("Expected Terminal variant"),
9015 }
9016 }
9017
9018 #[test]
9019 fn test_session_config_option_id_serialize() {
9020 let val = SessionConfigOptionValue::id("model-1");
9021 let json = serde_json::to_value(&val).unwrap();
9022 assert_eq!(json, json!({ "type": "id", "value": "model-1" }));
9023 }
9024
9025 #[test]
9026 fn test_session_config_option_value_boolean_serialize() {
9027 let val = SessionConfigOptionValue::boolean(true);
9028 let json = serde_json::to_value(&val).unwrap();
9029 assert_eq!(json, json!({ "type": "boolean", "value": true }));
9030 }
9031
9032 #[test]
9033 fn test_session_config_option_value_deserialize_id() {
9034 let json = json!({ "type": "id", "value": "model-1" });
9035 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
9036 assert_eq!(val, SessionConfigOptionValue::id("model-1"));
9037 assert_eq!(val.as_id().unwrap().to_string(), "model-1");
9038 }
9039
9040 #[test]
9041 fn test_session_config_option_value_deserialize_requires_type() {
9042 let json = json!({ "value": "model-1" });
9043 let result = serde_json::from_value::<SessionConfigOptionValue>(json);
9044 assert!(result.is_err());
9045 }
9046
9047 #[test]
9048 fn test_session_config_option_value_deserialize_boolean() {
9049 let json = json!({ "type": "boolean", "value": true });
9050 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
9051 assert_eq!(val, SessionConfigOptionValue::boolean(true));
9052 assert_eq!(val.as_bool(), Some(true));
9053 }
9054
9055 #[test]
9056 fn test_session_config_option_value_deserialize_boolean_false() {
9057 let json = json!({ "type": "boolean", "value": false });
9058 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
9059 assert_eq!(val, SessionConfigOptionValue::boolean(false));
9060 assert_eq!(val.as_bool(), Some(false));
9061 }
9062
9063 #[test]
9064 fn test_session_config_option_value_deserialize_unknown_type_with_string_value() {
9065 let json = json!({
9066 "type": "text",
9067 "value": "freeform input",
9068 "maxLength": 200
9069 });
9070 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
9071 let SessionConfigOptionValue::Other(unknown) = val else {
9072 panic!("Expected Other variant");
9073 };
9074 assert_eq!(unknown.type_, "text");
9075 assert_eq!(unknown.value, json!("freeform input"));
9076 assert_eq!(unknown.fields["maxLength"], json!(200));
9077 }
9078
9079 #[test]
9080 fn test_session_config_option_value_deserialize_unknown_type_with_object_value() {
9081 let json = json!({
9082 "type": "range",
9083 "value": { "min": 1, "max": 5 }
9084 });
9085 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
9086 let SessionConfigOptionValue::Other(unknown) = val else {
9087 panic!("Expected Other variant");
9088 };
9089 assert_eq!(unknown.type_, "range");
9090 assert_eq!(unknown.value, json!({ "min": 1, "max": 5 }));
9091 }
9092
9093 #[test]
9094 fn test_session_config_option_value_roundtrip_id() {
9095 let original = SessionConfigOptionValue::id("option-a");
9096 let json = serde_json::to_value(&original).unwrap();
9097 let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
9098 assert_eq!(original, roundtripped);
9099 }
9100
9101 #[test]
9102 fn test_session_config_option_value_roundtrip_boolean() {
9103 let original = SessionConfigOptionValue::boolean(false);
9104 let json = serde_json::to_value(&original).unwrap();
9105 let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
9106 assert_eq!(original, roundtripped);
9107 }
9108
9109 #[test]
9110 fn test_session_config_option_value_roundtrip_other() {
9111 let mut fields = BTreeMap::new();
9112 fields.insert("maxLength".to_string(), json!(200));
9113 let original = SessionConfigOptionValue::Other(OtherSessionConfigOptionValue::new(
9114 "text",
9115 json!("freeform input"),
9116 fields,
9117 ));
9118 let json = serde_json::to_value(&original).unwrap();
9119 let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
9120 assert_eq!(original, roundtripped);
9121 }
9122
9123 #[test]
9124 fn test_session_config_option_value_type_mismatch_boolean_with_string() {
9125 let json = json!({ "type": "boolean", "value": "not a bool" });
9126 let result = serde_json::from_value::<SessionConfigOptionValue>(json);
9127 assert!(result.is_err());
9128 }
9129
9130 #[test]
9131 fn test_session_config_option_value_from_impls() {
9132 let from_str: SessionConfigOptionValue = "model-1".into();
9133 assert_eq!(from_str.as_id().unwrap().to_string(), "model-1");
9134
9135 let from_id: SessionConfigOptionValue = SessionConfigValueId::new("model-2").into();
9136 assert_eq!(from_id.as_id().unwrap().to_string(), "model-2");
9137
9138 let from_bool: SessionConfigOptionValue = true.into();
9139 assert_eq!(from_bool.as_bool(), Some(true));
9140 }
9141
9142 #[test]
9143 fn test_set_session_config_option_request_id() {
9144 let req = SetSessionConfigOptionRequest::new("sess_1", "model", "model-1");
9145 let json = serde_json::to_value(&req).unwrap();
9146 assert_eq!(
9147 json,
9148 json!({
9149 "sessionId": "sess_1",
9150 "configId": "model",
9151 "type": "id",
9152 "value": "model-1"
9153 })
9154 );
9155 }
9156
9157 #[test]
9158 fn test_set_session_config_option_request_boolean() {
9159 let req = SetSessionConfigOptionRequest::new("sess_1", "brave_mode", true);
9160 let json = serde_json::to_value(&req).unwrap();
9161 assert_eq!(
9162 json,
9163 json!({
9164 "sessionId": "sess_1",
9165 "configId": "brave_mode",
9166 "type": "boolean",
9167 "value": true
9168 })
9169 );
9170 }
9171
9172 #[test]
9173 fn test_set_session_config_option_request_deserialize_requires_type() {
9174 let json = json!({
9175 "sessionId": "sess_1",
9176 "configId": "model",
9177 "value": "model-1"
9178 });
9179 let result = serde_json::from_value::<SetSessionConfigOptionRequest>(json);
9180 assert!(result.is_err());
9181 }
9182
9183 #[test]
9184 fn test_set_session_config_option_request_deserialize_boolean() {
9185 let json = json!({
9186 "sessionId": "sess_1",
9187 "configId": "brave_mode",
9188 "type": "boolean",
9189 "value": true
9190 });
9191 let req: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
9192 assert_eq!(req.value.as_bool(), Some(true));
9193 }
9194
9195 #[test]
9196 fn test_set_session_config_option_request_roundtrip_id() {
9197 let original = SetSessionConfigOptionRequest::new("s", "c", "v");
9198 let json = serde_json::to_value(&original).unwrap();
9199 let roundtripped: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
9200 assert_eq!(original, roundtripped);
9201 }
9202
9203 #[test]
9204 fn test_set_session_config_option_request_roundtrip_boolean() {
9205 let original = SetSessionConfigOptionRequest::new("s", "c", false);
9206 let json = serde_json::to_value(&original).unwrap();
9207 let roundtripped: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
9208 assert_eq!(original, roundtripped);
9209 }
9210
9211 #[test]
9212 fn test_session_config_boolean_serialization() {
9213 let cfg = SessionConfigBoolean::new(true);
9214 let json = serde_json::to_value(&cfg).unwrap();
9215 assert_eq!(json, json!({ "currentValue": true }));
9216
9217 let deserialized: SessionConfigBoolean = serde_json::from_value(json).unwrap();
9218 assert!(deserialized.current_value);
9219 }
9220
9221 #[test]
9222 fn test_session_config_option_boolean_variant() {
9223 let opt = SessionConfigOption::boolean("brave_mode", "Brave Mode", false)
9224 .description("Skip confirmation prompts")
9225 .meta(test_meta());
9226 assert_eq!(serialized_meta_key_count(&opt), 1);
9227
9228 let json = serde_json::to_value(&opt).unwrap();
9229 assert_eq!(
9230 json,
9231 json!({
9232 "configId": "brave_mode",
9233 "name": "Brave Mode",
9234 "description": "Skip confirmation prompts",
9235 "type": "boolean",
9236 "currentValue": false,
9237 "_meta": {
9238 "source": "test"
9239 }
9240 })
9241 );
9242
9243 let deserialized: SessionConfigOption = serde_json::from_value(json).unwrap();
9244 assert_eq!(deserialized.config_id.to_string(), "brave_mode");
9245 assert_eq!(deserialized.name, "Brave Mode");
9246 match deserialized.kind {
9247 SessionConfigKind::Boolean(ref b) => assert!(!b.current_value),
9248 _ => panic!("Expected Boolean kind"),
9249 }
9250 }
9251
9252 #[test]
9253 fn test_session_config_option_select_still_works() {
9254 let opt = SessionConfigOption::select(
9256 "model",
9257 "Model",
9258 "model-1",
9259 vec![
9260 SessionConfigSelectOption::new("model-1", "Model 1"),
9261 SessionConfigSelectOption::new("model-2", "Model 2"),
9262 ],
9263 )
9264 .meta(test_meta());
9265 assert_eq!(serialized_meta_key_count(&opt), 1);
9266
9267 let json = serde_json::to_value(&opt).unwrap();
9268 assert_eq!(json["type"], "select");
9269 assert_eq!(json["currentValue"], "model-1");
9270 assert_eq!(json["options"].as_array().unwrap().len(), 2);
9271 assert_eq!(json["_meta"]["source"], "test");
9272
9273 let deserialized: SessionConfigOption = serde_json::from_value(json).unwrap();
9274 match deserialized.kind {
9275 SessionConfigKind::Select(ref s) => {
9276 assert_eq!(s.current_value.to_string(), "model-1");
9277 }
9278 _ => panic!("Expected Select kind"),
9279 }
9280 }
9281
9282 #[test]
9283 fn test_session_config_option_unknown_kind_roundtrip() {
9284 let option: SessionConfigOption = serde_json::from_value(json!({
9285 "configId": "verbosity",
9286 "name": "Verbosity",
9287 "type": "_slider",
9288 "currentValue": 3,
9289 "min": 0,
9290 "max": 5,
9291 "_meta": {
9292 "source": "test"
9293 }
9294 }))
9295 .unwrap();
9296
9297 assert_eq!(option.config_id.to_string(), "verbosity");
9298 assert_eq!(option.meta.as_ref().unwrap()["source"], "test");
9299 let SessionConfigKind::Other(unknown) = &option.kind else {
9300 panic!("expected unknown config kind");
9301 };
9302 assert_eq!(unknown.type_, "_slider");
9303 assert_eq!(unknown.fields.get("currentValue"), Some(&json!(3)));
9304 assert!(!unknown.fields.contains_key("_meta"));
9305 assert_eq!(serialized_meta_key_count(&option), 1);
9306
9307 let json = serde_json::to_value(&option).unwrap();
9308 assert_eq!(json["type"], "_slider");
9309 assert_eq!(json["currentValue"], 3);
9310 assert_eq!(json["min"], 0);
9311 assert_eq!(json["max"], 5);
9312 assert_eq!(json["_meta"]["source"], "test");
9313 }
9314
9315 #[test]
9316 fn test_session_config_option_unknown_kind_does_not_duplicate_flattened_meta() {
9317 let mut fields = std::collections::BTreeMap::new();
9318 fields.insert("currentValue".to_string(), json!(3));
9319 fields.insert("_meta".to_string(), json!({ "inner": "ignored" }));
9320
9321 let option = SessionConfigOption::new(
9322 "verbosity",
9323 "Verbosity",
9324 SessionConfigKind::Other(OtherSessionConfigKind::new("_slider", fields)),
9325 )
9326 .meta(test_meta());
9327
9328 let SessionConfigKind::Other(unknown) = &option.kind else {
9329 panic!("expected unknown config kind");
9330 };
9331 assert!(!unknown.fields.contains_key("_meta"));
9332 assert_eq!(serialized_meta_key_count(&option), 1);
9333
9334 let json = serde_json::to_value(&option).unwrap();
9335 assert_eq!(json["type"], "_slider");
9336 assert_eq!(json["currentValue"], 3);
9337 assert_eq!(json["_meta"]["source"], "test");
9338 }
9339
9340 #[test]
9341 fn test_session_config_option_unknown_does_not_hide_malformed_known_kind() {
9342 assert!(
9343 serde_json::from_value::<SessionConfigOption>(json!({
9344 "configId": "model",
9345 "name": "Model",
9346 "type": "select"
9347 }))
9348 .is_err()
9349 );
9350 }
9351
9352 #[cfg(feature = "unstable_llm_providers")]
9353 #[test]
9354 fn test_llm_protocol_known_variants() {
9355 assert_eq!(
9356 serde_json::to_value(&LlmProtocol::Anthropic).unwrap(),
9357 json!("anthropic")
9358 );
9359 assert_eq!(
9360 serde_json::to_value(&LlmProtocol::OpenAi).unwrap(),
9361 json!("openai")
9362 );
9363 assert_eq!(
9364 serde_json::to_value(&LlmProtocol::Azure).unwrap(),
9365 json!("azure")
9366 );
9367 assert_eq!(
9368 serde_json::to_value(&LlmProtocol::Vertex).unwrap(),
9369 json!("vertex")
9370 );
9371 assert_eq!(
9372 serde_json::to_value(&LlmProtocol::Bedrock).unwrap(),
9373 json!("bedrock")
9374 );
9375
9376 assert_eq!(
9377 serde_json::from_str::<LlmProtocol>("\"anthropic\"").unwrap(),
9378 LlmProtocol::Anthropic
9379 );
9380 assert_eq!(
9381 serde_json::from_str::<LlmProtocol>("\"openai\"").unwrap(),
9382 LlmProtocol::OpenAi
9383 );
9384 assert_eq!(
9385 serde_json::from_str::<LlmProtocol>("\"azure\"").unwrap(),
9386 LlmProtocol::Azure
9387 );
9388 assert_eq!(
9389 serde_json::from_str::<LlmProtocol>("\"vertex\"").unwrap(),
9390 LlmProtocol::Vertex
9391 );
9392 assert_eq!(
9393 serde_json::from_str::<LlmProtocol>("\"bedrock\"").unwrap(),
9394 LlmProtocol::Bedrock
9395 );
9396 }
9397
9398 #[cfg(feature = "unstable_llm_providers")]
9399 #[test]
9400 fn test_llm_protocol_unknown_variant() {
9401 let unknown: LlmProtocol = serde_json::from_str("\"cohere\"").unwrap();
9402 assert_eq!(unknown, LlmProtocol::Other("cohere".to_string()));
9403
9404 let json = serde_json::to_value(&unknown).unwrap();
9405 assert_eq!(json, json!("cohere"));
9406 }
9407
9408 #[cfg(feature = "unstable_llm_providers")]
9409 #[test]
9410 fn test_provider_current_config_serialization() {
9411 let config =
9412 ProviderCurrentConfig::new(LlmProtocol::Anthropic, "https://api.anthropic.com");
9413
9414 let json = serde_json::to_value(&config).unwrap();
9415 assert_eq!(
9416 json,
9417 json!({
9418 "apiType": "anthropic",
9419 "baseUrl": "https://api.anthropic.com"
9420 })
9421 );
9422
9423 let deserialized: ProviderCurrentConfig = serde_json::from_value(json).unwrap();
9424 assert_eq!(deserialized.api_type, LlmProtocol::Anthropic);
9425 assert_eq!(deserialized.base_url, "https://api.anthropic.com");
9426 }
9427
9428 #[cfg(feature = "unstable_llm_providers")]
9429 #[test]
9430 fn test_provider_info_with_current_config() {
9431 let info = ProviderInfo::new(
9432 "main",
9433 vec![LlmProtocol::Anthropic, LlmProtocol::OpenAi],
9434 true,
9435 Some(ProviderCurrentConfig::new(
9436 LlmProtocol::Anthropic,
9437 "https://api.anthropic.com",
9438 )),
9439 );
9440
9441 let json = serde_json::to_value(&info).unwrap();
9442 assert_eq!(
9443 json,
9444 json!({
9445 "providerId": "main",
9446 "supported": ["anthropic", "openai"],
9447 "required": true,
9448 "current": {
9449 "apiType": "anthropic",
9450 "baseUrl": "https://api.anthropic.com"
9451 }
9452 })
9453 );
9454
9455 let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
9456 assert_eq!(deserialized.provider_id.to_string(), "main");
9457 assert_eq!(deserialized.supported.len(), 2);
9458 assert!(deserialized.required);
9459 assert!(deserialized.current.is_some());
9460 assert_eq!(
9461 deserialized.current.as_ref().unwrap().api_type,
9462 LlmProtocol::Anthropic
9463 );
9464 }
9465
9466 #[cfg(feature = "unstable_llm_providers")]
9467 #[test]
9468 fn test_provider_info_disabled() {
9469 let info = ProviderInfo::new(
9470 "secondary",
9471 vec![LlmProtocol::OpenAi],
9472 false,
9473 None::<ProviderCurrentConfig>,
9474 );
9475
9476 let json = serde_json::to_value(&info).unwrap();
9477 assert_eq!(
9478 json,
9479 json!({
9480 "providerId": "secondary",
9481 "supported": ["openai"],
9482 "required": false
9483 })
9484 );
9485
9486 let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
9487 assert_eq!(deserialized.provider_id.to_string(), "secondary");
9488 assert!(!deserialized.required);
9489 assert!(deserialized.current.is_none());
9490 }
9491
9492 #[cfg(feature = "unstable_llm_providers")]
9493 #[test]
9494 fn test_provider_info_missing_current_defaults_to_none() {
9495 let json = json!({
9497 "providerId": "main",
9498 "supported": ["anthropic"],
9499 "required": true
9500 });
9501 let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
9502 assert!(deserialized.current.is_none());
9503 }
9504
9505 #[cfg(feature = "unstable_llm_providers")]
9506 #[test]
9507 fn test_provider_info_explicit_null_current_decodes_to_none() {
9508 let json = json!({
9512 "providerId": "main",
9513 "supported": ["anthropic"],
9514 "required": true,
9515 "current": null
9516 });
9517 let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
9518 assert!(deserialized.current.is_none());
9519 }
9520
9521 #[cfg(feature = "unstable_llm_providers")]
9522 #[test]
9523 fn test_list_providers_response_serialization() {
9524 let response = ListProvidersResponse::new(vec![ProviderInfo::new(
9525 "main",
9526 vec![LlmProtocol::Anthropic],
9527 true,
9528 Some(ProviderCurrentConfig::new(
9529 LlmProtocol::Anthropic,
9530 "https://api.anthropic.com",
9531 )),
9532 )]);
9533
9534 let json = serde_json::to_value(&response).unwrap();
9535 assert_eq!(json["providers"].as_array().unwrap().len(), 1);
9536 assert_eq!(json["providers"][0]["providerId"], "main");
9537
9538 let deserialized: ListProvidersResponse = serde_json::from_value(json).unwrap();
9539 assert_eq!(deserialized.providers.len(), 1);
9540 }
9541
9542 #[cfg(feature = "unstable_llm_providers")]
9543 #[test]
9544 fn test_set_provider_request_serialization() {
9545 use std::collections::HashMap;
9546
9547 let mut headers = HashMap::new();
9548 headers.insert("Authorization".to_string(), "Bearer sk-test".to_string());
9549
9550 let request =
9551 SetProviderRequest::new("main", LlmProtocol::OpenAi, "https://api.openai.com/v1")
9552 .headers(headers);
9553
9554 let json = serde_json::to_value(&request).unwrap();
9555 assert_eq!(
9556 json,
9557 json!({
9558 "providerId": "main",
9559 "apiType": "openai",
9560 "baseUrl": "https://api.openai.com/v1",
9561 "headers": {
9562 "Authorization": "Bearer sk-test"
9563 }
9564 })
9565 );
9566
9567 let deserialized: SetProviderRequest = serde_json::from_value(json).unwrap();
9568 assert_eq!(deserialized.provider_id.to_string(), "main");
9569 assert_eq!(deserialized.api_type, LlmProtocol::OpenAi);
9570 assert_eq!(deserialized.base_url, "https://api.openai.com/v1");
9571 assert_eq!(deserialized.headers.len(), 1);
9572 assert_eq!(
9573 deserialized.headers.get("Authorization").unwrap(),
9574 "Bearer sk-test"
9575 );
9576 }
9577
9578 #[cfg(feature = "unstable_llm_providers")]
9579 #[test]
9580 fn test_set_provider_request_omits_empty_headers() {
9581 let request =
9582 SetProviderRequest::new("main", LlmProtocol::Anthropic, "https://api.anthropic.com");
9583
9584 let json = serde_json::to_value(&request).unwrap();
9585 assert!(!json.as_object().unwrap().contains_key("headers"));
9587 }
9588
9589 #[cfg(feature = "unstable_llm_providers")]
9590 #[test]
9591 fn test_disable_provider_request_serialization() {
9592 let request = DisableProviderRequest::new("secondary");
9593
9594 let json = serde_json::to_value(&request).unwrap();
9595 assert_eq!(json, json!({ "providerId": "secondary" }));
9596
9597 let deserialized: DisableProviderRequest = serde_json::from_value(json).unwrap();
9598 assert_eq!(deserialized.provider_id.to_string(), "secondary");
9599 }
9600
9601 #[cfg(feature = "unstable_llm_providers")]
9602 #[test]
9603 fn test_providers_capabilities_serialization() {
9604 let caps = ProvidersCapabilities::new();
9605
9606 let json = serde_json::to_value(&caps).unwrap();
9607 assert_eq!(json, json!({}));
9608
9609 let deserialized: ProvidersCapabilities = serde_json::from_value(json).unwrap();
9610 assert!(deserialized.meta.is_none());
9611 }
9612
9613 #[cfg(feature = "unstable_llm_providers")]
9614 #[test]
9615 fn test_agent_capabilities_with_providers() {
9616 let caps = AgentCapabilities::new().providers(ProvidersCapabilities::new());
9617
9618 let json = serde_json::to_value(&caps).unwrap();
9619 assert_eq!(json["providers"], json!({}));
9620
9621 let deserialized: AgentCapabilities = serde_json::from_value(json).unwrap();
9622 assert!(deserialized.providers.is_some());
9623 }
9624
9625 #[test]
9626 fn test_agent_capabilities_session_is_explicit() {
9627 let json = serde_json::to_value(AgentCapabilities::new()).unwrap();
9628 assert!(json.get("session").is_none());
9629
9630 let caps = AgentCapabilities::new().session(
9631 SessionCapabilities::new()
9632 .prompt(PromptCapabilities::new().image(PromptImageCapabilities::new()))
9633 .mcp(McpCapabilities::new().stdio(McpStdioCapabilities::new())),
9634 );
9635
9636 assert_eq!(
9637 serde_json::to_value(&caps).unwrap(),
9638 json!({
9639 "session": {
9640 "prompt": {
9641 "image": {}
9642 },
9643 "mcp": {
9644 "stdio": {}
9645 }
9646 }
9647 })
9648 );
9649
9650 let deserialized: AgentCapabilities = serde_json::from_value(json!({
9651 "session": false
9652 }))
9653 .unwrap();
9654 assert!(deserialized.session.is_none());
9655 }
9656
9657 #[test]
9658 fn test_prompt_capabilities_serialize_supported_content_as_objects() {
9659 let caps = PromptCapabilities::new()
9660 .image(PromptImageCapabilities::new())
9661 .audio(PromptAudioCapabilities::new())
9662 .embedded_context(PromptEmbeddedContextCapabilities::new());
9663
9664 assert_eq!(
9665 serde_json::to_value(&caps).unwrap(),
9666 json!({
9667 "image": {},
9668 "audio": {},
9669 "embeddedContext": {}
9670 })
9671 );
9672
9673 let deserialized: PromptCapabilities = serde_json::from_value(json!({
9674 "image": null,
9675 "audio": false,
9676 "embeddedContext": {}
9677 }))
9678 .unwrap();
9679 assert!(deserialized.image.is_none());
9680 assert!(deserialized.audio.is_none());
9681 assert!(deserialized.embedded_context.is_some());
9682 }
9683
9684 #[test]
9685 fn test_mcp_capabilities_serialize_supported_transports_as_objects() {
9686 let caps = McpCapabilities::new()
9687 .stdio(McpStdioCapabilities::new())
9688 .http(McpHttpCapabilities::new());
9689
9690 assert_eq!(
9691 serde_json::to_value(&caps).unwrap(),
9692 json!({
9693 "stdio": {},
9694 "http": {}
9695 })
9696 );
9697
9698 let deserialized: McpCapabilities = serde_json::from_value(json!({
9699 "stdio": null,
9700 "http": false
9701 }))
9702 .unwrap();
9703 assert!(deserialized.stdio.is_none());
9704 assert!(deserialized.http.is_none());
9705 }
9706
9707 #[cfg(feature = "unstable_mcp_over_acp")]
9708 #[test]
9709 fn test_mcp_capabilities_serialize_acp_support_as_object() {
9710 let caps = McpCapabilities::new().acp(McpAcpCapabilities::new());
9711
9712 assert_eq!(
9713 serde_json::to_value(&caps).unwrap(),
9714 json!({
9715 "acp": {}
9716 })
9717 );
9718 }
9719
9720 #[test]
9721 fn prompt_request_rejects_malformed_content_block() {
9722 use serde_json::json;
9723
9724 assert!(
9725 serde_json::from_value::<PromptRequest>(json!({
9726 "sessionId": "sess-1",
9727 "prompt": [{"type": "text"}]
9728 }))
9729 .is_err()
9730 );
9731 }
9732
9733 #[test]
9734 fn prompt_request_rejects_non_array_prompt() {
9735 use serde_json::json;
9736
9737 assert!(
9738 serde_json::from_value::<PromptRequest>(json!({
9739 "sessionId": "sess-1",
9740 "prompt": "hello"
9741 }))
9742 .is_err()
9743 );
9744 }
9745
9746 fn assert_session_setup_lists_are_strict<T>(base: &serde_json::Value)
9747 where
9748 T: serde::de::DeserializeOwned + PartialEq + std::fmt::Debug,
9749 {
9750 let name = std::any::type_name::<T>();
9751 let with = |field: &str, value: serde_json::Value| {
9752 let mut request = base.clone();
9753 request[field] = value;
9754 serde_json::from_value::<T>(request)
9755 };
9756
9757 for (field, value) in [
9758 (
9760 "mcpServers",
9761 json!([
9762 {"type": "stdio", "name": "github", "command": "/usr/bin/gh-mcp", "env": [{"name": "GITHUB_TOKEN", "value": null}]},
9763 {"type": "stdio", "name": "fs", "command": "/usr/bin/fs-mcp"}
9764 ]),
9765 ),
9766 ("mcpServers", json!({"type": "stdio", "name": "fs"})),
9767 ("additionalDirectories", json!(["/repo/lib", 42])),
9768 ("additionalDirectories", json!("/repo/lib")),
9769 ] {
9770 assert!(
9771 with(field, value.clone()).is_err(),
9772 "{name}.{field}: {value}"
9773 );
9774 }
9775
9776 let empty = serde_json::from_value::<T>(base.clone()).unwrap();
9777 for field in ["mcpServers", "additionalDirectories"] {
9778 assert_eq!(
9779 with(field, serde_json::Value::Null).unwrap(),
9780 empty,
9781 "{name}.{field}"
9782 );
9783 }
9784 }
9785
9786 #[test]
9787 fn session_setup_requests_reject_malformed_mcp_servers_and_directories() {
9788 assert_session_setup_lists_are_strict::<NewSessionRequest>(&json!({"cwd": "/repo"}));
9789 assert_session_setup_lists_are_strict::<ResumeSessionRequest>(&json!({
9790 "sessionId": "sess-1",
9791 "cwd": "/repo"
9792 }));
9793 #[cfg(feature = "unstable_session_fork")]
9794 assert_session_setup_lists_are_strict::<ForkSessionRequest>(&json!({
9795 "sessionId": "sess-1",
9796 "cwd": "/repo"
9797 }));
9798 }
9799
9800 #[test]
9801 fn resume_session_request_rejects_malformed_replay_from() {
9802 let request = |replay_from: serde_json::Value| {
9803 serde_json::from_value::<ResumeSessionRequest>(json!({
9804 "sessionId": "sess-1",
9805 "cwd": "/repo",
9806 "replayFrom": replay_from
9807 }))
9808 };
9809
9810 assert!(request(json!("start")).is_err());
9812 assert_eq!(request(serde_json::Value::Null).unwrap().replay_from, None);
9813 assert!(matches!(
9814 request(json!({"type": "start"})).unwrap().replay_from,
9815 Some(ReplayFrom::Start(_))
9816 ));
9817 }
9818
9819 #[test]
9820 fn mcp_server_lists_treat_null_as_empty() {
9821 let servers: Vec<McpServer> = serde_json::from_value(json!([
9822 {"type": "stdio", "name": "fs", "command": "/usr/bin/fs-mcp", "args": null, "env": null},
9823 {"type": "http", "name": "api", "url": "https://example.com/mcp", "headers": null}
9824 ]))
9825 .unwrap();
9826
9827 assert_eq!(
9828 servers,
9829 vec![
9830 McpServer::Stdio(McpServerStdio::new("fs", "/usr/bin/fs-mcp")),
9831 McpServer::Http(McpServerHttp::new("api", "https://example.com/mcp")),
9832 ]
9833 );
9834 }
9835
9836 #[test]
9837 fn terminal_auth_methods_reject_malformed_launch_config() {
9838 let response: InitializeResponse = serde_json::from_value(json!({
9842 "protocolVersion": 2,
9843 "info": {"name": "agent", "version": "1.0.0"},
9844 "authMethods": [
9845 {"type": "terminal", "methodId": "bad-args", "name": "Log in", "args": ["auth", "--port", 8123]},
9846 {"type": "terminal", "methodId": "bad-env", "name": "Log in", "env": [{"name": "DEBUG", "value": 1}]},
9847 {"type": "terminal", "methodId": "null-config", "name": "Log in", "args": null, "env": null},
9848 {"type": "agent", "methodId": "agent", "name": "Agent login"}
9849 ]
9850 }))
9851 .unwrap();
9852
9853 assert_eq!(
9854 response.auth_methods,
9855 vec![
9856 AuthMethod::Terminal(AuthMethodTerminal::new("null-config", "Log in")),
9857 AuthMethod::Agent(AuthMethodAgent::new("agent", "Agent login")),
9858 ]
9859 );
9860 }
9861
9862 #[cfg(feature = "unstable_auth_status")]
9863 #[test]
9864 fn auth_status_response_round_trips() {
9865 let response = AuthStatusResponse::new(AuthState::Authenticated).accounts(vec![
9866 AuthAccount::new("Anthropic API key").active(true),
9867 AuthAccount::new("Claude Max").email("user@example.com".to_string()),
9868 ]);
9869 let json = serde_json::to_value(&response).unwrap();
9870 assert_eq!(
9871 json,
9872 json!({
9873 "state": "authenticated",
9874 "accounts": [
9875 {
9876 "label": "Anthropic API key",
9877 "active": true
9878 },
9879 {
9880 "label": "Claude Max",
9881 "email": "user@example.com"
9882 }
9883 ]
9884 })
9885 );
9886 assert_eq!(
9887 serde_json::from_value::<AuthStatusResponse>(json).unwrap(),
9888 response
9889 );
9890 assert_eq!(
9891 serde_json::to_value(AuthStatusResponse::new(AuthState::NotRequired)).unwrap(),
9892 json!({"state": "not_required"})
9893 );
9894 }
9895
9896 #[cfg(feature = "unstable_auth_status")]
9897 #[test]
9898 fn auth_status_state_is_strict_and_accounts_are_lenient() {
9899 assert!(serde_json::from_value::<AuthStatusResponse>(json!({})).is_err());
9900 assert!(serde_json::from_value::<AuthStatusResponse>(json!({"state": null})).is_err());
9901 assert!(serde_json::from_value::<AuthStatusResponse>(json!({"state": 1})).is_err());
9902
9903 let response: AuthStatusResponse = serde_json::from_value(json!({
9904 "state": "_vendor_state",
9905 "accounts": [
9906 {"active": true},
9907 {
9908 "label": "Vertex AI",
9909 "active": "yes",
9910 "email": ["not", "a", "string"]
9911 }
9912 ]
9913 }))
9914 .unwrap();
9915 assert_eq!(
9916 response,
9917 AuthStatusResponse::new(AuthState::Other("_vendor_state".to_string()))
9918 .accounts(vec![AuthAccount::new("Vertex AI")])
9919 );
9920 assert_eq!(
9921 serde_json::from_value::<AuthStatusResponse>(
9922 json!({"state": "unknown", "accounts": null})
9923 )
9924 .unwrap(),
9925 AuthStatusResponse::new(AuthState::Unknown)
9926 );
9927 }
9928
9929 #[cfg(feature = "unstable_auth_status")]
9930 #[test]
9931 fn auth_status_update_notification_routes() {
9932 use crate::v2::{AgentNotification, CLIENT_METHOD_NAMES};
9933
9934 assert_eq!(AGENT_METHOD_NAMES.auth_status, "auth/status");
9935 assert_eq!(CLIENT_METHOD_NAMES.auth_status_update, "auth/status_update");
9936 let notification = AgentNotification::AuthStatusUpdateNotification(Box::new(
9937 AuthStatusUpdateNotification::new(AuthState::Unauthenticated),
9938 ));
9939 assert_eq!(notification.method(), "auth/status_update");
9940 assert_eq!(
9941 serde_json::to_value(¬ification).unwrap(),
9942 json!({"state": "unauthenticated"})
9943 );
9944 }
9945}