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, VecSkipError, serde_as, skip_serializing_none};
16
17use super::{
18 AbsolutePath, ClientCapabilities, ContentBlock, ExtNotification, ExtRequest, ExtResponse,
19 MessageId, Meta, SessionId,
20};
21use crate::{IntoOption, ProtocolVersion, SkipListener};
22
23#[cfg(feature = "unstable_mcp_over_acp")]
24use super::mcp::{
25 MCP_MESSAGE_METHOD_NAME, MessageMcpNotification, MessageMcpRequest, MessageMcpResponse,
26};
27
28#[cfg(feature = "unstable_nes")]
29use super::{
30 AcceptNesNotification, CloseNesRequest, CloseNesResponse, DidChangeDocumentNotification,
31 DidCloseDocumentNotification, DidFocusDocumentNotification, DidOpenDocumentNotification,
32 DidSaveDocumentNotification, NesCapabilities, PositionEncodingKind, RejectNesNotification,
33 StartNesRequest, StartNesResponse, SuggestNesRequest, SuggestNesResponse,
34};
35
36#[cfg(feature = "unstable_nes")]
37use super::{
38 DOCUMENT_DID_CHANGE_METHOD_NAME, DOCUMENT_DID_CLOSE_METHOD_NAME,
39 DOCUMENT_DID_FOCUS_METHOD_NAME, DOCUMENT_DID_OPEN_METHOD_NAME, DOCUMENT_DID_SAVE_METHOD_NAME,
40 NES_ACCEPT_METHOD_NAME, NES_CLOSE_METHOD_NAME, NES_REJECT_METHOD_NAME, NES_START_METHOD_NAME,
41 NES_SUGGEST_METHOD_NAME,
42};
43
44#[serde_as]
52#[skip_serializing_none]
53#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
54#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
55#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = INITIALIZE_METHOD_NAME)))]
56#[serde(rename_all = "camelCase")]
57#[non_exhaustive]
58pub struct InitializeRequest {
59 pub protocol_version: ProtocolVersion,
61 pub info: Implementation,
63 #[serde_as(deserialize_as = "DefaultOnError")]
65 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
66 #[serde(default)]
67 pub capabilities: ClientCapabilities,
68 #[serde_as(deserialize_as = "DefaultOnError")]
74 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
75 #[serde(default)]
76 #[serde(rename = "_meta")]
77 pub meta: Option<Meta>,
78}
79
80impl InitializeRequest {
81 #[must_use]
83 pub fn new(protocol_version: ProtocolVersion, info: Implementation) -> Self {
84 Self {
85 protocol_version,
86 capabilities: ClientCapabilities::default(),
87 info,
88 meta: None,
89 }
90 }
91
92 #[must_use]
94 pub fn capabilities(mut self, capabilities: ClientCapabilities) -> Self {
95 self.capabilities = capabilities;
96 self
97 }
98
99 #[must_use]
105 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
106 self.meta = meta.into_option();
107 self
108 }
109}
110
111#[serde_as]
117#[skip_serializing_none]
118#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
119#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
120#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = INITIALIZE_METHOD_NAME)))]
121#[serde(rename_all = "camelCase")]
122#[non_exhaustive]
123pub struct InitializeResponse {
124 pub protocol_version: ProtocolVersion,
129 pub info: Implementation,
131 #[serde_as(deserialize_as = "DefaultOnError")]
133 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
134 #[serde(default)]
135 pub capabilities: AgentCapabilities,
136 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
142 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
143 #[serde(default, skip_serializing_if = "Vec::is_empty")]
144 pub auth_methods: Vec<AuthMethod>,
145 #[serde_as(deserialize_as = "DefaultOnError")]
151 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
152 #[serde(default)]
153 #[serde(rename = "_meta")]
154 pub meta: Option<Meta>,
155}
156
157impl InitializeResponse {
158 #[must_use]
160 pub fn new(protocol_version: ProtocolVersion, info: Implementation) -> Self {
161 Self {
162 protocol_version,
163 capabilities: AgentCapabilities::default(),
164 auth_methods: vec![],
165 info,
166 meta: None,
167 }
168 }
169
170 #[must_use]
172 pub fn capabilities(mut self, capabilities: AgentCapabilities) -> Self {
173 self.capabilities = capabilities;
174 self
175 }
176
177 #[must_use]
182 pub fn auth_methods(mut self, auth_methods: Vec<AuthMethod>) -> Self {
183 self.auth_methods = auth_methods;
184 self
185 }
186
187 #[must_use]
193 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
194 self.meta = meta.into_option();
195 self
196 }
197}
198
199#[serde_as]
203#[skip_serializing_none]
204#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
205#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
206#[serde(rename_all = "camelCase")]
207#[non_exhaustive]
208pub struct Implementation {
209 pub name: String,
212 #[serde_as(deserialize_as = "DefaultOnError")]
217 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
218 #[serde(default)]
219 pub title: Option<String>,
220 pub version: String,
223 #[serde_as(deserialize_as = "DefaultOnError")]
229 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
230 #[serde(default)]
231 #[serde(rename = "_meta")]
232 pub meta: Option<Meta>,
233}
234
235impl Implementation {
236 #[must_use]
238 pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
239 Self {
240 name: name.into(),
241 title: None,
242 version: version.into(),
243 meta: None,
244 }
245 }
246
247 #[must_use]
252 pub fn title(mut self, title: impl IntoOption<String>) -> Self {
253 self.title = title.into_option();
254 self
255 }
256
257 #[must_use]
263 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
264 self.meta = meta.into_option();
265 self
266 }
267}
268
269#[serde_as]
279#[skip_serializing_none]
280#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
281#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
282#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGIN_METHOD_NAME)))]
283#[serde(rename_all = "camelCase")]
284#[non_exhaustive]
285pub struct LoginAuthRequest {
286 pub method_id: AuthMethodId,
289 #[serde_as(deserialize_as = "DefaultOnError")]
295 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
296 #[serde(default)]
297 #[serde(rename = "_meta")]
298 pub meta: Option<Meta>,
299}
300
301impl LoginAuthRequest {
302 #[must_use]
304 pub fn new(method_id: impl Into<AuthMethodId>) -> Self {
305 Self {
306 method_id: method_id.into(),
307 meta: None,
308 }
309 }
310
311 #[must_use]
317 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
318 self.meta = meta.into_option();
319 self
320 }
321}
322
323#[serde_as]
325#[skip_serializing_none]
326#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
327#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
328#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGIN_METHOD_NAME)))]
329#[serde(rename_all = "camelCase")]
330#[non_exhaustive]
331pub struct LoginAuthResponse {
332 #[serde_as(deserialize_as = "DefaultOnError")]
338 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
339 #[serde(default)]
340 #[serde(rename = "_meta")]
341 pub meta: Option<Meta>,
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
363#[serde_as]
373#[skip_serializing_none]
374#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
375#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
376#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGOUT_METHOD_NAME)))]
377#[serde(rename_all = "camelCase")]
378#[non_exhaustive]
379pub struct LogoutAuthRequest {
380 #[serde_as(deserialize_as = "DefaultOnError")]
386 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
387 #[serde(default)]
388 #[serde(rename = "_meta")]
389 pub meta: Option<Meta>,
390}
391
392impl LogoutAuthRequest {
393 #[must_use]
395 pub fn new() -> Self {
396 Self::default()
397 }
398
399 #[must_use]
405 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
406 self.meta = meta.into_option();
407 self
408 }
409}
410
411#[serde_as]
413#[skip_serializing_none]
414#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
415#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
416#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGOUT_METHOD_NAME)))]
417#[serde(rename_all = "camelCase")]
418#[non_exhaustive]
419pub struct LogoutAuthResponse {
420 #[serde_as(deserialize_as = "DefaultOnError")]
426 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
427 #[serde(default)]
428 #[serde(rename = "_meta")]
429 pub meta: Option<Meta>,
430}
431
432impl LogoutAuthResponse {
433 #[must_use]
435 pub fn new() -> Self {
436 Self::default()
437 }
438
439 #[must_use]
445 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
446 self.meta = meta.into_option();
447 self
448 }
449}
450
451#[serde_as]
457#[skip_serializing_none]
458#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
459#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
460#[serde(rename_all = "camelCase")]
461#[non_exhaustive]
462pub struct AgentAuthCapabilities {
463 #[serde_as(deserialize_as = "DefaultOnError")]
469 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
470 #[serde(default)]
471 #[serde(rename = "_meta")]
472 pub meta: Option<Meta>,
473}
474
475impl AgentAuthCapabilities {
476 #[must_use]
478 pub fn new() -> Self {
479 Self::default()
480 }
481
482 #[must_use]
488 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
489 self.meta = meta.into_option();
490 self
491 }
492}
493
494#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
496#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
497#[serde(transparent)]
498#[from(forward)]
499#[non_exhaustive]
500pub struct AuthMethodId(pub Arc<str>);
501
502impl AuthMethodId {
503 #[must_use]
505 pub fn new(id: impl Into<Self>) -> Self {
506 id.into()
507 }
508}
509
510#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
514#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
515#[serde(tag = "type", rename_all = "snake_case")]
516#[non_exhaustive]
517pub enum AuthMethod {
518 Terminal(AuthMethodTerminal),
521 Agent(AuthMethodAgent),
525 #[serde(untagged)]
535 Other(OtherAuthMethod),
536}
537
538impl AuthMethod {
539 #[must_use]
541 pub fn method_id(&self) -> &AuthMethodId {
542 match self {
543 Self::Agent(a) => &a.method_id,
544 Self::Other(a) => &a.method_id,
545 Self::Terminal(t) => &t.method_id,
546 }
547 }
548
549 #[must_use]
551 pub fn name(&self) -> &str {
552 match self {
553 Self::Agent(a) => &a.name,
554 Self::Other(a) => &a.name,
555 Self::Terminal(t) => &t.name,
556 }
557 }
558
559 #[must_use]
561 pub fn description(&self) -> Option<&str> {
562 match self {
563 Self::Agent(a) => a.description.as_deref(),
564 Self::Other(a) => a.description.as_deref(),
565 Self::Terminal(t) => t.description.as_deref(),
566 }
567 }
568
569 #[must_use]
575 pub fn meta(&self) -> Option<&Meta> {
576 match self {
577 Self::Agent(a) => a.meta.as_ref(),
578 Self::Other(a) => a.meta.as_ref(),
579 Self::Terminal(t) => t.meta.as_ref(),
580 }
581 }
582}
583
584#[serde_as]
586#[skip_serializing_none]
587#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
588#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
589#[cfg_attr(feature = "schemars", schemars(inline))]
590#[cfg_attr(feature = "schemars", schemars(transform = other_auth_method_schema))]
591#[serde(rename_all = "camelCase")]
592#[non_exhaustive]
593pub struct OtherAuthMethod {
594 #[serde(rename = "type")]
600 pub type_: String,
601 pub method_id: AuthMethodId,
603 pub name: String,
605 #[serde_as(deserialize_as = "DefaultOnError")]
607 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
608 #[serde(default)]
609 pub description: Option<String>,
610 #[serde_as(deserialize_as = "DefaultOnError")]
616 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
617 #[serde(default)]
618 #[serde(rename = "_meta")]
619 pub meta: Option<Meta>,
620 #[serde(flatten)]
622 pub fields: BTreeMap<String, serde_json::Value>,
623}
624
625impl OtherAuthMethod {
626 #[must_use]
628 pub fn new(
629 type_: impl Into<String>,
630 method_id: impl Into<AuthMethodId>,
631 name: impl Into<String>,
632 mut fields: BTreeMap<String, serde_json::Value>,
633 ) -> Self {
634 fields.remove("type");
635 fields.remove("methodId");
636 fields.remove("name");
637 fields.remove("description");
638 fields.remove("_meta");
639 Self {
640 type_: type_.into(),
641 method_id: method_id.into(),
642 name: name.into(),
643 description: None,
644 meta: None,
645 fields,
646 }
647 }
648
649 #[must_use]
651 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
652 self.description = description.into_option();
653 self
654 }
655
656 #[must_use]
662 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
663 self.meta = meta.into_option();
664 self
665 }
666}
667
668impl<'de> Deserialize<'de> for OtherAuthMethod {
669 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
670 where
671 D: serde::Deserializer<'de>,
672 {
673 #[derive(Deserialize)]
674 #[serde(rename_all = "camelCase")]
675 struct RawOtherAuthMethod {
676 #[serde(rename = "type")]
677 type_: String,
678 method_id: AuthMethodId,
679 name: String,
680 description: Option<String>,
681 #[serde(rename = "_meta")]
682 meta: Option<Meta>,
683 #[serde(flatten)]
684 fields: BTreeMap<String, serde_json::Value>,
685 }
686
687 let raw = RawOtherAuthMethod::deserialize(deserializer)?;
688 if is_known_auth_method_type(&raw.type_) {
689 return Err(serde::de::Error::custom(format!(
690 "known authentication method `{}` did not match its schema",
691 raw.type_
692 )));
693 }
694
695 Ok(Self {
696 type_: raw.type_,
697 method_id: raw.method_id,
698 name: raw.name,
699 description: raw.description,
700 meta: raw.meta,
701 fields: raw.fields,
702 })
703 }
704}
705
706fn is_known_auth_method_type(type_: &str) -> bool {
707 matches!(type_, "agent" | "terminal")
708}
709
710#[cfg(feature = "schemars")]
711fn other_auth_method_schema(schema: &mut Schema) {
712 super::schema_util::reject_known_string_discriminators(schema, "type", &["agent", "terminal"]);
713}
714
715#[serde_as]
719#[skip_serializing_none]
720#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
721#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
722#[serde(rename_all = "camelCase")]
723#[non_exhaustive]
724pub struct AuthMethodAgent {
725 pub method_id: AuthMethodId,
727 pub name: String,
729 #[serde_as(deserialize_as = "DefaultOnError")]
731 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
732 #[serde(default)]
733 pub description: Option<String>,
734 #[serde_as(deserialize_as = "DefaultOnError")]
740 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
741 #[serde(default)]
742 #[serde(rename = "_meta")]
743 pub meta: Option<Meta>,
744}
745
746impl AuthMethodAgent {
747 #[must_use]
749 pub fn new(method_id: impl Into<AuthMethodId>, name: impl Into<String>) -> Self {
750 Self {
751 method_id: method_id.into(),
752 name: name.into(),
753 description: None,
754 meta: None,
755 }
756 }
757
758 #[must_use]
760 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
761 self.description = description.into_option();
762 self
763 }
764
765 #[must_use]
771 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
772 self.meta = meta.into_option();
773 self
774 }
775}
776
777#[serde_as]
785#[skip_serializing_none]
786#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
787#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
788#[serde(rename_all = "camelCase")]
789#[non_exhaustive]
790pub struct AuthMethodTerminal {
791 pub method_id: AuthMethodId,
793 pub name: String,
795 #[serde_as(deserialize_as = "DefaultOnError")]
797 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
798 #[serde(default)]
799 pub description: Option<String>,
800 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
802 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
803 #[serde(default, skip_serializing_if = "Vec::is_empty")]
804 pub args: Vec<String>,
805 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
809 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
810 #[serde(default, skip_serializing_if = "Vec::is_empty")]
811 pub env: Vec<EnvVariable>,
812 #[serde_as(deserialize_as = "DefaultOnError")]
818 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
819 #[serde(default)]
820 #[serde(rename = "_meta")]
821 pub meta: Option<Meta>,
822}
823
824impl AuthMethodTerminal {
825 #[must_use]
827 pub fn new(method_id: impl Into<AuthMethodId>, name: impl Into<String>) -> Self {
828 Self {
829 method_id: method_id.into(),
830 name: name.into(),
831 description: None,
832 args: Vec::new(),
833 env: Vec::new(),
834 meta: None,
835 }
836 }
837
838 #[must_use]
840 pub fn args(mut self, args: Vec<String>) -> Self {
841 self.args = args;
842 self
843 }
844
845 #[must_use]
849 pub fn env(mut self, env: Vec<EnvVariable>) -> Self {
850 self.env = env;
851 self
852 }
853
854 #[must_use]
856 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
857 self.description = description.into_option();
858 self
859 }
860
861 #[must_use]
867 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
868 self.meta = meta.into_option();
869 self
870 }
871}
872
873#[serde_as]
879#[skip_serializing_none]
880#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
881#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
882#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_NEW_METHOD_NAME)))]
883#[serde(rename_all = "camelCase")]
884#[non_exhaustive]
885pub struct NewSessionRequest {
886 pub cwd: AbsolutePath,
888 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
894 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
895 #[serde(default, skip_serializing_if = "Vec::is_empty")]
896 pub additional_directories: Vec<AbsolutePath>,
897 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
899 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
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<_, SkipListener>>")]
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")]
982 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
983 #[serde(default)]
984 #[serde(rename = "_meta")]
985 pub meta: Option<Meta>,
986}
987
988impl NewSessionResponse {
989 #[must_use]
991 pub fn new(session_id: impl Into<SessionId>) -> Self {
992 Self {
993 session_id: session_id.into(),
994 config_options: Vec::new(),
995 meta: None,
996 }
997 }
998
999 #[must_use]
1001 pub fn config_options(mut self, config_options: Vec<SessionConfigOption>) -> Self {
1002 self.config_options = config_options;
1003 self
1004 }
1005
1006 #[must_use]
1012 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1013 self.meta = meta.into_option();
1014 self
1015 }
1016}
1017
1018#[cfg(feature = "unstable_session_fork")]
1031#[serde_as]
1032#[skip_serializing_none]
1033#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1034#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1035#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_FORK_METHOD_NAME)))]
1036#[serde(rename_all = "camelCase")]
1037#[non_exhaustive]
1038pub struct ForkSessionRequest {
1039 pub session_id: SessionId,
1041 pub cwd: AbsolutePath,
1043 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1049 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1050 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1051 pub additional_directories: Vec<AbsolutePath>,
1052 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1054 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1055 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1056 pub mcp_servers: Vec<McpServer>,
1057 #[serde_as(deserialize_as = "DefaultOnError")]
1063 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1064 #[serde(default)]
1065 #[serde(rename = "_meta")]
1066 pub meta: Option<Meta>,
1067}
1068
1069#[cfg(feature = "unstable_session_fork")]
1070impl ForkSessionRequest {
1071 #[must_use]
1073 pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<AbsolutePath>) -> Self {
1074 Self {
1075 session_id: session_id.into(),
1076 cwd: cwd.into(),
1077 additional_directories: vec![],
1078 mcp_servers: vec![],
1079 meta: None,
1080 }
1081 }
1082
1083 #[must_use]
1085 pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
1086 where
1087 I: IntoIterator<Item = P>,
1088 P: Into<AbsolutePath>,
1089 {
1090 self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
1091 self
1092 }
1093
1094 #[must_use]
1096 pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1097 self.mcp_servers = mcp_servers;
1098 self
1099 }
1100
1101 #[must_use]
1107 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1108 self.meta = meta.into_option();
1109 self
1110 }
1111}
1112
1113#[cfg(feature = "unstable_session_fork")]
1119#[serde_as]
1120#[skip_serializing_none]
1121#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1122#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1123#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_FORK_METHOD_NAME)))]
1124#[serde(rename_all = "camelCase")]
1125#[non_exhaustive]
1126pub struct ForkSessionResponse {
1127 pub session_id: SessionId,
1129 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1131 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1132 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1133 pub config_options: Vec<SessionConfigOption>,
1134 #[serde_as(deserialize_as = "DefaultOnError")]
1140 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1141 #[serde(default)]
1142 #[serde(rename = "_meta")]
1143 pub meta: Option<Meta>,
1144}
1145
1146#[cfg(feature = "unstable_session_fork")]
1147impl ForkSessionResponse {
1148 #[must_use]
1150 pub fn new(session_id: impl Into<SessionId>) -> Self {
1151 Self {
1152 session_id: session_id.into(),
1153 config_options: Vec::new(),
1154 meta: None,
1155 }
1156 }
1157
1158 #[must_use]
1160 pub fn config_options(mut self, config_options: Vec<SessionConfigOption>) -> Self {
1161 self.config_options = config_options;
1162 self
1163 }
1164
1165 #[must_use]
1171 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1172 self.meta = meta.into_option();
1173 self
1174 }
1175}
1176
1177#[serde_as]
1184#[skip_serializing_none]
1185#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1186#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1187#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_RESUME_METHOD_NAME)))]
1188#[serde(rename_all = "camelCase")]
1189#[non_exhaustive]
1190pub struct ResumeSessionRequest {
1191 pub session_id: SessionId,
1193 pub cwd: AbsolutePath,
1195 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1202 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1203 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1204 pub additional_directories: Vec<AbsolutePath>,
1205 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1207 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1208 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1209 pub mcp_servers: Vec<McpServer>,
1210 #[serde_as(deserialize_as = "DefaultOnError")]
1218 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1219 #[serde(default)]
1220 pub replay_from: Option<ReplayFrom>,
1221 #[serde_as(deserialize_as = "DefaultOnError")]
1227 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1228 #[serde(default)]
1229 #[serde(rename = "_meta")]
1230 pub meta: Option<Meta>,
1231}
1232
1233impl ResumeSessionRequest {
1234 #[must_use]
1236 pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<AbsolutePath>) -> Self {
1237 Self {
1238 session_id: session_id.into(),
1239 cwd: cwd.into(),
1240 additional_directories: vec![],
1241 mcp_servers: vec![],
1242 replay_from: None,
1243 meta: None,
1244 }
1245 }
1246
1247 #[must_use]
1249 pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
1250 where
1251 I: IntoIterator<Item = P>,
1252 P: Into<AbsolutePath>,
1253 {
1254 self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
1255 self
1256 }
1257
1258 #[must_use]
1260 pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1261 self.mcp_servers = mcp_servers;
1262 self
1263 }
1264
1265 #[must_use]
1273 pub fn replay_from(mut self, replay_from: impl IntoOption<ReplayFrom>) -> Self {
1274 self.replay_from = replay_from.into_option();
1275 self
1276 }
1277
1278 #[must_use]
1284 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1285 self.meta = meta.into_option();
1286 self
1287 }
1288}
1289
1290#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1294#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1295#[serde(tag = "type", rename_all = "snake_case")]
1296#[non_exhaustive]
1297pub enum ReplayFrom {
1298 Start(ReplayFromStart),
1300 #[serde(untagged)]
1310 Other(OtherReplayFrom),
1311}
1312
1313impl From<ReplayFromStart> for ReplayFrom {
1314 fn from(replay_from: ReplayFromStart) -> Self {
1315 Self::Start(replay_from)
1316 }
1317}
1318
1319#[serde_as]
1321#[skip_serializing_none]
1322#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1323#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1324#[serde(rename_all = "camelCase")]
1325#[non_exhaustive]
1326pub struct ReplayFromStart {
1327 #[serde_as(deserialize_as = "DefaultOnError")]
1333 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1334 #[serde(default)]
1335 #[serde(rename = "_meta")]
1336 pub meta: Option<Meta>,
1337}
1338
1339impl ReplayFromStart {
1340 #[must_use]
1342 pub fn new() -> Self {
1343 Self::default()
1344 }
1345
1346 #[must_use]
1352 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1353 self.meta = meta.into_option();
1354 self
1355 }
1356}
1357
1358#[serde_as]
1360#[skip_serializing_none]
1361#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1362#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
1363#[cfg_attr(feature = "schemars", schemars(inline))]
1364#[cfg_attr(feature = "schemars", schemars(transform = other_replay_from_schema))]
1365#[serde(rename_all = "camelCase")]
1366#[non_exhaustive]
1367pub struct OtherReplayFrom {
1368 #[serde(rename = "type")]
1374 pub type_: String,
1375 #[serde_as(deserialize_as = "DefaultOnError")]
1381 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1382 #[serde(default)]
1383 #[serde(rename = "_meta")]
1384 pub meta: Option<Meta>,
1385 #[serde(flatten)]
1387 pub fields: BTreeMap<String, serde_json::Value>,
1388}
1389
1390impl OtherReplayFrom {
1391 #[must_use]
1393 pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
1394 fields.remove("type");
1395 fields.remove("_meta");
1396 Self {
1397 type_: type_.into(),
1398 meta: None,
1399 fields,
1400 }
1401 }
1402
1403 #[must_use]
1409 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1410 self.meta = meta.into_option();
1411 self
1412 }
1413}
1414
1415impl<'de> Deserialize<'de> for OtherReplayFrom {
1416 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1417 where
1418 D: serde::Deserializer<'de>,
1419 {
1420 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
1421 let type_ = fields
1422 .remove("type")
1423 .ok_or_else(|| serde::de::Error::missing_field("type"))?;
1424 let serde_json::Value::String(type_) = type_ else {
1425 return Err(serde::de::Error::custom("`type` must be a string"));
1426 };
1427
1428 if is_known_replay_from_type(&type_) {
1429 return Err(serde::de::Error::custom(format!(
1430 "known replay cursor `{type_}` did not match its schema"
1431 )));
1432 }
1433
1434 let meta = fields
1435 .remove("_meta")
1436 .and_then(|value| serde_json::from_value(value).ok());
1437
1438 Ok(Self {
1439 type_,
1440 meta,
1441 fields,
1442 })
1443 }
1444}
1445
1446fn is_known_replay_from_type(type_: &str) -> bool {
1447 matches!(type_, "start")
1448}
1449
1450#[cfg(feature = "schemars")]
1451fn other_replay_from_schema(schema: &mut Schema) {
1452 super::schema_util::reject_known_string_discriminators(schema, "type", &["start"]);
1453}
1454
1455#[serde_as]
1457#[skip_serializing_none]
1458#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1459#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1460#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_RESUME_METHOD_NAME)))]
1461#[serde(rename_all = "camelCase")]
1462#[non_exhaustive]
1463pub struct ResumeSessionResponse {
1464 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1466 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1467 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1468 pub config_options: Vec<SessionConfigOption>,
1469 #[serde_as(deserialize_as = "DefaultOnError")]
1475 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1476 #[serde(default)]
1477 #[serde(rename = "_meta")]
1478 pub meta: Option<Meta>,
1479}
1480
1481impl ResumeSessionResponse {
1482 #[must_use]
1484 pub fn new() -> Self {
1485 Self::default()
1486 }
1487
1488 #[must_use]
1490 pub fn config_options(mut self, config_options: Vec<SessionConfigOption>) -> Self {
1491 self.config_options = config_options;
1492 self
1493 }
1494
1495 #[must_use]
1501 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1502 self.meta = meta.into_option();
1503 self
1504 }
1505}
1506
1507#[serde_as]
1515#[skip_serializing_none]
1516#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1517#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1518#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_CLOSE_METHOD_NAME)))]
1519#[serde(rename_all = "camelCase")]
1520#[non_exhaustive]
1521pub struct CloseSessionRequest {
1522 pub session_id: SessionId,
1524 #[serde_as(deserialize_as = "DefaultOnError")]
1530 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1531 #[serde(default)]
1532 #[serde(rename = "_meta")]
1533 pub meta: Option<Meta>,
1534}
1535
1536impl CloseSessionRequest {
1537 #[must_use]
1539 pub fn new(session_id: impl Into<SessionId>) -> Self {
1540 Self {
1541 session_id: session_id.into(),
1542 meta: None,
1543 }
1544 }
1545
1546 #[must_use]
1552 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1553 self.meta = meta.into_option();
1554 self
1555 }
1556}
1557
1558#[serde_as]
1560#[skip_serializing_none]
1561#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1562#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1563#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_CLOSE_METHOD_NAME)))]
1564#[serde(rename_all = "camelCase")]
1565#[non_exhaustive]
1566pub struct CloseSessionResponse {
1567 #[serde_as(deserialize_as = "DefaultOnError")]
1573 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1574 #[serde(default)]
1575 #[serde(rename = "_meta")]
1576 pub meta: Option<Meta>,
1577}
1578
1579impl CloseSessionResponse {
1580 #[must_use]
1582 pub fn new() -> Self {
1583 Self::default()
1584 }
1585
1586 #[must_use]
1592 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1593 self.meta = meta.into_option();
1594 self
1595 }
1596}
1597
1598#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1602#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
1603#[serde(transparent)]
1604#[from(Arc<str>, String, &str, &mut str, Box<str>, Cow<'_, str>)]
1605#[non_exhaustive]
1606pub struct SessionListCursor(pub Arc<str>);
1607
1608impl SessionListCursor {
1609 #[must_use]
1611 pub fn new(cursor: impl Into<Self>) -> Self {
1612 cursor.into()
1613 }
1614}
1615
1616impl AsRef<str> for SessionListCursor {
1617 fn as_ref(&self) -> &str {
1618 &self.0
1619 }
1620}
1621
1622impl From<&String> for SessionListCursor {
1623 fn from(cursor: &String) -> Self {
1624 Self(cursor.as_str().into())
1625 }
1626}
1627
1628macro_rules! impl_session_list_cursor_option_conversion {
1629 ($source:ty) => {
1630 impl IntoOption<SessionListCursor> for $source {
1631 fn into_option(self) -> Option<SessionListCursor> {
1632 Some(self.into())
1633 }
1634 }
1635 };
1636}
1637
1638impl_session_list_cursor_option_conversion!(Arc<str>);
1639impl_session_list_cursor_option_conversion!(String);
1640impl_session_list_cursor_option_conversion!(&str);
1641impl_session_list_cursor_option_conversion!(&mut str);
1642impl_session_list_cursor_option_conversion!(&String);
1643impl_session_list_cursor_option_conversion!(Box<str>);
1644impl_session_list_cursor_option_conversion!(Cow<'_, str>);
1645
1646#[serde_as]
1648#[skip_serializing_none]
1649#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1650#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1651#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_LIST_METHOD_NAME)))]
1652#[serde(rename_all = "camelCase")]
1653#[non_exhaustive]
1654pub struct ListSessionsRequest {
1655 #[serde(default)]
1657 pub cwd: Option<AbsolutePath>,
1658 #[serde(default)]
1660 pub cursor: Option<SessionListCursor>,
1661 #[serde_as(deserialize_as = "DefaultOnError")]
1667 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1668 #[serde(default)]
1669 #[serde(rename = "_meta")]
1670 pub meta: Option<Meta>,
1671}
1672
1673impl ListSessionsRequest {
1674 #[must_use]
1676 pub fn new() -> Self {
1677 Self::default()
1678 }
1679
1680 #[must_use]
1682 pub fn cwd(mut self, cwd: impl IntoOption<AbsolutePath>) -> Self {
1683 self.cwd = cwd.into_option();
1684 self
1685 }
1686
1687 #[must_use]
1689 pub fn cursor(mut self, cursor: impl IntoOption<SessionListCursor>) -> Self {
1690 self.cursor = cursor.into_option();
1691 self
1692 }
1693
1694 #[must_use]
1700 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1701 self.meta = meta.into_option();
1702 self
1703 }
1704}
1705
1706#[serde_as]
1708#[skip_serializing_none]
1709#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1710#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1711#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_LIST_METHOD_NAME)))]
1712#[serde(rename_all = "camelCase")]
1713#[non_exhaustive]
1714pub struct ListSessionsResponse {
1715 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1717 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1718 pub sessions: Vec<SessionInfo>,
1719 #[serde_as(deserialize_as = "DefaultOnError")]
1722 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1723 #[serde(default)]
1724 pub next_cursor: Option<SessionListCursor>,
1725 #[serde_as(deserialize_as = "DefaultOnError")]
1731 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1732 #[serde(default)]
1733 #[serde(rename = "_meta")]
1734 pub meta: Option<Meta>,
1735}
1736
1737impl ListSessionsResponse {
1738 #[must_use]
1740 pub fn new(sessions: Vec<SessionInfo>) -> Self {
1741 Self {
1742 sessions,
1743 next_cursor: None,
1744 meta: None,
1745 }
1746 }
1747
1748 #[must_use]
1750 pub fn next_cursor(mut self, next_cursor: impl IntoOption<SessionListCursor>) -> Self {
1751 self.next_cursor = next_cursor.into_option();
1752 self
1753 }
1754
1755 #[must_use]
1761 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1762 self.meta = meta.into_option();
1763 self
1764 }
1765}
1766
1767#[serde_as]
1773#[skip_serializing_none]
1774#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1775#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1776#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_DELETE_METHOD_NAME)))]
1777#[serde(rename_all = "camelCase")]
1778#[non_exhaustive]
1779pub struct DeleteSessionRequest {
1780 pub session_id: SessionId,
1782 #[serde_as(deserialize_as = "DefaultOnError")]
1788 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1789 #[serde(default)]
1790 #[serde(rename = "_meta")]
1791 pub meta: Option<Meta>,
1792}
1793
1794impl DeleteSessionRequest {
1795 #[must_use]
1797 pub fn new(session_id: impl Into<SessionId>) -> Self {
1798 Self {
1799 session_id: session_id.into(),
1800 meta: None,
1801 }
1802 }
1803
1804 #[must_use]
1810 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1811 self.meta = meta.into_option();
1812 self
1813 }
1814}
1815
1816#[serde_as]
1818#[skip_serializing_none]
1819#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1820#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1821#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_DELETE_METHOD_NAME)))]
1822#[serde(rename_all = "camelCase")]
1823#[non_exhaustive]
1824pub struct DeleteSessionResponse {
1825 #[serde_as(deserialize_as = "DefaultOnError")]
1831 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1832 #[serde(default)]
1833 #[serde(rename = "_meta")]
1834 pub meta: Option<Meta>,
1835}
1836
1837impl DeleteSessionResponse {
1838 #[must_use]
1840 pub fn new() -> Self {
1841 Self::default()
1842 }
1843
1844 #[must_use]
1850 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1851 self.meta = meta.into_option();
1852 self
1853 }
1854}
1855
1856#[serde_as]
1858#[skip_serializing_none]
1859#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1860#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1861#[serde(rename_all = "camelCase")]
1862#[non_exhaustive]
1863pub struct SessionInfo {
1864 pub session_id: SessionId,
1866 pub cwd: AbsolutePath,
1868 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1874 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1875 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1876 pub additional_directories: Vec<AbsolutePath>,
1877
1878 #[serde_as(deserialize_as = "DefaultOnError")]
1880 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1881 #[serde(default)]
1882 pub title: Option<String>,
1883 #[serde_as(deserialize_as = "DefaultOnError")]
1885 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "format" = "date-time")))]
1886 #[serde(default)]
1887 pub updated_at: Option<String>,
1888 #[serde_as(deserialize_as = "DefaultOnError")]
1894 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1895 #[serde(default)]
1896 #[serde(rename = "_meta")]
1897 pub meta: Option<Meta>,
1898}
1899
1900impl SessionInfo {
1901 #[must_use]
1903 pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<AbsolutePath>) -> Self {
1904 Self {
1905 session_id: session_id.into(),
1906 cwd: cwd.into(),
1907 additional_directories: vec![],
1908 title: None,
1909 updated_at: None,
1910 meta: None,
1911 }
1912 }
1913
1914 #[must_use]
1916 pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
1917 where
1918 I: IntoIterator<Item = P>,
1919 P: Into<AbsolutePath>,
1920 {
1921 self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
1922 self
1923 }
1924
1925 #[must_use]
1927 pub fn title(mut self, title: impl IntoOption<String>) -> Self {
1928 self.title = title.into_option();
1929 self
1930 }
1931
1932 #[must_use]
1934 pub fn updated_at(mut self, updated_at: impl IntoOption<String>) -> Self {
1935 self.updated_at = updated_at.into_option();
1936 self
1937 }
1938
1939 #[must_use]
1945 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1946 self.meta = meta.into_option();
1947 self
1948 }
1949}
1950
1951#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1955#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, From, Display)]
1956#[serde(transparent)]
1957#[from(forward)]
1958#[non_exhaustive]
1959pub struct SessionConfigId(pub Arc<str>);
1960
1961impl SessionConfigId {
1962 #[must_use]
1964 pub fn new(id: impl Into<Self>) -> Self {
1965 id.into()
1966 }
1967}
1968
1969#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1971#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, From, Display)]
1972#[serde(transparent)]
1973#[from(forward)]
1974#[non_exhaustive]
1975pub struct SessionConfigValueId(pub Arc<str>);
1976
1977impl SessionConfigValueId {
1978 #[must_use]
1980 pub fn new(id: impl Into<Self>) -> Self {
1981 id.into()
1982 }
1983}
1984
1985#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1987#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, From, Display)]
1988#[serde(transparent)]
1989#[from(forward)]
1990#[non_exhaustive]
1991pub struct SessionConfigGroupId(pub Arc<str>);
1992
1993impl SessionConfigGroupId {
1994 #[must_use]
1996 pub fn new(id: impl Into<Self>) -> Self {
1997 id.into()
1998 }
1999}
2000
2001#[serde_as]
2003#[skip_serializing_none]
2004#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2005#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2006#[serde(rename_all = "camelCase")]
2007#[non_exhaustive]
2008pub struct SessionConfigSelectOption {
2009 pub value: SessionConfigValueId,
2011 pub name: String,
2013 #[serde_as(deserialize_as = "DefaultOnError")]
2015 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2016 #[serde(default)]
2017 pub description: Option<String>,
2018 #[serde_as(deserialize_as = "DefaultOnError")]
2024 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2025 #[serde(default)]
2026 #[serde(rename = "_meta")]
2027 pub meta: Option<Meta>,
2028}
2029
2030impl SessionConfigSelectOption {
2031 #[must_use]
2033 pub fn new(value: impl Into<SessionConfigValueId>, name: impl Into<String>) -> Self {
2034 Self {
2035 value: value.into(),
2036 name: name.into(),
2037 description: None,
2038 meta: None,
2039 }
2040 }
2041
2042 #[must_use]
2044 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
2045 self.description = description.into_option();
2046 self
2047 }
2048
2049 #[must_use]
2055 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2056 self.meta = meta.into_option();
2057 self
2058 }
2059}
2060
2061#[serde_as]
2063#[skip_serializing_none]
2064#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2065#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2066#[serde(rename_all = "camelCase")]
2067#[non_exhaustive]
2068pub struct SessionConfigSelectGroup {
2069 pub group_id: SessionConfigGroupId,
2071 pub name: String,
2073 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
2075 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
2076 pub options: Vec<SessionConfigSelectOption>,
2077 #[serde_as(deserialize_as = "DefaultOnError")]
2083 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2084 #[serde(default)]
2085 #[serde(rename = "_meta")]
2086 pub meta: Option<Meta>,
2087}
2088
2089impl SessionConfigSelectGroup {
2090 #[must_use]
2092 pub fn new(
2093 group_id: impl Into<SessionConfigGroupId>,
2094 name: impl Into<String>,
2095 options: Vec<SessionConfigSelectOption>,
2096 ) -> Self {
2097 Self {
2098 group_id: group_id.into(),
2099 name: name.into(),
2100 options,
2101 meta: None,
2102 }
2103 }
2104
2105 #[must_use]
2111 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2112 self.meta = meta.into_option();
2113 self
2114 }
2115}
2116
2117#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2119#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2120#[serde(untagged)]
2121#[non_exhaustive]
2122pub enum SessionConfigSelectOptions {
2123 Ungrouped(Vec<SessionConfigSelectOption>),
2125 Grouped(Vec<SessionConfigSelectGroup>),
2127}
2128
2129impl From<Vec<SessionConfigSelectOption>> for SessionConfigSelectOptions {
2130 fn from(options: Vec<SessionConfigSelectOption>) -> Self {
2131 SessionConfigSelectOptions::Ungrouped(options)
2132 }
2133}
2134
2135impl From<Vec<SessionConfigSelectGroup>> for SessionConfigSelectOptions {
2136 fn from(groups: Vec<SessionConfigSelectGroup>) -> Self {
2137 SessionConfigSelectOptions::Grouped(groups)
2138 }
2139}
2140
2141#[skip_serializing_none]
2143#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2144#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2145#[serde(rename_all = "camelCase")]
2146#[non_exhaustive]
2147pub struct SessionConfigSelect {
2148 pub current_value: SessionConfigValueId,
2150 pub options: SessionConfigSelectOptions,
2152}
2153
2154impl SessionConfigSelect {
2155 #[must_use]
2157 pub fn new(
2158 current_value: impl Into<SessionConfigValueId>,
2159 options: impl Into<SessionConfigSelectOptions>,
2160 ) -> Self {
2161 Self {
2162 current_value: current_value.into(),
2163 options: options.into(),
2164 }
2165 }
2166}
2167
2168#[skip_serializing_none]
2170#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2171#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2172#[serde(rename_all = "camelCase")]
2173#[non_exhaustive]
2174pub struct SessionConfigBoolean {
2175 pub current_value: bool,
2177}
2178
2179impl SessionConfigBoolean {
2180 #[must_use]
2182 pub fn new(current_value: bool) -> Self {
2183 Self { current_value }
2184 }
2185}
2186
2187#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2197#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2198#[serde(rename_all = "snake_case")]
2199#[non_exhaustive]
2200pub enum SessionConfigOptionCategory {
2201 Mode,
2203 Model,
2205 ModelConfig,
2207 ThoughtLevel,
2209 #[serde(untagged)]
2215 Other(String),
2216}
2217
2218#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2220#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2221#[serde(tag = "type", rename_all = "snake_case")]
2222#[non_exhaustive]
2223pub enum SessionConfigKind {
2224 Select(SessionConfigSelect),
2226 Boolean(SessionConfigBoolean),
2228 #[serde(untagged)]
2238 Other(OtherSessionConfigKind),
2239}
2240
2241#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2243#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
2244#[cfg_attr(feature = "schemars", schemars(inline))]
2245#[cfg_attr(feature = "schemars", schemars(transform = other_session_config_kind_schema))]
2246#[serde(rename_all = "camelCase")]
2247#[non_exhaustive]
2248pub struct OtherSessionConfigKind {
2249 #[serde(rename = "type")]
2255 pub type_: String,
2256 #[serde(flatten)]
2258 pub fields: BTreeMap<String, serde_json::Value>,
2259}
2260
2261impl OtherSessionConfigKind {
2262 #[must_use]
2264 pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
2265 fields.remove("type");
2266 fields.remove("_meta");
2267 Self {
2268 type_: type_.into(),
2269 fields,
2270 }
2271 }
2272}
2273
2274impl<'de> Deserialize<'de> for OtherSessionConfigKind {
2275 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2276 where
2277 D: serde::Deserializer<'de>,
2278 {
2279 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
2280 let type_ = fields
2281 .remove("type")
2282 .ok_or_else(|| serde::de::Error::missing_field("type"))?;
2283 let serde_json::Value::String(type_) = type_ else {
2284 return Err(serde::de::Error::custom("`type` must be a string"));
2285 };
2286
2287 if is_known_session_config_kind_type(&type_) {
2288 return Err(serde::de::Error::custom(format!(
2289 "known session configuration option `{type_}` did not match its schema"
2290 )));
2291 }
2292
2293 Ok(Self { type_, fields })
2294 }
2295}
2296
2297fn is_known_session_config_kind_type(type_: &str) -> bool {
2298 matches!(type_, "select" | "boolean")
2299}
2300
2301#[cfg(feature = "schemars")]
2302fn other_session_config_kind_schema(schema: &mut Schema) {
2303 super::schema_util::reject_known_string_discriminators(schema, "type", &["select", "boolean"]);
2304}
2305
2306#[serde_as]
2308#[skip_serializing_none]
2309#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2310#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2311#[serde(rename_all = "camelCase")]
2312#[non_exhaustive]
2313pub struct SessionConfigOption {
2314 pub config_id: SessionConfigId,
2316 pub name: String,
2318 #[serde_as(deserialize_as = "DefaultOnError")]
2320 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2321 #[serde(default)]
2322 pub description: Option<String>,
2323 #[serde_as(deserialize_as = "DefaultOnError")]
2325 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2326 #[serde(default)]
2327 pub category: Option<SessionConfigOptionCategory>,
2328 #[serde(flatten)]
2330 pub kind: SessionConfigKind,
2331 #[serde_as(deserialize_as = "DefaultOnError")]
2337 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2338 #[serde(default)]
2339 #[serde(rename = "_meta")]
2340 pub meta: Option<Meta>,
2341}
2342
2343impl SessionConfigOption {
2344 #[must_use]
2346 pub fn new(
2347 config_id: impl Into<SessionConfigId>,
2348 name: impl Into<String>,
2349 kind: SessionConfigKind,
2350 ) -> Self {
2351 Self {
2352 config_id: config_id.into(),
2353 name: name.into(),
2354 description: None,
2355 category: None,
2356 kind,
2357 meta: None,
2358 }
2359 }
2360
2361 #[must_use]
2363 pub fn select(
2364 config_id: impl Into<SessionConfigId>,
2365 name: impl Into<String>,
2366 current_value: impl Into<SessionConfigValueId>,
2367 options: impl Into<SessionConfigSelectOptions>,
2368 ) -> Self {
2369 Self::new(
2370 config_id,
2371 name,
2372 SessionConfigKind::Select(SessionConfigSelect::new(current_value, options)),
2373 )
2374 }
2375
2376 #[must_use]
2378 pub fn boolean(
2379 config_id: impl Into<SessionConfigId>,
2380 name: impl Into<String>,
2381 current_value: bool,
2382 ) -> Self {
2383 Self::new(
2384 config_id,
2385 name,
2386 SessionConfigKind::Boolean(SessionConfigBoolean::new(current_value)),
2387 )
2388 }
2389
2390 #[must_use]
2392 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
2393 self.description = description.into_option();
2394 self
2395 }
2396
2397 #[must_use]
2399 pub fn category(mut self, category: impl IntoOption<SessionConfigOptionCategory>) -> Self {
2400 self.category = category.into_option();
2401 self
2402 }
2403
2404 #[must_use]
2410 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2411 self.meta = meta.into_option();
2412 self
2413 }
2414}
2415
2416#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2425#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
2426#[serde(tag = "type", rename_all = "snake_case")]
2427#[non_exhaustive]
2428pub enum SessionConfigOptionValue {
2429 Id {
2431 value: SessionConfigValueId,
2433 },
2434 Boolean {
2436 value: bool,
2438 },
2439 #[serde(untagged)]
2445 Other(OtherSessionConfigOptionValue),
2446}
2447
2448#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2450#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
2451#[cfg_attr(feature = "schemars", schemars(inline))]
2452#[cfg_attr(feature = "schemars", schemars(transform = other_session_config_option_value_schema))]
2453#[serde(rename_all = "camelCase")]
2454#[non_exhaustive]
2455pub struct OtherSessionConfigOptionValue {
2456 #[serde(rename = "type")]
2462 pub type_: String,
2463 pub value: serde_json::Value,
2465 #[serde(flatten)]
2467 pub fields: BTreeMap<String, serde_json::Value>,
2468}
2469
2470impl OtherSessionConfigOptionValue {
2471 #[must_use]
2473 pub fn new(
2474 type_: impl Into<String>,
2475 value: serde_json::Value,
2476 mut fields: BTreeMap<String, serde_json::Value>,
2477 ) -> Self {
2478 fields.remove("type");
2479 fields.remove("value");
2480 fields.remove("_meta");
2481 Self {
2482 type_: type_.into(),
2483 value,
2484 fields,
2485 }
2486 }
2487}
2488
2489impl<'de> Deserialize<'de> for OtherSessionConfigOptionValue {
2490 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2491 where
2492 D: serde::Deserializer<'de>,
2493 {
2494 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
2495 let type_ = fields
2496 .remove("type")
2497 .ok_or_else(|| serde::de::Error::missing_field("type"))?;
2498 let serde_json::Value::String(type_) = type_ else {
2499 return Err(serde::de::Error::custom("`type` must be a string"));
2500 };
2501
2502 if is_known_session_config_option_value_type(&type_) {
2503 return Err(serde::de::Error::custom(format!(
2504 "known session configuration option value `{type_}` did not match its schema"
2505 )));
2506 }
2507
2508 let value = fields
2509 .remove("value")
2510 .ok_or_else(|| serde::de::Error::missing_field("value"))?;
2511
2512 Ok(Self {
2513 type_,
2514 value,
2515 fields,
2516 })
2517 }
2518}
2519
2520impl<'de> Deserialize<'de> for SessionConfigOptionValue {
2521 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2522 where
2523 D: serde::Deserializer<'de>,
2524 {
2525 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
2526 let type_ = fields.remove("type");
2527 let value = fields
2528 .remove("value")
2529 .ok_or_else(|| serde::de::Error::missing_field("value"))?;
2530
2531 let type_ = type_.ok_or_else(|| serde::de::Error::missing_field("type"))?;
2532
2533 let serde_json::Value::String(type_) = type_ else {
2534 return Err(serde::de::Error::custom("`type` must be a string"));
2535 };
2536
2537 match type_.as_str() {
2538 "id" => {
2539 let value = serde_json::from_value(value).map_err(|error| {
2540 serde::de::Error::custom(format!(
2541 "`value` must be a string for `type: id`: {error}"
2542 ))
2543 })?;
2544 Ok(Self::Id { value })
2545 }
2546 "boolean" => {
2547 let value = serde_json::from_value(value).map_err(|error| {
2548 serde::de::Error::custom(format!(
2549 "`value` must be a boolean for `type: boolean`: {error}"
2550 ))
2551 })?;
2552 Ok(Self::Boolean { value })
2553 }
2554 _ => Ok(Self::Other(OtherSessionConfigOptionValue {
2555 type_,
2556 value,
2557 fields,
2558 })),
2559 }
2560 }
2561}
2562
2563fn is_known_session_config_option_value_type(type_: &str) -> bool {
2564 matches!(type_, "id" | "boolean")
2565}
2566
2567#[cfg(feature = "schemars")]
2568fn other_session_config_option_value_schema(schema: &mut Schema) {
2569 super::schema_util::reject_known_string_discriminators(schema, "type", &["id", "boolean"]);
2570}
2571
2572impl SessionConfigOptionValue {
2573 #[must_use]
2575 pub fn id(id: impl Into<SessionConfigValueId>) -> Self {
2576 Self::Id { value: id.into() }
2577 }
2578
2579 #[must_use]
2581 pub fn boolean(val: bool) -> Self {
2582 Self::Boolean { value: val }
2583 }
2584
2585 #[must_use]
2588 pub fn as_id(&self) -> Option<&SessionConfigValueId> {
2589 match self {
2590 Self::Id { value } => Some(value),
2591 _ => None,
2592 }
2593 }
2594
2595 #[must_use]
2597 pub fn as_bool(&self) -> Option<bool> {
2598 match self {
2599 Self::Boolean { value } => Some(*value),
2600 _ => None,
2601 }
2602 }
2603}
2604
2605impl From<SessionConfigValueId> for SessionConfigOptionValue {
2606 fn from(value: SessionConfigValueId) -> Self {
2607 Self::Id { value }
2608 }
2609}
2610
2611impl From<bool> for SessionConfigOptionValue {
2612 fn from(value: bool) -> Self {
2613 Self::Boolean { value }
2614 }
2615}
2616
2617impl From<&str> for SessionConfigOptionValue {
2618 fn from(value: &str) -> Self {
2619 Self::Id {
2620 value: SessionConfigValueId::new(value),
2621 }
2622 }
2623}
2624
2625#[serde_as]
2627#[skip_serializing_none]
2628#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2629#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2630#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_CONFIG_OPTION_METHOD_NAME)))]
2631#[serde(rename_all = "camelCase")]
2632#[non_exhaustive]
2633pub struct SetSessionConfigOptionRequest {
2634 pub session_id: SessionId,
2636 pub config_id: SessionConfigId,
2638 #[serde(flatten)]
2642 pub value: SessionConfigOptionValue,
2643 #[serde_as(deserialize_as = "DefaultOnError")]
2649 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2650 #[serde(default)]
2651 #[serde(rename = "_meta")]
2652 pub meta: Option<Meta>,
2653}
2654
2655impl SetSessionConfigOptionRequest {
2656 #[must_use]
2658 pub fn new(
2659 session_id: impl Into<SessionId>,
2660 config_id: impl Into<SessionConfigId>,
2661 value: impl Into<SessionConfigOptionValue>,
2662 ) -> Self {
2663 Self {
2664 session_id: session_id.into(),
2665 config_id: config_id.into(),
2666 value: value.into(),
2667 meta: None,
2668 }
2669 }
2670
2671 #[must_use]
2677 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2678 self.meta = meta.into_option();
2679 self
2680 }
2681}
2682
2683#[serde_as]
2685#[skip_serializing_none]
2686#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2687#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2688#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_CONFIG_OPTION_METHOD_NAME)))]
2689#[serde(rename_all = "camelCase")]
2690#[non_exhaustive]
2691pub struct SetSessionConfigOptionResponse {
2692 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
2694 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
2695 pub config_options: Vec<SessionConfigOption>,
2696 #[serde_as(deserialize_as = "DefaultOnError")]
2702 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2703 #[serde(default)]
2704 #[serde(rename = "_meta")]
2705 pub meta: Option<Meta>,
2706}
2707
2708impl SetSessionConfigOptionResponse {
2709 #[must_use]
2711 pub fn new(config_options: Vec<SessionConfigOption>) -> Self {
2712 Self {
2713 config_options,
2714 meta: None,
2715 }
2716 }
2717
2718 #[must_use]
2724 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2725 self.meta = meta.into_option();
2726 self
2727 }
2728}
2729
2730#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2739#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2740#[serde(tag = "type", rename_all = "snake_case")]
2741#[non_exhaustive]
2742pub enum McpServer {
2743 Http(McpServerHttp),
2747 #[cfg(feature = "unstable_mcp_over_acp")]
2756 Acp(McpServerAcp),
2757 Stdio(McpServerStdio),
2761 #[serde(untagged)]
2771 Other(OtherMcpServer),
2772}
2773
2774#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2776#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
2777#[cfg_attr(feature = "schemars", schemars(inline))]
2778#[cfg_attr(feature = "schemars", schemars(transform = other_mcp_server_schema))]
2779#[serde(rename_all = "camelCase")]
2780#[non_exhaustive]
2781pub struct OtherMcpServer {
2782 #[serde(rename = "type")]
2788 pub type_: String,
2789 #[serde(flatten)]
2791 pub fields: BTreeMap<String, serde_json::Value>,
2792}
2793
2794impl OtherMcpServer {
2795 #[must_use]
2797 pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
2798 fields.remove("type");
2799 Self {
2800 type_: type_.into(),
2801 fields,
2802 }
2803 }
2804}
2805
2806impl<'de> Deserialize<'de> for OtherMcpServer {
2807 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2808 where
2809 D: serde::Deserializer<'de>,
2810 {
2811 let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
2812 let type_ = fields
2813 .remove("type")
2814 .ok_or_else(|| serde::de::Error::missing_field("type"))?;
2815 let serde_json::Value::String(type_) = type_ else {
2816 return Err(serde::de::Error::custom("`type` must be a string"));
2817 };
2818
2819 if is_known_mcp_server_type(&type_) {
2820 return Err(serde::de::Error::custom(format!(
2821 "known MCP server transport `{type_}` did not match its schema"
2822 )));
2823 }
2824
2825 Ok(Self { type_, fields })
2826 }
2827}
2828
2829fn is_known_mcp_server_type(type_: &str) -> bool {
2830 match type_ {
2831 "http" | "stdio" => true,
2832 #[cfg(feature = "unstable_mcp_over_acp")]
2833 "acp" => true,
2834 _ => false,
2835 }
2836}
2837
2838#[cfg(feature = "schemars")]
2839fn other_mcp_server_schema(schema: &mut Schema) {
2840 super::schema_util::reject_known_string_discriminators(
2841 schema,
2842 "type",
2843 &[
2844 "http",
2845 "stdio",
2846 #[cfg(feature = "unstable_mcp_over_acp")]
2847 "acp",
2848 ],
2849 );
2850}
2851
2852#[serde_as]
2854#[skip_serializing_none]
2855#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2856#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2857#[serde(rename_all = "camelCase")]
2858#[non_exhaustive]
2859pub struct McpServerHttp {
2860 pub name: String,
2862 #[cfg_attr(feature = "schemars", schemars(url))]
2864 pub url: String,
2865 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2867 pub headers: Vec<HttpHeader>,
2868 #[serde_as(deserialize_as = "DefaultOnError")]
2874 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2875 #[serde(default)]
2876 #[serde(rename = "_meta")]
2877 pub meta: Option<Meta>,
2878}
2879
2880impl McpServerHttp {
2881 #[must_use]
2883 pub fn new(name: impl Into<String>, url: impl Into<String>) -> Self {
2884 Self {
2885 name: name.into(),
2886 url: url.into(),
2887 headers: Vec::new(),
2888 meta: None,
2889 }
2890 }
2891
2892 #[must_use]
2894 pub fn headers(mut self, headers: Vec<HttpHeader>) -> Self {
2895 self.headers = headers;
2896 self
2897 }
2898
2899 #[must_use]
2905 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2906 self.meta = meta.into_option();
2907 self
2908 }
2909}
2910
2911#[cfg(feature = "unstable_mcp_over_acp")]
2921#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2922#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
2923#[serde(transparent)]
2924#[from(forward)]
2925#[non_exhaustive]
2926pub struct McpServerAcpId(pub Arc<str>);
2927
2928#[cfg(feature = "unstable_mcp_over_acp")]
2929impl McpServerAcpId {
2930 #[must_use]
2932 pub fn new(id: impl Into<Self>) -> Self {
2933 id.into()
2934 }
2935}
2936
2937#[serde_as]
2946#[skip_serializing_none]
2947#[cfg(feature = "unstable_mcp_over_acp")]
2948#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2949#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2950#[serde(rename_all = "camelCase")]
2951#[non_exhaustive]
2952pub struct McpServerAcp {
2953 pub name: String,
2955 pub server_id: McpServerAcpId,
2960 #[serde_as(deserialize_as = "DefaultOnError")]
2966 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2967 #[serde(default)]
2968 #[serde(rename = "_meta")]
2969 pub meta: Option<Meta>,
2970}
2971
2972#[cfg(feature = "unstable_mcp_over_acp")]
2973impl McpServerAcp {
2974 #[must_use]
2976 pub fn new(name: impl Into<String>, server_id: impl Into<McpServerAcpId>) -> Self {
2977 Self {
2978 name: name.into(),
2979 server_id: server_id.into(),
2980 meta: None,
2981 }
2982 }
2983
2984 #[must_use]
2990 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2991 self.meta = meta.into_option();
2992 self
2993 }
2994}
2995
2996#[serde_as]
2998#[skip_serializing_none]
2999#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3000#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3001#[serde(rename_all = "camelCase")]
3002#[non_exhaustive]
3003pub struct McpServerStdio {
3004 pub name: String,
3006 pub command: AbsolutePath,
3008 #[serde(default, skip_serializing_if = "Vec::is_empty")]
3010 pub args: Vec<String>,
3011 #[serde(default, skip_serializing_if = "Vec::is_empty")]
3013 pub env: Vec<EnvVariable>,
3014 #[serde_as(deserialize_as = "DefaultOnError")]
3020 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3021 #[serde(default)]
3022 #[serde(rename = "_meta")]
3023 pub meta: Option<Meta>,
3024}
3025
3026impl McpServerStdio {
3027 #[must_use]
3029 pub fn new(name: impl Into<String>, command: impl Into<AbsolutePath>) -> Self {
3030 Self {
3031 name: name.into(),
3032 command: command.into(),
3033 args: Vec::new(),
3034 env: Vec::new(),
3035 meta: None,
3036 }
3037 }
3038
3039 #[must_use]
3041 pub fn args(mut self, args: Vec<String>) -> Self {
3042 self.args = args;
3043 self
3044 }
3045
3046 #[must_use]
3048 pub fn env(mut self, env: Vec<EnvVariable>) -> Self {
3049 self.env = env;
3050 self
3051 }
3052
3053 #[must_use]
3059 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3060 self.meta = meta.into_option();
3061 self
3062 }
3063}
3064
3065#[serde_as]
3067#[skip_serializing_none]
3068#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3069#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3070#[serde(rename_all = "camelCase")]
3071#[non_exhaustive]
3072pub struct EnvVariable {
3073 pub name: String,
3075 pub value: String,
3077 #[serde_as(deserialize_as = "DefaultOnError")]
3083 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3084 #[serde(default)]
3085 #[serde(rename = "_meta")]
3086 pub meta: Option<Meta>,
3087}
3088
3089impl EnvVariable {
3090 #[must_use]
3092 pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
3093 Self {
3094 name: name.into(),
3095 value: value.into(),
3096 meta: None,
3097 }
3098 }
3099
3100 #[must_use]
3106 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3107 self.meta = meta.into_option();
3108 self
3109 }
3110}
3111
3112#[serde_as]
3114#[skip_serializing_none]
3115#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3116#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3117#[serde(rename_all = "camelCase")]
3118#[non_exhaustive]
3119pub struct HttpHeader {
3120 pub name: String,
3122 pub value: String,
3124 #[serde_as(deserialize_as = "DefaultOnError")]
3130 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3131 #[serde(default)]
3132 #[serde(rename = "_meta")]
3133 pub meta: Option<Meta>,
3134}
3135
3136impl HttpHeader {
3137 #[must_use]
3139 pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
3140 Self {
3141 name: name.into(),
3142 value: value.into(),
3143 meta: None,
3144 }
3145 }
3146
3147 #[must_use]
3153 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3154 self.meta = meta.into_option();
3155 self
3156 }
3157}
3158
3159#[serde_as]
3167#[skip_serializing_none]
3168#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3169#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
3170#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_PROMPT_METHOD_NAME)))]
3171#[serde(rename_all = "camelCase")]
3172#[non_exhaustive]
3173pub struct PromptRequest {
3174 pub session_id: SessionId,
3176 pub prompt: Vec<ContentBlock>,
3190 #[serde_as(deserialize_as = "DefaultOnError")]
3196 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3197 #[serde(default)]
3198 #[serde(rename = "_meta")]
3199 pub meta: Option<Meta>,
3200}
3201
3202impl PromptRequest {
3203 #[must_use]
3205 pub fn new(session_id: impl Into<SessionId>, prompt: Vec<ContentBlock>) -> Self {
3206 Self {
3207 session_id: session_id.into(),
3208 prompt,
3209 meta: None,
3210 }
3211 }
3212
3213 #[must_use]
3219 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3220 self.meta = meta.into_option();
3221 self
3222 }
3223}
3224
3225#[serde_as]
3233#[skip_serializing_none]
3234#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3235#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3236#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_PROMPT_METHOD_NAME)))]
3237#[serde(rename_all = "camelCase")]
3238#[non_exhaustive]
3239pub struct PromptResponse {
3240 pub message_id: MessageId,
3248 #[serde_as(deserialize_as = "DefaultOnError")]
3254 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3255 #[serde(default)]
3256 #[serde(rename = "_meta")]
3257 pub meta: Option<Meta>,
3258}
3259
3260impl PromptResponse {
3261 #[must_use]
3263 pub fn new(message_id: impl Into<MessageId>) -> Self {
3264 Self {
3265 message_id: message_id.into(),
3266 meta: None,
3267 }
3268 }
3269
3270 #[must_use]
3276 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3277 self.meta = meta.into_option();
3278 self
3279 }
3280}
3281
3282#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3286#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
3287#[serde(rename_all = "snake_case")]
3288#[non_exhaustive]
3289pub enum StopReason {
3290 EndTurn,
3292 MaxTokens,
3294 MaxTurnRequests,
3297 Refusal,
3301 Cancelled,
3307 #[serde(untagged)]
3313 Other(String),
3314}
3315
3316#[cfg(feature = "unstable_end_turn_token_usage")]
3322#[serde_as]
3323#[skip_serializing_none]
3324#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3325#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3326#[serde(rename_all = "camelCase")]
3327#[non_exhaustive]
3328pub struct Usage {
3329 pub total_tokens: u64,
3331 pub input_tokens: u64,
3333 pub output_tokens: u64,
3335 #[serde_as(deserialize_as = "DefaultOnError")]
3337 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3338 #[serde(default)]
3339 pub thought_tokens: Option<u64>,
3340 #[serde_as(deserialize_as = "DefaultOnError")]
3342 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3343 #[serde(default)]
3344 pub cached_read_tokens: Option<u64>,
3345 #[serde_as(deserialize_as = "DefaultOnError")]
3347 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3348 #[serde(default)]
3349 pub cached_write_tokens: Option<u64>,
3350 #[serde_as(deserialize_as = "DefaultOnError")]
3356 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3357 #[serde(default)]
3358 #[serde(rename = "_meta")]
3359 pub meta: Option<Meta>,
3360}
3361
3362#[cfg(feature = "unstable_end_turn_token_usage")]
3363impl Usage {
3364 #[must_use]
3366 pub fn new(total_tokens: u64, input_tokens: u64, output_tokens: u64) -> Self {
3367 Self {
3368 total_tokens,
3369 input_tokens,
3370 output_tokens,
3371 thought_tokens: None,
3372 cached_read_tokens: None,
3373 cached_write_tokens: None,
3374 meta: None,
3375 }
3376 }
3377
3378 #[must_use]
3380 pub fn thought_tokens(mut self, thought_tokens: impl IntoOption<u64>) -> Self {
3381 self.thought_tokens = thought_tokens.into_option();
3382 self
3383 }
3384
3385 #[must_use]
3387 pub fn cached_read_tokens(mut self, cached_read_tokens: impl IntoOption<u64>) -> Self {
3388 self.cached_read_tokens = cached_read_tokens.into_option();
3389 self
3390 }
3391
3392 #[must_use]
3394 pub fn cached_write_tokens(mut self, cached_write_tokens: impl IntoOption<u64>) -> Self {
3395 self.cached_write_tokens = cached_write_tokens.into_option();
3396 self
3397 }
3398
3399 #[must_use]
3405 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3406 self.meta = meta.into_option();
3407 self
3408 }
3409}
3410
3411#[cfg(feature = "unstable_llm_providers")]
3424#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3425#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3426#[serde(rename_all = "snake_case")]
3427#[non_exhaustive]
3428#[expect(clippy::doc_markdown)]
3429pub enum LlmProtocol {
3430 Anthropic,
3432 #[serde(rename = "openai")]
3434 OpenAi,
3435 Azure,
3437 Vertex,
3439 Bedrock,
3441 #[serde(untagged)]
3447 Other(String),
3448}
3449
3450#[cfg(feature = "unstable_llm_providers")]
3456#[serde_as]
3457#[skip_serializing_none]
3458#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3459#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3460#[serde(rename_all = "camelCase")]
3461#[non_exhaustive]
3462pub struct ProviderCurrentConfig {
3463 pub api_type: LlmProtocol,
3465 #[cfg_attr(feature = "schemars", schemars(url))]
3467 pub base_url: String,
3468 #[serde_as(deserialize_as = "DefaultOnError")]
3474 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3475 #[serde(default)]
3476 #[serde(rename = "_meta")]
3477 pub meta: Option<Meta>,
3478}
3479
3480#[cfg(feature = "unstable_llm_providers")]
3481impl ProviderCurrentConfig {
3482 #[must_use]
3484 pub fn new(api_type: LlmProtocol, base_url: impl Into<String>) -> Self {
3485 Self {
3486 api_type,
3487 base_url: base_url.into(),
3488 meta: None,
3489 }
3490 }
3491
3492 #[must_use]
3498 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3499 self.meta = meta.into_option();
3500 self
3501 }
3502}
3503
3504#[cfg(feature = "unstable_llm_providers")]
3510#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3511#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
3512#[serde(transparent)]
3513#[from(forward)]
3514#[non_exhaustive]
3515pub struct ProviderId(pub Arc<str>);
3516
3517#[cfg(feature = "unstable_llm_providers")]
3518impl ProviderId {
3519 #[must_use]
3521 pub fn new(id: impl Into<Self>) -> Self {
3522 id.into()
3523 }
3524}
3525
3526#[cfg(feature = "unstable_llm_providers")]
3532#[serde_as]
3533#[skip_serializing_none]
3534#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3535#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3536#[serde(rename_all = "camelCase")]
3537#[non_exhaustive]
3538pub struct ProviderInfo {
3539 pub provider_id: ProviderId,
3541 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
3543 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
3544 pub supported: Vec<LlmProtocol>,
3545 pub required: bool,
3548 #[serde(default)]
3551 pub current: Option<ProviderCurrentConfig>,
3552 #[serde_as(deserialize_as = "DefaultOnError")]
3558 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3559 #[serde(default)]
3560 #[serde(rename = "_meta")]
3561 pub meta: Option<Meta>,
3562}
3563
3564#[cfg(feature = "unstable_llm_providers")]
3565impl ProviderInfo {
3566 #[must_use]
3568 pub fn new(
3569 provider_id: impl Into<ProviderId>,
3570 supported: Vec<LlmProtocol>,
3571 required: bool,
3572 current: impl IntoOption<ProviderCurrentConfig>,
3573 ) -> Self {
3574 Self {
3575 provider_id: provider_id.into(),
3576 supported,
3577 required,
3578 current: current.into_option(),
3579 meta: None,
3580 }
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")]
3601#[serde_as]
3602#[skip_serializing_none]
3603#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3604#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3605#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_LIST_METHOD_NAME)))]
3606#[serde(rename_all = "camelCase")]
3607#[non_exhaustive]
3608pub struct ListProvidersRequest {
3609 #[serde_as(deserialize_as = "DefaultOnError")]
3615 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3616 #[serde(default)]
3617 #[serde(rename = "_meta")]
3618 pub meta: Option<Meta>,
3619}
3620
3621#[cfg(feature = "unstable_llm_providers")]
3622impl ListProvidersRequest {
3623 #[must_use]
3625 pub fn new() -> Self {
3626 Self::default()
3627 }
3628
3629 #[must_use]
3635 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3636 self.meta = meta.into_option();
3637 self
3638 }
3639}
3640
3641#[cfg(feature = "unstable_llm_providers")]
3647#[serde_as]
3648#[skip_serializing_none]
3649#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3650#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3651#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_LIST_METHOD_NAME)))]
3652#[serde(rename_all = "camelCase")]
3653#[non_exhaustive]
3654pub struct ListProvidersResponse {
3655 pub providers: Vec<ProviderInfo>,
3657 #[serde_as(deserialize_as = "DefaultOnError")]
3663 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3664 #[serde(default)]
3665 #[serde(rename = "_meta")]
3666 pub meta: Option<Meta>,
3667}
3668
3669#[cfg(feature = "unstable_llm_providers")]
3670impl ListProvidersResponse {
3671 #[must_use]
3673 pub fn new(providers: Vec<ProviderInfo>) -> Self {
3674 Self {
3675 providers,
3676 meta: None,
3677 }
3678 }
3679
3680 #[must_use]
3686 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3687 self.meta = meta.into_option();
3688 self
3689 }
3690}
3691
3692#[cfg(feature = "unstable_llm_providers")]
3700#[serde_as]
3701#[skip_serializing_none]
3702#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3703#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
3704#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_SET_METHOD_NAME)))]
3705#[serde(rename_all = "camelCase")]
3706#[non_exhaustive]
3707pub struct SetProviderRequest {
3708 pub provider_id: ProviderId,
3710 pub api_type: LlmProtocol,
3712 #[cfg_attr(feature = "schemars", schemars(url))]
3714 pub base_url: String,
3715 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
3718 pub headers: HashMap<String, String>,
3719 #[serde_as(deserialize_as = "DefaultOnError")]
3725 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3726 #[serde(default)]
3727 #[serde(rename = "_meta")]
3728 pub meta: Option<Meta>,
3729}
3730
3731#[cfg(feature = "unstable_llm_providers")]
3732impl SetProviderRequest {
3733 #[must_use]
3735 pub fn new(
3736 provider_id: impl Into<ProviderId>,
3737 api_type: LlmProtocol,
3738 base_url: impl Into<String>,
3739 ) -> Self {
3740 Self {
3741 provider_id: provider_id.into(),
3742 api_type,
3743 base_url: base_url.into(),
3744 headers: HashMap::new(),
3745 meta: None,
3746 }
3747 }
3748
3749 #[must_use]
3752 pub fn headers(mut self, headers: HashMap<String, String>) -> Self {
3753 self.headers = headers;
3754 self
3755 }
3756
3757 #[must_use]
3763 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3764 self.meta = meta.into_option();
3765 self
3766 }
3767}
3768
3769#[cfg(feature = "unstable_llm_providers")]
3775#[serde_as]
3776#[skip_serializing_none]
3777#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3778#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3779#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_SET_METHOD_NAME)))]
3780#[serde(rename_all = "camelCase")]
3781#[non_exhaustive]
3782pub struct SetProviderResponse {
3783 #[serde_as(deserialize_as = "DefaultOnError")]
3789 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3790 #[serde(default)]
3791 #[serde(rename = "_meta")]
3792 pub meta: Option<Meta>,
3793}
3794
3795#[cfg(feature = "unstable_llm_providers")]
3796impl SetProviderResponse {
3797 #[must_use]
3799 pub fn new() -> Self {
3800 Self::default()
3801 }
3802
3803 #[must_use]
3809 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3810 self.meta = meta.into_option();
3811 self
3812 }
3813}
3814
3815#[cfg(feature = "unstable_llm_providers")]
3821#[serde_as]
3822#[skip_serializing_none]
3823#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3824#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3825#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_DISABLE_METHOD_NAME)))]
3826#[serde(rename_all = "camelCase")]
3827#[non_exhaustive]
3828pub struct DisableProviderRequest {
3829 pub provider_id: ProviderId,
3831 #[serde_as(deserialize_as = "DefaultOnError")]
3837 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3838 #[serde(default)]
3839 #[serde(rename = "_meta")]
3840 pub meta: Option<Meta>,
3841}
3842
3843#[cfg(feature = "unstable_llm_providers")]
3844impl DisableProviderRequest {
3845 #[must_use]
3847 pub fn new(provider_id: impl Into<ProviderId>) -> Self {
3848 Self {
3849 provider_id: provider_id.into(),
3850 meta: None,
3851 }
3852 }
3853
3854 #[must_use]
3860 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3861 self.meta = meta.into_option();
3862 self
3863 }
3864}
3865
3866#[cfg(feature = "unstable_llm_providers")]
3872#[serde_as]
3873#[skip_serializing_none]
3874#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3875#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3876#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_DISABLE_METHOD_NAME)))]
3877#[serde(rename_all = "camelCase")]
3878#[non_exhaustive]
3879pub struct DisableProviderResponse {
3880 #[serde_as(deserialize_as = "DefaultOnError")]
3886 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3887 #[serde(default)]
3888 #[serde(rename = "_meta")]
3889 pub meta: Option<Meta>,
3890}
3891
3892#[cfg(feature = "unstable_llm_providers")]
3893impl DisableProviderResponse {
3894 #[must_use]
3896 pub fn new() -> Self {
3897 Self::default()
3898 }
3899
3900 #[must_use]
3906 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3907 self.meta = meta.into_option();
3908 self
3909 }
3910}
3911
3912#[serde_as]
3921#[skip_serializing_none]
3922#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3923#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3924#[serde(rename_all = "camelCase")]
3925#[non_exhaustive]
3926pub struct AgentCapabilities {
3927 #[serde_as(deserialize_as = "DefaultOnError")]
3934 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3935 #[serde(default)]
3936 pub session: Option<SessionCapabilities>,
3937 #[serde_as(deserialize_as = "DefaultOnError")]
3944 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3945 #[serde(default)]
3946 pub auth: Option<AgentAuthCapabilities>,
3947 #[cfg(feature = "unstable_llm_providers")]
3956 #[serde_as(deserialize_as = "DefaultOnError")]
3957 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3958 #[serde(default)]
3959 pub providers: Option<ProvidersCapabilities>,
3960 #[cfg(feature = "unstable_nes")]
3969 #[serde_as(deserialize_as = "DefaultOnError")]
3970 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3971 #[serde(default)]
3972 pub nes: Option<NesCapabilities>,
3973 #[cfg(feature = "unstable_nes")]
3979 #[serde_as(deserialize_as = "DefaultOnError")]
3980 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3981 #[serde(default)]
3982 pub position_encoding: Option<PositionEncodingKind>,
3983 #[serde_as(deserialize_as = "DefaultOnError")]
3989 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3990 #[serde(default)]
3991 #[serde(rename = "_meta")]
3992 pub meta: Option<Meta>,
3993}
3994
3995impl AgentCapabilities {
3996 #[must_use]
3998 pub fn new() -> Self {
3999 Self::default()
4000 }
4001
4002 #[must_use]
4009 pub fn session(mut self, session: impl IntoOption<SessionCapabilities>) -> Self {
4010 self.session = session.into_option();
4011 self
4012 }
4013
4014 #[must_use]
4018 pub fn auth(mut self, auth: impl IntoOption<AgentAuthCapabilities>) -> Self {
4019 self.auth = auth.into_option();
4020 self
4021 }
4022
4023 #[cfg(feature = "unstable_llm_providers")]
4029 #[must_use]
4030 pub fn providers(mut self, providers: impl IntoOption<ProvidersCapabilities>) -> Self {
4031 self.providers = providers.into_option();
4032 self
4033 }
4034
4035 #[cfg(feature = "unstable_nes")]
4041 #[must_use]
4042 pub fn nes(mut self, nes: impl IntoOption<NesCapabilities>) -> Self {
4043 self.nes = nes.into_option();
4044 self
4045 }
4046
4047 #[cfg(feature = "unstable_nes")]
4051 #[must_use]
4052 pub fn position_encoding(
4053 mut self,
4054 position_encoding: impl IntoOption<PositionEncodingKind>,
4055 ) -> Self {
4056 self.position_encoding = position_encoding.into_option();
4057 self
4058 }
4059
4060 #[must_use]
4066 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4067 self.meta = meta.into_option();
4068 self
4069 }
4070}
4071
4072#[cfg(feature = "unstable_llm_providers")]
4080#[serde_as]
4081#[skip_serializing_none]
4082#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4083#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4084#[non_exhaustive]
4085pub struct ProvidersCapabilities {
4086 #[serde_as(deserialize_as = "DefaultOnError")]
4092 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4093 #[serde(default)]
4094 #[serde(rename = "_meta")]
4095 pub meta: Option<Meta>,
4096}
4097
4098#[cfg(feature = "unstable_llm_providers")]
4099impl ProvidersCapabilities {
4100 #[must_use]
4102 pub fn new() -> Self {
4103 Self::default()
4104 }
4105
4106 #[must_use]
4112 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4113 self.meta = meta.into_option();
4114 self
4115 }
4116}
4117
4118#[serde_as]
4130#[skip_serializing_none]
4131#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4132#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4133#[serde(rename_all = "camelCase")]
4134#[non_exhaustive]
4135pub struct SessionCapabilities {
4136 #[serde_as(deserialize_as = "DefaultOnError")]
4142 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4143 #[serde(default)]
4144 pub prompt: Option<PromptCapabilities>,
4145 #[serde_as(deserialize_as = "DefaultOnError")]
4150 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4151 #[serde(default)]
4152 pub mcp: Option<McpCapabilities>,
4153 #[serde_as(deserialize_as = "DefaultOnError")]
4158 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4159 #[serde(default)]
4160 pub delete: Option<SessionDeleteCapabilities>,
4161 #[serde_as(deserialize_as = "DefaultOnError")]
4170 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4171 #[serde(default)]
4172 pub additional_directories: Option<SessionAdditionalDirectoriesCapabilities>,
4173 #[cfg(feature = "unstable_session_fork")]
4182 #[serde_as(deserialize_as = "DefaultOnError")]
4183 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4184 #[serde(default)]
4185 pub fork: Option<SessionForkCapabilities>,
4186 #[serde_as(deserialize_as = "DefaultOnError")]
4192 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4193 #[serde(default)]
4194 #[serde(rename = "_meta")]
4195 pub meta: Option<Meta>,
4196}
4197
4198impl SessionCapabilities {
4199 #[must_use]
4201 pub fn new() -> Self {
4202 Self::default()
4203 }
4204
4205 #[must_use]
4211 pub fn prompt(mut self, prompt: impl IntoOption<PromptCapabilities>) -> Self {
4212 self.prompt = prompt.into_option();
4213 self
4214 }
4215
4216 #[must_use]
4221 pub fn mcp(mut self, mcp: impl IntoOption<McpCapabilities>) -> Self {
4222 self.mcp = mcp.into_option();
4223 self
4224 }
4225
4226 #[must_use]
4231 pub fn delete(mut self, delete: impl IntoOption<SessionDeleteCapabilities>) -> Self {
4232 self.delete = delete.into_option();
4233 self
4234 }
4235
4236 #[must_use]
4245 pub fn additional_directories(
4246 mut self,
4247 additional_directories: impl IntoOption<SessionAdditionalDirectoriesCapabilities>,
4248 ) -> Self {
4249 self.additional_directories = additional_directories.into_option();
4250 self
4251 }
4252
4253 #[cfg(feature = "unstable_session_fork")]
4254 #[must_use]
4259 pub fn fork(mut self, fork: impl IntoOption<SessionForkCapabilities>) -> Self {
4260 self.fork = fork.into_option();
4261 self
4262 }
4263
4264 #[must_use]
4270 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4271 self.meta = meta.into_option();
4272 self
4273 }
4274}
4275
4276#[serde_as]
4280#[skip_serializing_none]
4281#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4282#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4283#[non_exhaustive]
4284pub struct SessionDeleteCapabilities {
4285 #[serde_as(deserialize_as = "DefaultOnError")]
4291 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4292 #[serde(default)]
4293 #[serde(rename = "_meta")]
4294 pub meta: Option<Meta>,
4295}
4296
4297impl SessionDeleteCapabilities {
4298 #[must_use]
4300 pub fn new() -> Self {
4301 Self::default()
4302 }
4303
4304 #[must_use]
4310 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4311 self.meta = meta.into_option();
4312 self
4313 }
4314}
4315
4316#[serde_as]
4323#[skip_serializing_none]
4324#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4325#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4326#[non_exhaustive]
4327pub struct SessionAdditionalDirectoriesCapabilities {
4328 #[serde_as(deserialize_as = "DefaultOnError")]
4334 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4335 #[serde(default)]
4336 #[serde(rename = "_meta")]
4337 pub meta: Option<Meta>,
4338}
4339
4340impl SessionAdditionalDirectoriesCapabilities {
4341 #[must_use]
4343 pub fn new() -> Self {
4344 Self::default()
4345 }
4346
4347 #[must_use]
4353 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4354 self.meta = meta.into_option();
4355 self
4356 }
4357}
4358
4359#[cfg(feature = "unstable_session_fork")]
4367#[serde_as]
4368#[skip_serializing_none]
4369#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4370#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4371#[non_exhaustive]
4372pub struct SessionForkCapabilities {
4373 #[serde_as(deserialize_as = "DefaultOnError")]
4379 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4380 #[serde(default)]
4381 #[serde(rename = "_meta")]
4382 pub meta: Option<Meta>,
4383}
4384
4385#[cfg(feature = "unstable_session_fork")]
4386impl SessionForkCapabilities {
4387 #[must_use]
4389 pub fn new() -> Self {
4390 Self::default()
4391 }
4392
4393 #[must_use]
4399 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4400 self.meta = meta.into_option();
4401 self
4402 }
4403}
4404
4405#[serde_as]
4418#[skip_serializing_none]
4419#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4420#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4421#[serde(rename_all = "camelCase")]
4422#[non_exhaustive]
4423pub struct PromptCapabilities {
4424 #[serde_as(deserialize_as = "DefaultOnError")]
4429 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4430 #[serde(default)]
4431 pub image: Option<PromptImageCapabilities>,
4432 #[serde_as(deserialize_as = "DefaultOnError")]
4437 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4438 #[serde(default)]
4439 pub audio: Option<PromptAudioCapabilities>,
4440 #[serde_as(deserialize_as = "DefaultOnError")]
4448 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4449 #[serde(default)]
4450 pub embedded_context: Option<PromptEmbeddedContextCapabilities>,
4451 #[serde_as(deserialize_as = "DefaultOnError")]
4457 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4458 #[serde(default)]
4459 #[serde(rename = "_meta")]
4460 pub meta: Option<Meta>,
4461}
4462
4463impl PromptCapabilities {
4464 #[must_use]
4466 pub fn new() -> Self {
4467 Self::default()
4468 }
4469
4470 #[must_use]
4475 pub fn image(mut self, image: impl IntoOption<PromptImageCapabilities>) -> Self {
4476 self.image = image.into_option();
4477 self
4478 }
4479
4480 #[must_use]
4485 pub fn audio(mut self, audio: impl IntoOption<PromptAudioCapabilities>) -> Self {
4486 self.audio = audio.into_option();
4487 self
4488 }
4489
4490 #[must_use]
4498 pub fn embedded_context(
4499 mut self,
4500 embedded_context: impl IntoOption<PromptEmbeddedContextCapabilities>,
4501 ) -> Self {
4502 self.embedded_context = embedded_context.into_option();
4503 self
4504 }
4505
4506 #[must_use]
4512 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4513 self.meta = meta.into_option();
4514 self
4515 }
4516}
4517
4518#[serde_as]
4522#[skip_serializing_none]
4523#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4524#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4525#[non_exhaustive]
4526pub struct PromptImageCapabilities {
4527 #[serde_as(deserialize_as = "DefaultOnError")]
4533 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4534 #[serde(default)]
4535 #[serde(rename = "_meta")]
4536 pub meta: Option<Meta>,
4537}
4538
4539impl PromptImageCapabilities {
4540 #[must_use]
4542 pub fn new() -> Self {
4543 Self::default()
4544 }
4545
4546 #[must_use]
4552 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4553 self.meta = meta.into_option();
4554 self
4555 }
4556}
4557
4558#[serde_as]
4562#[skip_serializing_none]
4563#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4564#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4565#[non_exhaustive]
4566pub struct PromptAudioCapabilities {
4567 #[serde_as(deserialize_as = "DefaultOnError")]
4573 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4574 #[serde(default)]
4575 #[serde(rename = "_meta")]
4576 pub meta: Option<Meta>,
4577}
4578
4579impl PromptAudioCapabilities {
4580 #[must_use]
4582 pub fn new() -> Self {
4583 Self::default()
4584 }
4585
4586 #[must_use]
4592 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4593 self.meta = meta.into_option();
4594 self
4595 }
4596}
4597
4598#[serde_as]
4602#[skip_serializing_none]
4603#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4604#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4605#[non_exhaustive]
4606pub struct PromptEmbeddedContextCapabilities {
4607 #[serde_as(deserialize_as = "DefaultOnError")]
4613 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4614 #[serde(default)]
4615 #[serde(rename = "_meta")]
4616 pub meta: Option<Meta>,
4617}
4618
4619impl PromptEmbeddedContextCapabilities {
4620 #[must_use]
4622 pub fn new() -> Self {
4623 Self::default()
4624 }
4625
4626 #[must_use]
4632 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4633 self.meta = meta.into_option();
4634 self
4635 }
4636}
4637
4638#[serde_as]
4640#[skip_serializing_none]
4641#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4642#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4643#[serde(rename_all = "camelCase")]
4644#[non_exhaustive]
4645pub struct McpCapabilities {
4646 #[serde_as(deserialize_as = "DefaultOnError")]
4651 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4652 #[serde(default)]
4653 pub stdio: Option<McpStdioCapabilities>,
4654 #[serde_as(deserialize_as = "DefaultOnError")]
4659 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4660 #[serde(default)]
4661 pub http: Option<McpHttpCapabilities>,
4662 #[cfg(feature = "unstable_mcp_over_acp")]
4671 #[serde_as(deserialize_as = "DefaultOnError")]
4672 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4673 #[serde(default)]
4674 pub acp: Option<McpAcpCapabilities>,
4675 #[serde_as(deserialize_as = "DefaultOnError")]
4681 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4682 #[serde(default)]
4683 #[serde(rename = "_meta")]
4684 pub meta: Option<Meta>,
4685}
4686
4687impl McpCapabilities {
4688 #[must_use]
4690 pub fn new() -> Self {
4691 Self::default()
4692 }
4693
4694 #[must_use]
4699 pub fn stdio(mut self, stdio: impl IntoOption<McpStdioCapabilities>) -> Self {
4700 self.stdio = stdio.into_option();
4701 self
4702 }
4703
4704 #[must_use]
4709 pub fn http(mut self, http: impl IntoOption<McpHttpCapabilities>) -> Self {
4710 self.http = http.into_option();
4711 self
4712 }
4713
4714 #[cfg(feature = "unstable_mcp_over_acp")]
4720 #[must_use]
4724 pub fn acp(mut self, acp: impl IntoOption<McpAcpCapabilities>) -> Self {
4725 self.acp = acp.into_option();
4726 self
4727 }
4728
4729 #[must_use]
4735 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4736 self.meta = meta.into_option();
4737 self
4738 }
4739}
4740
4741#[serde_as]
4745#[skip_serializing_none]
4746#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4747#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4748#[non_exhaustive]
4749pub struct McpStdioCapabilities {
4750 #[serde_as(deserialize_as = "DefaultOnError")]
4756 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4757 #[serde(default)]
4758 #[serde(rename = "_meta")]
4759 pub meta: Option<Meta>,
4760}
4761
4762impl McpStdioCapabilities {
4763 #[must_use]
4765 pub fn new() -> Self {
4766 Self::default()
4767 }
4768
4769 #[must_use]
4775 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4776 self.meta = meta.into_option();
4777 self
4778 }
4779}
4780
4781#[serde_as]
4785#[skip_serializing_none]
4786#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4787#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4788#[non_exhaustive]
4789pub struct McpHttpCapabilities {
4790 #[serde_as(deserialize_as = "DefaultOnError")]
4796 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4797 #[serde(default)]
4798 #[serde(rename = "_meta")]
4799 pub meta: Option<Meta>,
4800}
4801
4802impl McpHttpCapabilities {
4803 #[must_use]
4805 pub fn new() -> Self {
4806 Self::default()
4807 }
4808
4809 #[must_use]
4815 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4816 self.meta = meta.into_option();
4817 self
4818 }
4819}
4820
4821#[cfg(feature = "unstable_mcp_over_acp")]
4829#[serde_as]
4830#[skip_serializing_none]
4831#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4832#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4833#[non_exhaustive]
4834pub struct McpAcpCapabilities {
4835 #[serde_as(deserialize_as = "DefaultOnError")]
4841 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4842 #[serde(default)]
4843 #[serde(rename = "_meta")]
4844 pub meta: Option<Meta>,
4845}
4846
4847#[cfg(feature = "unstable_mcp_over_acp")]
4848impl McpAcpCapabilities {
4849 #[must_use]
4851 pub fn new() -> Self {
4852 Self::default()
4853 }
4854
4855 #[must_use]
4861 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4862 self.meta = meta.into_option();
4863 self
4864 }
4865}
4866
4867#[serde_as]
4871#[skip_serializing_none]
4872#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4873#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4874#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_CANCEL_METHOD_NAME)))]
4875#[serde(rename_all = "camelCase")]
4876#[non_exhaustive]
4877pub struct CancelSessionNotification {
4878 pub session_id: SessionId,
4880 #[serde_as(deserialize_as = "DefaultOnError")]
4886 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4887 #[serde(default)]
4888 #[serde(rename = "_meta")]
4889 pub meta: Option<Meta>,
4890}
4891
4892impl CancelSessionNotification {
4893 #[must_use]
4895 pub fn new(session_id: impl Into<SessionId>) -> Self {
4896 Self {
4897 session_id: session_id.into(),
4898 meta: None,
4899 }
4900 }
4901
4902 #[must_use]
4908 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4909 self.meta = meta.into_option();
4910 self
4911 }
4912}
4913
4914#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4920#[non_exhaustive]
4921pub struct AgentMethodNames {
4922 pub initialize: &'static str,
4924 pub auth_login: &'static str,
4926 #[cfg(feature = "unstable_llm_providers")]
4928 pub providers_list: &'static str,
4929 #[cfg(feature = "unstable_llm_providers")]
4931 pub providers_set: &'static str,
4932 #[cfg(feature = "unstable_llm_providers")]
4934 pub providers_disable: &'static str,
4935 pub session_new: &'static str,
4937 pub session_set_config_option: &'static str,
4939 pub session_prompt: &'static str,
4941 pub session_cancel: &'static str,
4943 #[cfg(feature = "unstable_mcp_over_acp")]
4945 pub mcp_message: &'static str,
4946 pub session_list: &'static str,
4948 pub session_delete: &'static str,
4950 #[cfg(feature = "unstable_session_fork")]
4952 pub session_fork: &'static str,
4953 pub session_resume: &'static str,
4955 pub session_close: &'static str,
4957 pub auth_logout: &'static str,
4959 #[cfg(feature = "unstable_nes")]
4961 pub nes_start: &'static str,
4962 #[cfg(feature = "unstable_nes")]
4964 pub nes_suggest: &'static str,
4965 #[cfg(feature = "unstable_nes")]
4967 pub nes_accept: &'static str,
4968 #[cfg(feature = "unstable_nes")]
4970 pub nes_reject: &'static str,
4971 #[cfg(feature = "unstable_nes")]
4973 pub nes_close: &'static str,
4974 #[cfg(feature = "unstable_nes")]
4976 pub document_did_open: &'static str,
4977 #[cfg(feature = "unstable_nes")]
4979 pub document_did_change: &'static str,
4980 #[cfg(feature = "unstable_nes")]
4982 pub document_did_close: &'static str,
4983 #[cfg(feature = "unstable_nes")]
4985 pub document_did_save: &'static str,
4986 #[cfg(feature = "unstable_nes")]
4988 pub document_did_focus: &'static str,
4989}
4990
4991pub const AGENT_METHOD_NAMES: AgentMethodNames = AgentMethodNames {
4993 initialize: INITIALIZE_METHOD_NAME,
4994 auth_login: AUTH_LOGIN_METHOD_NAME,
4995 #[cfg(feature = "unstable_llm_providers")]
4996 providers_list: PROVIDERS_LIST_METHOD_NAME,
4997 #[cfg(feature = "unstable_llm_providers")]
4998 providers_set: PROVIDERS_SET_METHOD_NAME,
4999 #[cfg(feature = "unstable_llm_providers")]
5000 providers_disable: PROVIDERS_DISABLE_METHOD_NAME,
5001 session_new: SESSION_NEW_METHOD_NAME,
5002 session_set_config_option: SESSION_SET_CONFIG_OPTION_METHOD_NAME,
5003 session_prompt: SESSION_PROMPT_METHOD_NAME,
5004 session_cancel: SESSION_CANCEL_METHOD_NAME,
5005 #[cfg(feature = "unstable_mcp_over_acp")]
5006 mcp_message: MCP_MESSAGE_METHOD_NAME,
5007 session_list: SESSION_LIST_METHOD_NAME,
5008 session_delete: SESSION_DELETE_METHOD_NAME,
5009 #[cfg(feature = "unstable_session_fork")]
5010 session_fork: SESSION_FORK_METHOD_NAME,
5011 session_resume: SESSION_RESUME_METHOD_NAME,
5012 session_close: SESSION_CLOSE_METHOD_NAME,
5013 auth_logout: AUTH_LOGOUT_METHOD_NAME,
5014 #[cfg(feature = "unstable_nes")]
5015 nes_start: NES_START_METHOD_NAME,
5016 #[cfg(feature = "unstable_nes")]
5017 nes_suggest: NES_SUGGEST_METHOD_NAME,
5018 #[cfg(feature = "unstable_nes")]
5019 nes_accept: NES_ACCEPT_METHOD_NAME,
5020 #[cfg(feature = "unstable_nes")]
5021 nes_reject: NES_REJECT_METHOD_NAME,
5022 #[cfg(feature = "unstable_nes")]
5023 nes_close: NES_CLOSE_METHOD_NAME,
5024 #[cfg(feature = "unstable_nes")]
5025 document_did_open: DOCUMENT_DID_OPEN_METHOD_NAME,
5026 #[cfg(feature = "unstable_nes")]
5027 document_did_change: DOCUMENT_DID_CHANGE_METHOD_NAME,
5028 #[cfg(feature = "unstable_nes")]
5029 document_did_close: DOCUMENT_DID_CLOSE_METHOD_NAME,
5030 #[cfg(feature = "unstable_nes")]
5031 document_did_save: DOCUMENT_DID_SAVE_METHOD_NAME,
5032 #[cfg(feature = "unstable_nes")]
5033 document_did_focus: DOCUMENT_DID_FOCUS_METHOD_NAME,
5034};
5035
5036pub(crate) const INITIALIZE_METHOD_NAME: &str = "initialize";
5038pub(crate) const AUTH_LOGIN_METHOD_NAME: &str = "auth/login";
5040#[cfg(feature = "unstable_llm_providers")]
5042pub(crate) const PROVIDERS_LIST_METHOD_NAME: &str = "providers/list";
5043#[cfg(feature = "unstable_llm_providers")]
5045pub(crate) const PROVIDERS_SET_METHOD_NAME: &str = "providers/set";
5046#[cfg(feature = "unstable_llm_providers")]
5048pub(crate) const PROVIDERS_DISABLE_METHOD_NAME: &str = "providers/disable";
5049pub(crate) const SESSION_NEW_METHOD_NAME: &str = "session/new";
5051pub(crate) const SESSION_SET_CONFIG_OPTION_METHOD_NAME: &str = "session/set_config_option";
5053pub(crate) const SESSION_PROMPT_METHOD_NAME: &str = "session/prompt";
5055pub(crate) const SESSION_CANCEL_METHOD_NAME: &str = "session/cancel";
5057pub(crate) const SESSION_LIST_METHOD_NAME: &str = "session/list";
5059pub(crate) const SESSION_DELETE_METHOD_NAME: &str = "session/delete";
5061#[cfg(feature = "unstable_session_fork")]
5063pub(crate) const SESSION_FORK_METHOD_NAME: &str = "session/fork";
5064pub(crate) const SESSION_RESUME_METHOD_NAME: &str = "session/resume";
5066pub(crate) const SESSION_CLOSE_METHOD_NAME: &str = "session/close";
5068pub(crate) const AUTH_LOGOUT_METHOD_NAME: &str = "auth/logout";
5070
5071#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5078#[derive(Clone, Debug, Serialize, Deserialize)]
5079#[serde(untagged)]
5080#[cfg_attr(feature = "schemars", schemars(inline))]
5081#[non_exhaustive]
5082pub enum ClientRequest {
5083 InitializeRequest(Box<InitializeRequest>),
5094 LoginAuthRequest(Box<LoginAuthRequest>),
5109 #[cfg(feature = "unstable_llm_providers")]
5115 ListProvidersRequest(Box<ListProvidersRequest>),
5116 #[cfg(feature = "unstable_llm_providers")]
5122 SetProviderRequest(Box<SetProviderRequest>),
5123 #[cfg(feature = "unstable_llm_providers")]
5129 DisableProviderRequest(Box<DisableProviderRequest>),
5130 LogoutAuthRequest(Box<LogoutAuthRequest>),
5140 NewSessionRequest(Box<NewSessionRequest>),
5153 ListSessionsRequest(Box<ListSessionsRequest>),
5157 DeleteSessionRequest(Box<DeleteSessionRequest>),
5161 #[cfg(feature = "unstable_session_fork")]
5162 ForkSessionRequest(Box<ForkSessionRequest>),
5174 ResumeSessionRequest(Box<ResumeSessionRequest>),
5180 CloseSessionRequest(Box<CloseSessionRequest>),
5185 SetSessionConfigOptionRequest(Box<SetSessionConfigOptionRequest>),
5187 PromptRequest(Box<PromptRequest>),
5199 #[cfg(feature = "unstable_nes")]
5200 StartNesRequest(Box<StartNesRequest>),
5206 #[cfg(feature = "unstable_nes")]
5207 SuggestNesRequest(Box<SuggestNesRequest>),
5213 #[cfg(feature = "unstable_nes")]
5214 CloseNesRequest(Box<CloseNesRequest>),
5223 #[cfg(feature = "unstable_mcp_over_acp")]
5229 MessageMcpRequest(Box<MessageMcpRequest>),
5230 ExtMethodRequest(Box<ExtRequest>),
5237}
5238
5239impl ClientRequest {
5240 #[must_use]
5242 pub fn method(&self) -> &str {
5243 match self {
5244 Self::InitializeRequest(_) => AGENT_METHOD_NAMES.initialize,
5245 Self::LoginAuthRequest(_) => AGENT_METHOD_NAMES.auth_login,
5246 #[cfg(feature = "unstable_llm_providers")]
5247 Self::ListProvidersRequest(_) => AGENT_METHOD_NAMES.providers_list,
5248 #[cfg(feature = "unstable_llm_providers")]
5249 Self::SetProviderRequest(_) => AGENT_METHOD_NAMES.providers_set,
5250 #[cfg(feature = "unstable_llm_providers")]
5251 Self::DisableProviderRequest(_) => AGENT_METHOD_NAMES.providers_disable,
5252 Self::LogoutAuthRequest(_) => AGENT_METHOD_NAMES.auth_logout,
5253 Self::NewSessionRequest(_) => AGENT_METHOD_NAMES.session_new,
5254 Self::ListSessionsRequest(_) => AGENT_METHOD_NAMES.session_list,
5255 Self::DeleteSessionRequest(_) => AGENT_METHOD_NAMES.session_delete,
5256 #[cfg(feature = "unstable_session_fork")]
5257 Self::ForkSessionRequest(_) => AGENT_METHOD_NAMES.session_fork,
5258 Self::ResumeSessionRequest(_) => AGENT_METHOD_NAMES.session_resume,
5259 Self::CloseSessionRequest(_) => AGENT_METHOD_NAMES.session_close,
5260 Self::SetSessionConfigOptionRequest(_) => AGENT_METHOD_NAMES.session_set_config_option,
5261 Self::PromptRequest(_) => AGENT_METHOD_NAMES.session_prompt,
5262 #[cfg(feature = "unstable_nes")]
5263 Self::StartNesRequest(_) => AGENT_METHOD_NAMES.nes_start,
5264 #[cfg(feature = "unstable_nes")]
5265 Self::SuggestNesRequest(_) => AGENT_METHOD_NAMES.nes_suggest,
5266 #[cfg(feature = "unstable_nes")]
5267 Self::CloseNesRequest(_) => AGENT_METHOD_NAMES.nes_close,
5268 #[cfg(feature = "unstable_mcp_over_acp")]
5269 Self::MessageMcpRequest(_) => AGENT_METHOD_NAMES.mcp_message,
5270 Self::ExtMethodRequest(ext_request) => &ext_request.method,
5271 }
5272 }
5273}
5274
5275#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5282#[derive(Clone, Debug, Serialize, Deserialize)]
5283#[serde(untagged)]
5284#[cfg_attr(feature = "schemars", schemars(inline))]
5285#[non_exhaustive]
5286pub enum AgentResponse {
5287 InitializeResponse(Box<InitializeResponse>),
5289 LoginAuthResponse(#[serde(default)] Box<LoginAuthResponse>),
5291 #[cfg(feature = "unstable_llm_providers")]
5293 ListProvidersResponse(Box<ListProvidersResponse>),
5294 #[cfg(feature = "unstable_llm_providers")]
5296 SetProviderResponse(#[serde(default)] Box<SetProviderResponse>),
5297 #[cfg(feature = "unstable_llm_providers")]
5299 DisableProviderResponse(#[serde(default)] Box<DisableProviderResponse>),
5300 LogoutAuthResponse(#[serde(default)] Box<LogoutAuthResponse>),
5302 NewSessionResponse(Box<NewSessionResponse>),
5304 ListSessionsResponse(Box<ListSessionsResponse>),
5306 DeleteSessionResponse(#[serde(default)] Box<DeleteSessionResponse>),
5308 #[cfg(feature = "unstable_session_fork")]
5310 ForkSessionResponse(Box<ForkSessionResponse>),
5311 ResumeSessionResponse(#[serde(default)] Box<ResumeSessionResponse>),
5313 CloseSessionResponse(#[serde(default)] Box<CloseSessionResponse>),
5315 SetSessionConfigOptionResponse(Box<SetSessionConfigOptionResponse>),
5317 PromptResponse(Box<PromptResponse>),
5319 #[cfg(feature = "unstable_nes")]
5321 StartNesResponse(Box<StartNesResponse>),
5322 #[cfg(feature = "unstable_nes")]
5324 SuggestNesResponse(Box<SuggestNesResponse>),
5325 #[cfg(feature = "unstable_nes")]
5327 CloseNesResponse(#[serde(default)] Box<CloseNesResponse>),
5328 ExtMethodResponse(Box<ExtResponse>),
5330 #[cfg(feature = "unstable_mcp_over_acp")]
5332 MessageMcpResponse(Box<MessageMcpResponse>),
5333}
5334
5335#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5342#[derive(Clone, Debug, Serialize, Deserialize)]
5343#[serde(untagged)]
5344#[cfg_attr(feature = "schemars", schemars(inline))]
5345#[non_exhaustive]
5346pub enum ClientNotification {
5347 CancelSessionNotification(Box<CancelSessionNotification>),
5361 #[cfg(feature = "unstable_nes")]
5362 DidOpenDocumentNotification(Box<DidOpenDocumentNotification>),
5366 #[cfg(feature = "unstable_nes")]
5367 DidChangeDocumentNotification(Box<DidChangeDocumentNotification>),
5371 #[cfg(feature = "unstable_nes")]
5372 DidCloseDocumentNotification(Box<DidCloseDocumentNotification>),
5376 #[cfg(feature = "unstable_nes")]
5377 DidSaveDocumentNotification(Box<DidSaveDocumentNotification>),
5381 #[cfg(feature = "unstable_nes")]
5382 DidFocusDocumentNotification(Box<DidFocusDocumentNotification>),
5386 #[cfg(feature = "unstable_nes")]
5387 AcceptNesNotification(Box<AcceptNesNotification>),
5391 #[cfg(feature = "unstable_nes")]
5392 RejectNesNotification(Box<RejectNesNotification>),
5396 #[cfg(feature = "unstable_mcp_over_acp")]
5402 MessageMcpNotification(Box<MessageMcpNotification>),
5403 ExtNotification(Box<ExtNotification>),
5410}
5411
5412impl ClientNotification {
5413 #[must_use]
5415 pub fn method(&self) -> &str {
5416 match self {
5417 Self::CancelSessionNotification(_) => AGENT_METHOD_NAMES.session_cancel,
5418 #[cfg(feature = "unstable_nes")]
5419 Self::DidOpenDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_open,
5420 #[cfg(feature = "unstable_nes")]
5421 Self::DidChangeDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_change,
5422 #[cfg(feature = "unstable_nes")]
5423 Self::DidCloseDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_close,
5424 #[cfg(feature = "unstable_nes")]
5425 Self::DidSaveDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_save,
5426 #[cfg(feature = "unstable_nes")]
5427 Self::DidFocusDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_focus,
5428 #[cfg(feature = "unstable_nes")]
5429 Self::AcceptNesNotification(_) => AGENT_METHOD_NAMES.nes_accept,
5430 #[cfg(feature = "unstable_nes")]
5431 Self::RejectNesNotification(_) => AGENT_METHOD_NAMES.nes_reject,
5432 #[cfg(feature = "unstable_mcp_over_acp")]
5433 Self::MessageMcpNotification(_) => AGENT_METHOD_NAMES.mcp_message,
5434 Self::ExtNotification(ext_notification) => &ext_notification.method,
5435 }
5436 }
5437}
5438
5439#[cfg(test)]
5440mod test_serialization {
5441 use std::path::PathBuf;
5442
5443 use super::*;
5444 use serde_json::json;
5445
5446 fn test_meta() -> Meta {
5447 json!({ "source": "test" }).as_object().unwrap().clone()
5448 }
5449
5450 fn serialized_meta_key_count(value: &impl serde::Serialize) -> usize {
5451 serde_json::to_string(value)
5452 .unwrap()
5453 .matches("\"_meta\"")
5454 .count()
5455 }
5456
5457 #[test]
5458 fn prompt_response_without_metadata_round_trips() {
5459 let response = PromptResponse::new(MessageId::new("message-1"));
5460 let serialized = serde_json::to_value(&response).unwrap();
5461 assert_eq!(serialized, json!({ "messageId": "message-1" }));
5462 assert_eq!(
5463 serde_json::from_value::<PromptResponse>(serialized).unwrap(),
5464 response
5465 );
5466 assert_eq!(
5467 serde_json::from_value::<PromptResponse>(json!({
5468 "messageId": "message-1",
5469 "_meta": null
5470 }))
5471 .unwrap(),
5472 response
5473 );
5474 }
5475
5476 #[test]
5477 fn prompt_response_requires_message_id_and_preserves_metadata_tolerance() {
5478 let response = PromptResponse::new("message-1").meta(test_meta());
5479 let serialized = serde_json::to_value(&response).unwrap();
5480 assert_eq!(
5481 serialized,
5482 json!({
5483 "messageId": "message-1",
5484 "_meta": { "source": "test" }
5485 })
5486 );
5487
5488 let deserialized: PromptResponse = serde_json::from_value(serialized).unwrap();
5489 assert_eq!(deserialized.message_id, MessageId::new("message-1"));
5490 assert_eq!(deserialized.meta, Some(test_meta()));
5491
5492 let malformed_meta: PromptResponse = serde_json::from_value(json!({
5493 "messageId": "message-2",
5494 "_meta": false
5495 }))
5496 .unwrap();
5497 assert_eq!(malformed_meta.message_id, MessageId::new("message-2"));
5498 assert_eq!(malformed_meta.meta, None);
5499
5500 for invalid in [
5501 json!({}),
5502 json!({ "messageId": null }),
5503 json!({ "messageId": 1 }),
5504 json!({ "messageId": false }),
5505 json!({ "messageId": {} }),
5506 json!({ "messageId": [] }),
5507 ] {
5508 assert!(
5509 serde_json::from_value::<PromptResponse>(invalid).is_err(),
5510 "missing, null, and non-string message IDs must be rejected"
5511 );
5512 }
5513 }
5514
5515 #[cfg(feature = "schemars")]
5516 #[test]
5517 fn prompt_response_schema_requires_non_null_string_message_id_reference() {
5518 let schema = serde_json::to_value(schemars::schema_for!(PromptResponse)).unwrap();
5519
5520 assert_eq!(schema["required"], json!(["messageId"]));
5521 assert_eq!(
5522 schema["properties"]["messageId"]["$ref"],
5523 "#/$defs/MessageId"
5524 );
5525 assert_eq!(schema["$defs"]["MessageId"]["type"], "string");
5526 }
5527
5528 #[test]
5529 fn test_initialize_capabilities_default_on_malformed_values() {
5530 let request: InitializeRequest = serde_json::from_value(json!({
5531 "protocolVersion": 2,
5532 "capabilities": false,
5533 "info": {
5534 "name": "client",
5535 "version": "1.0.0"
5536 }
5537 }))
5538 .unwrap();
5539 assert_eq!(request.capabilities, ClientCapabilities::default());
5540
5541 let response: InitializeResponse = serde_json::from_value(json!({
5542 "protocolVersion": 2,
5543 "capabilities": false,
5544 "info": {
5545 "name": "agent",
5546 "version": "1.0.0"
5547 }
5548 }))
5549 .unwrap();
5550 assert_eq!(response.capabilities, AgentCapabilities::default());
5551 }
5552
5553 #[test]
5554 fn test_agent_capabilities_default_on_malformed_values() {
5555 let capabilities: AgentCapabilities = serde_json::from_value(json!({
5556 "session": false,
5557 "auth": false
5558 }))
5559 .unwrap();
5560
5561 assert!(capabilities.session.is_none());
5562 assert_eq!(capabilities.auth, None);
5563 }
5564
5565 #[test]
5566 fn test_mcp_server_stdio_serialization() {
5567 let server = McpServer::Stdio(
5568 McpServerStdio::new("test-server", "/usr/bin/server")
5569 .args(vec!["--port".to_string(), "3000".to_string()])
5570 .env(vec![EnvVariable::new("API_KEY", "secret123")]),
5571 );
5572
5573 let json = serde_json::to_value(&server).unwrap();
5574 assert_eq!(
5575 json,
5576 json!({
5577 "type": "stdio",
5578 "name": "test-server",
5579 "command": "/usr/bin/server",
5580 "args": ["--port", "3000"],
5581 "env": [
5582 {
5583 "name": "API_KEY",
5584 "value": "secret123"
5585 }
5586 ]
5587 })
5588 );
5589
5590 let deserialized: McpServer = serde_json::from_value(json).unwrap();
5591 match deserialized {
5592 McpServer::Stdio(McpServerStdio {
5593 name,
5594 command,
5595 args,
5596 env,
5597 meta: _,
5598 }) => {
5599 assert_eq!(name, "test-server");
5600 assert_eq!(command, AbsolutePath::new("/usr/bin/server"));
5601 assert_eq!(args, vec!["--port", "3000"]);
5602 assert_eq!(env.len(), 1);
5603 assert_eq!(env[0].name, "API_KEY");
5604 assert_eq!(env[0].value, "secret123");
5605 }
5606 _ => panic!("Expected Stdio variant"),
5607 }
5608 }
5609
5610 #[test]
5611 fn test_mcp_server_empty_arrays_are_optional() {
5612 let stdio = McpServer::Stdio(McpServerStdio::new("test-server", "/usr/bin/server"));
5613 assert_eq!(
5614 serde_json::to_value(&stdio).unwrap(),
5615 json!({
5616 "type": "stdio",
5617 "name": "test-server",
5618 "command": "/usr/bin/server"
5619 })
5620 );
5621
5622 let McpServer::Stdio(McpServerStdio { args, env, .. }) =
5623 serde_json::from_value::<McpServer>(json!({
5624 "type": "stdio",
5625 "name": "test-server",
5626 "command": "/usr/bin/server"
5627 }))
5628 .unwrap()
5629 else {
5630 panic!("Expected Stdio variant");
5631 };
5632 assert!(args.is_empty());
5633 assert!(env.is_empty());
5634
5635 let http = McpServer::Http(McpServerHttp::new("http-server", "https://api.example.com"));
5636 assert_eq!(
5637 serde_json::to_value(&http).unwrap(),
5638 json!({
5639 "type": "http",
5640 "name": "http-server",
5641 "url": "https://api.example.com"
5642 })
5643 );
5644
5645 let McpServer::Http(McpServerHttp { headers, .. }) =
5646 serde_json::from_value::<McpServer>(json!({
5647 "type": "http",
5648 "name": "http-server",
5649 "url": "https://api.example.com"
5650 }))
5651 .unwrap()
5652 else {
5653 panic!("Expected Http variant");
5654 };
5655 assert!(headers.is_empty());
5656 }
5657
5658 #[test]
5659 fn test_mcp_server_unknown_transport_serialization() {
5660 let json = json!({
5661 "type": "websocket",
5662 "name": "future-server",
5663 "url": "wss://example.com/mcp",
5664 "protocolVersion": "2026-01-01"
5665 });
5666
5667 let deserialized: McpServer = serde_json::from_value(json.clone()).unwrap();
5668 let McpServer::Other(OtherMcpServer { type_, fields }) = &deserialized else {
5669 panic!("Expected Other variant");
5670 };
5671
5672 assert_eq!(type_, "websocket");
5673 assert_eq!(fields["name"], "future-server");
5674 assert_eq!(fields["url"], "wss://example.com/mcp");
5675 assert_eq!(fields["protocolVersion"], "2026-01-01");
5676 assert_eq!(serde_json::to_value(&deserialized).unwrap(), json);
5677 }
5678
5679 #[test]
5680 fn test_mcp_server_stdio_requires_type() {
5681 let result = serde_json::from_value::<McpServer>(json!({
5682 "name": "test-server",
5683 "command": "/usr/bin/server",
5684 "args": [],
5685 "env": []
5686 }));
5687
5688 assert!(result.is_err());
5689 }
5690
5691 #[test]
5692 fn test_mcp_server_unknown_does_not_hide_malformed_known_transport() {
5693 let result = serde_json::from_value::<McpServer>(json!({
5694 "type": "stdio",
5695 "name": "test-server",
5696 "args": [],
5697 "env": []
5698 }));
5699
5700 assert!(result.is_err());
5701 }
5702
5703 #[test]
5704 fn test_mcp_server_http_serialization() {
5705 let server = McpServer::Http(
5706 McpServerHttp::new("http-server", "https://api.example.com").headers(vec![
5707 HttpHeader::new("Authorization", "Bearer token123"),
5708 HttpHeader::new("Content-Type", "application/json"),
5709 ]),
5710 );
5711
5712 let json = serde_json::to_value(&server).unwrap();
5713 assert_eq!(
5714 json,
5715 json!({
5716 "type": "http",
5717 "name": "http-server",
5718 "url": "https://api.example.com",
5719 "headers": [
5720 {
5721 "name": "Authorization",
5722 "value": "Bearer token123"
5723 },
5724 {
5725 "name": "Content-Type",
5726 "value": "application/json"
5727 }
5728 ]
5729 })
5730 );
5731
5732 let deserialized: McpServer = serde_json::from_value(json).unwrap();
5733 match deserialized {
5734 McpServer::Http(McpServerHttp {
5735 name,
5736 url,
5737 headers,
5738 meta: _,
5739 }) => {
5740 assert_eq!(name, "http-server");
5741 assert_eq!(url, "https://api.example.com");
5742 assert_eq!(headers.len(), 2);
5743 assert_eq!(headers[0].name, "Authorization");
5744 assert_eq!(headers[0].value, "Bearer token123");
5745 assert_eq!(headers[1].name, "Content-Type");
5746 assert_eq!(headers[1].value, "application/json");
5747 }
5748 _ => panic!("Expected Http variant"),
5749 }
5750 }
5751
5752 #[cfg(feature = "schemars")]
5753 #[test]
5754 fn mcp_server_http_schema_marks_url_as_uri() {
5755 let schema = serde_json::to_value(schemars::schema_for!(McpServerHttp)).unwrap();
5756
5757 assert_eq!(schema["properties"]["url"]["format"], "uri");
5758 }
5759
5760 #[cfg(feature = "unstable_mcp_over_acp")]
5761 #[test]
5762 fn test_client_mcp_message_method_names() {
5763 assert_eq!(AGENT_METHOD_NAMES.mcp_message, "mcp/message");
5764
5765 assert_eq!(
5766 ClientRequest::MessageMcpRequest(Box::new(MessageMcpRequest::new(
5767 "conn-1",
5768 "tools/list"
5769 )))
5770 .method(),
5771 "mcp/message"
5772 );
5773 assert_eq!(
5774 ClientNotification::MessageMcpNotification(Box::new(MessageMcpNotification::new(
5775 "conn-1",
5776 "notifications/progress"
5777 )))
5778 .method(),
5779 "mcp/message"
5780 );
5781 }
5782
5783 #[test]
5784 fn test_auth_method_names() {
5785 assert_eq!(AGENT_METHOD_NAMES.auth_login, "auth/login");
5786 assert_eq!(AGENT_METHOD_NAMES.auth_logout, "auth/logout");
5787
5788 assert_eq!(
5789 ClientRequest::LoginAuthRequest(Box::new(LoginAuthRequest::new("agent-login")))
5790 .method(),
5791 "auth/login"
5792 );
5793 assert_eq!(
5794 ClientRequest::LogoutAuthRequest(Box::new(LogoutAuthRequest::new())).method(),
5795 "auth/logout"
5796 );
5797 }
5798
5799 #[test]
5800 fn test_session_config_option_category_known_variants() {
5801 assert_eq!(
5803 serde_json::to_value(&SessionConfigOptionCategory::Mode).unwrap(),
5804 json!("mode")
5805 );
5806 assert_eq!(
5807 serde_json::to_value(&SessionConfigOptionCategory::Model).unwrap(),
5808 json!("model")
5809 );
5810 assert_eq!(
5811 serde_json::to_value(&SessionConfigOptionCategory::ModelConfig).unwrap(),
5812 json!("model_config")
5813 );
5814 assert_eq!(
5815 serde_json::to_value(&SessionConfigOptionCategory::ThoughtLevel).unwrap(),
5816 json!("thought_level")
5817 );
5818
5819 assert_eq!(
5821 serde_json::from_str::<SessionConfigOptionCategory>("\"mode\"").unwrap(),
5822 SessionConfigOptionCategory::Mode
5823 );
5824 assert_eq!(
5825 serde_json::from_str::<SessionConfigOptionCategory>("\"model\"").unwrap(),
5826 SessionConfigOptionCategory::Model
5827 );
5828 assert_eq!(
5829 serde_json::from_str::<SessionConfigOptionCategory>("\"model_config\"").unwrap(),
5830 SessionConfigOptionCategory::ModelConfig
5831 );
5832 assert_eq!(
5833 serde_json::from_str::<SessionConfigOptionCategory>("\"thought_level\"").unwrap(),
5834 SessionConfigOptionCategory::ThoughtLevel
5835 );
5836 }
5837
5838 #[test]
5839 fn test_session_config_option_category_unknown_variants() {
5840 let unknown: SessionConfigOptionCategory =
5842 serde_json::from_str("\"some_future_category\"").unwrap();
5843 assert_eq!(
5844 unknown,
5845 SessionConfigOptionCategory::Other("some_future_category".to_string())
5846 );
5847
5848 let json = serde_json::to_value(&unknown).unwrap();
5850 assert_eq!(json, json!("some_future_category"));
5851 }
5852
5853 #[test]
5854 fn test_session_config_option_category_custom_categories() {
5855 let custom: SessionConfigOptionCategory =
5857 serde_json::from_str("\"_my_custom_category\"").unwrap();
5858 assert_eq!(
5859 custom,
5860 SessionConfigOptionCategory::Other("_my_custom_category".to_string())
5861 );
5862
5863 let json = serde_json::to_value(&custom).unwrap();
5865 assert_eq!(json, json!("_my_custom_category"));
5866
5867 let deserialized: SessionConfigOptionCategory = serde_json::from_value(json).unwrap();
5869 assert_eq!(
5870 deserialized,
5871 SessionConfigOptionCategory::Other("_my_custom_category".to_string()),
5872 );
5873 }
5874
5875 fn test_config_option() -> SessionConfigOption {
5876 SessionConfigOption::select(
5877 "mode",
5878 "Mode",
5879 "ask",
5880 vec![SessionConfigSelectOption::new("ask", "Ask")],
5881 )
5882 }
5883
5884 #[test]
5885 fn test_session_response_config_options_default_empty_and_skip_serializing() {
5886 assert_eq!(
5887 serde_json::to_value(NewSessionResponse::new("sess")).unwrap(),
5888 json!({ "sessionId": "sess" })
5889 );
5890 assert_eq!(
5891 serde_json::to_value(ResumeSessionResponse::new()).unwrap(),
5892 json!({})
5893 );
5894 #[cfg(feature = "unstable_session_fork")]
5895 assert_eq!(
5896 serde_json::to_value(ForkSessionResponse::new("fork")).unwrap(),
5897 json!({ "sessionId": "fork" })
5898 );
5899
5900 let json = serde_json::to_value(
5901 NewSessionResponse::new("sess").config_options(vec![test_config_option()]),
5902 )
5903 .unwrap();
5904 assert_eq!(json["configOptions"].as_array().unwrap().len(), 1);
5905 }
5906
5907 #[test]
5908 fn test_session_response_config_options_deserialize_missing_null_and_invalid() {
5909 let missing: NewSessionResponse =
5910 serde_json::from_value(json!({ "sessionId": "sess" })).unwrap();
5911 assert!(missing.config_options.is_empty());
5912
5913 let null: NewSessionResponse = serde_json::from_value(json!({
5914 "sessionId": "sess",
5915 "configOptions": null
5916 }))
5917 .unwrap();
5918 assert!(null.config_options.is_empty());
5919
5920 let wrong_shape: NewSessionResponse = serde_json::from_value(json!({
5921 "sessionId": "sess",
5922 "configOptions": "oops"
5923 }))
5924 .unwrap();
5925 assert!(wrong_shape.config_options.is_empty());
5926
5927 let valid_option = serde_json::to_value(test_config_option()).unwrap();
5928 let mixed: NewSessionResponse = serde_json::from_value(json!({
5929 "sessionId": "sess",
5930 "configOptions": ["oops", valid_option]
5931 }))
5932 .unwrap();
5933 assert_eq!(mixed.config_options.len(), 1);
5934
5935 let resume: ResumeSessionResponse = serde_json::from_value(json!({})).unwrap();
5936 assert!(resume.config_options.is_empty());
5937 #[cfg(feature = "unstable_session_fork")]
5938 {
5939 let fork: ForkSessionResponse =
5940 serde_json::from_value(json!({ "sessionId": "fork" })).unwrap();
5941 assert!(fork.config_options.is_empty());
5942 }
5943 }
5944
5945 #[test]
5946 fn test_resume_session_replay_from_serialization() {
5947 assert_eq!(
5948 serde_json::to_value(ResumeSessionRequest::new(
5949 "sess_abc123",
5950 "/home/user/project"
5951 ))
5952 .unwrap(),
5953 json!({
5954 "sessionId": "sess_abc123",
5955 "cwd": "/home/user/project"
5956 })
5957 );
5958 assert_eq!(
5959 serde_json::to_value(
5960 ResumeSessionRequest::new("sess_abc123", "/home/user/project")
5961 .replay_from(ReplayFrom::from(ReplayFromStart::new()))
5962 )
5963 .unwrap(),
5964 json!({
5965 "sessionId": "sess_abc123",
5966 "cwd": "/home/user/project",
5967 "replayFrom": {
5968 "type": "start"
5969 }
5970 })
5971 );
5972
5973 let replay: ResumeSessionRequest = serde_json::from_value(json!({
5974 "sessionId": "sess_abc123",
5975 "cwd": "/home/user/project",
5976 "replayFrom": {
5977 "type": "start"
5978 }
5979 }))
5980 .unwrap();
5981 assert!(matches!(replay.replay_from, Some(ReplayFrom::Start(_))));
5982
5983 let none: ResumeSessionRequest = serde_json::from_value(json!({
5984 "sessionId": "sess_abc123",
5985 "cwd": "/home/user/project",
5986 "replayFrom": null
5987 }))
5988 .unwrap();
5989 assert!(none.replay_from.is_none());
5990 }
5991
5992 #[test]
5993 fn test_auth_method_agent_serialization() {
5994 let method = AuthMethod::Agent(AuthMethodAgent::new("default-auth", "Default Auth"));
5995
5996 let json = serde_json::to_value(&method).unwrap();
5997 assert_eq!(
5998 json,
5999 json!({
6000 "methodId": "default-auth",
6001 "name": "Default Auth",
6002 "type": "agent"
6003 })
6004 );
6005 assert!(!json.as_object().unwrap().contains_key("description"));
6007
6008 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
6009 match deserialized {
6010 AuthMethod::Agent(AuthMethodAgent {
6011 method_id, name, ..
6012 }) => {
6013 assert_eq!(method_id.0.as_ref(), "default-auth");
6014 assert_eq!(name, "Default Auth");
6015 }
6016 _ => panic!("Expected Agent variant"),
6017 }
6018 }
6019
6020 #[test]
6021 fn test_auth_method_agent_deserialization() {
6022 let json = json!({
6023 "methodId": "agent-auth",
6024 "name": "Agent Auth",
6025 "type": "agent"
6026 });
6027
6028 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
6029 assert!(matches!(deserialized, AuthMethod::Agent(_)));
6030 }
6031
6032 #[test]
6033 fn test_auth_method_agent_requires_type() {
6034 assert!(
6035 serde_json::from_value::<AuthMethod>(json!({
6036 "methodId": "agent-auth",
6037 "name": "Agent Auth"
6038 }))
6039 .is_err()
6040 );
6041 }
6042
6043 #[test]
6044 fn test_auth_method_agent_rejects_null_type() {
6045 assert!(
6046 serde_json::from_value::<AuthMethod>(json!({
6047 "methodId": "agent-auth",
6048 "name": "Agent Auth",
6049 "type": null
6050 }))
6051 .is_err()
6052 );
6053 }
6054
6055 #[test]
6056 fn test_auth_method_unknown_does_not_hide_malformed_agent() {
6057 assert!(
6058 serde_json::from_value::<AuthMethod>(json!({
6059 "methodId": "agent-auth",
6060 "type": "agent"
6061 }))
6062 .is_err()
6063 );
6064 assert!(
6065 serde_json::from_value::<AuthMethod>(json!({
6066 "methodId": "api-key",
6067 "type": "env_var",
6068 "vars": [{"name": "API_KEY"}]
6069 }))
6070 .is_err()
6071 );
6072 }
6073
6074 #[test]
6075 fn test_auth_method_unknown_variant_roundtrip() {
6076 let method: AuthMethod = serde_json::from_value(json!({
6077 "methodId": "oauth",
6078 "name": "OAuth",
6079 "type": "_oauth",
6080 "authorizationUrl": "https://example.com/auth"
6081 }))
6082 .unwrap();
6083
6084 assert_eq!(method.method_id().0.as_ref(), "oauth");
6085 assert_eq!(method.name(), "OAuth");
6086 let AuthMethod::Other(unknown) = method else {
6087 panic!("expected unknown auth method");
6088 };
6089 assert_eq!(unknown.type_, "_oauth");
6090 assert_eq!(
6091 unknown.fields.get("authorizationUrl"),
6092 Some(&json!("https://example.com/auth"))
6093 );
6094
6095 assert_eq!(
6096 serde_json::to_value(AuthMethod::Other(unknown)).unwrap(),
6097 json!({
6098 "methodId": "oauth",
6099 "name": "OAuth",
6100 "type": "_oauth",
6101 "authorizationUrl": "https://example.com/auth"
6102 })
6103 );
6104 }
6105
6106 #[test]
6107 fn test_auth_method_unknown_does_not_hide_malformed_known_variant() {
6108 assert!(
6109 serde_json::from_value::<AuthMethod>(json!({
6110 "methodId": "terminal-auth",
6111 "type": "terminal"
6112 }))
6113 .is_err()
6114 );
6115 }
6116
6117 #[test]
6118 fn test_session_delete_serialization() {
6119 assert_eq!(AGENT_METHOD_NAMES.session_delete, "session/delete");
6120 assert_eq!(
6121 ClientRequest::DeleteSessionRequest(Box::new(DeleteSessionRequest::new("sess_abc123")))
6122 .method(),
6123 "session/delete"
6124 );
6125 assert_eq!(
6126 serde_json::to_value(DeleteSessionRequest::new("sess_abc123")).unwrap(),
6127 json!({
6128 "sessionId": "sess_abc123"
6129 })
6130 );
6131 assert_eq!(
6132 serde_json::to_value(DeleteSessionResponse::new()).unwrap(),
6133 json!({})
6134 );
6135 assert_eq!(
6136 serde_json::to_value(
6137 SessionCapabilities::new().delete(SessionDeleteCapabilities::new())
6138 )
6139 .unwrap(),
6140 json!({
6141 "delete": {}
6142 })
6143 );
6144 }
6145 #[test]
6146 fn test_session_additional_directories_serialization() {
6147 assert_eq!(
6148 serde_json::to_value(NewSessionRequest::new("/home/user/project")).unwrap(),
6149 json!({
6150 "cwd": "/home/user/project",
6151 })
6152 );
6153 assert_eq!(
6154 serde_json::to_value(
6155 NewSessionRequest::new("/home/user/project").additional_directories(vec![
6156 PathBuf::from("/home/user/shared-lib"),
6157 PathBuf::from("/home/user/product-docs"),
6158 ])
6159 )
6160 .unwrap(),
6161 json!({
6162 "cwd": "/home/user/project",
6163 "additionalDirectories": [
6164 "/home/user/shared-lib",
6165 "/home/user/product-docs"
6166 ],
6167 })
6168 );
6169 assert_eq!(
6170 serde_json::to_value(ResumeSessionRequest::new(
6171 "sess_abc123",
6172 "/home/user/project"
6173 ))
6174 .unwrap(),
6175 json!({
6176 "sessionId": "sess_abc123",
6177 "cwd": "/home/user/project",
6178 })
6179 );
6180 assert_eq!(
6181 serde_json::from_value::<ResumeSessionRequest>(json!({
6182 "sessionId": "sess_abc123",
6183 "cwd": "/home/user/project"
6184 }))
6185 .unwrap()
6186 .mcp_servers,
6187 Vec::<McpServer>::new()
6188 );
6189 assert_eq!(
6190 serde_json::from_value::<ResumeSessionRequest>(json!({
6191 "sessionId": "sess_abc123",
6192 "cwd": "/home/user/project",
6193 "mcpServers": null
6194 }))
6195 .unwrap()
6196 .mcp_servers,
6197 Vec::<McpServer>::new()
6198 );
6199 assert_eq!(
6200 serde_json::to_value(SessionInfo::new("sess_abc123", "/home/user/project")).unwrap(),
6201 json!({
6202 "sessionId": "sess_abc123",
6203 "cwd": "/home/user/project"
6204 })
6205 );
6206 assert_eq!(
6207 serde_json::to_value(
6208 SessionInfo::new("sess_abc123", "/home/user/project").additional_directories(vec![
6209 PathBuf::from("/home/user/shared-lib"),
6210 PathBuf::from("/home/user/product-docs"),
6211 ])
6212 )
6213 .unwrap(),
6214 json!({
6215 "sessionId": "sess_abc123",
6216 "cwd": "/home/user/project",
6217 "additionalDirectories": [
6218 "/home/user/shared-lib",
6219 "/home/user/product-docs"
6220 ]
6221 })
6222 );
6223 assert_eq!(
6224 serde_json::from_value::<SessionInfo>(json!({
6225 "sessionId": "sess_abc123",
6226 "cwd": "/home/user/project"
6227 }))
6228 .unwrap()
6229 .additional_directories,
6230 Vec::<AbsolutePath>::new()
6231 );
6232 }
6233 #[test]
6234 fn test_session_additional_directories_capabilities_serialization() {
6235 assert_eq!(
6236 serde_json::to_value(
6237 SessionCapabilities::new()
6238 .additional_directories(SessionAdditionalDirectoriesCapabilities::new())
6239 )
6240 .unwrap(),
6241 json!({
6242 "additionalDirectories": {}
6243 })
6244 );
6245 }
6246
6247 #[test]
6248 fn test_auth_method_terminal_serialization() {
6249 let method = AuthMethod::Terminal(AuthMethodTerminal::new("tui-auth", "Terminal Auth"));
6250
6251 let json = serde_json::to_value(&method).unwrap();
6252 assert_eq!(
6253 json,
6254 json!({
6255 "methodId": "tui-auth",
6256 "name": "Terminal Auth",
6257 "type": "terminal"
6258 })
6259 );
6260 assert!(!json.as_object().unwrap().contains_key("args"));
6262 assert!(!json.as_object().unwrap().contains_key("env"));
6263
6264 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
6265 match deserialized {
6266 AuthMethod::Terminal(AuthMethodTerminal { args, env, .. }) => {
6267 assert!(args.is_empty());
6268 assert!(env.is_empty());
6269 }
6270 _ => panic!("Expected Terminal variant"),
6271 }
6272 }
6273
6274 #[test]
6275 fn test_auth_method_terminal_with_args_and_env_serialization() {
6276 let method = AuthMethod::Terminal(
6277 AuthMethodTerminal::new("tui-auth", "Terminal Auth")
6278 .args(vec!["--interactive".to_string(), "--color".to_string()])
6279 .env(vec![EnvVariable::new("TERM", "xterm-256color")]),
6280 );
6281
6282 let json = serde_json::to_value(&method).unwrap();
6283 assert_eq!(
6284 json,
6285 json!({
6286 "methodId": "tui-auth",
6287 "name": "Terminal Auth",
6288 "type": "terminal",
6289 "args": ["--interactive", "--color"],
6290 "env": [
6291 {
6292 "name": "TERM",
6293 "value": "xterm-256color"
6294 }
6295 ]
6296 })
6297 );
6298
6299 let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
6300 match deserialized {
6301 AuthMethod::Terminal(AuthMethodTerminal { args, env, .. }) => {
6302 assert_eq!(args, vec!["--interactive", "--color"]);
6303 assert_eq!(env.len(), 1);
6304 assert_eq!(env[0].name, "TERM");
6305 assert_eq!(env[0].value, "xterm-256color");
6306 }
6307 _ => panic!("Expected Terminal variant"),
6308 }
6309 }
6310
6311 #[test]
6312 fn test_session_config_option_id_serialize() {
6313 let val = SessionConfigOptionValue::id("model-1");
6314 let json = serde_json::to_value(&val).unwrap();
6315 assert_eq!(json, json!({ "type": "id", "value": "model-1" }));
6316 }
6317
6318 #[test]
6319 fn test_session_config_option_value_boolean_serialize() {
6320 let val = SessionConfigOptionValue::boolean(true);
6321 let json = serde_json::to_value(&val).unwrap();
6322 assert_eq!(json, json!({ "type": "boolean", "value": true }));
6323 }
6324
6325 #[test]
6326 fn test_session_config_option_value_deserialize_id() {
6327 let json = json!({ "type": "id", "value": "model-1" });
6328 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6329 assert_eq!(val, SessionConfigOptionValue::id("model-1"));
6330 assert_eq!(val.as_id().unwrap().to_string(), "model-1");
6331 }
6332
6333 #[test]
6334 fn test_session_config_option_value_deserialize_requires_type() {
6335 let json = json!({ "value": "model-1" });
6336 let result = serde_json::from_value::<SessionConfigOptionValue>(json);
6337 assert!(result.is_err());
6338 }
6339
6340 #[test]
6341 fn test_session_config_option_value_deserialize_boolean() {
6342 let json = json!({ "type": "boolean", "value": true });
6343 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6344 assert_eq!(val, SessionConfigOptionValue::boolean(true));
6345 assert_eq!(val.as_bool(), Some(true));
6346 }
6347
6348 #[test]
6349 fn test_session_config_option_value_deserialize_boolean_false() {
6350 let json = json!({ "type": "boolean", "value": false });
6351 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6352 assert_eq!(val, SessionConfigOptionValue::boolean(false));
6353 assert_eq!(val.as_bool(), Some(false));
6354 }
6355
6356 #[test]
6357 fn test_session_config_option_value_deserialize_unknown_type_with_string_value() {
6358 let json = json!({
6359 "type": "text",
6360 "value": "freeform input",
6361 "maxLength": 200
6362 });
6363 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6364 let SessionConfigOptionValue::Other(unknown) = val else {
6365 panic!("Expected Other variant");
6366 };
6367 assert_eq!(unknown.type_, "text");
6368 assert_eq!(unknown.value, json!("freeform input"));
6369 assert_eq!(unknown.fields["maxLength"], json!(200));
6370 }
6371
6372 #[test]
6373 fn test_session_config_option_value_deserialize_unknown_type_with_object_value() {
6374 let json = json!({
6375 "type": "range",
6376 "value": { "min": 1, "max": 5 }
6377 });
6378 let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6379 let SessionConfigOptionValue::Other(unknown) = val else {
6380 panic!("Expected Other variant");
6381 };
6382 assert_eq!(unknown.type_, "range");
6383 assert_eq!(unknown.value, json!({ "min": 1, "max": 5 }));
6384 }
6385
6386 #[test]
6387 fn test_session_config_option_value_roundtrip_id() {
6388 let original = SessionConfigOptionValue::id("option-a");
6389 let json = serde_json::to_value(&original).unwrap();
6390 let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6391 assert_eq!(original, roundtripped);
6392 }
6393
6394 #[test]
6395 fn test_session_config_option_value_roundtrip_boolean() {
6396 let original = SessionConfigOptionValue::boolean(false);
6397 let json = serde_json::to_value(&original).unwrap();
6398 let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6399 assert_eq!(original, roundtripped);
6400 }
6401
6402 #[test]
6403 fn test_session_config_option_value_roundtrip_other() {
6404 let mut fields = BTreeMap::new();
6405 fields.insert("maxLength".to_string(), json!(200));
6406 let original = SessionConfigOptionValue::Other(OtherSessionConfigOptionValue::new(
6407 "text",
6408 json!("freeform input"),
6409 fields,
6410 ));
6411 let json = serde_json::to_value(&original).unwrap();
6412 let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6413 assert_eq!(original, roundtripped);
6414 }
6415
6416 #[test]
6417 fn test_session_config_option_value_type_mismatch_boolean_with_string() {
6418 let json = json!({ "type": "boolean", "value": "not a bool" });
6419 let result = serde_json::from_value::<SessionConfigOptionValue>(json);
6420 assert!(result.is_err());
6421 }
6422
6423 #[test]
6424 fn test_session_config_option_value_from_impls() {
6425 let from_str: SessionConfigOptionValue = "model-1".into();
6426 assert_eq!(from_str.as_id().unwrap().to_string(), "model-1");
6427
6428 let from_id: SessionConfigOptionValue = SessionConfigValueId::new("model-2").into();
6429 assert_eq!(from_id.as_id().unwrap().to_string(), "model-2");
6430
6431 let from_bool: SessionConfigOptionValue = true.into();
6432 assert_eq!(from_bool.as_bool(), Some(true));
6433 }
6434
6435 #[test]
6436 fn test_set_session_config_option_request_id() {
6437 let req = SetSessionConfigOptionRequest::new("sess_1", "model", "model-1");
6438 let json = serde_json::to_value(&req).unwrap();
6439 assert_eq!(
6440 json,
6441 json!({
6442 "sessionId": "sess_1",
6443 "configId": "model",
6444 "type": "id",
6445 "value": "model-1"
6446 })
6447 );
6448 }
6449
6450 #[test]
6451 fn test_set_session_config_option_request_boolean() {
6452 let req = SetSessionConfigOptionRequest::new("sess_1", "brave_mode", true);
6453 let json = serde_json::to_value(&req).unwrap();
6454 assert_eq!(
6455 json,
6456 json!({
6457 "sessionId": "sess_1",
6458 "configId": "brave_mode",
6459 "type": "boolean",
6460 "value": true
6461 })
6462 );
6463 }
6464
6465 #[test]
6466 fn test_set_session_config_option_request_deserialize_requires_type() {
6467 let json = json!({
6468 "sessionId": "sess_1",
6469 "configId": "model",
6470 "value": "model-1"
6471 });
6472 let result = serde_json::from_value::<SetSessionConfigOptionRequest>(json);
6473 assert!(result.is_err());
6474 }
6475
6476 #[test]
6477 fn test_set_session_config_option_request_deserialize_boolean() {
6478 let json = json!({
6479 "sessionId": "sess_1",
6480 "configId": "brave_mode",
6481 "type": "boolean",
6482 "value": true
6483 });
6484 let req: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
6485 assert_eq!(req.value.as_bool(), Some(true));
6486 }
6487
6488 #[test]
6489 fn test_set_session_config_option_request_roundtrip_id() {
6490 let original = SetSessionConfigOptionRequest::new("s", "c", "v");
6491 let json = serde_json::to_value(&original).unwrap();
6492 let roundtripped: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
6493 assert_eq!(original, roundtripped);
6494 }
6495
6496 #[test]
6497 fn test_set_session_config_option_request_roundtrip_boolean() {
6498 let original = SetSessionConfigOptionRequest::new("s", "c", false);
6499 let json = serde_json::to_value(&original).unwrap();
6500 let roundtripped: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
6501 assert_eq!(original, roundtripped);
6502 }
6503
6504 #[test]
6505 fn test_session_config_boolean_serialization() {
6506 let cfg = SessionConfigBoolean::new(true);
6507 let json = serde_json::to_value(&cfg).unwrap();
6508 assert_eq!(json, json!({ "currentValue": true }));
6509
6510 let deserialized: SessionConfigBoolean = serde_json::from_value(json).unwrap();
6511 assert!(deserialized.current_value);
6512 }
6513
6514 #[test]
6515 fn test_session_config_option_boolean_variant() {
6516 let opt = SessionConfigOption::boolean("brave_mode", "Brave Mode", false)
6517 .description("Skip confirmation prompts")
6518 .meta(test_meta());
6519 assert_eq!(serialized_meta_key_count(&opt), 1);
6520
6521 let json = serde_json::to_value(&opt).unwrap();
6522 assert_eq!(
6523 json,
6524 json!({
6525 "configId": "brave_mode",
6526 "name": "Brave Mode",
6527 "description": "Skip confirmation prompts",
6528 "type": "boolean",
6529 "currentValue": false,
6530 "_meta": {
6531 "source": "test"
6532 }
6533 })
6534 );
6535
6536 let deserialized: SessionConfigOption = serde_json::from_value(json).unwrap();
6537 assert_eq!(deserialized.config_id.to_string(), "brave_mode");
6538 assert_eq!(deserialized.name, "Brave Mode");
6539 match deserialized.kind {
6540 SessionConfigKind::Boolean(ref b) => assert!(!b.current_value),
6541 _ => panic!("Expected Boolean kind"),
6542 }
6543 }
6544
6545 #[test]
6546 fn test_session_config_option_select_still_works() {
6547 let opt = SessionConfigOption::select(
6549 "model",
6550 "Model",
6551 "model-1",
6552 vec![
6553 SessionConfigSelectOption::new("model-1", "Model 1"),
6554 SessionConfigSelectOption::new("model-2", "Model 2"),
6555 ],
6556 )
6557 .meta(test_meta());
6558 assert_eq!(serialized_meta_key_count(&opt), 1);
6559
6560 let json = serde_json::to_value(&opt).unwrap();
6561 assert_eq!(json["type"], "select");
6562 assert_eq!(json["currentValue"], "model-1");
6563 assert_eq!(json["options"].as_array().unwrap().len(), 2);
6564 assert_eq!(json["_meta"]["source"], "test");
6565
6566 let deserialized: SessionConfigOption = serde_json::from_value(json).unwrap();
6567 match deserialized.kind {
6568 SessionConfigKind::Select(ref s) => {
6569 assert_eq!(s.current_value.to_string(), "model-1");
6570 }
6571 _ => panic!("Expected Select kind"),
6572 }
6573 }
6574
6575 #[test]
6576 fn test_session_config_option_unknown_kind_roundtrip() {
6577 let option: SessionConfigOption = serde_json::from_value(json!({
6578 "configId": "verbosity",
6579 "name": "Verbosity",
6580 "type": "_slider",
6581 "currentValue": 3,
6582 "min": 0,
6583 "max": 5,
6584 "_meta": {
6585 "source": "test"
6586 }
6587 }))
6588 .unwrap();
6589
6590 assert_eq!(option.config_id.to_string(), "verbosity");
6591 assert_eq!(option.meta.as_ref().unwrap()["source"], "test");
6592 let SessionConfigKind::Other(unknown) = &option.kind else {
6593 panic!("expected unknown config kind");
6594 };
6595 assert_eq!(unknown.type_, "_slider");
6596 assert_eq!(unknown.fields.get("currentValue"), Some(&json!(3)));
6597 assert!(!unknown.fields.contains_key("_meta"));
6598 assert_eq!(serialized_meta_key_count(&option), 1);
6599
6600 let json = serde_json::to_value(&option).unwrap();
6601 assert_eq!(json["type"], "_slider");
6602 assert_eq!(json["currentValue"], 3);
6603 assert_eq!(json["min"], 0);
6604 assert_eq!(json["max"], 5);
6605 assert_eq!(json["_meta"]["source"], "test");
6606 }
6607
6608 #[test]
6609 fn test_session_config_option_unknown_kind_does_not_duplicate_flattened_meta() {
6610 let mut fields = std::collections::BTreeMap::new();
6611 fields.insert("currentValue".to_string(), json!(3));
6612 fields.insert("_meta".to_string(), json!({ "inner": "ignored" }));
6613
6614 let option = SessionConfigOption::new(
6615 "verbosity",
6616 "Verbosity",
6617 SessionConfigKind::Other(OtherSessionConfigKind::new("_slider", fields)),
6618 )
6619 .meta(test_meta());
6620
6621 let SessionConfigKind::Other(unknown) = &option.kind else {
6622 panic!("expected unknown config kind");
6623 };
6624 assert!(!unknown.fields.contains_key("_meta"));
6625 assert_eq!(serialized_meta_key_count(&option), 1);
6626
6627 let json = serde_json::to_value(&option).unwrap();
6628 assert_eq!(json["type"], "_slider");
6629 assert_eq!(json["currentValue"], 3);
6630 assert_eq!(json["_meta"]["source"], "test");
6631 }
6632
6633 #[test]
6634 fn test_session_config_option_unknown_does_not_hide_malformed_known_kind() {
6635 assert!(
6636 serde_json::from_value::<SessionConfigOption>(json!({
6637 "configId": "model",
6638 "name": "Model",
6639 "type": "select"
6640 }))
6641 .is_err()
6642 );
6643 }
6644
6645 #[cfg(feature = "unstable_llm_providers")]
6646 #[test]
6647 fn test_llm_protocol_known_variants() {
6648 assert_eq!(
6649 serde_json::to_value(&LlmProtocol::Anthropic).unwrap(),
6650 json!("anthropic")
6651 );
6652 assert_eq!(
6653 serde_json::to_value(&LlmProtocol::OpenAi).unwrap(),
6654 json!("openai")
6655 );
6656 assert_eq!(
6657 serde_json::to_value(&LlmProtocol::Azure).unwrap(),
6658 json!("azure")
6659 );
6660 assert_eq!(
6661 serde_json::to_value(&LlmProtocol::Vertex).unwrap(),
6662 json!("vertex")
6663 );
6664 assert_eq!(
6665 serde_json::to_value(&LlmProtocol::Bedrock).unwrap(),
6666 json!("bedrock")
6667 );
6668
6669 assert_eq!(
6670 serde_json::from_str::<LlmProtocol>("\"anthropic\"").unwrap(),
6671 LlmProtocol::Anthropic
6672 );
6673 assert_eq!(
6674 serde_json::from_str::<LlmProtocol>("\"openai\"").unwrap(),
6675 LlmProtocol::OpenAi
6676 );
6677 assert_eq!(
6678 serde_json::from_str::<LlmProtocol>("\"azure\"").unwrap(),
6679 LlmProtocol::Azure
6680 );
6681 assert_eq!(
6682 serde_json::from_str::<LlmProtocol>("\"vertex\"").unwrap(),
6683 LlmProtocol::Vertex
6684 );
6685 assert_eq!(
6686 serde_json::from_str::<LlmProtocol>("\"bedrock\"").unwrap(),
6687 LlmProtocol::Bedrock
6688 );
6689 }
6690
6691 #[cfg(feature = "unstable_llm_providers")]
6692 #[test]
6693 fn test_llm_protocol_unknown_variant() {
6694 let unknown: LlmProtocol = serde_json::from_str("\"cohere\"").unwrap();
6695 assert_eq!(unknown, LlmProtocol::Other("cohere".to_string()));
6696
6697 let json = serde_json::to_value(&unknown).unwrap();
6698 assert_eq!(json, json!("cohere"));
6699 }
6700
6701 #[cfg(feature = "unstable_llm_providers")]
6702 #[test]
6703 fn test_provider_current_config_serialization() {
6704 let config =
6705 ProviderCurrentConfig::new(LlmProtocol::Anthropic, "https://api.anthropic.com");
6706
6707 let json = serde_json::to_value(&config).unwrap();
6708 assert_eq!(
6709 json,
6710 json!({
6711 "apiType": "anthropic",
6712 "baseUrl": "https://api.anthropic.com"
6713 })
6714 );
6715
6716 let deserialized: ProviderCurrentConfig = serde_json::from_value(json).unwrap();
6717 assert_eq!(deserialized.api_type, LlmProtocol::Anthropic);
6718 assert_eq!(deserialized.base_url, "https://api.anthropic.com");
6719 }
6720
6721 #[cfg(feature = "unstable_llm_providers")]
6722 #[test]
6723 fn test_provider_info_with_current_config() {
6724 let info = ProviderInfo::new(
6725 "main",
6726 vec![LlmProtocol::Anthropic, LlmProtocol::OpenAi],
6727 true,
6728 Some(ProviderCurrentConfig::new(
6729 LlmProtocol::Anthropic,
6730 "https://api.anthropic.com",
6731 )),
6732 );
6733
6734 let json = serde_json::to_value(&info).unwrap();
6735 assert_eq!(
6736 json,
6737 json!({
6738 "providerId": "main",
6739 "supported": ["anthropic", "openai"],
6740 "required": true,
6741 "current": {
6742 "apiType": "anthropic",
6743 "baseUrl": "https://api.anthropic.com"
6744 }
6745 })
6746 );
6747
6748 let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
6749 assert_eq!(deserialized.provider_id.to_string(), "main");
6750 assert_eq!(deserialized.supported.len(), 2);
6751 assert!(deserialized.required);
6752 assert!(deserialized.current.is_some());
6753 assert_eq!(
6754 deserialized.current.as_ref().unwrap().api_type,
6755 LlmProtocol::Anthropic
6756 );
6757 }
6758
6759 #[cfg(feature = "unstable_llm_providers")]
6760 #[test]
6761 fn test_provider_info_disabled() {
6762 let info = ProviderInfo::new(
6763 "secondary",
6764 vec![LlmProtocol::OpenAi],
6765 false,
6766 None::<ProviderCurrentConfig>,
6767 );
6768
6769 let json = serde_json::to_value(&info).unwrap();
6770 assert_eq!(
6771 json,
6772 json!({
6773 "providerId": "secondary",
6774 "supported": ["openai"],
6775 "required": false
6776 })
6777 );
6778
6779 let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
6780 assert_eq!(deserialized.provider_id.to_string(), "secondary");
6781 assert!(!deserialized.required);
6782 assert!(deserialized.current.is_none());
6783 }
6784
6785 #[cfg(feature = "unstable_llm_providers")]
6786 #[test]
6787 fn test_provider_info_missing_current_defaults_to_none() {
6788 let json = json!({
6790 "providerId": "main",
6791 "supported": ["anthropic"],
6792 "required": true
6793 });
6794 let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
6795 assert!(deserialized.current.is_none());
6796 }
6797
6798 #[cfg(feature = "unstable_llm_providers")]
6799 #[test]
6800 fn test_provider_info_explicit_null_current_decodes_to_none() {
6801 let json = json!({
6805 "providerId": "main",
6806 "supported": ["anthropic"],
6807 "required": true,
6808 "current": null
6809 });
6810 let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
6811 assert!(deserialized.current.is_none());
6812 }
6813
6814 #[cfg(feature = "unstable_llm_providers")]
6815 #[test]
6816 fn test_list_providers_response_serialization() {
6817 let response = ListProvidersResponse::new(vec![ProviderInfo::new(
6818 "main",
6819 vec![LlmProtocol::Anthropic],
6820 true,
6821 Some(ProviderCurrentConfig::new(
6822 LlmProtocol::Anthropic,
6823 "https://api.anthropic.com",
6824 )),
6825 )]);
6826
6827 let json = serde_json::to_value(&response).unwrap();
6828 assert_eq!(json["providers"].as_array().unwrap().len(), 1);
6829 assert_eq!(json["providers"][0]["providerId"], "main");
6830
6831 let deserialized: ListProvidersResponse = serde_json::from_value(json).unwrap();
6832 assert_eq!(deserialized.providers.len(), 1);
6833 }
6834
6835 #[cfg(feature = "unstable_llm_providers")]
6836 #[test]
6837 fn test_set_provider_request_serialization() {
6838 use std::collections::HashMap;
6839
6840 let mut headers = HashMap::new();
6841 headers.insert("Authorization".to_string(), "Bearer sk-test".to_string());
6842
6843 let request =
6844 SetProviderRequest::new("main", LlmProtocol::OpenAi, "https://api.openai.com/v1")
6845 .headers(headers);
6846
6847 let json = serde_json::to_value(&request).unwrap();
6848 assert_eq!(
6849 json,
6850 json!({
6851 "providerId": "main",
6852 "apiType": "openai",
6853 "baseUrl": "https://api.openai.com/v1",
6854 "headers": {
6855 "Authorization": "Bearer sk-test"
6856 }
6857 })
6858 );
6859
6860 let deserialized: SetProviderRequest = serde_json::from_value(json).unwrap();
6861 assert_eq!(deserialized.provider_id.to_string(), "main");
6862 assert_eq!(deserialized.api_type, LlmProtocol::OpenAi);
6863 assert_eq!(deserialized.base_url, "https://api.openai.com/v1");
6864 assert_eq!(deserialized.headers.len(), 1);
6865 assert_eq!(
6866 deserialized.headers.get("Authorization").unwrap(),
6867 "Bearer sk-test"
6868 );
6869 }
6870
6871 #[cfg(feature = "unstable_llm_providers")]
6872 #[test]
6873 fn test_set_provider_request_omits_empty_headers() {
6874 let request =
6875 SetProviderRequest::new("main", LlmProtocol::Anthropic, "https://api.anthropic.com");
6876
6877 let json = serde_json::to_value(&request).unwrap();
6878 assert!(!json.as_object().unwrap().contains_key("headers"));
6880 }
6881
6882 #[cfg(feature = "unstable_llm_providers")]
6883 #[test]
6884 fn test_disable_provider_request_serialization() {
6885 let request = DisableProviderRequest::new("secondary");
6886
6887 let json = serde_json::to_value(&request).unwrap();
6888 assert_eq!(json, json!({ "providerId": "secondary" }));
6889
6890 let deserialized: DisableProviderRequest = serde_json::from_value(json).unwrap();
6891 assert_eq!(deserialized.provider_id.to_string(), "secondary");
6892 }
6893
6894 #[cfg(feature = "unstable_llm_providers")]
6895 #[test]
6896 fn test_providers_capabilities_serialization() {
6897 let caps = ProvidersCapabilities::new();
6898
6899 let json = serde_json::to_value(&caps).unwrap();
6900 assert_eq!(json, json!({}));
6901
6902 let deserialized: ProvidersCapabilities = serde_json::from_value(json).unwrap();
6903 assert!(deserialized.meta.is_none());
6904 }
6905
6906 #[cfg(feature = "unstable_llm_providers")]
6907 #[test]
6908 fn test_agent_capabilities_with_providers() {
6909 let caps = AgentCapabilities::new().providers(ProvidersCapabilities::new());
6910
6911 let json = serde_json::to_value(&caps).unwrap();
6912 assert_eq!(json["providers"], json!({}));
6913
6914 let deserialized: AgentCapabilities = serde_json::from_value(json).unwrap();
6915 assert!(deserialized.providers.is_some());
6916 }
6917
6918 #[test]
6919 fn test_agent_capabilities_session_is_explicit() {
6920 let json = serde_json::to_value(AgentCapabilities::new()).unwrap();
6921 assert!(json.get("session").is_none());
6922
6923 let caps = AgentCapabilities::new().session(
6924 SessionCapabilities::new()
6925 .prompt(PromptCapabilities::new().image(PromptImageCapabilities::new()))
6926 .mcp(McpCapabilities::new().stdio(McpStdioCapabilities::new())),
6927 );
6928
6929 assert_eq!(
6930 serde_json::to_value(&caps).unwrap(),
6931 json!({
6932 "session": {
6933 "prompt": {
6934 "image": {}
6935 },
6936 "mcp": {
6937 "stdio": {}
6938 }
6939 }
6940 })
6941 );
6942
6943 let deserialized: AgentCapabilities = serde_json::from_value(json!({
6944 "session": false
6945 }))
6946 .unwrap();
6947 assert!(deserialized.session.is_none());
6948 }
6949
6950 #[test]
6951 fn test_prompt_capabilities_serialize_supported_content_as_objects() {
6952 let caps = PromptCapabilities::new()
6953 .image(PromptImageCapabilities::new())
6954 .audio(PromptAudioCapabilities::new())
6955 .embedded_context(PromptEmbeddedContextCapabilities::new());
6956
6957 assert_eq!(
6958 serde_json::to_value(&caps).unwrap(),
6959 json!({
6960 "image": {},
6961 "audio": {},
6962 "embeddedContext": {}
6963 })
6964 );
6965
6966 let deserialized: PromptCapabilities = serde_json::from_value(json!({
6967 "image": null,
6968 "audio": false,
6969 "embeddedContext": {}
6970 }))
6971 .unwrap();
6972 assert!(deserialized.image.is_none());
6973 assert!(deserialized.audio.is_none());
6974 assert!(deserialized.embedded_context.is_some());
6975 }
6976
6977 #[test]
6978 fn test_mcp_capabilities_serialize_supported_transports_as_objects() {
6979 let caps = McpCapabilities::new()
6980 .stdio(McpStdioCapabilities::new())
6981 .http(McpHttpCapabilities::new());
6982
6983 assert_eq!(
6984 serde_json::to_value(&caps).unwrap(),
6985 json!({
6986 "stdio": {},
6987 "http": {}
6988 })
6989 );
6990
6991 let deserialized: McpCapabilities = serde_json::from_value(json!({
6992 "stdio": null,
6993 "http": false
6994 }))
6995 .unwrap();
6996 assert!(deserialized.stdio.is_none());
6997 assert!(deserialized.http.is_none());
6998 }
6999
7000 #[cfg(feature = "unstable_mcp_over_acp")]
7001 #[test]
7002 fn test_mcp_capabilities_serialize_acp_support_as_object() {
7003 let caps = McpCapabilities::new().acp(McpAcpCapabilities::new());
7004
7005 assert_eq!(
7006 serde_json::to_value(&caps).unwrap(),
7007 json!({
7008 "acp": {}
7009 })
7010 );
7011 }
7012
7013 #[test]
7014 fn prompt_request_rejects_malformed_content_block() {
7015 use serde_json::json;
7016
7017 assert!(
7018 serde_json::from_value::<PromptRequest>(json!({
7019 "sessionId": "sess-1",
7020 "prompt": [{"type": "text"}]
7021 }))
7022 .is_err()
7023 );
7024 }
7025
7026 #[test]
7027 fn prompt_request_rejects_non_array_prompt() {
7028 use serde_json::json;
7029
7030 assert!(
7031 serde_json::from_value::<PromptRequest>(json!({
7032 "sessionId": "sess-1",
7033 "prompt": "hello"
7034 }))
7035 .is_err()
7036 );
7037 }
7038}