1use std::{collections::BTreeMap, path::PathBuf, sync::Arc};
7
8#[cfg(feature = "unstable_llm_providers")]
9use std::collections::HashMap;
10
11use derive_more::{Display, From};
12use schemars::{JsonSchema, Schema};
13use serde::{Deserialize, Serialize};
14use serde_with::{DefaultOnError, VecSkipError, serde_as, skip_serializing_none};
15
16use super::{
17 ClientCapabilities, ContentBlock, ExtNotification, ExtRequest, ExtResponse, Meta, SessionId,
18};
19#[cfg(feature = "unstable_auth_methods")]
20use crate::DefaultTrueOnError;
21use crate::{IntoOption, ProtocolVersion, SkipListener};
22
23#[cfg(feature = "unstable_mcp_over_acp")]
24use super::mcp::{
25 MCP_MESSAGE_METHOD_NAME, MessageMcpNotification, MessageMcpRequest, MessageMcpResponse,
26};
27
28#[cfg(feature = "unstable_nes")]
29use super::{
30 AcceptNesNotification, CloseNesRequest, CloseNesResponse, DidChangeDocumentNotification,
31 DidCloseDocumentNotification, DidFocusDocumentNotification, DidOpenDocumentNotification,
32 DidSaveDocumentNotification, NesCapabilities, PositionEncodingKind, RejectNesNotification,
33 StartNesRequest, StartNesResponse, SuggestNesRequest, SuggestNesResponse,
34};
35
36#[cfg(feature = "unstable_nes")]
37use super::{
38 DOCUMENT_DID_CHANGE_METHOD_NAME, DOCUMENT_DID_CLOSE_METHOD_NAME,
39 DOCUMENT_DID_FOCUS_METHOD_NAME, DOCUMENT_DID_OPEN_METHOD_NAME, DOCUMENT_DID_SAVE_METHOD_NAME,
40 NES_ACCEPT_METHOD_NAME, NES_CLOSE_METHOD_NAME, NES_REJECT_METHOD_NAME, NES_START_METHOD_NAME,
41 NES_SUGGEST_METHOD_NAME,
42};
43
44#[serde_as]
52#[skip_serializing_none]
53#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
54#[schemars(extend("x-side" = "agent", "x-method" = INITIALIZE_METHOD_NAME))]
55#[serde(rename_all = "camelCase")]
56#[non_exhaustive]
57pub struct InitializeRequest {
58 pub protocol_version: ProtocolVersion,
60 pub info: Implementation,
62 #[serde_as(deserialize_as = "DefaultOnError")]
64 #[schemars(extend("x-deserialize-default-on-error" = true))]
65 #[serde(default)]
66 pub capabilities: ClientCapabilities,
67 #[serde_as(deserialize_as = "DefaultOnError")]
73 #[schemars(extend("x-deserialize-default-on-error" = true))]
74 #[serde(default)]
75 #[serde(rename = "_meta")]
76 pub meta: Option<Meta>,
77}
78
79impl InitializeRequest {
80 #[must_use]
82 pub fn new(protocol_version: ProtocolVersion, info: Implementation) -> Self {
83 Self {
84 protocol_version,
85 capabilities: ClientCapabilities::default(),
86 info,
87 meta: None,
88 }
89 }
90
91 #[must_use]
93 pub fn capabilities(mut self, capabilities: ClientCapabilities) -> Self {
94 self.capabilities = capabilities;
95 self
96 }
97
98 #[must_use]
104 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
105 self.meta = meta.into_option();
106 self
107 }
108}
109
110#[serde_as]
116#[skip_serializing_none]
117#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
118#[schemars(extend("x-side" = "agent", "x-method" = INITIALIZE_METHOD_NAME))]
119#[serde(rename_all = "camelCase")]
120#[non_exhaustive]
121pub struct InitializeResponse {
122 pub protocol_version: ProtocolVersion,
127 pub info: Implementation,
129 #[serde_as(deserialize_as = "DefaultOnError")]
131 #[schemars(extend("x-deserialize-default-on-error" = true))]
132 #[serde(default)]
133 pub capabilities: AgentCapabilities,
134 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
136 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
137 #[serde(default, skip_serializing_if = "Vec::is_empty")]
138 pub auth_methods: Vec<AuthMethod>,
139 #[serde_as(deserialize_as = "DefaultOnError")]
145 #[schemars(extend("x-deserialize-default-on-error" = true))]
146 #[serde(default)]
147 #[serde(rename = "_meta")]
148 pub meta: Option<Meta>,
149}
150
151impl InitializeResponse {
152 #[must_use]
154 pub fn new(protocol_version: ProtocolVersion, info: Implementation) -> Self {
155 Self {
156 protocol_version,
157 capabilities: AgentCapabilities::default(),
158 auth_methods: vec![],
159 info,
160 meta: None,
161 }
162 }
163
164 #[must_use]
166 pub fn capabilities(mut self, capabilities: AgentCapabilities) -> Self {
167 self.capabilities = capabilities;
168 self
169 }
170
171 #[must_use]
173 pub fn auth_methods(mut self, auth_methods: Vec<AuthMethod>) -> Self {
174 self.auth_methods = auth_methods;
175 self
176 }
177
178 #[must_use]
184 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
185 self.meta = meta.into_option();
186 self
187 }
188}
189
190#[serde_as]
194#[skip_serializing_none]
195#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
196#[serde(rename_all = "camelCase")]
197#[non_exhaustive]
198pub struct Implementation {
199 pub name: String,
202 #[serde_as(deserialize_as = "DefaultOnError")]
207 #[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 #[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]
242 pub fn title(mut self, title: impl IntoOption<String>) -> Self {
243 self.title = title.into_option();
244 self
245 }
246
247 #[must_use]
253 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
254 self.meta = meta.into_option();
255 self
256 }
257}
258
259#[serde_as]
265#[skip_serializing_none]
266#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
267#[schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGIN_METHOD_NAME))]
268#[serde(rename_all = "camelCase")]
269#[non_exhaustive]
270pub struct LoginAuthRequest {
271 pub method_id: AuthMethodId,
274 #[serde_as(deserialize_as = "DefaultOnError")]
280 #[schemars(extend("x-deserialize-default-on-error" = true))]
281 #[serde(default)]
282 #[serde(rename = "_meta")]
283 pub meta: Option<Meta>,
284}
285
286impl LoginAuthRequest {
287 #[must_use]
289 pub fn new(method_id: impl Into<AuthMethodId>) -> Self {
290 Self {
291 method_id: method_id.into(),
292 meta: None,
293 }
294 }
295
296 #[must_use]
302 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
303 self.meta = meta.into_option();
304 self
305 }
306}
307
308#[serde_as]
310#[skip_serializing_none]
311#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
312#[schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGIN_METHOD_NAME))]
313#[serde(rename_all = "camelCase")]
314#[non_exhaustive]
315pub struct LoginAuthResponse {
316 #[serde_as(deserialize_as = "DefaultOnError")]
322 #[schemars(extend("x-deserialize-default-on-error" = true))]
323 #[serde(default)]
324 #[serde(rename = "_meta")]
325 pub meta: Option<Meta>,
326}
327
328impl LoginAuthResponse {
329 #[must_use]
331 pub fn new() -> Self {
332 Self::default()
333 }
334
335 #[must_use]
341 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
342 self.meta = meta.into_option();
343 self
344 }
345}
346
347#[serde_as]
353#[skip_serializing_none]
354#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
355#[schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGOUT_METHOD_NAME))]
356#[serde(rename_all = "camelCase")]
357#[non_exhaustive]
358pub struct LogoutAuthRequest {
359 #[serde_as(deserialize_as = "DefaultOnError")]
365 #[schemars(extend("x-deserialize-default-on-error" = true))]
366 #[serde(default)]
367 #[serde(rename = "_meta")]
368 pub meta: Option<Meta>,
369}
370
371impl LogoutAuthRequest {
372 #[must_use]
374 pub fn new() -> Self {
375 Self::default()
376 }
377
378 #[must_use]
384 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
385 self.meta = meta.into_option();
386 self
387 }
388}
389
390#[serde_as]
392#[skip_serializing_none]
393#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
394#[schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGOUT_METHOD_NAME))]
395#[serde(rename_all = "camelCase")]
396#[non_exhaustive]
397pub struct LogoutAuthResponse {
398 #[serde_as(deserialize_as = "DefaultOnError")]
404 #[schemars(extend("x-deserialize-default-on-error" = true))]
405 #[serde(default)]
406 #[serde(rename = "_meta")]
407 pub meta: Option<Meta>,
408}
409
410impl LogoutAuthResponse {
411 #[must_use]
413 pub fn new() -> Self {
414 Self::default()
415 }
416
417 #[must_use]
423 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
424 self.meta = meta.into_option();
425 self
426 }
427}
428
429#[serde_as]
431#[skip_serializing_none]
432#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
433#[serde(rename_all = "camelCase")]
434#[non_exhaustive]
435pub struct AgentAuthCapabilities {
436 #[serde_as(deserialize_as = "DefaultOnError")]
442 #[schemars(extend("x-deserialize-default-on-error" = true))]
443 #[serde(default)]
444 #[serde(rename = "_meta")]
445 pub meta: Option<Meta>,
446}
447
448impl AgentAuthCapabilities {
449 #[must_use]
451 pub fn new() -> Self {
452 Self::default()
453 }
454
455 #[must_use]
461 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
462 self.meta = meta.into_option();
463 self
464 }
465}
466
467#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, Display, From)]
469#[serde(transparent)]
470#[from(Arc<str>, String, &'static str)]
471#[non_exhaustive]
472pub struct AuthMethodId(pub Arc<str>);
473
474impl AuthMethodId {
475 #[must_use]
477 pub fn new(id: impl Into<Arc<str>>) -> Self {
478 Self(id.into())
479 }
480}
481
482#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
486#[serde(tag = "type", rename_all = "snake_case")]
487#[non_exhaustive]
488pub enum AuthMethod {
489 #[cfg(feature = "unstable_auth_methods")]
495 EnvVar(AuthMethodEnvVar),
496 #[cfg(feature = "unstable_auth_methods")]
502 Terminal(AuthMethodTerminal),
503 Agent(AuthMethodAgent),
507 #[serde(untagged)]
517 Other(OtherAuthMethod),
518}
519
520impl AuthMethod {
521 #[must_use]
523 pub fn method_id(&self) -> &AuthMethodId {
524 match self {
525 Self::Agent(a) => &a.method_id,
526 Self::Other(a) => &a.method_id,
527 #[cfg(feature = "unstable_auth_methods")]
528 Self::EnvVar(e) => &e.method_id,
529 #[cfg(feature = "unstable_auth_methods")]
530 Self::Terminal(t) => &t.method_id,
531 }
532 }
533
534 #[must_use]
536 pub fn name(&self) -> &str {
537 match self {
538 Self::Agent(a) => &a.name,
539 Self::Other(a) => &a.name,
540 #[cfg(feature = "unstable_auth_methods")]
541 Self::EnvVar(e) => &e.name,
542 #[cfg(feature = "unstable_auth_methods")]
543 Self::Terminal(t) => &t.name,
544 }
545 }
546
547 #[must_use]
549 pub fn description(&self) -> Option<&str> {
550 match self {
551 Self::Agent(a) => a.description.as_deref(),
552 Self::Other(a) => a.description.as_deref(),
553 #[cfg(feature = "unstable_auth_methods")]
554 Self::EnvVar(e) => e.description.as_deref(),
555 #[cfg(feature = "unstable_auth_methods")]
556 Self::Terminal(t) => t.description.as_deref(),
557 }
558 }
559
560 #[must_use]
566 pub fn meta(&self) -> Option<&Meta> {
567 match self {
568 Self::Agent(a) => a.meta.as_ref(),
569 Self::Other(a) => a.meta.as_ref(),
570 #[cfg(feature = "unstable_auth_methods")]
571 Self::EnvVar(e) => e.meta.as_ref(),
572 #[cfg(feature = "unstable_auth_methods")]
573 Self::Terminal(t) => t.meta.as_ref(),
574 }
575 }
576}
577
578#[serde_as]
580#[skip_serializing_none]
581#[derive(Debug, Clone, Serialize, JsonSchema, PartialEq, Eq)]
582#[schemars(inline)]
583#[schemars(transform = other_auth_method_schema)]
584#[serde(rename_all = "camelCase")]
585#[non_exhaustive]
586pub struct OtherAuthMethod {
587 #[serde(rename = "type")]
593 pub type_: String,
594 pub method_id: AuthMethodId,
596 pub name: String,
598 #[serde_as(deserialize_as = "DefaultOnError")]
600 #[schemars(extend("x-deserialize-default-on-error" = true))]
601 #[serde(default)]
602 pub description: Option<String>,
603 #[serde_as(deserialize_as = "DefaultOnError")]
609 #[schemars(extend("x-deserialize-default-on-error" = true))]
610 #[serde(default)]
611 #[serde(rename = "_meta")]
612 pub meta: Option<Meta>,
613 #[serde(flatten)]
615 pub fields: BTreeMap<String, serde_json::Value>,
616}
617
618impl OtherAuthMethod {
619 #[must_use]
621 pub fn new(
622 type_: impl Into<String>,
623 method_id: impl Into<AuthMethodId>,
624 name: impl Into<String>,
625 mut fields: BTreeMap<String, serde_json::Value>,
626 ) -> Self {
627 fields.remove("type");
628 fields.remove("methodId");
629 fields.remove("name");
630 fields.remove("description");
631 fields.remove("_meta");
632 Self {
633 type_: type_.into(),
634 method_id: method_id.into(),
635 name: name.into(),
636 description: None,
637 meta: None,
638 fields,
639 }
640 }
641
642 #[must_use]
644 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
645 self.description = description.into_option();
646 self
647 }
648
649 #[must_use]
655 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
656 self.meta = meta.into_option();
657 self
658 }
659}
660
661impl<'de> Deserialize<'de> for OtherAuthMethod {
662 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
663 where
664 D: serde::Deserializer<'de>,
665 {
666 #[derive(Deserialize)]
667 #[serde(rename_all = "camelCase")]
668 struct RawOtherAuthMethod {
669 #[serde(rename = "type")]
670 type_: String,
671 method_id: AuthMethodId,
672 name: String,
673 description: Option<String>,
674 #[serde(rename = "_meta")]
675 meta: Option<Meta>,
676 #[serde(flatten)]
677 fields: BTreeMap<String, serde_json::Value>,
678 }
679
680 let raw = RawOtherAuthMethod::deserialize(deserializer)?;
681 if is_known_auth_method_type(&raw.type_) {
682 return Err(serde::de::Error::custom(format!(
683 "known authentication method `{}` did not match its schema",
684 raw.type_
685 )));
686 }
687
688 Ok(Self {
689 type_: raw.type_,
690 method_id: raw.method_id,
691 name: raw.name,
692 description: raw.description,
693 meta: raw.meta,
694 fields: raw.fields,
695 })
696 }
697}
698
699fn is_known_auth_method_type(type_: &str) -> bool {
700 match type_ {
701 "agent" => true,
702 #[cfg(feature = "unstable_auth_methods")]
703 "env_var" | "terminal" => true,
704 _ => false,
705 }
706}
707
708fn other_auth_method_schema(schema: &mut Schema) {
709 super::schema_util::reject_known_string_discriminators(
710 schema,
711 "type",
712 &[
713 "agent",
714 #[cfg(feature = "unstable_auth_methods")]
715 "env_var",
716 #[cfg(feature = "unstable_auth_methods")]
717 "terminal",
718 ],
719 );
720}
721
722#[serde_as]
726#[skip_serializing_none]
727#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
728#[serde(rename_all = "camelCase")]
729#[non_exhaustive]
730pub struct AuthMethodAgent {
731 pub method_id: AuthMethodId,
733 pub name: String,
735 #[serde_as(deserialize_as = "DefaultOnError")]
737 #[schemars(extend("x-deserialize-default-on-error" = true))]
738 #[serde(default)]
739 pub description: Option<String>,
740 #[serde_as(deserialize_as = "DefaultOnError")]
746 #[schemars(extend("x-deserialize-default-on-error" = true))]
747 #[serde(default)]
748 #[serde(rename = "_meta")]
749 pub meta: Option<Meta>,
750}
751
752impl AuthMethodAgent {
753 #[must_use]
755 pub fn new(method_id: impl Into<AuthMethodId>, name: impl Into<String>) -> Self {
756 Self {
757 method_id: method_id.into(),
758 name: name.into(),
759 description: None,
760 meta: None,
761 }
762 }
763
764 #[must_use]
766 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
767 self.description = description.into_option();
768 self
769 }
770
771 #[must_use]
777 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
778 self.meta = meta.into_option();
779 self
780 }
781}
782
783#[cfg(feature = "unstable_auth_methods")]
791#[serde_as]
792#[skip_serializing_none]
793#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
794#[serde(rename_all = "camelCase")]
795#[non_exhaustive]
796pub struct AuthMethodEnvVar {
797 pub method_id: AuthMethodId,
799 pub name: String,
801 #[serde_as(deserialize_as = "DefaultOnError")]
803 #[schemars(extend("x-deserialize-default-on-error" = true))]
804 #[serde(default)]
805 pub description: Option<String>,
806 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
808 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
809 pub vars: Vec<AuthEnvVar>,
810 #[serde_as(deserialize_as = "DefaultOnError")]
812 #[schemars(extend("x-deserialize-default-on-error" = true))]
813 #[schemars(url)]
814 #[serde(default)]
815 pub link: Option<String>,
816 #[serde_as(deserialize_as = "DefaultOnError")]
822 #[schemars(extend("x-deserialize-default-on-error" = true))]
823 #[serde(default)]
824 #[serde(rename = "_meta")]
825 pub meta: Option<Meta>,
826}
827
828#[cfg(feature = "unstable_auth_methods")]
829impl AuthMethodEnvVar {
830 #[must_use]
832 pub fn new(
833 method_id: impl Into<AuthMethodId>,
834 name: impl Into<String>,
835 vars: Vec<AuthEnvVar>,
836 ) -> Self {
837 Self {
838 method_id: method_id.into(),
839 name: name.into(),
840 description: None,
841 vars,
842 link: None,
843 meta: None,
844 }
845 }
846
847 #[must_use]
849 pub fn link(mut self, link: impl IntoOption<String>) -> Self {
850 self.link = link.into_option();
851 self
852 }
853
854 #[must_use]
856 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
857 self.description = description.into_option();
858 self
859 }
860
861 #[must_use]
867 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
868 self.meta = meta.into_option();
869 self
870 }
871}
872
873#[cfg(feature = "unstable_auth_methods")]
879#[serde_as]
880#[skip_serializing_none]
881#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
882#[serde(rename_all = "camelCase")]
883#[non_exhaustive]
884pub struct AuthEnvVar {
885 pub name: String,
887 #[serde_as(deserialize_as = "DefaultOnError")]
889 #[schemars(extend("x-deserialize-default-on-error" = true))]
890 #[serde(default)]
891 pub label: Option<String>,
892 #[serde_as(deserialize_as = "DefaultTrueOnError")]
897 #[schemars(extend("x-deserialize-default-on-error" = true))]
898 #[serde(default = "default_true", skip_serializing_if = "is_true")]
899 #[schemars(extend("default" = true))]
900 pub secret: bool,
901 #[serde_as(deserialize_as = "DefaultOnError")]
905 #[schemars(extend("x-deserialize-default-on-error" = true))]
906 #[serde(default, skip_serializing_if = "is_false")]
907 #[schemars(extend("default" = false))]
908 pub optional: bool,
909 #[serde_as(deserialize_as = "DefaultOnError")]
915 #[schemars(extend("x-deserialize-default-on-error" = true))]
916 #[serde(default)]
917 #[serde(rename = "_meta")]
918 pub meta: Option<Meta>,
919}
920
921#[cfg(feature = "unstable_auth_methods")]
922fn default_true() -> bool {
923 true
924}
925
926#[cfg(feature = "unstable_auth_methods")]
927#[expect(clippy::trivially_copy_pass_by_ref)]
928fn is_true(v: &bool) -> bool {
929 *v
930}
931
932#[cfg(feature = "unstable_auth_methods")]
933#[expect(clippy::trivially_copy_pass_by_ref)]
934fn is_false(v: &bool) -> bool {
935 !*v
936}
937
938#[cfg(feature = "unstable_auth_methods")]
939impl AuthEnvVar {
940 #[must_use]
942 pub fn new(name: impl Into<String>) -> Self {
943 Self {
944 name: name.into(),
945 label: None,
946 secret: true,
947 optional: false,
948 meta: None,
949 }
950 }
951
952 #[must_use]
954 pub fn label(mut self, label: impl IntoOption<String>) -> Self {
955 self.label = label.into_option();
956 self
957 }
958
959 #[must_use]
962 pub fn secret(mut self, secret: bool) -> Self {
963 self.secret = secret;
964 self
965 }
966
967 #[must_use]
969 pub fn optional(mut self, optional: bool) -> Self {
970 self.optional = optional;
971 self
972 }
973
974 #[must_use]
980 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
981 self.meta = meta.into_option();
982 self
983 }
984}
985
986#[cfg(feature = "unstable_auth_methods")]
994#[serde_as]
995#[skip_serializing_none]
996#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
997#[serde(rename_all = "camelCase")]
998#[non_exhaustive]
999pub struct AuthMethodTerminal {
1000 pub method_id: AuthMethodId,
1002 pub name: String,
1004 #[serde_as(deserialize_as = "DefaultOnError")]
1006 #[schemars(extend("x-deserialize-default-on-error" = true))]
1007 #[serde(default)]
1008 pub description: Option<String>,
1009 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1011 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
1012 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1013 pub args: Vec<String>,
1014 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1016 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
1017 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1018 pub env: Vec<EnvVariable>,
1019 #[serde_as(deserialize_as = "DefaultOnError")]
1025 #[schemars(extend("x-deserialize-default-on-error" = true))]
1026 #[serde(default)]
1027 #[serde(rename = "_meta")]
1028 pub meta: Option<Meta>,
1029}
1030
1031#[cfg(feature = "unstable_auth_methods")]
1032impl AuthMethodTerminal {
1033 #[must_use]
1035 pub fn new(method_id: impl Into<AuthMethodId>, name: impl Into<String>) -> Self {
1036 Self {
1037 method_id: method_id.into(),
1038 name: name.into(),
1039 description: None,
1040 args: Vec::new(),
1041 env: Vec::new(),
1042 meta: None,
1043 }
1044 }
1045
1046 #[must_use]
1048 pub fn args(mut self, args: Vec<String>) -> Self {
1049 self.args = args;
1050 self
1051 }
1052
1053 #[must_use]
1055 pub fn env(mut self, env: Vec<EnvVariable>) -> Self {
1056 self.env = env;
1057 self
1058 }
1059
1060 #[must_use]
1062 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
1063 self.description = description.into_option();
1064 self
1065 }
1066
1067 #[must_use]
1073 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1074 self.meta = meta.into_option();
1075 self
1076 }
1077}
1078
1079#[serde_as]
1085#[skip_serializing_none]
1086#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1087#[schemars(extend("x-side" = "agent", "x-method" = SESSION_NEW_METHOD_NAME))]
1088#[serde(rename_all = "camelCase")]
1089#[non_exhaustive]
1090pub struct NewSessionRequest {
1091 pub cwd: PathBuf,
1093 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1099 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
1100 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1101 pub additional_directories: Vec<PathBuf>,
1102 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1104 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
1105 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1106 pub mcp_servers: Vec<McpServer>,
1107 #[serde_as(deserialize_as = "DefaultOnError")]
1113 #[schemars(extend("x-deserialize-default-on-error" = true))]
1114 #[serde(default)]
1115 #[serde(rename = "_meta")]
1116 pub meta: Option<Meta>,
1117}
1118
1119impl NewSessionRequest {
1120 #[must_use]
1122 pub fn new(cwd: impl Into<PathBuf>) -> Self {
1123 Self {
1124 cwd: cwd.into(),
1125 additional_directories: vec![],
1126 mcp_servers: vec![],
1127 meta: None,
1128 }
1129 }
1130
1131 #[must_use]
1133 pub fn additional_directories(mut self, additional_directories: Vec<PathBuf>) -> Self {
1134 self.additional_directories = additional_directories;
1135 self
1136 }
1137
1138 #[must_use]
1140 pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1141 self.mcp_servers = mcp_servers;
1142 self
1143 }
1144
1145 #[must_use]
1151 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1152 self.meta = meta.into_option();
1153 self
1154 }
1155}
1156
1157#[serde_as]
1161#[skip_serializing_none]
1162#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1163#[schemars(extend("x-side" = "agent", "x-method" = SESSION_NEW_METHOD_NAME))]
1164#[serde(rename_all = "camelCase")]
1165#[non_exhaustive]
1166pub struct NewSessionResponse {
1167 pub session_id: SessionId,
1171 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1173 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
1174 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1175 pub config_options: Vec<SessionConfigOption>,
1176 #[serde_as(deserialize_as = "DefaultOnError")]
1182 #[schemars(extend("x-deserialize-default-on-error" = true))]
1183 #[serde(default)]
1184 #[serde(rename = "_meta")]
1185 pub meta: Option<Meta>,
1186}
1187
1188impl NewSessionResponse {
1189 #[must_use]
1191 pub fn new(session_id: impl Into<SessionId>) -> Self {
1192 Self {
1193 session_id: session_id.into(),
1194 config_options: Vec::new(),
1195 meta: None,
1196 }
1197 }
1198
1199 #[must_use]
1201 pub fn config_options(mut self, config_options: Vec<SessionConfigOption>) -> Self {
1202 self.config_options = config_options;
1203 self
1204 }
1205
1206 #[must_use]
1212 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1213 self.meta = meta.into_option();
1214 self
1215 }
1216}
1217
1218#[cfg(feature = "unstable_session_fork")]
1231#[serde_as]
1232#[skip_serializing_none]
1233#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1234#[schemars(extend("x-side" = "agent", "x-method" = SESSION_FORK_METHOD_NAME))]
1235#[serde(rename_all = "camelCase")]
1236#[non_exhaustive]
1237pub struct ForkSessionRequest {
1238 pub session_id: SessionId,
1240 pub cwd: PathBuf,
1242 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1248 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
1249 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1250 pub additional_directories: Vec<PathBuf>,
1251 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1253 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
1254 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1255 pub mcp_servers: Vec<McpServer>,
1256 #[serde_as(deserialize_as = "DefaultOnError")]
1262 #[schemars(extend("x-deserialize-default-on-error" = true))]
1263 #[serde(default)]
1264 #[serde(rename = "_meta")]
1265 pub meta: Option<Meta>,
1266}
1267
1268#[cfg(feature = "unstable_session_fork")]
1269impl ForkSessionRequest {
1270 #[must_use]
1272 pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<PathBuf>) -> Self {
1273 Self {
1274 session_id: session_id.into(),
1275 cwd: cwd.into(),
1276 additional_directories: vec![],
1277 mcp_servers: vec![],
1278 meta: None,
1279 }
1280 }
1281
1282 #[must_use]
1284 pub fn additional_directories(mut self, additional_directories: Vec<PathBuf>) -> Self {
1285 self.additional_directories = additional_directories;
1286 self
1287 }
1288
1289 #[must_use]
1291 pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1292 self.mcp_servers = mcp_servers;
1293 self
1294 }
1295
1296 #[must_use]
1302 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1303 self.meta = meta.into_option();
1304 self
1305 }
1306}
1307
1308#[cfg(feature = "unstable_session_fork")]
1314#[serde_as]
1315#[skip_serializing_none]
1316#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1317#[schemars(extend("x-side" = "agent", "x-method" = SESSION_FORK_METHOD_NAME))]
1318#[serde(rename_all = "camelCase")]
1319#[non_exhaustive]
1320pub struct ForkSessionResponse {
1321 pub session_id: SessionId,
1323 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1325 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
1326 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1327 pub config_options: Vec<SessionConfigOption>,
1328 #[serde_as(deserialize_as = "DefaultOnError")]
1334 #[schemars(extend("x-deserialize-default-on-error" = true))]
1335 #[serde(default)]
1336 #[serde(rename = "_meta")]
1337 pub meta: Option<Meta>,
1338}
1339
1340#[cfg(feature = "unstable_session_fork")]
1341impl ForkSessionResponse {
1342 #[must_use]
1344 pub fn new(session_id: impl Into<SessionId>) -> Self {
1345 Self {
1346 session_id: session_id.into(),
1347 config_options: Vec::new(),
1348 meta: None,
1349 }
1350 }
1351
1352 #[must_use]
1354 pub fn config_options(mut self, config_options: Vec<SessionConfigOption>) -> Self {
1355 self.config_options = config_options;
1356 self
1357 }
1358
1359 #[must_use]
1365 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1366 self.meta = meta.into_option();
1367 self
1368 }
1369}
1370
1371#[serde_as]
1378#[skip_serializing_none]
1379#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1380#[schemars(extend("x-side" = "agent", "x-method" = SESSION_RESUME_METHOD_NAME))]
1381#[serde(rename_all = "camelCase")]
1382#[non_exhaustive]
1383pub struct ResumeSessionRequest {
1384 pub session_id: SessionId,
1386 pub cwd: PathBuf,
1388 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1395 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
1396 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1397 pub additional_directories: Vec<PathBuf>,
1398 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1400 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
1401 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1402 pub mcp_servers: Vec<McpServer>,
1403 #[serde_as(deserialize_as = "DefaultOnError")]
1411 #[schemars(extend("x-deserialize-default-on-error" = true))]
1412 #[serde(default)]
1413 pub replay_from: Option<ReplayFrom>,
1414 #[serde_as(deserialize_as = "DefaultOnError")]
1420 #[schemars(extend("x-deserialize-default-on-error" = true))]
1421 #[serde(default)]
1422 #[serde(rename = "_meta")]
1423 pub meta: Option<Meta>,
1424}
1425
1426impl ResumeSessionRequest {
1427 #[must_use]
1429 pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<PathBuf>) -> Self {
1430 Self {
1431 session_id: session_id.into(),
1432 cwd: cwd.into(),
1433 additional_directories: vec![],
1434 mcp_servers: vec![],
1435 replay_from: None,
1436 meta: None,
1437 }
1438 }
1439
1440 #[must_use]
1442 pub fn additional_directories(mut self, additional_directories: Vec<PathBuf>) -> Self {
1443 self.additional_directories = additional_directories;
1444 self
1445 }
1446
1447 #[must_use]
1449 pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1450 self.mcp_servers = mcp_servers;
1451 self
1452 }
1453
1454 #[must_use]
1462 pub fn replay_from(mut self, replay_from: impl IntoOption<ReplayFrom>) -> Self {
1463 self.replay_from = replay_from.into_option();
1464 self
1465 }
1466
1467 #[must_use]
1473 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1474 self.meta = meta.into_option();
1475 self
1476 }
1477}
1478
1479#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1483#[serde(tag = "type", rename_all = "snake_case")]
1484#[schemars(extend("discriminator" = {"propertyName": "type"}))]
1485#[non_exhaustive]
1486pub enum ReplayFrom {
1487 Start(ReplayFromStart),
1489 #[serde(untagged)]
1499 Other(OtherReplayFrom),
1500}
1501
1502impl From<ReplayFromStart> for ReplayFrom {
1503 fn from(replay_from: ReplayFromStart) -> Self {
1504 Self::Start(replay_from)
1505 }
1506}
1507
1508#[serde_as]
1510#[skip_serializing_none]
1511#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1512#[serde(rename_all = "camelCase")]
1513#[non_exhaustive]
1514pub struct ReplayFromStart {
1515 #[serde_as(deserialize_as = "DefaultOnError")]
1521 #[schemars(extend("x-deserialize-default-on-error" = true))]
1522 #[serde(default)]
1523 #[serde(rename = "_meta")]
1524 pub meta: Option<Meta>,
1525}
1526
1527impl ReplayFromStart {
1528 #[must_use]
1530 pub fn new() -> Self {
1531 Self::default()
1532 }
1533
1534 #[must_use]
1540 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1541 self.meta = meta.into_option();
1542 self
1543 }
1544}
1545
1546#[serde_as]
1548#[skip_serializing_none]
1549#[derive(Debug, Clone, Serialize, JsonSchema, PartialEq, Eq)]
1550#[schemars(inline)]
1551#[schemars(transform = other_replay_from_schema)]
1552#[serde(rename_all = "camelCase")]
1553#[non_exhaustive]
1554pub struct OtherReplayFrom {
1555 #[serde(rename = "type")]
1561 pub type_: String,
1562 #[serde_as(deserialize_as = "DefaultOnError")]
1568 #[schemars(extend("x-deserialize-default-on-error" = true))]
1569 #[serde(default)]
1570 #[serde(rename = "_meta")]
1571 pub meta: Option<Meta>,
1572 #[serde(flatten)]
1574 pub fields: BTreeMap<String, serde_json::Value>,
1575}
1576
1577impl OtherReplayFrom {
1578 #[must_use]
1580 pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
1581 fields.remove("type");
1582 fields.remove("_meta");
1583 Self {
1584 type_: type_.into(),
1585 meta: None,
1586 fields,
1587 }
1588 }
1589
1590 #[must_use]
1596 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1597 self.meta = meta.into_option();
1598 self
1599 }
1600}
1601
1602impl<'de> Deserialize<'de> for OtherReplayFrom {
1603 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1604 where
1605 D: serde::Deserializer<'de>,
1606 {
1607 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
1608 let type_ = fields
1609 .remove("type")
1610 .ok_or_else(|| serde::de::Error::missing_field("type"))?;
1611 let serde_json::Value::String(type_) = type_ else {
1612 return Err(serde::de::Error::custom("`type` must be a string"));
1613 };
1614
1615 if is_known_replay_from_type(&type_) {
1616 return Err(serde::de::Error::custom(format!(
1617 "known replay cursor `{type_}` did not match its schema"
1618 )));
1619 }
1620
1621 let meta = fields
1622 .remove("_meta")
1623 .and_then(|value| serde_json::from_value(value).ok());
1624
1625 Ok(Self {
1626 type_,
1627 meta,
1628 fields,
1629 })
1630 }
1631}
1632
1633fn is_known_replay_from_type(type_: &str) -> bool {
1634 matches!(type_, "start")
1635}
1636
1637fn other_replay_from_schema(schema: &mut Schema) {
1638 super::schema_util::reject_known_string_discriminators(schema, "type", &["start"]);
1639}
1640
1641#[serde_as]
1643#[skip_serializing_none]
1644#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1645#[schemars(extend("x-side" = "agent", "x-method" = SESSION_RESUME_METHOD_NAME))]
1646#[serde(rename_all = "camelCase")]
1647#[non_exhaustive]
1648pub struct ResumeSessionResponse {
1649 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1651 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
1652 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1653 pub config_options: Vec<SessionConfigOption>,
1654 #[serde_as(deserialize_as = "DefaultOnError")]
1660 #[schemars(extend("x-deserialize-default-on-error" = true))]
1661 #[serde(default)]
1662 #[serde(rename = "_meta")]
1663 pub meta: Option<Meta>,
1664}
1665
1666impl ResumeSessionResponse {
1667 #[must_use]
1669 pub fn new() -> Self {
1670 Self::default()
1671 }
1672
1673 #[must_use]
1675 pub fn config_options(mut self, config_options: Vec<SessionConfigOption>) -> Self {
1676 self.config_options = config_options;
1677 self
1678 }
1679
1680 #[must_use]
1686 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1687 self.meta = meta.into_option();
1688 self
1689 }
1690}
1691
1692#[serde_as]
1700#[skip_serializing_none]
1701#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1702#[schemars(extend("x-side" = "agent", "x-method" = SESSION_CLOSE_METHOD_NAME))]
1703#[serde(rename_all = "camelCase")]
1704#[non_exhaustive]
1705pub struct CloseSessionRequest {
1706 pub session_id: SessionId,
1708 #[serde_as(deserialize_as = "DefaultOnError")]
1714 #[schemars(extend("x-deserialize-default-on-error" = true))]
1715 #[serde(default)]
1716 #[serde(rename = "_meta")]
1717 pub meta: Option<Meta>,
1718}
1719
1720impl CloseSessionRequest {
1721 #[must_use]
1723 pub fn new(session_id: impl Into<SessionId>) -> Self {
1724 Self {
1725 session_id: session_id.into(),
1726 meta: None,
1727 }
1728 }
1729
1730 #[must_use]
1736 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1737 self.meta = meta.into_option();
1738 self
1739 }
1740}
1741
1742#[serde_as]
1744#[skip_serializing_none]
1745#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1746#[schemars(extend("x-side" = "agent", "x-method" = SESSION_CLOSE_METHOD_NAME))]
1747#[serde(rename_all = "camelCase")]
1748#[non_exhaustive]
1749pub struct CloseSessionResponse {
1750 #[serde_as(deserialize_as = "DefaultOnError")]
1756 #[schemars(extend("x-deserialize-default-on-error" = true))]
1757 #[serde(default)]
1758 #[serde(rename = "_meta")]
1759 pub meta: Option<Meta>,
1760}
1761
1762impl CloseSessionResponse {
1763 #[must_use]
1765 pub fn new() -> Self {
1766 Self::default()
1767 }
1768
1769 #[must_use]
1775 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1776 self.meta = meta.into_option();
1777 self
1778 }
1779}
1780
1781#[serde_as]
1785#[skip_serializing_none]
1786#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1787#[schemars(extend("x-side" = "agent", "x-method" = SESSION_LIST_METHOD_NAME))]
1788#[serde(rename_all = "camelCase")]
1789#[non_exhaustive]
1790pub struct ListSessionsRequest {
1791 #[serde(default)]
1793 pub cwd: Option<PathBuf>,
1794 #[serde(default)]
1796 pub cursor: Option<String>,
1797 #[serde_as(deserialize_as = "DefaultOnError")]
1803 #[schemars(extend("x-deserialize-default-on-error" = true))]
1804 #[serde(default)]
1805 #[serde(rename = "_meta")]
1806 pub meta: Option<Meta>,
1807}
1808
1809impl ListSessionsRequest {
1810 #[must_use]
1812 pub fn new() -> Self {
1813 Self::default()
1814 }
1815
1816 #[must_use]
1818 pub fn cwd(mut self, cwd: impl IntoOption<PathBuf>) -> Self {
1819 self.cwd = cwd.into_option();
1820 self
1821 }
1822
1823 #[must_use]
1825 pub fn cursor(mut self, cursor: impl IntoOption<String>) -> Self {
1826 self.cursor = cursor.into_option();
1827 self
1828 }
1829
1830 #[must_use]
1836 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1837 self.meta = meta.into_option();
1838 self
1839 }
1840}
1841
1842#[serde_as]
1844#[skip_serializing_none]
1845#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1846#[schemars(extend("x-side" = "agent", "x-method" = SESSION_LIST_METHOD_NAME))]
1847#[serde(rename_all = "camelCase")]
1848#[non_exhaustive]
1849pub struct ListSessionsResponse {
1850 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1852 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
1853 pub sessions: Vec<SessionInfo>,
1854 #[serde_as(deserialize_as = "DefaultOnError")]
1857 #[schemars(extend("x-deserialize-default-on-error" = true))]
1858 #[serde(default)]
1859 pub next_cursor: Option<String>,
1860 #[serde_as(deserialize_as = "DefaultOnError")]
1866 #[schemars(extend("x-deserialize-default-on-error" = true))]
1867 #[serde(default)]
1868 #[serde(rename = "_meta")]
1869 pub meta: Option<Meta>,
1870}
1871
1872impl ListSessionsResponse {
1873 #[must_use]
1875 pub fn new(sessions: Vec<SessionInfo>) -> Self {
1876 Self {
1877 sessions,
1878 next_cursor: None,
1879 meta: None,
1880 }
1881 }
1882
1883 #[must_use]
1885 pub fn next_cursor(mut self, next_cursor: impl IntoOption<String>) -> Self {
1886 self.next_cursor = next_cursor.into_option();
1887 self
1888 }
1889
1890 #[must_use]
1896 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1897 self.meta = meta.into_option();
1898 self
1899 }
1900}
1901
1902#[serde_as]
1908#[skip_serializing_none]
1909#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1910#[schemars(extend("x-side" = "agent", "x-method" = SESSION_DELETE_METHOD_NAME))]
1911#[serde(rename_all = "camelCase")]
1912#[non_exhaustive]
1913pub struct DeleteSessionRequest {
1914 pub session_id: SessionId,
1916 #[serde_as(deserialize_as = "DefaultOnError")]
1922 #[schemars(extend("x-deserialize-default-on-error" = true))]
1923 #[serde(default)]
1924 #[serde(rename = "_meta")]
1925 pub meta: Option<Meta>,
1926}
1927
1928impl DeleteSessionRequest {
1929 #[must_use]
1931 pub fn new(session_id: impl Into<SessionId>) -> Self {
1932 Self {
1933 session_id: session_id.into(),
1934 meta: None,
1935 }
1936 }
1937
1938 #[must_use]
1944 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1945 self.meta = meta.into_option();
1946 self
1947 }
1948}
1949
1950#[serde_as]
1952#[skip_serializing_none]
1953#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1954#[schemars(extend("x-side" = "agent", "x-method" = SESSION_DELETE_METHOD_NAME))]
1955#[serde(rename_all = "camelCase")]
1956#[non_exhaustive]
1957pub struct DeleteSessionResponse {
1958 #[serde_as(deserialize_as = "DefaultOnError")]
1964 #[schemars(extend("x-deserialize-default-on-error" = true))]
1965 #[serde(default)]
1966 #[serde(rename = "_meta")]
1967 pub meta: Option<Meta>,
1968}
1969
1970impl DeleteSessionResponse {
1971 #[must_use]
1973 pub fn new() -> Self {
1974 Self::default()
1975 }
1976
1977 #[must_use]
1983 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1984 self.meta = meta.into_option();
1985 self
1986 }
1987}
1988
1989#[serde_as]
1991#[skip_serializing_none]
1992#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1993#[serde(rename_all = "camelCase")]
1994#[non_exhaustive]
1995pub struct SessionInfo {
1996 pub session_id: SessionId,
1998 pub cwd: PathBuf,
2000 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
2006 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
2007 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2008 pub additional_directories: Vec<PathBuf>,
2009
2010 #[serde_as(deserialize_as = "DefaultOnError")]
2012 #[schemars(extend("x-deserialize-default-on-error" = true))]
2013 #[serde(default)]
2014 pub title: Option<String>,
2015 #[serde_as(deserialize_as = "DefaultOnError")]
2017 #[schemars(extend("x-deserialize-default-on-error" = true, "format" = "date-time"))]
2018 #[serde(default)]
2019 pub updated_at: Option<String>,
2020 #[serde_as(deserialize_as = "DefaultOnError")]
2026 #[schemars(extend("x-deserialize-default-on-error" = true))]
2027 #[serde(default)]
2028 #[serde(rename = "_meta")]
2029 pub meta: Option<Meta>,
2030}
2031
2032impl SessionInfo {
2033 #[must_use]
2035 pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<PathBuf>) -> Self {
2036 Self {
2037 session_id: session_id.into(),
2038 cwd: cwd.into(),
2039 additional_directories: vec![],
2040 title: None,
2041 updated_at: None,
2042 meta: None,
2043 }
2044 }
2045
2046 #[must_use]
2048 pub fn additional_directories(mut self, additional_directories: Vec<PathBuf>) -> Self {
2049 self.additional_directories = additional_directories;
2050 self
2051 }
2052
2053 #[must_use]
2055 pub fn title(mut self, title: impl IntoOption<String>) -> Self {
2056 self.title = title.into_option();
2057 self
2058 }
2059
2060 #[must_use]
2062 pub fn updated_at(mut self, updated_at: impl IntoOption<String>) -> Self {
2063 self.updated_at = updated_at.into_option();
2064 self
2065 }
2066
2067 #[must_use]
2073 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2074 self.meta = meta.into_option();
2075 self
2076 }
2077}
2078
2079#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, From, Display)]
2083#[serde(transparent)]
2084#[from(Arc<str>, String, &'static str)]
2085#[non_exhaustive]
2086pub struct SessionConfigId(pub Arc<str>);
2087
2088impl SessionConfigId {
2089 #[must_use]
2091 pub fn new(id: impl Into<Arc<str>>) -> Self {
2092 Self(id.into())
2093 }
2094}
2095
2096#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, From, Display)]
2098#[serde(transparent)]
2099#[from(Arc<str>, String, &'static str)]
2100#[non_exhaustive]
2101pub struct SessionConfigValueId(pub Arc<str>);
2102
2103impl SessionConfigValueId {
2104 #[must_use]
2106 pub fn new(id: impl Into<Arc<str>>) -> Self {
2107 Self(id.into())
2108 }
2109}
2110
2111#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, From, Display)]
2113#[serde(transparent)]
2114#[from(Arc<str>, String, &'static str)]
2115#[non_exhaustive]
2116pub struct SessionConfigGroupId(pub Arc<str>);
2117
2118impl SessionConfigGroupId {
2119 #[must_use]
2121 pub fn new(id: impl Into<Arc<str>>) -> Self {
2122 Self(id.into())
2123 }
2124}
2125
2126#[serde_as]
2128#[skip_serializing_none]
2129#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2130#[serde(rename_all = "camelCase")]
2131#[non_exhaustive]
2132pub struct SessionConfigSelectOption {
2133 pub value: SessionConfigValueId,
2135 pub name: String,
2137 #[serde_as(deserialize_as = "DefaultOnError")]
2139 #[schemars(extend("x-deserialize-default-on-error" = true))]
2140 #[serde(default)]
2141 pub description: Option<String>,
2142 #[serde_as(deserialize_as = "DefaultOnError")]
2148 #[schemars(extend("x-deserialize-default-on-error" = true))]
2149 #[serde(default)]
2150 #[serde(rename = "_meta")]
2151 pub meta: Option<Meta>,
2152}
2153
2154impl SessionConfigSelectOption {
2155 #[must_use]
2157 pub fn new(value: impl Into<SessionConfigValueId>, name: impl Into<String>) -> Self {
2158 Self {
2159 value: value.into(),
2160 name: name.into(),
2161 description: None,
2162 meta: None,
2163 }
2164 }
2165
2166 #[must_use]
2168 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
2169 self.description = description.into_option();
2170 self
2171 }
2172
2173 #[must_use]
2179 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2180 self.meta = meta.into_option();
2181 self
2182 }
2183}
2184
2185#[serde_as]
2187#[skip_serializing_none]
2188#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2189#[serde(rename_all = "camelCase")]
2190#[non_exhaustive]
2191pub struct SessionConfigSelectGroup {
2192 pub group_id: SessionConfigGroupId,
2194 pub name: String,
2196 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
2198 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
2199 pub options: Vec<SessionConfigSelectOption>,
2200 #[serde_as(deserialize_as = "DefaultOnError")]
2206 #[schemars(extend("x-deserialize-default-on-error" = true))]
2207 #[serde(default)]
2208 #[serde(rename = "_meta")]
2209 pub meta: Option<Meta>,
2210}
2211
2212impl SessionConfigSelectGroup {
2213 #[must_use]
2215 pub fn new(
2216 group_id: impl Into<SessionConfigGroupId>,
2217 name: impl Into<String>,
2218 options: Vec<SessionConfigSelectOption>,
2219 ) -> Self {
2220 Self {
2221 group_id: group_id.into(),
2222 name: name.into(),
2223 options,
2224 meta: None,
2225 }
2226 }
2227
2228 #[must_use]
2234 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2235 self.meta = meta.into_option();
2236 self
2237 }
2238}
2239
2240#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2242#[serde(untagged)]
2243#[non_exhaustive]
2244pub enum SessionConfigSelectOptions {
2245 Ungrouped(Vec<SessionConfigSelectOption>),
2247 Grouped(Vec<SessionConfigSelectGroup>),
2249}
2250
2251impl From<Vec<SessionConfigSelectOption>> for SessionConfigSelectOptions {
2252 fn from(options: Vec<SessionConfigSelectOption>) -> Self {
2253 SessionConfigSelectOptions::Ungrouped(options)
2254 }
2255}
2256
2257impl From<Vec<SessionConfigSelectGroup>> for SessionConfigSelectOptions {
2258 fn from(groups: Vec<SessionConfigSelectGroup>) -> Self {
2259 SessionConfigSelectOptions::Grouped(groups)
2260 }
2261}
2262
2263#[skip_serializing_none]
2265#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2266#[serde(rename_all = "camelCase")]
2267#[non_exhaustive]
2268pub struct SessionConfigSelect {
2269 pub current_value: SessionConfigValueId,
2271 pub options: SessionConfigSelectOptions,
2273}
2274
2275impl SessionConfigSelect {
2276 #[must_use]
2278 pub fn new(
2279 current_value: impl Into<SessionConfigValueId>,
2280 options: impl Into<SessionConfigSelectOptions>,
2281 ) -> Self {
2282 Self {
2283 current_value: current_value.into(),
2284 options: options.into(),
2285 }
2286 }
2287}
2288
2289#[skip_serializing_none]
2291#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2292#[serde(rename_all = "camelCase")]
2293#[non_exhaustive]
2294pub struct SessionConfigBoolean {
2295 pub current_value: bool,
2297}
2298
2299impl SessionConfigBoolean {
2300 #[must_use]
2302 pub fn new(current_value: bool) -> Self {
2303 Self { current_value }
2304 }
2305}
2306
2307#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2317#[serde(rename_all = "snake_case")]
2318#[non_exhaustive]
2319pub enum SessionConfigOptionCategory {
2320 Mode,
2322 Model,
2324 ModelConfig,
2326 ThoughtLevel,
2328 #[serde(untagged)]
2334 Other(String),
2335}
2336
2337#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2339#[serde(tag = "type", rename_all = "snake_case")]
2340#[schemars(extend("discriminator" = {"propertyName": "type"}))]
2341#[non_exhaustive]
2342pub enum SessionConfigKind {
2343 Select(SessionConfigSelect),
2345 Boolean(SessionConfigBoolean),
2347 #[serde(untagged)]
2357 Other(OtherSessionConfigKind),
2358}
2359
2360#[derive(Debug, Clone, Serialize, JsonSchema, PartialEq, Eq)]
2362#[schemars(inline)]
2363#[schemars(transform = other_session_config_kind_schema)]
2364#[serde(rename_all = "camelCase")]
2365#[non_exhaustive]
2366pub struct OtherSessionConfigKind {
2367 #[serde(rename = "type")]
2373 pub type_: String,
2374 #[serde(flatten)]
2376 pub fields: BTreeMap<String, serde_json::Value>,
2377}
2378
2379impl OtherSessionConfigKind {
2380 #[must_use]
2382 pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
2383 fields.remove("type");
2384 fields.remove("_meta");
2385 Self {
2386 type_: type_.into(),
2387 fields,
2388 }
2389 }
2390}
2391
2392impl<'de> Deserialize<'de> for OtherSessionConfigKind {
2393 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2394 where
2395 D: serde::Deserializer<'de>,
2396 {
2397 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
2398 let type_ = fields
2399 .remove("type")
2400 .ok_or_else(|| serde::de::Error::missing_field("type"))?;
2401 let serde_json::Value::String(type_) = type_ else {
2402 return Err(serde::de::Error::custom("`type` must be a string"));
2403 };
2404
2405 if is_known_session_config_kind_type(&type_) {
2406 return Err(serde::de::Error::custom(format!(
2407 "known session configuration option `{type_}` did not match its schema"
2408 )));
2409 }
2410
2411 Ok(Self { type_, fields })
2412 }
2413}
2414
2415fn is_known_session_config_kind_type(type_: &str) -> bool {
2416 matches!(type_, "select" | "boolean")
2417}
2418
2419fn other_session_config_kind_schema(schema: &mut Schema) {
2420 super::schema_util::reject_known_string_discriminators(schema, "type", &["select", "boolean"]);
2421}
2422
2423#[serde_as]
2425#[skip_serializing_none]
2426#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2427#[serde(rename_all = "camelCase")]
2428#[non_exhaustive]
2429pub struct SessionConfigOption {
2430 pub config_id: SessionConfigId,
2432 pub name: String,
2434 #[serde_as(deserialize_as = "DefaultOnError")]
2436 #[schemars(extend("x-deserialize-default-on-error" = true))]
2437 #[serde(default)]
2438 pub description: Option<String>,
2439 #[serde_as(deserialize_as = "DefaultOnError")]
2441 #[schemars(extend("x-deserialize-default-on-error" = true))]
2442 #[serde(default)]
2443 pub category: Option<SessionConfigOptionCategory>,
2444 #[serde(flatten)]
2446 pub kind: SessionConfigKind,
2447 #[serde_as(deserialize_as = "DefaultOnError")]
2453 #[schemars(extend("x-deserialize-default-on-error" = true))]
2454 #[serde(default)]
2455 #[serde(rename = "_meta")]
2456 pub meta: Option<Meta>,
2457}
2458
2459impl SessionConfigOption {
2460 #[must_use]
2462 pub fn new(
2463 config_id: impl Into<SessionConfigId>,
2464 name: impl Into<String>,
2465 kind: SessionConfigKind,
2466 ) -> Self {
2467 Self {
2468 config_id: config_id.into(),
2469 name: name.into(),
2470 description: None,
2471 category: None,
2472 kind,
2473 meta: None,
2474 }
2475 }
2476
2477 #[must_use]
2479 pub fn select(
2480 config_id: impl Into<SessionConfigId>,
2481 name: impl Into<String>,
2482 current_value: impl Into<SessionConfigValueId>,
2483 options: impl Into<SessionConfigSelectOptions>,
2484 ) -> Self {
2485 Self::new(
2486 config_id,
2487 name,
2488 SessionConfigKind::Select(SessionConfigSelect::new(current_value, options)),
2489 )
2490 }
2491
2492 #[must_use]
2494 pub fn boolean(
2495 config_id: impl Into<SessionConfigId>,
2496 name: impl Into<String>,
2497 current_value: bool,
2498 ) -> Self {
2499 Self::new(
2500 config_id,
2501 name,
2502 SessionConfigKind::Boolean(SessionConfigBoolean::new(current_value)),
2503 )
2504 }
2505
2506 #[must_use]
2508 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
2509 self.description = description.into_option();
2510 self
2511 }
2512
2513 #[must_use]
2515 pub fn category(mut self, category: impl IntoOption<SessionConfigOptionCategory>) -> Self {
2516 self.category = category.into_option();
2517 self
2518 }
2519
2520 #[must_use]
2526 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2527 self.meta = meta.into_option();
2528 self
2529 }
2530}
2531
2532#[derive(Debug, Clone, Serialize, JsonSchema, PartialEq, Eq)]
2541#[serde(tag = "type", rename_all = "snake_case")]
2542#[schemars(extend("discriminator" = {"propertyName": "type"}))]
2543#[non_exhaustive]
2544pub enum SessionConfigOptionValue {
2545 Id {
2547 value: SessionConfigValueId,
2549 },
2550 Boolean {
2552 value: bool,
2554 },
2555 #[serde(untagged)]
2561 Other(OtherSessionConfigOptionValue),
2562}
2563
2564#[derive(Debug, Clone, Serialize, JsonSchema, PartialEq, Eq)]
2566#[schemars(inline)]
2567#[schemars(transform = other_session_config_option_value_schema)]
2568#[serde(rename_all = "camelCase")]
2569#[non_exhaustive]
2570pub struct OtherSessionConfigOptionValue {
2571 #[serde(rename = "type")]
2577 pub type_: String,
2578 pub value: serde_json::Value,
2580 #[serde(flatten)]
2582 pub fields: BTreeMap<String, serde_json::Value>,
2583}
2584
2585impl OtherSessionConfigOptionValue {
2586 #[must_use]
2588 pub fn new(
2589 type_: impl Into<String>,
2590 value: serde_json::Value,
2591 mut fields: BTreeMap<String, serde_json::Value>,
2592 ) -> Self {
2593 fields.remove("type");
2594 fields.remove("value");
2595 fields.remove("_meta");
2596 Self {
2597 type_: type_.into(),
2598 value,
2599 fields,
2600 }
2601 }
2602}
2603
2604impl<'de> Deserialize<'de> for OtherSessionConfigOptionValue {
2605 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2606 where
2607 D: serde::Deserializer<'de>,
2608 {
2609 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
2610 let type_ = fields
2611 .remove("type")
2612 .ok_or_else(|| serde::de::Error::missing_field("type"))?;
2613 let serde_json::Value::String(type_) = type_ else {
2614 return Err(serde::de::Error::custom("`type` must be a string"));
2615 };
2616
2617 if is_known_session_config_option_value_type(&type_) {
2618 return Err(serde::de::Error::custom(format!(
2619 "known session configuration option value `{type_}` did not match its schema"
2620 )));
2621 }
2622
2623 let value = fields
2624 .remove("value")
2625 .ok_or_else(|| serde::de::Error::missing_field("value"))?;
2626
2627 Ok(Self {
2628 type_,
2629 value,
2630 fields,
2631 })
2632 }
2633}
2634
2635impl<'de> Deserialize<'de> for SessionConfigOptionValue {
2636 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2637 where
2638 D: serde::Deserializer<'de>,
2639 {
2640 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
2641 let type_ = fields.remove("type");
2642 let value = fields
2643 .remove("value")
2644 .ok_or_else(|| serde::de::Error::missing_field("value"))?;
2645
2646 let type_ = type_.ok_or_else(|| serde::de::Error::missing_field("type"))?;
2647
2648 let serde_json::Value::String(type_) = type_ else {
2649 return Err(serde::de::Error::custom("`type` must be a string"));
2650 };
2651
2652 match type_.as_str() {
2653 "id" => {
2654 let value = serde_json::from_value(value).map_err(|error| {
2655 serde::de::Error::custom(format!(
2656 "`value` must be a string for `type: id`: {error}"
2657 ))
2658 })?;
2659 Ok(Self::Id { value })
2660 }
2661 "boolean" => {
2662 let value = serde_json::from_value(value).map_err(|error| {
2663 serde::de::Error::custom(format!(
2664 "`value` must be a boolean for `type: boolean`: {error}"
2665 ))
2666 })?;
2667 Ok(Self::Boolean { value })
2668 }
2669 _ => Ok(Self::Other(OtherSessionConfigOptionValue {
2670 type_,
2671 value,
2672 fields,
2673 })),
2674 }
2675 }
2676}
2677
2678fn is_known_session_config_option_value_type(type_: &str) -> bool {
2679 matches!(type_, "id" | "boolean")
2680}
2681
2682fn other_session_config_option_value_schema(schema: &mut Schema) {
2683 super::schema_util::reject_known_string_discriminators(schema, "type", &["id", "boolean"]);
2684}
2685
2686impl SessionConfigOptionValue {
2687 #[must_use]
2689 pub fn id(id: impl Into<SessionConfigValueId>) -> Self {
2690 Self::Id { value: id.into() }
2691 }
2692
2693 #[must_use]
2695 pub fn boolean(val: bool) -> Self {
2696 Self::Boolean { value: val }
2697 }
2698
2699 #[must_use]
2702 pub fn as_id(&self) -> Option<&SessionConfigValueId> {
2703 match self {
2704 Self::Id { value } => Some(value),
2705 _ => None,
2706 }
2707 }
2708
2709 #[must_use]
2711 pub fn as_bool(&self) -> Option<bool> {
2712 match self {
2713 Self::Boolean { value } => Some(*value),
2714 _ => None,
2715 }
2716 }
2717}
2718
2719impl From<SessionConfigValueId> for SessionConfigOptionValue {
2720 fn from(value: SessionConfigValueId) -> Self {
2721 Self::Id { value }
2722 }
2723}
2724
2725impl From<bool> for SessionConfigOptionValue {
2726 fn from(value: bool) -> Self {
2727 Self::Boolean { value }
2728 }
2729}
2730
2731impl From<&str> for SessionConfigOptionValue {
2732 fn from(value: &str) -> Self {
2733 Self::Id {
2734 value: SessionConfigValueId::new(value),
2735 }
2736 }
2737}
2738
2739#[serde_as]
2741#[skip_serializing_none]
2742#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2743#[schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_CONFIG_OPTION_METHOD_NAME))]
2744#[serde(rename_all = "camelCase")]
2745#[non_exhaustive]
2746pub struct SetSessionConfigOptionRequest {
2747 pub session_id: SessionId,
2749 pub config_id: SessionConfigId,
2751 #[serde(flatten)]
2755 pub value: SessionConfigOptionValue,
2756 #[serde_as(deserialize_as = "DefaultOnError")]
2762 #[schemars(extend("x-deserialize-default-on-error" = true))]
2763 #[serde(default)]
2764 #[serde(rename = "_meta")]
2765 pub meta: Option<Meta>,
2766}
2767
2768impl SetSessionConfigOptionRequest {
2769 #[must_use]
2771 pub fn new(
2772 session_id: impl Into<SessionId>,
2773 config_id: impl Into<SessionConfigId>,
2774 value: impl Into<SessionConfigOptionValue>,
2775 ) -> Self {
2776 Self {
2777 session_id: session_id.into(),
2778 config_id: config_id.into(),
2779 value: value.into(),
2780 meta: None,
2781 }
2782 }
2783
2784 #[must_use]
2790 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2791 self.meta = meta.into_option();
2792 self
2793 }
2794}
2795
2796#[serde_as]
2798#[skip_serializing_none]
2799#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2800#[schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_CONFIG_OPTION_METHOD_NAME))]
2801#[serde(rename_all = "camelCase")]
2802#[non_exhaustive]
2803pub struct SetSessionConfigOptionResponse {
2804 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
2806 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
2807 pub config_options: Vec<SessionConfigOption>,
2808 #[serde_as(deserialize_as = "DefaultOnError")]
2814 #[schemars(extend("x-deserialize-default-on-error" = true))]
2815 #[serde(default)]
2816 #[serde(rename = "_meta")]
2817 pub meta: Option<Meta>,
2818}
2819
2820impl SetSessionConfigOptionResponse {
2821 #[must_use]
2823 pub fn new(config_options: Vec<SessionConfigOption>) -> Self {
2824 Self {
2825 config_options,
2826 meta: None,
2827 }
2828 }
2829
2830 #[must_use]
2836 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2837 self.meta = meta.into_option();
2838 self
2839 }
2840}
2841
2842#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2851#[serde(tag = "type", rename_all = "snake_case")]
2852#[schemars(extend("discriminator" = {"propertyName": "type"}))]
2853#[non_exhaustive]
2854pub enum McpServer {
2855 Http(McpServerHttp),
2859 #[cfg(feature = "unstable_mcp_over_acp")]
2868 Acp(McpServerAcp),
2869 Stdio(McpServerStdio),
2873 #[serde(untagged)]
2883 Other(OtherMcpServer),
2884}
2885
2886#[derive(Debug, Clone, Serialize, JsonSchema, PartialEq, Eq)]
2888#[schemars(inline)]
2889#[schemars(transform = other_mcp_server_schema)]
2890#[serde(rename_all = "camelCase")]
2891#[non_exhaustive]
2892pub struct OtherMcpServer {
2893 #[serde(rename = "type")]
2899 pub type_: String,
2900 #[serde(flatten)]
2902 pub fields: BTreeMap<String, serde_json::Value>,
2903}
2904
2905impl OtherMcpServer {
2906 #[must_use]
2908 pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
2909 fields.remove("type");
2910 Self {
2911 type_: type_.into(),
2912 fields,
2913 }
2914 }
2915}
2916
2917impl<'de> Deserialize<'de> for OtherMcpServer {
2918 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2919 where
2920 D: serde::Deserializer<'de>,
2921 {
2922 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
2923 let type_ = fields
2924 .remove("type")
2925 .ok_or_else(|| serde::de::Error::missing_field("type"))?;
2926 let serde_json::Value::String(type_) = type_ else {
2927 return Err(serde::de::Error::custom("`type` must be a string"));
2928 };
2929
2930 if is_known_mcp_server_type(&type_) {
2931 return Err(serde::de::Error::custom(format!(
2932 "known MCP server transport `{type_}` did not match its schema"
2933 )));
2934 }
2935
2936 Ok(Self { type_, fields })
2937 }
2938}
2939
2940fn is_known_mcp_server_type(type_: &str) -> bool {
2941 match type_ {
2942 "http" | "stdio" => true,
2943 #[cfg(feature = "unstable_mcp_over_acp")]
2944 "acp" => true,
2945 _ => false,
2946 }
2947}
2948
2949fn other_mcp_server_schema(schema: &mut Schema) {
2950 super::schema_util::reject_known_string_discriminators(
2951 schema,
2952 "type",
2953 &[
2954 "http",
2955 "stdio",
2956 #[cfg(feature = "unstable_mcp_over_acp")]
2957 "acp",
2958 ],
2959 );
2960}
2961
2962#[serde_as]
2964#[skip_serializing_none]
2965#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2966#[serde(rename_all = "camelCase")]
2967#[non_exhaustive]
2968pub struct McpServerHttp {
2969 pub name: String,
2971 #[schemars(url)]
2973 pub url: String,
2974 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2976 pub headers: Vec<HttpHeader>,
2977 #[serde_as(deserialize_as = "DefaultOnError")]
2983 #[schemars(extend("x-deserialize-default-on-error" = true))]
2984 #[serde(default)]
2985 #[serde(rename = "_meta")]
2986 pub meta: Option<Meta>,
2987}
2988
2989impl McpServerHttp {
2990 #[must_use]
2992 pub fn new(name: impl Into<String>, url: impl Into<String>) -> Self {
2993 Self {
2994 name: name.into(),
2995 url: url.into(),
2996 headers: Vec::new(),
2997 meta: None,
2998 }
2999 }
3000
3001 #[must_use]
3003 pub fn headers(mut self, headers: Vec<HttpHeader>) -> Self {
3004 self.headers = headers;
3005 self
3006 }
3007
3008 #[must_use]
3014 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3015 self.meta = meta.into_option();
3016 self
3017 }
3018}
3019
3020#[cfg(feature = "unstable_mcp_over_acp")]
3030#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, Display, From)]
3031#[serde(transparent)]
3032#[from(Arc<str>, String, &'static str)]
3033#[non_exhaustive]
3034pub struct McpServerAcpId(pub Arc<str>);
3035
3036#[cfg(feature = "unstable_mcp_over_acp")]
3037impl McpServerAcpId {
3038 #[must_use]
3040 pub fn new(id: impl Into<Arc<str>>) -> Self {
3041 Self(id.into())
3042 }
3043}
3044
3045#[serde_as]
3054#[skip_serializing_none]
3055#[cfg(feature = "unstable_mcp_over_acp")]
3056#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3057#[serde(rename_all = "camelCase")]
3058#[non_exhaustive]
3059pub struct McpServerAcp {
3060 pub name: String,
3062 pub server_id: McpServerAcpId,
3067 #[serde_as(deserialize_as = "DefaultOnError")]
3073 #[schemars(extend("x-deserialize-default-on-error" = true))]
3074 #[serde(default)]
3075 #[serde(rename = "_meta")]
3076 pub meta: Option<Meta>,
3077}
3078
3079#[cfg(feature = "unstable_mcp_over_acp")]
3080impl McpServerAcp {
3081 #[must_use]
3083 pub fn new(name: impl Into<String>, server_id: impl Into<McpServerAcpId>) -> Self {
3084 Self {
3085 name: name.into(),
3086 server_id: server_id.into(),
3087 meta: None,
3088 }
3089 }
3090
3091 #[must_use]
3097 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3098 self.meta = meta.into_option();
3099 self
3100 }
3101}
3102
3103#[serde_as]
3105#[skip_serializing_none]
3106#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3107#[serde(rename_all = "camelCase")]
3108#[non_exhaustive]
3109pub struct McpServerStdio {
3110 pub name: String,
3112 pub command: PathBuf,
3114 #[serde(default, skip_serializing_if = "Vec::is_empty")]
3116 pub args: Vec<String>,
3117 #[serde(default, skip_serializing_if = "Vec::is_empty")]
3119 pub env: Vec<EnvVariable>,
3120 #[serde_as(deserialize_as = "DefaultOnError")]
3126 #[schemars(extend("x-deserialize-default-on-error" = true))]
3127 #[serde(default)]
3128 #[serde(rename = "_meta")]
3129 pub meta: Option<Meta>,
3130}
3131
3132impl McpServerStdio {
3133 #[must_use]
3135 pub fn new(name: impl Into<String>, command: impl Into<PathBuf>) -> Self {
3136 Self {
3137 name: name.into(),
3138 command: command.into(),
3139 args: Vec::new(),
3140 env: Vec::new(),
3141 meta: None,
3142 }
3143 }
3144
3145 #[must_use]
3147 pub fn args(mut self, args: Vec<String>) -> Self {
3148 self.args = args;
3149 self
3150 }
3151
3152 #[must_use]
3154 pub fn env(mut self, env: Vec<EnvVariable>) -> Self {
3155 self.env = env;
3156 self
3157 }
3158
3159 #[must_use]
3165 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3166 self.meta = meta.into_option();
3167 self
3168 }
3169}
3170
3171#[serde_as]
3173#[skip_serializing_none]
3174#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3175#[serde(rename_all = "camelCase")]
3176#[non_exhaustive]
3177pub struct EnvVariable {
3178 pub name: String,
3180 pub value: String,
3182 #[serde_as(deserialize_as = "DefaultOnError")]
3188 #[schemars(extend("x-deserialize-default-on-error" = true))]
3189 #[serde(default)]
3190 #[serde(rename = "_meta")]
3191 pub meta: Option<Meta>,
3192}
3193
3194impl EnvVariable {
3195 #[must_use]
3197 pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
3198 Self {
3199 name: name.into(),
3200 value: value.into(),
3201 meta: None,
3202 }
3203 }
3204
3205 #[must_use]
3211 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3212 self.meta = meta.into_option();
3213 self
3214 }
3215}
3216
3217#[serde_as]
3219#[skip_serializing_none]
3220#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3221#[serde(rename_all = "camelCase")]
3222#[non_exhaustive]
3223pub struct HttpHeader {
3224 pub name: String,
3226 pub value: String,
3228 #[serde_as(deserialize_as = "DefaultOnError")]
3234 #[schemars(extend("x-deserialize-default-on-error" = true))]
3235 #[serde(default)]
3236 #[serde(rename = "_meta")]
3237 pub meta: Option<Meta>,
3238}
3239
3240impl HttpHeader {
3241 #[must_use]
3243 pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
3244 Self {
3245 name: name.into(),
3246 value: value.into(),
3247 meta: None,
3248 }
3249 }
3250
3251 #[must_use]
3257 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3258 self.meta = meta.into_option();
3259 self
3260 }
3261}
3262
3263#[serde_as]
3271#[skip_serializing_none]
3272#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
3273#[schemars(extend("x-side" = "agent", "x-method" = SESSION_PROMPT_METHOD_NAME))]
3274#[serde(rename_all = "camelCase")]
3275#[non_exhaustive]
3276pub struct PromptRequest {
3277 pub session_id: SessionId,
3279 pub prompt: Vec<ContentBlock>,
3293 #[serde_as(deserialize_as = "DefaultOnError")]
3299 #[schemars(extend("x-deserialize-default-on-error" = true))]
3300 #[serde(default)]
3301 #[serde(rename = "_meta")]
3302 pub meta: Option<Meta>,
3303}
3304
3305impl PromptRequest {
3306 #[must_use]
3308 pub fn new(session_id: impl Into<SessionId>, prompt: Vec<ContentBlock>) -> Self {
3309 Self {
3310 session_id: session_id.into(),
3311 prompt,
3312 meta: None,
3313 }
3314 }
3315
3316 #[must_use]
3322 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3323 self.meta = meta.into_option();
3324 self
3325 }
3326}
3327
3328#[serde_as]
3335#[skip_serializing_none]
3336#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3337#[schemars(extend("x-side" = "agent", "x-method" = SESSION_PROMPT_METHOD_NAME))]
3338#[serde(rename_all = "camelCase")]
3339#[non_exhaustive]
3340pub struct PromptResponse {
3341 #[serde_as(deserialize_as = "DefaultOnError")]
3347 #[schemars(extend("x-deserialize-default-on-error" = true))]
3348 #[serde(default)]
3349 #[serde(rename = "_meta")]
3350 pub meta: Option<Meta>,
3351}
3352
3353impl PromptResponse {
3354 #[must_use]
3356 pub fn new() -> Self {
3357 Self::default()
3358 }
3359
3360 #[must_use]
3366 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3367 self.meta = meta.into_option();
3368 self
3369 }
3370}
3371
3372#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
3376#[serde(rename_all = "snake_case")]
3377#[non_exhaustive]
3378pub enum StopReason {
3379 EndTurn,
3381 MaxTokens,
3383 MaxTurnRequests,
3386 Refusal,
3390 Cancelled,
3396 #[serde(untagged)]
3402 Other(String),
3403}
3404
3405#[cfg(feature = "unstable_end_turn_token_usage")]
3411#[serde_as]
3412#[skip_serializing_none]
3413#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3414#[serde(rename_all = "camelCase")]
3415#[non_exhaustive]
3416pub struct Usage {
3417 pub total_tokens: u64,
3419 pub input_tokens: u64,
3421 pub output_tokens: u64,
3423 #[serde_as(deserialize_as = "DefaultOnError")]
3425 #[schemars(extend("x-deserialize-default-on-error" = true))]
3426 #[serde(default)]
3427 pub thought_tokens: Option<u64>,
3428 #[serde_as(deserialize_as = "DefaultOnError")]
3430 #[schemars(extend("x-deserialize-default-on-error" = true))]
3431 #[serde(default)]
3432 pub cached_read_tokens: Option<u64>,
3433 #[serde_as(deserialize_as = "DefaultOnError")]
3435 #[schemars(extend("x-deserialize-default-on-error" = true))]
3436 #[serde(default)]
3437 pub cached_write_tokens: Option<u64>,
3438 #[serde_as(deserialize_as = "DefaultOnError")]
3444 #[schemars(extend("x-deserialize-default-on-error" = true))]
3445 #[serde(default)]
3446 #[serde(rename = "_meta")]
3447 pub meta: Option<Meta>,
3448}
3449
3450#[cfg(feature = "unstable_end_turn_token_usage")]
3451impl Usage {
3452 #[must_use]
3454 pub fn new(total_tokens: u64, input_tokens: u64, output_tokens: u64) -> Self {
3455 Self {
3456 total_tokens,
3457 input_tokens,
3458 output_tokens,
3459 thought_tokens: None,
3460 cached_read_tokens: None,
3461 cached_write_tokens: None,
3462 meta: None,
3463 }
3464 }
3465
3466 #[must_use]
3468 pub fn thought_tokens(mut self, thought_tokens: impl IntoOption<u64>) -> Self {
3469 self.thought_tokens = thought_tokens.into_option();
3470 self
3471 }
3472
3473 #[must_use]
3475 pub fn cached_read_tokens(mut self, cached_read_tokens: impl IntoOption<u64>) -> Self {
3476 self.cached_read_tokens = cached_read_tokens.into_option();
3477 self
3478 }
3479
3480 #[must_use]
3482 pub fn cached_write_tokens(mut self, cached_write_tokens: impl IntoOption<u64>) -> Self {
3483 self.cached_write_tokens = cached_write_tokens.into_option();
3484 self
3485 }
3486
3487 #[must_use]
3493 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3494 self.meta = meta.into_option();
3495 self
3496 }
3497}
3498
3499#[cfg(feature = "unstable_llm_providers")]
3512#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3513#[serde(rename_all = "snake_case")]
3514#[non_exhaustive]
3515#[expect(clippy::doc_markdown)]
3516pub enum LlmProtocol {
3517 Anthropic,
3519 #[serde(rename = "openai")]
3521 OpenAi,
3522 Azure,
3524 Vertex,
3526 Bedrock,
3528 #[serde(untagged)]
3534 Other(String),
3535}
3536
3537#[cfg(feature = "unstable_llm_providers")]
3543#[serde_as]
3544#[skip_serializing_none]
3545#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3546#[serde(rename_all = "camelCase")]
3547#[non_exhaustive]
3548pub struct ProviderCurrentConfig {
3549 pub api_type: LlmProtocol,
3551 #[schemars(url)]
3553 pub base_url: String,
3554 #[serde_as(deserialize_as = "DefaultOnError")]
3560 #[schemars(extend("x-deserialize-default-on-error" = true))]
3561 #[serde(default)]
3562 #[serde(rename = "_meta")]
3563 pub meta: Option<Meta>,
3564}
3565
3566#[cfg(feature = "unstable_llm_providers")]
3567impl ProviderCurrentConfig {
3568 #[must_use]
3570 pub fn new(api_type: LlmProtocol, base_url: impl Into<String>) -> Self {
3571 Self {
3572 api_type,
3573 base_url: base_url.into(),
3574 meta: None,
3575 }
3576 }
3577
3578 #[must_use]
3584 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3585 self.meta = meta.into_option();
3586 self
3587 }
3588}
3589
3590#[cfg(feature = "unstable_llm_providers")]
3596#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, Display, From)]
3597#[serde(transparent)]
3598#[from(Arc<str>, String, &'static str)]
3599#[non_exhaustive]
3600pub struct ProviderId(pub Arc<str>);
3601
3602#[cfg(feature = "unstable_llm_providers")]
3603impl ProviderId {
3604 #[must_use]
3606 pub fn new(id: impl Into<Arc<str>>) -> Self {
3607 Self(id.into())
3608 }
3609}
3610
3611#[cfg(feature = "unstable_llm_providers")]
3617#[serde_as]
3618#[skip_serializing_none]
3619#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3620#[serde(rename_all = "camelCase")]
3621#[non_exhaustive]
3622pub struct ProviderInfo {
3623 pub provider_id: ProviderId,
3625 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
3627 #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
3628 pub supported: Vec<LlmProtocol>,
3629 pub required: bool,
3632 #[serde(default)]
3635 pub current: Option<ProviderCurrentConfig>,
3636 #[serde_as(deserialize_as = "DefaultOnError")]
3642 #[schemars(extend("x-deserialize-default-on-error" = true))]
3643 #[serde(default)]
3644 #[serde(rename = "_meta")]
3645 pub meta: Option<Meta>,
3646}
3647
3648#[cfg(feature = "unstable_llm_providers")]
3649impl ProviderInfo {
3650 #[must_use]
3652 pub fn new(
3653 provider_id: impl Into<ProviderId>,
3654 supported: Vec<LlmProtocol>,
3655 required: bool,
3656 current: impl IntoOption<ProviderCurrentConfig>,
3657 ) -> Self {
3658 Self {
3659 provider_id: provider_id.into(),
3660 supported,
3661 required,
3662 current: current.into_option(),
3663 meta: None,
3664 }
3665 }
3666
3667 #[must_use]
3673 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3674 self.meta = meta.into_option();
3675 self
3676 }
3677}
3678
3679#[cfg(feature = "unstable_llm_providers")]
3685#[serde_as]
3686#[skip_serializing_none]
3687#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3688#[schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_LIST_METHOD_NAME))]
3689#[serde(rename_all = "camelCase")]
3690#[non_exhaustive]
3691pub struct ListProvidersRequest {
3692 #[serde_as(deserialize_as = "DefaultOnError")]
3698 #[schemars(extend("x-deserialize-default-on-error" = true))]
3699 #[serde(default)]
3700 #[serde(rename = "_meta")]
3701 pub meta: Option<Meta>,
3702}
3703
3704#[cfg(feature = "unstable_llm_providers")]
3705impl ListProvidersRequest {
3706 #[must_use]
3708 pub fn new() -> Self {
3709 Self::default()
3710 }
3711
3712 #[must_use]
3718 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3719 self.meta = meta.into_option();
3720 self
3721 }
3722}
3723
3724#[cfg(feature = "unstable_llm_providers")]
3730#[serde_as]
3731#[skip_serializing_none]
3732#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3733#[schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_LIST_METHOD_NAME))]
3734#[serde(rename_all = "camelCase")]
3735#[non_exhaustive]
3736pub struct ListProvidersResponse {
3737 pub providers: Vec<ProviderInfo>,
3739 #[serde_as(deserialize_as = "DefaultOnError")]
3745 #[schemars(extend("x-deserialize-default-on-error" = true))]
3746 #[serde(default)]
3747 #[serde(rename = "_meta")]
3748 pub meta: Option<Meta>,
3749}
3750
3751#[cfg(feature = "unstable_llm_providers")]
3752impl ListProvidersResponse {
3753 #[must_use]
3755 pub fn new(providers: Vec<ProviderInfo>) -> Self {
3756 Self {
3757 providers,
3758 meta: None,
3759 }
3760 }
3761
3762 #[must_use]
3768 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3769 self.meta = meta.into_option();
3770 self
3771 }
3772}
3773
3774#[cfg(feature = "unstable_llm_providers")]
3782#[serde_as]
3783#[skip_serializing_none]
3784#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3785#[schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_SET_METHOD_NAME))]
3786#[serde(rename_all = "camelCase")]
3787#[non_exhaustive]
3788pub struct SetProviderRequest {
3789 pub provider_id: ProviderId,
3791 pub api_type: LlmProtocol,
3793 #[schemars(url)]
3795 pub base_url: String,
3796 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
3799 pub headers: HashMap<String, String>,
3800 #[serde_as(deserialize_as = "DefaultOnError")]
3806 #[schemars(extend("x-deserialize-default-on-error" = true))]
3807 #[serde(default)]
3808 #[serde(rename = "_meta")]
3809 pub meta: Option<Meta>,
3810}
3811
3812#[cfg(feature = "unstable_llm_providers")]
3813impl SetProviderRequest {
3814 #[must_use]
3816 pub fn new(
3817 provider_id: impl Into<ProviderId>,
3818 api_type: LlmProtocol,
3819 base_url: impl Into<String>,
3820 ) -> Self {
3821 Self {
3822 provider_id: provider_id.into(),
3823 api_type,
3824 base_url: base_url.into(),
3825 headers: HashMap::new(),
3826 meta: None,
3827 }
3828 }
3829
3830 #[must_use]
3833 pub fn headers(mut self, headers: HashMap<String, String>) -> Self {
3834 self.headers = headers;
3835 self
3836 }
3837
3838 #[must_use]
3844 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3845 self.meta = meta.into_option();
3846 self
3847 }
3848}
3849
3850#[cfg(feature = "unstable_llm_providers")]
3856#[serde_as]
3857#[skip_serializing_none]
3858#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3859#[schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_SET_METHOD_NAME))]
3860#[serde(rename_all = "camelCase")]
3861#[non_exhaustive]
3862pub struct SetProviderResponse {
3863 #[serde_as(deserialize_as = "DefaultOnError")]
3869 #[schemars(extend("x-deserialize-default-on-error" = true))]
3870 #[serde(default)]
3871 #[serde(rename = "_meta")]
3872 pub meta: Option<Meta>,
3873}
3874
3875#[cfg(feature = "unstable_llm_providers")]
3876impl SetProviderResponse {
3877 #[must_use]
3879 pub fn new() -> Self {
3880 Self::default()
3881 }
3882
3883 #[must_use]
3889 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3890 self.meta = meta.into_option();
3891 self
3892 }
3893}
3894
3895#[cfg(feature = "unstable_llm_providers")]
3901#[serde_as]
3902#[skip_serializing_none]
3903#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3904#[schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_DISABLE_METHOD_NAME))]
3905#[serde(rename_all = "camelCase")]
3906#[non_exhaustive]
3907pub struct DisableProviderRequest {
3908 pub provider_id: ProviderId,
3910 #[serde_as(deserialize_as = "DefaultOnError")]
3916 #[schemars(extend("x-deserialize-default-on-error" = true))]
3917 #[serde(default)]
3918 #[serde(rename = "_meta")]
3919 pub meta: Option<Meta>,
3920}
3921
3922#[cfg(feature = "unstable_llm_providers")]
3923impl DisableProviderRequest {
3924 #[must_use]
3926 pub fn new(provider_id: impl Into<ProviderId>) -> Self {
3927 Self {
3928 provider_id: provider_id.into(),
3929 meta: None,
3930 }
3931 }
3932
3933 #[must_use]
3939 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3940 self.meta = meta.into_option();
3941 self
3942 }
3943}
3944
3945#[cfg(feature = "unstable_llm_providers")]
3951#[serde_as]
3952#[skip_serializing_none]
3953#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3954#[schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_DISABLE_METHOD_NAME))]
3955#[serde(rename_all = "camelCase")]
3956#[non_exhaustive]
3957pub struct DisableProviderResponse {
3958 #[serde_as(deserialize_as = "DefaultOnError")]
3964 #[schemars(extend("x-deserialize-default-on-error" = true))]
3965 #[serde(default)]
3966 #[serde(rename = "_meta")]
3967 pub meta: Option<Meta>,
3968}
3969
3970#[cfg(feature = "unstable_llm_providers")]
3971impl DisableProviderResponse {
3972 #[must_use]
3974 pub fn new() -> Self {
3975 Self::default()
3976 }
3977
3978 #[must_use]
3984 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3985 self.meta = meta.into_option();
3986 self
3987 }
3988}
3989
3990#[serde_as]
3999#[skip_serializing_none]
4000#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
4001#[serde(rename_all = "camelCase")]
4002#[non_exhaustive]
4003pub struct AgentCapabilities {
4004 #[serde_as(deserialize_as = "DefaultOnError")]
4011 #[schemars(extend("x-deserialize-default-on-error" = true))]
4012 #[serde(default)]
4013 pub session: Option<SessionCapabilities>,
4014 #[serde_as(deserialize_as = "DefaultOnError")]
4019 #[schemars(extend("x-deserialize-default-on-error" = true))]
4020 #[serde(default)]
4021 pub auth: Option<AgentAuthCapabilities>,
4022 #[cfg(feature = "unstable_llm_providers")]
4031 #[serde_as(deserialize_as = "DefaultOnError")]
4032 #[schemars(extend("x-deserialize-default-on-error" = true))]
4033 #[serde(default)]
4034 pub providers: Option<ProvidersCapabilities>,
4035 #[cfg(feature = "unstable_nes")]
4044 #[serde_as(deserialize_as = "DefaultOnError")]
4045 #[schemars(extend("x-deserialize-default-on-error" = true))]
4046 #[serde(default)]
4047 pub nes: Option<NesCapabilities>,
4048 #[cfg(feature = "unstable_nes")]
4054 #[serde_as(deserialize_as = "DefaultOnError")]
4055 #[schemars(extend("x-deserialize-default-on-error" = true))]
4056 #[serde(default)]
4057 pub position_encoding: Option<PositionEncodingKind>,
4058 #[serde_as(deserialize_as = "DefaultOnError")]
4064 #[schemars(extend("x-deserialize-default-on-error" = true))]
4065 #[serde(default)]
4066 #[serde(rename = "_meta")]
4067 pub meta: Option<Meta>,
4068}
4069
4070impl AgentCapabilities {
4071 #[must_use]
4073 pub fn new() -> Self {
4074 Self::default()
4075 }
4076
4077 #[must_use]
4084 pub fn session(mut self, session: impl IntoOption<SessionCapabilities>) -> Self {
4085 self.session = session.into_option();
4086 self
4087 }
4088
4089 #[must_use]
4091 pub fn auth(mut self, auth: impl IntoOption<AgentAuthCapabilities>) -> Self {
4092 self.auth = auth.into_option();
4093 self
4094 }
4095
4096 #[cfg(feature = "unstable_llm_providers")]
4102 #[must_use]
4103 pub fn providers(mut self, providers: impl IntoOption<ProvidersCapabilities>) -> Self {
4104 self.providers = providers.into_option();
4105 self
4106 }
4107
4108 #[cfg(feature = "unstable_nes")]
4114 #[must_use]
4115 pub fn nes(mut self, nes: impl IntoOption<NesCapabilities>) -> Self {
4116 self.nes = nes.into_option();
4117 self
4118 }
4119
4120 #[cfg(feature = "unstable_nes")]
4124 #[must_use]
4125 pub fn position_encoding(
4126 mut self,
4127 position_encoding: impl IntoOption<PositionEncodingKind>,
4128 ) -> Self {
4129 self.position_encoding = position_encoding.into_option();
4130 self
4131 }
4132
4133 #[must_use]
4139 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4140 self.meta = meta.into_option();
4141 self
4142 }
4143}
4144
4145#[cfg(feature = "unstable_llm_providers")]
4153#[serde_as]
4154#[skip_serializing_none]
4155#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
4156#[non_exhaustive]
4157pub struct ProvidersCapabilities {
4158 #[serde_as(deserialize_as = "DefaultOnError")]
4164 #[schemars(extend("x-deserialize-default-on-error" = true))]
4165 #[serde(default)]
4166 #[serde(rename = "_meta")]
4167 pub meta: Option<Meta>,
4168}
4169
4170#[cfg(feature = "unstable_llm_providers")]
4171impl ProvidersCapabilities {
4172 #[must_use]
4174 pub fn new() -> Self {
4175 Self::default()
4176 }
4177
4178 #[must_use]
4184 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4185 self.meta = meta.into_option();
4186 self
4187 }
4188}
4189
4190#[serde_as]
4202#[skip_serializing_none]
4203#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
4204#[serde(rename_all = "camelCase")]
4205#[non_exhaustive]
4206pub struct SessionCapabilities {
4207 #[serde_as(deserialize_as = "DefaultOnError")]
4213 #[schemars(extend("x-deserialize-default-on-error" = true))]
4214 #[serde(default)]
4215 pub prompt: Option<PromptCapabilities>,
4216 #[serde_as(deserialize_as = "DefaultOnError")]
4221 #[schemars(extend("x-deserialize-default-on-error" = true))]
4222 #[serde(default)]
4223 pub mcp: Option<McpCapabilities>,
4224 #[serde_as(deserialize_as = "DefaultOnError")]
4229 #[schemars(extend("x-deserialize-default-on-error" = true))]
4230 #[serde(default)]
4231 pub delete: Option<SessionDeleteCapabilities>,
4232 #[serde_as(deserialize_as = "DefaultOnError")]
4241 #[schemars(extend("x-deserialize-default-on-error" = true))]
4242 #[serde(default)]
4243 pub additional_directories: Option<SessionAdditionalDirectoriesCapabilities>,
4244 #[cfg(feature = "unstable_session_fork")]
4253 #[serde_as(deserialize_as = "DefaultOnError")]
4254 #[schemars(extend("x-deserialize-default-on-error" = true))]
4255 #[serde(default)]
4256 pub fork: Option<SessionForkCapabilities>,
4257 #[serde_as(deserialize_as = "DefaultOnError")]
4263 #[schemars(extend("x-deserialize-default-on-error" = true))]
4264 #[serde(default)]
4265 #[serde(rename = "_meta")]
4266 pub meta: Option<Meta>,
4267}
4268
4269impl SessionCapabilities {
4270 #[must_use]
4272 pub fn new() -> Self {
4273 Self::default()
4274 }
4275
4276 #[must_use]
4282 pub fn prompt(mut self, prompt: impl IntoOption<PromptCapabilities>) -> Self {
4283 self.prompt = prompt.into_option();
4284 self
4285 }
4286
4287 #[must_use]
4292 pub fn mcp(mut self, mcp: impl IntoOption<McpCapabilities>) -> Self {
4293 self.mcp = mcp.into_option();
4294 self
4295 }
4296
4297 #[must_use]
4302 pub fn delete(mut self, delete: impl IntoOption<SessionDeleteCapabilities>) -> Self {
4303 self.delete = delete.into_option();
4304 self
4305 }
4306
4307 #[must_use]
4316 pub fn additional_directories(
4317 mut self,
4318 additional_directories: impl IntoOption<SessionAdditionalDirectoriesCapabilities>,
4319 ) -> Self {
4320 self.additional_directories = additional_directories.into_option();
4321 self
4322 }
4323
4324 #[cfg(feature = "unstable_session_fork")]
4325 #[must_use]
4330 pub fn fork(mut self, fork: impl IntoOption<SessionForkCapabilities>) -> Self {
4331 self.fork = fork.into_option();
4332 self
4333 }
4334
4335 #[must_use]
4341 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4342 self.meta = meta.into_option();
4343 self
4344 }
4345}
4346
4347#[serde_as]
4351#[skip_serializing_none]
4352#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
4353#[non_exhaustive]
4354pub struct SessionDeleteCapabilities {
4355 #[serde_as(deserialize_as = "DefaultOnError")]
4361 #[schemars(extend("x-deserialize-default-on-error" = true))]
4362 #[serde(default)]
4363 #[serde(rename = "_meta")]
4364 pub meta: Option<Meta>,
4365}
4366
4367impl SessionDeleteCapabilities {
4368 #[must_use]
4370 pub fn new() -> Self {
4371 Self::default()
4372 }
4373
4374 #[must_use]
4380 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4381 self.meta = meta.into_option();
4382 self
4383 }
4384}
4385
4386#[serde_as]
4393#[skip_serializing_none]
4394#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
4395#[non_exhaustive]
4396pub struct SessionAdditionalDirectoriesCapabilities {
4397 #[serde_as(deserialize_as = "DefaultOnError")]
4403 #[schemars(extend("x-deserialize-default-on-error" = true))]
4404 #[serde(default)]
4405 #[serde(rename = "_meta")]
4406 pub meta: Option<Meta>,
4407}
4408
4409impl SessionAdditionalDirectoriesCapabilities {
4410 #[must_use]
4412 pub fn new() -> Self {
4413 Self::default()
4414 }
4415
4416 #[must_use]
4422 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4423 self.meta = meta.into_option();
4424 self
4425 }
4426}
4427
4428#[cfg(feature = "unstable_session_fork")]
4436#[serde_as]
4437#[skip_serializing_none]
4438#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
4439#[non_exhaustive]
4440pub struct SessionForkCapabilities {
4441 #[serde_as(deserialize_as = "DefaultOnError")]
4447 #[schemars(extend("x-deserialize-default-on-error" = true))]
4448 #[serde(default)]
4449 #[serde(rename = "_meta")]
4450 pub meta: Option<Meta>,
4451}
4452
4453#[cfg(feature = "unstable_session_fork")]
4454impl SessionForkCapabilities {
4455 #[must_use]
4457 pub fn new() -> Self {
4458 Self::default()
4459 }
4460
4461 #[must_use]
4467 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4468 self.meta = meta.into_option();
4469 self
4470 }
4471}
4472
4473#[serde_as]
4486#[skip_serializing_none]
4487#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
4488#[serde(rename_all = "camelCase")]
4489#[non_exhaustive]
4490pub struct PromptCapabilities {
4491 #[serde_as(deserialize_as = "DefaultOnError")]
4496 #[schemars(extend("x-deserialize-default-on-error" = true))]
4497 #[serde(default)]
4498 pub image: Option<PromptImageCapabilities>,
4499 #[serde_as(deserialize_as = "DefaultOnError")]
4504 #[schemars(extend("x-deserialize-default-on-error" = true))]
4505 #[serde(default)]
4506 pub audio: Option<PromptAudioCapabilities>,
4507 #[serde_as(deserialize_as = "DefaultOnError")]
4515 #[schemars(extend("x-deserialize-default-on-error" = true))]
4516 #[serde(default)]
4517 pub embedded_context: Option<PromptEmbeddedContextCapabilities>,
4518 #[serde_as(deserialize_as = "DefaultOnError")]
4524 #[schemars(extend("x-deserialize-default-on-error" = true))]
4525 #[serde(default)]
4526 #[serde(rename = "_meta")]
4527 pub meta: Option<Meta>,
4528}
4529
4530impl PromptCapabilities {
4531 #[must_use]
4533 pub fn new() -> Self {
4534 Self::default()
4535 }
4536
4537 #[must_use]
4542 pub fn image(mut self, image: impl IntoOption<PromptImageCapabilities>) -> Self {
4543 self.image = image.into_option();
4544 self
4545 }
4546
4547 #[must_use]
4552 pub fn audio(mut self, audio: impl IntoOption<PromptAudioCapabilities>) -> Self {
4553 self.audio = audio.into_option();
4554 self
4555 }
4556
4557 #[must_use]
4565 pub fn embedded_context(
4566 mut self,
4567 embedded_context: impl IntoOption<PromptEmbeddedContextCapabilities>,
4568 ) -> Self {
4569 self.embedded_context = embedded_context.into_option();
4570 self
4571 }
4572
4573 #[must_use]
4579 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4580 self.meta = meta.into_option();
4581 self
4582 }
4583}
4584
4585#[serde_as]
4589#[skip_serializing_none]
4590#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
4591#[non_exhaustive]
4592pub struct PromptImageCapabilities {
4593 #[serde_as(deserialize_as = "DefaultOnError")]
4599 #[schemars(extend("x-deserialize-default-on-error" = true))]
4600 #[serde(default)]
4601 #[serde(rename = "_meta")]
4602 pub meta: Option<Meta>,
4603}
4604
4605impl PromptImageCapabilities {
4606 #[must_use]
4608 pub fn new() -> Self {
4609 Self::default()
4610 }
4611
4612 #[must_use]
4618 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4619 self.meta = meta.into_option();
4620 self
4621 }
4622}
4623
4624#[serde_as]
4628#[skip_serializing_none]
4629#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
4630#[non_exhaustive]
4631pub struct PromptAudioCapabilities {
4632 #[serde_as(deserialize_as = "DefaultOnError")]
4638 #[schemars(extend("x-deserialize-default-on-error" = true))]
4639 #[serde(default)]
4640 #[serde(rename = "_meta")]
4641 pub meta: Option<Meta>,
4642}
4643
4644impl PromptAudioCapabilities {
4645 #[must_use]
4647 pub fn new() -> Self {
4648 Self::default()
4649 }
4650
4651 #[must_use]
4657 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4658 self.meta = meta.into_option();
4659 self
4660 }
4661}
4662
4663#[serde_as]
4667#[skip_serializing_none]
4668#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
4669#[non_exhaustive]
4670pub struct PromptEmbeddedContextCapabilities {
4671 #[serde_as(deserialize_as = "DefaultOnError")]
4677 #[schemars(extend("x-deserialize-default-on-error" = true))]
4678 #[serde(default)]
4679 #[serde(rename = "_meta")]
4680 pub meta: Option<Meta>,
4681}
4682
4683impl PromptEmbeddedContextCapabilities {
4684 #[must_use]
4686 pub fn new() -> Self {
4687 Self::default()
4688 }
4689
4690 #[must_use]
4696 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4697 self.meta = meta.into_option();
4698 self
4699 }
4700}
4701
4702#[serde_as]
4704#[skip_serializing_none]
4705#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
4706#[serde(rename_all = "camelCase")]
4707#[non_exhaustive]
4708pub struct McpCapabilities {
4709 #[serde_as(deserialize_as = "DefaultOnError")]
4714 #[schemars(extend("x-deserialize-default-on-error" = true))]
4715 #[serde(default)]
4716 pub stdio: Option<McpStdioCapabilities>,
4717 #[serde_as(deserialize_as = "DefaultOnError")]
4722 #[schemars(extend("x-deserialize-default-on-error" = true))]
4723 #[serde(default)]
4724 pub http: Option<McpHttpCapabilities>,
4725 #[cfg(feature = "unstable_mcp_over_acp")]
4734 #[serde_as(deserialize_as = "DefaultOnError")]
4735 #[schemars(extend("x-deserialize-default-on-error" = true))]
4736 #[serde(default)]
4737 pub acp: Option<McpAcpCapabilities>,
4738 #[serde_as(deserialize_as = "DefaultOnError")]
4744 #[schemars(extend("x-deserialize-default-on-error" = true))]
4745 #[serde(default)]
4746 #[serde(rename = "_meta")]
4747 pub meta: Option<Meta>,
4748}
4749
4750impl McpCapabilities {
4751 #[must_use]
4753 pub fn new() -> Self {
4754 Self::default()
4755 }
4756
4757 #[must_use]
4762 pub fn stdio(mut self, stdio: impl IntoOption<McpStdioCapabilities>) -> Self {
4763 self.stdio = stdio.into_option();
4764 self
4765 }
4766
4767 #[must_use]
4772 pub fn http(mut self, http: impl IntoOption<McpHttpCapabilities>) -> Self {
4773 self.http = http.into_option();
4774 self
4775 }
4776
4777 #[cfg(feature = "unstable_mcp_over_acp")]
4783 #[must_use]
4787 pub fn acp(mut self, acp: impl IntoOption<McpAcpCapabilities>) -> Self {
4788 self.acp = acp.into_option();
4789 self
4790 }
4791
4792 #[must_use]
4798 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4799 self.meta = meta.into_option();
4800 self
4801 }
4802}
4803
4804#[serde_as]
4808#[skip_serializing_none]
4809#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
4810#[non_exhaustive]
4811pub struct McpStdioCapabilities {
4812 #[serde_as(deserialize_as = "DefaultOnError")]
4818 #[schemars(extend("x-deserialize-default-on-error" = true))]
4819 #[serde(default)]
4820 #[serde(rename = "_meta")]
4821 pub meta: Option<Meta>,
4822}
4823
4824impl McpStdioCapabilities {
4825 #[must_use]
4827 pub fn new() -> Self {
4828 Self::default()
4829 }
4830
4831 #[must_use]
4837 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4838 self.meta = meta.into_option();
4839 self
4840 }
4841}
4842
4843#[serde_as]
4847#[skip_serializing_none]
4848#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
4849#[non_exhaustive]
4850pub struct McpHttpCapabilities {
4851 #[serde_as(deserialize_as = "DefaultOnError")]
4857 #[schemars(extend("x-deserialize-default-on-error" = true))]
4858 #[serde(default)]
4859 #[serde(rename = "_meta")]
4860 pub meta: Option<Meta>,
4861}
4862
4863impl McpHttpCapabilities {
4864 #[must_use]
4866 pub fn new() -> Self {
4867 Self::default()
4868 }
4869
4870 #[must_use]
4876 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4877 self.meta = meta.into_option();
4878 self
4879 }
4880}
4881
4882#[cfg(feature = "unstable_mcp_over_acp")]
4890#[serde_as]
4891#[skip_serializing_none]
4892#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
4893#[non_exhaustive]
4894pub struct McpAcpCapabilities {
4895 #[serde_as(deserialize_as = "DefaultOnError")]
4901 #[schemars(extend("x-deserialize-default-on-error" = true))]
4902 #[serde(default)]
4903 #[serde(rename = "_meta")]
4904 pub meta: Option<Meta>,
4905}
4906
4907#[cfg(feature = "unstable_mcp_over_acp")]
4908impl McpAcpCapabilities {
4909 #[must_use]
4911 pub fn new() -> Self {
4912 Self::default()
4913 }
4914
4915 #[must_use]
4921 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4922 self.meta = meta.into_option();
4923 self
4924 }
4925}
4926
4927#[serde_as]
4931#[skip_serializing_none]
4932#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
4933#[schemars(extend("x-side" = "agent", "x-method" = SESSION_CANCEL_METHOD_NAME))]
4934#[serde(rename_all = "camelCase")]
4935#[non_exhaustive]
4936pub struct CancelSessionNotification {
4937 pub session_id: SessionId,
4939 #[serde_as(deserialize_as = "DefaultOnError")]
4945 #[schemars(extend("x-deserialize-default-on-error" = true))]
4946 #[serde(default)]
4947 #[serde(rename = "_meta")]
4948 pub meta: Option<Meta>,
4949}
4950
4951impl CancelSessionNotification {
4952 #[must_use]
4954 pub fn new(session_id: impl Into<SessionId>) -> Self {
4955 Self {
4956 session_id: session_id.into(),
4957 meta: None,
4958 }
4959 }
4960
4961 #[must_use]
4967 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4968 self.meta = meta.into_option();
4969 self
4970 }
4971}
4972
4973#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4979#[non_exhaustive]
4980pub struct AgentMethodNames {
4981 pub initialize: &'static str,
4983 pub auth_login: &'static str,
4985 #[cfg(feature = "unstable_llm_providers")]
4987 pub providers_list: &'static str,
4988 #[cfg(feature = "unstable_llm_providers")]
4990 pub providers_set: &'static str,
4991 #[cfg(feature = "unstable_llm_providers")]
4993 pub providers_disable: &'static str,
4994 pub session_new: &'static str,
4996 pub session_set_config_option: &'static str,
4998 pub session_prompt: &'static str,
5000 pub session_cancel: &'static str,
5002 #[cfg(feature = "unstable_mcp_over_acp")]
5004 pub mcp_message: &'static str,
5005 pub session_list: &'static str,
5007 pub session_delete: &'static str,
5009 #[cfg(feature = "unstable_session_fork")]
5011 pub session_fork: &'static str,
5012 pub session_resume: &'static str,
5014 pub session_close: &'static str,
5016 pub auth_logout: &'static str,
5018 #[cfg(feature = "unstable_nes")]
5020 pub nes_start: &'static str,
5021 #[cfg(feature = "unstable_nes")]
5023 pub nes_suggest: &'static str,
5024 #[cfg(feature = "unstable_nes")]
5026 pub nes_accept: &'static str,
5027 #[cfg(feature = "unstable_nes")]
5029 pub nes_reject: &'static str,
5030 #[cfg(feature = "unstable_nes")]
5032 pub nes_close: &'static str,
5033 #[cfg(feature = "unstable_nes")]
5035 pub document_did_open: &'static str,
5036 #[cfg(feature = "unstable_nes")]
5038 pub document_did_change: &'static str,
5039 #[cfg(feature = "unstable_nes")]
5041 pub document_did_close: &'static str,
5042 #[cfg(feature = "unstable_nes")]
5044 pub document_did_save: &'static str,
5045 #[cfg(feature = "unstable_nes")]
5047 pub document_did_focus: &'static str,
5048}
5049
5050pub const AGENT_METHOD_NAMES: AgentMethodNames = AgentMethodNames {
5052 initialize: INITIALIZE_METHOD_NAME,
5053 auth_login: AUTH_LOGIN_METHOD_NAME,
5054 #[cfg(feature = "unstable_llm_providers")]
5055 providers_list: PROVIDERS_LIST_METHOD_NAME,
5056 #[cfg(feature = "unstable_llm_providers")]
5057 providers_set: PROVIDERS_SET_METHOD_NAME,
5058 #[cfg(feature = "unstable_llm_providers")]
5059 providers_disable: PROVIDERS_DISABLE_METHOD_NAME,
5060 session_new: SESSION_NEW_METHOD_NAME,
5061 session_set_config_option: SESSION_SET_CONFIG_OPTION_METHOD_NAME,
5062 session_prompt: SESSION_PROMPT_METHOD_NAME,
5063 session_cancel: SESSION_CANCEL_METHOD_NAME,
5064 #[cfg(feature = "unstable_mcp_over_acp")]
5065 mcp_message: MCP_MESSAGE_METHOD_NAME,
5066 session_list: SESSION_LIST_METHOD_NAME,
5067 session_delete: SESSION_DELETE_METHOD_NAME,
5068 #[cfg(feature = "unstable_session_fork")]
5069 session_fork: SESSION_FORK_METHOD_NAME,
5070 session_resume: SESSION_RESUME_METHOD_NAME,
5071 session_close: SESSION_CLOSE_METHOD_NAME,
5072 auth_logout: AUTH_LOGOUT_METHOD_NAME,
5073 #[cfg(feature = "unstable_nes")]
5074 nes_start: NES_START_METHOD_NAME,
5075 #[cfg(feature = "unstable_nes")]
5076 nes_suggest: NES_SUGGEST_METHOD_NAME,
5077 #[cfg(feature = "unstable_nes")]
5078 nes_accept: NES_ACCEPT_METHOD_NAME,
5079 #[cfg(feature = "unstable_nes")]
5080 nes_reject: NES_REJECT_METHOD_NAME,
5081 #[cfg(feature = "unstable_nes")]
5082 nes_close: NES_CLOSE_METHOD_NAME,
5083 #[cfg(feature = "unstable_nes")]
5084 document_did_open: DOCUMENT_DID_OPEN_METHOD_NAME,
5085 #[cfg(feature = "unstable_nes")]
5086 document_did_change: DOCUMENT_DID_CHANGE_METHOD_NAME,
5087 #[cfg(feature = "unstable_nes")]
5088 document_did_close: DOCUMENT_DID_CLOSE_METHOD_NAME,
5089 #[cfg(feature = "unstable_nes")]
5090 document_did_save: DOCUMENT_DID_SAVE_METHOD_NAME,
5091 #[cfg(feature = "unstable_nes")]
5092 document_did_focus: DOCUMENT_DID_FOCUS_METHOD_NAME,
5093};
5094
5095pub(crate) const INITIALIZE_METHOD_NAME: &str = "initialize";
5097pub(crate) const AUTH_LOGIN_METHOD_NAME: &str = "auth/login";
5099#[cfg(feature = "unstable_llm_providers")]
5101pub(crate) const PROVIDERS_LIST_METHOD_NAME: &str = "providers/list";
5102#[cfg(feature = "unstable_llm_providers")]
5104pub(crate) const PROVIDERS_SET_METHOD_NAME: &str = "providers/set";
5105#[cfg(feature = "unstable_llm_providers")]
5107pub(crate) const PROVIDERS_DISABLE_METHOD_NAME: &str = "providers/disable";
5108pub(crate) const SESSION_NEW_METHOD_NAME: &str = "session/new";
5110pub(crate) const SESSION_SET_CONFIG_OPTION_METHOD_NAME: &str = "session/set_config_option";
5112pub(crate) const SESSION_PROMPT_METHOD_NAME: &str = "session/prompt";
5114pub(crate) const SESSION_CANCEL_METHOD_NAME: &str = "session/cancel";
5116pub(crate) const SESSION_LIST_METHOD_NAME: &str = "session/list";
5118pub(crate) const SESSION_DELETE_METHOD_NAME: &str = "session/delete";
5120#[cfg(feature = "unstable_session_fork")]
5122pub(crate) const SESSION_FORK_METHOD_NAME: &str = "session/fork";
5123pub(crate) const SESSION_RESUME_METHOD_NAME: &str = "session/resume";
5125pub(crate) const SESSION_CLOSE_METHOD_NAME: &str = "session/close";
5127pub(crate) const AUTH_LOGOUT_METHOD_NAME: &str = "auth/logout";
5129
5130#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
5137#[serde(untagged)]
5138#[schemars(inline)]
5139#[non_exhaustive]
5140pub enum ClientRequest {
5141 InitializeRequest(Box<InitializeRequest>),
5152 LoginAuthRequest(Box<LoginAuthRequest>),
5162 #[cfg(feature = "unstable_llm_providers")]
5168 ListProvidersRequest(Box<ListProvidersRequest>),
5169 #[cfg(feature = "unstable_llm_providers")]
5175 SetProviderRequest(Box<SetProviderRequest>),
5176 #[cfg(feature = "unstable_llm_providers")]
5182 DisableProviderRequest(Box<DisableProviderRequest>),
5183 LogoutAuthRequest(Box<LogoutAuthRequest>),
5188 NewSessionRequest(Box<NewSessionRequest>),
5201 ListSessionsRequest(Box<ListSessionsRequest>),
5205 DeleteSessionRequest(Box<DeleteSessionRequest>),
5209 #[cfg(feature = "unstable_session_fork")]
5210 ForkSessionRequest(Box<ForkSessionRequest>),
5222 ResumeSessionRequest(Box<ResumeSessionRequest>),
5228 CloseSessionRequest(Box<CloseSessionRequest>),
5233 SetSessionConfigOptionRequest(Box<SetSessionConfigOptionRequest>),
5235 PromptRequest(Box<PromptRequest>),
5247 #[cfg(feature = "unstable_nes")]
5248 StartNesRequest(Box<StartNesRequest>),
5254 #[cfg(feature = "unstable_nes")]
5255 SuggestNesRequest(Box<SuggestNesRequest>),
5261 #[cfg(feature = "unstable_nes")]
5262 CloseNesRequest(Box<CloseNesRequest>),
5271 #[cfg(feature = "unstable_mcp_over_acp")]
5277 MessageMcpRequest(Box<MessageMcpRequest>),
5278 ExtMethodRequest(Box<ExtRequest>),
5285}
5286
5287impl ClientRequest {
5288 #[must_use]
5290 pub fn method(&self) -> &str {
5291 match self {
5292 Self::InitializeRequest(_) => AGENT_METHOD_NAMES.initialize,
5293 Self::LoginAuthRequest(_) => AGENT_METHOD_NAMES.auth_login,
5294 #[cfg(feature = "unstable_llm_providers")]
5295 Self::ListProvidersRequest(_) => AGENT_METHOD_NAMES.providers_list,
5296 #[cfg(feature = "unstable_llm_providers")]
5297 Self::SetProviderRequest(_) => AGENT_METHOD_NAMES.providers_set,
5298 #[cfg(feature = "unstable_llm_providers")]
5299 Self::DisableProviderRequest(_) => AGENT_METHOD_NAMES.providers_disable,
5300 Self::LogoutAuthRequest(_) => AGENT_METHOD_NAMES.auth_logout,
5301 Self::NewSessionRequest(_) => AGENT_METHOD_NAMES.session_new,
5302 Self::ListSessionsRequest(_) => AGENT_METHOD_NAMES.session_list,
5303 Self::DeleteSessionRequest(_) => AGENT_METHOD_NAMES.session_delete,
5304 #[cfg(feature = "unstable_session_fork")]
5305 Self::ForkSessionRequest(_) => AGENT_METHOD_NAMES.session_fork,
5306 Self::ResumeSessionRequest(_) => AGENT_METHOD_NAMES.session_resume,
5307 Self::CloseSessionRequest(_) => AGENT_METHOD_NAMES.session_close,
5308 Self::SetSessionConfigOptionRequest(_) => AGENT_METHOD_NAMES.session_set_config_option,
5309 Self::PromptRequest(_) => AGENT_METHOD_NAMES.session_prompt,
5310 #[cfg(feature = "unstable_nes")]
5311 Self::StartNesRequest(_) => AGENT_METHOD_NAMES.nes_start,
5312 #[cfg(feature = "unstable_nes")]
5313 Self::SuggestNesRequest(_) => AGENT_METHOD_NAMES.nes_suggest,
5314 #[cfg(feature = "unstable_nes")]
5315 Self::CloseNesRequest(_) => AGENT_METHOD_NAMES.nes_close,
5316 #[cfg(feature = "unstable_mcp_over_acp")]
5317 Self::MessageMcpRequest(_) => AGENT_METHOD_NAMES.mcp_message,
5318 Self::ExtMethodRequest(ext_request) => &ext_request.method,
5319 }
5320 }
5321}
5322
5323#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
5330#[serde(untagged)]
5331#[schemars(inline)]
5332#[non_exhaustive]
5333pub enum AgentResponse {
5334 InitializeResponse(Box<InitializeResponse>),
5336 LoginAuthResponse(#[serde(default)] Box<LoginAuthResponse>),
5338 #[cfg(feature = "unstable_llm_providers")]
5340 ListProvidersResponse(Box<ListProvidersResponse>),
5341 #[cfg(feature = "unstable_llm_providers")]
5343 SetProviderResponse(#[serde(default)] Box<SetProviderResponse>),
5344 #[cfg(feature = "unstable_llm_providers")]
5346 DisableProviderResponse(#[serde(default)] Box<DisableProviderResponse>),
5347 LogoutAuthResponse(#[serde(default)] Box<LogoutAuthResponse>),
5349 NewSessionResponse(Box<NewSessionResponse>),
5351 ListSessionsResponse(Box<ListSessionsResponse>),
5353 DeleteSessionResponse(#[serde(default)] Box<DeleteSessionResponse>),
5355 #[cfg(feature = "unstable_session_fork")]
5357 ForkSessionResponse(Box<ForkSessionResponse>),
5358 ResumeSessionResponse(#[serde(default)] Box<ResumeSessionResponse>),
5360 CloseSessionResponse(#[serde(default)] Box<CloseSessionResponse>),
5362 SetSessionConfigOptionResponse(Box<SetSessionConfigOptionResponse>),
5364 PromptResponse(Box<PromptResponse>),
5366 #[cfg(feature = "unstable_nes")]
5368 StartNesResponse(Box<StartNesResponse>),
5369 #[cfg(feature = "unstable_nes")]
5371 SuggestNesResponse(Box<SuggestNesResponse>),
5372 #[cfg(feature = "unstable_nes")]
5374 CloseNesResponse(#[serde(default)] Box<CloseNesResponse>),
5375 ExtMethodResponse(Box<ExtResponse>),
5377 #[cfg(feature = "unstable_mcp_over_acp")]
5379 MessageMcpResponse(Box<MessageMcpResponse>),
5380}
5381
5382#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
5389#[serde(untagged)]
5390#[schemars(inline)]
5391#[non_exhaustive]
5392pub enum ClientNotification {
5393 CancelSessionNotification(Box<CancelSessionNotification>),
5407 #[cfg(feature = "unstable_nes")]
5408 DidOpenDocumentNotification(Box<DidOpenDocumentNotification>),
5412 #[cfg(feature = "unstable_nes")]
5413 DidChangeDocumentNotification(Box<DidChangeDocumentNotification>),
5417 #[cfg(feature = "unstable_nes")]
5418 DidCloseDocumentNotification(Box<DidCloseDocumentNotification>),
5422 #[cfg(feature = "unstable_nes")]
5423 DidSaveDocumentNotification(Box<DidSaveDocumentNotification>),
5427 #[cfg(feature = "unstable_nes")]
5428 DidFocusDocumentNotification(Box<DidFocusDocumentNotification>),
5432 #[cfg(feature = "unstable_nes")]
5433 AcceptNesNotification(Box<AcceptNesNotification>),
5437 #[cfg(feature = "unstable_nes")]
5438 RejectNesNotification(Box<RejectNesNotification>),
5442 #[cfg(feature = "unstable_mcp_over_acp")]
5448 MessageMcpNotification(Box<MessageMcpNotification>),
5449 ExtNotification(Box<ExtNotification>),
5456}
5457
5458impl ClientNotification {
5459 #[must_use]
5461 pub fn method(&self) -> &str {
5462 match self {
5463 Self::CancelSessionNotification(_) => AGENT_METHOD_NAMES.session_cancel,
5464 #[cfg(feature = "unstable_nes")]
5465 Self::DidOpenDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_open,
5466 #[cfg(feature = "unstable_nes")]
5467 Self::DidChangeDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_change,
5468 #[cfg(feature = "unstable_nes")]
5469 Self::DidCloseDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_close,
5470 #[cfg(feature = "unstable_nes")]
5471 Self::DidSaveDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_save,
5472 #[cfg(feature = "unstable_nes")]
5473 Self::DidFocusDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_focus,
5474 #[cfg(feature = "unstable_nes")]
5475 Self::AcceptNesNotification(_) => AGENT_METHOD_NAMES.nes_accept,
5476 #[cfg(feature = "unstable_nes")]
5477 Self::RejectNesNotification(_) => AGENT_METHOD_NAMES.nes_reject,
5478 #[cfg(feature = "unstable_mcp_over_acp")]
5479 Self::MessageMcpNotification(_) => AGENT_METHOD_NAMES.mcp_message,
5480 Self::ExtNotification(ext_notification) => &ext_notification.method,
5481 }
5482 }
5483}
5484
5485#[cfg(test)]
5486mod test_serialization {
5487 use super::*;
5488 use serde_json::json;
5489
5490 fn test_meta() -> Meta {
5491 json!({ "source": "test" }).as_object().unwrap().clone()
5492 }
5493
5494 fn serialized_meta_key_count(value: &impl serde::Serialize) -> usize {
5495 serde_json::to_string(value)
5496 .unwrap()
5497 .matches("\"_meta\"")
5498 .count()
5499 }
5500
5501 #[test]
5502 fn test_initialize_capabilities_default_on_malformed_values() {
5503 let request: InitializeRequest = serde_json::from_value(json!({
5504 "protocolVersion": 2,
5505 "capabilities": false,
5506 "info": {
5507 "name": "client",
5508 "version": "1.0.0"
5509 }
5510 }))
5511 .unwrap();
5512 assert_eq!(request.capabilities, ClientCapabilities::default());
5513
5514 let response: InitializeResponse = serde_json::from_value(json!({
5515 "protocolVersion": 2,
5516 "capabilities": false,
5517 "info": {
5518 "name": "agent",
5519 "version": "1.0.0"
5520 }
5521 }))
5522 .unwrap();
5523 assert_eq!(response.capabilities, AgentCapabilities::default());
5524 }
5525
5526 #[test]
5527 fn test_agent_capabilities_default_on_malformed_values() {
5528 let capabilities: AgentCapabilities = serde_json::from_value(json!({
5529 "session": false,
5530 "auth": false
5531 }))
5532 .unwrap();
5533
5534 assert!(capabilities.session.is_none());
5535 assert_eq!(capabilities.auth, None);
5536 }
5537
5538 #[test]
5539 fn test_mcp_server_stdio_serialization() {
5540 let server = McpServer::Stdio(
5541 McpServerStdio::new("test-server", "/usr/bin/server")
5542 .args(vec!["--port".to_string(), "3000".to_string()])
5543 .env(vec![EnvVariable::new("API_KEY", "secret123")]),
5544 );
5545
5546 let json = serde_json::to_value(&server).unwrap();
5547 assert_eq!(
5548 json,
5549 json!({
5550 "type": "stdio",
5551 "name": "test-server",
5552 "command": "/usr/bin/server",
5553 "args": ["--port", "3000"],
5554 "env": [
5555 {
5556 "name": "API_KEY",
5557 "value": "secret123"
5558 }
5559 ]
5560 })
5561 );
5562
5563 let deserialized: McpServer = serde_json::from_value(json).unwrap();
5564 match deserialized {
5565 McpServer::Stdio(McpServerStdio {
5566 name,
5567 command,
5568 args,
5569 env,
5570 meta: _,
5571 }) => {
5572 assert_eq!(name, "test-server");
5573 assert_eq!(command, PathBuf::from("/usr/bin/server"));
5574 assert_eq!(args, vec!["--port", "3000"]);
5575 assert_eq!(env.len(), 1);
5576 assert_eq!(env[0].name, "API_KEY");
5577 assert_eq!(env[0].value, "secret123");
5578 }
5579 _ => panic!("Expected Stdio variant"),
5580 }
5581 }
5582
5583 #[test]
5584 fn test_mcp_server_empty_arrays_are_optional() {
5585 let stdio = McpServer::Stdio(McpServerStdio::new("test-server", "/usr/bin/server"));
5586 assert_eq!(
5587 serde_json::to_value(&stdio).unwrap(),
5588 json!({
5589 "type": "stdio",
5590 "name": "test-server",
5591 "command": "/usr/bin/server"
5592 })
5593 );
5594
5595 let McpServer::Stdio(McpServerStdio { args, env, .. }) =
5596 serde_json::from_value::<McpServer>(json!({
5597 "type": "stdio",
5598 "name": "test-server",
5599 "command": "/usr/bin/server"
5600 }))
5601 .unwrap()
5602 else {
5603 panic!("Expected Stdio variant");
5604 };
5605 assert!(args.is_empty());
5606 assert!(env.is_empty());
5607
5608 let http = McpServer::Http(McpServerHttp::new("http-server", "https://api.example.com"));
5609 assert_eq!(
5610 serde_json::to_value(&http).unwrap(),
5611 json!({
5612 "type": "http",
5613 "name": "http-server",
5614 "url": "https://api.example.com"
5615 })
5616 );
5617
5618 let McpServer::Http(McpServerHttp { headers, .. }) =
5619 serde_json::from_value::<McpServer>(json!({
5620 "type": "http",
5621 "name": "http-server",
5622 "url": "https://api.example.com"
5623 }))
5624 .unwrap()
5625 else {
5626 panic!("Expected Http variant");
5627 };
5628 assert!(headers.is_empty());
5629 }
5630
5631 #[test]
5632 fn test_mcp_server_unknown_transport_serialization() {
5633 let json = json!({
5634 "type": "websocket",
5635 "name": "future-server",
5636 "url": "wss://example.com/mcp",
5637 "protocolVersion": "2026-01-01"
5638 });
5639
5640 let deserialized: McpServer = serde_json::from_value(json.clone()).unwrap();
5641 let McpServer::Other(OtherMcpServer { type_, fields }) = &deserialized else {
5642 panic!("Expected Other variant");
5643 };
5644
5645 assert_eq!(type_, "websocket");
5646 assert_eq!(fields["name"], "future-server");
5647 assert_eq!(fields["url"], "wss://example.com/mcp");
5648 assert_eq!(fields["protocolVersion"], "2026-01-01");
5649 assert_eq!(serde_json::to_value(&deserialized).unwrap(), json);
5650 }
5651
5652 #[test]
5653 fn test_mcp_server_stdio_requires_type() {
5654 let result = serde_json::from_value::<McpServer>(json!({
5655 "name": "test-server",
5656 "command": "/usr/bin/server",
5657 "args": [],
5658 "env": []
5659 }));
5660
5661 assert!(result.is_err());
5662 }
5663
5664 #[test]
5665 fn test_mcp_server_unknown_does_not_hide_malformed_known_transport() {
5666 let result = serde_json::from_value::<McpServer>(json!({
5667 "type": "stdio",
5668 "name": "test-server",
5669 "args": [],
5670 "env": []
5671 }));
5672
5673 assert!(result.is_err());
5674 }
5675
5676 #[test]
5677 fn test_mcp_server_http_serialization() {
5678 let server = McpServer::Http(
5679 McpServerHttp::new("http-server", "https://api.example.com").headers(vec![
5680 HttpHeader::new("Authorization", "Bearer token123"),
5681 HttpHeader::new("Content-Type", "application/json"),
5682 ]),
5683 );
5684
5685 let json = serde_json::to_value(&server).unwrap();
5686 assert_eq!(
5687 json,
5688 json!({
5689 "type": "http",
5690 "name": "http-server",
5691 "url": "https://api.example.com",
5692 "headers": [
5693 {
5694 "name": "Authorization",
5695 "value": "Bearer token123"
5696 },
5697 {
5698 "name": "Content-Type",
5699 "value": "application/json"
5700 }
5701 ]
5702 })
5703 );
5704
5705 let deserialized: McpServer = serde_json::from_value(json).unwrap();
5706 match deserialized {
5707 McpServer::Http(McpServerHttp {
5708 name,
5709 url,
5710 headers,
5711 meta: _,
5712 }) => {
5713 assert_eq!(name, "http-server");
5714 assert_eq!(url, "https://api.example.com");
5715 assert_eq!(headers.len(), 2);
5716 assert_eq!(headers[0].name, "Authorization");
5717 assert_eq!(headers[0].value, "Bearer token123");
5718 assert_eq!(headers[1].name, "Content-Type");
5719 assert_eq!(headers[1].value, "application/json");
5720 }
5721 _ => panic!("Expected Http variant"),
5722 }
5723 }
5724
5725 #[test]
5726 fn mcp_server_http_schema_marks_url_as_uri() {
5727 let schema = serde_json::to_value(schemars::schema_for!(McpServerHttp)).unwrap();
5728
5729 assert_eq!(schema["properties"]["url"]["format"], "uri");
5730 }
5731
5732 #[cfg(feature = "unstable_mcp_over_acp")]
5733 #[test]
5734 fn test_client_mcp_message_method_names() {
5735 assert_eq!(AGENT_METHOD_NAMES.mcp_message, "mcp/message");
5736
5737 assert_eq!(
5738 ClientRequest::MessageMcpRequest(Box::new(MessageMcpRequest::new(
5739 "conn-1",
5740 "tools/list"
5741 )))
5742 .method(),
5743 "mcp/message"
5744 );
5745 assert_eq!(
5746 ClientNotification::MessageMcpNotification(Box::new(MessageMcpNotification::new(
5747 "conn-1",
5748 "notifications/progress"
5749 )))
5750 .method(),
5751 "mcp/message"
5752 );
5753 }
5754
5755 #[test]
5756 fn test_auth_method_names() {
5757 assert_eq!(AGENT_METHOD_NAMES.auth_login, "auth/login");
5758 assert_eq!(AGENT_METHOD_NAMES.auth_logout, "auth/logout");
5759
5760 assert_eq!(
5761 ClientRequest::LoginAuthRequest(Box::new(LoginAuthRequest::new("agent-login")))
5762 .method(),
5763 "auth/login"
5764 );
5765 assert_eq!(
5766 ClientRequest::LogoutAuthRequest(Box::new(LogoutAuthRequest::new())).method(),
5767 "auth/logout"
5768 );
5769 }
5770
5771 #[test]
5772 fn test_session_config_option_category_known_variants() {
5773 assert_eq!(
5775 serde_json::to_value(&SessionConfigOptionCategory::Mode).unwrap(),
5776 json!("mode")
5777 );
5778 assert_eq!(
5779 serde_json::to_value(&SessionConfigOptionCategory::Model).unwrap(),
5780 json!("model")
5781 );
5782 assert_eq!(
5783 serde_json::to_value(&SessionConfigOptionCategory::ModelConfig).unwrap(),
5784 json!("model_config")
5785 );
5786 assert_eq!(
5787 serde_json::to_value(&SessionConfigOptionCategory::ThoughtLevel).unwrap(),
5788 json!("thought_level")
5789 );
5790
5791 assert_eq!(
5793 serde_json::from_str::<SessionConfigOptionCategory>("\"mode\"").unwrap(),
5794 SessionConfigOptionCategory::Mode
5795 );
5796 assert_eq!(
5797 serde_json::from_str::<SessionConfigOptionCategory>("\"model\"").unwrap(),
5798 SessionConfigOptionCategory::Model
5799 );
5800 assert_eq!(
5801 serde_json::from_str::<SessionConfigOptionCategory>("\"model_config\"").unwrap(),
5802 SessionConfigOptionCategory::ModelConfig
5803 );
5804 assert_eq!(
5805 serde_json::from_str::<SessionConfigOptionCategory>("\"thought_level\"").unwrap(),
5806 SessionConfigOptionCategory::ThoughtLevel
5807 );
5808 }
5809
5810 #[test]
5811 fn test_session_config_option_category_unknown_variants() {
5812 let unknown: SessionConfigOptionCategory =
5814 serde_json::from_str("\"some_future_category\"").unwrap();
5815 assert_eq!(
5816 unknown,
5817 SessionConfigOptionCategory::Other("some_future_category".to_string())
5818 );
5819
5820 let json = serde_json::to_value(&unknown).unwrap();
5822 assert_eq!(json, json!("some_future_category"));
5823 }
5824
5825 #[test]
5826 fn test_session_config_option_category_custom_categories() {
5827 let custom: SessionConfigOptionCategory =
5829 serde_json::from_str("\"_my_custom_category\"").unwrap();
5830 assert_eq!(
5831 custom,
5832 SessionConfigOptionCategory::Other("_my_custom_category".to_string())
5833 );
5834
5835 let json = serde_json::to_value(&custom).unwrap();
5837 assert_eq!(json, json!("_my_custom_category"));
5838
5839 let deserialized: SessionConfigOptionCategory = serde_json::from_value(json).unwrap();
5841 assert_eq!(
5842 deserialized,
5843 SessionConfigOptionCategory::Other("_my_custom_category".to_string()),
5844 );
5845 }
5846
5847 fn test_config_option() -> SessionConfigOption {
5848 SessionConfigOption::select(
5849 "mode",
5850 "Mode",
5851 "ask",
5852 vec![SessionConfigSelectOption::new("ask", "Ask")],
5853 )
5854 }
5855
5856 #[test]
5857 fn test_session_response_config_options_default_empty_and_skip_serializing() {
5858 assert_eq!(
5859 serde_json::to_value(NewSessionResponse::new("sess")).unwrap(),
5860 json!({ "sessionId": "sess" })
5861 );
5862 assert_eq!(
5863 serde_json::to_value(ResumeSessionResponse::new()).unwrap(),
5864 json!({})
5865 );
5866 #[cfg(feature = "unstable_session_fork")]
5867 assert_eq!(
5868 serde_json::to_value(ForkSessionResponse::new("fork")).unwrap(),
5869 json!({ "sessionId": "fork" })
5870 );
5871
5872 let json = serde_json::to_value(
5873 NewSessionResponse::new("sess").config_options(vec![test_config_option()]),
5874 )
5875 .unwrap();
5876 assert_eq!(json["configOptions"].as_array().unwrap().len(), 1);
5877 }
5878
5879 #[test]
5880 fn test_session_response_config_options_deserialize_missing_null_and_invalid() {
5881 let missing: NewSessionResponse =
5882 serde_json::from_value(json!({ "sessionId": "sess" })).unwrap();
5883 assert!(missing.config_options.is_empty());
5884
5885 let null: NewSessionResponse = serde_json::from_value(json!({
5886 "sessionId": "sess",
5887 "configOptions": null
5888 }))
5889 .unwrap();
5890 assert!(null.config_options.is_empty());
5891
5892 let wrong_shape: NewSessionResponse = serde_json::from_value(json!({
5893 "sessionId": "sess",
5894 "configOptions": "oops"
5895 }))
5896 .unwrap();
5897 assert!(wrong_shape.config_options.is_empty());
5898
5899 let valid_option = serde_json::to_value(test_config_option()).unwrap();
5900 let mixed: NewSessionResponse = serde_json::from_value(json!({
5901 "sessionId": "sess",
5902 "configOptions": ["oops", valid_option]
5903 }))
5904 .unwrap();
5905 assert_eq!(mixed.config_options.len(), 1);
5906
5907 let resume: ResumeSessionResponse = serde_json::from_value(json!({})).unwrap();
5908 assert!(resume.config_options.is_empty());
5909 #[cfg(feature = "unstable_session_fork")]
5910 {
5911 let fork: ForkSessionResponse =
5912 serde_json::from_value(json!({ "sessionId": "fork" })).unwrap();
5913 assert!(fork.config_options.is_empty());
5914 }
5915 }
5916
5917 #[test]
5918 fn test_resume_session_replay_from_serialization() {
5919 assert_eq!(
5920 serde_json::to_value(ResumeSessionRequest::new(
5921 "sess_abc123",
5922 "/home/user/project"
5923 ))
5924 .unwrap(),
5925 json!({
5926 "sessionId": "sess_abc123",
5927 "cwd": "/home/user/project"
5928 })
5929 );
5930 assert_eq!(
5931 serde_json::to_value(
5932 ResumeSessionRequest::new("sess_abc123", "/home/user/project")
5933 .replay_from(ReplayFrom::from(ReplayFromStart::new()))
5934 )
5935 .unwrap(),
5936 json!({
5937 "sessionId": "sess_abc123",
5938 "cwd": "/home/user/project",
5939 "replayFrom": {
5940 "type": "start"
5941 }
5942 })
5943 );
5944
5945 let replay: ResumeSessionRequest = serde_json::from_value(json!({
5946 "sessionId": "sess_abc123",
5947 "cwd": "/home/user/project",
5948 "replayFrom": {
5949 "type": "start"
5950 }
5951 }))
5952 .unwrap();
5953 assert!(matches!(replay.replay_from, Some(ReplayFrom::Start(_))));
5954
5955 let none: ResumeSessionRequest = serde_json::from_value(json!({
5956 "sessionId": "sess_abc123",
5957 "cwd": "/home/user/project",
5958 "replayFrom": null
5959 }))
5960 .unwrap();
5961 assert!(none.replay_from.is_none());
5962 }
5963
5964 #[test]
5965 fn test_auth_method_agent_serialization() {
5966 let method = AuthMethod::Agent(AuthMethodAgent::new("default-auth", "Default Auth"));
5967
5968 let json = serde_json::to_value(&method).unwrap();
5969 assert_eq!(
5970 json,
5971 json!({
5972 "methodId": "default-auth",
5973 "name": "Default Auth",
5974 "type": "agent"
5975 })
5976 );
5977 assert!(!json.as_object().unwrap().contains_key("description"));
5979
5980 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
5981 match deserialized {
5982 AuthMethod::Agent(AuthMethodAgent {
5983 method_id, name, ..
5984 }) => {
5985 assert_eq!(method_id.0.as_ref(), "default-auth");
5986 assert_eq!(name, "Default Auth");
5987 }
5988 _ => panic!("Expected Agent variant"),
5989 }
5990 }
5991
5992 #[test]
5993 fn test_auth_method_agent_deserialization() {
5994 let json = json!({
5995 "methodId": "agent-auth",
5996 "name": "Agent Auth",
5997 "type": "agent"
5998 });
5999
6000 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
6001 assert!(matches!(deserialized, AuthMethod::Agent(_)));
6002 }
6003
6004 #[test]
6005 fn test_auth_method_agent_requires_type() {
6006 assert!(
6007 serde_json::from_value::<AuthMethod>(json!({
6008 "methodId": "agent-auth",
6009 "name": "Agent Auth"
6010 }))
6011 .is_err()
6012 );
6013 }
6014
6015 #[test]
6016 fn test_auth_method_agent_rejects_null_type() {
6017 assert!(
6018 serde_json::from_value::<AuthMethod>(json!({
6019 "methodId": "agent-auth",
6020 "name": "Agent Auth",
6021 "type": null
6022 }))
6023 .is_err()
6024 );
6025 }
6026
6027 #[test]
6028 fn test_auth_method_unknown_does_not_hide_malformed_agent() {
6029 assert!(
6030 serde_json::from_value::<AuthMethod>(json!({
6031 "methodId": "agent-auth",
6032 "type": "agent"
6033 }))
6034 .is_err()
6035 );
6036 }
6037
6038 #[test]
6039 fn test_auth_method_unknown_variant_roundtrip() {
6040 let method: AuthMethod = serde_json::from_value(json!({
6041 "methodId": "oauth",
6042 "name": "OAuth",
6043 "type": "_oauth",
6044 "authorizationUrl": "https://example.com/auth"
6045 }))
6046 .unwrap();
6047
6048 assert_eq!(method.method_id().0.as_ref(), "oauth");
6049 assert_eq!(method.name(), "OAuth");
6050 let AuthMethod::Other(unknown) = method else {
6051 panic!("expected unknown auth method");
6052 };
6053 assert_eq!(unknown.type_, "_oauth");
6054 assert_eq!(
6055 unknown.fields.get("authorizationUrl"),
6056 Some(&json!("https://example.com/auth"))
6057 );
6058
6059 assert_eq!(
6060 serde_json::to_value(AuthMethod::Other(unknown)).unwrap(),
6061 json!({
6062 "methodId": "oauth",
6063 "name": "OAuth",
6064 "type": "_oauth",
6065 "authorizationUrl": "https://example.com/auth"
6066 })
6067 );
6068 }
6069
6070 #[cfg(feature = "unstable_auth_methods")]
6071 #[test]
6072 fn test_auth_method_unknown_does_not_hide_malformed_known_variant() {
6073 assert!(
6074 serde_json::from_value::<AuthMethod>(json!({
6075 "methodId": "api-key",
6076 "name": "API Key",
6077 "type": "env_var"
6078 }))
6079 .is_err()
6080 );
6081 }
6082
6083 #[test]
6084 fn test_session_delete_serialization() {
6085 assert_eq!(AGENT_METHOD_NAMES.session_delete, "session/delete");
6086 assert_eq!(
6087 ClientRequest::DeleteSessionRequest(Box::new(DeleteSessionRequest::new("sess_abc123")))
6088 .method(),
6089 "session/delete"
6090 );
6091 assert_eq!(
6092 serde_json::to_value(DeleteSessionRequest::new("sess_abc123")).unwrap(),
6093 json!({
6094 "sessionId": "sess_abc123"
6095 })
6096 );
6097 assert_eq!(
6098 serde_json::to_value(DeleteSessionResponse::new()).unwrap(),
6099 json!({})
6100 );
6101 assert_eq!(
6102 serde_json::to_value(
6103 SessionCapabilities::new().delete(SessionDeleteCapabilities::new())
6104 )
6105 .unwrap(),
6106 json!({
6107 "delete": {}
6108 })
6109 );
6110 }
6111 #[test]
6112 fn test_session_additional_directories_serialization() {
6113 assert_eq!(
6114 serde_json::to_value(NewSessionRequest::new("/home/user/project")).unwrap(),
6115 json!({
6116 "cwd": "/home/user/project",
6117 })
6118 );
6119 assert_eq!(
6120 serde_json::to_value(
6121 NewSessionRequest::new("/home/user/project").additional_directories(vec![
6122 PathBuf::from("/home/user/shared-lib"),
6123 PathBuf::from("/home/user/product-docs"),
6124 ])
6125 )
6126 .unwrap(),
6127 json!({
6128 "cwd": "/home/user/project",
6129 "additionalDirectories": [
6130 "/home/user/shared-lib",
6131 "/home/user/product-docs"
6132 ],
6133 })
6134 );
6135 assert_eq!(
6136 serde_json::to_value(ResumeSessionRequest::new(
6137 "sess_abc123",
6138 "/home/user/project"
6139 ))
6140 .unwrap(),
6141 json!({
6142 "sessionId": "sess_abc123",
6143 "cwd": "/home/user/project",
6144 })
6145 );
6146 assert_eq!(
6147 serde_json::from_value::<ResumeSessionRequest>(json!({
6148 "sessionId": "sess_abc123",
6149 "cwd": "/home/user/project"
6150 }))
6151 .unwrap()
6152 .mcp_servers,
6153 Vec::<McpServer>::new()
6154 );
6155 assert_eq!(
6156 serde_json::from_value::<ResumeSessionRequest>(json!({
6157 "sessionId": "sess_abc123",
6158 "cwd": "/home/user/project",
6159 "mcpServers": null
6160 }))
6161 .unwrap()
6162 .mcp_servers,
6163 Vec::<McpServer>::new()
6164 );
6165 assert_eq!(
6166 serde_json::to_value(SessionInfo::new("sess_abc123", "/home/user/project")).unwrap(),
6167 json!({
6168 "sessionId": "sess_abc123",
6169 "cwd": "/home/user/project"
6170 })
6171 );
6172 assert_eq!(
6173 serde_json::to_value(
6174 SessionInfo::new("sess_abc123", "/home/user/project").additional_directories(vec![
6175 PathBuf::from("/home/user/shared-lib"),
6176 PathBuf::from("/home/user/product-docs"),
6177 ])
6178 )
6179 .unwrap(),
6180 json!({
6181 "sessionId": "sess_abc123",
6182 "cwd": "/home/user/project",
6183 "additionalDirectories": [
6184 "/home/user/shared-lib",
6185 "/home/user/product-docs"
6186 ]
6187 })
6188 );
6189 assert_eq!(
6190 serde_json::from_value::<SessionInfo>(json!({
6191 "sessionId": "sess_abc123",
6192 "cwd": "/home/user/project"
6193 }))
6194 .unwrap()
6195 .additional_directories,
6196 Vec::<PathBuf>::new()
6197 );
6198 }
6199 #[test]
6200 fn test_session_additional_directories_capabilities_serialization() {
6201 assert_eq!(
6202 serde_json::to_value(
6203 SessionCapabilities::new()
6204 .additional_directories(SessionAdditionalDirectoriesCapabilities::new())
6205 )
6206 .unwrap(),
6207 json!({
6208 "additionalDirectories": {}
6209 })
6210 );
6211 }
6212
6213 #[cfg(feature = "unstable_auth_methods")]
6214 #[test]
6215 fn test_auth_method_env_var_serialization() {
6216 let method = AuthMethod::EnvVar(AuthMethodEnvVar::new(
6217 "api-key",
6218 "API Key",
6219 vec![AuthEnvVar::new("API_KEY")],
6220 ));
6221
6222 let json = serde_json::to_value(&method).unwrap();
6223 assert_eq!(
6224 json,
6225 json!({
6226 "methodId": "api-key",
6227 "name": "API Key",
6228 "type": "env_var",
6229 "vars": [{"name": "API_KEY"}]
6230 })
6231 );
6232 assert!(!json["vars"][0].as_object().unwrap().contains_key("secret"));
6234 assert!(
6235 !json["vars"][0]
6236 .as_object()
6237 .unwrap()
6238 .contains_key("optional")
6239 );
6240
6241 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
6242 match deserialized {
6243 AuthMethod::EnvVar(AuthMethodEnvVar {
6244 method_id,
6245 name: method_name,
6246 vars,
6247 link,
6248 ..
6249 }) => {
6250 assert_eq!(method_id.0.as_ref(), "api-key");
6251 assert_eq!(method_name, "API Key");
6252 assert_eq!(vars.len(), 1);
6253 assert_eq!(vars[0].name, "API_KEY");
6254 assert!(vars[0].secret);
6255 assert!(!vars[0].optional);
6256 assert!(link.is_none());
6257 }
6258 _ => panic!("Expected EnvVar variant"),
6259 }
6260 }
6261
6262 #[cfg(feature = "unstable_auth_methods")]
6263 #[test]
6264 fn test_auth_method_env_var_with_link_serialization() {
6265 let method = AuthMethod::EnvVar(
6266 AuthMethodEnvVar::new("api-key", "API Key", vec![AuthEnvVar::new("API_KEY")])
6267 .link("https://example.com/keys"),
6268 );
6269
6270 let json = serde_json::to_value(&method).unwrap();
6271 assert_eq!(
6272 json,
6273 json!({
6274 "methodId": "api-key",
6275 "name": "API Key",
6276 "type": "env_var",
6277 "vars": [{"name": "API_KEY"}],
6278 "link": "https://example.com/keys"
6279 })
6280 );
6281
6282 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
6283 match deserialized {
6284 AuthMethod::EnvVar(AuthMethodEnvVar { link, .. }) => {
6285 assert_eq!(link.as_deref(), Some("https://example.com/keys"));
6286 }
6287 _ => panic!("Expected EnvVar variant"),
6288 }
6289 }
6290
6291 #[cfg(feature = "unstable_auth_methods")]
6292 #[test]
6293 fn test_auth_method_env_var_multiple_vars() {
6294 let method = AuthMethod::EnvVar(AuthMethodEnvVar::new(
6295 "azure-openai",
6296 "Azure OpenAI",
6297 vec![
6298 AuthEnvVar::new("AZURE_OPENAI_API_KEY").label("API Key"),
6299 AuthEnvVar::new("AZURE_OPENAI_ENDPOINT")
6300 .label("Endpoint URL")
6301 .secret(false),
6302 AuthEnvVar::new("AZURE_OPENAI_API_VERSION")
6303 .label("API Version")
6304 .secret(false)
6305 .optional(true),
6306 ],
6307 ));
6308
6309 let json = serde_json::to_value(&method).unwrap();
6310 assert_eq!(
6311 json,
6312 json!({
6313 "methodId": "azure-openai",
6314 "name": "Azure OpenAI",
6315 "type": "env_var",
6316 "vars": [
6317 {"name": "AZURE_OPENAI_API_KEY", "label": "API Key"},
6318 {"name": "AZURE_OPENAI_ENDPOINT", "label": "Endpoint URL", "secret": false},
6319 {"name": "AZURE_OPENAI_API_VERSION", "label": "API Version", "secret": false, "optional": true}
6320 ]
6321 })
6322 );
6323
6324 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
6325 match deserialized {
6326 AuthMethod::EnvVar(AuthMethodEnvVar { vars, .. }) => {
6327 assert_eq!(vars.len(), 3);
6328 assert_eq!(vars[0].name, "AZURE_OPENAI_API_KEY");
6330 assert_eq!(vars[0].label.as_deref(), Some("API Key"));
6331 assert!(vars[0].secret);
6332 assert!(!vars[0].optional);
6333 assert_eq!(vars[1].name, "AZURE_OPENAI_ENDPOINT");
6335 assert!(!vars[1].secret);
6336 assert!(!vars[1].optional);
6337 assert_eq!(vars[2].name, "AZURE_OPENAI_API_VERSION");
6339 assert!(!vars[2].secret);
6340 assert!(vars[2].optional);
6341 }
6342 _ => panic!("Expected EnvVar variant"),
6343 }
6344 }
6345
6346 #[cfg(feature = "unstable_auth_methods")]
6347 #[test]
6348 fn test_auth_method_terminal_serialization() {
6349 let method = AuthMethod::Terminal(AuthMethodTerminal::new("tui-auth", "Terminal Auth"));
6350
6351 let json = serde_json::to_value(&method).unwrap();
6352 assert_eq!(
6353 json,
6354 json!({
6355 "methodId": "tui-auth",
6356 "name": "Terminal Auth",
6357 "type": "terminal"
6358 })
6359 );
6360 assert!(!json.as_object().unwrap().contains_key("args"));
6362 assert!(!json.as_object().unwrap().contains_key("env"));
6363
6364 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
6365 match deserialized {
6366 AuthMethod::Terminal(AuthMethodTerminal { args, env, .. }) => {
6367 assert!(args.is_empty());
6368 assert!(env.is_empty());
6369 }
6370 _ => panic!("Expected Terminal variant"),
6371 }
6372 }
6373
6374 #[cfg(feature = "unstable_auth_methods")]
6375 #[test]
6376 fn test_auth_method_terminal_with_args_and_env_serialization() {
6377 let method = AuthMethod::Terminal(
6378 AuthMethodTerminal::new("tui-auth", "Terminal Auth")
6379 .args(vec!["--interactive".to_string(), "--color".to_string()])
6380 .env(vec![EnvVariable::new("TERM", "xterm-256color")]),
6381 );
6382
6383 let json = serde_json::to_value(&method).unwrap();
6384 assert_eq!(
6385 json,
6386 json!({
6387 "methodId": "tui-auth",
6388 "name": "Terminal Auth",
6389 "type": "terminal",
6390 "args": ["--interactive", "--color"],
6391 "env": [
6392 {
6393 "name": "TERM",
6394 "value": "xterm-256color"
6395 }
6396 ]
6397 })
6398 );
6399
6400 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
6401 match deserialized {
6402 AuthMethod::Terminal(AuthMethodTerminal { args, env, .. }) => {
6403 assert_eq!(args, vec!["--interactive", "--color"]);
6404 assert_eq!(env.len(), 1);
6405 assert_eq!(env[0].name, "TERM");
6406 assert_eq!(env[0].value, "xterm-256color");
6407 }
6408 _ => panic!("Expected Terminal variant"),
6409 }
6410 }
6411
6412 #[test]
6413 fn test_session_config_option_id_serialize() {
6414 let val = SessionConfigOptionValue::id("model-1");
6415 let json = serde_json::to_value(&val).unwrap();
6416 assert_eq!(json, json!({ "type": "id", "value": "model-1" }));
6417 }
6418
6419 #[test]
6420 fn test_session_config_option_value_boolean_serialize() {
6421 let val = SessionConfigOptionValue::boolean(true);
6422 let json = serde_json::to_value(&val).unwrap();
6423 assert_eq!(json, json!({ "type": "boolean", "value": true }));
6424 }
6425
6426 #[test]
6427 fn test_session_config_option_value_deserialize_id() {
6428 let json = json!({ "type": "id", "value": "model-1" });
6429 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6430 assert_eq!(val, SessionConfigOptionValue::id("model-1"));
6431 assert_eq!(val.as_id().unwrap().to_string(), "model-1");
6432 }
6433
6434 #[test]
6435 fn test_session_config_option_value_deserialize_requires_type() {
6436 let json = json!({ "value": "model-1" });
6437 let result = serde_json::from_value::<SessionConfigOptionValue>(json);
6438 assert!(result.is_err());
6439 }
6440
6441 #[test]
6442 fn test_session_config_option_value_deserialize_boolean() {
6443 let json = json!({ "type": "boolean", "value": true });
6444 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6445 assert_eq!(val, SessionConfigOptionValue::boolean(true));
6446 assert_eq!(val.as_bool(), Some(true));
6447 }
6448
6449 #[test]
6450 fn test_session_config_option_value_deserialize_boolean_false() {
6451 let json = json!({ "type": "boolean", "value": false });
6452 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6453 assert_eq!(val, SessionConfigOptionValue::boolean(false));
6454 assert_eq!(val.as_bool(), Some(false));
6455 }
6456
6457 #[test]
6458 fn test_session_config_option_value_deserialize_unknown_type_with_string_value() {
6459 let json = json!({
6460 "type": "text",
6461 "value": "freeform input",
6462 "maxLength": 200
6463 });
6464 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6465 let SessionConfigOptionValue::Other(unknown) = val else {
6466 panic!("Expected Other variant");
6467 };
6468 assert_eq!(unknown.type_, "text");
6469 assert_eq!(unknown.value, json!("freeform input"));
6470 assert_eq!(unknown.fields["maxLength"], json!(200));
6471 }
6472
6473 #[test]
6474 fn test_session_config_option_value_deserialize_unknown_type_with_object_value() {
6475 let json = json!({
6476 "type": "range",
6477 "value": { "min": 1, "max": 5 }
6478 });
6479 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6480 let SessionConfigOptionValue::Other(unknown) = val else {
6481 panic!("Expected Other variant");
6482 };
6483 assert_eq!(unknown.type_, "range");
6484 assert_eq!(unknown.value, json!({ "min": 1, "max": 5 }));
6485 }
6486
6487 #[test]
6488 fn test_session_config_option_value_roundtrip_id() {
6489 let original = SessionConfigOptionValue::id("option-a");
6490 let json = serde_json::to_value(&original).unwrap();
6491 let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6492 assert_eq!(original, roundtripped);
6493 }
6494
6495 #[test]
6496 fn test_session_config_option_value_roundtrip_boolean() {
6497 let original = SessionConfigOptionValue::boolean(false);
6498 let json = serde_json::to_value(&original).unwrap();
6499 let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6500 assert_eq!(original, roundtripped);
6501 }
6502
6503 #[test]
6504 fn test_session_config_option_value_roundtrip_other() {
6505 let mut fields = BTreeMap::new();
6506 fields.insert("maxLength".to_string(), json!(200));
6507 let original = SessionConfigOptionValue::Other(OtherSessionConfigOptionValue::new(
6508 "text",
6509 json!("freeform input"),
6510 fields,
6511 ));
6512 let json = serde_json::to_value(&original).unwrap();
6513 let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6514 assert_eq!(original, roundtripped);
6515 }
6516
6517 #[test]
6518 fn test_session_config_option_value_type_mismatch_boolean_with_string() {
6519 let json = json!({ "type": "boolean", "value": "not a bool" });
6520 let result = serde_json::from_value::<SessionConfigOptionValue>(json);
6521 assert!(result.is_err());
6522 }
6523
6524 #[test]
6525 fn test_session_config_option_value_from_impls() {
6526 let from_str: SessionConfigOptionValue = "model-1".into();
6527 assert_eq!(from_str.as_id().unwrap().to_string(), "model-1");
6528
6529 let from_id: SessionConfigOptionValue = SessionConfigValueId::new("model-2").into();
6530 assert_eq!(from_id.as_id().unwrap().to_string(), "model-2");
6531
6532 let from_bool: SessionConfigOptionValue = true.into();
6533 assert_eq!(from_bool.as_bool(), Some(true));
6534 }
6535
6536 #[test]
6537 fn test_set_session_config_option_request_id() {
6538 let req = SetSessionConfigOptionRequest::new("sess_1", "model", "model-1");
6539 let json = serde_json::to_value(&req).unwrap();
6540 assert_eq!(
6541 json,
6542 json!({
6543 "sessionId": "sess_1",
6544 "configId": "model",
6545 "type": "id",
6546 "value": "model-1"
6547 })
6548 );
6549 }
6550
6551 #[test]
6552 fn test_set_session_config_option_request_boolean() {
6553 let req = SetSessionConfigOptionRequest::new("sess_1", "brave_mode", true);
6554 let json = serde_json::to_value(&req).unwrap();
6555 assert_eq!(
6556 json,
6557 json!({
6558 "sessionId": "sess_1",
6559 "configId": "brave_mode",
6560 "type": "boolean",
6561 "value": true
6562 })
6563 );
6564 }
6565
6566 #[test]
6567 fn test_set_session_config_option_request_deserialize_requires_type() {
6568 let json = json!({
6569 "sessionId": "sess_1",
6570 "configId": "model",
6571 "value": "model-1"
6572 });
6573 let result = serde_json::from_value::<SetSessionConfigOptionRequest>(json);
6574 assert!(result.is_err());
6575 }
6576
6577 #[test]
6578 fn test_set_session_config_option_request_deserialize_boolean() {
6579 let json = json!({
6580 "sessionId": "sess_1",
6581 "configId": "brave_mode",
6582 "type": "boolean",
6583 "value": true
6584 });
6585 let req: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
6586 assert_eq!(req.value.as_bool(), Some(true));
6587 }
6588
6589 #[test]
6590 fn test_set_session_config_option_request_roundtrip_id() {
6591 let original = SetSessionConfigOptionRequest::new("s", "c", "v");
6592 let json = serde_json::to_value(&original).unwrap();
6593 let roundtripped: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
6594 assert_eq!(original, roundtripped);
6595 }
6596
6597 #[test]
6598 fn test_set_session_config_option_request_roundtrip_boolean() {
6599 let original = SetSessionConfigOptionRequest::new("s", "c", false);
6600 let json = serde_json::to_value(&original).unwrap();
6601 let roundtripped: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
6602 assert_eq!(original, roundtripped);
6603 }
6604
6605 #[test]
6606 fn test_session_config_boolean_serialization() {
6607 let cfg = SessionConfigBoolean::new(true);
6608 let json = serde_json::to_value(&cfg).unwrap();
6609 assert_eq!(json, json!({ "currentValue": true }));
6610
6611 let deserialized: SessionConfigBoolean = serde_json::from_value(json).unwrap();
6612 assert!(deserialized.current_value);
6613 }
6614
6615 #[test]
6616 fn test_session_config_option_boolean_variant() {
6617 let opt = SessionConfigOption::boolean("brave_mode", "Brave Mode", false)
6618 .description("Skip confirmation prompts")
6619 .meta(test_meta());
6620 assert_eq!(serialized_meta_key_count(&opt), 1);
6621
6622 let json = serde_json::to_value(&opt).unwrap();
6623 assert_eq!(
6624 json,
6625 json!({
6626 "configId": "brave_mode",
6627 "name": "Brave Mode",
6628 "description": "Skip confirmation prompts",
6629 "type": "boolean",
6630 "currentValue": false,
6631 "_meta": {
6632 "source": "test"
6633 }
6634 })
6635 );
6636
6637 let deserialized: SessionConfigOption = serde_json::from_value(json).unwrap();
6638 assert_eq!(deserialized.config_id.to_string(), "brave_mode");
6639 assert_eq!(deserialized.name, "Brave Mode");
6640 match deserialized.kind {
6641 SessionConfigKind::Boolean(ref b) => assert!(!b.current_value),
6642 _ => panic!("Expected Boolean kind"),
6643 }
6644 }
6645
6646 #[test]
6647 fn test_session_config_option_select_still_works() {
6648 let opt = SessionConfigOption::select(
6650 "model",
6651 "Model",
6652 "model-1",
6653 vec![
6654 SessionConfigSelectOption::new("model-1", "Model 1"),
6655 SessionConfigSelectOption::new("model-2", "Model 2"),
6656 ],
6657 )
6658 .meta(test_meta());
6659 assert_eq!(serialized_meta_key_count(&opt), 1);
6660
6661 let json = serde_json::to_value(&opt).unwrap();
6662 assert_eq!(json["type"], "select");
6663 assert_eq!(json["currentValue"], "model-1");
6664 assert_eq!(json["options"].as_array().unwrap().len(), 2);
6665 assert_eq!(json["_meta"]["source"], "test");
6666
6667 let deserialized: SessionConfigOption = serde_json::from_value(json).unwrap();
6668 match deserialized.kind {
6669 SessionConfigKind::Select(ref s) => {
6670 assert_eq!(s.current_value.to_string(), "model-1");
6671 }
6672 _ => panic!("Expected Select kind"),
6673 }
6674 }
6675
6676 #[test]
6677 fn test_session_config_option_unknown_kind_roundtrip() {
6678 let option: SessionConfigOption = serde_json::from_value(json!({
6679 "configId": "verbosity",
6680 "name": "Verbosity",
6681 "type": "_slider",
6682 "currentValue": 3,
6683 "min": 0,
6684 "max": 5,
6685 "_meta": {
6686 "source": "test"
6687 }
6688 }))
6689 .unwrap();
6690
6691 assert_eq!(option.config_id.to_string(), "verbosity");
6692 assert_eq!(option.meta.as_ref().unwrap()["source"], "test");
6693 let SessionConfigKind::Other(unknown) = &option.kind else {
6694 panic!("expected unknown config kind");
6695 };
6696 assert_eq!(unknown.type_, "_slider");
6697 assert_eq!(unknown.fields.get("currentValue"), Some(&json!(3)));
6698 assert!(!unknown.fields.contains_key("_meta"));
6699 assert_eq!(serialized_meta_key_count(&option), 1);
6700
6701 let json = serde_json::to_value(&option).unwrap();
6702 assert_eq!(json["type"], "_slider");
6703 assert_eq!(json["currentValue"], 3);
6704 assert_eq!(json["min"], 0);
6705 assert_eq!(json["max"], 5);
6706 assert_eq!(json["_meta"]["source"], "test");
6707 }
6708
6709 #[test]
6710 fn test_session_config_option_unknown_kind_does_not_duplicate_flattened_meta() {
6711 let mut fields = std::collections::BTreeMap::new();
6712 fields.insert("currentValue".to_string(), json!(3));
6713 fields.insert("_meta".to_string(), json!({ "inner": "ignored" }));
6714
6715 let option = SessionConfigOption::new(
6716 "verbosity",
6717 "Verbosity",
6718 SessionConfigKind::Other(OtherSessionConfigKind::new("_slider", fields)),
6719 )
6720 .meta(test_meta());
6721
6722 let SessionConfigKind::Other(unknown) = &option.kind else {
6723 panic!("expected unknown config kind");
6724 };
6725 assert!(!unknown.fields.contains_key("_meta"));
6726 assert_eq!(serialized_meta_key_count(&option), 1);
6727
6728 let json = serde_json::to_value(&option).unwrap();
6729 assert_eq!(json["type"], "_slider");
6730 assert_eq!(json["currentValue"], 3);
6731 assert_eq!(json["_meta"]["source"], "test");
6732 }
6733
6734 #[test]
6735 fn test_session_config_option_unknown_does_not_hide_malformed_known_kind() {
6736 assert!(
6737 serde_json::from_value::<SessionConfigOption>(json!({
6738 "configId": "model",
6739 "name": "Model",
6740 "type": "select"
6741 }))
6742 .is_err()
6743 );
6744 }
6745
6746 #[cfg(feature = "unstable_llm_providers")]
6747 #[test]
6748 fn test_llm_protocol_known_variants() {
6749 assert_eq!(
6750 serde_json::to_value(&LlmProtocol::Anthropic).unwrap(),
6751 json!("anthropic")
6752 );
6753 assert_eq!(
6754 serde_json::to_value(&LlmProtocol::OpenAi).unwrap(),
6755 json!("openai")
6756 );
6757 assert_eq!(
6758 serde_json::to_value(&LlmProtocol::Azure).unwrap(),
6759 json!("azure")
6760 );
6761 assert_eq!(
6762 serde_json::to_value(&LlmProtocol::Vertex).unwrap(),
6763 json!("vertex")
6764 );
6765 assert_eq!(
6766 serde_json::to_value(&LlmProtocol::Bedrock).unwrap(),
6767 json!("bedrock")
6768 );
6769
6770 assert_eq!(
6771 serde_json::from_str::<LlmProtocol>("\"anthropic\"").unwrap(),
6772 LlmProtocol::Anthropic
6773 );
6774 assert_eq!(
6775 serde_json::from_str::<LlmProtocol>("\"openai\"").unwrap(),
6776 LlmProtocol::OpenAi
6777 );
6778 assert_eq!(
6779 serde_json::from_str::<LlmProtocol>("\"azure\"").unwrap(),
6780 LlmProtocol::Azure
6781 );
6782 assert_eq!(
6783 serde_json::from_str::<LlmProtocol>("\"vertex\"").unwrap(),
6784 LlmProtocol::Vertex
6785 );
6786 assert_eq!(
6787 serde_json::from_str::<LlmProtocol>("\"bedrock\"").unwrap(),
6788 LlmProtocol::Bedrock
6789 );
6790 }
6791
6792 #[cfg(feature = "unstable_llm_providers")]
6793 #[test]
6794 fn test_llm_protocol_unknown_variant() {
6795 let unknown: LlmProtocol = serde_json::from_str("\"cohere\"").unwrap();
6796 assert_eq!(unknown, LlmProtocol::Other("cohere".to_string()));
6797
6798 let json = serde_json::to_value(&unknown).unwrap();
6799 assert_eq!(json, json!("cohere"));
6800 }
6801
6802 #[cfg(feature = "unstable_llm_providers")]
6803 #[test]
6804 fn test_provider_current_config_serialization() {
6805 let config =
6806 ProviderCurrentConfig::new(LlmProtocol::Anthropic, "https://api.anthropic.com");
6807
6808 let json = serde_json::to_value(&config).unwrap();
6809 assert_eq!(
6810 json,
6811 json!({
6812 "apiType": "anthropic",
6813 "baseUrl": "https://api.anthropic.com"
6814 })
6815 );
6816
6817 let deserialized: ProviderCurrentConfig = serde_json::from_value(json).unwrap();
6818 assert_eq!(deserialized.api_type, LlmProtocol::Anthropic);
6819 assert_eq!(deserialized.base_url, "https://api.anthropic.com");
6820 }
6821
6822 #[cfg(feature = "unstable_llm_providers")]
6823 #[test]
6824 fn test_provider_info_with_current_config() {
6825 let info = ProviderInfo::new(
6826 "main",
6827 vec![LlmProtocol::Anthropic, LlmProtocol::OpenAi],
6828 true,
6829 Some(ProviderCurrentConfig::new(
6830 LlmProtocol::Anthropic,
6831 "https://api.anthropic.com",
6832 )),
6833 );
6834
6835 let json = serde_json::to_value(&info).unwrap();
6836 assert_eq!(
6837 json,
6838 json!({
6839 "providerId": "main",
6840 "supported": ["anthropic", "openai"],
6841 "required": true,
6842 "current": {
6843 "apiType": "anthropic",
6844 "baseUrl": "https://api.anthropic.com"
6845 }
6846 })
6847 );
6848
6849 let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
6850 assert_eq!(deserialized.provider_id.to_string(), "main");
6851 assert_eq!(deserialized.supported.len(), 2);
6852 assert!(deserialized.required);
6853 assert!(deserialized.current.is_some());
6854 assert_eq!(
6855 deserialized.current.as_ref().unwrap().api_type,
6856 LlmProtocol::Anthropic
6857 );
6858 }
6859
6860 #[cfg(feature = "unstable_llm_providers")]
6861 #[test]
6862 fn test_provider_info_disabled() {
6863 let info = ProviderInfo::new(
6864 "secondary",
6865 vec![LlmProtocol::OpenAi],
6866 false,
6867 None::<ProviderCurrentConfig>,
6868 );
6869
6870 let json = serde_json::to_value(&info).unwrap();
6871 assert_eq!(
6872 json,
6873 json!({
6874 "providerId": "secondary",
6875 "supported": ["openai"],
6876 "required": false
6877 })
6878 );
6879
6880 let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
6881 assert_eq!(deserialized.provider_id.to_string(), "secondary");
6882 assert!(!deserialized.required);
6883 assert!(deserialized.current.is_none());
6884 }
6885
6886 #[cfg(feature = "unstable_llm_providers")]
6887 #[test]
6888 fn test_provider_info_missing_current_defaults_to_none() {
6889 let json = json!({
6891 "providerId": "main",
6892 "supported": ["anthropic"],
6893 "required": true
6894 });
6895 let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
6896 assert!(deserialized.current.is_none());
6897 }
6898
6899 #[cfg(feature = "unstable_llm_providers")]
6900 #[test]
6901 fn test_provider_info_explicit_null_current_decodes_to_none() {
6902 let json = json!({
6906 "providerId": "main",
6907 "supported": ["anthropic"],
6908 "required": true,
6909 "current": null
6910 });
6911 let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
6912 assert!(deserialized.current.is_none());
6913 }
6914
6915 #[cfg(feature = "unstable_llm_providers")]
6916 #[test]
6917 fn test_list_providers_response_serialization() {
6918 let response = ListProvidersResponse::new(vec![ProviderInfo::new(
6919 "main",
6920 vec![LlmProtocol::Anthropic],
6921 true,
6922 Some(ProviderCurrentConfig::new(
6923 LlmProtocol::Anthropic,
6924 "https://api.anthropic.com",
6925 )),
6926 )]);
6927
6928 let json = serde_json::to_value(&response).unwrap();
6929 assert_eq!(json["providers"].as_array().unwrap().len(), 1);
6930 assert_eq!(json["providers"][0]["providerId"], "main");
6931
6932 let deserialized: ListProvidersResponse = serde_json::from_value(json).unwrap();
6933 assert_eq!(deserialized.providers.len(), 1);
6934 }
6935
6936 #[cfg(feature = "unstable_llm_providers")]
6937 #[test]
6938 fn test_set_provider_request_serialization() {
6939 use std::collections::HashMap;
6940
6941 let mut headers = HashMap::new();
6942 headers.insert("Authorization".to_string(), "Bearer sk-test".to_string());
6943
6944 let request =
6945 SetProviderRequest::new("main", LlmProtocol::OpenAi, "https://api.openai.com/v1")
6946 .headers(headers);
6947
6948 let json = serde_json::to_value(&request).unwrap();
6949 assert_eq!(
6950 json,
6951 json!({
6952 "providerId": "main",
6953 "apiType": "openai",
6954 "baseUrl": "https://api.openai.com/v1",
6955 "headers": {
6956 "Authorization": "Bearer sk-test"
6957 }
6958 })
6959 );
6960
6961 let deserialized: SetProviderRequest = serde_json::from_value(json).unwrap();
6962 assert_eq!(deserialized.provider_id.to_string(), "main");
6963 assert_eq!(deserialized.api_type, LlmProtocol::OpenAi);
6964 assert_eq!(deserialized.base_url, "https://api.openai.com/v1");
6965 assert_eq!(deserialized.headers.len(), 1);
6966 assert_eq!(
6967 deserialized.headers.get("Authorization").unwrap(),
6968 "Bearer sk-test"
6969 );
6970 }
6971
6972 #[cfg(feature = "unstable_llm_providers")]
6973 #[test]
6974 fn test_set_provider_request_omits_empty_headers() {
6975 let request =
6976 SetProviderRequest::new("main", LlmProtocol::Anthropic, "https://api.anthropic.com");
6977
6978 let json = serde_json::to_value(&request).unwrap();
6979 assert!(!json.as_object().unwrap().contains_key("headers"));
6981 }
6982
6983 #[cfg(feature = "unstable_llm_providers")]
6984 #[test]
6985 fn test_disable_provider_request_serialization() {
6986 let request = DisableProviderRequest::new("secondary");
6987
6988 let json = serde_json::to_value(&request).unwrap();
6989 assert_eq!(json, json!({ "providerId": "secondary" }));
6990
6991 let deserialized: DisableProviderRequest = serde_json::from_value(json).unwrap();
6992 assert_eq!(deserialized.provider_id.to_string(), "secondary");
6993 }
6994
6995 #[cfg(feature = "unstable_llm_providers")]
6996 #[test]
6997 fn test_providers_capabilities_serialization() {
6998 let caps = ProvidersCapabilities::new();
6999
7000 let json = serde_json::to_value(&caps).unwrap();
7001 assert_eq!(json, json!({}));
7002
7003 let deserialized: ProvidersCapabilities = serde_json::from_value(json).unwrap();
7004 assert!(deserialized.meta.is_none());
7005 }
7006
7007 #[cfg(feature = "unstable_llm_providers")]
7008 #[test]
7009 fn test_agent_capabilities_with_providers() {
7010 let caps = AgentCapabilities::new().providers(ProvidersCapabilities::new());
7011
7012 let json = serde_json::to_value(&caps).unwrap();
7013 assert_eq!(json["providers"], json!({}));
7014
7015 let deserialized: AgentCapabilities = serde_json::from_value(json).unwrap();
7016 assert!(deserialized.providers.is_some());
7017 }
7018
7019 #[test]
7020 fn test_agent_capabilities_session_is_explicit() {
7021 let json = serde_json::to_value(AgentCapabilities::new()).unwrap();
7022 assert!(json.get("session").is_none());
7023
7024 let caps = AgentCapabilities::new().session(
7025 SessionCapabilities::new()
7026 .prompt(PromptCapabilities::new().image(PromptImageCapabilities::new()))
7027 .mcp(McpCapabilities::new().stdio(McpStdioCapabilities::new())),
7028 );
7029
7030 assert_eq!(
7031 serde_json::to_value(&caps).unwrap(),
7032 json!({
7033 "session": {
7034 "prompt": {
7035 "image": {}
7036 },
7037 "mcp": {
7038 "stdio": {}
7039 }
7040 }
7041 })
7042 );
7043
7044 let deserialized: AgentCapabilities = serde_json::from_value(json!({
7045 "session": false
7046 }))
7047 .unwrap();
7048 assert!(deserialized.session.is_none());
7049 }
7050
7051 #[test]
7052 fn test_prompt_capabilities_serialize_supported_content_as_objects() {
7053 let caps = PromptCapabilities::new()
7054 .image(PromptImageCapabilities::new())
7055 .audio(PromptAudioCapabilities::new())
7056 .embedded_context(PromptEmbeddedContextCapabilities::new());
7057
7058 assert_eq!(
7059 serde_json::to_value(&caps).unwrap(),
7060 json!({
7061 "image": {},
7062 "audio": {},
7063 "embeddedContext": {}
7064 })
7065 );
7066
7067 let deserialized: PromptCapabilities = serde_json::from_value(json!({
7068 "image": null,
7069 "audio": false,
7070 "embeddedContext": {}
7071 }))
7072 .unwrap();
7073 assert!(deserialized.image.is_none());
7074 assert!(deserialized.audio.is_none());
7075 assert!(deserialized.embedded_context.is_some());
7076 }
7077
7078 #[test]
7079 fn test_mcp_capabilities_serialize_supported_transports_as_objects() {
7080 let caps = McpCapabilities::new()
7081 .stdio(McpStdioCapabilities::new())
7082 .http(McpHttpCapabilities::new());
7083
7084 assert_eq!(
7085 serde_json::to_value(&caps).unwrap(),
7086 json!({
7087 "stdio": {},
7088 "http": {}
7089 })
7090 );
7091
7092 let deserialized: McpCapabilities = serde_json::from_value(json!({
7093 "stdio": null,
7094 "http": false
7095 }))
7096 .unwrap();
7097 assert!(deserialized.stdio.is_none());
7098 assert!(deserialized.http.is_none());
7099 }
7100
7101 #[cfg(feature = "unstable_mcp_over_acp")]
7102 #[test]
7103 fn test_mcp_capabilities_serialize_acp_support_as_object() {
7104 let caps = McpCapabilities::new().acp(McpAcpCapabilities::new());
7105
7106 assert_eq!(
7107 serde_json::to_value(&caps).unwrap(),
7108 json!({
7109 "acp": {}
7110 })
7111 );
7112 }
7113
7114 #[test]
7115 fn prompt_request_rejects_malformed_content_block() {
7116 use serde_json::json;
7117
7118 assert!(
7119 serde_json::from_value::<PromptRequest>(json!({
7120 "sessionId": "sess-1",
7121 "prompt": [{"type": "text"}]
7122 }))
7123 .is_err()
7124 );
7125 }
7126
7127 #[test]
7128 fn prompt_request_rejects_non_array_prompt() {
7129 use serde_json::json;
7130
7131 assert!(
7132 serde_json::from_value::<PromptRequest>(json!({
7133 "sessionId": "sess-1",
7134 "prompt": "hello"
7135 }))
7136 .is_err()
7137 );
7138 }
7139}