1use std::{path::PathBuf, sync::Arc};
7
8use derive_more::{Display, From};
9use serde::{Deserialize, Serialize};
10use serde_with::{DefaultOnError, VecSkipError, serde_as, skip_serializing_none};
11
12use super::{
13 CompleteElicitationNotification, CreateElicitationRequest, CreateElicitationResponse,
14 ElicitationCapabilities,
15};
16use crate::{IntoMaybeUndefined, IntoOption, MaybeUndefined, SkipListener};
17
18use super::{
19 ContentBlock, EnvVariable, ExtNotification, ExtRequest, ExtResponse, Meta, Plan,
20 SessionConfigOption, SessionId, SessionModeId, ToolCall, ToolCallUpdate,
21};
22#[cfg(feature = "unstable_plan_operations")]
23use super::{PlanCapabilities, PlanRemoved, PlanUpdate};
24
25#[cfg(feature = "unstable_mcp_over_acp")]
26use super::mcp::{
27 ConnectMcpRequest, ConnectMcpResponse, DisconnectMcpRequest, DisconnectMcpResponse,
28 MCP_CONNECT_METHOD_NAME, MCP_DISCONNECT_METHOD_NAME, MCP_MESSAGE_METHOD_NAME,
29 MessageMcpNotification, MessageMcpRequest, MessageMcpResponse,
30};
31
32#[cfg(feature = "unstable_nes")]
33use super::{ClientNesCapabilities, PositionEncodingKind};
34
35#[serde_as]
43#[skip_serializing_none]
44#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
45#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
46#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = SESSION_UPDATE_NOTIFICATION)))]
47#[serde(rename_all = "camelCase")]
48#[non_exhaustive]
49pub struct SessionNotification {
50 pub session_id: SessionId,
52 pub update: SessionUpdate,
54 #[serde_as(deserialize_as = "DefaultOnError")]
60 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
61 #[serde(default)]
62 #[serde(rename = "_meta")]
63 pub meta: Option<Meta>,
64}
65
66impl SessionNotification {
67 #[must_use]
69 pub fn new(session_id: impl Into<SessionId>, update: SessionUpdate) -> Self {
70 Self {
71 session_id: session_id.into(),
72 update,
73 meta: None,
74 }
75 }
76
77 #[must_use]
83 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
84 self.meta = meta.into_option();
85 self
86 }
87}
88
89#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
95#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
96#[serde(tag = "sessionUpdate", rename_all = "snake_case")]
97#[cfg_attr(feature = "schemars", schemars(extend("discriminator" = {"propertyName": "sessionUpdate"})))]
98#[non_exhaustive]
99pub enum SessionUpdate {
100 UserMessageChunk(ContentChunk),
102 AgentMessageChunk(ContentChunk),
104 AgentThoughtChunk(ContentChunk),
106 ToolCall(ToolCall),
108 ToolCallUpdate(ToolCallUpdate),
110 Plan(Plan),
113 #[cfg(feature = "unstable_plan_operations")]
119 PlanUpdate(PlanUpdate),
120 #[cfg(feature = "unstable_plan_operations")]
126 PlanRemoved(PlanRemoved),
127 AvailableCommandsUpdate(AvailableCommandsUpdate),
129 CurrentModeUpdate(CurrentModeUpdate),
133 ConfigOptionUpdate(ConfigOptionUpdate),
135 SessionInfoUpdate(SessionInfoUpdate),
137 UsageUpdate(UsageUpdate),
139 #[cfg(feature = "unstable_session_notices")]
148 Notice(Notice),
149 #[cfg(feature = "unstable_session_compaction")]
158 CompactionUpdate(CompactionUpdate),
159 #[cfg(feature = "unstable_session_compaction")]
168 CompactionSummaryChunk(CompactionSummaryChunk),
169}
170
171#[cfg(feature = "unstable_session_notices")]
177#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
178#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
179#[serde(rename_all = "snake_case")]
180#[non_exhaustive]
181pub enum NoticeSeverity {
182 Info,
184 Warning,
186 Error,
188 #[serde(untagged)]
193 Other(String),
194}
195
196#[cfg(feature = "unstable_session_notices")]
210#[serde_as]
211#[skip_serializing_none]
212#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
213#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
214#[serde(rename_all = "camelCase")]
215#[non_exhaustive]
216pub struct Notice {
217 pub severity: NoticeSeverity,
219 #[cfg_attr(feature = "schemars", schemars(length(min = 1)))]
221 pub title: String,
222 #[serde_as(deserialize_as = "DefaultOnError")]
226 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
227 #[serde(default)]
228 pub description: Option<String>,
229 #[serde_as(deserialize_as = "DefaultOnError")]
233 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
234 #[serde(default, rename = "_meta")]
235 pub meta: Option<Meta>,
236}
237
238#[cfg(feature = "unstable_session_notices")]
239impl Notice {
240 #[must_use]
242 pub fn new(severity: NoticeSeverity, title: impl Into<String>) -> Self {
243 Self {
244 severity,
245 title: title.into(),
246 description: None,
247 meta: None,
248 }
249 }
250
251 #[must_use]
253 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
254 self.description = description.into_option();
255 self
256 }
257
258 #[must_use]
260 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
261 self.meta = meta.into_option();
262 self
263 }
264}
265
266#[cfg(feature = "unstable_session_compaction")]
272#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
273#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
274#[serde(transparent)]
275#[from(Arc<str>, String, &'static str)]
276#[non_exhaustive]
277pub struct CompactionId(pub Arc<str>);
278
279#[cfg(feature = "unstable_session_compaction")]
280impl CompactionId {
281 #[must_use]
283 pub fn new(id: impl Into<Arc<str>>) -> Self {
284 Self(id.into())
285 }
286}
287
288#[cfg(feature = "unstable_session_compaction")]
294#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
295#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
296#[serde(rename_all = "snake_case")]
297#[non_exhaustive]
298pub enum CompactionStatus {
299 InProgress,
301 Completed,
303 Failed,
305 Cancelled,
307 #[serde(untagged)]
312 Other(String),
313}
314
315#[cfg(feature = "unstable_session_compaction")]
329#[serde_as]
330#[skip_serializing_none]
331#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
332#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
333#[serde(rename_all = "camelCase")]
334#[non_exhaustive]
335pub struct CompactionUpdate {
336 pub compaction_id: CompactionId,
338 pub status: CompactionStatus,
340 #[serde_as(deserialize_as = "DefaultOnError<MaybeUndefined<VecSkipError<_, SkipListener>>>")]
342 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
343 #[serde(default, skip_serializing_if = "MaybeUndefined::is_undefined")]
344 pub summary: MaybeUndefined<Vec<ContentBlock>>,
345 #[serde_as(deserialize_as = "DefaultOnError<MaybeUndefined<_>>")]
347 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
348 #[serde(default, skip_serializing_if = "MaybeUndefined::is_undefined")]
349 pub error: MaybeUndefined<String>,
350 #[serde_as(deserialize_as = "DefaultOnError<MaybeUndefined<_>>")]
352 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
353 #[serde(
354 rename = "_meta",
355 default,
356 skip_serializing_if = "MaybeUndefined::is_undefined"
357 )]
358 pub meta: MaybeUndefined<Meta>,
359}
360
361#[cfg(feature = "unstable_session_compaction")]
362impl CompactionUpdate {
363 #[must_use]
365 pub fn new(compaction_id: impl Into<CompactionId>, status: CompactionStatus) -> Self {
366 Self {
367 compaction_id: compaction_id.into(),
368 status,
369 summary: MaybeUndefined::Undefined,
370 error: MaybeUndefined::Undefined,
371 meta: MaybeUndefined::Undefined,
372 }
373 }
374
375 #[must_use]
377 pub fn summary(mut self, summary: impl IntoMaybeUndefined<Vec<ContentBlock>>) -> Self {
378 self.summary = summary.into_maybe_undefined();
379 self
380 }
381
382 #[must_use]
384 pub fn error(mut self, error: impl IntoMaybeUndefined<String>) -> Self {
385 self.error = error.into_maybe_undefined();
386 self
387 }
388
389 #[must_use]
391 pub fn meta(mut self, meta: impl IntoMaybeUndefined<Meta>) -> Self {
392 self.meta = meta.into_maybe_undefined();
393 self
394 }
395}
396
397#[cfg(feature = "unstable_session_compaction")]
406#[serde_as]
407#[skip_serializing_none]
408#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
409#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
410#[serde(rename_all = "camelCase")]
411#[non_exhaustive]
412pub struct CompactionSummaryChunk {
413 pub compaction_id: CompactionId,
415 pub content: ContentBlock,
417 #[serde_as(deserialize_as = "DefaultOnError")]
419 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
420 #[serde(default, rename = "_meta")]
421 pub meta: Option<Meta>,
422}
423
424#[cfg(feature = "unstable_session_compaction")]
425impl CompactionSummaryChunk {
426 #[must_use]
428 pub fn new(compaction_id: impl Into<CompactionId>, content: ContentBlock) -> Self {
429 Self {
430 compaction_id: compaction_id.into(),
431 content,
432 meta: None,
433 }
434 }
435
436 #[must_use]
438 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
439 self.meta = meta.into_option();
440 self
441 }
442}
443
444#[serde_as]
448#[skip_serializing_none]
449#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
450#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
451#[serde(rename_all = "camelCase")]
452#[non_exhaustive]
453pub struct CurrentModeUpdate {
454 pub current_mode_id: SessionModeId,
456 #[serde_as(deserialize_as = "DefaultOnError")]
462 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
463 #[serde(default)]
464 #[serde(rename = "_meta")]
465 pub meta: Option<Meta>,
466}
467
468impl CurrentModeUpdate {
469 #[must_use]
471 pub fn new(current_mode_id: impl Into<SessionModeId>) -> Self {
472 Self {
473 current_mode_id: current_mode_id.into(),
474 meta: None,
475 }
476 }
477
478 #[must_use]
484 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
485 self.meta = meta.into_option();
486 self
487 }
488}
489
490#[serde_as]
492#[skip_serializing_none]
493#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
494#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
495#[serde(rename_all = "camelCase")]
496#[non_exhaustive]
497pub struct ConfigOptionUpdate {
498 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
500 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
501 pub config_options: Vec<SessionConfigOption>,
502 #[serde_as(deserialize_as = "DefaultOnError")]
508 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
509 #[serde(default)]
510 #[serde(rename = "_meta")]
511 pub meta: Option<Meta>,
512}
513
514impl ConfigOptionUpdate {
515 #[must_use]
517 pub fn new(config_options: Vec<SessionConfigOption>) -> Self {
518 Self {
519 config_options,
520 meta: None,
521 }
522 }
523
524 #[must_use]
530 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
531 self.meta = meta.into_option();
532 self
533 }
534}
535
536#[serde_as]
541#[skip_serializing_none]
542#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
543#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
544#[serde(rename_all = "camelCase")]
545#[non_exhaustive]
546pub struct SessionInfoUpdate {
547 #[serde_as(deserialize_as = "DefaultOnError")]
549 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
550 #[serde(default, skip_serializing_if = "MaybeUndefined::is_undefined")]
551 pub title: MaybeUndefined<String>,
552 #[serde_as(deserialize_as = "DefaultOnError")]
554 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
555 #[serde(default, skip_serializing_if = "MaybeUndefined::is_undefined")]
556 pub updated_at: MaybeUndefined<String>,
557 #[serde_as(deserialize_as = "DefaultOnError")]
563 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
564 #[serde(default)]
565 #[serde(rename = "_meta")]
566 pub meta: Option<Meta>,
567}
568
569impl SessionInfoUpdate {
570 #[must_use]
572 pub fn new() -> Self {
573 Self::default()
574 }
575
576 #[must_use]
578 pub fn title(mut self, title: impl IntoMaybeUndefined<String>) -> Self {
579 self.title = title.into_maybe_undefined();
580 self
581 }
582
583 #[must_use]
585 pub fn updated_at(mut self, updated_at: impl IntoMaybeUndefined<String>) -> Self {
586 self.updated_at = updated_at.into_maybe_undefined();
587 self
588 }
589
590 #[must_use]
596 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
597 self.meta = meta.into_option();
598 self
599 }
600}
601
602#[serde_as]
604#[skip_serializing_none]
605#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
606#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
607#[serde(rename_all = "camelCase")]
608#[non_exhaustive]
609pub struct UsageUpdate {
610 pub used: u64,
612 pub size: u64,
614 #[serde_as(deserialize_as = "DefaultOnError")]
616 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
617 #[serde(default)]
618 pub cost: Option<Cost>,
619 #[serde_as(deserialize_as = "DefaultOnError")]
625 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
626 #[serde(default)]
627 #[serde(rename = "_meta")]
628 pub meta: Option<Meta>,
629}
630
631impl UsageUpdate {
632 #[must_use]
634 pub fn new(used: u64, size: u64) -> Self {
635 Self {
636 used,
637 size,
638 cost: None,
639 meta: None,
640 }
641 }
642
643 #[must_use]
645 pub fn cost(mut self, cost: impl IntoOption<Cost>) -> Self {
646 self.cost = cost.into_option();
647 self
648 }
649
650 #[must_use]
656 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
657 self.meta = meta.into_option();
658 self
659 }
660}
661
662#[serde_as]
664#[skip_serializing_none]
665#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
666#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
667#[serde(rename_all = "camelCase")]
668#[non_exhaustive]
669pub struct Cost {
670 pub amount: f64,
672 pub currency: String,
674 #[serde_as(deserialize_as = "DefaultOnError")]
680 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
681 #[serde(default)]
682 #[serde(rename = "_meta")]
683 pub meta: Option<Meta>,
684}
685
686impl Cost {
687 #[must_use]
689 pub fn new(amount: f64, currency: impl Into<String>) -> Self {
690 Self {
691 amount,
692 currency: currency.into(),
693 meta: None,
694 }
695 }
696
697 #[must_use]
703 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
704 self.meta = meta.into_option();
705 self
706 }
707}
708
709#[serde_as]
711#[skip_serializing_none]
712#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
713#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
714#[serde(rename_all = "camelCase")]
715#[non_exhaustive]
716pub struct ContentChunk {
717 pub content: ContentBlock,
719 #[serde_as(deserialize_as = "DefaultOnError")]
724 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
725 #[serde(default)]
726 pub message_id: Option<MessageId>,
727 #[serde_as(deserialize_as = "DefaultOnError")]
733 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
734 #[serde(default)]
735 #[serde(rename = "_meta")]
736 pub meta: Option<Meta>,
737}
738
739impl ContentChunk {
740 #[must_use]
742 pub fn new(content: ContentBlock) -> Self {
743 Self {
744 content,
745 message_id: None,
746 meta: None,
747 }
748 }
749
750 #[must_use]
755 pub fn message_id(mut self, message_id: impl IntoOption<MessageId>) -> Self {
756 self.message_id = message_id.into_option();
757 self
758 }
759
760 #[must_use]
766 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
767 self.meta = meta.into_option();
768 self
769 }
770}
771
772#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
774#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
775#[serde(transparent)]
776#[from(Arc<str>, String, &'static str)]
777#[non_exhaustive]
778pub struct MessageId(pub Arc<str>);
779
780impl MessageId {
781 #[must_use]
783 pub fn new(id: impl Into<Arc<str>>) -> Self {
784 Self(id.into())
785 }
786}
787
788impl IntoOption<MessageId> for &str {
789 fn into_option(self) -> Option<MessageId> {
790 Some(MessageId::new(self))
791 }
792}
793
794#[serde_as]
796#[skip_serializing_none]
797#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
798#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
799#[serde(rename_all = "camelCase")]
800#[non_exhaustive]
801pub struct AvailableCommandsUpdate {
802 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
804 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
805 pub available_commands: Vec<AvailableCommand>,
806 #[serde_as(deserialize_as = "DefaultOnError")]
812 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
813 #[serde(default)]
814 #[serde(rename = "_meta")]
815 pub meta: Option<Meta>,
816}
817
818impl AvailableCommandsUpdate {
819 #[must_use]
821 pub fn new(available_commands: Vec<AvailableCommand>) -> Self {
822 Self {
823 available_commands,
824 meta: None,
825 }
826 }
827
828 #[must_use]
834 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
835 self.meta = meta.into_option();
836 self
837 }
838}
839
840#[serde_as]
842#[skip_serializing_none]
843#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
844#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
845#[serde(rename_all = "camelCase")]
846#[non_exhaustive]
847pub struct AvailableCommand {
848 pub name: String,
850 pub description: String,
852 #[serde_as(deserialize_as = "DefaultOnError")]
854 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
855 #[serde(default)]
856 pub input: Option<AvailableCommandInput>,
857 #[serde_as(deserialize_as = "DefaultOnError")]
863 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
864 #[serde(default)]
865 #[serde(rename = "_meta")]
866 pub meta: Option<Meta>,
867}
868
869impl AvailableCommand {
870 #[must_use]
872 pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
873 Self {
874 name: name.into(),
875 description: description.into(),
876 input: None,
877 meta: None,
878 }
879 }
880
881 #[must_use]
883 pub fn input(mut self, input: impl IntoOption<AvailableCommandInput>) -> Self {
884 self.input = input.into_option();
885 self
886 }
887
888 #[must_use]
894 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
895 self.meta = meta.into_option();
896 self
897 }
898}
899
900#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
902#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
903#[serde(untagged, rename_all = "camelCase")]
904#[non_exhaustive]
905pub enum AvailableCommandInput {
906 Unstructured(UnstructuredCommandInput),
908}
909
910#[serde_as]
912#[skip_serializing_none]
913#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
914#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
915#[serde(rename_all = "camelCase")]
916#[non_exhaustive]
917pub struct UnstructuredCommandInput {
918 pub hint: String,
920 #[serde_as(deserialize_as = "DefaultOnError")]
926 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
927 #[serde(default)]
928 #[serde(rename = "_meta")]
929 pub meta: Option<Meta>,
930}
931
932impl UnstructuredCommandInput {
933 #[must_use]
935 pub fn new(hint: impl Into<String>) -> Self {
936 Self {
937 hint: hint.into(),
938 meta: None,
939 }
940 }
941
942 #[must_use]
948 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
949 self.meta = meta.into_option();
950 self
951 }
952}
953
954#[serde_as]
962#[skip_serializing_none]
963#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
964#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
965#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = SESSION_REQUEST_PERMISSION_METHOD_NAME)))]
966#[serde(rename_all = "camelCase")]
967#[non_exhaustive]
968pub struct RequestPermissionRequest {
969 pub session_id: SessionId,
971 pub tool_call: ToolCallUpdate,
973 pub options: Vec<PermissionOption>,
975 #[serde_as(deserialize_as = "DefaultOnError")]
981 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
982 #[serde(default)]
983 #[serde(rename = "_meta")]
984 pub meta: Option<Meta>,
985}
986
987impl RequestPermissionRequest {
988 #[must_use]
990 pub fn new(
991 session_id: impl Into<SessionId>,
992 tool_call: ToolCallUpdate,
993 options: Vec<PermissionOption>,
994 ) -> Self {
995 Self {
996 session_id: session_id.into(),
997 tool_call,
998 options,
999 meta: None,
1000 }
1001 }
1002
1003 #[must_use]
1009 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1010 self.meta = meta.into_option();
1011 self
1012 }
1013}
1014
1015#[serde_as]
1017#[skip_serializing_none]
1018#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1019#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1020#[serde(rename_all = "camelCase")]
1021#[non_exhaustive]
1022pub struct PermissionOption {
1023 pub option_id: PermissionOptionId,
1025 pub name: String,
1027 pub kind: PermissionOptionKind,
1029 #[serde_as(deserialize_as = "DefaultOnError")]
1035 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1036 #[serde(default)]
1037 #[serde(rename = "_meta")]
1038 pub meta: Option<Meta>,
1039}
1040
1041impl PermissionOption {
1042 #[must_use]
1044 pub fn new(
1045 option_id: impl Into<PermissionOptionId>,
1046 name: impl Into<String>,
1047 kind: PermissionOptionKind,
1048 ) -> Self {
1049 Self {
1050 option_id: option_id.into(),
1051 name: name.into(),
1052 kind,
1053 meta: None,
1054 }
1055 }
1056
1057 #[must_use]
1063 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1064 self.meta = meta.into_option();
1065 self
1066 }
1067}
1068
1069#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1071#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
1072#[serde(transparent)]
1073#[from(Arc<str>, String, &'static str)]
1074#[non_exhaustive]
1075pub struct PermissionOptionId(pub Arc<str>);
1076
1077impl PermissionOptionId {
1078 #[must_use]
1080 pub fn new(id: impl Into<Arc<str>>) -> Self {
1081 Self(id.into())
1082 }
1083}
1084
1085#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1089#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
1090#[serde(rename_all = "snake_case")]
1091#[non_exhaustive]
1092pub enum PermissionOptionKind {
1093 AllowOnce,
1095 AllowAlways,
1097 RejectOnce,
1099 RejectAlways,
1101}
1102
1103#[serde_as]
1105#[skip_serializing_none]
1106#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1107#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1108#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = SESSION_REQUEST_PERMISSION_METHOD_NAME)))]
1109#[serde(rename_all = "camelCase")]
1110#[non_exhaustive]
1111pub struct RequestPermissionResponse {
1112 pub outcome: RequestPermissionOutcome,
1115 #[serde_as(deserialize_as = "DefaultOnError")]
1121 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1122 #[serde(default)]
1123 #[serde(rename = "_meta")]
1124 pub meta: Option<Meta>,
1125}
1126
1127impl RequestPermissionResponse {
1128 #[must_use]
1130 pub fn new(outcome: RequestPermissionOutcome) -> Self {
1131 Self {
1132 outcome,
1133 meta: None,
1134 }
1135 }
1136
1137 #[must_use]
1143 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1144 self.meta = meta.into_option();
1145 self
1146 }
1147}
1148
1149#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1151#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1152#[serde(tag = "outcome", rename_all = "snake_case")]
1153#[cfg_attr(feature = "schemars", schemars(extend("discriminator" = {"propertyName": "outcome"})))]
1154#[non_exhaustive]
1155pub enum RequestPermissionOutcome {
1156 Cancelled,
1164 #[serde(rename_all = "camelCase")]
1166 Selected(SelectedPermissionOutcome),
1167}
1168
1169#[serde_as]
1171#[skip_serializing_none]
1172#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1173#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1174#[serde(rename_all = "camelCase")]
1175#[non_exhaustive]
1176pub struct SelectedPermissionOutcome {
1177 pub option_id: PermissionOptionId,
1179 #[serde_as(deserialize_as = "DefaultOnError")]
1185 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1186 #[serde(default)]
1187 #[serde(rename = "_meta")]
1188 pub meta: Option<Meta>,
1189}
1190
1191impl SelectedPermissionOutcome {
1192 #[must_use]
1194 pub fn new(option_id: impl Into<PermissionOptionId>) -> Self {
1195 Self {
1196 option_id: option_id.into(),
1197 meta: None,
1198 }
1199 }
1200
1201 #[must_use]
1207 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1208 self.meta = meta.into_option();
1209 self
1210 }
1211}
1212
1213#[serde_as]
1219#[skip_serializing_none]
1220#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1221#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1222#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = FS_WRITE_TEXT_FILE_METHOD_NAME)))]
1223#[serde(rename_all = "camelCase")]
1224#[non_exhaustive]
1225pub struct WriteTextFileRequest {
1226 pub session_id: SessionId,
1228 pub path: PathBuf,
1230 pub content: String,
1232 #[serde_as(deserialize_as = "DefaultOnError")]
1238 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1239 #[serde(default)]
1240 #[serde(rename = "_meta")]
1241 pub meta: Option<Meta>,
1242}
1243
1244impl WriteTextFileRequest {
1245 #[must_use]
1247 pub fn new(
1248 session_id: impl Into<SessionId>,
1249 path: impl Into<PathBuf>,
1250 content: impl Into<String>,
1251 ) -> Self {
1252 Self {
1253 session_id: session_id.into(),
1254 path: path.into(),
1255 content: content.into(),
1256 meta: None,
1257 }
1258 }
1259
1260 #[must_use]
1266 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1267 self.meta = meta.into_option();
1268 self
1269 }
1270}
1271
1272crate::serde_util::default_on_null! {
1273 #[serde_as]
1275 #[skip_serializing_none]
1276 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1277 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
1278 #[serde(rename_all = "camelCase")]
1279 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = FS_WRITE_TEXT_FILE_METHOD_NAME)))]
1280 #[non_exhaustive]
1281 pub struct WriteTextFileResponse {
1282 #[serde_as(deserialize_as = "DefaultOnError")]
1288 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1289 #[serde(default)]
1290 #[serde(rename = "_meta")]
1291 pub meta: Option<Meta>,
1292 }
1293}
1294
1295impl WriteTextFileResponse {
1296 #[must_use]
1298 pub fn new() -> Self {
1299 Self::default()
1300 }
1301
1302 #[must_use]
1308 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1309 self.meta = meta.into_option();
1310 self
1311 }
1312}
1313
1314#[serde_as]
1320#[skip_serializing_none]
1321#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1322#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1323#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = FS_READ_TEXT_FILE_METHOD_NAME)))]
1324#[serde(rename_all = "camelCase")]
1325#[non_exhaustive]
1326pub struct ReadTextFileRequest {
1327 pub session_id: SessionId,
1329 pub path: PathBuf,
1331 #[serde_as(deserialize_as = "DefaultOnError")]
1333 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1334 #[serde(default)]
1335 pub line: Option<u32>,
1336 #[serde_as(deserialize_as = "DefaultOnError")]
1338 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1339 #[serde(default)]
1340 pub limit: Option<u32>,
1341 #[serde_as(deserialize_as = "DefaultOnError")]
1347 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1348 #[serde(default)]
1349 #[serde(rename = "_meta")]
1350 pub meta: Option<Meta>,
1351}
1352
1353impl ReadTextFileRequest {
1354 #[must_use]
1356 pub fn new(session_id: impl Into<SessionId>, path: impl Into<PathBuf>) -> Self {
1357 Self {
1358 session_id: session_id.into(),
1359 path: path.into(),
1360 line: None,
1361 limit: None,
1362 meta: None,
1363 }
1364 }
1365
1366 #[must_use]
1368 pub fn line(mut self, line: impl IntoOption<u32>) -> Self {
1369 self.line = line.into_option();
1370 self
1371 }
1372
1373 #[must_use]
1375 pub fn limit(mut self, limit: impl IntoOption<u32>) -> Self {
1376 self.limit = limit.into_option();
1377 self
1378 }
1379
1380 #[must_use]
1386 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1387 self.meta = meta.into_option();
1388 self
1389 }
1390}
1391
1392#[serde_as]
1394#[skip_serializing_none]
1395#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1396#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1397#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = FS_READ_TEXT_FILE_METHOD_NAME)))]
1398#[serde(rename_all = "camelCase")]
1399#[non_exhaustive]
1400pub struct ReadTextFileResponse {
1401 pub content: String,
1403 #[serde_as(deserialize_as = "DefaultOnError")]
1409 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1410 #[serde(default)]
1411 #[serde(rename = "_meta")]
1412 pub meta: Option<Meta>,
1413}
1414
1415impl ReadTextFileResponse {
1416 #[must_use]
1418 pub fn new(content: impl Into<String>) -> Self {
1419 Self {
1420 content: content.into(),
1421 meta: None,
1422 }
1423 }
1424
1425 #[must_use]
1431 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1432 self.meta = meta.into_option();
1433 self
1434 }
1435}
1436
1437#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1441#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
1442#[serde(transparent)]
1443#[from(Arc<str>, String, &'static str)]
1444#[non_exhaustive]
1445pub struct TerminalId(pub Arc<str>);
1446
1447impl TerminalId {
1448 #[must_use]
1450 pub fn new(id: impl Into<Arc<str>>) -> Self {
1451 Self(id.into())
1452 }
1453}
1454
1455#[serde_as]
1457#[skip_serializing_none]
1458#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1459#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1460#[serde(rename_all = "camelCase")]
1461#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_CREATE_METHOD_NAME)))]
1462#[non_exhaustive]
1463pub struct CreateTerminalRequest {
1464 pub session_id: SessionId,
1466 pub command: String,
1468 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1470 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1471 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1472 pub args: Vec<String>,
1473 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1475 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1476 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1477 pub env: Vec<EnvVariable>,
1478 #[serde_as(deserialize_as = "DefaultOnError")]
1480 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1481 #[serde(default)]
1482 pub cwd: Option<PathBuf>,
1483 #[serde_as(deserialize_as = "DefaultOnError")]
1492 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1493 #[serde(default)]
1494 pub output_byte_limit: Option<u64>,
1495 #[serde_as(deserialize_as = "DefaultOnError")]
1501 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1502 #[serde(default)]
1503 #[serde(rename = "_meta")]
1504 pub meta: Option<Meta>,
1505}
1506
1507impl CreateTerminalRequest {
1508 #[must_use]
1510 pub fn new(session_id: impl Into<SessionId>, command: impl Into<String>) -> Self {
1511 Self {
1512 session_id: session_id.into(),
1513 command: command.into(),
1514 args: Vec::new(),
1515 env: Vec::new(),
1516 cwd: None,
1517 output_byte_limit: None,
1518 meta: None,
1519 }
1520 }
1521
1522 #[must_use]
1524 pub fn args(mut self, args: Vec<String>) -> Self {
1525 self.args = args;
1526 self
1527 }
1528
1529 #[must_use]
1531 pub fn env(mut self, env: Vec<EnvVariable>) -> Self {
1532 self.env = env;
1533 self
1534 }
1535
1536 #[must_use]
1538 pub fn cwd(mut self, cwd: impl IntoOption<PathBuf>) -> Self {
1539 self.cwd = cwd.into_option();
1540 self
1541 }
1542
1543 #[must_use]
1552 pub fn output_byte_limit(mut self, output_byte_limit: impl IntoOption<u64>) -> Self {
1553 self.output_byte_limit = output_byte_limit.into_option();
1554 self
1555 }
1556
1557 #[must_use]
1563 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1564 self.meta = meta.into_option();
1565 self
1566 }
1567}
1568
1569#[serde_as]
1571#[skip_serializing_none]
1572#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1573#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1574#[serde(rename_all = "camelCase")]
1575#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_CREATE_METHOD_NAME)))]
1576#[non_exhaustive]
1577pub struct CreateTerminalResponse {
1578 pub terminal_id: TerminalId,
1580 #[serde_as(deserialize_as = "DefaultOnError")]
1586 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1587 #[serde(default)]
1588 #[serde(rename = "_meta")]
1589 pub meta: Option<Meta>,
1590}
1591
1592impl CreateTerminalResponse {
1593 #[must_use]
1595 pub fn new(terminal_id: impl Into<TerminalId>) -> Self {
1596 Self {
1597 terminal_id: terminal_id.into(),
1598 meta: None,
1599 }
1600 }
1601
1602 #[must_use]
1608 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1609 self.meta = meta.into_option();
1610 self
1611 }
1612}
1613
1614#[serde_as]
1616#[skip_serializing_none]
1617#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1618#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1619#[serde(rename_all = "camelCase")]
1620#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_OUTPUT_METHOD_NAME)))]
1621#[non_exhaustive]
1622pub struct TerminalOutputRequest {
1623 pub session_id: SessionId,
1625 pub terminal_id: TerminalId,
1627 #[serde_as(deserialize_as = "DefaultOnError")]
1633 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1634 #[serde(default)]
1635 #[serde(rename = "_meta")]
1636 pub meta: Option<Meta>,
1637}
1638
1639impl TerminalOutputRequest {
1640 #[must_use]
1642 pub fn new(session_id: impl Into<SessionId>, terminal_id: impl Into<TerminalId>) -> Self {
1643 Self {
1644 session_id: session_id.into(),
1645 terminal_id: terminal_id.into(),
1646 meta: None,
1647 }
1648 }
1649
1650 #[must_use]
1656 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1657 self.meta = meta.into_option();
1658 self
1659 }
1660}
1661
1662#[serde_as]
1664#[skip_serializing_none]
1665#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1666#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1667#[serde(rename_all = "camelCase")]
1668#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_OUTPUT_METHOD_NAME)))]
1669#[non_exhaustive]
1670pub struct TerminalOutputResponse {
1671 pub output: String,
1673 pub truncated: bool,
1675 #[serde_as(deserialize_as = "DefaultOnError")]
1677 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1678 #[serde(default)]
1679 pub exit_status: Option<TerminalExitStatus>,
1680 #[serde_as(deserialize_as = "DefaultOnError")]
1686 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1687 #[serde(default)]
1688 #[serde(rename = "_meta")]
1689 pub meta: Option<Meta>,
1690}
1691
1692impl TerminalOutputResponse {
1693 #[must_use]
1695 pub fn new(output: impl Into<String>, truncated: bool) -> Self {
1696 Self {
1697 output: output.into(),
1698 truncated,
1699 exit_status: None,
1700 meta: None,
1701 }
1702 }
1703
1704 #[must_use]
1706 pub fn exit_status(mut self, exit_status: impl IntoOption<TerminalExitStatus>) -> Self {
1707 self.exit_status = exit_status.into_option();
1708 self
1709 }
1710
1711 #[must_use]
1717 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1718 self.meta = meta.into_option();
1719 self
1720 }
1721}
1722
1723#[serde_as]
1725#[skip_serializing_none]
1726#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1727#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1728#[serde(rename_all = "camelCase")]
1729#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_RELEASE_METHOD_NAME)))]
1730#[non_exhaustive]
1731pub struct ReleaseTerminalRequest {
1732 pub session_id: SessionId,
1734 pub terminal_id: TerminalId,
1736 #[serde_as(deserialize_as = "DefaultOnError")]
1742 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1743 #[serde(default)]
1744 #[serde(rename = "_meta")]
1745 pub meta: Option<Meta>,
1746}
1747
1748impl ReleaseTerminalRequest {
1749 #[must_use]
1751 pub fn new(session_id: impl Into<SessionId>, terminal_id: impl Into<TerminalId>) -> Self {
1752 Self {
1753 session_id: session_id.into(),
1754 terminal_id: terminal_id.into(),
1755 meta: None,
1756 }
1757 }
1758
1759 #[must_use]
1765 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1766 self.meta = meta.into_option();
1767 self
1768 }
1769}
1770
1771crate::serde_util::default_on_null! {
1772 #[serde_as]
1774 #[skip_serializing_none]
1775 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1776 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
1777 #[serde(rename_all = "camelCase")]
1778 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_RELEASE_METHOD_NAME)))]
1779 #[non_exhaustive]
1780 pub struct ReleaseTerminalResponse {
1781 #[serde_as(deserialize_as = "DefaultOnError")]
1787 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1788 #[serde(default)]
1789 #[serde(rename = "_meta")]
1790 pub meta: Option<Meta>,
1791 }
1792}
1793
1794impl ReleaseTerminalResponse {
1795 #[must_use]
1797 pub fn new() -> Self {
1798 Self::default()
1799 }
1800
1801 #[must_use]
1807 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1808 self.meta = meta.into_option();
1809 self
1810 }
1811}
1812
1813#[serde_as]
1815#[skip_serializing_none]
1816#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1817#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1818#[serde(rename_all = "camelCase")]
1819#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_KILL_METHOD_NAME)))]
1820#[non_exhaustive]
1821pub struct KillTerminalRequest {
1822 pub session_id: SessionId,
1824 pub terminal_id: TerminalId,
1826 #[serde_as(deserialize_as = "DefaultOnError")]
1832 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1833 #[serde(default)]
1834 #[serde(rename = "_meta")]
1835 pub meta: Option<Meta>,
1836}
1837
1838impl KillTerminalRequest {
1839 #[must_use]
1841 pub fn new(session_id: impl Into<SessionId>, terminal_id: impl Into<TerminalId>) -> Self {
1842 Self {
1843 session_id: session_id.into(),
1844 terminal_id: terminal_id.into(),
1845 meta: None,
1846 }
1847 }
1848
1849 #[must_use]
1855 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1856 self.meta = meta.into_option();
1857 self
1858 }
1859}
1860
1861crate::serde_util::default_on_null! {
1862 #[serde_as]
1864 #[skip_serializing_none]
1865 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1866 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
1867 #[serde(rename_all = "camelCase")]
1868 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_KILL_METHOD_NAME)))]
1869 #[non_exhaustive]
1870 pub struct KillTerminalResponse {
1871 #[serde_as(deserialize_as = "DefaultOnError")]
1877 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1878 #[serde(default)]
1879 #[serde(rename = "_meta")]
1880 pub meta: Option<Meta>,
1881 }
1882}
1883
1884impl KillTerminalResponse {
1885 #[must_use]
1887 pub fn new() -> Self {
1888 Self::default()
1889 }
1890
1891 #[must_use]
1897 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1898 self.meta = meta.into_option();
1899 self
1900 }
1901}
1902
1903#[serde_as]
1905#[skip_serializing_none]
1906#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1907#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1908#[serde(rename_all = "camelCase")]
1909#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_WAIT_FOR_EXIT_METHOD_NAME)))]
1910#[non_exhaustive]
1911pub struct WaitForTerminalExitRequest {
1912 pub session_id: SessionId,
1914 pub terminal_id: TerminalId,
1916 #[serde_as(deserialize_as = "DefaultOnError")]
1922 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1923 #[serde(default)]
1924 #[serde(rename = "_meta")]
1925 pub meta: Option<Meta>,
1926}
1927
1928impl WaitForTerminalExitRequest {
1929 #[must_use]
1931 pub fn new(session_id: impl Into<SessionId>, terminal_id: impl Into<TerminalId>) -> Self {
1932 Self {
1933 session_id: session_id.into(),
1934 terminal_id: terminal_id.into(),
1935 meta: None,
1936 }
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
1951crate::serde_util::default_on_null! {
1952 #[serde_as]
1954 #[skip_serializing_none]
1955 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1956 #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
1957 #[serde(rename_all = "camelCase")]
1958 #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_WAIT_FOR_EXIT_METHOD_NAME)))]
1959 #[non_exhaustive]
1960 pub struct WaitForTerminalExitResponse {
1961 #[serde(flatten)]
1963 pub exit_status: TerminalExitStatus,
1964 #[serde_as(deserialize_as = "DefaultOnError")]
1970 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1971 #[serde(default)]
1972 #[serde(rename = "_meta")]
1973 pub meta: Option<Meta>,
1974 }
1975}
1976
1977impl WaitForTerminalExitResponse {
1978 #[must_use]
1980 pub fn new(exit_status: TerminalExitStatus) -> Self {
1981 Self {
1982 exit_status,
1983 meta: None,
1984 }
1985 }
1986
1987 #[must_use]
1993 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1994 self.meta = meta.into_option();
1995 self
1996 }
1997}
1998
1999#[serde_as]
2001#[skip_serializing_none]
2002#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2003#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2004#[serde(rename_all = "camelCase")]
2005#[non_exhaustive]
2006pub struct TerminalExitStatus {
2007 #[serde_as(deserialize_as = "DefaultOnError")]
2009 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2010 #[serde(default)]
2011 pub exit_code: Option<u32>,
2012 #[serde_as(deserialize_as = "DefaultOnError")]
2014 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2015 #[serde(default)]
2016 pub signal: Option<String>,
2017 #[serde_as(deserialize_as = "DefaultOnError")]
2023 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2024 #[serde(default)]
2025 #[serde(rename = "_meta")]
2026 pub meta: Option<Meta>,
2027}
2028
2029impl TerminalExitStatus {
2030 #[must_use]
2032 pub fn new() -> Self {
2033 Self::default()
2034 }
2035
2036 #[must_use]
2038 pub fn exit_code(mut self, exit_code: impl IntoOption<u32>) -> Self {
2039 self.exit_code = exit_code.into_option();
2040 self
2041 }
2042
2043 #[must_use]
2045 pub fn signal(mut self, signal: impl IntoOption<String>) -> Self {
2046 self.signal = signal.into_option();
2047 self
2048 }
2049
2050 #[must_use]
2056 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2057 self.meta = meta.into_option();
2058 self
2059 }
2060}
2061
2062#[serde_as]
2071#[skip_serializing_none]
2072#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2073#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2074#[serde(rename_all = "camelCase")]
2075#[non_exhaustive]
2076pub struct ClientCapabilities {
2077 #[serde_as(deserialize_as = "DefaultOnError")]
2080 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2081 #[serde(default)]
2082 pub fs: FileSystemCapabilities,
2083 #[serde_as(deserialize_as = "DefaultOnError")]
2085 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2086 #[serde(default)]
2087 pub terminal: bool,
2088 #[serde_as(deserialize_as = "DefaultOnError")]
2093 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2094 #[serde(default)]
2095 pub session: Option<ClientSessionCapabilities>,
2096 #[cfg(feature = "unstable_plan_operations")]
2105 #[serde_as(deserialize_as = "DefaultOnError")]
2106 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2107 #[serde(default)]
2108 pub plan: Option<PlanCapabilities>,
2109 #[serde_as(deserialize_as = "DefaultOnError")]
2113 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2114 #[serde(default)]
2115 pub auth: AuthCapabilities,
2116 #[serde_as(deserialize_as = "DefaultOnError")]
2122 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2123 #[serde(default)]
2124 pub elicitation: Option<ElicitationCapabilities>,
2125 #[cfg(feature = "unstable_nes")]
2134 #[serde_as(deserialize_as = "DefaultOnError")]
2135 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2136 #[serde(default)]
2137 pub nes: Option<ClientNesCapabilities>,
2138 #[cfg(feature = "unstable_nes")]
2144 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
2145 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
2146 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2147 pub position_encodings: Vec<PositionEncodingKind>,
2148
2149 #[serde_as(deserialize_as = "DefaultOnError")]
2155 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2156 #[serde(default)]
2157 #[serde(rename = "_meta")]
2158 pub meta: Option<Meta>,
2159}
2160
2161impl ClientCapabilities {
2162 #[must_use]
2164 pub fn new() -> Self {
2165 Self::default()
2166 }
2167
2168 #[must_use]
2171 pub fn fs(mut self, fs: FileSystemCapabilities) -> Self {
2172 self.fs = fs;
2173 self
2174 }
2175
2176 #[must_use]
2178 pub fn terminal(mut self, terminal: bool) -> Self {
2179 self.terminal = terminal;
2180 self
2181 }
2182
2183 #[must_use]
2185 pub fn session(mut self, session: impl IntoOption<ClientSessionCapabilities>) -> Self {
2186 self.session = session.into_option();
2187 self
2188 }
2189
2190 #[cfg(feature = "unstable_plan_operations")]
2199 #[must_use]
2200 pub fn plan(mut self, plan: impl IntoOption<PlanCapabilities>) -> Self {
2201 self.plan = plan.into_option();
2202 self
2203 }
2204
2205 #[must_use]
2209 pub fn auth(mut self, auth: AuthCapabilities) -> Self {
2210 self.auth = auth;
2211 self
2212 }
2213
2214 #[must_use]
2217 pub fn elicitation(mut self, elicitation: impl IntoOption<ElicitationCapabilities>) -> Self {
2218 self.elicitation = elicitation.into_option();
2219 self
2220 }
2221
2222 #[cfg(feature = "unstable_nes")]
2226 #[must_use]
2227 pub fn nes(mut self, nes: impl IntoOption<ClientNesCapabilities>) -> Self {
2228 self.nes = nes.into_option();
2229 self
2230 }
2231
2232 #[cfg(feature = "unstable_nes")]
2236 #[must_use]
2237 pub fn position_encodings(mut self, position_encodings: Vec<PositionEncodingKind>) -> Self {
2238 self.position_encodings = position_encodings;
2239 self
2240 }
2241
2242 #[must_use]
2248 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2249 self.meta = meta.into_option();
2250 self
2251 }
2252}
2253
2254#[serde_as]
2256#[skip_serializing_none]
2257#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2258#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2259#[serde(rename_all = "camelCase")]
2260#[non_exhaustive]
2261pub struct ClientSessionCapabilities {
2262 #[cfg(feature = "unstable_session_compaction")]
2269 #[serde_as(deserialize_as = "DefaultOnError")]
2270 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2271 #[serde(default)]
2272 pub compaction: Option<CompactionCapabilities>,
2273 #[serde_as(deserialize_as = "DefaultOnError")]
2278 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2279 #[serde(default)]
2280 pub config_options: Option<SessionConfigOptionsCapabilities>,
2281 #[cfg(feature = "unstable_session_notices")]
2290 #[serde_as(deserialize_as = "DefaultOnError")]
2291 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2292 #[serde(default)]
2293 pub notices: Option<NoticeCapabilities>,
2294 #[serde_as(deserialize_as = "DefaultOnError")]
2300 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2301 #[serde(default)]
2302 #[serde(rename = "_meta")]
2303 pub meta: Option<Meta>,
2304}
2305
2306impl ClientSessionCapabilities {
2307 #[must_use]
2309 pub fn new() -> Self {
2310 Self::default()
2311 }
2312
2313 #[cfg(feature = "unstable_session_compaction")]
2315 #[must_use]
2316 pub fn compaction(mut self, compaction: impl IntoOption<CompactionCapabilities>) -> Self {
2317 self.compaction = compaction.into_option();
2318 self
2319 }
2320
2321 #[must_use]
2326 pub fn config_options(
2327 mut self,
2328 config_options: impl IntoOption<SessionConfigOptionsCapabilities>,
2329 ) -> Self {
2330 self.config_options = config_options.into_option();
2331 self
2332 }
2333
2334 #[cfg(feature = "unstable_session_notices")]
2336 #[must_use]
2337 pub fn notices(mut self, notices: impl IntoOption<NoticeCapabilities>) -> Self {
2338 self.notices = notices.into_option();
2339 self
2340 }
2341
2342 #[must_use]
2348 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2349 self.meta = meta.into_option();
2350 self
2351 }
2352}
2353
2354#[cfg(feature = "unstable_session_compaction")]
2360#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2361#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2362#[serde(rename_all = "camelCase")]
2363#[non_exhaustive]
2364pub struct CompactionCapabilities {}
2365
2366#[cfg(feature = "unstable_session_compaction")]
2367impl CompactionCapabilities {
2368 #[must_use]
2370 pub fn new() -> Self {
2371 Self {}
2372 }
2373}
2374
2375#[cfg(feature = "unstable_session_notices")]
2381#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2382#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2383#[serde(rename_all = "camelCase")]
2384#[non_exhaustive]
2385pub struct NoticeCapabilities {}
2386
2387#[cfg(feature = "unstable_session_notices")]
2388impl NoticeCapabilities {
2389 #[must_use]
2391 pub fn new() -> Self {
2392 Self {}
2393 }
2394}
2395
2396#[serde_as]
2398#[skip_serializing_none]
2399#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2400#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2401#[serde(rename_all = "camelCase")]
2402#[non_exhaustive]
2403pub struct SessionConfigOptionsCapabilities {
2404 #[serde_as(deserialize_as = "DefaultOnError")]
2411 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2412 #[serde(default)]
2413 pub boolean: Option<BooleanConfigOptionCapabilities>,
2414 #[serde_as(deserialize_as = "DefaultOnError")]
2420 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2421 #[serde(default)]
2422 #[serde(rename = "_meta")]
2423 pub meta: Option<Meta>,
2424}
2425
2426impl SessionConfigOptionsCapabilities {
2427 #[must_use]
2429 pub fn new() -> Self {
2430 Self::default()
2431 }
2432
2433 #[must_use]
2440 pub fn boolean(mut self, boolean: impl IntoOption<BooleanConfigOptionCapabilities>) -> Self {
2441 self.boolean = boolean.into_option();
2442 self
2443 }
2444
2445 #[must_use]
2451 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2452 self.meta = meta.into_option();
2453 self
2454 }
2455}
2456
2457#[serde_as]
2461#[skip_serializing_none]
2462#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2463#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2464#[non_exhaustive]
2465pub struct BooleanConfigOptionCapabilities {
2466 #[serde_as(deserialize_as = "DefaultOnError")]
2472 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2473 #[serde(default)]
2474 #[serde(rename = "_meta")]
2475 pub meta: Option<Meta>,
2476}
2477
2478impl BooleanConfigOptionCapabilities {
2479 #[must_use]
2481 pub fn new() -> Self {
2482 Self::default()
2483 }
2484
2485 #[must_use]
2491 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2492 self.meta = meta.into_option();
2493 self
2494 }
2495}
2496
2497#[serde_as]
2503#[skip_serializing_none]
2504#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2505#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2506#[serde(rename_all = "camelCase")]
2507#[non_exhaustive]
2508pub struct AuthCapabilities {
2509 #[serde_as(deserialize_as = "DefaultOnError")]
2515 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2516 #[serde(default)]
2517 pub terminal: bool,
2518 #[serde_as(deserialize_as = "DefaultOnError")]
2524 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2525 #[serde(default)]
2526 #[serde(rename = "_meta")]
2527 pub meta: Option<Meta>,
2528}
2529
2530impl AuthCapabilities {
2531 #[must_use]
2533 pub fn new() -> Self {
2534 Self::default()
2535 }
2536
2537 #[must_use]
2544 pub fn terminal(mut self, terminal: bool) -> Self {
2545 self.terminal = terminal;
2546 self
2547 }
2548
2549 #[must_use]
2555 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2556 self.meta = meta.into_option();
2557 self
2558 }
2559}
2560
2561#[serde_as]
2565#[skip_serializing_none]
2566#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2567#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2568#[serde(rename_all = "camelCase")]
2569#[non_exhaustive]
2570pub struct FileSystemCapabilities {
2571 #[serde_as(deserialize_as = "DefaultOnError")]
2573 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2574 #[serde(default)]
2575 pub read_text_file: bool,
2576 #[serde_as(deserialize_as = "DefaultOnError")]
2578 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2579 #[serde(default)]
2580 pub write_text_file: bool,
2581 #[serde_as(deserialize_as = "DefaultOnError")]
2587 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2588 #[serde(default)]
2589 #[serde(rename = "_meta")]
2590 pub meta: Option<Meta>,
2591}
2592
2593impl FileSystemCapabilities {
2594 #[must_use]
2596 pub fn new() -> Self {
2597 Self::default()
2598 }
2599
2600 #[must_use]
2602 pub fn read_text_file(mut self, read_text_file: bool) -> Self {
2603 self.read_text_file = read_text_file;
2604 self
2605 }
2606
2607 #[must_use]
2609 pub fn write_text_file(mut self, write_text_file: bool) -> Self {
2610 self.write_text_file = write_text_file;
2611 self
2612 }
2613
2614 #[must_use]
2620 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2621 self.meta = meta.into_option();
2622 self
2623 }
2624}
2625
2626#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2632#[non_exhaustive]
2633pub struct ClientMethodNames {
2634 pub session_request_permission: &'static str,
2636 pub session_update: &'static str,
2638 pub fs_write_text_file: &'static str,
2640 pub fs_read_text_file: &'static str,
2642 pub terminal_create: &'static str,
2644 pub terminal_output: &'static str,
2646 pub terminal_release: &'static str,
2648 pub terminal_wait_for_exit: &'static str,
2650 pub terminal_kill: &'static str,
2652 #[cfg(feature = "unstable_mcp_over_acp")]
2654 pub mcp_connect: &'static str,
2655 #[cfg(feature = "unstable_mcp_over_acp")]
2657 pub mcp_message: &'static str,
2658 #[cfg(feature = "unstable_mcp_over_acp")]
2660 pub mcp_disconnect: &'static str,
2661 pub elicitation_create: &'static str,
2663 pub elicitation_complete: &'static str,
2665}
2666
2667pub const CLIENT_METHOD_NAMES: ClientMethodNames = ClientMethodNames {
2669 session_update: SESSION_UPDATE_NOTIFICATION,
2670 session_request_permission: SESSION_REQUEST_PERMISSION_METHOD_NAME,
2671 fs_write_text_file: FS_WRITE_TEXT_FILE_METHOD_NAME,
2672 fs_read_text_file: FS_READ_TEXT_FILE_METHOD_NAME,
2673 terminal_create: TERMINAL_CREATE_METHOD_NAME,
2674 terminal_output: TERMINAL_OUTPUT_METHOD_NAME,
2675 terminal_release: TERMINAL_RELEASE_METHOD_NAME,
2676 terminal_wait_for_exit: TERMINAL_WAIT_FOR_EXIT_METHOD_NAME,
2677 terminal_kill: TERMINAL_KILL_METHOD_NAME,
2678 #[cfg(feature = "unstable_mcp_over_acp")]
2679 mcp_connect: MCP_CONNECT_METHOD_NAME,
2680 #[cfg(feature = "unstable_mcp_over_acp")]
2681 mcp_message: MCP_MESSAGE_METHOD_NAME,
2682 #[cfg(feature = "unstable_mcp_over_acp")]
2683 mcp_disconnect: MCP_DISCONNECT_METHOD_NAME,
2684 elicitation_create: ELICITATION_CREATE_METHOD_NAME,
2685 elicitation_complete: ELICITATION_COMPLETE_NOTIFICATION,
2686};
2687
2688pub(crate) const SESSION_UPDATE_NOTIFICATION: &str = "session/update";
2690pub(crate) const SESSION_REQUEST_PERMISSION_METHOD_NAME: &str = "session/request_permission";
2692pub(crate) const FS_WRITE_TEXT_FILE_METHOD_NAME: &str = "fs/write_text_file";
2694pub(crate) const FS_READ_TEXT_FILE_METHOD_NAME: &str = "fs/read_text_file";
2696pub(crate) const TERMINAL_CREATE_METHOD_NAME: &str = "terminal/create";
2698pub(crate) const TERMINAL_OUTPUT_METHOD_NAME: &str = "terminal/output";
2700pub(crate) const TERMINAL_RELEASE_METHOD_NAME: &str = "terminal/release";
2702pub(crate) const TERMINAL_WAIT_FOR_EXIT_METHOD_NAME: &str = "terminal/wait_for_exit";
2704pub(crate) const TERMINAL_KILL_METHOD_NAME: &str = "terminal/kill";
2706pub(crate) const ELICITATION_CREATE_METHOD_NAME: &str = "elicitation/create";
2708pub(crate) const ELICITATION_COMPLETE_NOTIFICATION: &str = "elicitation/complete";
2710
2711#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2718#[derive(Clone, Debug, Serialize, Deserialize)]
2719#[serde(untagged)]
2720#[cfg_attr(feature = "schemars", schemars(inline))]
2721#[non_exhaustive]
2722#[allow(clippy::large_enum_variant)]
2723pub enum AgentRequest {
2724 WriteTextFileRequest(WriteTextFileRequest),
2731 ReadTextFileRequest(ReadTextFileRequest),
2738 RequestPermissionRequest(RequestPermissionRequest),
2749 CreateTerminalRequest(CreateTerminalRequest),
2764 TerminalOutputRequest(TerminalOutputRequest),
2771 ReleaseTerminalRequest(ReleaseTerminalRequest),
2784 WaitForTerminalExitRequest(WaitForTerminalExitRequest),
2788 KillTerminalRequest(KillTerminalRequest),
2801 CreateElicitationRequest(CreateElicitationRequest),
2805 #[cfg(feature = "unstable_mcp_over_acp")]
2811 ConnectMcpRequest(ConnectMcpRequest),
2812 #[cfg(feature = "unstable_mcp_over_acp")]
2818 MessageMcpRequest(MessageMcpRequest),
2819 #[cfg(feature = "unstable_mcp_over_acp")]
2825 DisconnectMcpRequest(DisconnectMcpRequest),
2826 ExtMethodRequest(ExtRequest),
2834}
2835
2836impl AgentRequest {
2837 #[must_use]
2839 pub fn method(&self) -> &str {
2840 match self {
2841 Self::WriteTextFileRequest(_) => CLIENT_METHOD_NAMES.fs_write_text_file,
2842 Self::ReadTextFileRequest(_) => CLIENT_METHOD_NAMES.fs_read_text_file,
2843 Self::RequestPermissionRequest(_) => CLIENT_METHOD_NAMES.session_request_permission,
2844 Self::CreateTerminalRequest(_) => CLIENT_METHOD_NAMES.terminal_create,
2845 Self::TerminalOutputRequest(_) => CLIENT_METHOD_NAMES.terminal_output,
2846 Self::ReleaseTerminalRequest(_) => CLIENT_METHOD_NAMES.terminal_release,
2847 Self::WaitForTerminalExitRequest(_) => CLIENT_METHOD_NAMES.terminal_wait_for_exit,
2848 Self::KillTerminalRequest(_) => CLIENT_METHOD_NAMES.terminal_kill,
2849 Self::CreateElicitationRequest(_) => CLIENT_METHOD_NAMES.elicitation_create,
2850 #[cfg(feature = "unstable_mcp_over_acp")]
2851 Self::ConnectMcpRequest(_) => CLIENT_METHOD_NAMES.mcp_connect,
2852 #[cfg(feature = "unstable_mcp_over_acp")]
2853 Self::MessageMcpRequest(_) => CLIENT_METHOD_NAMES.mcp_message,
2854 #[cfg(feature = "unstable_mcp_over_acp")]
2855 Self::DisconnectMcpRequest(_) => CLIENT_METHOD_NAMES.mcp_disconnect,
2856 Self::ExtMethodRequest(ext_request) => &ext_request.method,
2857 }
2858 }
2859}
2860
2861#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2868#[derive(Clone, Debug, Serialize, Deserialize)]
2869#[serde(untagged)]
2870#[cfg_attr(feature = "schemars", schemars(inline))]
2871#[non_exhaustive]
2872pub enum ClientResponse {
2873 WriteTextFileResponse(#[serde(default)] WriteTextFileResponse),
2875 ReadTextFileResponse(ReadTextFileResponse),
2877 RequestPermissionResponse(RequestPermissionResponse),
2879 CreateTerminalResponse(CreateTerminalResponse),
2881 TerminalOutputResponse(TerminalOutputResponse),
2883 ReleaseTerminalResponse(#[serde(default)] ReleaseTerminalResponse),
2885 WaitForTerminalExitResponse(WaitForTerminalExitResponse),
2887 KillTerminalResponse(#[serde(default)] KillTerminalResponse),
2889 CreateElicitationResponse(CreateElicitationResponse),
2891 #[cfg(feature = "unstable_mcp_over_acp")]
2893 ConnectMcpResponse(ConnectMcpResponse),
2894 #[cfg(feature = "unstable_mcp_over_acp")]
2896 DisconnectMcpResponse(#[serde(default)] DisconnectMcpResponse),
2897 #[cfg(feature = "unstable_mcp_over_acp")]
2899 MessageMcpResponse(MessageMcpResponse),
2900 ExtMethodResponse(ExtResponse),
2902}
2903
2904#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2911#[derive(Clone, Debug, Serialize, Deserialize)]
2912#[serde(untagged)]
2913#[expect(clippy::large_enum_variant)]
2914#[cfg_attr(feature = "schemars", schemars(inline))]
2915#[non_exhaustive]
2916pub enum AgentNotification {
2917 SessionNotification(SessionNotification),
2929 CompleteElicitationNotification(CompleteElicitationNotification),
2933 #[cfg(feature = "unstable_mcp_over_acp")]
2939 MessageMcpNotification(MessageMcpNotification),
2940 ExtNotification(ExtNotification),
2948}
2949
2950impl AgentNotification {
2951 #[must_use]
2953 pub fn method(&self) -> &str {
2954 match self {
2955 Self::SessionNotification(_) => CLIENT_METHOD_NAMES.session_update,
2956 Self::CompleteElicitationNotification(_) => CLIENT_METHOD_NAMES.elicitation_complete,
2957 #[cfg(feature = "unstable_mcp_over_acp")]
2958 Self::MessageMcpNotification(_) => CLIENT_METHOD_NAMES.mcp_message,
2959 Self::ExtNotification(ext_notification) => &ext_notification.method,
2960 }
2961 }
2962}
2963
2964#[cfg(test)]
2965mod tests {
2966 use super::*;
2967
2968 #[cfg(feature = "unstable_session_notices")]
2969 #[test]
2970 fn notice_preserves_wire_shape_nullable_fields_and_open_severity() {
2971 use serde_json::json;
2972
2973 let mut meta = Meta::new();
2974 meta.insert("source".into(), json!("fallback"));
2975 assert_eq!(
2976 serde_json::to_value(SessionUpdate::Notice(
2977 Notice::new(NoticeSeverity::Warning, "MCP server unavailable")
2978 .description("Continuing without it.")
2979 .meta(meta),
2980 ))
2981 .unwrap(),
2982 json!({
2983 "sessionUpdate": "notice",
2984 "severity": "warning",
2985 "title": "MCP server unavailable",
2986 "description": "Continuing without it.",
2987 "_meta": { "source": "fallback" }
2988 })
2989 );
2990
2991 assert_eq!(
2992 serde_json::to_value(SessionUpdate::Notice(Notice::new(
2993 NoticeSeverity::Info,
2994 "Indexing workspace",
2995 )))
2996 .unwrap(),
2997 json!({
2998 "sessionUpdate": "notice",
2999 "severity": "info",
3000 "title": "Indexing workspace"
3001 })
3002 );
3003
3004 let SessionUpdate::Notice(notice) = serde_json::from_value(json!({
3005 "sessionUpdate": "notice",
3006 "severity": "critical",
3007 "title": "Provider degraded",
3008 "description": null,
3009 "_meta": null
3010 }))
3011 .unwrap() else {
3012 panic!("expected notice");
3013 };
3014
3015 assert_eq!(
3016 notice.severity,
3017 NoticeSeverity::Other("critical".to_string())
3018 );
3019 assert_eq!(notice.description, None);
3020 assert_eq!(notice.meta, None);
3021 assert_eq!(
3022 serde_json::to_value(SessionUpdate::Notice(notice)).unwrap(),
3023 json!({
3024 "sessionUpdate": "notice",
3025 "severity": "critical",
3026 "title": "Provider degraded"
3027 })
3028 );
3029 }
3030
3031 #[cfg(feature = "unstable_session_notices")]
3032 #[test]
3033 fn notice_requires_non_null_severity_and_title() {
3034 use serde_json::json;
3035
3036 for malformed in [
3037 json!({
3038 "sessionUpdate": "notice",
3039 "severity": "warning"
3040 }),
3041 json!({
3042 "sessionUpdate": "notice",
3043 "severity": "warning",
3044 "title": null
3045 }),
3046 json!({
3047 "sessionUpdate": "notice",
3048 "title": "MCP server unavailable"
3049 }),
3050 json!({
3051 "sessionUpdate": "notice",
3052 "severity": null,
3053 "title": "MCP server unavailable"
3054 }),
3055 ] {
3056 assert!(serde_json::from_value::<SessionUpdate>(malformed).is_err());
3057 }
3058 }
3059
3060 #[cfg(feature = "unstable_session_notices")]
3061 #[test]
3062 fn notice_capability_advertises_support_only_when_present() {
3063 use serde_json::json;
3064
3065 let capabilities = ClientCapabilities::new()
3066 .session(ClientSessionCapabilities::new().notices(NoticeCapabilities::new()));
3067 let value = serde_json::to_value(&capabilities).unwrap();
3068 assert_eq!(value["session"], json!({ "notices": {} }));
3069 assert_eq!(
3070 serde_json::from_value::<ClientCapabilities>(value).unwrap(),
3071 capabilities
3072 );
3073
3074 for unsupported in [
3075 json!({}),
3076 json!({ "session": null }),
3077 json!({ "session": {} }),
3078 json!({ "session": { "notices": null } }),
3079 json!({ "session": { "notices": false } }),
3080 json!({ "session": { "notices": true } }),
3081 json!({ "session": { "notices": "supported" } }),
3082 ] {
3083 let capabilities: ClientCapabilities = serde_json::from_value(unsupported).unwrap();
3084 assert!(
3085 capabilities
3086 .session
3087 .and_then(|session| session.notices)
3088 .is_none()
3089 );
3090 }
3091
3092 assert_eq!(
3093 serde_json::to_value(
3094 ClientSessionCapabilities::new()
3095 .notices(NoticeCapabilities::new())
3096 .notices(None)
3097 )
3098 .unwrap(),
3099 json!({})
3100 );
3101 }
3102
3103 #[cfg(not(feature = "unstable_session_notices"))]
3104 #[test]
3105 fn unsupported_notice_capability_is_ignored() {
3106 use serde_json::json;
3107
3108 let capabilities: ClientSessionCapabilities =
3109 serde_json::from_value(json!({ "notices": {} })).unwrap();
3110 assert_eq!(serde_json::to_value(capabilities).unwrap(), json!({}));
3111 }
3112
3113 #[cfg(not(feature = "unstable_session_notices"))]
3114 #[test]
3115 fn unsupported_notice_is_rejected_by_closed_update_union() {
3116 use serde_json::json;
3117
3118 assert!(
3119 serde_json::from_value::<SessionUpdate>(json!({
3120 "sessionUpdate": "notice",
3121 "severity": "warning",
3122 "title": "MCP server unavailable"
3123 }))
3124 .is_err()
3125 );
3126 }
3127
3128 #[cfg(feature = "unstable_session_compaction")]
3129 #[test]
3130 fn compaction_updates_preserve_patch_and_open_status_semantics() {
3131 use serde_json::json;
3132
3133 assert_eq!(
3134 serde_json::to_value(SessionUpdate::CompactionUpdate(CompactionUpdate::new(
3135 "cmp_001",
3136 CompactionStatus::InProgress,
3137 )))
3138 .unwrap(),
3139 json!({
3140 "sessionUpdate": "compaction_update",
3141 "compactionId": "cmp_001",
3142 "status": "in_progress"
3143 })
3144 );
3145
3146 let SessionUpdate::CompactionUpdate(update) = serde_json::from_value(json!({
3147 "sessionUpdate": "compaction_update",
3148 "compactionId": "cmp_001",
3149 "status": "paused",
3150 "summary": null,
3151 "error": "waiting"
3152 }))
3153 .unwrap() else {
3154 panic!("expected compaction update");
3155 };
3156 assert_eq!(update.status, CompactionStatus::Other("paused".into()));
3157 assert!(update.summary.is_null());
3158 assert_eq!(update.error.value().map(String::as_str), Some("waiting"));
3159 assert!(update.meta.is_undefined());
3160 }
3161
3162 #[cfg(feature = "unstable_session_compaction")]
3163 #[test]
3164 fn compaction_chunk_and_v1_capability_serialize() {
3165 use serde_json::json;
3166
3167 assert_eq!(
3168 serde_json::to_value(SessionUpdate::CompactionSummaryChunk(
3169 CompactionSummaryChunk::new(
3170 "cmp_001",
3171 ContentBlock::Text(crate::v1::TextContent::new("retained")),
3172 ),
3173 ))
3174 .unwrap(),
3175 json!({
3176 "sessionUpdate": "compaction_summary_chunk",
3177 "compactionId": "cmp_001",
3178 "content": { "type": "text", "text": "retained" }
3179 })
3180 );
3181 assert_eq!(
3182 serde_json::to_value(
3183 ClientSessionCapabilities::new().compaction(CompactionCapabilities::new())
3184 )
3185 .unwrap(),
3186 json!({ "compaction": {} })
3187 );
3188 let absent: ClientSessionCapabilities = serde_json::from_value(json!({})).unwrap();
3189 let null: ClientSessionCapabilities =
3190 serde_json::from_value(json!({ "compaction": null })).unwrap();
3191 assert!(absent.compaction.is_none());
3192 assert!(null.compaction.is_none());
3193 }
3194
3195 #[test]
3196 fn test_elicitation_capability_semantics() {
3197 use serde_json::json;
3198
3199 let unsupported: ClientCapabilities = serde_json::from_value(json!({})).unwrap();
3200 assert!(unsupported.elicitation.is_none());
3201
3202 let null: ClientCapabilities =
3203 serde_json::from_value(json!({ "elicitation": null })).unwrap();
3204 assert!(null.elicitation.is_none());
3205
3206 let malformed: ClientCapabilities =
3207 serde_json::from_value(json!({ "elicitation": false })).unwrap();
3208 assert!(malformed.elicitation.is_none());
3209
3210 let empty: ClientCapabilities =
3211 serde_json::from_value(json!({ "elicitation": {} })).unwrap();
3212 let empty = empty.elicitation.expect("present capability");
3213 assert!(!empty.supports_form());
3214 assert!(!empty.supports_url());
3215
3216 let form_only: ClientCapabilities = serde_json::from_value(json!({
3217 "elicitation": { "form": {} }
3218 }))
3219 .unwrap();
3220 let form_only = form_only.elicitation.expect("advertised capability");
3221 assert!(form_only.supports_form());
3222 assert!(!form_only.supports_url());
3223
3224 let url_only: ClientCapabilities = serde_json::from_value(json!({
3225 "elicitation": { "url": {} }
3226 }))
3227 .unwrap();
3228 let url_only = url_only.elicitation.expect("advertised capability");
3229 assert!(!url_only.supports_form());
3230 assert!(url_only.supports_url());
3231
3232 let both: ClientCapabilities = serde_json::from_value(json!({
3233 "elicitation": { "form": {}, "url": {} }
3234 }))
3235 .unwrap();
3236 let both = both.elicitation.expect("advertised capability");
3237 assert!(both.supports_form());
3238 assert!(both.supports_url());
3239 }
3240
3241 #[test]
3242 fn test_elicitation_method_routing_and_envelopes() {
3243 use serde_json::json;
3244
3245 assert_eq!(CLIENT_METHOD_NAMES.elicitation_create, "elicitation/create");
3246 assert_eq!(
3247 CLIENT_METHOD_NAMES.elicitation_complete,
3248 "elicitation/complete"
3249 );
3250
3251 let request = AgentRequest::CreateElicitationRequest(CreateElicitationRequest::new(
3252 crate::v1::ElicitationFormMode::new(
3253 crate::v1::ElicitationSessionScope::new("sess_1"),
3254 crate::v1::ElicitationSchema::new(),
3255 ),
3256 "Choose a value",
3257 ));
3258 assert_eq!(request.method(), "elicitation/create");
3259 let method = Arc::from(request.method());
3260 let request = crate::v1::JsonRpcMessage::wrap(crate::v1::Request {
3261 id: crate::v1::RequestId::Number(7),
3262 method,
3263 params: Some(request),
3264 });
3265 assert_eq!(
3266 serde_json::to_value(request).unwrap(),
3267 json!({
3268 "jsonrpc": "2.0",
3269 "id": 7,
3270 "method": "elicitation/create",
3271 "params": {
3272 "mode": "form",
3273 "sessionId": "sess_1",
3274 "message": "Choose a value",
3275 "requestedSchema": { "type": "object", "properties": {} }
3276 }
3277 })
3278 );
3279
3280 let notification = AgentNotification::CompleteElicitationNotification(
3281 CompleteElicitationNotification::new("elic_1"),
3282 );
3283 assert_eq!(notification.method(), "elicitation/complete");
3284 let method = Arc::from(notification.method());
3285 let notification = crate::v1::JsonRpcMessage::wrap(crate::v1::Notification {
3286 method,
3287 params: Some(notification),
3288 });
3289 assert_eq!(
3290 serde_json::to_value(notification).unwrap(),
3291 json!({
3292 "jsonrpc": "2.0",
3293 "method": "elicitation/complete",
3294 "params": { "elicitationId": "elic_1" }
3295 })
3296 );
3297 }
3298
3299 #[test]
3300 fn test_client_capabilities_default_on_malformed_values() {
3301 use serde_json::json;
3302
3303 let capabilities: ClientCapabilities = serde_json::from_value(json!({
3304 "fs": {
3305 "readTextFile": "yes",
3306 "writeTextFile": true
3307 },
3308 "terminal": {}
3309 }))
3310 .unwrap();
3311
3312 assert!(!capabilities.fs.read_text_file);
3313 assert!(capabilities.fs.write_text_file);
3314 assert!(!capabilities.terminal);
3315
3316 let capabilities: ClientCapabilities = serde_json::from_value(json!({
3317 "fs": false
3318 }))
3319 .unwrap();
3320 assert_eq!(capabilities.fs, FileSystemCapabilities::default());
3321
3322 {
3323 let capabilities: ClientCapabilities = serde_json::from_value(json!({
3324 "auth": false
3325 }))
3326 .unwrap();
3327 assert_eq!(capabilities.auth, AuthCapabilities::default());
3328
3329 let capabilities: AuthCapabilities = serde_json::from_value(json!({
3330 "terminal": {}
3331 }))
3332 .unwrap();
3333 assert!(!capabilities.terminal);
3334 }
3335 }
3336
3337 #[test]
3338 fn test_serialization_behavior() {
3339 use serde_json::json;
3340
3341 assert_eq!(
3342 serde_json::from_value::<SessionInfoUpdate>(json!({})).unwrap(),
3343 SessionInfoUpdate {
3344 title: MaybeUndefined::Undefined,
3345 updated_at: MaybeUndefined::Undefined,
3346 meta: None
3347 }
3348 );
3349 assert_eq!(
3350 serde_json::from_value::<SessionInfoUpdate>(json!({"title": null, "updatedAt": null}))
3351 .unwrap(),
3352 SessionInfoUpdate {
3353 title: MaybeUndefined::Null,
3354 updated_at: MaybeUndefined::Null,
3355 meta: None
3356 }
3357 );
3358 assert_eq!(
3359 serde_json::from_value::<SessionInfoUpdate>(
3360 json!({"title": "title", "updatedAt": "timestamp"})
3361 )
3362 .unwrap(),
3363 SessionInfoUpdate {
3364 title: MaybeUndefined::Value("title".to_string()),
3365 updated_at: MaybeUndefined::Value("timestamp".to_string()),
3366 meta: None
3367 }
3368 );
3369
3370 assert_eq!(
3371 serde_json::to_value(SessionInfoUpdate::new()).unwrap(),
3372 json!({})
3373 );
3374 assert_eq!(
3375 serde_json::to_value(SessionInfoUpdate::new().title("title")).unwrap(),
3376 json!({"title": "title"})
3377 );
3378 assert_eq!(
3379 serde_json::to_value(SessionInfoUpdate::new().title(None)).unwrap(),
3380 json!({"title": null})
3381 );
3382 assert_eq!(
3383 serde_json::to_value(
3384 SessionInfoUpdate::new()
3385 .title("title")
3386 .title(MaybeUndefined::Undefined)
3387 )
3388 .unwrap(),
3389 json!({})
3390 );
3391 }
3392
3393 #[test]
3394 fn test_content_chunk_message_id_serialization() {
3395 use serde_json::json;
3396
3397 assert_eq!(
3398 serde_json::to_value(SessionUpdate::AgentMessageChunk(ContentChunk::new(
3399 ContentBlock::Text(crate::v1::TextContent::new("Hello"))
3400 )))
3401 .unwrap(),
3402 json!({
3403 "sessionUpdate": "agent_message_chunk",
3404 "content": {
3405 "type": "text",
3406 "text": "Hello"
3407 }
3408 })
3409 );
3410
3411 assert_eq!(
3412 serde_json::to_value(SessionUpdate::AgentMessageChunk(
3413 ContentChunk::new(ContentBlock::Text(crate::v1::TextContent::new("Hello")))
3414 .message_id("msg_agent_c42b9")
3415 ))
3416 .unwrap(),
3417 json!({
3418 "sessionUpdate": "agent_message_chunk",
3419 "messageId": "msg_agent_c42b9",
3420 "content": {
3421 "type": "text",
3422 "text": "Hello"
3423 }
3424 })
3425 );
3426
3427 let SessionUpdate::AgentMessageChunk(chunk) = serde_json::from_value(json!({
3428 "sessionUpdate": "agent_message_chunk",
3429 "messageId": null,
3430 "content": {
3431 "type": "text",
3432 "text": "Hello"
3433 }
3434 }))
3435 .unwrap() else {
3436 panic!("expected agent message chunk");
3437 };
3438
3439 assert_eq!(chunk.message_id, None);
3440 }
3441
3442 #[test]
3443 fn test_usage_update_serialization() {
3444 use serde_json::json;
3445
3446 assert_eq!(
3447 serde_json::to_value(SessionUpdate::UsageUpdate(UsageUpdate::new(
3448 53_000, 200_000
3449 )))
3450 .unwrap(),
3451 json!({
3452 "sessionUpdate": "usage_update",
3453 "used": 53000,
3454 "size": 200_000
3455 })
3456 );
3457
3458 assert_eq!(
3459 serde_json::to_value(SessionUpdate::UsageUpdate(
3460 UsageUpdate::new(53_000, 200_000).cost(Cost::new(0.045, "USD"))
3461 ))
3462 .unwrap(),
3463 json!({
3464 "sessionUpdate": "usage_update",
3465 "used": 53000,
3466 "size": 200_000,
3467 "cost": {
3468 "amount": 0.045,
3469 "currency": "USD"
3470 }
3471 })
3472 );
3473
3474 let SessionUpdate::UsageUpdate(update) = serde_json::from_value(json!({
3475 "sessionUpdate": "usage_update",
3476 "used": 53000,
3477 "size": 200_000,
3478 "cost": null
3479 }))
3480 .unwrap() else {
3481 panic!("expected usage update");
3482 };
3483
3484 assert_eq!(update.cost, None);
3485 }
3486
3487 #[cfg(feature = "unstable_nes")]
3488 #[test]
3489 fn test_client_capabilities_position_encodings_serialization() {
3490 use serde_json::json;
3491
3492 let capabilities = ClientCapabilities::new().position_encodings(vec![
3493 PositionEncodingKind::Utf32,
3494 PositionEncodingKind::Utf16,
3495 ]);
3496 let json = serde_json::to_value(&capabilities).unwrap();
3497
3498 assert_eq!(json["positionEncodings"], json!(["utf-32", "utf-16"]));
3499 }
3500
3501 #[test]
3502 fn test_client_capabilities_boolean_config_options_serialization() {
3503 use serde_json::json;
3504
3505 let capabilities = ClientCapabilities::new().session(
3506 ClientSessionCapabilities::new().config_options(
3507 SessionConfigOptionsCapabilities::new()
3508 .boolean(BooleanConfigOptionCapabilities::new()),
3509 ),
3510 );
3511 let json = serde_json::to_value(&capabilities).unwrap();
3512
3513 assert_eq!(json["session"]["configOptions"]["boolean"], json!({}));
3514
3515 let omitted: ClientCapabilities = serde_json::from_value(json!({})).unwrap();
3516 assert!(omitted.session.is_none());
3517
3518 let null_session: ClientCapabilities = serde_json::from_value(json!({
3519 "session": null
3520 }))
3521 .unwrap();
3522 assert!(null_session.session.is_none());
3523
3524 let null_config_options: ClientCapabilities = serde_json::from_value(json!({
3525 "session": {
3526 "configOptions": null
3527 }
3528 }))
3529 .unwrap();
3530 assert!(
3531 null_config_options
3532 .session
3533 .and_then(|session| session.config_options)
3534 .is_none()
3535 );
3536
3537 let null_boolean: ClientCapabilities = serde_json::from_value(json!({
3538 "session": {
3539 "configOptions": {
3540 "boolean": null
3541 }
3542 }
3543 }))
3544 .unwrap();
3545 assert!(
3546 null_boolean
3547 .session
3548 .and_then(|session| session.config_options)
3549 .and_then(|config_options| config_options.boolean)
3550 .is_none()
3551 );
3552 }
3553
3554 #[cfg(feature = "unstable_plan_operations")]
3555 #[test]
3556 fn test_plan_operations_serialization() {
3557 use serde_json::json;
3558
3559 use crate::v1::{PlanEntry, PlanEntryPriority, PlanEntryStatus, PlanUpdateContent};
3560
3561 let plan_update = SessionUpdate::PlanUpdate(PlanUpdate::new(PlanUpdateContent::items(
3562 "plan-1",
3563 vec![PlanEntry::new(
3564 "Step 1",
3565 PlanEntryPriority::High,
3566 PlanEntryStatus::Pending,
3567 )],
3568 )));
3569
3570 assert_eq!(
3571 serde_json::to_value(plan_update).unwrap(),
3572 json!({
3573 "sessionUpdate": "plan_update",
3574 "plan": {
3575 "type": "items",
3576 "planId": "plan-1",
3577 "entries": [
3578 {
3579 "content": "Step 1",
3580 "priority": "high",
3581 "status": "pending"
3582 }
3583 ]
3584 }
3585 })
3586 );
3587
3588 assert_eq!(
3589 serde_json::to_value(SessionUpdate::PlanRemoved(PlanRemoved::new("plan-1"))).unwrap(),
3590 json!({
3591 "sessionUpdate": "plan_removed",
3592 "planId": "plan-1"
3593 })
3594 );
3595
3596 let capabilities = ClientCapabilities::new().plan(PlanCapabilities::new());
3597 let json = serde_json::to_value(&capabilities).unwrap();
3598 assert_eq!(json["plan"], json!({}));
3599
3600 assert_eq!(
3601 serde_json::from_value::<ClientCapabilities>(json!({ "plan": null }))
3602 .unwrap()
3603 .plan,
3604 None
3605 );
3606 }
3607
3608 #[cfg(feature = "unstable_mcp_over_acp")]
3609 #[test]
3610 fn test_agent_mcp_request_method_names() {
3611 use serde_json::json;
3612
3613 let params: serde_json::Map<String, serde_json::Value> =
3614 [("cursor".to_string(), json!("abc"))].into_iter().collect();
3615
3616 assert_eq!(CLIENT_METHOD_NAMES.mcp_connect, "mcp/connect");
3617 assert_eq!(CLIENT_METHOD_NAMES.mcp_message, "mcp/message");
3618 assert_eq!(CLIENT_METHOD_NAMES.mcp_disconnect, "mcp/disconnect");
3619
3620 assert_eq!(
3621 AgentRequest::ConnectMcpRequest(ConnectMcpRequest::new("server-1")).method(),
3622 "mcp/connect"
3623 );
3624 assert_eq!(
3625 AgentRequest::MessageMcpRequest(MessageMcpRequest::new("conn-1", "tools/list"))
3626 .method(),
3627 "mcp/message"
3628 );
3629 assert_eq!(
3630 AgentRequest::DisconnectMcpRequest(DisconnectMcpRequest::new("conn-1")).method(),
3631 "mcp/disconnect"
3632 );
3633 assert_eq!(
3634 AgentNotification::MessageMcpNotification(MessageMcpNotification::new(
3635 "conn-1",
3636 "notifications/progress"
3637 ))
3638 .method(),
3639 "mcp/message"
3640 );
3641
3642 assert_eq!(
3643 serde_json::to_value(ConnectMcpRequest::new("server-1")).unwrap(),
3644 json!({ "serverId": "server-1" })
3645 );
3646 assert_eq!(
3647 serde_json::to_value(ConnectMcpResponse::new("conn-1")).unwrap(),
3648 json!({ "connectionId": "conn-1" })
3649 );
3650 assert_eq!(
3651 serde_json::to_value(MessageMcpRequest::new("conn-1", "tools/list").params(params))
3652 .unwrap(),
3653 json!({
3654 "connectionId": "conn-1",
3655 "method": "tools/list",
3656 "params": { "cursor": "abc" }
3657 })
3658 );
3659 assert_eq!(
3660 serde_json::to_value(DisconnectMcpRequest::new("conn-1")).unwrap(),
3661 json!({ "connectionId": "conn-1" })
3662 );
3663 assert_eq!(
3664 serde_json::to_value(MessageMcpNotification::new(
3665 "conn-1",
3666 "notifications/progress"
3667 ))
3668 .unwrap(),
3669 json!({
3670 "connectionId": "conn-1",
3671 "method": "notifications/progress"
3672 })
3673 );
3674
3675 let request_with_null_params: MessageMcpRequest = serde_json::from_value(json!({
3676 "connectionId": "conn-1",
3677 "method": "tools/list",
3678 "params": null
3679 }))
3680 .unwrap();
3681 assert_eq!(request_with_null_params.params, None);
3682 }
3683
3684 #[test]
3685 fn request_permission_request_rejects_malformed_options() {
3686 use serde_json::json;
3687
3688 assert!(
3689 serde_json::from_value::<RequestPermissionRequest>(json!({
3690 "sessionId": "sess-1",
3691 "toolCall": {"toolCallId": "tc-1"},
3692 "options": "not-an-array"
3693 }))
3694 .is_err()
3695 );
3696 assert!(
3697 serde_json::from_value::<RequestPermissionRequest>(json!({
3698 "sessionId": "sess-1",
3699 "toolCall": {"toolCallId": "tc-1"},
3700 "options": [{"optionId": "allow"}]
3701 }))
3702 .is_err()
3703 );
3704 }
3705}