1use std::{borrow::Cow, collections::BTreeMap, sync::Arc};
7
8#[cfg(feature = "unstable_llm_providers")]
9use std::collections::HashMap;
10
11use derive_more::{Display, From};
12#[cfg(feature = "schemars")]
13use schemars::Schema;
14use serde::{Deserialize, Serialize};
15use serde_with::{DefaultOnError, DefaultOnNull, VecSkipError, serde_as, skip_serializing_none};
16
17use super::{
18 AbsolutePath, AvailableCommand, ClientCapabilities, ContentBlock, Error, ExtNotification,
19 ExtRequest, ExtResponse, MessageId, Meta, SessionId,
20};
21use crate::{IntoOption, ProtocolVersion};
22
23#[cfg(feature = "unstable_mcp_over_acp")]
24use super::mcp::{MCP_MESSAGE_METHOD_NAME, MessageMcpNotification};
25
26#[cfg(feature = "unstable_nes")]
27use super::{
28 AcceptNesNotification, CloseNesRequest, CloseNesResponse, DidChangeDocumentNotification,
29 DidCloseDocumentNotification, DidFocusDocumentNotification, DidOpenDocumentNotification,
30 DidSaveDocumentNotification, NesCapabilities, PositionEncodingKind, RejectNesNotification,
31 StartNesRequest, StartNesResponse, SuggestNesRequest, SuggestNesResponse,
32};
33
34#[cfg(feature = "unstable_nes")]
35use super::{
36 DOCUMENT_DID_CHANGE_METHOD_NAME, DOCUMENT_DID_CLOSE_METHOD_NAME,
37 DOCUMENT_DID_FOCUS_METHOD_NAME, DOCUMENT_DID_OPEN_METHOD_NAME, DOCUMENT_DID_SAVE_METHOD_NAME,
38 NES_ACCEPT_METHOD_NAME, NES_CLOSE_METHOD_NAME, NES_REJECT_METHOD_NAME, NES_START_METHOD_NAME,
39 NES_SUGGEST_METHOD_NAME,
40};
41
42#[serde_as]
50#[skip_serializing_none]
51#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
52#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
53#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = INITIALIZE_METHOD_NAME)))]
54#[serde(rename_all = "camelCase")]
55#[non_exhaustive]
56pub struct InitializeRequest {
57 pub protocol_version: ProtocolVersion,
59 pub info: Implementation,
61 #[serde_as(deserialize_as = "DefaultOnError")]
63 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
64 #[serde(default)]
65 pub capabilities: ClientCapabilities,
66 #[serde_as(deserialize_as = "DefaultOnError")]
72 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
73 #[serde(default)]
74 #[serde(rename = "_meta")]
75 pub meta: Option<Meta>,
76}
77
78impl InitializeRequest {
79 #[must_use]
81 pub fn new(protocol_version: ProtocolVersion, info: Implementation) -> Self {
82 Self {
83 protocol_version,
84 capabilities: ClientCapabilities::default(),
85 info,
86 meta: None,
87 }
88 }
89
90 #[must_use]
92 pub fn capabilities(mut self, capabilities: ClientCapabilities) -> Self {
93 self.capabilities = capabilities;
94 self
95 }
96
97 #[must_use]
103 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
104 self.meta = meta.into_option();
105 self
106 }
107}
108
109#[serde_as]
115#[skip_serializing_none]
116#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
117#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
118#[cfg_attr(feature = "schemars", 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 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
132 #[serde(default)]
133 pub capabilities: AgentCapabilities,
134 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
140 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
141 #[serde(default, skip_serializing_if = "Vec::is_empty")]
142 pub auth_methods: Vec<AuthMethod>,
143 #[serde_as(deserialize_as = "DefaultOnError")]
149 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
150 #[serde(default)]
151 #[serde(rename = "_meta")]
152 pub meta: Option<Meta>,
153}
154
155impl InitializeResponse {
156 #[must_use]
158 pub fn new(protocol_version: ProtocolVersion, info: Implementation) -> Self {
159 Self {
160 protocol_version,
161 capabilities: AgentCapabilities::default(),
162 auth_methods: vec![],
163 info,
164 meta: None,
165 }
166 }
167
168 #[must_use]
170 pub fn capabilities(mut self, capabilities: AgentCapabilities) -> Self {
171 self.capabilities = capabilities;
172 self
173 }
174
175 #[must_use]
180 pub fn auth_methods(mut self, auth_methods: Vec<AuthMethod>) -> Self {
181 self.auth_methods = auth_methods;
182 self
183 }
184
185 #[must_use]
191 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
192 self.meta = meta.into_option();
193 self
194 }
195}
196
197#[serde_as]
201#[skip_serializing_none]
202#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
203#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
204#[serde(rename_all = "camelCase")]
205#[non_exhaustive]
206pub struct Implementation {
207 pub name: String,
210 #[serde_as(deserialize_as = "DefaultOnError")]
215 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
216 #[serde(default)]
217 pub title: Option<String>,
218 pub version: String,
221 #[serde_as(deserialize_as = "DefaultOnError")]
227 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
228 #[serde(default)]
229 #[serde(rename = "_meta")]
230 pub meta: Option<Meta>,
231}
232
233impl Implementation {
234 #[must_use]
236 pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
237 Self {
238 name: name.into(),
239 title: None,
240 version: version.into(),
241 meta: None,
242 }
243 }
244
245 #[must_use]
250 pub fn title(mut self, title: impl IntoOption<String>) -> Self {
251 self.title = title.into_option();
252 self
253 }
254
255 #[must_use]
261 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
262 self.meta = meta.into_option();
263 self
264 }
265}
266
267#[serde_as]
277#[skip_serializing_none]
278#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
279#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
280#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGIN_METHOD_NAME)))]
281#[serde(rename_all = "camelCase")]
282#[non_exhaustive]
283pub struct LoginAuthRequest {
284 pub method_id: AuthMethodId,
287 #[serde_as(deserialize_as = "DefaultOnError")]
293 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
294 #[serde(default)]
295 #[serde(rename = "_meta")]
296 pub meta: Option<Meta>,
297}
298
299impl LoginAuthRequest {
300 #[must_use]
302 pub fn new(method_id: impl Into<AuthMethodId>) -> Self {
303 Self {
304 method_id: method_id.into(),
305 meta: None,
306 }
307 }
308
309 #[must_use]
315 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
316 self.meta = meta.into_option();
317 self
318 }
319}
320
321crate::serde_util::default_on_null! {
322 #[serde_as]
324 #[skip_serializing_none]
325 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
326 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
327 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGIN_METHOD_NAME)))]
328 #[serde(rename_all = "camelCase")]
329 #[non_exhaustive]
330 pub struct LoginAuthResponse {
331 #[serde_as(deserialize_as = "DefaultOnError")]
337 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
338 #[serde(default)]
339 #[serde(rename = "_meta")]
340 pub meta: Option<Meta>,
341 }
342}
343
344impl LoginAuthResponse {
345 #[must_use]
347 pub fn new() -> Self {
348 Self::default()
349 }
350
351 #[must_use]
357 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
358 self.meta = meta.into_option();
359 self
360 }
361}
362
363crate::serde_util::default_on_null! {
366 #[serde_as]
374 #[skip_serializing_none]
375 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
376 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
377 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGOUT_METHOD_NAME)))]
378 #[serde(rename_all = "camelCase")]
379 #[non_exhaustive]
380 pub struct LogoutAuthRequest {
381 #[serde_as(deserialize_as = "DefaultOnError")]
387 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
388 #[serde(default)]
389 #[serde(rename = "_meta")]
390 pub meta: Option<Meta>,
391 }
392}
393
394impl LogoutAuthRequest {
395 #[must_use]
397 pub fn new() -> Self {
398 Self::default()
399 }
400
401 #[must_use]
407 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
408 self.meta = meta.into_option();
409 self
410 }
411}
412
413crate::serde_util::default_on_null! {
414 #[serde_as]
416 #[skip_serializing_none]
417 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
418 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
419 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGOUT_METHOD_NAME)))]
420 #[serde(rename_all = "camelCase")]
421 #[non_exhaustive]
422 pub struct LogoutAuthResponse {
423 #[serde_as(deserialize_as = "DefaultOnError")]
429 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
430 #[serde(default)]
431 #[serde(rename = "_meta")]
432 pub meta: Option<Meta>,
433 }
434}
435
436impl LogoutAuthResponse {
437 #[must_use]
439 pub fn new() -> Self {
440 Self::default()
441 }
442
443 #[must_use]
449 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
450 self.meta = meta.into_option();
451 self
452 }
453}
454
455#[serde_as]
461#[skip_serializing_none]
462#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
463#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
464#[serde(rename_all = "camelCase")]
465#[non_exhaustive]
466pub struct AgentAuthCapabilities {
467 #[serde_as(deserialize_as = "DefaultOnError")]
473 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
474 #[serde(default)]
475 #[serde(rename = "_meta")]
476 pub meta: Option<Meta>,
477}
478
479impl AgentAuthCapabilities {
480 #[must_use]
482 pub fn new() -> Self {
483 Self::default()
484 }
485
486 #[must_use]
492 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
493 self.meta = meta.into_option();
494 self
495 }
496}
497
498#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
500#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
501#[serde(transparent)]
502#[from(forward)]
503#[non_exhaustive]
504pub struct AuthMethodId(pub Arc<str>);
505
506impl AuthMethodId {
507 #[must_use]
509 pub fn new(id: impl Into<Self>) -> Self {
510 id.into()
511 }
512}
513
514#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
518#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
519#[serde(tag = "type", rename_all = "snake_case")]
520#[non_exhaustive]
521pub enum AuthMethod {
522 Terminal(AuthMethodTerminal),
525 Agent(AuthMethodAgent),
529 #[serde(untagged)]
539 Other(OtherAuthMethod),
540}
541
542impl AuthMethod {
543 #[must_use]
545 pub fn method_id(&self) -> &AuthMethodId {
546 match self {
547 Self::Agent(a) => &a.method_id,
548 Self::Other(a) => &a.method_id,
549 Self::Terminal(t) => &t.method_id,
550 }
551 }
552
553 #[must_use]
555 pub fn name(&self) -> &str {
556 match self {
557 Self::Agent(a) => &a.name,
558 Self::Other(a) => &a.name,
559 Self::Terminal(t) => &t.name,
560 }
561 }
562
563 #[must_use]
565 pub fn description(&self) -> Option<&str> {
566 match self {
567 Self::Agent(a) => a.description.as_deref(),
568 Self::Other(a) => a.description.as_deref(),
569 Self::Terminal(t) => t.description.as_deref(),
570 }
571 }
572
573 #[must_use]
579 pub fn meta(&self) -> Option<&Meta> {
580 match self {
581 Self::Agent(a) => a.meta.as_ref(),
582 Self::Other(a) => a.meta.as_ref(),
583 Self::Terminal(t) => t.meta.as_ref(),
584 }
585 }
586}
587
588#[serde_as]
590#[skip_serializing_none]
591#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
592#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
593#[cfg_attr(feature = "schemars", schemars(inline))]
594#[cfg_attr(feature = "schemars", schemars(transform = other_auth_method_schema))]
595#[serde(rename_all = "camelCase")]
596#[non_exhaustive]
597pub struct OtherAuthMethod {
598 #[serde(rename = "type")]
604 pub type_: String,
605 pub method_id: AuthMethodId,
607 pub name: String,
609 #[serde_as(deserialize_as = "DefaultOnError")]
611 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
612 #[serde(default)]
613 pub description: Option<String>,
614 #[serde_as(deserialize_as = "DefaultOnError")]
620 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
621 #[serde(default)]
622 #[serde(rename = "_meta")]
623 pub meta: Option<Meta>,
624 #[serde(flatten)]
626 pub fields: BTreeMap<String, serde_json::Value>,
627}
628
629impl OtherAuthMethod {
630 #[must_use]
632 pub fn new(
633 type_: impl Into<String>,
634 method_id: impl Into<AuthMethodId>,
635 name: impl Into<String>,
636 mut fields: BTreeMap<String, serde_json::Value>,
637 ) -> Self {
638 fields.remove("type");
639 fields.remove("methodId");
640 fields.remove("name");
641 fields.remove("description");
642 fields.remove("_meta");
643 Self {
644 type_: type_.into(),
645 method_id: method_id.into(),
646 name: name.into(),
647 description: None,
648 meta: None,
649 fields,
650 }
651 }
652
653 #[must_use]
655 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
656 self.description = description.into_option();
657 self
658 }
659
660 #[must_use]
666 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
667 self.meta = meta.into_option();
668 self
669 }
670}
671
672impl<'de> Deserialize<'de> for OtherAuthMethod {
673 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
674 where
675 D: serde::Deserializer<'de>,
676 {
677 #[derive(Deserialize)]
678 #[serde(rename_all = "camelCase")]
679 struct RawOtherAuthMethod {
680 #[serde(rename = "type")]
681 type_: String,
682 method_id: AuthMethodId,
683 name: String,
684 description: Option<String>,
685 #[serde(rename = "_meta")]
686 meta: Option<Meta>,
687 #[serde(flatten)]
688 fields: BTreeMap<String, serde_json::Value>,
689 }
690
691 let raw = RawOtherAuthMethod::deserialize(deserializer)?;
692 if is_known_auth_method_type(&raw.type_) {
693 return Err(serde::de::Error::custom(format!(
694 "known authentication method `{}` did not match its schema",
695 raw.type_
696 )));
697 }
698
699 Ok(Self {
700 type_: raw.type_,
701 method_id: raw.method_id,
702 name: raw.name,
703 description: raw.description,
704 meta: raw.meta,
705 fields: raw.fields,
706 })
707 }
708}
709
710fn is_known_auth_method_type(type_: &str) -> bool {
711 matches!(type_, "agent" | "terminal")
712}
713
714#[cfg(feature = "schemars")]
715fn other_auth_method_schema(schema: &mut Schema) {
716 super::schema_util::reject_known_string_discriminators(schema, "type", &["agent", "terminal"]);
717}
718
719#[serde_as]
723#[skip_serializing_none]
724#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
725#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
726#[serde(rename_all = "camelCase")]
727#[non_exhaustive]
728pub struct AuthMethodAgent {
729 pub method_id: AuthMethodId,
731 pub name: String,
733 #[serde_as(deserialize_as = "DefaultOnError")]
735 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
736 #[serde(default)]
737 pub description: Option<String>,
738 #[serde_as(deserialize_as = "DefaultOnError")]
744 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
745 #[serde(default)]
746 #[serde(rename = "_meta")]
747 pub meta: Option<Meta>,
748}
749
750impl AuthMethodAgent {
751 #[must_use]
753 pub fn new(method_id: impl Into<AuthMethodId>, name: impl Into<String>) -> Self {
754 Self {
755 method_id: method_id.into(),
756 name: name.into(),
757 description: None,
758 meta: None,
759 }
760 }
761
762 #[must_use]
764 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
765 self.description = description.into_option();
766 self
767 }
768
769 #[must_use]
775 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
776 self.meta = meta.into_option();
777 self
778 }
779}
780
781#[serde_as]
789#[skip_serializing_none]
790#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
791#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
792#[serde(rename_all = "camelCase")]
793#[non_exhaustive]
794pub struct AuthMethodTerminal {
795 pub method_id: AuthMethodId,
797 pub name: String,
799 #[serde_as(deserialize_as = "DefaultOnError")]
801 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
802 #[serde(default)]
803 pub description: Option<String>,
804 #[serde_as(deserialize_as = "DefaultOnNull")]
806 #[serde(default, skip_serializing_if = "Vec::is_empty")]
807 pub args: Vec<String>,
808 #[serde_as(deserialize_as = "DefaultOnNull")]
812 #[serde(default, skip_serializing_if = "Vec::is_empty")]
813 pub env: Vec<EnvVariable>,
814 #[serde_as(deserialize_as = "DefaultOnError")]
820 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
821 #[serde(default)]
822 #[serde(rename = "_meta")]
823 pub meta: Option<Meta>,
824}
825
826impl AuthMethodTerminal {
827 #[must_use]
829 pub fn new(method_id: impl Into<AuthMethodId>, name: impl Into<String>) -> Self {
830 Self {
831 method_id: method_id.into(),
832 name: name.into(),
833 description: None,
834 args: Vec::new(),
835 env: Vec::new(),
836 meta: None,
837 }
838 }
839
840 #[must_use]
842 pub fn args(mut self, args: Vec<String>) -> Self {
843 self.args = args;
844 self
845 }
846
847 #[must_use]
851 pub fn env(mut self, env: Vec<EnvVariable>) -> Self {
852 self.env = env;
853 self
854 }
855
856 #[must_use]
858 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
859 self.description = description.into_option();
860 self
861 }
862
863 #[must_use]
869 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
870 self.meta = meta.into_option();
871 self
872 }
873}
874
875#[serde_as]
881#[skip_serializing_none]
882#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
883#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
884#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_NEW_METHOD_NAME)))]
885#[serde(rename_all = "camelCase")]
886#[non_exhaustive]
887pub struct NewSessionRequest {
888 pub cwd: AbsolutePath,
890 #[serde_as(deserialize_as = "DefaultOnNull")]
896 #[serde(default, skip_serializing_if = "Vec::is_empty")]
897 pub additional_directories: Vec<AbsolutePath>,
898 #[serde_as(deserialize_as = "DefaultOnNull")]
900 #[serde(default, skip_serializing_if = "Vec::is_empty")]
901 pub mcp_servers: Vec<McpServer>,
902 #[serde_as(deserialize_as = "DefaultOnError")]
908 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
909 #[serde(default)]
910 #[serde(rename = "_meta")]
911 pub meta: Option<Meta>,
912}
913
914impl NewSessionRequest {
915 #[must_use]
917 pub fn new(cwd: impl Into<AbsolutePath>) -> Self {
918 Self {
919 cwd: cwd.into(),
920 additional_directories: vec![],
921 mcp_servers: vec![],
922 meta: None,
923 }
924 }
925
926 #[must_use]
928 pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
929 where
930 I: IntoIterator<Item = P>,
931 P: Into<AbsolutePath>,
932 {
933 self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
934 self
935 }
936
937 #[must_use]
939 pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
940 self.mcp_servers = mcp_servers;
941 self
942 }
943
944 #[must_use]
950 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
951 self.meta = meta.into_option();
952 self
953 }
954}
955
956#[serde_as]
960#[skip_serializing_none]
961#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
962#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
963#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_NEW_METHOD_NAME)))]
964#[serde(rename_all = "camelCase")]
965#[non_exhaustive]
966pub struct NewSessionResponse {
967 pub session_id: SessionId,
971 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
973 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
974 #[serde(default, skip_serializing_if = "Vec::is_empty")]
975 pub config_options: Vec<SessionConfigOption>,
976 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
982 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
983 #[serde(default, skip_serializing_if = "Vec::is_empty")]
984 pub available_commands: Vec<AvailableCommand>,
985 #[serde_as(deserialize_as = "DefaultOnError")]
991 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
992 #[serde(default)]
993 #[serde(rename = "_meta")]
994 pub meta: Option<Meta>,
995}
996
997impl NewSessionResponse {
998 #[must_use]
1000 pub fn new(session_id: impl Into<SessionId>) -> Self {
1001 Self {
1002 session_id: session_id.into(),
1003 config_options: Vec::new(),
1004 available_commands: Vec::new(),
1005 meta: None,
1006 }
1007 }
1008
1009 #[must_use]
1011 pub fn config_options(mut self, config_options: Vec<SessionConfigOption>) -> Self {
1012 self.config_options = config_options;
1013 self
1014 }
1015
1016 #[must_use]
1018 pub fn available_commands(mut self, available_commands: Vec<AvailableCommand>) -> Self {
1019 self.available_commands = available_commands;
1020 self
1021 }
1022
1023 #[must_use]
1029 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1030 self.meta = meta.into_option();
1031 self
1032 }
1033}
1034
1035#[cfg(feature = "unstable_session_fork")]
1048#[serde_as]
1049#[skip_serializing_none]
1050#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1051#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1052#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_FORK_METHOD_NAME)))]
1053#[serde(rename_all = "camelCase")]
1054#[non_exhaustive]
1055pub struct ForkSessionRequest {
1056 pub session_id: SessionId,
1058 pub cwd: AbsolutePath,
1060 #[serde_as(deserialize_as = "DefaultOnNull")]
1066 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1067 pub additional_directories: Vec<AbsolutePath>,
1068 #[serde_as(deserialize_as = "DefaultOnNull")]
1070 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1071 pub mcp_servers: Vec<McpServer>,
1072 #[serde_as(deserialize_as = "DefaultOnError")]
1078 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1079 #[serde(default)]
1080 #[serde(rename = "_meta")]
1081 pub meta: Option<Meta>,
1082}
1083
1084#[cfg(feature = "unstable_session_fork")]
1085impl ForkSessionRequest {
1086 #[must_use]
1088 pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<AbsolutePath>) -> Self {
1089 Self {
1090 session_id: session_id.into(),
1091 cwd: cwd.into(),
1092 additional_directories: vec![],
1093 mcp_servers: vec![],
1094 meta: None,
1095 }
1096 }
1097
1098 #[must_use]
1100 pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
1101 where
1102 I: IntoIterator<Item = P>,
1103 P: Into<AbsolutePath>,
1104 {
1105 self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
1106 self
1107 }
1108
1109 #[must_use]
1111 pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1112 self.mcp_servers = mcp_servers;
1113 self
1114 }
1115
1116 #[must_use]
1122 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1123 self.meta = meta.into_option();
1124 self
1125 }
1126}
1127
1128#[cfg(feature = "unstable_session_fork")]
1134#[serde_as]
1135#[skip_serializing_none]
1136#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1137#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1138#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_FORK_METHOD_NAME)))]
1139#[serde(rename_all = "camelCase")]
1140#[non_exhaustive]
1141pub struct ForkSessionResponse {
1142 pub session_id: SessionId,
1144 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1146 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1147 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1148 pub config_options: Vec<SessionConfigOption>,
1149 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1155 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1156 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1157 pub available_commands: Vec<AvailableCommand>,
1158 #[serde_as(deserialize_as = "DefaultOnError")]
1164 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1165 #[serde(default)]
1166 #[serde(rename = "_meta")]
1167 pub meta: Option<Meta>,
1168}
1169
1170#[cfg(feature = "unstable_session_fork")]
1171impl ForkSessionResponse {
1172 #[must_use]
1174 pub fn new(session_id: impl Into<SessionId>) -> Self {
1175 Self {
1176 session_id: session_id.into(),
1177 config_options: Vec::new(),
1178 available_commands: Vec::new(),
1179 meta: None,
1180 }
1181 }
1182
1183 #[must_use]
1185 pub fn config_options(mut self, config_options: Vec<SessionConfigOption>) -> Self {
1186 self.config_options = config_options;
1187 self
1188 }
1189
1190 #[must_use]
1192 pub fn available_commands(mut self, available_commands: Vec<AvailableCommand>) -> Self {
1193 self.available_commands = available_commands;
1194 self
1195 }
1196
1197 #[must_use]
1203 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1204 self.meta = meta.into_option();
1205 self
1206 }
1207}
1208
1209#[serde_as]
1216#[skip_serializing_none]
1217#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1218#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1219#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_RESUME_METHOD_NAME)))]
1220#[serde(rename_all = "camelCase")]
1221#[non_exhaustive]
1222pub struct ResumeSessionRequest {
1223 pub session_id: SessionId,
1225 pub cwd: AbsolutePath,
1227 #[serde_as(deserialize_as = "DefaultOnNull")]
1234 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1235 pub additional_directories: Vec<AbsolutePath>,
1236 #[serde_as(deserialize_as = "DefaultOnNull")]
1238 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1239 pub mcp_servers: Vec<McpServer>,
1240 #[serde(default)]
1248 pub replay_from: Option<ReplayFrom>,
1249 #[serde_as(deserialize_as = "DefaultOnError")]
1255 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1256 #[serde(default)]
1257 #[serde(rename = "_meta")]
1258 pub meta: Option<Meta>,
1259}
1260
1261impl ResumeSessionRequest {
1262 #[must_use]
1264 pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<AbsolutePath>) -> Self {
1265 Self {
1266 session_id: session_id.into(),
1267 cwd: cwd.into(),
1268 additional_directories: vec![],
1269 mcp_servers: vec![],
1270 replay_from: None,
1271 meta: None,
1272 }
1273 }
1274
1275 #[must_use]
1277 pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
1278 where
1279 I: IntoIterator<Item = P>,
1280 P: Into<AbsolutePath>,
1281 {
1282 self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
1283 self
1284 }
1285
1286 #[must_use]
1288 pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1289 self.mcp_servers = mcp_servers;
1290 self
1291 }
1292
1293 #[must_use]
1301 pub fn replay_from(mut self, replay_from: impl IntoOption<ReplayFrom>) -> Self {
1302 self.replay_from = replay_from.into_option();
1303 self
1304 }
1305
1306 #[must_use]
1312 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1313 self.meta = meta.into_option();
1314 self
1315 }
1316}
1317
1318#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1322#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1323#[serde(tag = "type", rename_all = "snake_case")]
1324#[non_exhaustive]
1325pub enum ReplayFrom {
1326 Start(ReplayFromStart),
1328 #[serde(untagged)]
1338 Other(OtherReplayFrom),
1339}
1340
1341impl From<ReplayFromStart> for ReplayFrom {
1342 fn from(replay_from: ReplayFromStart) -> Self {
1343 Self::Start(replay_from)
1344 }
1345}
1346
1347#[serde_as]
1349#[skip_serializing_none]
1350#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1351#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1352#[serde(rename_all = "camelCase")]
1353#[non_exhaustive]
1354pub struct ReplayFromStart {
1355 #[serde_as(deserialize_as = "DefaultOnError")]
1361 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1362 #[serde(default)]
1363 #[serde(rename = "_meta")]
1364 pub meta: Option<Meta>,
1365}
1366
1367impl ReplayFromStart {
1368 #[must_use]
1370 pub fn new() -> Self {
1371 Self::default()
1372 }
1373
1374 #[must_use]
1380 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1381 self.meta = meta.into_option();
1382 self
1383 }
1384}
1385
1386#[serde_as]
1388#[skip_serializing_none]
1389#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1390#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
1391#[cfg_attr(feature = "schemars", schemars(inline))]
1392#[cfg_attr(feature = "schemars", schemars(transform = other_replay_from_schema))]
1393#[serde(rename_all = "camelCase")]
1394#[non_exhaustive]
1395pub struct OtherReplayFrom {
1396 #[serde(rename = "type")]
1402 pub type_: String,
1403 #[serde_as(deserialize_as = "DefaultOnError")]
1409 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1410 #[serde(default)]
1411 #[serde(rename = "_meta")]
1412 pub meta: Option<Meta>,
1413 #[serde(flatten)]
1415 pub fields: BTreeMap<String, serde_json::Value>,
1416}
1417
1418impl OtherReplayFrom {
1419 #[must_use]
1421 pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
1422 fields.remove("type");
1423 fields.remove("_meta");
1424 Self {
1425 type_: type_.into(),
1426 meta: None,
1427 fields,
1428 }
1429 }
1430
1431 #[must_use]
1437 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1438 self.meta = meta.into_option();
1439 self
1440 }
1441}
1442
1443impl<'de> Deserialize<'de> for OtherReplayFrom {
1444 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1445 where
1446 D: serde::Deserializer<'de>,
1447 {
1448 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
1449 let type_ = fields
1450 .remove("type")
1451 .ok_or_else(|| serde::de::Error::missing_field("type"))?;
1452 let serde_json::Value::String(type_) = type_ else {
1453 return Err(serde::de::Error::custom("`type` must be a string"));
1454 };
1455
1456 if is_known_replay_from_type(&type_) {
1457 return Err(serde::de::Error::custom(format!(
1458 "known replay cursor `{type_}` did not match its schema"
1459 )));
1460 }
1461
1462 let meta = fields
1463 .remove("_meta")
1464 .and_then(|value| serde_json::from_value(value).ok());
1465
1466 Ok(Self {
1467 type_,
1468 meta,
1469 fields,
1470 })
1471 }
1472}
1473
1474fn is_known_replay_from_type(type_: &str) -> bool {
1475 matches!(type_, "start")
1476}
1477
1478#[cfg(feature = "schemars")]
1479fn other_replay_from_schema(schema: &mut Schema) {
1480 super::schema_util::reject_known_string_discriminators(schema, "type", &["start"]);
1481}
1482
1483crate::serde_util::default_on_null! {
1484 #[serde_as]
1486 #[skip_serializing_none]
1487 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1488 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
1489 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_RESUME_METHOD_NAME)))]
1490 #[serde(rename_all = "camelCase")]
1491 #[non_exhaustive]
1492 pub struct ResumeSessionResponse {
1493 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1495 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1496 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1497 pub config_options: Vec<SessionConfigOption>,
1498 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1504 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1505 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1506 pub available_commands: Vec<AvailableCommand>,
1507 #[serde_as(deserialize_as = "DefaultOnError")]
1513 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1514 #[serde(default)]
1515 #[serde(rename = "_meta")]
1516 pub meta: Option<Meta>,
1517 }
1518}
1519
1520impl ResumeSessionResponse {
1521 #[must_use]
1523 pub fn new() -> Self {
1524 Self::default()
1525 }
1526
1527 #[must_use]
1529 pub fn config_options(mut self, config_options: Vec<SessionConfigOption>) -> Self {
1530 self.config_options = config_options;
1531 self
1532 }
1533
1534 #[must_use]
1536 pub fn available_commands(mut self, available_commands: Vec<AvailableCommand>) -> Self {
1537 self.available_commands = available_commands;
1538 self
1539 }
1540
1541 #[must_use]
1547 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1548 self.meta = meta.into_option();
1549 self
1550 }
1551}
1552
1553#[serde_as]
1561#[skip_serializing_none]
1562#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1563#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1564#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_CLOSE_METHOD_NAME)))]
1565#[serde(rename_all = "camelCase")]
1566#[non_exhaustive]
1567pub struct CloseSessionRequest {
1568 pub session_id: SessionId,
1570 #[serde_as(deserialize_as = "DefaultOnError")]
1576 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1577 #[serde(default)]
1578 #[serde(rename = "_meta")]
1579 pub meta: Option<Meta>,
1580}
1581
1582impl CloseSessionRequest {
1583 #[must_use]
1585 pub fn new(session_id: impl Into<SessionId>) -> Self {
1586 Self {
1587 session_id: session_id.into(),
1588 meta: None,
1589 }
1590 }
1591
1592 #[must_use]
1598 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1599 self.meta = meta.into_option();
1600 self
1601 }
1602}
1603
1604crate::serde_util::default_on_null! {
1605 #[serde_as]
1607 #[skip_serializing_none]
1608 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1609 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
1610 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_CLOSE_METHOD_NAME)))]
1611 #[serde(rename_all = "camelCase")]
1612 #[non_exhaustive]
1613 pub struct CloseSessionResponse {
1614 #[serde_as(deserialize_as = "DefaultOnError")]
1620 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1621 #[serde(default)]
1622 #[serde(rename = "_meta")]
1623 pub meta: Option<Meta>,
1624 }
1625}
1626
1627impl CloseSessionResponse {
1628 #[must_use]
1630 pub fn new() -> Self {
1631 Self::default()
1632 }
1633
1634 #[must_use]
1640 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1641 self.meta = meta.into_option();
1642 self
1643 }
1644}
1645
1646#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1650#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
1651#[serde(transparent)]
1652#[from(Arc<str>, String, &str, &mut str, Box<str>, Cow<'_, str>)]
1653#[non_exhaustive]
1654pub struct SessionListCursor(pub Arc<str>);
1655
1656impl SessionListCursor {
1657 #[must_use]
1659 pub fn new(cursor: impl Into<Self>) -> Self {
1660 cursor.into()
1661 }
1662}
1663
1664impl AsRef<str> for SessionListCursor {
1665 fn as_ref(&self) -> &str {
1666 &self.0
1667 }
1668}
1669
1670impl From<&String> for SessionListCursor {
1671 fn from(cursor: &String) -> Self {
1672 Self(cursor.as_str().into())
1673 }
1674}
1675
1676macro_rules! impl_session_list_cursor_option_conversion {
1677 ($source:ty) => {
1678 impl IntoOption<SessionListCursor> for $source {
1679 fn into_option(self) -> Option<SessionListCursor> {
1680 Some(self.into())
1681 }
1682 }
1683 };
1684}
1685
1686impl_session_list_cursor_option_conversion!(Arc<str>);
1687impl_session_list_cursor_option_conversion!(String);
1688impl_session_list_cursor_option_conversion!(&str);
1689impl_session_list_cursor_option_conversion!(&mut str);
1690impl_session_list_cursor_option_conversion!(&String);
1691impl_session_list_cursor_option_conversion!(Box<str>);
1692impl_session_list_cursor_option_conversion!(Cow<'_, str>);
1693
1694crate::serde_util::default_on_null! {
1695 #[serde_as]
1697 #[skip_serializing_none]
1698 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1699 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
1700 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_LIST_METHOD_NAME)))]
1701 #[serde(rename_all = "camelCase")]
1702 #[non_exhaustive]
1703 pub struct ListSessionsRequest {
1704 #[serde(default)]
1706 pub cwd: Option<AbsolutePath>,
1707 #[serde(default)]
1709 pub cursor: Option<SessionListCursor>,
1710 #[serde_as(deserialize_as = "DefaultOnError")]
1716 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1717 #[serde(default)]
1718 #[serde(rename = "_meta")]
1719 pub meta: Option<Meta>,
1720 }
1721}
1722
1723impl ListSessionsRequest {
1724 #[must_use]
1726 pub fn new() -> Self {
1727 Self::default()
1728 }
1729
1730 #[must_use]
1732 pub fn cwd(mut self, cwd: impl IntoOption<AbsolutePath>) -> Self {
1733 self.cwd = cwd.into_option();
1734 self
1735 }
1736
1737 #[must_use]
1739 pub fn cursor(mut self, cursor: impl IntoOption<SessionListCursor>) -> Self {
1740 self.cursor = cursor.into_option();
1741 self
1742 }
1743
1744 #[must_use]
1750 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1751 self.meta = meta.into_option();
1752 self
1753 }
1754}
1755
1756#[serde_as]
1758#[skip_serializing_none]
1759#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1760#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1761#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_LIST_METHOD_NAME)))]
1762#[serde(rename_all = "camelCase")]
1763#[non_exhaustive]
1764pub struct ListSessionsResponse {
1765 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1767 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1768 pub sessions: Vec<SessionInfo>,
1769 #[serde_as(deserialize_as = "DefaultOnError")]
1772 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1773 #[serde(default)]
1774 pub next_cursor: Option<SessionListCursor>,
1775 #[serde_as(deserialize_as = "DefaultOnError")]
1781 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1782 #[serde(default)]
1783 #[serde(rename = "_meta")]
1784 pub meta: Option<Meta>,
1785}
1786
1787impl ListSessionsResponse {
1788 #[must_use]
1790 pub fn new(sessions: Vec<SessionInfo>) -> Self {
1791 Self {
1792 sessions,
1793 next_cursor: None,
1794 meta: None,
1795 }
1796 }
1797
1798 #[must_use]
1800 pub fn next_cursor(mut self, next_cursor: impl IntoOption<SessionListCursor>) -> Self {
1801 self.next_cursor = next_cursor.into_option();
1802 self
1803 }
1804
1805 #[must_use]
1811 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1812 self.meta = meta.into_option();
1813 self
1814 }
1815}
1816
1817#[serde_as]
1823#[skip_serializing_none]
1824#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1825#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1826#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_DELETE_METHOD_NAME)))]
1827#[serde(rename_all = "camelCase")]
1828#[non_exhaustive]
1829pub struct DeleteSessionRequest {
1830 pub session_id: SessionId,
1832 #[serde_as(deserialize_as = "DefaultOnError")]
1838 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1839 #[serde(default)]
1840 #[serde(rename = "_meta")]
1841 pub meta: Option<Meta>,
1842}
1843
1844impl DeleteSessionRequest {
1845 #[must_use]
1847 pub fn new(session_id: impl Into<SessionId>) -> Self {
1848 Self {
1849 session_id: session_id.into(),
1850 meta: None,
1851 }
1852 }
1853
1854 #[must_use]
1860 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1861 self.meta = meta.into_option();
1862 self
1863 }
1864}
1865
1866crate::serde_util::default_on_null! {
1867 #[serde_as]
1869 #[skip_serializing_none]
1870 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1871 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
1872 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_DELETE_METHOD_NAME)))]
1873 #[serde(rename_all = "camelCase")]
1874 #[non_exhaustive]
1875 pub struct DeleteSessionResponse {
1876 #[serde_as(deserialize_as = "DefaultOnError")]
1882 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1883 #[serde(default)]
1884 #[serde(rename = "_meta")]
1885 pub meta: Option<Meta>,
1886 }
1887}
1888
1889impl DeleteSessionResponse {
1890 #[must_use]
1892 pub fn new() -> Self {
1893 Self::default()
1894 }
1895
1896 #[must_use]
1902 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1903 self.meta = meta.into_option();
1904 self
1905 }
1906}
1907
1908#[serde_as]
1910#[skip_serializing_none]
1911#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1912#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1913#[serde(rename_all = "camelCase")]
1914#[non_exhaustive]
1915pub struct SessionInfo {
1916 pub session_id: SessionId,
1918 pub cwd: AbsolutePath,
1920 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1926 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1927 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1928 pub additional_directories: Vec<AbsolutePath>,
1929
1930 #[serde_as(deserialize_as = "DefaultOnError")]
1932 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1933 #[serde(default)]
1934 pub title: Option<String>,
1935 #[serde_as(deserialize_as = "DefaultOnError")]
1937 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "format" = "date-time")))]
1938 #[serde(default)]
1939 pub updated_at: Option<String>,
1940 #[serde_as(deserialize_as = "DefaultOnError")]
1946 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1947 #[serde(default)]
1948 #[serde(rename = "_meta")]
1949 pub meta: Option<Meta>,
1950}
1951
1952impl SessionInfo {
1953 #[must_use]
1955 pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<AbsolutePath>) -> Self {
1956 Self {
1957 session_id: session_id.into(),
1958 cwd: cwd.into(),
1959 additional_directories: vec![],
1960 title: None,
1961 updated_at: None,
1962 meta: None,
1963 }
1964 }
1965
1966 #[must_use]
1968 pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
1969 where
1970 I: IntoIterator<Item = P>,
1971 P: Into<AbsolutePath>,
1972 {
1973 self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
1974 self
1975 }
1976
1977 #[must_use]
1979 pub fn title(mut self, title: impl IntoOption<String>) -> Self {
1980 self.title = title.into_option();
1981 self
1982 }
1983
1984 #[must_use]
1986 pub fn updated_at(mut self, updated_at: impl IntoOption<String>) -> Self {
1987 self.updated_at = updated_at.into_option();
1988 self
1989 }
1990
1991 #[must_use]
1997 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1998 self.meta = meta.into_option();
1999 self
2000 }
2001}
2002
2003#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2007#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, From, Display)]
2008#[serde(transparent)]
2009#[from(forward)]
2010#[non_exhaustive]
2011pub struct SessionConfigId(pub Arc<str>);
2012
2013impl SessionConfigId {
2014 #[must_use]
2016 pub fn new(id: impl Into<Self>) -> Self {
2017 id.into()
2018 }
2019}
2020
2021#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2023#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, From, Display)]
2024#[serde(transparent)]
2025#[from(forward)]
2026#[non_exhaustive]
2027pub struct SessionConfigValueId(pub Arc<str>);
2028
2029impl SessionConfigValueId {
2030 #[must_use]
2032 pub fn new(id: impl Into<Self>) -> Self {
2033 id.into()
2034 }
2035}
2036
2037#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2039#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, From, Display)]
2040#[serde(transparent)]
2041#[from(forward)]
2042#[non_exhaustive]
2043pub struct SessionConfigGroupId(pub Arc<str>);
2044
2045impl SessionConfigGroupId {
2046 #[must_use]
2048 pub fn new(id: impl Into<Self>) -> Self {
2049 id.into()
2050 }
2051}
2052
2053#[serde_as]
2055#[skip_serializing_none]
2056#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2057#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2058#[serde(rename_all = "camelCase")]
2059#[non_exhaustive]
2060pub struct SessionConfigSelectOption {
2061 pub value: SessionConfigValueId,
2063 pub name: String,
2065 #[serde_as(deserialize_as = "DefaultOnError")]
2067 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2068 #[serde(default)]
2069 pub description: Option<String>,
2070 #[serde_as(deserialize_as = "DefaultOnError")]
2076 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2077 #[serde(default)]
2078 #[serde(rename = "_meta")]
2079 pub meta: Option<Meta>,
2080}
2081
2082impl SessionConfigSelectOption {
2083 #[must_use]
2085 pub fn new(value: impl Into<SessionConfigValueId>, name: impl Into<String>) -> Self {
2086 Self {
2087 value: value.into(),
2088 name: name.into(),
2089 description: None,
2090 meta: None,
2091 }
2092 }
2093
2094 #[must_use]
2096 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
2097 self.description = description.into_option();
2098 self
2099 }
2100
2101 #[must_use]
2107 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2108 self.meta = meta.into_option();
2109 self
2110 }
2111}
2112
2113#[serde_as]
2115#[skip_serializing_none]
2116#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2117#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2118#[serde(rename_all = "camelCase")]
2119#[non_exhaustive]
2120pub struct SessionConfigSelectGroup {
2121 pub group_id: SessionConfigGroupId,
2123 pub name: String,
2125 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
2127 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
2128 pub options: Vec<SessionConfigSelectOption>,
2129 #[serde_as(deserialize_as = "DefaultOnError")]
2135 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2136 #[serde(default)]
2137 #[serde(rename = "_meta")]
2138 pub meta: Option<Meta>,
2139}
2140
2141impl SessionConfigSelectGroup {
2142 #[must_use]
2144 pub fn new(
2145 group_id: impl Into<SessionConfigGroupId>,
2146 name: impl Into<String>,
2147 options: Vec<SessionConfigSelectOption>,
2148 ) -> Self {
2149 Self {
2150 group_id: group_id.into(),
2151 name: name.into(),
2152 options,
2153 meta: None,
2154 }
2155 }
2156
2157 #[must_use]
2163 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2164 self.meta = meta.into_option();
2165 self
2166 }
2167}
2168
2169#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2171#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2172#[serde(untagged)]
2173#[non_exhaustive]
2174pub enum SessionConfigSelectOptions {
2175 Ungrouped(Vec<SessionConfigSelectOption>),
2177 Grouped(Vec<SessionConfigSelectGroup>),
2179}
2180
2181impl From<Vec<SessionConfigSelectOption>> for SessionConfigSelectOptions {
2182 fn from(options: Vec<SessionConfigSelectOption>) -> Self {
2183 SessionConfigSelectOptions::Ungrouped(options)
2184 }
2185}
2186
2187impl From<Vec<SessionConfigSelectGroup>> for SessionConfigSelectOptions {
2188 fn from(groups: Vec<SessionConfigSelectGroup>) -> Self {
2189 SessionConfigSelectOptions::Grouped(groups)
2190 }
2191}
2192
2193#[skip_serializing_none]
2195#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2196#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2197#[serde(rename_all = "camelCase")]
2198#[non_exhaustive]
2199pub struct SessionConfigSelect {
2200 pub current_value: SessionConfigValueId,
2202 pub options: SessionConfigSelectOptions,
2204}
2205
2206impl SessionConfigSelect {
2207 #[must_use]
2209 pub fn new(
2210 current_value: impl Into<SessionConfigValueId>,
2211 options: impl Into<SessionConfigSelectOptions>,
2212 ) -> Self {
2213 Self {
2214 current_value: current_value.into(),
2215 options: options.into(),
2216 }
2217 }
2218}
2219
2220#[skip_serializing_none]
2222#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2223#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2224#[serde(rename_all = "camelCase")]
2225#[non_exhaustive]
2226pub struct SessionConfigBoolean {
2227 pub current_value: bool,
2229}
2230
2231impl SessionConfigBoolean {
2232 #[must_use]
2234 pub fn new(current_value: bool) -> Self {
2235 Self { current_value }
2236 }
2237}
2238
2239#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2249#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2250#[serde(rename_all = "snake_case")]
2251#[non_exhaustive]
2252pub enum SessionConfigOptionCategory {
2253 Mode,
2255 Model,
2257 ModelConfig,
2259 ThoughtLevel,
2261 #[serde(untagged)]
2267 Other(String),
2268}
2269
2270#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2272#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2273#[serde(tag = "type", rename_all = "snake_case")]
2274#[non_exhaustive]
2275pub enum SessionConfigKind {
2276 Select(SessionConfigSelect),
2278 Boolean(SessionConfigBoolean),
2280 #[serde(untagged)]
2290 Other(OtherSessionConfigKind),
2291}
2292
2293#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2295#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
2296#[cfg_attr(feature = "schemars", schemars(inline))]
2297#[cfg_attr(feature = "schemars", schemars(transform = other_session_config_kind_schema))]
2298#[serde(rename_all = "camelCase")]
2299#[non_exhaustive]
2300pub struct OtherSessionConfigKind {
2301 #[serde(rename = "type")]
2307 pub type_: String,
2308 #[serde(flatten)]
2310 pub fields: BTreeMap<String, serde_json::Value>,
2311}
2312
2313impl OtherSessionConfigKind {
2314 #[must_use]
2316 pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
2317 fields.remove("type");
2318 fields.remove("_meta");
2319 Self {
2320 type_: type_.into(),
2321 fields,
2322 }
2323 }
2324}
2325
2326impl<'de> Deserialize<'de> for OtherSessionConfigKind {
2327 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2328 where
2329 D: serde::Deserializer<'de>,
2330 {
2331 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
2332 let type_ = fields
2333 .remove("type")
2334 .ok_or_else(|| serde::de::Error::missing_field("type"))?;
2335 let serde_json::Value::String(type_) = type_ else {
2336 return Err(serde::de::Error::custom("`type` must be a string"));
2337 };
2338
2339 if is_known_session_config_kind_type(&type_) {
2340 return Err(serde::de::Error::custom(format!(
2341 "known session configuration option `{type_}` did not match its schema"
2342 )));
2343 }
2344
2345 Ok(Self { type_, fields })
2346 }
2347}
2348
2349fn is_known_session_config_kind_type(type_: &str) -> bool {
2350 matches!(type_, "select" | "boolean")
2351}
2352
2353#[cfg(feature = "schemars")]
2354fn other_session_config_kind_schema(schema: &mut Schema) {
2355 super::schema_util::reject_known_string_discriminators(schema, "type", &["select", "boolean"]);
2356}
2357
2358#[serde_as]
2360#[skip_serializing_none]
2361#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2362#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2363#[serde(rename_all = "camelCase")]
2364#[non_exhaustive]
2365pub struct SessionConfigOption {
2366 pub config_id: SessionConfigId,
2368 pub name: String,
2370 #[serde_as(deserialize_as = "DefaultOnError")]
2372 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2373 #[serde(default)]
2374 pub description: Option<String>,
2375 #[serde_as(deserialize_as = "DefaultOnError")]
2377 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2378 #[serde(default)]
2379 pub category: Option<SessionConfigOptionCategory>,
2380 #[serde(flatten)]
2382 pub kind: SessionConfigKind,
2383 #[serde_as(deserialize_as = "DefaultOnError")]
2389 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2390 #[serde(default)]
2391 #[serde(rename = "_meta")]
2392 pub meta: Option<Meta>,
2393}
2394
2395impl SessionConfigOption {
2396 #[must_use]
2398 pub fn new(
2399 config_id: impl Into<SessionConfigId>,
2400 name: impl Into<String>,
2401 kind: SessionConfigKind,
2402 ) -> Self {
2403 Self {
2404 config_id: config_id.into(),
2405 name: name.into(),
2406 description: None,
2407 category: None,
2408 kind,
2409 meta: None,
2410 }
2411 }
2412
2413 #[must_use]
2415 pub fn select(
2416 config_id: impl Into<SessionConfigId>,
2417 name: impl Into<String>,
2418 current_value: impl Into<SessionConfigValueId>,
2419 options: impl Into<SessionConfigSelectOptions>,
2420 ) -> Self {
2421 Self::new(
2422 config_id,
2423 name,
2424 SessionConfigKind::Select(SessionConfigSelect::new(current_value, options)),
2425 )
2426 }
2427
2428 #[must_use]
2430 pub fn boolean(
2431 config_id: impl Into<SessionConfigId>,
2432 name: impl Into<String>,
2433 current_value: bool,
2434 ) -> Self {
2435 Self::new(
2436 config_id,
2437 name,
2438 SessionConfigKind::Boolean(SessionConfigBoolean::new(current_value)),
2439 )
2440 }
2441
2442 #[must_use]
2444 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
2445 self.description = description.into_option();
2446 self
2447 }
2448
2449 #[must_use]
2451 pub fn category(mut self, category: impl IntoOption<SessionConfigOptionCategory>) -> Self {
2452 self.category = category.into_option();
2453 self
2454 }
2455
2456 #[must_use]
2462 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2463 self.meta = meta.into_option();
2464 self
2465 }
2466}
2467
2468#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2477#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
2478#[serde(tag = "type", rename_all = "snake_case")]
2479#[non_exhaustive]
2480pub enum SessionConfigOptionValue {
2481 Id {
2483 value: SessionConfigValueId,
2485 },
2486 Boolean {
2488 value: bool,
2490 },
2491 #[serde(untagged)]
2497 Other(OtherSessionConfigOptionValue),
2498}
2499
2500#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2502#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
2503#[cfg_attr(feature = "schemars", schemars(inline))]
2504#[cfg_attr(feature = "schemars", schemars(transform = other_session_config_option_value_schema))]
2505#[serde(rename_all = "camelCase")]
2506#[non_exhaustive]
2507pub struct OtherSessionConfigOptionValue {
2508 #[serde(rename = "type")]
2514 pub type_: String,
2515 pub value: serde_json::Value,
2517 #[serde(flatten)]
2519 pub fields: BTreeMap<String, serde_json::Value>,
2520}
2521
2522impl OtherSessionConfigOptionValue {
2523 #[must_use]
2525 pub fn new(
2526 type_: impl Into<String>,
2527 value: serde_json::Value,
2528 mut fields: BTreeMap<String, serde_json::Value>,
2529 ) -> Self {
2530 fields.remove("type");
2531 fields.remove("value");
2532 fields.remove("_meta");
2533 Self {
2534 type_: type_.into(),
2535 value,
2536 fields,
2537 }
2538 }
2539}
2540
2541impl<'de> Deserialize<'de> for OtherSessionConfigOptionValue {
2542 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2543 where
2544 D: serde::Deserializer<'de>,
2545 {
2546 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
2547 let type_ = fields
2548 .remove("type")
2549 .ok_or_else(|| serde::de::Error::missing_field("type"))?;
2550 let serde_json::Value::String(type_) = type_ else {
2551 return Err(serde::de::Error::custom("`type` must be a string"));
2552 };
2553
2554 if is_known_session_config_option_value_type(&type_) {
2555 return Err(serde::de::Error::custom(format!(
2556 "known session configuration option value `{type_}` did not match its schema"
2557 )));
2558 }
2559
2560 let value = fields
2561 .remove("value")
2562 .ok_or_else(|| serde::de::Error::missing_field("value"))?;
2563
2564 Ok(Self {
2565 type_,
2566 value,
2567 fields,
2568 })
2569 }
2570}
2571
2572impl<'de> Deserialize<'de> for SessionConfigOptionValue {
2573 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2574 where
2575 D: serde::Deserializer<'de>,
2576 {
2577 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
2578 let type_ = fields.remove("type");
2579 let value = fields
2580 .remove("value")
2581 .ok_or_else(|| serde::de::Error::missing_field("value"))?;
2582
2583 let type_ = type_.ok_or_else(|| serde::de::Error::missing_field("type"))?;
2584
2585 let serde_json::Value::String(type_) = type_ else {
2586 return Err(serde::de::Error::custom("`type` must be a string"));
2587 };
2588
2589 match type_.as_str() {
2590 "id" => {
2591 let value = serde_json::from_value(value).map_err(|error| {
2592 serde::de::Error::custom(format!(
2593 "`value` must be a string for `type: id`: {error}"
2594 ))
2595 })?;
2596 Ok(Self::Id { value })
2597 }
2598 "boolean" => {
2599 let value = serde_json::from_value(value).map_err(|error| {
2600 serde::de::Error::custom(format!(
2601 "`value` must be a boolean for `type: boolean`: {error}"
2602 ))
2603 })?;
2604 Ok(Self::Boolean { value })
2605 }
2606 _ => Ok(Self::Other(OtherSessionConfigOptionValue {
2607 type_,
2608 value,
2609 fields,
2610 })),
2611 }
2612 }
2613}
2614
2615fn is_known_session_config_option_value_type(type_: &str) -> bool {
2616 matches!(type_, "id" | "boolean")
2617}
2618
2619#[cfg(feature = "schemars")]
2620fn other_session_config_option_value_schema(schema: &mut Schema) {
2621 super::schema_util::reject_known_string_discriminators(schema, "type", &["id", "boolean"]);
2622}
2623
2624impl SessionConfigOptionValue {
2625 #[must_use]
2627 pub fn id(id: impl Into<SessionConfigValueId>) -> Self {
2628 Self::Id { value: id.into() }
2629 }
2630
2631 #[must_use]
2633 pub fn boolean(val: bool) -> Self {
2634 Self::Boolean { value: val }
2635 }
2636
2637 #[must_use]
2640 pub fn as_id(&self) -> Option<&SessionConfigValueId> {
2641 match self {
2642 Self::Id { value } => Some(value),
2643 _ => None,
2644 }
2645 }
2646
2647 #[must_use]
2649 pub fn as_bool(&self) -> Option<bool> {
2650 match self {
2651 Self::Boolean { value } => Some(*value),
2652 _ => None,
2653 }
2654 }
2655}
2656
2657impl From<SessionConfigValueId> for SessionConfigOptionValue {
2658 fn from(value: SessionConfigValueId) -> Self {
2659 Self::Id { value }
2660 }
2661}
2662
2663impl From<bool> for SessionConfigOptionValue {
2664 fn from(value: bool) -> Self {
2665 Self::Boolean { value }
2666 }
2667}
2668
2669impl From<&str> for SessionConfigOptionValue {
2670 fn from(value: &str) -> Self {
2671 Self::Id {
2672 value: SessionConfigValueId::new(value),
2673 }
2674 }
2675}
2676
2677#[serde_as]
2679#[skip_serializing_none]
2680#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2681#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2682#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_CONFIG_OPTION_METHOD_NAME)))]
2683#[serde(rename_all = "camelCase")]
2684#[non_exhaustive]
2685pub struct SetSessionConfigOptionRequest {
2686 pub session_id: SessionId,
2688 pub config_id: SessionConfigId,
2690 #[serde(flatten)]
2694 pub value: SessionConfigOptionValue,
2695 #[serde_as(deserialize_as = "DefaultOnError")]
2701 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2702 #[serde(default)]
2703 #[serde(rename = "_meta")]
2704 pub meta: Option<Meta>,
2705}
2706
2707impl SetSessionConfigOptionRequest {
2708 #[must_use]
2710 pub fn new(
2711 session_id: impl Into<SessionId>,
2712 config_id: impl Into<SessionConfigId>,
2713 value: impl Into<SessionConfigOptionValue>,
2714 ) -> Self {
2715 Self {
2716 session_id: session_id.into(),
2717 config_id: config_id.into(),
2718 value: value.into(),
2719 meta: None,
2720 }
2721 }
2722
2723 #[must_use]
2729 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2730 self.meta = meta.into_option();
2731 self
2732 }
2733}
2734
2735#[serde_as]
2737#[skip_serializing_none]
2738#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2739#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2740#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_CONFIG_OPTION_METHOD_NAME)))]
2741#[serde(rename_all = "camelCase")]
2742#[non_exhaustive]
2743pub struct SetSessionConfigOptionResponse {
2744 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
2746 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
2747 pub config_options: Vec<SessionConfigOption>,
2748 #[serde_as(deserialize_as = "DefaultOnError")]
2754 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2755 #[serde(default)]
2756 #[serde(rename = "_meta")]
2757 pub meta: Option<Meta>,
2758}
2759
2760impl SetSessionConfigOptionResponse {
2761 #[must_use]
2763 pub fn new(config_options: Vec<SessionConfigOption>) -> Self {
2764 Self {
2765 config_options,
2766 meta: None,
2767 }
2768 }
2769
2770 #[must_use]
2776 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2777 self.meta = meta.into_option();
2778 self
2779 }
2780}
2781
2782#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2791#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2792#[serde(tag = "type", rename_all = "snake_case")]
2793#[non_exhaustive]
2794pub enum McpServer {
2795 Http(McpServerHttp),
2799 #[cfg(feature = "unstable_mcp_over_acp")]
2808 Acp(McpServerAcp),
2809 Stdio(McpServerStdio),
2813 #[serde(untagged)]
2823 Other(OtherMcpServer),
2824}
2825
2826#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2828#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
2829#[cfg_attr(feature = "schemars", schemars(inline))]
2830#[cfg_attr(feature = "schemars", schemars(transform = other_mcp_server_schema))]
2831#[serde(rename_all = "camelCase")]
2832#[non_exhaustive]
2833pub struct OtherMcpServer {
2834 #[serde(rename = "type")]
2840 pub type_: String,
2841 #[serde(flatten)]
2843 pub fields: BTreeMap<String, serde_json::Value>,
2844}
2845
2846impl OtherMcpServer {
2847 #[must_use]
2849 pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
2850 fields.remove("type");
2851 Self {
2852 type_: type_.into(),
2853 fields,
2854 }
2855 }
2856}
2857
2858impl<'de> Deserialize<'de> for OtherMcpServer {
2859 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2860 where
2861 D: serde::Deserializer<'de>,
2862 {
2863 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
2864 let type_ = fields
2865 .remove("type")
2866 .ok_or_else(|| serde::de::Error::missing_field("type"))?;
2867 let serde_json::Value::String(type_) = type_ else {
2868 return Err(serde::de::Error::custom("`type` must be a string"));
2869 };
2870
2871 if is_known_mcp_server_type(&type_) {
2872 return Err(serde::de::Error::custom(format!(
2873 "known MCP server transport `{type_}` did not match its schema"
2874 )));
2875 }
2876
2877 Ok(Self { type_, fields })
2878 }
2879}
2880
2881fn is_known_mcp_server_type(type_: &str) -> bool {
2882 match type_ {
2883 "http" | "stdio" => true,
2884 #[cfg(feature = "unstable_mcp_over_acp")]
2885 "acp" => true,
2886 _ => false,
2887 }
2888}
2889
2890#[cfg(feature = "schemars")]
2891fn other_mcp_server_schema(schema: &mut Schema) {
2892 super::schema_util::reject_known_string_discriminators(
2893 schema,
2894 "type",
2895 &[
2896 "http",
2897 "stdio",
2898 #[cfg(feature = "unstable_mcp_over_acp")]
2899 "acp",
2900 ],
2901 );
2902}
2903
2904#[serde_as]
2906#[skip_serializing_none]
2907#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2908#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2909#[serde(rename_all = "camelCase")]
2910#[non_exhaustive]
2911pub struct McpServerHttp {
2912 pub name: String,
2914 #[cfg_attr(feature = "schemars", schemars(url))]
2916 pub url: String,
2917 #[serde_as(deserialize_as = "DefaultOnNull")]
2919 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2920 pub headers: Vec<HttpHeader>,
2921 #[serde_as(deserialize_as = "DefaultOnError")]
2927 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2928 #[serde(default)]
2929 #[serde(rename = "_meta")]
2930 pub meta: Option<Meta>,
2931}
2932
2933impl McpServerHttp {
2934 #[must_use]
2936 pub fn new(name: impl Into<String>, url: impl Into<String>) -> Self {
2937 Self {
2938 name: name.into(),
2939 url: url.into(),
2940 headers: Vec::new(),
2941 meta: None,
2942 }
2943 }
2944
2945 #[must_use]
2947 pub fn headers(mut self, headers: Vec<HttpHeader>) -> Self {
2948 self.headers = headers;
2949 self
2950 }
2951
2952 #[must_use]
2958 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2959 self.meta = meta.into_option();
2960 self
2961 }
2962}
2963
2964#[cfg(feature = "unstable_mcp_over_acp")]
2973#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2974#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
2975#[serde(transparent)]
2976#[from(forward)]
2977#[non_exhaustive]
2978pub struct McpServerAcpId(pub Arc<str>);
2979
2980#[cfg(feature = "unstable_mcp_over_acp")]
2981impl McpServerAcpId {
2982 #[must_use]
2984 pub fn new(id: impl Into<Self>) -> Self {
2985 id.into()
2986 }
2987}
2988
2989#[serde_as]
2998#[skip_serializing_none]
2999#[cfg(feature = "unstable_mcp_over_acp")]
3000#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3001#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3002#[serde(rename_all = "camelCase")]
3003#[non_exhaustive]
3004pub struct McpServerAcp {
3005 pub name: String,
3007 pub server_id: McpServerAcpId,
3012 #[serde_as(deserialize_as = "DefaultOnError")]
3018 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3019 #[serde(default)]
3020 #[serde(rename = "_meta")]
3021 pub meta: Option<Meta>,
3022}
3023
3024#[cfg(feature = "unstable_mcp_over_acp")]
3025impl McpServerAcp {
3026 #[must_use]
3028 pub fn new(name: impl Into<String>, server_id: impl Into<McpServerAcpId>) -> Self {
3029 Self {
3030 name: name.into(),
3031 server_id: server_id.into(),
3032 meta: None,
3033 }
3034 }
3035
3036 #[must_use]
3042 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3043 self.meta = meta.into_option();
3044 self
3045 }
3046}
3047
3048#[serde_as]
3050#[skip_serializing_none]
3051#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3052#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3053#[serde(rename_all = "camelCase")]
3054#[non_exhaustive]
3055pub struct McpServerStdio {
3056 pub name: String,
3058 pub command: AbsolutePath,
3060 #[serde_as(deserialize_as = "DefaultOnNull")]
3062 #[serde(default, skip_serializing_if = "Vec::is_empty")]
3063 pub args: Vec<String>,
3064 #[serde_as(deserialize_as = "DefaultOnNull")]
3066 #[serde(default, skip_serializing_if = "Vec::is_empty")]
3067 pub env: Vec<EnvVariable>,
3068 #[serde_as(deserialize_as = "DefaultOnError")]
3074 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3075 #[serde(default)]
3076 #[serde(rename = "_meta")]
3077 pub meta: Option<Meta>,
3078}
3079
3080impl McpServerStdio {
3081 #[must_use]
3083 pub fn new(name: impl Into<String>, command: impl Into<AbsolutePath>) -> Self {
3084 Self {
3085 name: name.into(),
3086 command: command.into(),
3087 args: Vec::new(),
3088 env: Vec::new(),
3089 meta: None,
3090 }
3091 }
3092
3093 #[must_use]
3095 pub fn args(mut self, args: Vec<String>) -> Self {
3096 self.args = args;
3097 self
3098 }
3099
3100 #[must_use]
3102 pub fn env(mut self, env: Vec<EnvVariable>) -> Self {
3103 self.env = env;
3104 self
3105 }
3106
3107 #[must_use]
3113 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3114 self.meta = meta.into_option();
3115 self
3116 }
3117}
3118
3119#[serde_as]
3121#[skip_serializing_none]
3122#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3123#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3124#[serde(rename_all = "camelCase")]
3125#[non_exhaustive]
3126pub struct EnvVariable {
3127 pub name: String,
3129 pub value: String,
3131 #[serde_as(deserialize_as = "DefaultOnError")]
3137 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3138 #[serde(default)]
3139 #[serde(rename = "_meta")]
3140 pub meta: Option<Meta>,
3141}
3142
3143impl EnvVariable {
3144 #[must_use]
3146 pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
3147 Self {
3148 name: name.into(),
3149 value: value.into(),
3150 meta: None,
3151 }
3152 }
3153
3154 #[must_use]
3160 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3161 self.meta = meta.into_option();
3162 self
3163 }
3164}
3165
3166#[serde_as]
3168#[skip_serializing_none]
3169#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3170#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3171#[serde(rename_all = "camelCase")]
3172#[non_exhaustive]
3173pub struct HttpHeader {
3174 pub name: String,
3176 pub value: String,
3178 #[serde_as(deserialize_as = "DefaultOnError")]
3184 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3185 #[serde(default)]
3186 #[serde(rename = "_meta")]
3187 pub meta: Option<Meta>,
3188}
3189
3190impl HttpHeader {
3191 #[must_use]
3193 pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
3194 Self {
3195 name: name.into(),
3196 value: value.into(),
3197 meta: None,
3198 }
3199 }
3200
3201 #[must_use]
3207 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3208 self.meta = meta.into_option();
3209 self
3210 }
3211}
3212
3213#[serde_as]
3221#[skip_serializing_none]
3222#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3223#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
3224#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_PROMPT_METHOD_NAME)))]
3225#[serde(rename_all = "camelCase")]
3226#[non_exhaustive]
3227pub struct PromptRequest {
3228 pub session_id: SessionId,
3230 pub prompt: Vec<ContentBlock>,
3244 #[serde_as(deserialize_as = "DefaultOnError")]
3250 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3251 #[serde(default)]
3252 #[serde(rename = "_meta")]
3253 pub meta: Option<Meta>,
3254}
3255
3256impl PromptRequest {
3257 #[must_use]
3259 pub fn new(session_id: impl Into<SessionId>, prompt: Vec<ContentBlock>) -> Self {
3260 Self {
3261 session_id: session_id.into(),
3262 prompt,
3263 meta: None,
3264 }
3265 }
3266
3267 #[must_use]
3273 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3274 self.meta = meta.into_option();
3275 self
3276 }
3277}
3278
3279#[serde_as]
3287#[skip_serializing_none]
3288#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3289#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3290#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_PROMPT_METHOD_NAME)))]
3291#[serde(rename_all = "camelCase")]
3292#[non_exhaustive]
3293pub struct PromptResponse {
3294 pub message_id: MessageId,
3302 #[serde_as(deserialize_as = "DefaultOnError")]
3308 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3309 #[serde(default)]
3310 #[serde(rename = "_meta")]
3311 pub meta: Option<Meta>,
3312}
3313
3314impl PromptResponse {
3315 #[must_use]
3317 pub fn new(message_id: impl Into<MessageId>) -> Self {
3318 Self {
3319 message_id: message_id.into(),
3320 meta: None,
3321 }
3322 }
3323
3324 #[must_use]
3330 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3331 self.meta = meta.into_option();
3332 self
3333 }
3334}
3335
3336#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3340#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
3341#[serde(tag = "stopReason", rename_all = "snake_case")]
3342#[non_exhaustive]
3343pub enum StopReason {
3344 EndTurn,
3346 MaxTokens,
3348 MaxTurnRequests,
3351 Refusal,
3355 Cancelled,
3361 Error(ErrorStopReason),
3366 #[serde(untagged)]
3372 Other(OtherStopReason),
3373}
3374
3375impl From<ErrorStopReason> for StopReason {
3376 fn from(value: ErrorStopReason) -> Self {
3377 Self::Error(value)
3378 }
3379}
3380
3381#[serde_as]
3383#[skip_serializing_none]
3384#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3385#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
3386#[serde(rename_all = "camelCase")]
3387#[non_exhaustive]
3388pub struct ErrorStopReason {
3389 #[serde_as(deserialize_as = "DefaultOnError")]
3394 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3395 #[serde(default)]
3396 pub error: Option<Box<Error>>,
3397}
3398
3399impl ErrorStopReason {
3400 #[must_use]
3402 pub fn new() -> Self {
3403 Self::default()
3404 }
3405
3406 #[must_use]
3408 pub fn error(mut self, error: impl IntoOption<Error>) -> Self {
3409 self.error = error.into_option().map(Box::new);
3410 self
3411 }
3412}
3413
3414#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3416#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
3417#[cfg_attr(feature = "schemars", schemars(inline))]
3418#[cfg_attr(feature = "schemars", schemars(transform = other_stop_reason_schema))]
3419#[serde(rename_all = "camelCase")]
3420#[non_exhaustive]
3421pub struct OtherStopReason {
3422 pub stop_reason: String,
3428 #[serde(flatten)]
3430 pub fields: BTreeMap<String, serde_json::Value>,
3431}
3432
3433impl OtherStopReason {
3434 #[must_use]
3436 pub fn new(
3437 stop_reason: impl Into<String>,
3438 mut fields: BTreeMap<String, serde_json::Value>,
3439 ) -> Self {
3440 remove_idle_state_update_fields(&mut fields);
3441 Self {
3442 stop_reason: stop_reason.into(),
3443 fields,
3444 }
3445 }
3446}
3447
3448impl<'de> Deserialize<'de> for OtherStopReason {
3449 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3450 where
3451 D: serde::Deserializer<'de>,
3452 {
3453 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
3454 let stop_reason = fields
3455 .remove("stopReason")
3456 .ok_or_else(|| serde::de::Error::missing_field("stopReason"))?;
3457 let serde_json::Value::String(stop_reason) = stop_reason else {
3458 return Err(serde::de::Error::custom("`stopReason` must be a string"));
3459 };
3460
3461 if KNOWN_STOP_REASONS.contains(&stop_reason.as_str()) {
3462 return Err(serde::de::Error::custom(format!(
3463 "known stop reason `{stop_reason}` did not match its schema"
3464 )));
3465 }
3466 remove_idle_state_update_fields(&mut fields);
3467
3468 Ok(Self {
3469 stop_reason,
3470 fields,
3471 })
3472 }
3473}
3474
3475const KNOWN_STOP_REASONS: &[&str] = &[
3476 "end_turn",
3477 "max_tokens",
3478 "max_turn_requests",
3479 "refusal",
3480 "cancelled",
3481 "error",
3482];
3483
3484fn remove_idle_state_update_fields(fields: &mut BTreeMap<String, serde_json::Value>) {
3486 fields.remove("stopReason");
3487 fields.remove("usage");
3488 fields.remove("_meta");
3489}
3490
3491#[cfg(feature = "schemars")]
3492fn other_stop_reason_schema(schema: &mut Schema) {
3493 super::schema_util::reject_known_string_discriminators(
3494 schema,
3495 "stopReason",
3496 KNOWN_STOP_REASONS,
3497 );
3498}
3499
3500#[cfg(feature = "unstable_end_turn_token_usage")]
3506#[serde_as]
3507#[skip_serializing_none]
3508#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3509#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3510#[serde(rename_all = "camelCase")]
3511#[non_exhaustive]
3512pub struct Usage {
3513 pub total_tokens: u64,
3515 pub input_tokens: u64,
3517 pub output_tokens: u64,
3519 #[serde_as(deserialize_as = "DefaultOnError")]
3521 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3522 #[serde(default)]
3523 pub thought_tokens: Option<u64>,
3524 #[serde_as(deserialize_as = "DefaultOnError")]
3526 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3527 #[serde(default)]
3528 pub cached_read_tokens: Option<u64>,
3529 #[serde_as(deserialize_as = "DefaultOnError")]
3531 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3532 #[serde(default)]
3533 pub cached_write_tokens: Option<u64>,
3534 #[serde_as(deserialize_as = "DefaultOnError")]
3540 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3541 #[serde(default)]
3542 #[serde(rename = "_meta")]
3543 pub meta: Option<Meta>,
3544}
3545
3546#[cfg(feature = "unstable_end_turn_token_usage")]
3547impl Usage {
3548 #[must_use]
3550 pub fn new(total_tokens: u64, input_tokens: u64, output_tokens: u64) -> Self {
3551 Self {
3552 total_tokens,
3553 input_tokens,
3554 output_tokens,
3555 thought_tokens: None,
3556 cached_read_tokens: None,
3557 cached_write_tokens: None,
3558 meta: None,
3559 }
3560 }
3561
3562 #[must_use]
3564 pub fn thought_tokens(mut self, thought_tokens: impl IntoOption<u64>) -> Self {
3565 self.thought_tokens = thought_tokens.into_option();
3566 self
3567 }
3568
3569 #[must_use]
3571 pub fn cached_read_tokens(mut self, cached_read_tokens: impl IntoOption<u64>) -> Self {
3572 self.cached_read_tokens = cached_read_tokens.into_option();
3573 self
3574 }
3575
3576 #[must_use]
3578 pub fn cached_write_tokens(mut self, cached_write_tokens: impl IntoOption<u64>) -> Self {
3579 self.cached_write_tokens = cached_write_tokens.into_option();
3580 self
3581 }
3582
3583 #[must_use]
3589 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3590 self.meta = meta.into_option();
3591 self
3592 }
3593}
3594
3595#[cfg(feature = "unstable_llm_providers")]
3608#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3609#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3610#[serde(rename_all = "snake_case")]
3611#[non_exhaustive]
3612#[expect(clippy::doc_markdown)]
3613pub enum LlmProtocol {
3614 Anthropic,
3616 #[serde(rename = "openai")]
3618 OpenAi,
3619 Azure,
3621 Vertex,
3623 Bedrock,
3625 #[serde(untagged)]
3631 Other(String),
3632}
3633
3634#[cfg(feature = "unstable_llm_providers")]
3640#[serde_as]
3641#[skip_serializing_none]
3642#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3643#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3644#[serde(rename_all = "camelCase")]
3645#[non_exhaustive]
3646pub struct ProviderCurrentConfig {
3647 pub api_type: LlmProtocol,
3649 #[cfg_attr(feature = "schemars", schemars(url))]
3651 pub base_url: String,
3652 #[serde_as(deserialize_as = "DefaultOnError")]
3658 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3659 #[serde(default)]
3660 #[serde(rename = "_meta")]
3661 pub meta: Option<Meta>,
3662}
3663
3664#[cfg(feature = "unstable_llm_providers")]
3665impl ProviderCurrentConfig {
3666 #[must_use]
3668 pub fn new(api_type: LlmProtocol, base_url: impl Into<String>) -> Self {
3669 Self {
3670 api_type,
3671 base_url: base_url.into(),
3672 meta: None,
3673 }
3674 }
3675
3676 #[must_use]
3682 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3683 self.meta = meta.into_option();
3684 self
3685 }
3686}
3687
3688#[cfg(feature = "unstable_llm_providers")]
3694#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3695#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
3696#[serde(transparent)]
3697#[from(forward)]
3698#[non_exhaustive]
3699pub struct ProviderId(pub Arc<str>);
3700
3701#[cfg(feature = "unstable_llm_providers")]
3702impl ProviderId {
3703 #[must_use]
3705 pub fn new(id: impl Into<Self>) -> Self {
3706 id.into()
3707 }
3708}
3709
3710#[cfg(feature = "unstable_llm_providers")]
3716#[serde_as]
3717#[skip_serializing_none]
3718#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3719#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3720#[serde(rename_all = "camelCase")]
3721#[non_exhaustive]
3722pub struct ProviderInfo {
3723 pub provider_id: ProviderId,
3725 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
3727 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
3728 pub supported: Vec<LlmProtocol>,
3729 pub required: bool,
3732 #[serde(default)]
3735 pub current: Option<ProviderCurrentConfig>,
3736 #[serde_as(deserialize_as = "DefaultOnError")]
3742 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3743 #[serde(default)]
3744 #[serde(rename = "_meta")]
3745 pub meta: Option<Meta>,
3746}
3747
3748#[cfg(feature = "unstable_llm_providers")]
3749impl ProviderInfo {
3750 #[must_use]
3752 pub fn new(
3753 provider_id: impl Into<ProviderId>,
3754 supported: Vec<LlmProtocol>,
3755 required: bool,
3756 current: impl IntoOption<ProviderCurrentConfig>,
3757 ) -> Self {
3758 Self {
3759 provider_id: provider_id.into(),
3760 supported,
3761 required,
3762 current: current.into_option(),
3763 meta: None,
3764 }
3765 }
3766
3767 #[must_use]
3773 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3774 self.meta = meta.into_option();
3775 self
3776 }
3777}
3778
3779#[cfg(feature = "unstable_llm_providers")]
3780crate::serde_util::default_on_null! {
3781 #[serde_as]
3787 #[skip_serializing_none]
3788 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3789 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
3790 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_LIST_METHOD_NAME)))]
3791 #[serde(rename_all = "camelCase")]
3792 #[non_exhaustive]
3793 pub struct ListProvidersRequest {
3794 #[serde_as(deserialize_as = "DefaultOnError")]
3800 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3801 #[serde(default)]
3802 #[serde(rename = "_meta")]
3803 pub meta: Option<Meta>,
3804 }
3805}
3806
3807#[cfg(feature = "unstable_llm_providers")]
3808impl ListProvidersRequest {
3809 #[must_use]
3811 pub fn new() -> Self {
3812 Self::default()
3813 }
3814
3815 #[must_use]
3821 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3822 self.meta = meta.into_option();
3823 self
3824 }
3825}
3826
3827#[cfg(feature = "unstable_llm_providers")]
3833#[serde_as]
3834#[skip_serializing_none]
3835#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3836#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3837#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_LIST_METHOD_NAME)))]
3838#[serde(rename_all = "camelCase")]
3839#[non_exhaustive]
3840pub struct ListProvidersResponse {
3841 pub providers: Vec<ProviderInfo>,
3843 #[serde_as(deserialize_as = "DefaultOnError")]
3849 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3850 #[serde(default)]
3851 #[serde(rename = "_meta")]
3852 pub meta: Option<Meta>,
3853}
3854
3855#[cfg(feature = "unstable_llm_providers")]
3856impl ListProvidersResponse {
3857 #[must_use]
3859 pub fn new(providers: Vec<ProviderInfo>) -> Self {
3860 Self {
3861 providers,
3862 meta: None,
3863 }
3864 }
3865
3866 #[must_use]
3872 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3873 self.meta = meta.into_option();
3874 self
3875 }
3876}
3877
3878#[cfg(feature = "unstable_llm_providers")]
3886#[serde_as]
3887#[skip_serializing_none]
3888#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3889#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
3890#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_SET_METHOD_NAME)))]
3891#[serde(rename_all = "camelCase")]
3892#[non_exhaustive]
3893pub struct SetProviderRequest {
3894 pub provider_id: ProviderId,
3896 pub api_type: LlmProtocol,
3898 #[cfg_attr(feature = "schemars", schemars(url))]
3900 pub base_url: String,
3901 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
3904 pub headers: HashMap<String, String>,
3905 #[serde_as(deserialize_as = "DefaultOnError")]
3911 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3912 #[serde(default)]
3913 #[serde(rename = "_meta")]
3914 pub meta: Option<Meta>,
3915}
3916
3917#[cfg(feature = "unstable_llm_providers")]
3918impl SetProviderRequest {
3919 #[must_use]
3921 pub fn new(
3922 provider_id: impl Into<ProviderId>,
3923 api_type: LlmProtocol,
3924 base_url: impl Into<String>,
3925 ) -> Self {
3926 Self {
3927 provider_id: provider_id.into(),
3928 api_type,
3929 base_url: base_url.into(),
3930 headers: HashMap::new(),
3931 meta: None,
3932 }
3933 }
3934
3935 #[must_use]
3938 pub fn headers(mut self, headers: HashMap<String, String>) -> Self {
3939 self.headers = headers;
3940 self
3941 }
3942
3943 #[must_use]
3949 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3950 self.meta = meta.into_option();
3951 self
3952 }
3953}
3954
3955#[cfg(feature = "unstable_llm_providers")]
3956crate::serde_util::default_on_null! {
3957 #[serde_as]
3963 #[skip_serializing_none]
3964 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3965 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
3966 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_SET_METHOD_NAME)))]
3967 #[serde(rename_all = "camelCase")]
3968 #[non_exhaustive]
3969 pub struct SetProviderResponse {
3970 #[serde_as(deserialize_as = "DefaultOnError")]
3976 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3977 #[serde(default)]
3978 #[serde(rename = "_meta")]
3979 pub meta: Option<Meta>,
3980 }
3981}
3982
3983#[cfg(feature = "unstable_llm_providers")]
3984impl SetProviderResponse {
3985 #[must_use]
3987 pub fn new() -> Self {
3988 Self::default()
3989 }
3990
3991 #[must_use]
3997 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3998 self.meta = meta.into_option();
3999 self
4000 }
4001}
4002
4003#[cfg(feature = "unstable_llm_providers")]
4009#[serde_as]
4010#[skip_serializing_none]
4011#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4012#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4013#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_DISABLE_METHOD_NAME)))]
4014#[serde(rename_all = "camelCase")]
4015#[non_exhaustive]
4016pub struct DisableProviderRequest {
4017 pub provider_id: ProviderId,
4019 #[serde_as(deserialize_as = "DefaultOnError")]
4025 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4026 #[serde(default)]
4027 #[serde(rename = "_meta")]
4028 pub meta: Option<Meta>,
4029}
4030
4031#[cfg(feature = "unstable_llm_providers")]
4032impl DisableProviderRequest {
4033 #[must_use]
4035 pub fn new(provider_id: impl Into<ProviderId>) -> Self {
4036 Self {
4037 provider_id: provider_id.into(),
4038 meta: None,
4039 }
4040 }
4041
4042 #[must_use]
4048 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4049 self.meta = meta.into_option();
4050 self
4051 }
4052}
4053
4054#[cfg(feature = "unstable_llm_providers")]
4055crate::serde_util::default_on_null! {
4056 #[serde_as]
4062 #[skip_serializing_none]
4063 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4064 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
4065 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_DISABLE_METHOD_NAME)))]
4066 #[serde(rename_all = "camelCase")]
4067 #[non_exhaustive]
4068 pub struct DisableProviderResponse {
4069 #[serde_as(deserialize_as = "DefaultOnError")]
4075 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4076 #[serde(default)]
4077 #[serde(rename = "_meta")]
4078 pub meta: Option<Meta>,
4079 }
4080}
4081
4082#[cfg(feature = "unstable_llm_providers")]
4083impl DisableProviderResponse {
4084 #[must_use]
4086 pub fn new() -> Self {
4087 Self::default()
4088 }
4089
4090 #[must_use]
4096 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4097 self.meta = meta.into_option();
4098 self
4099 }
4100}
4101
4102#[serde_as]
4111#[skip_serializing_none]
4112#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4113#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4114#[serde(rename_all = "camelCase")]
4115#[non_exhaustive]
4116pub struct AgentCapabilities {
4117 #[serde_as(deserialize_as = "DefaultOnError")]
4124 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4125 #[serde(default)]
4126 pub session: Option<SessionCapabilities>,
4127 #[serde_as(deserialize_as = "DefaultOnError")]
4134 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4135 #[serde(default)]
4136 pub auth: Option<AgentAuthCapabilities>,
4137 #[cfg(feature = "unstable_llm_providers")]
4146 #[serde_as(deserialize_as = "DefaultOnError")]
4147 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4148 #[serde(default)]
4149 pub providers: Option<ProvidersCapabilities>,
4150 #[cfg(feature = "unstable_nes")]
4159 #[serde_as(deserialize_as = "DefaultOnError")]
4160 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4161 #[serde(default)]
4162 pub nes: Option<NesCapabilities>,
4163 #[cfg(feature = "unstable_nes")]
4169 #[serde_as(deserialize_as = "DefaultOnError")]
4170 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4171 #[serde(default)]
4172 pub position_encoding: Option<PositionEncodingKind>,
4173 #[serde_as(deserialize_as = "DefaultOnError")]
4179 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4180 #[serde(default)]
4181 #[serde(rename = "_meta")]
4182 pub meta: Option<Meta>,
4183}
4184
4185impl AgentCapabilities {
4186 #[must_use]
4188 pub fn new() -> Self {
4189 Self::default()
4190 }
4191
4192 #[must_use]
4199 pub fn session(mut self, session: impl IntoOption<SessionCapabilities>) -> Self {
4200 self.session = session.into_option();
4201 self
4202 }
4203
4204 #[must_use]
4208 pub fn auth(mut self, auth: impl IntoOption<AgentAuthCapabilities>) -> Self {
4209 self.auth = auth.into_option();
4210 self
4211 }
4212
4213 #[cfg(feature = "unstable_llm_providers")]
4219 #[must_use]
4220 pub fn providers(mut self, providers: impl IntoOption<ProvidersCapabilities>) -> Self {
4221 self.providers = providers.into_option();
4222 self
4223 }
4224
4225 #[cfg(feature = "unstable_nes")]
4231 #[must_use]
4232 pub fn nes(mut self, nes: impl IntoOption<NesCapabilities>) -> Self {
4233 self.nes = nes.into_option();
4234 self
4235 }
4236
4237 #[cfg(feature = "unstable_nes")]
4241 #[must_use]
4242 pub fn position_encoding(
4243 mut self,
4244 position_encoding: impl IntoOption<PositionEncodingKind>,
4245 ) -> Self {
4246 self.position_encoding = position_encoding.into_option();
4247 self
4248 }
4249
4250 #[must_use]
4256 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4257 self.meta = meta.into_option();
4258 self
4259 }
4260}
4261
4262#[cfg(feature = "unstable_llm_providers")]
4270#[serde_as]
4271#[skip_serializing_none]
4272#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4273#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4274#[non_exhaustive]
4275pub struct ProvidersCapabilities {
4276 #[serde_as(deserialize_as = "DefaultOnError")]
4282 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4283 #[serde(default)]
4284 #[serde(rename = "_meta")]
4285 pub meta: Option<Meta>,
4286}
4287
4288#[cfg(feature = "unstable_llm_providers")]
4289impl ProvidersCapabilities {
4290 #[must_use]
4292 pub fn new() -> Self {
4293 Self::default()
4294 }
4295
4296 #[must_use]
4302 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4303 self.meta = meta.into_option();
4304 self
4305 }
4306}
4307
4308#[serde_as]
4320#[skip_serializing_none]
4321#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4322#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4323#[serde(rename_all = "camelCase")]
4324#[non_exhaustive]
4325pub struct SessionCapabilities {
4326 #[serde_as(deserialize_as = "DefaultOnError")]
4332 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4333 #[serde(default)]
4334 pub prompt: Option<PromptCapabilities>,
4335 #[serde_as(deserialize_as = "DefaultOnError")]
4340 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4341 #[serde(default)]
4342 pub mcp: Option<McpCapabilities>,
4343 #[serde_as(deserialize_as = "DefaultOnError")]
4348 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4349 #[serde(default)]
4350 pub delete: Option<SessionDeleteCapabilities>,
4351 #[serde_as(deserialize_as = "DefaultOnError")]
4360 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4361 #[serde(default)]
4362 pub additional_directories: Option<SessionAdditionalDirectoriesCapabilities>,
4363 #[cfg(feature = "unstable_session_fork")]
4372 #[serde_as(deserialize_as = "DefaultOnError")]
4373 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4374 #[serde(default)]
4375 pub fork: Option<SessionForkCapabilities>,
4376 #[serde_as(deserialize_as = "DefaultOnError")]
4382 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4383 #[serde(default)]
4384 #[serde(rename = "_meta")]
4385 pub meta: Option<Meta>,
4386}
4387
4388impl SessionCapabilities {
4389 #[must_use]
4391 pub fn new() -> Self {
4392 Self::default()
4393 }
4394
4395 #[must_use]
4401 pub fn prompt(mut self, prompt: impl IntoOption<PromptCapabilities>) -> Self {
4402 self.prompt = prompt.into_option();
4403 self
4404 }
4405
4406 #[must_use]
4411 pub fn mcp(mut self, mcp: impl IntoOption<McpCapabilities>) -> Self {
4412 self.mcp = mcp.into_option();
4413 self
4414 }
4415
4416 #[must_use]
4421 pub fn delete(mut self, delete: impl IntoOption<SessionDeleteCapabilities>) -> Self {
4422 self.delete = delete.into_option();
4423 self
4424 }
4425
4426 #[must_use]
4435 pub fn additional_directories(
4436 mut self,
4437 additional_directories: impl IntoOption<SessionAdditionalDirectoriesCapabilities>,
4438 ) -> Self {
4439 self.additional_directories = additional_directories.into_option();
4440 self
4441 }
4442
4443 #[cfg(feature = "unstable_session_fork")]
4444 #[must_use]
4449 pub fn fork(mut self, fork: impl IntoOption<SessionForkCapabilities>) -> Self {
4450 self.fork = fork.into_option();
4451 self
4452 }
4453
4454 #[must_use]
4460 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4461 self.meta = meta.into_option();
4462 self
4463 }
4464}
4465
4466#[serde_as]
4470#[skip_serializing_none]
4471#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4472#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4473#[non_exhaustive]
4474pub struct SessionDeleteCapabilities {
4475 #[serde_as(deserialize_as = "DefaultOnError")]
4481 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4482 #[serde(default)]
4483 #[serde(rename = "_meta")]
4484 pub meta: Option<Meta>,
4485}
4486
4487impl SessionDeleteCapabilities {
4488 #[must_use]
4490 pub fn new() -> Self {
4491 Self::default()
4492 }
4493
4494 #[must_use]
4500 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4501 self.meta = meta.into_option();
4502 self
4503 }
4504}
4505
4506#[serde_as]
4513#[skip_serializing_none]
4514#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4515#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4516#[non_exhaustive]
4517pub struct SessionAdditionalDirectoriesCapabilities {
4518 #[serde_as(deserialize_as = "DefaultOnError")]
4524 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4525 #[serde(default)]
4526 #[serde(rename = "_meta")]
4527 pub meta: Option<Meta>,
4528}
4529
4530impl SessionAdditionalDirectoriesCapabilities {
4531 #[must_use]
4533 pub fn new() -> Self {
4534 Self::default()
4535 }
4536
4537 #[must_use]
4543 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4544 self.meta = meta.into_option();
4545 self
4546 }
4547}
4548
4549#[cfg(feature = "unstable_session_fork")]
4557#[serde_as]
4558#[skip_serializing_none]
4559#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4560#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4561#[non_exhaustive]
4562pub struct SessionForkCapabilities {
4563 #[serde_as(deserialize_as = "DefaultOnError")]
4569 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4570 #[serde(default)]
4571 #[serde(rename = "_meta")]
4572 pub meta: Option<Meta>,
4573}
4574
4575#[cfg(feature = "unstable_session_fork")]
4576impl SessionForkCapabilities {
4577 #[must_use]
4579 pub fn new() -> Self {
4580 Self::default()
4581 }
4582
4583 #[must_use]
4589 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4590 self.meta = meta.into_option();
4591 self
4592 }
4593}
4594
4595#[serde_as]
4608#[skip_serializing_none]
4609#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4610#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4611#[serde(rename_all = "camelCase")]
4612#[non_exhaustive]
4613pub struct PromptCapabilities {
4614 #[serde_as(deserialize_as = "DefaultOnError")]
4619 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4620 #[serde(default)]
4621 pub image: Option<PromptImageCapabilities>,
4622 #[serde_as(deserialize_as = "DefaultOnError")]
4627 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4628 #[serde(default)]
4629 pub audio: Option<PromptAudioCapabilities>,
4630 #[serde_as(deserialize_as = "DefaultOnError")]
4638 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4639 #[serde(default)]
4640 pub embedded_context: Option<PromptEmbeddedContextCapabilities>,
4641 #[serde_as(deserialize_as = "DefaultOnError")]
4647 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4648 #[serde(default)]
4649 #[serde(rename = "_meta")]
4650 pub meta: Option<Meta>,
4651}
4652
4653impl PromptCapabilities {
4654 #[must_use]
4656 pub fn new() -> Self {
4657 Self::default()
4658 }
4659
4660 #[must_use]
4665 pub fn image(mut self, image: impl IntoOption<PromptImageCapabilities>) -> Self {
4666 self.image = image.into_option();
4667 self
4668 }
4669
4670 #[must_use]
4675 pub fn audio(mut self, audio: impl IntoOption<PromptAudioCapabilities>) -> Self {
4676 self.audio = audio.into_option();
4677 self
4678 }
4679
4680 #[must_use]
4688 pub fn embedded_context(
4689 mut self,
4690 embedded_context: impl IntoOption<PromptEmbeddedContextCapabilities>,
4691 ) -> Self {
4692 self.embedded_context = embedded_context.into_option();
4693 self
4694 }
4695
4696 #[must_use]
4702 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4703 self.meta = meta.into_option();
4704 self
4705 }
4706}
4707
4708#[serde_as]
4712#[skip_serializing_none]
4713#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4714#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4715#[non_exhaustive]
4716pub struct PromptImageCapabilities {
4717 #[serde_as(deserialize_as = "DefaultOnError")]
4723 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4724 #[serde(default)]
4725 #[serde(rename = "_meta")]
4726 pub meta: Option<Meta>,
4727}
4728
4729impl PromptImageCapabilities {
4730 #[must_use]
4732 pub fn new() -> Self {
4733 Self::default()
4734 }
4735
4736 #[must_use]
4742 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4743 self.meta = meta.into_option();
4744 self
4745 }
4746}
4747
4748#[serde_as]
4752#[skip_serializing_none]
4753#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4754#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4755#[non_exhaustive]
4756pub struct PromptAudioCapabilities {
4757 #[serde_as(deserialize_as = "DefaultOnError")]
4763 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4764 #[serde(default)]
4765 #[serde(rename = "_meta")]
4766 pub meta: Option<Meta>,
4767}
4768
4769impl PromptAudioCapabilities {
4770 #[must_use]
4772 pub fn new() -> Self {
4773 Self::default()
4774 }
4775
4776 #[must_use]
4782 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4783 self.meta = meta.into_option();
4784 self
4785 }
4786}
4787
4788#[serde_as]
4792#[skip_serializing_none]
4793#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4794#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4795#[non_exhaustive]
4796pub struct PromptEmbeddedContextCapabilities {
4797 #[serde_as(deserialize_as = "DefaultOnError")]
4803 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4804 #[serde(default)]
4805 #[serde(rename = "_meta")]
4806 pub meta: Option<Meta>,
4807}
4808
4809impl PromptEmbeddedContextCapabilities {
4810 #[must_use]
4812 pub fn new() -> Self {
4813 Self::default()
4814 }
4815
4816 #[must_use]
4822 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4823 self.meta = meta.into_option();
4824 self
4825 }
4826}
4827
4828#[serde_as]
4830#[skip_serializing_none]
4831#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4832#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4833#[serde(rename_all = "camelCase")]
4834#[non_exhaustive]
4835pub struct McpCapabilities {
4836 #[serde_as(deserialize_as = "DefaultOnError")]
4841 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4842 #[serde(default)]
4843 pub stdio: Option<McpStdioCapabilities>,
4844 #[serde_as(deserialize_as = "DefaultOnError")]
4849 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4850 #[serde(default)]
4851 pub http: Option<McpHttpCapabilities>,
4852 #[cfg(feature = "unstable_mcp_over_acp")]
4861 #[serde_as(deserialize_as = "DefaultOnError")]
4862 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4863 #[serde(default)]
4864 pub acp: Option<McpAcpCapabilities>,
4865 #[serde_as(deserialize_as = "DefaultOnError")]
4871 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4872 #[serde(default)]
4873 #[serde(rename = "_meta")]
4874 pub meta: Option<Meta>,
4875}
4876
4877impl McpCapabilities {
4878 #[must_use]
4880 pub fn new() -> Self {
4881 Self::default()
4882 }
4883
4884 #[must_use]
4889 pub fn stdio(mut self, stdio: impl IntoOption<McpStdioCapabilities>) -> Self {
4890 self.stdio = stdio.into_option();
4891 self
4892 }
4893
4894 #[must_use]
4899 pub fn http(mut self, http: impl IntoOption<McpHttpCapabilities>) -> Self {
4900 self.http = http.into_option();
4901 self
4902 }
4903
4904 #[cfg(feature = "unstable_mcp_over_acp")]
4910 #[must_use]
4914 pub fn acp(mut self, acp: impl IntoOption<McpAcpCapabilities>) -> Self {
4915 self.acp = acp.into_option();
4916 self
4917 }
4918
4919 #[must_use]
4925 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4926 self.meta = meta.into_option();
4927 self
4928 }
4929}
4930
4931#[serde_as]
4935#[skip_serializing_none]
4936#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4937#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4938#[non_exhaustive]
4939pub struct McpStdioCapabilities {
4940 #[serde_as(deserialize_as = "DefaultOnError")]
4946 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4947 #[serde(default)]
4948 #[serde(rename = "_meta")]
4949 pub meta: Option<Meta>,
4950}
4951
4952impl McpStdioCapabilities {
4953 #[must_use]
4955 pub fn new() -> Self {
4956 Self::default()
4957 }
4958
4959 #[must_use]
4965 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4966 self.meta = meta.into_option();
4967 self
4968 }
4969}
4970
4971#[serde_as]
4975#[skip_serializing_none]
4976#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4977#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4978#[non_exhaustive]
4979pub struct McpHttpCapabilities {
4980 #[serde_as(deserialize_as = "DefaultOnError")]
4986 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4987 #[serde(default)]
4988 #[serde(rename = "_meta")]
4989 pub meta: Option<Meta>,
4990}
4991
4992impl McpHttpCapabilities {
4993 #[must_use]
4995 pub fn new() -> Self {
4996 Self::default()
4997 }
4998
4999 #[must_use]
5005 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5006 self.meta = meta.into_option();
5007 self
5008 }
5009}
5010
5011#[cfg(feature = "unstable_mcp_over_acp")]
5019#[serde_as]
5020#[skip_serializing_none]
5021#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5022#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5023#[non_exhaustive]
5024pub struct McpAcpCapabilities {
5025 #[serde_as(deserialize_as = "DefaultOnError")]
5031 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5032 #[serde(default)]
5033 #[serde(rename = "_meta")]
5034 pub meta: Option<Meta>,
5035}
5036
5037#[cfg(feature = "unstable_mcp_over_acp")]
5038impl McpAcpCapabilities {
5039 #[must_use]
5041 pub fn new() -> Self {
5042 Self::default()
5043 }
5044
5045 #[must_use]
5051 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5052 self.meta = meta.into_option();
5053 self
5054 }
5055}
5056
5057#[serde_as]
5061#[skip_serializing_none]
5062#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5063#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5064#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_CANCEL_METHOD_NAME)))]
5065#[serde(rename_all = "camelCase")]
5066#[non_exhaustive]
5067pub struct CancelSessionNotification {
5068 pub session_id: SessionId,
5070 #[serde_as(deserialize_as = "DefaultOnError")]
5076 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5077 #[serde(default)]
5078 #[serde(rename = "_meta")]
5079 pub meta: Option<Meta>,
5080}
5081
5082impl CancelSessionNotification {
5083 #[must_use]
5085 pub fn new(session_id: impl Into<SessionId>) -> Self {
5086 Self {
5087 session_id: session_id.into(),
5088 meta: None,
5089 }
5090 }
5091
5092 #[must_use]
5098 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5099 self.meta = meta.into_option();
5100 self
5101 }
5102}
5103
5104#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5110#[non_exhaustive]
5111pub struct AgentMethodNames {
5112 pub initialize: &'static str,
5114 pub auth_login: &'static str,
5116 #[cfg(feature = "unstable_llm_providers")]
5118 pub providers_list: &'static str,
5119 #[cfg(feature = "unstable_llm_providers")]
5121 pub providers_set: &'static str,
5122 #[cfg(feature = "unstable_llm_providers")]
5124 pub providers_disable: &'static str,
5125 pub session_new: &'static str,
5127 pub session_set_config_option: &'static str,
5129 pub session_prompt: &'static str,
5131 pub session_cancel: &'static str,
5133 #[cfg(feature = "unstable_mcp_over_acp")]
5135 pub mcp_message: &'static str,
5136 pub session_list: &'static str,
5138 pub session_delete: &'static str,
5140 #[cfg(feature = "unstable_session_fork")]
5142 pub session_fork: &'static str,
5143 pub session_resume: &'static str,
5145 pub session_close: &'static str,
5147 pub auth_logout: &'static str,
5149 #[cfg(feature = "unstable_nes")]
5151 pub nes_start: &'static str,
5152 #[cfg(feature = "unstable_nes")]
5154 pub nes_suggest: &'static str,
5155 #[cfg(feature = "unstable_nes")]
5157 pub nes_accept: &'static str,
5158 #[cfg(feature = "unstable_nes")]
5160 pub nes_reject: &'static str,
5161 #[cfg(feature = "unstable_nes")]
5163 pub nes_close: &'static str,
5164 #[cfg(feature = "unstable_nes")]
5166 pub document_did_open: &'static str,
5167 #[cfg(feature = "unstable_nes")]
5169 pub document_did_change: &'static str,
5170 #[cfg(feature = "unstable_nes")]
5172 pub document_did_close: &'static str,
5173 #[cfg(feature = "unstable_nes")]
5175 pub document_did_save: &'static str,
5176 #[cfg(feature = "unstable_nes")]
5178 pub document_did_focus: &'static str,
5179}
5180
5181pub const AGENT_METHOD_NAMES: AgentMethodNames = AgentMethodNames {
5183 initialize: INITIALIZE_METHOD_NAME,
5184 auth_login: AUTH_LOGIN_METHOD_NAME,
5185 #[cfg(feature = "unstable_llm_providers")]
5186 providers_list: PROVIDERS_LIST_METHOD_NAME,
5187 #[cfg(feature = "unstable_llm_providers")]
5188 providers_set: PROVIDERS_SET_METHOD_NAME,
5189 #[cfg(feature = "unstable_llm_providers")]
5190 providers_disable: PROVIDERS_DISABLE_METHOD_NAME,
5191 session_new: SESSION_NEW_METHOD_NAME,
5192 session_set_config_option: SESSION_SET_CONFIG_OPTION_METHOD_NAME,
5193 session_prompt: SESSION_PROMPT_METHOD_NAME,
5194 session_cancel: SESSION_CANCEL_METHOD_NAME,
5195 #[cfg(feature = "unstable_mcp_over_acp")]
5196 mcp_message: MCP_MESSAGE_METHOD_NAME,
5197 session_list: SESSION_LIST_METHOD_NAME,
5198 session_delete: SESSION_DELETE_METHOD_NAME,
5199 #[cfg(feature = "unstable_session_fork")]
5200 session_fork: SESSION_FORK_METHOD_NAME,
5201 session_resume: SESSION_RESUME_METHOD_NAME,
5202 session_close: SESSION_CLOSE_METHOD_NAME,
5203 auth_logout: AUTH_LOGOUT_METHOD_NAME,
5204 #[cfg(feature = "unstable_nes")]
5205 nes_start: NES_START_METHOD_NAME,
5206 #[cfg(feature = "unstable_nes")]
5207 nes_suggest: NES_SUGGEST_METHOD_NAME,
5208 #[cfg(feature = "unstable_nes")]
5209 nes_accept: NES_ACCEPT_METHOD_NAME,
5210 #[cfg(feature = "unstable_nes")]
5211 nes_reject: NES_REJECT_METHOD_NAME,
5212 #[cfg(feature = "unstable_nes")]
5213 nes_close: NES_CLOSE_METHOD_NAME,
5214 #[cfg(feature = "unstable_nes")]
5215 document_did_open: DOCUMENT_DID_OPEN_METHOD_NAME,
5216 #[cfg(feature = "unstable_nes")]
5217 document_did_change: DOCUMENT_DID_CHANGE_METHOD_NAME,
5218 #[cfg(feature = "unstable_nes")]
5219 document_did_close: DOCUMENT_DID_CLOSE_METHOD_NAME,
5220 #[cfg(feature = "unstable_nes")]
5221 document_did_save: DOCUMENT_DID_SAVE_METHOD_NAME,
5222 #[cfg(feature = "unstable_nes")]
5223 document_did_focus: DOCUMENT_DID_FOCUS_METHOD_NAME,
5224};
5225
5226pub(crate) const INITIALIZE_METHOD_NAME: &str = "initialize";
5228pub(crate) const AUTH_LOGIN_METHOD_NAME: &str = "auth/login";
5230#[cfg(feature = "unstable_llm_providers")]
5232pub(crate) const PROVIDERS_LIST_METHOD_NAME: &str = "providers/list";
5233#[cfg(feature = "unstable_llm_providers")]
5235pub(crate) const PROVIDERS_SET_METHOD_NAME: &str = "providers/set";
5236#[cfg(feature = "unstable_llm_providers")]
5238pub(crate) const PROVIDERS_DISABLE_METHOD_NAME: &str = "providers/disable";
5239pub(crate) const SESSION_NEW_METHOD_NAME: &str = "session/new";
5241pub(crate) const SESSION_SET_CONFIG_OPTION_METHOD_NAME: &str = "session/set_config_option";
5243pub(crate) const SESSION_PROMPT_METHOD_NAME: &str = "session/prompt";
5245pub(crate) const SESSION_CANCEL_METHOD_NAME: &str = "session/cancel";
5247pub(crate) const SESSION_LIST_METHOD_NAME: &str = "session/list";
5249pub(crate) const SESSION_DELETE_METHOD_NAME: &str = "session/delete";
5251#[cfg(feature = "unstable_session_fork")]
5253pub(crate) const SESSION_FORK_METHOD_NAME: &str = "session/fork";
5254pub(crate) const SESSION_RESUME_METHOD_NAME: &str = "session/resume";
5256pub(crate) const SESSION_CLOSE_METHOD_NAME: &str = "session/close";
5258pub(crate) const AUTH_LOGOUT_METHOD_NAME: &str = "auth/logout";
5260
5261#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5268#[derive(Clone, Debug, Serialize, Deserialize)]
5269#[serde(untagged)]
5270#[cfg_attr(feature = "schemars", schemars(inline))]
5271#[non_exhaustive]
5272pub enum ClientRequest {
5273 InitializeRequest(Box<InitializeRequest>),
5284 LoginAuthRequest(Box<LoginAuthRequest>),
5299 #[cfg(feature = "unstable_llm_providers")]
5305 ListProvidersRequest(Box<ListProvidersRequest>),
5306 #[cfg(feature = "unstable_llm_providers")]
5312 SetProviderRequest(Box<SetProviderRequest>),
5313 #[cfg(feature = "unstable_llm_providers")]
5319 DisableProviderRequest(Box<DisableProviderRequest>),
5320 LogoutAuthRequest(Box<LogoutAuthRequest>),
5330 NewSessionRequest(Box<NewSessionRequest>),
5343 ListSessionsRequest(Box<ListSessionsRequest>),
5347 DeleteSessionRequest(Box<DeleteSessionRequest>),
5351 #[cfg(feature = "unstable_session_fork")]
5352 ForkSessionRequest(Box<ForkSessionRequest>),
5364 ResumeSessionRequest(Box<ResumeSessionRequest>),
5370 CloseSessionRequest(Box<CloseSessionRequest>),
5375 SetSessionConfigOptionRequest(Box<SetSessionConfigOptionRequest>),
5377 PromptRequest(Box<PromptRequest>),
5393 #[cfg(feature = "unstable_nes")]
5394 StartNesRequest(Box<StartNesRequest>),
5400 #[cfg(feature = "unstable_nes")]
5401 SuggestNesRequest(Box<SuggestNesRequest>),
5407 #[cfg(feature = "unstable_nes")]
5408 CloseNesRequest(Box<CloseNesRequest>),
5417 ExtMethodRequest(Box<ExtRequest>),
5424}
5425
5426impl ClientRequest {
5427 #[must_use]
5429 pub fn method(&self) -> &str {
5430 match self {
5431 Self::InitializeRequest(_) => AGENT_METHOD_NAMES.initialize,
5432 Self::LoginAuthRequest(_) => AGENT_METHOD_NAMES.auth_login,
5433 #[cfg(feature = "unstable_llm_providers")]
5434 Self::ListProvidersRequest(_) => AGENT_METHOD_NAMES.providers_list,
5435 #[cfg(feature = "unstable_llm_providers")]
5436 Self::SetProviderRequest(_) => AGENT_METHOD_NAMES.providers_set,
5437 #[cfg(feature = "unstable_llm_providers")]
5438 Self::DisableProviderRequest(_) => AGENT_METHOD_NAMES.providers_disable,
5439 Self::LogoutAuthRequest(_) => AGENT_METHOD_NAMES.auth_logout,
5440 Self::NewSessionRequest(_) => AGENT_METHOD_NAMES.session_new,
5441 Self::ListSessionsRequest(_) => AGENT_METHOD_NAMES.session_list,
5442 Self::DeleteSessionRequest(_) => AGENT_METHOD_NAMES.session_delete,
5443 #[cfg(feature = "unstable_session_fork")]
5444 Self::ForkSessionRequest(_) => AGENT_METHOD_NAMES.session_fork,
5445 Self::ResumeSessionRequest(_) => AGENT_METHOD_NAMES.session_resume,
5446 Self::CloseSessionRequest(_) => AGENT_METHOD_NAMES.session_close,
5447 Self::SetSessionConfigOptionRequest(_) => AGENT_METHOD_NAMES.session_set_config_option,
5448 Self::PromptRequest(_) => AGENT_METHOD_NAMES.session_prompt,
5449 #[cfg(feature = "unstable_nes")]
5450 Self::StartNesRequest(_) => AGENT_METHOD_NAMES.nes_start,
5451 #[cfg(feature = "unstable_nes")]
5452 Self::SuggestNesRequest(_) => AGENT_METHOD_NAMES.nes_suggest,
5453 #[cfg(feature = "unstable_nes")]
5454 Self::CloseNesRequest(_) => AGENT_METHOD_NAMES.nes_close,
5455 Self::ExtMethodRequest(ext_request) => &ext_request.method,
5456 }
5457 }
5458}
5459
5460#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5467#[derive(Clone, Debug, Serialize, Deserialize)]
5468#[serde(untagged)]
5469#[cfg_attr(feature = "schemars", schemars(inline))]
5470#[non_exhaustive]
5471pub enum AgentResponse {
5472 InitializeResponse(Box<InitializeResponse>),
5474 LoginAuthResponse(#[serde(default)] Box<LoginAuthResponse>),
5476 #[cfg(feature = "unstable_llm_providers")]
5478 ListProvidersResponse(Box<ListProvidersResponse>),
5479 #[cfg(feature = "unstable_llm_providers")]
5481 SetProviderResponse(#[serde(default)] Box<SetProviderResponse>),
5482 #[cfg(feature = "unstable_llm_providers")]
5484 DisableProviderResponse(#[serde(default)] Box<DisableProviderResponse>),
5485 LogoutAuthResponse(#[serde(default)] Box<LogoutAuthResponse>),
5487 NewSessionResponse(Box<NewSessionResponse>),
5489 ListSessionsResponse(Box<ListSessionsResponse>),
5491 DeleteSessionResponse(#[serde(default)] Box<DeleteSessionResponse>),
5493 #[cfg(feature = "unstable_session_fork")]
5495 ForkSessionResponse(Box<ForkSessionResponse>),
5496 ResumeSessionResponse(#[serde(default)] Box<ResumeSessionResponse>),
5498 CloseSessionResponse(#[serde(default)] Box<CloseSessionResponse>),
5500 SetSessionConfigOptionResponse(Box<SetSessionConfigOptionResponse>),
5502 PromptResponse(Box<PromptResponse>),
5504 #[cfg(feature = "unstable_nes")]
5506 StartNesResponse(Box<StartNesResponse>),
5507 #[cfg(feature = "unstable_nes")]
5509 SuggestNesResponse(Box<SuggestNesResponse>),
5510 #[cfg(feature = "unstable_nes")]
5512 CloseNesResponse(#[serde(default)] Box<CloseNesResponse>),
5513 ExtMethodResponse(Box<ExtResponse>),
5515}
5516
5517#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5524#[derive(Clone, Debug, Serialize, Deserialize)]
5525#[serde(untagged)]
5526#[cfg_attr(feature = "schemars", schemars(inline))]
5527#[non_exhaustive]
5528pub enum ClientNotification {
5529 CancelSessionNotification(Box<CancelSessionNotification>),
5543 #[cfg(feature = "unstable_nes")]
5544 DidOpenDocumentNotification(Box<DidOpenDocumentNotification>),
5548 #[cfg(feature = "unstable_nes")]
5549 DidChangeDocumentNotification(Box<DidChangeDocumentNotification>),
5553 #[cfg(feature = "unstable_nes")]
5554 DidCloseDocumentNotification(Box<DidCloseDocumentNotification>),
5558 #[cfg(feature = "unstable_nes")]
5559 DidSaveDocumentNotification(Box<DidSaveDocumentNotification>),
5563 #[cfg(feature = "unstable_nes")]
5564 DidFocusDocumentNotification(Box<DidFocusDocumentNotification>),
5568 #[cfg(feature = "unstable_nes")]
5569 AcceptNesNotification(Box<AcceptNesNotification>),
5573 #[cfg(feature = "unstable_nes")]
5574 RejectNesNotification(Box<RejectNesNotification>),
5578 #[cfg(feature = "unstable_mcp_over_acp")]
5584 MessageMcpNotification(Box<MessageMcpNotification>),
5585 ExtNotification(Box<ExtNotification>),
5592}
5593
5594impl ClientNotification {
5595 #[must_use]
5597 pub fn method(&self) -> &str {
5598 match self {
5599 Self::CancelSessionNotification(_) => AGENT_METHOD_NAMES.session_cancel,
5600 #[cfg(feature = "unstable_nes")]
5601 Self::DidOpenDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_open,
5602 #[cfg(feature = "unstable_nes")]
5603 Self::DidChangeDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_change,
5604 #[cfg(feature = "unstable_nes")]
5605 Self::DidCloseDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_close,
5606 #[cfg(feature = "unstable_nes")]
5607 Self::DidSaveDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_save,
5608 #[cfg(feature = "unstable_nes")]
5609 Self::DidFocusDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_focus,
5610 #[cfg(feature = "unstable_nes")]
5611 Self::AcceptNesNotification(_) => AGENT_METHOD_NAMES.nes_accept,
5612 #[cfg(feature = "unstable_nes")]
5613 Self::RejectNesNotification(_) => AGENT_METHOD_NAMES.nes_reject,
5614 #[cfg(feature = "unstable_mcp_over_acp")]
5615 Self::MessageMcpNotification(_) => AGENT_METHOD_NAMES.mcp_message,
5616 Self::ExtNotification(ext_notification) => &ext_notification.method,
5617 }
5618 }
5619}
5620
5621#[cfg(test)]
5622mod test_serialization {
5623 use std::path::PathBuf;
5624
5625 use super::*;
5626 use serde_json::json;
5627
5628 fn test_meta() -> Meta {
5629 json!({ "source": "test" }).as_object().unwrap().clone()
5630 }
5631
5632 fn serialized_meta_key_count(value: &impl serde::Serialize) -> usize {
5633 serde_json::to_string(value)
5634 .unwrap()
5635 .matches("\"_meta\"")
5636 .count()
5637 }
5638
5639 #[test]
5640 fn prompt_response_without_metadata_round_trips() {
5641 let response = PromptResponse::new(MessageId::new("message-1"));
5642 let serialized = serde_json::to_value(&response).unwrap();
5643 assert_eq!(serialized, json!({ "messageId": "message-1" }));
5644 assert_eq!(
5645 serde_json::from_value::<PromptResponse>(serialized).unwrap(),
5646 response
5647 );
5648 assert_eq!(
5649 serde_json::from_value::<PromptResponse>(json!({
5650 "messageId": "message-1",
5651 "_meta": null
5652 }))
5653 .unwrap(),
5654 response
5655 );
5656 }
5657
5658 #[test]
5659 fn prompt_response_requires_message_id_and_preserves_metadata_tolerance() {
5660 let response = PromptResponse::new("message-1").meta(test_meta());
5661 let serialized = serde_json::to_value(&response).unwrap();
5662 assert_eq!(
5663 serialized,
5664 json!({
5665 "messageId": "message-1",
5666 "_meta": { "source": "test" }
5667 })
5668 );
5669
5670 let deserialized: PromptResponse = serde_json::from_value(serialized).unwrap();
5671 assert_eq!(deserialized.message_id, MessageId::new("message-1"));
5672 assert_eq!(deserialized.meta, Some(test_meta()));
5673
5674 let malformed_meta: PromptResponse = serde_json::from_value(json!({
5675 "messageId": "message-2",
5676 "_meta": false
5677 }))
5678 .unwrap();
5679 assert_eq!(malformed_meta.message_id, MessageId::new("message-2"));
5680 assert_eq!(malformed_meta.meta, None);
5681
5682 for invalid in [
5683 json!({}),
5684 json!({ "messageId": null }),
5685 json!({ "messageId": 1 }),
5686 json!({ "messageId": false }),
5687 json!({ "messageId": {} }),
5688 json!({ "messageId": [] }),
5689 ] {
5690 assert!(
5691 serde_json::from_value::<PromptResponse>(invalid).is_err(),
5692 "missing, null, and non-string message IDs must be rejected"
5693 );
5694 }
5695 }
5696
5697 #[cfg(feature = "schemars")]
5698 #[test]
5699 fn prompt_response_schema_requires_non_null_string_message_id_reference() {
5700 let schema = serde_json::to_value(schemars::schema_for!(PromptResponse)).unwrap();
5701
5702 assert_eq!(schema["required"], json!(["messageId"]));
5703 assert_eq!(
5704 schema["properties"]["messageId"]["$ref"],
5705 "#/$defs/MessageId"
5706 );
5707 assert_eq!(schema["$defs"]["MessageId"]["type"], "string");
5708 }
5709
5710 #[test]
5711 fn test_initialize_capabilities_default_on_malformed_values() {
5712 let request: InitializeRequest = serde_json::from_value(json!({
5713 "protocolVersion": 2,
5714 "capabilities": false,
5715 "info": {
5716 "name": "client",
5717 "version": "1.0.0"
5718 }
5719 }))
5720 .unwrap();
5721 assert_eq!(request.capabilities, ClientCapabilities::default());
5722
5723 let response: InitializeResponse = serde_json::from_value(json!({
5724 "protocolVersion": 2,
5725 "capabilities": false,
5726 "info": {
5727 "name": "agent",
5728 "version": "1.0.0"
5729 }
5730 }))
5731 .unwrap();
5732 assert_eq!(response.capabilities, AgentCapabilities::default());
5733 }
5734
5735 #[test]
5736 fn test_agent_capabilities_default_on_malformed_values() {
5737 let capabilities: AgentCapabilities = serde_json::from_value(json!({
5738 "session": false,
5739 "auth": false
5740 }))
5741 .unwrap();
5742
5743 assert!(capabilities.session.is_none());
5744 assert_eq!(capabilities.auth, None);
5745 }
5746
5747 #[test]
5748 fn test_mcp_server_stdio_serialization() {
5749 let server = McpServer::Stdio(
5750 McpServerStdio::new("test-server", "/usr/bin/server")
5751 .args(vec!["--port".to_string(), "3000".to_string()])
5752 .env(vec![EnvVariable::new("API_KEY", "secret123")]),
5753 );
5754
5755 let json = serde_json::to_value(&server).unwrap();
5756 assert_eq!(
5757 json,
5758 json!({
5759 "type": "stdio",
5760 "name": "test-server",
5761 "command": "/usr/bin/server",
5762 "args": ["--port", "3000"],
5763 "env": [
5764 {
5765 "name": "API_KEY",
5766 "value": "secret123"
5767 }
5768 ]
5769 })
5770 );
5771
5772 let deserialized: McpServer = serde_json::from_value(json).unwrap();
5773 match deserialized {
5774 McpServer::Stdio(McpServerStdio {
5775 name,
5776 command,
5777 args,
5778 env,
5779 meta: _,
5780 }) => {
5781 assert_eq!(name, "test-server");
5782 assert_eq!(command, AbsolutePath::new("/usr/bin/server"));
5783 assert_eq!(args, vec!["--port", "3000"]);
5784 assert_eq!(env.len(), 1);
5785 assert_eq!(env[0].name, "API_KEY");
5786 assert_eq!(env[0].value, "secret123");
5787 }
5788 _ => panic!("Expected Stdio variant"),
5789 }
5790 }
5791
5792 #[test]
5793 fn test_mcp_server_empty_arrays_are_optional() {
5794 let stdio = McpServer::Stdio(McpServerStdio::new("test-server", "/usr/bin/server"));
5795 assert_eq!(
5796 serde_json::to_value(&stdio).unwrap(),
5797 json!({
5798 "type": "stdio",
5799 "name": "test-server",
5800 "command": "/usr/bin/server"
5801 })
5802 );
5803
5804 let McpServer::Stdio(McpServerStdio { args, env, .. }) =
5805 serde_json::from_value::<McpServer>(json!({
5806 "type": "stdio",
5807 "name": "test-server",
5808 "command": "/usr/bin/server"
5809 }))
5810 .unwrap()
5811 else {
5812 panic!("Expected Stdio variant");
5813 };
5814 assert_eq!(args, Vec::<String>::new());
5815 assert_eq!(env, []);
5816
5817 let http = McpServer::Http(McpServerHttp::new("http-server", "https://api.example.com"));
5818 assert_eq!(
5819 serde_json::to_value(&http).unwrap(),
5820 json!({
5821 "type": "http",
5822 "name": "http-server",
5823 "url": "https://api.example.com"
5824 })
5825 );
5826
5827 let McpServer::Http(McpServerHttp { headers, .. }) =
5828 serde_json::from_value::<McpServer>(json!({
5829 "type": "http",
5830 "name": "http-server",
5831 "url": "https://api.example.com"
5832 }))
5833 .unwrap()
5834 else {
5835 panic!("Expected Http variant");
5836 };
5837 assert_eq!(headers, []);
5838 }
5839
5840 #[test]
5841 fn test_mcp_server_unknown_transport_serialization() {
5842 let json = json!({
5843 "type": "websocket",
5844 "name": "future-server",
5845 "url": "wss://example.com/mcp",
5846 "protocolVersion": "2026-01-01"
5847 });
5848
5849 let deserialized: McpServer = serde_json::from_value(json.clone()).unwrap();
5850 let McpServer::Other(OtherMcpServer { type_, fields }) = &deserialized else {
5851 panic!("Expected Other variant");
5852 };
5853
5854 assert_eq!(type_, "websocket");
5855 assert_eq!(fields["name"], "future-server");
5856 assert_eq!(fields["url"], "wss://example.com/mcp");
5857 assert_eq!(fields["protocolVersion"], "2026-01-01");
5858 assert_eq!(serde_json::to_value(&deserialized).unwrap(), json);
5859 }
5860
5861 #[test]
5862 fn test_mcp_server_stdio_requires_type() {
5863 let result = serde_json::from_value::<McpServer>(json!({
5864 "name": "test-server",
5865 "command": "/usr/bin/server",
5866 "args": [],
5867 "env": []
5868 }));
5869
5870 assert!(result.is_err());
5871 }
5872
5873 #[test]
5874 fn test_mcp_server_unknown_does_not_hide_malformed_known_transport() {
5875 let result = serde_json::from_value::<McpServer>(json!({
5876 "type": "stdio",
5877 "name": "test-server",
5878 "args": [],
5879 "env": []
5880 }));
5881
5882 assert!(result.is_err());
5883 }
5884
5885 #[test]
5886 fn test_mcp_server_http_serialization() {
5887 let server = McpServer::Http(
5888 McpServerHttp::new("http-server", "https://api.example.com").headers(vec![
5889 HttpHeader::new("Authorization", "Bearer token123"),
5890 HttpHeader::new("Content-Type", "application/json"),
5891 ]),
5892 );
5893
5894 let json = serde_json::to_value(&server).unwrap();
5895 assert_eq!(
5896 json,
5897 json!({
5898 "type": "http",
5899 "name": "http-server",
5900 "url": "https://api.example.com",
5901 "headers": [
5902 {
5903 "name": "Authorization",
5904 "value": "Bearer token123"
5905 },
5906 {
5907 "name": "Content-Type",
5908 "value": "application/json"
5909 }
5910 ]
5911 })
5912 );
5913
5914 let deserialized: McpServer = serde_json::from_value(json).unwrap();
5915 match deserialized {
5916 McpServer::Http(McpServerHttp {
5917 name,
5918 url,
5919 headers,
5920 meta: _,
5921 }) => {
5922 assert_eq!(name, "http-server");
5923 assert_eq!(url, "https://api.example.com");
5924 assert_eq!(headers.len(), 2);
5925 assert_eq!(headers[0].name, "Authorization");
5926 assert_eq!(headers[0].value, "Bearer token123");
5927 assert_eq!(headers[1].name, "Content-Type");
5928 assert_eq!(headers[1].value, "application/json");
5929 }
5930 _ => panic!("Expected Http variant"),
5931 }
5932 }
5933
5934 #[cfg(feature = "schemars")]
5935 #[test]
5936 fn mcp_server_http_schema_marks_url_as_uri() {
5937 let schema = serde_json::to_value(schemars::schema_for!(McpServerHttp)).unwrap();
5938
5939 assert_eq!(schema["properties"]["url"]["format"], "uri");
5940 }
5941
5942 #[cfg(feature = "unstable_mcp_over_acp")]
5943 #[test]
5944 fn test_client_mcp_message_method_names() {
5945 use serde_json::json;
5946
5947 assert_eq!(AGENT_METHOD_NAMES.mcp_message, "mcp/message");
5948
5949 let notification =
5950 MessageMcpNotification::new("server-1", "req-1", "notifications/progress");
5951 assert_eq!(
5952 ClientNotification::MessageMcpNotification(Box::new(notification.clone())).method(),
5953 "mcp/message"
5954 );
5955 assert_eq!(
5956 serde_json::to_value(notification).unwrap(),
5957 json!({
5958 "serverId": "server-1",
5959 "requestId": "req-1",
5960 "method": "notifications/progress"
5961 })
5962 );
5963 let notification: MessageMcpNotification = serde_json::from_value(json!({
5964 "serverId": "server-1", "requestId": "req-1", "method": "notifications/progress",
5965 "params": null, "_meta": null
5966 }))
5967 .unwrap();
5968 assert_eq!(notification.params, None);
5969 assert_eq!(notification.meta, None);
5970 for key in ["serverId", "requestId", "method"] {
5971 let mut value = json!({"serverId":"server-1", "requestId":"req-1", "method":"notifications/progress"});
5972 value.as_object_mut().unwrap().remove(key);
5973 assert!(serde_json::from_value::<MessageMcpNotification>(value).is_err());
5974 }
5975 }
5976
5977 #[test]
5978 fn test_auth_method_names() {
5979 assert_eq!(AGENT_METHOD_NAMES.auth_login, "auth/login");
5980 assert_eq!(AGENT_METHOD_NAMES.auth_logout, "auth/logout");
5981
5982 assert_eq!(
5983 ClientRequest::LoginAuthRequest(Box::new(LoginAuthRequest::new("agent-login")))
5984 .method(),
5985 "auth/login"
5986 );
5987 assert_eq!(
5988 ClientRequest::LogoutAuthRequest(Box::new(LogoutAuthRequest::new())).method(),
5989 "auth/logout"
5990 );
5991 }
5992
5993 #[test]
5994 fn test_session_config_option_category_known_variants() {
5995 assert_eq!(
5997 serde_json::to_value(&SessionConfigOptionCategory::Mode).unwrap(),
5998 json!("mode")
5999 );
6000 assert_eq!(
6001 serde_json::to_value(&SessionConfigOptionCategory::Model).unwrap(),
6002 json!("model")
6003 );
6004 assert_eq!(
6005 serde_json::to_value(&SessionConfigOptionCategory::ModelConfig).unwrap(),
6006 json!("model_config")
6007 );
6008 assert_eq!(
6009 serde_json::to_value(&SessionConfigOptionCategory::ThoughtLevel).unwrap(),
6010 json!("thought_level")
6011 );
6012
6013 assert_eq!(
6015 serde_json::from_str::<SessionConfigOptionCategory>("\"mode\"").unwrap(),
6016 SessionConfigOptionCategory::Mode
6017 );
6018 assert_eq!(
6019 serde_json::from_str::<SessionConfigOptionCategory>("\"model\"").unwrap(),
6020 SessionConfigOptionCategory::Model
6021 );
6022 assert_eq!(
6023 serde_json::from_str::<SessionConfigOptionCategory>("\"model_config\"").unwrap(),
6024 SessionConfigOptionCategory::ModelConfig
6025 );
6026 assert_eq!(
6027 serde_json::from_str::<SessionConfigOptionCategory>("\"thought_level\"").unwrap(),
6028 SessionConfigOptionCategory::ThoughtLevel
6029 );
6030 }
6031
6032 #[test]
6033 fn test_session_config_option_category_unknown_variants() {
6034 let unknown: SessionConfigOptionCategory =
6036 serde_json::from_str("\"some_future_category\"").unwrap();
6037 assert_eq!(
6038 unknown,
6039 SessionConfigOptionCategory::Other("some_future_category".to_string())
6040 );
6041
6042 let json = serde_json::to_value(&unknown).unwrap();
6044 assert_eq!(json, json!("some_future_category"));
6045 }
6046
6047 #[test]
6048 fn test_session_config_option_category_custom_categories() {
6049 let custom: SessionConfigOptionCategory =
6051 serde_json::from_str("\"_my_custom_category\"").unwrap();
6052 assert_eq!(
6053 custom,
6054 SessionConfigOptionCategory::Other("_my_custom_category".to_string())
6055 );
6056
6057 let json = serde_json::to_value(&custom).unwrap();
6059 assert_eq!(json, json!("_my_custom_category"));
6060
6061 let deserialized: SessionConfigOptionCategory = serde_json::from_value(json).unwrap();
6063 assert_eq!(
6064 deserialized,
6065 SessionConfigOptionCategory::Other("_my_custom_category".to_string()),
6066 );
6067 }
6068
6069 fn test_config_option() -> SessionConfigOption {
6070 SessionConfigOption::select(
6071 "mode",
6072 "Mode",
6073 "ask",
6074 vec![SessionConfigSelectOption::new("ask", "Ask")],
6075 )
6076 }
6077
6078 #[test]
6079 fn test_session_response_initial_state_default_empty_and_skip_serializing() {
6080 assert_eq!(
6081 serde_json::to_value(NewSessionResponse::new("sess")).unwrap(),
6082 json!({ "sessionId": "sess" })
6083 );
6084 assert_eq!(
6085 serde_json::to_value(ResumeSessionResponse::new()).unwrap(),
6086 json!({})
6087 );
6088 #[cfg(feature = "unstable_session_fork")]
6089 assert_eq!(
6090 serde_json::to_value(ForkSessionResponse::new("fork")).unwrap(),
6091 json!({ "sessionId": "fork" })
6092 );
6093
6094 let json = serde_json::to_value(
6095 NewSessionResponse::new("sess").config_options(vec![test_config_option()]),
6096 )
6097 .unwrap();
6098 assert_eq!(json["configOptions"].as_array().unwrap().len(), 1);
6099 }
6100
6101 #[test]
6102 fn test_session_response_config_options_deserialize_missing_null_and_invalid() {
6103 let missing: NewSessionResponse =
6104 serde_json::from_value(json!({ "sessionId": "sess" })).unwrap();
6105 assert_eq!(missing.config_options, []);
6106
6107 let null: NewSessionResponse = serde_json::from_value(json!({
6108 "sessionId": "sess",
6109 "configOptions": null
6110 }))
6111 .unwrap();
6112 assert_eq!(null.config_options, []);
6113
6114 let wrong_shape: NewSessionResponse = serde_json::from_value(json!({
6115 "sessionId": "sess",
6116 "configOptions": "oops"
6117 }))
6118 .unwrap();
6119 assert_eq!(wrong_shape.config_options, []);
6120
6121 let valid_option = serde_json::to_value(test_config_option()).unwrap();
6122 let mixed: NewSessionResponse = serde_json::from_value(json!({
6123 "sessionId": "sess",
6124 "configOptions": ["oops", valid_option]
6125 }))
6126 .unwrap();
6127 assert_eq!(mixed.config_options.len(), 1);
6128
6129 let resume: ResumeSessionResponse = serde_json::from_value(json!({})).unwrap();
6130 assert_eq!(resume.config_options, []);
6131 #[cfg(feature = "unstable_session_fork")]
6132 {
6133 let fork: ForkSessionResponse =
6134 serde_json::from_value(json!({ "sessionId": "fork" })).unwrap();
6135 assert_eq!(fork.config_options, []);
6136 }
6137 }
6138
6139 #[test]
6140 fn test_session_response_available_commands_roundtrip_with_config_options() {
6141 use super::super::{AvailableCommandInput, TextCommandInput};
6142
6143 let commands = vec![
6144 AvailableCommand::new("web", "Search the web")
6145 .input(AvailableCommandInput::Text(TextCommandInput::new("Query"))),
6146 AvailableCommand::new("test", "Run project tests"),
6147 ];
6148 let mut expected = json!({
6149 "sessionId": "sess",
6150 "configOptions": [test_config_option()],
6151 "availableCommands": [
6152 {
6153 "name": "web",
6154 "description": "Search the web",
6155 "input": { "type": "text", "hint": "Query" }
6156 },
6157 { "name": "test", "description": "Run project tests" }
6158 ]
6159 });
6160
6161 let new = NewSessionResponse::new("sess")
6162 .config_options(vec![test_config_option()])
6163 .available_commands(commands.clone());
6164 assert_eq!(serde_json::to_value(&new).unwrap(), expected);
6165 assert_eq!(
6166 serde_json::from_value::<NewSessionResponse>(expected.clone()).unwrap(),
6167 new
6168 );
6169
6170 expected.as_object_mut().unwrap().remove("sessionId");
6171 let resume = ResumeSessionResponse::new()
6172 .config_options(vec![test_config_option()])
6173 .available_commands(commands.clone());
6174 assert_eq!(serde_json::to_value(&resume).unwrap(), expected);
6175 assert_eq!(
6176 serde_json::from_value::<ResumeSessionResponse>(expected.clone()).unwrap(),
6177 resume
6178 );
6179
6180 #[cfg(feature = "unstable_session_fork")]
6181 {
6182 expected["sessionId"] = json!("fork");
6183 let fork = ForkSessionResponse::new("fork")
6184 .config_options(vec![test_config_option()])
6185 .available_commands(commands);
6186 assert_eq!(serde_json::to_value(&fork).unwrap(), expected);
6187 assert_eq!(
6188 serde_json::from_value::<ForkSessionResponse>(expected).unwrap(),
6189 fork
6190 );
6191 }
6192 }
6193
6194 #[test]
6195 fn test_session_response_available_commands_deserialize_missing_null_and_invalid() {
6196 let command = AvailableCommand::new("test", "Run project tests");
6197 let config_options = vec![test_config_option()];
6198
6199 for (commands, expected) in [
6200 (None, vec![]),
6201 (Some(json!(null)), vec![]),
6202 (Some(json!([])), vec![]),
6203 (Some(json!("oops")), vec![]),
6204 (Some(json!({})), vec![]),
6205 (Some(json!([null, {}, { "name": "invalid" }])), vec![]),
6206 (
6207 Some(json!([null, command, { "name": "invalid" }])),
6208 vec![command.clone()],
6209 ),
6210 ] {
6211 let mut value = json!({
6212 "sessionId": "sess",
6213 "configOptions": config_options
6214 });
6215 if let Some(commands) = commands {
6216 value["availableCommands"] = commands;
6217 }
6218
6219 let new: NewSessionResponse = serde_json::from_value(value.clone()).unwrap();
6220 assert_eq!(new.available_commands, expected);
6221 assert_eq!(new.config_options, config_options);
6222
6223 #[cfg(feature = "unstable_session_fork")]
6224 {
6225 let fork: ForkSessionResponse = serde_json::from_value(value.clone()).unwrap();
6226 assert_eq!(fork.available_commands, expected);
6227 assert_eq!(fork.config_options, config_options);
6228 }
6229
6230 value.as_object_mut().unwrap().remove("sessionId");
6231 let resume: ResumeSessionResponse = serde_json::from_value(value).unwrap();
6232 assert_eq!(resume.available_commands, expected);
6233 assert_eq!(resume.config_options, config_options);
6234 }
6235
6236 let resume: ResumeSessionResponse = serde_json::from_value(json!(null)).unwrap();
6237 assert_eq!(resume.available_commands, []);
6238 }
6239
6240 #[test]
6241 fn test_resume_session_replay_from_serialization() {
6242 assert_eq!(
6243 serde_json::to_value(ResumeSessionRequest::new(
6244 "sess_abc123",
6245 "/home/user/project"
6246 ))
6247 .unwrap(),
6248 json!({
6249 "sessionId": "sess_abc123",
6250 "cwd": "/home/user/project"
6251 })
6252 );
6253 assert_eq!(
6254 serde_json::to_value(
6255 ResumeSessionRequest::new("sess_abc123", "/home/user/project")
6256 .replay_from(ReplayFrom::from(ReplayFromStart::new()))
6257 )
6258 .unwrap(),
6259 json!({
6260 "sessionId": "sess_abc123",
6261 "cwd": "/home/user/project",
6262 "replayFrom": {
6263 "type": "start"
6264 }
6265 })
6266 );
6267
6268 let replay: ResumeSessionRequest = serde_json::from_value(json!({
6269 "sessionId": "sess_abc123",
6270 "cwd": "/home/user/project",
6271 "replayFrom": {
6272 "type": "start"
6273 }
6274 }))
6275 .unwrap();
6276 assert!(matches!(replay.replay_from, Some(ReplayFrom::Start(_))));
6277
6278 let none: ResumeSessionRequest = serde_json::from_value(json!({
6279 "sessionId": "sess_abc123",
6280 "cwd": "/home/user/project",
6281 "replayFrom": null
6282 }))
6283 .unwrap();
6284 assert!(none.replay_from.is_none());
6285 }
6286
6287 #[test]
6288 fn test_auth_method_agent_serialization() {
6289 let method = AuthMethod::Agent(AuthMethodAgent::new("default-auth", "Default Auth"));
6290
6291 let json = serde_json::to_value(&method).unwrap();
6292 assert_eq!(
6293 json,
6294 json!({
6295 "methodId": "default-auth",
6296 "name": "Default Auth",
6297 "type": "agent"
6298 })
6299 );
6300 assert!(!json.as_object().unwrap().contains_key("description"));
6302
6303 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
6304 match deserialized {
6305 AuthMethod::Agent(AuthMethodAgent {
6306 method_id, name, ..
6307 }) => {
6308 assert_eq!(method_id.0.as_ref(), "default-auth");
6309 assert_eq!(name, "Default Auth");
6310 }
6311 _ => panic!("Expected Agent variant"),
6312 }
6313 }
6314
6315 #[test]
6316 fn test_auth_method_agent_deserialization() {
6317 let json = json!({
6318 "methodId": "agent-auth",
6319 "name": "Agent Auth",
6320 "type": "agent"
6321 });
6322
6323 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
6324 assert!(matches!(deserialized, AuthMethod::Agent(_)));
6325 }
6326
6327 #[test]
6328 fn test_auth_method_agent_requires_type() {
6329 assert!(
6330 serde_json::from_value::<AuthMethod>(json!({
6331 "methodId": "agent-auth",
6332 "name": "Agent Auth"
6333 }))
6334 .is_err()
6335 );
6336 }
6337
6338 #[test]
6339 fn test_auth_method_agent_rejects_null_type() {
6340 assert!(
6341 serde_json::from_value::<AuthMethod>(json!({
6342 "methodId": "agent-auth",
6343 "name": "Agent Auth",
6344 "type": null
6345 }))
6346 .is_err()
6347 );
6348 }
6349
6350 #[test]
6351 fn test_auth_method_unknown_does_not_hide_malformed_agent() {
6352 assert!(
6353 serde_json::from_value::<AuthMethod>(json!({
6354 "methodId": "agent-auth",
6355 "type": "agent"
6356 }))
6357 .is_err()
6358 );
6359 assert!(
6360 serde_json::from_value::<AuthMethod>(json!({
6361 "methodId": "api-key",
6362 "type": "env_var",
6363 "vars": [{"name": "API_KEY"}]
6364 }))
6365 .is_err()
6366 );
6367 }
6368
6369 #[test]
6370 fn test_auth_method_unknown_variant_roundtrip() {
6371 let method: AuthMethod = serde_json::from_value(json!({
6372 "methodId": "oauth",
6373 "name": "OAuth",
6374 "type": "_oauth",
6375 "authorizationUrl": "https://example.com/auth"
6376 }))
6377 .unwrap();
6378
6379 assert_eq!(method.method_id().0.as_ref(), "oauth");
6380 assert_eq!(method.name(), "OAuth");
6381 let AuthMethod::Other(unknown) = method else {
6382 panic!("expected unknown auth method");
6383 };
6384 assert_eq!(unknown.type_, "_oauth");
6385 assert_eq!(
6386 unknown.fields.get("authorizationUrl"),
6387 Some(&json!("https://example.com/auth"))
6388 );
6389
6390 assert_eq!(
6391 serde_json::to_value(AuthMethod::Other(unknown)).unwrap(),
6392 json!({
6393 "methodId": "oauth",
6394 "name": "OAuth",
6395 "type": "_oauth",
6396 "authorizationUrl": "https://example.com/auth"
6397 })
6398 );
6399 }
6400
6401 #[test]
6402 fn test_auth_method_unknown_does_not_hide_malformed_known_variant() {
6403 assert!(
6404 serde_json::from_value::<AuthMethod>(json!({
6405 "methodId": "terminal-auth",
6406 "type": "terminal"
6407 }))
6408 .is_err()
6409 );
6410 }
6411
6412 #[test]
6413 fn test_session_delete_serialization() {
6414 assert_eq!(AGENT_METHOD_NAMES.session_delete, "session/delete");
6415 assert_eq!(
6416 ClientRequest::DeleteSessionRequest(Box::new(DeleteSessionRequest::new("sess_abc123")))
6417 .method(),
6418 "session/delete"
6419 );
6420 assert_eq!(
6421 serde_json::to_value(DeleteSessionRequest::new("sess_abc123")).unwrap(),
6422 json!({
6423 "sessionId": "sess_abc123"
6424 })
6425 );
6426 assert_eq!(
6427 serde_json::to_value(DeleteSessionResponse::new()).unwrap(),
6428 json!({})
6429 );
6430 assert_eq!(
6431 serde_json::to_value(
6432 SessionCapabilities::new().delete(SessionDeleteCapabilities::new())
6433 )
6434 .unwrap(),
6435 json!({
6436 "delete": {}
6437 })
6438 );
6439 }
6440 #[test]
6441 fn test_session_additional_directories_serialization() {
6442 assert_eq!(
6443 serde_json::to_value(NewSessionRequest::new("/home/user/project")).unwrap(),
6444 json!({
6445 "cwd": "/home/user/project",
6446 })
6447 );
6448 assert_eq!(
6449 serde_json::to_value(
6450 NewSessionRequest::new("/home/user/project").additional_directories(vec![
6451 PathBuf::from("/home/user/shared-lib"),
6452 PathBuf::from("/home/user/product-docs"),
6453 ])
6454 )
6455 .unwrap(),
6456 json!({
6457 "cwd": "/home/user/project",
6458 "additionalDirectories": [
6459 "/home/user/shared-lib",
6460 "/home/user/product-docs"
6461 ],
6462 })
6463 );
6464 assert_eq!(
6465 serde_json::to_value(ResumeSessionRequest::new(
6466 "sess_abc123",
6467 "/home/user/project"
6468 ))
6469 .unwrap(),
6470 json!({
6471 "sessionId": "sess_abc123",
6472 "cwd": "/home/user/project",
6473 })
6474 );
6475 assert_eq!(
6476 serde_json::from_value::<ResumeSessionRequest>(json!({
6477 "sessionId": "sess_abc123",
6478 "cwd": "/home/user/project"
6479 }))
6480 .unwrap()
6481 .mcp_servers,
6482 Vec::<McpServer>::new()
6483 );
6484 assert_eq!(
6485 serde_json::from_value::<ResumeSessionRequest>(json!({
6486 "sessionId": "sess_abc123",
6487 "cwd": "/home/user/project",
6488 "mcpServers": null
6489 }))
6490 .unwrap()
6491 .mcp_servers,
6492 Vec::<McpServer>::new()
6493 );
6494 assert_eq!(
6495 serde_json::to_value(SessionInfo::new("sess_abc123", "/home/user/project")).unwrap(),
6496 json!({
6497 "sessionId": "sess_abc123",
6498 "cwd": "/home/user/project"
6499 })
6500 );
6501 assert_eq!(
6502 serde_json::to_value(
6503 SessionInfo::new("sess_abc123", "/home/user/project").additional_directories(vec![
6504 PathBuf::from("/home/user/shared-lib"),
6505 PathBuf::from("/home/user/product-docs"),
6506 ])
6507 )
6508 .unwrap(),
6509 json!({
6510 "sessionId": "sess_abc123",
6511 "cwd": "/home/user/project",
6512 "additionalDirectories": [
6513 "/home/user/shared-lib",
6514 "/home/user/product-docs"
6515 ]
6516 })
6517 );
6518 assert_eq!(
6519 serde_json::from_value::<SessionInfo>(json!({
6520 "sessionId": "sess_abc123",
6521 "cwd": "/home/user/project"
6522 }))
6523 .unwrap()
6524 .additional_directories,
6525 Vec::<AbsolutePath>::new()
6526 );
6527 }
6528 #[test]
6529 fn test_session_additional_directories_capabilities_serialization() {
6530 assert_eq!(
6531 serde_json::to_value(
6532 SessionCapabilities::new()
6533 .additional_directories(SessionAdditionalDirectoriesCapabilities::new())
6534 )
6535 .unwrap(),
6536 json!({
6537 "additionalDirectories": {}
6538 })
6539 );
6540 }
6541
6542 #[test]
6543 fn test_auth_method_terminal_serialization() {
6544 let method = AuthMethod::Terminal(AuthMethodTerminal::new("tui-auth", "Terminal Auth"));
6545
6546 let json = serde_json::to_value(&method).unwrap();
6547 assert_eq!(
6548 json,
6549 json!({
6550 "methodId": "tui-auth",
6551 "name": "Terminal Auth",
6552 "type": "terminal"
6553 })
6554 );
6555 assert!(!json.as_object().unwrap().contains_key("args"));
6557 assert!(!json.as_object().unwrap().contains_key("env"));
6558
6559 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
6560 match deserialized {
6561 AuthMethod::Terminal(AuthMethodTerminal { args, env, .. }) => {
6562 assert_eq!(args, Vec::<String>::new());
6563 assert_eq!(env, []);
6564 }
6565 _ => panic!("Expected Terminal variant"),
6566 }
6567 }
6568
6569 #[test]
6570 fn test_auth_method_terminal_with_args_and_env_serialization() {
6571 let method = AuthMethod::Terminal(
6572 AuthMethodTerminal::new("tui-auth", "Terminal Auth")
6573 .args(vec!["--interactive".to_string(), "--color".to_string()])
6574 .env(vec![EnvVariable::new("TERM", "xterm-256color")]),
6575 );
6576
6577 let json = serde_json::to_value(&method).unwrap();
6578 assert_eq!(
6579 json,
6580 json!({
6581 "methodId": "tui-auth",
6582 "name": "Terminal Auth",
6583 "type": "terminal",
6584 "args": ["--interactive", "--color"],
6585 "env": [
6586 {
6587 "name": "TERM",
6588 "value": "xterm-256color"
6589 }
6590 ]
6591 })
6592 );
6593
6594 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
6595 match deserialized {
6596 AuthMethod::Terminal(AuthMethodTerminal { args, env, .. }) => {
6597 assert_eq!(args, vec!["--interactive", "--color"]);
6598 assert_eq!(env.len(), 1);
6599 assert_eq!(env[0].name, "TERM");
6600 assert_eq!(env[0].value, "xterm-256color");
6601 }
6602 _ => panic!("Expected Terminal variant"),
6603 }
6604 }
6605
6606 #[test]
6607 fn test_session_config_option_id_serialize() {
6608 let val = SessionConfigOptionValue::id("model-1");
6609 let json = serde_json::to_value(&val).unwrap();
6610 assert_eq!(json, json!({ "type": "id", "value": "model-1" }));
6611 }
6612
6613 #[test]
6614 fn test_session_config_option_value_boolean_serialize() {
6615 let val = SessionConfigOptionValue::boolean(true);
6616 let json = serde_json::to_value(&val).unwrap();
6617 assert_eq!(json, json!({ "type": "boolean", "value": true }));
6618 }
6619
6620 #[test]
6621 fn test_session_config_option_value_deserialize_id() {
6622 let json = json!({ "type": "id", "value": "model-1" });
6623 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6624 assert_eq!(val, SessionConfigOptionValue::id("model-1"));
6625 assert_eq!(val.as_id().unwrap().to_string(), "model-1");
6626 }
6627
6628 #[test]
6629 fn test_session_config_option_value_deserialize_requires_type() {
6630 let json = json!({ "value": "model-1" });
6631 let result = serde_json::from_value::<SessionConfigOptionValue>(json);
6632 assert!(result.is_err());
6633 }
6634
6635 #[test]
6636 fn test_session_config_option_value_deserialize_boolean() {
6637 let json = json!({ "type": "boolean", "value": true });
6638 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6639 assert_eq!(val, SessionConfigOptionValue::boolean(true));
6640 assert_eq!(val.as_bool(), Some(true));
6641 }
6642
6643 #[test]
6644 fn test_session_config_option_value_deserialize_boolean_false() {
6645 let json = json!({ "type": "boolean", "value": false });
6646 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6647 assert_eq!(val, SessionConfigOptionValue::boolean(false));
6648 assert_eq!(val.as_bool(), Some(false));
6649 }
6650
6651 #[test]
6652 fn test_session_config_option_value_deserialize_unknown_type_with_string_value() {
6653 let json = json!({
6654 "type": "text",
6655 "value": "freeform input",
6656 "maxLength": 200
6657 });
6658 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6659 let SessionConfigOptionValue::Other(unknown) = val else {
6660 panic!("Expected Other variant");
6661 };
6662 assert_eq!(unknown.type_, "text");
6663 assert_eq!(unknown.value, json!("freeform input"));
6664 assert_eq!(unknown.fields["maxLength"], json!(200));
6665 }
6666
6667 #[test]
6668 fn test_session_config_option_value_deserialize_unknown_type_with_object_value() {
6669 let json = json!({
6670 "type": "range",
6671 "value": { "min": 1, "max": 5 }
6672 });
6673 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6674 let SessionConfigOptionValue::Other(unknown) = val else {
6675 panic!("Expected Other variant");
6676 };
6677 assert_eq!(unknown.type_, "range");
6678 assert_eq!(unknown.value, json!({ "min": 1, "max": 5 }));
6679 }
6680
6681 #[test]
6682 fn test_session_config_option_value_roundtrip_id() {
6683 let original = SessionConfigOptionValue::id("option-a");
6684 let json = serde_json::to_value(&original).unwrap();
6685 let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6686 assert_eq!(original, roundtripped);
6687 }
6688
6689 #[test]
6690 fn test_session_config_option_value_roundtrip_boolean() {
6691 let original = SessionConfigOptionValue::boolean(false);
6692 let json = serde_json::to_value(&original).unwrap();
6693 let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6694 assert_eq!(original, roundtripped);
6695 }
6696
6697 #[test]
6698 fn test_session_config_option_value_roundtrip_other() {
6699 let mut fields = BTreeMap::new();
6700 fields.insert("maxLength".to_string(), json!(200));
6701 let original = SessionConfigOptionValue::Other(OtherSessionConfigOptionValue::new(
6702 "text",
6703 json!("freeform input"),
6704 fields,
6705 ));
6706 let json = serde_json::to_value(&original).unwrap();
6707 let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6708 assert_eq!(original, roundtripped);
6709 }
6710
6711 #[test]
6712 fn test_session_config_option_value_type_mismatch_boolean_with_string() {
6713 let json = json!({ "type": "boolean", "value": "not a bool" });
6714 let result = serde_json::from_value::<SessionConfigOptionValue>(json);
6715 assert!(result.is_err());
6716 }
6717
6718 #[test]
6719 fn test_session_config_option_value_from_impls() {
6720 let from_str: SessionConfigOptionValue = "model-1".into();
6721 assert_eq!(from_str.as_id().unwrap().to_string(), "model-1");
6722
6723 let from_id: SessionConfigOptionValue = SessionConfigValueId::new("model-2").into();
6724 assert_eq!(from_id.as_id().unwrap().to_string(), "model-2");
6725
6726 let from_bool: SessionConfigOptionValue = true.into();
6727 assert_eq!(from_bool.as_bool(), Some(true));
6728 }
6729
6730 #[test]
6731 fn test_set_session_config_option_request_id() {
6732 let req = SetSessionConfigOptionRequest::new("sess_1", "model", "model-1");
6733 let json = serde_json::to_value(&req).unwrap();
6734 assert_eq!(
6735 json,
6736 json!({
6737 "sessionId": "sess_1",
6738 "configId": "model",
6739 "type": "id",
6740 "value": "model-1"
6741 })
6742 );
6743 }
6744
6745 #[test]
6746 fn test_set_session_config_option_request_boolean() {
6747 let req = SetSessionConfigOptionRequest::new("sess_1", "brave_mode", true);
6748 let json = serde_json::to_value(&req).unwrap();
6749 assert_eq!(
6750 json,
6751 json!({
6752 "sessionId": "sess_1",
6753 "configId": "brave_mode",
6754 "type": "boolean",
6755 "value": true
6756 })
6757 );
6758 }
6759
6760 #[test]
6761 fn test_set_session_config_option_request_deserialize_requires_type() {
6762 let json = json!({
6763 "sessionId": "sess_1",
6764 "configId": "model",
6765 "value": "model-1"
6766 });
6767 let result = serde_json::from_value::<SetSessionConfigOptionRequest>(json);
6768 assert!(result.is_err());
6769 }
6770
6771 #[test]
6772 fn test_set_session_config_option_request_deserialize_boolean() {
6773 let json = json!({
6774 "sessionId": "sess_1",
6775 "configId": "brave_mode",
6776 "type": "boolean",
6777 "value": true
6778 });
6779 let req: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
6780 assert_eq!(req.value.as_bool(), Some(true));
6781 }
6782
6783 #[test]
6784 fn test_set_session_config_option_request_roundtrip_id() {
6785 let original = SetSessionConfigOptionRequest::new("s", "c", "v");
6786 let json = serde_json::to_value(&original).unwrap();
6787 let roundtripped: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
6788 assert_eq!(original, roundtripped);
6789 }
6790
6791 #[test]
6792 fn test_set_session_config_option_request_roundtrip_boolean() {
6793 let original = SetSessionConfigOptionRequest::new("s", "c", false);
6794 let json = serde_json::to_value(&original).unwrap();
6795 let roundtripped: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
6796 assert_eq!(original, roundtripped);
6797 }
6798
6799 #[test]
6800 fn test_session_config_boolean_serialization() {
6801 let cfg = SessionConfigBoolean::new(true);
6802 let json = serde_json::to_value(&cfg).unwrap();
6803 assert_eq!(json, json!({ "currentValue": true }));
6804
6805 let deserialized: SessionConfigBoolean = serde_json::from_value(json).unwrap();
6806 assert!(deserialized.current_value);
6807 }
6808
6809 #[test]
6810 fn test_session_config_option_boolean_variant() {
6811 let opt = SessionConfigOption::boolean("brave_mode", "Brave Mode", false)
6812 .description("Skip confirmation prompts")
6813 .meta(test_meta());
6814 assert_eq!(serialized_meta_key_count(&opt), 1);
6815
6816 let json = serde_json::to_value(&opt).unwrap();
6817 assert_eq!(
6818 json,
6819 json!({
6820 "configId": "brave_mode",
6821 "name": "Brave Mode",
6822 "description": "Skip confirmation prompts",
6823 "type": "boolean",
6824 "currentValue": false,
6825 "_meta": {
6826 "source": "test"
6827 }
6828 })
6829 );
6830
6831 let deserialized: SessionConfigOption = serde_json::from_value(json).unwrap();
6832 assert_eq!(deserialized.config_id.to_string(), "brave_mode");
6833 assert_eq!(deserialized.name, "Brave Mode");
6834 match deserialized.kind {
6835 SessionConfigKind::Boolean(ref b) => assert!(!b.current_value),
6836 _ => panic!("Expected Boolean kind"),
6837 }
6838 }
6839
6840 #[test]
6841 fn test_session_config_option_select_still_works() {
6842 let opt = SessionConfigOption::select(
6844 "model",
6845 "Model",
6846 "model-1",
6847 vec![
6848 SessionConfigSelectOption::new("model-1", "Model 1"),
6849 SessionConfigSelectOption::new("model-2", "Model 2"),
6850 ],
6851 )
6852 .meta(test_meta());
6853 assert_eq!(serialized_meta_key_count(&opt), 1);
6854
6855 let json = serde_json::to_value(&opt).unwrap();
6856 assert_eq!(json["type"], "select");
6857 assert_eq!(json["currentValue"], "model-1");
6858 assert_eq!(json["options"].as_array().unwrap().len(), 2);
6859 assert_eq!(json["_meta"]["source"], "test");
6860
6861 let deserialized: SessionConfigOption = serde_json::from_value(json).unwrap();
6862 match deserialized.kind {
6863 SessionConfigKind::Select(ref s) => {
6864 assert_eq!(s.current_value.to_string(), "model-1");
6865 }
6866 _ => panic!("Expected Select kind"),
6867 }
6868 }
6869
6870 #[test]
6871 fn test_session_config_option_unknown_kind_roundtrip() {
6872 let option: SessionConfigOption = serde_json::from_value(json!({
6873 "configId": "verbosity",
6874 "name": "Verbosity",
6875 "type": "_slider",
6876 "currentValue": 3,
6877 "min": 0,
6878 "max": 5,
6879 "_meta": {
6880 "source": "test"
6881 }
6882 }))
6883 .unwrap();
6884
6885 assert_eq!(option.config_id.to_string(), "verbosity");
6886 assert_eq!(option.meta.as_ref().unwrap()["source"], "test");
6887 let SessionConfigKind::Other(unknown) = &option.kind else {
6888 panic!("expected unknown config kind");
6889 };
6890 assert_eq!(unknown.type_, "_slider");
6891 assert_eq!(unknown.fields.get("currentValue"), Some(&json!(3)));
6892 assert!(!unknown.fields.contains_key("_meta"));
6893 assert_eq!(serialized_meta_key_count(&option), 1);
6894
6895 let json = serde_json::to_value(&option).unwrap();
6896 assert_eq!(json["type"], "_slider");
6897 assert_eq!(json["currentValue"], 3);
6898 assert_eq!(json["min"], 0);
6899 assert_eq!(json["max"], 5);
6900 assert_eq!(json["_meta"]["source"], "test");
6901 }
6902
6903 #[test]
6904 fn test_session_config_option_unknown_kind_does_not_duplicate_flattened_meta() {
6905 let mut fields = std::collections::BTreeMap::new();
6906 fields.insert("currentValue".to_string(), json!(3));
6907 fields.insert("_meta".to_string(), json!({ "inner": "ignored" }));
6908
6909 let option = SessionConfigOption::new(
6910 "verbosity",
6911 "Verbosity",
6912 SessionConfigKind::Other(OtherSessionConfigKind::new("_slider", fields)),
6913 )
6914 .meta(test_meta());
6915
6916 let SessionConfigKind::Other(unknown) = &option.kind else {
6917 panic!("expected unknown config kind");
6918 };
6919 assert!(!unknown.fields.contains_key("_meta"));
6920 assert_eq!(serialized_meta_key_count(&option), 1);
6921
6922 let json = serde_json::to_value(&option).unwrap();
6923 assert_eq!(json["type"], "_slider");
6924 assert_eq!(json["currentValue"], 3);
6925 assert_eq!(json["_meta"]["source"], "test");
6926 }
6927
6928 #[test]
6929 fn test_session_config_option_unknown_does_not_hide_malformed_known_kind() {
6930 assert!(
6931 serde_json::from_value::<SessionConfigOption>(json!({
6932 "configId": "model",
6933 "name": "Model",
6934 "type": "select"
6935 }))
6936 .is_err()
6937 );
6938 }
6939
6940 #[cfg(feature = "unstable_llm_providers")]
6941 #[test]
6942 fn test_llm_protocol_known_variants() {
6943 assert_eq!(
6944 serde_json::to_value(&LlmProtocol::Anthropic).unwrap(),
6945 json!("anthropic")
6946 );
6947 assert_eq!(
6948 serde_json::to_value(&LlmProtocol::OpenAi).unwrap(),
6949 json!("openai")
6950 );
6951 assert_eq!(
6952 serde_json::to_value(&LlmProtocol::Azure).unwrap(),
6953 json!("azure")
6954 );
6955 assert_eq!(
6956 serde_json::to_value(&LlmProtocol::Vertex).unwrap(),
6957 json!("vertex")
6958 );
6959 assert_eq!(
6960 serde_json::to_value(&LlmProtocol::Bedrock).unwrap(),
6961 json!("bedrock")
6962 );
6963
6964 assert_eq!(
6965 serde_json::from_str::<LlmProtocol>("\"anthropic\"").unwrap(),
6966 LlmProtocol::Anthropic
6967 );
6968 assert_eq!(
6969 serde_json::from_str::<LlmProtocol>("\"openai\"").unwrap(),
6970 LlmProtocol::OpenAi
6971 );
6972 assert_eq!(
6973 serde_json::from_str::<LlmProtocol>("\"azure\"").unwrap(),
6974 LlmProtocol::Azure
6975 );
6976 assert_eq!(
6977 serde_json::from_str::<LlmProtocol>("\"vertex\"").unwrap(),
6978 LlmProtocol::Vertex
6979 );
6980 assert_eq!(
6981 serde_json::from_str::<LlmProtocol>("\"bedrock\"").unwrap(),
6982 LlmProtocol::Bedrock
6983 );
6984 }
6985
6986 #[cfg(feature = "unstable_llm_providers")]
6987 #[test]
6988 fn test_llm_protocol_unknown_variant() {
6989 let unknown: LlmProtocol = serde_json::from_str("\"cohere\"").unwrap();
6990 assert_eq!(unknown, LlmProtocol::Other("cohere".to_string()));
6991
6992 let json = serde_json::to_value(&unknown).unwrap();
6993 assert_eq!(json, json!("cohere"));
6994 }
6995
6996 #[cfg(feature = "unstable_llm_providers")]
6997 #[test]
6998 fn test_provider_current_config_serialization() {
6999 let config =
7000 ProviderCurrentConfig::new(LlmProtocol::Anthropic, "https://api.anthropic.com");
7001
7002 let json = serde_json::to_value(&config).unwrap();
7003 assert_eq!(
7004 json,
7005 json!({
7006 "apiType": "anthropic",
7007 "baseUrl": "https://api.anthropic.com"
7008 })
7009 );
7010
7011 let deserialized: ProviderCurrentConfig = serde_json::from_value(json).unwrap();
7012 assert_eq!(deserialized.api_type, LlmProtocol::Anthropic);
7013 assert_eq!(deserialized.base_url, "https://api.anthropic.com");
7014 }
7015
7016 #[cfg(feature = "unstable_llm_providers")]
7017 #[test]
7018 fn test_provider_info_with_current_config() {
7019 let info = ProviderInfo::new(
7020 "main",
7021 vec![LlmProtocol::Anthropic, LlmProtocol::OpenAi],
7022 true,
7023 Some(ProviderCurrentConfig::new(
7024 LlmProtocol::Anthropic,
7025 "https://api.anthropic.com",
7026 )),
7027 );
7028
7029 let json = serde_json::to_value(&info).unwrap();
7030 assert_eq!(
7031 json,
7032 json!({
7033 "providerId": "main",
7034 "supported": ["anthropic", "openai"],
7035 "required": true,
7036 "current": {
7037 "apiType": "anthropic",
7038 "baseUrl": "https://api.anthropic.com"
7039 }
7040 })
7041 );
7042
7043 let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
7044 assert_eq!(deserialized.provider_id.to_string(), "main");
7045 assert_eq!(deserialized.supported.len(), 2);
7046 assert!(deserialized.required);
7047 assert!(deserialized.current.is_some());
7048 assert_eq!(
7049 deserialized.current.as_ref().unwrap().api_type,
7050 LlmProtocol::Anthropic
7051 );
7052 }
7053
7054 #[cfg(feature = "unstable_llm_providers")]
7055 #[test]
7056 fn test_provider_info_disabled() {
7057 let info = ProviderInfo::new(
7058 "secondary",
7059 vec![LlmProtocol::OpenAi],
7060 false,
7061 None::<ProviderCurrentConfig>,
7062 );
7063
7064 let json = serde_json::to_value(&info).unwrap();
7065 assert_eq!(
7066 json,
7067 json!({
7068 "providerId": "secondary",
7069 "supported": ["openai"],
7070 "required": false
7071 })
7072 );
7073
7074 let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
7075 assert_eq!(deserialized.provider_id.to_string(), "secondary");
7076 assert!(!deserialized.required);
7077 assert!(deserialized.current.is_none());
7078 }
7079
7080 #[cfg(feature = "unstable_llm_providers")]
7081 #[test]
7082 fn test_provider_info_missing_current_defaults_to_none() {
7083 let json = json!({
7085 "providerId": "main",
7086 "supported": ["anthropic"],
7087 "required": true
7088 });
7089 let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
7090 assert!(deserialized.current.is_none());
7091 }
7092
7093 #[cfg(feature = "unstable_llm_providers")]
7094 #[test]
7095 fn test_provider_info_explicit_null_current_decodes_to_none() {
7096 let json = json!({
7100 "providerId": "main",
7101 "supported": ["anthropic"],
7102 "required": true,
7103 "current": null
7104 });
7105 let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
7106 assert!(deserialized.current.is_none());
7107 }
7108
7109 #[cfg(feature = "unstable_llm_providers")]
7110 #[test]
7111 fn test_list_providers_response_serialization() {
7112 let response = ListProvidersResponse::new(vec![ProviderInfo::new(
7113 "main",
7114 vec![LlmProtocol::Anthropic],
7115 true,
7116 Some(ProviderCurrentConfig::new(
7117 LlmProtocol::Anthropic,
7118 "https://api.anthropic.com",
7119 )),
7120 )]);
7121
7122 let json = serde_json::to_value(&response).unwrap();
7123 assert_eq!(json["providers"].as_array().unwrap().len(), 1);
7124 assert_eq!(json["providers"][0]["providerId"], "main");
7125
7126 let deserialized: ListProvidersResponse = serde_json::from_value(json).unwrap();
7127 assert_eq!(deserialized.providers.len(), 1);
7128 }
7129
7130 #[cfg(feature = "unstable_llm_providers")]
7131 #[test]
7132 fn test_set_provider_request_serialization() {
7133 use std::collections::HashMap;
7134
7135 let mut headers = HashMap::new();
7136 headers.insert("Authorization".to_string(), "Bearer sk-test".to_string());
7137
7138 let request =
7139 SetProviderRequest::new("main", LlmProtocol::OpenAi, "https://api.openai.com/v1")
7140 .headers(headers);
7141
7142 let json = serde_json::to_value(&request).unwrap();
7143 assert_eq!(
7144 json,
7145 json!({
7146 "providerId": "main",
7147 "apiType": "openai",
7148 "baseUrl": "https://api.openai.com/v1",
7149 "headers": {
7150 "Authorization": "Bearer sk-test"
7151 }
7152 })
7153 );
7154
7155 let deserialized: SetProviderRequest = serde_json::from_value(json).unwrap();
7156 assert_eq!(deserialized.provider_id.to_string(), "main");
7157 assert_eq!(deserialized.api_type, LlmProtocol::OpenAi);
7158 assert_eq!(deserialized.base_url, "https://api.openai.com/v1");
7159 assert_eq!(deserialized.headers.len(), 1);
7160 assert_eq!(
7161 deserialized.headers.get("Authorization").unwrap(),
7162 "Bearer sk-test"
7163 );
7164 }
7165
7166 #[cfg(feature = "unstable_llm_providers")]
7167 #[test]
7168 fn test_set_provider_request_omits_empty_headers() {
7169 let request =
7170 SetProviderRequest::new("main", LlmProtocol::Anthropic, "https://api.anthropic.com");
7171
7172 let json = serde_json::to_value(&request).unwrap();
7173 assert!(!json.as_object().unwrap().contains_key("headers"));
7175 }
7176
7177 #[cfg(feature = "unstable_llm_providers")]
7178 #[test]
7179 fn test_disable_provider_request_serialization() {
7180 let request = DisableProviderRequest::new("secondary");
7181
7182 let json = serde_json::to_value(&request).unwrap();
7183 assert_eq!(json, json!({ "providerId": "secondary" }));
7184
7185 let deserialized: DisableProviderRequest = serde_json::from_value(json).unwrap();
7186 assert_eq!(deserialized.provider_id.to_string(), "secondary");
7187 }
7188
7189 #[cfg(feature = "unstable_llm_providers")]
7190 #[test]
7191 fn test_providers_capabilities_serialization() {
7192 let caps = ProvidersCapabilities::new();
7193
7194 let json = serde_json::to_value(&caps).unwrap();
7195 assert_eq!(json, json!({}));
7196
7197 let deserialized: ProvidersCapabilities = serde_json::from_value(json).unwrap();
7198 assert!(deserialized.meta.is_none());
7199 }
7200
7201 #[cfg(feature = "unstable_llm_providers")]
7202 #[test]
7203 fn test_agent_capabilities_with_providers() {
7204 let caps = AgentCapabilities::new().providers(ProvidersCapabilities::new());
7205
7206 let json = serde_json::to_value(&caps).unwrap();
7207 assert_eq!(json["providers"], json!({}));
7208
7209 let deserialized: AgentCapabilities = serde_json::from_value(json).unwrap();
7210 assert!(deserialized.providers.is_some());
7211 }
7212
7213 #[test]
7214 fn test_agent_capabilities_session_is_explicit() {
7215 let json = serde_json::to_value(AgentCapabilities::new()).unwrap();
7216 assert!(json.get("session").is_none());
7217
7218 let caps = AgentCapabilities::new().session(
7219 SessionCapabilities::new()
7220 .prompt(PromptCapabilities::new().image(PromptImageCapabilities::new()))
7221 .mcp(McpCapabilities::new().stdio(McpStdioCapabilities::new())),
7222 );
7223
7224 assert_eq!(
7225 serde_json::to_value(&caps).unwrap(),
7226 json!({
7227 "session": {
7228 "prompt": {
7229 "image": {}
7230 },
7231 "mcp": {
7232 "stdio": {}
7233 }
7234 }
7235 })
7236 );
7237
7238 let deserialized: AgentCapabilities = serde_json::from_value(json!({
7239 "session": false
7240 }))
7241 .unwrap();
7242 assert!(deserialized.session.is_none());
7243 }
7244
7245 #[test]
7246 fn test_prompt_capabilities_serialize_supported_content_as_objects() {
7247 let caps = PromptCapabilities::new()
7248 .image(PromptImageCapabilities::new())
7249 .audio(PromptAudioCapabilities::new())
7250 .embedded_context(PromptEmbeddedContextCapabilities::new());
7251
7252 assert_eq!(
7253 serde_json::to_value(&caps).unwrap(),
7254 json!({
7255 "image": {},
7256 "audio": {},
7257 "embeddedContext": {}
7258 })
7259 );
7260
7261 let deserialized: PromptCapabilities = serde_json::from_value(json!({
7262 "image": null,
7263 "audio": false,
7264 "embeddedContext": {}
7265 }))
7266 .unwrap();
7267 assert!(deserialized.image.is_none());
7268 assert!(deserialized.audio.is_none());
7269 assert!(deserialized.embedded_context.is_some());
7270 }
7271
7272 #[test]
7273 fn test_mcp_capabilities_serialize_supported_transports_as_objects() {
7274 let caps = McpCapabilities::new()
7275 .stdio(McpStdioCapabilities::new())
7276 .http(McpHttpCapabilities::new());
7277
7278 assert_eq!(
7279 serde_json::to_value(&caps).unwrap(),
7280 json!({
7281 "stdio": {},
7282 "http": {}
7283 })
7284 );
7285
7286 let deserialized: McpCapabilities = serde_json::from_value(json!({
7287 "stdio": null,
7288 "http": false
7289 }))
7290 .unwrap();
7291 assert!(deserialized.stdio.is_none());
7292 assert!(deserialized.http.is_none());
7293 }
7294
7295 #[cfg(feature = "unstable_mcp_over_acp")]
7296 #[test]
7297 fn test_mcp_capabilities_serialize_acp_support_as_object() {
7298 let caps = McpCapabilities::new().acp(McpAcpCapabilities::new());
7299
7300 assert_eq!(
7301 serde_json::to_value(&caps).unwrap(),
7302 json!({
7303 "acp": {}
7304 })
7305 );
7306 }
7307
7308 #[test]
7309 fn prompt_request_rejects_malformed_content_block() {
7310 use serde_json::json;
7311
7312 assert!(
7313 serde_json::from_value::<PromptRequest>(json!({
7314 "sessionId": "sess-1",
7315 "prompt": [{"type": "text"}]
7316 }))
7317 .is_err()
7318 );
7319 }
7320
7321 #[test]
7322 fn prompt_request_rejects_non_array_prompt() {
7323 use serde_json::json;
7324
7325 assert!(
7326 serde_json::from_value::<PromptRequest>(json!({
7327 "sessionId": "sess-1",
7328 "prompt": "hello"
7329 }))
7330 .is_err()
7331 );
7332 }
7333
7334 fn assert_session_setup_lists_are_strict<T>(base: &serde_json::Value)
7335 where
7336 T: serde::de::DeserializeOwned + PartialEq + std::fmt::Debug,
7337 {
7338 let name = std::any::type_name::<T>();
7339 let with = |field: &str, value: serde_json::Value| {
7340 let mut request = base.clone();
7341 request[field] = value;
7342 serde_json::from_value::<T>(request)
7343 };
7344
7345 for (field, value) in [
7346 (
7348 "mcpServers",
7349 json!([
7350 {"type": "stdio", "name": "github", "command": "/usr/bin/gh-mcp", "env": [{"name": "GITHUB_TOKEN", "value": null}]},
7351 {"type": "stdio", "name": "fs", "command": "/usr/bin/fs-mcp"}
7352 ]),
7353 ),
7354 ("mcpServers", json!({"type": "stdio", "name": "fs"})),
7355 ("additionalDirectories", json!(["/repo/lib", 42])),
7356 ("additionalDirectories", json!("/repo/lib")),
7357 ] {
7358 assert!(
7359 with(field, value.clone()).is_err(),
7360 "{name}.{field}: {value}"
7361 );
7362 }
7363
7364 let empty = serde_json::from_value::<T>(base.clone()).unwrap();
7365 for field in ["mcpServers", "additionalDirectories"] {
7366 assert_eq!(
7367 with(field, serde_json::Value::Null).unwrap(),
7368 empty,
7369 "{name}.{field}"
7370 );
7371 }
7372 }
7373
7374 #[test]
7375 fn session_setup_requests_reject_malformed_mcp_servers_and_directories() {
7376 assert_session_setup_lists_are_strict::<NewSessionRequest>(&json!({"cwd": "/repo"}));
7377 assert_session_setup_lists_are_strict::<ResumeSessionRequest>(&json!({
7378 "sessionId": "sess-1",
7379 "cwd": "/repo"
7380 }));
7381 #[cfg(feature = "unstable_session_fork")]
7382 assert_session_setup_lists_are_strict::<ForkSessionRequest>(&json!({
7383 "sessionId": "sess-1",
7384 "cwd": "/repo"
7385 }));
7386 }
7387
7388 #[test]
7389 fn resume_session_request_rejects_malformed_replay_from() {
7390 let request = |replay_from: serde_json::Value| {
7391 serde_json::from_value::<ResumeSessionRequest>(json!({
7392 "sessionId": "sess-1",
7393 "cwd": "/repo",
7394 "replayFrom": replay_from
7395 }))
7396 };
7397
7398 assert!(request(json!("start")).is_err());
7400 assert_eq!(request(serde_json::Value::Null).unwrap().replay_from, None);
7401 assert!(matches!(
7402 request(json!({"type": "start"})).unwrap().replay_from,
7403 Some(ReplayFrom::Start(_))
7404 ));
7405 }
7406
7407 #[test]
7408 fn mcp_server_lists_treat_null_as_empty() {
7409 let servers: Vec<McpServer> = serde_json::from_value(json!([
7410 {"type": "stdio", "name": "fs", "command": "/usr/bin/fs-mcp", "args": null, "env": null},
7411 {"type": "http", "name": "api", "url": "https://example.com/mcp", "headers": null}
7412 ]))
7413 .unwrap();
7414
7415 assert_eq!(
7416 servers,
7417 vec![
7418 McpServer::Stdio(McpServerStdio::new("fs", "/usr/bin/fs-mcp")),
7419 McpServer::Http(McpServerHttp::new("api", "https://example.com/mcp")),
7420 ]
7421 );
7422 }
7423
7424 #[test]
7425 fn terminal_auth_methods_reject_malformed_launch_config() {
7426 let response: InitializeResponse = serde_json::from_value(json!({
7430 "protocolVersion": 2,
7431 "info": {"name": "agent", "version": "1.0.0"},
7432 "authMethods": [
7433 {"type": "terminal", "methodId": "bad-args", "name": "Log in", "args": ["auth", "--port", 8123]},
7434 {"type": "terminal", "methodId": "bad-env", "name": "Log in", "env": [{"name": "DEBUG", "value": 1}]},
7435 {"type": "terminal", "methodId": "null-config", "name": "Log in", "args": null, "env": null},
7436 {"type": "agent", "methodId": "agent", "name": "Agent login"}
7437 ]
7438 }))
7439 .unwrap();
7440
7441 assert_eq!(
7442 response.auth_methods,
7443 vec![
7444 AuthMethod::Terminal(AuthMethodTerminal::new("null-config", "Log in")),
7445 AuthMethod::Agent(AuthMethodAgent::new("agent", "Agent login")),
7446 ]
7447 );
7448 }
7449}